From 7a4dd9640668a258383e8e14ce5ae230d33927e0 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Fri, 22 Nov 2019 17:07:56 +0100 Subject: [PATCH 001/237] udev-rules: Remove debug spew from udev rules Some debug output was ending up inside the udev rules. Remove it again. --- libfprint/fprint-list-udev-rules.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/libfprint/fprint-list-udev-rules.c b/libfprint/fprint-list-udev-rules.c index 0c1b0591..c0a3337f 100644 --- a/libfprint/fprint-list-udev-rules.c +++ b/libfprint/fprint-list-udev-rules.c @@ -99,8 +99,6 @@ main (int argc, char **argv) g_autoptr(GArray) drivers = g_array_new (FALSE, FALSE, sizeof (GType)); guint i; - g_print ("%p\n", drivers); - g_print ("%p\n", fpi_get_driver_types); fpi_get_driver_types (drivers); printed = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); From 8b28133beee5122c2a26c361cf2f2095888be2c2 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 25 Nov 2019 18:34:16 +0100 Subject: [PATCH 002/237] elan: Fix potential leak of dark frame Dark frames would be leaked, add an explicit free to avoid this. --- libfprint/drivers/elan.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libfprint/drivers/elan.c b/libfprint/drivers/elan.c index b417a419..6e9107e5 100644 --- a/libfprint/drivers/elan.c +++ b/libfprint/drivers/elan.c @@ -223,6 +223,7 @@ elan_save_img_frame (FpiDeviceElan *elandev) { fp_dbg ("frame darker than background; finger present during calibration?"); + g_free (frame); return -1; } From b16245ad588bf7467a3580726184f48af328414d Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 25 Nov 2019 18:34:55 +0100 Subject: [PATCH 003/237] elan: Fix switch in change_state The switch in change_state had a useless break and a useless if clause. Remove both. --- libfprint/drivers/elan.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/libfprint/drivers/elan.c b/libfprint/drivers/elan.c index 6e9107e5..961366ee 100644 --- a/libfprint/drivers/elan.c +++ b/libfprint/drivers/elan.c @@ -975,8 +975,6 @@ elan_change_state (FpImageDevice *idev) switch (next_state) { - break; - case FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON: /* activation completed or another enroll stage started */ elan_calibrate (dev); @@ -988,9 +986,8 @@ elan_change_state (FpImageDevice *idev) case FP_IMAGE_DEVICE_STATE_INACTIVE: case FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF: - if (self->dev_state != FP_IMAGE_DEVICE_STATE_INACTIVE || - self->dev_state != FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF) - elan_stop_capture (dev); + elan_stop_capture (dev); + break; } } From ada5d488fa769b4818a17a7042b8aa94ceea1519 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 25 Nov 2019 18:35:50 +0100 Subject: [PATCH 004/237] synaptics: Correctly unref pointer array The pointer arrays were unref'ed using g_ptr_array_free rather than g_ptr_array_unref from g_clear_pointer. Switch to the correct function. --- libfprint/drivers/synaptics/synaptics.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index b1d73650..4bac934a 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -447,7 +447,7 @@ list_msg_cb (FpiDeviceSynaptics *self, if (error) { - g_clear_pointer (&self->list_result, g_ptr_array_free); + g_clear_pointer (&self->list_result, g_ptr_array_unref); fpi_device_list_complete (FP_DEVICE (self), NULL, error); return; } @@ -468,7 +468,7 @@ list_msg_cb (FpiDeviceSynaptics *self, else { fp_info ("Failed to query enrolled users: %d", resp->result); - g_clear_pointer (&self->list_result, g_ptr_array_free); + g_clear_pointer (&self->list_result, g_ptr_array_unref); fpi_device_list_complete (FP_DEVICE (self), NULL, fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, From 8ba6f4dad2d9c04217153f32c11ac69967ddec00 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 25 Nov 2019 18:37:12 +0100 Subject: [PATCH 005/237] synaptics: Add an explicit assert on the response The response must be non-NULL in the function. Add an explicit assert to appease to static code analysis tools. --- libfprint/drivers/synaptics/synaptics.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index 4bac934a..8eba8528 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -604,6 +604,8 @@ verify_msg_cb (FpiDeviceSynaptics *self, return; } + g_assert (resp != NULL); + verify_resp = &resp->response.verify_resp; switch (resp->response_id) From 2f0824ab8843ddb8bb46f000f802e641a9252d6d Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 25 Nov 2019 18:38:32 +0100 Subject: [PATCH 006/237] upeksonly: Add default clauses to switch statements This effectively only annotates the code to make it clear that variables set in the switch are always initialized. --- libfprint/drivers/upeksonly.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libfprint/drivers/upeksonly.c b/libfprint/drivers/upeksonly.c index 76ba4e2f..ec813758 100644 --- a/libfprint/drivers/upeksonly.c +++ b/libfprint/drivers/upeksonly.c @@ -1249,6 +1249,9 @@ loopsm_run_state (FpiSsm *ssm, FpDevice *_dev) awfsm_1000_run_state, AWFSM_1000_NUM_STATES); break; + + default: + g_assert_not_reached (); } fpi_ssm_start_subsm (ssm, awfsm); } @@ -1290,6 +1293,9 @@ loopsm_run_state (FpiSsm *ssm, FpDevice *_dev) capsm_1001_run_state, CAPSM_1001_NUM_STATES); break; + + default: + g_assert_not_reached (); } fpi_ssm_start_subsm (ssm, capsm); break; @@ -1318,6 +1324,9 @@ loopsm_run_state (FpiSsm *ssm, FpDevice *_dev) deinitsm_1001_run_state, DEINITSM_1001_NUM_STATES); break; + + default: + g_assert_not_reached (); } self->capturing = FALSE; fpi_ssm_start_subsm (ssm, deinitsm); @@ -1441,6 +1450,9 @@ dev_activate (FpImageDevice *dev) ssm = fpi_ssm_new (FP_DEVICE (dev), initsm_1001_run_state, INITSM_1001_NUM_STATES); break; + + default: + g_assert_not_reached (); } fpi_ssm_start (ssm, initsm_complete); } From 25bc89a4f57a3d414134ac5e66d28b377b3b1914 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 25 Nov 2019 18:40:21 +0100 Subject: [PATCH 007/237] image-device: Remove unused fpi_device_get_current_action call There is a later call in the function which is sufficient. Simply remove the first call. --- libfprint/fp-image-device.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/libfprint/fp-image-device.c b/libfprint/fp-image-device.c index 8524e069..65cca164 100644 --- a/libfprint/fp-image-device.c +++ b/libfprint/fp-image-device.c @@ -493,8 +493,6 @@ fpi_image_device_report_finger_status (FpImageDevice *self, FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); FpDeviceAction action; - action = fpi_device_get_current_action (device); - if (priv->state == FP_IMAGE_DEVICE_STATE_INACTIVE) { /* Do we really want to always ignore such reports? We could From 14a41bdd485d484146a0a102d142239c2cb8a245 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 25 Nov 2019 18:40:59 +0100 Subject: [PATCH 008/237] print: Free temporary col variable The variable was leaked during serialization. Free it. --- libfprint/fp-print.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libfprint/fp-print.c b/libfprint/fp-print.c index 644370d6..592be145 100644 --- a/libfprint/fp-print.c +++ b/libfprint/fp-print.c @@ -921,6 +921,7 @@ fp_print_serialize (FpPrint *print, xyt->nrows, sizeof (col[0]))); g_variant_builder_close (&nested); + g_free (col); } g_variant_builder_close (&nested); From 9b48864c5b41111e1c6f40c1623b0f2393c3cc58 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 25 Nov 2019 18:41:44 +0100 Subject: [PATCH 009/237] print: Ensure xyt struct is not leaked during deserialization In the unlikely event of an error, the variable may have been leaked. Fix this by using g_autoptr combined with a g_steal_pointer. --- libfprint/fp-print.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libfprint/fp-print.c b/libfprint/fp-print.c index 592be145..1a6a70f7 100644 --- a/libfprint/fp-print.c +++ b/libfprint/fp-print.c @@ -1047,7 +1047,7 @@ fp_print_deserialize (const guchar *data, fpi_print_set_type (result, FP_PRINT_NBIS); for (i = 0; i < g_variant_n_children (prints); i++) { - struct xyt_struct *xyt = g_new0 (struct xyt_struct, 1); + g_autofree struct xyt_struct *xyt = g_new0 (struct xyt_struct, 1); const gint32 *xcol, *ycol, *thetacol; gsize xlen, ylen, thetalen; g_autoptr(GVariant) xyt_data = NULL; @@ -1078,7 +1078,7 @@ fp_print_deserialize (const guchar *data, memcpy (xyt->ycol, ycol, sizeof (xcol[0]) * xlen); memcpy (xyt->thetacol, thetacol, sizeof (xcol[0]) * xlen); - g_ptr_array_add (result->prints, xyt); + g_ptr_array_add (result->prints, g_steal_pointer (&xyt)); } } else if (type == FP_PRINT_RAW) From 76dd4066f328ed76794e834b797abcd588650736 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 22 Nov 2019 17:31:50 +0100 Subject: [PATCH 010/237] verify: Ensure we set set the autoptr value to NULL at definition --- examples/verify.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/verify.c b/examples/verify.c index 89a9b2c6..4e1c988f 100644 --- a/examples/verify.c +++ b/examples/verify.c @@ -182,7 +182,7 @@ start_verification (FpDevice *dev, VerifyData *verify_data) { g_print ("Loading previously enrolled %s finger data...\n", finger_to_string (verify_data->finger)); - g_autoptr(FpPrint) verify_print; + g_autoptr(FpPrint) verify_print = NULL; verify_print = print_data_load (dev, verify_data->finger); From d8efa336e5f82b15d467163c5c5cdcec4ed51b28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 21 Nov 2019 20:25:36 +0100 Subject: [PATCH 011/237] fpi-ssm, fp-device: Add missing copyright --- libfprint/fp-device.c | 1 + libfprint/fpi-ssm.c | 1 + libfprint/fpi-ssm.h | 1 + 3 files changed, 3 insertions(+) diff --git a/libfprint/fp-device.c b/libfprint/fp-device.c index f9ccb3cc..480d5cf9 100644 --- a/libfprint/fp-device.c +++ b/libfprint/fp-device.c @@ -1,6 +1,7 @@ /* * FpDevice - A fingerprint reader device * Copyright (C) 2019 Benjamin Berg + * Copyright (C) 2019 Marco Trevisan * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/libfprint/fpi-ssm.c b/libfprint/fpi-ssm.c index 6b63e1a1..f00af812 100644 --- a/libfprint/fpi-ssm.c +++ b/libfprint/fpi-ssm.c @@ -2,6 +2,7 @@ * Functions to assist with asynchronous driver <---> library communications * Copyright (C) 2007-2008 Daniel Drake * Copyright (C) 2019 Benjamin Berg + * Copyright (C) 2019 Marco Trevisan * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/libfprint/fpi-ssm.h b/libfprint/fpi-ssm.h index 31a33e56..8d45162f 100644 --- a/libfprint/fpi-ssm.h +++ b/libfprint/fpi-ssm.h @@ -2,6 +2,7 @@ * Copyright (C) 2007-2008 Daniel Drake * Copyright (C) 2018 Bastien Nocera * Copyright (C) 2019 Benjamin Berg + * Copyright (C) 2019 Marco Trevisan * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public From dd7d1baeceba8b2c8964b367797103f24a19adc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 21 Nov 2019 20:24:29 +0100 Subject: [PATCH 012/237] meson: Use multiline-array for default dirvers listing It will make reviews and diffs nicer to handle when adding new drivers. --- meson.build | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 8d06d450..a499a38d 100644 --- a/meson.build +++ b/meson.build @@ -51,7 +51,28 @@ mathlib_dep = cc.find_library('m', required: false) # Drivers drivers = get_option('drivers').split(',') virtual_drivers = [ 'virtual_image' ] -default_drivers = [ 'upektc_img', 'vfs5011', 'aes3500', 'aes4000', 'aes1610', 'aes1660', 'aes2660', 'aes2501', 'aes2550', 'vfs101', 'vfs301', 'vfs0050', 'etes603', 'vcom5s', 'synaptics', 'elan', 'uru4000', 'upektc', 'upeksonly', 'upekts' ] +default_drivers = [ + 'upektc_img', + 'vfs5011', + 'aes3500', + 'aes4000', + 'aes1610', + 'aes1660', + 'aes2660', + 'aes2501', + 'aes2550', + 'vfs101', + 'vfs301', + 'vfs0050', + 'etes603', + 'vcom5s', + 'synaptics', + 'elan', + 'uru4000', + 'upektc', + 'upeksonly', + 'upekts', +] all_drivers = default_drivers + virtual_drivers From 099fa9f005d22aa6d9c7ee79b9a11a088499994c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 21 Nov 2019 20:37:17 +0100 Subject: [PATCH 013/237] meson: Use preferred syntax everywhere Meson files are normally using 4-spaces to indent and functions use first parameter on the same line while others at next indentation level, not following the parenthesis indentation. So adapt libfprint to follow the meson standard. --- demo/meson.build | 27 +++++++------ doc/meson.build | 48 +++++++++++------------ doc/xml/meson.build | 4 +- examples/meson.build | 24 ++++++------ libfprint/meson.build | 89 +++++++++++++++++++++---------------------- meson.build | 31 ++++++++------- tests/meson.build | 9 ++--- 7 files changed, 116 insertions(+), 116 deletions(-) diff --git a/demo/meson.build b/demo/meson.build index ceca56d4..bf7a7ee1 100644 --- a/demo/meson.build +++ b/demo/meson.build @@ -1,21 +1,24 @@ -gtk_test_resources = gnome.compile_resources('gtk-test-resources', 'gtk-libfprint-test.gresource.xml', - source_dir : '.', - c_name : 'gtk_test') +gtk_test_resources = gnome.compile_resources('gtk-test-resources', + 'gtk-libfprint-test.gresource.xml', + source_dir : '.', + c_name : 'gtk_test') prefix = get_option('prefix') bindir = join_paths(prefix, get_option('bindir')) datadir = join_paths(prefix, get_option('datadir')) executable('gtk-libfprint-test', - [ 'gtk-libfprint-test.c', gtk_test_resources ], - dependencies: [ libfprint_dep, gtk_dep ], - include_directories: [ - root_inc, - ], - c_args: [ common_cflags, - '-DPACKAGE_VERSION="' + meson.project_version() + '"' ], - install: true, - install_dir: bindir) + [ 'gtk-libfprint-test.c', gtk_test_resources ], + dependencies: [ libfprint_dep, gtk_dep ], + include_directories: [ + root_inc, + ], + c_args: [ + common_cflags, + '-DPACKAGE_VERSION="' + meson.project_version() + '"' + ], + install: true, + install_dir: bindir) appdata = 'org.freedesktop.libfprint.Demo.appdata.xml' install_data(appdata, diff --git a/doc/meson.build b/doc/meson.build index 54186677..407413a9 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -1,14 +1,14 @@ subdir('xml') private_headers = [ - 'config.h', - 'nbis-helpers.h', - 'fprint.h', - 'fp_internal.h', + 'config.h', + 'nbis-helpers.h', + 'fprint.h', + 'fp_internal.h', - # Subdirectories to ignore - 'drivers', - 'nbis', + # Subdirectories to ignore + 'drivers', + 'nbis', ] html_images = [ @@ -25,20 +25,20 @@ glib_docpath = join_paths(glib_prefix, 'share', 'gtk-doc', 'html') docpath = join_paths(get_option('datadir'), 'gtk-doc', 'html') gnome.gtkdoc('libfprint', - main_xml: 'libfprint-docs.xml', - src_dir: join_paths(meson.source_root(), 'libfprint'), - dependencies: libfprint_dep, - content_files: content_files, - expand_content_files: expand_content_files, - scan_args: [ - #'--rebuild-sections', - '--ignore-decorators=API_EXPORTED', - '--ignore-headers=' + ' '.join(private_headers), - ], - fixxref_args: [ - '--html-dir=@0@'.format(docpath), - '--extra-dir=@0@'.format(join_paths(glib_docpath, 'glib')), - '--extra-dir=@0@'.format(join_paths(glib_docpath, 'gobject')), - ], - html_assets: html_images, - install: true) + main_xml: 'libfprint-docs.xml', + src_dir: join_paths(meson.source_root(), 'libfprint'), + dependencies: libfprint_dep, + content_files: content_files, + expand_content_files: expand_content_files, + scan_args: [ + #'--rebuild-sections', + '--ignore-decorators=API_EXPORTED', + '--ignore-headers=' + ' '.join(private_headers), + ], + fixxref_args: [ + '--html-dir=@0@'.format(docpath), + '--extra-dir=@0@'.format(join_paths(glib_docpath, 'glib')), + '--extra-dir=@0@'.format(join_paths(glib_docpath, 'gobject')), + ], + html_assets: html_images, + install: true) diff --git a/doc/xml/meson.build b/doc/xml/meson.build index e35f7eeb..2ca11007 100644 --- a/doc/xml/meson.build +++ b/doc/xml/meson.build @@ -7,4 +7,6 @@ ent_conf.set('PACKAGE_TARNAME', 'libfprint-' + meson.project_version()) ent_conf.set('PACKAGE_URL', 'https://fprint.freedesktop.org/') ent_conf.set('PACKAGE_VERSION', meson.project_version()) ent_conf.set('PACKAGE_API_VERSION', '1.0') -configure_file(input: 'gtkdocentities.ent.in', output: 'gtkdocentities.ent', configuration: ent_conf) +configure_file(input: 'gtkdocentities.ent.in', + output: 'gtkdocentities.ent', + configuration: ent_conf) diff --git a/examples/meson.build b/examples/meson.build index 5cd3d835..ff03ac65 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -2,18 +2,18 @@ examples = [ 'enroll', 'verify', 'manage-prints' ] foreach example: examples executable(example, - [example + '.c', 'storage.c', 'utilities.c'], - dependencies: [libfprint_dep, glib_dep], - include_directories: [ - root_inc, - ], - c_args: common_cflags) + [ example + '.c', 'storage.c', 'utilities.c' ], + dependencies: [ libfprint_dep, glib_dep ], + include_directories: [ + root_inc, + ], + c_args: common_cflags) endforeach executable('cpp-test', - 'cpp-test.cpp', - dependencies: libfprint_dep, - include_directories: [ - root_inc, - ], - c_args: common_cflags) + 'cpp-test.cpp', + dependencies: libfprint_dep, + include_directories: [ + root_inc, + ], + c_args: common_cflags) diff --git a/libfprint/meson.build b/libfprint/meson.build index af2fe849..f77965ad 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -162,73 +162,73 @@ endif other_sources = [] fp_enums = gnome.mkenums_simple('fp-enums', - sources: libfprint_public_headers, - install_header : true) + sources: libfprint_public_headers, + install_header : true) fp_enums_h = fp_enums[1] fpi_enums = gnome.mkenums_simple('fpi-enums', - sources: libfprint_private_headers, - install_header : true) + sources: libfprint_private_headers, + install_header : true) fpi_enums_h = fpi_enums[1] drivers_sources += configure_file(input: 'empty_file', - output: 'fp-drivers.c', - capture: true, - command: [ - 'echo', - drivers_type_list + '\n\n' + drivers_type_func - ]) + output: 'fp-drivers.c', + capture: true, + command: [ + 'echo', + drivers_type_list + '\n\n' + drivers_type_func + ]) mapfile = 'libfprint.ver' vflag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), mapfile) deps = [ mathlib_dep, glib_dep, gusb_dep, nss_dep, imaging_dep, gio_dep ] libfprint = library('fprint', - libfprint_sources + fp_enums + fpi_enums + - drivers_sources + nbis_sources + other_sources, - soversion: soversion, - version: libversion, - c_args: common_cflags + drivers_cflags, - include_directories: [ - root_inc, - include_directories('nbis/include'), - ], - link_args : vflag, - link_depends : mapfile, - dependencies: deps, - install: true) + libfprint_sources + fp_enums + fpi_enums + + drivers_sources + nbis_sources + other_sources, + soversion: soversion, + version: libversion, + c_args: common_cflags + drivers_cflags, + include_directories: [ + root_inc, + include_directories('nbis/include'), + ], + link_args : vflag, + link_depends : mapfile, + dependencies: deps, + install: true) libfprint_dep = declare_dependency(link_with: libfprint, - sources: [ fp_enums_h ], - include_directories: root_inc, - dependencies: [glib_dep, gusb_dep, gio_dep]) + sources: [ fp_enums_h ], + include_directories: root_inc, + dependencies: [ glib_dep, gusb_dep, gio_dep ]) install_headers(['fprint.h'] + libfprint_public_headers, subdir: 'libfprint') udev_rules = executable('fprint-list-udev-rules', - 'fprint-list-udev-rules.c', - include_directories: [ - root_inc, - ], - dependencies: [ deps, libfprint_dep ], - install: false) + 'fprint-list-udev-rules.c', + include_directories: [ + root_inc, + ], + dependencies: [ deps, libfprint_dep ], + install: false) if get_option('udev_rules') custom_target('udev-rules', - output: '60-fprint-autosuspend.rules', - capture: true, - command: [ udev_rules ], - install: true, - install_dir: udev_rules_dir) + output: '60-fprint-autosuspend.rules', + capture: true, + command: [ udev_rules ], + install: true, + install_dir: udev_rules_dir) endif supported_devices = executable('fprint-list-supported-devices', - 'fprint-list-supported-devices.c', - include_directories: [ - root_inc, - ], - dependencies: [ deps, libfprint_dep ], - install: false) + 'fprint-list-supported-devices.c', + include_directories: [ + root_inc, + ], + dependencies: [ deps, libfprint_dep ], + install: false) if get_option('introspection') @@ -256,8 +256,7 @@ if get_option('introspection') 'GObject-2.0', 'GUsb-1.0', ], - install : true - ) + install : true) libfprint_gir = libfprint_girtarget[0] libfprint_typelib = libfprint_girtarget[1] endif diff --git a/meson.build b/meson.build index a499a38d..158a2a0e 100644 --- a/meson.build +++ b/meson.build @@ -1,12 +1,12 @@ project('libfprint', [ 'c', 'cpp' ], - version: '1.90.0', - license: 'LGPLv2.1+', - default_options: [ - 'buildtype=debugoptimized', - 'warning_level=1', - 'c_std=c99', - ], - meson_version: '>= 0.46.0') + version: '1.90.0', + license: 'LGPLv2.1+', + default_options: [ + 'buildtype=debugoptimized', + 'warning_level=1', + 'c_std=c99', + ], + meson_version: '>= 0.46.0') gnome = import('gnome') @@ -160,11 +160,10 @@ endif pkgconfig = import('pkgconfig') pkgconfig.generate( - name: 'libfprint', - description: 'Generic C API for fingerprint reader access', - version: meson.project_version(), - libraries: libfprint, - subdirs: 'libfprint', - filebase: 'libfprint2', - install_dir: join_paths(get_option('libdir'), 'pkgconfig'), -) + name: 'libfprint', + description: 'Generic C API for fingerprint reader access', + version: meson.project_version(), + libraries: libfprint, + subdirs: 'libfprint', + filebase: 'libfprint2', + install_dir: join_paths(get_option('libdir'), 'pkgconfig')) diff --git a/tests/meson.build b/tests/meson.build index d02b05a6..79876929 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -16,8 +16,7 @@ envs.set('NO_AT_BRIDGE', '1') if get_option('introspection') if 'virtual_image' in drivers - test( - 'virtual-image', + test('virtual-image', find_program('virtual-image.py'), args: '--verbose', env: envs, @@ -26,8 +25,7 @@ if get_option('introspection') endif if 'vfs5011' in drivers - test( - 'vfs5011', + test('vfs5011', find_program('umockdev-test.py'), args: join_paths(meson.current_source_dir(), 'vfs5011'), env: envs, @@ -37,8 +35,7 @@ if get_option('introspection') endif if 'synaptics' in drivers - test( - 'synaptics', + test('synaptics', find_program('umockdev-test.py'), args: join_paths(meson.current_source_dir(), 'synaptics'), env: envs, From ceb62d7617a01de49d8e1580f14359972cd545d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 22 Nov 2019 14:09:51 +0100 Subject: [PATCH 014/237] meson: Avoid repeating the needed glib version multiple times Just define once and modify its syntax when needed. Use a more verbose definition for the min/max version (instead of just join the split version) so that in case we may depend on a specifc glib micro release during development. --- meson.build | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/meson.build b/meson.build index 158a2a0e..cf277f57 100644 --- a/meson.build +++ b/meson.build @@ -18,7 +18,10 @@ libfprint_conf = configuration_data() cc = meson.get_compiler('c') cpp = meson.get_compiler('cpp') host_system = host_machine.system() +glib_min_version = '2.50' +glib_version_def = 'GLIB_VERSION_@0@_@1@'.format( + glib_min_version.split('.')[0], glib_min_version.split('.')[1]) common_cflags = cc.get_supported_arguments([ '-fgnu89-inline', '-std=gnu99', @@ -30,8 +33,8 @@ common_cflags = cc.get_supported_arguments([ '-Werror-implicit-function-declaration', '-Wno-pointer-sign', '-Wshadow', - '-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_50', - '-DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_50', + '-DGLIB_VERSION_MIN_REQUIRED=' + glib_version_def, + '-DGLIB_VERSION_MAX_ALLOWED=' + glib_version_def, ]) # maintaining compatibility with the previous libtool versioning @@ -43,8 +46,8 @@ revision = 0 libversion = '@0@.@1@.@2@'.format(soversion, current, revision) # Dependencies -glib_dep = dependency('glib-2.0', version: '>= 2.50') -gio_dep = dependency('gio-unix-2.0', version: '>= 2.44.0') +glib_dep = dependency('glib-2.0', version: '>=' + glib_min_version) +gio_dep = dependency('gio-unix-2.0', version: '>=' + glib_min_version) gusb_dep = dependency('gusb', version: '>= 0.3.0') mathlib_dep = cc.find_library('m', required: false) From 8b270141f32411a02dffa1833564a8dafe6c1fd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 22 Nov 2019 13:06:08 +0100 Subject: [PATCH 015/237] image-device: Use g_clear_handle_id for timeouts As per this depend on glib 2.56: it has been released almost 2 years ago, I suppose we're fine with that. --- libfprint/fp-image-device.c | 12 ++---------- meson.build | 2 +- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/libfprint/fp-image-device.c b/libfprint/fp-image-device.c index 65cca164..44de5788 100644 --- a/libfprint/fp-image-device.c +++ b/libfprint/fp-image-device.c @@ -80,11 +80,7 @@ fp_image_device_change_state (FpImageDevice *self, FpImageDeviceState state) /* We might have been waiting for the finger to go OFF to start the * next operation. */ - if (priv->pending_activation_timeout_id) - { - g_source_remove (priv->pending_activation_timeout_id); - priv->pending_activation_timeout_id = 0; - } + g_clear_handle_id (&priv->pending_activation_timeout_id, g_source_remove); fp_dbg ("Image device internal state change from %d to %d\n", priv->state, state); @@ -110,11 +106,7 @@ fp_image_device_activate (FpImageDevice *self) /* We might have been waiting for deactivation to finish before * starting the next operation. */ - if (priv->pending_activation_timeout_id) - { - g_source_remove (priv->pending_activation_timeout_id); - priv->pending_activation_timeout_id = 0; - } + g_clear_handle_id (&priv->pending_activation_timeout_id, g_source_remove); fp_dbg ("Activating image device\n"); cls->activate (self); diff --git a/meson.build b/meson.build index cf277f57..ef352ba3 100644 --- a/meson.build +++ b/meson.build @@ -18,7 +18,7 @@ libfprint_conf = configuration_data() cc = meson.get_compiler('c') cpp = meson.get_compiler('cpp') host_system = host_machine.system() -glib_min_version = '2.50' +glib_min_version = '2.56' glib_version_def = 'GLIB_VERSION_@0@_@1@'.format( glib_min_version.split('.')[0], glib_min_version.split('.')[1]) From 201b5a9614afdc39f6cef08072834d59ab63ff65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 22 Nov 2019 14:19:10 +0100 Subject: [PATCH 016/237] fp-print: Use g_date_copy As per previous commit we depend on glib 2.56, we can use this utility function as well. --- libfprint/fp-print.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/libfprint/fp-print.c b/libfprint/fp-print.c index 1a6a70f7..39c5c0ad 100644 --- a/libfprint/fp-print.c +++ b/libfprint/fp-print.c @@ -534,11 +534,8 @@ fp_print_set_enroll_date (FpPrint *print, g_clear_pointer (&print->enroll_date, g_date_free); if (enroll_date) - { - /* XXX: Should use g_date_copy, but that is new in 2.56. */ - print->enroll_date = g_date_new (); - *print->enroll_date = *enroll_date; - } + print->enroll_date = g_date_copy (enroll_date); + g_object_notify_by_pspec (G_OBJECT (print), properties[PROP_ENROLL_DATE]); } From 60ad1ab9e3e5ecc1fe7bf390a2cec2e662dfd567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 22 Nov 2019 13:08:33 +0100 Subject: [PATCH 017/237] fp-image-device: Clear the pending activation timeout on finalize --- libfprint/fp-image-device.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libfprint/fp-image-device.c b/libfprint/fp-image-device.c index 44de5788..9aa9e1fb 100644 --- a/libfprint/fp-image-device.c +++ b/libfprint/fp-image-device.c @@ -278,6 +278,7 @@ fp_image_device_finalize (GObject *object) FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); g_assert (priv->active == FALSE); + g_clear_handle_id (&priv->pending_activation_timeout_id, g_source_remove); G_OBJECT_CLASS (fp_image_device_parent_class)->finalize (object); } From ea4da08af014343bffa6254d78514c2f3076575b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 22 Nov 2019 13:19:03 +0100 Subject: [PATCH 018/237] fp-image-device: Reactivate in idle on deactivation completed This is the same logic we apply to fp-device by default: any completed action should trigger the subsequent one when it is finished. So in case we want reactivate after a deactivation, let's do it in an idle, after removing the current pending timeout. --- libfprint/fp-image-device.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libfprint/fp-image-device.c b/libfprint/fp-image-device.c index 9aa9e1fb..84b1bb01 100644 --- a/libfprint/fp-image-device.c +++ b/libfprint/fp-image-device.c @@ -732,7 +732,11 @@ fpi_image_device_deactivate_complete (FpImageDevice *self, GError *error) /* We might be waiting to be able to activate again. */ if (priv->pending_activation_timeout_id) - fp_image_device_activate (self); + { + g_clear_handle_id (&priv->pending_activation_timeout_id, g_source_remove); + priv->pending_activation_timeout_id = + g_idle_add ((GSourceFunc) fp_image_device_activate, self); + } } /** From be367988ae4fc4d91d5cad7c9f7d47f67a878540 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 22 Nov 2019 13:51:16 +0100 Subject: [PATCH 019/237] fp-image-device: Add private "fp-image-device-state" property In this way drivers may get this without having to keep a copy of it --- libfprint/fp-image-device.c | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/libfprint/fp-image-device.c b/libfprint/fp-image-device.c index 84b1bb01..3565b904 100644 --- a/libfprint/fp-image-device.c +++ b/libfprint/fp-image-device.c @@ -21,6 +21,7 @@ #include "fpi-log.h" #include "fpi-image-device.h" +#include "fpi-enums.h" #include "fpi-print.h" #include "fpi-image.h" @@ -60,6 +61,13 @@ typedef struct G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (FpImageDevice, fp_image_device, FP_TYPE_DEVICE) +enum { + PROP_0, + PROP_FPI_STATE, + N_PROPS +}; + +static GParamSpec *properties[N_PROPS]; /*******************************************************/ @@ -85,6 +93,7 @@ fp_image_device_change_state (FpImageDevice *self, FpImageDeviceState state) fp_dbg ("Image device internal state change from %d to %d\n", priv->state, state); priv->state = state; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FPI_STATE]); /* change_state is the only callback which is optional and does not * have a default implementation. */ @@ -103,6 +112,7 @@ fp_image_device_activate (FpImageDevice *self) /* We don't have a neutral ACTIVE state, but we always will * go into WAIT_FINGER_ON afterwards. */ priv->state = FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FPI_STATE]); /* We might have been waiting for deactivation to finish before * starting the next operation. */ @@ -127,6 +137,7 @@ fp_image_device_deactivate (FpDevice *device) return; } priv->state = FP_IMAGE_DEVICE_STATE_INACTIVE; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FPI_STATE]); fp_dbg ("Deactivating image device\n"); cls->deactivate (self); @@ -295,6 +306,26 @@ fp_image_device_default_deactivate (FpImageDevice *self) fpi_image_device_deactivate_complete (self, NULL); } +static void +fp_image_device_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + FpImageDevice *self = FP_IMAGE_DEVICE (object); + FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); + + switch (prop_id) + { + case PROP_FPI_STATE: + g_value_set_enum (value, priv->state); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + static void fp_image_device_class_init (FpImageDeviceClass *klass) { @@ -302,6 +333,7 @@ fp_image_device_class_init (FpImageDeviceClass *klass) FpDeviceClass *fp_device_class = FP_DEVICE_CLASS (klass); object_class->finalize = fp_image_device_finalize; + object_class->get_property = fp_image_device_get_property; fp_device_class->open = fp_image_device_open; fp_device_class->close = fp_image_device_close; @@ -315,6 +347,16 @@ fp_image_device_class_init (FpImageDeviceClass *klass) /* Default implementations */ klass->activate = fp_image_device_default_activate; klass->deactivate = fp_image_device_default_deactivate; + + properties[PROP_FPI_STATE] = + g_param_spec_enum ("fp-image-device-state", + "Image Device State", + "Private: The state of the image device", + FP_TYPE_IMAGE_DEVICE_STATE, + FP_IMAGE_DEVICE_STATE_INACTIVE, + G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); + + g_object_class_install_properties (object_class, N_PROPS, properties); } static void @@ -760,6 +802,7 @@ fpi_image_device_open_complete (FpImageDevice *self, GError *error) g_debug ("Image device open completed"); priv->state = FP_IMAGE_DEVICE_STATE_INACTIVE; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FPI_STATE]); fpi_device_open_complete (FP_DEVICE (self), error); } @@ -785,6 +828,7 @@ fpi_image_device_close_complete (FpImageDevice *self, GError *error) g_return_if_fail (action == FP_DEVICE_ACTION_CLOSE); priv->state = FP_IMAGE_DEVICE_STATE_INACTIVE; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FPI_STATE]); fpi_device_close_complete (FP_DEVICE (self), error); } From cca6d3b04b74558e12df3aa43761faaa574c5ff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 22 Nov 2019 14:50:48 +0100 Subject: [PATCH 020/237] fp-image-device: Use a GObject signal to notify image state changed This is more GObject-friendly and we have the automatic call of the vfunc if one is set. --- libfprint/fp-image-device.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/libfprint/fp-image-device.c b/libfprint/fp-image-device.c index 3565b904..692727be 100644 --- a/libfprint/fp-image-device.c +++ b/libfprint/fp-image-device.c @@ -69,6 +69,14 @@ enum { static GParamSpec *properties[N_PROPS]; +enum { + FPI_STATE_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + /*******************************************************/ /* TODO: @@ -81,7 +89,6 @@ static void fp_image_device_change_state (FpImageDevice *self, FpImageDeviceState state) { FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); - FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (self); /* Cannot change to inactive using this function. */ g_assert (state != FP_IMAGE_DEVICE_STATE_INACTIVE); @@ -94,11 +101,7 @@ fp_image_device_change_state (FpImageDevice *self, FpImageDeviceState state) priv->state = state; g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FPI_STATE]); - - /* change_state is the only callback which is optional and does not - * have a default implementation. */ - if (cls->change_state) - cls->change_state (self, state); + g_signal_emit (self, signals[FPI_STATE_CHANGED], 0, priv->state); } static void @@ -356,6 +359,14 @@ fp_image_device_class_init (FpImageDeviceClass *klass) FP_IMAGE_DEVICE_STATE_INACTIVE, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); + signals[FPI_STATE_CHANGED] = + g_signal_new ("fp-image-device-state-changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (FpImageDeviceClass, change_state), + NULL, NULL, NULL, + G_TYPE_NONE, 1, FP_TYPE_IMAGE_DEVICE_STATE); + g_object_class_install_properties (object_class, N_PROPS, properties); } From 0a08a248966b3ecc9c30c0654ed1326f64a8b0cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 22 Nov 2019 16:27:18 +0100 Subject: [PATCH 021/237] fpi-ssm: Remove any reference to fpi_timeout_add() This doesn't exist anymore, while fpi_device_add_timeout does exists. --- libfprint/fpi-ssm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libfprint/fpi-ssm.c b/libfprint/fpi-ssm.c index f00af812..1569be83 100644 --- a/libfprint/fpi-ssm.c +++ b/libfprint/fpi-ssm.c @@ -367,11 +367,11 @@ fpi_ssm_dup_error (FpiSsm *machine) * @data: a pointer to an #FpiSsm state machine * * Same as fpi_ssm_next_state(), but to be used as a callback - * for an fpi_timeout_add() callback, when the state change needs - * to happen after a timeout. + * for an fpi_device_add_timeout() callback, when the state + * change needs to happen after a timeout. * * Make sure to pass the #FpiSsm as the `ssm_data` argument - * for that fpi_timeout_add() call. + * for that fpi_device_add_timeout() call. */ void fpi_ssm_next_state_timeout_cb (FpDevice *dev, From 15d218a112728c9ded518f55a985223861f79cda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 22 Nov 2019 17:56:24 +0100 Subject: [PATCH 022/237] fpi-log: Set fp_error as equal to g_critical --- libfprint/fpi-log.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libfprint/fpi-log.h b/libfprint/fpi-log.h index 1c3d5ade..8f2f6a15 100644 --- a/libfprint/fpi-log.h +++ b/libfprint/fpi-log.h @@ -68,11 +68,11 @@ /** * fp_err: * - * Same as g_warning(). In the future, this might be changed to a + * Same as g_critical(). In the future, this might be changed to a * g_assert() instead, so bear this in mind when adding those calls * to your driver. */ -#define fp_err g_warning +#define fp_err g_critical /** * BUG_ON: From 3b72b925b04ea75543b44b1631b804850ba3cf56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 25 Nov 2019 19:08:31 +0100 Subject: [PATCH 023/237] gitlab-ci: Check indentation in an initial check-source stage --- .gitlab-ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 20257bde..10c2cf17 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,6 @@ image: fedora:rawhide stages: + - check-source - build - test - flatpack @@ -49,7 +50,7 @@ test: - meson test -C _build --verbose --no-stdsplit test_indent: - stage: test + stage: check-source script: - dnf update -y --nogpgcheck && dnf install -y --nogpgcheck git uncrustify - scripts/uncrustify.sh --check From f6559ba8b19f8d0b0bdd065867ee1feef9304a0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 25 Nov 2019 21:22:47 +0100 Subject: [PATCH 024/237] fp-device: Support variadic arguments to error functions Make possible to generate a formatted message when creating an error from a device, without having save it first. --- libfprint/fp-device.c | 26 ++++++++++++++++++++++---- libfprint/fpi-device.h | 6 ++++-- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/libfprint/fp-device.c b/libfprint/fp-device.c index 480d5cf9..13f1b5ac 100644 --- a/libfprint/fp-device.c +++ b/libfprint/fp-device.c @@ -243,9 +243,18 @@ fpi_device_error_new (FpDeviceError error) * and similar calls. */ GError * -fpi_device_retry_new_msg (FpDeviceRetry error, const gchar *msg) +fpi_device_retry_new_msg (FpDeviceRetry device_error, + const gchar *msg, + ...) { - return g_error_new_literal (FP_DEVICE_RETRY, error, msg); + GError *error; + va_list args; + + va_start (args, msg); + error = g_error_new_valist (FP_DEVICE_RETRY, device_error, msg, args); + va_end (args); + + return error; } /** @@ -257,9 +266,18 @@ fpi_device_retry_new_msg (FpDeviceRetry error, const gchar *msg) * and similar calls. */ GError * -fpi_device_error_new_msg (FpDeviceError error, const gchar *msg) +fpi_device_error_new_msg (FpDeviceError device_error, + const gchar *msg, + ...) { - return g_error_new_literal (FP_DEVICE_ERROR, error, msg); + GError *error; + va_list args; + + va_start (args, msg); + error = g_error_new_valist (FP_DEVICE_ERROR, device_error, msg, args); + va_end (args); + + return error; } static gboolean diff --git a/libfprint/fpi-device.h b/libfprint/fpi-device.h index a206798e..d83a5a3a 100644 --- a/libfprint/fpi-device.h +++ b/libfprint/fpi-device.h @@ -181,9 +181,11 @@ GError * fpi_device_retry_new (FpDeviceRetry error); GError * fpi_device_error_new (FpDeviceError error); GError * fpi_device_retry_new_msg (FpDeviceRetry error, - const gchar *msg); + const gchar *msg, + ...) G_GNUC_PRINTF (2, 3); GError * fpi_device_error_new_msg (FpDeviceError error, - const gchar *msg); + const gchar *msg, + ...) G_GNUC_PRINTF (2, 3); guint64 fpi_device_get_driver_data (FpDevice *device); From 1b23f0efe17012c2e0419e8926ccac4bfa3b94df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 25 Nov 2019 21:23:31 +0100 Subject: [PATCH 025/237] drivers: Use clearer messages using parameters --- libfprint/drivers/aesx660.c | 16 +++++++++++----- libfprint/drivers/synaptics/synaptics.c | 17 ++++++++++++----- libfprint/drivers/upekts.c | 8 +++++--- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/libfprint/drivers/aesx660.c b/libfprint/drivers/aesx660.c index 8540a068..8ad4c630 100644 --- a/libfprint/drivers/aesx660.c +++ b/libfprint/drivers/aesx660.c @@ -131,7 +131,9 @@ aesX660_read_calibrate_data_cb (FpiUsbTransfer *transfer, fp_dbg ("Bogus calibrate response: %.2x\n", data[0]); fpi_ssm_mark_failed (transfer->ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, - "Bogus calibrate response")); + "Bogus calibrate " + "response: %.2x", + data[0])); return; } @@ -175,7 +177,8 @@ finger_det_read_fd_data_cb (FpiUsbTransfer *transfer, fp_dbg ("Bogus FD response: %.2x\n", data[0]); fpi_ssm_mark_failed (transfer->ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, - "Bogus FD response")); + "Bogus FD response %.2x", + data[0])); return; } @@ -538,7 +541,8 @@ activate_read_id_cb (FpiUsbTransfer *transfer, FpDevice *device, fp_dbg ("Bogus read ID response: %.2x\n", data[AESX660_RESPONSE_TYPE_OFFSET]); fpi_ssm_mark_failed (transfer->ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, - "Bogus read ID response")); + "Bogus read ID response %.2x", + data[AESX660_RESPONSE_TYPE_OFFSET])); return; } @@ -565,7 +569,8 @@ activate_read_id_cb (FpiUsbTransfer *transfer, FpDevice *device, fp_dbg ("Failed to init device! init status: %.2x\n", data[7]); fpi_ssm_mark_failed (transfer->ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, - "Failed to init device")); + "Failed to init device %.2x", + data[7])); break; } } @@ -594,7 +599,8 @@ activate_read_init_cb (FpiUsbTransfer *transfer, FpDevice *device, data[3]); fpi_ssm_mark_failed (transfer->ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, - "Bogus read init response")); + "Bogus read init response: " + "%.2x %.2x", data[0], data[3])); return; } priv->init_cmd_idx++; diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index 8eba8528..f6faf114 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -137,7 +137,8 @@ cmd_recieve_cb (FpiUsbTransfer *transfer, fp_warn ("Received General Error %d from the sensor", (guint) err); fpi_ssm_mark_failed (transfer->ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, - "Received general error from device")); + "Received general error %u from device", + (guint) err)); //fpi_ssm_jump_to_state (transfer->ssm, fpi_ssm_get_cur_state (transfer->ssm)); return; } @@ -472,7 +473,8 @@ list_msg_cb (FpiDeviceSynaptics *self, fpi_device_list_complete (FP_DEVICE (self), NULL, fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, - "Failed to query enrolled users")); + "Failed to query enrolled users: %d", + resp->result)); } break; @@ -770,7 +772,8 @@ enroll_msg_cb (FpiDeviceSynaptics *self, fpi_device_enroll_complete (device, NULL, fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, - "Enrollment failed")); + "Enrollment failed (%d)", + resp->result)); } break; } @@ -1052,7 +1055,11 @@ dev_probe (FpDevice *device) self->mis_version.build_num); error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, - "Unsupported firmware version"); + "Unsupported firmware version " + "(%d.%d with build number %d)", + self->mis_version.version_major, + self->mis_version.version_minor, + self->mis_version.build_num); goto err_close; } @@ -1120,7 +1127,7 @@ fps_deinit_cb (FpiDeviceSynaptics *self, case BMKT_RSP_POWER_DOWN_FAIL: fp_info ("Failed to go to power down mode: %d", resp->result); error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, - "Power down failed"); + "Power down failed: %d", resp->result); break; } diff --git a/libfprint/drivers/upekts.c b/libfprint/drivers/upekts.c index 24269077..b3481aa0 100644 --- a/libfprint/drivers/upekts.c +++ b/libfprint/drivers/upekts.c @@ -288,7 +288,7 @@ __handle_incoming_msg (FpDevice *device, { fp_warn ("cmd response too short (%d)", len); error = fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, - "CMD response too short"); + "CMD response too short (%d)", len); goto err; } if (innerbuf[0] != 0x28) @@ -371,7 +371,8 @@ read_msg_cb (FpiUsbTransfer *transfer, FpDevice *device, fp_err ("async msg read too short (%d)", (gint) transfer->actual_length); error = fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, - "Packet from device was too short"); + "Packet from device was too short (%lu)", + transfer->actual_length); goto err; } @@ -798,7 +799,8 @@ read_msg01_cb (FpDevice *dev, enum read_msg_type type, { fp_err ("expected seq=1, got %x", seq); fpi_ssm_mark_failed (ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, - "Got wrong sequence number")); + "Got wrong sequence number (%x)", + seq)); return; } From e812653acda29ee4936d36cebd7995e6f3340cab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 27 Nov 2019 16:50:54 +0100 Subject: [PATCH 026/237] synaptics: Use GDate getters to retrieve the DMY values As per commit 201b5a961 we use g_date_copy() to copy the date, however the GLib implementation is done assuming that the GDate getters are always used as the copy function doesn't preserve the original format of the date (whether is using julian days or dmy), and the synaptics driver access to the dmy values directly, without using the getter that would recompute the proper values. Causing a read error of unset values. So, to avoid this, just use the g_date_get_* getters to retrieve the day month and year for for defining the print enroll id. --- libfprint/drivers/synaptics/synaptics.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index f6faf114..9ecc6822 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -817,9 +817,9 @@ enroll (FpDevice *device) date = fp_print_get_enroll_date (print); if (date && g_date_valid (date)) { - y = date->year; - m = date->month; - d = date->day; + y = g_date_get_year (date); + m = g_date_get_month (date); + d = g_date_get_day (date); } else { From 519b5acc91b0f4b7ff7fde5d6e4176bbbb8c0d4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 27 Nov 2019 16:59:23 +0100 Subject: [PATCH 027/237] synaptics: Initialize user_id autoptr to NULL --- libfprint/drivers/synaptics/synaptics.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index 9ecc6822..a2286b24 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -803,7 +803,7 @@ enroll (FpDevice *device) GVariant *uid = NULL; const gchar *username; guint finger; - g_autofree gchar *user_id; + g_autofree gchar *user_id = NULL; gssize user_id_len; g_autofree guint8 *payload = NULL; const GDate *date; From 876924df6aec4318a5c2c824b514992cd80b0ba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 27 Nov 2019 19:14:35 +0100 Subject: [PATCH 028/237] examples: Handle the cases where the print date is not set --- examples/manage-prints.c | 17 +++++++++++------ examples/verify.c | 11 ++++++++--- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/examples/manage-prints.c b/examples/manage-prints.c index b865af75..7bbbc5ee 100644 --- a/examples/manage-prints.c +++ b/examples/manage-prints.c @@ -153,14 +153,19 @@ on_list_completed (FpDevice *dev, for (i = 0; i < prints->len; ++i) { FpPrint * print = prints->pdata[i]; + const GDate *date = fp_print_get_enroll_date (print); - g_date_strftime (buf, G_N_ELEMENTS (buf), "%Y-%m-%d", - fp_print_get_enroll_date (print)); - g_print ("[%d] Print of %s finger for username %s, enrolled " - "on %s. Description: %s\n", i + 1, + g_print ("[%d] Print of %s finger for username %s", i + 1, finger_to_string (fp_print_get_finger (print)), - fp_print_get_username (print), buf, - fp_print_get_description (print)); + fp_print_get_username (print)); + + if (date) + { + g_date_strftime (buf, G_N_ELEMENTS (buf), "%Y-%m-%d\0", date); + g_print (", enrolled on %s", buf); + } + + g_print (". Description: %s\n", fp_print_get_description (print)); } if (prints->len) diff --git a/examples/verify.c b/examples/verify.c index 4e1c988f..1249dce6 100644 --- a/examples/verify.c +++ b/examples/verify.c @@ -127,9 +127,14 @@ on_list_completed (FpDevice *dev, GAsyncResult *res, gpointer user_data) if (fp_print_get_finger (print) == verify_data->finger && g_strcmp0 (fp_print_get_username (print), g_get_user_name ()) == 0) { - if (!verify_print || - (g_date_compare (fp_print_get_enroll_date (print), - fp_print_get_enroll_date (verify_print)) >= 0)) + const GDate *verify_print_date = NULL; + const GDate *print_date = fp_print_get_enroll_date (print); + + if (verify_print) + verify_print_date = fp_print_get_enroll_date (verify_print); + + if (!verify_print || !print_date || !verify_print_date || + g_date_compare (print_date, verify_print_date) >= 0) verify_print = print; } } From a855c0cc7944c474936c104ee77959db7185cd3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 26 Nov 2019 16:18:14 +0100 Subject: [PATCH 029/237] fpi-ssm: Take ownership of the SSM when completing it When a machine is completed, we automatically free it since we can't consider it valid anymore since this point. Update the drivers not to free the SSM on completion callback anymore. --- libfprint/drivers/aes1610.c | 2 -- libfprint/drivers/aes2501.c | 2 -- libfprint/drivers/aes2550.c | 2 -- libfprint/drivers/aesx660.c | 3 --- libfprint/drivers/elan.c | 4 ---- libfprint/drivers/etes603.c | 6 ------ libfprint/drivers/synaptics/synaptics.c | 1 - libfprint/drivers/upeksonly.c | 2 -- libfprint/drivers/upektc.c | 2 -- libfprint/drivers/upektc_img.c | 3 --- libfprint/drivers/upekts.c | 4 ---- libfprint/drivers/uru4000.c | 1 - libfprint/drivers/vcom5s.c | 1 - libfprint/drivers/vfs0050.c | 2 -- libfprint/drivers/vfs101.c | 2 -- libfprint/drivers/vfs301.c | 2 -- libfprint/drivers/vfs5011.c | 2 -- libfprint/fpi-ssm.c | 15 +++++++++++---- 18 files changed, 11 insertions(+), 45 deletions(-) diff --git a/libfprint/drivers/aes1610.c b/libfprint/drivers/aes1610.c index c9742e9c..03265652 100644 --- a/libfprint/drivers/aes1610.c +++ b/libfprint/drivers/aes1610.c @@ -710,7 +710,6 @@ capture_sm_complete (FpiSsm *ssm, FpDevice *_dev, GError *error) { start_finger_detection (dev); } - fpi_ssm_free (ssm); } static void @@ -774,7 +773,6 @@ activate_sm_complete (FpiSsm *ssm, FpDevice *_dev, GError *error) if (!error) start_finger_detection (dev); - fpi_ssm_free (ssm); } static void diff --git a/libfprint/drivers/aes2501.c b/libfprint/drivers/aes2501.c index fad0218e..1b59c56b 100644 --- a/libfprint/drivers/aes2501.c +++ b/libfprint/drivers/aes2501.c @@ -575,7 +575,6 @@ capture_sm_complete (FpiSsm *ssm, FpDevice *_dev, GError *error) { start_finger_detection (dev); } - fpi_ssm_free (ssm); } static void @@ -806,7 +805,6 @@ activate_sm_complete (FpiSsm *ssm, FpDevice *dev, GError *error) if (!error) start_finger_detection (FP_IMAGE_DEVICE (dev)); - fpi_ssm_free (ssm); } static void diff --git a/libfprint/drivers/aes2550.c b/libfprint/drivers/aes2550.c index 2abcf767..b95b0532 100644 --- a/libfprint/drivers/aes2550.c +++ b/libfprint/drivers/aes2550.c @@ -391,7 +391,6 @@ capture_sm_complete (FpiSsm *ssm, FpDevice *_dev, GError *error) { start_finger_detection (dev); } - fpi_ssm_free (ssm); } static void @@ -537,7 +536,6 @@ activate_sm_complete (FpiSsm *ssm, FpDevice *_dev, GError *error) if (!error) start_finger_detection (dev); - fpi_ssm_free (ssm); } static void diff --git a/libfprint/drivers/aesx660.c b/libfprint/drivers/aesx660.c index 8ad4c630..3f13252c 100644 --- a/libfprint/drivers/aesx660.c +++ b/libfprint/drivers/aesx660.c @@ -215,7 +215,6 @@ finger_det_sm_complete (FpiSsm *ssm, FpDevice *_dev, GError *error) fp_dbg ("Finger detection completed"); fpi_image_device_report_finger_status (dev, TRUE); - fpi_ssm_free (ssm); if (priv->deactivating) { @@ -466,7 +465,6 @@ capture_sm_complete (FpiSsm *ssm, FpDevice *device, GError *error) FpiDeviceAesX660Private *priv = fpi_device_aes_x660_get_instance_private (self); fp_dbg ("Capture completed"); - fpi_ssm_free (ssm); if (priv->deactivating) { @@ -672,7 +670,6 @@ static void activate_sm_complete (FpiSsm *ssm, FpDevice *_dev, GError *error) { fpi_image_device_activate_complete (FP_IMAGE_DEVICE (_dev), error); - fpi_ssm_free (ssm); if (!error) start_finger_detection (FP_IMAGE_DEVICE (_dev)); diff --git a/libfprint/drivers/elan.c b/libfprint/drivers/elan.c index 961366ee..5e80be5b 100644 --- a/libfprint/drivers/elan.c +++ b/libfprint/drivers/elan.c @@ -479,7 +479,6 @@ stop_capture_complete (FpiSsm *ssm, FpDevice *_dev, GError *error) G_DEBUG_HERE (); - fpi_ssm_free (ssm); /* The device is inactive at this point. */ self->dev_state = FP_IMAGE_DEVICE_STATE_INACTIVE; @@ -606,7 +605,6 @@ capture_complete (FpiSsm *ssm, FpDevice *_dev, GError *error) fpi_image_device_session_error (dev, error); } - fpi_ssm_free (ssm); } static void @@ -789,7 +787,6 @@ calibrate_complete (FpiSsm *ssm, FpDevice *dev, GError *error) elan_capture (dev); } - fpi_ssm_free (ssm); } static void @@ -886,7 +883,6 @@ activate_complete (FpiSsm *ssm, FpDevice *dev, GError *error) fpi_image_device_activate_complete (idev, error); - fpi_ssm_free (ssm); } static void diff --git a/libfprint/drivers/etes603.c b/libfprint/drivers/etes603.c index 5c990dae..55f0139b 100644 --- a/libfprint/drivers/etes603.c +++ b/libfprint/drivers/etes603.c @@ -789,7 +789,6 @@ m_exit_complete (FpiSsm *ssm, FpDevice *dev, GError *error) else fp_dbg ("The device is now in idle state"); fpi_image_device_deactivate_complete (idev, error); - fpi_ssm_free (ssm); } static void @@ -911,7 +910,6 @@ m_capture_complete (FpiSsm *ssm, FpDevice *dev, GError *error) g_error_free (error); } } - fpi_ssm_free (ssm); if (self->is_active == TRUE) { @@ -1061,7 +1059,6 @@ m_finger_complete (FpiSsm *ssm, FpDevice *dev, GError *error) self->is_active = FALSE; } - fpi_ssm_free (ssm); } static void @@ -1265,7 +1262,6 @@ m_tunevrb_complete (FpiSsm *ssm, FpDevice *dev, GError *error) if (!self->is_active) m_exit_start (idev); - fpi_ssm_free (ssm); } /* @@ -1409,7 +1405,6 @@ m_tunedc_complete (FpiSsm *ssm, FpDevice *dev, GError *error) if (!self->is_active) m_exit_start (idev); - fpi_ssm_free (ssm); } static void @@ -1543,7 +1538,6 @@ m_init_complete (FpiSsm *ssm, FpDevice *dev, GError *error) reset_param (FPI_DEVICE_ETES603 (dev)); fpi_image_device_session_error (idev, error); } - fpi_ssm_free (ssm); } static void diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index a2286b24..4932d01c 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -290,7 +290,6 @@ cmd_ssm_done (FpiSsm *ssm, FpDevice *dev, GError *error) } self->cmd_complete_on_removal = FALSE; g_clear_pointer (&self->cmd_complete_error, g_error_free); - fpi_ssm_free (ssm); } static void diff --git a/libfprint/drivers/upeksonly.c b/libfprint/drivers/upeksonly.c index ec813758..e1cd7e52 100644 --- a/libfprint/drivers/upeksonly.c +++ b/libfprint/drivers/upeksonly.c @@ -1380,7 +1380,6 @@ loopsm_complete (FpiSsm *ssm, FpDevice *_dev, GError *error) FpImageDevice *dev = FP_IMAGE_DEVICE (_dev); FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY (_dev); - fpi_ssm_free (ssm); if (self->deactivating) { @@ -1401,7 +1400,6 @@ initsm_complete (FpiSsm *ssm, FpDevice *_dev, GError *error) FpImageDevice *dev = FP_IMAGE_DEVICE (_dev); FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY (_dev); - fpi_ssm_free (ssm); fpi_image_device_activate_complete (dev, error); if (error) return; diff --git a/libfprint/drivers/upektc.c b/libfprint/drivers/upektc.c index ff5b49ba..e1254ce6 100644 --- a/libfprint/drivers/upektc.c +++ b/libfprint/drivers/upektc.c @@ -157,7 +157,6 @@ activate_sm_complete (FpiSsm *ssm, FpDevice *_dev, GError *error) if (!error) start_finger_detection (dev); - fpi_ssm_free (ssm); } @@ -345,7 +344,6 @@ capture_sm_complete (FpiSsm *ssm, FpDevice *_dev, GError *error) else start_finger_detection (dev); - fpi_ssm_free (ssm); } static void diff --git a/libfprint/drivers/upektc_img.c b/libfprint/drivers/upektc_img.c index 1e06b903..28a709f7 100644 --- a/libfprint/drivers/upektc_img.c +++ b/libfprint/drivers/upektc_img.c @@ -389,7 +389,6 @@ capture_sm_complete (FpiSsm *ssm, FpDevice *_dev, GError *error_arg) g_autoptr(GError) error = error_arg; - fpi_ssm_free (ssm); /* Note: We assume that the error is a cancellation in the deactivation case */ if (self->deactivating) @@ -470,7 +469,6 @@ deactivate_sm_complete (FpiSsm *ssm, FpDevice *_dev, GError *error) FpiDeviceUpektcImg *self = FPI_DEVICE_UPEKTC_IMG (_dev); fp_dbg ("Deactivate completed"); - fpi_ssm_free (ssm); self->deactivating = FALSE; fpi_image_device_deactivate_complete (dev, error); @@ -601,7 +599,6 @@ activate_sm_complete (FpiSsm *ssm, FpDevice *_dev, GError *error) { FpImageDevice *dev = FP_IMAGE_DEVICE (_dev); - fpi_ssm_free (ssm); fpi_image_device_activate_complete (dev, error); if (!error) diff --git a/libfprint/drivers/upekts.c b/libfprint/drivers/upekts.c index b3481aa0..4bc6556f 100644 --- a/libfprint/drivers/upekts.c +++ b/libfprint/drivers/upekts.c @@ -990,7 +990,6 @@ enroll_stop_deinit_cb (FpiSsm *ssm, FpDevice *dev, GError *error) fp_warn ("Error deinitializing: %s", error->message); fpi_device_enroll_complete (dev, data->print, data->error); - fpi_ssm_free (ssm); } static void @@ -1217,7 +1216,6 @@ enroll_started (FpiSsm *ssm, FpDevice *dev, GError *error) else enroll_iterate (dev); - fpi_ssm_free (ssm); } static void @@ -1256,7 +1254,6 @@ verify_stop_deinit_cb (FpiSsm *ssm, FpDevice *dev, GError *error) fp_warn ("Error deinitializing: %s", error->message); fpi_device_verify_complete (dev, data->res, NULL, data->error); - fpi_ssm_free (ssm); } static void @@ -1540,7 +1537,6 @@ verify_started (FpiSsm *ssm, FpDevice *dev, GError *error) upekdev->first_verify_iteration = TRUE; verify_iterate (dev); - fpi_ssm_free (ssm); } static void diff --git a/libfprint/drivers/uru4000.c b/libfprint/drivers/uru4000.c index f2484119..89328d0f 100644 --- a/libfprint/drivers/uru4000.c +++ b/libfprint/drivers/uru4000.c @@ -789,7 +789,6 @@ imaging_complete (FpiSsm *ssm, FpDevice *dev, GError *error) { FpiDeviceUru4000 *self = FPI_DEVICE_URU4000 (dev); - fpi_ssm_free (ssm); /* Report error before exiting imaging loop - the error handler * can request state change, which needs to be postponed to end of diff --git a/libfprint/drivers/vcom5s.c b/libfprint/drivers/vcom5s.c index 0e102528..edd2dd40 100644 --- a/libfprint/drivers/vcom5s.c +++ b/libfprint/drivers/vcom5s.c @@ -301,7 +301,6 @@ loopsm_complete (FpiSsm *ssm, FpDevice *dev, GError *error) FpImageDevice *imgdev = FP_IMAGE_DEVICE (dev); FpDeviceVcom5s *self = FPI_DEVICE_VCOM5S (dev); - fpi_ssm_free (ssm); g_object_unref (self->capture_img); self->capture_img = NULL; self->loop_running = FALSE; diff --git a/libfprint/drivers/vfs0050.c b/libfprint/drivers/vfs0050.c index 4dc67827..1be272b9 100644 --- a/libfprint/drivers/vfs0050.c +++ b/libfprint/drivers/vfs0050.c @@ -669,7 +669,6 @@ dev_activate_callback (FpiSsm *ssm, FpDevice *dev, GError *error) g_error_free (error); } - fpi_ssm_free (ssm); } /* Activate device */ @@ -710,7 +709,6 @@ dev_open_callback (FpiSsm *ssm, FpDevice *dev, GError *error) { /* Notify open complete */ fpi_image_device_open_complete (FP_IMAGE_DEVICE (dev), error); - fpi_ssm_free (ssm); } /* Open device */ diff --git a/libfprint/drivers/vfs101.c b/libfprint/drivers/vfs101.c index 37e083c7..9ca1b0a6 100644 --- a/libfprint/drivers/vfs101.c +++ b/libfprint/drivers/vfs101.c @@ -960,7 +960,6 @@ m_loop_complete (FpiSsm *ssm, FpDevice *dev, GError *error) self->active = FALSE; - fpi_ssm_free (ssm); } /* Init ssm states */ @@ -1268,7 +1267,6 @@ m_init_complete (FpiSsm *ssm, FpDevice *_dev, GError *error) } /* Free sequential state machine */ - fpi_ssm_free (ssm); } /* Activate device */ diff --git a/libfprint/drivers/vfs301.c b/libfprint/drivers/vfs301.c index 8fdac7c7..7219792a 100644 --- a/libfprint/drivers/vfs301.c +++ b/libfprint/drivers/vfs301.c @@ -168,7 +168,6 @@ m_loop_complete (FpiSsm *ssm, FpDevice *_dev, GError *error) g_error_free (error); } /* Free sequential state machine */ - fpi_ssm_free (ssm); } /* Exec init sequential state machine */ @@ -201,7 +200,6 @@ m_init_complete (FpiSsm *ssm, FpDevice *dev, GError *error) } /* Free sequential state machine */ - fpi_ssm_free (ssm); } /* Activate device */ diff --git a/libfprint/drivers/vfs5011.c b/libfprint/drivers/vfs5011.c index 9eddca79..007e4868 100644 --- a/libfprint/drivers/vfs5011.c +++ b/libfprint/drivers/vfs5011.c @@ -745,7 +745,6 @@ activate_loop_complete (FpiSsm *ssm, FpDevice *_dev, GError *error) submit_image (ssm, self, dev); fpi_image_device_report_finger_status (dev, FALSE); } - fpi_ssm_free (ssm); self->loop_running = FALSE; @@ -793,7 +792,6 @@ open_loop_complete (FpiSsm *ssm, FpDevice *_dev, GError *error) self->init_sequence.receive_buf = NULL; fpi_image_device_open_complete (dev, error); - fpi_ssm_free (ssm); } static void diff --git a/libfprint/fpi-ssm.c b/libfprint/fpi-ssm.c index 1569be83..a614860d 100644 --- a/libfprint/fpi-ssm.c +++ b/libfprint/fpi-ssm.c @@ -51,6 +51,7 @@ * * To start a ssm, you pass in a completion callback function to fpi_ssm_start() * which gets called when the ssm completes (both on error and on failure). + * Starting a ssm also takes ownership of it. * * To iterate to the next state, call fpi_ssm_next_state(). It is legal to * attempt to iterate beyond the final state - this is equivalent to marking @@ -58,6 +59,7 @@ * * To mark successful completion of a SSM, either iterate beyond the final * state or call fpi_ssm_mark_completed() from any state. + * This will also invalidate the machine, freeing it. * * To mark failed completion of a SSM, call fpi_ssm_mark_failed() from any * state. You must pass a non-zero error code. @@ -125,7 +127,6 @@ fpi_ssm_new (FpDevice *dev, * @ssm_data_destroy: (nullable): #GDestroyNotify for @ssm_data * * Sets @machine's data (freeing the existing data, if any). - * */ void fpi_ssm_set_data (FpiSsm *machine, @@ -182,12 +183,16 @@ __ssm_call_handler (FpiSsm *machine) /** * fpi_ssm_start: - * @ssm: an #FpiSsm state machine + * @ssm: (transfer full): an #FpiSsm state machine * @callback: the #FpiSsmCompletedCallback callback to call on completion * * Starts a state machine. You can also use this function to restart * a completed or failed state machine. The @callback will be called * on completion. + * + * Note that @ssm will be stolen when this function is called. + * So that all associated data will be free'ed automatically, after the + * @callback is ran. */ void fpi_ssm_start (FpiSsm *ssm, FpiSsmCompletedCallback callback) @@ -210,7 +215,6 @@ __subsm_complete (FpiSsm *ssm, FpDevice *_dev, GError *error) fpi_ssm_mark_failed (parent, error); else fpi_ssm_next_state (parent); - fpi_ssm_free (ssm); } /** @@ -253,6 +257,7 @@ fpi_ssm_mark_completed (FpiSsm *machine) machine->callback (machine, machine->dev, error); } + fpi_ssm_free (machine); } /** @@ -260,7 +265,7 @@ fpi_ssm_mark_completed (FpiSsm *machine) * @machine: an #FpiSsm state machine * @error: a #GError * - * Mark a state machine as failed with @error as the error code. + * Mark a state machine as failed with @error as the error code, completing it. */ void fpi_ssm_mark_failed (FpiSsm *machine, GError *error) @@ -305,6 +310,8 @@ fpi_ssm_next_state (FpiSsm *machine) * @state: the state to jump to * * Jump to the @state state, bypassing intermediary states. + * If @state is the last state, the machine won't be completed unless + * fpi_ssm_mark_completed() isn't explicitly called. */ void fpi_ssm_jump_to_state (FpiSsm *machine, int state) From 2642fc65600033e8ba94287eab5cbee893ad3f6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 26 Nov 2019 21:23:42 +0100 Subject: [PATCH 030/237] fpi-usb-transfer: Take ownership of the transfer when submitting it When a transfer is completed, we automatically unref it since we can't consider it valid anymore since this point. Update the drivers not to free the transfer after submitting anymore. --- libfprint/drivers/aes1610.c | 3 --- libfprint/drivers/aes2501.c | 4 ---- libfprint/drivers/aes2550.c | 9 --------- libfprint/drivers/aes3k.c | 1 - libfprint/drivers/aeslib.c | 1 - libfprint/drivers/aesx660.c | 2 -- libfprint/drivers/elan.c | 2 -- libfprint/drivers/etes603.c | 1 - libfprint/drivers/synaptics/synaptics.c | 8 +++----- libfprint/drivers/upeksonly.c | 5 ----- libfprint/drivers/upektc.c | 6 ------ libfprint/drivers/upektc_img.c | 3 --- libfprint/drivers/upekts.c | 11 ----------- libfprint/drivers/uru4000.c | 3 --- libfprint/drivers/vcom5s.c | 3 --- libfprint/drivers/vfs0050.c | 5 ----- libfprint/drivers/vfs101.c | 3 --- libfprint/drivers/vfs301_proto.c | 10 +++------- libfprint/drivers/vfs5011.c | 3 --- libfprint/fpi-usb-transfer.c | 14 ++++---------- 20 files changed, 10 insertions(+), 87 deletions(-) diff --git a/libfprint/drivers/aes1610.c b/libfprint/drivers/aes1610.c index 03265652..4261b052 100644 --- a/libfprint/drivers/aes1610.c +++ b/libfprint/drivers/aes1610.c @@ -155,7 +155,6 @@ generic_read_ignore_data (FpiSsm *ssm, FpDevice *dev, transfer->short_is_error = TRUE; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, generic_ignore_data_cb, NULL); - fpi_usb_transfer_unref (transfer); } /****** FINGER PRESENCE DETECTION ******/ @@ -238,7 +237,6 @@ finger_det_reqs_cb (FpImageDevice *dev, GError *error, transfer->short_is_error = TRUE; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, finger_det_data_cb, NULL); - fpi_usb_transfer_unref (transfer); } static void @@ -683,7 +681,6 @@ capture_run_state (FpiSsm *ssm, FpDevice *_dev) transfer->short_is_error = TRUE; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, capture_read_strip_cb, NULL); - fpi_usb_transfer_unref (transfer); break; } ; diff --git a/libfprint/drivers/aes2501.c b/libfprint/drivers/aes2501.c index 1b59c56b..e18b4fe9 100644 --- a/libfprint/drivers/aes2501.c +++ b/libfprint/drivers/aes2501.c @@ -126,7 +126,6 @@ read_regs_rq_cb (FpImageDevice *dev, GError *error, void *user_data) fpi_usb_transfer_fill_bulk (transfer, EP_IN, READ_REGS_LEN); fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, read_regs_data_cb, rdata); - fpi_usb_transfer_unref (transfer); } static void @@ -210,7 +209,6 @@ generic_read_ignore_data (FpiSsm *ssm, FpDevice *dev, fpi_usb_transfer_fill_bulk (transfer, EP_IN, bytes); fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, generic_ignore_data_cb, NULL); - fpi_usb_transfer_unref (transfer); } /****** IMAGE PROCESSING ******/ @@ -315,7 +313,6 @@ finger_det_reqs_cb (FpImageDevice *dev, GError *error, fpi_usb_transfer_fill_bulk (transfer, EP_IN, FINGER_DETECTION_LEN); fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, finger_det_data_cb, NULL); - fpi_usb_transfer_unref (transfer); } static void @@ -547,7 +544,6 @@ capture_run_state (FpiSsm *ssm, FpDevice *device) fpi_usb_transfer_fill_bulk (transfer, EP_IN, STRIP_CAPTURE_LEN); fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, capture_read_strip_cb, NULL); - fpi_usb_transfer_unref (transfer); break; } } diff --git a/libfprint/drivers/aes2550.c b/libfprint/drivers/aes2550.c index b95b0532..f3f51d66 100644 --- a/libfprint/drivers/aes2550.c +++ b/libfprint/drivers/aes2550.c @@ -134,7 +134,6 @@ finger_det_reqs_cb (FpiUsbTransfer *t, FpDevice *device, fpi_usb_transfer_fill_bulk (transfer, EP_IN, AES2550_EP_IN_BUF_SIZE); fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, finger_det_data_cb, NULL); - fpi_usb_transfer_unref (transfer); } static void @@ -157,7 +156,6 @@ start_finger_detection (FpImageDevice *dev) sizeof (finger_det_reqs), NULL); fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, finger_det_reqs_cb, NULL); - fpi_usb_transfer_unref (transfer); } /****** CAPTURE ******/ @@ -335,7 +333,6 @@ capture_run_state (FpiSsm *ssm, FpDevice *dev) transfer->short_is_error = TRUE; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, capture_reqs_cb, NULL); - fpi_usb_transfer_unref (transfer); } break; @@ -347,7 +344,6 @@ capture_run_state (FpiSsm *ssm, FpDevice *dev) transfer->ssm = ssm; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, capture_read_data_cb, NULL); - fpi_usb_transfer_unref (transfer); } break; @@ -363,7 +359,6 @@ capture_run_state (FpiSsm *ssm, FpDevice *dev) transfer->short_is_error = TRUE; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, capture_set_idle_reqs_cb, NULL); - fpi_usb_transfer_unref (transfer); } break; } @@ -482,7 +477,6 @@ activate_run_state (FpiSsm *ssm, FpDevice *dev) transfer->short_is_error = TRUE; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, init_reqs_cb, NULL); - fpi_usb_transfer_unref (transfer); } break; @@ -494,7 +488,6 @@ activate_run_state (FpiSsm *ssm, FpDevice *dev) transfer->ssm = ssm; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, init_read_data_cb, NULL); - fpi_usb_transfer_unref (transfer); } break; @@ -509,7 +502,6 @@ activate_run_state (FpiSsm *ssm, FpDevice *dev) transfer->short_is_error = TRUE; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, init_reqs_cb, NULL); - fpi_usb_transfer_unref (transfer); } break; @@ -521,7 +513,6 @@ activate_run_state (FpiSsm *ssm, FpDevice *dev) transfer->ssm = ssm; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, calibrate_read_data_cb, NULL); - fpi_usb_transfer_unref (transfer); } break; } diff --git a/libfprint/drivers/aes3k.c b/libfprint/drivers/aes3k.c index f73ac026..da3b6a39 100644 --- a/libfprint/drivers/aes3k.c +++ b/libfprint/drivers/aes3k.c @@ -142,7 +142,6 @@ do_capture (FpImageDevice *dev) fpi_usb_transfer_submit (priv->img_trf, 0, fpi_device_get_cancellable (FP_DEVICE (dev)), img_cb, NULL); - fpi_usb_transfer_unref (priv->img_trf); } static void diff --git a/libfprint/drivers/aeslib.c b/libfprint/drivers/aeslib.c index 8f92d876..4839c62f 100644 --- a/libfprint/drivers/aeslib.c +++ b/libfprint/drivers/aeslib.c @@ -88,7 +88,6 @@ do_write_regv (FpImageDevice *dev, struct write_regv_data *wdata, int upper_boun transfer->short_is_error = TRUE; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, write_regv_trf_complete, wdata); - fpi_usb_transfer_unref (transfer); } /* write the next batch of registers to be written, or if there are no more, diff --git a/libfprint/drivers/aesx660.c b/libfprint/drivers/aesx660.c index 3f13252c..b4d86033 100644 --- a/libfprint/drivers/aesx660.c +++ b/libfprint/drivers/aesx660.c @@ -68,7 +68,6 @@ aesX660_send_cmd_timeout (FpiSsm *ssm, cmd_len, NULL); transfer->ssm = ssm; fpi_usb_transfer_submit (transfer, timeout, NULL, callback, NULL); - fpi_usb_transfer_unref (transfer); } static void @@ -100,7 +99,6 @@ aesX660_read_response (FpiSsm *ssm, transfer->ssm = ssm; transfer->short_is_error = short_is_error; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, cancel, callback, NULL); - fpi_usb_transfer_unref (transfer); } static void diff --git a/libfprint/drivers/elan.c b/libfprint/drivers/elan.c index 5e80be5b..e2767dd6 100644 --- a/libfprint/drivers/elan.c +++ b/libfprint/drivers/elan.c @@ -406,7 +406,6 @@ elan_cmd_read (FpiSsm *ssm, FpDevice *dev) cancellable = fpi_device_get_cancellable (dev); fpi_usb_transfer_submit (transfer, self->cmd_timeout, cancellable, elan_cmd_cb, NULL); - fpi_usb_transfer_unref (transfer); } static void @@ -449,7 +448,6 @@ elan_run_cmd (FpiSsm *ssm, cancellable, elan_cmd_cb, NULL); - fpi_usb_transfer_unref (transfer); } enum stop_capture_states { diff --git a/libfprint/drivers/etes603.c b/libfprint/drivers/etes603.c index 55f0139b..5cd7f0bd 100644 --- a/libfprint/drivers/etes603.c +++ b/libfprint/drivers/etes603.c @@ -710,7 +710,6 @@ async_tx (FpDevice *dev, unsigned int ep, void *cb, transfer->ssm = ssm; fpi_usb_transfer_fill_bulk_full (transfer, ep, buffer, length, NULL); fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, cb, NULL); - fpi_usb_transfer_unref (transfer); } diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index 4932d01c..ccaf60ee 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -205,7 +205,7 @@ static void synaptics_cmd_run_state (FpiSsm *ssm, FpDevice *dev) { - g_autoptr(FpiUsbTransfer) transfer = NULL; + FpiUsbTransfer *transfer; FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (dev); switch (fpi_ssm_get_cur_state (ssm)) @@ -219,7 +219,7 @@ synaptics_cmd_run_state (FpiSsm *ssm, NULL, fpi_ssm_usb_transfer_cb, NULL); - g_clear_pointer (&self->cmd_pending_transfer, fpi_usb_transfer_unref); + self->cmd_pending_transfer = NULL; } else { @@ -317,7 +317,7 @@ synaptics_sensor_cmd (FpiDeviceSynaptics *self, gssize payload_len, SynCmdMsgCallback callback) { - g_autoptr(FpiUsbTransfer) transfer = NULL; + FpiUsbTransfer *transfer; guint8 real_seq_num; gint msg_len; gint res; @@ -984,7 +984,6 @@ dev_probe (FpDevice *device) transfer->buffer[0] = SENSOR_CMD_GET_VERSION; if (!fpi_usb_transfer_submit_sync (transfer, 1000, &error)) goto err_close; - fpi_usb_transfer_unref (transfer); transfer = fpi_usb_transfer_new (device); @@ -1039,7 +1038,6 @@ dev_probe (FpDevice *device) fp_dbg ("Target: %d", self->mis_version.target); fp_dbg ("Product: %d", self->mis_version.product); - fpi_usb_transfer_unref (transfer); /* We need at least firmware version 10.1, and for 10.1 build 2989158 */ if (self->mis_version.version_major < 10 || diff --git a/libfprint/drivers/upeksonly.c b/libfprint/drivers/upeksonly.c index e1cd7e52..f477b836 100644 --- a/libfprint/drivers/upeksonly.c +++ b/libfprint/drivers/upeksonly.c @@ -635,7 +635,6 @@ write_regs_iterate (struct write_regs_data *wrdata) transfer->short_is_error = TRUE; transfer->ssm = wrdata->ssm; fpi_usb_transfer_submit (transfer, CTRL_TIMEOUT, NULL, write_regs_cb, NULL); - fpi_usb_transfer_unref (transfer); transfer->buffer[0] = regwrite->value; } @@ -688,7 +687,6 @@ sm_write_reg (FpiSsm *ssm, transfer->short_is_error = TRUE; transfer->ssm = ssm; fpi_usb_transfer_submit (transfer, CTRL_TIMEOUT, NULL, sm_write_reg_cb, NULL); - fpi_usb_transfer_unref (transfer); transfer->buffer[0] = value; } @@ -737,7 +735,6 @@ sm_read_reg (FpiSsm *ssm, NULL, sm_read_reg_cb, NULL); - fpi_usb_transfer_unref (transfer); } static void @@ -782,7 +779,6 @@ sm_await_intr (FpiSsm *ssm, fpi_device_get_cancellable (FP_DEVICE (dev)), sm_await_intr_cb, NULL); - fpi_usb_transfer_unref (transfer); } /***** AWAIT FINGER *****/ @@ -1419,7 +1415,6 @@ dev_activate (FpImageDevice *dev) self->deactivating = FALSE; self->capturing = FALSE; - self->img_transfers = g_ptr_array_new_full (NUM_BULK_TRANSFERS, (GDestroyNotify) fpi_usb_transfer_unref); self->num_flying = 0; for (i = 0; i < self->img_transfers->len; i++) diff --git a/libfprint/drivers/upektc.c b/libfprint/drivers/upektc.c index e1254ce6..92b1930b 100644 --- a/libfprint/drivers/upektc.c +++ b/libfprint/drivers/upektc.c @@ -128,7 +128,6 @@ activate_run_state (FpiSsm *ssm, FpDevice *dev) transfer->short_is_error = TRUE; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, write_init_cb, NULL); - fpi_usb_transfer_unref (transfer); } break; @@ -142,7 +141,6 @@ activate_run_state (FpiSsm *ssm, FpDevice *dev) transfer->ssm = ssm; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, read_init_data_cb, NULL); - fpi_usb_transfer_unref (transfer); } break; } @@ -225,7 +223,6 @@ finger_det_cmd_cb (FpiUsbTransfer *t, FpDevice *device, IMAGE_SIZE); fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, finger_det_data_cb, NULL); - fpi_usb_transfer_unref (transfer); } static void @@ -249,7 +246,6 @@ start_finger_detection (FpImageDevice *dev) UPEKTC_CMD_LEN, NULL); fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, finger_det_cmd_cb, NULL); - fpi_usb_transfer_unref (transfer); } /****** CAPTURE ******/ @@ -309,7 +305,6 @@ capture_run_state (FpiSsm *ssm, FpDevice *_dev) transfer->short_is_error = TRUE; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, capture_cmd_cb, NULL); - fpi_usb_transfer_unref (transfer); } break; @@ -323,7 +318,6 @@ capture_run_state (FpiSsm *ssm, FpDevice *_dev) transfer->short_is_error = TRUE; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, capture_read_data_cb, NULL); - fpi_usb_transfer_unref (transfer); } break; } diff --git a/libfprint/drivers/upektc_img.c b/libfprint/drivers/upektc_img.c index 28a709f7..b9724c17 100644 --- a/libfprint/drivers/upektc_img.c +++ b/libfprint/drivers/upektc_img.c @@ -100,7 +100,6 @@ upektc_img_submit_req (FpiSsm *ssm, transfer->ssm = ssm; transfer->short_is_error = TRUE; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, cb, NULL); - fpi_usb_transfer_unref (transfer); } static void @@ -120,7 +119,6 @@ upektc_img_read_data (FpiSsm *ssm, NULL); transfer->ssm = ssm; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, cb, NULL); - fpi_usb_transfer_unref (transfer); } /****** CAPTURE ******/ @@ -557,7 +555,6 @@ activate_run_state (FpiSsm *ssm, FpDevice *dev) transfer->ssm = ssm; fpi_usb_transfer_submit (transfer, CTRL_TIMEOUT, NULL, init_reqs_ctrl_cb, NULL); - fpi_usb_transfer_unref (transfer); } break; diff --git a/libfprint/drivers/upekts.c b/libfprint/drivers/upekts.c index 4bc6556f..05cd9c54 100644 --- a/libfprint/drivers/upekts.c +++ b/libfprint/drivers/upekts.c @@ -226,7 +226,6 @@ busy_ack_retry_read (FpDevice *device, struct read_msg_data *udata) transfer->short_is_error = TRUE; fpi_usb_transfer_submit (transfer, TIMEOUT, NULL, busy_ack_sent_cb, udata); - fpi_usb_transfer_unref (transfer); } /* Returns 0 if message was handled, 1 if it was a device-busy message, and @@ -416,7 +415,6 @@ read_msg_cb (FpiUsbTransfer *transfer, FpDevice *device, fpi_usb_transfer_submit (etransfer, TIMEOUT, NULL, read_msg_extend_cb, udata); - fpi_usb_transfer_unref (etransfer); return; } @@ -442,7 +440,6 @@ __read_msg_async (FpDevice *device, struct read_msg_data *udata) fpi_usb_transfer_fill_bulk_full (transfer, EP_IN, udata->buffer, udata->buflen, NULL); fpi_usb_transfer_submit (transfer, TIMEOUT, NULL, read_msg_cb, udata); - fpi_usb_transfer_unref (transfer); } static void @@ -676,7 +673,6 @@ initsm_send_msg28_handler (FpiSsm *ssm, transfer->ssm = ssm; transfer->short_is_error = TRUE; fpi_usb_transfer_submit (transfer, TIMEOUT, NULL, fpi_ssm_usb_transfer_cb, NULL); - fpi_usb_transfer_unref (transfer); } static void @@ -697,7 +693,6 @@ initsm_run_state (FpiSsm *ssm, FpDevice *dev) transfer->ssm = ssm; transfer->short_is_error = TRUE; fpi_usb_transfer_submit (transfer, TIMEOUT, NULL, fpi_ssm_usb_transfer_cb, NULL); - fpi_usb_transfer_unref (transfer); break; case READ_MSG03: @@ -709,7 +704,6 @@ initsm_run_state (FpiSsm *ssm, FpDevice *dev) transfer->ssm = ssm; transfer->short_is_error = TRUE; fpi_usb_transfer_submit (transfer, TIMEOUT, NULL, fpi_ssm_usb_transfer_cb, NULL); - fpi_usb_transfer_unref (transfer); break; case READ_MSG05: @@ -820,7 +814,6 @@ deinitsm_state_handler (FpiSsm *ssm, FpDevice *dev) transfer->short_is_error = TRUE; transfer->ssm = ssm; fpi_usb_transfer_submit (transfer, TIMEOUT, NULL, fpi_ssm_usb_transfer_cb, NULL); - fpi_usb_transfer_unref (transfer); break; case READ_MSG01:; @@ -953,7 +946,6 @@ enroll_start_sm_run_state (FpiSsm *ssm, FpDevice *dev) transfer->ssm = ssm; fpi_usb_transfer_submit (transfer, TIMEOUT, NULL, fpi_ssm_usb_transfer_cb, NULL); - fpi_usb_transfer_unref (transfer); break; case READ_ENROLL_MSG28:; @@ -1205,7 +1197,6 @@ enroll_iterate (FpDevice *dev) transfer->short_is_error = TRUE; fpi_usb_transfer_submit (transfer, TIMEOUT, NULL, enroll_iterate_cmd_cb, NULL); - fpi_usb_transfer_unref (transfer); } static void @@ -1319,7 +1310,6 @@ verify_start_sm_run_state (FpiSsm *ssm, FpDevice *dev) transfer->short_is_error = TRUE; transfer->ssm = ssm; fpi_usb_transfer_submit (transfer, TIMEOUT, NULL, fpi_ssm_usb_transfer_cb, NULL); - fpi_usb_transfer_unref (transfer); break; } @@ -1519,7 +1509,6 @@ verify_iterate (FpDevice *dev) transfer->short_is_error = TRUE; fpi_usb_transfer_submit (transfer, TIMEOUT, NULL, verify_wr2800_cb, NULL); - fpi_usb_transfer_unref (transfer); } } diff --git a/libfprint/drivers/uru4000.c b/libfprint/drivers/uru4000.c index 89328d0f..7e287244 100644 --- a/libfprint/drivers/uru4000.c +++ b/libfprint/drivers/uru4000.c @@ -181,7 +181,6 @@ write_regs (FpImageDevice *dev, uint16_t first_reg, num_regs); memcpy (transfer->buffer, values, num_regs); fpi_usb_transfer_submit (transfer, CTRL_TIMEOUT, NULL, callback, user_data); - fpi_usb_transfer_unref (transfer); } static void @@ -207,7 +206,6 @@ read_regs (FpImageDevice *dev, uint16_t first_reg, G_USB_DEVICE_RECIPIENT_DEVICE, USB_RQ, first_reg, 0, num_regs); fpi_usb_transfer_submit (transfer, CTRL_TIMEOUT, NULL, callback, user_data); - fpi_usb_transfer_unref (transfer); } /* @@ -365,7 +363,6 @@ start_irq_handler (FpImageDevice *dev) EP_INTR, IRQ_LENGTH); fpi_usb_transfer_submit (transfer, 0, self->irq_cancellable, irq_handler, NULL); - fpi_usb_transfer_unref (transfer); } static void diff --git a/libfprint/drivers/vcom5s.c b/libfprint/drivers/vcom5s.c index edd2dd40..1a2b7956 100644 --- a/libfprint/drivers/vcom5s.c +++ b/libfprint/drivers/vcom5s.c @@ -103,7 +103,6 @@ sm_write_reg (FpiSsm *ssm, transfer->ssm = ssm; fpi_usb_transfer_submit (transfer, CTRL_TIMEOUT, NULL, sm_write_reg_cb, NULL); - fpi_usb_transfer_unref (transfer); } static void @@ -133,7 +132,6 @@ sm_exec_cmd (FpiSsm *ssm, transfer->ssm = ssm; fpi_usb_transfer_submit (transfer, CTRL_TIMEOUT, NULL, sm_exec_cmd_cb, NULL); - fpi_usb_transfer_unref (transfer); } /***** FINGER DETECTION *****/ @@ -227,7 +225,6 @@ capture_iterate (FpiSsm *ssm, NULL); fpi_usb_transfer_submit (transfer, CTRL_TIMEOUT, NULL, capture_cb, NULL); - fpi_usb_transfer_unref (transfer); } diff --git a/libfprint/drivers/vfs0050.c b/libfprint/drivers/vfs0050.c index 1be272b9..43252c03 100644 --- a/libfprint/drivers/vfs0050.c +++ b/libfprint/drivers/vfs0050.c @@ -56,7 +56,6 @@ async_write (FpiSsm *ssm, transfer->short_is_error = TRUE; fpi_usb_transfer_submit (transfer, VFS_USB_TIMEOUT, NULL, async_write_callback, NULL); - fpi_usb_transfer_unref (transfer); } /* Callback for async_read */ @@ -108,7 +107,6 @@ async_read (FpiSsm *ssm, fpi_usb_transfer_submit (transfer, VFS_USB_TIMEOUT, NULL, async_read_callback, NULL); - fpi_usb_transfer_unref (transfer); } /* Callback for async_abort */ @@ -160,7 +158,6 @@ async_abort (FpDevice *dev, FpiSsm *ssm, int ep) fpi_usb_transfer_submit (transfer, VFS_USB_ABORT_TIMEOUT, NULL, async_abort_callback, NULL); - fpi_usb_transfer_unref (transfer); } /* Image processing functions */ @@ -564,7 +561,6 @@ activate_ssm (FpiSsm *ssm, FpDevice *dev) 0, fpi_device_get_cancellable (dev), interrupt_callback, NULL); - fpi_usb_transfer_unref (transfer); /* I've put it here to be sure that data is cleared */ clear_data (self); @@ -614,7 +610,6 @@ activate_ssm (FpiSsm *ssm, FpDevice *dev) transfer->ssm = ssm; fpi_usb_transfer_submit (transfer, VFS_USB_TIMEOUT, NULL, receive_callback, NULL); - fpi_usb_transfer_unref (transfer); break; } diff --git a/libfprint/drivers/vfs101.c b/libfprint/drivers/vfs101.c index 9ca1b0a6..5dedab7d 100644 --- a/libfprint/drivers/vfs101.c +++ b/libfprint/drivers/vfs101.c @@ -219,7 +219,6 @@ async_send (FpiSsm *ssm, transfer->short_is_error = TRUE; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, async_send_cb, NULL); - fpi_usb_transfer_unref (transfer); } /* Callback of asynchronous recv */ @@ -282,7 +281,6 @@ async_recv (FpiSsm *ssm, transfer->ssm = ssm; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, async_recv_cb, NULL); - fpi_usb_transfer_unref (transfer); } static void async_load (FpiSsm *ssm, @@ -369,7 +367,6 @@ async_load (FpiSsm *ssm, transfer->ssm = ssm; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, async_load_cb, NULL); - fpi_usb_transfer_unref (transfer); } /* Submit asynchronous sleep */ diff --git a/libfprint/drivers/vfs301_proto.c b/libfprint/drivers/vfs301_proto.c index 5d02597f..103e890b 100644 --- a/libfprint/drivers/vfs301_proto.c +++ b/libfprint/drivers/vfs301_proto.c @@ -67,8 +67,7 @@ static void usb_recv (FpDeviceVfs301 *dev, guint8 endpoint, int max_bytes, FpiUsbTransfer **out, GError **error) { GError *err = NULL; - - g_autoptr(FpiUsbTransfer) transfer = NULL; + FpiUsbTransfer *transfer; /* XXX: This function swallows any transfer errors, that is obviously * quite bad (it used to assert on no-error)! */ @@ -98,8 +97,7 @@ static void usb_send (FpDeviceVfs301 *dev, const guint8 *data, gssize length, GError **error) { GError *err = NULL; - - g_autoptr(FpiUsbTransfer) transfer = NULL; + FpiUsbTransfer *transfer = NULL; /* XXX: This function swallows any transfer errors, that is obviously * quite bad (it used to assert on no-error)! */ @@ -471,7 +469,7 @@ int vfs301_proto_peek_event (FpDeviceVfs301 *dev) { g_autoptr(GError) error = NULL; - g_autoptr(FpiUsbTransfer) transfer = NULL; + FpiUsbTransfer *transfer; const char no_event[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; const char got_event[] = {0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00}; @@ -540,7 +538,6 @@ vfs301_proto_process_event_cb (FpiUsbTransfer *transfer, fpi_usb_transfer_fill_bulk (new, VFS301_RECEIVE_ENDPOINT_DATA, VFS301_FP_RECV_LEN_2); fpi_usb_transfer_submit (new, VFS301_FP_RECV_TIMEOUT, NULL, vfs301_proto_process_event_cb, NULL); - fpi_usb_transfer_unref (new); return; } } @@ -580,7 +577,6 @@ vfs301_proto_process_event_start (FpDeviceVfs301 *dev) fpi_usb_transfer_fill_bulk (transfer, VFS301_RECEIVE_ENDPOINT_DATA, VFS301_FP_RECV_LEN_1); fpi_usb_transfer_submit (transfer, VFS301_FP_RECV_TIMEOUT, NULL, vfs301_proto_process_event_cb, NULL); - fpi_usb_transfer_unref (transfer); } int diff --git a/libfprint/drivers/vfs5011.c b/libfprint/drivers/vfs5011.c index 007e4868..dbf82767 100644 --- a/libfprint/drivers/vfs5011.c +++ b/libfprint/drivers/vfs5011.c @@ -168,7 +168,6 @@ usbexchange_loop (FpiSsm *ssm, FpDevice *_dev) transfer->short_is_error = TRUE; fpi_usb_transfer_submit (transfer, data->timeout, NULL, async_send_cb, NULL); - fpi_usb_transfer_unref (transfer); break; case ACTION_RECEIVE: @@ -180,7 +179,6 @@ usbexchange_loop (FpiSsm *ssm, FpDevice *_dev) transfer->ssm = ssm; fpi_usb_transfer_submit (transfer, data->timeout, NULL, async_recv_cb, NULL); - fpi_usb_transfer_unref (transfer); break; default: @@ -466,7 +464,6 @@ capture_chunk_async (FpDeviceVfs5011 *self, transfer->ssm = ssm; fpi_usb_transfer_submit (transfer, timeout, fpi_device_get_cancellable (FP_DEVICE (self)), chunk_capture_callback, NULL); - fpi_usb_transfer_unref (transfer); } /* diff --git a/libfprint/fpi-usb-transfer.c b/libfprint/fpi-usb-transfer.c index 6b296212..64d706f1 100644 --- a/libfprint/fpi-usb-transfer.c +++ b/libfprint/fpi-usb-transfer.c @@ -356,7 +356,7 @@ transfer_finish_cb (GObject *source_object, GAsyncResult *res, gpointer user_dat /** * fpi_usb_transfer_submit: - * @transfer: The transfer to submit, must have been filled. + * @transfer: (transfer full): The transfer to submit, must have been filled. * @timeout_ms: Timeout for the transfer in ms * @cancellable: Cancellable to use, e.g. fpi_device_get_cancellable() * @callback: Callback on completion or error @@ -364,10 +364,9 @@ transfer_finish_cb (GObject *source_object, GAsyncResult *res, gpointer user_dat * * Submit a USB transfer with a specific timeout and callback functions. * - * Note that #FpiUsbTransfer is owned by the user. In most cases, you - * should call fpi_usb_transfer_unref() just after calling this function. - * Doing so means that all associated data will be free'ed automatically - * after the callback ran. + * Note that #FpiUsbTransfer will be stolen when this function is called. + * So that all associated data will be free'ed automatically, after the + * callback ran unless fpi_usb_transfer_ref() is explictly called. */ void fpi_usb_transfer_submit (FpiUsbTransfer *transfer, @@ -385,11 +384,6 @@ fpi_usb_transfer_submit (FpiUsbTransfer *transfer, transfer->callback = callback; transfer->user_data = user_data; - /* Grab a reference, this means that one can simply unref after submit and - * trust for the data to disappear without explicit management by the callback - * function. */ - fpi_usb_transfer_ref (transfer); - log_transfer (transfer, TRUE, NULL); switch (transfer->type) From 65d0d5e3e0884369687cd6214a76e167d3e1a1dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 27 Nov 2019 19:36:24 +0100 Subject: [PATCH 031/237] fpi-ssm: Add a usb transfer callback with data as weak pointer Allow to pass a double-pointer to be nullified as the transfer data in order to mark it as NULL when the transfer is done. This is useful if we're keeping the transfer around in order to check that no one is currently running. --- libfprint/fpi-ssm.c | 29 +++++++++++++++++++++++++++++ libfprint/fpi-ssm.h | 6 +++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/libfprint/fpi-ssm.c b/libfprint/fpi-ssm.c index a614860d..6a02a2c7 100644 --- a/libfprint/fpi-ssm.c +++ b/libfprint/fpi-ssm.c @@ -413,3 +413,32 @@ fpi_ssm_usb_transfer_cb (FpiUsbTransfer *transfer, FpDevice *device, else fpi_ssm_next_state (transfer->ssm); } + +/** + * fpi_ssm_usb_transfer_with_weak_pointer_cb: + * @transfer: a #FpiUsbTransfer + * @device: a #FpDevice + * @weak_ptr: A #gpointer pointer to nullify. You can pass a pointer to any + * #gpointer to nullify when the callback is completed. I.e a + * pointer to the current #FpiUsbTransfer. + * @error: The #GError or %NULL + * + * Can be used in as a #FpiUsbTransfer callback handler to automatically + * advance or fail a statemachine on transfer completion. + * Passing a #gpointer* as @weak_ptr permits to nullify it once we're done + * with the transfer. + * + * Make sure to set the #FpiSsm on the transfer. + */ +void +fpi_ssm_usb_transfer_with_weak_pointer_cb (FpiUsbTransfer *transfer, + FpDevice *device, gpointer weak_ptr, + GError *error) +{ + g_return_if_fail (transfer->ssm); + + if (weak_ptr) + g_nullify_pointer ((gpointer *) weak_ptr); + + fpi_ssm_usb_transfer_cb (transfer, device, weak_ptr, error); +} diff --git a/libfprint/fpi-ssm.h b/libfprint/fpi-ssm.h index 8d45162f..704271d4 100644 --- a/libfprint/fpi-ssm.h +++ b/libfprint/fpi-ssm.h @@ -91,5 +91,9 @@ void fpi_ssm_next_state_timeout_cb (FpDevice *dev, void *data); void fpi_ssm_usb_transfer_cb (FpiUsbTransfer *transfer, FpDevice *device, - gpointer user_data, + gpointer user_date, GError *error); +void fpi_ssm_usb_transfer_with_weak_pointer_cb (FpiUsbTransfer *transfer, + FpDevice *device, + gpointer weak_ptr, + GError *error); From 587131a6bdfa9f6dd6df90cb28384513aa897e5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 27 Nov 2019 20:07:02 +0100 Subject: [PATCH 032/237] drivers: Use more fpi_ssm_usb_transfer_cb when possible Replace all the transfer callbacks where we just switch to the next state or fail with fpi_ssm_usb_transfer_cb. --- libfprint/drivers/aes1610.c | 14 +---------- libfprint/drivers/aes2501.c | 15 +----------- libfprint/drivers/aes2550.c | 43 ++++------------------------------ libfprint/drivers/aesx660.c | 26 +++++++------------- libfprint/drivers/upeksonly.c | 14 ++--------- libfprint/drivers/upektc.c | 12 +--------- libfprint/drivers/upektc_img.c | 12 +--------- libfprint/drivers/vcom5s.c | 28 ++++------------------ 8 files changed, 23 insertions(+), 141 deletions(-) diff --git a/libfprint/drivers/aes1610.c b/libfprint/drivers/aes1610.c index 4261b052..bc39b24f 100644 --- a/libfprint/drivers/aes1610.c +++ b/libfprint/drivers/aes1610.c @@ -116,18 +116,6 @@ stub_capture_stop_cb (FpImageDevice *dev, GError *error, } } - -/* check that read succeeded but ignore all data */ -static void -generic_ignore_data_cb (FpiUsbTransfer *transfer, FpDevice *device, - gpointer user_data, GError *error) -{ - if (error) - fpi_ssm_mark_failed (transfer->ssm, error); - else - fpi_ssm_next_state (transfer->ssm); -} - static void generic_write_regv_cb (FpImageDevice *dev, GError *error, void *user_data) @@ -154,7 +142,7 @@ generic_read_ignore_data (FpiSsm *ssm, FpDevice *dev, transfer->ssm = ssm; transfer->short_is_error = TRUE; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, - generic_ignore_data_cb, NULL); + fpi_ssm_usb_transfer_cb, NULL); } /****** FINGER PRESENCE DETECTION ******/ diff --git a/libfprint/drivers/aes2501.c b/libfprint/drivers/aes2501.c index e18b4fe9..1aa05388 100644 --- a/libfprint/drivers/aes2501.c +++ b/libfprint/drivers/aes2501.c @@ -182,19 +182,6 @@ generic_write_regv_cb (FpImageDevice *dev, GError *error, fpi_ssm_mark_failed (ssm, error); } -/* check that read succeeded but ignore all data */ -static void -generic_ignore_data_cb (FpiUsbTransfer *transfer, FpDevice *dev, - gpointer user_data, GError *error) -{ - FpiSsm *ssm = transfer->ssm; - - if (error) - fpi_ssm_mark_failed (ssm, error); - else - fpi_ssm_next_state (ssm); -} - /* read the specified number of bytes from the IN endpoint but throw them * away, then increment the SSM */ static void @@ -208,7 +195,7 @@ generic_read_ignore_data (FpiSsm *ssm, FpDevice *dev, transfer->short_is_error = TRUE; fpi_usb_transfer_fill_bulk (transfer, EP_IN, bytes); fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, - generic_ignore_data_cb, NULL); + fpi_ssm_usb_transfer_cb, NULL); } /****** IMAGE PROCESSING ******/ diff --git a/libfprint/drivers/aes2550.c b/libfprint/drivers/aes2550.c index f3f51d66..1ebf933a 100644 --- a/libfprint/drivers/aes2550.c +++ b/libfprint/drivers/aes2550.c @@ -216,16 +216,6 @@ process_strip_data (FpiSsm *ssm, FpImageDevice *dev, return TRUE; } -static void -capture_reqs_cb (FpiUsbTransfer *transfer, FpDevice *device, - gpointer user_data, GError *error) -{ - if (!error) - fpi_ssm_next_state (transfer->ssm); - else - fpi_ssm_mark_failed (transfer->ssm, error); -} - static void capture_set_idle_reqs_cb (FpiUsbTransfer *transfer, FpDevice *device, gpointer user_data, @@ -332,7 +322,7 @@ capture_run_state (FpiSsm *ssm, FpDevice *dev) transfer->ssm = ssm; transfer->short_is_error = TRUE; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, - capture_reqs_cb, NULL); + fpi_ssm_usb_transfer_cb, NULL); } break; @@ -430,36 +420,13 @@ enum activate_states { ACTIVATE_NUM_STATES, }; -static void -init_reqs_cb (FpiUsbTransfer *transfer, FpDevice *device, - gpointer user_data, GError *error) -{ - if (!error) - fpi_ssm_next_state (transfer->ssm); - else - fpi_ssm_mark_failed (transfer->ssm, error); -} - -static void -init_read_data_cb (FpiUsbTransfer *transfer, FpDevice *device, - gpointer user_data, GError *error) -{ - if (!error) - fpi_ssm_next_state (transfer->ssm); - else - fpi_ssm_mark_failed (transfer->ssm, error); -} - /* TODO: use calibration table, datasheet is rather terse on that * need more info for implementation */ static void calibrate_read_data_cb (FpiUsbTransfer *transfer, FpDevice *device, gpointer user_data, GError *error) { - if (!error) - fpi_ssm_next_state (transfer->ssm); - else - fpi_ssm_mark_failed (transfer->ssm, error); + fpi_ssm_usb_transfer_cb (transfer, device, user_data, error); } static void @@ -476,7 +443,7 @@ activate_run_state (FpiSsm *ssm, FpDevice *dev) transfer->ssm = ssm; transfer->short_is_error = TRUE; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, - init_reqs_cb, NULL); + fpi_ssm_usb_transfer_cb, NULL); } break; @@ -487,7 +454,7 @@ activate_run_state (FpiSsm *ssm, FpDevice *dev) fpi_usb_transfer_fill_bulk (transfer, EP_IN, AES2550_EP_IN_BUF_SIZE); transfer->ssm = ssm; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, - init_read_data_cb, NULL); + fpi_ssm_usb_transfer_cb, NULL); } break; @@ -501,7 +468,7 @@ activate_run_state (FpiSsm *ssm, FpDevice *dev) transfer->ssm = ssm; transfer->short_is_error = TRUE; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, - init_reqs_cb, NULL); + fpi_ssm_usb_transfer_cb, NULL); } break; diff --git a/libfprint/drivers/aesx660.c b/libfprint/drivers/aesx660.c index b4d86033..0781606a 100644 --- a/libfprint/drivers/aesx660.c +++ b/libfprint/drivers/aesx660.c @@ -101,16 +101,6 @@ aesX660_read_response (FpiSsm *ssm, fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, cancel, callback, NULL); } -static void -aesX660_send_cmd_cb (FpiUsbTransfer *transfer, FpDevice *device, - gpointer user_data, GError *error) -{ - if (!error) - fpi_ssm_next_state (transfer->ssm); - else - fpi_ssm_mark_failed (transfer->ssm, error); -} - static void aesX660_read_calibrate_data_cb (FpiUsbTransfer *transfer, FpDevice *device, @@ -238,12 +228,12 @@ finger_det_run_state (FpiSsm *ssm, FpDevice *dev) { case FINGER_DET_SEND_LED_CMD: aesX660_send_cmd (ssm, dev, led_blink_cmd, sizeof (led_blink_cmd), - aesX660_send_cmd_cb); + fpi_ssm_usb_transfer_cb); break; case FINGER_DET_SEND_FD_CMD: aesX660_send_cmd_timeout (ssm, dev, wait_for_finger_cmd, sizeof (wait_for_finger_cmd), - aesX660_send_cmd_cb, 0); + fpi_ssm_usb_transfer_cb, 0); break; case FINGER_DET_READ_FD_DATA: @@ -433,14 +423,14 @@ capture_run_state (FpiSsm *ssm, FpDevice *_dev) { case CAPTURE_SEND_LED_CMD: aesX660_send_cmd (ssm, _dev, led_solid_cmd, sizeof (led_solid_cmd), - aesX660_send_cmd_cb); + fpi_ssm_usb_transfer_cb); break; case CAPTURE_SEND_CAPTURE_CMD: g_byte_array_set_size (priv->stripe_packet, 0); aesX660_send_cmd (ssm, _dev, cls->start_imaging_cmd, cls->start_imaging_cmd_len, - aesX660_send_cmd_cb); + fpi_ssm_usb_transfer_cb); break; case CAPTURE_READ_STRIPE_DATA: @@ -625,13 +615,13 @@ activate_run_state (FpiSsm *ssm, FpDevice *_dev) priv->init_seq_idx = 0; fp_dbg ("Activate: set idle\n"); aesX660_send_cmd (ssm, _dev, set_idle_cmd, sizeof (set_idle_cmd), - aesX660_send_cmd_cb); + fpi_ssm_usb_transfer_cb); break; case ACTIVATE_SEND_READ_ID_CMD: fp_dbg ("Activate: read ID\n"); aesX660_send_cmd (ssm, _dev, read_id_cmd, sizeof (read_id_cmd), - aesX660_send_cmd_cb); + fpi_ssm_usb_transfer_cb); break; case ACTIVATE_READ_ID: @@ -645,7 +635,7 @@ activate_run_state (FpiSsm *ssm, FpDevice *_dev) aesX660_send_cmd (ssm, _dev, priv->init_seq[priv->init_cmd_idx].cmd, priv->init_seq[priv->init_cmd_idx].len, - aesX660_send_cmd_cb); + fpi_ssm_usb_transfer_cb); break; case ACTIVATE_READ_INIT_RESPONSE: @@ -655,7 +645,7 @@ activate_run_state (FpiSsm *ssm, FpDevice *_dev) case ACTIVATE_SEND_CALIBRATE_CMD: aesX660_send_cmd (ssm, _dev, calibrate_cmd, sizeof (calibrate_cmd), - aesX660_send_cmd_cb); + fpi_ssm_usb_transfer_cb); break; case ACTIVATE_READ_CALIBRATE_DATA: diff --git a/libfprint/drivers/upeksonly.c b/libfprint/drivers/upeksonly.c index f477b836..9dd31045 100644 --- a/libfprint/drivers/upeksonly.c +++ b/libfprint/drivers/upeksonly.c @@ -656,17 +656,6 @@ sm_write_regs (FpiSsm *ssm, write_regs_iterate (wrdata); } -static void -sm_write_reg_cb (FpiUsbTransfer *transfer, FpDevice *device, - gpointer user_data, GError *error) -{ - if (error) - fpi_ssm_mark_failed (transfer->ssm, error); - else - fpi_ssm_next_state (transfer->ssm); - -} - static void sm_write_reg (FpiSsm *ssm, FpImageDevice *dev, @@ -686,7 +675,8 @@ sm_write_reg (FpiSsm *ssm, 1); transfer->short_is_error = TRUE; transfer->ssm = ssm; - fpi_usb_transfer_submit (transfer, CTRL_TIMEOUT, NULL, sm_write_reg_cb, NULL); + fpi_usb_transfer_submit (transfer, CTRL_TIMEOUT, NULL, + fpi_ssm_usb_transfer_cb, NULL); transfer->buffer[0] = value; } diff --git a/libfprint/drivers/upektc.c b/libfprint/drivers/upektc.c index 92b1930b..d0c97afb 100644 --- a/libfprint/drivers/upektc.c +++ b/libfprint/drivers/upektc.c @@ -256,16 +256,6 @@ enum capture_states { CAPTURE_NUM_STATES, }; -static void -capture_cmd_cb (FpiUsbTransfer *transfer, FpDevice *device, - gpointer user_data, GError *error) -{ - if (!error) - fpi_ssm_next_state (transfer->ssm); - else - fpi_ssm_mark_failed (transfer->ssm, error); -} - static void capture_read_data_cb (FpiUsbTransfer *transfer, FpDevice *device, gpointer user_data, GError *error) @@ -304,7 +294,7 @@ capture_run_state (FpiSsm *ssm, FpDevice *_dev) transfer->ssm = ssm; transfer->short_is_error = TRUE; fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, - capture_cmd_cb, NULL); + fpi_ssm_usb_transfer_cb, NULL); } break; diff --git a/libfprint/drivers/upektc_img.c b/libfprint/drivers/upektc_img.c index b9724c17..d5aaf72f 100644 --- a/libfprint/drivers/upektc_img.c +++ b/libfprint/drivers/upektc_img.c @@ -501,16 +501,6 @@ enum activate_states { ACTIVATE_NUM_STATES, }; -static void -init_reqs_ctrl_cb (FpiUsbTransfer *transfer, FpDevice *device, - gpointer user_data, GError *error) -{ - if (!error) - fpi_ssm_next_state (transfer->ssm); - else - fpi_ssm_mark_failed (transfer->ssm, error); -} - static void init_reqs_cb (FpiUsbTransfer *transfer, FpDevice *device, gpointer user_data, GError *error) @@ -554,7 +544,7 @@ activate_run_state (FpiSsm *ssm, FpDevice *dev) transfer->buffer[0] = '\0'; transfer->ssm = ssm; fpi_usb_transfer_submit (transfer, CTRL_TIMEOUT, NULL, - init_reqs_ctrl_cb, NULL); + fpi_ssm_usb_transfer_cb, NULL); } break; diff --git a/libfprint/drivers/vcom5s.c b/libfprint/drivers/vcom5s.c index 1a2b7956..e1875c35 100644 --- a/libfprint/drivers/vcom5s.c +++ b/libfprint/drivers/vcom5s.c @@ -76,16 +76,6 @@ enum v5s_cmd { /***** REGISTER I/O *****/ -static void -sm_write_reg_cb (FpiUsbTransfer *transfer, FpDevice *device, - gpointer user_data, GError *error) -{ - if (error) - fpi_ssm_mark_failed (transfer->ssm, error); - else - fpi_ssm_next_state (transfer->ssm); -} - static void sm_write_reg (FpiSsm *ssm, FpDevice *dev, @@ -101,18 +91,8 @@ sm_write_reg (FpiSsm *ssm, G_USB_DEVICE_RECIPIENT_DEVICE, reg, value, 0, 0); transfer->ssm = ssm; - fpi_usb_transfer_submit (transfer, CTRL_TIMEOUT, NULL, sm_write_reg_cb, - NULL); -} - -static void -sm_exec_cmd_cb (FpiUsbTransfer *transfer, FpDevice *device, - gpointer user_data, GError *error) -{ - if (error) - fpi_ssm_mark_failed (transfer->ssm, error); - else - fpi_ssm_next_state (transfer->ssm); + fpi_usb_transfer_submit (transfer, CTRL_TIMEOUT, NULL, + fpi_ssm_usb_transfer_cb, NULL); } static void @@ -130,8 +110,8 @@ sm_exec_cmd (FpiSsm *ssm, G_USB_DEVICE_RECIPIENT_DEVICE, cmd, param, 0, 0); transfer->ssm = ssm; - fpi_usb_transfer_submit (transfer, CTRL_TIMEOUT, NULL, sm_exec_cmd_cb, - NULL); + fpi_usb_transfer_submit (transfer, CTRL_TIMEOUT, NULL, + fpi_ssm_usb_transfer_cb, NULL); } /***** FINGER DETECTION *****/ From 9892eb1c033db68c6be71365d94e20aa554c953d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 27 Nov 2019 20:19:54 +0100 Subject: [PATCH 033/237] fpi-ssm: Make clearer that data is unused in fpi_ssm_usb_transfer_cb --- libfprint/fpi-ssm.c | 4 ++-- libfprint/fpi-ssm.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libfprint/fpi-ssm.c b/libfprint/fpi-ssm.c index 6a02a2c7..5367e327 100644 --- a/libfprint/fpi-ssm.c +++ b/libfprint/fpi-ssm.c @@ -394,7 +394,7 @@ fpi_ssm_next_state_timeout_cb (FpDevice *dev, * fpi_ssm_usb_transfer_cb: * @transfer: a #FpiUsbTransfer * @device: a #FpDevice - * @ssm_data: User data (unused) + * @unused_data: User data (unused) * @error: The #GError or %NULL * * Can be used in as a #FpiUsbTransfer callback handler to automatically @@ -404,7 +404,7 @@ fpi_ssm_next_state_timeout_cb (FpDevice *dev, */ void fpi_ssm_usb_transfer_cb (FpiUsbTransfer *transfer, FpDevice *device, - gpointer ssm_data, GError *error) + gpointer unused_data, GError *error) { g_return_if_fail (transfer->ssm); diff --git a/libfprint/fpi-ssm.h b/libfprint/fpi-ssm.h index 704271d4..57e7d10f 100644 --- a/libfprint/fpi-ssm.h +++ b/libfprint/fpi-ssm.h @@ -91,7 +91,7 @@ void fpi_ssm_next_state_timeout_cb (FpDevice *dev, void *data); void fpi_ssm_usb_transfer_cb (FpiUsbTransfer *transfer, FpDevice *device, - gpointer user_date, + gpointer unused_data, GError *error); void fpi_ssm_usb_transfer_with_weak_pointer_cb (FpiUsbTransfer *transfer, FpDevice *device, From 19a50cfdc37659526801b1dd5436f528b2df5da5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 26 Nov 2019 18:39:09 +0100 Subject: [PATCH 034/237] umockdev-test: Make possible to use a wrapper to run tests Support LIBFPRINT_TEST_WRAPPER env variable to run tests with a wrapper. --- tests/umockdev-test.py | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/tests/umockdev-test.py b/tests/umockdev-test.py index f1387d6d..d91fcb9e 100755 --- a/tests/umockdev-test.py +++ b/tests/umockdev-test.py @@ -50,18 +50,23 @@ def cmp_pngs(png_a, png_b): for y in range(img_a.get_height()): assert(data_a[y * stride + x * 4] == data_b[y * stride + x * 4]) -def capture(): - ioctl = os.path.join(ddir, "capture.ioctl") +def get_umockdev_runner(ioctl_basename): + ioctl = os.path.join(ddir, "{}.ioctl".format(ioctl_basename)) device = os.path.join(ddir, "device") dev = open(ioctl).readline().strip() assert dev.startswith('@DEV ') dev = dev[5:] - subprocess.check_call(['umockdev-run', '-d', device, - '-i', "%s=%s" % (dev, ioctl), - '--', - '%s' % os.path.join(edir, "capture.py"), - '%s' % os.path.join(tmpdir, "capture.png")]) + umockdev = ['umockdev-run', '-d', device, + '-i', "%s=%s" % (dev, ioctl), + '--'] + wrapper = os.getenv('LIBFPRINT_TEST_WRAPPER') + return umockdev + (wrapper.split(' ') if wrapper else []) + [sys.executable] + +def capture(): + subprocess.check_call(get_umockdev_runner("capture") + + ['%s' % os.path.join(edir, "capture.py"), + '%s' % os.path.join(tmpdir, "capture.png")]) assert os.path.isfile(os.path.join(tmpdir, "capture.png")) if os.path.isfile(os.path.join(ddir, "capture.png")): @@ -69,16 +74,8 @@ def capture(): cmp_pngs(os.path.join(tmpdir, "capture.png"), os.path.join(ddir, "capture.png")) def custom(): - ioctl = os.path.join(ddir, "custom.ioctl") - device = os.path.join(ddir, "device") - dev = open(ioctl).readline().strip() - assert dev.startswith('@DEV ') - dev = dev[5:] - - subprocess.check_call(['umockdev-run', '-d', device, - '-i', "%s=%s" % (dev, ioctl), - '--', - '%s' % os.path.join(ddir, "custom.py")]) + subprocess.check_call(get_umockdev_runner("custom") + + ['%s' % os.path.join(ddir, "custom.py")]) try: if os.path.exists(os.path.join(ddir, "capture.ioctl")): From 222c33ec3298dda37919bfa9c28ee42e093bbe4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 26 Nov 2019 19:37:55 +0100 Subject: [PATCH 035/237] virtual-image: Re-run the test using the defined wrapper if any In case a LIBFPRINT_TEST_WRAPPER is defined, execute again the script using the same python processor but using the passed wrapper command. --- tests/virtual-image.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/virtual-image.py b/tests/virtual-image.py index a9fe8f5e..363219af 100755 --- a/tests/virtual-image.py +++ b/tests/virtual-image.py @@ -10,11 +10,20 @@ import sys import unittest import socket import struct +import subprocess import shutil import glob import cairo import tempfile +# Re-run the test with the passed wrapper if set +wrapper = os.getenv('LIBFPRINT_TEST_WRAPPER') +if wrapper: + wrap_cmd = wrapper.split(' ') + [sys.executable, os.path.abspath(__file__)] + \ + sys.argv[1:] + os.unsetenv('LIBFPRINT_TEST_WRAPPER') + sys.exit(subprocess.check_call(wrap_cmd)) + class Connection: def __init__(self, addr): @@ -274,7 +283,6 @@ class VirtualImage(unittest.TestCase): ctx.iteration(True) assert(not self._verify_match) - # avoid writing to stderr unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2)) From 53713c0098944a5e2a8652b505a50e39306fcb17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 26 Nov 2019 18:45:31 +0100 Subject: [PATCH 036/237] tests: Add 'gdb' setup to run tests using gdb When using --setup=gdb the tests will be running using gdb, however this as per meson limitation allows only running a test when using verbose mode. --- tests/meson.build | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/meson.build b/tests/meson.build index 79876929..1645028a 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -44,3 +44,13 @@ if get_option('introspection') ) endif endif + +gdb = find_program('gdb', required: false) +if gdb.found() + add_test_setup('gdb', + timeout_multiplier: 1000, + env: [ + 'LIBFPRINT_TEST_WRAPPER=@0@ --args'.format( + gdb.path()) + ]) +endif From c9216cf96c578ac030027ca1893b6078b82169c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 26 Nov 2019 18:50:18 +0100 Subject: [PATCH 037/237] tests: Add setup mode to run tests using valgrind In such case we need to ignore the python errors, so including a suppression file when using --setup=valgrind. --- tests/meson.build | 16 +++++++++++ tests/valgrind-python.supp | 55 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 tests/valgrind-python.supp diff --git a/tests/meson.build b/tests/meson.build index 1645028a..0fe8096a 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -54,3 +54,19 @@ if gdb.found() gdb.path()) ]) endif + +valgrind = find_program('valgrind', required: false) +if valgrind.found() + glib_share = glib_dep.get_pkgconfig_variable('prefix') / 'share' / glib_dep.name() + glib_suppressions = glib_share + '/valgrind/glib.supp' + python_suppressions = '@0@/@1@'.format(meson.source_root(), + files('valgrind-python.supp')[0]) + add_test_setup('valgrind', + timeout_multiplier: 10, + env: [ + 'G_SLICE=always-malloc', + ('LIBFPRINT_TEST_WRAPPER=@0@ --tool=memcheck --leak-check=full ' + + '--suppressions=@1@ --suppressions=@2@').format( + valgrind.path(), glib_suppressions, python_suppressions) + ]) +endif diff --git a/tests/valgrind-python.supp b/tests/valgrind-python.supp new file mode 100644 index 00000000..395e8015 --- /dev/null +++ b/tests/valgrind-python.supp @@ -0,0 +1,55 @@ +{ + ignore_py_cond + Memcheck:Cond + ... + fun:Py* +} + +{ + ignore__py_cond + Memcheck:Cond + ... + fun:_Py* +} + +{ + ignore_py_value8 + Memcheck:Value8 + ... + fun:Py* +} + +{ + ignore__py_value8 + Memcheck:Value8 + ... + fun:_Py* +} + +{ + ignore_py_addr4 + Memcheck:Addr4 + ... + fun:Py* +} + +{ + ignore__py_addr4 + Memcheck:Addr4 + ... + fun:_Py* +} + +{ + ignore_py_leaks + Memcheck:Leak + ... + fun:Py* +} + +{ + ignore__py_leaks + Memcheck:Leak + ... + fun:_Py* +} From 71625ec1cfe60727c6d4aa681d3f00f78dd75c9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 26 Nov 2019 19:07:26 +0100 Subject: [PATCH 038/237] fp-device: Unref the usb device on finalize Each device adds a ref to the underlying usb device, but it doesn't remove the reference on finalization. So clear the object to fix the leak --- libfprint/fp-device.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libfprint/fp-device.c b/libfprint/fp-device.c index 13f1b5ac..b2190bd9 100644 --- a/libfprint/fp-device.c +++ b/libfprint/fp-device.c @@ -382,6 +382,7 @@ fp_device_finalize (GObject *object) g_clear_pointer (&priv->device_id, g_free); g_clear_pointer (&priv->device_name, g_free); + g_clear_object (&priv->usb_device); G_OBJECT_CLASS (fp_device_parent_class)->finalize (object); } From 5dc3edf07ca7186a533283d94f25c2e545342b6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 26 Nov 2019 19:16:07 +0100 Subject: [PATCH 039/237] fp-context: Run dispose on the usb context to deal with circular refs Ensure that we dispose the USB context before unreffing it, so that it will release any reference it has and destroy the internal libusb context. --- libfprint/fp-context.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libfprint/fp-context.c b/libfprint/fp-context.c index 74dda51f..eed7847e 100644 --- a/libfprint/fp-context.c +++ b/libfprint/fp-context.c @@ -186,6 +186,8 @@ fp_context_finalize (GObject *object) g_cancellable_cancel (priv->cancellable); g_clear_object (&priv->cancellable); g_clear_pointer (&priv->drivers, g_array_unref); + + g_object_run_dispose (G_OBJECT (priv->usb_ctx)); g_clear_object (&priv->usb_ctx); G_OBJECT_CLASS (fp_context_parent_class)->finalize (object); From f6f689f9cd51e8e8ad620c6132180fda6bf7e5ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 26 Nov 2019 20:33:56 +0100 Subject: [PATCH 040/237] fp-print: Unref the prints on finalize --- libfprint/fp-print.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libfprint/fp-print.c b/libfprint/fp-print.c index 39c5c0ad..776fc71d 100644 --- a/libfprint/fp-print.c +++ b/libfprint/fp-print.c @@ -101,6 +101,7 @@ fp_print_finalize (GObject *object) g_clear_pointer (&self->description, g_free); g_clear_pointer (&self->enroll_date, g_date_free); g_clear_pointer (&self->data, g_variant_unref); + g_clear_pointer (&self->prints, g_ptr_array_unref); G_OBJECT_CLASS (fp_print_parent_class)->finalize (object); } From 92db82e3d44049f33850342569e69d0a75afe5b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 27 Nov 2019 19:25:34 +0100 Subject: [PATCH 041/237] fp-print: Assert the prints aren't set when initialized --- libfprint/fp-print.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libfprint/fp-print.c b/libfprint/fp-print.c index 776fc71d..ff7927ae 100644 --- a/libfprint/fp-print.c +++ b/libfprint/fp-print.c @@ -580,7 +580,10 @@ fpi_print_set_type (FpPrint *print, print->type = type; if (print->type == FP_PRINT_NBIS) - print->prints = g_ptr_array_new_with_free_func (g_free); + { + g_assert_null (print->prints); + print->prints = g_ptr_array_new_with_free_func (g_free); + } g_object_notify_by_pspec (G_OBJECT (print), properties[PROP_FPI_TYPE]); } From 8c05f3b78ca492a6e8b696a0e42d01ebebad1668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 26 Nov 2019 20:35:39 +0100 Subject: [PATCH 042/237] fp-print: Unref print data and get static strings when deserializing --- libfprint/fp-print.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libfprint/fp-print.c b/libfprint/fp-print.c index ff7927ae..7777db2b 100644 --- a/libfprint/fp-print.c +++ b/libfprint/fp-print.c @@ -978,6 +978,7 @@ fp_print_deserialize (const guchar *data, g_autoptr(FpPrint) result = NULL; g_autoptr(GVariant) raw_value = NULL; g_autoptr(GVariant) value = NULL; + g_autoptr(GVariant) print_data = NULL; guchar *aligned_data = NULL; GDate *date = NULL; guint8 finger_int8; @@ -989,7 +990,6 @@ fp_print_deserialize (const guchar *data, const gchar *driver; const gchar *device_id; gboolean device_stored; - GVariant *print_data; g_assert (data); g_assert (length > 3); @@ -1020,7 +1020,7 @@ fp_print_deserialize (const guchar *data, value = g_variant_get_normal_form (raw_value); g_variant_get (value, - "(issbymsmsi@a{sv}v)", + "(i&s&sbymsmsi@a{sv}v)", &type, &driver, &device_id, From 5927a205e308ca50d2f68434f66c6e3109ffc102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 26 Nov 2019 20:36:44 +0100 Subject: [PATCH 043/237] virtual-image: Also unref the object when closing a the stream While a stream is closed when completely unreffed, the other way around isn't true, so always unref the object. --- libfprint/drivers/virtual-image.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libfprint/drivers/virtual-image.c b/libfprint/drivers/virtual-image.c index 6fdd3dbb..612863da 100644 --- a/libfprint/drivers/virtual-image.c +++ b/libfprint/drivers/virtual-image.c @@ -81,7 +81,7 @@ recv_image_img_recv_cb (GObject *source_object, self = FPI_DEVICE_VIRTUAL_IMAGE (user_data); g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL); - self->connection = NULL; + g_clear_object (&self->connection); return; } @@ -118,7 +118,7 @@ recv_image_hdr_recv_cb (GObject *source_object, self = FPI_DEVICE_VIRTUAL_IMAGE (user_data); g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL); - self->connection = NULL; + g_clear_object (&self->connection); return; } @@ -127,7 +127,7 @@ recv_image_hdr_recv_cb (GObject *source_object, { g_warning ("Image header suggests an unrealistically large image, disconnecting client."); g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL); - self->connection = NULL; + g_clear_object (&self->connection); } if (self->recv_img_hdr[0] < 0 || self->recv_img_hdr[1] < 0) @@ -148,7 +148,7 @@ recv_image_hdr_recv_cb (GObject *source_object, default: /* disconnect client, it didn't play fair */ g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL); - self->connection = NULL; + g_clear_object (&self->connection); } /* And, listen for more images from the same client. */ @@ -206,6 +206,7 @@ new_connection_cb (GObject *source_object, GAsyncResult *res, gpointer user_data if (dev->connection) { g_io_stream_close (G_IO_STREAM (connection), NULL, NULL); + g_object_unref (connection); return; } From b1d99e7608cc0479b766663134bffdbf6686d524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 26 Nov 2019 20:38:16 +0100 Subject: [PATCH 044/237] fp-device: Use an autopointer and steal the print when passed Make it clearer that we're stealing the print when passing it away, instead of just doing this silently. --- libfprint/fp-image-device.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libfprint/fp-image-device.c b/libfprint/fp-image-device.c index 692727be..e45b6a94 100644 --- a/libfprint/fp-image-device.c +++ b/libfprint/fp-image-device.c @@ -389,8 +389,8 @@ static void fpi_image_device_minutiae_detected (GObject *source_object, GAsyncResult *res, gpointer user_data) { g_autoptr(FpImage) image = FP_IMAGE (source_object); + g_autoptr(FpPrint) print = NULL; GError *error = NULL; - FpPrint *print = NULL; FpDevice *device = FP_DEVICE (user_data); FpImageDevicePrivate *priv; FpDeviceAction action; @@ -443,7 +443,8 @@ fpi_image_device_minutiae_detected (GObject *source_object, GAsyncResult *res, g priv->enroll_stage += 1; } - fpi_device_enroll_progress (device, priv->enroll_stage, print, error); + fpi_device_enroll_progress (device, priv->enroll_stage, + g_steal_pointer (&print), error); if (priv->enroll_stage == IMG_ENROLL_STAGES) { @@ -462,7 +463,7 @@ fpi_image_device_minutiae_detected (GObject *source_object, GAsyncResult *res, g else result = FPI_MATCH_ERROR; - fpi_device_verify_complete (device, result, print, error); + fpi_device_verify_complete (device, result, g_steal_pointer (&print), error); fp_image_device_deactivate (device); } else if (action == FP_DEVICE_ACTION_IDENTIFY) @@ -483,7 +484,7 @@ fpi_image_device_minutiae_detected (GObject *source_object, GAsyncResult *res, g } } - fpi_device_identify_complete (device, result, print, error); + fpi_device_identify_complete (device, result, g_steal_pointer (&print), error); fp_image_device_deactivate (device); } else From c6298ede72f352cc76a2c3e774311b551852d6b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 26 Nov 2019 20:43:33 +0100 Subject: [PATCH 045/237] fp-device: Unref the print once we've notified the progress When we notify the enroll progress with a print, this needs to be unreffed once we're done, but this only was happening in case of error. Since it's not up to the callback function to free it, let's do it at the end of the function. As per this, clarify the docs for FpEnrollProgress marking it as transfer none. --- libfprint/fp-device.c | 2 ++ libfprint/fp-device.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/libfprint/fp-device.c b/libfprint/fp-device.c index b2190bd9..c9d1b893 100644 --- a/libfprint/fp-device.c +++ b/libfprint/fp-device.c @@ -2344,6 +2344,8 @@ fpi_device_enroll_progress (FpDevice *device, } if (error) g_error_free (error); + + g_clear_object (&print); } diff --git a/libfprint/fp-device.h b/libfprint/fp-device.h index 821514d7..47850649 100644 --- a/libfprint/fp-device.h +++ b/libfprint/fp-device.h @@ -113,7 +113,7 @@ GQuark fp_device_error_quark (void); * FpEnrollProgress: * @device: a #FpDevice * @completed_stages: Number of completed stages - * @print: (nullable): The last scaned print + * @print: (nullable) (transfer none): The last scaned print * @user_data: (nullable): User provided data * @error: (nullable) (transfer none): #GError or %NULL * From 712853d1e3f2a3ac78dff5e1441cdd8c6a7035fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 27 Nov 2019 21:39:53 +0100 Subject: [PATCH 046/237] fp-device: Mark user data in FpEnrollProgress as transfer none The data has its own DestroyNotify set, so while no generic DestroyNotify exists for generic data, let's make it clear. --- libfprint/fp-device.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfprint/fp-device.h b/libfprint/fp-device.h index 47850649..a15fc308 100644 --- a/libfprint/fp-device.h +++ b/libfprint/fp-device.h @@ -114,7 +114,7 @@ GQuark fp_device_error_quark (void); * @device: a #FpDevice * @completed_stages: Number of completed stages * @print: (nullable) (transfer none): The last scaned print - * @user_data: (nullable): User provided data + * @user_data: (nullable) (transfer none): User provided data * @error: (nullable) (transfer none): #GError or %NULL * * The passed error is guaranteed to be of type %FP_DEVICE_RETRY if set. From db905a2048ac8784ddb4ad2d22a79e5cdfa0b818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 26 Nov 2019 20:44:15 +0100 Subject: [PATCH 047/237] fp-device: Use g_clear_error instead of check + free --- libfprint/fp-device.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libfprint/fp-device.c b/libfprint/fp-device.c index c9d1b893..0a1f8deb 100644 --- a/libfprint/fp-device.c +++ b/libfprint/fp-device.c @@ -2342,9 +2342,8 @@ fpi_device_enroll_progress (FpDevice *device, data->enroll_progress_data, error); } - if (error) - g_error_free (error); + g_clear_error (&error); g_clear_object (&print); } From 545af2353601b24dd52e8d0860e5607cb5b2a7e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 26 Nov 2019 20:59:09 +0100 Subject: [PATCH 048/237] tests: Use a loop for generating drivers tests and use suites So we can just run drivers tests with --suite=drivers --- tests/meson.build | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/tests/meson.build b/tests/meson.build index 0fe8096a..d6196bee 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -24,25 +24,21 @@ if get_option('introspection') ) endif - if 'vfs5011' in drivers - test('vfs5011', - find_program('umockdev-test.py'), - args: join_paths(meson.current_source_dir(), 'vfs5011'), - env: envs, - timeout: 10, - depends: libfprint_typelib, - ) - endif + drivers_tests = [ + 'vfs5011', + 'synaptics', + ] - if 'synaptics' in drivers - test('synaptics', + foreach driver_test: drivers_tests + test(driver_test, find_program('umockdev-test.py'), - args: join_paths(meson.current_source_dir(), 'synaptics'), + args: join_paths(meson.current_source_dir(), driver_test), env: envs, + suite: ['drivers'], timeout: 10, depends: libfprint_typelib, ) - endif + endforeach endif gdb = find_program('gdb', required: false) From 4447a0d183d2cc2d66f1b8af3351c75ead85f945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 26 Nov 2019 19:17:26 +0100 Subject: [PATCH 049/237] ci: Add a test case where we run tests with valgrind --- .gitlab-ci.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 10c2cf17..122a02ed 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -49,6 +49,14 @@ test: - ninja -C _build - meson test -C _build --verbose --no-stdsplit +test_valgrind: + stage: test + script: + - dnf update -y --nogpgcheck && dnf install -y --nogpgcheck $DEPENDENCIES valgrind + - meson -Ddrivers=all . _build + - ninja -C _build + - meson test -C _build --verbose --no-stdsplit --setup=valgrind + test_indent: stage: check-source script: From b9ff75c4e9b0ffa3cbb21d1cb83166571fb69f61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 28 Nov 2019 21:30:17 +0100 Subject: [PATCH 050/237] fp-print: Set the aligned_data as the data used by the cleanup function g_variant_new_from_data() allows to destroy some other user_data passed as parameter that might be different from the aligned_data itself. But since in this case they match, pass it to be set as g_free parameter or it won't be free'd. --- libfprint/fp-print.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfprint/fp-print.c b/libfprint/fp-print.c index 7777db2b..ddf87479 100644 --- a/libfprint/fp-print.c +++ b/libfprint/fp-print.c @@ -1009,7 +1009,7 @@ fp_print_deserialize (const guchar *data, memcpy (aligned_data, data + 3, length - 3); raw_value = g_variant_new_from_data (FP_PRINT_VARIANT_TYPE, aligned_data, length - 3, - FALSE, g_free, NULL); + FALSE, g_free, aligned_data); if (!raw_value) goto invalid_format; From a7541b1f76fdfa763085991a8789ab2aeda68226 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 28 Nov 2019 11:37:33 +0100 Subject: [PATCH 051/237] tests: Fix endianness issue in test suite The test suite needs to compare greyscale images and was picking an undefined byte in the pixel data on big-endian. Select a byte that works on any endian instead. See: #200 --- tests/umockdev-test.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/umockdev-test.py b/tests/umockdev-test.py index d91fcb9e..1b556e17 100755 --- a/tests/umockdev-test.py +++ b/tests/umockdev-test.py @@ -48,7 +48,10 @@ def cmp_pngs(png_a, png_b): for x in range(img_a.get_width()): for y in range(img_a.get_height()): - assert(data_a[y * stride + x * 4] == data_b[y * stride + x * 4]) + # RGB24 format is endian dependent, using +1 means we test either + # the G or B component, which works on any endian for the greyscale + # test. + assert(data_a[y * stride + x * 4 + 1] == data_b[y * stride + x * 4 + 1]) def get_umockdev_runner(ioctl_basename): ioctl = os.path.join(ddir, "{}.ioctl".format(ioctl_basename)) From 8cc0fd321f51c1f98d9b7aabf6896bed3286b770 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 28 Nov 2019 15:50:20 +0100 Subject: [PATCH 052/237] assembling: Use fixed point for image assembly Using floating point causes architecture dependent results due to accuracy/rounding differences. It is not hard to switch to fixed point, and while this does cause quite different rounding errors, the difference is small. Fixes: #200 --- libfprint/fpi-assembling.c | 32 +++++++++++++++++++++----------- tests/vfs5011/capture.png | Bin 65243 -> 65213 bytes 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/libfprint/fpi-assembling.c b/libfprint/fpi-assembling.c index 75291c09..fef08f04 100644 --- a/libfprint/fpi-assembling.c +++ b/libfprint/fpi-assembling.c @@ -385,8 +385,10 @@ median_filter (int *data, int size, int filtersize) static void interpolate_lines (struct fpi_line_asmbl_ctx *ctx, - GSList *line1, float y1, GSList *line2, - float y2, unsigned char *output, float yi, int size) + GSList *line1, gint32 y1_f, + GSList *line2, gint32 y2_f, + unsigned char *output, gint32 yi_f, + int size) { int i; unsigned char p1, p2; @@ -396,10 +398,12 @@ interpolate_lines (struct fpi_line_asmbl_ctx *ctx, for (i = 0; i < size; i++) { + gint unscaled; p1 = ctx->get_pixel (ctx, line1, i); p2 = ctx->get_pixel (ctx, line2, i); - output[i] = (float) p1 - + (yi - y1) / (y2 - y1) * (p2 - p1); + + unscaled = (yi_f - y1_f) * p2 + (y2_f - yi_f) * p1; + output[i] = (unscaled) / (y2_f - y1_f); } } @@ -424,7 +428,13 @@ fpi_assemble_lines (struct fpi_line_asmbl_ctx *ctx, /* Number of output lines per distance between two scanners */ int i; GSList *row1, *row2; - float y = 0.0; + /* The y coordinate is tracked as a 16.16 fixed point number. All + * variables postfixed with _f follow this format here and in + * interpolate_lines. + * We could also use floating point here, but using fixed point means + * we get consistent results across architectures. + */ + gint32 y_f = 0; int line_ind = 0; int *offsets = g_new0 (int, num_lines / 2); unsigned char *output = g_malloc0 (ctx->line_width * ctx->max_height); @@ -476,21 +486,21 @@ fpi_assemble_lines (struct fpi_line_asmbl_ctx *ctx, int offset = offsets[i / 2]; if (offset > 0) { - float ynext = y + (float) ctx->resolution / offset; - while (line_ind < ynext) + gint32 ynext_f = y_f + (ctx->resolution << 16) / offset; + while ((line_ind << 16) < ynext_f) { if (line_ind > ctx->max_height - 1) goto out; interpolate_lines (ctx, - row1, y, + row1, y_f, g_slist_next (row1), - ynext, + ynext_f, output + line_ind * ctx->line_width, - line_ind, + line_ind << 16, ctx->line_width); line_ind++; } - y = ynext; + y_f = ynext_f; } } out: diff --git a/tests/vfs5011/capture.png b/tests/vfs5011/capture.png index c33f01a7c4c7c83269a3b54935adda862c120cd3..969f9d922cb331b0ee035c621aa2a304ef03b495 100644 GIT binary patch literal 65213 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 literal 65243 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

+^#@0) znQoV*PufPkU~DukJByZ+Tn~g=`8+7^!Ju!YlO)s$P%psjEDU@ti?y;0OV-{+ERH7dH@pu&prtk0Ni% zQYeZ3m0{GoOre&eCtSeyISs=s#S|1Q+AY|X;O_nR)5yOm=pc=`Ya^AmHgotx9>mJd zBo7R#Gsgw{s!|a1YV3A6yHKGkMawpr(!(aYK}-BRL15L}_!6M>E^PPp1;bHtVtz7W z_eoQ!)g-$p&0Cz#y+~JoEQhw19K|e9{hs{Q5L4EItc_>WO-@Nh36mk z9m;IMOkuoz~ zShO)qDS1CRD3q}GLYA%Dfi<0H$Fs%4LrJsGx7XKBCMZ6rVvY^k9OacW?}+!9C35mg zY(UxpoOJFzhU6SHdtTQfJ{DxePf{5@`{X8%h8?lJCo-pVqG>c-iG&pMrN`0mhnBFa z4d0xc^7dT$Zohcb`_(6Da4(+T0@V!%GG`AIYy+C6!#4t>thK8B4O5B~VX{W$#tT)+ z1&?!%yoW2SVgZHq@R06gpxG7f0TZATwZr4#L>gFVaq+6Dk_BKDdVYRBJNxzN;L_7j z3?`glaH81s_Fd4C*-jy7oIxpp#|S}@ce=BaSJ#7NU}Qwz)wu8gE*=Yj1B@7vABpx@dj? z`O80>bCRbq!jM#j`?dm!VzI8!p88^Z z-?lDJTIF|si}!GItEz@!I}f=-V3OY)1krVnM$w|j_bMFGfCRQPJQsx$JV7xu4m1Lg zoi&P!g*G~xGXK3;wv366PDuJie;;Z9sBp@@gzf;Rd9hTD+;`w?wDhF03A9l7*ON)m*G7HoRs22WpTan9$D4{9+>}^BT464)YvTm7n|ivi$8x zTY5L-fY!Y39uWl7Kd{-4O-!trt#kTmZSTL-(s)QsL)>E4j%n3)M2)C0Y~VQeAOkyL z6UwQo_1PxFy%ysTCsP%m)etZdfJ21iaP=@;=%s=f{e~mN#l*IaDfAGNIQLlnZF`h~ zx~z_S*nYe5RZ?2o!o=i-0X?~L*lMZ)wd)ciPm3U9!>3s5Mg2XT?W>e0U*W7!Dk*Af}^``nkuZMifE1$V*8Dn zvPv_bV|#8x2Bjb=+)3%kV~9(=J*h7TuV_oBX32kOJ*0ok@o7G`OMCCafj=J>x}mxn zNykE%s~{zC0q0ZxmQrlY|8CCyBcOOFqHxlf>8lS&sTo>Hr8FBx*5dAtu0>~7PM1oa z{z^_timkzNz7+Z=LHPEiJKO^2_u0SVh^=1a9|{seblIzet`JQFK1LiB`rwN%>+AV* z=&Q&kdXlpb`$N2xw*tfj1qFqKNM(($3PQ-e+)L+Xv-&WH9<&~tjT@`{#lUKf?v#vr z)9*I6zwg9O!M`)#kB#eLpwwpC+tX75Zftz3V^ zNE*Rktf=ttbk9}xl-?je8g{KAhU zXYQo3ej#vZHdA~0`8n!jxKAxEF2d0^R|OX>90t6I%6<=K=gt_u1Ct98Lvs?efvj8>O|~eu$;b$0=2SYGi9@N$cW+24 z%>N4?`h$c|P>)032Xi2hn1uA|gYw83AwQ=@`1%u79h<8c8w7Z4cXr~?0FJ8DObEiLuMKPoS($8j2iusK$VQkZm z!79oyLs;y`VboYS{Nb^xkDE*@m6M;J7+_Q0(#5zxgY8f$`!te*RM=+Is#<^I-+o<| za~MCH5~ETAqIYyOG!|CYFmF4i$i3+%Ng}Je&^b~gNjIc#7jI5~8JgPt0hHUvN@7|A zgm3_o^;=;td0_~ck-X6uLXAMkC<`&g=C<_lfFF;Qt3=$xut!HnAGYzbv5`aFKWzQ< zAN{mjWL{iodf9|?a>8s>6F?u{7;;BHI-x)uHcehNU2+BeI8kuMx#lK(+xdHs1sP z*tznAf&y9--zQJ>@a0EwIwG`xB41ND)OlB?AiuHAl=9mn4-QzAi?v^&G=KX1c^w`K zJ#v-=AFR)&CMJ9>KcRH(SN!%<|872o>RIiOUjeGOR8*2GHm$lKh(R zMdH=_AdX(UbuV)vs=u&`%=|Sp{c*2lAfUkY%~0O!GOK

G)3NshEwqCS%?;mCA+j ze;&a&qdP_Y#;**?^ZDeIfHm-C+a*|5e;Xq|Gs^XwH%|Rbzsimyj7Xi{y_Jim(`)fB zy%ybb91YVpmTTtc+qcsD>@1jWDvJsWad7N*!5gNuju@z<@_|Me3`>^n{!729DNvn_ zjj8lnSi<>lU%Yl6P$YL>b)kTxVc|f<0xxWBT*@XS#PV+_ulG!Ybi%LxI0xv$QUO1bI-wy zP!;w2MeNhpKetda1dCS%(|u+X+bp2I`!FC;jgwLjy0_9T7Q7TWP(=Q{H$ooigBZ}m-kw;3OjoS)bJ_x;q=zU|>3 zD8F1kXpGe7q<0wqJ^ogFT&tkLwaX0cv0C{&j9j3Gy}T3-*6F|-OG8?J;oU`D@gXm& zKC^3P>ja^qd|PrE>h?$Yt3}B&Uw~Q;OOJj2DWiiIw0!mDgWjkQ)oIwMG6f0P3K);dIeNIFzj}SQiMg23}6Znw;(Q zKz=J8L9NOtKTC3E*mDK=(sw~OXYo95W69{vQ5@CejM!9NA31o5^9{oFK<+Is2#zt7 zix;ZGA|i?MATQDzU~r+IPWxm@m#~8YP;iOrk_Pq;Adn#7AuxV(6-|YrvD~`u4usK1 z-moW^ncK<%h#elHaRi&{O9kE-ol~>UA`7+7^amrKt`^%uxJL%Oi`oyK-XkMx)Hlh| z-YXrnlXi&XeBFd-M5M^$nTKazWAppuMBCe@+xo|IS66jAL%LfQAStX*)|Zxqq^19W zuZVfX->f-h8GM3(O4i`eqa`v%HT$}O;E6(%*@W>T##>fnfS;^musoiVcXwu|R?(f3=LAC0~-=G=3Z z_v2(g5s>U5nSS5Y07ab7+r6e zoI6}BJmTR3@OJWGZ1a%H8DJBD)%!hpQkvwm^NN1q=pasG@G`1aYA zmOepNs{~`|gQrl0qWY4IfC@J`_^SSuQR>@qy7av6GfUD6h<9&u6-G0dL>$ye(Ge$Cqg(ZTuf?B|{)#s}ayV4QpY*aj^`|7y9jJ45M7Bv1>c@?Z=ydsLj3mfngADn%q8PmE&JQd22`7I4XM6?J3ea4D!;2HlNerMb z9bN!a1WWP=HZ)HHVq*IG?MI+e?$8}>0WBI6o&GgAMMF!=QKcUV0vZ}>YK2a0Ub#bX zlAJ!{`*3`qD&e8A7$yJM&ktIYMQ68e-xPrcTgQp@Q$bV|Dot#+9cIEqL{63b@3rh@ zH{h@gwQ4GD)aPhoBSc$0xGzqx!i^h5pYq}Tm*Zxgpr`dHAz6fOWNC3er0h%o7etEC01=Inw|EuBuPSI7>@&ooM^U} z%N<)7w2|qi9l-Bm3(A_!A4yqi<>2nO!kR{LfjbuLI>PaKnQV!Nl>&mdvu?uQaUuP6 z8J&}5^u#y~Y3i^qyj)zdsBqO(Tk#_MnY(cAkB<rk!cAYHZcS-_t!@aRuU z0;A0Xjuclo*Z!5dcBgagKy$>eMb-Go@sDLwV}{x)bdO_)NMy5=xT_RVbvtm?J@97I zm{B5vj<@$aU7y~=ih|ovQ^Ln3MDyHjL;fC( z(I)s@p}aY*nUsE2%dlMZ6^u3TXCle4nYt8(ZF>`k+UcjY%ZFeI$08k5X+9r(&H3&t zxZgji<@w?DY{BU~vT4JOunA`Y;B_HCKxGZuBv^JqhCk$Vtstks5LMsWoOho@gE*C4 z9lN?fGJuF!Q=AV)K19uTfP39>cnx?%mEM&2p2Q6Hmy?$%!;_#Zt#~%CArz~8G(JYJ zj}&+!y)dn3YehtmtZOyZ(9PM@#(T$<~fJUp;fO;kTP=cC)jy3l4tU z&@l5}WsdgmJ*suVAmeho7lo@UE5)qr<>ji>XcFUUS_y5&ONw@3$3L*f2uB9`>#^c2 z{$C3qG|5k|fZfIVpX^V5jXhE<)M#-9tuwhPoaV2B5^|)NVH~K55`Isg4ih4FL0R%Y z50b~byR167SQ=~U+aAtI;4%)(Jsrx7tCqpA^q*s$rM(l*D8*>}Gc!&y((YlXcPn?` zqAswfppWdrsZNqjSlFZ4yfTszszP#l3m)#16~{tce03csfcoX8(Jo0z|R%$ zZCU;~dkMEKGN-$MiUheiIZ#rqDCl>A5APjHpGA;lM;Z$n zeEtPK1>OS9p_N0ekaba>7$$!IsC>#g`y8vfV6TWWdj3<)c(cZLEp&`EtP5i?o{uS_ zByy>$hFrlI9wy%=M5X-3>y0biIzG`Ru~kW)G8D6#5q!mD4V8vah#+7d;li({UNyN= z{E)tW!UY);Fk5TUwr_zs4u~Ugqv2OOx}O;ijQ&OQcxgPHMS9$fwFGvBuMdBp48lzZT4D^GAICoz6M!fN1uIZj zb{R}f0j4*V2K8zcBJy@`j8}T4p4Zv6k?$Su6>&IIh52|tf6k@$jI87V4ZixlWaEa# zne&Q7eV7$?|AM@-DSZ74i~3~q(fI|pJRQ9f?CGaXhcVUvD*cm6J#g|~zHN54G zJ)*fcuPbpeZoYb3G`hUG`ER2dQ40=eSy{vY5V_=uw~(vz3Y9T@qZ7GPdQX4wVHFqS ze~ZYsVf@Yw+TdaGU}iaT3W@+&9#M_s>~#X2oG|{JsrQTBID?WOs-s{m;VP$5bD_Ua zs5jUwr7rR4k(Sn~Ga9tdMU&?%r?^I*Uw(!G0JGvZk;dy2TE>hhIoS@K={q<=+1J9f z51#t@mD1nQR-y}WIa??7NhKk;kZZQ@hmX#y6`AT`?@y*>pPcSMIq0=*kb}CT=gxds z@*MzF+dpq_S1Y^;&;!tSvFZs5<$31%zMrsU?!aX-s@8P`^U%K)8yHvIN~fipqW|+? zZ?n5Rlr##$bwrjkZEwGMa~~i$l)(vCCkrFBYf5r*-W)PYv}sI!M6M;>YC!>k(j176 zo38kvh~TaJU$3yWCtROEl3P8R;Ia1fDaZ>LBiUnK3fdaNb2%rZQdfO85`WI- zXx}p zp0W96jbd3ROaM1phZUi|`SyAIj-u@=Im4E&w;&II=!$kBJCl)|6vlA!U|-*nmlKcs zE1EpgF!GE%<{}tyOLvMVguhf+goiz@%UTbmZGKFyytZBxX;6!Xv-nEr`qP2W)a9~^ z(0798@t`u_KiMIWD(9R$dw2KUDiz&L-sYNjD}&J(^&vbXFm?XwQRHj5 z&3xOK%&04(m z4o{zEPCc`-La3}-L0=P%_o)%q(4IVN7*BgHq5#PRm_XcjXUF31GqD@_pZXUwM$0vy_{~;%Ne(~-C8m*cM zb!@GPS?<`^$;l}&aP?y>e)T59#0?pwxj--pAk2yskOVr|RNrk(i8;W7xQ{dG^fL8! zg!Oc>buF`T9x<1zCs8*Temo@P{?F1rU?$LndpYd*KlO74$aHCuWQiIj50~nlKxmOc zWnfryUyyv3eQ2@#V5xBTRm1+fq7S@O^Qx`}K)no7F5-*+IzObRec!CH>Fn(MqJe3q zP`5g=LD?yr{;(`xubmb2o;N|w>|1m*slT+7OCAf&FeU^1Og=kfTOs}H=${EJoZ>U6 z?r?$t=Vc?wLqitob-ssW8GrKIyBH#S-ZnNC{W5`7b^U?7RDIPVD+`N8-^dG2mS3hl z%5bNH4jIP{Mx=kRMZL4&?hbY13Yf>>WyiZE*dCL%f$zjjUy3nA@;S%IDgAF?zi3Nf zue;p1b7mmOTmXO@8Pz^2`COUDoWdeH{AO4f>2HTVNA-lfDE(Fn{;=vMa z4bf7VhvtxVg3zoLY-DRqRlChADk|LY4t>N~+_%4JRoebXE=JmGn`Q4}d5COUX9z3b z*w`4%%j0w=-*oi0NWu(`(Og9Z1$UoqQqd)i7SXdLOxa@v5IQ_l2cc2;mmFuGczXGnfwphQ3 zzrz=vi!)%rO!5!UlmC|BgdHZ&P-Yg&e6K;mc#Hgz+JsDzm5qG_vULeu{kWns{307uaWjlc6ZoGYd*c4f&keOi4po-zM zp!F#!F?IX?n1TH1!78YViXZ$b;aPGLG&_~K4lTeeRrw7UaETO$QnH=5_q^rIzkE9w zH}x>W=yA#Pk2-3fpB1&&h8#-h)T0Nto-RWI`f z^ur&Yt`Gi5VYv=MZ>wXsRI4bI(t?tRA_lo^*+uI#<&_d}(zVZHAi^Vp@u=T+! zUE<&+8IR8%(c+wWLLH7zGZ&${3EAb&FXW2pgwa-fA=qrc>#9n5MEM8A*#kcKZ;%Q*8IXqe`L(kyz{W(a#f-$mMpCc_o z)RMO{jO(*Wnkx~PHHtpHDn9Xp5+k$ZBJ>?u)r8f56gQ9mfzu@UYZ-}}f>N^8%}w;m zbxF63gS2aACjF6h>Q3M|792`!61wGw0}2+bhc9XJqzx$^8F7;J?yyyL*3R8tBf9kV zrI`?6qnDei80Nf2==fj^*2etqe5}{t-U^IfED1Ec-Hfu^WwDsIhUu-YzTW$Eycvvz zA|p}%P6^uRz7nDTk#MCrsNC@}VDzCX)2`lhcipfbMl+%tr|}hB8-;y49U);HN!ZyLml? z`!nQXOd;Gq1k_82#3(bIZPA}Y>K`m?$!lt<$}st1OGdNO@H`7LFq3X{@D~d(`G^!@ z34slnfxBEo2%hKPmTExJY}0&hv_EK{rr*8p>w-*-t(4vG9E-Oh!Bj0N4NxVFDh(DVzl)prV4+0xL?cW-asgzlXk^x1a% zQz3X{yC_7?_LuVyxzQJmI=ITlClX%4gl9peHZ-$_Kc|AGr%Qw%^U9ZjWFdCE`lc?T83@i=uE$^Sn;UbT-cAB8e=24rnBPu zJwR3CU#lwZ^l=RkLB&zJ4rnWyUC_2%h7`!kYTVw}3ae7?0 zaBpj?k0)}zcMB3Rx3{-{cOSoP2^1{wFZhZg9-PmBFyYa2Ol^@@-uFESh@Y`fn zcTci+*HOm-@BG!s9dg1FEmH??-AnMcAHKdZ7?9Xl!Z3o=lZ$Ulkb}sQkHKPF9r1MT zrg5!d?|HW546xKH@u4rr$5r_!{KGHjREgE>Fw{S06bw$3E*yZV7tH8uzhlKmcZcAJ zW;+E1t97@UY5%rI>cKy>=8&C@L8Q60jX-Wb5+P1X$s6r#OdF5%(|6%%ogS;=hc*?d zpqi`S0E3?Ee)}PkyVQ zDsjnJJG%u$i99v7l+_q9FEe1TH0Jb1W*uVg=KDS&Biu7EjmB|tGEb+wRm(XEv=eU1 zHu}He^yX{fJO5KTukl6FcW6uK-G%tvA?s5&UpRyZqiSebKzi4yKt(Yd&(jhPXM;!nwh)#U9gyr)Su$7(*BZ#U zz~3vLiiCee26#RR|1?3$6N92Xb`G$g{}o)F`Z|gdv;UPh@DK{JGpoptzkQqkILZd` zz$PNHUXHGj=;rE&!_2J6xFz;kXp68TQZzYMcM3Ld7p^kqC5H|9H*jasnbjGt&vBmS z{E2fyY)=VB+bCAT()ZvT=DG^15gKyXB+_27fP0xI!iUOuv#`XR-eYP8icVmQMo%wB~#=6L)^KCCd}h|_{ULWCs_;3=6dKCP z3!5j~qI1J$luggrFugit=VZE3K9T6JWx&P?OX<7YZ#9r6_s=iCx2N>jG<^@k4cMAD z3yI>Sn6{0 z*)|9!w@F?`k8#-%sagDS{L<<jfK@T(XIq{qu1QQa%?ObBYfTIVOycVEcCX81;~E znm8AU)Xq_K&xc9hTYXD<2U7znmw80s(d@YvXP^%(YwR{Nk2U3I4 z=O4g@!|goz)}PG?L$8c5X1eWIsZf5Km9;tUJ`GK;x_ZB`uy7+^Kmir;jb4Q66$Aiw zye3xI6V{fzz$mmq}FSLSr*UTpMb=jt3(loAlFCJ}-2wU0QB zd>VWtk_6k2O!dV-7}k6d=u-MF=pUO%d~U``_n;`$e$AWKF^5y60iyC#7 znang|eHFT9J1YB9HC8c=SnJ@vGO;}svgFj)Ap9)trOp`3*`Qu~ug}30zB-lG>U4}H z#`eDd!v`{o;)KxmJ71Zdo-s}#m8s8k?PnqoLRC6GvIeDXLZo({y$NqeLaUANO1#ea zO#xZcGiuO8oU7?Xd`pipn>H528pi!u&|`Irt0T^)S|@I6{SDn~Twedvn2DZv!1dn+q*+Pc}+Q@F#PF2H8;rKy{+ z$NMq+t%9i^z>gdeF2-{t_nDP)42-;Ttoe3^8Cn_|+|1-jDco&yD~Q4Rlqm zxN%&r^*GKHu*ZJafuO4<8^p5L;LLeb1u94@dK|5rS1AH zO06z!Ty>SCVwAc-s~jx-49T43DEy3eu_VEBv`QOFPvmqO?hd?)NHM=t+lf^ebG{yx z6J)%X{mm|aNI{bkd-Sjl!wF4_GI0bLOSp?cUGg8>N6b2vXM91?wrA==i5rMJWBVIkrW_u||O#i@p9et6;H z;YIu2N2ur)o}eQJbW$W&bD|SGoe%zH!-l?F7)V1@U`tsKa`<~FJ~JY8z8WS zGGf)M5p5~idyycu_#%RqcN#t(Ri(T8-ihsn)erkN)66nk{_J|4zEE!ix`FDB8uilE zxi23&!FeM5`QCI1=Rr?KLP4kbHbnS6AdCOV>_1IZ57DG8yB?cwmmYpqPO&$!;~_v( z^#ig|*~e(LQczl2T0mg%>BE7?^wZDv-ge1zm|xohSmNpF31TYas5w&740!@qLh20A z-EHX2a_3-7(yzp$n3njB+wmx1A-oLx1en{y|4zi-ym5ikW{M?(^Flz{q;jDN6{#UH zBGN%eA2j2I`1J81DxF*YiaN*q!O<^!L|OE}l0Qh{AXt&1Mv6TD3F8;t2DFO&)#Yb*i`3bPt(~oDRMvkWd~c~!Ne23} z5iuv-kquHq&d|+$vg`v%(H`kz&~`MPZfzol`9E1|Jv)$A$1?uR$6yv=>k}#5G*MK~ zZ{6U`#ZDH*gk7~(*88jU>7rP8Zc99O3Pz^y9tzELyg zRFl$ntX=thO}aBrh$NxPV4*GFLgmKm{DF# zO3I$Mv1HUk<$?C)7c+TLRO)g%mXin)wZ^ZAI3 z_wSUQ{L8F-NK}r!)I0UaqkpGB`8#%2UJ}uLyL+k6Fs0@d`t#>$Vi@rGNYYR}w_)kK z)e?N5*#CQfKURw{ufahMky0&VQn!a9+M2~dd$GN>l@47^Nk%<1v1%5%s+ujYOrPF&FW>+|we_n2 zsse=g!~frei59=Obd-%FzF?5qy5 zuLmfpr%_{N6_<6Abi28gE*>5qSLXIB9`EmmS~8H_KSQb{o7j2@fgi&=r;yQ^(=Ws& zDr|4&J<0u3Y(h;281dUm#3PijTaJnNWXSmNraH0~comRS~ z&$d#2BWp(2gt`LycZE1qXS=-n={$BpNI{g**24keti&gx&pYvA2E)*8+w#p!&VmtG ztQ4I&i0e|BKKF)

k^D=01@YZG;9(CUxV#)YcZg>c_3<@uAH4Eb^F1_^dZJt)$5e zY!BTUKz)oeb;Y}_dWMQHT2p(_73f4o;t*|^YNLg|L6`kyco_AQrdX2X%bBn8vjiVV zH7##L=gUdew{~Bfwnb=QUBwGj)}k+PQphCyeO1|jMSn>8RX=ELh9SMI9vJ{LQYB?>=>K_6kONezriVk zz+_zl>AogswuFpqBv?gXkQC`pru$#|S_tInvXRV+Sl`r6{Z)Cv%uK<{m4Xmla56WG z90Bq2kQPCnJ!|~m{0>6d?biQm0d~#g?EZy8rB82Nfcow6V=C6rWVN`72bsRbQ}VkP zmqwA?5M_fJ_@vuBobkKptJ2*i^GA)0G~szSkn8x2nmt`*1$rZ^sKy;VJR0EAmeN?q zl}C+k;XOrcve(vVrV0!10;lFlFszU`3|2;1&bo=qaAx%B2}7g>%9D7h5HAsssl0mA)_D6i6a7BTDGET&TU} z*@L4UFnBJ^T`w>r0g_c(y2ye668hgz6LmZk^C>c^QW*=@489jPRH-_0_ zuq&}hnVphYF@IsJ%vF#!>gb2?E(axRB5_142=S!BRb#>}TDlJhFPKV%MW%RO&TBc5 z7>{NohAeV5h?C5gk}h{TZ7elmYXp(a>Gq5a{`DGD5Iw)>w_*;6bIT;&%Po&EV|c3^T;4Jlm`7SpGCg4(kUB{qSvsd$FvU#fheVG?{jt_B z#Lw3kb{Wh&tS@NpXdVbQ!p}OHet1fVErcf_{uOdn9Q%f7Lp(&ZorWaMJW`jmB#hI#7M2h+{hlQp;Zw+0XgVNw4Fkc}eRcAk$ zS~sm$hEC>Qmx0GE%jH(#saA6x;(hMGkk3Z4IbfKj3~Z0NlpZzI!h~c9nq=)+@1nGA z^|30$nxcpyqZEA(hk1{310l974DmHm7hEwmCsj3sJ!Cygm82K^wm-K}lT4k>klqtF z3OffW#`AjX&0USz@kb6s7Ys1qwSTiel%y;k#W5)xPX{r> z#oL>o8V{0Ax2~$q2+f8b4D?<8x&s@S@^QwH>@A`iyDl&2#1Ty7n8ntW7Fin6qX#Gs6*i-zmMuWxo1@iVqU zxzlp=EXrT$zMkK+Rbg8};PtZ2CeF*ru{jGRhN8;xMFJky3kc(DqC(}<^D zluXt6z)O&pVPJ(@8hf- z{lE{p_z1VWX@`|kO?#XsC0ZOd;~}c5x7LVA%JRz&8H6`{--uh05Et?T@pBDoiiK+w zQXZsz7^lQNQbvT@Cw~X$N?V*#!j^zg}su%v~YZ z(w%PK!z41PGX%A(${j{kBkKv%2=V4_qT;x*iXt@Pdv)OV*0&!JrENfrudQAm*rg)nDyL_ zx|oW0gqpG}*rURx9RxZB(V%?AF`sEgUUPDx_~}3ZSp1ue-JU(mU9EWiQkB5|%hRx= zetd*z#uxC3F_i$FXSJEO zH|rJAr%9%UwXUJ7uNJ1(9H+ZyMu+f|7Tk567J#$K^`=|&`uy2B?Q~Nsr5At%T|Pe9 zIN75vNTF6>04y#;3MwOYr^|*%tqEa+NvBF@4%N`%&OHyB190Kc28a%JrKkpEsv2WX z=oE=t6}T$+hERq>JaDB4=NXhpdTU?Pr>ZFUdWP#ft~1nMm@OYxv}H{z2DciSPxghk z%>Zxo+R6(2kKqq@PW`QB`WHs}ye)2+mf0T2q6VUcuMe>jayex=5g*RSdATS`+}gAT zD^f&aV=1PY(<8jMBl~sCMoBe#`Bp!WHh zX!T>KvGlhvz#DV2pk~gS4)>8{@CklTA7`qy_|sTk4t*nLhWfgkO?U~_0W32}VSOJz zW-qR&EpNhRtr0VjfIwtTNV%q6BI+06PCGG5?PS5fFetd0ja3wMyE43b?trRmsD`1% zrvEgEE~cifZRvG~7)RaE#9>k1m#O)6Nq9YcsLb+stWkwV#Xx)c+x;M9r0-@QpmB4{6E-f^NHrdhDHGcQz{G22^!SvNzg5PCXS!CpNO~$qGJlf_f zz|JD7dF$l){rFgqeH>5AOa&gpdZy-wpU{vHt4}$}CP4+nPL7W8mgW560XKZ-?QLz= zEfBAN5eRWPTfa`_Ptf-Z`!1A2Vj1P0DHSD0% zUh21Gk@&JFoBR2zwzlC#uv8G|7PFmkm9fkOgtnQL0&c)APrpeglMg4O9}_$A0j00@ zR??UlwCX70Z4=w`2Vt5JJq+{bOcZ|g&@hlua2;<>2r1DqgzzBNz{3Kp+|du}Gvt1z z;e2?F+Lf&V80FV;Yf`L&=8ZFeUyrYTjo)|fpkqvmAd$ zVcJSOXLh^0uiDgKTJLxkMpPYLv&69|+8WH>SzQMcBnRO>FTWfEKYkFJ_p@?T&vAjW zt-Zn%wA4&5@%$AxvY^~t8K1KC7yo&8nThA`^-NTiA{_K+$QdiDSI@E7Fk)$y>vQxk z>>Q=t37)HT`w#--+AKrDIla*&_EaSPE_8BxH|znS1bff5`q0sc|8EXz#68g?-(L``4(_GRHyveL& z@%?{Et4O`D@emtw++wpHJa&eP>T?#hX%OnyhJB70x7t#tD6* zaY9m7R)iRyHJnQ{KGvA~pEYd|mIRTvPW|PI3F6x@AJ=jTom*7+OX4Bd%rcECe9gUS zk7n5?78#y{cM9`~xv}Z{dke=2qYIxt%Qea8fF-?bt7ErVu zCw1~uO{D8hcQ!Mjo5f!5uAV9g|d zoK^DJsAh>Ri>U@3t*f(BCM$9xH!K-KaPZ1~%Xa8=1Pf*USFRw+PR^R11iC1`-7LS*vVTfUZAeW}8u7uqD+31FrnsZTLUY|&+nl!AUnumO6cR5uc zf{$<~)P#Ia+jN7l2ld)CEdoF|<5_q%#^M*_D4dty#-M(gZ9WiuX^hg>n(cDiY}odL z?fyOO@Lc6xg3AIG0pZqGU|X5f`nS*gC8MpidE)#f?_vAllA=)1YEcLZ+@dSsy$O~s zOg7`oorc19;IT7&_;?5HrpC7DvzoCa!t7k=eIBD#a>6Au#!Ig8khef1uKUA`JEk>DS1s^Yb0}pO zA|mHOg*FPPte)(T%VQcxrn$6D{8ua!V|*VgDoDD}jWBEn!3=pUVu{+ck&hPG?r!W>N1PYN!sqh5T z))9AIQ3rB<_w6oU!DB>7p>D8hGKN@P@q7@0QOmOMl53&3Ihux^BSqq&zaRec?8wyp zmH(s1yqRhsKNt8E!z!a$_<;-+dm8O`y3cMsF3uDr8fd)jL)lweB7Gr2KuSb*!iLkU z9Epy_ z4PRBX1{87J$yqu4M0-WoO%Eqln7e3v*}3;us1)Y8SEroj0(Za~64mQW-PSBkmh@SI z<`vEn0C5(LWCS(BZ6$A=-L#4qj25~dV4!4I!ox(I-4Q!(y}v(f`7r8uGzA@RYOPhp z{9r_2t*J1bGi3@UtMc-4_^j;s10X;@ei+=dpb)d73;lHJTgHN>+OMixf)Sr;Yry?- z=kyvJ4pvX+$aXKpsPW$D{$IG?slS;a#-sopTCL!*T%N$4v+29*4M+%8VS-&j{ST6c zb;}AcmN1`rtabK3k|9EE7caNVwb+*5N>E_n*ZKJ2gHkFoW7Kd`kHqRO{1{8`UXl~& zcX&9tnqwTsAN63n_-8Pk)pt%XVF~9Zjw{R=BQBuDjV3;6VCqe2nBmxRMSc9#@j{XgsZ=cH_{?}Zrzk%VU?$2Q+RD7`|_zzK<+yC?DtswJo3pCHVh_06K?yJ-} zx7`LwXH^=vo|%+7f51ITSUum}RUorcRaJWI3GQAkghWpLmH=!#E^W30wy66Vq`}zt zU+k)krNW*ti+mbpQUKcjtlS6(&SaNARq z+P%Wj+g!!a-jDJ)iX>wU*FHy;czarYw10$m+KOR<#U&^IB9LYSKNN?8ok@M};9s|e zs3@J}4(piNH9bxg$WZ^MqpJ*vvTLGCt;Eu`bR!_Kq=0m%gh&fZNQ;yTNK1FOgp`Fy z2qG5T=h$5a(-XoOwt$xaCTi9^@4IzEAh-QSawr|KDlE3nOKzN(plN$hKN{F@o^_BLy= z?z|rH-!~%SNd^cS4h}=XjF$Ltc=agBVt~PT+wA2eI@Lm?|NEvf^NlR0`dL5OBvwqS zh^7D<#TjW@^C^mu^d;1YxLGx>Ld84RGX{J5AC0Lmnz@qE_vRWlc+q<7n7pfMdGn@M ziL{8Hf^fbyAfwC)-3O4W1Y_2%fa!jKGrVVioId(UMZ*{T?S7lIgnW2-mi;7N(au1LoWO{5*8LA0QXYx47{7 zWtFohABO3V*w!9PZn;#Z3hWjR2#iaytANRo61&%*1$jEd)XToON8#*5s9VY}%kzk% zQGYb~X9V#gEI$MI_Wh?s7y~nH4sd5(I<|Jb-@}*)2HTa6PfY|$R0MU09mN1Uh6(6; zl`m|?*Rc&nN6TWg;YkxtOLGXkr$P`>P-^Jn?hcH;zuyu>z~a~COMQWG4bMq{a8R@z z8buIs3Co|b;L;hkc5xRwWU)#5P8W2L@N;^oPHf>|m%n}Tf;W_n5pSLzm-|5iRn%!z zX1aYe2Om48W0AZj@j!CaWS-_VO)+tb+TCKW-b3($KMGRlSiC#)B{fzdUCXx#9)zlX zkU^V`W>maizW?&JzvtI?v}7?jCph8THnSc;R$w#6ERc9UwII1Sv)@SXMK$@*vc9z# zrh7sB+ZEQrO>f>1;NlS4uFyGFVw~}xtLiW;CDMsiu_>hr=_PtzG=>WSr@d_9i9T3Q zQ4vQ0$EjimPon?yIH)Mg>2+$RF*BLHKK^F(ZAL}PZ?sW@P7Pzw;I8k^$ zgb$UKDJ*;)KPBjSKX0}s!;-6z>q>d0JMu)^b1=5hi~3?~9odg=R2V0xI^)zIr^S3bfJ}5pK=I6eA;XAd617i6y4k%86amUH6 z)EMnsX$Do7_e39Cp2-u2R44{!AbcgNWLpiS)5qQy&T;qfKz$NOWNw@bM-8CxFVkbc zI@RB~dslo%mu4@pZ4XC#KDPo(OCgfhNBYEIaZN z`CBWrZCUxmRW>!<@(>PRzEpN}L#LJ@iv!syC#FaCu-cSb1QsdkHpG8Le_Ku!m>-Ap z&36*A;~*ulDlwZB4cU+#0ob|@a(kC>`AS7@&C1h>aX)Ehf3BGBuHPv^0w1b^ z!wK+sinHFUzhelj|do6~=Pn zMUU+0lsSl_RA3^T&baH_XgpEI=mU|JrUf##LeXMPVdZ3 z>bd>q-z^Gfk9hs1urktNVPo5c77z|Eq-b^+2n~Cf^3eotEh$SgSDuE+MP0reLUGzT z%Ja1(N;_E4fhDW5f}>FAh-hn%;Ts7)EEl~3|zd})X?_+`j4`Aq=w-tF6~ z5CPR1;XtaTIi%5sFPWiA)TP?@SX)jvP?^O{FSc$XRAoy zsYCOx#g3;l9=H7C=AQ9=swEc*fg9|+8LnW8_(23iP(oot{2+pjXM9H-KD7G}9z3fH zdj}elr#s-kOBILDML+sAYK-(0^fqFq1VR#ddd9m-q#X|rJ*AnEHZf>9aq&ks6605H zc`E!{eLXW_WMy@_#*zDc(9!;V#86#DMbu%hk@U4|4%`tWfLlOs27KhttDUuFL`fkm zK{vx}DFUwFY_`@gPaPNDqu=%)Pl2NvduZs5QAd8m~GQ!c+9$La@cI&p&{G6SA1N*v{%CjYp-f@0F@e@rF97Fd>jImlO|H zKF|QXj&*zU^L`6>YXIxfQL2DCPECSQUQw~ip!4(sd}pMYp;x%{n4UvL>ZyfbHt^fw zrI(ak-91JJR@p?7XueAR1V;CzUxaCh0Z}LvI1vZN>#|p*3P=2pd{Z?I-po?|7p83B ziVP!)Gl{!GF;v%J$U&+$h4rQLAc+k(8UB2Icq15lJqPn!UUD3EoswX76Ce;!mR z{_2m@>~J=PAoFJ?yu7^apdtYF1hiYw+h6bk(>SV93CLhFkPoVzc#!KZlRZ!1NMQF#tJ+7xskIF?K)j z>#zNgi?c(J#5%q|4hnH~2rQ zefG5)WnISY(M44!1bCs_z4U<5Zu_@dRzpKxax2yQCw2Yo@58?rn}5zXV<6S%Z^y-7 zsG@3|+h8Z8c)pzyditI10-{Kq0|J^&U%!8gTVH!^P%2Rj7lk(ACP-{dvC+<(_v<63M50Q6_;|PwjN)50K1R*8K{5I zaU}HOo)zE@{&#<|HK4wtfP(3}aW>f#2%MAj) z4Ppz>h6R9?Mzi%nV4IrA(f$_i_M~tQ$>+y+r~r>;=md}P_V#n|y8p(fyw+M+XSp9?5ryMJJ zB&k$Dnu<7mU`WXM$^I{J-1d;twT(Qb8Pm*g2o1e}@I}y}jyl!Tkf}T%MLq(eSGdx@ zkP&E20|JhLfGbf@YLevHUNE|+nEPSWQ4IbjmW3itp`||? zj+g41G~h_!AOh5K3TIeZfTk@T|m z{^#s7e0$Fugo=Mb%JAqBUhWKYK;KD;^hC+QanL_I%-q~mmbz%W9{XB&pzVSCZmzD+ zUN#wq`z*FR82fX$=F<#Afv}+dRiaFNp&O zAt`%mLqgxBRrc@i($4@lCsQ}nl-+#&<@e3 z5DV%jJGyhdX-@xrY37sXr|Keintj*E8U!WS>Slj~2^AdX`Fgk^;B5q%GxQGc-Ua?e z1#%-0ta93F$|hc32m#oyY=g_M68r0p4sy0f+xFNFC}~iF!8qKf7r2J?&SS8Nz7Yvq zDY)$6%6j$b*Eb}etK(2o$Pq02Tt2{ECP)Wh&wKi=HFYK*fBz$}>||zPiIOAWh~^Fm z3i{_53!!Mr<#$|NH;ONgi!b)76oo`Yn4*>W;aIpv;G6@W6I#<7+rC9_Z^AydxVX5< zToCNt>dXb^yxRuEiAT=gCH61Ej*Qo88uSOm&KzxkSdf>ewv!t#2 zCUD9Z2UM+JC3+u7>m8@7A`nM$u?@=T9fBWB8X6jamu1K`uz(#X$R|#*T0Tf`cD#!~mZWdTh&woy~Xd+$lAD z3S>yu6d3ipa-iV?mRT3}wM`9IIV}naK}s>YAL~$W!4NnsJFp+Yd2Q8~9FZeN#=~ zokEU}t49uyNC>1zm<@69k($Xo{s5jsAaDkK1bd}^O-6R2@z9^cf7cYSgn`uv*Ct45 z!9#G+slMUXlC)o`jfIaBmYL2af|5lhAm8mwooZZV1=N5Fb2dCzA4Y1If zPuCO`L;oJ!F?X2ghF=$cmLmm|6pT!7nz)C94|QH+0^~pZXKD-M2+ic@Y@1X%)-1_ z6M7-+A$@S=u9X@8?dIUWjsM?lzkPTpJn=9vFh5z4F>2eNd8lgf4+N{gxS<(JjEMZG z4*{n8L?o|W93~baaOecw_Tk8=fndrzjV_~m8Cz@*g&CSISPVRIH=zfwO2ARYmf+Zi z`AXYh@jVkqom8>8(X`>MZ{F;~hi)6R-CL%a5_0i(5z2Ck9Ns1Hs)5_>1a2S2vke38 z&oJ(~xWr1NhCu^Xnrrt7DkktP(61c(dz}=96s`>Zhx>$n2JuD zjkC~Y;`Ti&@V64k8iG&%=TA4@3}x^9^5S%Gd7^j{)?TWJOHh{z6IoV zkT?Mzs0dmza9=JSn=1dFtwb2KwbTXenWpBYcE^c?s<<97^-NnOuyEik3V`g1 zsnFw`(Bme^8Mrv9xqz3C+AQ`N*~7^HBfEm^Aam2Kk7D!n1LRVPoI=4B=;(-fDJ3xc-F?;FPm z9Wg;lzv_sJ`#%Mg{4>f60gljlM_rwYbG9ZVqm$QNJ0IH2&Gg!({lw zM+Rpc61P5m{0Kwhi%Dj(OHcAglIu*Bf0Vf*5wl<=`Ym&b1aS-vi?djszZ`3lM$M~N zsRFle;Ff0tnO&zMB6=FS3rI*p=vaep{`wk7lEJQC@#(mt9Tn_XgA`t3(o;=JRoPTI zG|insvMX8~;S!nl$O_5-p9<2I4el$IZjlhW4B!xop7O`nHx3#S*RdG(teloW9_vecm zct_yPg8>2dH+ZD?TORXDxDTk5j&E&wDHJ=GlF798+S`4c!=REyICeg5UYSRq%zL?} zarG~tGA&^SRts==W2xN1=QJ>}mV2*Vry9++T?<$>uVOgP8=p{JV42;g0Q z?=XEqE35{zOgF8U>8Z7+z)JB7gQ>+|F1e8*yPF3r@PI18=@J{?y}d2UH7sUA?xkTR zo2OT|fc(PKMvCZ#PX$T=>>k95x&>t#WVH^w!d`PyKTq8U1rv0rLq-&%fQWW4Z$Yhq z%t$8Kz+VRkzIT`&BmWkdl7SBi!3qD4RFVAL{CuP#s=w?xJ=Kj>&9Vuz8XNiGlRdbu zD=0xan57fttoiGJzWR!9$DSwwT}7!fnXe0V)UoZx49^c5na>~$M4?-%sp;0JJnZb@JiC&am_`B9z(^Yp714(-u>blKa@Ybsl!UFGO%MJ3 za|7g0`a!D?u25$&p+_;HCsm;*XMaL3mb&nAhG`ReLF@gm!qq?z5FstNYXeOgpxrvp zTqOP(DwzvLJ0N`4?j7#$!=wW5dA;-BO)F#oJN^n zY{A;4)r8&1dFr^f#CxS$n$l3}vj4RIg(d06{;y8}J7Rob?}7O@EkK}_GA`I%04dUZ zJWZ#fgwK~3E7{Q4$V(LmWm4|p0qS(NJO@*tR|$+l2}U3z0akUw1)tBqv{RUV@^rCF zC19~jP(x8Na2r1L&pvEP8XJ_grTt*H4Mh_C$-&AwDh>CA1^+&zia?fF>&A9E|fgp#0&o`b;LxQ(3=^~l3Fd8N1)dJ4Jt*xk)>+wScq9_MP z$MU8odKO_*M{$lYGWza2Fmb{wfc6HPez>MT)!06|zz|(B?ft`pEZt35!~QTmADqt_ zz%g$>Klud)g>|NSDus)4bGqDA(5Ng?g?V8J?bvA&)am#@PB&$h*#Er&OIfJt8L`Z> z(#77+^($fXlAzFYXBEl0KrfCZp%IQr9JLb&0ufIex!Pj=3SgkjH~`vxo0(_o$0u{& zyqWcYz7BfGfrHc?Oye(~<`N0$RiSr!Dh2!{ELMp>YOgGDVsfTRVe{87hqz7v4!2b4 z5(s)!H-c|$U9)(Av!JOK!_!qzUdD5kc9Q#^moKmS6l#&=Yw}jtTImaYJJNgL8j{B` zz+b@*DkGVU!Re_faR~{vcDy^;@uY-AY}Xg*KVGg+1DrzCnAlx_*rlqxj7}&PCz#@l z9ziTfynm^O)+E*#maA4$z=KiU&`V1iA{!L{j_X&q@pk(iQRWg?NHGh<48kfjpL>=$YHGFWoJW|dQ%5q^a z-rHocSIIVRA3cYWGUO9t&^h>ZOd?F|B6OG{7U!$1wF$lWI(C3|+fl=8c(rb7V`Xe( zxQ-IxPkxT9uctXulZ>7-q7}{4gU9Lds*H7&1SN?^4K;A9iP1c}ZmslayZ4PN_`W4L?l?EqvTb{8;j-Hjn9`(hT~71n(q4DO##wp?p{VSGXto@o`5p;YQwU7i9uoYVTQg3tPT6bL~WwG0?vaJ z4DR(*z0bkQ&0nK8DP&ziQ|Zc5ybX+A_VTo*@wr>bijyw?1hbf$C?hMIZq1_M1jg?s zxP+>$)YlP4_TC6x_!jps3(B%%wKNBAl%rq^7mQb@{2t`4@sta%B`=VV=%pkW`ZALzZn~-NJE^KAe!-> zQ}Mn4MFx<3WZ+37D`*c`RZ1>A9`H>fI8C@lmA@z?Ua6dNVEuCQkEic?tgTXY^~7$6)`w&Bl{RE z!NE8<>OZULx!oh=I3H|YCy8HqmC9_F$M5wvuhI$G>4!K^UC}veajH0^Rf%f2T0N6N zdXSpEky-j8C0Y?L7zkbz-#8{u&O&0H8?@`0bLXJcdpDN*v6{< zlJCk_lG0nC{C3?Dh0jFOHu#(|@UH9-E^pg%o;PVSbm?5g>6xN^V_KKbESEJSy0;8B z{47264D^uSwc0y32xp>AA3A+A_TTdJ)%Hl6&x*ZbmU`jX7;hb^*g^KwSt(7zVNY4$RHcwA74()js8U~kbXh2eZZwb?v zVF|YkvoXTh`q{8;tcdtwQaHn5wzjP$du*mpZ{7~#Wl2>JhtemqT->^OaEt>R@kU3h zD`(^a1n=?facDRiinSxwAB1<(@74W+0l2?RbNIR_R(%08BE}$M8Ys!D%~zH=$%^#p zGI6}gm(~>-X?Xz|`C}CL z$h%kErJ4I|(*oiT7`K_6U<;Sce94|CCQ(&&29}3?XO+y{HyJ=xj-sQ^-lr1g8wKi8 zsc0&LhQu?5&Lj>D)I;%W5WI!cm1_#^-<5@;XY#xYuNOga{DCgc>WQE}ZsjVsL39VH zzuDBoMeStk(6&A!AJIb0dv<^Rk=e;f`-vw8Qq`B-C){0*3{*o)uPFsY+!LZpu!xXa z<}My9d(MrFwhRmRwk}5{VhuYvBqsq0LLp&6Y;n_0ERTCs)HUi96j zxrp@QO#D3ZQ zVT}2r^5eanu<;Xa|5K7?JdV-t+vAt*oQnLAV+e*vzXn*|O+L)Gw6by~dyl9fR~AZ1 z(jF%?+V(%)*_m{EL-o@U5UzclHXKi$^eS`8uSSeSYJMLW8tfDxP*AZ!ml=6ahVmWg z=VhNy3*km~Ev#Y(S$)-bSNgWhl2cNk!%8yBuYPjIHRZo^+hx{|il!T*mB_#Fw#Mf{ zl==HZuX>&nd&^CMs@V|)jz=*55yhvjPLqs1E>TSVbB~xKE4?jd??CjA|0+RRUkH}Jgm&rMEn2YeY5tXnYj$9#dutAL! z&W?{$R5i|aBKUnAj;>%|^xCKO<6P8ki0ogrwa1=|^wqoE^U1K8j}O=0c^r7R=+^MJ zw6tJPZ0w~7=hFYQQJ7yA!K`tntuND+EC@51D{G%}r@8K`xK?GPn>V;bd+2nx?P>)X zO?r3Sct&#vTVj^~81cH5#`OC*y8sRGQUtUHfLFqpm#85|6icGc68%#pp7_0xFDJa` zHZ@Ivr5^~V+z`lu*;#2G8?5wfO^@U!i9Xw-Wip&QVJU`scZ%$@e(5Gh$S?)!3gpq} z6sZ1~9{!EPr7ol@d^MGFP--;~9(D$QY`D=j9STKT2J2@qC4ZKQMtfUND>EPz<>bn9 zj@^+zq;(6(pRo7|5XqG9NfnTGm&}l>ldRxONG^OAVa!cuWVnaYGsu@?$YO703m^1s za^A1Ifl3UzG=k1MJX6ObS&M$CF??%8gH$7WKu4+NYw~p8;yag&h4U-OK7V^GmB-70 zsGh#^DSNE<*UTvBIPDjh z9utC9b^<(o*ZW&pZ;+UBvKMq@2!l!?)dLR87vJ8D|DeQ`rMi}?7Vd%}@91@^@AB@i z7F`8`r`EH9ED940>d^!`p2X6b-IRhZ)rKpFcf0(U;|;~*CN1vw-0Xoe+;b`vh=d9$ zoOueZyc2?!)W+ng(X_P+x#filr5 zsoo+&wA&pJ2lqINQR4EO9F;^0W7*RMy_F-j?9@-vgFL(RD>R?@JtXeHeeFCI7@Qs~ z5gJTKu<^72l>X8iw69;sy( zMWa7^Hc{hN2inQJN^$L$Lsj^&`2Jll&satiUw)K~$C(&Z<`xjF&KRVQ#>+!4;mH(JrP+j&SY?L72Qt z^2aVn4$^C;DhFd<0%tS~|FGzkz$L0o35`5kh-v-*P!jn(Iozllj70bz-7r=~M_6$D z$NQS~=-$>X7w1~8D&uFdj3tIvXnW{$v3fLkx}x14WTk4YvdGHI8kA$(_Ulwk@F}B4 zXv0NguWP-rRR6juj^}WzqNyR3fxpcrOT3=~F9%IVu+D)2CJ-eXFOHS#0gF-J%N)ww zH^Tpo<(jIBT;{T)9*>Dlvf}y<(A9fq2v=J+aQUv)K;5+Xd_UWDUu#jN+yK3A7m`5V z#jxW4?O@}kmx(C(lmkLO2y@Ck!|jMf3ptJ2bGW3WO(8q27*hr5BIb>^#gYv?I*%le!yhwz3W)mzzRepKF5SaEL++Tjn-4MJIp^eDJp8X0l0 zHwj@W##h}6`!ou@p-w#Cwl+V3+VxAwUFs0R8O+p5hBvBN5B<4P#sdvOUj^2-s1el& znw>S~o8iOD?MpJLKaWQ`@3#$PUh8W1lzOc#LdkR2Fpx`(P08lcYs%1pnH7SW{S&Gm z78uMZ9%J(KUg7m8#;Sj~Gn|r_&`SPtVt4MHlAc}ZM95(b)LPla7z3w`P^in+AtIy> zbwbjSlQVD0%LbvP2z)?_GBzRX{htP z-!Jb6pbMjhn*WW7)3(-ZtsQjayx}hOTHRg#%l;1vl=al`dF(3gU;YZ0OM|4)Fm{kx z%B`XC07y-ci`D+aMT;mVLo3d{%qhrVz{&7-BY5J|^3~YS4v9kTt8Zl6s=jF42_*Ut zB#uUk33PKj7TpCtIY*amKOuhze?QxToH{+v$)`0IzQN6J-w#S{B!#0S8KTo>nEJCc ztbo7ddqsz&b9dQ`zrp==o}73z2Vz*c??Rkoj0DVR0{h?GtYM&*OW#cRTC9?_N;X3>JGOu8><udBUM!AATK?sG;M From 4115ae7ced77d392ee11ea55212206d9404356f0 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 2 Dec 2019 17:02:07 +0100 Subject: [PATCH 053/237] Fix indentation issues using newer uncrustify Seems like the older uncrustify versions did not find these indentation issues. Fix them. Old versions of uncrustify will leave things as is, so this is not a problem if developers are using an old version of uncrustify. --- libfprint/drivers/vfs301_proto.c | 2 +- libfprint/fpi-print.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libfprint/drivers/vfs301_proto.c b/libfprint/drivers/vfs301_proto.c index 103e890b..84e2318e 100644 --- a/libfprint/drivers/vfs301_proto.c +++ b/libfprint/drivers/vfs301_proto.c @@ -498,7 +498,7 @@ vfs301_proto_peek_event (FpDeviceVfs301 *dev) usb_recv (dev, e1, l1, NULL, &error); \ usb_recv (dev, e2, l2, NULL, NULL); \ if (g_error_matches (error, G_USB_DEVICE_ERROR, G_USB_DEVICE_ERROR_TIMED_OUT)) \ - usb_recv(dev, e1, l1, NULL, NULL); \ + usb_recv (dev, e1, l1, NULL, NULL); \ } static void diff --git a/libfprint/fpi-print.h b/libfprint/fpi-print.h index fe07c260..94670a02 100644 --- a/libfprint/fpi-print.h +++ b/libfprint/fpi-print.h @@ -43,7 +43,7 @@ gboolean fpi_print_add_from_image (FpPrint *print, GError **error); FpiMatchResult fpi_print_bz3_match (FpPrint * template, - FpPrint *print, + FpPrint * print, gint bz3_threshold, GError **error); From f9b2c7f9c3a85f679b6e99166705a01cbb57b640 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 2 Dec 2019 16:48:04 +0100 Subject: [PATCH 054/237] virtual-image: Fix driver reading insufficient data In rare occasions it could happen that the driver was reading insufficient data. Fix this by using g_input_stream_read_all_async which will ensure that incomplete data will not be misinterpreted. This fixes rare test failures seen in fprintd. --- libfprint/drivers/virtual-image.c | 46 ++++++++++++++++--------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/libfprint/drivers/virtual-image.c b/libfprint/drivers/virtual-image.c index 612863da..c271c7a7 100644 --- a/libfprint/drivers/virtual-image.c +++ b/libfprint/drivers/virtual-image.c @@ -66,13 +66,14 @@ recv_image_img_recv_cb (GObject *source_object, g_autoptr(GError) error = NULL; FpDeviceVirtualImage *self; FpImageDevice *device; - gssize bytes; + gboolean success; + gsize bytes = 0; - bytes = g_input_stream_read_finish (G_INPUT_STREAM (source_object), res, &error); + success = g_input_stream_read_all_finish (G_INPUT_STREAM (source_object), res, &bytes, &error); - if (bytes <= 0) + if (!success || bytes == 0) { - if (bytes < 0) + if (!success) { g_warning ("Error receiving header for image data: %s", error->message); if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) @@ -103,13 +104,14 @@ recv_image_hdr_recv_cb (GObject *source_object, { g_autoptr(GError) error = NULL; FpDeviceVirtualImage *self; - gssize bytes; + gboolean success; + gsize bytes; - bytes = g_input_stream_read_finish (G_INPUT_STREAM (source_object), res, &error); + success = g_input_stream_read_all_finish (G_INPUT_STREAM (source_object), res, &bytes, &error); - if (bytes <= 0) + if (!success || bytes == 0) { - if (bytes < 0) + if (!success) { g_warning ("Error receiving header for image data: %s", error->message); if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) @@ -158,25 +160,25 @@ recv_image_hdr_recv_cb (GObject *source_object, self->recv_img = fp_image_new (self->recv_img_hdr[0], self->recv_img_hdr[1]); g_debug ("image data: %p", self->recv_img->data); - g_input_stream_read_async (G_INPUT_STREAM (source_object), - (guint8 *) self->recv_img->data, - self->recv_img->width * self->recv_img->height, - G_PRIORITY_DEFAULT, - self->cancellable, - recv_image_img_recv_cb, - self); + g_input_stream_read_all_async (G_INPUT_STREAM (source_object), + (guint8 *) self->recv_img->data, + self->recv_img->width * self->recv_img->height, + G_PRIORITY_DEFAULT, + self->cancellable, + recv_image_img_recv_cb, + self); } static void recv_image (FpDeviceVirtualImage *dev, GInputStream *stream) { - g_input_stream_read_async (stream, - dev->recv_img_hdr, - sizeof (dev->recv_img_hdr), - G_PRIORITY_DEFAULT, - dev->cancellable, - recv_image_hdr_recv_cb, - dev); + g_input_stream_read_all_async (stream, + dev->recv_img_hdr, + sizeof (dev->recv_img_hdr), + G_PRIORITY_DEFAULT, + dev->cancellable, + recv_image_hdr_recv_cb, + dev); } static void From ee606ae49e57de0ff09b4c3be238a2bd4709177d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 28 Nov 2019 20:34:20 +0100 Subject: [PATCH 055/237] synaptics: Use an autoptr to handle the FpiUsbTransfer sync transfers When using fpi_usb_transfer_submit_sync we still need to unref the transfer once done with it, so let's use an auto pointer so we free it also on errors and early returns without having to handle this manually. --- libfprint/drivers/synaptics/synaptics.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index ccaf60ee..284973cf 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -950,7 +950,8 @@ dev_probe (FpDevice *device) { FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (device); GUsbDevice *usb_dev; - FpiUsbTransfer *transfer; + + g_autoptr(FpiUsbTransfer) transfer = NULL; FpiByteReader reader; GError *error = NULL; guint16 status; @@ -985,7 +986,7 @@ dev_probe (FpDevice *device) if (!fpi_usb_transfer_submit_sync (transfer, 1000, &error)) goto err_close; - + g_clear_pointer (&transfer, fpi_usb_transfer_unref); transfer = fpi_usb_transfer_new (device); fpi_usb_transfer_fill_bulk (transfer, USB_EP_REPLY, 40); if (!fpi_usb_transfer_submit_sync (transfer, 1000, &error)) From 42db16364db0c1616c83064593578adcafe4703d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 28 Nov 2019 20:35:33 +0100 Subject: [PATCH 056/237] synaptics: Close the usb device if reset failed If reseting the device failed, we still need to close the usb device before returning. --- libfprint/drivers/synaptics/synaptics.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index 284973cf..1524c451 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -970,10 +970,7 @@ dev_probe (FpDevice *device) } if (!g_usb_device_reset (fpi_device_get_usb_device (device), &error)) - { - fpi_device_probe_complete (device, NULL, NULL, error); - return; - } + goto err_close; if (!g_usb_device_claim_interface (fpi_device_get_usb_device (device), 0, 0, &error)) goto err_close; From 20a52593ebd6d1ed24c8a9ad9e373a9f8171b6ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 28 Nov 2019 20:40:32 +0100 Subject: [PATCH 057/237] vfs301: Use a transfer autopointer to cleanup it on sync submission Partially revert commit a855c0cc7, since the driver uses a sync transfer and in such case the caller still keeps the ownership. --- libfprint/drivers/vfs301_proto.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libfprint/drivers/vfs301_proto.c b/libfprint/drivers/vfs301_proto.c index 84e2318e..2bf8bbd2 100644 --- a/libfprint/drivers/vfs301_proto.c +++ b/libfprint/drivers/vfs301_proto.c @@ -67,7 +67,8 @@ static void usb_recv (FpDeviceVfs301 *dev, guint8 endpoint, int max_bytes, FpiUsbTransfer **out, GError **error) { GError *err = NULL; - FpiUsbTransfer *transfer; + + g_autoptr(FpiUsbTransfer) transfer = NULL; /* XXX: This function swallows any transfer errors, that is obviously * quite bad (it used to assert on no-error)! */ @@ -78,7 +79,6 @@ usb_recv (FpDeviceVfs301 *dev, guint8 endpoint, int max_bytes, FpiUsbTransfer ** fpi_usb_transfer_submit_sync (transfer, VFS301_DEFAULT_WAIT_TIMEOUT, &err); - #ifdef DEBUG usb_print_packet (0, err, transfer->buffer, transfer->actual_length); #endif @@ -97,7 +97,8 @@ static void usb_send (FpDeviceVfs301 *dev, const guint8 *data, gssize length, GError **error) { GError *err = NULL; - FpiUsbTransfer *transfer = NULL; + + g_autoptr(FpiUsbTransfer) transfer = NULL; /* XXX: This function swallows any transfer errors, that is obviously * quite bad (it used to assert on no-error)! */ From 0241617713554e83ed21942eeaa7f213f9318625 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 22 Nov 2019 18:03:08 +0100 Subject: [PATCH 058/237] fpi-ssm: Also bug-on negative state value Being an integer, anything could happen. --- libfprint/fpi-ssm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfprint/fpi-ssm.c b/libfprint/fpi-ssm.c index 5367e327..5299e2d5 100644 --- a/libfprint/fpi-ssm.c +++ b/libfprint/fpi-ssm.c @@ -317,7 +317,7 @@ void fpi_ssm_jump_to_state (FpiSsm *machine, int state) { BUG_ON (machine->completed); - BUG_ON (state >= machine->nr_states); + BUG_ON (state < 0 || state >= machine->nr_states); machine->cur_state = state; __ssm_call_handler (machine); } From 3ed73aa17c11081351701409ed14990d19c97314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 22 Nov 2019 17:11:29 +0100 Subject: [PATCH 059/237] fpi-device: Make possible to set a DestroyNotify for timeout data Since GSource data can be automatically cleaned up on source destruction, we can mimic this for the devices timeout easily as well. Add an extra parameter, and let's use this cocci file to adapt all the drivers like magic: @@ expression e1, e2, e3, e4; @@ fpi_device_add_timeout (e1, e2, e3, e4 + , NULL ) --- libfprint/drivers/elan.c | 4 ++-- libfprint/drivers/uru4000.c | 6 +++--- libfprint/drivers/vfs0050.c | 4 ++-- libfprint/drivers/vfs101.c | 2 +- libfprint/drivers/vfs301.c | 2 +- libfprint/drivers/vfs5011.c | 2 +- libfprint/fp-device.c | 14 ++++++++------ libfprint/fpi-device.h | 10 +++++----- 8 files changed, 23 insertions(+), 21 deletions(-) diff --git a/libfprint/drivers/elan.c b/libfprint/drivers/elan.c index e2767dd6..f9e87638 100644 --- a/libfprint/drivers/elan.c +++ b/libfprint/drivers/elan.c @@ -756,7 +756,7 @@ calibrate_run_state (FpiSsm *ssm, FpDevice *dev) self->calib_status = 0x01; timeout = fpi_device_add_timeout (dev, 50, fpi_ssm_next_state_timeout_cb, - ssm); + ssm, NULL); g_source_set_name (timeout, "calibrate_run_state"); } break; @@ -1020,7 +1020,7 @@ dev_change_state (FpImageDevice *dev, FpImageDeviceState state) self->dev_state_next = state; timeout = fpi_device_add_timeout (FP_DEVICE (dev), 10, elan_change_state_async, - NULL); + NULL, NULL); name = g_strdup_printf ("dev_change_state to %d", state); g_source_set_name (timeout, name); diff --git a/libfprint/drivers/uru4000.c b/libfprint/drivers/uru4000.c index 7e287244..1deadd3c 100644 --- a/libfprint/drivers/uru4000.c +++ b/libfprint/drivers/uru4000.c @@ -875,7 +875,7 @@ rebootpwr_run_state (FpiSsm *ssm, FpDevice *_dev) break; case REBOOTPWR_PAUSE: - fpi_device_add_timeout (_dev, 10, rebootpwr_pause_cb, ssm); + fpi_device_add_timeout (_dev, 10, rebootpwr_pause_cb, ssm, NULL); break; } } @@ -971,7 +971,7 @@ powerup_run_state (FpiSsm *ssm, FpDevice *_dev) break; case POWERUP_PAUSE: - fpi_device_add_timeout (_dev, 10, powerup_pause_cb, ssm); + fpi_device_add_timeout (_dev, 10, powerup_pause_cb, ssm, NULL); break; case POWERUP_CHALLENGE_RESPONSE: @@ -1130,7 +1130,7 @@ init_run_state (FpiSsm *ssm, FpDevice *_dev) self->scanpwr_irq_timeout = fpi_device_add_timeout (_dev, 300, init_scanpwr_timeout, - ssm); + ssm, NULL); break; case INIT_DONE: diff --git a/libfprint/drivers/vfs0050.c b/libfprint/drivers/vfs0050.c index 43252c03..63776390 100644 --- a/libfprint/drivers/vfs0050.c +++ b/libfprint/drivers/vfs0050.c @@ -619,7 +619,7 @@ activate_ssm (FpiSsm *ssm, FpDevice *dev) /* Wait for probable vdev->active changing */ fpi_device_add_timeout (dev, VFS_SSM_TIMEOUT, - fpi_ssm_next_state_timeout_cb, ssm); + fpi_ssm_next_state_timeout_cb, ssm, NULL); break; case SSM_NEXT_RECEIVE: @@ -639,7 +639,7 @@ activate_ssm (FpiSsm *ssm, FpDevice *dev) case SSM_WAIT_ANOTHER_SCAN: /* Orange light is on now */ fpi_device_add_timeout (dev, VFS_SSM_ORANGE_TIMEOUT, - another_scan, ssm); + another_scan, ssm, NULL); break; default: diff --git a/libfprint/drivers/vfs101.c b/libfprint/drivers/vfs101.c index 5dedab7d..0df9b73c 100644 --- a/libfprint/drivers/vfs101.c +++ b/libfprint/drivers/vfs101.c @@ -376,7 +376,7 @@ async_sleep (unsigned int msec, FpImageDevice *dev) { fpi_device_add_timeout (FP_DEVICE (dev), msec, - fpi_ssm_next_state_timeout_cb, ssm); + fpi_ssm_next_state_timeout_cb, ssm, NULL); } /* Swap ssm states */ diff --git a/libfprint/drivers/vfs301.c b/libfprint/drivers/vfs301.c index 7219792a..1d0f0662 100644 --- a/libfprint/drivers/vfs301.c +++ b/libfprint/drivers/vfs301.c @@ -36,7 +36,7 @@ async_sleep (unsigned int msec, { /* Add timeout */ fpi_device_add_timeout (FP_DEVICE (dev), msec, - fpi_ssm_next_state_timeout_cb, ssm); + fpi_ssm_next_state_timeout_cb, ssm, NULL); } static int diff --git a/libfprint/drivers/vfs5011.c b/libfprint/drivers/vfs5011.c index dbf82767..4fac03ce 100644 --- a/libfprint/drivers/vfs5011.c +++ b/libfprint/drivers/vfs5011.c @@ -706,7 +706,7 @@ activate_loop (FpiSsm *ssm, FpDevice *_dev) case DEV_ACTIVATE_DATA_COMPLETE: fpi_device_add_timeout (_dev, 1, fpi_ssm_next_state_timeout_cb, - ssm); + ssm, NULL); break; diff --git a/libfprint/fp-device.c b/libfprint/fp-device.c index 0a1f8deb..182be510 100644 --- a/libfprint/fp-device.c +++ b/libfprint/fp-device.c @@ -1475,7 +1475,8 @@ fpi_device_set_scan_type (FpDevice *device, * @device: The #FpDevice * @interval: The interval in milliseconds * @func: The #FpTimeoutFunc to call on timeout - * @user_data: User data to pass to the callback + * @user_data: (nullable): User data to pass to the callback + * @destroy_notify: (nullable): #GDestroyNotify for @user_data * * Register a timeout to run. Drivers should always make sure that timers are * cancelled when appropriate. @@ -1483,10 +1484,11 @@ fpi_device_set_scan_type (FpDevice *device, * Returns: (transfer none): A newly created and attached #GSource */ GSource * -fpi_device_add_timeout (FpDevice *device, - gint interval, - FpTimeoutFunc func, - gpointer user_data) +fpi_device_add_timeout (FpDevice *device, + gint interval, + FpTimeoutFunc func, + gpointer user_data, + GDestroyNotify destroy_notify) { FpDevicePrivate *priv = fp_device_get_instance_private (device); FpDeviceTimeoutSource *source; @@ -1497,7 +1499,7 @@ fpi_device_add_timeout (FpDevice *device, source->user_data = user_data; g_source_attach (&source->source, NULL); - g_source_set_callback (&source->source, (GSourceFunc) func, user_data, NULL); + g_source_set_callback (&source->source, (GSourceFunc) func, user_data, destroy_notify); g_source_set_ready_time (&source->source, g_source_get_time (&source->source) + interval * (guint64) 1000); priv->sources = g_slist_prepend (priv->sources, source); diff --git a/libfprint/fpi-device.h b/libfprint/fpi-device.h index d83a5a3a..2333ae22 100644 --- a/libfprint/fpi-device.h +++ b/libfprint/fpi-device.h @@ -203,11 +203,11 @@ void fpi_device_get_delete_data (FpDevice *device, GCancellable *fpi_device_get_cancellable (FpDevice *device); - -GSource * fpi_device_add_timeout (FpDevice *device, - gint interval, - FpTimeoutFunc func, - gpointer user_data); +GSource * fpi_device_add_timeout (FpDevice *device, + gint interval, + FpTimeoutFunc func, + gpointer user_data, + GDestroyNotify destroy_notify); void fpi_device_set_nr_enroll_stages (FpDevice *device, gint enroll_stages); From 7ec2df24051e59bcd8cfef50aa8397041fd89b26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 22 Nov 2019 17:19:27 +0100 Subject: [PATCH 060/237] fpi-ssm: Add possibility to jump to a state (or next one) with delay This allows to have an automatic cleanup of the timeout source when the the callback is reached and to avoid to do further state changes in the middle. --- doc/libfprint-sections.txt | 3 + libfprint/fpi-ssm.c | 118 +++++++++++++++++++++++++++++++++++++ libfprint/fpi-ssm.h | 6 ++ 3 files changed, 127 insertions(+) diff --git a/doc/libfprint-sections.txt b/doc/libfprint-sections.txt index 0abe584d..9fb01bde 100644 --- a/doc/libfprint-sections.txt +++ b/doc/libfprint-sections.txt @@ -215,7 +215,10 @@ fpi_ssm_free fpi_ssm_start fpi_ssm_start_subsm fpi_ssm_next_state +fpi_ssm_next_state_delayed fpi_ssm_jump_to_state +fpi_ssm_jump_to_state_delayed +fpi_ssm_cancel_delayed_state_change fpi_ssm_mark_completed fpi_ssm_mark_failed fpi_ssm_set_data diff --git a/libfprint/fpi-ssm.c b/libfprint/fpi-ssm.c index 5299e2d5..38186d21 100644 --- a/libfprint/fpi-ssm.c +++ b/libfprint/fpi-ssm.c @@ -87,6 +87,7 @@ struct _FpiSsm int nr_states; int cur_state; gboolean completed; + GSource *timeout; GError *error; FpiSsmCompletedCallback callback; FpiSsmHandlerCallback handler; @@ -170,6 +171,7 @@ fpi_ssm_free (FpiSsm *machine) if (machine->ssm_data_destroy) g_clear_pointer (&machine->ssm_data, machine->ssm_data_destroy); g_clear_pointer (&machine->error, g_error_free); + g_clear_pointer (&machine->timeout, g_source_destroy); g_free (machine); } @@ -231,7 +233,9 @@ __subsm_complete (FpiSsm *ssm, FpDevice *_dev, GError *error) void fpi_ssm_start_subsm (FpiSsm *parent, FpiSsm *child) { + BUG_ON (parent->timeout); child->parentsm = parent; + g_clear_pointer (&parent->timeout, g_source_destroy); fpi_ssm_start (child, __subsm_complete); } @@ -246,7 +250,12 @@ void fpi_ssm_mark_completed (FpiSsm *machine) { BUG_ON (machine->completed); + BUG_ON (machine->timeout); + BUG_ON (machine->timeout != NULL); + + g_clear_pointer (&machine->timeout, g_source_destroy); machine->completed = TRUE; + if (machine->error) fp_dbg ("%p completed with error: %s", machine, machine->error->message); else @@ -297,6 +306,10 @@ fpi_ssm_next_state (FpiSsm *machine) g_return_if_fail (machine != NULL); BUG_ON (machine->completed); + BUG_ON (machine->timeout != NULL); + + g_clear_pointer (&machine->timeout, g_source_destroy); + machine->cur_state++; if (machine->cur_state == machine->nr_states) fpi_ssm_mark_completed (machine); @@ -304,6 +317,56 @@ fpi_ssm_next_state (FpiSsm *machine) __ssm_call_handler (machine); } +void +fpi_ssm_cancel_delayed_state_change (FpiSsm *machine) +{ + g_return_if_fail (machine); + BUG_ON (machine->completed); + BUG_ON (machine->timeout == NULL); + + g_clear_pointer (&machine->timeout, g_source_destroy); +} + +static void +on_device_timeout_next_state (FpDevice *dev, + gpointer user_data) +{ + FpiSsm *machine = user_data; + + machine->timeout = NULL; + fpi_ssm_next_state (machine); +} + +/** + * fpi_ssm_next_state_delayed: + * @machine: an #FpiSsm state machine + * @delay: the milliseconds to wait before switching to the next state + * + * Iterate to next state of a state machine with a delay of @delay ms. If the + * current state is the last state, then the state machine will be marked as + * completed, as if calling fpi_ssm_mark_completed(). + */ +void +fpi_ssm_next_state_delayed (FpiSsm *machine, + int delay) +{ + g_autofree char *source_name = NULL; + + g_return_if_fail (machine != NULL); + BUG_ON (machine->completed); + BUG_ON (machine->timeout != NULL); + + g_clear_pointer (&machine->timeout, g_source_destroy); + machine->timeout = fpi_device_add_timeout (machine->dev, delay, + on_device_timeout_next_state, + machine); + + source_name = g_strdup_printf ("[%s] ssm %p jump to next state %d", + fp_device_get_device_id (machine->dev), + machine, machine->cur_state + 1); + g_source_set_name (machine->timeout, source_name); +} + /** * fpi_ssm_jump_to_state: * @machine: an #FpiSsm state machine @@ -318,10 +381,65 @@ fpi_ssm_jump_to_state (FpiSsm *machine, int state) { BUG_ON (machine->completed); BUG_ON (state < 0 || state >= machine->nr_states); + BUG_ON (machine->timeout != NULL); + + g_clear_pointer (&machine->timeout, g_source_destroy); machine->cur_state = state; __ssm_call_handler (machine); } +typedef struct +{ + FpiSsm *machine; + int next_state; +} FpiSsmJumpToStateDelayedData; + +static void +on_device_timeout_jump_to_state (FpDevice *dev, + gpointer user_data) +{ + FpiSsmJumpToStateDelayedData *data = user_data; + + data->machine->timeout = NULL; + fpi_ssm_jump_to_state (data->machine, data->next_state); +} + +/** + * fpi_ssm_jump_to_state_delayed: + * @machine: an #FpiSsm state machine + * @state: the state to jump to + * @delay: the milliseconds to wait before switching to @state state + * + * Jump to the @state state with a delay of @delay milliseconds, bypassing + * intermediary states. + */ +void +fpi_ssm_jump_to_state_delayed (FpiSsm *machine, + int state, + int delay) +{ + FpiSsmJumpToStateDelayedData *data; + g_autofree char *source_name = NULL; + + g_return_if_fail (machine != NULL); + BUG_ON (machine->completed); + BUG_ON (machine->timeout != NULL); + + data = g_new0 (FpiSsmJumpToStateDelayedData, 1); + data->machine = machine; + data->next_state = state; + + g_clear_pointer (&machine->timeout, g_source_destroy); + machine->timeout = fpi_device_add_timeout_full (machine->dev, delay, + on_device_timeout_jump_to_state, + data, g_free); + + source_name = g_strdup_printf ("[%s] ssm %p jump to state %d", + fp_device_get_device_id (machine->dev), + machine, state); + g_source_set_name (machine->timeout, source_name); +} + /** * fpi_ssm_get_cur_state: * @machine: an #FpiSsm state machine diff --git a/libfprint/fpi-ssm.h b/libfprint/fpi-ssm.h index 57e7d10f..05e6cf00 100644 --- a/libfprint/fpi-ssm.h +++ b/libfprint/fpi-ssm.h @@ -73,6 +73,12 @@ void fpi_ssm_start_subsm (FpiSsm *parent, void fpi_ssm_next_state (FpiSsm *machine); void fpi_ssm_jump_to_state (FpiSsm *machine, int state); +void fpi_ssm_next_state_delayed (FpiSsm *machine, + int delay); +void fpi_ssm_jump_to_state_delayed (FpiSsm *machine, + int state, + int delay); +void fpi_ssm_cancel_delayed_state_change (FpiSsm *machine); void fpi_ssm_mark_completed (FpiSsm *machine); void fpi_ssm_mark_failed (FpiSsm *machine, GError *error); From 1ba95db379ff1225b2e3cf7955c353072f168a47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 22 Nov 2019 18:22:50 +0100 Subject: [PATCH 061/237] drivers: Use fpi_ssm_next_state_delayed instead of custom callbacks As per this fpi_ssm_next_state_timeout_cb can be removed --- libfprint/drivers/elan.c | 7 +------ libfprint/drivers/vfs0050.c | 3 +-- libfprint/drivers/vfs101.c | 24 +++++++----------------- libfprint/drivers/vfs301.c | 15 ++------------- libfprint/drivers/vfs5011.c | 5 +---- libfprint/fpi-ssm.c | 30 ++++-------------------------- libfprint/fpi-ssm.h | 2 -- 7 files changed, 16 insertions(+), 70 deletions(-) diff --git a/libfprint/drivers/elan.c b/libfprint/drivers/elan.c index f9e87638..f6229880 100644 --- a/libfprint/drivers/elan.c +++ b/libfprint/drivers/elan.c @@ -749,15 +749,10 @@ calibrate_run_state (FpiSsm *ssm, FpDevice *dev) } else { - GSource *timeout; - if (self->calib_status == 0x00 && self->last_read[0] == 0x01) self->calib_status = 0x01; - timeout = fpi_device_add_timeout (dev, 50, - fpi_ssm_next_state_timeout_cb, - ssm, NULL); - g_source_set_name (timeout, "calibrate_run_state"); + fpi_ssm_next_state_delayed (ssm, 50); } break; diff --git a/libfprint/drivers/vfs0050.c b/libfprint/drivers/vfs0050.c index 63776390..af70db5d 100644 --- a/libfprint/drivers/vfs0050.c +++ b/libfprint/drivers/vfs0050.c @@ -618,8 +618,7 @@ activate_ssm (FpiSsm *ssm, FpDevice *dev) clear_data (self); /* Wait for probable vdev->active changing */ - fpi_device_add_timeout (dev, VFS_SSM_TIMEOUT, - fpi_ssm_next_state_timeout_cb, ssm, NULL); + fpi_ssm_next_state_delayed (ssm, VFS_SSM_TIMEOUT); break; case SSM_NEXT_RECEIVE: diff --git a/libfprint/drivers/vfs101.c b/libfprint/drivers/vfs101.c index 0df9b73c..7020726a 100644 --- a/libfprint/drivers/vfs101.c +++ b/libfprint/drivers/vfs101.c @@ -369,16 +369,6 @@ async_load (FpiSsm *ssm, async_load_cb, NULL); } -/* Submit asynchronous sleep */ -static void -async_sleep (unsigned int msec, - FpiSsm *ssm, - FpImageDevice *dev) -{ - fpi_device_add_timeout (FP_DEVICE (dev), msec, - fpi_ssm_next_state_timeout_cb, ssm, NULL); -} - /* Swap ssm states */ enum { M_SWAP_SEND, @@ -795,7 +785,7 @@ m_loop_state (FpiSsm *ssm, FpDevice *_dev) case M_LOOP_0_SLEEP: /* Wait fingerprint scanning */ - async_sleep (50, ssm, dev); + fpi_ssm_next_state_delayed (ssm, 50); break; case M_LOOP_0_GET_STATE: @@ -838,7 +828,7 @@ m_loop_state (FpiSsm *ssm, FpDevice *_dev) img_extract (ssm, dev); /* Wait handling image */ - async_sleep (10, ssm, dev); + fpi_ssm_next_state_delayed (ssm, 10); break; case M_LOOP_0_CHECK_ACTION: @@ -861,7 +851,7 @@ m_loop_state (FpiSsm *ssm, FpDevice *_dev) if (vfs_finger_state (self) == VFS_FINGER_PRESENT) { fpi_image_device_report_finger_status (dev, TRUE); - async_sleep (250, ssm, dev); + fpi_ssm_next_state_delayed (ssm, 250); } else { @@ -891,7 +881,7 @@ m_loop_state (FpiSsm *ssm, FpDevice *_dev) case M_LOOP_1_SLEEP: /* Wait fingerprint scanning */ - async_sleep (10, ssm, dev); + fpi_ssm_next_state_delayed (ssm, 10); break; case M_LOOP_2_ABORT_PRINT: @@ -927,7 +917,7 @@ m_loop_state (FpiSsm *ssm, FpDevice *_dev) { /* Wait aborting */ self->counter++; - async_sleep (100, ssm, dev); + fpi_ssm_next_state_delayed (ssm, 100); } else { @@ -1065,7 +1055,7 @@ m_init_state (FpiSsm *ssm, FpDevice *_dev) { /* Wait aborting */ self->counter++; - async_sleep (100, ssm, dev); + fpi_ssm_next_state_delayed (ssm, 100); } else { @@ -1094,7 +1084,7 @@ m_init_state (FpiSsm *ssm, FpDevice *_dev) { /* Wait removing finger */ self->counter++; - async_sleep (250, ssm, dev); + fpi_ssm_next_state_delayed (ssm, 250); } else { diff --git a/libfprint/drivers/vfs301.c b/libfprint/drivers/vfs301.c index 1d0f0662..38708799 100644 --- a/libfprint/drivers/vfs301.c +++ b/libfprint/drivers/vfs301.c @@ -28,17 +28,6 @@ G_DEFINE_TYPE (FpDeviceVfs301, fpi_device_vfs301, FP_TYPE_IMAGE_DEVICE) /************************** GENERIC STUFF *************************************/ -/* Submit asynchronous sleep */ -static void -async_sleep (unsigned int msec, - FpiSsm *ssm, - FpImageDevice *dev) -{ - /* Add timeout */ - fpi_device_add_timeout (FP_DEVICE (dev), msec, - fpi_ssm_next_state_timeout_cb, ssm, NULL); -} - static int submit_image (FpiSsm *ssm, FpImageDevice *dev) @@ -108,7 +97,7 @@ m_loop_state (FpiSsm *ssm, FpDevice *_dev) case M_WAIT_PRINT: /* Wait fingerprint scanning */ - async_sleep (200, ssm, dev); + fpi_ssm_next_state_delayed (ssm, 200); break; case M_CHECK_PRINT: @@ -126,7 +115,7 @@ m_loop_state (FpiSsm *ssm, FpDevice *_dev) case M_READ_PRINT_WAIT: /* Wait fingerprint scanning */ - async_sleep (200, ssm, dev); + fpi_ssm_next_state_delayed (ssm, 200); break; case M_READ_PRINT_POLL: diff --git a/libfprint/drivers/vfs5011.c b/libfprint/drivers/vfs5011.c index 4fac03ce..b769e319 100644 --- a/libfprint/drivers/vfs5011.c +++ b/libfprint/drivers/vfs5011.c @@ -704,10 +704,7 @@ activate_loop (FpiSsm *ssm, FpDevice *_dev) break; case DEV_ACTIVATE_DATA_COMPLETE: - fpi_device_add_timeout (_dev, 1, - fpi_ssm_next_state_timeout_cb, - ssm, NULL); - + fpi_ssm_next_state_delayed (ssm, 1); break; case DEV_ACTIVATE_PREPARE_NEXT_CAPTURE: diff --git a/libfprint/fpi-ssm.c b/libfprint/fpi-ssm.c index 38186d21..931c8fe3 100644 --- a/libfprint/fpi-ssm.c +++ b/libfprint/fpi-ssm.c @@ -359,7 +359,7 @@ fpi_ssm_next_state_delayed (FpiSsm *machine, g_clear_pointer (&machine->timeout, g_source_destroy); machine->timeout = fpi_device_add_timeout (machine->dev, delay, on_device_timeout_next_state, - machine); + machine, NULL); source_name = g_strdup_printf ("[%s] ssm %p jump to next state %d", fp_device_get_device_id (machine->dev), @@ -430,9 +430,9 @@ fpi_ssm_jump_to_state_delayed (FpiSsm *machine, data->next_state = state; g_clear_pointer (&machine->timeout, g_source_destroy); - machine->timeout = fpi_device_add_timeout_full (machine->dev, delay, - on_device_timeout_jump_to_state, - data, g_free); + machine->timeout = fpi_device_add_timeout (machine->dev, delay, + on_device_timeout_jump_to_state, + data, g_free); source_name = g_strdup_printf ("[%s] ssm %p jump to state %d", fp_device_get_device_id (machine->dev), @@ -486,28 +486,6 @@ fpi_ssm_dup_error (FpiSsm *machine) return NULL; } -/** - * fpi_ssm_next_state_timeout_cb: - * @dev: a struct #fp_dev - * @data: a pointer to an #FpiSsm state machine - * - * Same as fpi_ssm_next_state(), but to be used as a callback - * for an fpi_device_add_timeout() callback, when the state - * change needs to happen after a timeout. - * - * Make sure to pass the #FpiSsm as the `ssm_data` argument - * for that fpi_device_add_timeout() call. - */ -void -fpi_ssm_next_state_timeout_cb (FpDevice *dev, - void *data) -{ - g_return_if_fail (dev != NULL); - g_return_if_fail (data != NULL); - - fpi_ssm_next_state (data); -} - /** * fpi_ssm_usb_transfer_cb: * @transfer: a #FpiUsbTransfer diff --git a/libfprint/fpi-ssm.h b/libfprint/fpi-ssm.h index 05e6cf00..b426fff8 100644 --- a/libfprint/fpi-ssm.h +++ b/libfprint/fpi-ssm.h @@ -93,8 +93,6 @@ int fpi_ssm_get_cur_state (FpiSsm *machine); /* Callbacks to be used by the driver instead of implementing their own * logic. */ -void fpi_ssm_next_state_timeout_cb (FpDevice *dev, - void *data); void fpi_ssm_usb_transfer_cb (FpiUsbTransfer *transfer, FpDevice *device, gpointer unused_data, From e12978f4022e02a6ef9f8a155d177f318e72b7bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 22 Nov 2019 18:25:59 +0100 Subject: [PATCH 062/237] fpi-ssm: Clarify the ownership of error in fpi_ssm_mark_failed --- libfprint/fpi-ssm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libfprint/fpi-ssm.c b/libfprint/fpi-ssm.c index 931c8fe3..e2cb48a8 100644 --- a/libfprint/fpi-ssm.c +++ b/libfprint/fpi-ssm.c @@ -272,7 +272,7 @@ fpi_ssm_mark_completed (FpiSsm *machine) /** * fpi_ssm_mark_failed: * @machine: an #FpiSsm state machine - * @error: a #GError + * @error: (transfer full): a #GError * * Mark a state machine as failed with @error as the error code, completing it. */ @@ -288,7 +288,7 @@ fpi_ssm_mark_failed (FpiSsm *machine, GError *error) } fp_dbg ("SSM failed in state %d with error: %s", machine->cur_state, error->message); - machine->error = error; + machine->error = g_steal_pointer (&error); fpi_ssm_mark_completed (machine); } From bac6382f6700434b2e6a0695439d283ffd541dd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 22 Nov 2019 18:39:02 +0100 Subject: [PATCH 063/237] drivers: Use SSM delayed actions when possible --- libfprint/drivers/uru4000.c | 72 ++++++++++++++----------------------- libfprint/drivers/vfs0050.c | 13 +------ libfprint/fpi-ssm.c | 3 +- 3 files changed, 29 insertions(+), 59 deletions(-) diff --git a/libfprint/drivers/uru4000.c b/libfprint/drivers/uru4000.c index 1deadd3c..e15f1ca2 100644 --- a/libfprint/drivers/uru4000.c +++ b/libfprint/drivers/uru4000.c @@ -829,26 +829,6 @@ enum rebootpwr_states { REBOOTPWR_NUM_STATES, }; -static void -rebootpwr_pause_cb (FpDevice *dev, - void *data) -{ - FpiSsm *ssm = data; - FpiDeviceUru4000 *self = FPI_DEVICE_URU4000 (dev); - - if (!--self->rebootpwr_ctr) - { - fp_err ("could not reboot device power"); - fpi_ssm_mark_failed (ssm, - fpi_device_error_new_msg (FP_DEVICE_ERROR, - "Could not reboot device")); - } - else - { - fpi_ssm_jump_to_state (ssm, REBOOTPWR_GET_HWSTAT); - } -} - static void rebootpwr_run_state (FpiSsm *ssm, FpDevice *_dev) { @@ -875,7 +855,17 @@ rebootpwr_run_state (FpiSsm *ssm, FpDevice *_dev) break; case REBOOTPWR_PAUSE: - fpi_device_add_timeout (_dev, 10, rebootpwr_pause_cb, ssm, NULL); + if (!--self->rebootpwr_ctr) + { + fp_err ("could not reboot device power"); + fpi_ssm_mark_failed (ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR, + "Could not reboot device")); + } + else + { + fpi_ssm_jump_to_state_delayed (ssm, 10, REBOOTPWR_GET_HWSTAT); + } break; } } @@ -916,30 +906,6 @@ enum powerup_states { POWERUP_NUM_STATES, }; -static void -powerup_pause_cb (FpDevice *dev, - void *data) -{ - FpiSsm *ssm = data; - FpiDeviceUru4000 *self = FPI_DEVICE_URU4000 (dev); - - if (!--self->powerup_ctr) - { - fp_err ("could not power device up"); - fpi_ssm_mark_failed (ssm, - fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, - "could not power device up")); - } - else if (!self->profile->auth_cr) - { - fpi_ssm_jump_to_state (ssm, POWERUP_SET_HWSTAT); - } - else - { - fpi_ssm_next_state (ssm); - } -} - static void powerup_run_state (FpiSsm *ssm, FpDevice *_dev) { @@ -971,7 +937,21 @@ powerup_run_state (FpiSsm *ssm, FpDevice *_dev) break; case POWERUP_PAUSE: - fpi_device_add_timeout (_dev, 10, powerup_pause_cb, ssm, NULL); + if (!--self->powerup_ctr) + { + fp_err ("could not power device up"); + fpi_ssm_mark_failed (ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, + "could not power device up")); + } + else if (!self->profile->auth_cr) + { + fpi_ssm_jump_to_state_delayed (ssm, POWERUP_SET_HWSTAT, 10); + } + else + { + fpi_ssm_next_state_delayed (ssm, 10); + } break; case POWERUP_CHALLENGE_RESPONSE: diff --git a/libfprint/drivers/vfs0050.c b/libfprint/drivers/vfs0050.c index af70db5d..22e9ae95 100644 --- a/libfprint/drivers/vfs0050.c +++ b/libfprint/drivers/vfs0050.c @@ -479,16 +479,6 @@ receive_callback (FpiUsbTransfer *transfer, FpDevice *device, } } -/* SSM stub to prepare device to another scan after orange light was on */ -static void -another_scan (FpDevice *dev, - void *data) -{ - FpiSsm *ssm = data; - - fpi_ssm_jump_to_state (ssm, SSM_TURN_ON); -} - /* Main SSM loop */ static void activate_ssm (FpiSsm *ssm, FpDevice *dev) @@ -637,8 +627,7 @@ activate_ssm (FpiSsm *ssm, FpDevice *dev) case SSM_WAIT_ANOTHER_SCAN: /* Orange light is on now */ - fpi_device_add_timeout (dev, VFS_SSM_ORANGE_TIMEOUT, - another_scan, ssm, NULL); + fpi_ssm_jump_to_state_delayed (ssm, SSM_TURN_ON, VFS_SSM_ORANGE_TIMEOUT); break; default: diff --git a/libfprint/fpi-ssm.c b/libfprint/fpi-ssm.c index e2cb48a8..19712ef0 100644 --- a/libfprint/fpi-ssm.c +++ b/libfprint/fpi-ssm.c @@ -168,6 +168,8 @@ fpi_ssm_free (FpiSsm *machine) if (!machine) return; + BUG_ON (machine->timeout != NULL); + if (machine->ssm_data_destroy) g_clear_pointer (&machine->ssm_data, machine->ssm_data_destroy); g_clear_pointer (&machine->error, g_error_free); @@ -250,7 +252,6 @@ void fpi_ssm_mark_completed (FpiSsm *machine) { BUG_ON (machine->completed); - BUG_ON (machine->timeout); BUG_ON (machine->timeout != NULL); g_clear_pointer (&machine->timeout, g_source_destroy); From ff67bf5a16cdbc9d738b1f3072fca7fd93f25fa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 28 Nov 2019 19:24:55 +0100 Subject: [PATCH 064/237] fpi-ssm: Make delayed actions cancellable Add a GCancellable parameter to fpi_ssm_nex_state_delayed and fpi_ssm_jump_to_state_delayed() so that it's possible to cancel an action from the caller and in case the driver wants to cancel a delayed operation when a device action has been cancelled. --- libfprint/drivers/elan.c | 2 +- libfprint/drivers/uru4000.c | 6 +- libfprint/drivers/vfs0050.c | 5 +- libfprint/drivers/vfs101.c | 14 ++-- libfprint/drivers/vfs301.c | 4 +- libfprint/drivers/vfs5011.c | 2 +- libfprint/fpi-ssm.c | 123 ++++++++++++++++++++++++++++++------ libfprint/fpi-ssm.h | 12 ++-- 8 files changed, 127 insertions(+), 41 deletions(-) diff --git a/libfprint/drivers/elan.c b/libfprint/drivers/elan.c index f6229880..7c7fb26e 100644 --- a/libfprint/drivers/elan.c +++ b/libfprint/drivers/elan.c @@ -752,7 +752,7 @@ calibrate_run_state (FpiSsm *ssm, FpDevice *dev) if (self->calib_status == 0x00 && self->last_read[0] == 0x01) self->calib_status = 0x01; - fpi_ssm_next_state_delayed (ssm, 50); + fpi_ssm_next_state_delayed (ssm, 50, NULL); } break; diff --git a/libfprint/drivers/uru4000.c b/libfprint/drivers/uru4000.c index e15f1ca2..122544da 100644 --- a/libfprint/drivers/uru4000.c +++ b/libfprint/drivers/uru4000.c @@ -864,7 +864,7 @@ rebootpwr_run_state (FpiSsm *ssm, FpDevice *_dev) } else { - fpi_ssm_jump_to_state_delayed (ssm, 10, REBOOTPWR_GET_HWSTAT); + fpi_ssm_jump_to_state_delayed (ssm, 10, REBOOTPWR_GET_HWSTAT, NULL); } break; } @@ -946,11 +946,11 @@ powerup_run_state (FpiSsm *ssm, FpDevice *_dev) } else if (!self->profile->auth_cr) { - fpi_ssm_jump_to_state_delayed (ssm, POWERUP_SET_HWSTAT, 10); + fpi_ssm_jump_to_state_delayed (ssm, POWERUP_SET_HWSTAT, 10, NULL); } else { - fpi_ssm_next_state_delayed (ssm, 10); + fpi_ssm_next_state_delayed (ssm, 10, NULL); } break; diff --git a/libfprint/drivers/vfs0050.c b/libfprint/drivers/vfs0050.c index 22e9ae95..9b99dc3d 100644 --- a/libfprint/drivers/vfs0050.c +++ b/libfprint/drivers/vfs0050.c @@ -608,7 +608,7 @@ activate_ssm (FpiSsm *ssm, FpDevice *dev) clear_data (self); /* Wait for probable vdev->active changing */ - fpi_ssm_next_state_delayed (ssm, VFS_SSM_TIMEOUT); + fpi_ssm_next_state_delayed (ssm, VFS_SSM_TIMEOUT, NULL); break; case SSM_NEXT_RECEIVE: @@ -627,7 +627,8 @@ activate_ssm (FpiSsm *ssm, FpDevice *dev) case SSM_WAIT_ANOTHER_SCAN: /* Orange light is on now */ - fpi_ssm_jump_to_state_delayed (ssm, SSM_TURN_ON, VFS_SSM_ORANGE_TIMEOUT); + fpi_ssm_jump_to_state_delayed (ssm, SSM_TURN_ON, VFS_SSM_ORANGE_TIMEOUT, + NULL); break; default: diff --git a/libfprint/drivers/vfs101.c b/libfprint/drivers/vfs101.c index 7020726a..ccce7db2 100644 --- a/libfprint/drivers/vfs101.c +++ b/libfprint/drivers/vfs101.c @@ -785,7 +785,7 @@ m_loop_state (FpiSsm *ssm, FpDevice *_dev) case M_LOOP_0_SLEEP: /* Wait fingerprint scanning */ - fpi_ssm_next_state_delayed (ssm, 50); + fpi_ssm_next_state_delayed (ssm, 50, NULL); break; case M_LOOP_0_GET_STATE: @@ -828,7 +828,7 @@ m_loop_state (FpiSsm *ssm, FpDevice *_dev) img_extract (ssm, dev); /* Wait handling image */ - fpi_ssm_next_state_delayed (ssm, 10); + fpi_ssm_next_state_delayed (ssm, 10, NULL); break; case M_LOOP_0_CHECK_ACTION: @@ -851,7 +851,7 @@ m_loop_state (FpiSsm *ssm, FpDevice *_dev) if (vfs_finger_state (self) == VFS_FINGER_PRESENT) { fpi_image_device_report_finger_status (dev, TRUE); - fpi_ssm_next_state_delayed (ssm, 250); + fpi_ssm_next_state_delayed (ssm, 250, NULL); } else { @@ -881,7 +881,7 @@ m_loop_state (FpiSsm *ssm, FpDevice *_dev) case M_LOOP_1_SLEEP: /* Wait fingerprint scanning */ - fpi_ssm_next_state_delayed (ssm, 10); + fpi_ssm_next_state_delayed (ssm, 10, NULL); break; case M_LOOP_2_ABORT_PRINT: @@ -917,7 +917,7 @@ m_loop_state (FpiSsm *ssm, FpDevice *_dev) { /* Wait aborting */ self->counter++; - fpi_ssm_next_state_delayed (ssm, 100); + fpi_ssm_next_state_delayed (ssm, 100, NULL); } else { @@ -1055,7 +1055,7 @@ m_init_state (FpiSsm *ssm, FpDevice *_dev) { /* Wait aborting */ self->counter++; - fpi_ssm_next_state_delayed (ssm, 100); + fpi_ssm_next_state_delayed (ssm, 100, NULL); } else { @@ -1084,7 +1084,7 @@ m_init_state (FpiSsm *ssm, FpDevice *_dev) { /* Wait removing finger */ self->counter++; - fpi_ssm_next_state_delayed (ssm, 250); + fpi_ssm_next_state_delayed (ssm, 250, NULL); } else { diff --git a/libfprint/drivers/vfs301.c b/libfprint/drivers/vfs301.c index 38708799..f912a363 100644 --- a/libfprint/drivers/vfs301.c +++ b/libfprint/drivers/vfs301.c @@ -97,7 +97,7 @@ m_loop_state (FpiSsm *ssm, FpDevice *_dev) case M_WAIT_PRINT: /* Wait fingerprint scanning */ - fpi_ssm_next_state_delayed (ssm, 200); + fpi_ssm_next_state_delayed (ssm, 200, NULL); break; case M_CHECK_PRINT: @@ -115,7 +115,7 @@ m_loop_state (FpiSsm *ssm, FpDevice *_dev) case M_READ_PRINT_WAIT: /* Wait fingerprint scanning */ - fpi_ssm_next_state_delayed (ssm, 200); + fpi_ssm_next_state_delayed (ssm, 200, NULL); break; case M_READ_PRINT_POLL: diff --git a/libfprint/drivers/vfs5011.c b/libfprint/drivers/vfs5011.c index b769e319..ef318f2e 100644 --- a/libfprint/drivers/vfs5011.c +++ b/libfprint/drivers/vfs5011.c @@ -704,7 +704,7 @@ activate_loop (FpiSsm *ssm, FpDevice *_dev) break; case DEV_ACTIVATE_DATA_COMPLETE: - fpi_ssm_next_state_delayed (ssm, 1); + fpi_ssm_next_state_delayed (ssm, 1, NULL); break; case DEV_ACTIVATE_PREPARE_NEXT_CAPTURE: diff --git a/libfprint/fpi-ssm.c b/libfprint/fpi-ssm.c index 19712ef0..0f54b1da 100644 --- a/libfprint/fpi-ssm.c +++ b/libfprint/fpi-ssm.c @@ -88,6 +88,8 @@ struct _FpiSsm int cur_state; gboolean completed; GSource *timeout; + GCancellable *cancellable; + gulong cancellable_id; GError *error; FpiSsmCompletedCallback callback; FpiSsmHandlerCallback handler; @@ -155,6 +157,81 @@ fpi_ssm_get_data (FpiSsm *machine) return machine->ssm_data; } +static void +fpi_ssm_clear_delayed_action (FpiSsm *machine) +{ + if (machine->cancellable_id) + { + g_cancellable_disconnect (machine->cancellable, machine->cancellable_id); + machine->cancellable_id = 0; + } + + g_clear_object (&machine->cancellable); + g_clear_pointer (&machine->timeout, g_source_destroy); +} + +typedef struct _CancelledActionIdleData +{ + gulong cancellable_id; + GCancellable *cancellable; +} CancelledActionIdleData; + +static gboolean +on_delayed_action_cancelled_idle (gpointer user_data) +{ + CancelledActionIdleData *data = user_data; + + g_cancellable_disconnect (data->cancellable, data->cancellable_id); + g_object_unref (data->cancellable); + g_free (data); + + return G_SOURCE_REMOVE; +} + +static void +on_delayed_action_cancelled (GCancellable *cancellable, + FpiSsm *machine) +{ + CancelledActionIdleData *data; + + g_clear_pointer (&machine->timeout, g_source_destroy); + + data = g_new0 (CancelledActionIdleData, 1); + data->cancellable = g_steal_pointer (&machine->cancellable); + data->cancellable_id = machine->cancellable_id; + machine->cancellable_id = 0; + + g_idle_add_full (G_PRIORITY_HIGH_IDLE, on_delayed_action_cancelled_idle, + data, NULL); +} + +static void +fpi_ssm_set_delayed_action_timeout (FpiSsm *machine, + int delay, + FpTimeoutFunc callback, + GCancellable *cancellable, + gpointer user_data, + GDestroyNotify destroy_func) +{ + BUG_ON (machine->completed); + BUG_ON (machine->timeout != NULL); + + fpi_ssm_clear_delayed_action (machine); + + if (cancellable != NULL) + { + g_set_object (&machine->cancellable, cancellable); + + machine->cancellable_id = + g_cancellable_connect (machine->cancellable, + G_CALLBACK (on_delayed_action_cancelled), + machine, NULL); + } + + machine->timeout = fpi_device_add_timeout (machine->dev, delay, callback, + user_data, destroy_func); +} + /** * fpi_ssm_free: * @machine: an #FpiSsm state machine @@ -173,7 +250,7 @@ fpi_ssm_free (FpiSsm *machine) if (machine->ssm_data_destroy) g_clear_pointer (&machine->ssm_data, machine->ssm_data_destroy); g_clear_pointer (&machine->error, g_error_free); - g_clear_pointer (&machine->timeout, g_source_destroy); + fpi_ssm_clear_delayed_action (machine); g_free (machine); } @@ -254,7 +331,8 @@ fpi_ssm_mark_completed (FpiSsm *machine) BUG_ON (machine->completed); BUG_ON (machine->timeout != NULL); - g_clear_pointer (&machine->timeout, g_source_destroy); + fpi_ssm_clear_delayed_action (machine); + machine->completed = TRUE; if (machine->error) @@ -309,7 +387,7 @@ fpi_ssm_next_state (FpiSsm *machine) BUG_ON (machine->completed); BUG_ON (machine->timeout != NULL); - g_clear_pointer (&machine->timeout, g_source_destroy); + fpi_ssm_clear_delayed_action (machine); machine->cur_state++; if (machine->cur_state == machine->nr_states) @@ -325,7 +403,7 @@ fpi_ssm_cancel_delayed_state_change (FpiSsm *machine) BUG_ON (machine->completed); BUG_ON (machine->timeout == NULL); - g_clear_pointer (&machine->timeout, g_source_destroy); + fpi_ssm_clear_delayed_action (machine); } static void @@ -342,25 +420,26 @@ on_device_timeout_next_state (FpDevice *dev, * fpi_ssm_next_state_delayed: * @machine: an #FpiSsm state machine * @delay: the milliseconds to wait before switching to the next state + * @cancellable: (nullable): a #GCancellable to cancel the delayed operation * * Iterate to next state of a state machine with a delay of @delay ms. If the * current state is the last state, then the state machine will be marked as * completed, as if calling fpi_ssm_mark_completed(). + * Passing a valid #GCancellable will cause the action to be cancelled when + * @cancellable is. */ void -fpi_ssm_next_state_delayed (FpiSsm *machine, - int delay) +fpi_ssm_next_state_delayed (FpiSsm *machine, + int delay, + GCancellable *cancellable) { g_autofree char *source_name = NULL; g_return_if_fail (machine != NULL); - BUG_ON (machine->completed); - BUG_ON (machine->timeout != NULL); - g_clear_pointer (&machine->timeout, g_source_destroy); - machine->timeout = fpi_device_add_timeout (machine->dev, delay, - on_device_timeout_next_state, - machine, NULL); + fpi_ssm_set_delayed_action_timeout (machine, delay, + on_device_timeout_next_state, cancellable, + machine, NULL); source_name = g_strdup_printf ("[%s] ssm %p jump to next state %d", fp_device_get_device_id (machine->dev), @@ -384,7 +463,8 @@ fpi_ssm_jump_to_state (FpiSsm *machine, int state) BUG_ON (state < 0 || state >= machine->nr_states); BUG_ON (machine->timeout != NULL); - g_clear_pointer (&machine->timeout, g_source_destroy); + fpi_ssm_clear_delayed_action (machine); + machine->cur_state = state; __ssm_call_handler (machine); } @@ -410,14 +490,18 @@ on_device_timeout_jump_to_state (FpDevice *dev, * @machine: an #FpiSsm state machine * @state: the state to jump to * @delay: the milliseconds to wait before switching to @state state + * @cancellable: (nullable): a #GCancellable to cancel the delayed operation * * Jump to the @state state with a delay of @delay milliseconds, bypassing * intermediary states. + * Passing a valid #GCancellable will cause the action to be cancelled when + * @cancellable is. */ void -fpi_ssm_jump_to_state_delayed (FpiSsm *machine, - int state, - int delay) +fpi_ssm_jump_to_state_delayed (FpiSsm *machine, + int state, + int delay, + GCancellable *cancellable) { FpiSsmJumpToStateDelayedData *data; g_autofree char *source_name = NULL; @@ -430,10 +514,9 @@ fpi_ssm_jump_to_state_delayed (FpiSsm *machine, data->machine = machine; data->next_state = state; - g_clear_pointer (&machine->timeout, g_source_destroy); - machine->timeout = fpi_device_add_timeout (machine->dev, delay, - on_device_timeout_jump_to_state, - data, g_free); + fpi_ssm_set_delayed_action_timeout (machine, delay, + on_device_timeout_jump_to_state, + cancellable, data, g_free); source_name = g_strdup_printf ("[%s] ssm %p jump to state %d", fp_device_get_device_id (machine->dev), diff --git a/libfprint/fpi-ssm.h b/libfprint/fpi-ssm.h index b426fff8..8dff27d1 100644 --- a/libfprint/fpi-ssm.h +++ b/libfprint/fpi-ssm.h @@ -73,11 +73,13 @@ void fpi_ssm_start_subsm (FpiSsm *parent, void fpi_ssm_next_state (FpiSsm *machine); void fpi_ssm_jump_to_state (FpiSsm *machine, int state); -void fpi_ssm_next_state_delayed (FpiSsm *machine, - int delay); -void fpi_ssm_jump_to_state_delayed (FpiSsm *machine, - int state, - int delay); +void fpi_ssm_next_state_delayed (FpiSsm *machine, + int delay, + GCancellable *cancellable); +void fpi_ssm_jump_to_state_delayed (FpiSsm *machine, + int state, + int delay, + GCancellable *cancellable); void fpi_ssm_cancel_delayed_state_change (FpiSsm *machine); void fpi_ssm_mark_completed (FpiSsm *machine); void fpi_ssm_mark_failed (FpiSsm *machine, From b0effae9907e095afb463c3e88cd62049f762166 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 28 Nov 2019 20:15:21 +0100 Subject: [PATCH 065/237] fpi-ssm: Bug on handler set to a NULL function We would crash otherwise, while this is quite obvious there was no code enforcing this. --- libfprint/fpi-ssm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libfprint/fpi-ssm.c b/libfprint/fpi-ssm.c index 0f54b1da..4498ce9d 100644 --- a/libfprint/fpi-ssm.c +++ b/libfprint/fpi-ssm.c @@ -114,6 +114,7 @@ fpi_ssm_new (FpDevice *dev, FpiSsm *machine; BUG_ON (nr_states < 1); + BUG_ON (handler == NULL); machine = g_new0 (FpiSsm, 1); machine->handler = handler; From 1e2f19ea3d0ad87e3093091c5a085a86ef0bcc6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 3 Dec 2019 17:22:20 +0100 Subject: [PATCH 066/237] fpi-ssm: Mark a fpi-ssm completed on delay --- libfprint/fpi-ssm.c | 40 ++++++++++++++++++++++++++++++++++++++++ libfprint/fpi-ssm.h | 3 +++ 2 files changed, 43 insertions(+) diff --git a/libfprint/fpi-ssm.c b/libfprint/fpi-ssm.c index 4498ce9d..09a31e3f 100644 --- a/libfprint/fpi-ssm.c +++ b/libfprint/fpi-ssm.c @@ -349,6 +349,46 @@ fpi_ssm_mark_completed (FpiSsm *machine) fpi_ssm_free (machine); } +static void +on_device_timeout_complete (FpDevice *dev, + gpointer user_data) +{ + FpiSsm *machine = user_data; + + machine->timeout = NULL; + fpi_ssm_mark_completed (machine); +} + +/** + * fpi_ssm_mark_completed_delayed: + * @machine: an #FpiSsm state machine + * @delay: the milliseconds to wait before switching to the next state + * @cancellable: (nullable): a #GCancellable to cancel the delayed operation + * + * Mark a ssm as completed successfully with a delay of @delay ms. + * The callback set when creating the state machine with fpi_ssm_new () will be + * called when the timeout is over. + * The request can be cancelled passing a #GCancellable as @cancellable. + */ +void +fpi_ssm_mark_completed_delayed (FpiSsm *machine, + int delay, + GCancellable *cancellable) +{ + g_autofree char *source_name = NULL; + + g_return_if_fail (machine != NULL); + + fpi_ssm_set_delayed_action_timeout (machine, delay, + on_device_timeout_complete, cancellable, + machine, NULL); + + source_name = g_strdup_printf ("[%s] ssm %p complete %d", + fp_device_get_device_id (machine->dev), + machine, machine->cur_state + 1); + g_source_set_name (machine->timeout, source_name); +} + /** * fpi_ssm_mark_failed: * @machine: an #FpiSsm state machine diff --git a/libfprint/fpi-ssm.h b/libfprint/fpi-ssm.h index 8dff27d1..3ef653e4 100644 --- a/libfprint/fpi-ssm.h +++ b/libfprint/fpi-ssm.h @@ -82,6 +82,9 @@ void fpi_ssm_jump_to_state_delayed (FpiSsm *machine, GCancellable *cancellable); void fpi_ssm_cancel_delayed_state_change (FpiSsm *machine); void fpi_ssm_mark_completed (FpiSsm *machine); +void fpi_ssm_mark_completed_delayed (FpiSsm *machine, + int delay, + GCancellable *cancellable); void fpi_ssm_mark_failed (FpiSsm *machine, GError *error); void fpi_ssm_set_data (FpiSsm *machine, From ae285e790d4b2bbc76591b90c8e737af9970ebbb Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Wed, 4 Dec 2019 13:21:11 +0100 Subject: [PATCH 067/237] tests: Fix image writing on big endian The code tried to only write the RGB bytes of FORMAT_RGB24, however, the in-memory layout is different on big-endian which would result in the wrong bytes being written. Fix this by simply also writing the byte we do not care about. --- tests/capture.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/capture.py b/tests/capture.py index 2ad9385c..a7b75831 100755 --- a/tests/capture.py +++ b/tests/capture.py @@ -36,10 +36,12 @@ c_buf = c_img.get_data() for x in range(width): for y in range(height): + # The upper byte is don't care, but the location depends on endianness, + # so just set all of them. c_buf[y * c_rowstride + x * 4 + 0] = buf[y * width + x] c_buf[y * c_rowstride + x * 4 + 1] = buf[y * width + x] c_buf[y * c_rowstride + x * 4 + 2] = buf[y * width + x] - # Byte 4 is don't care + c_buf[y * c_rowstride + x * 4 + 3] = buf[y * width + x] c_img.mark_dirty() c_img.write_to_png(sys.argv[1]) From 98cd1c2680950aee01be0c53241bfd8d1ec04f55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 4 Dec 2019 15:38:36 +0100 Subject: [PATCH 068/237] ci: Use a docker image for builds and tests Avoid repeating the machine updates and deps installation at every stage, just reuse the docker image Registered images are at: https://gitlab.freedesktop.org/libfprint/libfprint/container_registry --- .gitlab-ci.yml | 11 +---------- .gitlab-ci/Dockerfile | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 10 deletions(-) create mode 100644 .gitlab-ci/Dockerfile diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 122a02ed..74d4f465 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: fedora:rawhide +image: registry.freedesktop.org/libfprint/libfprint/master:v1 stages: - check-source - build @@ -6,15 +6,11 @@ stages: - flatpack variables: - DEPENDENCIES: libgusb-devel glib2-devel nss-devel pixman-devel systemd meson gtk-doc - gcc gcc-c++ glibc-devel libX11-devel libXv-devel gtk3-devel flatpak-builder - gobject-introspection-devel python3-cairo python3-gobject umockdev BUNDLE: "org.freedesktop.libfprint.Demo.flatpak" LAST_ABI_BREAK: "056ea541ddc97f5806cffbd99a12dc87e4da3546" .build_one_driver_template: &build_one_driver script: - - dnf update -y --nogpgcheck && dnf install -y --nogpgcheck $DEPENDENCIES # Build with a driver that doesn't need imaging, or nss - meson -Ddrivers=$driver . _build - ninja -C _build @@ -22,7 +18,6 @@ variables: .build_template: &build script: - - dnf update -y --nogpgcheck && dnf install -y --nogpgcheck $DEPENDENCIES # And build with everything - meson -Ddrivers=all . _build - ninja -C _build @@ -30,7 +25,6 @@ variables: .build_template: &check_abi script: - - dnf update -y --nogpgcheck && dnf install -y --nogpgcheck $DEPENDENCIES doxygen libabigail git - ./.ci/check-abi ${LAST_ABI_BREAK} $(git rev-parse HEAD) build: @@ -44,7 +38,6 @@ build: test: stage: test script: - - dnf update -y --nogpgcheck && dnf install -y --nogpgcheck $DEPENDENCIES - meson -Ddrivers=all . _build - ninja -C _build - meson test -C _build --verbose --no-stdsplit @@ -52,7 +45,6 @@ test: test_valgrind: stage: test script: - - dnf update -y --nogpgcheck && dnf install -y --nogpgcheck $DEPENDENCIES valgrind - meson -Ddrivers=all . _build - ninja -C _build - meson test -C _build --verbose --no-stdsplit --setup=valgrind @@ -60,7 +52,6 @@ test_valgrind: test_indent: stage: check-source script: - - dnf update -y --nogpgcheck && dnf install -y --nogpgcheck git uncrustify - scripts/uncrustify.sh --check .flatpak_script_template: &flatpak_script diff --git a/.gitlab-ci/Dockerfile b/.gitlab-ci/Dockerfile new file mode 100644 index 00000000..bf0eb36c --- /dev/null +++ b/.gitlab-ci/Dockerfile @@ -0,0 +1,36 @@ +# Rebuild and push with +# +# cd .gitlab-ci/ +# docker build --no-cache -t registry.freedesktop.org/libfprint/libfprint/master:v1 . +# docker push registry.freedesktop.org/libfprint/libfprint/master:v1 +# + +FROM fedora:rawhide + +RUN dnf -y update && dnf -y upgrade && \ + dnf -y install \ + doxygen \ + flatpak-builder \ + gcc \ + gcc-c++ \ + git \ + glib2-devel \ + glibc-devel \ + gobject-introspection-devel \ + gtk-doc \ + gtk3-devel \ + libabigail \ + libgusb-devel \ + libX11-devel \ + libXv-devel \ + meson \ + nss-devel \ + pixman-devel \ + python3-cairo \ + python3-gobject \ + systemd \ + umockdev \ + uncrustify \ + valgrind \ + && \ + dnf clean all From a29586f3983ee6fb702cc07b4b09dacd18647180 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 3 Dec 2019 19:34:58 +0100 Subject: [PATCH 069/237] fpi-usb: Use unsigned length for USB async transfers Properly follow function signature using a temporary gsize variable address to make the function use the same pointer type and avoid troubles at deferencing it, while use automatic-casting to switch to signed one if transfer succeeded. --- libfprint/fpi-usb-transfer.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libfprint/fpi-usb-transfer.c b/libfprint/fpi-usb-transfer.c index 64d706f1..08e75cb1 100644 --- a/libfprint/fpi-usb-transfer.c +++ b/libfprint/fpi-usb-transfer.c @@ -454,6 +454,7 @@ fpi_usb_transfer_submit_sync (FpiUsbTransfer *transfer, GError **error) { gboolean res; + gsize actual_length; g_return_val_if_fail (transfer, FALSE); @@ -469,7 +470,7 @@ fpi_usb_transfer_submit_sync (FpiUsbTransfer *transfer, transfer->endpoint, transfer->buffer, transfer->length, - &transfer->actual_length, + &actual_length, timeout_ms, NULL, error); @@ -485,7 +486,7 @@ fpi_usb_transfer_submit_sync (FpiUsbTransfer *transfer, transfer->idx, transfer->buffer, transfer->length, - &transfer->actual_length, + &actual_length, timeout_ms, NULL, error); @@ -496,7 +497,7 @@ fpi_usb_transfer_submit_sync (FpiUsbTransfer *transfer, transfer->endpoint, transfer->buffer, transfer->length, - &transfer->actual_length, + &actual_length, timeout_ms, NULL, error); @@ -511,6 +512,8 @@ fpi_usb_transfer_submit_sync (FpiUsbTransfer *transfer, if (!res) transfer->actual_length = -1; + else + transfer->actual_length = actual_length; return res; } From e7c7f368c97e4acbb6a227b470355e6319ec4152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 3 Dec 2019 19:36:26 +0100 Subject: [PATCH 070/237] drivers, examples: Don't use -Wno-pointer-sign and fix relative errors --- examples/storage.c | 2 +- libfprint/drivers/synaptics/synaptics.c | 4 ++-- libfprint/drivers/upekts.c | 2 +- libfprint/drivers/vfs0050.c | 2 +- meson.build | 1 - 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/examples/storage.c b/examples/storage.c index 932163e0..db358547 100644 --- a/examples/storage.c +++ b/examples/storage.c @@ -57,7 +57,7 @@ load_data (void) GVariantDict *res; GVariant *var; g_autofree gchar *contents = NULL; - gssize length = 0; + gsize length = 0; if (!g_file_get_contents (STORAGE_FILE, &contents, &length, NULL)) { diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index 1524c451..247b6581 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -407,7 +407,7 @@ static gboolean parse_print_data (GVariant *data, guint8 *finger, const guint8 **user_id, - gssize *user_id_len) + gsize *user_id_len) { g_autoptr(GVariant) user_id_var = NULL; @@ -506,7 +506,7 @@ list_msg_cb (FpiDeviceSynaptics *self, get_enroll_templates_resp->templates[n].user_id, get_enroll_templates_resp->templates[n].finger_id); - userid = get_enroll_templates_resp->templates[n].user_id; + userid = (gchar *) get_enroll_templates_resp->templates[n].user_id; print = fp_print_new (FP_DEVICE (self)); uid = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, diff --git a/libfprint/drivers/upekts.c b/libfprint/drivers/upekts.c index 05cd9c54..6ce8136f 100644 --- a/libfprint/drivers/upekts.c +++ b/libfprint/drivers/upekts.c @@ -375,7 +375,7 @@ read_msg_cb (FpiUsbTransfer *transfer, FpDevice *device, goto err; } - if (strncmp (udata->buffer, "Ciao", 4) != 0) + if (strncmp ((char *) udata->buffer, "Ciao", 4) != 0) { fp_err ("no Ciao for you!!"); error = fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, diff --git a/libfprint/drivers/vfs0050.c b/libfprint/drivers/vfs0050.c index 9b99dc3d..bb6851f3 100644 --- a/libfprint/drivers/vfs0050.c +++ b/libfprint/drivers/vfs0050.c @@ -399,7 +399,7 @@ interrupt_callback (FpiUsbTransfer *transfer, FpDevice *device, gpointer user_data, GError *error) { FpDeviceVfs0050 *self = FPI_DEVICE_VFS0050 (device); - char *interrupt = transfer->buffer; + unsigned char *interrupt = transfer->buffer; /* we expect a cancellation error when the device is deactivating * go into the SSM_CLEAR_EP2 state in that case. */ diff --git a/meson.build b/meson.build index ef352ba3..54761c40 100644 --- a/meson.build +++ b/meson.build @@ -31,7 +31,6 @@ common_cflags = cc.get_supported_arguments([ '-Wunused', '-Wstrict-prototypes', '-Werror-implicit-function-declaration', - '-Wno-pointer-sign', '-Wshadow', '-DGLIB_VERSION_MIN_REQUIRED=' + glib_version_def, '-DGLIB_VERSION_MAX_ALLOWED=' + glib_version_def, From 44af2173a8657f08d9bdd6caa89889f636f8585b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 3 Dec 2019 19:53:19 +0100 Subject: [PATCH 071/237] fp-print: Clear the data not the description when setting the property --- libfprint/fp-print.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfprint/fp-print.c b/libfprint/fp-print.c index ddf87479..e7b119aa 100644 --- a/libfprint/fp-print.c +++ b/libfprint/fp-print.c @@ -207,7 +207,7 @@ fp_print_set_property (GObject *object, break; case PROP_FPI_DATA: - g_clear_pointer (&self->description, g_variant_unref); + g_clear_pointer (&self->data, g_variant_unref); self->data = g_value_dup_variant (value); break; From 7e70344b4a7f6250797b2a6594f16171ba24e3d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 4 Dec 2019 12:32:14 +0100 Subject: [PATCH 072/237] meson: Use add_project_arguments for common cflags We were passing around the common cflags and setting them for each library or executable, but this is just a repetition given we can just use add_project_arguments for this. --- demo/meson.build | 5 +---- examples/meson.build | 6 ++---- libfprint/meson.build | 2 +- meson.build | 13 ++++++++----- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/demo/meson.build b/demo/meson.build index bf7a7ee1..279a43c5 100644 --- a/demo/meson.build +++ b/demo/meson.build @@ -13,10 +13,7 @@ executable('gtk-libfprint-test', include_directories: [ root_inc, ], - c_args: [ - common_cflags, - '-DPACKAGE_VERSION="' + meson.project_version() + '"' - ], + c_args: '-DPACKAGE_VERSION="' + meson.project_version() + '"', install: true, install_dir: bindir) diff --git a/examples/meson.build b/examples/meson.build index ff03ac65..eef8c3f6 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -6,8 +6,7 @@ foreach example: examples dependencies: [ libfprint_dep, glib_dep ], include_directories: [ root_inc, - ], - c_args: common_cflags) + ]) endforeach executable('cpp-test', @@ -15,5 +14,4 @@ executable('cpp-test', dependencies: libfprint_dep, include_directories: [ root_inc, - ], - c_args: common_cflags) + ]) diff --git a/libfprint/meson.build b/libfprint/meson.build index f77965ad..964744e0 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -188,7 +188,7 @@ libfprint = library('fprint', drivers_sources + nbis_sources + other_sources, soversion: soversion, version: libversion, - c_args: common_cflags + drivers_cflags, + c_args: drivers_cflags, include_directories: [ root_inc, include_directories('nbis/include'), diff --git a/meson.build b/meson.build index 54761c40..09abc1f5 100644 --- a/meson.build +++ b/meson.build @@ -10,9 +10,6 @@ project('libfprint', [ 'c', 'cpp' ], gnome = import('gnome') -add_project_arguments([ '-D_GNU_SOURCE' ], language: 'c') -add_project_arguments([ '-DG_LOG_DOMAIN="libfprint"' ], language: 'c') - libfprint_conf = configuration_data() cc = meson.get_compiler('c') @@ -23,8 +20,6 @@ glib_min_version = '2.56' glib_version_def = 'GLIB_VERSION_@0@_@1@'.format( glib_min_version.split('.')[0], glib_min_version.split('.')[1]) common_cflags = cc.get_supported_arguments([ - '-fgnu89-inline', - '-std=gnu99', '-Wall', '-Wtype-limits', '-Wundef', @@ -34,7 +29,15 @@ common_cflags = cc.get_supported_arguments([ '-Wshadow', '-DGLIB_VERSION_MIN_REQUIRED=' + glib_version_def, '-DGLIB_VERSION_MAX_ALLOWED=' + glib_version_def, + '-D_GNU_SOURCE', + '-DG_LOG_DOMAIN="libfprint"', ]) +c_cflags = cc.get_supported_arguments([ + '-fgnu89-inline', + '-std=gnu99', +]) +add_project_arguments(common_cflags + c_cflags, language: 'c') +add_project_arguments(common_cflags, language: 'cpp') # maintaining compatibility with the previous libtool versioning # current = binary - interface From e64c18f8ded9cf242a9028538a5ccf3e44204710 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 4 Dec 2019 12:32:28 +0100 Subject: [PATCH 073/237] cpp-test: Fix indentation --- examples/cpp-test.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/cpp-test.cpp b/examples/cpp-test.cpp index a0eb2ed7..99967e20 100644 --- a/examples/cpp-test.cpp +++ b/examples/cpp-test.cpp @@ -6,10 +6,10 @@ int main (int argc, char **argv) { - FpContext *ctx; + FpContext *ctx; - ctx = fp_context_new (); - g_object_unref (ctx); + ctx = fp_context_new (); + g_object_unref (ctx); - return 0; + return 0; } From e143f12e57aefbc118bb84db1302aac5cc96252e Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Wed, 4 Dec 2019 11:37:30 +0100 Subject: [PATCH 074/237] meson: Build nbis separately to allow changing flags As nbis is an external source bundle, it does not necessarily make sense to enable/fix all warnings to the extend we do for our own library code. As such, separate the build process into multiple stages. --- libfprint/meson.build | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/libfprint/meson.build b/libfprint/meson.build index 964744e0..7742ecc8 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -183,9 +183,19 @@ mapfile = 'libfprint.ver' vflag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), mapfile) deps = [ mathlib_dep, glib_dep, gusb_dep, nss_dep, imaging_dep, gio_dep ] + +nbis_lib = static_library('nbis', + nbis_sources, + include_directories: [ + root_inc, + include_directories('nbis/include'), + ], + dependencies: deps, + install: false) + libfprint = library('fprint', libfprint_sources + fp_enums + fpi_enums + - drivers_sources + nbis_sources + other_sources, + drivers_sources + other_sources, soversion: soversion, version: libversion, c_args: drivers_cflags, @@ -195,6 +205,7 @@ libfprint = library('fprint', ], link_args : vflag, link_depends : mapfile, + link_with: nbis_lib, dependencies: deps, install: true) From e1d181887f1961fded84343dbdf1961e5906780c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 4 Dec 2019 12:43:12 +0100 Subject: [PATCH 075/237] meson: Add the include directories to deps So we don't have to repeat them everywhere. --- libfprint/meson.build | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/libfprint/meson.build b/libfprint/meson.build index 7742ecc8..100865d1 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -184,12 +184,13 @@ vflag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), mapfil deps = [ mathlib_dep, glib_dep, gusb_dep, nss_dep, imaging_dep, gio_dep ] -nbis_lib = static_library('nbis', +deps += declare_dependency(include_directories: [ + root_inc, + include_directories('nbis/include'), +]) + +libnbis = static_library('nbis', nbis_sources, - include_directories: [ - root_inc, - include_directories('nbis/include'), - ], dependencies: deps, install: false) @@ -199,13 +200,9 @@ libfprint = library('fprint', soversion: soversion, version: libversion, c_args: drivers_cflags, - include_directories: [ - root_inc, - include_directories('nbis/include'), - ], link_args : vflag, link_depends : mapfile, - link_with: nbis_lib, + link_with: libnbis, dependencies: deps, install: true) @@ -218,9 +215,6 @@ install_headers(['fprint.h'] + libfprint_public_headers, subdir: 'libfprint') udev_rules = executable('fprint-list-udev-rules', 'fprint-list-udev-rules.c', - include_directories: [ - root_inc, - ], dependencies: [ deps, libfprint_dep ], install: false) @@ -235,9 +229,6 @@ endif supported_devices = executable('fprint-list-supported-devices', 'fprint-list-supported-devices.c', - include_directories: [ - root_inc, - ], dependencies: [ deps, libfprint_dep ], install: false) From 6a090656b6715e8ee5c47b80fd4d41c51f29067f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 4 Dec 2019 13:12:52 +0100 Subject: [PATCH 076/237] nbis: Add a global include file with all the definitions ignoring erros The nbis headers are full of redundant declarations, we can't fix them all now, so in the mean time let's use an header using pragma to ignore such errors. Add the error to nbis private include folder that should be used only by headers of libfprint-nbis. --- libfprint/fp-image.c | 2 +- libfprint/fp-print.c | 3 +-- libfprint/meson.build | 6 +++++ libfprint/nbis/libfprint-include/nbis.h | 35 +++++++++++++++++++++++++ 4 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 libfprint/nbis/libfprint-include/nbis.h diff --git a/libfprint/fp-image.c b/libfprint/fp-image.c index 4b8b3cd1..c66b010e 100644 --- a/libfprint/fp-image.c +++ b/libfprint/fp-image.c @@ -20,7 +20,7 @@ #include "fpi-image.h" -#include "nbis/include/lfs.h" +#include #if HAVE_PIXMAN #include diff --git a/libfprint/fp-print.c b/libfprint/fp-print.c index e7b119aa..ed29ec13 100644 --- a/libfprint/fp-print.c +++ b/libfprint/fp-print.c @@ -22,8 +22,7 @@ #include "fpi-image.h" #include "fpi-device.h" -#include "nbis/include/bozorth.h" -#include "nbis/include/lfs.h" +#include /** * SECTION: fp-print diff --git a/libfprint/meson.build b/libfprint/meson.build index 100865d1..99ebf736 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -187,11 +187,17 @@ deps = [ mathlib_dep, glib_dep, gusb_dep, nss_dep, imaging_dep, gio_dep ] deps += declare_dependency(include_directories: [ root_inc, include_directories('nbis/include'), + include_directories('nbis/libfprint-include'), ]) libnbis = static_library('nbis', nbis_sources, dependencies: deps, + c_args: cc.get_supported_arguments([ + '-Wno-error=redundant-decls', + '-Wno-redundant-decls', + '-Wno-discarded-qualifiers', + ]), install: false) libfprint = library('fprint', diff --git a/libfprint/nbis/libfprint-include/nbis.h b/libfprint/nbis/libfprint-include/nbis.h new file mode 100644 index 00000000..e3f667f0 --- /dev/null +++ b/libfprint/nbis/libfprint-include/nbis.h @@ -0,0 +1,35 @@ +/* + * Example fingerprint device prints listing and deletion + * Enrolls your right index finger and saves the print to disk + * Copyright (C) 2019 Marco Trevisan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wredundant-decls" + +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma GCC diagnostic pop From 7ed9b0c2f9a95d89b0879978534ab56636ab9b63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 4 Dec 2019 15:08:12 +0100 Subject: [PATCH 077/237] cleanup: Don't make nbis depend on libfprint built-sources Now that nbis is a static library it should be possible to compile it without any fprint-built dependency, although since it included fp_internal there was a compile-time dependency on the fp-enums that can be generated at later times. So: - Move nbis-helpers to nbis includes (and remove inclusion in fp_internal) - Move the Minutiae definitions inside a standalone fpi-minutiae header - Include fpi-minutiae.h in fp_internal.h - Include nbis-hepers.h and fpi-minutiae.h in nbis' lfs.h - Adapt missing definitions in libfprint --- libfprint/fp-image.c | 1 + libfprint/fp-print.c | 1 + libfprint/fp_internal.h | 35 ++------------- libfprint/fpi-minutiae.h | 45 +++++++++++++++++++ libfprint/nbis/include/lfs.h | 3 +- libfprint/nbis/lfs.h.patch | 13 +++--- .../libfprint-include}/nbis-helpers.h | 0 7 files changed, 59 insertions(+), 39 deletions(-) create mode 100644 libfprint/fpi-minutiae.h rename libfprint/{ => nbis/libfprint-include}/nbis-helpers.h (100%) diff --git a/libfprint/fp-image.c b/libfprint/fp-image.c index c66b010e..16837a81 100644 --- a/libfprint/fp-image.c +++ b/libfprint/fp-image.c @@ -19,6 +19,7 @@ */ #include "fpi-image.h" +#include "fpi-log.h" #include diff --git a/libfprint/fp-print.c b/libfprint/fp-print.c index ed29ec13..f724c776 100644 --- a/libfprint/fp-print.c +++ b/libfprint/fp-print.c @@ -20,6 +20,7 @@ #include "fpi-print.h" #include "fpi-image.h" +#include "fpi-log.h" #include "fpi-device.h" #include diff --git a/libfprint/fp_internal.h b/libfprint/fp_internal.h index 81470893..56ada183 100644 --- a/libfprint/fp_internal.h +++ b/libfprint/fp_internal.h @@ -1,6 +1,6 @@ /* * Internal/private definitions for libfprint - * Copyright (C) 2007-2008 Daniel Drake + * Copyright (C) 2019 Marco Trevisan * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -17,38 +17,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef __FPRINT_INTERNAL_H__ -#define __FPRINT_INTERNAL_H__ +#pragma once #include "fpi-log.h" -#include "nbis-helpers.h" #include "fpi-image.h" #include "fpi-image-device.h" - -/* fp_minutia structure definition */ -struct fp_minutia -{ - int x; - int y; - int ex; - int ey; - int direction; - double reliability; - int type; - int appearing; - int feature_id; - int *nbrs; - int *ridge_counts; - int num_nbrs; -}; - -/* fp_minutiae structure definition */ -struct fp_minutiae -{ - int alloc; - int num; - struct fp_minutia **list; -}; - - -#endif +#include "fpi-minutiae.h" diff --git a/libfprint/fpi-minutiae.h b/libfprint/fpi-minutiae.h new file mode 100644 index 00000000..24dc7611 --- /dev/null +++ b/libfprint/fpi-minutiae.h @@ -0,0 +1,45 @@ +/* + * Internal/private definitions for libfprint + * Copyright (C) 2007-2008 Daniel Drake + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +/* fp_minutia structure definition */ +struct fp_minutia +{ + int x; + int y; + int ex; + int ey; + int direction; + double reliability; + int type; + int appearing; + int feature_id; + int *nbrs; + int *ridge_counts; + int num_nbrs; +}; + +/* fp_minutiae structure definition */ +struct fp_minutiae +{ + int alloc; + int num; + struct fp_minutia **list; +}; diff --git a/libfprint/nbis/include/lfs.h b/libfprint/nbis/include/lfs.h index ae7aee5c..f4f38d71 100644 --- a/libfprint/nbis/include/lfs.h +++ b/libfprint/nbis/include/lfs.h @@ -66,7 +66,8 @@ of the software. #include #include -#include +#include +#include /*************************************************************************/ /* OUTPUT FILE EXTENSIONS */ diff --git a/libfprint/nbis/lfs.h.patch b/libfprint/nbis/lfs.h.patch index 2be6ebf8..3342bc5f 100644 --- a/libfprint/nbis/lfs.h.patch +++ b/libfprint/nbis/lfs.h.patch @@ -1,15 +1,16 @@ ---- include/lfs.h 2018-08-24 15:31:54.535579623 +0200 -+++ include/lfs.h.orig 2018-08-24 15:31:48.781587933 +0200 -@@ -66,7 +43,7 @@ of the software. +--- include/lfs.h ++++ include/lfs.h +@@ -66,7 +66,8 @@ of the software. #include #include -#include /* Needed by to_type9.c */ -+#include ++#include ++#include /*************************************************************************/ /* OUTPUT FILE EXTENSIONS */ -@@ -154,26 +131,8 @@ typedef struct rotgrids{ +@@ -154,26 +155,8 @@ typedef struct rotgrids{ #define DISAPPEARING 0 #define APPEARING 1 @@ -38,7 +39,7 @@ typedef struct feature_pattern{ int type; -@@ -1185,17 +1185,6 @@ extern void bubble_sort_double_inc_2(double *, int *, const int); +@@ -1203,17 +1186,6 @@ extern void bubble_sort_double_inc_2(double *, int *, const int); extern void bubble_sort_double_dec_2(double *, int *, const int); extern void bubble_sort_int_inc(int *, const int); diff --git a/libfprint/nbis-helpers.h b/libfprint/nbis/libfprint-include/nbis-helpers.h similarity index 100% rename from libfprint/nbis-helpers.h rename to libfprint/nbis/libfprint-include/nbis-helpers.h From 70a0d6f0fedde2556c817503333796fb698620b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 4 Dec 2019 13:13:14 +0100 Subject: [PATCH 078/237] nbis/log: Don't use old-style function declarations --- libfprint/nbis/mindtct/log.c | 4 ++-- libfprint/nbis/update-from-nbis.sh | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/libfprint/nbis/mindtct/log.c b/libfprint/nbis/mindtct/log.c index b1dcaadf..dcd3db78 100644 --- a/libfprint/nbis/mindtct/log.c +++ b/libfprint/nbis/mindtct/log.c @@ -66,7 +66,7 @@ of the software. /***************************************************************************/ /***************************************************************************/ -int open_logfile() +int open_logfile(void) { #ifdef LOG_REPORT fprintf(stderr, "ERROR : open_logfile : fopen : %s\n", LOG_FILE); @@ -91,7 +91,7 @@ void print2log(char *fmt, ...) /***************************************************************************/ /***************************************************************************/ -int close_logfile() +int close_logfile(void) { #ifdef LOG_REPORT fprintf(stderr, "ERROR : close_logfile : fclose : %s\n", LOG_FILE); diff --git a/libfprint/nbis/update-from-nbis.sh b/libfprint/nbis/update-from-nbis.sh index c8cde80b..742c8cbc 100755 --- a/libfprint/nbis/update-from-nbis.sh +++ b/libfprint/nbis/update-from-nbis.sh @@ -179,9 +179,13 @@ sed -i 's/[ \t]*$//' `find -name "*.[ch]"` # Remove usebsd.h sed -i '/usebsd.h/d' `find -name "*.[ch]"` +# Replace functions with empty parameters using (void) +sed -i 's/^\([[:space:]]*[[:alnum:]_]\+[\*[:space:]]\+'\ +'[[:alnum:]_]\+[[:space:]]*\)([[:space:]]*)/\1(void)/g' `find -name "*.[ch]"` + # Use GLib memory management spatch --sp-file glib-memory.cocci --dir . --in-place # The above leaves an unused variable around, triggering a warning # remove it. -patch -p0 < glib-mem-warning.patch \ No newline at end of file +patch -p0 < glib-mem-warning.patch From 35e9f19c0cc591005e26179c6c281514b57ef468 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 4 Dec 2019 14:22:07 +0100 Subject: [PATCH 079/237] nbis: Make the extern global bozworth 'y' variable as bz_y Othewise this could create issues with other 'y' variable definitions shadowing it. Add a cocci file that performs the change automatically --- libfprint/nbis/bozorth3/bozorth3.c | 34 ++++++++++++++-------------- libfprint/nbis/bozorth3/bz_gbls.c | 4 ++-- libfprint/nbis/include/bozorth.h | 2 +- libfprint/nbis/remove-global-y.cocci | 21 +++++++++++++++++ libfprint/nbis/update-from-nbis.sh | 3 +++ 5 files changed, 44 insertions(+), 20 deletions(-) create mode 100644 libfprint/nbis/remove-global-y.cocci diff --git a/libfprint/nbis/bozorth3/bozorth3.c b/libfprint/nbis/bozorth3/bozorth3.c index 0a8b0ff7..e2e668fe 100644 --- a/libfprint/nbis/bozorth3/bozorth3.c +++ b/libfprint/nbis/bozorth3/bozorth3.c @@ -896,7 +896,7 @@ for ( k = 0; k < np - 1; k++ ) { for ( i = 0; i < tot; i++ ) { - int colp_value = colp[ y[i]-1 ][0]; + int colp_value = colp[ bz_y[i]-1 ][0]; if ( colp_value < 0 ) { kk += colp_value; n++; @@ -933,7 +933,7 @@ for ( k = 0; k < np - 1; k++ ) { kk = 0; for ( i = 0; i < tot; i++ ) { - int diff = colp[ y[i]-1 ][0] - jj; + int diff = colp[ bz_y[i]-1 ][0] - jj; j = SQUARED( diff ); @@ -942,7 +942,7 @@ for ( k = 0; k < np - 1; k++ ) { if ( j > TXS && j < CTXS ) kk++; else - y[i-kk] = y[i]; + bz_y[i-kk] = bz_y[i]; } /* END FOR i */ tot -= kk; /* Adjust the total edge pairs TOT based on # of edge pairs skipped */ @@ -958,7 +958,7 @@ for ( k = 0; k < np - 1; k++ ) { for ( i = tot-1 ; i >= 0; i-- ) { - int idx = y[i] - 1; + int idx = bz_y[i] - 1; if ( rk[idx] == 0 ) { sc[idx] = -1; } else { @@ -976,7 +976,7 @@ for ( k = 0; k < np - 1; k++ ) { int pd = 0; for ( i = 0; i < tot; i++ ) { - int idx = y[i] - 1; + int idx = bz_y[i] - 1; for ( ii = 1; ii < 4; ii++ ) { @@ -1476,7 +1476,7 @@ return match_score; /* extern int rk[ RK_SIZE ]; */ /* extern int cp[ CP_SIZE ]; */ /* extern int rp[ RP_SIZE ]; */ -/* extern int y[ Y_SIZE ]; */ +/* extern int bz_y[ Y_SIZE ]; */ void bz_sift( int * ww, /* INPUT and OUTPUT; endpoint groups index; *ww may be bumped by one or by two */ @@ -1507,7 +1507,7 @@ if ( n == 0 && t == 0 ) { if ( sc[kx-1] != ftt ) { - y[ (*tot)++ ] = kx; + bz_y[ (*tot)++ ] = kx; rk[kx-1] = sc[kx-1]; sc[kx-1] = ftt; } @@ -1553,7 +1553,7 @@ if ( n == l ) { qq[*qh] = kz; zz[kz-1] = (*qh)++; } - y[(*tot)++] = kx; + bz_y[(*tot)++] = kx; rk[kx-1] = sc[kx-1]; sc[kx-1] = ftt; } @@ -1697,12 +1697,12 @@ for ( ii = 0; ii < tp; ii++ ) { /* For each index up to the current value of } t = 0; - y[0] = lim; + bz_y[0] = lim; cp[0] = 1; b = 0; n = 1; do { /* looping until T < 0 ... */ - if ( y[t] - cp[t] > 1 ) { + if (bz_y[t] - cp[t] > 1 ) { k = sct[cp[t]][t]; j = ctt[k] + 1; for ( i = 0; i < j; i++ ) { @@ -1715,25 +1715,25 @@ for ( ii = 0; ii < tp; ii++ ) { /* For each index up to the current value of do { while ( rp[jj] < sct[kk][t] && jj < j ) jj++; - while ( rp[jj] > sct[kk][t] && kk < y[t] ) + while ( rp[jj] > sct[kk][t] && kk < bz_y[t] ) kk++; - while ( rp[jj] == sct[kk][t] && kk < y[t] && jj < j ) { + while ( rp[jj] == sct[kk][t] && kk < bz_y[t] && jj < j ) { sct[k][t+1] = sct[kk][t]; k++; kk++; jj++; } - } while ( kk < y[t] && jj < j ); + } while ( kk < bz_y[t] && jj < j ); t++; cp[t] = 1; - y[t] = k; + bz_y[t] = k; b = t; n = 1; } else { int tot = 0; - lim = y[t]; + lim = bz_y[t]; for ( i = n-1; i < lim; i++ ) { tot += ct[ sct[i][t] ]; } @@ -1750,7 +1750,7 @@ for ( ii = 0; ii < tp; ii++ ) { /* For each index up to the current value of { int rk_index = b; - lim = y[t]; + lim = bz_y[t]; for ( i = n-1; i < lim; ) { rk[ rk_index++ ] = sct[ i++ ][ t ]; } @@ -1760,7 +1760,7 @@ for ( ii = 0; ii < tp; ii++ ) { /* For each index up to the current value of t--; if ( t >= 0 ) { ++cp[t]; - n = y[t]; + n = bz_y[t]; } } /* END IF */ diff --git a/libfprint/nbis/bozorth3/bz_gbls.c b/libfprint/nbis/bozorth3/bz_gbls.c index dd828dc3..ea283d8f 100644 --- a/libfprint/nbis/bozorth3/bz_gbls.c +++ b/libfprint/nbis/bozorth3/bz_gbls.c @@ -102,7 +102,7 @@ int yl[ YL_SIZE_1 ][ YL_SIZE_2 ]; int rf[RF_SIZE_1][RF_SIZE_2]; int cf[CF_SIZE_1][CF_SIZE_2]; - int y[20000]; + int bz_y[20000]; #else int rq[ RQ_SIZE ] = {}; int tq[ TQ_SIZE ] = {}; @@ -122,6 +122,6 @@ int yl[ YL_SIZE_1 ][ YL_SIZE_2 ]; int rf[RF_SIZE_1][RF_SIZE_2] = {}; int cf[CF_SIZE_1][CF_SIZE_2] = {}; - int y[20000] = {}; + int bz_y[20000] = {}; #endif diff --git a/libfprint/nbis/include/bozorth.h b/libfprint/nbis/include/bozorth.h index 08ec4b1c..a705da98 100644 --- a/libfprint/nbis/include/bozorth.h +++ b/libfprint/nbis/include/bozorth.h @@ -245,7 +245,7 @@ extern int cp[ CP_SIZE ]; extern int rp[ RP_SIZE ]; extern int rf[RF_SIZE_1][RF_SIZE_2]; extern int cf[CF_SIZE_1][CF_SIZE_2]; -extern int y[20000]; +extern int bz_y[20000]; /**************************************************************************/ /**************************************************************************/ diff --git a/libfprint/nbis/remove-global-y.cocci b/libfprint/nbis/remove-global-y.cocci new file mode 100644 index 00000000..3b740af3 --- /dev/null +++ b/libfprint/nbis/remove-global-y.cocci @@ -0,0 +1,21 @@ +@ global_y @ +identifier y; +@@ +int +- y ++ bz_y +[20000]; + +@@ +identifier global_y.y; +@@ +- y ++ bz_y +[...] + +@@ +@@ +int +- y ++ bz_y +[20000] = {}; diff --git a/libfprint/nbis/update-from-nbis.sh b/libfprint/nbis/update-from-nbis.sh index 742c8cbc..75e82bab 100755 --- a/libfprint/nbis/update-from-nbis.sh +++ b/libfprint/nbis/update-from-nbis.sh @@ -186,6 +186,9 @@ sed -i 's/^\([[:space:]]*[[:alnum:]_]\+[\*[:space:]]\+'\ # Use GLib memory management spatch --sp-file glib-memory.cocci --dir . --in-place +# Rename global "y" variable in "bz_y" +spatch --sp-file remove-global-y.cocci bozorth3/* include/bozorth.h --in-place + # The above leaves an unused variable around, triggering a warning # remove it. patch -p0 < glib-mem-warning.patch From 1d48b70f388f4c4631c97f51198fb203a9dada4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 4 Dec 2019 13:13:40 +0100 Subject: [PATCH 080/237] storage: Include storage header so that we have declarations --- examples/storage.c | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/storage.c b/examples/storage.c index db358547..6ca6efcb 100644 --- a/examples/storage.c +++ b/examples/storage.c @@ -20,6 +20,7 @@ */ #include +#include "storage.h" #include #include From 2f2ea65d32b4992b4963655fe42c15bbd73bd617 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 4 Dec 2019 13:23:26 +0100 Subject: [PATCH 081/237] fp-device: Remove unused timeout function and source data These were probably added in previous iterations, but they are not uneeded anymore as the GSource embeds already a callback function. So make just this clearer in the dispatch function. --- libfprint/fp-device.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/libfprint/fp-device.c b/libfprint/fp-device.c index 182be510..334b9982 100644 --- a/libfprint/fp-device.c +++ b/libfprint/fp-device.c @@ -1382,22 +1382,10 @@ fp_device_list_prints_finish (FpDevice *device, typedef struct { - GSource source; - FpDevice *device; - FpTimeoutFunc callback; - gpointer user_data; + GSource source; + FpDevice *device; } FpDeviceTimeoutSource; -gboolean -device_timeout_cb (gpointer user_data) -{ - FpDeviceTimeoutSource *source = user_data; - - source->callback (source->device, source->user_data); - - return G_SOURCE_REMOVE; -} - void timeout_finalize (GSource *source) { @@ -1409,11 +1397,12 @@ timeout_finalize (GSource *source) } static gboolean -timeout_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) +timeout_dispatch (GSource *source, GSourceFunc gsource_func, gpointer user_data) { FpDeviceTimeoutSource *timeout_source = (FpDeviceTimeoutSource *) source; + FpTimeoutFunc callback = (FpTimeoutFunc) gsource_func; - ((FpTimeoutFunc) callback)(timeout_source->device, user_data); + callback (timeout_source->device, user_data); return G_SOURCE_REMOVE; } @@ -1496,7 +1485,6 @@ fpi_device_add_timeout (FpDevice *device, source = (FpDeviceTimeoutSource *) g_source_new (&timeout_funcs, sizeof (FpDeviceTimeoutSource)); source->device = device; - source->user_data = user_data; g_source_attach (&source->source, NULL); g_source_set_callback (&source->source, (GSourceFunc) func, user_data, destroy_notify); From 2b8c524928520f5e780e8baf9325bf1d0d4861a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 4 Dec 2019 13:25:27 +0100 Subject: [PATCH 082/237] cleanup: Use static functions for non-declared methods --- libfprint/drivers/aes2501.c | 2 +- libfprint/drivers/elan.c | 4 ++-- libfprint/fp-device.c | 2 +- libfprint/fpi-usb-transfer.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libfprint/drivers/aes2501.c b/libfprint/drivers/aes2501.c index 1aa05388..57b0cca8 100644 --- a/libfprint/drivers/aes2501.c +++ b/libfprint/drivers/aes2501.c @@ -686,7 +686,7 @@ enum activate_states { ACTIVATE_NUM_STATES, }; -void +static void activate_read_regs_cb (FpImageDevice *dev, GError *error, unsigned char *regs, void *user_data) { diff --git a/libfprint/drivers/elan.c b/libfprint/drivers/elan.c index 7c7fb26e..90a03069 100644 --- a/libfprint/drivers/elan.c +++ b/libfprint/drivers/elan.c @@ -41,7 +41,7 @@ #include "drivers_api.h" #include "elan.h" -unsigned char +static unsigned char elan_get_pixel (struct fpi_frame_asmbl_ctx *ctx, struct fpi_frame *frame, unsigned int x, unsigned int y) @@ -91,7 +91,7 @@ G_DECLARE_FINAL_TYPE (FpiDeviceElan, fpi_device_elan, FPI, DEVICE_ELAN, FpImageDevice); G_DEFINE_TYPE (FpiDeviceElan, fpi_device_elan, FP_TYPE_IMAGE_DEVICE); -int +static int cmp_short (const void *a, const void *b) { return (int) (*(short *) a - *(short *) b); diff --git a/libfprint/fp-device.c b/libfprint/fp-device.c index 334b9982..2f706b35 100644 --- a/libfprint/fp-device.c +++ b/libfprint/fp-device.c @@ -1386,7 +1386,7 @@ typedef struct FpDevice *device; } FpDeviceTimeoutSource; -void +static void timeout_finalize (GSource *source) { FpDeviceTimeoutSource *timeout_source = (FpDeviceTimeoutSource *) source; diff --git a/libfprint/fpi-usb-transfer.c b/libfprint/fpi-usb-transfer.c index 08e75cb1..99fe3d4b 100644 --- a/libfprint/fpi-usb-transfer.c +++ b/libfprint/fpi-usb-transfer.c @@ -298,7 +298,7 @@ fpi_usb_transfer_fill_interrupt_full (FpiUsbTransfer *transfer, transfer->free_buffer = free_func; } -void +static void transfer_finish_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GError *error = NULL; From 0c655be159a820c38724e862f57e0b6b2d2a4527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 4 Dec 2019 14:04:04 +0100 Subject: [PATCH 083/237] gtk-demo: Use G_DECLARE to avoid missing declarations --- demo/gtk-libfprint-test.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/demo/gtk-libfprint-test.c b/demo/gtk-libfprint-test.c index c6dd90e2..8026815a 100644 --- a/demo/gtk-libfprint-test.c +++ b/demo/gtk-libfprint-test.c @@ -22,9 +22,11 @@ #include #include -typedef GtkApplication LibfprintDemo; -typedef GtkApplicationClass LibfprintDemoClass; - +struct _LibfprintDemo +{ + GtkApplication parent; +}; +G_DECLARE_FINAL_TYPE (LibfprintDemo, libfprint_demo, FP, DEMO, GtkApplication) G_DEFINE_TYPE (LibfprintDemo, libfprint_demo, GTK_TYPE_APPLICATION) typedef enum { @@ -33,7 +35,7 @@ typedef enum { IMAGE_DISPLAY_BINARY = 1 << 1 } ImageDisplayFlags; -typedef struct +struct _LibfprintDemoWindow { GtkApplicationWindow parent_instance; @@ -52,10 +54,9 @@ typedef struct FpImage *img; ImageDisplayFlags img_flags; -} LibfprintDemoWindow; - -typedef GtkApplicationWindowClass LibfprintDemoWindowClass; +}; +G_DECLARE_FINAL_TYPE (LibfprintDemoWindow, libfprint_demo_window, FP, DEMO_WINDOW, GtkApplicationWindow) G_DEFINE_TYPE (LibfprintDemoWindow, libfprint_demo_window, GTK_TYPE_APPLICATION_WINDOW) typedef enum { From 5ab4d6c45432db9818faeeed57bffd900ce49f09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 4 Dec 2019 14:09:09 +0100 Subject: [PATCH 084/237] vfs5011: Cast gpointer data values to proper type to do math operations --- libfprint/drivers/vfs5011.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libfprint/drivers/vfs5011.c b/libfprint/drivers/vfs5011.c index ef318f2e..265495ae 100644 --- a/libfprint/drivers/vfs5011.c +++ b/libfprint/drivers/vfs5011.c @@ -210,8 +210,8 @@ vfs5011_get_deviation2 (struct fpi_line_asmbl_ctx *ctx, GSList *row1, GSList *ro int res = 0, mean = 0, i; const int size = 64; - buf1 = row1->data + 56; - buf2 = row2->data + 168; + buf1 = (unsigned char *) row1->data + 56; + buf2 = (unsigned char *) row2->data + 168; for (i = 0; i < size; i++) mean += (int) buf1[i] + (int) buf2[i]; @@ -232,7 +232,7 @@ vfs5011_get_pixel (struct fpi_line_asmbl_ctx *ctx, GSList *row, unsigned x) { - unsigned char *data = row->data + 8; + unsigned char *data = (unsigned char *) row->data + 8; return data[x]; } From cacce50ef9011117bbe73ba04a2cfce01bb9afce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 4 Dec 2019 14:22:30 +0100 Subject: [PATCH 085/237] vfs0050: Use proper casting summing pointers first Cast the pointers as what fpi usb transfer expects. --- libfprint/drivers/vfs0050.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libfprint/drivers/vfs0050.c b/libfprint/drivers/vfs0050.c index bb6851f3..b08a865f 100644 --- a/libfprint/drivers/vfs0050.c +++ b/libfprint/drivers/vfs0050.c @@ -595,7 +595,8 @@ activate_ssm (FpiSsm *ssm, FpDevice *dev) /* Receive chunk of data */ transfer = fpi_usb_transfer_new (dev); fpi_usb_transfer_fill_bulk_full (transfer, 0x82, - (void *) self->lines_buffer + self->bytes, + (guint8 *) + (self->lines_buffer + self->bytes), VFS_USB_BUFFER_SIZE, NULL); transfer->ssm = ssm; fpi_usb_transfer_submit (transfer, VFS_USB_TIMEOUT, NULL, From a176fa1d346c5d1bfb935dc870509b6fe6005d89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 4 Dec 2019 13:34:14 +0100 Subject: [PATCH 086/237] meson: Include fpi-context.h in generated fp-drivers.c --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index 09abc1f5..265ce304 100644 --- a/meson.build +++ b/meson.build @@ -118,6 +118,7 @@ endforeach # Export the drivers' types to the core code drivers_type_list = '#include \n' +drivers_type_list += '#include "fpi-context.h"\n' drivers_type_func = 'void fpi_get_driver_types(GArray *drivers)\n{\n\tGType t;\n' foreach driver: drivers drivers_type_list += 'extern GType (fpi_device_' + driver + '_get_type) (void);\n' From b2e55308d668c19cec7dc4a2bd62884ddb1fea31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 4 Dec 2019 13:44:08 +0100 Subject: [PATCH 087/237] meson: Move generated source to fpi- prefix and use more readable code Instead of concatenating strings, use an array of strings and finally join them using newline. --- libfprint/meson.build | 4 ++-- meson.build | 19 +++++++++++++------ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/libfprint/meson.build b/libfprint/meson.build index 99ebf736..f73aba3b 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -172,11 +172,11 @@ fpi_enums = gnome.mkenums_simple('fpi-enums', fpi_enums_h = fpi_enums[1] drivers_sources += configure_file(input: 'empty_file', - output: 'fp-drivers.c', + output: 'fpi-drivers.c', capture: true, command: [ 'echo', - drivers_type_list + '\n\n' + drivers_type_func + '\n'.join(drivers_type_list + [] + drivers_type_func) ]) mapfile = 'libfprint.ver' diff --git a/meson.build b/meson.build index 265ce304..65077c5e 100644 --- a/meson.build +++ b/meson.build @@ -117,15 +117,22 @@ foreach driver: drivers endforeach # Export the drivers' types to the core code -drivers_type_list = '#include \n' -drivers_type_list += '#include "fpi-context.h"\n' -drivers_type_func = 'void fpi_get_driver_types(GArray *drivers)\n{\n\tGType t;\n' +drivers_type_list = [] +drivers_type_func = [] +drivers_type_list += '#include ' +drivers_type_list += '#include "fpi-context.h"' +drivers_type_list += '' +drivers_type_func += 'void fpi_get_driver_types (GArray *drivers)' +drivers_type_func += ' {' +drivers_type_func += ' GType t;' +drivers_type_func += '' foreach driver: drivers - drivers_type_list += 'extern GType (fpi_device_' + driver + '_get_type) (void);\n' - drivers_type_func += ' t = fpi_device_' + driver + '_get_type(); g_array_append_val (drivers, t);\n' + drivers_type_list += 'extern GType (fpi_device_' + driver + '_get_type) (void);' + drivers_type_func += ' t = fpi_device_' + driver + '_get_type ();' + drivers_type_func += ' g_array_append_val (drivers, t);\n' endforeach drivers_type_list += '' -drivers_type_func += '};' +drivers_type_func += '}' root_inc = include_directories('.') From c678b9021c3b1d6a430cbd96833be2557fa5ad3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 4 Dec 2019 14:27:33 +0100 Subject: [PATCH 088/237] meson: Use stricter C arguments to compile libfprint These are based on what mutter does, being a quite strict project on c code quality. --- meson.build | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/meson.build b/meson.build index 65077c5e..1561ebf6 100644 --- a/meson.build +++ b/meson.build @@ -21,20 +21,49 @@ glib_version_def = 'GLIB_VERSION_@0@_@1@'.format( glib_min_version.split('.')[0], glib_min_version.split('.')[1]) common_cflags = cc.get_supported_arguments([ '-Wall', + '-Wcast-align', + '-Wformat-nonliteral', + '-Wformat-security', + '-Wformat=2', + '-Wignored-qualifiers', + '-Wlogical-op', + '-Wmissing-declarations', + '-Wmissing-format-attribute', + '-Wmissing-include-dirs', + '-Wmissing-noreturn', + '-Wpointer-arith', + '-Wshadow', '-Wtype-limits', '-Wundef', '-Wunused', - '-Wstrict-prototypes', - '-Werror-implicit-function-declaration', - '-Wshadow', + '-Werror=address', + '-Werror=array-bounds', + '-Werror=empty-body', + '-Werror=init-self', + '-Werror=int-to-pointer-cast', + '-Werror=main', + '-Werror=missing-braces', + '-Werror=nonnull', + '-Werror=redundant-decls', + '-Werror=return-type', + '-Werror=sequence-point', + '-Werror=trigraphs', + '-Werror=write-strings', + '-fno-strict-aliasing', '-DGLIB_VERSION_MIN_REQUIRED=' + glib_version_def, '-DGLIB_VERSION_MAX_ALLOWED=' + glib_version_def, '-D_GNU_SOURCE', '-DG_LOG_DOMAIN="libfprint"', ]) c_cflags = cc.get_supported_arguments([ - '-fgnu89-inline', '-std=gnu99', + '-Wimplicit-function-declaration', + '-Wmissing-prototypes', + '-Wnested-externs', + '-Wold-style-definition', + '-Wstrict-prototypes', + '-Werror=implicit', + '-Werror=pointer-to-int-cast', ]) add_project_arguments(common_cflags + c_cflags, language: 'c') add_project_arguments(common_cflags, language: 'cpp') From 2fcc2deb430f215a470d26de7af3fe0d9e105ae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 4 Dec 2019 14:28:12 +0100 Subject: [PATCH 089/237] ci: Use --werror by default in meson at build time --- .gitlab-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 74d4f465..7c4a05ac 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -12,14 +12,14 @@ variables: .build_one_driver_template: &build_one_driver script: # Build with a driver that doesn't need imaging, or nss - - meson -Ddrivers=$driver . _build + - meson --werror -Ddrivers=$driver . _build - ninja -C _build - rm -rf _build/ .build_template: &build script: # And build with everything - - meson -Ddrivers=all . _build + - meson --werror -Ddrivers=all . _build - ninja -C _build - ninja -C _build install @@ -38,7 +38,7 @@ build: test: stage: test script: - - meson -Ddrivers=all . _build + - meson --werror -Ddrivers=all . _build - ninja -C _build - meson test -C _build --verbose --no-stdsplit From dccb5b3ab2751947b9aca81f16dad91ce27b2378 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Wed, 4 Dec 2019 14:28:08 +0100 Subject: [PATCH 090/237] elan: Fix internal state machine to ensure correct deactivation During calibration, the internal state was stored as INACTIVE. This is not true though, the device is actively running state machines. Move the state update to the start of the operation, therefore ensuring we don't deactivate without completing the SSM. Note that this will prevent a crash, but the driver still does not behave quite correctly when such a state change does happen. However, this is just a safety measure as the state change should not happen in the first place. See: #203 --- libfprint/drivers/elan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfprint/drivers/elan.c b/libfprint/drivers/elan.c index 90a03069..9495a480 100644 --- a/libfprint/drivers/elan.c +++ b/libfprint/drivers/elan.c @@ -776,7 +776,6 @@ calibrate_complete (FpiSsm *ssm, FpDevice *dev, GError *error) } else { - self->dev_state = FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON; elan_capture (dev); } @@ -966,6 +965,7 @@ elan_change_state (FpImageDevice *idev) { case FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON: /* activation completed or another enroll stage started */ + self->dev_state = FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON; elan_calibrate (dev); break; From 7b683443942299629d0e1839a7f55537a8eb389f Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Wed, 4 Dec 2019 15:50:06 +0100 Subject: [PATCH 091/237] image-device: Prevent deactivation when waiting for finger At the end of enroll, the image device would put the driver into the AWAIT_FINGER_ON state and then deactivate the device afterwards. Doing this adds additional complexity to drivers which would need to handle both cancellation and normal deactivation from that state. Only put the device into the AWAIT_FINGER_ON state when we know that another enroll stage is needed. This avoids the critical state transition simplifying the driver state machine. Fixes: #203 Fixes: 689aff023253e4ca970c9f76f9e4209188175d3d --- libfprint/fp-image-device.c | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/libfprint/fp-image-device.c b/libfprint/fp-image-device.c index e45b6a94..26c3cb01 100644 --- a/libfprint/fp-image-device.c +++ b/libfprint/fp-image-device.c @@ -51,6 +51,7 @@ typedef struct FpImageDeviceState state; gboolean active; + gboolean enroll_await_on_pending; gint enroll_stage; guint pending_activation_timeout_id; @@ -256,6 +257,7 @@ fp_image_device_start_capture_action (FpDevice *device) } priv->enroll_stage = 0; + priv->enroll_await_on_pending = FALSE; /* The device might still be deactivating from a previous call. * In that situation, try to wait for a bit before reporting back an @@ -385,6 +387,22 @@ fp_image_device_init (FpImageDevice *self) } +static void +fp_image_device_enroll_maybe_await_finger_on (FpImageDevice *self) +{ + FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); + + if (priv->enroll_await_on_pending) + { + priv->enroll_await_on_pending = FALSE; + fp_image_device_change_state (self, FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON); + } + else + { + priv->enroll_await_on_pending = TRUE; + } +} + static void fpi_image_device_minutiae_detected (GObject *source_object, GAsyncResult *res, gpointer user_data) { @@ -446,11 +464,16 @@ fpi_image_device_minutiae_detected (GObject *source_object, GAsyncResult *res, g fpi_device_enroll_progress (device, priv->enroll_stage, g_steal_pointer (&print), error); + /* Start another scan or deactivate. */ if (priv->enroll_stage == IMG_ENROLL_STAGES) { fpi_device_enroll_complete (device, g_object_ref (enroll_print), NULL); fp_image_device_deactivate (device); } + else + { + fp_image_device_enroll_maybe_await_finger_on (FP_IMAGE_DEVICE (device)); + } } else if (action == FP_DEVICE_ACTION_VERIFY) { @@ -572,13 +595,15 @@ fpi_image_device_report_finger_status (FpImageDevice *self, * 2. We are still deactivating the device after an action completed * 3. We were waiting for finger removal to start the new action * Either way, we always end up deactivating except for the enroll case. - * XXX: This is not quite correct though, as we assume we need another finger - * scan even though we might be processing the last one (successfully). + * + * The enroll case is special as AWAIT_FINGER_ON should only happen after + * minutiae detection to prevent deactivation (without cancellation) + * from the AWAIT_FINGER_ON state. */ if (action != FP_DEVICE_ACTION_ENROLL) fp_image_device_deactivate (device); else - fp_image_device_change_state (self, FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON); + fp_image_device_enroll_maybe_await_finger_on (self); } } From 8f21aa1b2603520560ba8af4f358b15f8402e110 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Wed, 4 Dec 2019 17:14:35 +0100 Subject: [PATCH 092/237] uru4000: Fix state change from IRQ handler The IRQ handler will re-register itself automatically. However, if this happens after the callback is called, then the check whether the IRQ handler is running fails. Re-start the IRQ handler before calling the callback. This way the state changes happening from the callback will see the correct IRQ handler registration state. See: #205 --- libfprint/drivers/uru4000.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libfprint/drivers/uru4000.c b/libfprint/drivers/uru4000.c index 122544da..4385f29e 100644 --- a/libfprint/drivers/uru4000.c +++ b/libfprint/drivers/uru4000.c @@ -332,6 +332,8 @@ irq_handler (FpiUsbTransfer *transfer, return; } + start_irq_handler (imgdev); + type = GUINT16_FROM_BE (*((uint16_t *) data)); fp_dbg ("recv irq type %04x", type); @@ -344,8 +346,6 @@ irq_handler (FpiUsbTransfer *transfer, urudev->irq_cb (imgdev, NULL, type, urudev->irq_cb_data); else fp_dbg ("ignoring interrupt"); - - start_irq_handler (imgdev); } static void From 10c5bdade7a073eb65bc727153ef670108909005 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Wed, 4 Dec 2019 13:07:10 +0100 Subject: [PATCH 093/237] uru4000: Fix control transfer request type During porting the request type was accidentally changed from VENDOR to DEVICE. Change the type back to VENDOR. See: #205 --- libfprint/drivers/uru4000.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libfprint/drivers/uru4000.c b/libfprint/drivers/uru4000.c index 4385f29e..5128a12c 100644 --- a/libfprint/drivers/uru4000.c +++ b/libfprint/drivers/uru4000.c @@ -175,7 +175,7 @@ write_regs (FpImageDevice *dev, uint16_t first_reg, transfer->short_is_error = TRUE; fpi_usb_transfer_fill_control (transfer, G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, - G_USB_DEVICE_REQUEST_TYPE_STANDARD, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, G_USB_DEVICE_RECIPIENT_DEVICE, USB_RQ, first_reg, 0, num_regs); @@ -202,7 +202,7 @@ read_regs (FpImageDevice *dev, uint16_t first_reg, fpi_usb_transfer_fill_control (transfer, G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, - G_USB_DEVICE_REQUEST_TYPE_STANDARD, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, G_USB_DEVICE_RECIPIENT_DEVICE, USB_RQ, first_reg, 0, num_regs); fpi_usb_transfer_submit (transfer, CTRL_TIMEOUT, NULL, callback, user_data); From fe967d0ac2827c6f648b6a5f1f5c7e762c9a4c59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 4 Dec 2019 19:04:04 +0100 Subject: [PATCH 094/237] ci: Build flatpak automatically only on master and tagso While allowing to build it manually in other cases --- .gitlab-ci.yml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7c4a05ac..f71d3847 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -77,7 +77,7 @@ test_indent: <<: *flatpak_script <<: *flatpak_artifacts -flatpak master: +.flatpak_master_template: &flatpak_master image: registry.gitlab.gnome.org/gnome/gnome-runtime-images/gnome:3.32 stage: flatpack variables: @@ -87,3 +87,17 @@ flatpak master: FLATPAK_MODULE: "libfprint" DBUS_ID: "org.freedesktop.libfprint.Demo" <<: *flatpak + +flatpak-auto master: + <<: *flatpak_master + when: always + only: + - tags + - master + +flatpak-manual master: + <<: *flatpak_master + when: manual + except: + - tags + - master From 65828e0e56f054ced0ca5c2d8ab2ee803f6a69c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 4 Dec 2019 20:03:23 +0100 Subject: [PATCH 095/237] fpi-ssm: Support named SSMs and use a fallback macro Add fpi_ssm_new_full() that allows to pass a name to the state-machine constructor, not to change all the drivers, provide a fpi_ssm_new() macro that stringifies the nr_parameters value (usually an enum value) as the name so that we can use it for better debugging. --- doc/libfprint-sections.txt | 1 + libfprint/fpi-ssm.c | 43 +++++++++++++++++++++++++++----------- libfprint/fpi-ssm.h | 9 +++++--- 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/doc/libfprint-sections.txt b/doc/libfprint-sections.txt index 9fb01bde..9e17f8ef 100644 --- a/doc/libfprint-sections.txt +++ b/doc/libfprint-sections.txt @@ -211,6 +211,7 @@ fpi_print_bz3_match FpiSsmCompletedCallback FpiSsmHandlerCallback fpi_ssm_new +fpi_ssm_new_full fpi_ssm_free fpi_ssm_start fpi_ssm_start_subsm diff --git a/libfprint/fpi-ssm.c b/libfprint/fpi-ssm.c index 09a31e3f..96336e1c 100644 --- a/libfprint/fpi-ssm.c +++ b/libfprint/fpi-ssm.c @@ -81,6 +81,7 @@ struct _FpiSsm { FpDevice *dev; + const char *name; FpiSsm *parentsm; gpointer ssm_data; GDestroyNotify ssm_data_destroy; @@ -103,13 +104,29 @@ struct _FpiSsm * * Allocate a new ssm, with @nr_states states. The @handler callback * will be called after each state transition. + * This is a macro that calls fpi_ssm_new_full() using the stringified + * version of @nr_states, so will work better with named parameters. + * + * Returns: a new #FpiSsm state machine + */ + +/** + * fpi_ssm_new_full: + * @dev: a #fp_dev fingerprint device + * @handler: the callback function + * @nr_states: the number of states + * @name: the name of the state machine (for debug purposes) + * + * Allocate a new ssm, with @nr_states states. The @handler callback + * will be called after each state transition. * * Returns: a new #FpiSsm state machine */ FpiSsm * -fpi_ssm_new (FpDevice *dev, - FpiSsmHandlerCallback handler, - int nr_states) +fpi_ssm_new_full (FpDevice *dev, + FpiSsmHandlerCallback handler, + int nr_states, + const char *name) { FpiSsm *machine; @@ -120,6 +137,7 @@ fpi_ssm_new (FpDevice *dev, machine->handler = handler; machine->nr_states = nr_states; machine->dev = dev; + machine->name = g_strdup (name); machine->completed = TRUE; return machine; } @@ -251,6 +269,7 @@ fpi_ssm_free (FpiSsm *machine) if (machine->ssm_data_destroy) g_clear_pointer (&machine->ssm_data, machine->ssm_data_destroy); g_clear_pointer (&machine->error, g_error_free); + g_clear_pointer (&machine->name, g_free); fpi_ssm_clear_delayed_action (machine); g_free (machine); } @@ -259,7 +278,7 @@ fpi_ssm_free (FpiSsm *machine) static void __ssm_call_handler (FpiSsm *machine) { - fp_dbg ("%p entering state %d", machine, machine->cur_state); + fp_dbg ("%s entering state %d", machine->name, machine->cur_state); machine->handler (machine, machine->dev); } @@ -337,9 +356,9 @@ fpi_ssm_mark_completed (FpiSsm *machine) machine->completed = TRUE; if (machine->error) - fp_dbg ("%p completed with error: %s", machine, machine->error->message); + fp_dbg ("%s completed with error: %s", machine->name, machine->error->message); else - fp_dbg ("%p completed successfully", machine); + fp_dbg ("%s completed successfully", machine->name); if (machine->callback) { GError *error = machine->error ? g_error_copy (machine->error) : NULL; @@ -383,9 +402,9 @@ fpi_ssm_mark_completed_delayed (FpiSsm *machine, on_device_timeout_complete, cancellable, machine, NULL); - source_name = g_strdup_printf ("[%s] ssm %p complete %d", + source_name = g_strdup_printf ("[%s] ssm %s complete %d", fp_device_get_device_id (machine->dev), - machine, machine->cur_state + 1); + machine->name, machine->cur_state + 1); g_source_set_name (machine->timeout, source_name); } @@ -482,9 +501,9 @@ fpi_ssm_next_state_delayed (FpiSsm *machine, on_device_timeout_next_state, cancellable, machine, NULL); - source_name = g_strdup_printf ("[%s] ssm %p jump to next state %d", + source_name = g_strdup_printf ("[%s] ssm %s jump to next state %d", fp_device_get_device_id (machine->dev), - machine, machine->cur_state + 1); + machine->name, machine->cur_state + 1); g_source_set_name (machine->timeout, source_name); } @@ -559,9 +578,9 @@ fpi_ssm_jump_to_state_delayed (FpiSsm *machine, on_device_timeout_jump_to_state, cancellable, data, g_free); - source_name = g_strdup_printf ("[%s] ssm %p jump to state %d", + source_name = g_strdup_printf ("[%s] ssm %s jump to state %d", fp_device_get_device_id (machine->dev), - machine, state); + machine->name, state); g_source_set_name (machine->timeout, source_name); } diff --git a/libfprint/fpi-ssm.h b/libfprint/fpi-ssm.h index 3ef653e4..d1334b58 100644 --- a/libfprint/fpi-ssm.h +++ b/libfprint/fpi-ssm.h @@ -60,9 +60,12 @@ typedef void (*FpiSsmHandlerCallback)(FpiSsm *ssm, FpDevice *dev); /* for library and drivers */ -FpiSsm *fpi_ssm_new (FpDevice *dev, - FpiSsmHandlerCallback handler, - int nr_states); +#define fpi_ssm_new(dev, handler, nr_states) \ + fpi_ssm_new_full (dev, handler, nr_states, #nr_states) +FpiSsm *fpi_ssm_new_full (FpDevice *dev, + FpiSsmHandlerCallback handler, + int nr_states, + const char *machine_name); void fpi_ssm_free (FpiSsm *machine); void fpi_ssm_start (FpiSsm *ssm, FpiSsmCompletedCallback callback); From a522e3fd6f5946751a7b8465690b7a8583ec9dbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 4 Dec 2019 20:14:16 +0100 Subject: [PATCH 096/237] fpi-ssm: Improve debugging of SSM using driver infos Always mention the driver that is triggering it --- libfprint/fpi-ssm.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/libfprint/fpi-ssm.c b/libfprint/fpi-ssm.c index 96336e1c..8b3e4bd6 100644 --- a/libfprint/fpi-ssm.c +++ b/libfprint/fpi-ssm.c @@ -278,7 +278,8 @@ fpi_ssm_free (FpiSsm *machine) static void __ssm_call_handler (FpiSsm *machine) { - fp_dbg ("%s entering state %d", machine->name, machine->cur_state); + fp_dbg ("[%s] %s entering state %d", fp_device_get_driver (machine->dev), + machine->name, machine->cur_state); machine->handler (machine, machine->dev); } @@ -356,9 +357,11 @@ fpi_ssm_mark_completed (FpiSsm *machine) machine->completed = TRUE; if (machine->error) - fp_dbg ("%s completed with error: %s", machine->name, machine->error->message); + fp_dbg ("[%s] %s completed with error: %s", fp_device_get_driver (machine->dev), + machine->name, machine->error->message); else - fp_dbg ("%s completed successfully", machine->name); + fp_dbg ("[%s] %s completed successfully", fp_device_get_driver (machine->dev), + machine->name); if (machine->callback) { GError *error = machine->error ? g_error_copy (machine->error) : NULL; @@ -421,12 +424,15 @@ fpi_ssm_mark_failed (FpiSsm *machine, GError *error) g_assert (error); if (machine->error) { - fp_warn ("SSM already has an error set, ignoring new error %s", error->message); + fp_warn ("[%s] SSM %s already has an error set, ignoring new error %s", + fp_device_get_driver (machine->dev), machine->name, error->message); g_error_free (error); return; } - fp_dbg ("SSM failed in state %d with error: %s", machine->cur_state, error->message); + fp_dbg ("[%s] SSM %s failed in state %d with error: %s", + fp_device_get_driver (machine->dev), machine->name, + machine->cur_state, error->message); machine->error = g_steal_pointer (&error); fpi_ssm_mark_completed (machine); } From a64ac2296b0b2f4510942da98c2d326e6ab3a3ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 4 Dec 2019 20:14:49 +0100 Subject: [PATCH 097/237] vfs0051: Use named SSMs for usb async exchanges --- libfprint/drivers/vfs5011.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/libfprint/drivers/vfs5011.c b/libfprint/drivers/vfs5011.c index 265495ae..4af32073 100644 --- a/libfprint/drivers/vfs5011.c +++ b/libfprint/drivers/vfs5011.c @@ -190,11 +190,13 @@ usbexchange_loop (FpiSsm *ssm, FpDevice *_dev) static void usb_exchange_async (FpiSsm *ssm, - struct usbexchange_data *data) + struct usbexchange_data *data, + const char *exchange_name) { - FpiSsm *subsm = fpi_ssm_new (FP_DEVICE (data->device), - usbexchange_loop, - data->stepcount); + FpiSsm *subsm = fpi_ssm_new_full (FP_DEVICE (data->device), + usbexchange_loop, + data->stepcount, + exchange_name); fpi_ssm_set_data (subsm, data, NULL); fpi_ssm_start_subsm (ssm, subsm); @@ -684,7 +686,7 @@ activate_loop (FpiSsm *ssm, FpDevice *_dev) self->init_sequence.receive_buf = g_malloc0 (VFS5011_RECEIVE_BUF_SIZE); self->init_sequence.timeout = 1000; - usb_exchange_async (ssm, &self->init_sequence); + usb_exchange_async (ssm, &self->init_sequence, "ACTIVATE REQUEST"); break; case DEV_ACTIVATE_INIT_COMPLETE: @@ -716,7 +718,7 @@ activate_loop (FpiSsm *ssm, FpDevice *_dev) self->init_sequence.receive_buf = g_malloc0 (VFS5011_RECEIVE_BUF_SIZE); self->init_sequence.timeout = VFS5011_DEFAULT_WAIT_TIMEOUT; - usb_exchange_async (ssm, &self->init_sequence); + usb_exchange_async (ssm, &self->init_sequence, "PREPARE CAPTURE"); break; } @@ -769,7 +771,7 @@ open_loop (FpiSsm *ssm, FpDevice *_dev) self->init_sequence.receive_buf = g_malloc0 (VFS5011_RECEIVE_BUF_SIZE); self->init_sequence.timeout = VFS5011_DEFAULT_WAIT_TIMEOUT; - usb_exchange_async (ssm, &self->init_sequence); + usb_exchange_async (ssm, &self->init_sequence, "DEVICE OPEN"); break; } ; From 702255b182829cc6706df13e1c7d8461260b552e Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Wed, 4 Dec 2019 19:54:07 +0100 Subject: [PATCH 098/237] image-device: Print warning for incorrect deactivation Drivers may not handle deactivation properly when they are awaiting a finger. This should be prevented by the internal FpImageDevice state machine. --- libfprint/fp-image-device.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libfprint/fp-image-device.c b/libfprint/fp-image-device.c index 26c3cb01..252f414c 100644 --- a/libfprint/fp-image-device.c +++ b/libfprint/fp-image-device.c @@ -50,6 +50,7 @@ typedef struct { FpImageDeviceState state; gboolean active; + gboolean cancelling; gboolean enroll_await_on_pending; gint enroll_stage; @@ -140,6 +141,9 @@ fp_image_device_deactivate (FpDevice *device) fp_dbg ("Already deactivated, ignoring request."); return; } + if (!priv->cancelling && priv->state == FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON) + g_warning ("Deactivating image device while waiting for finger, this should not happen."); + priv->state = FP_IMAGE_DEVICE_STATE_INACTIVE; g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FPI_STATE]); @@ -203,6 +207,7 @@ static void fp_image_device_cancel_action (FpDevice *device) { FpImageDevice *self = FP_IMAGE_DEVICE (device); + FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); FpDeviceAction action; action = fpi_device_get_current_action (device); @@ -214,7 +219,9 @@ fp_image_device_cancel_action (FpDevice *device) action == FP_DEVICE_ACTION_IDENTIFY || action == FP_DEVICE_ACTION_CAPTURE) { + priv->cancelling = TRUE; fp_image_device_deactivate (FP_DEVICE (self)); + priv->cancelling = FALSE; /* XXX: Some nicer way of doing this would be good. */ fpi_device_action_error (FP_DEVICE (self), From a8a2a757ed3f9b1ebaa1d7786feb781bbeffb910 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Wed, 4 Dec 2019 19:55:25 +0100 Subject: [PATCH 099/237] virtual-image: Only print warnings for actual errors No need to warn for lost connections (if we don't expect more data) or cancellations. --- libfprint/drivers/virtual-image.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libfprint/drivers/virtual-image.c b/libfprint/drivers/virtual-image.c index c271c7a7..07a631f5 100644 --- a/libfprint/drivers/virtual-image.c +++ b/libfprint/drivers/virtual-image.c @@ -75,9 +75,9 @@ recv_image_img_recv_cb (GObject *source_object, { if (!success) { - g_warning ("Error receiving header for image data: %s", error->message); if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) return; + g_warning ("Error receiving header for image data: %s", error->message); } self = FPI_DEVICE_VIRTUAL_IMAGE (user_data); @@ -113,9 +113,10 @@ recv_image_hdr_recv_cb (GObject *source_object, { if (!success) { - g_warning ("Error receiving header for image data: %s", error->message); - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) || + g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED)) return; + g_warning ("Error receiving header for image data: %s", error->message); } self = FPI_DEVICE_VIRTUAL_IMAGE (user_data); From 3e958ab7b4fc47108de76622624cd551bd612fc6 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Wed, 4 Dec 2019 19:56:54 +0100 Subject: [PATCH 100/237] virtual-image: Allow fine control over the finger state Add commands to disable automatic finger reporting for images and to send a specific finger report. This is useful to test more code paths inside the image-device code. --- libfprint/drivers/virtual-image.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/libfprint/drivers/virtual-image.c b/libfprint/drivers/virtual-image.c index 07a631f5..33f322e4 100644 --- a/libfprint/drivers/virtual-image.c +++ b/libfprint/drivers/virtual-image.c @@ -47,6 +47,7 @@ struct _FpDeviceVirtualImage gint socket_fd; gint client_fd; + gboolean automatic_finger; FpImage *recv_img; gint recv_img_hdr[2]; }; @@ -89,9 +90,11 @@ recv_image_img_recv_cb (GObject *source_object, self = FPI_DEVICE_VIRTUAL_IMAGE (user_data); device = FP_IMAGE_DEVICE (self); - fpi_image_device_report_finger_status (device, TRUE); + if (self->automatic_finger) + fpi_image_device_report_finger_status (device, TRUE); fpi_image_device_image_captured (device, g_steal_pointer (&self->recv_img)); - fpi_image_device_report_finger_status (device, FALSE); + if (self->automatic_finger) + fpi_image_device_report_finger_status (device, FALSE); /* And, listen for more images from the same client. */ recv_image (self, G_INPUT_STREAM (source_object)); @@ -148,6 +151,17 @@ recv_image_hdr_recv_cb (GObject *source_object, fpi_device_error_new (self->recv_img_hdr[1])); break; + case -3: + /* -3 sets/clears automatic finger detection for images */ + self->automatic_finger = !!self->recv_img_hdr[1]; + break; + + case -4: + /* -4 submits a finger detection report */ + fpi_image_device_report_finger_status (FP_IMAGE_DEVICE (self), + !!self->recv_img_hdr[1]); + break; + default: /* disconnect client, it didn't play fair */ g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL); @@ -214,6 +228,7 @@ new_connection_cb (GObject *source_object, GAsyncResult *res, gpointer user_data } dev->connection = connection; + dev->automatic_finger = TRUE; stream = g_io_stream_get_input_stream (G_IO_STREAM (connection)); recv_image (dev, stream); From 50a837573d2992456e778cecd53516c2e07c5e3a Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Wed, 4 Dec 2019 19:58:26 +0100 Subject: [PATCH 101/237] tests: Update helper functions for new virtual-image features This also changes the code to keep the connection open and adds automatic mainloop iteration to ensure the driver processes the request. This is important so we will not deadlock when we send multiple requests. --- tests/virtual-image.py | 63 +++++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/tests/virtual-image.py b/tests/virtual-image.py index 363219af..86bd86d4 100755 --- a/tests/virtual-image.py +++ b/tests/virtual-image.py @@ -24,20 +24,6 @@ if wrapper: os.unsetenv('LIBFPRINT_TEST_WRAPPER') sys.exit(subprocess.check_call(wrap_cmd)) -class Connection: - - def __init__(self, addr): - self.addr = addr - - def __enter__(self): - self.con = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - self.con.connect(self.addr) - return self.con - - def __exit__(self, exc_type, exc_val, exc_tb): - self.con.close() - del self.con - def load_image(img): png = cairo.ImageSurface.create_from_png(img) @@ -101,24 +87,51 @@ class VirtualImage(unittest.TestCase): def setUp(self): self.dev.open_sync() + self.con = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.con.connect(self.sockaddr) + def tearDown(self): + self.con.close() + del self.con self.dev.close_sync() - def report_finger(self, state): - with Connection(self.sockaddr) as con: - con.write(struct.pack('ii', -1, 1 if state else 0)) + def send_retry(self, retry_error=1, iterate=True): + # The default (1) is too-short + self.sendall(struct.pack('ii', -1, retry_error)) + while iterate and ctx.pending(): + ctx.iteration(False) - def send_image(self, image): + def send_error(self, device_error=0, iterate=True): + # The default (0) is a generic error + self.sendall(struct.pack('ii', -1, retry_error)) + while iterate and ctx.pending(): + ctx.iteration(False) + + def send_finger_automatic(self, automatic, iterate=True): + # Set whether finger on/off is reported around images + self.con.sendall(struct.pack('ii', -3, 1 if automatic else 0)) + while iterate and ctx.pending(): + ctx.iteration(False) + + def send_finger_report(self, has_finger, iterate=True): + # Send finger on/off + self.con.sendall(struct.pack('ii', -4, 1 if has_finger else 0)) + while iterate and ctx.pending(): + ctx.iteration(False) + + def send_image(self, image, iterate=True): img = self.prints[image] - with Connection(self.sockaddr) as con: - mem = img.get_data() - mem = mem.tobytes() - assert len(mem) == img.get_width() * img.get_height() - encoded_img = struct.pack('ii', img.get_width(), img.get_height()) - encoded_img += mem + mem = img.get_data() + mem = mem.tobytes() + assert len(mem) == img.get_width() * img.get_height() - con.sendall(encoded_img) + encoded_img = struct.pack('ii', img.get_width(), img.get_height()) + encoded_img += mem + + self.con.sendall(encoded_img) + while iterate and ctx.pending(): + ctx.iteration(False) def test_capture_prevents_close(self): cancel = Gio.Cancellable() From f404a69b73eaba72e5e0d181bec1fcdff127c5e7 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Wed, 4 Dec 2019 20:00:49 +0100 Subject: [PATCH 102/237] tests: Test finger removal after minutiae scan completion --- tests/virtual-image.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/virtual-image.py b/tests/virtual-image.py index 86bd86d4..87c221b2 100755 --- a/tests/virtual-image.py +++ b/tests/virtual-image.py @@ -182,10 +182,16 @@ class VirtualImage(unittest.TestCase): while self._step < 1: ctx.iteration(True) + # Test the image-device path where the finger is removed after + # the minutiae scan is completed. + self.send_finger_automatic(False) + self.send_finger_report(True) self.send_image(image) while self._step < 2: ctx.iteration(True) + self.send_finger_report(False) + self.send_finger_automatic(True) self.send_image(image) while self._step < 3: ctx.iteration(True) From 6209b22e3b8dbfc416e4ac02b96a1e47b0e97ca4 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 5 Dec 2019 10:53:22 +0100 Subject: [PATCH 103/237] print: Fix match error propagation The FPI_MATCH_ERROR constant was set to 0, however it is propagated to the task completion using g_task_propagate_int. As g_task_propagate_int will always return -1 on error, we either need to add an explicit -1 check or we just need to match the semantics. Change the constant to -1, also rearange FP_MATCH_SUCCESS so that it does not end up being 0. --- libfprint/fpi-print.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libfprint/fpi-print.h b/libfprint/fpi-print.h index 94670a02..04500d68 100644 --- a/libfprint/fpi-print.h +++ b/libfprint/fpi-print.h @@ -21,13 +21,13 @@ typedef enum { /** * FpiMatchResult: * @FPI_MATCH_ERROR: An error occured during matching - * @FPI_MATCH_SUCCESS: The prints matched * @FPI_MATCH_FAIL: The prints did not match + * @FPI_MATCH_SUCCESS: The prints matched */ typedef enum { - FPI_MATCH_ERROR = 0, - FPI_MATCH_SUCCESS, + FPI_MATCH_ERROR = -1, /* -1 for g_task_propagate_int */ FPI_MATCH_FAIL, + FPI_MATCH_SUCCESS, } FpiMatchResult; void fpi_print_add_print (FpPrint *print, From 0c7d2d8ecdb5886a0cb2182d538ffa2a1a545e21 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 5 Dec 2019 13:56:12 +0100 Subject: [PATCH 104/237] ci: Define a variable for the fedora docker image --- .gitlab-ci.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f71d3847..cf19872a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,13 +1,15 @@ -image: registry.freedesktop.org/libfprint/libfprint/master:v1 +variables: + FEDORA_IMAGE: registry.freedesktop.org/libfprint/libfprint/master:v1 + BUNDLE: "org.freedesktop.libfprint.Demo.flatpak" + LAST_ABI_BREAK: "056ea541ddc97f5806cffbd99a12dc87e4da3546" + stages: - check-source - build - test - flatpack -variables: - BUNDLE: "org.freedesktop.libfprint.Demo.flatpak" - LAST_ABI_BREAK: "056ea541ddc97f5806cffbd99a12dc87e4da3546" +image: "$FEDORA_IMAGE" .build_one_driver_template: &build_one_driver script: From 9c8360ad671742f6fe61f2d1b368d2cb5743666f Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 5 Dec 2019 13:58:39 +0100 Subject: [PATCH 105/237] ci: Do not run usual targets from a scheduled job This is in preparation to building docker images automatically. --- .gitlab-ci.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cf19872a..e7427e95 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -31,6 +31,9 @@ image: "$FEDORA_IMAGE" build: stage: build + except: + variables: + - $CI_PIPELINE_SOURCE == "schedule" variables: driver: virtual_image <<: *build_one_driver @@ -39,6 +42,9 @@ build: test: stage: test + except: + variables: + - $CI_PIPELINE_SOURCE == "schedule" script: - meson --werror -Ddrivers=all . _build - ninja -C _build @@ -46,6 +52,9 @@ test: test_valgrind: stage: test + except: + variables: + - $CI_PIPELINE_SOURCE == "schedule" script: - meson -Ddrivers=all . _build - ninja -C _build @@ -53,6 +62,9 @@ test_valgrind: test_indent: stage: check-source + except: + variables: + - $CI_PIPELINE_SOURCE == "schedule" script: - scripts/uncrustify.sh --check @@ -82,6 +94,9 @@ test_indent: .flatpak_master_template: &flatpak_master image: registry.gitlab.gnome.org/gnome/gnome-runtime-images/gnome:3.32 stage: flatpack + except: + variables: + - $CI_PIPELINE_SOURCE == "schedule" variables: MANIFEST_PATH: "demo/org.freedesktop.libfprint.Demo.json" # From demo/org.freedesktop.libfprint.Demo.json From a1a393319177d31a316dd27e28fc30f30939acdd Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 5 Dec 2019 14:23:35 +0100 Subject: [PATCH 106/237] ci: Add fedora image builder target for schedule --- .gitlab-ci.yml | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e7427e95..744c84ef 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,8 +1,15 @@ variables: + FEDORA_TAG: rawhide + FEDORA_VERSION: rawhide FEDORA_IMAGE: registry.freedesktop.org/libfprint/libfprint/master:v1 BUNDLE: "org.freedesktop.libfprint.Demo.flatpak" LAST_ABI_BREAK: "056ea541ddc97f5806cffbd99a12dc87e4da3546" +include: + - project: 'wayland/ci-templates' + ref: master + file: '/templates/fedora.yml' + stages: - check-source - build @@ -118,3 +125,37 @@ flatpak-manual master: except: - tags - master + +# CONTAINERS creation stage +container_fedora_build: + extends: .fedora@container-build + only: + variables: + - $CI_PIPELINE_SOURCE == "schedule" && $CRON_TASK == "BUILD_CI_IMAGES" + variables: + GIT_STRATEGY: none # no need to pull the whole tree for rebuilding the image + # a list of packages to install + FEDORA_RPMS: + doxygen + flatpak-builder + gcc + gcc-c++ + git + glib2-devel + glibc-devel + gobject-introspection-devel + gtk-doc + gtk3-devel + libabigail + libgusb-devel + libX11-devel + libXv-devel + meson + nss-devel + pixman-devel + python3-cairo + python3-gobject + systemd + umockdev + uncrustify + valgrind From 1dee7985b996b6607936aa3b3a4a9eff1f342d5f Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 5 Dec 2019 14:24:06 +0100 Subject: [PATCH 107/237] ci: Remove Dockerfile as it is replaced by new logic --- .gitlab-ci/Dockerfile | 36 ------------------------------------ 1 file changed, 36 deletions(-) delete mode 100644 .gitlab-ci/Dockerfile diff --git a/.gitlab-ci/Dockerfile b/.gitlab-ci/Dockerfile deleted file mode 100644 index bf0eb36c..00000000 --- a/.gitlab-ci/Dockerfile +++ /dev/null @@ -1,36 +0,0 @@ -# Rebuild and push with -# -# cd .gitlab-ci/ -# docker build --no-cache -t registry.freedesktop.org/libfprint/libfprint/master:v1 . -# docker push registry.freedesktop.org/libfprint/libfprint/master:v1 -# - -FROM fedora:rawhide - -RUN dnf -y update && dnf -y upgrade && \ - dnf -y install \ - doxygen \ - flatpak-builder \ - gcc \ - gcc-c++ \ - git \ - glib2-devel \ - glibc-devel \ - gobject-introspection-devel \ - gtk-doc \ - gtk3-devel \ - libabigail \ - libgusb-devel \ - libX11-devel \ - libXv-devel \ - meson \ - nss-devel \ - pixman-devel \ - python3-cairo \ - python3-gobject \ - systemd \ - umockdev \ - uncrustify \ - valgrind \ - && \ - dnf clean all From 0a475196e0bc15e261a452e214f766466c8474c2 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 5 Dec 2019 14:30:30 +0100 Subject: [PATCH 108/237] ci: Use CI generated build image --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 744c84ef..d6ca5389 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,7 +1,7 @@ variables: FEDORA_TAG: rawhide FEDORA_VERSION: rawhide - FEDORA_IMAGE: registry.freedesktop.org/libfprint/libfprint/master:v1 + FEDORA_IMAGE: "$CI_REGISTRY_IMAGE/fedora/$FEDORA_VERSION:$FEDORA_TAG" BUNDLE: "org.freedesktop.libfprint.Demo.flatpak" LAST_ABI_BREAK: "056ea541ddc97f5806cffbd99a12dc87e4da3546" From bb0ef04b85ddebb9ee8ed326297708c271efc37f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 5 Dec 2019 17:18:14 +0100 Subject: [PATCH 109/237] ci: Partially hardcode the fedora image path --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d6ca5389..42a39746 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,7 +1,7 @@ variables: FEDORA_TAG: rawhide FEDORA_VERSION: rawhide - FEDORA_IMAGE: "$CI_REGISTRY_IMAGE/fedora/$FEDORA_VERSION:$FEDORA_TAG" + FEDORA_IMAGE: "$CI_REGISTRY/libfprint/$CI_PROJECT_NAME/fedora/$FEDORA_VERSION:$FEDORA_TAG" BUNDLE: "org.freedesktop.libfprint.Demo.flatpak" LAST_ABI_BREAK: "056ea541ddc97f5806cffbd99a12dc87e4da3546" From 107fdfde3221e5619cac8735d4f3d2935e21ec68 Mon Sep 17 00:00:00 2001 From: Vincent Huang Date: Mon, 9 Dec 2019 14:12:54 +0800 Subject: [PATCH 110/237] synaptics: Fix problem after match is failed This fixes the the problem that the sensor becomes unresponsive after pressing the wrong fingerprint. We fix the problem by making sure that the non-match report is delayed until the finger is removed. With this we cannot run into the situation that the next match request fails immediately as the finger is still present. Fixes: #208 --- libfprint/drivers/synaptics/synaptics.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index 247b6581..6ed6791e 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -168,7 +168,7 @@ cmd_recieve_cb (FpiUsbTransfer *transfer, * depending on resp.complete. */ if (self->cmd_pending_transfer) fpi_ssm_jump_to_state (transfer->ssm, SYNAPTICS_CMD_SEND_PENDING); - else if (!resp.complete) + else if (!resp.complete || self->cmd_complete_on_removal) fpi_ssm_next_state (transfer->ssm); /* SYNAPTICS_CMD_WAIT_INTERRUPT */ else fpi_ssm_mark_completed (transfer->ssm); From ff0107fc0a9e4bc4e52bcd497573e7a26bfd8715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 5 Dec 2019 13:16:11 +0100 Subject: [PATCH 111/237] fp-device: Use different pointers for device handlers A Fp-device use an union to track the handle to the lower-level device, and the value depends on the object type. So in case of using a virtual device, the priv->usb_device location matches the priv->virtual_env string location, and thus we'd end up unreffing a string location as it was a GObject, while we'd leak the string. To avoid such errors, instead of just checking the device type when we finalize the device, let's just use different pointers to avoid other possible clashes. --- libfprint/fp-device.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/libfprint/fp-device.c b/libfprint/fp-device.c index 2f706b35..08023f21 100644 --- a/libfprint/fp-device.c +++ b/libfprint/fp-device.c @@ -50,11 +50,8 @@ typedef struct { FpDeviceType type; - union - { - GUsbDevice *usb_device; - const gchar *virtual_env; - }; + GUsbDevice *usb_device; + const gchar *virtual_env; gboolean is_open; @@ -382,7 +379,9 @@ fp_device_finalize (GObject *object) g_clear_pointer (&priv->device_id, g_free); g_clear_pointer (&priv->device_name, g_free); + g_clear_object (&priv->usb_device); + g_clear_pointer (&priv->virtual_env, g_free); G_OBJECT_CLASS (fp_device_parent_class)->finalize (object); } From ad88a5a78f91fb71eaafb52b6dc2761f960d19bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 5 Dec 2019 14:23:11 +0100 Subject: [PATCH 112/237] docs: Don't ignore the deprecated API_EXPORTED definition --- doc/meson.build | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/meson.build b/doc/meson.build index 407413a9..bed320dc 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -32,7 +32,6 @@ gnome.gtkdoc('libfprint', expand_content_files: expand_content_files, scan_args: [ #'--rebuild-sections', - '--ignore-decorators=API_EXPORTED', '--ignore-headers=' + ' '.join(private_headers), ], fixxref_args: [ From d01bb41b7cf63e8e78be281f794cae9271b804ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 5 Dec 2019 14:39:45 +0100 Subject: [PATCH 113/237] tests/meson: Set the typelib env var only if we have introspection --- tests/meson.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/meson.build b/tests/meson.build index d6196bee..6e56cb39 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -5,7 +5,6 @@ envs.set('G_MESSAGES_DEBUG', 'all') # Setup paths envs.set('MESON_SOURCE_ROOT', meson.build_root()) -envs.prepend('GI_TYPELIB_PATH', join_paths(meson.build_root(), 'libfprint')) envs.prepend('LD_LIBRARY_PATH', join_paths(meson.build_root(), 'libfprint')) # Set FP_DEVICE_EMULATION so that drivers can adapt (e.g. to use fixed @@ -15,6 +14,8 @@ envs.set('FP_DEVICE_EMULATION', '1') envs.set('NO_AT_BRIDGE', '1') if get_option('introspection') + envs.prepend('GI_TYPELIB_PATH', join_paths(meson.build_root(), 'libfprint')) + if 'virtual_image' in drivers test('virtual-image', find_program('virtual-image.py'), From 92a5278a740903f30967b31924a2158e41b8610b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 5 Dec 2019 15:05:59 +0100 Subject: [PATCH 114/237] fp-device: Add a "open" property and method to check its state --- libfprint/fp-device.c | 33 ++++++++++++++++++++++++++++++++- libfprint/fp-device.h | 1 + 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/libfprint/fp-device.c b/libfprint/fp-device.c index 08023f21..91a3aae6 100644 --- a/libfprint/fp-device.c +++ b/libfprint/fp-device.c @@ -88,6 +88,7 @@ enum { PROP_DRIVER, PROP_DEVICE_ID, PROP_NAME, + PROP_OPEN, PROP_NR_ENROLL_STAGES, PROP_SCAN_TYPE, PROP_FPI_ENVIRON, @@ -417,6 +418,10 @@ fp_device_get_property (GObject *object, g_value_set_string (value, priv->device_name); break; + case PROP_OPEN: + g_value_set_boolean (value, priv->is_open); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -551,6 +556,12 @@ fp_device_class_init (FpDeviceClass *klass) NULL, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); + properties[PROP_OPEN] = + g_param_spec_boolean ("open", + "Opened", + "Wether the device is open or not", FALSE, + G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); + properties[PROP_FPI_ENVIRON] = g_param_spec_string ("fp-environ", "Virtual Environment", @@ -628,6 +639,22 @@ fp_device_get_name (FpDevice *device) return priv->device_name; } +/** + * fp_device_is_open: + * @device: A #FpDevice + * + * Returns: Whether the device is open or not + */ +gboolean +fp_device_is_open (FpDevice *device) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + + g_return_val_if_fail (FP_IS_DEVICE (device), FALSE); + + return priv->is_open; +} + /** * fp_device_get_scan_type: * @device: A #FpDevice @@ -1959,7 +1986,10 @@ fpi_device_open_complete (FpDevice *device, GError *error) clear_device_cancel_action (device); if (!error) - priv->is_open = TRUE; + { + priv->is_open = TRUE; + g_object_notify_by_pspec (G_OBJECT (device), properties[PROP_OPEN]); + } if (!error) fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL, @@ -1988,6 +2018,7 @@ fpi_device_close_complete (FpDevice *device, GError *error) clear_device_cancel_action (device); priv->is_open = FALSE; + g_object_notify_by_pspec (G_OBJECT (device), properties[PROP_OPEN]); switch (priv->type) { diff --git a/libfprint/fp-device.h b/libfprint/fp-device.h index a15fc308..4f7acaca 100644 --- a/libfprint/fp-device.h +++ b/libfprint/fp-device.h @@ -129,6 +129,7 @@ typedef void (*FpEnrollProgress) (FpDevice *device, const gchar *fp_device_get_driver (FpDevice *device); const gchar *fp_device_get_device_id (FpDevice *device); const gchar *fp_device_get_name (FpDevice *device); +gboolean fp_device_is_open (FpDevice *device); FpScanType fp_device_get_scan_type (FpDevice *device); gint fp_device_get_nr_enroll_stages (FpDevice *device); From 7114d97f25de68e9a338cc9b646c435c2134a7d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 5 Dec 2019 14:27:33 +0100 Subject: [PATCH 115/237] libfprint: Introduce libfprint_private static library Split the library into a private part with all the symbols that we can use for unit-test all the fpi functions. --- libfprint/meson.build | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/libfprint/meson.build b/libfprint/meson.build index f73aba3b..1e98e2df 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -4,6 +4,9 @@ libfprint_sources = [ 'fp-image.c', 'fp-print.c', 'fp-image-device.c', +] + +libfprint_private_sources = [ 'fpi-assembling.c', 'fpi-ssm.c', 'fpi-usb-transfer.c', @@ -200,15 +203,19 @@ libnbis = static_library('nbis', ]), install: false) +libfprint_private = static_library('fprint-private', + sources: libfprint_private_sources + fpi_enums, + dependencies: deps, + install: false) + libfprint = library('fprint', - libfprint_sources + fp_enums + fpi_enums + - drivers_sources + other_sources, + sources: libfprint_sources + fp_enums + drivers_sources + other_sources, soversion: soversion, version: libversion, c_args: drivers_cflags, link_args : vflag, link_depends : mapfile, - link_with: libnbis, + link_with: [libnbis, libfprint_private], dependencies: deps, install: true) From d9de941a47a46d0aabf8405a3309b5261391ec74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 10 Dec 2019 20:24:04 +0100 Subject: [PATCH 116/237] fp-device: Move fpi code into its own unit that can be compiled a part In order to be able to test the private device code (used by drivers) we need to have that split a part in a different .c file so that we can compile it alone and link with it both the shared library and the test executables. Redefine fp_device_get_instance_private for private usage, not to move the private struct as part of FpDevice. --- libfprint/fp-device-private.h | 65 ++ libfprint/fp-device.c | 1186 +-------------------------------- libfprint/fpi-device.c | 1177 ++++++++++++++++++++++++++++++++ libfprint/meson.build | 1 + 4 files changed, 1245 insertions(+), 1184 deletions(-) create mode 100644 libfprint/fp-device-private.h create mode 100644 libfprint/fpi-device.c diff --git a/libfprint/fp-device-private.h b/libfprint/fp-device-private.h new file mode 100644 index 00000000..65fb1cb1 --- /dev/null +++ b/libfprint/fp-device-private.h @@ -0,0 +1,65 @@ +/* + * FpDevice - A fingerprint reader device + * Copyright (C) 2019 Benjamin Berg + * Copyright (C) 2019 Marco Trevisan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +#include "fpi-device.h" + +typedef struct +{ + FpDeviceType type; + + GUsbDevice *usb_device; + const gchar *virtual_env; + + gboolean is_open; + + gchar *device_id; + gchar *device_name; + FpScanType scan_type; + + guint64 driver_data; + + gint nr_enroll_stages; + GSList *sources; + + /* We always make sure that only one task is run at a time. */ + FpDeviceAction current_action; + GTask *current_task; + GAsyncReadyCallback current_user_cb; + gulong current_cancellable_id; + GSource *current_idle_cancel_source; + GSource *current_task_idle_return_source; + + /* State for tasks */ + gboolean wait_for_finger; +} FpDevicePrivate; + + +typedef struct +{ + FpPrint *print; + + FpEnrollProgress enroll_progress_cb; + gpointer enroll_progress_data; + GDestroyNotify enroll_progress_destroy; +} FpEnrollData; + +void enroll_data_free (FpEnrollData *enroll_data); diff --git a/libfprint/fp-device.c b/libfprint/fp-device.c index 91a3aae6..c49e5a92 100644 --- a/libfprint/fp-device.c +++ b/libfprint/fp-device.c @@ -21,17 +21,7 @@ #define FP_COMPONENT "device" #include "fpi-log.h" -#include "fpi-device.h" - -/** - * SECTION: fp-device - * @title: FpDevice - * @short_description: Fingerprint device handling - * - * The #FpDevice object allows you to interact with fingerprint readers. - * Befor doing any other operation you need to fp_device_open() the device - * and after you are done you need to fp_device_close() it again. - */ +#include "fp-device-private.h" /** * SECTION: fpi-device @@ -46,36 +36,6 @@ * Also see the public #FpDevice routines. */ -typedef struct -{ - FpDeviceType type; - - GUsbDevice *usb_device; - const gchar *virtual_env; - - gboolean is_open; - - gchar *device_id; - gchar *device_name; - FpScanType scan_type; - - guint64 driver_data; - - gint nr_enroll_stages; - GSList *sources; - - /* We always make sure that only one task is run at a time. */ - FpDeviceAction current_action; - GTask *current_task; - GAsyncReadyCallback current_user_cb; - gulong current_cancellable_id; - GSource *current_idle_cancel_source; - GSource *current_task_idle_return_source; - - /* State for tasks */ - gboolean wait_for_finger; -} FpDevicePrivate; - static void fp_device_async_initable_iface_init (GAsyncInitableIface *iface); G_DEFINE_TYPE_EXTENDED (FpDevice, fp_device, G_TYPE_OBJECT, G_TYPE_FLAG_ABSTRACT, @@ -99,27 +59,6 @@ enum { static GParamSpec *properties[N_PROPS]; -typedef struct -{ - FpPrint *print; - - FpEnrollProgress enroll_progress_cb; - gpointer enroll_progress_data; - GDestroyNotify enroll_progress_destroy; -} FpEnrollData; - -static void -enroll_data_free (gpointer free_data) -{ - FpEnrollData *data = free_data; - - if (data->enroll_progress_destroy) - data->enroll_progress_destroy (data->enroll_progress_data); - data->enroll_progress_data = NULL; - g_clear_object (&data->print); - g_free (data); -} - /** * fp_device_retry_quark: * @@ -134,150 +73,6 @@ G_DEFINE_QUARK (fp - device - retry - quark, fp_device_retry) **/ G_DEFINE_QUARK (fp - device - error - quark, fp_device_error) -/** - * fpi_device_retry_new: - * @error: The #FpDeviceRetry error value describing the issue - * - * Create a new retry error code for use with fpi_device_verify_complete() - * and similar calls. - */ -GError * -fpi_device_retry_new (FpDeviceRetry error) -{ - const gchar *msg; - - switch (error) - { - case FP_DEVICE_RETRY_GENERAL: - msg = "Please try again."; - break; - - case FP_DEVICE_RETRY_TOO_SHORT: - msg = "The swipe was too short, please try again."; - break; - - case FP_DEVICE_RETRY_CENTER_FINGER: - msg = "The finger was not centered properly, please try again."; - break; - - case FP_DEVICE_RETRY_REMOVE_FINGER: - msg = "Please try again after removing the finger first."; - break; - - default: - g_warning ("Unsupported error, returning general error instead!"); - error = FP_DEVICE_RETRY_GENERAL; - msg = "Please try again."; - } - - return g_error_new_literal (FP_DEVICE_RETRY, error, msg); -} - -/** - * fpi_device_error_new: - * @error: The #FpDeviceRetry error value describing the issue - * - * Create a new error code for use with fpi_device_verify_complete() and - * similar calls. - */ -GError * -fpi_device_error_new (FpDeviceError error) -{ - const gchar *msg; - - switch (error) - { - case FP_DEVICE_ERROR_GENERAL: - msg = "An unspecified error occured!"; - break; - - case FP_DEVICE_ERROR_NOT_SUPPORTED: - msg = "The operation is not supported on this device!"; - break; - - case FP_DEVICE_ERROR_NOT_OPEN: - msg = "The device needs to be opened first!"; - break; - - case FP_DEVICE_ERROR_ALREADY_OPEN: - msg = "The device has already been opened!"; - break; - - case FP_DEVICE_ERROR_BUSY: - msg = "The device is still busy with another operation, please try again later."; - break; - - case FP_DEVICE_ERROR_PROTO: - msg = "The driver encountered a protocol error with the device."; - break; - - case FP_DEVICE_ERROR_DATA_INVALID: - msg = "Passed (print) data is not valid."; - break; - - case FP_DEVICE_ERROR_DATA_FULL: - msg = "On device storage space is full."; - break; - - case FP_DEVICE_ERROR_DATA_NOT_FOUND: - msg = "Print was not found on the devices storage."; - break; - - default: - g_warning ("Unsupported error, returning general error instead!"); - error = FP_DEVICE_ERROR_GENERAL; - msg = "An unspecified error occured!"; - } - - return g_error_new_literal (FP_DEVICE_ERROR, error, msg); -} - -/** - * fpi_device_retry_new_msg: - * @error: The #FpDeviceRetry error value describing the issue - * @msg: Custom message to use - * - * Create a new retry error code for use with fpi_device_verify_complete() - * and similar calls. - */ -GError * -fpi_device_retry_new_msg (FpDeviceRetry device_error, - const gchar *msg, - ...) -{ - GError *error; - va_list args; - - va_start (args, msg); - error = g_error_new_valist (FP_DEVICE_RETRY, device_error, msg, args); - va_end (args); - - return error; -} - -/** - * fpi_device_error_new_msg: - * @error: The #FpDeviceRetry error value describing the issue - * @msg: Custom message to use - * - * Create a new error code for use with fpi_device_verify_complete() - * and similar calls. - */ -GError * -fpi_device_error_new_msg (FpDeviceError device_error, - const gchar *msg, - ...) -{ - GError *error; - va_list args; - - va_start (args, msg); - error = g_error_new_valist (FP_DEVICE_ERROR, device_error, msg, args); - va_end (args); - - return error; -} - static gboolean fp_device_cancel_in_idle_cb (gpointer user_data) { @@ -330,21 +125,6 @@ maybe_cancel_on_cancelled (FpDevice *device, NULL); } -static void -clear_device_cancel_action (FpDevice *device) -{ - FpDevicePrivate *priv = fp_device_get_instance_private (device); - - g_clear_pointer (&priv->current_idle_cancel_source, g_source_destroy); - - if (priv->current_cancellable_id) - { - g_cancellable_disconnect (g_task_get_cancellable (priv->current_task), - priv->current_cancellable_id); - priv->current_cancellable_id = 0; - } -} - static void fp_device_constructed (GObject *object) { @@ -976,7 +756,7 @@ fp_device_enroll (FpDevice *device, data->enroll_progress_data = progress_data; // Attach the progress data as task data so that it is destroyed - g_task_set_task_data (priv->current_task, data, enroll_data_free); + g_task_set_task_data (priv->current_task, data, (GDestroyNotify) enroll_data_free); FP_DEVICE_GET_CLASS (device)->enroll (device); } @@ -1406,968 +1186,6 @@ fp_device_list_prints_finish (FpDevice *device, return g_task_propagate_pointer (G_TASK (result), error); } -typedef struct -{ - GSource source; - FpDevice *device; -} FpDeviceTimeoutSource; - -static void -timeout_finalize (GSource *source) -{ - FpDeviceTimeoutSource *timeout_source = (FpDeviceTimeoutSource *) source; - FpDevicePrivate *priv; - - priv = fp_device_get_instance_private (timeout_source->device); - priv->sources = g_slist_remove (priv->sources, source); -} - -static gboolean -timeout_dispatch (GSource *source, GSourceFunc gsource_func, gpointer user_data) -{ - FpDeviceTimeoutSource *timeout_source = (FpDeviceTimeoutSource *) source; - FpTimeoutFunc callback = (FpTimeoutFunc) gsource_func; - - callback (timeout_source->device, user_data); - - return G_SOURCE_REMOVE; -} - -static GSourceFuncs timeout_funcs = { - NULL, /* prepare */ - NULL, /* check */ - timeout_dispatch, - timeout_finalize, - NULL, NULL -}; - -/* Private API functions */ - -/** - * fpi_device_set_nr_enroll_stages: - * @device: The #FpDevice - * @enroll_stages: The number of enroll stages - * - * Updates the reported number of enroll stages that the device needs. - * If all supported devices have the same number of stages, then the - * value can simply be set in the class. - */ -void -fpi_device_set_nr_enroll_stages (FpDevice *device, - gint enroll_stages) -{ - FpDevicePrivate *priv = fp_device_get_instance_private (device); - - g_return_if_fail (FP_IS_DEVICE (device)); - - priv->nr_enroll_stages = enroll_stages; - g_object_notify_by_pspec (G_OBJECT (device), properties[PROP_NR_ENROLL_STAGES]); -} - -/** - * fpi_device_set_scan_type: - * @device: The #FpDevice - * @scan_type: The scan type of the device - * - * Updates the the scan type of the device from the default. - * If all supported devices have the same scan type, then the - * value can simply be set in the class. - */ -void -fpi_device_set_scan_type (FpDevice *device, - FpScanType scan_type) -{ - FpDevicePrivate *priv = fp_device_get_instance_private (device); - - g_return_if_fail (FP_IS_DEVICE (device)); - - priv->scan_type = scan_type; - g_object_notify_by_pspec (G_OBJECT (device), properties[PROP_SCAN_TYPE]); -} - -/** - * fpi_device_add_timeout: - * @device: The #FpDevice - * @interval: The interval in milliseconds - * @func: The #FpTimeoutFunc to call on timeout - * @user_data: (nullable): User data to pass to the callback - * @destroy_notify: (nullable): #GDestroyNotify for @user_data - * - * Register a timeout to run. Drivers should always make sure that timers are - * cancelled when appropriate. - * - * Returns: (transfer none): A newly created and attached #GSource - */ -GSource * -fpi_device_add_timeout (FpDevice *device, - gint interval, - FpTimeoutFunc func, - gpointer user_data, - GDestroyNotify destroy_notify) -{ - FpDevicePrivate *priv = fp_device_get_instance_private (device); - FpDeviceTimeoutSource *source; - - source = (FpDeviceTimeoutSource *) g_source_new (&timeout_funcs, - sizeof (FpDeviceTimeoutSource)); - source->device = device; - - g_source_attach (&source->source, NULL); - g_source_set_callback (&source->source, (GSourceFunc) func, user_data, destroy_notify); - g_source_set_ready_time (&source->source, - g_source_get_time (&source->source) + interval * (guint64) 1000); - priv->sources = g_slist_prepend (priv->sources, source); - g_source_unref (&source->source); - - return &source->source; -} - -/** - * fpi_device_get_usb_device: - * @device: The #FpDevice - * - * Get the #GUsbDevice for this #FpDevice. Only permissible to call if the - * #FpDevice is of type %FP_DEVICE_TYPE_USB. - * - * Returns: The #GUsbDevice - */ -GUsbDevice * -fpi_device_get_usb_device (FpDevice *device) -{ - FpDevicePrivate *priv = fp_device_get_instance_private (device); - - g_return_val_if_fail (FP_IS_DEVICE (device), NULL); - g_return_val_if_fail (priv->type == FP_DEVICE_TYPE_USB, NULL); - - return priv->usb_device; -} - -/** - * fpi_device_get_virtual_env: - * @device: The #FpDevice - * - * Get the value of the environment variable that caused the virtual #FpDevice to be - * generated. Only permissible to call if the #FpDevice is of type %FP_DEVICE_TYPE_VIRTUAL. - * - * Returns: The value of the environment variable - */ -const gchar * -fpi_device_get_virtual_env (FpDevice *device) -{ - FpDevicePrivate *priv = fp_device_get_instance_private (device); - - g_return_val_if_fail (FP_IS_DEVICE (device), NULL); - g_return_val_if_fail (priv->type == FP_DEVICE_TYPE_VIRTUAL, NULL); - - return priv->virtual_env; -} - -/** - * fpi_device_get_current_action: - * @device: The #FpDevice - * - * Get the currently ongoing action or %FP_DEVICE_ACTION_NONE if there - * is no operation at this time. - * - * This is useful for drivers that might share code paths between different - * actions (e.g. verify and identify) and want to find out again later which - * action was started in the beginning. - * - * Returns: The ongoing #FpDeviceAction - */ -FpDeviceAction -fpi_device_get_current_action (FpDevice *device) -{ - FpDevicePrivate *priv = fp_device_get_instance_private (device); - - g_return_val_if_fail (FP_IS_DEVICE (device), FP_DEVICE_ACTION_NONE); - - return priv->current_action; -} - -/** - * fpi_device_action_is_cancelled: - * @device: The #FpDevice - * - * Checks whether the current action has been cancelled by the user. - * This is equivalent to first getting the cancellable using - * fpi_device_get_cancellable() and then checking whether it has been - * cancelled (if it is non-NULL). - * - * Returns: %TRUE if action should be cancelled - */ -gboolean -fpi_device_action_is_cancelled (FpDevice *device) -{ - FpDevicePrivate *priv = fp_device_get_instance_private (device); - GCancellable *cancellable; - - g_return_val_if_fail (FP_IS_DEVICE (device), TRUE); - g_return_val_if_fail (priv->current_action != FP_DEVICE_ACTION_NONE, TRUE); - - cancellable = g_task_get_cancellable (priv->current_task); - - return cancellable ? g_cancellable_is_cancelled (cancellable) : FALSE; -} - -/** - * fpi_device_get_driver_data: - * @device: The #FpDevice - * - * Returns: The driver data from the #FpIdEntry table entry - */ -guint64 -fpi_device_get_driver_data (FpDevice *device) -{ - FpDevicePrivate *priv = fp_device_get_instance_private (device); - - g_return_val_if_fail (FP_IS_DEVICE (device), 0); - - return priv->driver_data; -} - -/** - * fpi_device_get_enroll_data: - * @device: The #FpDevice - * @print: (out) (transfer none): The user provided template print - * - * Get data for enrollment. - */ -void -fpi_device_get_enroll_data (FpDevice *device, - FpPrint **print) -{ - FpDevicePrivate *priv = fp_device_get_instance_private (device); - FpEnrollData *data; - - g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_ENROLL); - - data = g_task_get_task_data (priv->current_task); - g_assert (data); - - if (print) - *print = data->print; -} - -/** - * fpi_device_get_capture_data: - * @device: The #FpDevice - * @wait_for_finger: (out): Whether to wait for finger or not - * - * Get data for capture. - */ -void -fpi_device_get_capture_data (FpDevice *device, - gboolean *wait_for_finger) -{ - FpDevicePrivate *priv = fp_device_get_instance_private (device); - - g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_CAPTURE); - - if (wait_for_finger) - *wait_for_finger = priv->wait_for_finger; -} - -/** - * fpi_device_get_verify_data: - * @device: The #FpDevice - * @print: (out) (transfer none): The enrolled print - * - * Get data for verify. - */ -void -fpi_device_get_verify_data (FpDevice *device, - FpPrint **print) -{ - FpDevicePrivate *priv = fp_device_get_instance_private (device); - - g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_VERIFY); - - if (print) - *print = g_task_get_task_data (priv->current_task); -} - -/** - * fpi_device_get_identify_data: - * @device: The #FpDevice - * @prints: (out) (transfer none) (element-type FpPrint): The gallery of prints - * - * Get data for identify. - */ -void -fpi_device_get_identify_data (FpDevice *device, - GPtrArray **prints) -{ - FpDevicePrivate *priv = fp_device_get_instance_private (device); - - g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_IDENTIFY); - - if (prints) - *prints = g_task_get_task_data (priv->current_task); -} - -/** - * fpi_device_get_delete_data: - * @device: The #FpDevice - * @print: (out) (transfer none): The print to delete - * - * Get data for delete. - */ -void -fpi_device_get_delete_data (FpDevice *device, - FpPrint **print) -{ - FpDevicePrivate *priv = fp_device_get_instance_private (device); - - g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_DELETE); - - if (print) - *print = g_task_get_task_data (priv->current_task); -} - -/** - * fpi_device_get_cancellable: - * @device: The #FpDevice - * - * Retrieve the #GCancellable that may cancel the currently ongoing operation. This - * is primarily useful to pass directly to e.g. fpi_usb_transfer_submit() for cancellable - * transfers. - * In many cases the cancel vfunc may be more convenient to react to cancellation in some - * way. - * - * Returns: (transfer none): The #GCancellable for the current action. - */ -GCancellable * -fpi_device_get_cancellable (FpDevice *device) -{ - FpDevicePrivate *priv = fp_device_get_instance_private (device); - - g_return_val_if_fail (FP_IS_DEVICE (device), NULL); - g_return_val_if_fail (priv->current_action != FP_DEVICE_ACTION_NONE, NULL); - - return g_task_get_cancellable (priv->current_task); -} - -/** - * fpi_device_action_error: - * @device: The #FpDevice - * @error: The #GError to return - * - * Finish an ongoing action with an error. This is the same as calling - * the corresponding complete function such as fpi_device_open_complete() - * with an error set. If possible, use the correct complete function as - * that results in improved error detection. - */ -void -fpi_device_action_error (FpDevice *device, - GError *error) -{ - FpDevicePrivate *priv = fp_device_get_instance_private (device); - - g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action != FP_DEVICE_ACTION_NONE); - - if (error != NULL) - { - g_debug ("Device reported generic error during action; action was: %i", priv->current_action); - } - else - { - g_warning ("Device failed to pass an error to generic action error function"); - error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, "Device reported error but did not provide an error condition"); - } - - - switch (priv->current_action) - { - case FP_DEVICE_ACTION_PROBE: - fpi_device_probe_complete (device, NULL, NULL, error); - break; - - case FP_DEVICE_ACTION_OPEN: - fpi_device_open_complete (device, error); - break; - - case FP_DEVICE_ACTION_CLOSE: - fpi_device_close_complete (device, error); - break; - - case FP_DEVICE_ACTION_ENROLL: - fpi_device_enroll_complete (device, NULL, error); - break; - - case FP_DEVICE_ACTION_VERIFY: - fpi_device_verify_complete (device, FPI_MATCH_ERROR, NULL, error); - break; - - case FP_DEVICE_ACTION_IDENTIFY: - fpi_device_identify_complete (device, NULL, NULL, error); - break; - - case FP_DEVICE_ACTION_CAPTURE: - fpi_device_capture_complete (device, NULL, error); - break; - - case FP_DEVICE_ACTION_DELETE: - fpi_device_delete_complete (device, error); - break; - - case FP_DEVICE_ACTION_LIST: - fpi_device_list_complete (device, NULL, error); - break; - - default: - case FP_DEVICE_ACTION_NONE: - g_return_if_reached (); - break; - } -} - -typedef enum _FpDeviceTaskReturnType { - FP_DEVICE_TASK_RETURN_INT, - FP_DEVICE_TASK_RETURN_BOOL, - FP_DEVICE_TASK_RETURN_OBJECT, - FP_DEVICE_TASK_RETURN_PTR_ARRAY, - FP_DEVICE_TASK_RETURN_ERROR, -} FpDeviceTaskReturnType; - -typedef struct _FpDeviceTaskReturnData -{ - FpDevice *device; - FpDeviceTaskReturnType type; - gpointer result; -} FpDeviceTaskReturnData; - -static gboolean -fp_device_task_return_in_idle_cb (gpointer user_data) -{ - FpDeviceTaskReturnData *data = user_data; - FpDevicePrivate *priv = fp_device_get_instance_private (data->device); - - g_autoptr(GTask) task = NULL; - - g_debug ("Completing action %d in idle!", priv->current_action); - - task = g_steal_pointer (&priv->current_task); - priv->current_action = FP_DEVICE_ACTION_NONE; - priv->current_task_idle_return_source = NULL; - - switch (data->type) - { - case FP_DEVICE_TASK_RETURN_INT: - g_task_return_int (task, GPOINTER_TO_INT (data->result)); - break; - - case FP_DEVICE_TASK_RETURN_BOOL: - g_task_return_boolean (task, GPOINTER_TO_UINT (data->result)); - break; - - case FP_DEVICE_TASK_RETURN_OBJECT: - g_task_return_pointer (task, data->result, g_object_unref); - break; - - case FP_DEVICE_TASK_RETURN_PTR_ARRAY: - g_task_return_pointer (task, data->result, - (GDestroyNotify) g_ptr_array_unref); - break; - - case FP_DEVICE_TASK_RETURN_ERROR: - g_task_return_error (task, data->result); - break; - - default: - g_assert_not_reached (); - } - - return G_SOURCE_REMOVE; -} - -static void -fp_device_task_return_data_free (FpDeviceTaskReturnData *data) -{ - g_object_unref (data->device); - g_free (data); -} - -static void -fp_device_return_task_in_idle (FpDevice *device, - FpDeviceTaskReturnType return_type, - gpointer return_data) -{ - FpDevicePrivate *priv = fp_device_get_instance_private (device); - FpDeviceTaskReturnData *data; - - data = g_new0 (FpDeviceTaskReturnData, 1); - data->device = g_object_ref (device); - data->type = return_type; - data->result = return_data; - - priv->current_task_idle_return_source = g_idle_source_new (); - g_source_set_priority (priv->current_task_idle_return_source, - g_task_get_priority (priv->current_task)); - g_source_set_callback (priv->current_task_idle_return_source, - fp_device_task_return_in_idle_cb, - data, - (GDestroyNotify) fp_device_task_return_data_free); - - g_source_attach (priv->current_task_idle_return_source, NULL); - g_source_unref (priv->current_task_idle_return_source); -} - -/** - * fpi_device_probe_complete: - * @device: The #FpDevice - * @device_id: Unique ID for the device or %NULL - * @device_name: Human readable name or %NULL for driver name - * @error: The #GError or %NULL on success - * - * Finish an ongoing probe operation. If error is %NULL success is assumed. - */ -void -fpi_device_probe_complete (FpDevice *device, - const gchar *device_id, - const gchar *device_name, - GError *error) -{ - FpDevicePrivate *priv = fp_device_get_instance_private (device); - - g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_PROBE); - - g_debug ("Device reported probe completion"); - - clear_device_cancel_action (device); - - if (!error) - { - if (device_id) - { - g_clear_pointer (&priv->device_id, g_free); - priv->device_id = g_strdup (device_id); - g_object_notify_by_pspec (G_OBJECT (device), properties[PROP_DEVICE_ID]); - } - if (device_name) - { - g_clear_pointer (&priv->device_name, g_free); - priv->device_name = g_strdup (device_name); - g_object_notify_by_pspec (G_OBJECT (device), properties[PROP_NAME]); - } - fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL, - GUINT_TO_POINTER (TRUE)); - } - else - { - fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); - } -} - -/** - * fpi_device_open_complete: - * @device: The #FpDevice - * @error: The #GError or %NULL on success - * - * Finish an ongoing open operation. If error is %NULL success is assumed. - */ -void -fpi_device_open_complete (FpDevice *device, GError *error) -{ - FpDevicePrivate *priv = fp_device_get_instance_private (device); - - g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_OPEN); - - g_debug ("Device reported open completion"); - - clear_device_cancel_action (device); - - if (!error) - { - priv->is_open = TRUE; - g_object_notify_by_pspec (G_OBJECT (device), properties[PROP_OPEN]); - } - - if (!error) - fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL, - GUINT_TO_POINTER (TRUE)); - else - fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); -} - -/** - * fpi_device_close_complete: - * @device: The #FpDevice - * @error: The #GError or %NULL on success - * - * Finish an ongoing close operation. If error is %NULL success is assumed. - */ -void -fpi_device_close_complete (FpDevice *device, GError *error) -{ - GError *nested_error = NULL; - FpDevicePrivate *priv = fp_device_get_instance_private (device); - - g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_CLOSE); - - g_debug ("Device reported close completion"); - - clear_device_cancel_action (device); - priv->is_open = FALSE; - g_object_notify_by_pspec (G_OBJECT (device), properties[PROP_OPEN]); - - switch (priv->type) - { - case FP_DEVICE_TYPE_USB: - if (!g_usb_device_close (priv->usb_device, &nested_error)) - { - if (error == NULL) - error = nested_error; - fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); - return; - } - break; - - case FP_DEVICE_TYPE_VIRTUAL: - break; - - default: - g_assert_not_reached (); - fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, - fpi_device_error_new (FP_DEVICE_ERROR_GENERAL)); - return; - } - - if (!error) - fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL, - GUINT_TO_POINTER (TRUE)); - else - fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); -} - -/** - * fpi_device_enroll_complete: - * @device: The #FpDevice - * @print: (nullable) (transfer full): The #FpPrint or %NULL on failure - * @error: The #GError or %NULL on success - * - * Finish an ongoing enroll operation. The #FpPrint can be stored by the - * caller for later verification. - */ -void -fpi_device_enroll_complete (FpDevice *device, FpPrint *print, GError *error) -{ - FpDevicePrivate *priv = fp_device_get_instance_private (device); - - g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_ENROLL); - - g_debug ("Device reported enroll completion"); - - clear_device_cancel_action (device); - - if (!error) - { - if (FP_IS_PRINT (print)) - { - fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_OBJECT, print); - } - else - { - g_warning ("Driver did not provide a valid print and failed to provide an error!"); - error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, - "Driver failed to provide print data!"); - fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); - } - } - else - { - fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); - if (FP_IS_PRINT (print)) - { - g_warning ("Driver passed an error but also provided a print, returning error!"); - g_object_unref (print); - } - } -} - -/** - * fpi_device_verify_complete: - * @device: The #FpDevice - * @result: The #FpiMatchResult of the operation - * @print: The scanned #FpPrint - * @error: A #GError if result is %FPI_MATCH_ERROR - * - * Finish an ongoing verify operation. The returned print should be - * representing the new scan and not the one passed for verification. - */ -void -fpi_device_verify_complete (FpDevice *device, - FpiMatchResult result, - FpPrint *print, - GError *error) -{ - FpDevicePrivate *priv = fp_device_get_instance_private (device); - - g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_VERIFY); - - g_debug ("Device reported verify completion"); - - clear_device_cancel_action (device); - - g_object_set_data_full (G_OBJECT (priv->current_task), - "print", - print, - g_object_unref); - - if (!error) - { - if (result != FPI_MATCH_ERROR) - { - fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_INT, - GINT_TO_POINTER (result)); - } - else - { - g_warning ("Driver did not provide an error for a failed verify operation!"); - error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, - "Driver failed to provide an error!"); - fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); - } - } - else - { - fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); - if (result != FPI_MATCH_ERROR) - { - g_warning ("Driver passed an error but also provided a match result, returning error!"); - g_object_unref (print); - } - } -} - -/** - * fpi_device_identify_complete: - * @device: The #FpDevice - * @match: The matching #FpPrint from the passed gallery, or %NULL if none matched - * @print: The scanned #FpPrint, may be %NULL - * @error: The #GError or %NULL on success - * - * Finish an ongoing identify operation. The match that was identified is - * returned in @match. The @print parameter returns the newly created scan - * that was used for matching. - */ -void -fpi_device_identify_complete (FpDevice *device, - FpPrint *match, - FpPrint *print, - GError *error) -{ - FpDevicePrivate *priv = fp_device_get_instance_private (device); - - g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_IDENTIFY); - - g_debug ("Device reported identify completion"); - - clear_device_cancel_action (device); - - g_object_set_data_full (G_OBJECT (priv->current_task), - "print", - print, - g_object_unref); - g_object_set_data_full (G_OBJECT (priv->current_task), - "match", - match, - g_object_unref); - if (!error) - { - fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL, - GUINT_TO_POINTER (TRUE)); - } - else - { - fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); - if (match) - { - g_warning ("Driver passed an error but also provided a match result, returning error!"); - g_clear_object (&match); - } - } -} - - -/** - * fpi_device_capture_complete: - * @device: The #FpDevice - * @image: The #FpImage, or %NULL on error - * @error: The #GError or %NULL on success - * - * Finish an ongoing capture operation. - */ -void -fpi_device_capture_complete (FpDevice *device, - FpImage *image, - GError *error) -{ - FpDevicePrivate *priv = fp_device_get_instance_private (device); - - g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_CAPTURE); - - g_debug ("Device reported capture completion"); - - clear_device_cancel_action (device); - - if (!error) - { - if (image) - { - fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_OBJECT, image); - } - else - { - g_warning ("Driver did not provide an error for a failed capture operation!"); - error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, - "Driver failed to provide an error!"); - fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); - } - } - else - { - fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); - if (image) - { - g_warning ("Driver passed an error but also provided an image, returning error!"); - g_clear_object (&image); - } - } -} - -/** - * fpi_device_delete_complete: - * @device: The #FpDevice - * @error: The #GError or %NULL on success - * - * Finish an ongoing delete operation. - */ -void -fpi_device_delete_complete (FpDevice *device, - GError *error) -{ - FpDevicePrivate *priv = fp_device_get_instance_private (device); - - g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_DELETE); - - g_debug ("Device reported deletion completion"); - - clear_device_cancel_action (device); - - if (!error) - fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL, - GUINT_TO_POINTER (TRUE)); - else - fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); -} - -/** - * fpi_device_list_complete: - * @device: The #FpDevice - * @prints: (element-type FpPrint) (transfer container): Possibly empty array of prints or %NULL on error - * @error: The #GError or %NULL on success - * - * Finish an ongoing list operation. - * - * Please note that the @prints array will be free'ed using - * g_ptr_array_unref() and the elements are destroyed automatically. - * As such, you must use g_ptr_array_new_with_free_func() with - * g_object_unref() as free func to create the array. - */ -void -fpi_device_list_complete (FpDevice *device, - GPtrArray *prints, - GError *error) -{ - FpDevicePrivate *priv = fp_device_get_instance_private (device); - - g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_LIST); - - g_debug ("Device reported listing completion"); - - clear_device_cancel_action (device); - - if (prints && error) - { - g_warning ("Driver reported back prints and error, ignoring prints"); - g_clear_pointer (&prints, g_ptr_array_unref); - } - else if (!prints && !error) - { - g_warning ("Driver did not pass array but failed to provide an error"); - error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, - "Driver failed to provide a list of prints"); - } - - if (!error) - fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_PTR_ARRAY, prints); - else - fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); -} - -/** - * fpi_device_enroll_progress: - * @device: The #FpDevice - * @completed_stages: The number of stages that are completed at this point - * @print: The #FpPrint for the newly completed stage or %NULL on failure - * @error: The #GError or %NULL on success - * - * Notify about the progress of the enroll operation. This is important for UI interaction. - * The passed error may be used if a scan needs to be retried, use fpi_device_retry_new(). - */ -void -fpi_device_enroll_progress (FpDevice *device, - gint completed_stages, - FpPrint *print, - GError *error) -{ - FpDevicePrivate *priv = fp_device_get_instance_private (device); - FpEnrollData *data; - - g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_ENROLL); - g_return_if_fail (error == NULL || error->domain == FP_DEVICE_RETRY); - - g_debug ("Device reported enroll progress, reported %i of %i have been completed", completed_stages, priv->nr_enroll_stages); - - if (error && print) - { - g_warning ("Driver passed an error and also provided a print, returning error!"); - g_clear_object (&print); - } - - data = g_task_get_task_data (priv->current_task); - - if (data->enroll_progress_cb) - { - data->enroll_progress_cb (device, - completed_stages, - print, - data->enroll_progress_data, - error); - } - - g_clear_error (&error); - g_clear_object (&print); -} - - static void async_result_ready (GObject *source_object, GAsyncResult *res, gpointer user_data) { diff --git a/libfprint/fpi-device.c b/libfprint/fpi-device.c new file mode 100644 index 00000000..3eee0627 --- /dev/null +++ b/libfprint/fpi-device.c @@ -0,0 +1,1177 @@ +/* + * FpDevice - A fingerprint reader device - Private APIs + * Copyright (C) 2019 Benjamin Berg + * Copyright (C) 2019 Marco Trevisan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define FP_COMPONENT "device" +#include "fpi-log.h" + +#include "fp-device-private.h" + +/** + * SECTION: fpi-device + * @title: Internal FpDevice + * @short_description: Internal device routines + * + * The methods that are availabe for drivers to manipulate a device. See + * #FpDeviceClass for more information. Also note that most of these are + * not relevant for image based devices, see #FpImageDeviceClass in that + * case. + * + * Also see the public #FpDevice routines. + */ + +/* Manually redefine what G_DEFINE_* macro does */ +static inline gpointer +fp_device_get_instance_private (FpDevice *self) +{ + FpDeviceClass *dev_class = g_type_class_peek_static (FP_TYPE_DEVICE); + + return G_STRUCT_MEMBER_P (self, + g_type_class_get_instance_private_offset (dev_class)); +} + +/** + * fpi_device_retry_new: + * @error: The #FpDeviceRetry error value describing the issue + * + * Create a new retry error code for use with fpi_device_verify_complete() + * and similar calls. + */ +GError * +fpi_device_retry_new (FpDeviceRetry error) +{ + const gchar *msg; + + switch (error) + { + case FP_DEVICE_RETRY_GENERAL: + msg = "Please try again."; + break; + + case FP_DEVICE_RETRY_TOO_SHORT: + msg = "The swipe was too short, please try again."; + break; + + case FP_DEVICE_RETRY_CENTER_FINGER: + msg = "The finger was not centered properly, please try again."; + break; + + case FP_DEVICE_RETRY_REMOVE_FINGER: + msg = "Please try again after removing the finger first."; + break; + + default: + g_warning ("Unsupported error, returning general error instead!"); + error = FP_DEVICE_RETRY_GENERAL; + msg = "Please try again."; + } + + return g_error_new_literal (FP_DEVICE_RETRY, error, msg); +} + +/** + * fpi_device_error_new: + * @error: The #FpDeviceRetry error value describing the issue + * + * Create a new error code for use with fpi_device_verify_complete() and + * similar calls. + */ +GError * +fpi_device_error_new (FpDeviceError error) +{ + const gchar *msg; + + switch (error) + { + case FP_DEVICE_ERROR_GENERAL: + msg = "An unspecified error occured!"; + break; + + case FP_DEVICE_ERROR_NOT_SUPPORTED: + msg = "The operation is not supported on this device!"; + break; + + case FP_DEVICE_ERROR_NOT_OPEN: + msg = "The device needs to be opened first!"; + break; + + case FP_DEVICE_ERROR_ALREADY_OPEN: + msg = "The device has already been opened!"; + break; + + case FP_DEVICE_ERROR_BUSY: + msg = "The device is still busy with another operation, please try again later."; + break; + + case FP_DEVICE_ERROR_PROTO: + msg = "The driver encountered a protocol error with the device."; + break; + + case FP_DEVICE_ERROR_DATA_INVALID: + msg = "Passed (print) data is not valid."; + break; + + case FP_DEVICE_ERROR_DATA_FULL: + msg = "On device storage space is full."; + break; + + case FP_DEVICE_ERROR_DATA_NOT_FOUND: + msg = "Print was not found on the devices storage."; + break; + + default: + g_warning ("Unsupported error, returning general error instead!"); + error = FP_DEVICE_ERROR_GENERAL; + msg = "An unspecified error occured!"; + } + + return g_error_new_literal (FP_DEVICE_ERROR, error, msg); +} + +/** + * fpi_device_retry_new_msg: + * @error: The #FpDeviceRetry error value describing the issue + * @msg: Custom message to use with printf-style formatting + * @...: args for @msg + * + * Create a new retry error code for use with fpi_device_verify_complete() + * and similar calls. + */ +GError * +fpi_device_retry_new_msg (FpDeviceRetry device_error, + const gchar *msg, + ...) +{ + GError *error; + va_list args; + + va_start (args, msg); + error = g_error_new_valist (FP_DEVICE_RETRY, device_error, msg, args); + va_end (args); + + return error; +} + +/** + * fpi_device_error_new_msg: + * @error: The #FpDeviceRetry error value describing the issue + * @msg: Custom message to use with printf-style formatting + * @...: args for @msg + * + * Create a new error code for use with fpi_device_verify_complete() + * and similar calls. + */ +GError * +fpi_device_error_new_msg (FpDeviceError device_error, + const gchar *msg, + ...) +{ + GError *error; + va_list args; + + va_start (args, msg); + error = g_error_new_valist (FP_DEVICE_ERROR, device_error, msg, args); + va_end (args); + + return error; +} + +/** + * fpi_device_set_nr_enroll_stages: + * @device: The #FpDevice + * @enroll_stages: The number of enroll stages + * + * Updates the reported number of enroll stages that the device needs. + * If all supported devices have the same number of stages, then the + * value can simply be set in the class. + */ +void +fpi_device_set_nr_enroll_stages (FpDevice *device, + gint enroll_stages) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + + g_return_if_fail (FP_IS_DEVICE (device)); + + priv->nr_enroll_stages = enroll_stages; + g_object_notify (G_OBJECT (device), "nr-enroll-stages"); +} + +/** + * fpi_device_set_scan_type: + * @device: The #FpDevice + * @scan_type: The scan type of the device + * + * Updates the the scan type of the device from the default. + * If all supported devices have the same scan type, then the + * value can simply be set in the class. + */ +void +fpi_device_set_scan_type (FpDevice *device, + FpScanType scan_type) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + + g_return_if_fail (FP_IS_DEVICE (device)); + + priv->scan_type = scan_type; + g_object_notify (G_OBJECT (device), "scan-type"); +} + +typedef struct +{ + GSource source; + FpDevice *device; +} FpDeviceTimeoutSource; + +static void +timeout_finalize (GSource *source) +{ + FpDeviceTimeoutSource *timeout_source = (FpDeviceTimeoutSource *) source; + FpDevicePrivate *priv; + + priv = fp_device_get_instance_private (timeout_source->device); + priv->sources = g_slist_remove (priv->sources, source); +} + +static gboolean +timeout_dispatch (GSource *source, GSourceFunc gsource_func, gpointer user_data) +{ + FpDeviceTimeoutSource *timeout_source = (FpDeviceTimeoutSource *) source; + FpTimeoutFunc callback = (FpTimeoutFunc) gsource_func; + + callback (timeout_source->device, user_data); + + return G_SOURCE_REMOVE; +} + +static GSourceFuncs timeout_funcs = { + NULL, /* prepare */ + NULL, /* check */ + timeout_dispatch, + timeout_finalize, + NULL, NULL +}; + +/** + * fpi_device_add_timeout: + * @device: The #FpDevice + * @interval: The interval in milliseconds + * @func: The #FpTimeoutFunc to call on timeout + * @user_data: (nullable): User data to pass to the callback + * @destroy_notify: (nullable): #GDestroyNotify for @user_data + * + * Register a timeout to run. Drivers should always make sure that timers are + * cancelled when appropriate. + * + * Returns: (transfer none): A newly created and attached #GSource + */ +GSource * +fpi_device_add_timeout (FpDevice *device, + gint interval, + FpTimeoutFunc func, + gpointer user_data, + GDestroyNotify destroy_notify) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + FpDeviceTimeoutSource *source; + + source = (FpDeviceTimeoutSource *) g_source_new (&timeout_funcs, + sizeof (FpDeviceTimeoutSource)); + source->device = device; + + g_source_attach (&source->source, NULL); + g_source_set_callback (&source->source, (GSourceFunc) func, user_data, destroy_notify); + g_source_set_ready_time (&source->source, + g_source_get_time (&source->source) + interval * (guint64) 1000); + priv->sources = g_slist_prepend (priv->sources, source); + g_source_unref (&source->source); + + return &source->source; +} + +/** + * fpi_device_get_usb_device: + * @device: The #FpDevice + * + * Get the #GUsbDevice for this #FpDevice. Only permissible to call if the + * #FpDevice is of type %FP_DEVICE_TYPE_USB. + * + * Returns: The #GUsbDevice + */ +GUsbDevice * +fpi_device_get_usb_device (FpDevice *device) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + + g_return_val_if_fail (FP_IS_DEVICE (device), NULL); + g_return_val_if_fail (priv->type == FP_DEVICE_TYPE_USB, NULL); + + return priv->usb_device; +} + +/** + * fpi_device_get_virtual_env: + * @device: The #FpDevice + * + * Get the value of the environment variable that caused the virtual #FpDevice to be + * generated. Only permissible to call if the #FpDevice is of type %FP_DEVICE_TYPE_VIRTUAL. + * + * Returns: The value of the environment variable + */ +const gchar * +fpi_device_get_virtual_env (FpDevice *device) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + + g_return_val_if_fail (FP_IS_DEVICE (device), NULL); + g_return_val_if_fail (priv->type == FP_DEVICE_TYPE_VIRTUAL, NULL); + + return priv->virtual_env; +} + +/** + * fpi_device_get_current_action: + * @device: The #FpDevice + * + * Get the currently ongoing action or %FP_DEVICE_ACTION_NONE if there + * is no operation at this time. + * + * This is useful for drivers that might share code paths between different + * actions (e.g. verify and identify) and want to find out again later which + * action was started in the beginning. + * + * Returns: The ongoing #FpDeviceAction + */ +FpDeviceAction +fpi_device_get_current_action (FpDevice *device) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + + g_return_val_if_fail (FP_IS_DEVICE (device), FP_DEVICE_ACTION_NONE); + + return priv->current_action; +} + +/** + * fpi_device_action_is_cancelled: + * @device: The #FpDevice + * + * Checks whether the current action has been cancelled by the user. + * This is equivalent to first getting the cancellable using + * fpi_device_get_cancellable() and then checking whether it has been + * cancelled (if it is non-NULL). + * + * Returns: %TRUE if action should be cancelled + */ +gboolean +fpi_device_action_is_cancelled (FpDevice *device) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + GCancellable *cancellable; + + g_return_val_if_fail (FP_IS_DEVICE (device), TRUE); + g_return_val_if_fail (priv->current_action != FP_DEVICE_ACTION_NONE, TRUE); + + cancellable = g_task_get_cancellable (priv->current_task); + + return cancellable ? g_cancellable_is_cancelled (cancellable) : FALSE; +} + +/** + * fpi_device_get_driver_data: + * @device: The #FpDevice + * + * Returns: The driver data from the #FpIdEntry table entry + */ +guint64 +fpi_device_get_driver_data (FpDevice *device) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + + g_return_val_if_fail (FP_IS_DEVICE (device), 0); + + return priv->driver_data; +} + +void +enroll_data_free (FpEnrollData *data) +{ + if (data->enroll_progress_destroy) + data->enroll_progress_destroy (data->enroll_progress_data); + data->enroll_progress_data = NULL; + g_clear_object (&data->print); + g_free (data); +} + +/** + * fpi_device_get_enroll_data: + * @device: The #FpDevice + * @print: (out) (transfer none): The user provided template print + * + * Get data for enrollment. + */ +void +fpi_device_get_enroll_data (FpDevice *device, + FpPrint **print) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + FpEnrollData *data; + + g_return_if_fail (FP_IS_DEVICE (device)); + g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_ENROLL); + + data = g_task_get_task_data (priv->current_task); + g_assert (data); + + if (print) + *print = data->print; +} + +/** + * fpi_device_get_capture_data: + * @device: The #FpDevice + * @wait_for_finger: (out): Whether to wait for finger or not + * + * Get data for capture. + */ +void +fpi_device_get_capture_data (FpDevice *device, + gboolean *wait_for_finger) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + + g_return_if_fail (FP_IS_DEVICE (device)); + g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_CAPTURE); + + if (wait_for_finger) + *wait_for_finger = priv->wait_for_finger; +} + +/** + * fpi_device_get_verify_data: + * @device: The #FpDevice + * @print: (out) (transfer none): The enrolled print + * + * Get data for verify. + */ +void +fpi_device_get_verify_data (FpDevice *device, + FpPrint **print) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + + g_return_if_fail (FP_IS_DEVICE (device)); + g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_VERIFY); + + if (print) + *print = g_task_get_task_data (priv->current_task); +} + +/** + * fpi_device_get_identify_data: + * @device: The #FpDevice + * @prints: (out) (transfer none) (element-type FpPrint): The gallery of prints + * + * Get data for identify. + */ +void +fpi_device_get_identify_data (FpDevice *device, + GPtrArray **prints) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + + g_return_if_fail (FP_IS_DEVICE (device)); + g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_IDENTIFY); + + if (prints) + *prints = g_task_get_task_data (priv->current_task); +} + +/** + * fpi_device_get_delete_data: + * @device: The #FpDevice + * @print: (out) (transfer none): The print to delete + * + * Get data for delete. + */ +void +fpi_device_get_delete_data (FpDevice *device, + FpPrint **print) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + + g_return_if_fail (FP_IS_DEVICE (device)); + g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_DELETE); + + if (print) + *print = g_task_get_task_data (priv->current_task); +} + +/** + * fpi_device_get_cancellable: + * @device: The #FpDevice + * + * Retrieve the #GCancellable that may cancel the currently ongoing operation. This + * is primarily useful to pass directly to e.g. fpi_usb_transfer_submit() for cancellable + * transfers. + * In many cases the cancel vfunc may be more convenient to react to cancellation in some + * way. + * + * Returns: (transfer none): The #GCancellable for the current action. + */ +GCancellable * +fpi_device_get_cancellable (FpDevice *device) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + + g_return_val_if_fail (FP_IS_DEVICE (device), NULL); + g_return_val_if_fail (priv->current_action != FP_DEVICE_ACTION_NONE, NULL); + + return g_task_get_cancellable (priv->current_task); +} + +/** + * fpi_device_action_error: + * @device: The #FpDevice + * @error: The #GError to return + * + * Finish an ongoing action with an error. This is the same as calling + * the corresponding complete function such as fpi_device_open_complete() + * with an error set. If possible, use the correct complete function as + * that results in improved error detection. + */ +void +fpi_device_action_error (FpDevice *device, + GError *error) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + + g_return_if_fail (FP_IS_DEVICE (device)); + g_return_if_fail (priv->current_action != FP_DEVICE_ACTION_NONE); + + if (error != NULL) + { + g_debug ("Device reported generic error during action; action was: %i", priv->current_action); + } + else + { + g_warning ("Device failed to pass an error to generic action error function"); + error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, "Device reported error but did not provide an error condition"); + } + + + switch (priv->current_action) + { + case FP_DEVICE_ACTION_PROBE: + fpi_device_probe_complete (device, NULL, NULL, error); + break; + + case FP_DEVICE_ACTION_OPEN: + fpi_device_open_complete (device, error); + break; + + case FP_DEVICE_ACTION_CLOSE: + fpi_device_close_complete (device, error); + break; + + case FP_DEVICE_ACTION_ENROLL: + fpi_device_enroll_complete (device, NULL, error); + break; + + case FP_DEVICE_ACTION_VERIFY: + fpi_device_verify_complete (device, FPI_MATCH_ERROR, NULL, error); + break; + + case FP_DEVICE_ACTION_IDENTIFY: + fpi_device_identify_complete (device, NULL, NULL, error); + break; + + case FP_DEVICE_ACTION_CAPTURE: + fpi_device_capture_complete (device, NULL, error); + break; + + case FP_DEVICE_ACTION_DELETE: + fpi_device_delete_complete (device, error); + break; + + case FP_DEVICE_ACTION_LIST: + fpi_device_list_complete (device, NULL, error); + break; + + default: + case FP_DEVICE_ACTION_NONE: + g_return_if_reached (); + break; + } +} + +static void +clear_device_cancel_action (FpDevice *device) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + + g_clear_pointer (&priv->current_idle_cancel_source, g_source_destroy); + + if (priv->current_cancellable_id) + { + g_cancellable_disconnect (g_task_get_cancellable (priv->current_task), + priv->current_cancellable_id); + priv->current_cancellable_id = 0; + } +} + +typedef enum _FpDeviceTaskReturnType { + FP_DEVICE_TASK_RETURN_INT, + FP_DEVICE_TASK_RETURN_BOOL, + FP_DEVICE_TASK_RETURN_OBJECT, + FP_DEVICE_TASK_RETURN_PTR_ARRAY, + FP_DEVICE_TASK_RETURN_ERROR, +} FpDeviceTaskReturnType; + +typedef struct _FpDeviceTaskReturnData +{ + FpDevice *device; + FpDeviceTaskReturnType type; + gpointer result; +} FpDeviceTaskReturnData; + +static gboolean +fp_device_task_return_in_idle_cb (gpointer user_data) +{ + FpDeviceTaskReturnData *data = user_data; + FpDevicePrivate *priv = fp_device_get_instance_private (data->device); + + g_autoptr(GTask) task = NULL; + + g_debug ("Completing action %d in idle!", priv->current_action); + + task = g_steal_pointer (&priv->current_task); + priv->current_action = FP_DEVICE_ACTION_NONE; + priv->current_task_idle_return_source = NULL; + + switch (data->type) + { + case FP_DEVICE_TASK_RETURN_INT: + g_task_return_int (task, GPOINTER_TO_INT (data->result)); + break; + + case FP_DEVICE_TASK_RETURN_BOOL: + g_task_return_boolean (task, GPOINTER_TO_UINT (data->result)); + break; + + case FP_DEVICE_TASK_RETURN_OBJECT: + g_task_return_pointer (task, data->result, g_object_unref); + break; + + case FP_DEVICE_TASK_RETURN_PTR_ARRAY: + g_task_return_pointer (task, data->result, + (GDestroyNotify) g_ptr_array_unref); + break; + + case FP_DEVICE_TASK_RETURN_ERROR: + g_task_return_error (task, data->result); + break; + + default: + g_assert_not_reached (); + } + + return G_SOURCE_REMOVE; +} + +static void +fpi_device_task_return_data_free (FpDeviceTaskReturnData *data) +{ + g_object_unref (data->device); + g_free (data); +} + +static void +fpi_device_return_task_in_idle (FpDevice *device, + FpDeviceTaskReturnType return_type, + gpointer return_data) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + FpDeviceTaskReturnData *data; + + data = g_new0 (FpDeviceTaskReturnData, 1); + data->device = g_object_ref (device); + data->type = return_type; + data->result = return_data; + + priv->current_task_idle_return_source = g_idle_source_new (); + g_source_set_priority (priv->current_task_idle_return_source, + g_task_get_priority (priv->current_task)); + g_source_set_callback (priv->current_task_idle_return_source, + fp_device_task_return_in_idle_cb, + data, + (GDestroyNotify) fpi_device_task_return_data_free); + + g_source_attach (priv->current_task_idle_return_source, NULL); + g_source_unref (priv->current_task_idle_return_source); +} + +/** + * fpi_device_probe_complete: + * @device: The #FpDevice + * @device_id: Unique ID for the device or %NULL + * @device_name: Human readable name or %NULL for driver name + * @error: The #GError or %NULL on success + * + * Finish an ongoing probe operation. If error is %NULL success is assumed. + */ +void +fpi_device_probe_complete (FpDevice *device, + const gchar *device_id, + const gchar *device_name, + GError *error) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + + g_return_if_fail (FP_IS_DEVICE (device)); + g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_PROBE); + + g_debug ("Device reported probe completion"); + + clear_device_cancel_action (device); + + if (!error) + { + if (device_id) + { + g_clear_pointer (&priv->device_id, g_free); + priv->device_id = g_strdup (device_id); + g_object_notify (G_OBJECT (device), "device-id"); + } + if (device_name) + { + g_clear_pointer (&priv->device_name, g_free); + priv->device_name = g_strdup (device_name); + g_object_notify (G_OBJECT (device), "name"); + } + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL, + GUINT_TO_POINTER (TRUE)); + } + else + { + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); + } +} + +/** + * fpi_device_open_complete: + * @device: The #FpDevice + * @error: The #GError or %NULL on success + * + * Finish an ongoing open operation. If error is %NULL success is assumed. + */ +void +fpi_device_open_complete (FpDevice *device, GError *error) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + + g_return_if_fail (FP_IS_DEVICE (device)); + g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_OPEN); + + g_debug ("Device reported open completion"); + + clear_device_cancel_action (device); + + if (!error) + { + priv->is_open = TRUE; + g_object_notify (G_OBJECT (device), "open"); + } + + if (!error) + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL, + GUINT_TO_POINTER (TRUE)); + else + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); +} + +/** + * fpi_device_close_complete: + * @device: The #FpDevice + * @error: The #GError or %NULL on success + * + * Finish an ongoing close operation. If error is %NULL success is assumed. + */ +void +fpi_device_close_complete (FpDevice *device, GError *error) +{ + GError *nested_error = NULL; + FpDevicePrivate *priv = fp_device_get_instance_private (device); + + g_return_if_fail (FP_IS_DEVICE (device)); + g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_CLOSE); + + g_debug ("Device reported close completion"); + + clear_device_cancel_action (device); + priv->is_open = FALSE; + g_object_notify (G_OBJECT (device), "open"); + + switch (priv->type) + { + case FP_DEVICE_TYPE_USB: + if (!g_usb_device_close (priv->usb_device, &nested_error)) + { + if (error == NULL) + error = nested_error; + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); + return; + } + break; + + case FP_DEVICE_TYPE_VIRTUAL: + break; + + default: + g_assert_not_reached (); + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, + fpi_device_error_new (FP_DEVICE_ERROR_GENERAL)); + return; + } + + if (!error) + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL, + GUINT_TO_POINTER (TRUE)); + else + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); +} + +/** + * fpi_device_enroll_complete: + * @device: The #FpDevice + * @print: (nullable) (transfer full): The #FpPrint or %NULL on failure + * @error: The #GError or %NULL on success + * + * Finish an ongoing enroll operation. The #FpPrint can be stored by the + * caller for later verification. + */ +void +fpi_device_enroll_complete (FpDevice *device, FpPrint *print, GError *error) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + + g_return_if_fail (FP_IS_DEVICE (device)); + g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_ENROLL); + + g_debug ("Device reported enroll completion"); + + clear_device_cancel_action (device); + + if (!error) + { + if (FP_IS_PRINT (print)) + { + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_OBJECT, print); + } + else + { + g_warning ("Driver did not provide a valid print and failed to provide an error!"); + error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, + "Driver failed to provide print data!"); + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); + } + } + else + { + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); + if (FP_IS_PRINT (print)) + { + g_warning ("Driver passed an error but also provided a print, returning error!"); + g_object_unref (print); + } + } +} + +/** + * fpi_device_verify_complete: + * @device: The #FpDevice + * @result: The #FpiMatchResult of the operation + * @print: The scanned #FpPrint + * @error: A #GError if result is %FPI_MATCH_ERROR + * + * Finish an ongoing verify operation. The returned print should be + * representing the new scan and not the one passed for verification. + */ +void +fpi_device_verify_complete (FpDevice *device, + FpiMatchResult result, + FpPrint *print, + GError *error) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + + g_return_if_fail (FP_IS_DEVICE (device)); + g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_VERIFY); + + g_debug ("Device reported verify completion"); + + clear_device_cancel_action (device); + + g_object_set_data_full (G_OBJECT (priv->current_task), + "print", + print, + g_object_unref); + + if (!error) + { + if (result != FPI_MATCH_ERROR) + { + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_INT, + GINT_TO_POINTER (result)); + } + else + { + g_warning ("Driver did not provide an error for a failed verify operation!"); + error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, + "Driver failed to provide an error!"); + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); + } + } + else + { + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); + if (result != FPI_MATCH_ERROR) + { + g_warning ("Driver passed an error but also provided a match result, returning error!"); + g_object_unref (print); + } + } +} + +/** + * fpi_device_identify_complete: + * @device: The #FpDevice + * @match: The matching #FpPrint from the passed gallery, or %NULL if none matched + * @print: The scanned #FpPrint, may be %NULL + * @error: The #GError or %NULL on success + * + * Finish an ongoing identify operation. The match that was identified is + * returned in @match. The @print parameter returns the newly created scan + * that was used for matching. + */ +void +fpi_device_identify_complete (FpDevice *device, + FpPrint *match, + FpPrint *print, + GError *error) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + + g_return_if_fail (FP_IS_DEVICE (device)); + g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_IDENTIFY); + + g_debug ("Device reported identify completion"); + + clear_device_cancel_action (device); + + g_object_set_data_full (G_OBJECT (priv->current_task), + "print", + print, + g_object_unref); + g_object_set_data_full (G_OBJECT (priv->current_task), + "match", + match, + g_object_unref); + if (!error) + { + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL, + GUINT_TO_POINTER (TRUE)); + } + else + { + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); + if (match) + { + g_warning ("Driver passed an error but also provided a match result, returning error!"); + g_clear_object (&match); + } + } +} + + +/** + * fpi_device_capture_complete: + * @device: The #FpDevice + * @image: The #FpImage, or %NULL on error + * @error: The #GError or %NULL on success + * + * Finish an ongoing capture operation. + */ +void +fpi_device_capture_complete (FpDevice *device, + FpImage *image, + GError *error) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + + g_return_if_fail (FP_IS_DEVICE (device)); + g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_CAPTURE); + + g_debug ("Device reported capture completion"); + + clear_device_cancel_action (device); + + if (!error) + { + if (image) + { + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_OBJECT, image); + } + else + { + g_warning ("Driver did not provide an error for a failed capture operation!"); + error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, + "Driver failed to provide an error!"); + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); + } + } + else + { + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); + if (image) + { + g_warning ("Driver passed an error but also provided an image, returning error!"); + g_clear_object (&image); + } + } +} + +/** + * fpi_device_delete_complete: + * @device: The #FpDevice + * @error: The #GError or %NULL on success + * + * Finish an ongoing delete operation. + */ +void +fpi_device_delete_complete (FpDevice *device, + GError *error) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + + g_return_if_fail (FP_IS_DEVICE (device)); + g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_DELETE); + + g_debug ("Device reported deletion completion"); + + clear_device_cancel_action (device); + + if (!error) + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL, + GUINT_TO_POINTER (TRUE)); + else + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); +} + +/** + * fpi_device_list_complete: + * @device: The #FpDevice + * @prints: (element-type FpPrint) (transfer container): Possibly empty array of prints or %NULL on error + * @error: The #GError or %NULL on success + * + * Finish an ongoing list operation. + * + * Please note that the @prints array will be free'ed using + * g_ptr_array_unref() and the elements are destroyed automatically. + * As such, you must use g_ptr_array_new_with_free_func() with + * g_object_unref() as free func to create the array. + */ +void +fpi_device_list_complete (FpDevice *device, + GPtrArray *prints, + GError *error) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + + g_return_if_fail (FP_IS_DEVICE (device)); + g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_LIST); + + g_debug ("Device reported listing completion"); + + clear_device_cancel_action (device); + + if (prints && error) + { + g_warning ("Driver reported back prints and error, ignoring prints"); + g_clear_pointer (&prints, g_ptr_array_unref); + } + else if (!prints && !error) + { + g_warning ("Driver did not pass array but failed to provide an error"); + error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, + "Driver failed to provide a list of prints"); + } + + if (!error) + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_PTR_ARRAY, prints); + else + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); +} + +/** + * fpi_device_enroll_progress: + * @device: The #FpDevice + * @completed_stages: The number of stages that are completed at this point + * @print: The #FpPrint for the newly completed stage or %NULL on failure + * @error: The #GError or %NULL on success + * + * Notify about the progress of the enroll operation. This is important for UI interaction. + * The passed error may be used if a scan needs to be retried, use fpi_device_retry_new(). + */ +void +fpi_device_enroll_progress (FpDevice *device, + gint completed_stages, + FpPrint *print, + GError *error) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + FpEnrollData *data; + + g_return_if_fail (FP_IS_DEVICE (device)); + g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_ENROLL); + g_return_if_fail (error == NULL || error->domain == FP_DEVICE_RETRY); + + g_debug ("Device reported enroll progress, reported %i of %i have been completed", completed_stages, priv->nr_enroll_stages); + + if (error && print) + { + g_warning ("Driver passed an error and also provided a print, returning error!"); + g_clear_object (&print); + } + + data = g_task_get_task_data (priv->current_task); + + if (data->enroll_progress_cb) + { + data->enroll_progress_cb (device, + completed_stages, + print, + data->enroll_progress_data, + error); + } + + g_clear_error (&error); + g_clear_object (&print); +} diff --git a/libfprint/meson.build b/libfprint/meson.build index 1e98e2df..4a34cbde 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -8,6 +8,7 @@ libfprint_sources = [ libfprint_private_sources = [ 'fpi-assembling.c', + 'fpi-device.c', 'fpi-ssm.c', 'fpi-usb-transfer.c', 'fpi-byte-reader.c', From ae7021e529ca47207e87537be9d6b747d17c0647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 11 Dec 2019 13:40:47 +0100 Subject: [PATCH 117/237] fp-image-device: Move fpi code into its own unit that can be compiled a part In order to be able to test the private device code (used by drivers) we need to have that split a part in a different .c file so that we can compile it alone and link with it both the shared library and the test executables. Redefine fp_image_device_get_instance_private for private usage, not to move the private struct as part of FpDevice. --- libfprint/fp-image-device-private.h | 43 ++ libfprint/fp-image-device.c | 585 +-------------------------- libfprint/fpi-image-device.c | 595 ++++++++++++++++++++++++++++ libfprint/meson.build | 1 + 4 files changed, 643 insertions(+), 581 deletions(-) create mode 100644 libfprint/fp-image-device-private.h create mode 100644 libfprint/fpi-image-device.c diff --git a/libfprint/fp-image-device-private.h b/libfprint/fp-image-device-private.h new file mode 100644 index 00000000..01454fd0 --- /dev/null +++ b/libfprint/fp-image-device-private.h @@ -0,0 +1,43 @@ +/* + * FpImageDevice - An image based fingerprint reader device + * Copyright (C) 2019 Benjamin Berg + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +#include "fpi-image-device.h" + +#define IMG_ENROLL_STAGES 5 + +typedef struct +{ + FpImageDeviceState state; + gboolean active; + gboolean cancelling; + + gboolean enroll_await_on_pending; + gint enroll_stage; + + guint pending_activation_timeout_id; + gboolean pending_activation_timeout_waiting_finger_off; + + gint bz3_threshold; +} FpImageDevicePrivate; + + +void fpi_image_device_activate (FpImageDevice *image_device); +void fpi_image_device_deactivate (FpImageDevice *image_device); diff --git a/libfprint/fp-image-device.c b/libfprint/fp-image-device.c index 252f414c..24d324df 100644 --- a/libfprint/fp-image-device.c +++ b/libfprint/fp-image-device.c @@ -20,14 +20,9 @@ #define FP_COMPONENT "image_device" #include "fpi-log.h" -#include "fpi-image-device.h" -#include "fpi-enums.h" -#include "fpi-print.h" -#include "fpi-image.h" +#include "fp-image-device-private.h" -#define MIN_ACCEPTABLE_MINUTIAE 10 #define BOZORTH3_DEFAULT_THRESHOLD 40 -#define IMG_ENROLL_STAGES 5 /** * SECTION: fp-image-device @@ -37,30 +32,6 @@ * This is a helper class for the commonly found image based devices. */ -/** - * SECTION: fpi-image-device - * @title: Internal FpImageDevice - * @short_description: Internal image device routines - * - * See #FpImageDeviceClass for more details. Also see the public - * #FpImageDevice routines. - */ - -typedef struct -{ - FpImageDeviceState state; - gboolean active; - gboolean cancelling; - - gboolean enroll_await_on_pending; - gint enroll_stage; - - guint pending_activation_timeout_id; - gboolean pending_activation_timeout_waiting_finger_off; - - gint bz3_threshold; -} FpImageDevicePrivate; - G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (FpImageDevice, fp_image_device, FP_TYPE_DEVICE) enum { @@ -87,70 +58,6 @@ static guint signals[LAST_SIGNAL] = { 0 }; /* Static helper functions */ -static void -fp_image_device_change_state (FpImageDevice *self, FpImageDeviceState state) -{ - FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); - - /* Cannot change to inactive using this function. */ - g_assert (state != FP_IMAGE_DEVICE_STATE_INACTIVE); - - /* We might have been waiting for the finger to go OFF to start the - * next operation. */ - g_clear_handle_id (&priv->pending_activation_timeout_id, g_source_remove); - - fp_dbg ("Image device internal state change from %d to %d\n", priv->state, state); - - priv->state = state; - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FPI_STATE]); - g_signal_emit (self, signals[FPI_STATE_CHANGED], 0, priv->state); -} - -static void -fp_image_device_activate (FpImageDevice *self) -{ - FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); - FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (self); - - g_assert (!priv->active); - - /* We don't have a neutral ACTIVE state, but we always will - * go into WAIT_FINGER_ON afterwards. */ - priv->state = FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON; - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FPI_STATE]); - - /* We might have been waiting for deactivation to finish before - * starting the next operation. */ - g_clear_handle_id (&priv->pending_activation_timeout_id, g_source_remove); - - fp_dbg ("Activating image device\n"); - cls->activate (self); -} - -static void -fp_image_device_deactivate (FpDevice *device) -{ - FpImageDevice *self = FP_IMAGE_DEVICE (device); - FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); - FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (device); - - if (!priv->active) - { - /* XXX: We currently deactivate both from minutiae scan result - * and finger off report. */ - fp_dbg ("Already deactivated, ignoring request."); - return; - } - if (!priv->cancelling && priv->state == FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON) - g_warning ("Deactivating image device while waiting for finger, this should not happen."); - - priv->state = FP_IMAGE_DEVICE_STATE_INACTIVE; - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FPI_STATE]); - - fp_dbg ("Deactivating image device\n"); - cls->deactivate (self); -} - static gboolean pending_activation_timeout (gpointer user_data) { @@ -200,7 +107,7 @@ fp_image_device_close (FpDevice *device) if (!priv->active) cls->img_close (self); else if (priv->state != FP_IMAGE_DEVICE_STATE_INACTIVE) - fp_image_device_deactivate (device); + fpi_image_device_deactivate (self); } static void @@ -220,7 +127,7 @@ fp_image_device_cancel_action (FpDevice *device) action == FP_DEVICE_ACTION_CAPTURE) { priv->cancelling = TRUE; - fp_image_device_deactivate (FP_DEVICE (self)); + fpi_image_device_deactivate (self); priv->cancelling = FALSE; /* XXX: Some nicer way of doing this would be good. */ @@ -288,7 +195,7 @@ fp_image_device_start_capture_action (FpDevice *device) /* And activate the device; we rely on fpi_image_device_activate_complete() * to be called when done (or immediately). */ - fp_image_device_activate (self); + fpi_image_device_activate (self); } @@ -391,488 +298,4 @@ fp_image_device_init (FpImageDevice *self) priv->bz3_threshold = BOZORTH3_DEFAULT_THRESHOLD; if (cls->bz3_threshold > 0) priv->bz3_threshold = cls->bz3_threshold; - -} - -static void -fp_image_device_enroll_maybe_await_finger_on (FpImageDevice *self) -{ - FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); - - if (priv->enroll_await_on_pending) - { - priv->enroll_await_on_pending = FALSE; - fp_image_device_change_state (self, FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON); - } - else - { - priv->enroll_await_on_pending = TRUE; - } -} - -static void -fpi_image_device_minutiae_detected (GObject *source_object, GAsyncResult *res, gpointer user_data) -{ - g_autoptr(FpImage) image = FP_IMAGE (source_object); - g_autoptr(FpPrint) print = NULL; - GError *error = NULL; - FpDevice *device = FP_DEVICE (user_data); - FpImageDevicePrivate *priv; - FpDeviceAction action; - - /* Note: We rely on the device to not disappear during an operation. */ - - if (!fp_image_detect_minutiae_finish (image, res, &error)) - { - /* Cancel operation . */ - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - { - fpi_device_action_error (device, g_steal_pointer (&error)); - fp_image_device_deactivate (device); - return; - } - - /* Replace error with a retry condition. */ - g_warning ("Failed to detect minutiae: %s", error->message); - g_clear_pointer (&error, g_error_free); - - error = fpi_device_retry_new_msg (FP_DEVICE_RETRY_GENERAL, "Minutiae detection failed, please retry"); - } - - priv = fp_image_device_get_instance_private (FP_IMAGE_DEVICE (device)); - action = fpi_device_get_current_action (device); - - if (action == FP_DEVICE_ACTION_CAPTURE) - { - fpi_device_capture_complete (device, g_steal_pointer (&image), error); - fp_image_device_deactivate (device); - return; - } - - if (!error) - { - print = fp_print_new (device); - fpi_print_set_type (print, FP_PRINT_NBIS); - if (!fpi_print_add_from_image (print, image, &error)) - g_clear_object (&print); - } - - if (action == FP_DEVICE_ACTION_ENROLL) - { - FpPrint *enroll_print; - fpi_device_get_enroll_data (device, &enroll_print); - - if (print) - { - fpi_print_add_print (enroll_print, print); - priv->enroll_stage += 1; - } - - fpi_device_enroll_progress (device, priv->enroll_stage, - g_steal_pointer (&print), error); - - /* Start another scan or deactivate. */ - if (priv->enroll_stage == IMG_ENROLL_STAGES) - { - fpi_device_enroll_complete (device, g_object_ref (enroll_print), NULL); - fp_image_device_deactivate (device); - } - else - { - fp_image_device_enroll_maybe_await_finger_on (FP_IMAGE_DEVICE (device)); - } - } - else if (action == FP_DEVICE_ACTION_VERIFY) - { - FpPrint *template; - FpiMatchResult result; - - fpi_device_get_verify_data (device, &template); - if (print) - result = fpi_print_bz3_match (template, print, priv->bz3_threshold, &error); - else - result = FPI_MATCH_ERROR; - - fpi_device_verify_complete (device, result, g_steal_pointer (&print), error); - fp_image_device_deactivate (device); - } - else if (action == FP_DEVICE_ACTION_IDENTIFY) - { - gint i; - GPtrArray *templates; - FpPrint *result = NULL; - - fpi_device_get_identify_data (device, &templates); - for (i = 0; !error && i < templates->len; i++) - { - FpPrint *template = g_ptr_array_index (templates, i); - - if (fpi_print_bz3_match (template, print, priv->bz3_threshold, &error) == FPI_MATCH_SUCCESS) - { - result = g_object_ref (template); - break; - } - } - - fpi_device_identify_complete (device, result, g_steal_pointer (&print), error); - fp_image_device_deactivate (device); - } - else - { - /* XXX: This can be hit currently due to a race condition in the enroll code! - * In that case we scan a further image even though the minutiae for the previous - * one have not yet been detected. - * We need to keep track on the pending minutiae detection and the fact that - * it will finish eventually (or we may need to retry on error and activate the - * device again). */ - g_assert_not_reached (); - } -} - -/*********************************************************/ -/* Private API */ - -/** - * fpi_image_device_set_bz3_threshold: - * @self: a #FpImageDevice imaging fingerprint device - * @bz3_threshold: BZ3 threshold to use - * - * Dynamically adjust the bz3 threshold. This is only needed for drivers - * that support devices with different properties. It should generally be - * called from the probe callback, but is acceptable to call from the open - * callback. - */ -void -fpi_image_device_set_bz3_threshold (FpImageDevice *self, - gint bz3_threshold) -{ - FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); - - g_return_if_fail (FP_IS_IMAGE_DEVICE (self)); - g_return_if_fail (bz3_threshold > 0); - - priv->bz3_threshold = bz3_threshold; -} - -/** - * fpi_image_device_report_finger_status: - * @self: a #FpImageDevice imaging fingerprint device - * @present: whether the finger is present on the sensor - * - * Reports from the driver whether the user's finger is on - * the sensor. - */ -void -fpi_image_device_report_finger_status (FpImageDevice *self, - gboolean present) -{ - FpDevice *device = FP_DEVICE (self); - FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); - FpDeviceAction action; - - if (priv->state == FP_IMAGE_DEVICE_STATE_INACTIVE) - { - /* Do we really want to always ignore such reports? We could - * also track the state in case the user had the finger on - * the device at initialisation time and the driver reports - * this early. - */ - g_debug ("Ignoring finger presence report as the device is not active!"); - return; - } - - action = fpi_device_get_current_action (device); - - g_assert (action != FP_DEVICE_ACTION_OPEN); - g_assert (action != FP_DEVICE_ACTION_CLOSE); - - g_debug ("Image device reported finger status: %s", present ? "on" : "off"); - - if (present && priv->state == FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON) - { - fp_image_device_change_state (self, FP_IMAGE_DEVICE_STATE_CAPTURE); - } - else if (!present && priv->state == FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF) - { - /* We need to deactivate or continue to await finger */ - - /* There are three possible situations: - * 1. We are deactivating the device and the action is still in progress - * (minutiae detection). - * 2. We are still deactivating the device after an action completed - * 3. We were waiting for finger removal to start the new action - * Either way, we always end up deactivating except for the enroll case. - * - * The enroll case is special as AWAIT_FINGER_ON should only happen after - * minutiae detection to prevent deactivation (without cancellation) - * from the AWAIT_FINGER_ON state. - */ - if (action != FP_DEVICE_ACTION_ENROLL) - fp_image_device_deactivate (device); - else - fp_image_device_enroll_maybe_await_finger_on (self); - } -} - -/** - * fpi_image_device_image_captured: - * @self: a #FpImageDevice imaging fingerprint device - * @image: whether the finger is present on the sensor - * - * Reports an image capture. Only use this function if the image was - * captured successfully. If there was an issue where the user should - * retry, use fpi_image_device_retry_scan() to report the retry condition. - * - * In the event of a fatal error for the operation use - * fpi_image_device_session_error(). This will abort the entire operation - * including e.g. an enroll operation which captures multiple images during - * one session. - */ -void -fpi_image_device_image_captured (FpImageDevice *self, FpImage *image) -{ - FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); - FpDeviceAction action; - - action = fpi_device_get_current_action (FP_DEVICE (self)); - - g_return_if_fail (image != NULL); - g_return_if_fail (priv->state == FP_IMAGE_DEVICE_STATE_CAPTURE); - g_return_if_fail (action == FP_DEVICE_ACTION_ENROLL || - action == FP_DEVICE_ACTION_VERIFY || - action == FP_DEVICE_ACTION_IDENTIFY || - action == FP_DEVICE_ACTION_CAPTURE); - - fp_image_device_change_state (self, FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF); - - g_debug ("Image device captured an image"); - - /* XXX: We also detect minutiae in capture mode, we solely do this - * to normalize the image which will happen as a by-product. */ - fp_image_detect_minutiae (image, - fpi_device_get_cancellable (FP_DEVICE (self)), - fpi_image_device_minutiae_detected, - self); -} - -/** - * fpi_image_device_retry_scan: - * @self: a #FpImageDevice imaging fingerprint device - * @retry: The #FpDeviceRetry error code to report - * - * Reports a scan failure to the user. This may or may not abort the - * current session. It is the equivalent of fpi_image_device_image_captured() - * in the case of a retryable error condition (e.g. short swipe). - */ -void -fpi_image_device_retry_scan (FpImageDevice *self, FpDeviceRetry retry) -{ - FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); - FpDeviceAction action; - GError *error; - - action = fpi_device_get_current_action (FP_DEVICE (self)); - - /* We might be waiting for a finger at this point, so just accept - * all but INACTIVE */ - g_return_if_fail (priv->state != FP_IMAGE_DEVICE_STATE_INACTIVE); - g_return_if_fail (action == FP_DEVICE_ACTION_ENROLL || - action == FP_DEVICE_ACTION_VERIFY || - action == FP_DEVICE_ACTION_IDENTIFY || - action == FP_DEVICE_ACTION_CAPTURE); - - error = fpi_device_retry_new (retry); - - if (action == FP_DEVICE_ACTION_ENROLL) - { - g_debug ("Reporting retry during enroll"); - fpi_device_enroll_progress (FP_DEVICE (self), priv->enroll_stage, NULL, error); - } - else - { - /* We abort the operation and let the surrounding code retry in the - * non-enroll case (this is identical to a session error). */ - g_debug ("Abort current operation due to retry (non-enroll case)"); - fp_image_device_deactivate (FP_DEVICE (self)); - fpi_device_action_error (FP_DEVICE (self), error); - } -} - -/** - * fpi_image_device_session_error: - * @self: a #FpImageDevice imaging fingerprint device - * @error: The #GError to report - * - * Report an error while interacting with the device. This effectively - * aborts the current ongoing action. - */ -void -fpi_image_device_session_error (FpImageDevice *self, GError *error) -{ - FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); - - g_return_if_fail (self); - - if (!error) - { - g_warning ("Driver did not provide an error, generating a generic one"); - error = g_error_new (FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL, "Driver reported session error without an error"); - } - - if (!priv->active) - { - FpDeviceAction action = fpi_device_get_current_action (FP_DEVICE (self)); - g_warning ("Driver reported session error, but device is inactive."); - - if (action != FP_DEVICE_ACTION_NONE) - { - g_warning ("Translating to activation failure!"); - fpi_image_device_activate_complete (self, error); - return; - } - } - else if (priv->state == FP_IMAGE_DEVICE_STATE_INACTIVE) - { - g_warning ("Driver reported session error; translating to deactivation failure."); - fpi_image_device_deactivate_complete (self, error); - return; - } - - if (error->domain == FP_DEVICE_RETRY) - g_warning ("Driver should report retries using fpi_image_device_retry_scan!"); - - fp_image_device_deactivate (FP_DEVICE (self)); - fpi_device_action_error (FP_DEVICE (self), error); -} - -/** - * fpi_image_device_activate_complete: - * @self: a #FpImageDevice imaging fingerprint device - * @error: A #GError or %NULL on success - * - * Reports completion of device activation. - */ -void -fpi_image_device_activate_complete (FpImageDevice *self, GError *error) -{ - FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); - FpDeviceAction action; - - action = fpi_device_get_current_action (FP_DEVICE (self)); - - g_return_if_fail (priv->active == FALSE); - g_return_if_fail (action == FP_DEVICE_ACTION_ENROLL || - action == FP_DEVICE_ACTION_VERIFY || - action == FP_DEVICE_ACTION_IDENTIFY || - action == FP_DEVICE_ACTION_CAPTURE); - - if (error) - { - g_debug ("Image device activation failed"); - fpi_device_action_error (FP_DEVICE (self), error); - return; - } - - g_debug ("Image device activation completed"); - - priv->active = TRUE; - - /* We always want to capture at this point, move to AWAIT_FINGER - * state. */ - fp_image_device_change_state (self, FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON); -} - -/** - * fpi_image_device_deactivate_complete: - * @self: a #FpImageDevice imaging fingerprint device - * @error: A #GError or %NULL on success - * - * Reports completion of device deactivation. - */ -void -fpi_image_device_deactivate_complete (FpImageDevice *self, GError *error) -{ - FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); - FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (self); - FpDeviceAction action; - - g_return_if_fail (priv->active == TRUE); - g_return_if_fail (priv->state == FP_IMAGE_DEVICE_STATE_INACTIVE); - - g_debug ("Image device deactivation completed"); - - priv->active = FALSE; - - /* Deactivation completed. As we deactivate in the background - * there may already be a new task pending. Check whether we - * need to do anything. */ - action = fpi_device_get_current_action (FP_DEVICE (self)); - - /* Special case, if we should be closing, but didn't due to a running - * deactivation, then do so now. */ - if (action == FP_DEVICE_ACTION_CLOSE) - { - cls->img_close (self); - return; - } - - /* We might be waiting to be able to activate again. */ - if (priv->pending_activation_timeout_id) - { - g_clear_handle_id (&priv->pending_activation_timeout_id, g_source_remove); - priv->pending_activation_timeout_id = - g_idle_add ((GSourceFunc) fp_image_device_activate, self); - } -} - -/** - * fpi_image_device_open_complete: - * @self: a #FpImageDevice imaging fingerprint device - * @error: A #GError or %NULL on success - * - * Reports completion of open operation. - */ -void -fpi_image_device_open_complete (FpImageDevice *self, GError *error) -{ - FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); - FpDeviceAction action; - - action = fpi_device_get_current_action (FP_DEVICE (self)); - - g_return_if_fail (priv->active == FALSE); - g_return_if_fail (action == FP_DEVICE_ACTION_OPEN); - - g_debug ("Image device open completed"); - - priv->state = FP_IMAGE_DEVICE_STATE_INACTIVE; - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FPI_STATE]); - - fpi_device_open_complete (FP_DEVICE (self), error); -} - -/** - * fpi_image_device_close_complete: - * @self: a #FpImageDevice imaging fingerprint device - * @error: A #GError or %NULL on success - * - * Reports completion of close operation. - */ -void -fpi_image_device_close_complete (FpImageDevice *self, GError *error) -{ - FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); - FpDeviceAction action; - - action = fpi_device_get_current_action (FP_DEVICE (self)); - - g_debug ("Image device close completed"); - - g_return_if_fail (priv->active == FALSE); - g_return_if_fail (action == FP_DEVICE_ACTION_CLOSE); - - priv->state = FP_IMAGE_DEVICE_STATE_INACTIVE; - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FPI_STATE]); - - fpi_device_close_complete (FP_DEVICE (self), error); } diff --git a/libfprint/fpi-image-device.c b/libfprint/fpi-image-device.c new file mode 100644 index 00000000..6e5802ed --- /dev/null +++ b/libfprint/fpi-image-device.c @@ -0,0 +1,595 @@ +/* + * FpImageDevice - An image based fingerprint reader device - Private APIs + * Copyright (C) 2019 Benjamin Berg + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define FP_COMPONENT "image_device" +#include "fpi-log.h" + +#include "fp-image-device-private.h" +#include "fp-image-device.h" + +/** + * SECTION: fpi-image + * @title: Internal FpImage + * @short_description: Internal image handling routines + * + * Internal image handling routines. Also see the public FpImage routines. + */ + +/* Manually redefine what G_DEFINE_* macro does */ +static inline gpointer +fp_image_device_get_instance_private (FpImageDevice *self) +{ + FpImageDeviceClass *img_class = g_type_class_peek_static (FP_TYPE_IMAGE_DEVICE); + + return G_STRUCT_MEMBER_P (self, + g_type_class_get_instance_private_offset (img_class)); +} + +/* Private shared functions */ + +void +fpi_image_device_activate (FpImageDevice *self) +{ + FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); + FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (self); + + g_assert (!priv->active); + + /* We don't have a neutral ACTIVE state, but we always will + * go into WAIT_FINGER_ON afterwards. */ + priv->state = FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON; + g_object_notify (G_OBJECT (self), "fp-image-device-state"); + + /* We might have been waiting for deactivation to finish before + * starting the next operation. */ + g_clear_handle_id (&priv->pending_activation_timeout_id, g_source_remove); + + fp_dbg ("Activating image device\n"); + cls->activate (self); +} + +void +fpi_image_device_deactivate (FpImageDevice *self) +{ + FpDevice *device = FP_DEVICE (self); + FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); + FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (device); + + if (!priv->active) + { + /* XXX: We currently deactivate both from minutiae scan result + * and finger off report. */ + fp_dbg ("Already deactivated, ignoring request."); + return; + } + if (!priv->cancelling && priv->state == FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON) + g_warning ("Deactivating image device while waiting for finger, this should not happen."); + + priv->state = FP_IMAGE_DEVICE_STATE_INACTIVE; + g_object_notify (G_OBJECT (self), "fp-image-device-state"); + + fp_dbg ("Deactivating image device\n"); + cls->deactivate (self); +} + +/* Static helper functions */ + +static void +fp_image_device_change_state (FpImageDevice *self, FpImageDeviceState state) +{ + FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); + + /* Cannot change to inactive using this function. */ + g_assert (state != FP_IMAGE_DEVICE_STATE_INACTIVE); + + /* We might have been waiting for the finger to go OFF to start the + * next operation. */ + g_clear_handle_id (&priv->pending_activation_timeout_id, g_source_remove); + + fp_dbg ("Image device internal state change from %d to %d\n", priv->state, state); + + priv->state = state; + g_object_notify (G_OBJECT (self), "fp-image-device-state"); + g_signal_emit_by_name (self, "fp-image-device-state-changed", priv->state); +} + +static void +fp_image_device_enroll_maybe_await_finger_on (FpImageDevice *self) +{ + FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); + + if (priv->enroll_await_on_pending) + { + priv->enroll_await_on_pending = FALSE; + fp_image_device_change_state (self, FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON); + } + else + { + priv->enroll_await_on_pending = TRUE; + } +} + +static void +fpi_image_device_minutiae_detected (GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(FpImage) image = FP_IMAGE (source_object); + g_autoptr(FpPrint) print = NULL; + GError *error = NULL; + FpImageDevice *self = FP_IMAGE_DEVICE (user_data); + FpDevice *device = FP_DEVICE (self); + FpImageDevicePrivate *priv; + FpDeviceAction action; + + /* Note: We rely on the device to not disappear during an operation. */ + + if (!fp_image_detect_minutiae_finish (image, res, &error)) + { + /* Cancel operation . */ + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + { + fpi_device_action_error (device, g_steal_pointer (&error)); + fpi_image_device_deactivate (self); + return; + } + + /* Replace error with a retry condition. */ + g_warning ("Failed to detect minutiae: %s", error->message); + g_clear_pointer (&error, g_error_free); + + error = fpi_device_retry_new_msg (FP_DEVICE_RETRY_GENERAL, "Minutiae detection failed, please retry"); + } + + priv = fp_image_device_get_instance_private (FP_IMAGE_DEVICE (device)); + action = fpi_device_get_current_action (device); + + if (action == FP_DEVICE_ACTION_CAPTURE) + { + fpi_device_capture_complete (device, g_steal_pointer (&image), error); + fpi_image_device_deactivate (self); + return; + } + + if (!error) + { + print = fp_print_new (device); + fpi_print_set_type (print, FP_PRINT_NBIS); + if (!fpi_print_add_from_image (print, image, &error)) + g_clear_object (&print); + } + + if (action == FP_DEVICE_ACTION_ENROLL) + { + FpPrint *enroll_print; + fpi_device_get_enroll_data (device, &enroll_print); + + if (print) + { + fpi_print_add_print (enroll_print, print); + priv->enroll_stage += 1; + } + + fpi_device_enroll_progress (device, priv->enroll_stage, + g_steal_pointer (&print), error); + + /* Start another scan or deactivate. */ + if (priv->enroll_stage == IMG_ENROLL_STAGES) + { + fpi_device_enroll_complete (device, g_object_ref (enroll_print), NULL); + fpi_image_device_deactivate (self); + } + else + { + fp_image_device_enroll_maybe_await_finger_on (FP_IMAGE_DEVICE (device)); + } + } + else if (action == FP_DEVICE_ACTION_VERIFY) + { + FpPrint *template; + FpiMatchResult result; + + fpi_device_get_verify_data (device, &template); + if (print) + result = fpi_print_bz3_match (template, print, priv->bz3_threshold, &error); + else + result = FPI_MATCH_ERROR; + + fpi_device_verify_complete (device, result, g_steal_pointer (&print), error); + fpi_image_device_deactivate (self); + } + else if (action == FP_DEVICE_ACTION_IDENTIFY) + { + gint i; + GPtrArray *templates; + FpPrint *result = NULL; + + fpi_device_get_identify_data (device, &templates); + for (i = 0; !error && i < templates->len; i++) + { + FpPrint *template = g_ptr_array_index (templates, i); + + if (fpi_print_bz3_match (template, print, priv->bz3_threshold, &error) == FPI_MATCH_SUCCESS) + { + result = g_object_ref (template); + break; + } + } + + fpi_device_identify_complete (device, result, g_steal_pointer (&print), error); + fpi_image_device_deactivate (self); + } + else + { + /* XXX: This can be hit currently due to a race condition in the enroll code! + * In that case we scan a further image even though the minutiae for the previous + * one have not yet been detected. + * We need to keep track on the pending minutiae detection and the fact that + * it will finish eventually (or we may need to retry on error and activate the + * device again). */ + g_assert_not_reached (); + } +} + +/*********************************************************/ +/* Private API */ + +/** + * fpi_image_device_set_bz3_threshold: + * @self: a #FpImageDevice imaging fingerprint device + * @bz3_threshold: BZ3 threshold to use + * + * Dynamically adjust the bz3 threshold. This is only needed for drivers + * that support devices with different properties. It should generally be + * called from the probe callback, but is acceptable to call from the open + * callback. + */ +void +fpi_image_device_set_bz3_threshold (FpImageDevice *self, + gint bz3_threshold) +{ + FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); + + g_return_if_fail (FP_IS_IMAGE_DEVICE (self)); + g_return_if_fail (bz3_threshold > 0); + + priv->bz3_threshold = bz3_threshold; +} + +/** + * fpi_image_device_report_finger_status: + * @self: a #FpImageDevice imaging fingerprint device + * @present: whether the finger is present on the sensor + * + * Reports from the driver whether the user's finger is on + * the sensor. + */ +void +fpi_image_device_report_finger_status (FpImageDevice *self, + gboolean present) +{ + FpDevice *device = FP_DEVICE (self); + FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); + FpDeviceAction action; + + if (priv->state == FP_IMAGE_DEVICE_STATE_INACTIVE) + { + /* Do we really want to always ignore such reports? We could + * also track the state in case the user had the finger on + * the device at initialisation time and the driver reports + * this early. + */ + g_debug ("Ignoring finger presence report as the device is not active!"); + return; + } + + action = fpi_device_get_current_action (device); + + g_assert (action != FP_DEVICE_ACTION_OPEN); + g_assert (action != FP_DEVICE_ACTION_CLOSE); + + g_debug ("Image device reported finger status: %s", present ? "on" : "off"); + + if (present && priv->state == FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON) + { + fp_image_device_change_state (self, FP_IMAGE_DEVICE_STATE_CAPTURE); + } + else if (!present && priv->state == FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF) + { + /* We need to deactivate or continue to await finger */ + + /* There are three possible situations: + * 1. We are deactivating the device and the action is still in progress + * (minutiae detection). + * 2. We are still deactivating the device after an action completed + * 3. We were waiting for finger removal to start the new action + * Either way, we always end up deactivating except for the enroll case. + * + * The enroll case is special as AWAIT_FINGER_ON should only happen after + * minutiae detection to prevent deactivation (without cancellation) + * from the AWAIT_FINGER_ON state. + */ + if (action != FP_DEVICE_ACTION_ENROLL) + fpi_image_device_deactivate (self); + else + fp_image_device_enroll_maybe_await_finger_on (self); + } +} + +/** + * fpi_image_device_image_captured: + * @self: a #FpImageDevice imaging fingerprint device + * @image: whether the finger is present on the sensor + * + * Reports an image capture. Only use this function if the image was + * captured successfully. If there was an issue where the user should + * retry, use fpi_image_device_retry_scan() to report the retry condition. + * + * In the event of a fatal error for the operation use + * fpi_image_device_session_error(). This will abort the entire operation + * including e.g. an enroll operation which captures multiple images during + * one session. + */ +void +fpi_image_device_image_captured (FpImageDevice *self, FpImage *image) +{ + FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); + FpDeviceAction action; + + action = fpi_device_get_current_action (FP_DEVICE (self)); + + g_return_if_fail (image != NULL); + g_return_if_fail (priv->state == FP_IMAGE_DEVICE_STATE_CAPTURE); + g_return_if_fail (action == FP_DEVICE_ACTION_ENROLL || + action == FP_DEVICE_ACTION_VERIFY || + action == FP_DEVICE_ACTION_IDENTIFY || + action == FP_DEVICE_ACTION_CAPTURE); + + fp_image_device_change_state (self, FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF); + + g_debug ("Image device captured an image"); + + /* XXX: We also detect minutiae in capture mode, we solely do this + * to normalize the image which will happen as a by-product. */ + fp_image_detect_minutiae (image, + fpi_device_get_cancellable (FP_DEVICE (self)), + fpi_image_device_minutiae_detected, + self); +} + +/** + * fpi_image_device_retry_scan: + * @self: a #FpImageDevice imaging fingerprint device + * @retry: The #FpDeviceRetry error code to report + * + * Reports a scan failure to the user. This may or may not abort the + * current session. It is the equivalent of fpi_image_device_image_captured() + * in the case of a retryable error condition (e.g. short swipe). + */ +void +fpi_image_device_retry_scan (FpImageDevice *self, FpDeviceRetry retry) +{ + FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); + FpDeviceAction action; + GError *error; + + action = fpi_device_get_current_action (FP_DEVICE (self)); + + /* We might be waiting for a finger at this point, so just accept + * all but INACTIVE */ + g_return_if_fail (priv->state != FP_IMAGE_DEVICE_STATE_INACTIVE); + g_return_if_fail (action == FP_DEVICE_ACTION_ENROLL || + action == FP_DEVICE_ACTION_VERIFY || + action == FP_DEVICE_ACTION_IDENTIFY || + action == FP_DEVICE_ACTION_CAPTURE); + + error = fpi_device_retry_new (retry); + + if (action == FP_DEVICE_ACTION_ENROLL) + { + g_debug ("Reporting retry during enroll"); + fpi_device_enroll_progress (FP_DEVICE (self), priv->enroll_stage, NULL, error); + } + else + { + /* We abort the operation and let the surrounding code retry in the + * non-enroll case (this is identical to a session error). */ + g_debug ("Abort current operation due to retry (non-enroll case)"); + fpi_image_device_deactivate (self); + fpi_device_action_error (FP_DEVICE (self), error); + } +} + +/** + * fpi_image_device_session_error: + * @self: a #FpImageDevice imaging fingerprint device + * @error: The #GError to report + * + * Report an error while interacting with the device. This effectively + * aborts the current ongoing action. + */ +void +fpi_image_device_session_error (FpImageDevice *self, GError *error) +{ + FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); + + g_return_if_fail (self); + + if (!error) + { + g_warning ("Driver did not provide an error, generating a generic one"); + error = g_error_new (FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL, "Driver reported session error without an error"); + } + + if (!priv->active) + { + FpDeviceAction action = fpi_device_get_current_action (FP_DEVICE (self)); + g_warning ("Driver reported session error, but device is inactive."); + + if (action != FP_DEVICE_ACTION_NONE) + { + g_warning ("Translating to activation failure!"); + fpi_image_device_activate_complete (self, error); + return; + } + } + else if (priv->state == FP_IMAGE_DEVICE_STATE_INACTIVE) + { + g_warning ("Driver reported session error; translating to deactivation failure."); + fpi_image_device_deactivate_complete (self, error); + return; + } + + if (error->domain == FP_DEVICE_RETRY) + g_warning ("Driver should report retries using fpi_image_device_retry_scan!"); + + fpi_image_device_deactivate (self); + fpi_device_action_error (FP_DEVICE (self), error); +} + +/** + * fpi_image_device_activate_complete: + * @self: a #FpImageDevice imaging fingerprint device + * @error: A #GError or %NULL on success + * + * Reports completion of device activation. + */ +void +fpi_image_device_activate_complete (FpImageDevice *self, GError *error) +{ + FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); + FpDeviceAction action; + + action = fpi_device_get_current_action (FP_DEVICE (self)); + + g_return_if_fail (priv->active == FALSE); + g_return_if_fail (action == FP_DEVICE_ACTION_ENROLL || + action == FP_DEVICE_ACTION_VERIFY || + action == FP_DEVICE_ACTION_IDENTIFY || + action == FP_DEVICE_ACTION_CAPTURE); + + if (error) + { + g_debug ("Image device activation failed"); + fpi_device_action_error (FP_DEVICE (self), error); + return; + } + + g_debug ("Image device activation completed"); + + priv->active = TRUE; + + /* We always want to capture at this point, move to AWAIT_FINGER + * state. */ + fp_image_device_change_state (self, FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON); +} + +/** + * fpi_image_device_deactivate_complete: + * @self: a #FpImageDevice imaging fingerprint device + * @error: A #GError or %NULL on success + * + * Reports completion of device deactivation. + */ +void +fpi_image_device_deactivate_complete (FpImageDevice *self, GError *error) +{ + FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); + FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (self); + FpDeviceAction action; + + g_return_if_fail (priv->active == TRUE); + g_return_if_fail (priv->state == FP_IMAGE_DEVICE_STATE_INACTIVE); + + g_debug ("Image device deactivation completed"); + + priv->active = FALSE; + + /* Deactivation completed. As we deactivate in the background + * there may already be a new task pending. Check whether we + * need to do anything. */ + action = fpi_device_get_current_action (FP_DEVICE (self)); + + /* Special case, if we should be closing, but didn't due to a running + * deactivation, then do so now. */ + if (action == FP_DEVICE_ACTION_CLOSE) + { + cls->img_close (self); + return; + } + + /* We might be waiting to be able to activate again. */ + if (priv->pending_activation_timeout_id) + { + g_clear_handle_id (&priv->pending_activation_timeout_id, g_source_remove); + priv->pending_activation_timeout_id = + g_idle_add ((GSourceFunc) fpi_image_device_activate, self); + } +} + +/** + * fpi_image_device_open_complete: + * @self: a #FpImageDevice imaging fingerprint device + * @error: A #GError or %NULL on success + * + * Reports completion of open operation. + */ +void +fpi_image_device_open_complete (FpImageDevice *self, GError *error) +{ + FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); + FpDeviceAction action; + + action = fpi_device_get_current_action (FP_DEVICE (self)); + + g_return_if_fail (priv->active == FALSE); + g_return_if_fail (action == FP_DEVICE_ACTION_OPEN); + + g_debug ("Image device open completed"); + + priv->state = FP_IMAGE_DEVICE_STATE_INACTIVE; + g_object_notify (G_OBJECT (self), "fp-image-device-state"); + + fpi_device_open_complete (FP_DEVICE (self), error); +} + +/** + * fpi_image_device_close_complete: + * @self: a #FpImageDevice imaging fingerprint device + * @error: A #GError or %NULL on success + * + * Reports completion of close operation. + */ +void +fpi_image_device_close_complete (FpImageDevice *self, GError *error) +{ + FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); + FpDeviceAction action; + + action = fpi_device_get_current_action (FP_DEVICE (self)); + + g_debug ("Image device close completed"); + + g_return_if_fail (priv->active == FALSE); + g_return_if_fail (action == FP_DEVICE_ACTION_CLOSE); + + priv->state = FP_IMAGE_DEVICE_STATE_INACTIVE; + g_object_notify (G_OBJECT (self), "fp-image-device-state"); + + fpi_device_close_complete (FP_DEVICE (self), error); +} diff --git a/libfprint/meson.build b/libfprint/meson.build index 4a34cbde..9eb48498 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -9,6 +9,7 @@ libfprint_sources = [ libfprint_private_sources = [ 'fpi-assembling.c', 'fpi-device.c', + 'fpi-image-device.c', 'fpi-ssm.c', 'fpi-usb-transfer.c', 'fpi-byte-reader.c', From d7100e41df96d3f6f4929296a2c30503d648e0e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 11 Dec 2019 19:55:47 +0100 Subject: [PATCH 118/237] fp-image, fp-print: Move private methods to own code units --- libfprint/fp-image.c | 128 +----------------- libfprint/fp-print-private.h | 46 +++++++ libfprint/fp-print.c | 247 +--------------------------------- libfprint/fpi-image.c | 150 +++++++++++++++++++++ libfprint/fpi-print.c | 249 +++++++++++++++++++++++++++++++++++ libfprint/meson.build | 2 + 6 files changed, 452 insertions(+), 370 deletions(-) create mode 100644 libfprint/fp-print-private.h create mode 100644 libfprint/fpi-image.c create mode 100644 libfprint/fpi-print.c diff --git a/libfprint/fp-image.c b/libfprint/fp-image.c index 16837a81..ac70d68d 100644 --- a/libfprint/fp-image.c +++ b/libfprint/fp-image.c @@ -18,15 +18,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#define FP_COMPONENT "image" + #include "fpi-image.h" #include "fpi-log.h" #include -#if HAVE_PIXMAN -#include -#endif - /** * SECTION: fp-image * @title: FpImage @@ -36,15 +34,6 @@ * this object allows accessing this data. */ -/** - * SECTION: fpi-image - * @title: Internal FpImage - * @short_description: Internal image handling routines - * - * Internal image handling routines. Also see the public FpImage routines. - */ - G_DEFINE_TYPE (FpImage, fp_image, G_TYPE_OBJECT) enum { @@ -479,78 +468,6 @@ fp_image_detect_minutiae_finish (FpImage *self, return g_task_propagate_boolean (G_TASK (result), error); } - - -/** - * fpi_std_sq_dev: - * @buf: buffer (usually bitmap, one byte per pixel) - * @size: size of @buffer - * - * Calculates the squared standard deviation of the individual - * pixels in the buffer, as per the following formula: - * |[ - * mean = sum (buf[0..size]) / size - * sq_dev = sum ((buf[0.size] - mean) ^ 2) - * ]| - * This function is usually used to determine whether image - * is empty. - * - * Returns: the squared standard deviation for @buffer - */ -gint -fpi_std_sq_dev (const guint8 *buf, - gint size) -{ - guint64 res = 0, mean = 0; - gint i; - - for (i = 0; i < size; i++) - mean += buf[i]; - - mean /= size; - - for (i = 0; i < size; i++) - { - int dev = (int) buf[i] - mean; - res += dev * dev; - } - - return res / size; -} - -/** - * fpi_mean_sq_diff_norm: - * @buf1: buffer (usually bitmap, one byte per pixel) - * @buf2: buffer (usually bitmap, one byte per pixel) - * @size: buffer size of smallest buffer - * - * This function calculates the normalized mean square difference of - * two buffers, usually two lines, as per the following formula: - * |[ - * sq_diff = sum ((buf1[0..size] - buf2[0..size]) ^ 2) / size - * ]| - * - * This functions is usually used to get numerical difference - * between two images. - * - * Returns: the normalized mean squared difference between @buf1 and @buf2 - */ -gint -fpi_mean_sq_diff_norm (const guint8 *buf1, - const guint8 *buf2, - gint size) -{ - int res = 0, i; - - for (i = 0; i < size; i++) - { - int dev = (int) buf1[i] - (int) buf2[i]; - res += dev * dev; - } - - return res / size; -} - /** * fp_minutia_get_coords: * @min: A #FpMinutia @@ -568,44 +485,3 @@ fp_minutia_get_coords (FpMinutia *min, gint *x, gint *y) if (y) *y = min->y; } - -#if HAVE_PIXMAN -FpImage * -fpi_image_resize (FpImage *orig_img, - guint w_factor, - guint h_factor) -{ - int new_width = orig_img->width * w_factor; - int new_height = orig_img->height * h_factor; - pixman_image_t *orig, *resized; - pixman_transform_t transform; - FpImage *newimg; - - orig = pixman_image_create_bits (PIXMAN_a8, orig_img->width, orig_img->height, (uint32_t *) orig_img->data, orig_img->width); - resized = pixman_image_create_bits (PIXMAN_a8, new_width, new_height, NULL, new_width); - - pixman_transform_init_identity (&transform); - pixman_transform_scale (NULL, &transform, pixman_int_to_fixed (w_factor), pixman_int_to_fixed (h_factor)); - pixman_image_set_transform (orig, &transform); - pixman_image_set_filter (orig, PIXMAN_FILTER_BILINEAR, NULL, 0); - pixman_image_composite32 (PIXMAN_OP_SRC, - orig, /* src */ - NULL, /* mask */ - resized, /* dst */ - 0, 0, /* src x y */ - 0, 0, /* mask x y */ - 0, 0, /* dst x y */ - new_width, new_height /* width height */ - ); - - newimg = fp_image_new (new_width, new_height); - newimg->flags = orig_img->flags; - - memcpy (newimg->data, pixman_image_get_data (resized), new_width * new_height); - - pixman_image_unref (orig); - pixman_image_unref (resized); - - return newimg; -} -#endif diff --git a/libfprint/fp-print-private.h b/libfprint/fp-print-private.h new file mode 100644 index 00000000..f5822b3a --- /dev/null +++ b/libfprint/fp-print-private.h @@ -0,0 +1,46 @@ +/* + * FPrint Print handling + * Copyright (C) 2007 Daniel Drake + * Copyright (C) 2019 Benjamin Berg + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "fpi-print.h" +#include "fpi-image.h" + +#include + +struct _FpPrint +{ + GInitiallyUnowned parent_instance; + + FpPrintType type; + + gchar *driver; + gchar *device_id; + gboolean device_stored; + + FpImage *image; + + /* Metadata */ + FpFinger finger; + gchar *username; + gchar *description; + GDate *enroll_date; + + GVariant *data; + GPtrArray *prints; +}; diff --git a/libfprint/fp-print.c b/libfprint/fp-print.c index f724c776..30fdf1a4 100644 --- a/libfprint/fp-print.c +++ b/libfprint/fp-print.c @@ -18,12 +18,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "fpi-print.h" -#include "fpi-image.h" -#include "fpi-log.h" -#include "fpi-device.h" +#define FP_COMPONENT "print" -#include +#include "fp-print-private.h" +#include "fpi-log.h" /** * SECTION: fp-print @@ -42,28 +40,6 @@ * #FpPrint routines. */ -struct _FpPrint -{ - GInitiallyUnowned parent_instance; - - FpPrintType type; - - gchar *driver; - gchar *device_id; - gboolean device_stored; - - FpImage *image; - - /* Metadata */ - FpFinger finger; - gchar *username; - gchar *description; - GDate *enroll_date; - - GVariant *data; - GPtrArray *prints; -}; - G_DEFINE_TYPE (FpPrint, fp_print, G_TYPE_INITIALLY_UNOWNED) enum { @@ -540,223 +516,6 @@ fp_print_set_enroll_date (FpPrint *print, g_object_notify_by_pspec (G_OBJECT (print), properties[PROP_ENROLL_DATE]); } - - -/** - * fpi_print_add_print: - * @print: A #FpPrint - * @add: Print to append to @print - * - * Appends the single #FP_PRINT_NBIS print from @add to the collection of - * prints in @print. Both print objects need to be of type #FP_PRINT_NBIS - * for this to work. - */ -void -fpi_print_add_print (FpPrint *print, FpPrint *add) -{ - g_return_if_fail (print->type == FP_PRINT_NBIS); - g_return_if_fail (add->type == FP_PRINT_NBIS); - - g_assert (add->prints->len == 1); - g_ptr_array_add (print->prints, g_memdup (add->prints->pdata[0], sizeof (struct xyt_struct))); -} - -/** - * fpi_print_set_type: - * @print: A #FpPrint - * @type: The newly type of the print data - * - * This function can only be called exactly once. Drivers should - * call it after creating a new print, or to initialize the template - * print passed during enrollment. - */ -void -fpi_print_set_type (FpPrint *print, - FpPrintType type) -{ - g_return_if_fail (FP_IS_PRINT (print)); - /* We only allow setting this once! */ - g_return_if_fail (print->type == FP_PRINT_UNDEFINED); - - print->type = type; - if (print->type == FP_PRINT_NBIS) - { - g_assert_null (print->prints); - print->prints = g_ptr_array_new_with_free_func (g_free); - } - g_object_notify_by_pspec (G_OBJECT (print), properties[PROP_FPI_TYPE]); -} - -/** - * fpi_print_set_device_stored: - * @print: A #FpPrint - * @device_stored: Whether the print is stored on the device or not - * - * Drivers must set this to %TRUE for any print that is really a handle - * for data that is stored on the device itself. - */ -void -fpi_print_set_device_stored (FpPrint *print, - gboolean device_stored) -{ - g_return_if_fail (FP_IS_PRINT (print)); - - print->device_stored = device_stored; - g_object_notify_by_pspec (G_OBJECT (print), properties[PROP_DEVICE_STORED]); -} - -/* XXX: This is the old version, but wouldn't it be smarter to instead - * use the highest quality mintutiae? Possibly just using bz_prune from - * upstream? */ -static void -minutiae_to_xyt (struct fp_minutiae *minutiae, - int bwidth, - int bheight, - struct xyt_struct *xyt) -{ - int i; - struct fp_minutia *minutia; - struct minutiae_struct c[MAX_FILE_MINUTIAE]; - - /* struct xyt_struct uses arrays of MAX_BOZORTH_MINUTIAE (200) */ - int nmin = min (minutiae->num, MAX_BOZORTH_MINUTIAE); - - for (i = 0; i < nmin; i++) - { - minutia = minutiae->list[i]; - - lfs2nist_minutia_XYT (&c[i].col[0], &c[i].col[1], &c[i].col[2], - minutia, bwidth, bheight); - c[i].col[3] = sround (minutia->reliability * 100.0); - - if (c[i].col[2] > 180) - c[i].col[2] -= 360; - } - - qsort ((void *) &c, (size_t) nmin, sizeof (struct minutiae_struct), - sort_x_y); - - for (i = 0; i < nmin; i++) - { - xyt->xcol[i] = c[i].col[0]; - xyt->ycol[i] = c[i].col[1]; - xyt->thetacol[i] = c[i].col[2]; - } - xyt->nrows = nmin; -} - -/** - * fpi_print_add_from_image: - * @print: A #FpPrint - * @image: A #FpImage - * @error: Return location for error - * - * Extracts the minutiae from the given image and adds it to @print of - * type #FP_PRINT_NBIS. - * - * The @image will be kept so that API users can get retrieve it e.g. - * for debugging purposes. - * - * Returns: %TRUE on success - */ -gboolean -fpi_print_add_from_image (FpPrint *print, - FpImage *image, - GError **error) -{ - GPtrArray *minutiae; - struct fp_minutiae _minutiae; - struct xyt_struct *xyt; - - if (print->type != FP_PRINT_NBIS || !image) - { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "Cannot add print data from image!"); - return FALSE; - } - - minutiae = fp_image_get_minutiae (image); - if (!minutiae || minutiae->len == 0) - { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "No minutiae found in image or not yet detected!"); - return FALSE; - } - - _minutiae.num = minutiae->len; - _minutiae.list = (struct fp_minutia **) minutiae->pdata; - _minutiae.alloc = minutiae->len; - - xyt = g_new0 (struct xyt_struct, 1); - minutiae_to_xyt (&_minutiae, image->width, image->height, xyt); - g_ptr_array_add (print->prints, xyt); - - g_clear_object (&print->image); - print->image = g_object_ref (image); - g_object_notify_by_pspec (G_OBJECT (print), properties[PROP_IMAGE]); - - return TRUE; -} - -/** - * fpi_print_bz3_match: - * @template: A #FpPrint containing one or more prints - * @print: A newly scanned #FpPrint to test - * @bz3_threshold: The BZ3 match threshold - * @error: Return location for error - * - * Match the newly scanned @print (containing exactly one print) against the - * prints contained in @template which will have been stored during enrollment. - * - * Both @template and @print need to be of type #FP_PRINT_NBIS for this to - * work. - * - * Returns: Whether the prints match, @error will be set if #FPI_MATCH_ERROR is returned - */ -FpiMatchResult -fpi_print_bz3_match (FpPrint *template, FpPrint *print, gint bz3_threshold, GError **error) -{ - struct xyt_struct *pstruct; - gint probe_len; - gint i; - - /* XXX: Use a different error type? */ - if (template->type != FP_PRINT_NBIS || print->type != FP_PRINT_NBIS) - { - *error = fpi_device_error_new_msg (FP_DEVICE_ERROR_NOT_SUPPORTED, - "It is only possible to match NBIS type print data"); - return FPI_MATCH_ERROR; - } - - if (print->prints->len != 1) - { - *error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, - "New print contains more than one print!"); - return FPI_MATCH_ERROR; - } - - pstruct = g_ptr_array_index (print->prints, 0); - probe_len = bozorth_probe_init (pstruct); - - for (i = 0; i < template->prints->len; i++) - { - struct xyt_struct *gstruct; - gint score; - gstruct = g_ptr_array_index (template->prints, i); - score = bozorth_to_gallery (probe_len, pstruct, gstruct); - fp_dbg ("score %d", score); - - if (score >= bz3_threshold) - return FPI_MATCH_SUCCESS; - } - - return FPI_MATCH_FAIL; -} - /** * fp_print_compatible: * @self: A #FpPrint diff --git a/libfprint/fpi-image.c b/libfprint/fpi-image.c new file mode 100644 index 00000000..83440376 --- /dev/null +++ b/libfprint/fpi-image.c @@ -0,0 +1,150 @@ +/* + * FPrint Image - Private APIs + * Copyright (C) 2007 Daniel Drake + * Copyright (C) 2019 Benjamin Berg + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define FP_COMPONENT "image" + +#include "fpi-image.h" +#include "fpi-log.h" + +#include + +#if HAVE_PIXMAN +#include +#endif + +/** + * SECTION: fpi-image + * @title: Internal FpImage + * @short_description: Internal image handling routines + * + * Internal image handling routines. Also see the public FpImage routines. + */ + +/** + * fpi_std_sq_dev: + * @buf: buffer (usually bitmap, one byte per pixel) + * @size: size of @buffer + * + * Calculates the squared standard deviation of the individual + * pixels in the buffer, as per the following formula: + * |[ + * mean = sum (buf[0..size]) / size + * sq_dev = sum ((buf[0.size] - mean) ^ 2) + * ]| + * This function is usually used to determine whether image + * is empty. + * + * Returns: the squared standard deviation for @buffer + */ +gint +fpi_std_sq_dev (const guint8 *buf, + gint size) +{ + guint64 res = 0, mean = 0; + gint i; + + for (i = 0; i < size; i++) + mean += buf[i]; + + mean /= size; + + for (i = 0; i < size; i++) + { + int dev = (int) buf[i] - mean; + res += dev * dev; + } + + return res / size; +} + +/** + * fpi_mean_sq_diff_norm: + * @buf1: buffer (usually bitmap, one byte per pixel) + * @buf2: buffer (usually bitmap, one byte per pixel) + * @size: buffer size of smallest buffer + * + * This function calculates the normalized mean square difference of + * two buffers, usually two lines, as per the following formula: + * |[ + * sq_diff = sum ((buf1[0..size] - buf2[0..size]) ^ 2) / size + * ]| + * + * This functions is usually used to get numerical difference + * between two images. + * + * Returns: the normalized mean squared difference between @buf1 and @buf2 + */ +gint +fpi_mean_sq_diff_norm (const guint8 *buf1, + const guint8 *buf2, + gint size) +{ + int res = 0, i; + + for (i = 0; i < size; i++) + { + int dev = (int) buf1[i] - (int) buf2[i]; + res += dev * dev; + } + + return res / size; +} + +#if HAVE_PIXMAN +FpImage * +fpi_image_resize (FpImage *orig_img, + guint w_factor, + guint h_factor) +{ + int new_width = orig_img->width * w_factor; + int new_height = orig_img->height * h_factor; + pixman_image_t *orig, *resized; + pixman_transform_t transform; + FpImage *newimg; + + orig = pixman_image_create_bits (PIXMAN_a8, orig_img->width, orig_img->height, (uint32_t *) orig_img->data, orig_img->width); + resized = pixman_image_create_bits (PIXMAN_a8, new_width, new_height, NULL, new_width); + + pixman_transform_init_identity (&transform); + pixman_transform_scale (NULL, &transform, pixman_int_to_fixed (w_factor), pixman_int_to_fixed (h_factor)); + pixman_image_set_transform (orig, &transform); + pixman_image_set_filter (orig, PIXMAN_FILTER_BILINEAR, NULL, 0); + pixman_image_composite32 (PIXMAN_OP_SRC, + orig, /* src */ + NULL, /* mask */ + resized, /* dst */ + 0, 0, /* src x y */ + 0, 0, /* mask x y */ + 0, 0, /* dst x y */ + new_width, new_height /* width height */ + ); + + newimg = fp_image_new (new_width, new_height); + newimg->flags = orig_img->flags; + + memcpy (newimg->data, pixman_image_get_data (resized), new_width * new_height); + + pixman_image_unref (orig); + pixman_image_unref (resized); + + return newimg; +} +#endif diff --git a/libfprint/fpi-print.c b/libfprint/fpi-print.c new file mode 100644 index 00000000..a407dd9d --- /dev/null +++ b/libfprint/fpi-print.c @@ -0,0 +1,249 @@ +/* + * FPrint Print handling - Private APIs + * Copyright (C) 2007 Daniel Drake + * Copyright (C) 2019 Benjamin Berg + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define FP_COMPONENT "print" +#include "fpi-log.h" + +#include "fp-print-private.h" +#include "fpi-device.h" + +/** + * SECTION: fpi-print + * @title: Internal FpPrint + * @short_description: Internal fingerprint handling routines + * + * Interaction with prints and their storage. See also the public + * #FpPrint routines. + */ + +/** + * fpi_print_add_print: + * @print: A #FpPrint + * @add: Print to append to @print + * + * Appends the single #FP_PRINT_NBIS print from @add to the collection of + * prints in @print. Both print objects need to be of type #FP_PRINT_NBIS + * for this to work. + */ +void +fpi_print_add_print (FpPrint *print, FpPrint *add) +{ + g_return_if_fail (print->type == FP_PRINT_NBIS); + g_return_if_fail (add->type == FP_PRINT_NBIS); + + g_assert (add->prints->len == 1); + g_ptr_array_add (print->prints, g_memdup (add->prints->pdata[0], sizeof (struct xyt_struct))); +} + +/** + * fpi_print_set_type: + * @print: A #FpPrint + * @type: The newly type of the print data + * + * This function can only be called exactly once. Drivers should + * call it after creating a new print, or to initialize the template + * print passed during enrollment. + */ +void +fpi_print_set_type (FpPrint *print, + FpPrintType type) +{ + g_return_if_fail (FP_IS_PRINT (print)); + /* We only allow setting this once! */ + g_return_if_fail (print->type == FP_PRINT_UNDEFINED); + + print->type = type; + if (print->type == FP_PRINT_NBIS) + { + g_assert_null (print->prints); + print->prints = g_ptr_array_new_with_free_func (g_free); + } + g_object_notify (G_OBJECT (print), "fp-type"); +} + +/** + * fpi_print_set_device_stored: + * @print: A #FpPrint + * @device_stored: Whether the print is stored on the device or not + * + * Drivers must set this to %TRUE for any print that is really a handle + * for data that is stored on the device itself. + */ +void +fpi_print_set_device_stored (FpPrint *print, + gboolean device_stored) +{ + g_return_if_fail (FP_IS_PRINT (print)); + + print->device_stored = device_stored; + g_object_notify (G_OBJECT (print), "device-stored"); +} + +/* XXX: This is the old version, but wouldn't it be smarter to instead + * use the highest quality mintutiae? Possibly just using bz_prune from + * upstream? */ +static void +minutiae_to_xyt (struct fp_minutiae *minutiae, + int bwidth, + int bheight, + struct xyt_struct *xyt) +{ + int i; + struct fp_minutia *minutia; + struct minutiae_struct c[MAX_FILE_MINUTIAE]; + + /* struct xyt_struct uses arrays of MAX_BOZORTH_MINUTIAE (200) */ + int nmin = min (minutiae->num, MAX_BOZORTH_MINUTIAE); + + for (i = 0; i < nmin; i++) + { + minutia = minutiae->list[i]; + + lfs2nist_minutia_XYT (&c[i].col[0], &c[i].col[1], &c[i].col[2], + minutia, bwidth, bheight); + c[i].col[3] = sround (minutia->reliability * 100.0); + + if (c[i].col[2] > 180) + c[i].col[2] -= 360; + } + + qsort ((void *) &c, (size_t) nmin, sizeof (struct minutiae_struct), + sort_x_y); + + for (i = 0; i < nmin; i++) + { + xyt->xcol[i] = c[i].col[0]; + xyt->ycol[i] = c[i].col[1]; + xyt->thetacol[i] = c[i].col[2]; + } + xyt->nrows = nmin; +} + +/** + * fpi_print_add_from_image: + * @print: A #FpPrint + * @image: A #FpImage + * @error: Return location for error + * + * Extracts the minutiae from the given image and adds it to @print of + * type #FP_PRINT_NBIS. + * + * The @image will be kept so that API users can get retrieve it e.g. + * for debugging purposes. + * + * Returns: %TRUE on success + */ +gboolean +fpi_print_add_from_image (FpPrint *print, + FpImage *image, + GError **error) +{ + GPtrArray *minutiae; + struct fp_minutiae _minutiae; + struct xyt_struct *xyt; + + if (print->type != FP_PRINT_NBIS || !image) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "Cannot add print data from image!"); + return FALSE; + } + + minutiae = fp_image_get_minutiae (image); + if (!minutiae || minutiae->len == 0) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "No minutiae found in image or not yet detected!"); + return FALSE; + } + + _minutiae.num = minutiae->len; + _minutiae.list = (struct fp_minutia **) minutiae->pdata; + _minutiae.alloc = minutiae->len; + + xyt = g_new0 (struct xyt_struct, 1); + minutiae_to_xyt (&_minutiae, image->width, image->height, xyt); + g_ptr_array_add (print->prints, xyt); + + g_clear_object (&print->image); + print->image = g_object_ref (image); + g_object_notify (G_OBJECT (print), "image"); + + return TRUE; +} + +/** + * fpi_print_bz3_match: + * @template: A #FpPrint containing one or more prints + * @print: A newly scanned #FpPrint to test + * @bz3_threshold: The BZ3 match threshold + * @error: Return location for error + * + * Match the newly scanned @print (containing exactly one print) against the + * prints contained in @template which will have been stored during enrollment. + * + * Both @template and @print need to be of type #FP_PRINT_NBIS for this to + * work. + * + * Returns: Whether the prints match, @error will be set if #FPI_MATCH_ERROR is returned + */ +FpiMatchResult +fpi_print_bz3_match (FpPrint *template, FpPrint *print, gint bz3_threshold, GError **error) +{ + struct xyt_struct *pstruct; + gint probe_len; + gint i; + + /* XXX: Use a different error type? */ + if (template->type != FP_PRINT_NBIS || print->type != FP_PRINT_NBIS) + { + *error = fpi_device_error_new_msg (FP_DEVICE_ERROR_NOT_SUPPORTED, + "It is only possible to match NBIS type print data"); + return FPI_MATCH_ERROR; + } + + if (print->prints->len != 1) + { + *error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, + "New print contains more than one print!"); + return FPI_MATCH_ERROR; + } + + pstruct = g_ptr_array_index (print->prints, 0); + probe_len = bozorth_probe_init (pstruct); + + for (i = 0; i < template->prints->len; i++) + { + struct xyt_struct *gstruct; + gint score; + gstruct = g_ptr_array_index (template->prints, i); + score = bozorth_to_gallery (probe_len, pstruct, gstruct); + fp_dbg ("score %d", score); + + if (score >= bz3_threshold) + return FPI_MATCH_SUCCESS; + } + + return FPI_MATCH_FAIL; +} diff --git a/libfprint/meson.build b/libfprint/meson.build index 9eb48498..8cb86097 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -9,7 +9,9 @@ libfprint_sources = [ libfprint_private_sources = [ 'fpi-assembling.c', 'fpi-device.c', + 'fpi-image.c', 'fpi-image-device.c', + 'fpi-print.c', 'fpi-ssm.c', 'fpi-usb-transfer.c', 'fpi-byte-reader.c', From 5bed81025e0d39844800ca09af4e588f334db393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 5 Dec 2019 14:28:05 +0100 Subject: [PATCH 119/237] meson: Use files to track the map file presence --- libfprint/meson.build | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libfprint/meson.build b/libfprint/meson.build index 8cb86097..d3d0fd69 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -186,9 +186,6 @@ drivers_sources += configure_file(input: 'empty_file', '\n'.join(drivers_type_list + [] + drivers_type_func) ]) -mapfile = 'libfprint.ver' -vflag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), mapfile) - deps = [ mathlib_dep, glib_dep, gusb_dep, nss_dep, imaging_dep, gio_dep ] deps += declare_dependency(include_directories: [ @@ -212,6 +209,9 @@ libfprint_private = static_library('fprint-private', dependencies: deps, install: false) +mapfile = files('libfprint.ver') +vflag = '-Wl,--version-script,@0@/@1@'.format(meson.source_root(), mapfile[0]) + libfprint = library('fprint', sources: libfprint_sources + fp_enums + drivers_sources + other_sources, soversion: soversion, From c3bf6fe86353c28405f66157f2bb46cceaf9e33c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 12 Dec 2019 14:52:23 +0100 Subject: [PATCH 120/237] meson: Rely on libfprint dependency to get root_include No need to redefine it in all our tools --- demo/meson.build | 3 --- examples/meson.build | 8 ++------ 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/demo/meson.build b/demo/meson.build index 279a43c5..20f89625 100644 --- a/demo/meson.build +++ b/demo/meson.build @@ -10,9 +10,6 @@ datadir = join_paths(prefix, get_option('datadir')) executable('gtk-libfprint-test', [ 'gtk-libfprint-test.c', gtk_test_resources ], dependencies: [ libfprint_dep, gtk_dep ], - include_directories: [ - root_inc, - ], c_args: '-DPACKAGE_VERSION="' + meson.project_version() + '"', install: true, install_dir: bindir) diff --git a/examples/meson.build b/examples/meson.build index eef8c3f6..7b313d09 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -4,14 +4,10 @@ foreach example: examples executable(example, [ example + '.c', 'storage.c', 'utilities.c' ], dependencies: [ libfprint_dep, glib_dep ], - include_directories: [ - root_inc, - ]) + ) endforeach executable('cpp-test', 'cpp-test.cpp', dependencies: libfprint_dep, - include_directories: [ - root_inc, - ]) +) From 6f2b1f97daee59c05d0f9c7497113471bcb6e782 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 11 Dec 2019 20:21:01 +0100 Subject: [PATCH 121/237] fprint: Move drivers to private internal library This allows us to finally remove fpi_get_driver_types from the exported list of symbols. --- libfprint/libfprint.ver | 3 --- libfprint/meson.build | 25 ++++++++++++++++++++----- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/libfprint/libfprint.ver b/libfprint/libfprint.ver index 7b484f6a..d99a456a 100644 --- a/libfprint/libfprint.ver +++ b/libfprint/libfprint.ver @@ -1,9 +1,6 @@ LIBFPRINT_2.0.0 { global: fp_*; - - /* Needs to be public for the listing commands. */ - fpi_get_driver_types; local: *; }; diff --git a/libfprint/meson.build b/libfprint/meson.build index d3d0fd69..06668b3d 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -207,19 +207,26 @@ libnbis = static_library('nbis', libfprint_private = static_library('fprint-private', sources: libfprint_private_sources + fpi_enums, dependencies: deps, + link_with: libnbis, + install: false) + +libfprint_drivers = static_library('fprint-drivers', + sources: drivers_sources + [ fp_enums_h ], + c_args: drivers_cflags, + dependencies: deps, + link_with: libfprint_private, install: false) mapfile = files('libfprint.ver') vflag = '-Wl,--version-script,@0@/@1@'.format(meson.source_root(), mapfile[0]) libfprint = library('fprint', - sources: libfprint_sources + fp_enums + drivers_sources + other_sources, + sources: libfprint_sources + fp_enums + other_sources, soversion: soversion, version: libversion, - c_args: drivers_cflags, link_args : vflag, link_depends : mapfile, - link_with: [libnbis, libfprint_private], + link_with: [libfprint_private, libfprint_drivers], dependencies: deps, install: true) @@ -230,9 +237,16 @@ libfprint_dep = declare_dependency(link_with: libfprint, install_headers(['fprint.h'] + libfprint_public_headers, subdir: 'libfprint') +libfprint_private_dep = declare_dependency( + include_directories: include_directories('.'), + link_with: libfprint_private, + dependencies: [ deps, libfprint_dep ] +) + udev_rules = executable('fprint-list-udev-rules', 'fprint-list-udev-rules.c', - dependencies: [ deps, libfprint_dep ], + dependencies: libfprint_private_dep, + link_with: libfprint_drivers, install: false) if get_option('udev_rules') @@ -246,7 +260,8 @@ endif supported_devices = executable('fprint-list-supported-devices', 'fprint-list-supported-devices.c', - dependencies: [ deps, libfprint_dep ], + dependencies: libfprint_private_dep, + link_with: libfprint_drivers, install: false) From 8184e33dd65670b4410106448330fc57d320882e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Sat, 14 Dec 2019 16:45:11 +0100 Subject: [PATCH 122/237] meson: Fix syntax in the auto-generated fpi-drivers file Better to be nice everywhere :) --- meson.build | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/meson.build b/meson.build index 1561ebf6..b7ba901e 100644 --- a/meson.build +++ b/meson.build @@ -152,13 +152,18 @@ drivers_type_list += '#include ' drivers_type_list += '#include "fpi-context.h"' drivers_type_list += '' drivers_type_func += 'void fpi_get_driver_types (GArray *drivers)' -drivers_type_func += ' {' -drivers_type_func += ' GType t;' +drivers_type_func += '{' +drivers_type_func += ' GType t;' drivers_type_func += '' +idx = 0 foreach driver: drivers drivers_type_list += 'extern GType (fpi_device_' + driver + '_get_type) (void);' - drivers_type_func += ' t = fpi_device_' + driver + '_get_type ();' - drivers_type_func += ' g_array_append_val (drivers, t);\n' + drivers_type_func += ' t = fpi_device_' + driver + '_get_type ();' + drivers_type_func += ' g_array_append_val (drivers, t);' + if idx != drivers.length() - 1 + drivers_type_func += '' + idx += 1 + endif endforeach drivers_type_list += '' drivers_type_func += '}' From 09576e52091853d6cba72f4df57854c20d385ac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Sat, 14 Dec 2019 16:56:15 +0100 Subject: [PATCH 123/237] fpi-context: Make fpi_get_driver_types to return an array No need to create one all the times and the fill it with what we need. --- libfprint/fp-context.c | 3 +-- libfprint/fpi-context.h | 5 +++-- libfprint/fprint-list-supported-devices.c | 4 +--- libfprint/fprint-list-udev-rules.c | 4 +--- meson.build | 11 +++++------ 5 files changed, 11 insertions(+), 16 deletions(-) diff --git a/libfprint/fp-context.c b/libfprint/fp-context.c index eed7847e..3e47f030 100644 --- a/libfprint/fp-context.c +++ b/libfprint/fp-context.c @@ -243,8 +243,7 @@ fp_context_init (FpContext *self) g_autoptr(GError) error = NULL; FpContextPrivate *priv = fp_context_get_instance_private (self); - priv->drivers = g_array_new (TRUE, FALSE, sizeof (GType)); - fpi_get_driver_types (priv->drivers); + priv->drivers = fpi_get_driver_types (); priv->devices = g_ptr_array_new_with_free_func (g_object_unref); diff --git a/libfprint/fpi-context.h b/libfprint/fpi-context.h index c5a10750..48fecb48 100644 --- a/libfprint/fpi-context.h +++ b/libfprint/fpi-context.h @@ -23,11 +23,12 @@ /** * fpi_get_driver_types: - * @drivers: #GArray to be filled with all driver types * * This function is purely for private used. It is solely part of the public * API as it is useful during build time. * * Stability: private + * Returns: (element-type GType) (transfer container): a #GArray filled with + * all driver types */ -void fpi_get_driver_types (GArray *drivers); +GArray *fpi_get_driver_types (void); diff --git a/libfprint/fprint-list-supported-devices.c b/libfprint/fprint-list-supported-devices.c index 124e9d9e..55da252a 100644 --- a/libfprint/fprint-list-supported-devices.c +++ b/libfprint/fprint-list-supported-devices.c @@ -31,11 +31,9 @@ GHashTable *printed = NULL; static GList * insert_drivers (GList *list) { - g_autoptr(GArray) drivers = g_array_new (FALSE, FALSE, sizeof (GType)); + g_autoptr(GArray) drivers = fpi_get_driver_types (); gint i; - fpi_get_driver_types (drivers); - /* Find the best driver to handle this USB device. */ for (i = 0; i < drivers->len; i++) { diff --git a/libfprint/fprint-list-udev-rules.c b/libfprint/fprint-list-udev-rules.c index c0a3337f..335c37b7 100644 --- a/libfprint/fprint-list-udev-rules.c +++ b/libfprint/fprint-list-udev-rules.c @@ -96,11 +96,9 @@ print_driver (const FpDeviceClass *cls) int main (int argc, char **argv) { - g_autoptr(GArray) drivers = g_array_new (FALSE, FALSE, sizeof (GType)); + g_autoptr(GArray) drivers = fpi_get_driver_types (); guint i; - fpi_get_driver_types (drivers); - printed = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); for (i = 0; i < drivers->len; i++) diff --git a/meson.build b/meson.build index b7ba901e..3f72118e 100644 --- a/meson.build +++ b/meson.build @@ -151,21 +151,20 @@ drivers_type_func = [] drivers_type_list += '#include ' drivers_type_list += '#include "fpi-context.h"' drivers_type_list += '' -drivers_type_func += 'void fpi_get_driver_types (GArray *drivers)' +drivers_type_func += 'GArray *' +drivers_type_func += 'fpi_get_driver_types (void)' drivers_type_func += '{' +drivers_type_func += ' GArray *drivers = g_array_new (TRUE, FALSE, sizeof (GType));' drivers_type_func += ' GType t;' drivers_type_func += '' -idx = 0 foreach driver: drivers drivers_type_list += 'extern GType (fpi_device_' + driver + '_get_type) (void);' drivers_type_func += ' t = fpi_device_' + driver + '_get_type ();' drivers_type_func += ' g_array_append_val (drivers, t);' - if idx != drivers.length() - 1 - drivers_type_func += '' - idx += 1 - endif + drivers_type_func += '' endforeach drivers_type_list += '' +drivers_type_func += ' return drivers;' drivers_type_func += '}' root_inc = include_directories('.') From 43a8c909bf7ac9a8867ca34a564f63528e3f9b24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 13 Dec 2019 20:34:08 +0100 Subject: [PATCH 124/237] fp-context: Use an env to define a whitelist of drivers to enable This avoids that we pick unwanted drivers when running the tests in a machine that has some supported device attached. --- libfprint/fp-context.c | 45 ++++++++++++++++++++++++++++++++++++++++++ tests/meson.build | 8 +++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/libfprint/fp-context.c b/libfprint/fp-context.c index 3e47f030..6764241e 100644 --- a/libfprint/fp-context.c +++ b/libfprint/fp-context.c @@ -57,6 +57,35 @@ enum { }; static guint signals[LAST_SIGNAL] = { 0 }; +static const char * +get_drivers_whitelist_env (void) +{ + return g_getenv ("FP_DRIVERS_WHITELIST"); +} + +static gboolean +is_driver_allowed (const gchar *driver) +{ + g_auto(GStrv) whitelisted_drivers = NULL; + const char *fp_drivers_whitelist_env; + int i; + + g_return_val_if_fail (driver, TRUE); + + fp_drivers_whitelist_env = get_drivers_whitelist_env (); + + if (!fp_drivers_whitelist_env) + return TRUE; + + whitelisted_drivers = g_strsplit (fp_drivers_whitelist_env, ":", -1); + + for (i = 0; whitelisted_drivers[i]; ++i) + if (g_strcmp0 (driver, whitelisted_drivers[i]) == 0) + return TRUE; + + return FALSE; +} + static void async_device_init_done_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { @@ -242,9 +271,25 @@ fp_context_init (FpContext *self) { g_autoptr(GError) error = NULL; FpContextPrivate *priv = fp_context_get_instance_private (self); + guint i; priv->drivers = fpi_get_driver_types (); + if (get_drivers_whitelist_env ()) + { + for (i = 0; i < priv->drivers->len;) + { + GType driver = g_array_index (priv->drivers, GType, i); + g_autoptr(GTypeClass) type_class = g_type_class_ref (driver); + FpDeviceClass *cls = FP_DEVICE_CLASS (type_class); + + if (!is_driver_allowed (cls->id)) + g_array_remove_index (priv->drivers, i); + else + ++i; + } + } + priv->devices = g_ptr_array_new_with_free_func (g_object_unref); priv->cancellable = g_cancellable_new (); diff --git a/tests/meson.build b/tests/meson.build index 6e56cb39..082ce86e 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -11,6 +11,9 @@ envs.prepend('LD_LIBRARY_PATH', join_paths(meson.build_root(), 'libfprint')) # random numbers rather than proper ones) envs.set('FP_DEVICE_EMULATION', '1') +# Set a colon-separated list of native drivers we enable in tests +envs.set('FP_DRIVERS_WHITELIST', 'virtual_image') + envs.set('NO_AT_BRIDGE', '1') if get_option('introspection') @@ -31,10 +34,13 @@ if get_option('introspection') ] foreach driver_test: drivers_tests + driver_envs = envs + driver_envs.set('FP_DRIVERS_WHITELIST', driver_test) + test(driver_test, find_program('umockdev-test.py'), args: join_paths(meson.current_source_dir(), driver_test), - env: envs, + env: driver_envs, suite: ['drivers'], timeout: 10, depends: libfprint_typelib, From eeddd8c7bc6196cd1152fffe185b04fc86ac5979 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 13 Dec 2019 20:40:41 +0100 Subject: [PATCH 125/237] fp-context, tools: Use auto-ptr to handle GTypeClass ownership This also fixes a small leak we might have if reffing a type that was not a virtual one. --- libfprint/fp-context.c | 15 +++++---------- libfprint/fprint-list-supported-devices.c | 10 +++------- libfprint/fprint-list-udev-rules.c | 10 +++------- 3 files changed, 11 insertions(+), 24 deletions(-) diff --git a/libfprint/fp-context.c b/libfprint/fp-context.c index 6764241e..f64968df 100644 --- a/libfprint/fp-context.c +++ b/libfprint/fp-context.c @@ -131,14 +131,12 @@ usb_device_added_cb (FpContext *self, GUsbDevice *device, GUsbContext *usb_ctx) for (i = 0; i < priv->drivers->len; i++) { GType driver = g_array_index (priv->drivers, GType, i); - FpDeviceClass *cls = FP_DEVICE_CLASS (g_type_class_ref (driver)); + g_autoptr(GTypeClass) type_class = g_type_class_ref (driver); + FpDeviceClass *cls = FP_DEVICE_CLASS (type_class); const FpIdEntry *entry; if (cls->type != FP_DEVICE_TYPE_USB) - { - g_type_class_unref (cls); - continue; - } + continue; for (entry = cls->id_table; entry->pid; entry++) { @@ -158,8 +156,6 @@ usb_device_added_cb (FpContext *self, GUsbDevice *device, GUsbContext *usb_ctx) found_driver = driver; found_entry = entry; } - - g_type_class_unref (cls); } if (found_driver == G_TYPE_NONE) @@ -355,7 +351,8 @@ fp_context_enumerate (FpContext *context) for (i = 0; i < priv->drivers->len; i++) { GType driver = g_array_index (priv->drivers, GType, i); - FpDeviceClass *cls = FP_DEVICE_CLASS (g_type_class_ref (driver)); + g_autoptr(GTypeClass) type_class = g_type_class_ref (driver); + FpDeviceClass *cls = FP_DEVICE_CLASS (type_class); const FpIdEntry *entry; if (cls->type != FP_DEVICE_TYPE_VIRTUAL) @@ -381,8 +378,6 @@ fp_context_enumerate (FpContext *context) NULL); g_debug ("created"); } - - g_type_class_unref (cls); } while (priv->pending_devices) diff --git a/libfprint/fprint-list-supported-devices.c b/libfprint/fprint-list-supported-devices.c index 55da252a..cb2803f1 100644 --- a/libfprint/fprint-list-supported-devices.c +++ b/libfprint/fprint-list-supported-devices.c @@ -38,14 +38,12 @@ insert_drivers (GList *list) for (i = 0; i < drivers->len; i++) { GType driver = g_array_index (drivers, GType, i); - FpDeviceClass *cls = FP_DEVICE_CLASS (g_type_class_ref (driver)); + g_autoptr(GTypeClass) type_class = g_type_class_ref (driver); + FpDeviceClass *cls = FP_DEVICE_CLASS (type_class); const FpIdEntry *entry; if (cls->type != FP_DEVICE_TYPE_USB) - { - g_type_class_unref (cls); - continue; - } + continue; for (entry = cls->id_table; entry->vid; entry++) { @@ -63,8 +61,6 @@ insert_drivers (GList *list) list = g_list_prepend (list, g_strdup_printf ("%s | %s\n", key, cls->full_name)); } - - g_type_class_unref (cls); } return list; diff --git a/libfprint/fprint-list-udev-rules.c b/libfprint/fprint-list-udev-rules.c index 335c37b7..ac507971 100644 --- a/libfprint/fprint-list-udev-rules.c +++ b/libfprint/fprint-list-udev-rules.c @@ -104,17 +104,13 @@ main (int argc, char **argv) for (i = 0; i < drivers->len; i++) { GType driver = g_array_index (drivers, GType, i); - FpDeviceClass *cls = FP_DEVICE_CLASS (g_type_class_ref (driver)); + g_autoptr(GTypeClass) type_class = g_type_class_ref (driver); + FpDeviceClass *cls = FP_DEVICE_CLASS (type_class); if (cls->type != FP_DEVICE_TYPE_USB) - { - g_type_class_unref (cls); - continue; - } + continue; print_driver (cls); - - g_type_class_unref (cls); } print_driver (&whitelist); From da46f53e82cad5f9cef8a410436febc269833556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 5 Dec 2019 14:38:41 +0100 Subject: [PATCH 126/237] tests: Add basic unit tests for fp-context Link the tests with the private library using an utils library that will be useful to share other tests functions --- meson.build | 5 +- tests/meson.build | 26 ++++++++++ tests/test-fp-context.c | 106 ++++++++++++++++++++++++++++++++++++++++ tests/test-runner.sh | 3 ++ tests/test-utils.c | 66 +++++++++++++++++++++++++ tests/test-utils.h | 23 +++++++++ 6 files changed, 225 insertions(+), 4 deletions(-) create mode 100644 tests/test-fp-context.c create mode 100755 tests/test-runner.sh create mode 100644 tests/test-utils.c create mode 100644 tests/test-utils.h diff --git a/meson.build b/meson.build index 3f72118e..8ea4a8b2 100644 --- a/meson.build +++ b/meson.build @@ -199,10 +199,7 @@ if get_option('gtk-examples') subdir('demo') endif -# The tests require introspeciton support to run -if get_option('introspection') - subdir('tests') -endif +subdir('tests') pkgconfig = import('pkgconfig') pkgconfig.generate( diff --git a/tests/meson.build b/tests/meson.build index 082ce86e..307d9a19 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -48,6 +48,32 @@ if get_option('introspection') endforeach endif +if 'virtual_image' in drivers + test_utils = static_library('fprint-test-utils', + sources: ['test-utils.c'], + dependencies: libfprint_private_dep, + install: false) + + unit_tests = [ + 'fp-context', + ] + + foreach test_name: unit_tests + basename = 'test-' + test_name + test_exe = executable(basename, + sources: basename + '.c', + dependencies: libfprint_private_dep, + c_args: common_cflags, + link_with: test_utils) + test(test_name, + find_program('test-runner.sh'), + suite: ['unit-tests'], + args: [test_exe], + env: envs, + ) + endforeach +endif + gdb = find_program('gdb', required: false) if gdb.found() add_test_setup('gdb', diff --git a/tests/test-fp-context.c b/tests/test-fp-context.c new file mode 100644 index 00000000..01516b96 --- /dev/null +++ b/tests/test-fp-context.c @@ -0,0 +1,106 @@ +/* + * FpContext Unit tests + * Copyright (C) 2019 Marco Trevisan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "test-utils.h" + +static void +test_context_new (void) +{ + g_autoptr(FpContext) context = fp_context_new (); + g_assert_true (FP_CONTEXT (context)); +} + +static void +test_context_has_no_devices (void) +{ + g_autoptr(FpContext) context = NULL; + GPtrArray *devices; + + context = fp_context_new (); + devices = fp_context_get_devices (context); + + g_assert_nonnull (devices); + g_assert_cmpuint (devices->len, ==, 0); +} + +static void +test_context_has_virtual_device (void) +{ + g_autoptr(FpContext) context = NULL; + FpDevice *virtual_device = NULL; + GPtrArray *devices; + unsigned int i; + + fpt_setup_virtual_device_environment (); + + context = fp_context_new (); + devices = fp_context_get_devices (context); + + g_assert_nonnull (devices); + g_assert_cmpuint (devices->len, ==, 1); + + for (i = 0; i < devices->len; ++i) + { + FpDevice *device = devices->pdata[i]; + + if (g_strcmp0 (fp_device_get_driver (device), "virtual_image") == 0) + { + virtual_device = device; + break; + } + } + + g_assert_true (FP_IS_DEVICE (virtual_device)); + + fpt_teardown_virtual_device_environment (); +} + +static void +test_context_enumerates_new_devices (void) +{ + g_autoptr(FpContext) context = NULL; + GPtrArray *devices; + + context = fp_context_new (); + + fpt_setup_virtual_device_environment (); + + fp_context_enumerate (context); + devices = fp_context_get_devices (context); + + g_assert_nonnull (devices); + g_assert_cmpuint (devices->len, ==, 1); + + fpt_teardown_virtual_device_environment (); +} + +int +main (int argc, char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/context/new", test_context_new); + g_test_add_func ("/context/no-devices", test_context_has_no_devices); + g_test_add_func ("/context/has-virtual-device", test_context_has_virtual_device); + g_test_add_func ("/context/enumerates-new-devices", test_context_enumerates_new_devices); + + return g_test_run (); +} diff --git a/tests/test-runner.sh b/tests/test-runner.sh new file mode 100755 index 00000000..18b038b8 --- /dev/null +++ b/tests/test-runner.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +exec $LIBFPRINT_TEST_WRAPPER $@ diff --git a/tests/test-utils.c b/tests/test-utils.c new file mode 100644 index 00000000..f7890585 --- /dev/null +++ b/tests/test-utils.c @@ -0,0 +1,66 @@ +/* + * Unit tests for libfprint + * Copyright (C) 2019 Marco Trevisan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "test-utils.h" + +void +fpt_teardown_virtual_device_environment (void) +{ + const char *path = g_getenv ("FP_VIRTUAL_IMAGE"); + + if (path) + { + g_autofree char *temp_dir = g_path_get_dirname (path); + + g_unsetenv ("FP_VIRTUAL_IMAGE"); + g_unlink (path); + g_rmdir (temp_dir); + } +} + +static void +on_signal_event (int sig) +{ + fpt_teardown_virtual_device_environment (); +} + +void +fpt_setup_virtual_device_environment (void) +{ + g_autoptr(GError) error = NULL; + g_autofree char *temp_dir = NULL; + g_autofree char *temp_path = NULL; + + g_assert_null (g_getenv ("FP_VIRTUAL_IMAGE")); + + temp_dir = g_dir_make_tmp ("libfprint-XXXXXX", &error); + g_assert_no_error (error); + + temp_path = g_build_filename (temp_dir, "virtual-image.socket", NULL); + g_setenv ("FP_VIRTUAL_IMAGE", temp_path, TRUE); + + signal (SIGKILL, on_signal_event); + signal (SIGABRT, on_signal_event); + signal (SIGSEGV, on_signal_event); + signal (SIGTERM, on_signal_event); + signal (SIGQUIT, on_signal_event); + signal (SIGPIPE, on_signal_event); +} diff --git a/tests/test-utils.h b/tests/test-utils.h new file mode 100644 index 00000000..369da786 --- /dev/null +++ b/tests/test-utils.h @@ -0,0 +1,23 @@ +/* + * Unit tests for libfprint + * Copyright (C) 2019 Marco Trevisan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +void fpt_setup_virtual_device_environment (void); +void fpt_teardown_virtual_device_environment (void); From b4c3756ab0e9056460b52081c18f9516ed8230e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 5 Dec 2019 17:05:21 +0100 Subject: [PATCH 127/237] tests: Add fp-device basic unit tests Use the virtual image device as base for now, while the new setup allows to create easily fake device drivers without including the driver in libfprint itself and test all the fpi_device functionalities. --- tests/meson.build | 1 + tests/test-fp-device.c | 238 +++++++++++++++++++++++++++++++++++++++++ tests/test-utils.c | 61 +++++++++++ tests/test-utils.h | 14 +++ 4 files changed, 314 insertions(+) create mode 100644 tests/test-fp-device.c diff --git a/tests/meson.build b/tests/meson.build index 307d9a19..1ef6e34d 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -56,6 +56,7 @@ if 'virtual_image' in drivers unit_tests = [ 'fp-context', + 'fp-device', ] foreach test_name: unit_tests diff --git a/tests/test-fp-device.c b/tests/test-fp-device.c new file mode 100644 index 00000000..a279b46c --- /dev/null +++ b/tests/test-fp-device.c @@ -0,0 +1,238 @@ +/* + * FpDevice Unit tests + * Copyright (C) 2019 Marco Trevisan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "test-utils.h" + +static void +on_device_opened (FpDevice *dev, GAsyncResult *res, FptContext *tctx) +{ + g_autoptr(GError) error = NULL; + + g_assert_true (fp_device_open_finish (dev, res, &error)); + g_assert_no_error (error); + g_assert_true (fp_device_is_open (tctx->device)); + + tctx->user_data = GUINT_TO_POINTER (TRUE); +} + +static void +test_device_open_async (void) +{ + g_autoptr(FptContext) tctx = fpt_context_new_with_virtual_imgdev (); + + fp_device_open (tctx->device, NULL, (GAsyncReadyCallback) on_device_opened, tctx); + + while (!GPOINTER_TO_UINT (tctx->user_data)) + g_main_context_iteration (NULL, TRUE); +} + +static void +on_device_closed (FpDevice *dev, GAsyncResult *res, FptContext *tctx) +{ + g_autoptr(GError) error = NULL; + + g_assert_true (fp_device_close_finish (dev, res, &error)); + g_assert_no_error (error); + g_assert_false (fp_device_is_open (tctx->device)); + + tctx->user_data = GUINT_TO_POINTER (TRUE); +} + +static void +test_device_close_async (void) +{ + g_autoptr(FptContext) tctx = fpt_context_new_with_virtual_imgdev (); + + fp_device_open (tctx->device, NULL, (GAsyncReadyCallback) on_device_opened, tctx); + while (!tctx->user_data) + g_main_context_iteration (NULL, TRUE); + + tctx->user_data = GUINT_TO_POINTER (FALSE); + fp_device_close (tctx->device, NULL, (GAsyncReadyCallback) on_device_closed, tctx); + + while (!GPOINTER_TO_UINT (tctx->user_data)) + g_main_context_iteration (NULL, TRUE); +} + +static void +test_device_open_sync (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FptContext) tctx = fpt_context_new_with_virtual_imgdev (); + + fp_device_open_sync (tctx->device, NULL, &error); + g_assert_no_error (error); + g_assert_true (fp_device_is_open (tctx->device)); + + fp_device_open_sync (tctx->device, NULL, &error); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_ALREADY_OPEN); +} + +static void +on_open_notify (FpDevice *rdev, GParamSpec *spec, FptContext *tctx) +{ + g_assert_cmpstr (spec->name, ==, "open"); + tctx->user_data = GUINT_TO_POINTER (TRUE); +} + +static void +test_device_open_sync_notify (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FptContext) tctx = fpt_context_new_with_virtual_imgdev (); + + g_signal_connect (tctx->device, "notify::open", G_CALLBACK (on_open_notify), tctx); + fp_device_open_sync (tctx->device, NULL, &error); + g_assert_no_error (error); + g_assert_true (GPOINTER_TO_INT (tctx->user_data)); +} + +static void +test_device_close_sync (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FptContext) tctx = fpt_context_new_with_virtual_imgdev (); + + fp_device_open_sync (tctx->device, NULL, NULL); + fp_device_close_sync (tctx->device, NULL, &error); + g_assert_no_error (error); + g_assert_false (fp_device_is_open (tctx->device)); + + fp_device_close_sync (tctx->device, NULL, &error); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_NOT_OPEN); +} + +static void +on_close_notify (FpDevice *rdev, GParamSpec *spec, FptContext *tctx) +{ + g_assert_cmpstr (spec->name, ==, "open"); + tctx->user_data = GUINT_TO_POINTER (TRUE); +} + +static void +test_device_close_sync_notify (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FptContext) tctx = fpt_context_new_with_virtual_imgdev (); + + fp_device_open_sync (tctx->device, NULL, NULL); + + g_signal_connect (tctx->device, "notify::open", G_CALLBACK (on_close_notify), tctx); + fp_device_close_sync (tctx->device, NULL, &error); + g_assert_no_error (error); + g_assert_true (GPOINTER_TO_INT (tctx->user_data)); +} + +static void +test_device_get_driver (void) +{ + g_autoptr(FptContext) tctx = fpt_context_new_with_virtual_imgdev (); + + fp_device_open_sync (tctx->device, NULL, NULL); + g_assert_cmpstr (fp_device_get_driver (tctx->device), ==, "virtual_image"); +} + +static void +test_device_get_device_id (void) +{ + g_autoptr(FptContext) tctx = fpt_context_new_with_virtual_imgdev (); + + fp_device_open_sync (tctx->device, NULL, NULL); + g_assert_cmpstr (fp_device_get_device_id (tctx->device), ==, "0"); +} + +static void +test_device_get_name (void) +{ + g_autoptr(FptContext) tctx = fpt_context_new_with_virtual_imgdev (); + + fp_device_open_sync (tctx->device, NULL, NULL); + g_assert_cmpstr (fp_device_get_name (tctx->device), ==, + "Virtual image device for debugging"); +} + +static void +test_device_get_scan_type (void) +{ + g_autoptr(FptContext) tctx = fpt_context_new_with_virtual_imgdev (); + + fp_device_open_sync (tctx->device, NULL, NULL); + g_assert_cmpint (fp_device_get_scan_type (tctx->device), ==, FP_SCAN_TYPE_SWIPE); +} + +static void +test_device_get_nr_enroll_stages (void) +{ + g_autoptr(FptContext) tctx = fpt_context_new_with_virtual_imgdev (); + + fp_device_open_sync (tctx->device, NULL, NULL); + g_assert_cmpuint (fp_device_get_nr_enroll_stages (tctx->device), ==, 5); +} + +static void +test_device_supports_identify (void) +{ + g_autoptr(FptContext) tctx = fpt_context_new_with_virtual_imgdev (); + + fp_device_open_sync (tctx->device, NULL, NULL); + g_assert_true (fp_device_supports_identify (tctx->device)); +} + +static void +test_device_supports_capture (void) +{ + g_autoptr(FptContext) tctx = fpt_context_new_with_virtual_imgdev (); + + fp_device_open_sync (tctx->device, NULL, NULL); + g_assert_true (fp_device_supports_capture (tctx->device)); +} + +static void +test_device_has_storage (void) +{ + g_autoptr(FptContext) tctx = fpt_context_new_with_virtual_imgdev (); + + fp_device_open_sync (tctx->device, NULL, NULL); + g_assert_false (fp_device_has_storage (tctx->device)); +} + +int +main (int argc, char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/device/async/open", test_device_open_async); + g_test_add_func ("/device/async/close", test_device_close_async); + g_test_add_func ("/device/sync/open", test_device_open_sync); + g_test_add_func ("/device/sync/open/notify", test_device_open_sync_notify); + g_test_add_func ("/device/sync/close", test_device_close_sync); + g_test_add_func ("/device/sync/close/notify", test_device_close_sync_notify); + g_test_add_func ("/device/sync/get_driver", test_device_get_driver); + g_test_add_func ("/device/sync/get_device_id", test_device_get_device_id); + g_test_add_func ("/device/sync/get_name", test_device_get_name); + g_test_add_func ("/device/sync/get_scan_type", test_device_get_scan_type); + g_test_add_func ("/device/sync/get_nr_enroll_stages", test_device_get_nr_enroll_stages); + g_test_add_func ("/device/sync/supports_identify", test_device_supports_identify); + g_test_add_func ("/device/sync/supports_capture", test_device_supports_capture); + g_test_add_func ("/device/sync/has_storage", test_device_has_storage); + + return g_test_run (); +} diff --git a/tests/test-utils.c b/tests/test-utils.c index f7890585..834a90e8 100644 --- a/tests/test-utils.c +++ b/tests/test-utils.c @@ -17,6 +17,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include #include #include "test-utils.h" @@ -64,3 +65,63 @@ fpt_setup_virtual_device_environment (void) signal (SIGQUIT, on_signal_event); signal (SIGPIPE, on_signal_event); } + +FptContext * +fpt_context_new (void) +{ + FptContext *tctx; + + tctx = g_new0 (FptContext, 1); + tctx->fp_context = fp_context_new (); + + return tctx; +} + +FptContext * +fpt_context_new_with_virtual_imgdev (void) +{ + FptContext *tctx; + GPtrArray *devices; + unsigned int i; + + fpt_setup_virtual_device_environment (); + + tctx = fpt_context_new (); + devices = fp_context_get_devices (tctx->fp_context); + + g_assert_nonnull (devices); + g_assert_cmpuint (devices->len, ==, 1); + + for (i = 0; i < devices->len; ++i) + { + FpDevice *device = devices->pdata[i]; + + if (g_strcmp0 (fp_device_get_driver (device), "virtual_image") == 0) + { + tctx->device = device; + break; + } + } + + g_assert_true (FP_IS_DEVICE (tctx->device)); + g_object_add_weak_pointer (G_OBJECT (tctx->device), (gpointer) & tctx->device); + + return tctx; +} + +void +fpt_context_free (FptContext *tctx) +{ + if (tctx->device && fp_device_is_open (tctx->device)) + { + g_autoptr(GError) error = NULL; + + fp_device_close_sync (tctx->device, NULL, &error); + g_assert_no_error (error); + } + + g_clear_object (&tctx->fp_context); + g_free (tctx); + + fpt_teardown_virtual_device_environment (); +} diff --git a/tests/test-utils.h b/tests/test-utils.h index 369da786..4bc1e699 100644 --- a/tests/test-utils.h +++ b/tests/test-utils.h @@ -21,3 +21,17 @@ void fpt_setup_virtual_device_environment (void); void fpt_teardown_virtual_device_environment (void); + +typedef struct _FptContext +{ + FpContext *fp_context; + FpDevice *device; + gpointer user_data; +} FptContext; + +FptContext * fpt_context_new (void); +FptContext * fpt_context_new_with_virtual_imgdev (void); + +void fpt_context_free (FptContext *test_context); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (FptContext, fpt_context_free) From c7b7f78273db250cf7a264a450fc7cfd895f65a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 6 Dec 2019 17:18:26 +0100 Subject: [PATCH 128/237] fp-device: Call identify device class method on identification Identify on device was broken because we were calling verify device method on devices instead of the right one. Thank you unit tests! :) --- libfprint/fp-device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfprint/fp-device.c b/libfprint/fp-device.c index c49e5a92..3ac3a1c3 100644 --- a/libfprint/fp-device.c +++ b/libfprint/fp-device.c @@ -923,7 +923,7 @@ fp_device_identify (FpDevice *device, g_ptr_array_ref (prints), (GDestroyNotify) g_ptr_array_unref); - FP_DEVICE_GET_CLASS (device)->verify (device); + FP_DEVICE_GET_CLASS (device)->identify (device); } /** From 98fa6efce350058b3ea7ae2d00a403d5d859e075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 11 Dec 2019 18:53:36 +0100 Subject: [PATCH 129/237] fpi-device: Clarify ownership of parameters for progress call --- libfprint/fpi-device.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libfprint/fpi-device.c b/libfprint/fpi-device.c index 3eee0627..5fc6b760 100644 --- a/libfprint/fpi-device.c +++ b/libfprint/fpi-device.c @@ -1134,8 +1134,8 @@ fpi_device_list_complete (FpDevice *device, * fpi_device_enroll_progress: * @device: The #FpDevice * @completed_stages: The number of stages that are completed at this point - * @print: The #FpPrint for the newly completed stage or %NULL on failure - * @error: The #GError or %NULL on success + * @print: (transfer full): The #FpPrint for the newly completed stage or %NULL on failure + * @error: (transfer full): The #GError or %NULL on success * * Notify about the progress of the enroll operation. This is important for UI interaction. * The passed error may be used if a scan needs to be retried, use fpi_device_retry_new(). From f578ebe82d25c44eff39c916f621d7bcbf984e3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 6 Dec 2019 17:20:52 +0100 Subject: [PATCH 130/237] test-device-fake: Add fake test driver to verify fpi functions --- tests/meson.build | 5 +- tests/test-device-fake.c | 205 +++++++++++++++++++++++++++++++++++++++ tests/test-device-fake.h | 43 ++++++++ 3 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 tests/test-device-fake.c create mode 100644 tests/test-device-fake.h diff --git a/tests/meson.build b/tests/meson.build index 1ef6e34d..e37991d4 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -50,7 +50,10 @@ endif if 'virtual_image' in drivers test_utils = static_library('fprint-test-utils', - sources: ['test-utils.c'], + sources: [ + 'test-utils.c', + 'test-device-fake.c', + ], dependencies: libfprint_private_dep, install: false) diff --git a/tests/test-device-fake.c b/tests/test-device-fake.c new file mode 100644 index 00000000..e3b6f38c --- /dev/null +++ b/tests/test-device-fake.c @@ -0,0 +1,205 @@ +/* + * Virtual driver for device debugging + * + * Copyright (C) 2019 Marco Trevisan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define FP_COMPONENT "fake_test_dev" + +#include "test-device-fake.h" + +G_DEFINE_TYPE (FpiDeviceFake, fpi_device_fake, FP_TYPE_DEVICE) + +static const FpIdEntry driver_ids[] = { + { .virtual_envvar = "FP_VIRTUAL_FAKE_DEVICE" }, + { .virtual_envvar = NULL } +}; + +static void +fpi_device_fake_probe (FpDevice *device) +{ + FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_PROBE); + + fake_dev->last_called_function = fpi_device_fake_probe; + fpi_device_probe_complete (device, dev_class->id, dev_class->full_name, + fake_dev->ret_error); +} + +static void +fpi_device_fake_open (FpDevice *device) +{ + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_OPEN); + + fake_dev->last_called_function = fpi_device_fake_open; + fpi_device_open_complete (device, fake_dev->ret_error); +} + +static void +fpi_device_fake_close (FpDevice *device) +{ + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_CLOSE); + + fake_dev->last_called_function = fpi_device_fake_close; + fpi_device_close_complete (device, fake_dev->ret_error); +} + +static void +fpi_device_fake_enroll (FpDevice *device) +{ + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + FpPrint *print = fake_dev->ret_print; + + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_ENROLL); + fpi_device_get_enroll_data (device, (FpPrint **) &fake_dev->action_data); + + if (!print && !fake_dev->ret_error) + fpi_device_get_enroll_data (device, &print); + + fake_dev->last_called_function = fpi_device_fake_enroll; + fpi_device_enroll_complete (device, print, fake_dev->ret_error); +} + +static void +fpi_device_fake_verify (FpDevice *device) +{ + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + FpPrint *print = fake_dev->ret_print; + + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_VERIFY); + fpi_device_get_verify_data (device, (FpPrint **) &fake_dev->action_data); + + if (!print && !fake_dev->ret_error) + fpi_device_get_verify_data (device, &print); + + fake_dev->last_called_function = fpi_device_fake_verify; + fpi_device_verify_complete (device, fake_dev->ret_result, print, + fake_dev->ret_error); +} + +static void +fpi_device_fake_identify (FpDevice *device) +{ + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + FpPrint *match = fake_dev->ret_match; + + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_IDENTIFY); + fpi_device_get_identify_data (device, (GPtrArray **) &fake_dev->action_data); + + if (!match && !fake_dev->ret_error) + { + GPtrArray *prints; + unsigned int i; + + fpi_device_get_identify_data (device, &prints); + + for (i = 0; prints && i < prints->len; ++i) + { + FpPrint *print = g_ptr_array_index (prints, i); + + if (g_strcmp0 (fp_print_get_description (print), "fake-verified") == 0) + { + match = print; + break; + } + } + } + + fake_dev->last_called_function = fpi_device_fake_identify; + fpi_device_identify_complete (device, match, fake_dev->ret_print, + fake_dev->ret_error); +} + +static void +fpi_device_fake_capture (FpDevice *device) +{ + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_CAPTURE); + fpi_device_get_capture_data (device, (gboolean *) &fake_dev->action_data); + + fake_dev->last_called_function = fpi_device_fake_capture; + fpi_device_capture_complete (device, fake_dev->ret_image, fake_dev->ret_error); +} + +static void +fpi_device_fake_list (FpDevice *device) +{ + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_LIST); + + fake_dev->last_called_function = fpi_device_fake_list; + fpi_device_list_complete (device, fake_dev->ret_list, fake_dev->ret_error); +} + +static void +fpi_device_fake_delete (FpDevice *device) +{ + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_DELETE); + fpi_device_get_delete_data (device, (gpointer) & fake_dev->action_data); + + fake_dev->last_called_function = fpi_device_fake_delete; + fpi_device_delete_complete (device, fake_dev->ret_error); +} + +static void +fpi_device_fake_cancel (FpDevice *device) +{ + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + + g_assert_cmpuint (fpi_device_get_current_action (device), !=, FP_DEVICE_ACTION_NONE); + + fake_dev->last_called_function = fpi_device_fake_cancel; +} + +static void +fpi_device_fake_init (FpiDeviceFake *self) +{ +} + +static void +fpi_device_fake_class_init (FpiDeviceFakeClass *klass) +{ + FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass); + + dev_class->id = FP_COMPONENT; + dev_class->full_name = "Virtual device for debugging"; + dev_class->type = FP_DEVICE_TYPE_VIRTUAL; + dev_class->id_table = driver_ids; + dev_class->nr_enroll_stages = 5; + dev_class->scan_type = FP_SCAN_TYPE_PRESS; + + dev_class->probe = fpi_device_fake_probe; + dev_class->open = fpi_device_fake_open; + dev_class->close = fpi_device_fake_close; + dev_class->enroll = fpi_device_fake_enroll; + dev_class->verify = fpi_device_fake_verify; + dev_class->identify = fpi_device_fake_identify; + dev_class->capture = fpi_device_fake_capture; + dev_class->list = fpi_device_fake_list; + dev_class->delete = fpi_device_fake_delete; + dev_class->cancel = fpi_device_fake_cancel; +} diff --git a/tests/test-device-fake.h b/tests/test-device-fake.h new file mode 100644 index 00000000..e8a09193 --- /dev/null +++ b/tests/test-device-fake.h @@ -0,0 +1,43 @@ +/* + * Virtual driver for device debugging + * + * Copyright (C) 2019 Marco Trevisan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +#include "fpi-device.h" + +#define FPI_TYPE_DEVICE_FAKE (fpi_device_fake_get_type ()) +G_DECLARE_FINAL_TYPE (FpiDeviceFake, fpi_device_fake, FPI, DEVICE_FAKE, FpDevice) + +struct _FpiDeviceFake +{ + FpDevice parent; + + gpointer last_called_function; + + GError *ret_error; + FpPrint *ret_print; + FpPrint *ret_match; + FpiMatchResult ret_result; + FpImage *ret_image; + GPtrArray *ret_list; + + gpointer action_data; + gpointer user_data; +}; From 324258bc8c807f9dfbf7ad89fe5cbdd4d1652de9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 11 Dec 2019 21:09:02 +0100 Subject: [PATCH 131/237] tests/meson: Support unit-tests non depending on virtual driver Since tests depending on the fake device don't depend on virtual-image driver anymore, let's change the way we organize the things, by putting everything in the test lib, but enabling unit-tests depending on what they depend on. --- tests/meson.build | 51 +++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/tests/meson.build b/tests/meson.build index e37991d4..7482a66e 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -48,36 +48,39 @@ if get_option('introspection') endforeach endif -if 'virtual_image' in drivers - test_utils = static_library('fprint-test-utils', - sources: [ - 'test-utils.c', - 'test-device-fake.c', - ], - dependencies: libfprint_private_dep, - install: false) +test_utils = static_library('fprint-test-utils', + sources: [ + 'test-utils.c', + 'test-device-fake.c', + ], + dependencies: libfprint_private_dep, + install: false) - unit_tests = [ +unit_tests = [] + +if 'virtual_image' in drivers + unit_tests += [ 'fp-context', 'fp-device', ] - - foreach test_name: unit_tests - basename = 'test-' + test_name - test_exe = executable(basename, - sources: basename + '.c', - dependencies: libfprint_private_dep, - c_args: common_cflags, - link_with: test_utils) - test(test_name, - find_program('test-runner.sh'), - suite: ['unit-tests'], - args: [test_exe], - env: envs, - ) - endforeach endif +foreach test_name: unit_tests + basename = 'test-' + test_name + test_exe = executable(basename, + sources: basename + '.c', + dependencies: libfprint_private_dep, + c_args: common_cflags, + link_with: test_utils, + ) + test(test_name, + find_program('test-runner.sh'), + suite: ['unit-tests'], + args: [test_exe], + env: envs, + ) +endforeach + gdb = find_program('gdb', required: false) if gdb.found() add_test_setup('gdb', From f31b8483d420cac89ef67b4b8f8143204a4b42c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 6 Dec 2019 17:21:38 +0100 Subject: [PATCH 132/237] tests: Add fpi device tests Verify drivers operations simulating a fake driver to check that the lib interaction is correct. --- tests/meson.build | 4 +- tests/test-fpi-device.c | 1411 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 1414 insertions(+), 1 deletion(-) create mode 100644 tests/test-fpi-device.c diff --git a/tests/meson.build b/tests/meson.build index 7482a66e..d082908d 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -56,7 +56,9 @@ test_utils = static_library('fprint-test-utils', dependencies: libfprint_private_dep, install: false) -unit_tests = [] +unit_tests = [ + 'fpi-device', +] if 'virtual_image' in drivers unit_tests += [ diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c new file mode 100644 index 00000000..165fc7f1 --- /dev/null +++ b/tests/test-fpi-device.c @@ -0,0 +1,1411 @@ +/* + * Example fingerprint device prints listing and deletion + * Enrolls your right index finger and saves the print to disk + * Copyright (C) 2019 Marco Trevisan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#define FP_COMPONENT "device" + +#include "fpi-device.h" +#include "fpi-log.h" +#include "test-device-fake.h" + +/* Utility functions */ + +typedef FpDevice FpAutoCloseDevice; + +static FpAutoCloseDevice * +auto_close_fake_device_new (void) +{ + FpAutoCloseDevice *device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + + g_assert_true (fp_device_open_sync (device, NULL, NULL)); + + return device; +} + +static void +auto_close_fake_device_free (FpAutoCloseDevice *device) +{ + if (fp_device_is_open (device)) + g_assert_true (fp_device_close_sync (device, NULL, NULL)); + + g_object_unref (device); +} +G_DEFINE_AUTOPTR_CLEANUP_FUNC (FpAutoCloseDevice, auto_close_fake_device_free) + +typedef FpDeviceClass FpAutoResetClass; +static FpAutoResetClass default_fake_dev_class = {0}; + +static FpAutoResetClass * +auto_reset_device_class (void) +{ + g_autoptr(GTypeClass) type_class = NULL; + FpDeviceClass *dev_class = g_type_class_peek_static (FPI_TYPE_DEVICE_FAKE); + + if (!dev_class) + { + type_class = g_type_class_ref (FPI_TYPE_DEVICE_FAKE); + dev_class = (FpDeviceClass *) type_class; + g_assert_nonnull (dev_class); + } + + default_fake_dev_class = *dev_class; + + return dev_class; +} + +static void +auto_reset_device_class_cleanup (FpAutoResetClass *dev_class) +{ + *dev_class = default_fake_dev_class; + + g_assert_cmpint (memcmp (dev_class, &default_fake_dev_class, + sizeof (FpAutoResetClass)), ==, 0); +} +G_DEFINE_AUTOPTR_CLEANUP_FUNC (FpAutoResetClass, auto_reset_device_class_cleanup) + +static void +on_device_notify (FpDevice *device, GParamSpec *spec, gpointer user_data) +{ + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + + fake_dev->last_called_function = on_device_notify; + fake_dev->user_data = g_param_spec_ref (spec); +} + +static void +test_driver_action_error_vfunc (FpDevice *device) +{ + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + + fake_dev->last_called_function = test_driver_action_error_vfunc; + + fpi_device_action_error (device, fake_dev->user_data); +} + +/* Tests */ + +static void +test_driver_get_driver (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpDevice) device = NULL; + + dev_class->id = "test-fpi-device-driver"; + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + + g_assert_cmpstr (fp_device_get_driver (device), ==, "test-fpi-device-driver"); +} + +static void +test_driver_get_device_id (void) +{ + g_autoptr(FpDevice) device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + + g_assert_cmpstr (fp_device_get_device_id (device), ==, "0"); +} + +static void +test_driver_get_name (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpDevice) device = NULL; + + dev_class->full_name = "Test Device Full Name!"; + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + + g_assert_cmpstr (fp_device_get_name (device), ==, "Test Device Full Name!"); +} + +static void +test_driver_is_open (void) +{ + g_autoptr(FpDevice) device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + + g_assert_false (fp_device_is_open (device)); + fp_device_open_sync (device, NULL, NULL); + g_assert_true (fp_device_is_open (device)); + fp_device_close_sync (FP_DEVICE (device), NULL, NULL); + g_assert_false (fp_device_is_open (device)); +} + +static void +test_driver_get_scan_type_press (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpDevice) device = NULL; + + dev_class->scan_type = FP_SCAN_TYPE_PRESS; + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + g_assert_cmpuint (fp_device_get_scan_type (device), ==, FP_SCAN_TYPE_PRESS); +} + +static void +test_driver_get_scan_type_swipe (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpDevice) device = NULL; + + dev_class->scan_type = FP_SCAN_TYPE_SWIPE; + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + g_assert_cmpuint (fp_device_get_scan_type (device), ==, FP_SCAN_TYPE_SWIPE); +} + +static void +test_driver_set_scan_type_press (void) +{ + g_autoptr(FpDevice) device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + g_autoptr(GParamSpec) pspec = NULL; + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + + g_signal_connect (device, "notify::scan-type", G_CALLBACK (on_device_notify), NULL); + + fpi_device_set_scan_type (device, FP_SCAN_TYPE_PRESS); + g_assert_cmpuint (fp_device_get_scan_type (device), ==, FP_SCAN_TYPE_PRESS); + g_assert (fake_dev->last_called_function == on_device_notify); + + pspec = g_steal_pointer (&fake_dev->user_data); + g_assert_cmpstr (pspec->name, ==, "scan-type"); +} + +static void +test_driver_set_scan_type_swipe (void) +{ + g_autoptr(FpDevice) device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + g_autoptr(GParamSpec) pspec = NULL; + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + + g_signal_connect (device, "notify::scan-type", G_CALLBACK (on_device_notify), NULL); + + fpi_device_set_scan_type (device, FP_SCAN_TYPE_SWIPE); + g_assert_cmpuint (fp_device_get_scan_type (device), ==, FP_SCAN_TYPE_SWIPE); + g_assert (fake_dev->last_called_function == on_device_notify); + + pspec = g_steal_pointer (&fake_dev->user_data); + g_assert_cmpstr (pspec->name, ==, "scan-type"); +} + +static void +test_driver_get_nr_enroll_stages (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpDevice) device = NULL; + int expected_stages = g_random_int_range (G_MININT32, G_MAXINT32); + + dev_class->nr_enroll_stages = expected_stages; + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + + g_assert_cmpint (fp_device_get_nr_enroll_stages (device), ==, expected_stages); +} + +static void +test_driver_set_nr_enroll_stages (void) +{ + g_autoptr(FpDevice) device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + g_autoptr(GParamSpec) pspec = NULL; + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + int expected_stages = g_random_int_range (G_MININT32, G_MAXINT32); + + g_signal_connect (device, "notify::nr-enroll-stages", G_CALLBACK (on_device_notify), NULL); + fpi_device_set_nr_enroll_stages (device, expected_stages); + + g_assert_cmpint (fp_device_get_nr_enroll_stages (device), ==, expected_stages); + g_assert (fake_dev->last_called_function == on_device_notify); + + pspec = g_steal_pointer (&fake_dev->user_data); + g_assert_cmpstr (pspec->name, ==, "nr-enroll-stages"); +} + +static void +test_driver_get_usb_device (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpDevice) device = NULL; + + dev_class->type = FP_DEVICE_TYPE_USB; + device = g_object_new (FPI_TYPE_DEVICE_FAKE, "fp-usb-device", NULL); + g_assert_null (fpi_device_get_usb_device (device)); + + g_clear_object (&device); + dev_class->type = FP_DEVICE_TYPE_VIRTUAL; + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*assertion*type*FP_DEVICE_TYPE_USB*failed*"); + g_assert_null (fpi_device_get_usb_device (device)); + g_test_assert_expected_messages (); +} + +static void +test_driver_get_virtual_env (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpDevice) device = NULL; + + dev_class->type = FP_DEVICE_TYPE_VIRTUAL; + device = g_object_new (FPI_TYPE_DEVICE_FAKE, "fp-environ", "TEST_VIRTUAL_ENV_GETTER", NULL); + g_assert_cmpstr (fpi_device_get_virtual_env (device), ==, "TEST_VIRTUAL_ENV_GETTER"); + + g_clear_object (&device); + dev_class->type = FP_DEVICE_TYPE_USB; + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*assertion*type*FP_DEVICE_TYPE_VIRTUAL*failed*"); + g_assert_null (fpi_device_get_virtual_env (device)); + g_test_assert_expected_messages (); +} + +static void +test_driver_get_driver_data (void) +{ + g_autoptr(FpDevice) device = NULL; + guint64 driver_data; + + driver_data = g_random_int (); + device = g_object_new (FPI_TYPE_DEVICE_FAKE, "fp-driver-data", driver_data, NULL); + g_assert_cmpuint (fpi_device_get_driver_data (device), ==, driver_data); +} + +static void +on_driver_probe_async (GObject *initable, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GError) error = NULL; + FpDevice **out_device = user_data; + FpDevice *device; + FpDeviceClass *dev_class; + FpiDeviceFake *fake_dev; + + device = FP_DEVICE (g_async_initable_new_finish (G_ASYNC_INITABLE (initable), res, &error)); + dev_class = FP_DEVICE_GET_CLASS (device); + fake_dev = FPI_DEVICE_FAKE (device); + + g_assert (fake_dev->last_called_function == dev_class->probe); + g_assert_no_error (error); + + g_assert_false (fp_device_is_open (device)); + + *out_device = device; +} + +static void +test_driver_probe (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpDevice) device = NULL; + + dev_class->id = "Probed device ID"; + dev_class->full_name = "Probed device name"; + g_async_initable_new_async (FPI_TYPE_DEVICE_FAKE, G_PRIORITY_DEFAULT, NULL, + on_driver_probe_async, &device, NULL); + + while (!FP_IS_DEVICE (device)) + g_main_context_iteration (NULL, TRUE); + + g_assert_false (fp_device_is_open (device)); + g_assert_cmpstr (fp_device_get_device_id (device), ==, "Probed device ID"); + g_assert_cmpstr (fp_device_get_name (device), ==, "Probed device name"); +} + +static void +test_driver_open (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpDevice) device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + + g_assert (fake_dev->last_called_function != dev_class->probe); + + fp_device_open_sync (device, NULL, &error); + g_assert (fake_dev->last_called_function == dev_class->open); + g_assert_no_error (error); + g_assert_true (fp_device_is_open (device)); + + fp_device_close_sync (FP_DEVICE (device), NULL, &error); + g_assert_no_error (error); +} + +static void +test_driver_open_error (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpDevice) device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + + fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_GENERAL); + fp_device_open_sync (device, NULL, &error); + g_assert (fake_dev->last_called_function == dev_class->open); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + g_assert_false (fp_device_is_open (device)); + g_assert (error == g_steal_pointer (&fake_dev->ret_error)); +} + +static void +test_driver_close (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); + FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + + fp_device_close_sync (device, NULL, &error); + g_assert (fake_dev->last_called_function == dev_class->close); + + g_assert_no_error (error); + g_assert_false (fp_device_is_open (device)); +} + +static void +test_driver_close_error (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); + FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + + fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_GENERAL); + fp_device_close_sync (device, NULL, &error); + + g_assert (fake_dev->last_called_function == dev_class->close); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + g_assert (error == g_steal_pointer (&fake_dev->ret_error)); +} + +static void +test_driver_enroll (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); + FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + FpPrint *template_print = fp_print_new (device); + FpPrint *out_print = NULL; + + out_print = + fp_device_enroll_sync (device, template_print, NULL, NULL, NULL, &error); + + g_assert (fake_dev->last_called_function == dev_class->enroll); + g_assert (fake_dev->action_data == template_print); + + g_assert_no_error (error); + g_assert (out_print == template_print); +} + +static void +test_driver_enroll_error (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); + FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + FpPrint *template_print = fp_print_new (device); + FpPrint *out_print = NULL; + + fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_GENERAL); + out_print = + fp_device_enroll_sync (device, template_print, NULL, NULL, NULL, &error); + + g_assert (fake_dev->last_called_function == dev_class->enroll); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + g_assert (error == g_steal_pointer (&fake_dev->ret_error)); + g_assert_null (out_print); +} + +typedef struct +{ + gint completed_stages; + FpPrint *print; + GError *error; +} ExpectedEnrollData; + +static void +test_driver_enroll_progress_callback (FpDevice *device, + gint completed_stages, + FpPrint *print, + gpointer user_data, + GError *error) +{ + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + ExpectedEnrollData *expected_data = user_data; + + g_assert_cmpint (expected_data->completed_stages, ==, completed_stages); + g_assert (expected_data->print == print); + g_assert_true (print == NULL || FP_IS_PRINT (print)); + g_assert (expected_data->error == error); + + fake_dev->last_called_function = test_driver_enroll_progress_callback; +} + +static void +test_driver_enroll_progress_vfunc (FpDevice *device) +{ + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + ExpectedEnrollData *expected_data = fake_dev->user_data; + + g_autoptr(GError) error = NULL; + + expected_data->completed_stages = + g_random_int_range (fp_device_get_nr_enroll_stages (device), G_MAXINT32); + expected_data->print = fp_print_new (device); + expected_data->error = NULL; + + g_object_add_weak_pointer (G_OBJECT (expected_data->print), + (gpointer) & expected_data->print); + + fpi_device_enroll_progress (device, expected_data->completed_stages, + expected_data->print, expected_data->error); + g_assert (fake_dev->last_called_function == test_driver_enroll_progress_callback); + g_assert_null (expected_data->print); + + + expected_data->completed_stages = + g_random_int_range (fp_device_get_nr_enroll_stages (device), G_MAXINT32); + expected_data->print = NULL; + expected_data->error = fpi_device_retry_new (FP_DEVICE_RETRY_TOO_SHORT); + + fpi_device_enroll_progress (device, expected_data->completed_stages, + expected_data->print, expected_data->error); + g_assert (fake_dev->last_called_function == test_driver_enroll_progress_callback); + + + expected_data->completed_stages = + g_random_int_range (fp_device_get_nr_enroll_stages (device), G_MAXINT32); + expected_data->print = fp_print_new (device); + expected_data->error = NULL; + + error = fpi_device_error_new (FP_DEVICE_ERROR_GENERAL); + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*assertion*error*FP_DEVICE_RETRY*failed"); + fpi_device_enroll_progress (device, expected_data->completed_stages, + expected_data->print, error); + g_assert (fake_dev->last_called_function == test_driver_enroll_progress_callback); + g_clear_object (&expected_data->print); + g_test_assert_expected_messages (); + + expected_data->completed_stages = + g_random_int_range (fp_device_get_nr_enroll_stages (device), G_MAXINT32); + expected_data->print = NULL; + expected_data->error = fpi_device_retry_new (FP_DEVICE_RETRY_TOO_SHORT); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Driver passed an error and also provided a print*"); + fpi_device_enroll_progress (device, expected_data->completed_stages, + fp_print_new (device), expected_data->error); + g_assert (fake_dev->last_called_function == test_driver_enroll_progress_callback); + g_test_assert_expected_messages (); + + default_fake_dev_class.enroll (device); + fake_dev->last_called_function = test_driver_enroll_progress_vfunc; +} + +static void +test_driver_enroll_progress (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpAutoCloseDevice) device = NULL; + ExpectedEnrollData expected_enroll_data = {0}; + FpiDeviceFake *fake_dev; + + dev_class->nr_enroll_stages = g_random_int_range (10, G_MAXINT32); + dev_class->enroll = test_driver_enroll_progress_vfunc; + device = auto_close_fake_device_new (); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*assertion*current_action*FP_DEVICE_ACTION_ENROLL*failed"); + fpi_device_enroll_progress (device, 0, NULL, NULL); + g_test_assert_expected_messages (); + + fake_dev = FPI_DEVICE_FAKE (device); + fake_dev->user_data = &expected_enroll_data; + + fp_device_enroll_sync (device, fp_print_new (device), NULL, + test_driver_enroll_progress_callback, &expected_enroll_data, NULL); + + g_assert (fake_dev->last_called_function == test_driver_enroll_progress_vfunc); +} + +static void +test_driver_verify (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); + g_autoptr(FpPrint) enrolled_print = fp_print_new (device); + FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + FpPrint *out_print = NULL; + gboolean match; + + fake_dev->ret_result = FPI_MATCH_SUCCESS; + fp_device_verify_sync (device, enrolled_print, NULL, &match, &out_print, &error); + + g_assert (fake_dev->last_called_function == dev_class->verify); + g_assert (fake_dev->action_data == enrolled_print); + g_assert_no_error (error); + + g_assert (out_print == enrolled_print); + g_assert_true (match); +} + +static void +test_driver_verify_fail (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); + g_autoptr(FpPrint) enrolled_print = fp_print_new (device); + FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + FpPrint *out_print = NULL; + gboolean match; + + fake_dev->ret_result = FPI_MATCH_FAIL; + fp_device_verify_sync (device, enrolled_print, NULL, &match, &out_print, &error); + + g_assert (fake_dev->last_called_function == dev_class->verify); + g_assert_no_error (error); + + g_assert (out_print == enrolled_print); + g_assert_false (match); +} + +static void +test_driver_verify_error (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); + g_autoptr(FpPrint) enrolled_print = fp_print_new (device); + FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + FpPrint *out_print = NULL; + gboolean match; + + fake_dev->ret_result = FPI_MATCH_ERROR; + fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_GENERAL); + fp_device_verify_sync (device, enrolled_print, NULL, &match, &out_print, &error); + + g_assert (fake_dev->last_called_function == dev_class->verify); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + g_assert (error == g_steal_pointer (&fake_dev->ret_error)); + g_assert_false (match); +} + +static void +fake_device_stub_identify (FpDevice *device) +{ +} + +static void +test_driver_supports_identify (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpDevice) device = NULL; + + dev_class->identify = fake_device_stub_identify; + + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + g_assert_true (fp_device_supports_identify (device)); +} + +static void +test_driver_do_not_support_identify (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpDevice) device = NULL; + + dev_class->identify = NULL; + + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + g_assert_false (fp_device_supports_identify (device)); +} + +static void +test_driver_identify (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpPrint) print = NULL; + g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); + g_autoptr(GPtrArray) prints = g_ptr_array_new_with_free_func (g_object_unref); + FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + FpPrint *matched_print; + FpPrint *expected_matched; + unsigned int i; + + for (i = 0; i < 500; ++i) + g_ptr_array_add (prints, fp_print_new (device)); + + expected_matched = g_ptr_array_index (prints, g_random_int_range (0, 499)); + fp_print_set_description (expected_matched, "fake-verified"); + + g_assert_true (fp_device_supports_identify (device)); + + fake_dev->ret_print = fp_print_new (device); + fp_device_identify_sync (device, prints, NULL, &matched_print, &print, &error); + + g_assert (fake_dev->last_called_function == dev_class->identify); + g_assert (fake_dev->action_data == prints); + g_assert_no_error (error); + + g_assert (print != NULL && print == fake_dev->ret_print); + g_assert (expected_matched == matched_print); +} + +static void +test_driver_identify_fail (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpPrint) print = NULL; + g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); + g_autoptr(GPtrArray) prints = g_ptr_array_new_with_free_func (g_object_unref); + FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + FpPrint *matched_print; + unsigned int i; + + for (i = 0; i < 500; ++i) + g_ptr_array_add (prints, fp_print_new (device)); + + g_assert_true (fp_device_supports_identify (device)); + + fake_dev->ret_print = fp_print_new (device); + fp_device_identify_sync (device, prints, NULL, &matched_print, &print, &error); + + g_assert (fake_dev->last_called_function == dev_class->identify); + g_assert_no_error (error); + + g_assert (print != NULL && print == fake_dev->ret_print); + g_assert_null (matched_print); +} + +static void +test_driver_identify_error (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpPrint) print = NULL; + g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); + g_autoptr(GPtrArray) prints = g_ptr_array_new_with_free_func (g_object_unref); + FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + FpPrint *matched_print; + FpPrint *expected_matched; + unsigned int i; + + for (i = 0; i < 500; ++i) + g_ptr_array_add (prints, fp_print_new (device)); + + expected_matched = g_ptr_array_index (prints, g_random_int_range (0, 499)); + fp_print_set_description (expected_matched, "fake-verified"); + + g_assert_true (fp_device_supports_identify (device)); + + fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_GENERAL); + fp_device_identify_sync (device, prints, NULL, &matched_print, &print, &error); + + g_assert (fake_dev->last_called_function == dev_class->identify); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + g_assert (error == g_steal_pointer (&fake_dev->ret_error)); + g_assert_null (matched_print); + g_assert_null (print); +} + +static void +fake_device_stub_capture (FpDevice *device) +{ +} + +static void +test_driver_supports_capture (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpDevice) device = NULL; + + dev_class->capture = fake_device_stub_capture; + + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + g_assert_true (fp_device_supports_capture (device)); +} + +static void +test_driver_do_not_support_capture (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpDevice) device = NULL; + + dev_class->capture = NULL; + + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + g_assert_false (fp_device_supports_capture (device)); +} + +static void +test_driver_capture (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpImage) image = NULL; + g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); + FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + gboolean wait_for_finger = TRUE; + + fake_dev->ret_image = fp_image_new (500, 500); + image = fp_device_capture_sync (device, wait_for_finger, NULL, &error); + g_assert (fake_dev->last_called_function == dev_class->capture); + g_assert_true (GPOINTER_TO_UINT (fake_dev->action_data)); + g_assert_no_error (error); + + g_assert (image == fake_dev->ret_image); +} + +static void +test_driver_capture_error (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpImage) image = NULL; + g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); + FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + gboolean wait_for_finger = TRUE; + + fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_GENERAL); + image = fp_device_capture_sync (device, wait_for_finger, NULL, &error); + g_assert (fake_dev->last_called_function == dev_class->capture); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + g_assert (error == g_steal_pointer (&fake_dev->ret_error)); + + g_assert_null (image); +} + +static void +fake_device_stub_list (FpDevice *device) +{ +} + +static void +test_driver_has_storage (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpDevice) device = NULL; + + dev_class->list = fake_device_stub_list; + + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + g_assert_true (fp_device_has_storage (device)); +} + +static void +test_driver_has_not_storage (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpDevice) device = NULL; + + dev_class->list = NULL; + + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + g_assert_false (fp_device_has_storage (device)); +} + +static void +test_driver_list (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); + FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + g_autoptr(GPtrArray) prints = g_ptr_array_new_with_free_func (g_object_unref); + unsigned int i; + + for (i = 0; i < 500; ++i) + g_ptr_array_add (prints, fp_print_new (device)); + + fake_dev->ret_list = g_steal_pointer (&prints); + prints = fp_device_list_prints_sync (device, NULL, &error); + + g_assert (fake_dev->last_called_function == dev_class->list); + g_assert_no_error (error); + + g_assert (prints == fake_dev->ret_list); +} + +static void +test_driver_list_error (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); + FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + g_autoptr(GPtrArray) prints = NULL; + + fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_GENERAL); + prints = fp_device_list_prints_sync (device, NULL, &error); + + g_assert (fake_dev->last_called_function == dev_class->list); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + g_assert (error == g_steal_pointer (&fake_dev->ret_error)); + + g_assert_null (prints); +} + +static void +test_driver_delete (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); + g_autoptr(FpPrint) enrolled_print = fp_print_new (device); + FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + gboolean ret; + + ret = fp_device_delete_print_sync (device, enrolled_print, NULL, &error); + g_assert (fake_dev->last_called_function == dev_class->delete); + g_assert (fake_dev->action_data == enrolled_print); + g_assert_no_error (error); + g_assert_true (ret); +} + +static void +test_driver_delete_error (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); + g_autoptr(FpPrint) enrolled_print = fp_print_new (device); + FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + gboolean ret; + + fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_GENERAL); + ret = fp_device_delete_print_sync (device, enrolled_print, NULL, &error); + g_assert (fake_dev->last_called_function == dev_class->delete); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + g_assert (error == g_steal_pointer (&fake_dev->ret_error)); + + g_assert_false (ret); +} + +static gboolean +fake_device_delete_wait_for_cancel_timeout (gpointer data) +{ + FpDevice *device = data; + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); + + g_assert (fake_dev->last_called_function == dev_class->cancel); + default_fake_dev_class.delete (device); + + g_assert (fake_dev->last_called_function == default_fake_dev_class.delete); + fake_dev->last_called_function = fake_device_delete_wait_for_cancel_timeout; + + return G_SOURCE_REMOVE; +} + +static void +fake_device_delete_wait_for_cancel (FpDevice *device) +{ + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + + fake_dev->last_called_function = fake_device_delete_wait_for_cancel; + + g_timeout_add (100, fake_device_delete_wait_for_cancel_timeout, device); +} + +static void +on_driver_cancel_delete (GObject *obj, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GError) error = NULL; + FpDevice *device = FP_DEVICE (obj); + gboolean *completed = user_data; + + fp_device_delete_print_finish (device, res, &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); + + *completed = TRUE; +} + +static void +test_driver_cancel (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(GCancellable) cancellable = NULL; + g_autoptr(FpPrint) enrolled_print = NULL; + gboolean completed = FALSE; + FpiDeviceFake *fake_dev; + + dev_class->delete = fake_device_delete_wait_for_cancel; + + device = auto_close_fake_device_new (); + fake_dev = FPI_DEVICE_FAKE (device); + cancellable = g_cancellable_new (); + enrolled_print = fp_print_new (device); + + fp_device_delete_print (device, enrolled_print, cancellable, + on_driver_cancel_delete, &completed); + g_cancellable_cancel (cancellable); + + while (!completed) + g_main_context_iteration (NULL, TRUE); + + g_assert (fake_dev->last_called_function == fake_device_delete_wait_for_cancel_timeout); +} + +static void +test_driver_cancel_fail (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); + g_autoptr(GCancellable) cancellable = g_cancellable_new (); + g_autoptr(FpPrint) enrolled_print = fp_print_new (device); + FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + + fp_device_delete_print_sync (device, enrolled_print, cancellable, &error); + g_assert (fake_dev->last_called_function == dev_class->delete); + g_cancellable_cancel (cancellable); + + while (g_main_context_iteration (NULL, FALSE)) + ; + + g_assert (fake_dev->last_called_function == dev_class->delete); + g_assert_no_error (error); +} + +static void +test_driver_current_action (void) +{ + g_autoptr(FpDevice) device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + + g_assert_cmpint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_NONE); +} + +static void +test_driver_current_action_open_vfunc (FpDevice *device) +{ + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_OPEN); + fake_dev->last_called_function = test_driver_current_action_open_vfunc; + + fpi_device_open_complete (device, NULL); +} + +static void +test_driver_current_action_open (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpAutoCloseDevice) device = NULL; + FpiDeviceFake *fake_dev; + + dev_class->open = test_driver_current_action_open_vfunc; + device = auto_close_fake_device_new (); + fake_dev = FPI_DEVICE_FAKE (device); + g_assert (fake_dev->last_called_function == test_driver_current_action_open_vfunc); + + g_assert_cmpint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_NONE); +} + +static void +test_driver_action_get_cancellable_open_vfunc (FpDevice *device) +{ + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_OPEN); + fake_dev->last_called_function = test_driver_action_get_cancellable_open_vfunc; + + g_assert_true (G_IS_CANCELLABLE (fpi_device_get_cancellable (device))); + + fpi_device_open_complete (device, NULL); +} + +static void +test_driver_action_get_cancellable_open (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(GCancellable) cancellable = NULL; + FpiDeviceFake *fake_dev; + + dev_class->open = test_driver_action_get_cancellable_open_vfunc; + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + fake_dev = FPI_DEVICE_FAKE (device); + + cancellable = g_cancellable_new (); + fp_device_open_sync (device, cancellable, NULL); + + g_assert (fake_dev->last_called_function == test_driver_action_get_cancellable_open_vfunc); +} + +static void +test_driver_action_get_cancellable_open_fail_vfunc (FpDevice *device) +{ + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_OPEN); + fake_dev->last_called_function = test_driver_action_get_cancellable_open_fail_vfunc; + + g_assert_false (G_IS_CANCELLABLE (fpi_device_get_cancellable (device))); + + fpi_device_open_complete (device, NULL); +} + +static void +test_driver_action_get_cancellable_open_fail (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpAutoCloseDevice) device = NULL; + FpiDeviceFake *fake_dev; + + dev_class->open = test_driver_action_get_cancellable_open_fail_vfunc; + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + fake_dev = FPI_DEVICE_FAKE (device); + + g_assert_true (fp_device_open_sync (device, NULL, NULL)); + + g_assert (fake_dev->last_called_function == test_driver_action_get_cancellable_open_fail_vfunc); +} + +static void +test_driver_action_get_cancellable_error (void) +{ + g_autoptr(FpDevice) device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*assertion*current_action*FP_DEVICE_ACTION_NONE*failed"); + g_assert_null (fpi_device_get_cancellable (device)); + g_test_assert_expected_messages (); +} + +static void +test_driver_action_is_cancelled_open_vfunc (FpDevice *device) +{ + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_OPEN); + fake_dev->last_called_function = test_driver_action_is_cancelled_open_vfunc; + + g_assert_true (G_IS_CANCELLABLE (fpi_device_get_cancellable (device))); + g_assert_false (fpi_device_action_is_cancelled (device)); + + g_cancellable_cancel (fpi_device_get_cancellable (device)); + g_assert_true (fpi_device_action_is_cancelled (device)); + + fpi_device_open_complete (device, NULL); +} + +static void +test_driver_action_is_cancelled_open (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(GCancellable) cancellable = NULL; + g_autoptr(GError) error = NULL; + FpiDeviceFake *fake_dev; + + dev_class->open = test_driver_action_is_cancelled_open_vfunc; + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + fake_dev = FPI_DEVICE_FAKE (device); + + cancellable = g_cancellable_new (); + g_assert_false (fp_device_open_sync (device, cancellable, &error)); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); + + g_assert (fake_dev->last_called_function == test_driver_action_is_cancelled_open_vfunc); +} + +static void +test_driver_action_is_cancelled_error (void) +{ + g_autoptr(FpDevice) device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*assertion*current_action*FP_DEVICE_ACTION_NONE*failed"); + g_assert_true (fpi_device_action_is_cancelled (device)); + g_test_assert_expected_messages (); +} + +static void +test_driver_complete_actions_errors (void) +{ + g_autoptr(FpDevice) device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*assertion*current_action*failed"); + fpi_device_probe_complete (device, NULL, NULL, NULL); + g_test_assert_expected_messages (); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*assertion*current_action*failed"); + fpi_device_open_complete (device, NULL); + g_test_assert_expected_messages (); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*assertion*current_action*failed"); + fpi_device_close_complete (device, NULL); + g_test_assert_expected_messages (); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*assertion*current_action*failed"); + fpi_device_enroll_complete (device, NULL, NULL); + g_test_assert_expected_messages (); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*assertion*current_action*failed"); + fpi_device_verify_complete (device, FPI_MATCH_FAIL, NULL, NULL); + g_test_assert_expected_messages (); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*assertion*current_action*failed"); + fpi_device_identify_complete (device, NULL, NULL, NULL); + g_test_assert_expected_messages (); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*assertion*current_action*failed"); + fpi_device_capture_complete (device, NULL, NULL); + g_test_assert_expected_messages (); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*assertion*current_action*failed"); + fpi_device_delete_complete (device, NULL); + g_test_assert_expected_messages (); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*assertion*current_action*failed"); + fpi_device_list_complete (device, NULL, NULL); + g_test_assert_expected_messages (); +} + +static void +test_driver_action_error_error (void) +{ + g_autoptr(FpDevice) device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*assertion*current_action*FP_DEVICE_ACTION_NONE*failed"); + fpi_device_action_error (device, NULL); + g_test_assert_expected_messages (); +} + +static void +test_driver_action_error_open (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(GError) error = NULL; + FpiDeviceFake *fake_dev; + + dev_class->open = test_driver_action_error_vfunc; + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + fake_dev = FPI_DEVICE_FAKE (device); + fake_dev->user_data = fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID); + + g_assert_false (fp_device_open_sync (device, NULL, &error)); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_DATA_INVALID); + + g_assert (fake_dev->last_called_function == test_driver_action_error_vfunc); +} + +static void +test_driver_action_error_fallback_open (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(GError) error = NULL; + FpiDeviceFake *fake_dev; + + dev_class->open = test_driver_action_error_vfunc; + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + fake_dev = FPI_DEVICE_FAKE (device); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Device failed to pass an error to generic action " + "error function*"); + + g_assert_false (fp_device_open_sync (device, NULL, &error)); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + + g_assert (fake_dev->last_called_function == test_driver_action_error_vfunc); + g_test_assert_expected_messages (); +} + +static void +test_driver_add_timeout_func (FpDevice *device, gpointer user_data) +{ + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + + fake_dev->last_called_function = test_driver_add_timeout_func; +} + +static void +test_driver_add_timeout (void) +{ + g_autoptr(FpDevice) device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + FpDevice *data_check = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + + g_object_add_weak_pointer (G_OBJECT (data_check), (gpointer) & data_check); + fpi_device_add_timeout (device, 50, test_driver_add_timeout_func, + data_check, g_object_unref); + + g_assert_nonnull (data_check); + + while (FP_IS_DEVICE (data_check)) + g_main_context_iteration (NULL, TRUE); + + g_assert_null (data_check); + g_assert (fake_dev->last_called_function == test_driver_add_timeout_func); +} + +static gboolean +test_driver_add_timeout_cancelled_timeout (gpointer data) +{ + GSource *source = data; + + g_source_destroy (source); + + return G_SOURCE_REMOVE; +} + +static void +test_driver_add_timeout_cancelled (void) +{ + g_autoptr(FpDevice) device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + FpDevice *data_check = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + GSource *source; + + g_object_add_weak_pointer (G_OBJECT (data_check), (gpointer) & data_check); + source = fpi_device_add_timeout (device, 2000, test_driver_add_timeout_func, + data_check, g_object_unref); + + g_timeout_add (20, test_driver_add_timeout_cancelled_timeout, source); + g_assert_nonnull (data_check); + + while (FP_IS_DEVICE (data_check)) + g_main_context_iteration (NULL, TRUE); + + g_assert_null (data_check); + g_assert_null (fake_dev->last_called_function); +} + +static void +test_driver_error_types (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(GEnumClass) errors_enum = g_type_class_ref (FP_TYPE_DEVICE_ERROR); + int i; + + for (i = 0; g_enum_get_value (errors_enum, i); ++i) + { + error = fpi_device_error_new (i); + g_assert_error (error, FP_DEVICE_ERROR, i); + g_clear_error (&error); + } + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "*Unsupported error*"); + error = fpi_device_error_new (i + 1); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + g_test_assert_expected_messages (); +} + +static void +test_driver_retry_error_types (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(GEnumClass) errors_enum = g_type_class_ref (FP_TYPE_DEVICE_RETRY); + int i; + + for (i = 0; g_enum_get_value (errors_enum, i); ++i) + { + error = fpi_device_retry_new (i); + g_assert_error (error, FP_DEVICE_RETRY, i); + g_clear_error (&error); + } + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "*Unsupported error*"); + error = fpi_device_retry_new (i + 1); + g_assert_error (error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_GENERAL); + g_test_assert_expected_messages (); +} + +int +main (int argc, char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/driver/get_driver", test_driver_get_driver); + g_test_add_func ("/driver/get_device_id", test_driver_get_device_id); + g_test_add_func ("/driver/get_name", test_driver_get_name); + g_test_add_func ("/driver/is_open", test_driver_is_open); + g_test_add_func ("/driver/get_scan_type/press", test_driver_get_scan_type_press); + g_test_add_func ("/driver/get_scan_type/swipe", test_driver_get_scan_type_swipe); + g_test_add_func ("/driver/set_scan_type/press", test_driver_set_scan_type_press); + g_test_add_func ("/driver/set_scan_type/swipe", test_driver_set_scan_type_swipe); + g_test_add_func ("/driver/get_nr_enroll_stages", test_driver_get_nr_enroll_stages); + g_test_add_func ("/driver/set_nr_enroll_stages", test_driver_set_nr_enroll_stages); + g_test_add_func ("/driver/supports_identify", test_driver_supports_identify); + g_test_add_func ("/driver/supports_capture", test_driver_supports_capture); + g_test_add_func ("/driver/has_storage", test_driver_has_storage); + g_test_add_func ("/driver/do_not_support_identify", test_driver_do_not_support_identify); + g_test_add_func ("/driver/do_not_support_capture", test_driver_do_not_support_capture); + g_test_add_func ("/driver/has_not_storage", test_driver_has_not_storage); + g_test_add_func ("/driver/get_usb_device", test_driver_get_usb_device); + g_test_add_func ("/driver/get_virtual_env", test_driver_get_virtual_env); + g_test_add_func ("/driver/get_driver_data", test_driver_get_driver_data); + + g_test_add_func ("/driver/probe", test_driver_probe); + g_test_add_func ("/driver/open", test_driver_open); + g_test_add_func ("/driver/open/error", test_driver_open_error); + g_test_add_func ("/driver/close", test_driver_close); + g_test_add_func ("/driver/close/error", test_driver_close_error); + g_test_add_func ("/driver/enroll", test_driver_enroll); + g_test_add_func ("/driver/enroll/error", test_driver_enroll_error); + g_test_add_func ("/driver/enroll/progress", test_driver_enroll_progress); + g_test_add_func ("/driver/verify", test_driver_verify); + g_test_add_func ("/driver/verify/fail", test_driver_verify_fail); + g_test_add_func ("/driver/verify/error", test_driver_verify_error); + g_test_add_func ("/driver/identify", test_driver_identify); + g_test_add_func ("/driver/identify/fail", test_driver_identify_fail); + g_test_add_func ("/driver/identify/error", test_driver_identify_error); + g_test_add_func ("/driver/capture", test_driver_capture); + g_test_add_func ("/driver/capture/error", test_driver_capture_error); + g_test_add_func ("/driver/list", test_driver_list); + g_test_add_func ("/driver/list/error", test_driver_list_error); + g_test_add_func ("/driver/delete", test_driver_delete); + g_test_add_func ("/driver/delete/error", test_driver_delete_error); + g_test_add_func ("/driver/cancel", test_driver_cancel); + g_test_add_func ("/driver/cancel/fail", test_driver_cancel_fail); + + g_test_add_func ("/driver/get_current_action", test_driver_current_action); + g_test_add_func ("/driver/get_current_action/open", test_driver_current_action_open); + g_test_add_func ("/driver/get_cancellable/error", test_driver_action_get_cancellable_error); + g_test_add_func ("/driver/get_cancellable/open", test_driver_action_get_cancellable_open); + g_test_add_func ("/driver/get_cancellable/open/fail", test_driver_action_get_cancellable_open_fail); + g_test_add_func ("/driver/action_is_cancelled/open", test_driver_action_is_cancelled_open); + g_test_add_func ("/driver/action_is_cancelled/error", test_driver_action_is_cancelled_error); + g_test_add_func ("/driver/complete_action/all/error", test_driver_complete_actions_errors); + g_test_add_func ("/driver/action_error/error", test_driver_action_error_error); + g_test_add_func ("/driver/action_error/open", test_driver_action_error_open); + g_test_add_func ("/driver/action_error/fail/open", test_driver_action_error_fallback_open); + + g_test_add_func ("/driver/timeout", test_driver_add_timeout); + g_test_add_func ("/driver/timeout/cancelled", test_driver_add_timeout_cancelled); + + g_test_add_func ("/driver/error_types", test_driver_error_types); + g_test_add_func ("/driver/retry_error_types", test_driver_retry_error_types); + + return g_test_run (); +} From 3bb18407501fae5b2858f00171df4ab26080903b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 11 Dec 2019 20:07:59 +0100 Subject: [PATCH 133/237] fpi-ssm: Use same argument names of header file So we can mute a Gtk-doc parser warning --- libfprint/fpi-ssm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libfprint/fpi-ssm.c b/libfprint/fpi-ssm.c index 8b3e4bd6..0f3e6fb3 100644 --- a/libfprint/fpi-ssm.c +++ b/libfprint/fpi-ssm.c @@ -115,7 +115,7 @@ struct _FpiSsm * @dev: a #fp_dev fingerprint device * @handler: the callback function * @nr_states: the number of states - * @name: the name of the state machine (for debug purposes) + * @machine_name: the name of the state machine (for debug purposes) * * Allocate a new ssm, with @nr_states states. The @handler callback * will be called after each state transition. @@ -126,7 +126,7 @@ FpiSsm * fpi_ssm_new_full (FpDevice *dev, FpiSsmHandlerCallback handler, int nr_states, - const char *name) + const char *machine_name) { FpiSsm *machine; @@ -137,7 +137,7 @@ fpi_ssm_new_full (FpDevice *dev, machine->handler = handler; machine->nr_states = nr_states; machine->dev = dev; - machine->name = g_strdup (name); + machine->name = g_strdup (machine_name); machine->completed = TRUE; return machine; } From c59486363969ce5a4afd7d8d34857ae7efbce812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 11 Dec 2019 20:57:15 +0100 Subject: [PATCH 134/237] fpi-ssm: Define autoptr cleanup function --- libfprint/fpi-ssm.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libfprint/fpi-ssm.h b/libfprint/fpi-ssm.h index d1334b58..956e3552 100644 --- a/libfprint/fpi-ssm.h +++ b/libfprint/fpi-ssm.h @@ -109,3 +109,5 @@ void fpi_ssm_usb_transfer_with_weak_pointer_cb (FpiUsbTransfer *transfer, FpDevice *device, gpointer weak_ptr, GError *error); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (FpiSsm, fpi_ssm_free) From 299a797423527e183ea7167f26f19c901d2285c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 12 Dec 2019 14:54:26 +0100 Subject: [PATCH 135/237] fpi-ssm: Bug on wrong state passed to jump_to_state_delayed While remove the checks that are already part of the common function fpi_ssm_set_delayed_action_timeout(). --- libfprint/fpi-ssm.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libfprint/fpi-ssm.c b/libfprint/fpi-ssm.c index 0f3e6fb3..d672064b 100644 --- a/libfprint/fpi-ssm.c +++ b/libfprint/fpi-ssm.c @@ -573,8 +573,7 @@ fpi_ssm_jump_to_state_delayed (FpiSsm *machine, g_autofree char *source_name = NULL; g_return_if_fail (machine != NULL); - BUG_ON (machine->completed); - BUG_ON (machine->timeout != NULL); + BUG_ON (state < 0 || state >= machine->nr_states); data = g_new0 (FpiSsmJumpToStateDelayedData, 1); data->machine = machine; From 0471edbf104910ee1bfe6d5214fa8d98e3eb5a1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 12 Dec 2019 15:49:35 +0100 Subject: [PATCH 136/237] fpi-ssm: Add debug message when a delayed state change is cancelled --- libfprint/fpi-ssm.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libfprint/fpi-ssm.c b/libfprint/fpi-ssm.c index d672064b..6e56e448 100644 --- a/libfprint/fpi-ssm.c +++ b/libfprint/fpi-ssm.c @@ -213,6 +213,9 @@ on_delayed_action_cancelled (GCancellable *cancellable, { CancelledActionIdleData *data; + fp_dbg ("[%s] %s cancelled delayed state change", + fp_device_get_driver (machine->dev), machine->name); + g_clear_pointer (&machine->timeout, g_source_destroy); data = g_new0 (CancelledActionIdleData, 1); @@ -469,6 +472,9 @@ fpi_ssm_cancel_delayed_state_change (FpiSsm *machine) BUG_ON (machine->completed); BUG_ON (machine->timeout == NULL); + fp_dbg ("[%s] %s cancelled delayed state change", + fp_device_get_driver (machine->dev), machine->name); + fpi_ssm_clear_delayed_action (machine); } From cf5473a55c343347ada9f38776acccdd5216dba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 12 Dec 2019 16:06:37 +0100 Subject: [PATCH 137/237] fpi-ssm: Make clear that the completed callback owns the error --- libfprint/fpi-ssm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfprint/fpi-ssm.h b/libfprint/fpi-ssm.h index 956e3552..fe649467 100644 --- a/libfprint/fpi-ssm.h +++ b/libfprint/fpi-ssm.h @@ -39,7 +39,7 @@ typedef struct _FpiSsm FpiSsm; * FpiSsmCompletedCallback: * @ssm: a #FpiSsm state machine * @dev: the #fp_dev fingerprint device - * @error: The #GError or %NULL on successful completion + * @error: (transfer full): The #GError or %NULL on successful completion * * The callback called when a state machine completes successfully, * as set when calling fpi_ssm_start(). From fe828d0bb2da9a196f12bf40c0d9609f41ee2848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 12 Dec 2019 18:41:26 +0100 Subject: [PATCH 138/237] fpi-ssm: Clear delayed actions for parent and child on subssm start While timeout was already cleared for parent, we didn't properly delete the cancellable. Although we'd warn anyways when starting the SSM, is still better to clear any delayed action also for the sub-SSM --- libfprint/fpi-ssm.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libfprint/fpi-ssm.c b/libfprint/fpi-ssm.c index 6e56e448..3cc39a7f 100644 --- a/libfprint/fpi-ssm.c +++ b/libfprint/fpi-ssm.c @@ -338,7 +338,10 @@ fpi_ssm_start_subsm (FpiSsm *parent, FpiSsm *child) { BUG_ON (parent->timeout); child->parentsm = parent; - g_clear_pointer (&parent->timeout, g_source_destroy); + + fpi_ssm_clear_delayed_action (parent); + fpi_ssm_clear_delayed_action (child); + fpi_ssm_start (child, __subsm_complete); } From 42b1deaeeaf1e0a3f401c3b3468c5fffe94f4546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 12 Dec 2019 18:42:46 +0100 Subject: [PATCH 139/237] tests: Add unit tests for fpi-ssm Verify that the state machine actions are done as we expected, being the main tool for drivers, better to check that is done as we expect. --- tests/meson.build | 1 + tests/test-fpi-ssm.c | 1396 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1397 insertions(+) create mode 100644 tests/test-fpi-ssm.c diff --git a/tests/meson.build b/tests/meson.build index d082908d..b4022a4e 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -58,6 +58,7 @@ test_utils = static_library('fprint-test-utils', unit_tests = [ 'fpi-device', + 'fpi-ssm', ] if 'virtual_image' in drivers diff --git a/tests/test-fpi-ssm.c b/tests/test-fpi-ssm.c new file mode 100644 index 00000000..a3bd9daa --- /dev/null +++ b/tests/test-fpi-ssm.c @@ -0,0 +1,1396 @@ +/* + * FpiSsm Unit tests + * Copyright (C) 2019 Marco Trevisan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "fp-device.h" +#define FP_COMPONENT "SSM" + +#include "drivers_api.h" +#include "test-device-fake.h" +#include "fpi-log.h" + +/* Utility functions and shared data */ + +static FpDevice *fake_device = NULL; + +typedef struct +{ + volatile int ref_count; + int handler_state; + GSList *handlers_chain; + gboolean completed; + GError *error; + gboolean ssm_destroyed; + gboolean expected_last_state; +} FpiSsmTestData; + +static FpiSsmTestData * +fpi_ssm_test_data_new (void) +{ + FpiSsmTestData *data = g_new0 (FpiSsmTestData, 1); + + data->ref_count = 1; + data->handler_state = -1; + + return data; +} + +static FpiSsmTestData * +fpi_ssm_test_data_ref (FpiSsmTestData *data) +{ + g_atomic_int_inc (&data->ref_count); + return data; +} + +static void +fpi_ssm_test_data_unref (FpiSsmTestData *data) +{ + if (g_atomic_int_dec_and_test (&data->ref_count)) + { + g_clear_error (&data->error); + g_slist_free (data->handlers_chain); + g_free (data); + } +} +G_DEFINE_AUTOPTR_CLEANUP_FUNC (FpiSsmTestData, fpi_ssm_test_data_unref) + +static void +fpi_ssm_test_data_unref_by_ssm (FpiSsmTestData *data) +{ + data->ssm_destroyed = TRUE; + + fpi_ssm_test_data_unref (data); +} + +enum { + FPI_TEST_SSM_STATE_0, + FPI_TEST_SSM_STATE_1, + FPI_TEST_SSM_STATE_2, + FPI_TEST_SSM_STATE_3, + FPI_TEST_SSM_STATE_NUM +}; + +static void +test_ssm_handler (FpiSsm *ssm, + FpDevice *dev) +{ + FpiSsmTestData *data; + + g_assert (dev == fake_device); + g_assert_true (FP_IS_DEVICE (dev)); + + data = fpi_ssm_get_data (ssm); + data->handler_state = fpi_ssm_get_cur_state (ssm); + data->handlers_chain = g_slist_append (data->handlers_chain, + GINT_TO_POINTER (fpi_ssm_get_cur_state (ssm))); +} + +static void +test_ssm_completed_callback (FpiSsm *ssm, + FpDevice *dev, + GError *error) +{ + FpiSsmTestData *data; + + g_assert (dev == fake_device); + g_assert_true (FP_IS_DEVICE (dev)); + + data = fpi_ssm_get_data (ssm); + data->completed = TRUE; + data->handlers_chain = g_slist_append (data->handlers_chain, + GINT_TO_POINTER (fpi_ssm_get_cur_state (ssm))); + g_clear_error (&data->error); + data->error = error; + + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, data->expected_last_state); +} + +static FpiSsm * +ssm_test_new_full (int nr_states, const char *name) +{ + FpiSsm *ssm; + FpiSsmTestData *data; + + ssm = fpi_ssm_new_full (fake_device, test_ssm_handler, nr_states, name); + data = fpi_ssm_test_data_new (); + data->expected_last_state = nr_states; + fpi_ssm_set_data (ssm, data, (GDestroyNotify) fpi_ssm_test_data_unref_by_ssm); + + return ssm; +} + +static FpiSsm * +ssm_test_new (void) +{ + return ssm_test_new_full (FPI_TEST_SSM_STATE_NUM, "FPI_TEST_SSM"); +} + +static gboolean +test_ssm_cancel_delayed_action_delayed (gpointer data) +{ + FpiSsm *ssm = data; + + fpi_ssm_cancel_delayed_state_change (ssm); + + return G_SOURCE_REMOVE; +} + +static gboolean +test_ssm_cancel_cancellable_delayed (gpointer data) +{ + g_cancellable_cancel (G_CANCELLABLE (data)); + + return G_SOURCE_REMOVE; +} + +/* Tests */ + +static void +test_ssm_new (void) +{ + FpiSsm *ssm; + + ssm = fpi_ssm_new (fake_device, test_ssm_handler, FPI_TEST_SSM_STATE_NUM); + + g_assert_null (fpi_ssm_get_data (ssm)); + g_assert_no_error (fpi_ssm_get_error (ssm)); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + + fpi_ssm_free (ssm); +} + +static void +test_ssm_new_full (void) +{ + FpiSsm *ssm; + + ssm = fpi_ssm_new_full (fake_device, test_ssm_handler, + FPI_TEST_SSM_STATE_NUM, "Test SSM Name"); + + g_assert_null (fpi_ssm_get_data (ssm)); + g_assert_no_error (fpi_ssm_get_error (ssm)); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + + fpi_ssm_free (ssm); +} + +static void +test_ssm_new_no_handler (void) +{ + g_autoptr(FpiSsm) ssm = NULL; + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*BUG:*handler*"); + ssm = fpi_ssm_new (fake_device, NULL, FPI_TEST_SSM_STATE_NUM); + g_test_assert_expected_messages (); +} + +static void +test_ssm_new_wrong_states (void) +{ + g_autoptr(FpiSsm) ssm = NULL; + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*BUG:*nr_states*"); + ssm = fpi_ssm_new (fake_device, test_ssm_handler, -1); + g_test_assert_expected_messages (); +} + +static void +test_ssm_set_data (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + GObject *object = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + + g_object_add_weak_pointer (object, (gpointer) & object); + + fpi_ssm_set_data (ssm, object, g_object_unref); + g_assert (fpi_ssm_get_data (ssm) == object); + + fpi_ssm_set_data (ssm, (gpointer) 0xdeadbeef, NULL); + g_assert (fpi_ssm_get_data (ssm) == (gpointer) 0xdeadbeef); + g_assert_null (object); +} + +static void +test_ssm_set_data_cleanup (void) +{ + FpiSsm *ssm = ssm_test_new (); + GObject *object = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + + g_object_add_weak_pointer (object, (gpointer) & object); + + fpi_ssm_set_data (ssm, object, g_object_unref); + g_assert (fpi_ssm_get_data (ssm) == object); + + fpi_ssm_free (ssm); + g_assert_null (object); +} + +static void +test_ssm_start (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + FpiSsmTestData *data = fpi_ssm_get_data (ssm); + + g_assert_null (data->handlers_chain); + + fpi_ssm_start (ssm, NULL); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + g_assert_no_error (data->error); + g_assert_false (data->ssm_destroyed); +} + +static void +test_ssm_start_single (void) +{ + g_autoptr(FpiSsmTestData) data = NULL; + FpiSsm *ssm; + + ssm = ssm_test_new_full (1, "FPI_TEST_SSM_SINGLE_STATE"); + data = fpi_ssm_test_data_ref (fpi_ssm_get_data (ssm)); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, 0); + g_assert_cmpint (data->handler_state, ==, 0); + + fpi_ssm_next_state (ssm); + g_assert_cmpint (data->handler_state, ==, 0); + + g_assert_true (data->completed); + g_assert_no_error (data->error); + g_assert_true (data->ssm_destroyed); +} + +static void +test_ssm_next (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + g_autoptr(FpiSsmTestData) data = fpi_ssm_test_data_ref (fpi_ssm_get_data (ssm)); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + fpi_ssm_next_state (ssm); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 2); + + g_assert_false (data->completed); + g_assert_no_error (data->error); +} + +static void +test_ssm_next_not_started (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + FpiSsmTestData *data = fpi_ssm_get_data (ssm); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*completed*"); + fpi_ssm_next_state (ssm); + g_test_assert_expected_messages (); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + g_assert_false (data->completed); + g_assert_no_error (data->error); +} + +static void +test_ssm_next_with_delayed (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + FpiSsmTestData *data = fpi_ssm_get_data (ssm); + gpointer timeout_tracker = GUINT_TO_POINTER (TRUE); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + fpi_ssm_next_state_delayed (ssm, 10, NULL); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*timeout*"); + fpi_ssm_next_state (ssm); + g_test_assert_expected_messages (); + + g_timeout_add (100, (GSourceFunc) g_nullify_pointer, &timeout_tracker); + while (timeout_tracker) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 2); + + g_assert_false (data->completed); + g_assert_no_error (data->error); +} + +static void +test_ssm_next_complete (void) +{ + FpiSsm *ssm = ssm_test_new (); + + g_autoptr(FpiSsmTestData) data = fpi_ssm_test_data_ref (fpi_ssm_get_data (ssm)); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + fpi_ssm_next_state (ssm); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 2); + + fpi_ssm_next_state (ssm); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_2); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_2); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 3); + + fpi_ssm_next_state (ssm); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_3); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_3); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 4); + + fpi_ssm_next_state (ssm); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_3); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 5); + + g_assert_true (data->completed); + g_assert_no_error (data->error); +} + +static void +test_ssm_jump_to_state (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + FpiSsmTestData *data = fpi_ssm_get_data (ssm); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + fpi_ssm_jump_to_state (ssm, FPI_TEST_SSM_STATE_2); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_2); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_2); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 2); + + fpi_ssm_jump_to_state (ssm, FPI_TEST_SSM_STATE_1); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 3); + + g_assert_false (data->completed); + g_assert_no_error (data->error); +} + +static void +test_ssm_jump_to_state_not_started (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + FpiSsmTestData *data = fpi_ssm_get_data (ssm); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*completed*"); + fpi_ssm_jump_to_state (ssm, FPI_TEST_SSM_STATE_2); + g_test_assert_expected_messages (); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_2); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_2); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + g_assert_false (data->completed); + g_assert_no_error (data->error); +} + +static void +test_ssm_jump_to_state_with_delayed (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + FpiSsmTestData *data = fpi_ssm_get_data (ssm); + gpointer timeout_tracker = GUINT_TO_POINTER (TRUE); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + fpi_ssm_jump_to_state_delayed (ssm, FPI_TEST_SSM_STATE_2, 10, NULL); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*timeout*"); + fpi_ssm_jump_to_state (ssm, FPI_TEST_SSM_STATE_2); + g_test_assert_expected_messages (); + + g_timeout_add (100, (GSourceFunc) g_nullify_pointer, &timeout_tracker); + while (timeout_tracker) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_2); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_2); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 2); + + g_assert_false (data->completed); + g_assert_no_error (data->error); +} + +static void +test_ssm_jump_to_state_last (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + FpiSsmTestData *data = fpi_ssm_get_data (ssm); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + fpi_ssm_jump_to_state (ssm, FPI_TEST_SSM_STATE_3); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_3); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_3); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 2); + + g_assert_false (data->completed); + g_assert_no_error (data->error); +} + +static void +test_ssm_jump_to_state_wrong (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + FpiSsmTestData *data = fpi_ssm_get_data (ssm); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*nr_states*"); + fpi_ssm_jump_to_state (ssm, FPI_TEST_SSM_STATE_NUM + 10); + g_test_assert_expected_messages (); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_NUM + 10); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_NUM + 10); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 2); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*state*"); + fpi_ssm_jump_to_state (ssm, FPI_TEST_SSM_STATE_0 - 10); + g_test_assert_expected_messages (); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0 - 10); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0 - 10); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 3); + + g_assert_false (data->completed); + g_assert_no_error (data->error); +} + +static void +test_ssm_mark_completed (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + g_autoptr(FpiSsmTestData) data = fpi_ssm_test_data_ref (fpi_ssm_get_data (ssm)); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + data->expected_last_state = FPI_TEST_SSM_STATE_0; + fpi_ssm_mark_completed (g_steal_pointer (&ssm)); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 2); + + g_assert_true (data->completed); + g_assert_no_error (data->error); + g_assert_true (data->ssm_destroyed); +} + +static void +test_ssm_mark_completed_not_started (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + g_autoptr(FpiSsmTestData) data = fpi_ssm_test_data_ref (fpi_ssm_get_data (ssm)); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*completed*"); + fpi_ssm_mark_completed (g_steal_pointer (&ssm)); + g_test_assert_expected_messages (); + + g_assert_cmpint (data->handler_state, ==, -1); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 0); + g_assert_true (data->ssm_destroyed); +} + +static void +test_ssm_mark_completed_with_delayed (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + g_autoptr(FpiSsmTestData) data = fpi_ssm_test_data_ref (fpi_ssm_get_data (ssm)); + gpointer timeout_tracker = GUINT_TO_POINTER (TRUE); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + data->expected_last_state = FPI_TEST_SSM_STATE_0; + fpi_ssm_mark_completed_delayed (ssm, 10, NULL); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*timeout*"); + fpi_ssm_mark_completed (g_steal_pointer (&ssm)); + g_test_assert_expected_messages (); + + g_timeout_add (100, (GSourceFunc) g_nullify_pointer, &timeout_tracker); + while (timeout_tracker) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 2); + g_assert_true (data->ssm_destroyed); +} + +static void +test_ssm_mark_failed (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + g_autoptr(FpiSsmTestData) data = fpi_ssm_test_data_ref (fpi_ssm_get_data (ssm)); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + data->expected_last_state = FPI_TEST_SSM_STATE_0; + fpi_ssm_mark_failed (g_steal_pointer (&ssm), + fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 2); + + g_assert_true (data->completed); + g_assert_error (data->error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_PROTO); + g_assert_true (data->ssm_destroyed); +} + +static void +test_ssm_mark_failed_not_started (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + g_autoptr(FpiSsmTestData) data = fpi_ssm_test_data_ref (fpi_ssm_get_data (ssm)); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*completed*"); + fpi_ssm_mark_failed (g_steal_pointer (&ssm), + fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); + g_test_assert_expected_messages (); + + g_assert_cmpint (data->handler_state, ==, -1); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 0); + g_assert_true (data->ssm_destroyed); +} + +static void +test_ssm_mark_failed_with_delayed (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + g_autoptr(FpiSsmTestData) data = fpi_ssm_test_data_ref (fpi_ssm_get_data (ssm)); + gpointer timeout_tracker = GUINT_TO_POINTER (TRUE); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + fpi_ssm_mark_completed_delayed (ssm, 10, NULL); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*timeout*"); + data->expected_last_state = FPI_TEST_SSM_STATE_0; + fpi_ssm_mark_failed (g_steal_pointer (&ssm), + fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); + g_test_assert_expected_messages (); + + g_timeout_add (100, (GSourceFunc) g_nullify_pointer, &timeout_tracker); + while (timeout_tracker) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 2); + + g_assert_true (data->completed); + g_assert_error (data->error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_PROTO); + g_assert_true (data->ssm_destroyed); +} + +static void +test_ssm_delayed_next (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + FpiSsmTestData *data = fpi_ssm_get_data (ssm); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + fpi_ssm_next_state_delayed (ssm, 10, NULL); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + while (data->handler_state == FPI_TEST_SSM_STATE_0) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 2); + + g_assert_false (data->completed); + g_assert_no_error (data->error); +} + +static void +test_ssm_delayed_next_cancel (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + FpiSsmTestData *data = fpi_ssm_get_data (ssm); + gpointer timeout_tracker = GUINT_TO_POINTER (TRUE); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + fpi_ssm_next_state_delayed (ssm, 10, NULL); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + g_idle_add_full (G_PRIORITY_HIGH, test_ssm_cancel_delayed_action_delayed, ssm, NULL); + g_timeout_add (100, (GSourceFunc) g_nullify_pointer, &timeout_tracker); + + while (timeout_tracker) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + g_assert_false (data->completed); + g_assert_no_error (data->error); +} + +static void +test_ssm_delayed_next_cancellable (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + g_autoptr(GCancellable) cancellable = g_cancellable_new (); + FpiSsmTestData *data = fpi_ssm_get_data (ssm); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + fpi_ssm_next_state_delayed (ssm, 10, cancellable); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + g_idle_add_full (G_PRIORITY_HIGH, test_ssm_cancel_cancellable_delayed, cancellable, NULL); + + while (!g_cancellable_is_cancelled (cancellable)) + g_main_context_iteration (NULL, TRUE); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*timeout*"); + fpi_ssm_cancel_delayed_state_change (ssm); + g_test_assert_expected_messages (); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + g_assert_false (data->completed); + g_assert_no_error (data->error); +} + +static void +test_ssm_delayed_next_not_started (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + FpiSsmTestData *data = fpi_ssm_get_data (ssm); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*completed*"); + fpi_ssm_next_state_delayed (ssm, 10, NULL); + g_test_assert_expected_messages (); + + g_assert_cmpint (data->handler_state, ==, -1); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 0); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*completed*"); + while (data->handler_state == -1) + g_main_context_iteration (NULL, TRUE); + g_test_assert_expected_messages (); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + g_assert_false (data->completed); + g_assert_no_error (data->error); +} + +static void +test_ssm_delayed_next_complete (void) +{ + FpiSsm *ssm = ssm_test_new (); + + g_autoptr(FpiSsmTestData) data = fpi_ssm_test_data_ref (fpi_ssm_get_data (ssm)); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + fpi_ssm_next_state_delayed (ssm, 10, NULL); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + while (data->handler_state == FPI_TEST_SSM_STATE_0) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 2); + + fpi_ssm_next_state_delayed (ssm, 10, NULL); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 2); + + while (data->handler_state == FPI_TEST_SSM_STATE_1) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_2); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_2); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 3); + + fpi_ssm_next_state_delayed (ssm, 10, NULL); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_2); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_2); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 3); + + while (data->handler_state == FPI_TEST_SSM_STATE_2) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_3); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_3); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 4); + + fpi_ssm_next_state_delayed (ssm, 10, NULL); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_3); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_3); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 4); + + while (!data->completed) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_3); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 5); + + g_assert_true (data->completed); + g_assert_no_error (data->error); +} + +static void +test_ssm_delayed_jump_to_state (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + FpiSsmTestData *data = fpi_ssm_get_data (ssm); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + fpi_ssm_jump_to_state_delayed (ssm, FPI_TEST_SSM_STATE_2, 10, NULL); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + while (data->handler_state == FPI_TEST_SSM_STATE_0) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_2); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_2); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 2); + + fpi_ssm_jump_to_state_delayed (ssm, FPI_TEST_SSM_STATE_1, 10, NULL); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_2); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_2); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 2); + + while (data->handler_state == FPI_TEST_SSM_STATE_2) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 3); + + g_assert_false (data->completed); + g_assert_no_error (data->error); +} + +static void +test_ssm_delayed_jump_to_state_cancel (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + FpiSsmTestData *data = fpi_ssm_get_data (ssm); + gpointer timeout_tracker = GUINT_TO_POINTER (TRUE); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + fpi_ssm_jump_to_state_delayed (ssm, FPI_TEST_SSM_STATE_2, 10, NULL); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + g_idle_add_full (G_PRIORITY_HIGH, test_ssm_cancel_delayed_action_delayed, ssm, NULL); + g_timeout_add (100, (GSourceFunc) g_nullify_pointer, &timeout_tracker); + + while (timeout_tracker) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + g_assert_false (data->completed); + g_assert_no_error (data->error); +} + +static void +test_ssm_delayed_jump_to_state_cancellable (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + g_autoptr(GCancellable) cancellable = g_cancellable_new (); + FpiSsmTestData *data = fpi_ssm_get_data (ssm); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + fpi_ssm_jump_to_state_delayed (ssm, FPI_TEST_SSM_STATE_2, 10, cancellable); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + g_idle_add_full (G_PRIORITY_HIGH, test_ssm_cancel_cancellable_delayed, cancellable, NULL); + + while (!g_cancellable_is_cancelled (cancellable)) + g_main_context_iteration (NULL, TRUE); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*timeout*"); + fpi_ssm_cancel_delayed_state_change (ssm); + g_test_assert_expected_messages (); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + g_assert_false (data->completed); + g_assert_no_error (data->error); +} + +static void +test_ssm_delayed_jump_to_state_not_started (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + FpiSsmTestData *data = fpi_ssm_get_data (ssm); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*completed*"); + fpi_ssm_jump_to_state_delayed (ssm, FPI_TEST_SSM_STATE_2, 10, NULL); + g_test_assert_expected_messages (); + + g_assert_cmpint (data->handler_state, ==, -1); + g_assert_null (data->handlers_chain); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*completed*"); + while (data->handler_state == -1) + g_main_context_iteration (NULL, TRUE); + g_test_assert_expected_messages (); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_2); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_2); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + g_assert_false (data->completed); + g_assert_no_error (data->error); +} + +static void +test_ssm_delayed_jump_to_state_last (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + FpiSsmTestData *data = fpi_ssm_get_data (ssm); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + fpi_ssm_jump_to_state_delayed (ssm, FPI_TEST_SSM_STATE_3, 10, NULL); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + while (data->handler_state == FPI_TEST_SSM_STATE_0) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_3); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_3); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 2); + + g_assert_false (data->completed); + g_assert_no_error (data->error); +} + +static void +test_ssm_delayed_jump_to_state_wrong (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + FpiSsmTestData *data = fpi_ssm_get_data (ssm); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*nr_states*"); + fpi_ssm_jump_to_state_delayed (ssm, FPI_TEST_SSM_STATE_NUM + 10, 10, NULL); + g_test_assert_expected_messages (); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*nr_states*"); + while (g_slist_length (data->handlers_chain) == 1) + g_main_context_iteration (NULL, TRUE); + g_test_assert_expected_messages (); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_NUM + 10); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_NUM + 10); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 2); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*state*"); + fpi_ssm_jump_to_state_delayed (ssm, FPI_TEST_SSM_STATE_0 - 10, 10, NULL); + g_test_assert_expected_messages (); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_NUM + 10); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_NUM + 10); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 2); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*state*"); + while (g_slist_length (data->handlers_chain) == 2) + g_main_context_iteration (NULL, TRUE); + g_test_assert_expected_messages (); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0 - 10); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0 - 10); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 3); + + g_assert_false (data->completed); + g_assert_no_error (data->error); +} + +static void +test_ssm_delayed_mark_completed (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + g_autoptr(FpiSsmTestData) data = fpi_ssm_test_data_ref (fpi_ssm_get_data (ssm)); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + data->expected_last_state = FPI_TEST_SSM_STATE_0; + fpi_ssm_mark_completed_delayed (g_steal_pointer (&ssm), 10, NULL); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + while (g_slist_length (data->handlers_chain) == 1) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 2); + g_assert_true (data->completed); + g_assert_no_error (data->error); + g_assert_true (data->ssm_destroyed); +} + +static void +test_ssm_delayed_mark_completed_not_started (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + g_autoptr(FpiSsmTestData) data = fpi_ssm_test_data_ref (fpi_ssm_get_data (ssm)); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*completed*"); + fpi_ssm_mark_completed_delayed (ssm, 10, NULL); + g_test_assert_expected_messages (); + + g_timeout_add (100, (GSourceFunc) g_nullify_pointer, &ssm); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*completed*"); + while (ssm != NULL) + g_main_context_iteration (NULL, TRUE); + g_test_assert_expected_messages (); + + g_assert_cmpint (data->handler_state, ==, -1); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 0); + g_assert_true (data->ssm_destroyed); +} + +static void +test_ssm_delayed_mark_completed_cancel (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + FpiSsmTestData *data = fpi_ssm_get_data (ssm); + gpointer timeout_tracker = GUINT_TO_POINTER (TRUE); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + fpi_ssm_mark_completed_delayed (ssm, 10, NULL); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + g_idle_add_full (G_PRIORITY_HIGH, test_ssm_cancel_delayed_action_delayed, ssm, NULL); + g_timeout_add (100, (GSourceFunc) g_nullify_pointer, &timeout_tracker); + + while (timeout_tracker) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + g_assert_false (data->completed); + g_assert_no_error (data->error); + g_assert_false (data->ssm_destroyed); +} + +static void +test_ssm_delayed_mark_completed_cancellable (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + g_autoptr(GCancellable) cancellable = g_cancellable_new (); + FpiSsmTestData *data = fpi_ssm_get_data (ssm); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + fpi_ssm_mark_completed_delayed (ssm, 10, cancellable); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + g_idle_add_full (G_PRIORITY_HIGH, test_ssm_cancel_cancellable_delayed, cancellable, NULL); + + while (!g_cancellable_is_cancelled (cancellable)) + g_main_context_iteration (NULL, TRUE); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*timeout*"); + fpi_ssm_cancel_delayed_state_change (ssm); + g_test_assert_expected_messages (); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + g_assert_false (data->completed); + g_assert_no_error (data->error); +} + +static void +test_ssm_delayed_cancel_error (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + FpiSsmTestData *data = fpi_ssm_get_data (ssm); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*completed*"); + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*timeout*"); + fpi_ssm_cancel_delayed_state_change (ssm); + g_test_assert_expected_messages (); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*timeout*"); + fpi_ssm_cancel_delayed_state_change (ssm); + g_test_assert_expected_messages (); +} + +static void +test_ssm_subssm_start (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + g_autoptr(FpiSsm) subssm = + ssm_test_new_full (FPI_TEST_SSM_STATE_NUM, "FPI_TEST_SUB_SSM"); + FpiSsmTestData *data = fpi_ssm_get_data (ssm); + g_autoptr(FpiSsmTestData) subdata = + fpi_ssm_test_data_ref (fpi_ssm_get_data (subssm)); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + fpi_ssm_start_subsm (ssm, subssm); + g_assert_cmpint (subdata->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (subssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (subdata->handlers_chain), ==, 1); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + fpi_ssm_next_state (subssm); + + g_assert_cmpint (subdata->handler_state, ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpuint (g_slist_length (subdata->handlers_chain), ==, 2); + g_assert_no_error (subdata->error); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + subdata->expected_last_state = FPI_TEST_SSM_STATE_1; + fpi_ssm_mark_completed (g_steal_pointer (&subssm)); + + g_assert_cmpint (subdata->handler_state, ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpuint (g_slist_length (subdata->handlers_chain), ==, 2); + g_assert_true (subdata->ssm_destroyed); + g_assert_no_error (subdata->error); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 2); + + g_assert_false (data->completed); + g_assert_false (data->ssm_destroyed); + g_assert_no_error (data->error); +} + +static void +test_ssm_subssm_mark_failed (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + g_autoptr(FpiSsm) subssm = + ssm_test_new_full (FPI_TEST_SSM_STATE_NUM, "FPI_TEST_SUB_SSM"); + g_autoptr(FpiSsmTestData) data = + fpi_ssm_test_data_ref (fpi_ssm_get_data (ssm)); + g_autoptr(FpiSsmTestData) subdata = + fpi_ssm_test_data_ref (fpi_ssm_get_data (subssm)); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + fpi_ssm_start_subsm (g_steal_pointer (&ssm), subssm); + g_assert_cmpint (subdata->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (subssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (subdata->handlers_chain), ==, 1); + + data->expected_last_state = FPI_TEST_SSM_STATE_0; + subdata->expected_last_state = FPI_TEST_SSM_STATE_0; + fpi_ssm_mark_failed (g_steal_pointer (&subssm), + fpi_device_error_new (FP_DEVICE_ERROR_BUSY)); + + g_assert_cmpint (subdata->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (subdata->handlers_chain), ==, 1); + g_assert_true (subdata->ssm_destroyed); + g_assert_no_error (subdata->error); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 2); + + g_assert_true (data->completed); + g_assert_true (data->ssm_destroyed); + g_assert_error (data->error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_BUSY); +} + +static void +test_ssm_subssm_start_with_started (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + g_autoptr(FpiSsm) subssm = + ssm_test_new_full (FPI_TEST_SSM_STATE_NUM, "FPI_TEST_SUB_SSM"); + FpiSsmTestData *data = fpi_ssm_get_data (ssm); + g_autoptr(FpiSsmTestData) subdata = + fpi_ssm_test_data_ref (fpi_ssm_get_data (subssm)); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + fpi_ssm_start (subssm, test_ssm_completed_callback); + g_assert_cmpuint (g_slist_length (subdata->handlers_chain), ==, 1); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*completed*"); + fpi_ssm_start_subsm (ssm, subssm); + g_test_assert_expected_messages (); + + g_assert_cmpint (subdata->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (subssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (subdata->handlers_chain), ==, 2); + + subdata->expected_last_state = FPI_TEST_SSM_STATE_0; + fpi_ssm_mark_completed (g_steal_pointer (&subssm)); + + g_assert_cmpint (subdata->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (subdata->handlers_chain), ==, 2); + g_assert_true (subdata->ssm_destroyed); + g_assert_no_error (subdata->error); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 2); + + g_assert_false (data->completed); + g_assert_false (data->ssm_destroyed); + g_assert_no_error (data->error); +} + +static void +test_ssm_subssm_start_with_delayed (void) +{ + g_autoptr(FpiSsm) ssm = ssm_test_new (); + g_autoptr(FpiSsm) subssm = + ssm_test_new_full (FPI_TEST_SSM_STATE_NUM, "FPI_TEST_SUB_SSM"); + FpiSsmTestData *data = fpi_ssm_get_data (ssm); + g_autoptr(FpiSsmTestData) subdata = + fpi_ssm_test_data_ref (fpi_ssm_get_data (subssm)); + gpointer timeout_tracker = GUINT_TO_POINTER (TRUE); + + fpi_ssm_start (ssm, test_ssm_completed_callback); + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); + + fpi_ssm_next_state_delayed (ssm, 10, NULL); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*timeout*"); + fpi_ssm_start_subsm (ssm, subssm); + g_test_assert_expected_messages (); + + g_timeout_add (100, (GSourceFunc) g_nullify_pointer, &timeout_tracker); + while (timeout_tracker) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpint (subdata->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpint (fpi_ssm_get_cur_state (subssm), ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (subdata->handlers_chain), ==, 1); + + subdata->expected_last_state = FPI_TEST_SSM_STATE_0; + fpi_ssm_mark_completed (g_steal_pointer (&subssm)); + + g_assert_cmpint (subdata->handler_state, ==, FPI_TEST_SSM_STATE_0); + g_assert_cmpuint (g_slist_length (subdata->handlers_chain), ==, 1); + g_assert_true (subdata->ssm_destroyed); + g_assert_no_error (subdata->error); + + g_assert_cmpint (data->handler_state, ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpint (fpi_ssm_get_cur_state (ssm), ==, FPI_TEST_SSM_STATE_1); + g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 2); + + g_assert_false (data->completed); + g_assert_false (data->ssm_destroyed); + g_assert_no_error (data->error); +} + +int +main (int argc, char *argv[]) +{ + g_autoptr(FpDevice) device = NULL; + + g_test_init (&argc, &argv, NULL); + + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + fake_device = device; + g_object_add_weak_pointer (G_OBJECT (device), (gpointer) & fake_device); + + g_test_add_func ("/ssm/new", test_ssm_new); + g_test_add_func ("/ssm/new/full", test_ssm_new_full); + g_test_add_func ("/ssm/new/no_handler", test_ssm_new_no_handler); + g_test_add_func ("/ssm/new/wrong_states", test_ssm_new_wrong_states); + g_test_add_func ("/ssm/set_data", test_ssm_set_data); + g_test_add_func ("/ssm/set_data/cleanup", test_ssm_set_data_cleanup); + g_test_add_func ("/ssm/start", test_ssm_start); + g_test_add_func ("/ssm/start/single", test_ssm_start_single); + g_test_add_func ("/ssm/next", test_ssm_next); + g_test_add_func ("/ssm/next/complete", test_ssm_next_complete); + g_test_add_func ("/ssm/next/not_started", test_ssm_next_not_started); + g_test_add_func ("/ssm/next/with_delayed", test_ssm_next_with_delayed); + g_test_add_func ("/ssm/jump_to_state", test_ssm_jump_to_state); + g_test_add_func ("/ssm/jump_to_state/not_started", test_ssm_jump_to_state_not_started); + g_test_add_func ("/ssm/jump_to_state/with_delayed", test_ssm_jump_to_state_with_delayed); + g_test_add_func ("/ssm/jump_to_state/last", test_ssm_jump_to_state_last); + g_test_add_func ("/ssm/jump_to_state/wrong", test_ssm_jump_to_state_wrong); + g_test_add_func ("/ssm/mark_completed", test_ssm_mark_completed); + g_test_add_func ("/ssm/mark_completed/not_started", test_ssm_mark_completed_not_started); + g_test_add_func ("/ssm/mark_completed/with_delayed", test_ssm_mark_completed_with_delayed); + g_test_add_func ("/ssm/mark_failed", test_ssm_mark_failed); + g_test_add_func ("/ssm/mark_failed/not_started", test_ssm_mark_failed_not_started); + g_test_add_func ("/ssm/mark_failed/with_delayed", test_ssm_mark_failed_with_delayed); + g_test_add_func ("/ssm/delayed/next", test_ssm_delayed_next); + g_test_add_func ("/ssm/delayed/next/cancel", test_ssm_delayed_next_cancel); + g_test_add_func ("/ssm/delayed/next/cancellable", test_ssm_delayed_next_cancellable); + g_test_add_func ("/ssm/delayed/next/not_started", test_ssm_delayed_next_not_started); + g_test_add_func ("/ssm/delayed/next/complete", test_ssm_delayed_next_complete); + g_test_add_func ("/ssm/delayed/jump_to_state", test_ssm_delayed_jump_to_state); + g_test_add_func ("/ssm/delayed/jump_to_state/cancel", test_ssm_delayed_jump_to_state_cancel); + g_test_add_func ("/ssm/delayed/jump_to_state/cancellable", test_ssm_delayed_jump_to_state_cancellable); + g_test_add_func ("/ssm/delayed/jump_to_state/not_started", test_ssm_delayed_jump_to_state_not_started); + g_test_add_func ("/ssm/delayed/jump_to_state/last", test_ssm_delayed_jump_to_state_last); + g_test_add_func ("/ssm/delayed/jump_to_state/wrong", test_ssm_delayed_jump_to_state_wrong); + g_test_add_func ("/ssm/delayed/mark_completed", test_ssm_delayed_mark_completed); + g_test_add_func ("/ssm/delayed/mark_completed/cancel", test_ssm_delayed_mark_completed_cancel); + g_test_add_func ("/ssm/delayed/mark_completed/cancellable", test_ssm_delayed_mark_completed_cancellable); + g_test_add_func ("/ssm/delayed/mark_completed/not_started", test_ssm_delayed_mark_completed_not_started); + g_test_add_func ("/ssm/delayed/cancel/error", test_ssm_delayed_cancel_error); + g_test_add_func ("/ssm/subssm/start", test_ssm_subssm_start); + g_test_add_func ("/ssm/subssm/start/with_started", test_ssm_subssm_start_with_started); + g_test_add_func ("/ssm/subssm/start/with_delayed", test_ssm_subssm_start_with_delayed); + g_test_add_func ("/ssm/subssm/mark_failed", test_ssm_subssm_mark_failed); + + return g_test_run (); +} From edb09463f4b8a331624079c23e059b4a81a651b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 12 Dec 2019 18:47:31 +0100 Subject: [PATCH 140/237] ci: Save coverage reports when running tests --- .gitlab-ci.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 42a39746..5dfd8e19 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -53,9 +53,14 @@ test: variables: - $CI_PIPELINE_SOURCE == "schedule" script: - - meson --werror -Ddrivers=all . _build + - meson --werror -Ddrivers=all -Db_coverage=true . _build - ninja -C _build - meson test -C _build --verbose --no-stdsplit + - ninja -C _build coverage + artifacts: + paths: + - _build/meson-logs + expire_in: 1 week test_valgrind: stage: test @@ -140,6 +145,7 @@ container_fedora_build: flatpak-builder gcc gcc-c++ + gcovr git glib2-devel glibc-devel From af42b3e4682d5920bab56ef565221dfb5a16ea3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 12 Dec 2019 19:10:39 +0100 Subject: [PATCH 141/237] ci: Increase the timeout multiplier for tests Change the FEDORA_TAG to the current date in order to rebuild the image --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5dfd8e19..270aec2c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -55,7 +55,7 @@ test: script: - meson --werror -Ddrivers=all -Db_coverage=true . _build - ninja -C _build - - meson test -C _build --verbose --no-stdsplit + - meson test -C _build --verbose --no-stdsplit --timeout-multiplier 3 - ninja -C _build coverage artifacts: paths: From 24d388f92390a7cbfb9944d1fe3e0e0ae0f23ad2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 13 Dec 2019 19:33:12 +0100 Subject: [PATCH 142/237] meson: Split single-line dependencies to reduce the diff on changes Make it more readable and in case we add something else, it's easier to track --- demo/meson.build | 5 ++++- examples/meson.build | 5 ++++- libfprint/meson.build | 11 +++++++++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/demo/meson.build b/demo/meson.build index 20f89625..68d865fd 100644 --- a/demo/meson.build +++ b/demo/meson.build @@ -9,7 +9,10 @@ datadir = join_paths(prefix, get_option('datadir')) executable('gtk-libfprint-test', [ 'gtk-libfprint-test.c', gtk_test_resources ], - dependencies: [ libfprint_dep, gtk_dep ], + dependencies: [ + gtk_dep, + libfprint_dep, + ], c_args: '-DPACKAGE_VERSION="' + meson.project_version() + '"', install: true, install_dir: bindir) diff --git a/examples/meson.build b/examples/meson.build index 7b313d09..90a1178c 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -3,7 +3,10 @@ examples = [ 'enroll', 'verify', 'manage-prints' ] foreach example: examples executable(example, [ example + '.c', 'storage.c', 'utilities.c' ], - dependencies: [ libfprint_dep, glib_dep ], + dependencies: [ + libfprint_dep, + glib_dep, + ], ) endforeach diff --git a/libfprint/meson.build b/libfprint/meson.build index 06668b3d..61fd5062 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -233,14 +233,21 @@ libfprint = library('fprint', libfprint_dep = declare_dependency(link_with: libfprint, sources: [ fp_enums_h ], include_directories: root_inc, - dependencies: [ glib_dep, gusb_dep, gio_dep ]) + dependencies: [ + gio_dep, + glib_dep, + gusb_dep, + ]) install_headers(['fprint.h'] + libfprint_public_headers, subdir: 'libfprint') libfprint_private_dep = declare_dependency( include_directories: include_directories('.'), link_with: libfprint_private, - dependencies: [ deps, libfprint_dep ] + dependencies: [ + deps, + libfprint_dep, + ] ) udev_rules = executable('fprint-list-udev-rules', From 7e2db8e988a26b797c72803f033128951210a957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 13 Dec 2019 21:31:43 +0100 Subject: [PATCH 143/237] driver_ids.h: Remove the legacy ID file This is not used anymore by drivers as they all use GType's, so remove it and any (dead) reference to it. --- libfprint/drivers/driver_ids.h | 47 ---------------------------------- libfprint/drivers/etes603.c | 1 - 2 files changed, 48 deletions(-) delete mode 100644 libfprint/drivers/driver_ids.h diff --git a/libfprint/drivers/driver_ids.h b/libfprint/drivers/driver_ids.h deleted file mode 100644 index 4270842d..00000000 --- a/libfprint/drivers/driver_ids.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Driver IDs - * Copyright (C) 2012 Vasily Khoruzhick - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __DRIVER_IDS -#define __DRIVER_IDS - -enum { - UPEKTS_ID = 1, - URU4000_ID = 2, - AES4000_ID = 3, - AES2501_ID = 4, - UPEKTC_ID = 5, - AES1610_ID = 6, - /* FDU2000_ID = 7, */ - VCOM5S_ID = 8, - UPEKSONLY_ID = 9, - VFS101_ID = 10, - VFS301_ID = 11, - AES2550_ID = 12, - /* UPEKE2_ID = 13 */ - AES1660_ID = 14, - AES2660_ID = 15, - AES3500_ID = 16, - UPEKTC_IMG_ID = 17, - ETES603_ID = 18, - VFS5011_ID = 19, - VFS0050_ID = 20, - ELAN_ID = 21, -}; - -#endif diff --git a/libfprint/drivers/etes603.c b/libfprint/drivers/etes603.c index 5cd7f0bd..f798f5b8 100644 --- a/libfprint/drivers/etes603.c +++ b/libfprint/drivers/etes603.c @@ -36,7 +36,6 @@ #define FP_COMPONENT "etes603" #include "drivers_api.h" -#include "driver_ids.h" /* libusb defines */ #define EP_IN 0x81 From 4948a85e97205be22fe2822ef9f91a5c42677668 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 5 Dec 2019 15:48:57 +0100 Subject: [PATCH 144/237] synaptics: Use local variable rather than re-fetching usb device --- libfprint/drivers/synaptics/synaptics.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index 6ed6791e..97d9d212 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -969,10 +969,10 @@ dev_probe (FpDevice *device) return; } - if (!g_usb_device_reset (fpi_device_get_usb_device (device), &error)) + if (!g_usb_device_reset (usb_dev, &error)) goto err_close; - if (!g_usb_device_claim_interface (fpi_device_get_usb_device (device), 0, 0, &error)) + if (!g_usb_device_claim_interface (usb_dev, 0, 0, &error)) goto err_close; /* TODO: Do not do this synchronous. */ From 1d1c39c234d8e7e84185a1dde2665baa022e85c4 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 5 Dec 2019 15:49:43 +0100 Subject: [PATCH 145/237] tests: Ensure objects are free'ed at the end of tests The objects may not be garbage collected otherwise. --- tests/capture.py | 4 ++++ tests/synaptics/custom.py | 4 ++++ tests/virtual-image.py | 2 ++ 3 files changed, 10 insertions(+) diff --git a/tests/capture.py b/tests/capture.py index a7b75831..88ed81fb 100755 --- a/tests/capture.py +++ b/tests/capture.py @@ -17,6 +17,7 @@ c.enumerate() devices = c.get_devices() d = devices[0] +del devices d.open_sync() @@ -24,6 +25,9 @@ img = d.capture_sync(True) d.close_sync() +del d +del c + width = img.get_width() height = img.get_height() diff --git a/tests/synaptics/custom.py b/tests/synaptics/custom.py index 60167997..b0f1c54f 100755 --- a/tests/synaptics/custom.py +++ b/tests/synaptics/custom.py @@ -11,6 +11,7 @@ c.enumerate() devices = c.get_devices() d = devices[0] +del devices assert d.get_driver() == "synaptics" @@ -40,3 +41,6 @@ print("deleting") d.delete_print_sync(p) print("delete done") d.close_sync() + +del d +del c diff --git a/tests/virtual-image.py b/tests/virtual-image.py index 87c221b2..11ec8ae8 100755 --- a/tests/virtual-image.py +++ b/tests/virtual-image.py @@ -83,6 +83,8 @@ class VirtualImage(unittest.TestCase): @classmethod def tearDownClass(cls): shutil.rmtree(cls.tmpdir) + del cls.dev + del cls.ctx def setUp(self): self.dev.open_sync() From 113bef8f3f6f019df39c0c47c0a4cf1b28fa6ddc Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 5 Dec 2019 17:28:47 +0100 Subject: [PATCH 146/237] examples: Fix double device closing in manage-prints The manage-prints command would close the device twice when the user hits an invalid number or 'n' and no prints need to be deleted. Simply remove the erroneous call to fix the issue. --- examples/manage-prints.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/manage-prints.c b/examples/manage-prints.c index 7bbbc5ee..d64b5fa6 100644 --- a/examples/manage-prints.c +++ b/examples/manage-prints.c @@ -197,9 +197,6 @@ on_list_completed (FpDevice *dev, list_data->ret_value = EXIT_SUCCESS; else g_warning ("Invalid finger selected"); - - fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed, - list_data); } } From 9c806e60f46b96bd52f7bccc8e02dd167eb51e20 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Fri, 6 Dec 2019 16:30:34 +0100 Subject: [PATCH 147/237] elan: Do not leak converted frames The elan driver converts frames into a different format. These frames are only needed to assemable the image and should be free'ed afterwards. Fixes: #213 --- libfprint/drivers/elan.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libfprint/drivers/elan.c b/libfprint/drivers/elan.c index 9495a480..415aaefd 100644 --- a/libfprint/drivers/elan.c +++ b/libfprint/drivers/elan.c @@ -321,6 +321,8 @@ elan_submit_image (FpImageDevice *dev) fpi_do_movement_estimation (&assembling_ctx, frames); img = fpi_assemble_frames (&assembling_ctx, frames); + g_slist_free_full (frames, g_free); + fpi_image_device_image_captured (dev, img); } From bfd68bbc0156612adb9ae2edb18ffe526818ca31 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 16 Dec 2019 11:36:28 +0100 Subject: [PATCH 148/237] meson: Add missing dependency on fp-enum.h for private library The private library needs to indirectly include fp-enum.h. This dependency was not listed anyway, resulting in a race condition during the build process. --- libfprint/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfprint/meson.build b/libfprint/meson.build index 61fd5062..74908f46 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -205,7 +205,7 @@ libnbis = static_library('nbis', install: false) libfprint_private = static_library('fprint-private', - sources: libfprint_private_sources + fpi_enums, + sources: libfprint_private_sources + fpi_enums + [ fp_enums_h ], dependencies: deps, link_with: libnbis, install: false) From 2af053199490ab2be9b9fc91ca1793f9479e70bc Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 17 Dec 2019 14:23:54 +0100 Subject: [PATCH 149/237] tests: Fix stack corruption in FpiSsm test Using a function with a void return value for a g_timeout_add is not a good idea after all. --- tests/test-fpi-ssm.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/tests/test-fpi-ssm.c b/tests/test-fpi-ssm.c index a3bd9daa..07e28c00 100644 --- a/tests/test-fpi-ssm.c +++ b/tests/test-fpi-ssm.c @@ -39,6 +39,14 @@ typedef struct gboolean expected_last_state; } FpiSsmTestData; +static gboolean +fpi_ssm_test_nullify_pointer (gpointer * nullify_location) +{ + *nullify_location = NULL; + + return G_SOURCE_REMOVE; +} + static FpiSsmTestData * fpi_ssm_test_data_new (void) { @@ -334,7 +342,7 @@ test_ssm_next_with_delayed (void) fpi_ssm_next_state (ssm); g_test_assert_expected_messages (); - g_timeout_add (100, (GSourceFunc) g_nullify_pointer, &timeout_tracker); + g_timeout_add (100, G_SOURCE_FUNC (fpi_ssm_test_nullify_pointer), &timeout_tracker); while (timeout_tracker) g_main_context_iteration (NULL, TRUE); @@ -442,7 +450,7 @@ test_ssm_jump_to_state_with_delayed (void) fpi_ssm_jump_to_state (ssm, FPI_TEST_SSM_STATE_2); g_test_assert_expected_messages (); - g_timeout_add (100, (GSourceFunc) g_nullify_pointer, &timeout_tracker); + g_timeout_add (100, G_SOURCE_FUNC (fpi_ssm_test_nullify_pointer), &timeout_tracker); while (timeout_tracker) g_main_context_iteration (NULL, TRUE); @@ -559,7 +567,7 @@ test_ssm_mark_completed_with_delayed (void) fpi_ssm_mark_completed (g_steal_pointer (&ssm)); g_test_assert_expected_messages (); - g_timeout_add (100, (GSourceFunc) g_nullify_pointer, &timeout_tracker); + g_timeout_add (100, G_SOURCE_FUNC (fpi_ssm_test_nullify_pointer), &timeout_tracker); while (timeout_tracker) g_main_context_iteration (NULL, TRUE); @@ -623,7 +631,7 @@ test_ssm_mark_failed_with_delayed (void) fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); g_test_assert_expected_messages (); - g_timeout_add (100, (GSourceFunc) g_nullify_pointer, &timeout_tracker); + g_timeout_add (100, G_SOURCE_FUNC (fpi_ssm_test_nullify_pointer), &timeout_tracker); while (timeout_tracker) g_main_context_iteration (NULL, TRUE); @@ -680,7 +688,7 @@ test_ssm_delayed_next_cancel (void) g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); g_idle_add_full (G_PRIORITY_HIGH, test_ssm_cancel_delayed_action_delayed, ssm, NULL); - g_timeout_add (100, (GSourceFunc) g_nullify_pointer, &timeout_tracker); + g_timeout_add (100, G_SOURCE_FUNC (fpi_ssm_test_nullify_pointer), &timeout_tracker); while (timeout_tracker) g_main_context_iteration (NULL, TRUE); @@ -875,7 +883,7 @@ test_ssm_delayed_jump_to_state_cancel (void) g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); g_idle_add_full (G_PRIORITY_HIGH, test_ssm_cancel_delayed_action_delayed, ssm, NULL); - g_timeout_add (100, (GSourceFunc) g_nullify_pointer, &timeout_tracker); + g_timeout_add (100, G_SOURCE_FUNC (fpi_ssm_test_nullify_pointer), &timeout_tracker); while (timeout_tracker) g_main_context_iteration (NULL, TRUE); @@ -1058,7 +1066,7 @@ test_ssm_delayed_mark_completed_not_started (void) fpi_ssm_mark_completed_delayed (ssm, 10, NULL); g_test_assert_expected_messages (); - g_timeout_add (100, (GSourceFunc) g_nullify_pointer, &ssm); + g_timeout_add (100, G_SOURCE_FUNC (fpi_ssm_test_nullify_pointer), &ssm); g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*BUG:*completed*"); while (ssm != NULL) @@ -1088,7 +1096,7 @@ test_ssm_delayed_mark_completed_cancel (void) g_assert_cmpuint (g_slist_length (data->handlers_chain), ==, 1); g_idle_add_full (G_PRIORITY_HIGH, test_ssm_cancel_delayed_action_delayed, ssm, NULL); - g_timeout_add (100, (GSourceFunc) g_nullify_pointer, &timeout_tracker); + g_timeout_add (100, G_SOURCE_FUNC (fpi_ssm_test_nullify_pointer), &timeout_tracker); while (timeout_tracker) g_main_context_iteration (NULL, TRUE); @@ -1312,7 +1320,7 @@ test_ssm_subssm_start_with_delayed (void) fpi_ssm_start_subsm (ssm, subssm); g_test_assert_expected_messages (); - g_timeout_add (100, (GSourceFunc) g_nullify_pointer, &timeout_tracker); + g_timeout_add (100, G_SOURCE_FUNC (fpi_ssm_test_nullify_pointer), &timeout_tracker); while (timeout_tracker) g_main_context_iteration (NULL, TRUE); From 68b5c5d98f07312ca5b417e8b1872ad7be3c05fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 16 Dec 2019 19:44:17 +0100 Subject: [PATCH 150/237] fpi-ssm, fpi-usb-transfer: Use fwd-declarations to avoid headers dependencies Don't make headers inclusions dependent on each others, given that FpiSsm depends on FpiUsbTransfer and vice-versa, so fix the dependency cycle by using forwarded declarations. --- libfprint/fpi-ssm.h | 3 ++- libfprint/fpi-usb-transfer.h | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libfprint/fpi-ssm.h b/libfprint/fpi-ssm.h index fe649467..0e18ab6b 100644 --- a/libfprint/fpi-ssm.h +++ b/libfprint/fpi-ssm.h @@ -22,7 +22,6 @@ #pragma once #include "fp-device.h" -#include "fpi-usb-transfer.h" /* async drv <--> lib comms */ @@ -101,6 +100,8 @@ int fpi_ssm_get_cur_state (FpiSsm *machine); /* Callbacks to be used by the driver instead of implementing their own * logic. */ +typedef struct _FpiUsbTransfer FpiUsbTransfer; + void fpi_ssm_usb_transfer_cb (FpiUsbTransfer *transfer, FpDevice *device, gpointer unused_data, diff --git a/libfprint/fpi-usb-transfer.h b/libfprint/fpi-usb-transfer.h index 5b8fe9cb..09d22e85 100644 --- a/libfprint/fpi-usb-transfer.h +++ b/libfprint/fpi-usb-transfer.h @@ -30,8 +30,7 @@ G_BEGIN_DECLS #define FPI_USB_ENDPOINT_OUT 0x00 typedef struct _FpiUsbTransfer FpiUsbTransfer; - -#include "fpi-ssm.h" +typedef struct _FpiSsm FpiSsm; typedef void (*FpiUsbTransferCallback)(FpiUsbTransfer *transfer, FpDevice *dev, From 9ebb3fd23197b748a2b0458d8e74dbc544bbbcad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 16 Dec 2019 18:07:30 +0100 Subject: [PATCH 151/237] meson: Parse all private headers So we don't have to list each one in case we add new enums. Also, as per previous commit we can reorder them alphabetically. --- libfprint/meson.build | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/libfprint/meson.build b/libfprint/meson.build index 74908f46..781ea816 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -8,14 +8,14 @@ libfprint_sources = [ libfprint_private_sources = [ 'fpi-assembling.c', + 'fpi-byte-reader.c', + 'fpi-byte-writer.c', 'fpi-device.c', - 'fpi-image.c', 'fpi-image-device.c', + 'fpi-image.c', 'fpi-print.c', 'fpi-ssm.c', 'fpi-usb-transfer.c', - 'fpi-byte-reader.c', - 'fpi-byte-writer.c', ] libfprint_public_headers = [ @@ -27,13 +27,18 @@ libfprint_public_headers = [ libfprint_private_headers = [ 'fpi-assembling.h', - 'fpi-device.h', - 'fpi-image.h', - 'fpi-image-device.h', - 'fpi-print.h', 'fpi-byte-reader.h', - 'fpi-byte-writer.h', 'fpi-byte-utils.h', + 'fpi-byte-writer.h', + 'fpi-context.h', + 'fpi-device.h', + 'fpi-image-device.h', + 'fpi-image.h', + 'fpi-log.h', + 'fpi-minutiae.h', + 'fpi-print.h', + 'fpi-usb-transfer.h', + 'fpi-ssm.h', ] nbis_sources = [ From d255a91e9796b133f9e781dd122f3f4827c4676d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 16 Dec 2019 18:46:07 +0100 Subject: [PATCH 152/237] meson: List deps in multiple lines, to have better diffs on changes --- libfprint/meson.build | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libfprint/meson.build b/libfprint/meson.build index 781ea816..a693f805 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -191,7 +191,14 @@ drivers_sources += configure_file(input: 'empty_file', '\n'.join(drivers_type_list + [] + drivers_type_func) ]) -deps = [ mathlib_dep, glib_dep, gusb_dep, nss_dep, imaging_dep, gio_dep ] +deps = [ + gio_dep, + glib_dep, + gusb_dep, + imaging_dep, + mathlib_dep, + nss_dep, +] deps += declare_dependency(include_directories: [ root_inc, From 95cb62fd3b943d77d9e2a890396223025ea60a93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 16 Dec 2019 18:52:46 +0100 Subject: [PATCH 153/237] meson: No need to redefine default pkgconfig install dir This value would be the default anyways --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 8ea4a8b2..1d101a73 100644 --- a/meson.build +++ b/meson.build @@ -209,4 +209,4 @@ pkgconfig.generate( libraries: libfprint, subdirs: 'libfprint', filebase: 'libfprint2', - install_dir: join_paths(get_option('libdir'), 'pkgconfig')) +) From c806993cb90979aa66ff1c5c198ba44a9f4cc0e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 16 Dec 2019 19:00:36 +0100 Subject: [PATCH 154/237] meson: Don't install fpi-enums --- libfprint/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfprint/meson.build b/libfprint/meson.build index a693f805..23ab60a7 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -180,7 +180,7 @@ fp_enums_h = fp_enums[1] fpi_enums = gnome.mkenums_simple('fpi-enums', sources: libfprint_private_headers, - install_header : true) + install_header : false) fpi_enums_h = fpi_enums[1] drivers_sources += configure_file(input: 'empty_file', From c57defda929a5cf7634a6facddaa3891c6a86707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 16 Dec 2019 19:02:00 +0100 Subject: [PATCH 155/237] meson: Use more meson's project_name() Not that libfprint is long to write, but in case we'll ever change the basename, we do it once. --- doc/meson.build | 2 +- doc/xml/meson.build | 6 +++--- libfprint/meson.build | 4 +++- meson.build | 6 +++--- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/doc/meson.build b/doc/meson.build index bed320dc..2c7a3844 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -24,7 +24,7 @@ glib_prefix = dependency('glib-2.0').get_pkgconfig_variable('prefix') glib_docpath = join_paths(glib_prefix, 'share', 'gtk-doc', 'html') docpath = join_paths(get_option('datadir'), 'gtk-doc', 'html') -gnome.gtkdoc('libfprint', +gnome.gtkdoc(meson.project_name(), main_xml: 'libfprint-docs.xml', src_dir: join_paths(meson.source_root(), 'libfprint'), dependencies: libfprint_dep, diff --git a/doc/xml/meson.build b/doc/xml/meson.build index 2ca11007..5e56bb40 100644 --- a/doc/xml/meson.build +++ b/doc/xml/meson.build @@ -1,8 +1,8 @@ ent_conf = configuration_data() -ent_conf.set('PACKAGE', 'libfprint') +ent_conf.set('PACKAGE', meson.project_name()) ent_conf.set('PACKAGE_BUGREPORT', 'https://gitlab.freedesktop.org/libfprint/libfprint/issues') -ent_conf.set('PACKAGE_NAME', 'libfprint') -ent_conf.set('PACKAGE_STRING', 'libfprint') +ent_conf.set('PACKAGE_NAME', meson.project_name()) +ent_conf.set('PACKAGE_STRING', meson.project_name()) ent_conf.set('PACKAGE_TARNAME', 'libfprint-' + meson.project_version()) ent_conf.set('PACKAGE_URL', 'https://fprint.freedesktop.org/') ent_conf.set('PACKAGE_VERSION', meson.project_version()) diff --git a/libfprint/meson.build b/libfprint/meson.build index 23ab60a7..210e45cb 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -251,7 +251,9 @@ libfprint_dep = declare_dependency(link_with: libfprint, gusb_dep, ]) -install_headers(['fprint.h'] + libfprint_public_headers, subdir: 'libfprint') +install_headers(['fprint.h'] + libfprint_public_headers, + subdir: meson.project_name() +) libfprint_private_dep = declare_dependency( include_directories: include_directories('.'), diff --git a/meson.build b/meson.build index 1d101a73..29bdff56 100644 --- a/meson.build +++ b/meson.build @@ -203,10 +203,10 @@ subdir('tests') pkgconfig = import('pkgconfig') pkgconfig.generate( - name: 'libfprint', + name: meson.project_name(), description: 'Generic C API for fingerprint reader access', version: meson.project_version(), libraries: libfprint, - subdirs: 'libfprint', - filebase: 'libfprint2', + subdirs: meson.project_name(), + filebase: meson.project_name() + '2', ) From 4562f9dae39e5cdc671865cdd6a7b8e0a0742cf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 16 Dec 2019 19:05:35 +0100 Subject: [PATCH 156/237] meson: Use soversion everywhere Don't repeat the 2 version number hard-coding it, so we can easily track updates --- libfprint/meson.build | 2 +- meson.build | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libfprint/meson.build b/libfprint/meson.build index 210e45cb..8132a1bf 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -293,7 +293,7 @@ if get_option('introspection') libfprint_public_headers, libfprint_sources, ], - nsversion : '2.0', + nsversion : '@0@.0'.format(soversion), namespace : 'FPrint', symbol_prefix : 'fp_', identifier_prefix : 'Fp', diff --git a/meson.build b/meson.build index 29bdff56..afd98db4 100644 --- a/meson.build +++ b/meson.build @@ -208,5 +208,5 @@ pkgconfig.generate( version: meson.project_version(), libraries: libfprint, subdirs: meson.project_name(), - filebase: meson.project_name() + '2', + filebase: meson.project_name() + '@0@'.format(soversion), ) From 806ad106735828dd3d20a7dde15386479eec0a0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 17 Dec 2019 05:14:54 +0100 Subject: [PATCH 157/237] meson: Add fp-image-device to public headers --- libfprint/meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/libfprint/meson.build b/libfprint/meson.build index 8132a1bf..382fe763 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -21,6 +21,7 @@ libfprint_private_sources = [ libfprint_public_headers = [ 'fp-context.h', 'fp-device.h', + 'fp-image-device.h', 'fp-image.h', 'fp-print.h', ] From 10945f85461138610ab6d4ceba7c6fabb2931a34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 16 Dec 2019 20:37:15 +0100 Subject: [PATCH 158/237] cleanup: Remove fp_internal.h and update drivers_api.h Remove the uneeded internal API, as we can now include each header directly if needed, while move the assembling stuff to the drivers API. --- doc/meson.build | 1 - libfprint/drivers/aeslib.c | 4 +--- libfprint/drivers_api.h | 12 ++++++------ libfprint/fp_internal.h | 25 ------------------------- libfprint/fpi-assembling.c | 3 ++- 5 files changed, 9 insertions(+), 36 deletions(-) delete mode 100644 libfprint/fp_internal.h diff --git a/doc/meson.build b/doc/meson.build index 2c7a3844..e138ea29 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -4,7 +4,6 @@ private_headers = [ 'config.h', 'nbis-helpers.h', 'fprint.h', - 'fp_internal.h', # Subdirectories to ignore 'drivers', diff --git a/libfprint/drivers/aeslib.c b/libfprint/drivers/aeslib.c index 4839c62f..de56c6b5 100644 --- a/libfprint/drivers/aeslib.c +++ b/libfprint/drivers/aeslib.c @@ -19,13 +19,11 @@ #define FP_COMPONENT "aeslib" -#include "fp_internal.h" +#include "drivers_api.h" #include #include -#include "fpi-usb-transfer.h" -#include "fpi-assembling.h" #include "aeslib.h" #define MAX_REGWRITES_PER_REQUEST 16 diff --git a/libfprint/drivers_api.h b/libfprint/drivers_api.h index bb401cd3..e8ed9004 100644 --- a/libfprint/drivers_api.h +++ b/libfprint/drivers_api.h @@ -2,6 +2,7 @@ * Driver API definitions * Copyright (C) 2007-2008 Daniel Drake * Copyright (C) 2018 Bastien Nocera + * Copyright (C) 2019 Marco Trevisan * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,14 +22,13 @@ #ifndef __DRIVERS_API_H__ #define __DRIVERS_API_H__ -#include - -#include "fp_internal.h" - +#include "fpi-assembling.h" +#include "fpi-device.h" +#include "fpi-image-device.h" +#include "fpi-image.h" #include "fpi-log.h" +#include "fpi-print.h" #include "fpi-usb-transfer.h" #include "fpi-ssm.h" -#include "fpi-assembling.h" -#include "fpi-image-device.h" #endif diff --git a/libfprint/fp_internal.h b/libfprint/fp_internal.h deleted file mode 100644 index 56ada183..00000000 --- a/libfprint/fp_internal.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Internal/private definitions for libfprint - * Copyright (C) 2019 Marco Trevisan - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#pragma once - -#include "fpi-log.h" -#include "fpi-image.h" -#include "fpi-image-device.h" -#include "fpi-minutiae.h" diff --git a/libfprint/fpi-assembling.c b/libfprint/fpi-assembling.c index fef08f04..2b55ee3d 100644 --- a/libfprint/fpi-assembling.c +++ b/libfprint/fpi-assembling.c @@ -21,7 +21,8 @@ #define FP_COMPONENT "assembling" -#include "fp_internal.h" +#include "fpi-log.h" +#include "fpi-image.h" #include From 2158c5e2d1372c786af55b77acf4052cade9320e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 16 Dec 2019 20:50:04 +0100 Subject: [PATCH 159/237] cleanup: Use #pragma once everywhere Remove legacy header guards, and use compiler newer features. --- examples/storage.h | 6 +----- examples/utilities.h | 5 +---- libfprint/drivers/aes1660.h | 5 +---- libfprint/drivers/aes2501.h | 5 +---- libfprint/drivers/aes2550.h | 5 +---- libfprint/drivers/aes2660.h | 5 +---- libfprint/drivers/aeslib.h | 5 +---- libfprint/drivers/elan.h | 5 +---- libfprint/drivers/synaptics/bmkt.h | 5 +---- libfprint/drivers/synaptics/bmkt_message.h | 6 +----- libfprint/drivers/synaptics/bmkt_response.h | 6 +----- libfprint/drivers/synaptics/sensor.h | 4 +--- libfprint/drivers/synaptics/synaptics.h | 5 +---- libfprint/drivers/upektc.h | 5 +---- libfprint/drivers/upektc_img.h | 5 +---- libfprint/drivers/vfs5011_proto.h | 5 +---- libfprint/drivers_api.h | 5 +---- libfprint/fpi-assembling.h | 5 +---- libfprint/fpi-byte-reader.h | 5 +---- libfprint/fpi-byte-utils.h | 5 +---- libfprint/fpi-byte-writer.h | 5 +---- libfprint/fpi-log.h | 5 +---- 22 files changed, 22 insertions(+), 90 deletions(-) diff --git a/examples/storage.h b/examples/storage.h index bcbd0090..6c6c2206 100644 --- a/examples/storage.h +++ b/examples/storage.h @@ -18,9 +18,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef __STORAGE_H -#define __STORAGE_H - +#pragma once int print_data_save (FpPrint *print, FpFinger finger); @@ -30,5 +28,3 @@ FpPrint * print_create_template (FpDevice *dev, FpFinger finger); gboolean print_image_save (FpPrint *print, const char *path); - -#endif /* __STORAGE_H */ diff --git a/examples/utilities.h b/examples/utilities.h index 7e436acf..7efad29b 100644 --- a/examples/utilities.h +++ b/examples/utilities.h @@ -18,11 +18,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef __UTILITIES_H -#define __UTILITIES_H +#pragma once FpDevice * discover_device (GPtrArray *devices); FpFinger finger_chooser (void); const char * finger_to_string (FpFinger finger); - -#endif /* __UTILITIES_H */ diff --git a/libfprint/drivers/aes1660.h b/libfprint/drivers/aes1660.h index 55a94e20..18e4e0c9 100644 --- a/libfprint/drivers/aes1660.h +++ b/libfprint/drivers/aes1660.h @@ -18,8 +18,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef __AES1660_H -#define __AES1660_H +#pragma once #define AES1660_FRAME_SIZE 0x244 @@ -1986,5 +1985,3 @@ static const unsigned char aes1660_start_imaging_cmd[] = { 0x55, 0x07, 0x00, 0x80, 0x42, 0x00, 0x7f, 0x00, 0x00, 0x14, 0x49, 0x03, 0x00, 0x20, 0x00, 0xc8 }; - -#endif diff --git a/libfprint/drivers/aes2501.h b/libfprint/drivers/aes2501.h index dc802ca8..171a6722 100644 --- a/libfprint/drivers/aes2501.h +++ b/libfprint/drivers/aes2501.h @@ -19,8 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef __AES2501_H -#define __AES2501_H +#pragma once enum aes2501_regs { AES2501_REG_CTRL1 = 0x80, @@ -172,5 +171,3 @@ enum aes2501_sensor_gain2 { #define AES2501_SUM_HIGH_THRESH 1000 #define AES2501_SUM_LOW_THRESH 700 - -#endif /* __AES2501_H */ diff --git a/libfprint/drivers/aes2550.h b/libfprint/drivers/aes2550.h index 8e4ca173..5f38660c 100644 --- a/libfprint/drivers/aes2550.h +++ b/libfprint/drivers/aes2550.h @@ -17,8 +17,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef __AES2550_H -#define __AES2550_H +#pragma once /* Registers bits */ @@ -110,5 +109,3 @@ enum aes2550_cmds { #define AES2550_HEARTBEAT_MAGIC 0xdb #define AES2550_EP_IN_BUF_SIZE 8192 - -#endif diff --git a/libfprint/drivers/aes2660.h b/libfprint/drivers/aes2660.h index d59f4be2..5427c80a 100644 --- a/libfprint/drivers/aes2660.h +++ b/libfprint/drivers/aes2660.h @@ -17,8 +17,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef __AES2660_H -#define __AES2660_H +#pragma once #define AES2660_FRAME_SIZE 0x354 @@ -1960,5 +1959,3 @@ static const unsigned char aes2660_start_imaging_cmd[] = { 0x55, 0x07, 0x00, 0x80, 0x42, 0x00, 0xbf, 0x00, 0x00, 0x18, 0x49, 0x03, 0x00, 0x20, 0x08, 0xc8 }; - -#endif diff --git a/libfprint/drivers/aeslib.h b/libfprint/drivers/aeslib.h index 389b3e56..d3ea370b 100644 --- a/libfprint/drivers/aeslib.h +++ b/libfprint/drivers/aeslib.h @@ -17,8 +17,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef __AESLIB_H__ -#define __AESLIB_H__ +#pragma once #include @@ -45,5 +44,3 @@ unsigned char aes_get_pixel (struct fpi_frame_asmbl_ctx *ctx, struct fpi_frame *frame, unsigned int x, unsigned int y); - -#endif diff --git a/libfprint/drivers/elan.h b/libfprint/drivers/elan.h index 169498a6..1fdd820d 100644 --- a/libfprint/drivers/elan.h +++ b/libfprint/drivers/elan.h @@ -18,8 +18,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef __ELAN_H -#define __ELAN_H +#pragma once #include @@ -224,5 +223,3 @@ static void elan_capture (FpDevice *dev); static void dev_change_state (FpImageDevice *dev, FpImageDeviceState state); - -#endif diff --git a/libfprint/drivers/synaptics/bmkt.h b/libfprint/drivers/synaptics/bmkt.h index 67c48f2e..1af6d292 100644 --- a/libfprint/drivers/synaptics/bmkt.h +++ b/libfprint/drivers/synaptics/bmkt.h @@ -17,8 +17,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef _BMKT_H_ -#define _BMKT_H_ +#pragma once /**< User ID maximum length allowed */ #define BMKT_MAX_USER_ID_LEN 100 @@ -228,5 +227,3 @@ typedef struct bmkt_user_id #ifdef __cplusplus } #endif - -#endif /* _BMKT_H_ */ diff --git a/libfprint/drivers/synaptics/bmkt_message.h b/libfprint/drivers/synaptics/bmkt_message.h index d41e3d2f..98d38e4e 100644 --- a/libfprint/drivers/synaptics/bmkt_message.h +++ b/libfprint/drivers/synaptics/bmkt_message.h @@ -16,10 +16,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#ifndef BMKT_MESSAGE_H_ -#define BMKT_MESSAGE_H_ - +#pragma once #define BMKT_MESSAGE_HEADER_ID 0xFE #define BMKT_MESSAGE_HEADER_LEN (4) @@ -90,4 +87,3 @@ int bmkt_parse_message_header (uint8_t *resp_buf, bmkt_msg_resp_t *msg_resp); int bmkt_parse_message_payload (bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp); -#endif /* BMKT_MESSAGE_H_ */ diff --git a/libfprint/drivers/synaptics/bmkt_response.h b/libfprint/drivers/synaptics/bmkt_response.h index cfd77033..f13ed947 100644 --- a/libfprint/drivers/synaptics/bmkt_response.h +++ b/libfprint/drivers/synaptics/bmkt_response.h @@ -17,9 +17,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#ifndef _BMKT_RESPONSE_H_ -#define _BMKT_RESPONSE_H_ +#pragma once #include "bmkt.h" @@ -485,5 +483,3 @@ typedef struct bmkt_response int complete; /**< Operation completion status 1: complete / 0: not completed */ bmkt_response_data_t response; /**< Operation specific response union */ } bmkt_response_t; - -#endif /* _BMKT_RESPONSE_H_ */ diff --git a/libfprint/drivers/synaptics/sensor.h b/libfprint/drivers/synaptics/sensor.h index 922b1dd6..32134fe3 100644 --- a/libfprint/drivers/synaptics/sensor.h +++ b/libfprint/drivers/synaptics/sensor.h @@ -16,8 +16,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef _SENSOR_H_ -#define _SENSOR_H_ +#pragma once #include "usb_transport.h" #define BMKT_MAX_PENDING_SESSIONS 2 @@ -84,4 +83,3 @@ int bmkt_sensor_handle_response (bmkt_sensor_t *sensor, bmkt_msg_resp_t *msg_resp); int bmkt_sensor_send_async_read_command (bmkt_sensor_t *sensor); -#endif /* _SENSOR_H_ */ diff --git a/libfprint/drivers/synaptics/synaptics.h b/libfprint/drivers/synaptics/synaptics.h index cce6be9c..37eb362e 100644 --- a/libfprint/drivers/synaptics/synaptics.h +++ b/libfprint/drivers/synaptics/synaptics.h @@ -16,8 +16,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef __synaptics_h__ -#define __synaptics_h__ +#pragma once #include "fpi-device.h" #include "fpi-ssm.h" @@ -126,5 +125,3 @@ struct _FpiDeviceSynaptics struct syna_enroll_resp_data enroll_resp_data; syna_state_t state; }; - -#endif //__synaptics_h__ diff --git a/libfprint/drivers/upektc.h b/libfprint/drivers/upektc.h index 7ea919aa..a1f9a231 100644 --- a/libfprint/drivers/upektc.h +++ b/libfprint/drivers/upektc.h @@ -19,8 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef __UPEKTC_H -#define __UPEKTC_H +#pragma once #define UPEKTC_CMD_LEN 0x40 #define IMAGE_WIDTH 208 @@ -1936,5 +1935,3 @@ static const unsigned char scan_cmd[0x40] = { 0x05, 0x90, 0xf6, 0x77, 0x84, 0xf5, 0x2f, 0x01, 0x05, 0x90, 0xf6, 0x00, 0xc8, 0x00, 0xec, 0x00 }; - -#endif diff --git a/libfprint/drivers/upektc_img.h b/libfprint/drivers/upektc_img.h index 9185aa8e..3052b652 100644 --- a/libfprint/drivers/upektc_img.h +++ b/libfprint/drivers/upektc_img.h @@ -17,8 +17,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef __UPEKTC_IMG_H -#define __UPEKTC_IMG_H +#pragma once static const unsigned char upek2020_init_1[] = { 'C', 'i', 'a', 'o', @@ -140,5 +139,3 @@ static const unsigned char upek2020_ack_frame[] = { 0x30, 0xac, 0x5b /* CRC */ }; - -#endif diff --git a/libfprint/drivers/vfs5011_proto.h b/libfprint/drivers/vfs5011_proto.h index f71a10f6..5b2f8f41 100644 --- a/libfprint/drivers/vfs5011_proto.h +++ b/libfprint/drivers/vfs5011_proto.h @@ -1,5 +1,4 @@ -#ifndef __VFS5011_PROTO_H -#define __VFS5011_PROTO_H +#pragma once #define VFS5011_LINE_SIZE 240 #define VFS5011_IMAGE_WIDTH 160 @@ -6182,5 +6181,3 @@ static unsigned char vfs5011_prepare_04[] = { /* 2903 B */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, }; - -#endif diff --git a/libfprint/drivers_api.h b/libfprint/drivers_api.h index e8ed9004..7476ba73 100644 --- a/libfprint/drivers_api.h +++ b/libfprint/drivers_api.h @@ -19,8 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef __DRIVERS_API_H__ -#define __DRIVERS_API_H__ +#pragma once #include "fpi-assembling.h" #include "fpi-device.h" @@ -30,5 +29,3 @@ #include "fpi-print.h" #include "fpi-usb-transfer.h" #include "fpi-ssm.h" - -#endif diff --git a/libfprint/fpi-assembling.h b/libfprint/fpi-assembling.h index 77e3c558..295e3159 100644 --- a/libfprint/fpi-assembling.h +++ b/libfprint/fpi-assembling.h @@ -17,8 +17,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef __FPI_ASSEMBLING_H__ -#define __FPI_ASSEMBLING_H__ +#pragma once #include @@ -116,5 +115,3 @@ struct fpi_line_asmbl_ctx FpImage *fpi_assemble_lines (struct fpi_line_asmbl_ctx *ctx, GSList *lines, size_t num_lines); - -#endif diff --git a/libfprint/fpi-byte-reader.h b/libfprint/fpi-byte-reader.h index 0a661c6c..4a14ec8d 100644 --- a/libfprint/fpi-byte-reader.h +++ b/libfprint/fpi-byte-reader.h @@ -19,8 +19,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __FPI_BYTE_READER_H__ -#define __FPI_BYTE_READER_H__ +#pragma once #include #include "fpi-byte-utils.h" @@ -676,5 +675,3 @@ fpi_byte_reader_skip_inline (FpiByteReader * reader, guint nbytes) #endif /* FPI_BYTE_READER_DISABLE_INLINES */ G_END_DECLS - -#endif /* __FPI_BYTE_READER_H__ */ diff --git a/libfprint/fpi-byte-utils.h b/libfprint/fpi-byte-utils.h index 8a991212..52acb20c 100644 --- a/libfprint/fpi-byte-utils.h +++ b/libfprint/fpi-byte-utils.h @@ -21,9 +21,7 @@ * Boston, MA 02110-1301, USA. */ - -#ifndef __FP_UTILS_H__ -#define __FP_UTILS_H__ +#pragma once #include @@ -485,4 +483,3 @@ FP_WRITE_DOUBLE_BE(guint8 *data, gdouble num) G_END_DECLS #endif /* __GTK_DOC_IGNORE__ */ -#endif /* __FP_UTILS_H__ */ diff --git a/libfprint/fpi-byte-writer.h b/libfprint/fpi-byte-writer.h index b15a9a1e..ccdaf0bf 100644 --- a/libfprint/fpi-byte-writer.h +++ b/libfprint/fpi-byte-writer.h @@ -18,8 +18,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __FPI_BYTE_WRITER_H__ -#define __FPI_BYTE_WRITER_H__ +#pragma once #include "fpi-byte-reader.h" #include @@ -409,5 +408,3 @@ fpi_byte_writer_fill_inline (FpiByteWriter * writer, guint8 value, guint size) #endif G_END_DECLS - -#endif /* __FPI_BYTE_WRITER_H__ */ diff --git a/libfprint/fpi-log.h b/libfprint/fpi-log.h index 8f2f6a15..da612049 100644 --- a/libfprint/fpi-log.h +++ b/libfprint/fpi-log.h @@ -17,8 +17,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef __FPI_LOG_H__ -#define __FPI_LOG_H__ +#pragma once /** * SECTION:fpi-log @@ -94,5 +93,3 @@ * Same as BUG_ON() but is always true. */ #define BUG() BUG_ON (1) - -#endif From c8e1269f61074aca1a976f578d40ee62013456a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 16 Dec 2019 22:45:00 +0100 Subject: [PATCH 160/237] cleanup: Use FPI prefix for all the internal enum types --- doc/libfprint-sections.txt | 6 +- doc/libfprint-sections.txt-new-manual | 4 +- libfprint/drivers/elan.c | 60 +++++++------- libfprint/drivers/elan.h | 4 +- libfprint/drivers/synaptics/synaptics.c | 4 +- libfprint/drivers/uru4000.c | 24 +++--- libfprint/fp-device-private.h | 2 +- libfprint/fp-device.c | 26 +++--- libfprint/fp-image-device-private.h | 16 ++-- libfprint/fp-image-device.c | 30 +++---- libfprint/fp-print-private.h | 2 +- libfprint/fp-print.c | 28 +++---- libfprint/fpi-device.c | 66 +++++++-------- libfprint/fpi-device.h | 46 +++++------ libfprint/fpi-image-device.c | 104 ++++++++++++------------ libfprint/fpi-image-device.h | 24 +++--- libfprint/fpi-print.c | 24 +++--- libfprint/fpi-print.h | 20 ++--- tests/test-device-fake.c | 20 ++--- tests/test-fpi-device.c | 20 ++--- 20 files changed, 264 insertions(+), 266 deletions(-) diff --git a/doc/libfprint-sections.txt b/doc/libfprint-sections.txt index 9e17f8ef..30a4e9bf 100644 --- a/doc/libfprint-sections.txt +++ b/doc/libfprint-sections.txt @@ -129,7 +129,7 @@ fpi_get_driver_types fpi-device FpDeviceClass FpTimeoutFunc -FpDeviceAction +FpiDeviceAction FpIdEntry fpi_device_get_usb_device fpi_device_get_virtual_env @@ -173,7 +173,7 @@ fpi_image_resize

fpi-image-device FpImageDevice -FpImageDeviceState +FpiImageDeviceState FpImageDeviceClass fpi_image_device_session_error fpi_image_device_open_complete @@ -197,7 +197,7 @@ BUG
fpi-print -FpPrintType +FpiPrintType FpiMatchResult fpi_print_add_print fpi_print_set_type diff --git a/doc/libfprint-sections.txt-new-manual b/doc/libfprint-sections.txt-new-manual index 857425b9..da850db0 100644 --- a/doc/libfprint-sections.txt-new-manual +++ b/doc/libfprint-sections.txt-new-manual @@ -90,7 +90,7 @@ fp_image_get_width Base class for image devices FpImageDevice FpImageDeviceClass -FpImageDeviceState +FpiImageDeviceState
@@ -114,5 +114,3 @@ FpUsbTransferCallback FP_USB_ENDPOINT_IN FP_USB_ENDPOINT_OUT
- - diff --git a/libfprint/drivers/elan.c b/libfprint/drivers/elan.c index 415aaefd..233e4a8a 100644 --- a/libfprint/drivers/elan.c +++ b/libfprint/drivers/elan.c @@ -73,18 +73,18 @@ struct _FpiDeviceElan /* end commands */ /* state */ - gboolean deactivating; - FpImageDeviceState dev_state; - FpImageDeviceState dev_state_next; - unsigned char *last_read; - unsigned char calib_atts_left; - unsigned char calib_status; - unsigned short *background; - unsigned char frame_width; - unsigned char frame_height; - unsigned char raw_frame_height; - int num_frames; - GSList *frames; + gboolean deactivating; + FpiImageDeviceState dev_state; + FpiImageDeviceState dev_state_next; + unsigned char *last_read; + unsigned char calib_atts_left; + unsigned char calib_status; + unsigned short *background; + unsigned char frame_width; + unsigned char frame_height; + unsigned char raw_frame_height; + int num_frames; + GSList *frames; /* end state */ }; G_DECLARE_FINAL_TYPE (FpiDeviceElan, fpi_device_elan, FPI, DEVICE_ELAN, @@ -481,7 +481,7 @@ stop_capture_complete (FpiSsm *ssm, FpDevice *_dev, GError *error) /* The device is inactive at this point. */ - self->dev_state = FP_IMAGE_DEVICE_STATE_INACTIVE; + self->dev_state = FPI_IMAGE_DEVICE_STATE_INACTIVE; if (self->deactivating) { @@ -538,7 +538,7 @@ capture_run_state (FpiSsm *ssm, FpDevice *dev) break; case CAPTURE_READ_DATA: - self->dev_state = FP_IMAGE_DEVICE_STATE_CAPTURE; + self->dev_state = FPI_IMAGE_DEVICE_STATE_CAPTURE; /* 0x55 - finger present * 0xff - device not calibrated (probably) */ @@ -773,7 +773,7 @@ calibrate_complete (FpiSsm *ssm, FpDevice *dev, GError *error) if (error) { - self->dev_state = FP_IMAGE_DEVICE_STATE_INACTIVE; + self->dev_state = FPI_IMAGE_DEVICE_STATE_INACTIVE; fpi_image_device_session_error (FP_IMAGE_DEVICE (dev), error); } else @@ -951,7 +951,7 @@ elan_change_state (FpImageDevice *idev) { FpDevice *dev = FP_DEVICE (idev); FpiDeviceElan *self = FPI_DEVICE_ELAN (dev); - FpImageDeviceState next_state = self->dev_state_next; + FpiImageDeviceState next_state = self->dev_state_next; if (self->dev_state == next_state) { @@ -965,18 +965,18 @@ elan_change_state (FpImageDevice *idev) switch (next_state) { - case FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON: + case FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON: /* activation completed or another enroll stage started */ - self->dev_state = FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON; + self->dev_state = FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON; elan_calibrate (dev); break; - case FP_IMAGE_DEVICE_STATE_CAPTURE: + case FPI_IMAGE_DEVICE_STATE_CAPTURE: /* not used */ break; - case FP_IMAGE_DEVICE_STATE_INACTIVE: - case FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF: + case FPI_IMAGE_DEVICE_STATE_INACTIVE: + case FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF: elan_stop_capture (dev); break; } @@ -991,7 +991,7 @@ elan_change_state_async (FpDevice *dev, } static void -dev_change_state (FpImageDevice *dev, FpImageDeviceState state) +dev_change_state (FpImageDevice *dev, FpiImageDeviceState state) { FpiDeviceElan *self = FPI_DEVICE_ELAN (dev); GSource *timeout; @@ -999,17 +999,17 @@ dev_change_state (FpImageDevice *dev, FpImageDeviceState state) G_DEBUG_HERE (); /* Inactive and await finger off are equivalent for the elan driver. */ - if (state == FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF) - state = FP_IMAGE_DEVICE_STATE_INACTIVE; + if (state == FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF) + state = FPI_IMAGE_DEVICE_STATE_INACTIVE; if (self->dev_state_next == state) fp_dbg ("change to state %d already queued", state); switch (state) { - case FP_IMAGE_DEVICE_STATE_INACTIVE: - case FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON: - case FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF: { + case FPI_IMAGE_DEVICE_STATE_INACTIVE: + case FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON: + case FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF: { char *name; /* schedule state change instead of calling it directly to allow all actions @@ -1026,7 +1026,7 @@ dev_change_state (FpImageDevice *dev, FpImageDeviceState state) break; } - case FP_IMAGE_DEVICE_STATE_CAPTURE: + case FPI_IMAGE_DEVICE_STATE_CAPTURE: /* TODO MAYBE: split capture ssm into smaller ssms and use this state */ self->dev_state = state; self->dev_state_next = state; @@ -1044,7 +1044,7 @@ dev_deactivate (FpImageDevice *dev) G_DEBUG_HERE (); - if (self->dev_state == FP_IMAGE_DEVICE_STATE_INACTIVE) + if (self->dev_state == FPI_IMAGE_DEVICE_STATE_INACTIVE) { /* The device is inactive already, complete the operation immediately. */ fpi_image_device_deactivate_complete (dev, NULL); @@ -1055,7 +1055,7 @@ dev_deactivate (FpImageDevice *dev) * need to signal back deactivation) and then ensure we will change * to the inactive state eventually. */ self->deactivating = TRUE; - dev_change_state (dev, FP_IMAGE_DEVICE_STATE_INACTIVE); + dev_change_state (dev, FPI_IMAGE_DEVICE_STATE_INACTIVE); } } diff --git a/libfprint/drivers/elan.h b/libfprint/drivers/elan.h index 1fdd820d..2b1c0894 100644 --- a/libfprint/drivers/elan.h +++ b/libfprint/drivers/elan.h @@ -221,5 +221,5 @@ static void elan_cmd_read (FpiSsm *ssm, static void elan_calibrate (FpDevice *dev); static void elan_capture (FpDevice *dev); -static void dev_change_state (FpImageDevice *dev, - FpImageDeviceState state); +static void dev_change_state (FpImageDevice *dev, + FpiImageDeviceState state); diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index 97d9d212..af4a2fd0 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -517,7 +517,7 @@ list_msg_cb (FpiDeviceSynaptics *self, get_enroll_templates_resp->templates[n].finger_id, uid); - fpi_print_set_type (print, FP_PRINT_RAW); + fpi_print_set_type (print, FPI_PRINT_RAW); fpi_print_set_device_stored (print, TRUE); g_object_set (print, "fp-data", data, NULL); g_object_set (print, "description", get_enroll_templates_resp->templates[n].user_id, NULL); @@ -856,7 +856,7 @@ enroll (FpDevice *device) finger, uid); - fpi_print_set_type (print, FP_PRINT_RAW); + fpi_print_set_type (print, FPI_PRINT_RAW); fpi_print_set_device_stored (print, TRUE); g_object_set (print, "fp-data", data, NULL); g_object_set (print, "description", user_id, NULL); diff --git a/libfprint/drivers/uru4000.c b/libfprint/drivers/uru4000.c index 5128a12c..b86c6c8b 100644 --- a/libfprint/drivers/uru4000.c +++ b/libfprint/drivers/uru4000.c @@ -122,7 +122,7 @@ struct _FpiDeviceUru4000 const struct uru4k_dev_profile *profile; uint8_t interface; - FpImageDeviceState activate_state; + FpiImageDeviceState activate_state; unsigned char last_reg_rd[16]; unsigned char last_hwstat; @@ -408,16 +408,16 @@ change_state_write_reg_cb (FpiUsbTransfer *transfer, } static void -dev_change_state (FpImageDevice *dev, FpImageDeviceState state) +dev_change_state (FpImageDevice *dev, FpiImageDeviceState state) { FpiDeviceUru4000 *self = FPI_DEVICE_URU4000 (dev); switch (state) { - case FP_IMAGE_DEVICE_STATE_INACTIVE: - case FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON: - case FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF: - case FP_IMAGE_DEVICE_STATE_CAPTURE: + case FPI_IMAGE_DEVICE_STATE_INACTIVE: + case FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON: + case FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF: + case FPI_IMAGE_DEVICE_STATE_CAPTURE: break; default: @@ -773,7 +773,7 @@ imaging_run_state (FpiSsm *ssm, FpDevice *_dev) fpimg->flags |= FPI_IMAGE_V_FLIPPED | FPI_IMAGE_H_FLIPPED; fpi_image_device_image_captured (dev, fpimg); - if (self->activate_state == FP_IMAGE_DEVICE_STATE_CAPTURE) + if (self->activate_state == FPI_IMAGE_DEVICE_STATE_CAPTURE) fpi_ssm_jump_to_state (ssm, IMAGING_CAPTURE); else fpi_ssm_mark_completed (ssm); @@ -1176,7 +1176,7 @@ deactivate_write_reg_cb (FpiUsbTransfer *transfer, FpDevice *dev, static void dev_deactivate (FpImageDevice *dev) { - dev_change_state (dev, FP_IMAGE_DEVICE_STATE_INACTIVE); + dev_change_state (dev, FPI_IMAGE_DEVICE_STATE_INACTIVE); } static void @@ -1187,7 +1187,7 @@ execute_state_change (FpImageDevice *dev) switch (self->activate_state) { - case FP_IMAGE_DEVICE_STATE_INACTIVE: + case FPI_IMAGE_DEVICE_STATE_INACTIVE: fp_dbg ("deactivating"); self->irq_cb = NULL; self->irq_cb_data = NULL; @@ -1195,7 +1195,7 @@ execute_state_change (FpImageDevice *dev) deactivate_write_reg_cb, NULL); break; - case FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON: + case FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON: fp_dbg ("wait finger on"); if (!IRQ_HANDLER_IS_RUNNING (self)) { @@ -1209,7 +1209,7 @@ execute_state_change (FpImageDevice *dev) change_state_write_reg_cb, NULL); break; - case FP_IMAGE_DEVICE_STATE_CAPTURE: + case FPI_IMAGE_DEVICE_STATE_CAPTURE: fp_dbg ("starting capture"); self->irq_cb = NULL; @@ -1229,7 +1229,7 @@ execute_state_change (FpImageDevice *dev) change_state_write_reg_cb, NULL); break; - case FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF: + case FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF: fp_dbg ("await finger off"); if (!IRQ_HANDLER_IS_RUNNING (self)) { diff --git a/libfprint/fp-device-private.h b/libfprint/fp-device-private.h index 65fb1cb1..1a350fe5 100644 --- a/libfprint/fp-device-private.h +++ b/libfprint/fp-device-private.h @@ -41,7 +41,7 @@ typedef struct GSList *sources; /* We always make sure that only one task is run at a time. */ - FpDeviceAction current_action; + FpiDeviceAction current_action; GTask *current_task; GAsyncReadyCallback current_user_cb; gulong current_cancellable_id; diff --git a/libfprint/fp-device.c b/libfprint/fp-device.c index 3ac3a1c3..8041a868 100644 --- a/libfprint/fp-device.c +++ b/libfprint/fp-device.c @@ -81,7 +81,7 @@ fp_device_cancel_in_idle_cb (gpointer user_data) FpDevicePrivate *priv = fp_device_get_instance_private (self); g_assert (cls->cancel); - g_assert (priv->current_action != FP_DEVICE_ACTION_NONE); + g_assert (priv->current_action != FPI_DEVICE_ACTION_NONE); g_debug ("Idle cancelling on ongoing operation!"); @@ -148,7 +148,7 @@ fp_device_finalize (GObject *object) FpDevice *self = (FpDevice *) object; FpDevicePrivate *priv = fp_device_get_instance_private (self); - g_assert (priv->current_action == FP_DEVICE_ACTION_NONE); + g_assert (priv->current_action == FPI_DEVICE_ACTION_NONE); g_assert (priv->current_task == NULL); if (priv->is_open) g_warning ("User destroyed open device! Not cleaning up properly!"); @@ -268,7 +268,7 @@ fp_device_async_initable_init_async (GAsyncInitable *initable, return; } - priv->current_action = FP_DEVICE_ACTION_PROBE; + priv->current_action = FPI_DEVICE_ACTION_PROBE; priv->current_task = g_steal_pointer (&task); maybe_cancel_on_cancelled (self, cancellable); @@ -584,7 +584,7 @@ fp_device_open (FpDevice *device, return; } - priv->current_action = FP_DEVICE_ACTION_OPEN; + priv->current_action = FPI_DEVICE_ACTION_OPEN; priv->current_task = g_steal_pointer (&task); maybe_cancel_on_cancelled (device, cancellable); @@ -648,7 +648,7 @@ fp_device_close (FpDevice *device, return; } - priv->current_action = FP_DEVICE_ACTION_CLOSE; + priv->current_action = FPI_DEVICE_ACTION_CLOSE; priv->current_task = g_steal_pointer (&task); maybe_cancel_on_cancelled (device, cancellable); @@ -709,7 +709,7 @@ fp_device_enroll (FpDevice *device, g_autoptr(GTask) task = NULL; FpDevicePrivate *priv = fp_device_get_instance_private (device); FpEnrollData *data; - FpPrintType print_type; + FpiPrintType print_type; task = g_task_new (device, cancellable, callback, user_data); if (g_task_return_error_if_cancelled (task)) @@ -738,7 +738,7 @@ fp_device_enroll (FpDevice *device, } g_object_get (template_print, "fp-type", &print_type, NULL); - if (print_type != FP_PRINT_UNDEFINED) + if (print_type != FPI_PRINT_UNDEFINED) { g_warning ("Passed print template must be newly created and blank!"); g_task_return_error (task, @@ -746,7 +746,7 @@ fp_device_enroll (FpDevice *device, return; } - priv->current_action = FP_DEVICE_ACTION_ENROLL; + priv->current_action = FPI_DEVICE_ACTION_ENROLL; priv->current_task = g_steal_pointer (&task); maybe_cancel_on_cancelled (device, cancellable); @@ -822,7 +822,7 @@ fp_device_verify (FpDevice *device, return; } - priv->current_action = FP_DEVICE_ACTION_VERIFY; + priv->current_action = FPI_DEVICE_ACTION_VERIFY; priv->current_task = g_steal_pointer (&task); maybe_cancel_on_cancelled (device, cancellable); @@ -915,7 +915,7 @@ fp_device_identify (FpDevice *device, return; } - priv->current_action = FP_DEVICE_ACTION_IDENTIFY; + priv->current_action = FPI_DEVICE_ACTION_IDENTIFY; priv->current_task = g_steal_pointer (&task); maybe_cancel_on_cancelled (device, cancellable); @@ -1008,7 +1008,7 @@ fp_device_capture (FpDevice *device, return; } - priv->current_action = FP_DEVICE_ACTION_CAPTURE; + priv->current_action = FPI_DEVICE_ACTION_CAPTURE; priv->current_task = g_steal_pointer (&task); maybe_cancel_on_cancelled (device, cancellable); @@ -1089,7 +1089,7 @@ fp_device_delete_print (FpDevice *device, return; } - priv->current_action = FP_DEVICE_ACTION_DELETE; + priv->current_action = FPI_DEVICE_ACTION_DELETE; priv->current_task = g_steal_pointer (&task); maybe_cancel_on_cancelled (device, cancellable); @@ -1159,7 +1159,7 @@ fp_device_list_prints (FpDevice *device, return; } - priv->current_action = FP_DEVICE_ACTION_LIST; + priv->current_action = FPI_DEVICE_ACTION_LIST; priv->current_task = g_steal_pointer (&task); maybe_cancel_on_cancelled (device, cancellable); diff --git a/libfprint/fp-image-device-private.h b/libfprint/fp-image-device-private.h index 01454fd0..07a03478 100644 --- a/libfprint/fp-image-device-private.h +++ b/libfprint/fp-image-device-private.h @@ -25,17 +25,17 @@ typedef struct { - FpImageDeviceState state; - gboolean active; - gboolean cancelling; + FpiImageDeviceState state; + gboolean active; + gboolean cancelling; - gboolean enroll_await_on_pending; - gint enroll_stage; + gboolean enroll_await_on_pending; + gint enroll_stage; - guint pending_activation_timeout_id; - gboolean pending_activation_timeout_waiting_finger_off; + guint pending_activation_timeout_id; + gboolean pending_activation_timeout_waiting_finger_off; - gint bz3_threshold; + gint bz3_threshold; } FpImageDevicePrivate; diff --git a/libfprint/fp-image-device.c b/libfprint/fp-image-device.c index 24d324df..9e6c375f 100644 --- a/libfprint/fp-image-device.c +++ b/libfprint/fp-image-device.c @@ -106,7 +106,7 @@ fp_image_device_close (FpDevice *device) if (!priv->active) cls->img_close (self); - else if (priv->state != FP_IMAGE_DEVICE_STATE_INACTIVE) + else if (priv->state != FPI_IMAGE_DEVICE_STATE_INACTIVE) fpi_image_device_deactivate (self); } @@ -115,16 +115,16 @@ fp_image_device_cancel_action (FpDevice *device) { FpImageDevice *self = FP_IMAGE_DEVICE (device); FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); - FpDeviceAction action; + FpiDeviceAction action; action = fpi_device_get_current_action (device); /* We can only cancel capture operations, in that case, deactivate and return * an error immediately. */ - if (action == FP_DEVICE_ACTION_ENROLL || - action == FP_DEVICE_ACTION_VERIFY || - action == FP_DEVICE_ACTION_IDENTIFY || - action == FP_DEVICE_ACTION_CAPTURE) + if (action == FPI_DEVICE_ACTION_ENROLL || + action == FPI_DEVICE_ACTION_VERIFY || + action == FPI_DEVICE_ACTION_IDENTIFY || + action == FPI_DEVICE_ACTION_CAPTURE) { priv->cancelling = TRUE; fpi_image_device_deactivate (self); @@ -143,14 +143,14 @@ fp_image_device_start_capture_action (FpDevice *device) { FpImageDevice *self = FP_IMAGE_DEVICE (device); FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); - FpDeviceAction action; + FpiDeviceAction action; /* There is just one action that we cannot support out * of the box, which is a capture without first waiting * for a finger to be on the device. */ action = fpi_device_get_current_action (device); - if (action == FP_DEVICE_ACTION_CAPTURE) + if (action == FPI_DEVICE_ACTION_CAPTURE) { gboolean wait_for_finger; @@ -162,12 +162,12 @@ fp_image_device_start_capture_action (FpDevice *device) return; } } - else if (action == FP_DEVICE_ACTION_ENROLL) + else if (action == FPI_DEVICE_ACTION_ENROLL) { FpPrint *enroll_print = NULL; fpi_device_get_enroll_data (device, &enroll_print); - fpi_print_set_type (enroll_print, FP_PRINT_NBIS); + fpi_print_set_type (enroll_print, FPI_PRINT_NBIS); } priv->enroll_stage = 0; @@ -178,14 +178,14 @@ fp_image_device_start_capture_action (FpDevice *device) * error (which will usually say that the user should remove the * finger). */ - if (priv->state != FP_IMAGE_DEVICE_STATE_INACTIVE || priv->active) + if (priv->state != FPI_IMAGE_DEVICE_STATE_INACTIVE || priv->active) { g_debug ("Got a new request while the device was still active"); g_assert (priv->pending_activation_timeout_id == 0); priv->pending_activation_timeout_id = g_timeout_add (100, pending_activation_timeout, device); - if (priv->state == FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF) + if (priv->state == FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF) priv->pending_activation_timeout_waiting_finger_off = TRUE; else priv->pending_activation_timeout_waiting_finger_off = FALSE; @@ -271,8 +271,8 @@ fp_image_device_class_init (FpImageDeviceClass *klass) g_param_spec_enum ("fp-image-device-state", "Image Device State", "Private: The state of the image device", - FP_TYPE_IMAGE_DEVICE_STATE, - FP_IMAGE_DEVICE_STATE_INACTIVE, + FPI_TYPE_IMAGE_DEVICE_STATE, + FPI_IMAGE_DEVICE_STATE_INACTIVE, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); signals[FPI_STATE_CHANGED] = @@ -281,7 +281,7 @@ fp_image_device_class_init (FpImageDeviceClass *klass) G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (FpImageDeviceClass, change_state), NULL, NULL, NULL, - G_TYPE_NONE, 1, FP_TYPE_IMAGE_DEVICE_STATE); + G_TYPE_NONE, 1, FPI_TYPE_IMAGE_DEVICE_STATE); g_object_class_install_properties (object_class, N_PROPS, properties); } diff --git a/libfprint/fp-print-private.h b/libfprint/fp-print-private.h index f5822b3a..6d447005 100644 --- a/libfprint/fp-print-private.h +++ b/libfprint/fp-print-private.h @@ -27,7 +27,7 @@ struct _FpPrint { GInitiallyUnowned parent_instance; - FpPrintType type; + FpiPrintType type; gchar *driver; gchar *device_id; diff --git a/libfprint/fp-print.c b/libfprint/fp-print.c index 30fdf1a4..dd45b951 100644 --- a/libfprint/fp-print.c +++ b/libfprint/fp-print.c @@ -272,8 +272,8 @@ fp_print_class_init (FpPrintClass *klass) g_param_spec_enum ("fp-type", "Type", "Private: The type of the print data", - FP_TYPE_PRINT_TYPE, - FP_PRINT_RAW, + FPI_TYPE_PRINT_TYPE, + FPI_PRINT_RAW, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); properties[PROP_FPI_DATA] = @@ -555,8 +555,8 @@ fp_print_equal (FpPrint *self, FpPrint *other) { g_return_val_if_fail (FP_IS_PRINT (self), FALSE); g_return_val_if_fail (FP_IS_PRINT (other), FALSE); - g_return_val_if_fail (self->type != FP_PRINT_UNDEFINED, FALSE); - g_return_val_if_fail (other->type != FP_PRINT_UNDEFINED, FALSE); + g_return_val_if_fail (self->type != FPI_PRINT_UNDEFINED, FALSE); + g_return_val_if_fail (other->type != FPI_PRINT_UNDEFINED, FALSE); if (self->type != other->type) return FALSE; @@ -567,11 +567,11 @@ fp_print_equal (FpPrint *self, FpPrint *other) if (g_strcmp0 (self->device_id, other->device_id)) return FALSE; - if (self->type == FP_PRINT_RAW) + if (self->type == FPI_PRINT_RAW) { return g_variant_equal (self->data, other->data); } - else if (self->type == FP_PRINT_NBIS) + else if (self->type == FPI_PRINT_NBIS) { gint i; @@ -595,7 +595,7 @@ fp_print_equal (FpPrint *self, FpPrint *other) } } -#define FP_PRINT_VARIANT_TYPE G_VARIANT_TYPE ("(issbymsmsia{sv}v)") +#define FPI_PRINT_VARIANT_TYPE G_VARIANT_TYPE ("(issbymsmsia{sv}v)") G_STATIC_ASSERT (sizeof (((struct xyt_struct *) NULL)->xcol[0]) == 4); @@ -618,7 +618,7 @@ fp_print_serialize (FpPrint *print, GError **error) { g_autoptr(GVariant) result = NULL; - GVariantBuilder builder = G_VARIANT_BUILDER_INIT (FP_PRINT_VARIANT_TYPE); + GVariantBuilder builder = G_VARIANT_BUILDER_INIT (FPI_PRINT_VARIANT_TYPE); gsize len; g_assert (data); @@ -643,7 +643,7 @@ fp_print_serialize (FpPrint *print, g_variant_builder_close (&builder); /* Insert NBIS print data for type NBIS, otherwise the GVariant directly */ - if (print->type == FP_PRINT_NBIS) + if (print->type == FPI_PRINT_NBIS) { GVariantBuilder nested = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("(a(aiaiai))")); gint i; @@ -745,7 +745,7 @@ fp_print_deserialize (const guchar *data, g_autofree gchar *username = NULL; g_autofree gchar *description = NULL; gint julian_date; - FpPrintType type; + FpiPrintType type; const gchar *driver; const gchar *device_id; gboolean device_stored; @@ -766,7 +766,7 @@ fp_print_deserialize (const guchar *data, * longer. */ aligned_data = g_malloc (length - 3); memcpy (aligned_data, data + 3, length - 3); - raw_value = g_variant_new_from_data (FP_PRINT_VARIANT_TYPE, + raw_value = g_variant_new_from_data (FPI_PRINT_VARIANT_TYPE, aligned_data, length - 3, FALSE, g_free, aligned_data); @@ -794,7 +794,7 @@ fp_print_deserialize (const guchar *data, finger = finger_int8; /* Assume data is valid at this point if the values are somewhat sane. */ - if (type == FP_PRINT_NBIS) + if (type == FPI_PRINT_NBIS) { g_autoptr(GVariant) prints = g_variant_get_child_value (print_data, 0); gint i; @@ -804,7 +804,7 @@ fp_print_deserialize (const guchar *data, "device-id", device_id, "device-stored", device_stored, NULL); - fpi_print_set_type (result, FP_PRINT_NBIS); + fpi_print_set_type (result, FPI_PRINT_NBIS); for (i = 0; i < g_variant_n_children (prints); i++) { g_autofree struct xyt_struct *xyt = g_new0 (struct xyt_struct, 1); @@ -841,7 +841,7 @@ fp_print_deserialize (const guchar *data, g_ptr_array_add (result->prints, g_steal_pointer (&xyt)); } } - else if (type == FP_PRINT_RAW) + else if (type == FPI_PRINT_RAW) { g_autoptr(GVariant) fp_data = g_variant_get_child_value (print_data, 0); diff --git a/libfprint/fpi-device.c b/libfprint/fpi-device.c index 5fc6b760..51dbee17 100644 --- a/libfprint/fpi-device.c +++ b/libfprint/fpi-device.c @@ -350,21 +350,21 @@ fpi_device_get_virtual_env (FpDevice *device) * fpi_device_get_current_action: * @device: The #FpDevice * - * Get the currently ongoing action or %FP_DEVICE_ACTION_NONE if there + * Get the currently ongoing action or %FPI_DEVICE_ACTION_NONE if there * is no operation at this time. * * This is useful for drivers that might share code paths between different * actions (e.g. verify and identify) and want to find out again later which * action was started in the beginning. * - * Returns: The ongoing #FpDeviceAction + * Returns: The ongoing #FpiDeviceAction */ -FpDeviceAction +FpiDeviceAction fpi_device_get_current_action (FpDevice *device) { FpDevicePrivate *priv = fp_device_get_instance_private (device); - g_return_val_if_fail (FP_IS_DEVICE (device), FP_DEVICE_ACTION_NONE); + g_return_val_if_fail (FP_IS_DEVICE (device), FPI_DEVICE_ACTION_NONE); return priv->current_action; } @@ -387,7 +387,7 @@ fpi_device_action_is_cancelled (FpDevice *device) GCancellable *cancellable; g_return_val_if_fail (FP_IS_DEVICE (device), TRUE); - g_return_val_if_fail (priv->current_action != FP_DEVICE_ACTION_NONE, TRUE); + g_return_val_if_fail (priv->current_action != FPI_DEVICE_ACTION_NONE, TRUE); cancellable = g_task_get_cancellable (priv->current_task); @@ -435,7 +435,7 @@ fpi_device_get_enroll_data (FpDevice *device, FpEnrollData *data; g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_ENROLL); + g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_ENROLL); data = g_task_get_task_data (priv->current_task); g_assert (data); @@ -458,7 +458,7 @@ fpi_device_get_capture_data (FpDevice *device, FpDevicePrivate *priv = fp_device_get_instance_private (device); g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_CAPTURE); + g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_CAPTURE); if (wait_for_finger) *wait_for_finger = priv->wait_for_finger; @@ -478,7 +478,7 @@ fpi_device_get_verify_data (FpDevice *device, FpDevicePrivate *priv = fp_device_get_instance_private (device); g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_VERIFY); + g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_VERIFY); if (print) *print = g_task_get_task_data (priv->current_task); @@ -498,7 +498,7 @@ fpi_device_get_identify_data (FpDevice *device, FpDevicePrivate *priv = fp_device_get_instance_private (device); g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_IDENTIFY); + g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_IDENTIFY); if (prints) *prints = g_task_get_task_data (priv->current_task); @@ -518,7 +518,7 @@ fpi_device_get_delete_data (FpDevice *device, FpDevicePrivate *priv = fp_device_get_instance_private (device); g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_DELETE); + g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_DELETE); if (print) *print = g_task_get_task_data (priv->current_task); @@ -542,7 +542,7 @@ fpi_device_get_cancellable (FpDevice *device) FpDevicePrivate *priv = fp_device_get_instance_private (device); g_return_val_if_fail (FP_IS_DEVICE (device), NULL); - g_return_val_if_fail (priv->current_action != FP_DEVICE_ACTION_NONE, NULL); + g_return_val_if_fail (priv->current_action != FPI_DEVICE_ACTION_NONE, NULL); return g_task_get_cancellable (priv->current_task); } @@ -564,7 +564,7 @@ fpi_device_action_error (FpDevice *device, FpDevicePrivate *priv = fp_device_get_instance_private (device); g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action != FP_DEVICE_ACTION_NONE); + g_return_if_fail (priv->current_action != FPI_DEVICE_ACTION_NONE); if (error != NULL) { @@ -579,44 +579,44 @@ fpi_device_action_error (FpDevice *device, switch (priv->current_action) { - case FP_DEVICE_ACTION_PROBE: + case FPI_DEVICE_ACTION_PROBE: fpi_device_probe_complete (device, NULL, NULL, error); break; - case FP_DEVICE_ACTION_OPEN: + case FPI_DEVICE_ACTION_OPEN: fpi_device_open_complete (device, error); break; - case FP_DEVICE_ACTION_CLOSE: + case FPI_DEVICE_ACTION_CLOSE: fpi_device_close_complete (device, error); break; - case FP_DEVICE_ACTION_ENROLL: + case FPI_DEVICE_ACTION_ENROLL: fpi_device_enroll_complete (device, NULL, error); break; - case FP_DEVICE_ACTION_VERIFY: + case FPI_DEVICE_ACTION_VERIFY: fpi_device_verify_complete (device, FPI_MATCH_ERROR, NULL, error); break; - case FP_DEVICE_ACTION_IDENTIFY: + case FPI_DEVICE_ACTION_IDENTIFY: fpi_device_identify_complete (device, NULL, NULL, error); break; - case FP_DEVICE_ACTION_CAPTURE: + case FPI_DEVICE_ACTION_CAPTURE: fpi_device_capture_complete (device, NULL, error); break; - case FP_DEVICE_ACTION_DELETE: + case FPI_DEVICE_ACTION_DELETE: fpi_device_delete_complete (device, error); break; - case FP_DEVICE_ACTION_LIST: + case FPI_DEVICE_ACTION_LIST: fpi_device_list_complete (device, NULL, error); break; default: - case FP_DEVICE_ACTION_NONE: + case FPI_DEVICE_ACTION_NONE: g_return_if_reached (); break; } @@ -663,7 +663,7 @@ fp_device_task_return_in_idle_cb (gpointer user_data) g_debug ("Completing action %d in idle!", priv->current_action); task = g_steal_pointer (&priv->current_task); - priv->current_action = FP_DEVICE_ACTION_NONE; + priv->current_action = FPI_DEVICE_ACTION_NONE; priv->current_task_idle_return_source = NULL; switch (data->type) @@ -746,7 +746,7 @@ fpi_device_probe_complete (FpDevice *device, FpDevicePrivate *priv = fp_device_get_instance_private (device); g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_PROBE); + g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_PROBE); g_debug ("Device reported probe completion"); @@ -788,7 +788,7 @@ fpi_device_open_complete (FpDevice *device, GError *error) FpDevicePrivate *priv = fp_device_get_instance_private (device); g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_OPEN); + g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_OPEN); g_debug ("Device reported open completion"); @@ -821,7 +821,7 @@ fpi_device_close_complete (FpDevice *device, GError *error) FpDevicePrivate *priv = fp_device_get_instance_private (device); g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_CLOSE); + g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_CLOSE); g_debug ("Device reported close completion"); @@ -873,7 +873,7 @@ fpi_device_enroll_complete (FpDevice *device, FpPrint *print, GError *error) FpDevicePrivate *priv = fp_device_get_instance_private (device); g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_ENROLL); + g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_ENROLL); g_debug ("Device reported enroll completion"); @@ -923,7 +923,7 @@ fpi_device_verify_complete (FpDevice *device, FpDevicePrivate *priv = fp_device_get_instance_private (device); g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_VERIFY); + g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_VERIFY); g_debug ("Device reported verify completion"); @@ -980,7 +980,7 @@ fpi_device_identify_complete (FpDevice *device, FpDevicePrivate *priv = fp_device_get_instance_private (device); g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_IDENTIFY); + g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_IDENTIFY); g_debug ("Device reported identify completion"); @@ -1027,7 +1027,7 @@ fpi_device_capture_complete (FpDevice *device, FpDevicePrivate *priv = fp_device_get_instance_private (device); g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_CAPTURE); + g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_CAPTURE); g_debug ("Device reported capture completion"); @@ -1072,7 +1072,7 @@ fpi_device_delete_complete (FpDevice *device, FpDevicePrivate *priv = fp_device_get_instance_private (device); g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_DELETE); + g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_DELETE); g_debug ("Device reported deletion completion"); @@ -1106,7 +1106,7 @@ fpi_device_list_complete (FpDevice *device, FpDevicePrivate *priv = fp_device_get_instance_private (device); g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_LIST); + g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_LIST); g_debug ("Device reported listing completion"); @@ -1150,7 +1150,7 @@ fpi_device_enroll_progress (FpDevice *device, FpEnrollData *data; g_return_if_fail (FP_IS_DEVICE (device)); - g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_ENROLL); + g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_ENROLL); g_return_if_fail (error == NULL || error->domain == FP_DEVICE_RETRY); g_debug ("Device reported enroll progress, reported %i of %i have been completed", completed_stages, priv->nr_enroll_stages); diff --git a/libfprint/fpi-device.h b/libfprint/fpi-device.h index 2333ae22..3d66ee58 100644 --- a/libfprint/fpi-device.h +++ b/libfprint/fpi-device.h @@ -142,39 +142,39 @@ typedef void (*FpTimeoutFunc) (FpDevice *device, gpointer user_data); /** - * FpDeviceAction: - * @FP_DEVICE_ACTION_NONE: No action is active. - * @FP_DEVICE_ACTION_PROBE: Probe device for support and information. - * @FP_DEVICE_ACTION_OPEN: Device is currently being opened. - * @FP_DEVICE_ACTION_CLOSE: Device is currently being closed. - * @FP_DEVICE_ACTION_ENROLL: Device is currently enrolling. - * @FP_DEVICE_ACTION_VERIFY: Device is currently verifying. - * @FP_DEVICE_ACTION_IDENTIFY: Device is currently identifying. - * @FP_DEVICE_ACTION_CAPTURE: Device is currently capturing an image. - * @FP_DEVICE_ACTION_LIST: Device stored prints are being queried. - * @FP_DEVICE_ACTION_DELETE: Device stored print is being deleted. + * FpiDeviceAction: + * @FPI_DEVICE_ACTION_NONE: No action is active. + * @FPI_DEVICE_ACTION_PROBE: Probe device for support and information. + * @FPI_DEVICE_ACTION_OPEN: Device is currently being opened. + * @FPI_DEVICE_ACTION_CLOSE: Device is currently being closed. + * @FPI_DEVICE_ACTION_ENROLL: Device is currently enrolling. + * @FPI_DEVICE_ACTION_VERIFY: Device is currently verifying. + * @FPI_DEVICE_ACTION_IDENTIFY: Device is currently identifying. + * @FPI_DEVICE_ACTION_CAPTURE: Device is currently capturing an image. + * @FPI_DEVICE_ACTION_LIST: Device stored prints are being queried. + * @FPI_DEVICE_ACTION_DELETE: Device stored print is being deleted. * * Current active action of the device. A driver can retrieve the action. */ typedef enum { - FP_DEVICE_ACTION_NONE = 0, - FP_DEVICE_ACTION_PROBE, - FP_DEVICE_ACTION_OPEN, - FP_DEVICE_ACTION_CLOSE, - FP_DEVICE_ACTION_ENROLL, - FP_DEVICE_ACTION_VERIFY, - FP_DEVICE_ACTION_IDENTIFY, - FP_DEVICE_ACTION_CAPTURE, - FP_DEVICE_ACTION_LIST, - FP_DEVICE_ACTION_DELETE, -} FpDeviceAction; + FPI_DEVICE_ACTION_NONE = 0, + FPI_DEVICE_ACTION_PROBE, + FPI_DEVICE_ACTION_OPEN, + FPI_DEVICE_ACTION_CLOSE, + FPI_DEVICE_ACTION_ENROLL, + FPI_DEVICE_ACTION_VERIFY, + FPI_DEVICE_ACTION_IDENTIFY, + FPI_DEVICE_ACTION_CAPTURE, + FPI_DEVICE_ACTION_LIST, + FPI_DEVICE_ACTION_DELETE, +} FpiDeviceAction; GUsbDevice *fpi_device_get_usb_device (FpDevice *device); const gchar *fpi_device_get_virtual_env (FpDevice *device); //const gchar *fpi_device_get_spi_dev (FpDevice *device); -FpDeviceAction fpi_device_get_current_action (FpDevice *device); +FpiDeviceAction fpi_device_get_current_action (FpDevice *device); gboolean fpi_device_action_is_cancelled (FpDevice *device); GError * fpi_device_retry_new (FpDeviceRetry error); diff --git a/libfprint/fpi-image-device.c b/libfprint/fpi-image-device.c index 6e5802ed..e03d60cb 100644 --- a/libfprint/fpi-image-device.c +++ b/libfprint/fpi-image-device.c @@ -54,7 +54,7 @@ fpi_image_device_activate (FpImageDevice *self) /* We don't have a neutral ACTIVE state, but we always will * go into WAIT_FINGER_ON afterwards. */ - priv->state = FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON; + priv->state = FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON; g_object_notify (G_OBJECT (self), "fp-image-device-state"); /* We might have been waiting for deactivation to finish before @@ -79,10 +79,10 @@ fpi_image_device_deactivate (FpImageDevice *self) fp_dbg ("Already deactivated, ignoring request."); return; } - if (!priv->cancelling && priv->state == FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON) + if (!priv->cancelling && priv->state == FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON) g_warning ("Deactivating image device while waiting for finger, this should not happen."); - priv->state = FP_IMAGE_DEVICE_STATE_INACTIVE; + priv->state = FPI_IMAGE_DEVICE_STATE_INACTIVE; g_object_notify (G_OBJECT (self), "fp-image-device-state"); fp_dbg ("Deactivating image device\n"); @@ -92,12 +92,12 @@ fpi_image_device_deactivate (FpImageDevice *self) /* Static helper functions */ static void -fp_image_device_change_state (FpImageDevice *self, FpImageDeviceState state) +fp_image_device_change_state (FpImageDevice *self, FpiImageDeviceState state) { FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); /* Cannot change to inactive using this function. */ - g_assert (state != FP_IMAGE_DEVICE_STATE_INACTIVE); + g_assert (state != FPI_IMAGE_DEVICE_STATE_INACTIVE); /* We might have been waiting for the finger to go OFF to start the * next operation. */ @@ -118,7 +118,7 @@ fp_image_device_enroll_maybe_await_finger_on (FpImageDevice *self) if (priv->enroll_await_on_pending) { priv->enroll_await_on_pending = FALSE; - fp_image_device_change_state (self, FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON); + fp_image_device_change_state (self, FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON); } else { @@ -135,7 +135,7 @@ fpi_image_device_minutiae_detected (GObject *source_object, GAsyncResult *res, g FpImageDevice *self = FP_IMAGE_DEVICE (user_data); FpDevice *device = FP_DEVICE (self); FpImageDevicePrivate *priv; - FpDeviceAction action; + FpiDeviceAction action; /* Note: We rely on the device to not disappear during an operation. */ @@ -159,7 +159,7 @@ fpi_image_device_minutiae_detected (GObject *source_object, GAsyncResult *res, g priv = fp_image_device_get_instance_private (FP_IMAGE_DEVICE (device)); action = fpi_device_get_current_action (device); - if (action == FP_DEVICE_ACTION_CAPTURE) + if (action == FPI_DEVICE_ACTION_CAPTURE) { fpi_device_capture_complete (device, g_steal_pointer (&image), error); fpi_image_device_deactivate (self); @@ -169,12 +169,12 @@ fpi_image_device_minutiae_detected (GObject *source_object, GAsyncResult *res, g if (!error) { print = fp_print_new (device); - fpi_print_set_type (print, FP_PRINT_NBIS); + fpi_print_set_type (print, FPI_PRINT_NBIS); if (!fpi_print_add_from_image (print, image, &error)) g_clear_object (&print); } - if (action == FP_DEVICE_ACTION_ENROLL) + if (action == FPI_DEVICE_ACTION_ENROLL) { FpPrint *enroll_print; fpi_device_get_enroll_data (device, &enroll_print); @@ -199,7 +199,7 @@ fpi_image_device_minutiae_detected (GObject *source_object, GAsyncResult *res, g fp_image_device_enroll_maybe_await_finger_on (FP_IMAGE_DEVICE (device)); } } - else if (action == FP_DEVICE_ACTION_VERIFY) + else if (action == FPI_DEVICE_ACTION_VERIFY) { FpPrint *template; FpiMatchResult result; @@ -213,7 +213,7 @@ fpi_image_device_minutiae_detected (GObject *source_object, GAsyncResult *res, g fpi_device_verify_complete (device, result, g_steal_pointer (&print), error); fpi_image_device_deactivate (self); } - else if (action == FP_DEVICE_ACTION_IDENTIFY) + else if (action == FPI_DEVICE_ACTION_IDENTIFY) { gint i; GPtrArray *templates; @@ -285,9 +285,9 @@ fpi_image_device_report_finger_status (FpImageDevice *self, { FpDevice *device = FP_DEVICE (self); FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); - FpDeviceAction action; + FpiDeviceAction action; - if (priv->state == FP_IMAGE_DEVICE_STATE_INACTIVE) + if (priv->state == FPI_IMAGE_DEVICE_STATE_INACTIVE) { /* Do we really want to always ignore such reports? We could * also track the state in case the user had the finger on @@ -300,16 +300,16 @@ fpi_image_device_report_finger_status (FpImageDevice *self, action = fpi_device_get_current_action (device); - g_assert (action != FP_DEVICE_ACTION_OPEN); - g_assert (action != FP_DEVICE_ACTION_CLOSE); + g_assert (action != FPI_DEVICE_ACTION_OPEN); + g_assert (action != FPI_DEVICE_ACTION_CLOSE); g_debug ("Image device reported finger status: %s", present ? "on" : "off"); - if (present && priv->state == FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON) + if (present && priv->state == FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON) { - fp_image_device_change_state (self, FP_IMAGE_DEVICE_STATE_CAPTURE); + fp_image_device_change_state (self, FPI_IMAGE_DEVICE_STATE_CAPTURE); } - else if (!present && priv->state == FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF) + else if (!present && priv->state == FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF) { /* We need to deactivate or continue to await finger */ @@ -324,7 +324,7 @@ fpi_image_device_report_finger_status (FpImageDevice *self, * minutiae detection to prevent deactivation (without cancellation) * from the AWAIT_FINGER_ON state. */ - if (action != FP_DEVICE_ACTION_ENROLL) + if (action != FPI_DEVICE_ACTION_ENROLL) fpi_image_device_deactivate (self); else fp_image_device_enroll_maybe_await_finger_on (self); @@ -349,18 +349,18 @@ void fpi_image_device_image_captured (FpImageDevice *self, FpImage *image) { FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); - FpDeviceAction action; + FpiDeviceAction action; action = fpi_device_get_current_action (FP_DEVICE (self)); g_return_if_fail (image != NULL); - g_return_if_fail (priv->state == FP_IMAGE_DEVICE_STATE_CAPTURE); - g_return_if_fail (action == FP_DEVICE_ACTION_ENROLL || - action == FP_DEVICE_ACTION_VERIFY || - action == FP_DEVICE_ACTION_IDENTIFY || - action == FP_DEVICE_ACTION_CAPTURE); + g_return_if_fail (priv->state == FPI_IMAGE_DEVICE_STATE_CAPTURE); + g_return_if_fail (action == FPI_DEVICE_ACTION_ENROLL || + action == FPI_DEVICE_ACTION_VERIFY || + action == FPI_DEVICE_ACTION_IDENTIFY || + action == FPI_DEVICE_ACTION_CAPTURE); - fp_image_device_change_state (self, FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF); + fp_image_device_change_state (self, FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF); g_debug ("Image device captured an image"); @@ -385,22 +385,22 @@ void fpi_image_device_retry_scan (FpImageDevice *self, FpDeviceRetry retry) { FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); - FpDeviceAction action; + FpiDeviceAction action; GError *error; action = fpi_device_get_current_action (FP_DEVICE (self)); /* We might be waiting for a finger at this point, so just accept * all but INACTIVE */ - g_return_if_fail (priv->state != FP_IMAGE_DEVICE_STATE_INACTIVE); - g_return_if_fail (action == FP_DEVICE_ACTION_ENROLL || - action == FP_DEVICE_ACTION_VERIFY || - action == FP_DEVICE_ACTION_IDENTIFY || - action == FP_DEVICE_ACTION_CAPTURE); + g_return_if_fail (priv->state != FPI_IMAGE_DEVICE_STATE_INACTIVE); + g_return_if_fail (action == FPI_DEVICE_ACTION_ENROLL || + action == FPI_DEVICE_ACTION_VERIFY || + action == FPI_DEVICE_ACTION_IDENTIFY || + action == FPI_DEVICE_ACTION_CAPTURE); error = fpi_device_retry_new (retry); - if (action == FP_DEVICE_ACTION_ENROLL) + if (action == FPI_DEVICE_ACTION_ENROLL) { g_debug ("Reporting retry during enroll"); fpi_device_enroll_progress (FP_DEVICE (self), priv->enroll_stage, NULL, error); @@ -438,17 +438,17 @@ fpi_image_device_session_error (FpImageDevice *self, GError *error) if (!priv->active) { - FpDeviceAction action = fpi_device_get_current_action (FP_DEVICE (self)); + FpiDeviceAction action = fpi_device_get_current_action (FP_DEVICE (self)); g_warning ("Driver reported session error, but device is inactive."); - if (action != FP_DEVICE_ACTION_NONE) + if (action != FPI_DEVICE_ACTION_NONE) { g_warning ("Translating to activation failure!"); fpi_image_device_activate_complete (self, error); return; } } - else if (priv->state == FP_IMAGE_DEVICE_STATE_INACTIVE) + else if (priv->state == FPI_IMAGE_DEVICE_STATE_INACTIVE) { g_warning ("Driver reported session error; translating to deactivation failure."); fpi_image_device_deactivate_complete (self, error); @@ -473,15 +473,15 @@ void fpi_image_device_activate_complete (FpImageDevice *self, GError *error) { FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); - FpDeviceAction action; + FpiDeviceAction action; action = fpi_device_get_current_action (FP_DEVICE (self)); g_return_if_fail (priv->active == FALSE); - g_return_if_fail (action == FP_DEVICE_ACTION_ENROLL || - action == FP_DEVICE_ACTION_VERIFY || - action == FP_DEVICE_ACTION_IDENTIFY || - action == FP_DEVICE_ACTION_CAPTURE); + g_return_if_fail (action == FPI_DEVICE_ACTION_ENROLL || + action == FPI_DEVICE_ACTION_VERIFY || + action == FPI_DEVICE_ACTION_IDENTIFY || + action == FPI_DEVICE_ACTION_CAPTURE); if (error) { @@ -496,7 +496,7 @@ fpi_image_device_activate_complete (FpImageDevice *self, GError *error) /* We always want to capture at this point, move to AWAIT_FINGER * state. */ - fp_image_device_change_state (self, FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON); + fp_image_device_change_state (self, FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON); } /** @@ -511,10 +511,10 @@ fpi_image_device_deactivate_complete (FpImageDevice *self, GError *error) { FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (self); - FpDeviceAction action; + FpiDeviceAction action; g_return_if_fail (priv->active == TRUE); - g_return_if_fail (priv->state == FP_IMAGE_DEVICE_STATE_INACTIVE); + g_return_if_fail (priv->state == FPI_IMAGE_DEVICE_STATE_INACTIVE); g_debug ("Image device deactivation completed"); @@ -527,7 +527,7 @@ fpi_image_device_deactivate_complete (FpImageDevice *self, GError *error) /* Special case, if we should be closing, but didn't due to a running * deactivation, then do so now. */ - if (action == FP_DEVICE_ACTION_CLOSE) + if (action == FPI_DEVICE_ACTION_CLOSE) { cls->img_close (self); return; @@ -553,16 +553,16 @@ void fpi_image_device_open_complete (FpImageDevice *self, GError *error) { FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); - FpDeviceAction action; + FpiDeviceAction action; action = fpi_device_get_current_action (FP_DEVICE (self)); g_return_if_fail (priv->active == FALSE); - g_return_if_fail (action == FP_DEVICE_ACTION_OPEN); + g_return_if_fail (action == FPI_DEVICE_ACTION_OPEN); g_debug ("Image device open completed"); - priv->state = FP_IMAGE_DEVICE_STATE_INACTIVE; + priv->state = FPI_IMAGE_DEVICE_STATE_INACTIVE; g_object_notify (G_OBJECT (self), "fp-image-device-state"); fpi_device_open_complete (FP_DEVICE (self), error); @@ -579,16 +579,16 @@ void fpi_image_device_close_complete (FpImageDevice *self, GError *error) { FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); - FpDeviceAction action; + FpiDeviceAction action; action = fpi_device_get_current_action (FP_DEVICE (self)); g_debug ("Image device close completed"); g_return_if_fail (priv->active == FALSE); - g_return_if_fail (action == FP_DEVICE_ACTION_CLOSE); + g_return_if_fail (action == FPI_DEVICE_ACTION_CLOSE); - priv->state = FP_IMAGE_DEVICE_STATE_INACTIVE; + priv->state = FPI_IMAGE_DEVICE_STATE_INACTIVE; g_object_notify (G_OBJECT (self), "fp-image-device-state"); fpi_device_close_complete (FP_DEVICE (self), error); diff --git a/libfprint/fpi-image-device.h b/libfprint/fpi-image-device.h index 06d1a647..155390de 100644 --- a/libfprint/fpi-image-device.h +++ b/libfprint/fpi-image-device.h @@ -23,11 +23,11 @@ #include "fp-image-device.h" /** - * FpImageDeviceState: - * @FP_IMAGE_DEVICE_STATE_INACTIVE: inactive - * @FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON: waiting for the finger to be pressed or swiped - * @FP_IMAGE_DEVICE_STATE_CAPTURE: capturing an image - * @FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF: waiting for the finger to be removed + * FpiImageDeviceState: + * @FPI_IMAGE_DEVICE_STATE_INACTIVE: inactive + * @FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON: waiting for the finger to be pressed or swiped + * @FPI_IMAGE_DEVICE_STATE_CAPTURE: capturing an image + * @FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF: waiting for the finger to be removed * * The state of an imaging device while doing a capture. The state is * passed through to the driver using the ::activate() or ::change_state() vfuncs. @@ -37,11 +37,11 @@ * unconditionally if the device supports raw capturing. */ typedef enum { - FP_IMAGE_DEVICE_STATE_INACTIVE, - FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON, - FP_IMAGE_DEVICE_STATE_CAPTURE, - FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF, -} FpImageDeviceState; + FPI_IMAGE_DEVICE_STATE_INACTIVE, + FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON, + FPI_IMAGE_DEVICE_STATE_CAPTURE, + FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF, +} FpiImageDeviceState; /** * FpImageDeviceClass: @@ -90,8 +90,8 @@ struct _FpImageDeviceClass void (*img_open) (FpImageDevice *dev); void (*img_close) (FpImageDevice *dev); void (*activate) (FpImageDevice *dev); - void (*change_state) (FpImageDevice *dev, - FpImageDeviceState state); + void (*change_state) (FpImageDevice *dev, + FpiImageDeviceState state); void (*deactivate) (FpImageDevice *dev); }; diff --git a/libfprint/fpi-print.c b/libfprint/fpi-print.c index a407dd9d..7a5e1e29 100644 --- a/libfprint/fpi-print.c +++ b/libfprint/fpi-print.c @@ -38,15 +38,15 @@ * @print: A #FpPrint * @add: Print to append to @print * - * Appends the single #FP_PRINT_NBIS print from @add to the collection of - * prints in @print. Both print objects need to be of type #FP_PRINT_NBIS + * Appends the single #FPI_PRINT_NBIS print from @add to the collection of + * prints in @print. Both print objects need to be of type #FPI_PRINT_NBIS * for this to work. */ void fpi_print_add_print (FpPrint *print, FpPrint *add) { - g_return_if_fail (print->type == FP_PRINT_NBIS); - g_return_if_fail (add->type == FP_PRINT_NBIS); + g_return_if_fail (print->type == FPI_PRINT_NBIS); + g_return_if_fail (add->type == FPI_PRINT_NBIS); g_assert (add->prints->len == 1); g_ptr_array_add (print->prints, g_memdup (add->prints->pdata[0], sizeof (struct xyt_struct))); @@ -62,15 +62,15 @@ fpi_print_add_print (FpPrint *print, FpPrint *add) * print passed during enrollment. */ void -fpi_print_set_type (FpPrint *print, - FpPrintType type) +fpi_print_set_type (FpPrint *print, + FpiPrintType type) { g_return_if_fail (FP_IS_PRINT (print)); /* We only allow setting this once! */ - g_return_if_fail (print->type == FP_PRINT_UNDEFINED); + g_return_if_fail (print->type == FPI_PRINT_UNDEFINED); print->type = type; - if (print->type == FP_PRINT_NBIS) + if (print->type == FPI_PRINT_NBIS) { g_assert_null (print->prints); print->prints = g_ptr_array_new_with_free_func (g_free); @@ -143,7 +143,7 @@ minutiae_to_xyt (struct fp_minutiae *minutiae, * @error: Return location for error * * Extracts the minutiae from the given image and adds it to @print of - * type #FP_PRINT_NBIS. + * type #FPI_PRINT_NBIS. * * The @image will be kept so that API users can get retrieve it e.g. * for debugging purposes. @@ -159,7 +159,7 @@ fpi_print_add_from_image (FpPrint *print, struct fp_minutiae _minutiae; struct xyt_struct *xyt; - if (print->type != FP_PRINT_NBIS || !image) + if (print->type != FPI_PRINT_NBIS || !image) { g_set_error (error, G_IO_ERROR, @@ -203,7 +203,7 @@ fpi_print_add_from_image (FpPrint *print, * Match the newly scanned @print (containing exactly one print) against the * prints contained in @template which will have been stored during enrollment. * - * Both @template and @print need to be of type #FP_PRINT_NBIS for this to + * Both @template and @print need to be of type #FPI_PRINT_NBIS for this to * work. * * Returns: Whether the prints match, @error will be set if #FPI_MATCH_ERROR is returned @@ -216,7 +216,7 @@ fpi_print_bz3_match (FpPrint *template, FpPrint *print, gint bz3_threshold, GErr gint i; /* XXX: Use a different error type? */ - if (template->type != FP_PRINT_NBIS || print->type != FP_PRINT_NBIS) + if (template->type != FPI_PRINT_NBIS || print->type != FPI_PRINT_NBIS) { *error = fpi_device_error_new_msg (FP_DEVICE_ERROR_NOT_SUPPORTED, "It is only possible to match NBIS type print data"); diff --git a/libfprint/fpi-print.h b/libfprint/fpi-print.h index 04500d68..c969f12d 100644 --- a/libfprint/fpi-print.h +++ b/libfprint/fpi-print.h @@ -7,16 +7,16 @@ G_BEGIN_DECLS /** - * FpPrintType: - * @FP_PRINT_UNDEFINED: Undefined type, this happens prior to enrollment - * @FP_PRINT_RAW: A raw print where the data is directly compared - * @FP_PRINT_NBIS: NBIS minutiae comparison + * FpiPrintType: + * @FPI_PRINT_UNDEFINED: Undefined type, this happens prior to enrollment + * @FPI_PRINT_RAW: A raw print where the data is directly compared + * @FPI_PRINT_NBIS: NBIS minutiae comparison */ typedef enum { - FP_PRINT_UNDEFINED = 0, - FP_PRINT_RAW, - FP_PRINT_NBIS, -} FpPrintType; + FPI_PRINT_UNDEFINED = 0, + FPI_PRINT_RAW, + FPI_PRINT_NBIS, +} FpiPrintType; /** * FpiMatchResult: @@ -33,8 +33,8 @@ typedef enum { void fpi_print_add_print (FpPrint *print, FpPrint *add); -void fpi_print_set_type (FpPrint *print, - FpPrintType type); +void fpi_print_set_type (FpPrint *print, + FpiPrintType type); void fpi_print_set_device_stored (FpPrint *print, gboolean device_stored); diff --git a/tests/test-device-fake.c b/tests/test-device-fake.c index e3b6f38c..096d140e 100644 --- a/tests/test-device-fake.c +++ b/tests/test-device-fake.c @@ -35,7 +35,7 @@ fpi_device_fake_probe (FpDevice *device) FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - g_assert_cmpuint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_PROBE); + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_PROBE); fake_dev->last_called_function = fpi_device_fake_probe; fpi_device_probe_complete (device, dev_class->id, dev_class->full_name, @@ -47,7 +47,7 @@ fpi_device_fake_open (FpDevice *device) { FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - g_assert_cmpuint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_OPEN); + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_OPEN); fake_dev->last_called_function = fpi_device_fake_open; fpi_device_open_complete (device, fake_dev->ret_error); @@ -58,7 +58,7 @@ fpi_device_fake_close (FpDevice *device) { FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - g_assert_cmpuint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_CLOSE); + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_CLOSE); fake_dev->last_called_function = fpi_device_fake_close; fpi_device_close_complete (device, fake_dev->ret_error); @@ -70,7 +70,7 @@ fpi_device_fake_enroll (FpDevice *device) FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); FpPrint *print = fake_dev->ret_print; - g_assert_cmpuint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_ENROLL); + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_ENROLL); fpi_device_get_enroll_data (device, (FpPrint **) &fake_dev->action_data); if (!print && !fake_dev->ret_error) @@ -86,7 +86,7 @@ fpi_device_fake_verify (FpDevice *device) FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); FpPrint *print = fake_dev->ret_print; - g_assert_cmpuint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_VERIFY); + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_VERIFY); fpi_device_get_verify_data (device, (FpPrint **) &fake_dev->action_data); if (!print && !fake_dev->ret_error) @@ -103,7 +103,7 @@ fpi_device_fake_identify (FpDevice *device) FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); FpPrint *match = fake_dev->ret_match; - g_assert_cmpuint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_IDENTIFY); + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_IDENTIFY); fpi_device_get_identify_data (device, (GPtrArray **) &fake_dev->action_data); if (!match && !fake_dev->ret_error) @@ -135,7 +135,7 @@ fpi_device_fake_capture (FpDevice *device) { FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - g_assert_cmpuint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_CAPTURE); + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_CAPTURE); fpi_device_get_capture_data (device, (gboolean *) &fake_dev->action_data); fake_dev->last_called_function = fpi_device_fake_capture; @@ -147,7 +147,7 @@ fpi_device_fake_list (FpDevice *device) { FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - g_assert_cmpuint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_LIST); + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_LIST); fake_dev->last_called_function = fpi_device_fake_list; fpi_device_list_complete (device, fake_dev->ret_list, fake_dev->ret_error); @@ -158,7 +158,7 @@ fpi_device_fake_delete (FpDevice *device) { FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - g_assert_cmpuint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_DELETE); + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_DELETE); fpi_device_get_delete_data (device, (gpointer) & fake_dev->action_data); fake_dev->last_called_function = fpi_device_fake_delete; @@ -170,7 +170,7 @@ fpi_device_fake_cancel (FpDevice *device) { FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - g_assert_cmpuint (fpi_device_get_current_action (device), !=, FP_DEVICE_ACTION_NONE); + g_assert_cmpuint (fpi_device_get_current_action (device), !=, FPI_DEVICE_ACTION_NONE); fake_dev->last_called_function = fpi_device_fake_cancel; } diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c index 165fc7f1..b269ec44 100644 --- a/tests/test-fpi-device.c +++ b/tests/test-fpi-device.c @@ -528,7 +528,7 @@ test_driver_enroll_progress (void) device = auto_close_fake_device_new (); g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, - "*assertion*current_action*FP_DEVICE_ACTION_ENROLL*failed"); + "*assertion*current_action*FPI_DEVICE_ACTION_ENROLL*failed"); fpi_device_enroll_progress (device, 0, NULL, NULL); g_test_assert_expected_messages (); @@ -989,7 +989,7 @@ test_driver_current_action (void) { g_autoptr(FpDevice) device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); - g_assert_cmpint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_NONE); + g_assert_cmpint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_NONE); } static void @@ -997,7 +997,7 @@ test_driver_current_action_open_vfunc (FpDevice *device) { FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - g_assert_cmpuint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_OPEN); + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_OPEN); fake_dev->last_called_function = test_driver_current_action_open_vfunc; fpi_device_open_complete (device, NULL); @@ -1015,7 +1015,7 @@ test_driver_current_action_open (void) fake_dev = FPI_DEVICE_FAKE (device); g_assert (fake_dev->last_called_function == test_driver_current_action_open_vfunc); - g_assert_cmpint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_NONE); + g_assert_cmpint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_NONE); } static void @@ -1023,7 +1023,7 @@ test_driver_action_get_cancellable_open_vfunc (FpDevice *device) { FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - g_assert_cmpuint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_OPEN); + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_OPEN); fake_dev->last_called_function = test_driver_action_get_cancellable_open_vfunc; g_assert_true (G_IS_CANCELLABLE (fpi_device_get_cancellable (device))); @@ -1054,7 +1054,7 @@ test_driver_action_get_cancellable_open_fail_vfunc (FpDevice *device) { FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - g_assert_cmpuint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_OPEN); + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_OPEN); fake_dev->last_called_function = test_driver_action_get_cancellable_open_fail_vfunc; g_assert_false (G_IS_CANCELLABLE (fpi_device_get_cancellable (device))); @@ -1084,7 +1084,7 @@ test_driver_action_get_cancellable_error (void) g_autoptr(FpDevice) device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, - "*assertion*current_action*FP_DEVICE_ACTION_NONE*failed"); + "*assertion*current_action*FPI_DEVICE_ACTION_NONE*failed"); g_assert_null (fpi_device_get_cancellable (device)); g_test_assert_expected_messages (); } @@ -1094,7 +1094,7 @@ test_driver_action_is_cancelled_open_vfunc (FpDevice *device) { FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - g_assert_cmpuint (fpi_device_get_current_action (device), ==, FP_DEVICE_ACTION_OPEN); + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_OPEN); fake_dev->last_called_function = test_driver_action_is_cancelled_open_vfunc; g_assert_true (G_IS_CANCELLABLE (fpi_device_get_cancellable (device))); @@ -1132,7 +1132,7 @@ test_driver_action_is_cancelled_error (void) g_autoptr(FpDevice) device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, - "*assertion*current_action*FP_DEVICE_ACTION_NONE*failed"); + "*assertion*current_action*FPI_DEVICE_ACTION_NONE*failed"); g_assert_true (fpi_device_action_is_cancelled (device)); g_test_assert_expected_messages (); } @@ -1194,7 +1194,7 @@ test_driver_action_error_error (void) g_autoptr(FpDevice) device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, - "*assertion*current_action*FP_DEVICE_ACTION_NONE*failed"); + "*assertion*current_action*FPI_DEVICE_ACTION_NONE*failed"); fpi_device_action_error (device, NULL); g_test_assert_expected_messages (); } From 0566f82219bfd30ee8761e9a1b93993a4eebecca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 17 Dec 2019 03:28:12 +0100 Subject: [PATCH 161/237] tests: Add a reference to the enrolled print before returning it --- tests/test-device-fake.c | 4 +++- tests/test-fpi-device.c | 8 +++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/test-device-fake.c b/tests/test-device-fake.c index 096d140e..eaa1fa63 100644 --- a/tests/test-device-fake.c +++ b/tests/test-device-fake.c @@ -77,7 +77,9 @@ fpi_device_fake_enroll (FpDevice *device) fpi_device_get_enroll_data (device, &print); fake_dev->last_called_function = fpi_device_fake_enroll; - fpi_device_enroll_complete (device, print, fake_dev->ret_error); + fpi_device_enroll_complete (device, + print ? g_object_ref (print) : NULL, + fake_dev->ret_error); } static void diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c index b269ec44..398407a9 100644 --- a/tests/test-fpi-device.c +++ b/tests/test-fpi-device.c @@ -393,9 +393,9 @@ test_driver_enroll (void) { g_autoptr(GError) error = NULL; g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); + g_autoptr(FpPrint) template_print = fp_print_new (device); FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - FpPrint *template_print = fp_print_new (device); FpPrint *out_print = NULL; out_print = @@ -520,6 +520,7 @@ test_driver_enroll_progress (void) { g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(FpPrint) enrolled_print = NULL; ExpectedEnrollData expected_enroll_data = {0}; FpiDeviceFake *fake_dev; @@ -535,8 +536,9 @@ test_driver_enroll_progress (void) fake_dev = FPI_DEVICE_FAKE (device); fake_dev->user_data = &expected_enroll_data; - fp_device_enroll_sync (device, fp_print_new (device), NULL, - test_driver_enroll_progress_callback, &expected_enroll_data, NULL); + enrolled_print = fp_device_enroll_sync (device, fp_print_new (device), NULL, + test_driver_enroll_progress_callback, + &expected_enroll_data, NULL); g_assert (fake_dev->last_called_function == test_driver_enroll_progress_vfunc); } From dbb26c5ade0c9d67261954e5339dbc3c5cce6133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 17 Dec 2019 07:02:16 +0100 Subject: [PATCH 162/237] meson: Define enum dependency and ensure we generate them before using Avoid setting the headers as sources everywhere, but instead use a dependency to manage the headers creation in time --- libfprint/meson.build | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/libfprint/meson.build b/libfprint/meson.build index 382fe763..d812cd9d 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -184,6 +184,10 @@ fpi_enums = gnome.mkenums_simple('fpi-enums', install_header : false) fpi_enums_h = fpi_enums[1] +enums_dep = declare_dependency( + sources: [ fp_enums_h, fpi_enums_h ] +) + drivers_sources += configure_file(input: 'empty_file', output: 'fpi-drivers.c', capture: true, @@ -193,6 +197,7 @@ drivers_sources += configure_file(input: 'empty_file', ]) deps = [ + enums_dep, gio_dep, glib_dep, gusb_dep, @@ -218,13 +223,16 @@ libnbis = static_library('nbis', install: false) libfprint_private = static_library('fprint-private', - sources: libfprint_private_sources + fpi_enums + [ fp_enums_h ], + sources: [ + fpi_enums, + libfprint_private_sources, + ], dependencies: deps, link_with: libnbis, install: false) libfprint_drivers = static_library('fprint-drivers', - sources: drivers_sources + [ fp_enums_h ], + sources: drivers_sources, c_args: drivers_cflags, dependencies: deps, link_with: libfprint_private, @@ -234,7 +242,11 @@ mapfile = files('libfprint.ver') vflag = '-Wl,--version-script,@0@/@1@'.format(meson.source_root(), mapfile[0]) libfprint = library('fprint', - sources: libfprint_sources + fp_enums + other_sources, + sources: [ + fp_enums, + libfprint_sources, + other_sources, + ], soversion: soversion, version: libversion, link_args : vflag, @@ -244,9 +256,9 @@ libfprint = library('fprint', install: true) libfprint_dep = declare_dependency(link_with: libfprint, - sources: [ fp_enums_h ], include_directories: root_inc, dependencies: [ + enums_dep, gio_dep, glib_dep, gusb_dep, From f3f768e73855f13c0f34303503ecd058db89e809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 16 Dec 2019 19:00:36 +0100 Subject: [PATCH 163/237] meson: Fix syntax for fpi_enums generation call --- libfprint/meson.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libfprint/meson.build b/libfprint/meson.build index d812cd9d..c4984e24 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -181,7 +181,8 @@ fp_enums_h = fp_enums[1] fpi_enums = gnome.mkenums_simple('fpi-enums', sources: libfprint_private_headers, - install_header : false) + install_header: false, +) fpi_enums_h = fpi_enums[1] enums_dep = declare_dependency( From bfc75de77897451e13c8d982f0b4b70d0e19bdc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 17 Dec 2019 15:39:24 +0100 Subject: [PATCH 164/237] libfprint: Make sure we install fp-enums.h in the right folder Since we were not explictly setting the install_dir, it was endind up in $PREFIX/include by default, while we use our project name as subfolder. --- libfprint/meson.build | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libfprint/meson.build b/libfprint/meson.build index c4984e24..50df8a0c 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -176,7 +176,9 @@ other_sources = [] fp_enums = gnome.mkenums_simple('fp-enums', sources: libfprint_public_headers, - install_header : true) + install_header: true, + install_dir: get_option('includedir') / meson.project_name(), +) fp_enums_h = fp_enums[1] fpi_enums = gnome.mkenums_simple('fpi-enums', From 022b4a75b1b9e84fc499e0fb757fc72c55b494d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 17 Dec 2019 20:44:37 +0100 Subject: [PATCH 165/237] meson: Bump dependency on 0.49.0 We're using some new features, and we may use more in future so better to bump the version to the minimum required than look back given we're still unstable. Fixes #204 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index afd98db4..c42cf2d6 100644 --- a/meson.build +++ b/meson.build @@ -6,7 +6,7 @@ project('libfprint', [ 'c', 'cpp' ], 'warning_level=1', 'c_std=c99', ], - meson_version: '>= 0.46.0') + meson_version: '>= 0.49.0') gnome = import('gnome') From 5b17eda0110d1b9a973626275d52a8298436df12 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Wed, 18 Dec 2019 12:03:42 +0100 Subject: [PATCH 166/237] Prefix internal properties/signals with fpi- and annotate them We prefixed them with fp- which is not as obvious as fpi-. Also, explicitly mark them as private and to be skipped in the GObject Introspection annotatinos. Warning: FPrint: (Signal)fp-image-device-state-changed: argument object: Unresolved type: 'FpiImageDeviceState' --- libfprint/drivers/synaptics/synaptics.c | 8 +++---- libfprint/drivers/upekts.c | 4 ++-- libfprint/fp-context.c | 8 +++---- libfprint/fp-device.c | 29 +++++++++++++++++++++---- libfprint/fp-image-device.c | 20 +++++++++++++++-- libfprint/fp-print.c | 22 +++++++++++++++---- libfprint/fpi-image-device.c | 12 +++++----- libfprint/fpi-print.c | 2 +- tests/test-fpi-device.c | 6 ++--- 9 files changed, 81 insertions(+), 30 deletions(-) diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index af4a2fd0..2aac75e8 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -519,7 +519,7 @@ list_msg_cb (FpiDeviceSynaptics *self, fpi_print_set_type (print, FPI_PRINT_RAW); fpi_print_set_device_stored (print, TRUE); - g_object_set (print, "fp-data", data, NULL); + g_object_set (print, "fpi-data", data, NULL); g_object_set (print, "description", get_enroll_templates_resp->templates[n].user_id, NULL); /* The format has 24 bytes at the start and some dashes in the right places */ @@ -670,7 +670,7 @@ verify (FpDevice *device) fpi_device_get_verify_data (device, &print); - g_object_get (print, "fp-data", &data, NULL); + g_object_get (print, "fpi-data", &data, NULL); g_debug ("data is %p", data); if (!parse_print_data (data, &finger, &user_id, &user_id_len)) { @@ -858,7 +858,7 @@ enroll (FpDevice *device) fpi_print_set_type (print, FPI_PRINT_RAW); fpi_print_set_device_stored (print, TRUE); - g_object_set (print, "fp-data", data, NULL); + g_object_set (print, "fpi-data", data, NULL); g_object_set (print, "description", user_id, NULL); g_debug ("user_id: %s, finger: %d", user_id, finger); @@ -927,7 +927,7 @@ delete_print (FpDevice *device) fpi_device_get_delete_data (device, &print); - g_object_get (print, "fp-data", &data, NULL); + g_object_get (print, "fpi-data", &data, NULL); g_debug ("data is %p", data); if (!parse_print_data (data, &finger, &user_id, &user_id_len)) { diff --git a/libfprint/drivers/upekts.c b/libfprint/drivers/upekts.c index 6ce8136f..16534d39 100644 --- a/libfprint/drivers/upekts.c +++ b/libfprint/drivers/upekts.c @@ -1126,7 +1126,7 @@ e_handle_resp02 (FpDevice *dev, unsigned char *data, data_len - sizeof (scan_comp), 1); - g_object_set (print, "fp-data", fp_data, NULL); + g_object_set (print, "fpi-data", fp_data, NULL); g_object_ref (print); } @@ -1293,7 +1293,7 @@ verify_start_sm_run_state (FpiSsm *ssm, FpDevice *dev) case VERIFY_INIT: fpi_device_get_verify_data (dev, &print); - g_object_get (dev, "fp-data", &fp_data, NULL); + g_object_get (dev, "fpi-data", &fp_data, NULL); data = g_variant_get_fixed_array (fp_data, &data_len, 1); diff --git a/libfprint/fp-context.c b/libfprint/fp-context.c index f64968df..0e7c17f7 100644 --- a/libfprint/fp-context.c +++ b/libfprint/fp-context.c @@ -170,8 +170,8 @@ usb_device_added_cb (FpContext *self, GUsbDevice *device, GUsbContext *usb_ctx) priv->cancellable, async_device_init_done_cb, self, - "fp-usb-device", device, - "fp-driver-data", found_entry->driver_data, + "fpi-usb-device", device, + "fpi-driver-data", found_entry->driver_data, NULL); } @@ -373,8 +373,8 @@ fp_context_enumerate (FpContext *context) priv->cancellable, async_device_init_done_cb, context, - "fp-environ", val, - "fp-driver-data", entry->driver_data, + "fpi-environ", val, + "fpi-driver-data", entry->driver_data, NULL); g_debug ("created"); } diff --git a/libfprint/fp-device.c b/libfprint/fp-device.c index 8041a868..116f9f87 100644 --- a/libfprint/fp-device.c +++ b/libfprint/fp-device.c @@ -342,22 +342,43 @@ fp_device_class_init (FpDeviceClass *klass) "Wether the device is open or not", FALSE, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); + /** + * FpDevice::fpi-environ: (skip) + * + * This property is only for internal purposes. + * + * Stability: private + */ properties[PROP_FPI_ENVIRON] = - g_param_spec_string ("fp-environ", + g_param_spec_string ("fpi-environ", "Virtual Environment", "Private: The environment variable for the virtual device", NULL, G_PARAM_STATIC_STRINGS | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); + /** + * FpDevice::fpi-usb-device: (skip) + * + * This property is only for internal purposes. + * + * Stability: private + */ properties[PROP_FPI_USB_DEVICE] = - g_param_spec_object ("fp-usb-device", + g_param_spec_object ("fpi-usb-device", "USB Device", "Private: The USB device for the device", G_USB_TYPE_DEVICE, G_PARAM_STATIC_STRINGS | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); + /** + * FpDevice::fpi-driver-data: (skip) + * + * This property is only for internal purposes. + * + * Stability: private + */ properties[PROP_FPI_DRIVER_DATA] = - g_param_spec_uint64 ("fp-driver-data", + g_param_spec_uint64 ("fpi-driver-data", "Driver Data", "Private: The driver data from the ID table entry", 0, @@ -737,7 +758,7 @@ fp_device_enroll (FpDevice *device, return; } - g_object_get (template_print, "fp-type", &print_type, NULL); + g_object_get (template_print, "fpi-type", &print_type, NULL); if (print_type != FPI_PRINT_UNDEFINED) { g_warning ("Passed print template must be newly created and blank!"); diff --git a/libfprint/fp-image-device.c b/libfprint/fp-image-device.c index 9e6c375f..20e181e5 100644 --- a/libfprint/fp-image-device.c +++ b/libfprint/fp-image-device.c @@ -267,16 +267,32 @@ fp_image_device_class_init (FpImageDeviceClass *klass) klass->activate = fp_image_device_default_activate; klass->deactivate = fp_image_device_default_deactivate; + /** + * FpImageDevice::fpi-image-device-state: (skip) + * + * This property is only for internal purposes. + * + * Stability: private + */ properties[PROP_FPI_STATE] = - g_param_spec_enum ("fp-image-device-state", + g_param_spec_enum ("fpi-image-device-state", "Image Device State", "Private: The state of the image device", FPI_TYPE_IMAGE_DEVICE_STATE, FPI_IMAGE_DEVICE_STATE_INACTIVE, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); + /** + * FpImageDevice::fpi-image-device-state-changed: (skip) + * @image_device: A #FpImageDevice + * @new_state: The new state of the device + * + * This signal is only for internal purposes. + * + * Stability: private + */ signals[FPI_STATE_CHANGED] = - g_signal_new ("fp-image-device-state-changed", + g_signal_new ("fpi-image-device-state-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (FpImageDeviceClass, change_state), diff --git a/libfprint/fp-print.c b/libfprint/fp-print.c index dd45b951..34139ce8 100644 --- a/libfprint/fp-print.c +++ b/libfprint/fp-print.c @@ -268,16 +268,30 @@ fp_print_class_init (FpPrintClass *klass) G_TYPE_DATE, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); + /** + * FpPrint::fpi-type: (skip) + * + * This property is only for internal purposes. + * + * Stability: private + */ properties[PROP_FPI_TYPE] = - g_param_spec_enum ("fp-type", + g_param_spec_enum ("fpi-type", "Type", "Private: The type of the print data", FPI_TYPE_PRINT_TYPE, FPI_PRINT_RAW, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); + /** + * FpPrint::fpi-data: (skip) + * + * This property is only for internal purposes. + * + * Stability: private + */ properties[PROP_FPI_DATA] = - g_param_spec_variant ("fp-data", + g_param_spec_variant ("fpi-data", "Raw Data", "The raw data for internal use only", G_VARIANT_TYPE_ANY, @@ -846,11 +860,11 @@ fp_print_deserialize (const guchar *data, g_autoptr(GVariant) fp_data = g_variant_get_child_value (print_data, 0); result = g_object_new (FP_TYPE_PRINT, - "fp-type", type, + "fpi-type", type, "driver", driver, "device-id", device_id, "device-stored", device_stored, - "fp-data", fp_data, + "fpi-data", fp_data, NULL); } else diff --git a/libfprint/fpi-image-device.c b/libfprint/fpi-image-device.c index e03d60cb..975e3a17 100644 --- a/libfprint/fpi-image-device.c +++ b/libfprint/fpi-image-device.c @@ -55,7 +55,7 @@ fpi_image_device_activate (FpImageDevice *self) /* We don't have a neutral ACTIVE state, but we always will * go into WAIT_FINGER_ON afterwards. */ priv->state = FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON; - g_object_notify (G_OBJECT (self), "fp-image-device-state"); + g_object_notify (G_OBJECT (self), "fpi-image-device-state"); /* We might have been waiting for deactivation to finish before * starting the next operation. */ @@ -83,7 +83,7 @@ fpi_image_device_deactivate (FpImageDevice *self) g_warning ("Deactivating image device while waiting for finger, this should not happen."); priv->state = FPI_IMAGE_DEVICE_STATE_INACTIVE; - g_object_notify (G_OBJECT (self), "fp-image-device-state"); + g_object_notify (G_OBJECT (self), "fpi-image-device-state"); fp_dbg ("Deactivating image device\n"); cls->deactivate (self); @@ -106,8 +106,8 @@ fp_image_device_change_state (FpImageDevice *self, FpiImageDeviceState state) fp_dbg ("Image device internal state change from %d to %d\n", priv->state, state); priv->state = state; - g_object_notify (G_OBJECT (self), "fp-image-device-state"); - g_signal_emit_by_name (self, "fp-image-device-state-changed", priv->state); + g_object_notify (G_OBJECT (self), "fpi-image-device-state"); + g_signal_emit_by_name (self, "fpi-image-device-state-changed", priv->state); } static void @@ -563,7 +563,7 @@ fpi_image_device_open_complete (FpImageDevice *self, GError *error) g_debug ("Image device open completed"); priv->state = FPI_IMAGE_DEVICE_STATE_INACTIVE; - g_object_notify (G_OBJECT (self), "fp-image-device-state"); + g_object_notify (G_OBJECT (self), "fpi-image-device-state"); fpi_device_open_complete (FP_DEVICE (self), error); } @@ -589,7 +589,7 @@ fpi_image_device_close_complete (FpImageDevice *self, GError *error) g_return_if_fail (action == FPI_DEVICE_ACTION_CLOSE); priv->state = FPI_IMAGE_DEVICE_STATE_INACTIVE; - g_object_notify (G_OBJECT (self), "fp-image-device-state"); + g_object_notify (G_OBJECT (self), "fpi-image-device-state"); fpi_device_close_complete (FP_DEVICE (self), error); } diff --git a/libfprint/fpi-print.c b/libfprint/fpi-print.c index 7a5e1e29..4e3ed034 100644 --- a/libfprint/fpi-print.c +++ b/libfprint/fpi-print.c @@ -75,7 +75,7 @@ fpi_print_set_type (FpPrint *print, g_assert_null (print->prints); print->prints = g_ptr_array_new_with_free_func (g_free); } - g_object_notify (G_OBJECT (print), "fp-type"); + g_object_notify (G_OBJECT (print), "fpi-type"); } /** diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c index 398407a9..3fa800c9 100644 --- a/tests/test-fpi-device.c +++ b/tests/test-fpi-device.c @@ -240,7 +240,7 @@ test_driver_get_usb_device (void) g_autoptr(FpDevice) device = NULL; dev_class->type = FP_DEVICE_TYPE_USB; - device = g_object_new (FPI_TYPE_DEVICE_FAKE, "fp-usb-device", NULL); + device = g_object_new (FPI_TYPE_DEVICE_FAKE, "fpi-usb-device", NULL); g_assert_null (fpi_device_get_usb_device (device)); g_clear_object (&device); @@ -259,7 +259,7 @@ test_driver_get_virtual_env (void) g_autoptr(FpDevice) device = NULL; dev_class->type = FP_DEVICE_TYPE_VIRTUAL; - device = g_object_new (FPI_TYPE_DEVICE_FAKE, "fp-environ", "TEST_VIRTUAL_ENV_GETTER", NULL); + device = g_object_new (FPI_TYPE_DEVICE_FAKE, "fpi-environ", "TEST_VIRTUAL_ENV_GETTER", NULL); g_assert_cmpstr (fpi_device_get_virtual_env (device), ==, "TEST_VIRTUAL_ENV_GETTER"); g_clear_object (&device); @@ -278,7 +278,7 @@ test_driver_get_driver_data (void) guint64 driver_data; driver_data = g_random_int (); - device = g_object_new (FPI_TYPE_DEVICE_FAKE, "fp-driver-data", driver_data, NULL); + device = g_object_new (FPI_TYPE_DEVICE_FAKE, "fpi-driver-data", driver_data, NULL); g_assert_cmpuint (fpi_device_get_driver_data (device), ==, driver_data); } From c5aedc9970eaaa65626f761dd07d42146eac7883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 17 Dec 2019 18:15:12 +0100 Subject: [PATCH 167/237] fp-print: Add aliases for First and Last finger in our order We might want to iterate through the supported fingers values, to do that we were hardcoding FP_FINGER_LEFT_THUMB and FP_FINGER_RIGHT_LITTLE as first and last fingers. Not that we'd ever get more fingers (unless some weird radiation would do the job), but it's logically nicer to read than random hardcoded values. --- libfprint/fp-print.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libfprint/fp-print.h b/libfprint/fp-print.h index fcb9532e..94034ce1 100644 --- a/libfprint/fp-print.h +++ b/libfprint/fp-print.h @@ -43,6 +43,8 @@ G_DECLARE_FINAL_TYPE (FpPrint, fp_print, FP, PRINT, GInitiallyUnowned) * @FP_FINGER_RIGHT_MIDDLE: Right middle finger * @FP_FINGER_RIGHT_RING: Right ring finger * @FP_FINGER_RIGHT_LITTLE: Right little finger + * @FP_FINGER_FIRST: The first finger in the fp-print order + * @FP_FINGER_LAST: The last finger in the fp-print order */ typedef enum { FP_FINGER_UNKNOWN = 0, @@ -56,6 +58,9 @@ typedef enum { FP_FINGER_RIGHT_MIDDLE, FP_FINGER_RIGHT_RING, FP_FINGER_RIGHT_LITTLE, + + FP_FINGER_FIRST = FP_FINGER_LEFT_THUMB, + FP_FINGER_LAST = FP_FINGER_RIGHT_LITTLE, } FpFinger; FpPrint *fp_print_new (FpDevice *device); From c9e1a7f283e6dceebe3bc6c0a0b0e259d50ca0a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 17 Dec 2019 18:15:37 +0100 Subject: [PATCH 168/237] examples: Iterate through fingers via first/last refs --- examples/utilities.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/examples/utilities.c b/examples/utilities.c index 379ad0ab..eb18600d 100644 --- a/examples/utilities.c +++ b/examples/utilities.c @@ -107,29 +107,19 @@ finger_to_string (FpFinger finger) FpFinger finger_chooser (void) { - int i; - const FpFinger all_fingers[] = { - FP_FINGER_LEFT_THUMB, - FP_FINGER_LEFT_INDEX, - FP_FINGER_LEFT_MIDDLE, - FP_FINGER_LEFT_RING, - FP_FINGER_LEFT_LITTLE, - FP_FINGER_RIGHT_THUMB, - FP_FINGER_RIGHT_INDEX, - FP_FINGER_RIGHT_MIDDLE, - FP_FINGER_RIGHT_RING, - FP_FINGER_RIGHT_LITTLE, - }; + int i = FP_FINGER_UNKNOWN; - for (i = all_fingers[0]; i <= G_N_ELEMENTS (all_fingers); ++i) - g_print (" [%d] %s\n", (i - all_fingers[0]), finger_to_string (i)); + for (i = FP_FINGER_FIRST; i <= FP_FINGER_LAST; ++i) + g_print (" [%d] %s\n", (i - FP_FINGER_FIRST), finger_to_string (i)); g_print ("> "); if (!scanf ("%d%*c", &i)) return FP_FINGER_UNKNOWN; - if (i < 0 || i >= G_N_ELEMENTS (all_fingers)) + i += FP_FINGER_FIRST; + + if (i < FP_FINGER_FIRST || i > FP_FINGER_LAST) return FP_FINGER_UNKNOWN; - return all_fingers[i]; + return i; } From b8e558452a97ac5cd026456b4e5a514d628b6747 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 19 Dec 2019 14:20:00 +0100 Subject: [PATCH 169/237] fp-print: Add FP_FINGER_IS_VALID This is coming directly from fprintd, but being something generic is better to have it insinde libfprint itself. --- libfprint/fp-print.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libfprint/fp-print.h b/libfprint/fp-print.h index 94034ce1..3408e731 100644 --- a/libfprint/fp-print.h +++ b/libfprint/fp-print.h @@ -28,6 +28,9 @@ G_BEGIN_DECLS #define FP_TYPE_PRINT (fp_print_get_type ()) G_DECLARE_FINAL_TYPE (FpPrint, fp_print, FP, PRINT, GInitiallyUnowned) +#define FP_FINGER_IS_VALID(finger) \ + ((finger) >= FP_FINGER_FIRST && (finger) <= FP_FINGER_LAST) + #include "fp-device.h" /** From d9bcf9b9cc0995150491d225719255d1671236a2 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Fri, 6 Dec 2019 18:54:49 +0100 Subject: [PATCH 170/237] fpi-assembling: Accept error of zero Rather than discarding a zero error, check that the constraints are sane. This way a perfect match is possible. --- libfprint/fpi-assembling.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libfprint/fpi-assembling.c b/libfprint/fpi-assembling.c index 2b55ee3d..a809a2dc 100644 --- a/libfprint/fpi-assembling.c +++ b/libfprint/fpi-assembling.c @@ -52,6 +52,9 @@ calc_error (struct fpi_frame_asmbl_ctx *ctx, width = ctx->frame_width - (dx > 0 ? dx : -dx); height = ctx->frame_height - dy; + if (height == 0 || width == 0) + return INT_MAX; + y1 = 0; y2 = dy; i = 0; @@ -86,9 +89,6 @@ calc_error (struct fpi_frame_asmbl_ctx *ctx, err *= (ctx->frame_height * ctx->frame_width); err /= (height * width); - if (err == 0) - return INT_MAX; - return err; } From 0d604fa34e3671cfc7991706dcbe27e60ec6d9d3 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Fri, 6 Dec 2019 18:55:52 +0100 Subject: [PATCH 171/237] fpi-assembling: Fix offsets to be relative to the previous frame The offset stored for a frame was not always relative to the previous frame. This was the case for reverse movement, but for forwrad movement the offset was the one to the next frame. Make the offset handling consistent and alwasy store the offset to the previous frame. Also update the frame assembling code to add the offset before blitting the frame (i.e. making it relative to the previous frame there too). Note that fpi_assemble_lines already made the assumption that this was the case as it forced the offset for the first frame to be zero. As such, the code was inconsistent. This could affect the AES drivers slightly as they use hardware reported values which might not adhere to these assumptions. --- libfprint/fpi-assembling.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/libfprint/fpi-assembling.c b/libfprint/fpi-assembling.c index a809a2dc..6d34679f 100644 --- a/libfprint/fpi-assembling.c +++ b/libfprint/fpi-assembling.c @@ -99,6 +99,8 @@ static void find_overlap (struct fpi_frame_asmbl_ctx *ctx, struct fpi_frame *first_frame, struct fpi_frame *second_frame, + int *dx_out, + int *dy_out, unsigned int *min_error) { int dx, dy; @@ -120,8 +122,8 @@ find_overlap (struct fpi_frame_asmbl_ctx *ctx, if (err < *min_error) { *min_error = err; - second_frame->delta_x = -dx; - second_frame->delta_y = dy; + *dx_out = -dx; + *dy_out = dy; } } } @@ -133,7 +135,7 @@ do_movement_estimation (struct fpi_frame_asmbl_ctx *ctx, { GSList *l; GTimer *timer; - guint num_frames = 0; + guint num_frames = 1; struct fpi_frame *prev_stripe; unsigned int min_error; /* Max error is width * height * 255, for AES2501 which has the largest @@ -143,20 +145,27 @@ do_movement_estimation (struct fpi_frame_asmbl_ctx *ctx, unsigned long long total_error = 0; timer = g_timer_new (); + + /* Skip the first frame */ prev_stripe = stripes->data; - for (l = stripes; l != NULL; l = l->next, num_frames++) + + for (l = stripes->next; l != NULL; l = l->next, num_frames++) { struct fpi_frame *cur_stripe = l->data; if (reverse) { - find_overlap (ctx, prev_stripe, cur_stripe, &min_error); + find_overlap (ctx, prev_stripe, cur_stripe, + &cur_stripe->delta_x, &cur_stripe->delta_y, + &min_error); cur_stripe->delta_y = -cur_stripe->delta_y; cur_stripe->delta_x = -cur_stripe->delta_x; } else { - find_overlap (ctx, cur_stripe, prev_stripe, &min_error); + find_overlap (ctx, cur_stripe, prev_stripe, + &cur_stripe->delta_x, &cur_stripe->delta_y, + &min_error); } total_error += min_error; @@ -328,19 +337,10 @@ fpi_assemble_frames (struct fpi_frame_asmbl_ctx *ctx, { fpi_frame = l->data; - if(reverse) - { - y += fpi_frame->delta_y; - x += fpi_frame->delta_x; - } + y += fpi_frame->delta_y; + x += fpi_frame->delta_x; aes_blit_stripe (ctx, img, fpi_frame, x, y); - - if(!reverse) - { - y += fpi_frame->delta_y; - x += fpi_frame->delta_x; - } } return img; From f91e5310bbc64c412c4c6ecd04cdfc1190e5e22e Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 9 Dec 2019 11:51:12 +0100 Subject: [PATCH 172/237] tests: Set MESON_SOURCE_ROOT to source root not build root --- tests/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/meson.build b/tests/meson.build index b4022a4e..8ab37913 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -4,7 +4,7 @@ envs.set('G_DEBUG', 'fatal-warnings') envs.set('G_MESSAGES_DEBUG', 'all') # Setup paths -envs.set('MESON_SOURCE_ROOT', meson.build_root()) +envs.set('MESON_SOURCE_ROOT', meson.source_root()) envs.prepend('LD_LIBRARY_PATH', join_paths(meson.build_root(), 'libfprint')) # Set FP_DEVICE_EMULATION so that drivers can adapt (e.g. to use fixed From 19f239ce617ffaf965871696167d0f767a0d6cea Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 9 Dec 2019 11:52:05 +0100 Subject: [PATCH 173/237] tests: Add some frame assembly unit tests --- meson.build | 3 + tests/meson.build | 35 +++++++++- tests/test-fpi-assembling.c | 135 ++++++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+), 2 deletions(-) create mode 100644 tests/test-fpi-assembling.c diff --git a/meson.build b/meson.build index c42cf2d6..96700f6b 100644 --- a/meson.build +++ b/meson.build @@ -82,6 +82,9 @@ gio_dep = dependency('gio-unix-2.0', version: '>=' + glib_min_version) gusb_dep = dependency('gusb', version: '>= 0.3.0') mathlib_dep = cc.find_library('m', required: false) +# The following dependencies are only used for tests +cairo_dep = dependency('cairo', required: false) + # Drivers drivers = get_option('drivers').split(',') virtual_drivers = [ 'virtual_image' ] diff --git a/tests/meson.build b/tests/meson.build index 8ab37913..29ff6afe 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -59,6 +59,7 @@ test_utils = static_library('fprint-test-utils', unit_tests = [ 'fpi-device', 'fpi-ssm', + 'fpi-assembling', ] if 'virtual_image' in drivers @@ -68,11 +69,41 @@ if 'virtual_image' in drivers ] endif +unit_tests_deps = { 'fpi-assembling' : [cairo_dep] } + +test_config = configuration_data() +test_config.set_quoted('SOURCE_ROOT', meson.source_root()) +test_config_h = configure_file(output: 'test-config.h', configuration: test_config) + foreach test_name: unit_tests + if unit_tests_deps.has_key(test_name) + missing_deps = false + foreach dep: unit_tests_deps[test_name] + if not dep.found() + missing_deps = true + break + endif + endforeach + + if missing_deps + # Create a dummy test that always skips instead + warning('Test @0@ cannot be compiled due to missing dependencies'.format(test_name)) + test(test_name, + find_program('sh'), + suite: ['unit-tests'], + args: ['-c', 'exit 77'], + ) + continue + endif + extra_deps = unit_tests_deps[test_name] + else + extra_deps = [] + endif + basename = 'test-' + test_name test_exe = executable(basename, - sources: basename + '.c', - dependencies: libfprint_private_dep, + sources: [basename + '.c', test_config_h], + dependencies: [ libfprint_private_dep ] + extra_deps, c_args: common_cflags, link_with: test_utils, ) diff --git a/tests/test-fpi-assembling.c b/tests/test-fpi-assembling.c new file mode 100644 index 00000000..c5b1bca9 --- /dev/null +++ b/tests/test-fpi-assembling.c @@ -0,0 +1,135 @@ +/* + * Example fingerprint device prints listing and deletion + * Enrolls your right index finger and saves the print to disk + * Copyright (C) 2019 Benjamin Berg + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "fpi-assembling.h" +#include "fpi-image.h" +#include "test-config.h" + +typedef struct +{ + struct fpi_frame frame; + cairo_surface_t *surf; + guchar *data; + guint stride; + guint width; + guint height; + guint x; + guint y; +} cairo_frame; + +static unsigned char +cairo_get_pixel (struct fpi_frame_asmbl_ctx *ctx, + struct fpi_frame *frame, + unsigned int x, + unsigned int y) +{ + cairo_frame *c_frame = (void *) frame; /* Indirect cast to avoid alignment warning. */ + + x = x + c_frame->x; + y = y + c_frame->y; + + g_assert (x < c_frame->width); + g_assert (y < c_frame->height); + + return c_frame->data[x * 4 + y * c_frame->stride + 1]; +} + +static void +test_frame_assembling (void) +{ + g_autofree char *path = NULL; + cairo_surface_t *img = NULL; + int width, height, stride, offset; + int test_height; + guchar *data; + struct fpi_frame_asmbl_ctx ctx = { 0, }; + + g_autoptr(FpImage) fp_img = NULL; + GSList *frames = NULL; + + g_assert_false (SOURCE_ROOT == NULL); + path = g_build_path (G_DIR_SEPARATOR_S, SOURCE_ROOT, "tests", "vfs5011", "capture.png", NULL); + + img = cairo_image_surface_create_from_png (path); + data = cairo_image_surface_get_data (img); + width = cairo_image_surface_get_width (img); + height = cairo_image_surface_get_height (img); + stride = cairo_image_surface_get_stride (img); + g_assert_cmpint (cairo_image_surface_get_format (img), ==, CAIRO_FORMAT_RGB24); + + ctx.get_pixel = cairo_get_pixel; + ctx.frame_width = width; + ctx.frame_height = 20; + ctx.image_width = width; + + offset = 10; + test_height = height - (height - ctx.frame_height) % offset; + + /* for now, fixed offset */ + for (int y = 0; y + ctx.frame_height < height; y += offset) + { + cairo_frame *frame = g_new0 (cairo_frame, 1); + + frame->surf = img; + frame->width = width; + frame->height = height; + frame->stride = stride; + frame->data = data; + frame->x = 0; + frame->y = y; + //frame->y = test_height - ctx.frame_height - y; + + frames = g_slist_append (frames, frame); + } + //offset = -offset; + + fpi_do_movement_estimation (&ctx, frames); + for (GSList *l = frames->next; l != NULL; l = l->next) + { + cairo_frame * frame = l->data; + + g_assert_cmpint (frame->frame.delta_x, ==, 0); + g_assert_cmpint (frame->frame.delta_y, ==, offset); + } + + fp_img = fpi_assemble_frames (&ctx, frames); + g_assert_cmpint (fp_img->height, ==, test_height); + + /* The FpImage and cairo surface need to be identical in the test area */ + for (int y = 0; y < test_height; y++) + for (int x = 0; x < width; x++) + g_assert_cmpint (data[x * 4 + y * stride + 1], ==, fp_img->data[x + y * width]); + + g_slist_free_full (frames, g_free); + cairo_surface_destroy (img); + g_assert (1); +} + +int +main (int argc, char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/assembling/frames", test_frame_assembling); + + return g_test_run (); +} From 36108f9f826a16f4dedda4175e92106975766f40 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 2 Jan 2020 18:38:19 +0100 Subject: [PATCH 174/237] examples: Fix possible use-after-free in storage code The variant may need the buffer, so we should only free the buffer together with the variant. --- examples/storage.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/examples/storage.c b/examples/storage.c index 6ca6efcb..bb693059 100644 --- a/examples/storage.c +++ b/examples/storage.c @@ -57,7 +57,7 @@ load_data (void) { GVariantDict *res; GVariant *var; - g_autofree gchar *contents = NULL; + gchar *contents = NULL; gsize length = 0; if (!g_file_get_contents (STORAGE_FILE, &contents, &length, NULL)) @@ -66,7 +66,12 @@ load_data (void) return g_variant_dict_new (NULL); } - var = g_variant_new_from_data (G_VARIANT_TYPE_VARDICT, contents, length, FALSE, NULL, NULL); + var = g_variant_new_from_data (G_VARIANT_TYPE_VARDICT, + contents, + length, + FALSE, + g_free, + contents); res = g_variant_dict_new (var); g_variant_unref (var); From 4db1b84c7a00b9e327252631b076e9057334521c Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 2 Jan 2020 18:39:57 +0100 Subject: [PATCH 175/237] examples: Do not free data returned by g_variant_get_fixed_array This data is owned by the variant, so do not free it. --- examples/storage.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/storage.c b/examples/storage.c index bb693059..14a6432d 100644 --- a/examples/storage.c +++ b/examples/storage.c @@ -134,7 +134,7 @@ print_data_load (FpDevice *dev, FpFinger finger) g_autoptr(GVariant) val = NULL; g_autoptr(GVariantDict) dict = NULL; - g_autofree guchar *stored_data = NULL; + const guchar *stored_data = NULL; gsize stored_len; dict = load_data (); @@ -145,7 +145,7 @@ print_data_load (FpDevice *dev, FpFinger finger) FpPrint *print; g_autoptr(GError) error = NULL; - stored_data = (guchar *) g_variant_get_fixed_array (val, &stored_len, 1); + stored_data = (const guchar *) g_variant_get_fixed_array (val, &stored_len, 1); print = fp_print_deserialize (stored_data, stored_len, &error); if (error) From 3c6ba0b6785c863a8ad97598968d185172c51bba Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 2 Jan 2020 18:42:18 +0100 Subject: [PATCH 176/237] storage: Do not free image data owned by FpPrint The data returned by fp_print_get_image is owned by the FpPrint and should not be free'ed. --- examples/storage.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/storage.c b/examples/storage.c index 14a6432d..0ab49468 100644 --- a/examples/storage.c +++ b/examples/storage.c @@ -218,7 +218,7 @@ save_image_to_pgm (FpImage *img, const char *path) gboolean print_image_save (FpPrint *print, const char *path) { - g_autoptr(FpImage) img = NULL; + FpImage *img = NULL; g_return_val_if_fail (FP_IS_PRINT (print), FALSE); g_return_val_if_fail (path != NULL, FALSE); From dcc7e6de902867bdab180003554f26c2f68b0a96 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 2 Jan 2020 18:43:16 +0100 Subject: [PATCH 177/237] examples: Save image even on match failure Move the image saving out, so that it is always done when an image is available. This allows viewing the image that corresponds to a match failure. --- examples/verify.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/verify.c b/examples/verify.c index 1249dce6..83d74ecb 100644 --- a/examples/verify.c +++ b/examples/verify.c @@ -75,13 +75,13 @@ on_verify_completed (FpDevice *dev, GAsyncResult *res, void *user_data) return; } + if (print && fp_device_supports_capture (dev) && + print_image_save (print, "verify.pgm")) + g_print ("Print image saved as verify.pgm\n"); + if (match) { g_print ("MATCH!\n"); - if (fp_device_supports_capture (dev) && - print_image_save (print, "verify.pgm")) - g_print ("Print image saved as verify.pgm"); - verify_data->ret_value = EXIT_SUCCESS; } else From 516c1593bb02f5f4218743fdcf0b681d8179e78b Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 2 Jan 2020 18:43:59 +0100 Subject: [PATCH 178/237] examples: Continue verification when return is pressed The message says [Y/n], but will not do Y by default. Fix this. --- examples/verify.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/verify.c b/examples/verify.c index 83d74ecb..d85ce4e9 100644 --- a/examples/verify.c +++ b/examples/verify.c @@ -92,7 +92,7 @@ on_verify_completed (FpDevice *dev, GAsyncResult *res, void *user_data) g_print ("Verify again? [Y/n]? "); if (fgets (buffer, sizeof (buffer), stdin) && - (buffer[0] == 'Y' || buffer[0] == 'y')) + (buffer[0] == 'Y' || buffer[0] == 'y' || buffer[0] == '\n')) { start_verification (dev, verify_data); return; From 87dee93633886864e82d72dc623d60ab428f56bb Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 2 Jan 2020 18:44:34 +0100 Subject: [PATCH 179/237] examples: Do not re-prompt the finger when repeating verification Let's assume the user will want to re-scan the same finger rather than being prompted again to change it. --- examples/verify.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/verify.c b/examples/verify.c index d85ce4e9..7fcc64cf 100644 --- a/examples/verify.c +++ b/examples/verify.c @@ -165,8 +165,11 @@ on_list_completed (FpDevice *dev, GAsyncResult *res, gpointer user_data) static void start_verification (FpDevice *dev, VerifyData *verify_data) { - g_print ("Choose the finger to verify:\n"); - verify_data->finger = finger_chooser (); + if (verify_data->finger == FP_FINGER_UNKNOWN) + { + g_print ("Choose the finger to verify:\n"); + verify_data->finger = finger_chooser (); + } if (verify_data->finger == FP_FINGER_UNKNOWN) { From 6c6df626c81623f40905351738425f2ce02a696a Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 2 Jan 2020 18:46:00 +0100 Subject: [PATCH 180/237] image-device: Fix reading default values from the class We cannot copy information from the class in the _init routine, instead, this needs to be done in _constructed. Move the relevant code into a new _constructed function to fix importing the bz3_threshold override from drivers. Fixes: #206 --- libfprint/fp-image-device.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/libfprint/fp-image-device.c b/libfprint/fp-image-device.c index 20e181e5..c4de7bbe 100644 --- a/libfprint/fp-image-device.c +++ b/libfprint/fp-image-device.c @@ -245,6 +245,23 @@ fp_image_device_get_property (GObject *object, } } +static void +fp_image_device_constructed (GObject *obj) +{ + FpImageDevice *self = FP_IMAGE_DEVICE (obj); + FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); + FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (self); + + /* Set default values. */ + fpi_device_set_nr_enroll_stages (FP_DEVICE (self), IMG_ENROLL_STAGES); + + priv->bz3_threshold = BOZORTH3_DEFAULT_THRESHOLD; + if (cls->bz3_threshold > 0) + priv->bz3_threshold = cls->bz3_threshold; + + G_OBJECT_CLASS (fp_image_device_parent_class)->constructed (obj); +} + static void fp_image_device_class_init (FpImageDeviceClass *klass) { @@ -253,6 +270,7 @@ fp_image_device_class_init (FpImageDeviceClass *klass) object_class->finalize = fp_image_device_finalize; object_class->get_property = fp_image_device_get_property; + object_class->constructed = fp_image_device_constructed; fp_device_class->open = fp_image_device_open; fp_device_class->close = fp_image_device_close; @@ -305,13 +323,4 @@ fp_image_device_class_init (FpImageDeviceClass *klass) static void fp_image_device_init (FpImageDevice *self) { - FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); - FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (self); - - /* Set default values. */ - fpi_device_set_nr_enroll_stages (FP_DEVICE (self), IMG_ENROLL_STAGES); - - priv->bz3_threshold = BOZORTH3_DEFAULT_THRESHOLD; - if (cls->bz3_threshold > 0) - priv->bz3_threshold = cls->bz3_threshold; } From 8992e559f812f9ca21cbf66fe0bcd2af12b00b14 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 2 Jan 2020 18:50:01 +0100 Subject: [PATCH 181/237] image-device: Fix enroll continuation after retry error Continuing an enroll was broken in case of a retry error. Explicitly add code to wait for the finger to go OFF after a retry error, and ensure that the enroll will continue once that has happened. --- libfprint/fpi-image-device.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libfprint/fpi-image-device.c b/libfprint/fpi-image-device.c index 975e3a17..efdbb532 100644 --- a/libfprint/fpi-image-device.c +++ b/libfprint/fpi-image-device.c @@ -404,6 +404,11 @@ fpi_image_device_retry_scan (FpImageDevice *self, FpDeviceRetry retry) { g_debug ("Reporting retry during enroll"); fpi_device_enroll_progress (FP_DEVICE (self), priv->enroll_stage, NULL, error); + + /* Wait for finger removal and re-touch. + * TODO: Do we need to check that the finger is already off? */ + priv->enroll_await_on_pending = TRUE; + fp_image_device_change_state (self, FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF); } else { From c27a4faeca3e023d62460a5e9958cb98d56ebd4b Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Fri, 3 Jan 2020 15:20:23 +0100 Subject: [PATCH 182/237] elan: Add umockdev based test Unfortunately, the timeout handling cannot be simulated properly. This also adds a workaround in the driver to not consider it a protocol error if this happens. --- libfprint/drivers/elan.c | 6 +- tests/elan/capture.ioctl | 90 +++++++++ tests/elan/capture.ioctl-recording | 47 +++++ tests/elan/capture.png | Bin 0 -> 47670 bytes tests/elan/device | 284 +++++++++++++++++++++++++++++ tests/meson.build | 1 + 6 files changed, 427 insertions(+), 1 deletion(-) create mode 100644 tests/elan/capture.ioctl create mode 100644 tests/elan/capture.ioctl-recording create mode 100644 tests/elan/capture.png create mode 100644 tests/elan/device diff --git a/libfprint/drivers/elan.c b/libfprint/drivers/elan.c index 233e4a8a..1c2a7a37 100644 --- a/libfprint/drivers/elan.c +++ b/libfprint/drivers/elan.c @@ -549,7 +549,11 @@ capture_run_state (FpiSsm *ssm, FpDevice *dev) } else { - fpi_ssm_mark_failed (ssm, fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); + /* XXX: The timeout is emulated incorrectly, resulting in a zero byte read. */ + if (g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") == 0) + fpi_ssm_mark_completed (ssm); + else + fpi_ssm_mark_failed (ssm, fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); } break; diff --git a/tests/elan/capture.ioctl b/tests/elan/capture.ioctl new file mode 100644 index 00000000..bb76bfd1 --- /dev/null +++ b/tests/elan/capture.ioctl @@ -0,0 +1,90 @@ +@DEV /dev/bus/usb/001/094 +USBDEVFS_GET_CAPABILITIES 0 FD000000 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 4019 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 2 2 0 0140 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 000C + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 4 4 0 40009000 + +# Callibration starts by grabbing the background once +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 0009 + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 771E7618FD18BB16711785165415F3156116D7150B15BD14DB144A142D160014DC133616D0148213471539148716D31470159D14EE154B15C71415158316E114A413F614D913A112DE12CE140414E414D313F215F9136F14C3142D147E130215A9151F15E7140713FC144414A0132616A313E716A01514154514F017B1182C2410227A19FD16D0189D18DF16F916A417C9171A16FF16FE152F15E615A8154B1542144615D314DC14FB143816BE16CB170215C1162915301693153E168716F7141F158116101590151414DB161F14C714BD16E8155F14DF14F214381723164D153C156415DF1597145A164B1674157D154315D9165A170017BB17F117531AC02391215D1ABD1971186417E4160B175A187616F918371821188216FF157F1660166A162B174C16131515151617421607162317631747164D165416F11221169B162A162F172E166E163815EB15B814EF144B15D01591140816F61585133916BA1504161B16921544146A1633167E1636151D15B3157F1875166117C7173E1B0C255E227B1D841AA6172F186717A3187117AD16BB16BD162A16B216D2158C17B91471160D161E16CA17B915DC153115B5166A165E1783160317AE15DD148813ED15B216FB168617EC14E114C01583141514D5153F160B168E16BD1595158816E2164C1726168616D31577159F14B714C71526163616D8169D1897169A18941A8A242323BD1B5518911782171C187D17CC17E7175017EC16151701166E15231748166815D3160016E815FE15C71520187F14C4155218A515B81254168515BF14031575161D1782163614E11560154214BA16E414FD165E159B150816761674152115E0166A162E17091578164B15FF1596153116F915D2169915D61602195319232318213D1B8418B719DF16CB18E1160C177218A81792172318F115FC158416F016D4166016F8155817CB15B81562163A17FA14BB16A21541157D15C6151F1525165F16EC16F815471409176914F3151415BE152F166B162E1681151516B315FE15F4163F17C21552165B16981607169E16A7159E1642186017D0169719BB1ADC230A21F2188F18061841177C17561855170B17281857177416DD160A1691174117A8176B16FC16FD15A317A317001657167C143315B815F415FD15641501170017DE153816CC152515D7145E15FF149C14671680173D156715D1157616AF141516D816F91446167D152816E7159D15211625178A15CD16FA15A8179D18FD1819249D21A61A10196C199F17F015AE1685172A173D181F16C016C717C7187C16811698172117561706186F17E915ED16CD18D4159C15371690159116011727168415D7159C166D1614156C15DF1620178E14E61779161215B614AB153814F716DB151E16C41500164616F4165016FF15E2169B1644165A16A5154818E318AF1A0D251920BB1A7A19D5172B18DC166E159D1765187717FA16B6166F160016C4164E15DF15CF163E172D171D17EB15431462173315BD16A314AF149014CE168E153515AC15C3162416AD15C614AD14A41417159D160C150C16441458182016AF15CD15291640152C17FE161D18EC15EA1414167A17E1154C160D168116D3180A1914256C22D8186217591751176516771718179018DD1624185C17CB152417D716301603171C16F316B216A216A616E2143C16AE14CB17D816F714E916BF14E9149915AF151517C915F41565145416EA15AD14381657160E166415391536164C15C4154919791650153E174A176C17A417841612178B17D2163317A8160619D519A626D220271C6619FA17A518EF164D172917A3170018E918F2176D179315E91653171A17B91600172117EA147C164815EC1680148314FB14E914F01519153C149214A9160216EB15FD154014E714F7155F14CB1577148A154E15921646164C1650174E171216EB15001773179E15A2142917A716181662166B15B117B318B4199B266721191AFC17041A3C17AF15A216101605182B187F17BC16E1150C160F162017781686162116CE183216A917E014471515158915FC15981580144214BF158D149B1499157D146715B8147214F41576155016D2144F1689166C16B8156115BB16CF16DD16D5170D16181826170516A31704177B160F167516A3161D17061B5A25A122271CA018FC176717C2160B171816BC170E176F1632186E16C91706171D188017E0167C15D217301605169C140B1887165816ED154315A91574165B15DE15BB143A169216F31403140016C0171E16F414AB16821539169017891794157117FB157516C61498158F16941630163D17821624168418A016A3178E18671B62237621411BDF18DF185E19B6165117F717A7184718B816BE160B163516C417AF151E173A179716A0165F16E1151516FC163117251772151B154F174115C314E013A2160616D9154B15BD15B01533177C15E7175F182E16BF158C15D216CC1669179417C5141F161315D916D31446146116E116CF17A21716171918B418A31A0F26E1206D1A9819EE17471953179E18C516271968160B184A18D1151A163A164517B2152818AE18801670150315221653175517D215D9143116BF1594146C150715F81663165216C815A3150E155E15191641167B1753179715E2159C1608157815E615E9166816D015471746167F15801688174E164218B615011769189419C3235D22681B8E185217C21782181A183C18A2169B173D181F177A152F178E173D18A616D817C516BB1658186818B115681630153416A416D915BC15DC154F15021507162D15AE153D1555140218081633166E18DA1435151815CA1551156F15EA151317EC149716E2159C175F18A9157017BC167E17CF17321896170A189E1A13249E20AA1B1A1AD8199617C6178A179717F6167B17F6180A183E177E1740179417A717CC166417B316DB173A16D8168D17A7162A177F1605152A169E162B16741620166617BB174016CA14F81390151D164B16F5155F1530163F185916B915FC1698168815CB1676156F16F21512162D1438182F17CD1760187F18EB18541B4525A121F21AE519EF162818AE18D117C817F3177C18E117E117641781170116981699165D176218AE1649166D17E61604175B157C1775173416AC165715B5166A16C2171117DB156E15CD1684169415BE146D1601186716C8144718E1151F150A15BE1715153516CA1446175B156618A3176318AE17C0169B17851850181C1AE926F61F571C2B1897182E181418C9188B175817DC16B5184916A216DC16F817A016D1163619EB164B19BE17B815771763187B15F0165816C516F81586150C176D1621172417CB16321490170F16B616031420178615EF15691502153B14ED145815DF15C716A6178D14CB16E5150E15CD1467160D16DC15F715CA184F18AD1A1C25C322131A6E17A817251777172317B8172D1613163E17AA176917651557185618B0174015E7174D18FF151016B9155816421743163B14B614AD18F81504173115DF16CD174E1637168F1596158A148E15AD15F2158E14E3148C16DE1465153F169C1747165E1683168A178514FB143E17F415FD154116EE1507171E19EF199B242122361BE719C718F0172718A71633178317FA176B19A6175F169F169C16F417F916DB197F17CA16B21639183017D8169617F61535169B15F7152F17D31652180B18C918A917441764150A154917A514E916C8164E165416C014FC169F15D9159A162515CF15DB159B1797168C168019CC16AA16811887151118B118BB198E244D21441A3D1AAD171117A717C01759175618EC17FD151F182B171A1885165B1786183A16BD18F9166F174D179417FC16C0155D16F4133215AF16D61508182C17E617DA1892171317A41612164715F4154017CA16D014BA15B9150016C7146715A9156816221543164717E6159E16A6155D150F17B715BE170918D017EB194B24D220CE190118BC18A6187B18E517131769187717351810188217F8154718C2163117C116FD16E11402158B15EE15E115121560154114A715A215B014F31601170616FE161317AF16B7157D18D216FF14901700176F162615DD15B615BF15941678166216E617E2159617341806172E16091640150B16C116F816AD17E618F8242F21A1190819851860183D168F175A18DC161216CA1606185A15B816E5189B16F616E0176F16DD161D162F167417BA17AC166C1544147716DA15F7144D162F168216A317C315B71383172F173A173517D317A616C8174016E716BD158E154217B514D6150516BF1510177D15AF166B175217AA157F16AC16FF171C17611A22251A220C1ABA171618A2189E170E172317D7183317E5181618EF16BB16D11557164716951615176619491679155E17FF16F315E2150B164115B014C9154A154F1608162B17FB16C916B216E815EC1730165C16C41697172F17601791156E15E6153B172C15E914F3157415F51599154B18C0177316F01670177C19EC191A1936244A21D21912180C196217AD17E9178A177617FE15FA163D172516C2163017FA160D18881501178316D816F4179415A81601168816AA16FD149116E815021744162917AE1601177116E51446164C16171517179716501574142F17BC14CB145B1600154B1572150414F316B21569160518DC17A215C1157A15A51775175B1AB1242222261B4018531792185418DC170818BB17ED16F317D017131620171617211709181D1897157417C216ED15DD15B0177616181647177C156B1657162E17B515241728175C1872155C171216BB16CF161F166D17051637156015761641155417A015BC140416E7156D1692156316EB16B7165916F5167417BF1627188119A724D221241C9F17D918CB17DF171E18C6182618EF169B18D816DF193D1797176F17D2168D17011759179C167916A9163B170417DF158514E1153615DB153416E9169A171218AA17C0151B178E15DE16F7155C16101620155415F914B41583148F150C177E15A3149C165C1643164E16F2155D163715E41639172C181718461B9A2510227D1AA0190B19E417A016B317D617B518B1173216B317E216AF18511731186518D416A217B016AA16FA16CD167E16BD15D516A4147414CE152F1615151717E31717185A180216491612166614F815D316DF15BA151E15A9165F14EF15BF16D71585152B14411632167416B616D817641674176F178D1608176319CA19A8241D23921B87160A17CF16C5187D17DA17DE180C178219B217D7177D168B172C18D418D2172418AB19B3164617F5155C189315F2154A162A158415A2150716CB162F1714179B17F61646169C16DF15A61654171D173718FD14C516E3153A1661160A172317E5167B160617771747165D1678153F16F817C41675175419CC1966258521C619FE17E2165E18C516D6163818651746191616A817FC159116CC15A1143D16A416971717175615FB1446171A17ED159D158416F8132D161D168D160316D815BD165D16E215B3154814EC155F15D6150D18C8144E155D16BE164215E1151F156C176B168F14FE17EC17CA1845179315DE1442168E16BA180619A21909245921111C46181319011946167916FF15E518D419591A92184716F516E218FB18C1161C175717AF179714A915391595147F157A1610164F159716031542174716A5182A178F174F1480152816E81511142D16CC162B15C3158616BE16D71675155016541605152F16A816E81570163916B21548171216EA1692165C19591A4224DF21251A1A18AB17E21799179018D9177717AE185D1890171318E0177D17D317BB170616A517781618161B164F1582145515AC167816E21698167C16331511171417F917E716BC1535148417DB15D415DB16961778162816A8167E16E916161553161F160816C515561873179D16D5158F169916DA1776168E17D61883198F24F7214A1B5D18AB189B176D18C21650173118B817E41648171F178C17F5174A172C16621705160216C1162615B7165215FD14E815A916F7138316DA169F15D815EB17E017FE152E1490163814B315C2146A17C6178B14131653169815E3154B150E15EB15021772174E17ED148714F01440175B1784155616FF177918EF18B3240122481AA417C8186D19E8167A188E16FF188117B3175A178B1820174A181D173917831746174117621974178A16BB1758161816011832152D1522168416F416DC16CD1854167F14FE15FB15DD157E140D171017CF154B167B159816BB170915C416DE15681566153D161417E01589160016F9164E183517EE174C194F198D243020A4191C199F17B518E71796176F16E01625172618FA18EF172D171B173217C4188B1762160718CA16E5162018581772158316D417FE1453159D16B0157F16F31515169217D51336149F17E516741491150D18651765159216A017CE163317CE15E215F316041831188316BC13E81578166017C217A8169917F1182B197E25F221161A56193819D718AE1620166E173B176918371995177E17D415EC16871785180B19DC170E19A317C91677169D18B7164B160219DA162D164C17E215F3160C163D17D7156E13D113D916E11585156C179217D8163E16F6162E153616E415A01831151E173418D216F31523161C172517C617EC180118F318E518D4199C24EA21E81A8018EB17F9180D18B018E9187C1835186C181016EA145F183117A4167B189917541794171D18A91448167017AF16B215DD162F1614160B16E3156717AE169216DF15FD13ED148E140816EA1434171416CD173C16B11535167C166B158716CF16FC156D154B179615EB163E175617F215B8186E17EA170219911AA62559223A1A421837185418701767172D17DB19C3160017921715171217E517561754198217A91681172D16E9164C18F815D8178E161115A416CE148215CD168F16DB162D17FE16E413BC14DF14B814B7159F164E153616C91528161715CA150E17D41639160D16CD15A715051642166E175116DD1582170B152318DD18271BE923EA217A1AE418D717BB1613178E1738171B172C177318D7179D16CB154216A816AA168E17A516A0166318AB162517581885163E16B415D2153C153715AE162916DA167C16EC154715C315AE153715B31457168E168A15CE14F4162F156915A3153516DD1670176C16E61641152516DC163B17BB16111820175B1808191E1B4A25AA213C1C2319A119581966181218E9174419DE17ED158C1588168617A7172117B7189A17DA145A18411656178C17B7164716A1169F1467153C176014361665162C165F15C2156515F014B5155814BE15E616A91644161F151515EB1485166917EF154D16C416B6167717E0169C160E171D16EC150E161D16F116E617F41B56258322411A2817CA18FC165917BD175618D417F6167517D81694155515FD16E71634175E1793172917A717DB16C9173918F2178615DA14A2147A155E15D2140C16D7148F1566172214EB140D1632167B15B915DB1661171E177217BC159015AE15851791161E15BB1527171016F916BF160F16B6156017EB15F417B519351B2B232623691BA8190518401AEE15CD174C184917BB1674169316881571159517071770172017AA178B169816A81635169A172517CD15BA154F16AD150C150B166615F114F6142117F715B4151D15A615B415E41679169A17551561161416E8153F151D16631629156E155816E016BC16C31669161917EA17D9187D180517951A4F247D21D11AA3188B184E18B118AA16D11779177C1731179916AE15CC1697153C16A415C9178A174F1631147B164217CA179815C118621782145117B314D214DE163416FF15DB153E14A514D1142117A91592167016F5154C153816291525159316E5146414CD151015E415B615A2153D167B16941692176916DD1563179D1A992419226E1B1A195A184E191B1918161516C118BA1698167316A7141F155C17AC16FF15B2177616A016DC174F170518D1178A16B115D2155E166C16E7150F163C15FC142F1600168C13C0153B1667158C16BB15D3170C162117D9141216351531151E164916901631164C177314C7156916D115B5160E1872172418FD176319B62377218E1A13185E188718991753170718C117271892169116E316A517A41760161B1642179217C718DF16FC16FA17541743180F17031638165F1641137516F814F21519166816A91529152E14F41672153F15D41695154A164017AE1403175D160E16CA152A16B514D01790163815DE166116CD160919B1178718B21A511A57245321981B4419BD17E3189F1821186B177A17CB15A916A4157A15DC167117C416F316FC18DF154417C216991598161F18F7169316EE156E171817861548166A16611673143816F2151015FF14C31675141D15A51465164B169314B1165E16E0153C1401173414D415B31678151C15DF172C166C15191807171418DF18481A3C25E021A81A5819C11832177B186616F01669178E1730161B17F81617170917B016AF17E2162517D317A616FA155018A917391686165A166A163D16471755164715A6159414651661156115BE15C614F214CF14901678158D152C166015F6159A163F162D1732162915F615BF15B8165D16671583164D179217BF176618261A4C256F22021CE11846181F183D185917D915A216D0168B18D11676160616FA162415A517F714C716851670156E17AE1745193118381653149D15B616B414CA1528154315EC145B173B15F015ED1467157C146D15E61669158515941617165915A41654165B14D01596147E165216AC153717EF163916D315A7167D18BE18681B39242521F41BAF187A184319B117561593164D18581711178A172A16BE16BF16E4177C15F9158E165218BB177D16041727167617C717B2166016FC1429142616E1150917CE160417B016BC1590154115F6151915DE16541672142B16C516AD15E4140314681729154516F315F515CA156216C7168017FF162D160D18C317D4192A25FE214E1BAE1997171F182D18A4199F18CC17F816B117561750178016271777152417CD19A317F618B3156017C0160918961415155716FE14161519169B16B4154E154E164F162A1684153D14AE15F415931739166916B516AF16C817B51572167C16FC171B163615431618161C16FD166B16AC15E016D816FE19C5163E1A2B237E22A41C511AA1188917B3189418FA17E8185F18C118811620160817E2166316A315A616C2166419AA1639166017941769140B17831505163217981441160116DF164D18041632158114E715081567155B166E17FA152E159215E715C81578150316E7167716A3143618D11622163116211650165D1656158816621871195524D921E81ABA1B7C18DC187E181C1813172019CD199318131867189A16E017E416A5164A184B16DE1869188016F2166F1537163F1405171116C616F2148316F61569162618731747166F1682152D16E615B315F41533140B16891765164D15BC153B151D16BB152F164C15AA156917C7163815621518186E1603180518A319F4269B234A1A0F19DD178A18EB177217FE1853182D1797182417A515C415A716E618D61647189518A218AF159B16C416D0168F15F0155715CF1547154D158C1672166818E917E916CB151A1501161C15F0142516D915D81475168C1554160A14FD14461660146A144B1662173C15EB16A3183B16D514911727166318F0184C1AF6240B226A1B31185A193B1A0319FD172E18D016AB17EA1868195B15F2164316D4168016331649177A179818FD15A91716174A153816B61885152116CA164016541682177117BD161B1716163C157C159314101607172316DC15AB152D17561505164B1709153A151116D1166C168D1583160E164E16471966178A1890198219C0222023861B9E1A4C1950174C198A1779186519C9177018661816188D155916E5149617EA16E3163C19A617AE180817C217F3159116EB159A17771640172D18F11638172F1981161918ED166A161B14781598153F166F145E150D157416B31623166C167816C217B714B817A0161F153117E516C316291A201892177A19041B6924DD20771ADA184A191A178317481712192417B215D5174B15DB16DB158D16F3158E1780162C162B179C18361561172C16C2172A17151623153515B0151617461791169918DC17BB172017A415CA1776151515B1166D15E814EE15391654169516F415741567163A16AD15E21558174617CC143A14801886167218F817FE19FE234722FA18A8184418A418D416ED189F17EC1779188D17D215231611164017DC1539171017931681179E166716A2156A169D162016381791167115EF14EE152D167017481828185817911672156317AF1579178617351721155817DE156716171726178D171818561568173B162817CB169E15A017CD189718991A7218411A9C24CE2059198E181E186617FF17FD17B218A8181F1828182617ED1795153717DB16C216FC16AF171218211775163217BF161916FF15D317FA154A17D0155B177F166E16CF17EC18CD16EB16B517F1160F16F316BF16FC157116BE15FC15A416EE157D163717C717DB15CB170017CD170118AB158B18D917DE168F18D417A21A082779216F19301AB318A118B01804186C184019C31724189E16AC161F17C5173B164417E917E8176B18C0169E163C169517AD19C517EC15781479166C17B016F917EA16B518751795171217B617AD17DD16131783158817411610174E1669151217A71666154D170215B51785164014ED177B174B17FF160D172E173219AB1B4B267221511A2B191019EE1863190C17DD18BA183818F018BE1785179115F315A118AC18FC18EF186D18841770187C174718721792160817E5165F15751824169315D6168318D0170517F9162317B1161B165116D616C61540179F158515F316B7163217E915771565158D17A2168416DE1603174A17701783155E17BA19DF1B3C263520B31AEA171A181C170A17A51720185F19F5178B18A1169D176317D917851560175817D5171F18AE166C188117C41663162417A61547153D157C16971679169E177A175B1835182F16FA15F417DD153A1615169C161C17B217E11516156F158C177617B71749158316E215AD167817C01623173A18F91754186E17901A84241D211C1BBC1A17174917D517A01744175518DC1858194B188217D81828175518F01647178E16D41831169C18A11632175216D417C315EA14FE158F164416FE169916861690191E169B16AF160B18B6159C177817B215A115D418C01570156C1698177A17A3160917C7161D17BF17C5171D174E15BF1AF916B617B91A081BA324CB229E19F4186B172518071A0A18DC17F1181C18A61943195818FD170D185315E3175118B01719164318B217701567177017DF158417C9157F150B157D16B116FB16C4170017B0169F1520160918C515A8166A17A1164D1651174D172315D71646166F17F8150016EC1739166A16FB19EC16BE1547189D177D17391AE31884253A22A61B77178917D318C0177218D417DE186219E8173A1849174417AD170C187B17C9170F18A7186C17A5181A180116C41546177E166016F316FD15C015A316D1173417CC15A6145F164C170B160C176218AC164E17491696176E159C1515165B1794140F16F6172B186116901583176F17D41812176E1737173219961A5C252821421B9B187D194A1791187B195B18F51718161E1ABC169A18E316B51724162818C117AB187317BF169B168E15AA177F1670165C17AE1562168816C914701674174C17E4148714E0176B167C15CA144A16A316A9164C162C16FA156D166F15C81744161416EA1566166116F214CD171316D5166F17C9178417B017581AEF24821FBF1A3D18E41793198018A616E018C51666178A17141655182617A718961785174618971781180C178816F51797171616F815D0159E162116A615E715C8162317001762172716B5149616411618146714B4175E145D164C181718A81739175E156F15831604159418A0169B1686173C173017411719170D17C119361B6A256F20881AEE19F819D5184E180F17EE185E174F17F317F8189616BA17DC170E186418D717A1168B18061835164318EF15201643179E177715C615F916DF158D1574170E17DD154A15AA1522169816B316BD16AE16B3167416DE15D115BC16CE14551532176D16F71543165E15B6183617BE167F17BA18BC168916C218421BCD239D21741A3419E317441846191618DD1716180F170B17D7150A17F417FF179D1808179D17FC16F41879186D17EB162118DF15E715C315C915BB1620164216DF1484175D170F153B162D160E15C4166115DD166A181E164D1788167D148416C4158B1617172D150717FE15FD17A816CA166E178817A1165117CF1866187F19D0239A20CF1AD6186C173E18C818FF17531640182717D0174118F116AA17851913169216A618AF163116E6165F163E17321714176B1610178E15A0163217BD167317F817F9178A17521422161A172A179715E11725176216B414D1171B1611153E17051789167F1709176B176416DE167E18141763188E178817CD17A718331AAC236E20F4193719C11816177E176C171918FB185F18D8161519491611177519A11794181817EB16A8165A16E716BA16EC169F166E16F0169F1595175216EE154E15BB176716BA166C14CE153716EE166716B81790160D16DD14B917BE1576165817C516641625176D166D175216DC176B17C11681178516A817FE161018011AFA24BC236E1B75193119A5185518A116D717461980183E17F418EC16AB17FC17551935184018FA179516B017AB16A11719181117EB1683163717D716CB164714BF153418DF16E817A7141B161017C31666169E167A17BF1416162015D417FC1525162A181F16111873173E1667163A1691174018A017BD172718E618BE17E11AFB2636229A1A331887177E185B18E3186D18D7178617B418D3176A17A215FB1636198B17AE1649186B181E169915BE14DD16AD151417BE1669160F1714158315A117FE16681735174D1507172716B415CA15941778167B170F16C516B3157516731607178F165116E9168E167916BF16B218F3161B1708186217FC184C189B1ADC26E9216A1B6317CF180D185D177E193A185818E5178C17BD1722168F17931714183218A218DB17EC179215F81898162F17B5159817E7151F16F7168016FA14D41511169E15FA163F15A1151D17C416FB154316A3174218D916E716DF161817CB173D18C816DE163B172F184616CA162217A017E917F81792160C185C183D1AAF266D226D1BDD19E619B3186C17F7173417C518F4178A19BF17B417C418FE162F193C192918CB1795187A1605168F1814197616B715BE14EE1553164B14A9162416F51639175218F0151B162F174E16AD153E16BC166816D614CF1750174A16BD1726173218F116AD15191905178F18FC161016631839198C170318CC18811AF6243621AE1A17193B18D119ED17AD15A41689185718BF1806178217B617A3187018751878181E1824190F16E916D616E01796148C16E01527150A17801568167716CE160217EE16AB165315B3158A17151668156C17CA15C015A2171E189C18CC167C173C167D171D1632186C17FE16DA17D9159817D41938176D171E19451BED24F520641B82185B18F718D3173917AB188E185E18F0177C17D5160117A51684184517A718FD17DB1673177E1823183B16A416FA157C16AD16F115BA16EC14CF16E718B516EC17ED1543157715C61634168717F9177C1669184018DF16E6182519DB1873174E17881846186C17B116DF185E1743172717E518F0176C18C21AD024A8217E1AC6180F188C17D717D217DC161819031883184617E8160F1897180E1919192618A6189917701776170F17F0161F1675171D184616A5165915C51712174B18FC170B1751151315631631168014CD1733175E173418E7175A18841749181D173A18B5196C168D183A18F6151F18FF15FF1783179315E9161D19651A08252020811A751816185917BA1708183317CB16D218E4164817D61706186A1771170418FE161C174F173717AB18511772176617C3187817B814A716031641167614E9158E17EB171C165C15AA17E2149716B41705186A1522161618CC177E17A517E9176B188C151417ED16ED17C71664177E16E9179E175A17C418CB18EE19592482216B1A171A14188D18D31826174519241999180B1880175F180D18C51684193A158E1760183A187E17F315A7173A173E156D16E0159816F916EF155D178516E8163117A7163A162D167F15D315691622174718AD164B17C517EF181B19A117C816F6168616C4167B187417FD17B518751767177517A217BA18F8189E1ABB259321191B2C187E182B186D17D417EF18061817197B180118461663175716061A3018C5179817E4164616B415AB155E184F142A176E1629161F178517381561162D178317F41628179116AE1657178316CA16C917EC16D2178618CE161218471719162317D115041792173116CD17C716F21514182F1801169817A617911BF024BE22F01B3E18AD17BE17E81741174E181F19EF18DF180E19BB18C616BC17F0171817CB17FC1714189517D9169117C716C4171F1740164217DA1869164E1518173F17F5154F155615FE15E6153317CE15CD179917CD1759160E18E4176116CE175F17C4160517C5171017E71644171818CD175C17E1181D18F5181419B31BB626B321D31BE019BA1757173F187D16EF167C171518061863189E17EF167D18221703182C18C018B217EF17CD15CA154C16F4142216A616AC15A118A6161B17501589161F16FA16A815271533151F174516D4168917B3164C14B21A0B175D170717C61685189C1765161E176D16291764179416D517D6170718D7178418A31A9C2429212A1BC417221836189518451895168717E5179617A5170717FF162017B5175216481827177D18D71810161E16DB15201692161417E11518187217F2176117F11645179B1767169F15B8147E1744156415DF163915CF169A169C17FE16E0160617D5170A179F16EE16F11638175D1796161918BC176F16C5176D18661ABC24D822A4191E19D41819189F17CB166F18DB1607198018B3171817CD174818EE17C317CB164A187A18461859164017EA16021871161E17C3153D18901693161017D0173C1827162E177216EB16BF164A17AF188816E2168515F9177A178F16E617CD16AC17631625171418F715AA179D175A180D189217F615E8187F18A21A1526D420FC1A14181717DC16F9166E1603179917FF1633189A148A17311792166A18981756177D16A1181A167215901713171617731687167017B316FA1502170817B91644176A160D158B16B315F7162C16AE16C316DF1603155819D7164D192519781763173A16BC15A317FE16F5166217D716DF16F019E116A0161217C6198B23EA228A1B2019AE177B181A199017E619671832180F18DE16BA168517F9164B199F180117C8175417F0160D17E31622179916F7179A161318A9176417471731178D17A317C41795150514FD17F018F715EB16E31823186117A917731614174A178918BD1745177E1816187E178917FE1890179F165E17EA162317E819331BB924E520581B8F176B1852183019FB177718AA17FB172B19C617FA1784163C1874171918371740186E176B178D161318AF17AF16B417E81469161316B2150617BC165B159116CB16021724176D178316151674178E176F151116E616C4169617FF1659182816C417B517F3169A178A1619179E18B617531725167A185A19101AEF259722021B6618E017A619A1187218AD170C191918B61811196C180F18C1172F188716B1184B1794180518F415A5182216F715E9165516A616B9168917CB157915E916EB17271710177D160D1829191C15AC17D21621170D183118DE159B1771165218E9160318D216B01839180A176C17011847185F18D8175D185019801B83257022A61B0E18BB179D1776185C18531835191419341898179717DF17FD1691173D175C1616176D17D0161516B716D315A4159C17F5145915B516F3164915A4158816AF16C316E015DE15C11582166116E8168717EB18E415D7169B172B16C617BB1794177518E314DD1655180E17F317E316B1171C18D11668185B1974198C25F5226A1C4C187A178318DE17D6164E185B19C817EB18C0185A17931681188117C91547189F179116F8160B17C217901771156B170517FC15A818581632161317B81797177C176014D7157B159E151115AE17EF1780171B171118DA15D1172F181D1778178717401822198415C516F617FA171E1707172B18DB18AA179C1A1526CA23A31B7719D717EF18D918E418A118BA169D174716CA174D154816A9174F1703170518DB15DF16C515FF14DC141516E315D115DA15D6167416BD1761174715581736174A153F1384166815C5157C162716201957161D15AF157715A8180A1861171B166D159616D8185317C9175D18A4189F17B217E3161E170B17A4182824B622431B201B52187818DC1624184719AB17E0160C1883172316B215BA151417A2171118E7168317A5161116B4162418CC1571164D15071607175115D8160217A7169115391613167316C715691536173815D31854162E14AF1679153A15BB147C1565168A18C016AD186018A618901706173D170417EA16D7188E198A1BA925A922DF1A8218D318A6174C1751185B185518601702162E17A4161C16331635173F17F9161918B5163817D3159B17F2164E16A2150116A7150716D815141661171B1672163316281383165E1409164915F4149C163E15DA15C215FE15DD16F714D015F216CD17C01730174317FF1662170B16171774176A1686179A18231A22238E22E419D617301727180E1727196B175118B91882171717E115EC15E0179717B616FB17E616C1173416AB16FB18EB17F515BF154C15FC163D1641154116B116B916A71566154E145316EC148616AA156E155C165515AA159E174215F21672167D178A166D17D315C918D816B416C818FA17E2162917AA17DF16C1182D1BE422E321A31B69190A19DF185817AA1939177A198C186C15771635181D17C516B21753172C187C178D178A171617DE17AE18EC16CE16EB161B17D9158216C2153F16D616CB16A5168414A3173317C0158C1407180318E6164F164F16EC151918961740185417C5166D1804187716C516FD16AE17C4186F187916FF169A175119122479219D1B1F195C170518BE167F163E174F16D81634170C176617AA16AB172C18EB16D3157218A216F71719177718711779154817BB13A315AF164B173615C4160C176E1838175C1301163D14261848142816A117661610150517E3177B1786173B169417EF18B01648193518301870179A18A1156017A517B3176A197019D4248121281CB2189918AB179D162C166718811848176218DB18BE16D1164218DF17DE150C1767171418E416FA151F162016F3167618A6161416CF17021805171117A216191742161214FE140116C816F9155B16F016B115531748160F151917BC162819A9163B176A17811765174618F317FD146317421750169417EC18521B0624B220761B4F1874185C185C18F9159318B0186F17B91698177117E5160F176018A61656174B17601954174F169A182117FA179D170217DD1617160F17D9163F17F615DA166E16331688150316DC1637177116701631164416EC159A156015B6152218081724164E18CC173A17ED16741604171617AE164B16C9160F18AF19EB22A624D51ABF17E917FB18EB16D4171F171417251AF01769174C164A15BE17F8163016BF1848161D185B17EA16EF16F81727177E17E5161A149B16791643166417C01645158114E0150C153A16A216281671167817D116CE15AB17A6151F16B5164017281652167D150A18DF16D816D5172A164F178116DC1454172F1793181D24EB228A1C59186C189516E31712179C175F186F1849194E17AC162A189D17551843176118D417BD1708164317A0180D171D17A71880164015C51751156617FA14B21651151E15701654144B15FE153415C315FC15F6154116DF1669160B1634177918BC17AB166B16B817AB17E0170A18E51548172F16F71517173718011B8F2418222D1ADC181B163817E117DE16A917AE1674175C175B17231724175518DC1825176A187F17E717AC17A616CB161917A9192217131653157D15201661166515DD1514166D169714B613EE161B1553145416D6175A167417B41610178A184518E91686162617E01766167D16B415A017E916B81521169516A815B818581B5F24AA21DF1A5E186B19B6194A1993177219B018E218AF1669179817AF163A1862186A17841671171F18D915CB160B169B17DF156D17C315101554169316961521180F17E71433163315751485169716C814AB1793184716C71425162716F616C4161E186117531647163C16971686162817EE1745175E17DC1674178717EE1895244A22CF1AE218321A1018721A271955184619AF19E118CB17E41772156517121742179F17D817AD172C184D163718FF168B16B7179D152917241705170916AD156817E717CE15AB157C157C164215C814731834169C1785155417FE187917D916E8185B18F218BA168A178017B2175919EE1700185A17FA154C18F7187619C7246922621C1A1A4A170B18B916AC1973185B17FB172F18281726156117B1173718AC17F716A3175D188417AF1769179417FC179617BF16DA1790176716D51680160C183618FD1640159D1517177B16D5159E17E1179816B915BF16551778176615ED17EC170916E4152B17CB17AA157C1763169917C816421607184E18351AFE24F321B81BDA194B196F1821180F181A17BF17B7183717C3178017D5166E16C816FD152018F817FB164B162F16AE1AED17401506158E16D21545172C175E16BC1707178216BB16B1140715541513168F150818BF16A4163217F516CA150B17C516F7166817B9174F14E016C0156F1642188916771743179C16F6164918561A15238023BE1C2A1AF319281903181E1982178B197819D7178E187A173418F0173117F3167E1666186617A218991745178F16341735173A16521881178E169316E3172417C9174917B514C4163616C715E116ED175E1872165A16AB153B16011644181618811775174E178E161A16F315A61784174C161916A8163517AB17E519C0247B22D21B1C19E8197318CA17781844198E18FA16721785172E17B31858184C16B3165E163F1AA518CA16C7160F17A117DC168716C6177E17D914631571150816EE166A17A815F014FB14B717D715C516F8178517AB1682168616EF16081974186C17D1162E1816178718E4161C17CC1675166E177417D316DF15C618291A892538217F1BD81867167E1806197418AF18C818C118A2172418EE168516FA175C18DE16E7170B193F18851792197C170F194E183817D916E616AC15BF1564155216021891160C1694150E14E0155D1676163B185D180B1739161417C5159E17A1177317BE178F160E1684173C191917AE1606165118CA167715A2178317391B7225C121911A2C18E917F217D617D118721708184F182C17E4161E172F170E1849189F162D168E189618C018D118811787168416CD1779176616EC168515D41526157416FE15BC15EC1432155216E116841594163318C3178E169317EF1577168316C017B916F8162A1568174C17C41653174A165B186218DC167917F2185D1BDF24D6225E1B541993175919FB177117FA184A175E1860173417BA16A615B215A1161117381846197F198F175D1732195617EC151A16B8167A15BD16821585153517A416DB152D15ED13DD152E156D15F514EE15B91719163416E1150516C716F61590165B169916E5151617B3164218DC17EE167517F117D815621749196619A8243B221C1A641951177A17A7176917BC176C18A718BC1617167F15B41608174E162A172D183C175C180D175A17BB17E3153117641682157815FA15E716FD151816C3165B15E214E9140115C5148714DC151016E1175616BD151817B7167D154516EA152B17E117EB150517F616CE162A179D17BB161917ED179717C6184F1A34257422791B001A63184A180B172C17F816D318C2179817811760168615FE157E175417D417B3166718BA16B016E6188417A416C4176A1555154917991522150E158A16DB15D413B01445146616A113FC15AA179416E516D51517183017F915EB16AA15411619176316DE17A2172D15FC164418FD15D917BF16E11774194D1BCE247F20D61ABB170C1989184A1603175017C417EF171D1861162116FB14FD14DA16C9162D17DF17B617E0162D1713170F18DB16E21517166D16C71791177F168F163A17CE163A153714311548168915E9134A18E4186117E815F1176816A71537174818DE1657177116A617061655165617871734179A17DF16E7174318B61AA2240421061A2519A1167D18A2164618D6183A17CD1723187C173416B415F416AC168A1691160F1A941790177D1517178617901661157516F81698163D1581147616DF165115561423165515AC142617BA156A194C173F1798160D17E4164B1641161916A116A818F2163B18BA16C816D715AF17E1181B1AB9180818E4174E1BEF2431213919E6167E184B174217C215091988170B1857182119AB164916BF1854188D167817FD152E19AB178214EC170A1898178D166D15D6158D16B5156514EA156814EC14F514E5131B1444162D156715AC17EB159D16FC17C917B515FF143C17A7167B171B179E167417AD1565163C18DE158F172D18001658177C18E61B5D25AC218D198A1928175A1889173617AB181018A516621768177D182F174918F818C01665172917D7186D18D916CF16EA1523154E173A16D115A0150816B7149614F51556170A1534158915DC124F17E71513182F174A1604164B16C8158C1568163D174B164618EC1679180B161B170716D117FF16F21828176E18AE19A51A96254F209C1AE2171818D8151718D716FF15A817D7176918E1169D16FB17DB1635169F174B17371805188B162E172C178F166E1626175816E815C7150514401553147A144F1599144F142B15A114B7141C161918B4186517C9168916F81696160E186B1804164815D9153918511792171E1774173A17F817E0162C178818321B432687212F1AC918EB16A416C2165517C318AB17931615181917B9178E158416CC169A158E1884186A183F1705186916D818AA17E816F115EA16C516FF1553161B16C1150716BB149E157A140317B6169E179D166A188117C4163F1719160417B817C3178B160117BF17BA18E6160C177B17D917ED16281783178B183819AB196725CA20BE199A17C6172C1754171617CA1678188516A616E9169916BD167B175C18B6173917A41611170217F416531887160916F015A0153E15251613161A1511150815301306146013DB16BA14F5167216EF1652180516A2152916A817871647182F1747161716111762165E15211848184217D616B616CC16DC174518231BC925B4218D1AC019CD161F16111728175117EA1722177417BB18D716AC15F01722187A170517DA1700164917F815A9168917431762176318B115F815FB15FD133E15F6139A1319150E141D164715DB153E1583164E16BC15B5170919BE16F416CB17C8189D18E21500172418FE165E16BB16FD154316E8164816F817C118301B4925C62279195B18D5178517AE176F1793165C177217B3168017DA160E16E8161417C016FB169D17C916EB16B8164A16561768167A164F15CC16F0158615E8156F148E144E133415A71595167216DB15A116801727166516D018BE16CC16F116B715A1188B180618FD1638176A17051744189616611648184F180618DE19231AC1254720311AF1188117C916D7175015D3161D1757170B18CA163D1851175B1803175617B617EA17F41663175A1685178119B0158116291615175E157F15AF1538146214BE1427145A1427167C164C16A915EF1702169416F71508177915961548180917C717F116A316AA17B517001787172417B51512171C1454170718051A6425F7203F1B26177F1809173817A11746168B189E15061671177018A11743177618E6152B17B417461865166016FE16B518CE161316BA14E4153D16B515B41374145C1465150E149314C4140516E216BD1529166B17651656163917F714F115A7150318FD146115151546160F157017B315B01895151516D317201762180F1AE523DB20BC1B2B188F175317B0170D177E178A188C1766172F176E1812150316701669176E182716C918B7164216641767165716D314A315A013C317E814541759150616E51409154E141D17E81566168616EC15B415A0169415A217FB14A9164816AF17B0167617ED16A11890154817691709172316F716CA17A21985196D1A9A25CD22BB1B5A180B18301815198C16A417AC1854189818B217D016F117FF177F170D18C6185D1727198D162A16D91647186B16EE1615154414F81519161715871692168613D014D91586148E176A17E614C8179F1782160E144917BA165D188B162A17FF167217281616189E178A166516B018ED16DC1735160F18331736193A249A214B1AC318A317C7166E17A31822184E19B118F417621874180C17B5189217F417B017FC1766177716E3174318EC179D1680176E1642151B165717C0168B153916F214041620154216D116AC16B715A815781647162615001694168C17C3166316B7173417EB1545160B16A815CB1729178E172E1898162E18DB18A71A55262020DF185618CA17CE1628180F168D176019541825183217D517EE162F17D11732173517A71866182F16FA15CC161217E81587178B157C176F165B1445174D1512157315E2168F14CE166116AC1451163916D216E015921651170417451630153916B4164E1606159816AE156A16CC160D167A1633185D1648184B18451A19248521EF190D170419AF18FC17C6173E185D19C6164817E618EB16BC177817A71799189117D7176C184D17F8164618D8177C155817BE16E2151716A816ED16A3167F16BB15B314E0151E1612166B16D416AD1755178B16D7152F1627176C15DC168D16F4159A154F16E516F616D01641198E1604175B171417FA176E18101B3825C620E51AF0199117251860184917D2160A18C1188E17DD18D916A91792175C19B317DF173419A517401823173418B9166516DE171A16F616ED165C154215D0140C167114DF15821519156C155D162C16481784162215721739171216591670160C1879160B17C217DE17D016B616A6165B1736182716D1176318531820191626B722DC1BB118C517DC17C8176C173B17D71706187816B61621167D1604180515E71688171C17DF179418C6174117E915EC16D1174418D8151417F5143916CE1641167915E9159F13221681152415DD145217751786165C160416E71506165C178717C517F3169A163A1869163117C5188116D7163F17F3158B16161A6D1ACB26CC20B51A6418DF179B17201893160E198415EA16FC161E177616B917CF172C160016CC178016FD17151663160417AD16E4155E1762172216BE17B0164A172C168C16D7155115E513F015F515AC15A815AB15B217CE1602169E157116A416BC167417F416C716BA15E4175515C416581619166F175817D216C518BE188C1AE824FF20B518F41745195217A5170C17A6178F177216AC18CD1776169D179D179F1712186B16F01701198B17E3154617EB173615AF163A16E115F31526154C16BA141216F5151417B7142D157F155216461536163818B015A415771793168A1741175F1522150A164A166A173C17CA15E7162E17AF1533163C162316C6174A19FC2278217F1B2318301842190517EC173317311887184E1745176B1853175416AB18E418BE1823188218DE1670156617BF1741168515D816BB173515A716C8159A14CF17131767150614FD17AC15C216FB1416172317F115C01537176C15841869152D18551717175C15F61651169C163A189F16151523164616C21751187C1AA7252F23261B5F18E617C61943175218F416BB17E0170518CB16EE16F515C716B2163A17B91724165617B4165116B9164418F6175416F515581373160115E616FD15D4158F153C16CB15E913371811160215F8158416531409178F16F4158015B41686160616F216AD142317C4164B163016A81673161B1712176517EF178719CB258821B11AE6191D175717ED16B0174517A018B217231721175D16341823188515C41746176B175518B917531624179316D4160217C9142B14C117AB163A16271531166B178E159514CD1394164014FF157D167916BD13DA151D176216E5169914BF17751786160E173A162A16BD158B1704161F1648168D144118CB18FD1A4124B7207E1A41190F181417C217B51851183518D5168117F9179C165D163C17151832175C165A1643179B177816D515A1166417E2154A14C51538141F179D150716CA158B1552164C1423148B15F214D914B915C514F014F514F015C916B815F517C3161D1501165E178917C814C6153316B215F514BE16F415C1172A19351A6723A821ED1B58187C17F3168816D91823184018F5162518DF15B21692160917E416951771165217491776182515F216B619721883170E1667154716DA159E1700187316D915C916D813AB15DA153715D4154516F916B5178715B817B916E9159C150818DC1638157A155C175717AE159316D71495160B183417D517CD19B51BDC240422A41AB519B0176118BC17741760172618F116A6174D160017C2153F16D2151C15A01632166216EE14D9141B165617F416B81597141A161B1613168716CF16BC168616AB159E1455163316FF15FF155A154716EA144C16EA165716EB14141653168F155B16A8162B170F152415FB153B1705157716B6159616BB16B21AAE257221A919661843189517BF1600176F1858160517AB1672168D161B15A915A116BE167015AA17AE17B9161115131781163E16DD16131732162A16771513151317E416B417D81429151A140317F9144A159715F715DC14911539173C15F21554160D1641162A168616551687169217851495151A169216271717185B19001BE823B121701A4C1717182E18D7167E17B917E317CC18881831167D165416B6161F173C17CC172917BF1658171C158717D2142D175517A41441175E152416A6153D17A3161C163F162416771451174116D91529188E17DA137B1708165516A116081892165D14C917611605153E165115A715D7158616BC17E3159E18B118851B7A243E21891AEA17EE16FB171B18421731199117FD1690168516F0140915B516D2172816BF16F618E2152A17B1160017F1151417F117EC1504161E15D6151D156D16DE15D8136916D0147114BA1606168B148817F11502167B14B51791157F186216FB15B9151116DF14D816FD136C16AA166117C9154616F316011861163D19D8243922351B9317FF1871185D18BD17D6192819C31799172C18E71695153617A61787182018F517DD151418BC169F1791163F161E184F168E146B166818F915BC152015CC13DA162A152F14F816CA15E014AA17DB178B153916B315E9147D15D41521170516B115C014C7162D1645166517C616AC176316E4155117D619BD1AF1269422061B7619AE167319A719FF17A7187619B718C419081851178316DF1767161717C31894170516D616C6158917CC16991641185017ED15E416CC14671546157014A414A1138E157D15DD14A2154E176517851695162814B1136C150A15D9162116A416C815DE1442168C15DC153C17CD15F01871176F153A161D19BF1A9F24 +# Then we get the mean once +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 4024 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 2 2 0 2106 +# And query the status until the device reports callibration +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 4023 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 01 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 4023 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 01 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 4023 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 01 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 4023 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 01 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 4023 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 01 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 4023 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 03 +# Then the background is fetched a second time, and we get the mean one more time +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 0009 + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 F327E021F221D31F8520F31FE21E3F1FC01F4D1F951E0F1E021E421DD41E9F1DA01CC71E681E971C4C1ED41D741FD91D901EF81DDF1EBC1E4A1E061E0A208B1EF61CBA1DEA1C2D1BE31B891D071D981D261DBC1E6B1D121D9D1D251D741CD71DCB1E7B1E801E301CDE1D6F1D551CC11E171DA31F051F801EAD1DD8202F22752DEC2A2023E11F6A217321A11F0E208020AE208D1EAA1FE61EF01DC71E231EF71D0A1D671D031E951DC11DE61E521F4520D11D9F1F251E111F2D1EDA1EE41F8B1DF71DBF1EFE1DA21EB31C091FD11C1C1DA91FAD1EFE1C251DAD1D8B1FFF1EBE1D0B1E361E031F7C1D9F1EED1E321E741E241EA41F4920D01F7320CC203223A92CF72A6824F422CB21BC2013204520A521601F3922CD215521E91F1E1F8D1FAB1F6B1FEC1F4F1F451E4F1E3120A81F321F3D20A920A31F981FCC1F7A1CB61ED71F891F8B20861FB31F9E1E571F191ED51D761E441F1E1EC11E5D1FE51CCD1E131F611F8E1FE71EDB1D7B1F911FB01F4C1EA61E321F8F21B41FE92045217D24CF2EAA2B19277E23DF203F21BA20EE217B20FC1F8D1FBA1F821FCF1FF41EA9200D1E651F421F5A1F3521881E041F361E1F20C61F70208B1F3520AB1EFA1D9D1C711E4520941F8B203F1EE51DE51E741DEB1C9D1E521F291FA51FE51E7E1EB71FF51FC2202F1F5C1FC91EBC1ECE1DD41D561EF81E3F1F4E20C021D11FAF217C23BD2DAF2B752534216F2085201B213B2043209A2045200020B21F181F5D1E2F20AF1F1C1E981FEE1EFF1EDE1EB91E0D21981D2E1E8021D71E8D1BC41E8D1ED61DFD1D8B1F2220321F671D2E1E5C1E741D2A1F121EBA1FFE1D7B1EFA1ED91F5D1E0B1EB01FAB1F8020151E591F481ECC1E8D1E321FCF1EC81FB31EF21FEC217E22562CF629C6248A21B422C31F9C210620BC1F242152209020F720921E341F581F7D1F521F3B1FA61E2120CC1E791EB81F3820EC1D671F911E3F1E301E761EBC1DE81E321FBA1FD11EF11C1C1F4B1D4E1EF91DA61EC61E0D1F2A1F5D1EEE1E781E091FCB1F0B20791E3E1F2B1F551F0F1F2F1F511E9B1F5B216620F61FBA22B723AB2CE129F922AB2115216A202F206E2143200220442133204D1FD31FF41E9B205220E820551FBE1F2A1FCB209620F71E3B1F671D4F1DE21E1A1F431FAC1E4720E21FFA1E9A1FCC1EE91DB51DB01DC91D911D0D1F9820A71E721ECE1E6C1F641EEA1EDF1F281E731FA61E431F151F6D1E0A1F5620B61EFC1FDB1E11216F21ED21152D502A4A2471214322C320C61E651F832077200021E11E981F9F208F21741F4D1FB620FF1F8420C4204B20CF1EC71F6821A41E5A1EE11EA31E501FBC1FFC1E951EDA1E5C1F6E1F361E0A1EC61F6020241D8220871F111E701D9B1EE51C381FD21E181FF01E291F201FE41F621FE91EC51F881F621F671FFA1E70218221E623F42DBB2861231E22ED202221CB1F711EB7209021B520FF1F531F371F111F0420431ECB1E022076204D201B20C11E361DC01F261E7E1F0A1E141D681D0A1F6E1E371EC31EAC1F2E1FD21ED61D7A1DD31CE61D341F281E021F771D7E20451FD91EE61E3B1FF81D5320E01F1A21051F1D1E4D1F5820EA1E651F1E1F821FCA21FE21E62D972BB82255203F209320A61F2B203C209521052020219820E31EFB1F4B20631FF81F041FEE1FC61F9D1FD91FE91D5A1FD51D6C20F81FD41DF71FCC1DB81D8C1EA21E2520B51E111FA81DE41EC71E951DF41E841F1F1FD91E571E711F2A1EFE1EF621491F531E9E2079201D206A20AA1F4620B62053202C20881FE9212E23E92FFD29BC251122C1204E21C21F2C202020AD20A120042279202020591EC61FE61FDB1F511FB71F2F20B01D471F4A1EDD1F3B1D4B1D891DDC1D971E961D3D1DC41C331F7B1E8F1ED41E091D141D831E1B1D571E741DDE1D3D1E961F3D1F2E1FFE1FD21FC31EA41EB61F8420821E911DAB1FA31FE71E171F2B1E73207E2175226F2F132B5C2473211C2352200C1FF31FAC1FA1217621A62055202B1F991F671F38205F1F701F9F1F17223F1FBB20F21D5C1E971E771E061FA11E731D211D481EB31D7A1DAA1EC91D0E1E101E851D971EB51EA81F2E1E341FA31FC21FF61E661EA71F112021203E21401F65214720701FBF202820951F611FC61FE61F2C204124DB2EFF2BE22518224421712026202420E21EF62034209D1F7021A41FF92067207B214620D61FAF1E5D20611F741F041E0C21931F9B1F191F481EEE1ED71F351E511F181E531FC71F131EF51CA11EBB203B1F6B1EED1F921E011FB1209C20F01EC020551F721FF01D9A1E821FBC1F371F63206E1F421FB621B71F9D20AE219A24982C732ADD2495219C2111223A1F0C20AE2079210E21491F6F1FC81EDD1E5920771E0D2006208C1F961F2D1F911EEB1E8D1F2A20F21F5A1E071EFB1FE31D931D851CC31EDB1E871E931D6E1E921E3620971EA020E8201E1FDF1E4D1E881FDE1F49206F20921D8B1ED61DF31FB01DDA1CB91E951FA82090200C20C52070215D23A72EF22926246E22F6207B229F20C621CD1F4B22B51F41217421191F741F671F3F20EE1E7F21C821A11FA71E3D1E041F56209320021F0D1E641FE81EFC1D011E231EFB1F461F481FD81EAD1E1C1EB91E9D1F531F7F205220DD1ED61E931F121E7F1EE41EE31FA41FCC1E6720851FDA1E6B1F80208A1F4E21C31E29206721DB22BA2CB52BD525EE21C02056210F2217217621E71F6821F02195208C1E4A203721A021D71F59215020CC1F8721B421531FE31F491E7F1F13205E1FDA1EFA1EBB1E691E431FB61EF31E491E9A1DCD20821FE81F9C21F31D641E671E491F5A1E9B1E291F4E20121E02205C1F0B21C4211D1FB120B51F9E205D21A9212F21A321B8237F2DB92950250523AF229620C520AA20B42002209D202A2258216B2071208720A420B2205420AA20DF1F4721A11F6520D620E01F9620C81F031E871FC91F601F9F1F711FD920D9205D1F421E1F1DFA1D661F7E1F751FCE1E2E1F5821B21FD91E5620D81FB81EDA1F791E901F531F721FC11DC3208820FE20AD21C5211822D524942E2F2B86247122C71FEF20C321B620F220BD2009218C20C4206F207320E41EA21F571F5A209321711F5E1F5420971FC31F5E1E572005206A1F851F711ED11F4E1FDB20F11F061F3A1EC91F6D1F5A1ED51DDB1ED420311F7A1D0721DE1E201EEE1D7620F71D351F7A1DE91F3E1E5421AD203721AA20EB1F5B206321802155237D2F2529FA25F920AC218A214D210A22962085206220C021611F9F1F25203821D01F5120B7226F2075226521371F9A208121C31E5320891FD11F371FB01E2620F81FA2202F204920FB1DC31F1D1FF81F651DBD1F961EB21EAA1E0E1E8C1DD31D6C1E1E1F42202521F91DAE1FCB1E731E2F1E3E1F8C1F2B1F471F2A228D2112249B2EBB2B0A24C8209E202720B720162070203C1F5C1F1520A6209120761E43215E210B213E1ECD206F21401FBA1E9B1E6D1F3120371F511DA01C2A21011F6020731EA21F9B20491F381F741E661E5C1D501ED91EAF1ECB1D6B1D361F921D501E9E1FB720781F5E1FB51F4C207B1D871D3820191FE01E731FE91E02202222ED226D2D362B1025D522E121242146219B1F58209E204A215922C9209F1F8B1F1D20F3200C20CF22C620C61FDF1F76214D20F01FCB202D1F491FBF1E191F64202520752144210122AD204E20A81E211E5E20BA1DD71F0020BB1F551F1A1EBA1FF11E341FFF1F6D1ECB1E0C1FBC20B11F921FEE22A31FF61FAD21B01E0821CE216B22BD2D702A4024702310216320D820C920E6207F217E21331F7A21CE2062210120AD209E215F1FE1210B20CC206820B7204620131F821F781D0E1EF91F5D1F3F2162206F213222C820732016204C1F921E361F72202220361EE01EC31E7E1F311E831E0C1FE81F701E531F68204A1FC11FE31E451E4A20371FE9208221592127239A2D232AF0234821E721EE21F6214A2177209921F32082218D21A7203D1FB6213920AF20192025203A1E701EF91E461F4A1F2F1E7F1E751D671E121F0E1E14208020531F7E206B20C91FBB1EB12148205C1ED7203620971F391E061FD91ECC1EF41F821FCA1F3921381FD520A9218B20BD1F6D1F561E691FE51FE61FC620F321C22DE82AF7235822D1219D21E31FFC20A72164207A1F8A209821BA1E4D204D22762099207D21F61F3B20BB1FD11FD72043211D20CB1E6D1D721F971F4A1EEC1FD91F312006213B1F481D5F20AC20C020C72062213A20612103204620361F9F1ED9203B1E301F931F011F2920B21E1E2017210821211FCA1F27205921AF20AA23A42E1B2BE92307211D21A021EE20F91F6820D2213A2042221E21A41F621FF41E7B1F961F711F1D205622651F5D1E6B201C205D1F5D1F161FC71EF11D781E391E5C1F321F2920CE1FED1FF91F581F0B213D1F671FD41F9A2059208D209D1E7E1ED21E70203D1E221ED31E791EBD1EDC1EBC21BB205B1F1920A020C122F2226122512D8A2A3B248B21A122562148216C211B210921B01F8820BB20941F7320D0207E207621271F5B2013200F207721051F1A206F1FDE1F3620531E04205E1F5120E61FD4203520E820FF1F501EB71F00209B1EA5203220121F2D1E47204F1E031E0C20861EC81E451FB01D1720081FF71F36217A212F1F501F431F3A21FF20C123032E622BB92452215B209A217121E9202B21D2200A203421E7202B1F43200520DD1FE1205421F31EB920BD1FF91EFE1E9E20811F2B1F6F20631E141F6A1F7120DD1E342030202721E81EA6200E1FA21FFF1FC91E3820161F7B1E391EB31F3F1E4D206A1E0E1EEF1E931E9F1FB31E881F15208B1F8D1FEA1F5E207F1F9721B022A42D702B2D26FE201E2268217221762164227B21672009222E2034233E20BB20DE20E61F032151209220FA1F02200F2085201420EB1ED01D791EB31E191F9C1F6F2020214621E620031F7A20321F27201B1F9F1F651F811EEE1E3C1E2A1FFB1D871EE820D21E341E7D1F7D1F621FD21F461F761FCF1E8B208B20802162213624032FB22BB3242C2375224C2111202A214221D0210B21B71F3C218A200322D120B321D9217B20502124208D208820FA1F951F081F0A20DE1D911DB91E991F981E8D200F216821CC21401FCE1F751F011ECA1E57205C1F6F1FB51E2C20F21DC41E4A208F1FF91E881D601FA41FEC1F44205F21CC1F1521F520EE1FEE200D236023FE2D0C2CEC24C91F1A20F61FFF21A020C020FA21482069227920DB20C31F80205821CA2106214521D122A61FB620101F69219D1E021FED1E081EAB1EAF1EE61E1420342094206C2020206D1FBA1FBC1E741FB720EE1F0F21FF1D941F231F721FA51F0D200020C11FD21FDF1FB320901F471FB71E551FF02029205A207922C6228D2EDF2AE92311214820B72123200D204621B8209122891FE320731FC21FF61EE31D721FDE1FEE2079209B1E101EB4202D20F41EF21EA21F4D1DE61E8A1F851F3E1FFC1E0620C21F0A1FAD1E781D6D1EB31E0E1F4F21EC1DAA1EB71F0420701EE11E551E6820E61FB31DFF20862199217D20F21E1F1E511F7E1F12222E229D22502DB72A3E2666216E227A22861F0620931F4822EF22C1230A22991F7720542266224C2076209220BB20DF1DE41E891E0B1E781E15203E1FB61EC61F691E0B20581FF221C220EA20C11D171E921F151F9E1DC31E6D201F1EEE1E0D20961F3920FA1E971F7A1F3A1EB31F0220621FF31FB31F0D1FB5205A1F1520E91FF3228F23A52D232B08248321AE203021CC20D721E220CA202922AD21A5205421BE2013211521DA201C1F0621671FBF1E3F1F971E761D031E9C1F7F1F27208A1F821F431EE31F1B2073214420C51E0C1DBA1FE31ECD1EEA1F7020A41F1C1F9B1FBF1FE21F151E371F2C1F1A1F3E1F99218E20B11FE71EE41FA71FD620781FD7200522E322032E6D2B5125BB215C22E020ED2109209420D32124214220CA20A62028216421D520971F2321CB1F991F4720C41E0F20A61E771E5E1FEB1F881D5C1F7B20F11E4C1F63214221761FD61D2F1FCD1D911E841EC7201721291EF31EC51F0D1F2F1F5E1EB61E611F3A20CC20D520A21E391E001E4A20A820FD1EAD1FC12106225B22772E5B2B7E240521F121C82262207821EB1F26221C21EF20CE20CD2189209B2170205B20872065202E2067228D20991F1621AA1F8D1F16217E1E711E751FB71F7A206D20E221401F7A1DDC1E491F021FB41DDA1F2A200A1FBE1FA31EB21FDA20121EC51FE81E6C1E991E401F8A20441FCF1F1C1F3B2085218E20B62095227722D92D48294E238E22EB2028220C21A920AE1F1A201F2086215F2238219D2052208F200622C820C11F92210920F61F4121B120C71ED91F6121821EB21E801FA31EB31F1F1F751FA520F21C4B1C21209A20B31D311E8221D820AF1E1620DC201F2092203B1F311F0F20CA216721CF1F421D3F1E881F8D20D3202320A32026229A220C2FF92AF52358224222E021FD1FDB1EFA1F3E2087218722C7206C20D91EC11FB32096211C229A20FA21BF209C1F3C1F5E21DB1F561F4F223620191F1920DC1ED31F141F1D20061F731C301CCE1E981E9E1E51208F201820281FD11FF01D931F241F9B211E1E312059210820E41E201F2A207720D520C921D6203822A921D522AB2D752B1125132294212B2275211A22312243229D21FF217F1F771EBE2186200F20D821C520DB20F920B821031E781FFB20C11F101F6720A41F771F6C1F681FFB2029200020901F941DBE1DDE1D5E1F821ECF209F1F85216D1F1C1FEF1FA61F241FFC1F15207E1F161FDC20381F9F20D9201E21501F2F22D62029214C22DF23492F782B3B24B421A4215421C1209B209B206E232D206C20B5205420732080217E209622BE20F01F8620951F69209D21471FF420C21F541EB11F0D1EB31E6720D31F4E208F205F20431D8A1D351EB41DEE1E2F204D1E321F2B1F941F6C1E2F1F59206320691F141FCD1EB31E2A1FA01F8F20991F461F1B215F1E2D2126226E245E2D712B6A242E2267213F2037209B20AB20B120AC20B0215A210D20DA1E961F3D2009202E212A20D21F5B21FA1F2A206221B61F851F231F181F8E1EAD1EF51F131FE71F9A1F5E1FA71EC71E0D1F731E141E9B1FD11FF81E1F1E18207C1EF41EE81E8F1F06209020C91F0820B51E3E1F2C20D020042078218A20CB21C5224524832E202B062637225123A922D8218E216321D6221321601FED1EA81F002122218B2009220B21341EAA21B01F99202321D31FDE1F0F20101E9F1E81200B1E1C1FAE1F841FB01EFD1EAD1E421E381FA91D941E4B2019208E1F521E1F1E1C1E871FC8204C1F2E203F201D20DF203820F61F35205E1F151F781F7A1F53200C213225842E992B3424852033222E20CC20BE206121372154207C203420F11ED81E1E2035208A206B20B52045200B213820BC201D21D020E11E271EB71D951ECC1EEA1D1F1FD91DAD1E6920441DCF1D521F7D1FE31E0F1F20209720A020D1203C1FEC1EF11EE220941F3F1E941E6B20681FED1FE71F501F1C1FC220431F372126235C249D2C0A2CE224CC2213218D239D1EED20D7215820B91FA21F711FA91E6F1E2F20E61F4D2030200C21DD1F9E1F8F1F2F1F05214C201F1FB31E6C1FC81E3B1E9D1E7F1E261ED21DCC1FF41EB11E5E1EF91ECE1EFB1F111FAC20C51E3B1F0A1FD91E1D1ED11E7F1F281EF31D5A1FFD1FD71FD01F5F1F1620C120DD217621FF1F9C23672DE12AA5249B21AC213621A821E31FD720442093203B20561F7E1ED61F6B1E021F6C1EDF207B20901F6E1DBA1E5520C820701ECF212820931D1D20FD1DB31D9C1FF81E351FD31ED51C921DC21DBC1F851E6D1F731F0B1F931E341F8B1E731E8D1F0C1E6C1D1A1E191E9A1ED31EBE1E8A1FB41F8C1F1A218D1F9F1EA3208623912D3D2B30253522792194227C226F1F501FF62155201220CE1F2A1E661E9720DD1F3F1F2721721FD51F4221DB2028211421FD1F1A1F0F1F0D20A71F491F551FA51E461E801F381F321D8D1EB11FE71E0B20181F28216C1F8720FE1DF81E8F1E831E6A1F681F1C20BF1FBD20FB1D2F1EA61F041FD81F05217B204E21162170221C2DF42A5D2449215721A1218F206520FC2019216821A21FC91FE51FA0209E20AC1F4F1F6C20A2201922E01F582016218520A321FF1F4F1F221F8E1F5B1C1B1F111E0A1F311FAB1F931EF21D511D6B1FB41EAB1EF51F901E471F8B20E41DA71FB11F021F061F621FBF1DBE207B1F6A1E11204C1F0A2031222A21B821EC23B3237B2DF42A9925582230213222DA21C921CD201821B41F4220011F1C1F7320F120512058207C22651FC6208820571F1320662190200E20AB1FC8205C203C1FC71F3720DD1F271E171F541FC01EAB1E2E20E51D361E451ED71F1220151EFD1F0520601FE61D801FC11D9C1EEE1FF61EBE1E2021C31F1E1F99217920C5210222D023202F252BD8246222CB216620CC21B31F2F20F8209C20731F6720CF1F7820392042201421E31F8720E420C31F371FA221EF20711FB51FC41FF91F971F9720BF1F801E8B1EC01D3A1F621EBC1E211F0F1E901D761EBE1F151F8E1E321F781EF71EDC1F3F1F19201D1F491E561F021FD01FCD1FB21EC21F872099201521C8215123882EB72B8E251422822142217A21A4200D1FE81F4420C2212720451F501F2720861E9F200C1E2E20B21FBE1E65207F208E228321A11FF91D541ED51F931D871EA81E981EF01D8A20B21EF61ED81D411EC61D291E1A20541EE01EED1F501F761ECF1F531F6D1D4E1EFD1D531F521F1E1F5E2064205F1F091FBD1F9121132270248B2DB72A8D25FA21C4217022E420891E941F9F217720F41FBF20671FB11FF71FEC20A61EC31ED51F6221FE20D81FED1F291F99200821C71F591F151E441D551EE71E442004201D20BC1FA71EBB1E351EE81E471E5B20A31F711DDC1EF21FCC1EF21DE81C0920191E281F061F6D1FD61E3A1FC61F9C2031203A1F4F21C9200F23292EE22AFE242B22702000219A20632226213221671F672029203B20231FA31F7A1E3020C5223A208821C11E30207C1FB920AC1D411DDC1EF31D091EF81E271F821EC11D3D1F2D1F881E591E0E1D801D791E4820EB1E321F751FDF1F9820881E801F4C1F9520D51E3E1E2F1FB01EE31E2B203E1F701ECE1F8A1F7C22A91F0123422CCE2BA026C023FD21CF2021220D225521B522A721E0210E2005205B204E20A91F3C1F2A206A20B422FC1FB91FE420FE20EE1D1E208E1E6C1FB620091E951F2E1F3A209421801FC41EF01DC41EAC1E751EBE1FC8207B1F9A1E1A1F691F631F261F851F7A207520111EA0216A20921F981FA41F1D20C11FEC1E3A20D521FA22B82D082BE7246724A121072245211921E21F6B22F422C22122219C21F51FB920F41FE51F5421391FD0215821901F03209C1E331F3C1D8F1FD51EFE1F121E761FFC1E9F1F0F219E207B1FD21F641E521F361FE71EEB1E4D1D741EAF20291F4F1ED71E761EFF1EE01E1C1F581EB41E7E20D51F241E851E2B21B81F0F21C020C722D42FE02C28243F223721D521F520A7208F22C52164209F215D20CD1EE21EA21F3D220D205721C1210222E91EBA1F59201E20D81E571F911E131F601EA61EB11F8E1FCE2171212D20371F8B1E401F901E1B1E761F051F0A1EAD1FE21E6D1F301DD31DA81FBB1D841DC41EB420E61E5F200E22A71F7D1EDD207C1FA1214A226523152E482B76257921932236239F22682173211D20F420132278223B1EBD1F371FFD1FB61F741F75203020D021991F062125204E1E6A1F1D22811E701F1F205D1F8A1FCE20A720BF1F52204B1FA11EC91EA71D141F19200E1F1D1FC01E0620701E141F5B20B41E9A1E181F5820A31FBD1EA71F591FBB1F6922A120EC21A822CB223C2C442C5225462342229120C222E520B621B52239217F218D215E21AE1E5E1F651EA120AA1FC31F3522EC206921D41FCE20371FC01F151FBB20781F56202021E71F58200422AF1FC7200320591FA71DB61DB41E6D1F871DFD1D6B1E691F8C1FA41FA81F821F8F20D91DB320D21F301E7A20D31F132043234A21AC20B7220524612DAE2A38259322D8221C214321A720CE22D0205F1F0122E11EB520C21FA7206E1F13213320DD1F86208922E91E242136206B21F420E21FA51EDD1E5F1F2221CF201E20692206215F21F420851F6F210D1F031F9320181FC31EF31FF91FB61FD41F9C1F9F1F4220E01FDF1FDA1F4521DD20371E931D56213720D3218221CF23CE2D422BEE22FA214F21022227201922FD20602175218220E71EB31F301F7120591F7B204920ED1FBF20D31F721F581EAE1F11205B1F3320861FA31E111E071F8A1FBC207F214C217720DE1F8C1E9020241FC62098208320701E5420211FA81F3E20DA1FA5204521341E6B205D1F2D20C91FFF1EC5201C227321BB238D211723DF2DFB298A23A821102167200E21F820B421EC215F215521FC1FF7209E1E6420941FCE1FE01F802034211C20101F3720BD1F401F301F1621FE1E1C20951E5F20901FEE1E9F20DA21A71F1720C020FA1FD11EF61FB61FE51E761F061F361FCD1F101FA11F26206A20F31EA320CC1F8F20C720B71EA4216F200720A1211721C7234F306B2A86231B23C821F92130226C21C4211822272170218A1F04205A203821901F612094211121C3213420EC1F511FAA20CF221F21391FC41D501F3520921F352119200D22992085205E2033211C211C203420CD1ECE20771F0120C91FC91E0820D51FDB1E8B20321ECC208F1F7C1D9920C120AA20E31FD51F5F204F221225D92FEA2A342453224922182226224F20FE2118226521EC21F7209820A41E211FBE21AA21D521EF2158215B208E21962092217120B31F7820CF1F2A1E5821551F021FCE1F6C2103215220022039208E1F011F681FF91FD21E8620E81EF41E622004207B20CE1E671EB31EB220FD1F961F961F5620B920BA20BF1E9520AC220925922F9729A8240E21A2217B204C20262186210C2336218C213120BE2076201C21EF1EC520A6202C21A3211420CA210E212520BC1F1B20B41E9F1E6C1E931FC61FE31FA920CB20BD216B21871F741F2B21EE1E861F231F0E205B20F920061F851E941E3C20E720E2207F1EFF1F841F9F1F8F200A20472071210E21AC21A620E423F32D462AE6241924F01F69202B210E2132202C2118225222A721C220B121FF1F6821372075209C1FE221641F41210220D9208A1F2421BE1E2D1E2E1FD41F9A1FF11F6F1FD01FAC22941FCD1F0D202521091F92202B20DE1E0E1F0C22A81E741EAB1F0C21C920AB1FFC1F4420B320B720CE208220391EFB235320EC20C0232C24182EC22B7F230622A7203D2159231521D720FA212F2179226C224F21F0200D21431ECF209321C220F81E76219E20CA1ECF20A320E21E7D20D41E641E001EA71FCF1F4220E420BD1FDD1FCB1E511F1821081FC91FB820F91F511F5B20B520491E06204E1F9720C81E0E1F4421451F6A1FCF22A31FD81E5621B620382047230922BD2E342B9125B820EF20DD21D020F221F5201E223D2205213B214B208220DD20642149205321252181213420DC212C213B1F7C1EA220951F421FB61F3A1F051FD91F1B217E20151FB81DF11E8F20471F35209921A71F8F20551FE520A61EDB1E481F87200E1EC01E35218921DB1FEC1EB320BD2001226D205120DE1F4022B523762E2B2A4D25F021AA22C0209E21A622B721BA215C1F77231020022289202821A81F8921FE2031220521AF1FED1FBF1EFA201A20D71FCC20EE1EC91F901F2E1E4B1F7720A220541EEA1DD120E51FCA1E511E2E1F9F1F2E208E1F791F791FE71F101FE420CB1F881F441FCB1F9B1F981EED206B1F4820BB2017211721592188234D2E5E28D62359210E21FC229521B91F0622E51F39209C203C1F44217F20BD21A2207C205A21B420B221E21F7E1F1B21C120EF1E1F1FF31EBA1F4C1FEF1EFF1EAF1FC61F212093206A1FF41D1E1F171F101DF71C4120531DA91E8A216221D4205220891E6B1E7F1F201E47218B1FC11FA3207920422084203E206720CE220124422EE3297B24C122B122E2218B211E20E2219720DC1F2B21C5217A1FBB201A211F218C210521BA1F0C22C0201E1F7C21FD1E231F8820C9207E1EEE1E0020F91EA21EB620EE1FDF1E431ED61E721F9A1FE51FE41FEC1FC91FB11FF71E991ED21FB41D6C1E3C206E1F1D1F351F661EA621492024209C20B221801F861FBD21BF23C52CB82A512462229520B7215B222221E6203C218F206820061F4B2013212E217D211520E720E21FFF21C021CA204B207921C91EDE1ED31E041F68208A1F7D1F301E9C203520691E7F1F5F1F4E1EC71F6A1E3A20D9215C1F9220781F841D591FA91E641FFB1F641EDB1F041F3521CE1FF51FBB20BF20CE1F522019228E21AA22282D8F297824CB216C203E2106221A218E1F912177201C2163215320D92088224F1FA41FC721A41F4F1F2120711F502066201220921F0420C41EE51F0220C01FAF200E21FA209220441DAA1E40202820CC1EF6207620BA1FD11D5820821F6B1E6E202520CF1FB920E91FAF203B1FF21FCB212B207821C9207120B420B5219323D32C6229DD231F2251215120C92013200C21A5217721FD1F7322881FD71F3422C92021224020B31FB41F8F1F2A20A91FEC1FBE1F651FF21FB91EA420091FFB1E921E8C204E1FD81F3E1D721E4D1F0920581FCF209A1F5A1FDD1D7220941EAA1F7220D21F611F2520501F6C207E1F0C219B201220CB20BB1F5E2039200D2106231B2ECB2CEF2418220F225B214921791FC320492246212F20E121A41F5520E120592220212A21F2208A1F4620541F8220E8200220CF1F9A1F3E20FC1F711F241D031ED520B31FC820921DCA1EFD1F781F4D1FA91F7820381D641EDE1DBC20BB1E271FCE20B71EDE2017202C1F201FDA1E1820362183207820BE201C22AA203523972F382B252423213E206621E1202D211A21E82059208E2157201F205E1EC21FD0213A208A1F48216021131F461EAA1DB71FF11EF01F511F141FC51FF81D771E7F20B11F1D20F81F2B1EFF1F1D1FA01EA31E9720531F3920491F0320541E5E1F791FAD1FAB1FFA1EB61F761F611FC01F7B21D11FEA1F15214520CB2144217523E62FC22A19252E20C821E9208B2093225A211121FE20B920E120871FAA20872036215F218D21CE20F720901EE121CB1F7E20AA1E84201F1FF51EC31FBE1F061EED1E141FDE1EE71F651E641E6720A91FE81E2A1FA220272113201E20FB1F432000219C211520062013202521761FE51F4020E920CA201E218E1F0D21A6214C23D62FD12B65254B231923D821E1209B21542032229121EF223E21E220482229208A22862265210A21C321A81F711FF0215022FA1F0E1F151EBC1E8C1F931D661F9C1F692046208F21681F6A1F8C20E41F371F5B1F0E209B1F471EEE20C320B31F0F218D20F82187200B1F74222620D1217020201FC621E622ED208E211222A5235D2EB82A942488229C212B233B21D01E28200722A62147226920F4207F213322C621CF210B228B215B22791F2A207E204A21F91D761F291F741E9220C71EA41F951F0C209D2006202720C11E201FD820671F7E1EF920191F2A1FD4206021E0217220B320B41FAF205A1F84216121392015213A1F622122238520D420AA22B7249A2E1C2AE32440214821E62115215C20A02152216D21FC209620CF1F3720EF1F92217B20B921D320CC1F5D207121EE20681F751F011FBD1F511FE41ECD1FE51D631FAC21D81F6621EF1E3B1E811EC81F5C1F8820C820771F40210521971F30221A22BB21A4202D20742152215220C71FC32158206B204E20E121DC2081214524B22DB82A12248F21F9208420C2204A201320C82100215621FC1F9E1F012183210A221922FE206D216A204120B520E81FDE1F411F7220DC20BD1E221F341E7120ED1FE520D320DD1F281ED21D531F131F591D2B20FF1F0720D520E5202C218020FD20CA1FFB206D220A1F6C217621211FB320DA1EA9207520081EBB1FF4210823CD2DAE29A12499217621C220B7204421B6202720CC2166205120F1205821E4200921232149202621F220AC20EC213121EB2076205522AC20C11DE01F3E1F5B1FF11DF71EDA207721631F671EC720691EED1F1D215921A21E02206D213421DB2027215A210B22F31EAF2043205B212520AB200C2067213C2125213F224F227623792DBA2A3F247A23FD208A21E32164208022A5229B2149219B209B215421EC1FA122DA1EF320C5215A216020591FA32096206F1E8C1FDA1EC01F17206D1FC220AF1FE31F4820CE1F611F601F551EEB1EA71F8A207821CC1F8C202E21F3213C229E20DE1FF11F761F07207A216D205521A2217E20402080202E21A9213422C223732EB12A90244C21522140213420EF20DD2110210F228221D3202F1FE21F5C1FFE224721D820452041201D1FA71EAF1E6D218A1D901F391F071FE91F8B207E1E441FEA1F53201D203C20861F681F3320791FC51FBB20D31FD02055212820E3207020011F0420D91EEB1F6420E31E0421B71FB51EC72014210D1F88205B204C24F92DD32BCE2554218D20EE20C3200E206521C621C521D821D1215521C01FBF20E22018208420182195207B20A81F5A207C1FDB20FB1F591F4A209021211F021ED41FF01FF81E1F1EB41DF31E101F2620C51ED2205D20C620581FF820DE20A11FF2204B20941F851FA4202B2002202120FA20D420A320EC21F420FB21FE21DC24612F0C2B3E25D0220E21B9204321A91F4E20872001212E212821DE2000207D212A201E2152218F21F220EE200A1FD31EB91FC31D1D1FB61FD51EB221AA1F3020321E8E1FF21E1A207A1EF71D5B1E3220041F0420B5201D207B1D3A231E207B200A206A1F6A21A620331F23209A1F4A206420771FF520B120182185209821DD23252EBC2AF624D520B321CA21DA217921C51FE3202C21FF20CD203E20392085203E21E81F8C219820E6213322931F7E1F501F2D1FDB1F7E20761FB221D020DA211A216A20D020C920F01FF91E141E9920B21EE41E6120991E34205B2018215A205B2087201E2187207A20422051208020F820E01F7B21E120D51F0B215122C223EA2D212C5223352213221521BC20D61F83215F200D228821F4202B202A218021AB20E520F81F3321A6216721B21F4B20CB1F5D215A1F1D20091F8B21971F7E1FF71FB0204221421F6020531F1820F91F72200F22861F2B20DA1E7721A5200220E220BB1FDD20631F862037212A1FCB20DF20D7216E2170203B1F23225A21D5236A2F2C2ABA2456216320DC1F0A20A51F33200521F41F5921E41DBC209820D51F7F21CA208120531F5D216A1F041F7120E31F1020C91F941FC720C61F601F6D206320BB1F4820BD1F4E1EA41FFB1EF41F051FD41F5520FB1F101EA622502052222722C1208B20281FC31ECC204F2049207220502063201D23E41FC91F2D202023C02CAB2B16253622B1201621CF21DE1F6722FF20F620D720DA1F671F2C20072032225D21B01F6B20CF1FAB1FDF1F991FF11F2E1FCF20921FF3204D20F71FE01FC71F44201B2090204E1EE21C0F2098218E1EB51FAD210D217B205C20551FBA1F4420BF219E2076206921FF204A20512026224B206D1F5120C91F2420A022D123442D9D29EC24942081212521D62114218A219520082123228E20FA20491F16215D20142137207C21CD2075206C1FF820D420D31F1021201E4D1F351FD91EF81F751F721EA81FA81FC31FE31F5820761F471F48208220351E501FEA1FA31F692036205321391FE3208F20E01FC6207C1F18208721CC205A204A1F5E215322D322BD2EBB2B9F24B5217421DC22DB21DF21E5205622842117225222A7212021E5206121F71FC8219F20802120214F1FF1217A1F7A1FFC1FAD1FDB1FB71F5520081F121F1220E020AE2052200E201F215422541EFB2002205720812190214B1FA020841FA621762039211F209C21572150208B205221642158213421A8215A22A2249B2E9D2B8D257C21A920E4206B217521AB2134221A223C217220522008212B207E205A20781F6F206520D81FF51EDE1F191F041F9220C61D9C1ED91F1620831EAC1E9A1F8D1F0F20DF1EE11EF71E751FA31FE81F80201B22F01EEB1F8E20041FA6201421B2208D21FF1D811F2A2125204821CF1F11216F21C51F7E21F8210023652E3B2C40264A2153207721C8200D20AE217522CA205222BE214E20811FA221E420E81E2121D420AC1FEE1F3320AF20AF20861E49201520221FA521911F091FDA1F67206B208D207B1DE21EFC1D7E1E181E9920D520AE2013200B21071FDB203B2108208E2099200E211B22851EF11FD92024211B20F91F32211622B420DC23172FE52C94257D226321F62138225522DE213420BD20941F1B21C61EDC1F5A216C209D203821131F2C20201F331E181E151F5E1F241F0E1F2F20CB1F54217A20E11E99203620561EDF1C381FBB1EA11ED01F851F4422D11F8A1EE91EF71ED5215621B920821F8E1ED41F242289200821BD2195210D21F020AC2091202F20F221512D802BF02449246F214D2125203D214922B120E11F01219B20281F741E9E1E5220BC201D2119209E20F51F1B1FCB1F3121051FA01F7A1E161F1A207D1EF21F6D20A01F751EC81E171FAF1F941E471E57204B1E0622481F571D021F761E481EDF1D341E6D1FC021D41FF621B421A421C42048205120D91F4520BD21A022A024D32E032CF024E8215222D920D720532196217D217B20631F9220E91F941FC01F872097209720652150209720851FB8206320F11FCC1E2D1FE21E721F371F961FC7200F1F6F1F7E1F581C4D1FAB1DBF1EA21E221E8A1F801E581F531F691F56206E1E011F6C20572117215D2098208520DF20581FB520C620BD1FB820012248238E2C9B2B6A23E22061206421342046224B200821502197203920691F101FFA20DF20E71FE5201E20FD203E1FDE1F2A220B211D1FBB1E9E1E4620621FF91D791FEA1FF11F6D1ED11E8B1DF41EB41D741FBA1E9C1E861FA51EA31EBE20591EFC1FA41F4620D91F8B20FC1E96210520F41FF221E720FB1F7A20D020B71FFA21FB23F52BD62AA4257A222E2283225B20BE222F20BE22A521921ECB1F76217B201420F22046206B21F020C320B4202F20AE20DD21EE1FA11F06200620F81E9C1FD31E521FC21F0E20721F731D67203220121FAE1DF2204321F31FAD1F901FFA1E3421D82051217B20601F7B21F420941F0820182092206721C121E91F372069204722262D712A852510224720F720A71FB81F4320981F03202520E71F6020DB1F83201F211E20D21E4C21811F1B21352054213D20731E3A20EC1C331EBA1F8B20551E7B1FEB1F6F21E91F8F1C691E481D7120581D931E5220611FEF1DB51FD32079206720321FDF20DA21831F482222210821AE209A216B1E1E20AD20DE2085224022962DA52AD425C0214421BB208E1F2B1F21214B2137207821CE21991FEC1F4121A620B01E29203020F220C51F131FF01EF71E1B2066219D1F311FDB20E9202D2037208F1FA91F7E1F131D6F1D2E1FC11FCF1E3C1FDE1FB51E3620411F051E1120BE1FEF21781F7E2037204C200C203921D620DF1D0420E71F761FC5209C21EE23042D6A2A1126C2213422F421B521B21F34223C22F72008203121F720A820F0208A213720D120B3203523FC202320D221EB20C3210D2192208820AC1F8C205920B020781F89205C20BD1F1B1F871F5B20E120C01FB41F651FCA1FA41F0E1FED1E0B1FC0217B20B51F622129219B2061204A208A20E2208220DC1F302083210A23712C3E2ED7244F21832166220A2010213E206F20B0236A21F220981FBE1E04212A20C31F1F22FF1F8621E620792051203721C820F920AA20BD1D601FA41F541FB7201420E61E171EF91E6F1EC81FB81F481FA71FC2201F20461F0A214A1F7A1F1D20BB20DE1FB41FCD1E6721842031202321CF1FC020D91F501EEA20CE202522BE2D252C76265821B721DB1F3E215320F6208D216D218D22CC20E51F5721BE20442127205121DE20C6203B1FA120B7216B207520A421B71FB61EF3207D1EBB207A1EEF1FAC1E731EB81FB71D581E4B1F8A1E161F7B1F001F571F2C20C01F411F80208221DD20FD1FF11FD120FB20192138210D1F7B203B1F591FB0208C21CB23012E682B3724F4216C1F5A2020213F2011212520F920B220E1207B205A209A21132231209021ED2029210521352037205B2092224420541FD21E861E671FB71F331F6A1F461FB41FC91DBB1C7F1F431E791D111F2121361F72203B20FC1F9B218A21DD1FE91F7A200521AD1F1620611FC4204B201F1F8D1FB21F9D1EFE21C924AA2DAF2A0D25B32190220C239F22D520E822F621D321F81FAD20B020C81F9121B5215920F71F10210C21EE1ED31F5E1FCA20221FC920EE1E5B1EC21FEC1FE31E2A2162204F1E8B1F791E811D281F19204F1EA720FC218A1F341E951F431F3B200D208421BB20851FAB1F871F35201820712034217B20D620CF1FA0209F203222402E582B9724D4217B238F21652328227E212D22FE223B22C620CE20D81E4E202C206A204B20DA20CF202221601F50213A20FC1FD520061F702005205C202E1F8F1EA120DE20981EF81EA91EBF1F661EB41D0321201F5C20B11E3220DA216E20F41FBC211821FE21D71FF2207420DB2072220B2128214220371F85212D226D22D62DC92B84266723CF2050210220F8229B215520F8205D21AB206E1E7920E620842157200820C620AC21A9204821D720CE20FC20A5204E203B21E120461FAA1F9A1F3E217E21C61F711EDA1E2520AF1FE01E9520F420B11FE81EF51FE1209720871E2521F720F71E151F5D205921FB1EBC209C1FB9202920411F1D2157214F23892E332BDE257D23D222AE219421AB216C20FD20EC21E8204821F7202420F51F3720921F922166213820841F6D1FEC239521AD1E611EF41F881FA9206D20BD1F3821902029202020281E1F1E721E741FD41E5B213820CA1F86205A204D1F7120FF1F7F20A420E820BE1D7D1F2E1FD01F9B21B91F04218320C81F8B20B821A523A12CD12CD926AE23262324220A212022D7200823912206211C227A207B2152217B20F31F851FB421C620EB2188206D209D1F5920CE20831F4F219C20EE1FFA1F4221622053218F20F11DB41F691F481F0420BC21B5219C1F6D1F371F881F321F8F213D21CA20E720A020A41F281F8A1F1721C320CE1F741FF31F97204221EE22012E9C2B78253C2224239421FB2071212F229F21E61F512050200420C2216321491F8F1F5D1F2A234E21F21FAE1F0D20F820E71F851F99208620EE1D2C1E6E1EF21E45208C20C51E391E2B1EA9203F1FED1F0D218B20BC1F8E1FCE1FF81F392290214520B61F122101209021F01F5920A41F681F6720612022200B1FD4212723782E4D2A6025ED21C71FA4211B229D2121224A224922332160213C20FE1F2321A4210E2057215722B521DC20E12296204E2265217D2032205320551FE51EDF1EAC1F4C210720731FCF1E751D2D1ED91FC91FAE2152216B20C01F3020F51E30211B21D4201021FE1FB61FD020792245200220A31F92210E20B21E3921BD209224FC2EDA2AB8245E2130210721EA2023228E204B214B210A200A2063206C205B213F210B20B51FEE210122C9210D22C220C71FAE1F29219120B81F3520C71E2B1F4D1E751F541F081FDC1D0E1E801FFC1FD01EEE1F8C21C52000201F210C1F491F9A1F1321AF1FD31F271E5120B020FE1F5220E51FAB21772185209320E5214D24082E1B2C4B259E22B020A0221A2182203D226720B52143205E20EE1FE51E071FE81F582092217122BA22902063208E229C201B1F8D1FAF1FCD1ECE1FB61E091F6920A71FF11E771E531D431E4F1E951ECE1D251FF920421F421F351F6B1FF81FCE1EB21F951FEB1F2F1FF61FDB1F812125212E205A20C020071F8C207E229322D42DDB2BE023B522B420A120E620B020ED20B021C9210A20A01FD41E06202720461F9620C321932092215020A0204D21441F6220951FEC1ED31E201F1D20251F9E1F27205F1EEF1D491E0E1E4E1EE51D7E1E9C1F2321C91F191F4D20BE1FEF1E841F171F4E20CF209B1FB7200F20032063200B2134204C2007219D20F721C823EB2ECF2B27251A234D21BC2143206420DA1F0422E820B320D8208A1F9B1E521FD5203E20D720BE1F722122201920F621B120CC1F1E218D1E571E1420C41E631EA91DD01F2D1F0F1DDD1C451D841ECB1C5B1EDC20B71F2B20EF1E07214C202C1FE81F011F5F1F40205F1FE0208720B01E4A20F720FA1E472104201A2192225124DB2DDB29BD24EB20DB21BD21611FD21F95203921C9204921AE1F791F141EC21DFD1FD91F65208720C420B51F1B2018202D21F11F0D1FF01E571FBA20A4208F1F7E1F5520F41F701EB31DB31D391F8B1E3B1DF0200A224C20231FBF20B11FA71E5B201B21F31F4520731F7E20321FB41F2420E2205A208920232005217521D823152E742AF9231522CB1F8621082072212B229F20472132218E20B21FF21E7E20CD1F2820EC1F8023E120DC20D01E3E20DB20D71F011FE11F0420C11FA81EE01D231F2C20A11EE11DE21E8D1EE61D3E20F11EC222AB204320E31F32203420721FB61F611FDB1FFC2151205821C01F5B201B1F0A2117226A23C9215721432183247A2EC92A36237D20C621B820C820351F6322F120B921BA216B223220CC1F1D22C3210B20F9209C1F9722FA20191E2621BA2141212920E71E321F06203A1FC11D0A1FEA1DD61D661E3C1D161DC41E781E851E0D21811FD41F3C211E21631F3D1E79200A20EA2092203D20C620431FA01FA321451FAF20A821911FAE200F226F257F2EE12A55239A2257207B21AE2014209521F520CD1FC8208720D8218020492104229E1F8E202C20FC218321DD1F1C20FF1E2B1E2620711FFC1E041F0C1FD31D1F1D1C1E4820C61DCB1D7E1ED51BCE1F1E1F96210720681FEA1E7F1FF11EC31EDE1F6C208C1F2521961FC6211E1F1C201C1F7020FB1F1E2242205521A422E523FD2EBD29A72481215221271F83213B20861F95203E21C3211C202A202D214920A31F0F21BC2076216621C51F65208320FA1FD81FBF20741F211F081F681DBF1DDE1D321DE61DC01D661DF41DF41DDB1DF91E6B210822B7200020D81F452031202D21C6217C1FA71E471FEF21C920DE208620AE208A204A216820BE20F6216B24B02F1E2BF2230F22ED1FCF1FE91F6B20DD21F920791FF7203920CF207F1E321FB21FAA1EC1215C2157213F202721D21F0A2291201220AB1EF61FC91F1B1F871F031FDD1E911E981D611E241DA11FC91F6420691F6B214820BD1F7B20F11EFE1FF520C020B81FDD1FD220AE21E11F20207520ED20C51F47208D20972147229F22472EB729FE233C21CA20572036205F20D01F6021881FE11FF21F9F1FD71FC220EE21B82057204A204820152013206021A61F111FF91EEB1E771E491F281F631E471E521EDF1BB71C001C181F8C1D1320A81F22206F212A1FE11E911F1D216E1F0D21A720921F4E1FF81F801FAB1E35214D218A200F20F51F032015215521DF23F82ED82A5E24F0221E20661F7B2073206720CF208720C220D5211120281F3721242170204420F820701F6820411FB81F022175204D204F21E41E491FC71E091DE11D041D021CBB1D511DC61E921E081F671E591F931FFE1E0D2165220B20F61FF920DB21EA21071F6B203D213A208E1F35206B1F6F1F5420CB1F3D21F1217824B52EF92B6F235821F3209720C8208020CA1FAA20A320AF1F87203F20451FC21FE51FFB1FF71FD620C61F0520D91FA41F9D20301F531F361EF21FAE1E861EA81E701D641D211CA51DE91E811FA81F071FD31F85201F1F3F1FB3210B20B51FF51F4E1F9321822107210B2042205A20C51F24214F1FD21F202173211A210B235F23BA2E87292B248022E02065203121B41E05209F204C202C21ED1FC6216C20A5214320D320D0205221DE1F8A208A1FB520D922CC1E4C208F1F6320241EC11E1D1F611D031D281D6B1D471DF81ED11FC11FBA1E4321B91FD51F431F05204B1EE41EC3216020E12028209E1F0121D6201E209D205420BF1E9820921DA91F2F215D23D22EE1295D25612097217D20D520F020881F2522091F441FC9209721FE208920D8214B1F2320B620AF21731F741F2E20BD21FF1F611F381E281F861FB71E1A1D041DF51C3F1E2A1D671DD81D081F2A20121FB21FB820C31F6E1FAC20761EFC1EBE1E8821F81DAA1E581E7A1F331E66202C1FB721DD1E4B1F17211A20AE210F23192DF12969255F2176204520B720532048207C21642025203F207A211A1E051FA21F9B205D21F01EAD21DC1F321FFE1F1A1F321FEF1DCE1EA11CFE1FD11DFB1F701EDB1E2E1E2C1E5E1DA31FE01E361F8F1FD41ED21EA41F931E9320D31D9A1F1C1FD02007206D20DD1F8221D31E63201C20C91F2A1FFE1FD220C0228B22B423D62ECF2B64250F212C214A21C721D61FE7206E210B21EA21BB20E81FD420ED2096200B21D0216520D921B01F271FED1F4621AD1FCB1F771EB41D7C1E601F1D1EA51F811FDE1C441D7C1E7C1DFA1FBA200D1EB720F720711F3E1D3620D61F8521C01F4C20E81F9D203B1F2721DB20DC1F0F1F2121BE1F1721651F332184207E227E2DE22A99240E22CC2019208520E5210021C522B221AB205D21ED215720E021A8200321B4203921CB20B31FE7205B210621D01FBE20B61FA31E4B1F9C20AF1F8C1E201F691EDD1E491E3E1F2420ED1FCB1EB91E0420AB1F3A1E0D1FA91FC420DB1F931FCA2063200E1F9D1F5D1FC31EE1205520DB202C21A11F61210D22B423722F7B29BE22CC21EB202E206621201FCC204222382178218720A02012206420012142205820D3216721221F491F31204420B51E7E208C1E51206D1FB11DE71F941E331EDC1E0020591D2A1FCA1F251E2C1F591F1820F41E0920B520E41F761F6D1E841FB01FC11F6F1E9F1F1E1F8C1F1320411FD61F76218E1F712158216723742D802AB4235D202D22C42104219C207521972201200920C7212020DF208620FC20CC21E420EF209E218E2014208D21EB20B31EF81FB61FFB1E041FC51FD91FCD1F7D1FF71ED81D8A1E141F041FC41F0020E5206820CA1F111F8A1F2A207E1E0D20CE1F3C1FDB1E571FC21F6F2010207F22E31F522089203320F5207E213924682ED8297924F8227520F42010210020C81FED209821C720AB21A21FAA204B205E22A920DB20FE219520402111206121741F811FED20DF1E4620E61F051E561EA11D9F1E401D371E5C1E381E2F1E6F1F2E1F08204A1F201E1120BA1F321F551F9F1FF620B21FEF1F9820CA20781FA11F701F36201821F31EB020332124212622F42EDE2BAF251222A520E1209520D120702025214321A81FE31F351F8A1F56216B1EA91F712069203321E62106216620BA1E1320F7200B21CD1E4B20581E691FD71F521F5E1E291FB41C891E9E1E341E1B1E1720D620B71FAF1F5B1F6F1F271F5C20572081201C2019206521A71F35201922A91FCC1F7F20FA1EA21FEE228823D32F3E2AA924C521D020CB205D21B01F2022C21E23204C206120B21F26212E21271F261FF020C01F12214D1F641F3720CF1FFF1E4F209920741FCF20DF1F6820C71F931FC91E681E621D611E4F1FF51EB81EC91ECD2020202C1FFC1E6E1F871FE51F8620E81FF61F301F1921881EC71F7C1F4C1FBC205E20A21FB421D921D823612E672AA1223021AF22E32025214920FA20E520A91FEF215F21A41F1621FE20DE201722BE1F6F211E22D320481FD7203321B01EF51F731F6D1F4A1F511EB41F411E3F1FA81F9520101E611E121FBA1F901E851FA021311F0B1FE6203020F320E320D51E9D1E631FD01FB420C020E61E5320A320261FD51F911F9E1F1521D322672CFC2A6D253E217E21E2220C200421B1205321EA21A8208D20D8218A209D1FF3214422F52166215C222E20A21E68201021991F051F30208520401E8E1F641F071E8A203720251F6D1D7920F51E4C203F1E88206D200C1F221FE520AB1ED121B21E4F2103210B20DB1E2D20D31F1D207621F31F381E861FB41F3C21A121ED23202F862CCB249F21282102234A204C2141200721DA20F920E41F1E20E31E881FF81F6120C1204C1F8720E81F651FAB1F82210B21821FF91E8A1CF31E331E1620051F301F831E8F1FF31E301DF8205A1FEE1D021FC41FAD1DA31F861F6E1F9B1EBB1F8B1F091FEC1FA31D2420E31FC31F421F8E1F831F3B201F2060201C21BE22052FDD2AB02428233D203620302026218F201022AB2084200B20B71F49219621AF1ECA206B20A420AA21C220AE1F7820A11FAF1F5220EE1D081D1E20A31F2C1F841E5D1FCC20B71EC61DEF1C0D1F891D7A1E791FA91F1E1D251E4A209D1F1D20CC1D4A206E20AA1F28208B1F651F371F13211E1F471FC81FC21D3F21FE214024532DA92AAF24B722512191203A214622E321972148201F219E214120DF1F84202C21A320DD1FC51FE0202521DD1FB61F0220C1205B1F0D1E0B1F861D3120F21E941FFC1E081FEE1F151E561D991E7B1E411E721F5A1E7D1E551E431F5420051F4C212F20431E491FD7200B211F1E181FD41F3D1F541E3A207D1F5521B422D723172D8A2A6A251B21A120FB1FAD1FED2102214521E71F0D21C01E991F411FFC1FDD1FBD20AD1FA92080204121581EAD1F91227F21AD206D1F501E5C1FE11EFE2023214C1F351FEA1F251D1D1EBC1E601E141F461F3420C320CB1EB1208E1F7F1FC71E0521DD1FB21E661E6D203F20D21EC01F5A1E7F1F36212D20BC20BE22AE24E02DEC2A86247C22BA20522103211D2050200821C81FF5200A1F1D20FC1E6F1F271F701EAB1F561F981F0D1EA81DD41E6B204820C41EB51D251F0D1F181FB91FE61F3E20E61F6A1E9B1D2C1F351FE41EAA1E411E2F1FD41D871F0220311F001E2E1F631F9B1E211F0C205D20541EFB1DC21E6720FC1D8C1FAE1E831FB81F9723BE2E882A8123DB21BF212921292048209C219B1FA8206020F41FB21F421EE51E2420E51F0A1F05210D210320361E90200F20B01F2B206E207D1F8B1FE91E3C1E55206620EE20061E881E841DBE1F551EAC1EBC1E3A1FE91DDD1E9920631E4E1F831F801FB21F671F651FC01F5520EB20B61D421E6D1F1F20F32086210523F423252D902A1524522034213421D31FD720BC20C120F7215221201F961F6A1F282079206B20CE203520D51F69202F1E9E20F21DD21F4720D11DCB1F511EF51ECB1EB820DC1FF11E811F891F571D9D1F661FB61E3D216D20E41CA41FBB1E0E1F421F13218B1F741D78208C1F351E491F661EBA1EED1EFC1F6020CE1EB321E021AA249D2DB12AD0244F219D2057212B21CC208E2221216120D11FA11F711E611E18209621D71F53207A225E1FA220D91F9F20731FA620A121691F801F021F761FA11E0320A71F681D471F3D1EE21D7A1F6A1F221EA120801F631F231E6920EA1EFC21A01F461F4A1F5E1F691E8120891D771F0E20C020F01EED1F7B207821F01FAF22682ED82BDC2411215322B321B121F520FC227C22FF20DC2078212E20981E9A20D620DD21A1215421191F2821EE1FBF200120A71F8321A21FD61D3C1F9B21481FD31E601E181DFC1E291E861D571F081F591EB520EC20DC1E861FB41EE51D921E061F8E20F21EFF1EC21DB71F351F981F8D204F20DA20681F081F3C2030234F244C30EA2BD524D122E21F9F22D8224A21D2217F22EB2113237C216120C51F4B219C1F61204A22EB20161F0C20041F95204A207F1F8B21BC20581F30206A1EC01EB51EC31D8F1D951C0C1EE71E341E9F1E6120A520991FDA1F3D1D7F1CFC1D461E26203E1F9E1F1E1FF61D551FA51E471F8A201F1F2822C920C51E4E1F75222724152E +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 4024 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 2 2 0 20B3 +# DONE callibrating + +# First turn on LED +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 4031 +# The capture loop does 1. report finger (403F) 2. get image (0009) +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 403F + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 55 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 0009 + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 042817222D2226209D202A20071F4A1FBC1F031F561EC31DDD1D7F1D211F861DBF1CAE1E6B1EA01C5A1EB81D891FD81DD01E2C1E011FDE1E4E1E1E1E0420BB1E141DEB1D3D1D531BFA1B911D091DC41D421DB41E501D621DF81D5C1D411CA81DBE1EA11EB91E2E1CDA1D411D7B1C021F211DCA1F1F1FCA1E0F1EBE205322902DDD2A3A23DA1F7F218521A61F4F20B820BF20AD1EDB1F021F0A1EE11E891E1E1E6D1DD01DC41D7F1DF91D201F681F5D20FA1D921F1C1E271FA71E2E1F9A1F231E031E051FFD1DB21ED41C1F1F561D7F1D5B1F911E6A1D521DDD1DFA1F1B1FFB1D4F1EAD1E281F181E071F381F6A1EAB1E801EE31FCA20CE1FB320BC20B3230B2D932AF7238D228D216B20AA1F6F2034211D1FF52154213321EB1FC81E691F681F001FCA1F271F0C1EE01DB81FFD1EFA1EF61F7420521F2B1F691F631C4D1E931F2B1F1620181F251F3D1ECB1E951D601D321E161FAF1D7C1E051F7B1CBD1EEE1E0B1FD11E981E7E1D241F061F311FD41DF41DAE1E3F214C1F8120BF201B24342EE52B7A2799232C219F21D220D721BA202C20DA1FB41FB01F1D20FE1ECE204C1E891F991F841F2521E91E351F711E1820F61FBF20DA1F5120CF1E2A1EA51C8B1E3020BF1F9420461E051EFE1EB21D371D7E1E541F461FAE1F0D1FCF1ED21F1020F420741F8B1F991E8A1E2B1EEE1D301E221F8E1F3020AD214420AB218523DF2D082CA3256621B5207B201F219C20C020202152201E200B205B1FB51E4520E91F911E03200F1F531F2F1F261F8421BD1D5E1E6B21F31EE81BEA1E881E521E3D1EE81F8C20A81F701D841ECE1EE01D2F1F0D1E67203D1EE71E331FDE1F701E801EE91FC01F8D20941E791F9E1E5C1FFA1E621F321FFD1FCB1E502022229422A72C3A2A5525EA21EC22F61F152243207B20EA21B120F3205D21361F8A1FC41F2E20FF1F911F0C1FCE20F41E141F1D205C208D1EBE1FEA1EA01E6C1EFB1E711E8E1F7B1F13200B1F571D7B1FB11DF31E4D1E161F3A1F841F921F941E371FAC1E861F1F206520021FBF1F771FFA1F6C1FB51FA91EDB1F87216F20F71FF0222424C42C7F2A3823072278219A2096209C21B82028207821AC20911F1C204A1FC020A8202321981F3F20801F4721DA20631F8F1FD41DD21D191F291F7F1FEC1E8620D71FF11EED1F511F691E0D1E421E281EF51D3D1FC920B01E971E851FC41F971E091F4520861ED61F0D1F5B1F541F041F3E1F6120D31E4920371F3C21FD21ED21752DBF2AB324052289229520E41EEF1FC5209C201F21301FAF1FDD20D621D61FAE1F03217320DC204B217420ED1EE61F8B21FF1EF31E621FC71EB61F4220761FD91E061F641FC81F681E821E03205620AF1DA2209E1F551EAA1DB11E9C1D5F1FE81EBF1F691F5A1F7D1F1F20AF1FEC1EFA1FBD1FB51F711F0F1F8721D0210F242A2E7029932484221B215F21C21F811EA820AA21CC2045209C1FA01F051F21205B1EC11E4720902071203920061F661DEF1F541E1920D21D1F1DFD1D971FB81E7C1E2E1FF01F761FE11E1F1EE41D161D321E9F1FA11E3A1FB91DE0204D1FE11E361F951F731E8A2047202721891F251EC31F7F20111F6F1F7C1FFB1FEC21F421592EC82B822291208720E7207A1F9120FB1FD721022062216D20CF1EEA1F1920961F3920301F2B20DE1FEF1FF01F4C1E8D1FD61D772021207A1E31202B1EE11DD71E121F5520131F5D1FB21DFE1EDF1E701D141FC21F551FD31E761EB71F901EEF1E5F22951F6C1E42205A206F20A020681F1C20D72047207920A91F2B224623F32FF0291926AC2288209821FC1FB9203B20CC20D8202B22CF208720C61EE81F0F204520F51F05204620131E801F921E2F20AD1D911DD71DB41DF61E281EB01D781D871FFC1EEB1E301F731D401DE51E721DCC1EC81D441E8B1EFC1F3D1F411F7B2027203E1FFC1EF11FD520E81E1F1EDE1FAE1F381F991F821E8720CD216E22D52F672B8524AA213623C520421F2E20D81FA6215421C0208820931F901F681F6420D61FF41F7B1F13224A1FDA20361EBF1EBA1EC61E291FCC1EC71D581DD71E0C1E681D2C1F011E5F1E5D1ED61D0D1FEC1ECC1F761EA11FBE1FE51F381FE71E37204C2045207F217A1F9021A120651F13213A20C71F8C1FE91FEB1F94209024B12E1F2C1F263E224221C22032206120951FF42091209B1FA1219D1F29218620A621D620EE1FDC1EC520E21FAD1F5D1E2521B81FEB1F3F1FB71EC41EB21F951E521F1A1E911F0920671E4A1DD01EFB20521F841E4220F51E571F1B21B620DE1EFB20B91FCD1F241EB61EF51F941F901F7C20F31F9A1FA6210720E020FE21ED249D2CEE2A592529222F228522D41F74205C212F22862106200920761F431F2F21171F8D207D20E31FEC1F9E1F281F581F24204C204D20E11E921EC220551E171E441D5E1F521F431F1D1EFB1EEF1EAC20321F31216221F41F361F0F1F26201820CA205721301E081F621E6C201D1E5E1D261F3C203B21232173209821ED21D923802F182A5D24AD22DA20802296200522E31F9E22C71F1D219C21FC1E821F391F3C20F71E85211522C31FE11E701E401F93206C20191F3D1E861F611FD11D311E391EDC1F641F6D1F2F1FAD1E401EB81E9B1FA41F9F20B920A91E071F8B1F321EC51E331F1D200720551F9F20D01FEC1EE31FC220B61F5721151F4420A421C922402D852B9425BB218D203F21A32132217621EE1F4721D22162209D1E8D201721C321FD1F44213A20D81F6421CE21551FC51F561EAD1F3220221FDA1EC71E801E311E621F7D1E3E1F471EA61DF2206B1FA91F8C211B1E551E4E1E561FEC1ED71E751F5D20741E0A20621FF820AB211D1FE020CD1FF9203021B321D12097210324492D5E2966251223C422AA207220D5208F2015205020F921302170207B20482096209C202120A320F51F4321651F4A206C20D21F9820CD1F0B1E661FE21F7E1F851F4E1FBE20CE20591FE81DDD1C291E8C1F971F181F941E4C1F7B217E1FD41E2220F41FCB1EC81FD01EAB1F1D1F8A1FAB1D80206D20FB209421AE21FC2180241B2E0C2BB424C82275206C21FE21192104212E217D21F0200D21EB20CF20191FEC1FCE1FBE20A1211220DC1FA92015204C20AD1EC42070209D1FF31FE51E27208F1F73213620231F8B1E2B20C11F1E1F0D1E481F2721B81FEC1D2E21241F711E6F1E2421821E971FDD1D5820F11EAE211321AC21F9202B20E420A1219A217223E12F90296B26A821F721F62185215622ED20AB204F20DA219D1F68206C205C210520AB201323A920FC22B821C11F6B210422E91EA32031207420EA1F1B1FC62064202121AD20C420171EAB20CB1F8020A81D4020181F2A1FE91E781E0B1E411E121F971F7B209A21451EB020481FE31E911E0620B61F6B1FC51F352207224924C52ED82B07240921F120A82005216E201F21751FC81F7B20EE20C520F51E7A217D213721E51E22216221A91F9A1FFC1EB31F9A20E61FAE1D201DC6211B1FAF20EE1E762032210620C71F2E1FE41EE51DB01E681F6A1F1A1EE11DC51F321E1E1F0E2064213E20182048204A21331E2A1EAC20971F6E1FCB1F2F1FB02088227823D62D4A2B0225EF2202228B213E2112209320F82064218522FA200A20CA1F232047214820502326216B202820EA21B5209F203721851FF51F1B1F961FBD20B2201D22F1216922F320D520031FEC1E0021211E2D2066201F20F91F411E6620431F9F1F6B204E1F741F921F63218C2058205E232F209E2068225A1FC2216D222523382EA32A4124AF230921832047212921D420BC219C218F1F7321E12093217620F8207921A91F81222D20D0209D2000216A204D1FD11F9D1D951E5F209A1F8521AA20BE217C226B2106216C20D11F091FA51FE1205B20701E111F9E1FC91F9E1E1D1FBA1F6020FC1E12200A21E01FA4208C1F1B1F29218F1F8221CD21AD219023AD2D9A2A4C24FA2178228A220F22D32131212B22AC214B22132298210920662215218A219B20C920FB1E3F1FCF1FD41F22200C1F711F6E1E471FDA1FEC1EB8207221FF1F60211921E620D21FAB22ED20031FAC21DE208920581F1820E21FAE1FF820212100212D226620F2211E235721D4205720801F5820F420EF20DE219F22972E3A2B1B24CA2238223422352049213322F820F71FC220D021681FC920FF22EE201721132252200F215C207D208F21C821CA20441F231E1F207B20031FD720BF202F212922ED1F2F1E1121A52178218221F421F820D721FC2029212420B21FA921621F30209520F41F6A21D21F1521AE21B6210820B020D3203C2272210724032F7B2B782477219D2125227E219D20F2209A22E720F822A021842054209F1F1F2056204F20F62009231E20991F2D21E0201420F01FC91F2B1FE41E651FFF1E672021203521DD20D120D1201920E12151204220C02076210121B9219D1F741FFD1FE0215A1F291F3320861FCD1F1420C2220322DB207A2165216F23D423EB22D72D1F2BED24FE210E237321B92125227A21D72150203E21982171203B21862132215622F71F3221A320F320F021EF1FA8202F20B3201D21551F1E2171207F210221AD219421FD215821781F01212221BC1F8B211A210B20351FAA21591F401F2A21FC1F51206F20AD1E502181209E21A222A42268205A2053203522C8218E24AF2E8E2B1225BC21F5204E22E221A421DA219221A620DB21CA21D41FEC20C520EE200A22ED215E1F44214F200020BC1F822191203A206921BA1F60209A20C6214D204C214421B9222F201E22682029215D21582082212E20701FC21F0221CE1FEA216320B61FF820732003213A203A2195215C21EA203E218D21F02044227B23952ED92BD1267B21922219223C226322DB2242225421A922412127245821AB219821D1202B2255218D21D220A5201A2186213D216820321F02201D20AE20A020962172225C225222D3205222FB20E421912019218D20B01F3E20601FCB201320B820E322E2203F20A7217F217D21A52173214C217A200B221E22DC2291222825DB2F332C33253624C0231C220521F021162227233D224D201D224221DB227C21A52282226A215B220C21132159211821D120BC20A3219C1FE91E33201E2145200A22E12243237B231B21A521C721AE1F3920C421B12009213A20EA21CB1F4721D1221322BF210320A421A921F32137228B23CA21E922DB22912170226B248124DD2E032DF725F5203521F3202C23BA21F521222325214E23A321A321A4207C217222DE221E228222CA23FE20BC21AA201F234A20F020F820F01FB1202920D120D121422287221623AF22C821EE21BF206D21B022EE210823F01FCF218521FC21B72246231023AA221722AC2272235A2262228921E12166234222A12261247424D92FBF2BF4247122D32106238F216F2193221D22D623D120F221CF202D217720451FE920B021A422BE2106200220AC221B2259215721552232202221E62144220F222E22E1220423F1219A210920EF207D21EC212324A3201021F321F022AB214722F521B5232223DC203524E424B9258A245622572190227E22C424AC24E524B32E172BFB26A6227D234A23B020AC207E2027231724BC243123D9208521CB23B123A021FF2147229C227F1F6A201A201F20EC207C22F821F7203F229F20CF226A227E25122403247720B5201D2228229A2049227B2313219E2135226A224A23BA2189226A224521182300245623F423CC232323F323BF226F23F8227625A325EE2EDC2B2525B0220D22B822F521E4223F22CF2148231E232F22BF2217224A22BC221D2335212423AA211F218B21102172204A21D622CF22B1227822842227224F24EA24F32596248E2265207023FB22E323D624E92460232B229A227B221A231C2180221E2230220D234826ED259924FE2340241024152574234224DE24F924662FBB2CCB263C239223692284238E21E12125238322072238223F22F422402326232622D92393224222142388216D2326221D22EF2273238F2094221624B423F12468272D27F024832255232A22A1239E242D27632614223722A422BA211D228721712163220224C5258126A524F423C923E025E425CB23E72360253E25B5240C30242C562515220A23BC234721B222112130231A2274225E22812305224723B2222423A023F022DD220025BE23C122C62393224A22E4237121DA21C7233625EB263327CF28122572222923152495252325CA26A025FC226F222821E62123235820F5218621F221B2230226082756258D25BA24012580251824CF23CE241924A22EBD2ABA240A24B0229B23C5229522A7214122C2222D2489241C23C722B622102377259F244F23CD244D236B23C12419241922C122B62433225823C2259C26A92805287C27A927AD22DB21CB2615292D28DE2780280126BA22ED226F2347221D231C226722E624CD271929C1273424FF2412255F2525254E23B823632459245A303D2C69250C24FC2355235C213621F122AB22F62321257423BA220F215E227A2392241F25F1235725842311237C22AC24E9224722B2252124ED246C281B28492AD8295F29DD25AA219321FF251F288A29FB2A0E292A26322333238320B02160211524AB214F2533289C275D26A325D4253125AC2410253E232024672338241F2FA42C9E262F247523C0239D238E2451252625D5242A2590221021F923862282224D24AA23B2236023F2233421DC22C224892394222D24B624D326EE28582AAB2C622B64293E26FA217D228324C2285E29C42B1429AF2865242F2256228F211121D2220B241E2578265A28C325E425132586242E226B24AF22C522902354259730A52DEE2684242E243B248024092580253E28BA24B724D9249D232523EA232E235925D023A922D32314236E24D326E524CB266125B6234D261C278929432C542C0B2C312A7B27422296220B25F3264529182BF728B227022545234D218321FC2214245B25CF2634279D269C25CD24BC243A235822A523BC206823B5241A279830A22E08280B26D624CE23E3248726ED267926EE25BD2676252D23E1212E227F22A92204244E232B2467260D260128282A9528B626D9254F279C28282A902C9C2BD42BEF28A925E723A9240926A226D8266B28D628CA268C2365246B217521B921C123C4250D2895272E274F24D3238824E124BC23FC24932309253E261729B233CD2E822A8D276027CC266E27F827BF2739285D2623241D230F23D823982329230B25F124072399272327BA28492ADF2AFF291928D5247A26EA2A232A832BC82B592A2C27B4243D24E924582621257025F92692266425B222AD219120F7214F234F2327253A263B2670263C25AB246425CA249B24FF23572356242226EB2AD03472311E2A7226D22607250127BD2736286727052609263625F422D2214C23E3232F25EC26D628082A9C2B4C2B502CE02CE72A8B269C245A255C28DB29EC29772A97273326D526312425257726B726A925ED25F726A626962511255222AA21ED2115252F25FA248925F626E925FE265227F82669261527CD244927C629792B013351321A2B2C295426BC282B25C62778280227F02693261126D923B0228E2400254E272729F12BE92B772B612BC12A002C4529C025DC23F725DD2656273C2848276D2542243C269425D6251B259C2684276B298129722AD2262D258C23DC220A2228247C268D263C2797288F28F628252A0E2A482939287328D128FB26CA29F4327F30F9290827F72597260C2832266327102801292D29732767244924472382254227E12AFB2BA42A4628B7282E2A722BC1261E27A3244D227925CE24AD242126042573240724CC22CE239025A8281529D12A092CF12A1D298A276A2428236224B1232425A9278828F9283C29B629D32A8D2A40289B27E825AC2501266A288431672F032985268F253C27E027BC257026B629AF2924296C271723B0223B264627BC27DE2910291128AC288328D4290D2AF226B5237D22F0224C2371230B242A23A122D0239F233C22E924A7271229DF2B842B932D422BC32A422623257123E722442480260529052AB22B2A299529A02ADC27D3254A268F251426FB24B2257E2F0F2EFF276E257226932705270327CB28582AD52BBA29C3279025D3252C27E827222867289F27AF28A826D427BE29032AB528B9248C22EB226E23E1204B24C723A124A124D924C82458267627092B7E2B502B222C9F29D628472792238224FB23D72349255B27DC26732AF62AE529C929AA26AA242326D8243025C026F225A12F462D4729AC274627382937299F29252AB42BF32A2C2AAF263124B825B727C028DB28692982253F260C2769273B293B2AF6260424DD223F248F24A8247D268327F3267B2441259926B527E129082D092BCB2A64291829132789231A2587241223D621ED237623342544270927282667278524A2225224692314246B24A4259130FE2D8C28DF285F2A082A802AE4282B2A832CF02BF928A126932441257326CF277B283D26C725A1267F260928112BAA2995257923BB23D623B8245E273428D12733272D25F525EC25D627F729942A152AD729B3297F262624ED23FC22D9226423A422BC23ED22592200240224BB24B023E12100220023CE221923C72392253A306D2ECC2A962AA02BFF2A3D2BB02AEC295F2BC02A392A1926BF23F62325263525B2270C24AC2597258C256E29A62AC12A652769239221EF2255256125CA274B28A427CB253827112539266E265427AD26B5262A27E423242394230E2310225623E522D120DD21382169234E2377225023C222B5214D21F321FA2306256D277030742ED52B622B4B2C0C2D902B5F29582B062D2E2A60272625872327248525912702265626592637286028BB2789278B255025F224DA2365241524CE24F5267F27DD27872643265A256024EC249D24F824BF230925A42336210B234024F7228E226721AB24CE22E323A0231F2346225D228F225B23BC23C5224A252825B227A6321F30632CB12C002C8D2C4E2C222EAA2C602B26285426A4243824E02397262727C229DF2CBA29452A4F2650261C251A2566210D2123238B2258231C25C1250B2540243125162587241F244B2238229A229124F12282234C242A25F42660251B2638259626BD24B7235C244C23A2225623BC22CE22B6244D256D282F260F2A13330132292FB72D272C092BE02BC82B512A6C2A5F28BB262224252431264528AF29B62A672B302A582BF1268C24E124792412210B235F226623F424EE22B724C324692660283C266F25E223C82350221622AD232225CB2401258D26322867282127F9250026D1253623CD25EA239F22EF22DE23D225E026C7261D28322A282C7635E531102D472D89297E298F28A227022675273C27F925D125A926632623296B2A912AA12BBD285329F5277E24DD23E2219422D12025233423A82417236825BA25C0276C2AE62AF2284027B32388237423B0238B2530255127862AF7297128B42612250E250124DE2332227E22212435241A24FF25E72A322AD12A2D2AF62B74370934532CB92A7D2865275A2685252927E425E7241D27E1264A25AA2522277D2994271E289127FE269423DF23CC23C5238D229F239D23B22421244425D326E227D12B1B2DEC2B0D2A552743253B238223B0262628FC27B729F5282E280C25F62464261D24942240235C2464221D24072737266B264D2A4129F9299D29762A8833F3324C2EF62A392BFD2A7D29BB27C0268525AC27292AA82B9626B326E1245B2589245D2450252F25C8260F241326DD2514247A2509295B265E27C22791276A28232BA92C622CC52BF327882469240725A928292BA02A1E29492742273D2525260028D725AA248B24DA249724D6233E254A258726992919283F2787271B275A2FE332962D9D2C262B8F289929A227652792282F28F829FE2A4F2986242B24E1225F2535253D25CA27D6266D288F27CA2815270427C226BC299129DD298B29BE287B2AD22D182BD22AA127DF24A8234D2632298C2A6F289F266E251D26D7264F270928C026F526F523C926DC25762312253924BE245628D725BE24A326B6270430F92F5F2B6B2A522A352884277226DA274F26F22526296426E6266B2525260D25FA26DA26D4260028102B6629472C0D2B7C2BA729F6287D281D296A2888284C28D728B52BF32A5D2921279D24FF274328E229AA2CBF298627A926EB26112872298629CC27B6265726CC263C262126CD242222C0214326B12524273126DA272A31D72EEE26A3267D26EA2660242C26CB245D25D326C2268C2558263D26B527F625122789273D271D29322AA52B2F2B172C042B1B29C928CE28F7271D26B1254225B726F9279E27D325172451232527DF27432B422C1E2BAE270E27DF25C0278529002AE829862AD42717286725B8248B2311230026B3282B29632B2929FC298733D92CE72614268425BC24D32480245F252726B026B8277127532971286E290B2852262A2664277E29562BBB2B742D622C042B5A298D29972726285F25D2256924CE2387258B26E023A024EF25BF2641274A29962AAA29602899255325C52613271B288A29082BFF28872898251E258F258324C22834291F2A202CF62AE32C6D37DF2D33287B28C826D1258C2551243825D226B6265128C227CA28D029A62A2727E42506266326CC285529CD2A2D2B482C592D8029CF2517231124E72490237E24802335257224E12477259627AA28B5287729A228BF2A9B28342601252F241A26EF2673274F290D276327F92493223926BF275029C12A722B622B4C2CDD2DAF36C52FBF2A542963280827C526472451261A2782273A29F7288329262768266927FC25F2251526D2269D270E2AFE29812A4F287E259224FB23C521C424BD220922D722B524F024B625AE276829592AD029222A022B2E2AD8299B25CA23D424192585267F26A9260F268127D1257D256C262F292D2B9E2B4B28FB28982A852CE635CE2F6F2CF729AF29532889261B2774279A293A299C2A4F29532914281D2757243725B2245B25FB25892510288427112673246E24892269227922B123BC2372236A24AE2489268F287F295B2B542DCD2AEB2AF729EB297A28A82667236C226123AB2639288F285A268E2728266E26D228422ABC2B7F2B052A2C298C27202A5A345F307E2DFF2D422ABF2AF32AF52900285F28EB29682CCA2B9E29242927265C27A1256E25FA23A62598234C26A824DC2436232324DB21DA218623B324A724D124E82303240E28DF261329152AC12B2229A929C628EE25D0249626672246229724B92773299129002A5029F727CE274F29202BB028102C832756275F2AB92BD334DA32932CCB2DAB2C712D0C2F1E2C722A9C2A612A782C492CE8299B286B28CF26AD294D299A2665236B252125FD227D245D24C4226D2402241E25622522278B26CD260A2786268E2722276827D228D326CA2643273826A025C72504251F2380258327262B0C2B702AF12ABF2748273B2C412B94292E2BC2298D29D52C362BDF364C31E32D8C2B7E2CA22D372C2A2D962B6F2BCF2AF3281D288226C6269C28A82AB92ADE295A27A725DC2352250F255C23CD22EB24C12484268A286D28FA27D2282229A528FC2637251826C626B624A925D626A9257527932676271724D02336257B283C28CD296C2C9D2A9627FF26952AD42B372D132B302B1B2B642DF32DC036D62E012CB62A162C032A862B2E2D1B2CD32AB726B2281B250F270A2616288F276829D227E6269D2467238F23FE22422516243924ED269727762A102BCF283929C12AEE2A9D27E624BF261D256B23F722BC246F2667278F279A261325E62471240228AD28BB297529862776266B26572AEE29782A502B282C902C0C2CE92CD834722CCE2961280A28492A2A2A40290C2BDF274426A325FB23A7263126392862270E27D2264525162639243224C9263C26AB241E25D026162AAE2BCB2A252AC52AA52A022A7A299326A323A424CF24092312248F28EB252226822766268C259F25472539276E29E32782284C2645274829EF287328C728372AEB2B552DFB2BDA33292E5E29FF28CC292F2A1A2A81289028C92566243B257026C924FC2535269E26DA264126DC24352704278A2585287926ED266128202AA529A92A742ABB28EF26A028BD2764251024582434252A269B270329B7298328DC254B24C623F12428233D2487270D27672682256D242F2806271226F6268D29FE295D2A492B942A03316930402A262AAA29442B292CB029FD277F26DB24452573243526B826B2267B2772268B275F27342A222B072AC929122C612A432A4A2AB42A702BA029BE287626F0287C28B0255D261D26A3251928F227B92A4B2CB42709278925AD23BB25DC24C22498254D24BC25C1243C276626B4265727DF27F727232A512CA6299F288E30CC2E792A7A293429252A052B6F28AE244B265125ED260E28E026AB26C9277D24D6259329472A8F2A342B30294929732AE62B602B6D2B8229612ACB2A492A8E2AE22AB42A342A61265926FF27CD28A627632997283526F023E52695265825CE2550246D2357242C241226C1258D27F9292E297029072904291F284927CA271C30B72EEE29952AF42A8A29142980267426F327BB285D28092B9328F7269327812564271128532A882BF62A78292C288C28A3293D2A8D2A1829DA2A532AF729B128A72A832A5A2BA627352724275227F22511274F25BF248F230827CC257A256C25EE2306238D23FA23DD2668273C2AB82A49295728E12510264625A6251027DD31B830332A652AD02BA32AB8282E25BF26202A4F2BB32A402C58291A282E268B263126AD28722A4C2A7F2A27287B274F287F27F5268A26D22703288F27F324CD255529D2298C2BC226C12501268424F1239A235A245022ED239123FF25AE231C234224ED21142424255326F327DC28262A272AA8276C25F724722623253528CA33292F372A442947291C294D27D227FD28E129A02A282CBE2A4129AA25422521273726AC26D829932A86271825FA23A625B32420253324CC24A8252D248B2435278127E6282B28EA241A255723B6229E227624BC23BD25EB244B257B236124EE23952318237A23B225CA27B629F12A642CB0282126DF25032522278827742A3736CB2E482A762767282027B026B229D429592A1C2A8C295C29FD26C2269126AF27E3285D2A722921288A249B27E9247A24CC229824212397230925ED24AD2304257526DC253B2610239D222B245223DB22F723802603284B272D27812682260527E126B4243625A127862AE42AC22B2C2B79296F270E2655240427CE28852B9337D62F382A2D29E7286D287F28192A5429532A7F29862A5928F226C0277126562ADF2BB32A5C293A29B726D72494267D26AD23392353228123C2246623A32578252826B825062670234D234E24C3232A24EA25B027A928AA27762AAA2A1229A72912284B27E12574269C2B2C2BAD2C282A1727F5263227FA252F28B129992C9135F82E4F291728F6263A2A262BD3291A2A3C2BE62A872ABB270A26DE2529288329662AD329EE286029962755270B26E5251E235F252926B825DE2799264627D725602587255D2571254A24EC249926AC26A027692B9A2AC72A782C222DD32C0A2ACE28F0257626F0251A290E2A3A2ADC297226A926FB28A127E528192B7E2D3835A52EF42945273327092A432C092CF92CB92C132CB72A3728FC241F254E25D4275E26AA27292787277F29D92BEB29BF2640261627ED285729D6287C29B726C2254A27F92560282727022629264228FC28752B2F2DFC2BE42DB22DC82BBA2C1A2BF02833260225D4262B28C328BB28E9290528A3271D29DB2BFD2A7F2ABF2BDC336F2F94289D260E276C292C2C962CB32BC32DF32B162B5827C2246626292733282328D926DC277F288B2A042CB52A2E294D287B2A7E2CB22A062A8727AD272D263027782845295A2871270329F3290D29D42BB82B722C4A2D392D352D3F2A9F2837257025EB26A4249B287E2AFA28152A3C28662A0D2CE429572A872AF7298633832DB128562605286B29C02B912CF22BF02AED2B42282326FA25BE27E8288B298829A82741277428F929712D592C352BC62AE22C592B2E288127C325A5250224C6250C29972A75293D29542C2329D029A32AAA2AB3289229E42C282C182A4D2866261A263A235B26EE28842BE12ABB2A9F2AF82CE22C102CF82B1D2A7B29DF32252E48281D288127B0293B2C0B2BD52C972B35295027D7258D27F928E5293A2D16285B284A28832821298329372B4B2AB2274D283F27E3260E261825C2265F26ED26A72794275828572964295328D62693266D27022610285229592B5A2B9028C125FD24FA243227EE2A772BE12B622CFA2B4A2C152CB12B742B212A512A0134AE2F9E2A7128C028332A412B402C7B2C9E29F428A92768272E271F2ABA2AEE2DA22A5A29792804296C296E29B528BC296A25252743265A254126B427D026E527C727D427F62781294F2A172AC4299827B7254726C4250D270928A1272229FF28B32636272D265128F8291F29422AF529EB291A2D222DA42A3E2C0B2AE02BB3337831212C7F28A82723294D2A3D2ABE2914295B27C1279629FB2A052A2E2B9A29D4278527BC284B2A822B982A54295E26F62506251624412532282528DF27A62959297B270726FD258927C52738286625AF26FF25BA25A824BB267D2726279B28C1272B278F27BA28272837275F26882766284029642C5C2CBD2C982BBF2B7E3490309D2B2A2ADE288429412B8D28D1261126D526F228702B0F2C2A2BFD2BA029072AB929EE2A202C272D1B2A8C2757258D22432309243024DE287929982BA729C9296C29EF298F2711267A2632288F267126A22649260024932A75296B2A4929F027CC28DB28BD276B27F125E92541261E265F282929082B172B232BB92A72324B2F672A8F27C628892AD92BCA2943268E2666284B2A7C2C762B5D2B882B5C2B0629042AC4295A2C3B2D8E297827BC24B123B523B924A024B528182ACD2B452B212BD02B0A2C5429E7260E253627AA25C825A327CF255228F629A52CCC2B552A9A28D72792263F26D225F925892627275527FB2866283828F7295A2ACD290E32853034281D28A929C82A612A6A271D2720260A2A182C7A2BDA29972ACF2BDE2A8F291527D1278C29F92A7A283527C32429255D239F24BB24E8285128B2287F29522B662CA229082925264D255A250227642A3F290B290C28FB2A022C462B622BB628CF261924DE24C62521250D284D296A2B4C2B7629842758299E2708286A32092F3C2A5D298229D129C72810260326E327B1285E2A2C2771283A28F427F7299A28252616250A288426ED25A9266A2580253125FC2568282F294229A72ADE2ABE29E82942289224B22448246B268A27BF29FD2AB52990275C2BBD2A032E692D272A0C28F323A323A026CC274229FD2A552B9F2BCB2D86297A28EE262E2875305030BD2AA12A582A722A7729A3266729322AC42B172BA428BF26EE26C92678297228A425072612265D267E265E268726352641287327232A6F2B6A2B8F2A7F29E5285D273227BC23FF213B267F293C292B2B3D2DC42A9F29812A362AC62B162C6B2CC8281926B826C527F128F929082CF12A322B772CB02BAC2A3B2C922B2A33692EC42AF8279A28D227CE2753270729DF2AC22CD32DA02AC329AE27D52805285B286027B427FD260227672665288228E32790293D28B928672819283B285E27E825CE26FB2508269F260C280029762AB32C602C25294E29A22A452B2A2CAF2B542CED27B8264B26D92662291D29BC28DC2A6C2C8B2C952B122DEC2C1C2CCA358E309F299027D826222834278828E129712D1F2D4D2D3D2D8F2C922B612AA02A4029522B5129E5291D293627B02AC429C229962A4E2A912994283A28AA26C126C3278328B327CD2746289A2A0E2DDB29882C732B402B722CB62C162A8B2A00290B2A1A283C2758252D283729E4285D287329B22BA22C192CA22B902BE42C9A35F72FAB29B825B7244A251A274529692BFD2C862CE52B652B4D2BC32B192A662ABC2A9A2ACC2A372A5A295C291C2BE22A562A132C4F2839275B2725285F27AB285F29E4282729AF2831292D295C2918290D299A29702BF8285829B629EF26CE270628532725281C24C225DB27692775282F27C8282F2AF6287C292D2AF22A7C35532F2129AB2478235525AB260B28D22A3C2CF829912BB32B462A7829462ACA285E27A12A9A2AC8284A29222A7C2B422B7A28F82801282026E0280A2809282D29E7299828D628E7269528342792266425A42782275027612649273325D42623270327DA271128AD27FB277D246126E8276A28A4272727EB27BF28C2273E2BD535F02F4228D925BE24D4269728F52A312C332AEB2AEE29E52BB829392AC929EF276C279228E725EA261226B2257A255026D3254225DE2421267E269D2887283626562765260E25AF248127CE26AE26B8278627A029492607247424BF24BC281229372A0A2ACF286D285A29A0278928952ADC2BC12A982911288C277A277029E3333D2F6529542901274027A827CA2AB42DFC2BC52B3B2DE12C102BB629C127D5269B26FE262E25CF254925AB242C2575263624F724E823C724D726A025BB261F261525FA2368254A2661272A270628F22A4B29532B70273E235A254C26D0277928C1293E2C792E572B7A2B4E2A442BF62B532C202CF72A7629052AD729702B9A3472307A2A6728ED2713277F28922B312D502D242C302BB72BEF295C282C26D72518258A24B625E624372501246A25FA2408256D2446250925222687256B251A263A243B25FD2508232E272D2753297A2AAC29AF29C926A12534263D289E2AA42A5E2B3B2DCE2D942CDD2979284628472AD92A312CDE2B4A29192894274928D5303131962A2D28FC25C12667278B2B7D2B052CD82B512AED28C22571240C2670257524EB2598253127D22569260D28F8269425CC26DF26B628EE264424F724F4258E26FA25B8261A2584276127D129632A5129692970265E255C274C26F428CA298C2C372C6F2CC529022A6D26D5254A29F02952298F29CA289226D527EC29E8313C31B82CE229552876271A26192A5629482B0A2AF3250F26F92683256F25CE269A2684288B28B229D12ADA29A829D629C1281329AD2AEB2A11282D261F25D2269028D5296D2A49277A287928CB27A6263C2929290F279525D22502269A28F728E52A5B2B902A6A2BC228B624A92434255326452814296327DA273928FD2971346430972CB029A926D22528254626BC27EC265827C2271E27382765268227FF28C2286A27102A7A29EE2BE62A4B2B5B284F2546280C26B5279527DE26AD248E27D229BD2C782BD526F2265D254C28F8241326C1270C27A025BE27212901292129BD270C29482AA9277C28DE256425F524FD263C259F28FD29462BA12C6A2BC1340731FA2CAD29A428E826B82577265229D42A182AA12B122CA0293629442A052B2929A9280C2842291E294228952680258F26AA28AE27CB260C2793264A261C28F728862A2B2A2F2709262928BA282C276327BB283D280F2A672AF0285E2A502AF32B56280E276C263A268F244725FD2419237427FA2818294D2AE22A5D2B9032D730082DBB2A6F2B792A932A3D29DC2BB32C482B5D2B9D2C012C0B2BFE2A7B2B6F2944290C298B2B092A3A2897282B277F28C8285D28D7263F25662654272C29DB284729F028172859287F294F2B842B372AFC2A082B892B252B9B2A932A4E2A1A2C522A6B28C4284F27882562243424C22591278D282D28FA27C5276A28973031330A2BEC29972B362CBA29742ACC29FA2A922E862C8E2CA02AA728F62805283B27302A9929582C4C2BAF29CE27CF275128EA28DD273E240926D52623287E2A8A2AE22763254F26D526EE287329BE28BB28792AF22A152AAA2BC8293B2AF12A132BCE29312AEF287429C5277C25C625D2244A27F32749275A290829A628FD320930A62B4729D82A2329E129B329DA2A502CDE2C812E5B2CA12A4B2A2A28C727C527422A1E2C312C0A2A432AEC290F2891275A295E277126792972283F2A44293D2B4A29D527AE28C4264627C8270C268C269D2798287A29202BD12A5E2A482B0F2C072CC42BC22BF02B6B2AD4281F274D255428F528092AD42BF92B042DFE34792E5B28F22738268627E128EA28662B3A2BAA2CB22CAE2C602A3E28B4278F280228E42ACA2BEF2BE92A1E2955272726F528BF273827E5273428F328BB284E284429ED291B2940275126E428B126082478257728A628CE2A592B6F2BD62C772CBC2A462B962C9C2CD9296829E4260B27C526AC27F629D92B212A232D5A2EE1341E2D482843262B288129FA2AAE2AA12DC62C9A2CCC2A142BD829CC26FE26B2270D28CA28EC29292AC226E5258324BE252D24B226C3262A270A28A427F6255728F327EF2561267B258D250527D726E42376263C2884263A260128EE271529E128972AC52A292A18293B2790268B258C2515279A28E92AB02B262C542BC92AB634542E6C284427A72956299D2B9C2BEF2A9E2B252C352BBD290E29F6255B266B26C8272929B529FC27F4267324E425D424AC24B4261F26B8282228942795250425F526372777245F2433250126D0230023F0259B24F425032519276C280327DE262A29EE29942BA92853273126C0251427C226CB28F7297D2AA42C532C982B3C35352FDE2A602A41298C2A1B29822A1E2977276D27C72755271E26FC28CB2878284C281F29872906297E2742277B266226F72696272A287B2ABD2A2D284427CC26EA2734275025FE23BD24C325AD24E3232626AF26DB2556250426312673268D243328CF295D2940287E2798276224DE25E224AB276D298729E92AE22AEB2C1336D62E392B952B9B2C9C2CEE2B432A9E27B826722743265128F729D42A2629552898278F2A0C2BF429DF28A4278B2BF228D425BE266629FC2A5B2C922B7C2A6A2BB52987271B266824182580253A2690258128F827F927D2282D28E92612284228F128642A902BC6274C284C275527F327AC253627C7287C29C029E22AD92C5234AB306E2C742C6E2D412D912C042C3429352A7029D2273129B72A7F2CBD2A67287C27D9272B2B9D2BC22C572ADA28B02698270E298229622DC92CFA2BCA2B992CF02A6D29AB27B524AD27B928AF285E295C2B102C092AF7288C28B0292B2A622C6F2BDA2ABD2ADB2ABA2A3D2A6E292E295C27622585269128B329312A0E2B9E340A30442CF12B842D9C2C402C832C9E2CF62AAA28DB27A3289A290B2C7E2A4626D725FD26882C752C712A2E29E927D12786262827322AEA2B212A502A6D2A242ACC295F28162536252627442BA12AC02A7F2B9E2A002910280329172BB52D162C542A8C294B2C752B932DC92BA32B87286B25E525FB2633294729382BC52A5534D22E1E2C242B37298A2A812BB62B052C8F2BAB2A23291F2A002A5729F1284D273C2502285B2B4F2C362B412C9D295C2A38291A286A285B2AE22A552AFF283F285528F82592253D26DF26A4284A2A282A452B452A8A28FA2680283A28B629FF294E2ADB2BB82B7B2B7D2D092FCA2B53297B2695272227A627332B572A782BB133782F3E2A9828C1279826BA260128E4267F276C278626C22684278327412713266C244C250F2AEB2B302CA42CA62A87281627CB285D2AA32A632BD4289427B025DB259724DE24A52584278129832AF328D828062A5728E8258C27F5254526972660295B2A922B392A772CCD2CEC2B8A2A8D27EA270B286A28FA29312B3F2BB732C62F232A4D28D3245026AF2449241426DF24C4254A2563252725032460237E23EE23D5257528162AC529E52ADE2C77298A26F1260F297B29A22A7528F126A3277B2570247524E724FC27B728BF281728A7280A29D2255A2413248C2485255E25DD27942877298C296D2A3D2B292DA92B1D29B727C8266F250B284A2A8B29A832302F5028262777240D2444242B24EE24C125C825312406247823B0246F246C230724ED25B6258B2748279C283829C226C726E526AD27AE287229492B8A29292852268024CD24F5260E288D283728792821291F2A702769246025F724FC249B27DD28C42A372B3029A529962AAF2BE82B422BB728D826D8266727D329542B1C34242E7228C126C024B224C52359246A24D8268225572549254C24A723E323E7244D24EB248F24DD261A269026B1285E27C026C927DE2557263D295129DC28B026AB26DD25D2243D260B27622839263828DD2A6729AD28E125F526C5260A27012AE62A3A2B362BF928E529BB2B732AFC2B0C2CB8277827F1252328582BA62D6C34142CA9270C24C1257125A52307253D2644277C26E326EB24AE243723AA227A247C24A6254427DC27B1269B2643272729D927AB252525C2253A2862296C2987280728EB27CE278A27B1278A282F2857276D2BC12C602A2928222869272F28A62BC52DF32B5D2BBE29E52A712A842B352CB32A6328E826AC265829472BC62D7335BB2C28279726CC2439270A26B927CC28DC272628E92737278725C323AA244524A625D726F12B2C2A3A295926EC27B02922291D2611262D2682262A267C268F27F6277A27F427292A442AD528512A2B2A8B2DEF2A8A2AB029BF29622AF22AEC2B982B7B2B6D2CA32AC62C902B422C972AC12A2C2A762B112B6F2B942B8B2E46369A2D612702262D299F299D290E28102BAA2A522BCD2AC22AD226492451268826142653292D29AD2BB0282824D8279C29592A2F2836264026B926ED25E3243C264D259A269D28A6282C289A2932299128172AA92970296C2A4A2A8429D329472C352B6F2B9829A729D82A6E2A652B222CC2279527D4298E296D2A142BD72D0836D52D97270A295A29192C242B9C2A522CF22B592A1C2A3C282F27DB24F92559276226EB28F028C02914286725242673260027932927298F28E226D725FF2384232D25F22744263527B028EA25E1287527502A502A5A29A727BF2667278528DB29622AA928052AB6296D2C422A432B5A288A278D26912A7C2AA32BA02BF82BFC35702C5E285027B32999296C2BCD298D28AE29322AAD29402665244F259B24A224AE26C227582879275F256A254626EA268128972AAE2A8B292929F225F8249024D52386244824F1230C255925BA251E27692A082CFA297827CC257F27CA28262AAD2AFB2776279229EE2C1A2CB02A35282D26AE265729A52A9C2BFC2B6A2D2537DA2DF82752276C27A5284A28F32744299428B8252526A924A424CB22492412251B244B27F02610279D25A526B725AD29032B572BC929632A992AE129AA29D52806273F26262405247F237D260028ED29082A342BB228F3259B265F26E828492A6C2A5829C22A8D2C7B2EDB2B182A3928D626F026C429702CA22DC32D9E2C6136E02C7927A025CF26BA27B7275A2701260E272824A4239823562385245426F22734277A264E255D25C324E424DC26CB26ED278A287D27A026B927BF28E228CE2825287B24FF223D21B7254E25B928DC29892AD32A7D262824E924FE27C5284C2BE82B582AB52AD52BA42B112A5A2AC428D8261D28C629FA2B3C2D7A2C7C2D2C36892D8F27AF26CE245B252F2798261B266E25FF23D12331252A24E824B228E529D629A7288E2788247F244323C52479274928AC28F628D125D825B926F525F1266B267E242825662488265227C528CD28682917289D258F26852797260529652B842D262D132AA82BDE2C9A2AF427C626382633286B2A952BFE2C4F2C512CEF335D2E6326352565254126C727DE26A724AF24D823A4221F24C7244525D7274B29892A9E2AA529C126F8242B249224C52657268A27FF251E277A253325C325AA245925AA246626CF272429002A7629432A902A0D28B126F2275425CB25AA273E29592D1B2DA82CA72BBA2B7F2A41284D2845271029432C902DAB2CE52C592AE933A12BD9264925AC24CB257B27872464241224082492248F236926E02553288128702A8B2B172B7E28FB26E7242726FF28DA250E28DA276728EC255C252F25B32350242A267A268A26D9284C2AC929A127B2284A264426902585259F235B25202A292B522C972BD62ABF2B5E2AA5282428F3281528CA29F127FA28CA29192A9D33532C1628DC231A265526CE26FB25D1233625CB228A234E25BA26232623268A287D27E529AF2BB62B90286026FB260429DD289D2833280F2986295227D423722339241B27CF2593250A27A0289228EC250D264C272F267D25C925D62208244C25192AF628702917291429DE26FE282028442A522785261328C927F0289B295432B42CD628A3256225FA257D26F8244B249F254B25D3250126482778247025E5254D2799291929282CAA29E8270A28492737289E27A1288227A22AE827B62790254B263326E2259D2476274B27FE264126AC254126BA27A82508266E22EC23B6240C286429452B862A9A2B6428EB29432AE828D3259F25242616298B2A642B4535EE2E6D2986263D261E2729272324F1246226FE264528612736268D27EA277D276A273E289927DE29D32763264B2749283C27DD277A267426E227A628192640273D27A6249024A625F924ED27C127A524FF270429BA271B2411251724A325A224EF26EE28492B482A922B712B472AA628542842250926FB24AB27B027242A79341C2D87276326AB250D250F25C425012583278927182785279427022638287F266226D325242675258024D5255F265A263D254B2608252D24C12506282A27E125A8267C260527D125A026F926632622256125CB268D265124EB23A623132506251D2684295F2B9E29252AC4299F281C2911279F255226832521288329712B89358F2B7525232517252224352501232225832707272C279D257225FF24432517264A25C224FE254325A3229E22D2236824BB2344255523DF24A22439234D262425662519271729822678270427B9241C258D256B26DD242A2558254B2439244524172687275E2865278F283928AB280C2851250E25B326CE2557285528AE2918334D2C0126CD22DB24D024F6232F246C251F2792247A24F225F323D6249C24022542267B25F7245A2588236B23C024EE24F8224B2596248423092476258226CC264E279B274B2737282A2814273426832533269825F22449249E24F824592434262426B125C325C9261E287F29492A052BC1269A254326B02664283529AB2BC0341C2B2D2636259C226723A623FC223023B6242A252424C72404230024DD233326AE24732570267B24A224122345247023D623FB255424BE25B325FA240126CE25E0268525F726BF26F225D02402250C24B72434244523F4258225EB231F2450250B276325042681279028BE27012864269925F825DB24C427DC29492B732BBA36F12CE0267123AA2274227622FD22B2228323BA238F22DE22152268228224FE2196239124D324462569252B24D0236122E0239A251327CC251F275C25D6260C271D26F8247C25D122D423C523A8228222BE24D0259E25C725C1243E24232490252C267F2618261E26DF27F725EE251227BF231624D12520263828472CF62C8D37412BBA25A022222245223C23AE215E2418218B22EE225823F42258246124F3221F2370256924F525CF236B23E32308248123DF25B7267B260E283E269E26D325CC25B2240824E42204247D242D245224B124E326C0275A277B25D5255C26A326B42702277C26A4246E264423F4235A23A822902497257E26F529E12AB32CFE35C62A6623B621B223F4215922EC218722EE22ED214A24C22388222024012442248E25B823D7252E272D252D238224682511231C25BA25CE2590259B23DC245423F4247C25C3269324EB243A25362611251626C5280227A82742292029792A9D2AAA281C27E8265C2517253A24F3214C23AF2380224C2431251B268D28DD292333622B0B262A225F22F523D221BC229C22A5236C2430237723A1245523B9223725E3253B264D26B327E6240A23BE24C1253B248A24B226B227AE24FA24EC23F7225E26EE2634266B243B275E25E825A8246127102705261B273429CF27A52B11296C2B712B0D2AA7266525AC236723C02462238522D3246B26C928BE29432C8B36E72C742520223B22DF237F21EF22D3211623D0228A2342229C2280217722FB22D123CB245A241D26CA248723B72371259B256524E324AF22AB24B02206243C23AD231824E7245F241822BE2571244423B9244B25592301253B253C25EB248A260F279627C82925279D262825D9231823932322245B2649287229252B3E2C9F368F2B8B25F72365215F214B21AE220722C02379229322D222D721C423D92388210B24792457252E269625E82310244C236A236D2483222F22442527240023F02104236324322257214B20B32245215023CA24762484211122E5232223212488220926D227B028FA298228E625B424E1255F23E223EC257225D929362BC92CAA33652BA2259623A822CE217822BD2366232423F821EC2287239122182210233224F4230E237E23A624AF24792309235123C12363223C21AE2248213E23B2212B22FA21E721AD22D3200420B42110226922A5237322D321AB212B22E72208221E252E25B0241527362950299125CC24AB24B923D822FD24282521273728B928D530012B5E26ED21342194203920E322AB211822C0205F225B2030213B21C421C721B122FC21DD223923CD23E420292212259F239022A121B0209B21BD20BD2223237921DE205D22161F0F205C2168215822032394238C231B21BE22C121A8216B21BA24F223222392237425D524FF22A223CE21FB22BE2484239C23802507270430962B6F258A238D214C22F4214F213B215C2240210222BF209C2199206021C8204C20CA2180210E22A0202C200E218D221322C820AE1FD620FE201021A3210A22F221DF21A2200520CB21FC2148222E22C9219E22C920E021332246213320A8216122DA21B622B622A7234E2179212D22D0232F2154228521C921BD219A2528304B2B102434224A22C021A620CC20B7225B2090212B21D520C720CA1F73208D2170219220D522FB2217222A206122C6213F21D421BB21142140211E20FE1F2422E8218922E51F4E20841F45228021B021E3211E2291209C20072231200E213121632196217B219D21AA214C225C239D20172128226922E7221D2333244E25522E9A2BD1244421FA214422F6203322CF21C1212A238F222020F2208B209421D6210B2296227222C921D4225C20D622BE1FA3211A22791F50211120D1209C204C221922172141217721F71F492286221922F123FA22801F0222D12035217321BA2287211D1F30229D21EE1F8621DB208A21B9216722FC22F2209723F1236D264B2F302B4C250422E7202F22E421E02062237E21F320F220CE20401F741F3321E022F420B721C723BD206522A921D621D320D22190223C20B720CA1F9520E71F7221EB20A51EE720F81FB51F0B2203229C20A0228B212021A51F33222E203A23D1209A206720AB20E61F06224E1F1221332230233321DD2167225E2306228A24FF2F072C2D255D21D0221D2218224D215B23B722B9219F21FC21C820981F6A219921FA22B022B82299208C223921E721DB209E2058227E20F61E7020AD2286203B207A1F771E62206B1FF71E8C2148214F20A022B9224720B920FC1F3A1F911F4320802130202C20381F2B212C217921BB226522C42262214721E0229325772682321B2CEB243B23752009239523F321D0224823A522C023012235219F202A2290203D210D233B225C209C21592074211021E420A5228C2183201E212D1FDA1FEC1FD61EF51E421EB61F9B20CE1FBA2075223B221E212C216E1E211D2F1F961F25217420F4205520661FF520A6201B21B0222E211524BA220221CD213B25E8268330 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 403F + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 55 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 0009 + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 F227FD21CD211F2083200420111F211FCD1F3C1FBD1E401EE31D511DE91EA81DA11CBA1E711E841C791EEB1D9A1FD51DC21E161E061FF81E8E1E2F1EA91F491E231DFF1D151D5D1BC61B891D0C1DA31D2B1DB11E751D351DED1D391D561C9E1DF01E9F1EB61E761CFB1D491D711C181F1B1DCE1F301FDF1E9A1DDA206222822D492BB72353205222DE212C2087203121FD20101FFF1F261F591E391FB21EAB1EA31D0A1E481EB51D381E1D1FFC1FC320801E1D20A81EA41FE51EAA1F1E20231E521EB01F8A1EB61E321D841F931D891D6D1F071FF81D891D341E1520901F641E721EC51E3C1F0B1E641F451F6B1E911E601E2F20C720192001215121A323392DDF2A5624E522DA2162203020A42095217E1F3E22DA21C6211020561F9D1FF61FA61F24204A1F7E1E4F1E6420A81F4F1FC020B920BC1F971FCE1F8B1C841E02208E1F7F209B1FFA1F7E1E231F0F1EEF1D8B1E7E1F0F1ED31E661FDF1C0B1F2B1F6F1F9A1F231FA11D5D1F731F971F3A1EAD1E1E1F9B21B81FAC20FC20B8247B2EE12BA02702248C2185210D2137220B21502008202920B21F6C20BD1F1E21751EC21FA71FA71F7821E81E451FA21E2A20EC1FCC200F208820211F5D1ED91C9C1E0F201520BF20391E0F1E491F1D1E561DA91ED91F811FF61F6B1F171F06201A20F5208B1FAC1F181FDC1EEF1DF51D921E4C1FE71FBD2002225920C221E123F12D0B2C81259621D820A4202321A1202521D9204E2049205D205A1FBF1E5020E21F951EE01F481F1A1F341F421F8C21F51D591E8F21041FD41BBD1E9D1E201E531ED81F8D20BB1FAA1D671EAE1E8A1D4C1F2A1E1F20931EBF1E3B1F12207A1E841E0020FF1F9F20931EB51FAE1E661F121F4D1F401F3F20DE1EF31FF9219B229D2CF0292F25112227234B202D2232203E20A021B2201521AE21511FA71FBD1F25202320B81F381FDE200A1FD11E352099205C1EF01FE61ED71EB71E1B1F931E9A1FBC1F07203B1F861DE31FBA1DD61E6F1E4B1F491F891FA01F091F471FD11E591F582082201B1FC21F9F1FDC1F421FB91FF11ED01FDA21CD207120FB222A24152D392A9C22CB2110218F207320C7215720FF1F4D215820A41F0520101FAB208E20FA20B61F1A204A1FF220C0203C1F5F1F861DBE1DF01E261F751F1D1F8020B51FF31E901FF91E661EF31DF11D421EAA1D281FF220901E741E5C1F741F2F1E261FF81F501ECC1FF61E191F171FD31E021F3F20931E3920061FC8207E21F221982D092BCC24F521A722FC20311FCF1FD12089208A21701F062029213522062019204D217220D1202F21D620871F1620E5216F1FF31E6C1FD51EB21F4820D51FBD1E531FBD1FCF1F8B1EBB1E6F209B20C81DC320B01F9A1EF81D9E1E8D1D741F301FB61F601F741F7A1F4420DD1FDD1EBE1FF01FA41FB11F2F1FAC2138223924F62DA329BF24C5226621AE21F61FE61E0621D3211821AF20E51F9A1F811F4D20AD1ED11E77209520D420BD20461F091E4620AF1E08205C1EBF1DD21D651FE51E7E1E7C1F1020A41F3A1F5C1ED41D491D441ECF1F741E861FC41DE9208C1FFF1E061F631FA31E992060209A21A01F551E871F7B20341F9D1F501F242005224F226D2E7C2B9F225A206720A7204D1F99205420E3211E2065218220EC1E15201420A41F3120461F4820E71FE41F2220001E571FE51D4A20F41F1E1E2720091EE21DBC1EE51E3320AF1E171F971D2B1FE21E9A1D001FC41F121FAE1E681EB41F6D1ECC1E4722781F511E4B202E203F20BC20E21F5320D020FB1F3920B21F4822E0223530142A2B26ED22832105224720F320BA2033215C21C0226321BD20101F4A207420A820022079209E204D1EEC1FB61E7F20051EA01D1D1E6F1E411F7B1EC91D851DB71F2B1FFB1E491FDB1D881D4D1FC11DD61EF61D6A1EE71E1A20921FA21F9E208820491F221F2C200721661F281E4A200F207D1FC71FB61E10213022E7221F30272BAF240122AA23F0209D1F4E201320FC21D321F1209F20941FE41FD31FC3202620062003207722C41F1821651E091FCA1EAC1E631F341FF71D971DD91E301ED81DFF1E561EC11E561EFC1D321FFE1EE41FAF1E7E1F2B201C203E1FE91E47205A2071207A218C1FC421EA20DC1F4821A4200920CA1FC41F2B208320BC24092FAA2B0926E2213021D0201C204420321F1C219620B71F5721AF1F522153205F21C920F31FC71EBB20831F2B1F481E1921FF1FC11F071F371ECD1E8C1F821E391F0D1E341FDC1F181EE41C601EF5202E1F5D1EE01FCE1EEE1EF920C020E31E0921611F931FFC1DDA1EC51F9B1FA71FCA208A1F4D1F9B213620FD20A8213A24BA2CE02A93254B226D22ED223520992046213B22B9213A200620B81FBF1F1521451FA920C620EE1F1520A21F4E1F981F1020B02084208D1E781EBB20871EEB1DBF1C661FAE1F561F541E071F2A1F9A20071F33215521C21F311F091F1C202F20082152214A1EC41E751E9D206A1EB41D2A1F7F207C215C21A920B821052226247C2F4A2A7624F6228621F422FC2028224A209122AB1F6521F5218F1FB01F8C1F5B200D1FEC2128220820301F961E5C1F9F20D220221F4E1E961F711F0C1E611E4C1E1B208D1FA61F331FD41E9B1EDF1ED81FCE1F90200521C81E591F3D20BF1E391F891F83204D20441FF9200B202B1F24202821F41F8F21661F46200222F122362D6E2B9A25BE21FD206121F5217121E22130206F21D3218C20BE1EA02041219E21072070217C200920A621F821631FDB1F691E7C1FD91F361FFD1E481FC61E441E2E1FBD1E391F8E1E7B1DE920691FB41F7021121E8C1EAA1E941FC51EF91E6A1F59208E1E80207F1F4A21FD214D1FF1202F2004218921FB210D21BE21F623772DBE299525A6233C23A820FB20FE203C219120C72074228721B720E4208820FB20F32022208B2032206D21D21F8A203B21F71FC02031203A1ED11F24207E1F991FB01FF5200621AC1F171E491DA71EE31FE01FB91FFA1ECB1F01221420411F6E207F20721F8820611F1320062017205C1EDD20F7205E2103222C2269220925F32E692B4E259123D52077218E227B217D2163210422662181211A21EE20811F43202B201E210A2250201D20F12090207A20481F2921BB20132055200E1F4F200720A721A720661F0C1FA420F21F3C1F971EEF1F90214820A21EEB21A41F421FF11ECA213D1F65209C1E2F21B91F9D228F218C22B221BA207F21642275223524C430F929F226EC215E225B2228222E237C215B213421622211206F20CA20DF2187203A213423D1204C23C5211A206E212522621F2121552088202520821F2321AA206D2148210E21A81EF4204120FB202B1EA620EC1FC11F541FDF1E9D1EE31EB61F43209F216C22411F4A214920C71F641FE02082207220A7207223B1221A255B2F432C7C247A2175211A214221C7205B21C71FE21FF8203821F120501FDF21D821AA21D11E54211622D91FB91F6C1FE51FE920F71FE51D9E1D0022831FFC20401FBF20A8216C204820A91F701F541E7F1F9A1FA81F6B1E6A1E6020C81E381FB1202C225621912002210822361FE81E82218E207120D0205420B421632324247F2E322BEF24FE221A22A8215321E11F8420D02081218122D720A11FC41F582019216220F12207210F203F206C21BB202E20CA20861FD61F291F781FD1208920462244225E224C21F020551FC51E2121401E1B207620E81FC81F701EBF20CC1F74201421951F3D2085202122FE20A920D823C82050210B23F21F4722ED228523812ED62A8524D7236A21C7203E213C214521DE21E421AE1F9F210321E4216120F620C4219F1F8F229A200721E1206521B0205E1F0C20F01D9F1E5120881FE12125212022F622E7215821E4201F20541FC21F2821DE20FA1EA81FFB1F5D20741F172070204121142030214B2200217F21372023205322E3209222CE2281222C24022E9F2A4724F721612254225122C42128216D22B1213122E7219D2136205322F8204D21B32013210D1F551FE31F34201E20511FA01FAE1E651F2520501F4421CC218120FB21F2213A213F2033238A21971F25222B21C520481F722059209B20A221D8210E224C237D211223A9239022FD217F21D520B7212B220422EC225823272F462B812437238D226D226920DF21AB222221062030213022D51F15213223112155212722BF206121B120B920FE2162221F21C21FBB1EB32001219B1F6C215D21EB21CC222B212D1F192244224822EF218C2260219F228221BA213521D520C7223E207A21A72146216E22FE2070227C23C423CA2196228B22E123CE227225CD2FC82BD024B721F2219922B7211E21F620E622252121231222B520C5200120AF20A520CB203B21AF236B20DD1FA5214121A1208B20B2200E20E71F7A20382059211A21522210221B22B8211F21C9222A212021662148222B225C22B820A2206A21DF22C620AB2046219A203D21BB21C0243F24FC22612399233F2551250E24AE2E532B1E255622B82337222F22A422FB213522CC20A321F821EC2096216722F421C2226E20AF215621B821B92252206121E820B921D521CC208222A9217F22D3217F225F22EB225C2200219D226B22CD20C122D621CE203A20D722C620B220B922CD2132229B22AE201B235622E1232326B9255523DF22CE22F6234923D325492F632CAE256922A5210823A522632267221E226021B72265227020BB217B21B821A2221F23622000223821BD2035204A22442157217C22E520F4211722FB22FE2033228A229D23FC21DF238222B922C322392121223D21B1200221DA221422B4242E23E422D02306238323E922C024D225C424BC239E2389234122A92389243D2F3D2C102752228C239322C022DE22B023AF2200227F23BE218624BB21632257226D218B22B92119224C2159217E2123222022C02176203C2182212D220E22C3223E239E23C4235522F823BF228223A921DF214D218F2032213F210223C12242245F279D258B244825A924512450251725BB24EA221D2462231424A1235A26B030B82C45261625B6241D23F7211B232D23FD23F6227221032335222E247F22722386232722E022EA21F5213422B021B921B4211423402112212222A5227D212B23C923BB249825BF23F82394238221B221A222BC211A22AE21D32327220D252C2849285628E825DB25FF242225D42597279125A4252025EC231F24612650269630952DAE26F82178226C227F243023D22227241A22112472229C226A216E2236235923B7223723762467211C22522113243222FA22E8222622C222402265222E239C2383243C25EB24172407243B22D2228D235F233E2432214B2309242826A2287A2A572A0A298C26D02527260826BC26EE258F255E26DF24B9254D27E82696312C2C592541230123DE23A0225C227223802288243F2193220821FB210521D61F67213722662386220021B520A423FD2378231D24DC2456223C2349233A237C2330244A25EF244424592397214F22A4227823E5253422902205249625DE250828C027F828E6260E249A27C928D52AF02A0F2970265C26702628297329A9282D31382CF7278323D7249D24EC218B216121CA239B247125F023622128227224B524852227239323F023E220D7211D229122292439264425D4230A243722D2242A25B0288C27A726F6227A22CB23C72306233C25F126A9232124842448251027962606271D2641240326D827B428202A602B602A862A6028712880288A2A0B2AC331042D0A26C82333237723D522A423B9228122D123D623E1227B2341236A231E249224F0221225F0239A2248234B23C923602510273D26BB255D244224D623F626E728DB29F8277724CA21D024B524AD26E828FE2880267A248A24D924C6254E244125592410240226D02AF42B3D2B8F2A0F2BC729F22A9829AE2A2D2AA92825312A2DA1278224B5245123F623FE219B22D123B923AD223023422303245924C3243E243B262D25EE246925EC23DC268826D0261E27AC26DA222A24EA25C0253828632BD52BA9288124DB2481230626E127CA2AAB2A9225F2235324D923EA2369231F239A238D258A28D42A932A012AA9290B2CEE2BBA29B329862A55291F2701313F2DA92664233324162547229023DB216924B023FE23A32393246523D924E0242F2642275727F526DD289927BD276C29E4272A26E3268A233D23C125F027B52A192C9A2DFA28F924D024A7257E27F827322A9629E6251324CB2266238424C32176230A2388235026A9293C2C2A2BB82B132B9E2B692B002990278927E4251430E32A6525B7240F231E242B23F1222322CC22C9232025B8259B24BD23CF232925EB27BF27F32655282B26982640285D279924CF24E3252D2351243D275B28FD2A662B172B982AD02491226427282A77297229B72AED273F236A23EC23192377233F22DA2252251929682CD02B07299329362AF5296128962506259C2501258C30E32C3926C8249124212408229B218E23842425263527C52575246922E62345253A2781280A279F27F2250C25B8240C261F2425235526BD24E0256329572A992C872B912B40280E232B22A026DF28652A052C632ACB272F24C12322214722D121D82423225026FA29202BA92AB529632AAF2876271327C8241B251C24DD24732F732D7C278424EF236B2404246E25982681273F2773271225EA2269256723C923E3252B25F524D024E024D0217423F924FA231D2357241A251E2782293B2B0B2DDA2B222A4927CF220423842472282A29A02B8B2930292925E122CD2200223521D4227B241026AF27B92AE4285728492713263F2329255723EE22A6237025F0300B2F302886253625E3241225772602289C2BD12814286027AA258524BC241D243126E324E6239224CC23EC241E277B257F27A1250924D5267F27322A942C3E2C412CF62A1A28BB22C8220325BC269729D72BA32917293B264924F2210F2285238A24CF25EE27BD28D128F7275D26B425BC23DB221A24FC20DC23F3245A27B530652FA4287F2654253C24C7256328EA29712A7429462944280C2596228E229122F8222824C2237024C7264B261528402A6129AE27A325FB269A28B229292C032B6A2B2829E9251C245924B625622607278D28E3287F2710247D24C2211521E921B5230D2663287528E127A324ED2330247B247423B02448239724FF25AE2873331730C22BD8280928BC27BD28EE2A8C2BFC2BA3292C27D0249324EC2431249D239A251B25A3232D28BE27A4299B2B742B012B1829AD25E326372B3C2AA12BF62BB42AEC275525862468252727A3254026D12776278326F423782264217122A3236B23F3255E2720272827B125FC247C25DF24462402248623C0248226722B3E352232522BB927CE27CF251D287E29F62A1D2A722881270C274B24CE22CD2399232025FE2632292B2B752C242C342DB72DDD2BAF27322569256A28742A782AD62A61283B260A272D24B925C126E62604264A2637271D2716265125B622E2219722D825F8258625082673271626B826AA273E275A260A270F258927FA291B2C743346338F2CEA2908278729A9261629B4292229412819286627FF247A23E1246C253D27ED280E2C482C522CE52B742BED2C682AD8266E2425265C27EC27B428F82753267F247A269B25FB254425902690279A29CC29AB2A2127C12526246723A322F0241A27E027242857297829C829E82A152AD329C528A828A9292C28B82AB7337C31162B20288226BA261C28F2264728E9289F29112AD4289525E324572383251E27202B1E2C3F2B8728FC28052BE42BF8271C284D25E02224261E25E624D3267A2590244F24BC22C5237225E3284F29462BD72B0C2BB72990286A25FA23A4246324C8254A28E828CB291F2A6E2A9B2BEE2AE12802288A262E260227E4289A320431562A79271E26CB278C289026BA26B12A9E2A562A2629372547234C269B277B28B02AC6297E29032AF329762BBD2B8A28BD243223D3230D242B24F524D62356236324EC238722A7259228E7296D2C272C752E0A2C172C27286D268A240C246F259F27092A6E2B352C6F29082A942BDA281027F4269A260827E22545265030C62E14286B2522269A2736276427FC28552A312C652A9A281A261526EE26AF279628E92850284F298A272128372AF92AD229E5251923BF2282232F216324E523AA249F24A324DA248726FE271E2BA02BD42BBF2C6C2A0D2A4B2936255425D124C2240D2676280228812B402B432AB22ACC2779259A265425862537274726DD2F532DC6287F2687262E2935294E293729DC2AA92A542AFB26FA234A2500274B287B281B2930253726E22640277329BA2ABC275324C322D32346244324A8267527FC2671240A2568269727A129022D8F2ABD2AA029E62936285F245625C6248A23CE2138249E235925B7274027B926DB27D3247D2260244C23BD230C247E2559302D2E5D286F28F6290B2A302B3829772A8D2CCA2C1E2A162807259325A226362818296E27732648278927E728B72C492B04278F24E3234B243725F1277F28992821283226EB26D426BB28C02AE02AC52AC02AD52AB027662527251D240724602476234624E4234D23DF24F7241626EE24F8220F2374236E23CC235E24F125F230492E332AB729F62A662B842BE02A142A882B512BAC2AAD260324C823F2251F25F1279E2410262726F3269E2A8D2B302CA7284924F421552365260F26F12895294D294227FB27002627273227532864273C273D2890249023EF23AA23A622362495238721DB224822072403242123DE234823E321B7212D225B247025DD27BF301A2E0F2BFF2A922C212DBD2B9029B62B312DE52A3128C8257A23F3238825F4276226EB266E27DF28A029D629BD2959270A268A255F24EC24C924B2250628A3281229A827A126462655257D256F252626BB24C9257D24EC215023B02414248D237D228E2545247825372562242623D9225323202410245023A825F62598282433622FE22B912C962BA22C752C472EE52CAC2B9F28A326E22454241924C6261B27F429BC2DA42A4B2B73279C2711262C26F42155219023DA22D223B42586260526BA2486252F25FD246924B5229B226323E1246D23F8233E246125C92735267B27C926F6277A260326E4251B247F23D32331238023AD2565264F299B27402B80334731882E422D0D2CEC2A382CCE2B932AD82AB0289B26FA23FE23E625C927082A222BF42B2D2BFB2C112887253B25E124172173239E225F236A25642323255D25AE26AD28A626FC257524E823A1223722A523592539255725E926B528D528582869276A27772719256727A22462235B2332245926A5278027E0282C2BC22C9D358F31552D9E2D2E2A212A6429C728B926C427032868260C263827FF26F829822B7C2B552C9C29F02A3D298A258F246C2246233C211924C423992515240126CB2628283E2B8F2BBB29B928DF241B24AD235124EB25DD25FC271A2BF72A6829D3288C26B226CF2548259123EA22A5240C2512256627712BF52AB92B512B8C2CF137EC33D62C492BD228DE278326C9257F27FE2527255C2722275326E3260F28502ADC281329C328D027EC234B2448243524B323AC249D249D254B250E26C027CE281C2C652D072CA42A2A2894257D237523CC2686285728E129A7295E291926AF25A627D8251E24FE23402537232A25AB2722278D273C2BBB29BF2A892A162B3834C832622E102B7F2BE42A3C299727C226FE243827C8292A2C71277927AC25E6253E25F124A8259625FB269C24C526A0268F25B4264F2A11286B28002965280A29C12BC22C482C3B2CC6281325B1240E25FA28BD2BAD2A85292B289327102610274929F627FC25FA24BF254425A324E7250B261027E1295928F827FB276027A32F8933572EB62DF42B5B29F02A7428D52705298E28712AA82B222A0626EB24502329260A264D26D628F1276B299529802B4F29282960281E2B3B2BF72A9D2A9529A32B692ED52BF82B9528D02547246E26E629A12BFF284A27EE25F0265E27F728B1293D297D288024412873279E24E8254E259D2509291D27C6257427112882306E30BE2BAD2AD42A11296B28B12612283226CE2542298F2601276A2559266B25B227D027AE27DB289F2BE129142DB62B242C292A44295F29FD299F29DC297429B629652C902B392A8F27B7241928A128712AD72CF729EF2734270B277E28442A0B2A2A28532744279B272E27C6264325AC2220221B273D26DF27DD2642284231682FFE27942774275128902531273825F22549272F277225CD261D274B28E7263828E828C7282C2A2B2B342C962BBB2C9D2BFD29752A762AB9292A28F1268A26FB2744290C29122701252D24BB27A328502CF82C4A2C11283C2810279128C82AFE2A8F2A2F2B57285B29DD2676253E24F12354278B291C2BE42C712A532B2534842DA4271D27E726F025A4253D25AC257E2632276728E127B0294F299B2ADF2870272B275528272AD72B0E2C6C2D192DA42B2A2AB32A9228F928F926EB261E2592241C2632278B240A256C2667277B287C2A5F2B0E2A2229DB264F26E227FA27BA282A2ADA2BE2291E2AE726E0256C261425EF29412BB02B412DB02B872DF237832E0829B8291D287026C925A5243725EA266A27EC28E3274229EB2A602B952789269126C7260629FF29EE2BE52BE52C542E4A2AA1267D23AE2439250224D424CC23C325B524B724DC2537289C298D2A2D2BB829EC2BFA28C026B025B524D52673272C28752A2D2834284F25EF22E2266928132A7D2B022CC82BDD2CF12D01377130CD2BE02AC529312892266E246A2675276428142AB629082AD127DD26DA274E26E22573266C279928742B802BF72BCE291727F22411242322C424D7223B22FF22CE24A124F7253928EA291D2B952AB02A8D2B6D2A482A142607240425A8257D2795272327EC2604281926E3255027F229022CB82BEA284829442BC82CD635FE30D12D2F2B792AC62812276A27D3270F2A3E2ABF2BEA29062AAC287627A02477251925DA25F6263C27352A0A2A2A28B42517250B238D22C7220624ED23092475249E24C6262929522A992BD02D272B622B052BE92A4D299E276223EA224324A0274329092AF1272E288826A6269729FD2ADB2BE62B372A8E2944289D2AAF344031372E422FF52A2B2BB42B972A0329C029B12B432D5D2C492AD12954272F28DD2673261D25162773257328CB267E2678247125D222E922192528260926EB25BE24552539299628572AC42BC52C452A302B102AD8273A26DF2778238923C5252129BD2AED2A1A2B1C2A21291229BF2AFD2BE129A92D5128DB27502B8F2C9835F932EB2CE22DE12CB42DAD2F4D2CD12A2A2BB02AFF2CCA2C542AA528B7288627E42AC52ADF2720246A2634263724F1254E257F235C2524251B26C1267928D0276F2749271C2761286728E228E529E1271D27D12709279126EF26B225EC239A268E28C42B962BE12A812B44281528E42C882B362A752B002A162A692DB62B0837F931C92ED12B052D142EB72C842DDA2B1C2C742B7429D828532732272829F82BB42B1A2B9028B126B524A926AD267024FD23D525C8258627E029BB2A1B2A582AF72AFF299128CE265D27D427CB2528267C2782269F2861282F295C25EC248826BC29B729B02AC82C4B2B1E289827262B9D2CC72D802BE92B922BD82D7F2E2437632F2D2C862BB72C9B2AEF2B862DAC2C452B25271B29AE257927C726EC28E528DC2A7C298E28B8253824AB2436249D266E2567253728FB28C02BEC2BD729BD2AF92B182C862844262F283D26212467238F2510277F287828A6271626D325C42579298F2A4C2B272A51284A273527F02A3E2A132BB92BD32C0F2D3B2C6B2DC8350A2DF929DE282528A62A962A8E29FE2B83288926DB257A2428270C27EC280F289627A72711268A26262573259B274427AC25AF25AB277D2A0C2CF82A192A062BA82BDA2A212A4C278624DD258125CF23B024E3285126B5267D285B27AF260F27C2266228322A54282F29DF26DA2776292A29A12868294D2B122CCC2D952C47349E2E9A29C229A42ABE2AC92A7829DE290327C524E6255827B92572261A277127B027C626B12550280D28C6261E2A5428E7270629F82A0B2ACD2AE12A36295A2887292829FC26F12421251B26212763289229852A6B292627D4248624C6256324C1252D29BD2856271D260625FE28BB27B6264727182A7F2A142BA82B512B3931C730952A822A1E2A982B952C4C2A33286D26FA243B25D224F526FE26BA26CA27D826EF27F2273B2B3C2CA52A972AB52CCF2A102A612AA72A552BDC29172904273629D728BE26EA268A263F26C0289D28702B8A2C2F28B4278F26ED24E926DE25B9254826C62442263025C327D1267127CF275428DF28892A702C562A5C29AE31C82FD12AD829EC29732BBF2B6E29C2259F26CD254D27AE28EC2765277028D5243826802AF72A672BDE2B98293F2A912B3F2C592BD82B182A0F2B572BDA2A662B402B632BEE2AD1264B27E728F6290929AB2A9129802738259C2893280E2721274B253124F7249D249A26B22681280C2BC529AF2A292A0A2A5A296B285928FF304E2F7B2A292B9B2B592AA729BD27DA2695286B29B9298A2CA929B6282728F5250F28C3282C2B552C692B242A1029D629FD2A022BF02AD529062CFD2A4F2A4A297D2B4D2BC22B572809286A288528FE26AB271026C1254C25DD285D275C27D826CC248B2325249824B22775285A2B5C2BF329AB29C2271327E5254F26F327E032D131272B342B7F2C8B2B6F299D26CA27FF2A532C942B282DCE2A522949278D2742278129EF2BF82A7C2B392944290B2AC529A628BE27EE282729E7280326C226512AFE2ADC2CEF2739270D27D625F024C624AD25B523D025842598270B259E245D25F62218253A2640274F29682A8A2B6A2B0129DA26EC2531278926BE29A235CF2F872AED29362AFA2970281F286D29032B502BA22C8D2B352AC026F0258D27E2268127582A4B2BFC27EF2522255926F524BD25F7245D2571268C241E2518287228482A252A8626EF25F5232B232C23652517252327AA26852674245225DE249924AA235324912695284A2AE82B1C2DE3291F289726C425CF273129F72BFE36BB2F432B7528432902283E276D2ABF2A182BC72A822A6C2A2D28D1273827B1289229922B6E2AA529652685287E257B259E236C25142407248925E1256D243F26B427F726122714245123DF247F244924C725A028982A4E2A48297428CE272528B4279A2557267328672BB02B542CDA2BD12AA728E526362576284E2A492D28381C316D2B5E2A4F2A6D2984293D2BF929362B772A4F2B5F291A28AA287B27642BF32C882B372A6D2AF527FD2559272F277524372445235E24F0259424DA26B42698270427EF2649242F246B25E824DD2526286E2A502B222AB62CE62B8B2A722B72294C29A527E927ED2C322CC02D702B002828286728452784295A2CFA2D9C36962F2D2A94288C27B32AA72BC4298F2AF12B682BC12BB02881268826C4283A2A0D2B582A5E29D62AA9281B28E426A626AC230A26D4265D268828CC274428D126452636262D2673263B25DF25BF27F927D4285D2C7F2BC12B252DCC2DD22D2E2B092A9A271A279E260C2A3F2B2B2B122B1E278727C2293029692AD32C712EF935782FAF2A7D2730276D2AAD2C4F2C1E2D042DC92CC42BEA2890256725C9258F28D926082892274628122A582C3B2A2F27CA26CA278729712AB429862A9C276A26CE2756262F2982283A274127AE288129A02C8C2D332C012E9F2DEC2B672DC72B0D2A55279F257827E128E4294E2A6C2BE328BF28112AEA2C872B2B2BAA2C7034FE2F22297327B827CC29842CC52C2D2C522E942CCD2BD4274B25C426F227C928B82893278D2806295A2B9B2C0A2BB229E2282A2B0D2DFB2A8D2A7828C128EA26E42748295D2AD4282128022ACD2A2529132C542CCE2CE32D852D932DE12A7129B52644265F275B259D29BC2B9B29952A5D28DD2ABC2C572A8A2A012B652ACD33D72D1B2937274128F2292C2CB32C552C622BFB2B902824268826EF273F297A2A492A9828BF271029CA2AA02DA42C752B082B6D2D1E2CA6280F2847264426CA24E026AE29A52B74298D29792CD229C42AEF2AF32A70291A2B9D2D072D3B2B54299227C3260C2449276A29A62C532BF32AE12A3C2D562DB32CA22CD12A0C2A6133242EDD28A128BD276C2A932C422B442D2F2C4629A727412616288729C02A0D2EB128D72804291B29DA290C2AA02B922A7628F6282C289B27CF26BB25AA276827EF276828012865289329DA29EE28D92719270128D926B428A52A2A2DDC2C3E2AF5274D26D125BD27B12BE12B0B2C742C232CA12C502C3A2CD52B992A882A8134412F902ADA28EB28F629422B992C922C9C29AE286D276027C126052AB02AF82DE22A7C29D8285629B829802995285B298B258527A5267425A1264B28942741286F283A28F8275929702A8B2A2B2AB1270B26B82637266D27BF288B286E2A552B1029B928FF26B7286C2A4F290F2AF729CE29902C132DB82A2C2C362AD62B9B337931B92C47298128A3297A2B732A7F2A6F29ED272328222A962B892A8B2B402A8328A728FE29A82B252C272BFB29FF26EC265226D7242526F9281F298529002B3E2A50289826C6266328D9280C2A03283628E026DE26A025B627AE28D328292BF02A002A0A2ABE2ACB299828FD260A285129602A482DE42C6B2D1E2C532CD334C430FC2B892A2329EB29DE2B1729592736265A274C29E12B322CAF2B412C3C2AD42AA52ADF2B542C662DA92A1428EC25E122CC23A2249024F329F82A7D2C3A2ACF2A592A082B5128052771278C29AD28802731282227F3245B2B6C2AEB2B412B012ACC2BC12A5B296829102755265B267726EF288429AF2B892BC62B542BF632A32FC02AA227B4286A2AAF2B042A8F26DC266E286D2A502C702B712B8E2BDE2B9A29612A302AE72CC82D1F2A6028D7242A24F6231525082531291D2B7B2C6D2BA42B2B2C4C2CE1299527AD25CA273126B0266B28C22605294D2A022D9F2C4F2BF829F629C1284F27CF2637269C2697275A27F72838290129082B072B682AF432F430C22879281E2A332BE82AE0276F278926742A842C832B7C2A042B182C892B382A092880289E2A302CAA295D29A226F7255024BE259725B9299B29092A082BE32BBA2C692AB7296127132604264D27EF2AA0290A2A8C28C62B972C802BCB2B9729BF284325872574267A253C28A7296D2C642B7129F0270D2AC128A0283C336F2FAC2AC7293B2A962AAB29EE26B12635288729892BC3271C29DB281329A62A1829B126FA255A29B6281F286C280927DA269F2646278F29652A4A2A532B6A2B8B2ABD2A1529792525259A244D274128792A452B362AEB27DA2B742B802E232E162B15294A25CF231E279728E229992BB12B392C812E212A35296E281A2964319630E72A142BDD2AD72AC529D52663298C2AB32B5E2B9A287F2657272227A029DB28F3253F26BB2675279F27AD272628C127BE299B29ED2BC32B982BD82AF7296F29D4284227EF232322502669298929712B722D092B4E29482A552ABB2B6D2CF42C3F29BA26F926FC278329982A3B2CF92AEC2A562CAE2B282BD72C2E2C6833DF2E442BF928E22948291E290D28AE298E2B102DB32DE22A452A4D286D291D29F6280A287128D827A0272F27BF29242B1B2AA12B8C29862A95292E292E291D289626FF2630264826CE2669284529FC2A242DAF2C8729F529632B9A2B872C0F2C3E2DC228A02706276F27092AB12971294B2B632CAD2CB02B862D342D992C54362331672A6E288C278B284F27BB28162AD42D3F2DA52D6B2DC92CD62B242B442B9929FB2BE3295A2AD329B4280C2CE72A8F2A352BE92A492A342926299327352774285B29CC284828C928CE2A432D022ADC2CBC2B9F2B6F2C182D7D2A5C2BCB29B82B7129E72882262929692A7C295129082AC92B002D792C152C3A2C5B2D12369830202A69260525C4259E27B629612CF42CAA2C152CBD2BC72B2C2C5D2AFE2A2E2BDF2A2F2BBA2A4E2AFD29262C752B0E2B792CAC28B9270028A428D727FC28032A8729A729BE29812AEE290A2A262AFB29862A242C3929B4290B2AD7272D280A2929299F298025C3268F28E927D428D2279329232B53294D2AFE2A2D2B0636E32FAD29962599239E25F62694283C2B7A2C5A2AB82BD82B982ADE29E22A66295428562B222BA329A32A142B0E2CC72B6629752A87296F27D5297E29F929C62A382A9229FE29652823291328E726B4252E2823289527DD26F72785255227B927952712299F29CF28012956252927B1289E29C628FE27832811298728C62B9236153095280D262D25EE264629342CC92C742A452B352A512C262A872A2F2A6B28F127352923277B27BC26AA262C26E0267B261F26C92516277E27E5293A2AC527BF27F726E1257625482852279F27A9287528982A1C279A242D2583255229792AE02AAF2ADD29AF29BC2A90281E296D2BAC2C792BC02AEE28AF289728162AD734582F86299C29582742276628922BF42DFC2BEA2B452DB62C862B9A29B22765272E27B6265A25DF257B2505258725AE260D24F0241624662595279A269A27D526552552249125912604286A271A285A2B242ACE2C62281724D425BF261E28CC28442AF92B4A2E842BC52B8F2AC02B722C812C882CB62B7A2AEB2ACA2A1B2C1235BF30F82A0429A3286E2716293F2C512D4E2D6D2CD92AC32B672AE228D226F12554259D24D325F72493251D24A025E72430257E2481259925FD2612261A269A26E424A9256426C023B02781278229922A8F2AFF2A9228CC261427C028AE2BAB2A8D2B3B2DB12DE42CAF2A56294E29E82B842B9F2C712C0A2A40295628A5283831B131342B2429102733272C286F2C0C2CA52C5D2CD02A75290F2723258626CD251B257D26E32593277226B8264A28502722263A275C27AA29EC285C252F26E1268827D3268827022616280528502ABE2A602AA72A312869269E284D27BB29012BE12C8E2CF52C9B2A592B9227D726672A162B2D2A862AA9297E273D28782A67324C31252D912AE028F8276E266B2AD029F12B5C2A752645263B27D625A225FD261927DC283E29532A052B202AFB29252A10297929FB2A722BA528FF26C225A727A4294F2BE32AC82758298529E628CC27502AA42AF9278E269B268D265529DB29F12BF32B362B7E2CA2298C26A22517266227E628912994287F28BB28662ABC34A030A22CEA29E026F9251E258826A12751275727C02724277227DD26F82750292E2909284E2AC829282C032B212B7228BA25992861267927D227D126EC243628652A692DD62B0A2700273E253E284325942651284A270F260D286B293B292C291A28D6299F2B0229E7297027E42536253A27C025FA28B62A782BA42C4F2BF334C631872D5F2A99291928A1262D27262ACD2BB52A062C182D622A572A172C202CD929072A9D29DB2A562A5B29BB27F9253E276829CD285827BD27432729272129912ABF2BFE2A23283B272529E2296C286028C929F029DE2B422BE329D42BF12A9A2C02298128C1274327CE2553261D268024DA289E2A602A7A2BC12B592C46333031C62D692B072C472B4F2B032A142D5E2D162CCD2BF72CCA2CCA2B9A2B682C8E2A212A3A2AE22CF92ACF283B2957285329392AB6295C2835263F272428302A422AE62A092A0E29F4284E2AEC2BF72BC12A492BAC2B0B2CAB2B0B2BEB2AF22ABD2CC72A0D29FF299D28FC26AF259A253B271A29432AF629C929AD295A2AC231CA33BD2B872A0E2CD92C742A572BC02A872BFB2EDC2CE82C1C2B5029692AA129F328A62B162AD72CB82B182ACC28E7280829352A4029D125C626CE27FE28B12BF62AD528C72616276427A129FB2AF229532AEF2BC62BC72A572CA12ADF2A8F2BE42BB42A0D2BBA296A2B7729A42741278D261B29012AE5282D2AFF298C2AAC349330092C9229972BAB29942A052A232BAB2C0C2DA02E9C2CBF2AD12AA828A628A828142B432C7D2CA12AEE2A512A73287D28EF295E289426D329F028B82AC529B22B692959280829BF271A286D28A5263927C12877297D2A8B2B092BCC2A802BBF2C312CF22B8F2C922CF32BA42A3329FD268F29BE2AF92A132C8B2CAE2D7B35A32EB9281B28CF26C4274A29C129D42B592BB02CFE2CBF2CAE2A6528A627BC28AB28A92B072C212C602BA0290828AA267E296228C6274C28B5288D294329E2281F2A632A8F29142858270E2A602843259826DC296C2A3F2CFF2B262C802D4C2D142BFF2B242D092DF32ADC2ACF28AD282E280A29B82A2E2C8C2A5A2D7B2E1B35DE2D11294727C428DA297A2B4D2B022E692D732D502B922B892A5527A6276128C4287229162B992A4B278726EA2402267B2476279A278927E1287028CC26F828EC28A626492768267B2678281428FD2445273729382875275A29E629EF2A4C2ACC2BA42BED2AFE290C2986285E270F27B828F729B42CD72BAE2C902B8B2B7C35D52E4B292A287A2A9429642C9F2B492B4D2CD32CD02B982AC429AB26F326AC26772898294B2A9628622758253B262D252C254827E026F3287828F327CC257725B2274927392572251E264027E224AD23ED26782526272F26F127D9292829F5279C29132B892CDE2984296A28F62643283C28612AD52B0C2B212DF52C122C983525304B2C742B6B2AD42B172ADA2BD629A4288C2855285D289B27232A1C2A162A592ACD2A7D2A3C2A99281B28A6279D27FC27A12861290D2CA82B5329C62855284F290B29CE2647258D266327F825CE24FA262728252789266027A6274B27C5256B29662BA72A572A912AAF2A6827F627D326C029D12B6A2B6A2C522CFE2DED368B2F1A2CA82C442D212D452CB92AF327F226A227DA26F128C82A782B352A8629F628B92B382B3A2A88291629CE2C0C2AF6264F277B2A642BA02CD32BBA2A372CBC2A6228EB26A9255726AC26E8261C26E12813295A29F0298829F427D0286F294B2AEF2BE92C1829CC299229092A8E2A9327B128672A272B5C2B4C2C9A2DD4348C31AF2D9F2D462E032E172D882C8329E52AE229C228572A792B402DAB2B5729212949298B2C1B2C352D3F2BF629C3285828E429EE2A092E292DAE2C4E2C772DA42B8F2AD428E0253C294F2A522A302B562CF72CDB2A5E2A312A1C2BE82A442D112D782C832CFD2BC62B712B102B1C2BC128EA262828582A142CDD2BC62C8C35AB306E2DAA2C2E2EE22C562CDD2CDE2C742B1F291229322A382B092DEF2AD32660267A270F2DC92CF32A85290429D0288C27C927952A282C3D2A962A902AF12A662A19290D26ED251128CE2B182B362B062C472BA229A0296E2A032C5C2EEC2C342C4D2B712D722CFB2D282C662C052A402722277228382A762A9B2C092C1935A92FD32C8F2B8229342B0D2C352C962C882CD12BAB2AC22B4D2B8B2A492A7828DB25A828082CB02C3E2BE72C0B2A9A2A84290A2953290F2BE82A762A962965294629E826D425852654276F290A2B7F2AD52BDD2ADF28DD275B297929362B242B4F2BCB2C402C1E2C7C2D2A2F1A2C502AA62758282A28B328142CFB2AA62C4434A430EE2BC929D0284C275527E72801287728152913289C28B929EC294E29132708255E26252BDC2CBA2C112D362BEA287C270629C62A132BD22B4F2959285326462614256D2553260428D529352B3629092A3C2B2829A7265A2800272227A627332A302B5D2C702A302D132D642C6D2BDA2839298629E729462B4C2C1F2C47337A31182C262A29277027F02597252E27F5256227BB26D827792726262125E82417254627FA298B2C132B052CD52D202AAA274E28712AC82AAD2BA929D628D728EA268E255525F425B828FC296A2AA829922A3F2BB027CF2521258625D826F5268329602A422BD52AF12B482C022EEF2CCF2AFA299629812774299E2CEF2AAB33E62F99292B29D7259F24AC24CE24AE25C3261E277F250C25EA241A26902551240E256C2626265228FE2723290E2A5D27532792275B280F29342A8A2B212AFD28FB26CC247E256527A5286D29D7286129C229FE2ADC27F624C5258D25B7253D288529862B612B8229EE29142BF32B192C422CEF290A28942720286D2A212C7E34F62EF329E428FF25C7258324F9256526B2285D270C27E1264A26DC25F42512265D25D1257525C027F02674278229C72878276528F6265B27462A692AC829B12768278C26C625F6268827CB28FD2602298F2B542AE5294C27BA278D270128142BA72B3E2CF52BA229A42AE52BAF2A762CDB2CD3288428DE264729972C692E4C35F92C092916260827E72625251427782845290A2929299E273627F62591249E25AF25F4264E28BB287A2784271228142A2E29C0260A26BD264629082BC02A9229602975296E290929D928AA29A829F828A52CC72D4C2B1D299529FB288729B02C382E692C2F2C662A7B2B0E2B202C952CBD2B60299128BE27662A6F2C4F2E16365D2DC128192886264428632717294B2A092A252A632AED292F287A25BA2538258626FA27EB2CCE2A87298926FF27E8296B2908271B264026CB26E1260327EC276828A7274D28F629532AE928C12A472AD62D582BE92A5C2A7F2A8B2A3E2BE32BC52B892B7E2C132BCF2C9C2B7A2CB72A312B9A2AB52B7F2BD62B012C9C2E73361F2F6F294529D32BAA2B312B4629682C862B902C472C2A2C4B28AF253027582745277C2AA92AA52C6E2934256828792A472B2529BD26CE2637277B268425A526B1250B273529F728E3287C2AE2294629342BB929792AAF2B222B2C2A272A9E2CCE2BD22B5D2A292AB42B4A2BDB2BCB2C52287228092B752A8D2BC42B932E4536DA2F8D29DC2B6A2B3F2D442C372B9A2C902C592B472B75292329182680261C284427112AEC2A692B2629BF26F6267627D727D229CC29FF283527F2258524E42360256B28F226BA27E92853264A2970283F2AC62AFE294C284D27CE27FB287E2AB82A5F296F2A542ADF2CF92AD12B45299B28AD27802B332B1F2C302C952C1D36152FDC2AE329E52B8C2A6C2C852A8C29CE2B9F2BEB2AA927AE253626E3253E261E28F6285D2AAA2929271727862759289229D92BB12A152A8D29B42694254A2569240825B7248524EA2508263B268927522AB42C972A33288A2633287F29BA2B572BDC284028F129CA2DF62CFF2B4629D427AF27632A902B702CCE2C152EA3379D30002AEE298729C92A7F2A112A742B802ADB2762275B254C2566230125E025062533281028F227F126CD27F926A52AF22BAE2B162A602AD52A542A662A88298528992669246C24D123FC266828362AAA2AEA2B3E29FA2607273227E1297F2BDF2AFF29862B032D672E2B2C152B33292028FE27AD2AAB2CE32D332EC32CB0362330A22914288F288C29662AF2299A28AB2857257224762425245A2562270E297828552785265326C325B2253F280A282C2977299B28AC265E287829F329FE299E29E025B823E121D525AF259129CA2A2A2B232B1C27DC24AC25EE28B429AE2C032C402B912B8B2CF82BC12A7E2BA82943287028652A2E2C532DB62CF62D8636C430E52955290B276D273929D02812283A27D724B024D025ED242226C029AC2BF42A8D296629422671250224B72578285E290E2A122A00276E269227C726DB27DF262225E925E424C726F327FC281B29152A9E296527DA27432833274C295E2CF32DFA2D082B312C3C2D7B2BC928B327DE2608294C2BEC2B502DD22C012D513409315828B726D3264528172A0C2A86279A25BF244B23A9245F257226E328892A872B252BF82ADF27DC25B62413259927E927BF28122705286B2619267F265425D92563253A2769289F298A2ACA29D72A442B142937289A2910276D2696289029F82D872D202D232C332C082BF228D128EF278329DC2CA32D3D2D922DE12A26345A2EDD28A1273826EF27A22A3B2860262525D0247B25B0240127DF26F42871298B2B662C1E2CAD298028CB25D526612A1A289829FE290D2BF627A226EF253F243D25E7261227002782296D2BFB293F289B29F42702283D271027C3243626162BC42BD62C082C3C2B9D2CFE2A48296C290D2A6129362BCC28DD29162BBE2A0F34462FBC2AA726C6272328EF297729D425B52616241C25D226E82760272C275329F728DB2A302CDE2C882960288727162ADF29C0296A29642AF82AF528242560244125B4275B261826AB27AB29F8294B27DB260F28CD2732271E279A23F6243626E62AAB296A2AB729292A3328A0294329732B59286B27EA28AE28272AF92A39338030932C9C29E028842869290C283526052799279128E2289729F6266427B527D328DA2A662A5C2DE42A5529EC29D92800290428252937288B2B4E29A529AC2696270B27BD2659255628A128EF28C1276D26A427FF289D2775273823A7244F25F3285D2A312C1F2BCC2BAF28DF2A7B2BC1295A273F2647270F2AF42B512CB8351233B52D7D2A4F2AD12AC62A08278D266128EF296A2CFB2A66299B2A6B2B912AE129392A1D29AD2AAE28B32750286A292328F3279426A5268B284B29B827F82744289A25E3241F26DD255629F229E3251D29DB2A202A6E264326C1245626762577271A29522B522AC22B9E2BD92AAB29282AB726FD26E325EB280629122BB334B231152D872C642B4C2A8C296C294028562BD52C4B2C192C192BF0296A2C2C2B472A8E289227902664254727EA27D027C2269027EC25262502275B2985290D282228B227DF27F0261C28A72971299A27BC278C295F29C32680253825A726A3268B27E02A0B2CC02A062B922AF429BB2B20296E27FA27A827DB2A5D2CE22C27363630EC2AA72BE12A23295A29C226C328F02C8E2C4A2C612A3F291E288B28372AA5298C283B28B426B123EE23A125BF2639266E27DB24752699256C244A2771267C26C927A1293B284429F029AE279727EC272C2992270828C3275F26BB26A8264328FA285D2A6A29632A1F2A002B2E2B5928E32612296A28C32AEA2A8D2BB9331C30B32A5A280C2971285227F627272A722D5B2A4D29062A5F273F2724273B28F829CD29322991271B25C7248C261927A12672289F273A257225DA264727A327D52716282A286429652A212A472931281829A428E2276327EC26F1260527C229EC29D0283F288229AB2A6B2C842C472E89299528C228CC29062B602B522D9C35CD2DAF295029BD258B25222612266D27912859292D28C727A825A82691260C29642808298B2A5528E1268F2426264325A4264229F627EF27B8275F26232773266827FB256127A5274B279B2665266C258B26FC258325582841278525AE269828772A35293C29DE2A202C5E2BB52B2C2A072965282227142A3B2C2D2C352CFC36A42E592931263324222424241425A525A926AB2624257E254825E8256227A724B3264728FB285E297428DE2538255E240926162896297F283929812761282728B3265625EB256D23C824642472234D238825C32638275E27E1255925DB251928F928DC29152ADE29BA2BC829BE295D2A602639262328E328BF2A702EA12D92373C2DCC27AB24A7236123A624ED230027E22394258A2628277527D32883285E26712668299529D82A88276325962521268725F6270A292329662A59280B284A27822628255D248123CC2430250F254D2546250A28BB28E2270B265A261F27B2273329CA296429C627B9281A251C26D724282429269C273D29952C4E2C662D0E36F22D562653242625B62399241B2557265427D726BB2A702BC529832A142A2629B52952299C2BEF2CFE2983264C27A62844263F28D8282A298128F1256426762598269727BD285626AD2651275A2832273028562AEA28B8282B2A842A642B8E2B8E2936298928F02677267B25B823BB2464256224942628288528342AD52B0234BF2E65298F25EB237C25A523C825BA2656281D2A1C2B3B2B712B0029E6260129EE295B2BAB2B322DEC29E72675278628182816287A2AEC2BB1284A275C259624D028A829C429F027D1295F28A4284727E429AA2A97287928F629F0284A2CD5299F2C232C132B8F274D265F244324F7259024D7239D268B28692A412B1C2DDA36B2307529D625462420262224412665262F28B1285E2AD629B4295227A8262427A228802A0F2B422CD82AC0286F270E299629BA29AE2A8128E8285726C5266025712609273C285F27DD24482822271F275C295F2A3C271E27FD26CA26C726BD280B299B29B02B63283028BF26A225D9243825F725D428A32AE72B9D2C6A2D5037E12E27292C27972331237523D1242025972795263E275F28892885297029F126102A9D2B1C2CDD2CD62BE3292A2935271227AD28D9271A289D2ABF28CB25F423EE247F269E2493237F221625DE246E28592A362BE7263525E92532257E263825F328AA2A6B2B492C642AE628BB273F284C25ED255628C627182C062D6E2E2E35CD2D212896252B245C2313242B25612594258824CA25B027F0276028A82833298A29F1288B2888299B2A792995280B27F3260026A325D52748264F272D243A24BF23832382247A220022DC246726E727232A8329682865259224E624B124902842299B28212A4E2C152CBA28C52773274426F924822784279229E82A312BEA32B42C5628DC2308232022B921F8233523CA238C225A24C2222C255E26F926CE26A527BF2604271B27F627CA243F26B428F126E42513250A25DC25DA238B257A25BC2345234F245621FA227B2522278429042B542BDB2AAB26F425CE2327244025382905280D261826B0277D27E125BF2672248B25DD26B725842534278328E1301E2D10278A258423712496238922B12271238C22C323B922A9248A24ED2570255224E625E925CA2656258F24102562266125A423B3228D24D52485240625B525DE2513256E2382221E2566277529CC2A8A2A3D2BC528AD27D225F623D922F02403261825E224FF248425A7236E24F9254D285C25C625B22368230C23B626DB30312C6625DF235C242A24F3225222CD23AC21B42288228522F422AF22D6237C25C7246124C9271129BE283D267527F425D324C0249424C7230024AC234824CB261F263026B5220B2360230B288F283C29EF29A82AEA2875274D26BA223823A023C92315246F2336238923CA24A7265625F0264127E325FE24A824F225212701307C2CE925AB221F24B12456239D2327233523732407240422DF225223BF2493250F26022750284B29552BA52847297325BE25F224BB21D623C022A424CB25EF27472614249E23F5239B2313286B29F728312B3A2BAF278D28DF253A246523BD246F231F21302402231122112421247026C227D527C8269423FF25A926AF297432362C892652232123B024BD245023FA24262375227C22A4224C21552245259527BD26C027302B372A1D2C5E2AA6290627C0256C25C0220D23C82226253225B526322502222D2398225F23322782286D263728312754277925F826FB23BC2510232023A622E722E8215B243C22FA24A927BE290028F9252E26C6272A27DD290E35F72C72268F2267244D24AC248A230A251E24B822C32287236422CB2169252227DB286729082B202A812CED294829D325E723B6248D223F218523D22618259F2487238F2119236E22B72268263227E825FA26CA267824A524BE23A322A02229235B241B2306232B225A24D724B32606294E29DE282726CC258F284C2C342DBC37CB2C1C26B0244B223B25A625E82319248D24262403254423E922BA221F2593247D261E299D297F29D42AD0281C287025AF239B249F2316238C24042357239123BF22B02295215A23FF245D25CB26D228C327D0242A249B216F206722CB225725D9249E2551257E246226B92679283B2AD828CC2909289E265429492D2B2ECA35 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 403F + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 55 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 0009 + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 4F282D223122112002210A201C1FC41FCF1F541FCE1E2A1E3B1E8D1D3A1FDF1DCE1CE51E6F1EA11C7C1EC51DC91F521ED71E221E501F3A1F781E8E1E3F20AA1E4B1D0C1E251DAA1B221CC51D461DAA1D431DC61E8B1D8B1D1D1E2B1DE61CF71D3D1FAF1EA21E831C2A1E931DA01CF11E571DE61F4A1F001F541ECB208522B22D4D2B892337205122B8211E20BD2018214621E91E59207D1F841E301FD11E851EB31D5E1E2A1E381E4A1E6B1FBB1F0621661E0A20AF1EA61FE01E361F0520651E7E1E821F701E341F7C1DB11FA41DD31DA21FDC1ED11D9A1D501E3920541F571E971EAA1E371F3D1E861F781FD41E021F9D1E2120F620592017210321C123612D362B8124EC22062290206B209220AA21C11F7D22AE21AE21D31F601F931FE71FCB1F70205D1F3A1E4F1E4A20AF1F2B1F7420BB20AA1F8C1FD31FBE1CDF1E1420C31F7020951FDC1F931E1A1F431E0E1EAA1E541F111E901E341F061DF41E441F561F671FD81E981D601F8D1FAD1F971E8C1E1A1F1122BF1FB5202121A624AA2E9E2B6427EA23FF209F21F920E92196203E201A20D61F771F6B20791FD120591EA81F8D1FA41F7121DD1E471F631E112002200D21F21F73201F1FDA1DCD1CBC1E1620AC1F1021651E231E371F111E601D7E1E771F581FCA1F061FEC1EC91F0320E2208D1FA01F031F1E1F211ED41D831E2C1F751F8320CE214420CA21CD23ED2D882CC725D321D520F520AF210F211E2140219E20AF208420A71F3B1F94200020041F5920791F6C1FAD1FC41FC621221EA51E04227D1F491C571F081FAD1E691E4D20E8203120F91DB91EFA1E0A1EBB1F9D1EC120D41E2B1F961F3220F31EDF1E49201E20AC20E71E1220C11E5E1F801F9B1F9F1F6320491F522096224E23B72C5B2A802543220823632025225B208920E521C3205F21A9212E1FF41F21209120B020DA1F891F3521AF1F261F5820B020B91E2720311FF51ED21E411F991EBB1FD41F2D20891F871D0820F71D221FAB1E201FAC1FDB1FFE1FE41ED71F281F7D1F9420A720691FDE1F991FFF1F681FEC1F0D1FDC1FC221D720B3204E234124F62CA42A57231522A421ED20C6201522F620A020B7210721CB1F3620AD1F10211721812125208020D81F7D215221AC1F1D205D1E1E1EBE1FC91FBF1F2F1FB42063206A1FFF1F751FC71E321E551E961E221EA91F29211B1F041FA81FEC1FCB1E581F9020B91E2620451F981FA01F451F671FCD202A1F8B20871F4521F8219322A12D9C2AAF240622B32215212E1FE41F8E208320F020F61E27204F210622E81FC61FF2206920E7205921ED20331F2920CA21EA1EB81E6A1FC01EBA1F3E205B1FB71E151F9B1FD61F951E861E3D2067208B1DE320971F741EB91D821E531D741F011F561F271F4E1F8E1F3B20711FDD1EF41FA91F5C1F721FF51E9321E021EB23462E57297B24CA2265217E210920E31ED320B22101215820DF1F9F1F571FAD20B31EFE1EA220D1209420AF20151FBE1D3E209C1E1C20361E891DA01D8D1FE51EAB1E671FD81F531F2A1F4E1EED1D791D281EBD1F951E841FEC1DE5206C1FF01E3C1F981F911E7C207B205621511F201EA31FA220241FC11F481F0C202C225222542E0D2C0623BB20B9200421B91FE8206F20132272207A21D820111F8120A4202420D320B41FB8204120262081205C1E8C1F8B1EB4206A20891E9220891E621EF81E1C1F9C206F1FB81FD61D6E1F201FE61D851F23206E1F281FC01EB81FB11E761FB222AF1FE41E9220A420672004215B20602006215F208620CE1F762275230530622A9626A92282210D228F200921E72035216B21AC227821F9204B1F5D20BA209920412080208E20331EED1FAD1E8E20F81DFA1D2E1E4C1E461F9A1EE81DA71DEF1F1D1F331FAF1FDE1DBD1D401FA81DEC1E021E8C1EBB1E1D20BE1FD01FCA206820611F571F5B201A21531F371E3520FA1F781FC01FA11ECE203A223223E52F832BB024C6217A23D8209A1F842053200322C7215821D020B11FC81F751F6920C91F0020CD1F7922B91F42217B1EDF1EF01E121F8B1FF11E1B1E931DEE1E331ECC1D561F0A1E841E851EF71D261FE71EEA1F9C1EB01FF61F26207E1FEF1E7B2061209D209221691FBF21B520DB1F4821C5200020D21F2620EC1F6120B024D02E412C4D266922CD21C1205720BE20681F3321C1200E208221DE1F6B21B020CF21D4203220F21E59218C1FC91F6C1E85210E20C81F971F7A1EDF1EE31F181F851F7A1E6C1F1220BF1E421DBE1E2821971FA31E4F20CD1E361F2F21D220311F0121AC1FDC1F631EE71ED01FE51FE61FDD20C11FEB1FD42148200621F3217F249D2C1F2B422593226422BF223620C320B9211622BF2144204E208E1F891F7621551FC620CF200F203720CF1F2A1F8C1F5C20C520B720EB1E771E8A208B1E211E1C1D651F8F1F5D1F541EE91E1F1FAA200D1F7221B421C21F8F1F1F1F4A205B20D6202421601E3E1FCD1E9A20841ED31D381F632081216821AB20A1212D22FE23312F6B2AB72423238921EB222C2119225F201B237320BD211422AA1F0520C51FE4205F1FDF21B322EA1FDF1ECA1ED31FC420FE20771F871E811F931FA61E821EAF1E3E20BA1FCA1F721F0F1FC51EFE1E5020B31F0D210721191F961FFB1FBD1E1E1F531F7A206620B31FDB20F21F681F4A2039212320E221521F7720582272233D2DDA2BD5252422C9204C212222B421CE21FA1F8F211522C220D61ED520622114221020822199202220F321FC21411FF51F731EE21F3A204E1F121F5E1F9E1E9F1E151F831E5F1FBF1EE01D3E21A21FDE1F8C216E1E8C1E8D1E7F1FDB1E201F541F7820C41E9D20901F4D210022251F2021522030217921E9215D21002224246A2DF129CF25D0236D2326214C217621B3218A201421A9229E21D7200921FA20382118218620D2205420C6211520C220A820192011215620601ECA1F3220671F9B1FB91FEC20FC208B1F551EB41D851EAA1FBF1F5D1F0F1FA31F0E220F20191FAF207D208F1F9B20601F3A20F41F0020491E5D21262168215222832289222F251D2FAC2B31255923C420E5219F229A215C21B6210F22AB219221482152219C1F6C203D20082126224620282014217120AA201F1F2E21E6202A204620011FC220F61F7F21A020C31F031FAC207520651F731EEB1FAF2137205D1EF321A31F0D1F221FC4210F1F8820E31E2821B71F7C22CB21C222A621042169219A2293227C24D630E929F2262D22AE228B224F220823082246210C218622F71FA020C020ED215720F1200523D4203C231222EC1F90216322401FDA207020AF20F21F401FBC208A203521F320B320761EFC203220CB20541EB620A81FA71FA51FF51E511E881EE81F60209E217E22231F3F2158200920781FFD20CE205D2082204923CD222425842FCB2CB82418220C22772104222321FE217220612059212B22A021A91F5522952214226A1FC02134223620F81FB31F5A20152177203C1E0E1E3D220F207321281F1421D421AD20B8202A203620E01EB61F54201220021FD71EBA203D1F15200F21772262216621B4219922981F8B1F3822FA20AB2055210F2141221C24A124482FA42B6125A0238D22092212225620012151219221F8226921212036209820B321842057234E21782087200022D820AA20452100200A203F1FEF1FE820DE2069224F22B122AE215521931F591F8B21A21EAB20B82047202E20D71EC220EC1FAD2095211420B2200721E022A621B42198248621B5217B239020332304245C24292FD92A6824D9236D21E42066214D2119211C22DF21751F8E210D21F121A1201C21F621D11F12227320412128213421CA20751FED1F3A1EB81E5720A31FCB211C216E220623CD214621D8203E208A1F37203C21DA20C81E721FDC1F8220251F0C20CE20C92193209E21DD228D210522112180204E222B214A23C12382233225AF2EE22AD924CC219E22BE22802220226C219422C3216B224622BF2152203B22E4204721C0203D210D1F581FC61F5A204F20A81F731FC31E9D1FFE1FE81E3A21E321D320FE21E021AC219F20A523A021F81F41224921EB20AA1F92208020D1204C223C222522F1237022AB23982420235C223D2247219F22AF2365234524C4243230542B96247B236322AC227F20062258225D217F2025210122A51F702145233C2157211A228D207421CE20D120C52120223321D01FC81E8920D920D41F5A21B321E021F1226321611F1F226D22C2228922F522F4211D238B21D32115210321D422A120D2213B2212226923A921BF22A123F323CE22E223FB23512534246826DB30A42B8424D121E72182225121D220B820DF22E720B8228821B92062209D1F572060209620262185232D20A91F5C210E211D202D202820D71F751F24200320602120215022D121C0219C21562134236821422155211922092260228F206D20FE20E022F6209E207B214E217721D9217A249024F523BA24E624D826B52649251D2F1E2BB22440228823E32106229E221C22152297203D217C219A209821E321C221C7222D20C22148216021862254204521822034216A2105201322CA21D1224722C52241229D221A2285209C22CC221E21E5226322CD20CE1FA7229E208220A6229B214A228B22CB2058236922E9235F260C2761257425A2240126D124AC2666300A2CD1255D22B5210F230D238D226A2234227621A6228222ED20FD21C3212022032334237D204C226E21A4204C201A22E320E2205022EB202422B622E723EA214223B122A523BA211724E4227F23D5232E22D02275218120DA209922B52161243423352364246A24BB240923D024F626AC27D12719273C264C2411254025B62FFD2B072703227B2319230F232623D223F8222022C423EB21AF241822A3229322A421A922E0212522782180215E21D821C221FC20242066210622D22239238123B623E62380236E2248245F239B24F322D022D0210E212021DF2096220922B9236527382605267F27A82690257625D7266528A726B226482592258224DF26DF30E42C0C263C25C624C22362223923BE2375249123A3214A239E223A24A922E823DF235F221423DB21FA211D22ED215D213C21C0228D21B5212723F523D122152482249C246425A42332256F25F7220A23BD2315223A22812170238C21FC23EE2637284529BD28B2299D289526E92504284A27CA2780267A2451243B268326B530B62D29276622CF22C4224125FC23FF23DA24AB22C92439232723DF218F22BB23D823EA22302394247B215B222A218A237521C522D62387234924AA23D423722406248724DE25F125FA25EE2568241E244D24B0230A25FA21CD23C52349259427342A272C9C2B3E2AE82808278025DA25F925DE2664276A255725FF26B427BE32DB2CCC25D5236123CB24D223C423FB2498238525FD2124235D2122222B213E20702112223723BD22102182202F23F622E422FD23EB258D2456253625A32409240424CC25DC264326C225AB23B22335235623FD25E622C3234C242D25C024102750285F2B9D2AC026EF276A27EF28C428F927462777273B263F28D628A9299E335E2C2628EE23C225F42505236523A222C4244325DA25F2239A219C22D324B324552202234923DF232B21BE217F213D21062353263E278226C42602242425A2246E28592868293F26BB24552536241F227E2439277C2536266626D325C8267D26A4289E284C261326EF25852659285F29F729B42B162950288A27E92A2F2C2534EC2C65268224C624F12475245225D12317230E24F02345232B2462238523EF2300248E220325B923052314238E226E225124AA2792287628C4260C256623DF256E289C2B4E2B8C283F249A25DD23AE248A27022AEA295728D2260E26A92665252327D02565244924C227FA280F2A6B2A4F2BDD2A012B1B29FD29822B142BCF33C52DA828E7256B260C250D262C232C23FD235523E422BC23D0234F24A4242324A1239C25BD241525A72596235925F22442264428D828662595256C2582248B261F2B6B2D952B5028D3269B239C23F0242B2A702CCB289927B226FC24CB2482245A242D24A42489257527F927B328B7287F2BF52BE429372A6B2B282B842921336F2D3627442486255A26A623532440221624FF2221242A248725F523B9244024F7245526C526B326C928D2269D25E327AB2781276B288324952381248125F128C22A762E5D2B2F287F263C25FE244825012AF92B92290A2871244C2437258822CC23A9225C2283236926BE29B02AA72BE82A3D2CAB2C062BD729F6292928A230E32B8C2603269C24952549248D235A220F232E244C26B627422655256F24FC243E274627B726D728932602262F272E27C2250D26B52656239D234E25CA26FB299E2B682C2B2DAD275524962669274B2793296F2D472BE1267625422531245524DA2268224E24FF268229022BB2294D2B662C142D052C1829E12711271B267531AC2D1B274C26F8254625132315227D2331248D2601290D287427E224EF2424252B260128922771283326952433246C26C524DB237F260E24682420271228362BDD2B022DC62A552655234F2519267428032C9B2C402A152790252322042336228F24792163249827B1286E2ACC2B8F2C052C1E2B632AAC275126F8244F25932F822E2D290427D425E625F62431251C265927D0284D2AC5280D27502984264F24DF251A25AE257E257E259B2156232E254E247223A924DF2321255C27DB291B2D782CFF2BDA29E4258A230C235B261E28C92BCF2ACA2BD927E6244324EE228621B7225A232F2461262A2A6E2AA12BAF2AC8294A262227B924CC231C249825B1304C30E7299A2847272226CF2432256126602BCF2AFF2AC92AF9290F297B278B24D925B724872321242123FF23EE259F2429275F263524AC2547250A28702B1A2C782CFB2B472AFF24AE222423EE246C28DC2A2D2A562AED280E279523A02240237A23BA23C825D5275229102A4A2959287325BA235124F720BC2340248D26B02F3831692B1F2AB128722506255F26B628D52ADB2B6C2C3E2B7328C8257B246923F622032444239A23D5253425DA263C290329F22819272C26E426B528072B0A2BC22BAA2AA128B7241424DA24922528273329F9299729FF27802780231622AF211223B6249E27EC28F629F827F02586252925C323DA241A231B248C25F427A132CE31F02D7B2B632B32290F288028F72AF92D322C5D29E3273A276027E9256A247925BE24B5225F27D626C428242AA12A522B562BD32759264B2934292B2B6C2CF52BA329C927F7249824BB267C267727B128872861287B26D12401234A2370246823562557275128DF280727AF257725AD241A24F22352234D24F325B22A3E35EC322E2D732AE32AAB264E263528612A722B032AAF29BC285D261625D424AC23FC238B257C27E728E62A512BB92BB82C412CC6292B2756247F268728CE28352A1429A0274527502363248326FA26CC25C925F626862706272626602386228C2209252B257F256E26BA27B725E6254326BE259C25B4268F248526BF29BA2B19330C34402EF72C43290E2A0225CE27A929A2294329F328F828692744254D2536241A2595269C29152BC72B132B522AF22B7E2B0229D4252525DB241C26B727A7270D263824942549247824332455250226D82741284F29B0260026B524F9239D22BA231D26ED265027BE282328AE27632852288F283328D427B8274527652A013396325B2D1C2B26297627512783268D28EF28102AE22A8C2AAB281E27B423F923C82453295F2B732B1329D728BD29172BAB29AD2B7E27C222DD24C42362245A2630255B249A23C4218022B5232E2797279029702B9D2AA529BE290C27F0246C25D8231D259F27A528702974292A29192A602B092A5D29D526CA252A27F828E5318832DA2CBB29412702278A270D26A6266C2A8E2A1D2B062BE4271725A7251D252626EB29D929F329962A59299429AE2A382AB227592491231523E522EA2308236A229A23562369218E236A268627C32A5A2BA22DED2BFF2BB2284B273B252324BC243626DA28062A402CEF281C290B2B752A1829B3279F257C26AD25F625A42F6431972A3B270826E7261027A927ED281B2AAE2C6F2BD12AE228BB26BD25732546273D29CA292B2A362783277829D92AD82B34281C24E422FC22FF1F592350236924D9247024C323B0243E26372AD02A442B0E2DD12AEE2A3A2A9925CD25252523242D2590279027572B0F2B4D2AE52B352AB6274527D2249825F0261926842FFC2F552A6C27952542289329DA2AD929092BE42A252C8229F426AE25B8258526D927572A46277F26A72643262A29D92B692A66272124F8230C247923A925702770271B25772591256226CE28C22B3C2AF52AD42A812BA629F625C825FD24B123F7215A24A2237625062887277627FD296928CD2427259F230E247824AA25B030DB2E7728A026B5275628152B6B2ACA2A0F2CA82CF72A642A61276F25E824DA2517286D271427592769265C277E2B322C252963266A24B123FF239E26E1274B28C6285B2733272F26CB27FD29C32A972A162BD12B7C2921275425AB23DE232E246B23B22403241F23D324F82449260C26F723BE23B8230523D623802435261331532E8C2815260328F429162C472B9229D32A402B7D2CAC28F52469232E2496233027F224A7267C26BD259229502B172D0A2A0726E221AA22ED24CC24E8272529012A6528ED288A264027582747281828CF288329C726CC2421246B2381221E24872379219F2235225B24A924BB23CF24A5236A22D0216622AA24B9254428F430CC2C03297A27B829AC2CBD2B8F29392B262D832BCC2918283B2496237C24A826EC2581273F28A129202A482AD02ABA28AD27FC25E6230324B823E424A12767299C2A9F293228DD26C82533266A26F3261B26102740251B2219239D24B32385237522922528242326AF254525E723382344236A242324D3237826FD2651294B34A72D0D296729D529E02B872CB12D9D2CF82B89299228DB25A72450230C250226F7292B2E942B2B2CC328B028EC26B62638226321F6226A221323B5259E2661265025C225A72583252225E92305244424B42598239E2329241B255327172653270027E927592698262B2718259423E9230723F822BB25D926702AEC28D02BD533412F542C2A2C342C182B162CBF2B472BCA2B88293028CF243024BA2547273229AC2A642C1F2C172E6929A2278926A325C7215423F922CA23F0251B247825C025CB26FC286C272A276F265426C424212315247F256C257225F326E0285A295E2993288B2823280F268B280126F02393235924862637288B281F2AB72C162DFC356C2F8A2B592E4F2B912A38297428AB265D2833289826F225F3269E267529ED2A432BA82CE329772BCB29A426EF24B62211236F216F2488243526C12456265C26D227E82AD62BC62A922AA3278C25E12318247B258625F527D52AAF2A7D29D128E126EF26D4259325E5234C23B924D124D2242827BF2B352B122C5A2B632CF4372732022C792C8F2AAA2919283B26B1272926A824FC26E12696269527E3289D2ABE28C3286328FD273A24C0249424A424BD23F6246C25FD26AF26D7266B28C128A22B402D932CA72B8729F3277824A423502610283D28812AE229B0298226B3255C277D25512494246A2528230325F22743278E27CC2AF529212BE32A412B593400312B2E712CDA2C572C472A86284E2758258526D628742B8028482987275B267225F4247925BB252F27AF24FE26F3264526D427BD2A1729002AF32A142A142AF92BF32CC52C1B2D442A2827FF24B4245E283A2B3F2B522AF6283328F325BF26FE2881270B264C2524269E254F25562628268227332AA22830287928D32712309D32182E202ECD2CF929472BC7288928B5281D2772292D2CB72B2427A72529248B26252642260B2931287D299929C72B8B2AB42A6A29782B8D2B5A2CC72B142A882B732E422C0A2DE02941277B2492269129FB2B7329A0281A274527912769289A2973292E295425A6287328DE252C27B7252326A7296928CB260D28E8286531982F772BA52A402BEF288328BC278A28DC25CD24E828D726E0273E269E269F253127792766273B28682A2C292D2C5D2C3E2D3B2BAE2909292B2A092BD32AE829232A1E2D142CC12A53284925FE273928522AE72C9B2AB928D8273E2721289429AC2918285D273427CC27FF27A327CC255023F2222E28A327DE28F927D3282E322C2FA9279327AA27D128E72635288C25B725F0263927B0259526DE2657287C27DE27F12721277428D228372A352B982C4E2CE42A8C2A132AEC2947299B286727D9272C29F3283A2701250F24A227B4281D2C0D2D102C70281F28B2263628102A5A2A9F29432A132864294B27992573247B24E527D42A332C002ECE2A4A2B18348C2D0428C3270D287F27582737261A2688266327BF28402895298F28892A3B2A86287F26DE263A28A529402B452DD42C122C102BEE2BE52898290328C227ED25F7243B262B277124A4250A27F4276529E52B032C5E2A7D2963277F26B627902709283729F92A97292F2A2927EE258B26E4258E2A7E2C362C902D262CAC2D1D38832E76295B2BDA2ABA296028B225F3255F271E28732A0E2A5F2ABA2A3D2C79291628A2262526CA278128442AC22BAE2D2D2FB32BE72740244725CE25F324A4253124C325EC2463257B262D29472BF82B922C532A6E2CED2959283E261F2515276627D727602A46287D28DA255B236A279129242C662C7A2C262C092D332E05378F2FBD2B502CAA2B342AEF282D25A5261B28A229132C422B6C2A2F28EE272E29A4276B260426B4265F28632B312C172DA02ADD27BB2584244F224C25FF227122FA22E924152511263F28732AD82B522BA72B1D2CBA2A032B0D27F0246B25D025412779278727D52624288E26172659270C2A432C8A2C50293A2A9E2BFA2C3D36402F5D2DDE2BCA2BB8297F28DE2700286B2AF62A9E2CBF2A3C2B6F29D02848255F26D325FC25EC266127442B8E2BF3297627B8252F23C722D5223D24262421249224D124DA26E128DC29F02BDC2D472BC02BF12A1E2B202AE4287E2476238C241128A529CF2AFB276C28B526E7266F292B2B442C942CF22AFF29BB28112BC434EE2F7B2D8E2F842B8A2B662BBE2ACE28D6297E2BAF2C592CA72A3F2A8427D227E0266527E5253027C12501290D28CA2715259225A12275227424D925922581255124C3248928DF275B2ABC2BB22CFA29EF2A0F2A3928BA269628CB23B623EB25AD29982BEE2A252B392A0A296F28192ABA2B9D29A02DB42893289B2BC82C4F3503326E2C2E2EDF2C292EB02FC12CAB2BD12BEA2A8D2C592C922A23296B28EC26F42A492C4729AD25E826C026812514270826C8235C2515252D26C9261F282D275327D52789270F29EB29302AD72A042862276428E127E227CA27E026E3247D274129922C7F2B282B5E2B3F28AB27CB2CCD2BB32A402C792A6D2A392E692C01378230952D6D2B032D1A2E0E2C0A2D3C2C912CF22B4C2939286D26C0263B28C02A7C2BE62BCE29F22733250F2702278B257424D7255C252E27C1295D2A8C2968294E2AC629C9286827842890281326702689276526CA28D5287F2974263525DD262D2AF629242B8C2CDE2AC2271127B82A262CAF2D3D2B9F2B9F2BE22D382EE836A82ECB2B232BB52C942AA92BFB2CB42C612CAF28552A04263E27822676286E288B2AFC29D5290F27D2247625DC2521282526A825A4273F28E02AA62B6829FB29512BF02B5F299927B5281027C724BC2345250F27B0282229822859265D26F825D929BF2AF62A2A2A6E2815271127D82A412AB92A5A2B762CE02C302C8C2DE5351A2DE72992283B28A22A2D2A2A29972CBF299328DB266E245E273B271029B728C628632878260E274326BF267529C629102725268127762A012C912AF429802A8B2B402BEC2A322858258A26C925C8239324F62897265327A7288A27E526AF27FA26C028522A7A28E028EB262328032AAC29DE281329CB2A1B2CCC2D162D4F34DA2FBA2A442AD12AF52A5F2B052AC72BF428B1267A26BB2760260428F9271628452886272A262229CA29E428F52B9B2946292A2A8F2B102ACD2A5F2B48294828052AFA295528FD25E325CE26BD276528AA29202BF429CD279A253125AC26382552266B29FD288527A3269425D9293129B7270A285A2AE72A2C2B032CC12B9131E2316D2BB02A442A8C2BFE2CCE2B4E2A7B28CF25E825D32505285D285D27CA27AB26B5271728AA2BE22CB02BE22AEA2CB12A492ADC29822A8D2BFE294B297F27CB29E429EC27742700273726C0282728CE2AB52C5D28B22789267525B527F52680269926A3240B263E2510288427CD273D28B4280729762AA22C872A2F29B4317630892B94292629A02AE22B502A2F275F272226E32783299529BF295B291225CA255A29FD293A2BD72B002A442AF82A282C632B5E2B8929D42A0E2B8F2A342B5A2BB12B5E2B69276C276629632AB228E22940293627E124F728022964286A28AD2536242125CE241E270C27F228452B362AE62A2C2A162A262933288528D430D62FB42AB42A5D2BA62A822A81286A27BA288B29192A272DB02A2E2ACD29B5269327CF275E2A552C672B552A8229772A1B2BD42ADA2A6C29E62BDF2A802A86295A2B432BDE2BA82852282628B528F626CF271326D7255B254E295A28D62795275B258F236F24B424F9272F292F2CFC2B312AA2296127782720264126F927DE32EF31F12AF42A7D2CD62B852A08275E273D2ADE2B562B252DC12ADB2935286E2795268F28082B362B9A2B4E29EA29A52A272A3129C927B828FD280D29642690260F2AE72ABA2CE12726272527C725C1248824CD251E2484261D2628286425CB247D250B23412599269D27B029082BA02BA42B1529DC2634266F278726D229DE352730A42A2B2ADE2A522B3129F7286D29AE2A632BCC2CFB2B662A84279926E2274C27E227C52A8E2B99280C27E6257627022666263D257725D4261A25B0254828AA28BC2A4F2AD6260F264D24AC234E230D26C6256828E627C3274325BE253025F2244C249424002746291F2B152CB02DD02ACA285F2708265D285829B32C4D372A30B12BF728402A0F297427042AB22A182B942AB42A7E2A5D28C32717276928D929852BF32A512A30272529F026412676235A2518242724BC254426F9248C262228AD274527E7236F23A62448241724CC25AF286B2BAC2A99298D2852286E28DF277C257526B628072C7D2BFE2BFD2B0A2B8928BB2633259128C52AB62DF7376E32B72C442BE02A912988297C2B022B632C682B302C412A3A298829D027702B772DE22BA32A072B3F2929287528DB271025CC2446245425A72691250828DC27C228F027AD271D25DD2428265C252A266C28ED2AD22B9C2AFB2C6E2C212B192CF329FB2963282128382D8D2C062EE02BAE286028C02845289D2AC02C3D2EBA360B31582B5B29CE27AC29682AC329092B712C512C442C6A29A727E6266F28DC29F62A852A7029C32A4E2980290C28FD269823AE266D27D626E12858282B298527562676262F26D6269A2531269D27E727D528C92C682B5D2B4A2DCA2DB82D812B2E2A6227E9269326CA29862B4A2B632B65279827092A9529EF2A2E2D712ECB3524307A2BF327E9267329A52B532C432D972DEC2C2D2C472926267E25ED256D2857274528CD270128522AE32C392BA827FC26D127EE29EF2AFD29BC2AEE27C426082886267429C628C5271F27CE288029B02C942D162CF22DD92D4F2C0C2DE32BF2295A279B255B27F828A029822AB82B30299F281E2A092DB12B732BE32C8E34BF301A2A1F284C274229542C0F2D382C572E0D2D202C7A28C42522278528BA294D2AFE28F5281629712BE82C992BFB290A29D72A1A2D4A2BE22A99288628252739286F29702A5129E228262ADE2A5E29382C132CBC2CC82DC52D732DCC2A7E29CD26622680276D25A329112CF5290C2BE328202B5D2C7A2A0D2B3E2BBA2A4A348F2E7529EC276528D5297B2C222DB32CBB2B662CD628BC26FC268428F929EA2B722B6529E2286D29552BD42DD32C9E2B962B6C2D392C2429E8280A27B226A0257D27B22A442C272A002AC62CB029DB2A392B7F2B9F295F2BD02D712D8F2BDA297F282B275D24AF27142A5F2D952B552B402B892D9D2DF82C242D7A2B892A8033122F0D2921297E28CA2A332D112CD22DB92C082A45287A263828E929402BAD2E9129782A472A0A2AAC2A752AD72B662A96285E2900296328FA263F2607290E297629A7291C297B29982A5C2AB229E228AD2775286927B9290E2CA02D8A2DE82A9828A22621267228FB2B182C742CBD2CC02CC42CE52C722C782C342B5B2BE734262FDC2A5C29FC29EE2A4A2B372CE22CDD294029D52772273D27192ACE2AFC2DF32AC1290A296E29D4291B2A0E29D829EF256B27A526C525C726A0284F286B29B12981294C292A2A202BE62A7C2A0D282B26B22637269F2729294229A12B982B42292629A7277729252B6829A32A0B2A392A1B2D5E2DDF2A352C3F2ACA2B04349B312C2D822ABA29AA2A932B5C2BCD2AE729A128B228862A562C452B542CD22A14293029282AE82B7F2C962B7A2AC927C3277E267625E826EC29BA2A2C2A192C542B4729A4271027AE28E129522BB428EA280828C427982668286C29772AB82CDA2BDA2AAA2A7E2B7F2A3F291128D028E129822BC42D452DBC2DB02CCA2C1835A630602C9F2AD4280F294F2B5629BD277B2636274329B92B512CAC2BAF2C6C2AEA2A902A802B672C682D1A2BA72863263E231424CC241925792A862B5E2C482AEF2A812A472B93284B272328D3291229C6284F29F527A5250A2CAF2AFD2BAD2BD02A732C862BDE29BB29D4277826A226A126F828012A042C942BA72B8A2B533310306B2B24288B28792A252C7D2A08271127A828902A9B2CD62B882B412C6C2C162A0A2B0A2AB12C2A2E4C2B58296426932436247225AD252F2A2B2CE92C3E2CC42BB82CC12C0F2AFB27D025422859260A27E9285F278129402B122DD82CBF2BD52AF32A442931283527C126EC26BF27E42745298A298629752B4C2BBF2A09334031E0289628ED29C72AB62A402875274926392A312CC22B292AF92A052C752B902A0C287228A92A9C2C6C2A0D2A442784267824AE250F26502A252A992A022BFD2BFD2CC92A032A0D27002617269B275C2B212A482A27298D2C6A2CB82B192CF0294229EB25BF257226AF25A528E329432C512B9E2915283F2A2E2940299C333730CE2A7A29112ADC2A0F2A8A27E62652283E29ED2B1C2853294B29AB29D92BB6292A283E26092AED29B229262AC8286F275F27F0272B2A592B0D2BE92B1C2C312B5C2BA2299E26B725DE241F27D128CB2AB32B9D2A2E28922C0A2CB12E522E9B2B39296F255C2468279328112AAF2BCD2B962CA32E1C2A59297D284F29223246311F2B782A432A172B272AFC2669295D2A1A2C1E2C5C29A827CA27CC27712ACD296B27DC267127C0281F29C228E22888281F2A012A3C2C102C012C2E2B802A022A95296B282E248922B426422AB829F12BE12D582BE629C72AAA2A052CBB2C632D8D29472745274A28B529062B462C1D2B4F2B922C262CB22B072DBE2CE433712FFF2A3028532929291329B0279C29232B802D5C2E172C412B2229E42A4A2ACA2A812987292A2924298328CA2AEA2BBD2A402C052AD42AD3298E29BD2958299D278B279D267B262227B228FD29852B512DC22C7D293C2ACB2B672C102D972CCD2D3A2987282B270928862AE929F129FF2BAF2CE82CF42BA52D802DB62C9436A031522A23280527FC275427BF28E129872D132D722DD02D642D892C852BFF2B872A8C2C6D2A142B372A6E29062D262B872A642B142B932A202957293228BB271E29DA291329AE28E7281D2B5D2DE429D52CC92B682BDD2C0B2D762A832B3A2A302CA2297229262725295F2AD32965299A2AEE2BE22CA32C3D2CE92B592D36367031CB2ABE26F824452526278729F12B282DFC2CDF2BAF2BFC2B462CDE2A472B762B4D2BA12B542BE32A602A4B2CB92BDE2A9F2C4B29B9288B285D2900298F29AB2A7F2A2E2A1B2A9F2AFE292C2A192A462A052B402C84299D2AA42A4F282F29D829B329792AE32516272029DC28CA29BF28EB297D2BD229F72A452B812B1D36E130A52A19263B24BF25FD26EE28DA2B3D2D172BCC2C482C6F2B982A562BCF29FE28DB2B8E2B0A2A692BB82B8B2C342C7929172B672AEF28372B002A842A222BF42A1E2AB02AE228EC2932292128B926D3282729C828E827B4287B263B28AC28C428102A942A112A4A2A5726ED277429262A242931299729832A8F29C52C173795301D29BD264425AC268F28C62BE72CDA2ABC2BC62AA12C582A4E2BE92AB0283428A12915279E27FE26D02655264427DA269D261D269C273328A32AF42A242839286A276C26DC25B428442800285A295A295A2B7027AF244B25B325E329F22A882BD22A3C2A862A242B1D29D2294C2CF42C802BBB2A7D291C29F928AB2A0535402FBA29FC29072799262F27842AAE2DD32BCF2B3A2D902CFA2AF42994285C270127D0260125AA254B252C25492575267D24752545246025D2273827DA27102781257E24CB259F2648287A274028802B492ADC2C282833245626F8265B28C628FF29BF2B592E402B422CD82AA32B522CB52C772C662B8D2A242B022B422CFB34F630AF2B862905292C274B288A2BA02DC72DA02C902BFF2B9E2A4929D8279C26D42563253D262A25FB25E6240F2656259725232551268326D5276027A72620276A256F26CB263A244A283528202AE92A042B772BFA289727A7278E29822CE62AFF2B582DF22D4A2D022BBE29812A652C752B022DE22CA82AA629F828A429AF319031CC2BAD29B727F4263327C12B442CE22C8E2CDC2A7F299727D32520277C26BF25E1265B26FE27DD268327D5289727C626EB2759285A2A712980268526762780289F27CC277A2696288528162B442BF82AF02A662826275A294028392A6F2B112D9E2C122DE92AEB2B2D286727D12AC32BC82AD62A4A2A1528BA280B2BCF323D31662D212B0329B027FC25F129DA29412CA62A36265C26932759267026FA271F288F290B2AC32A6F2B532A0A2A1B2ADA2868292A2B972B17292F27BE25EA27B329422B002BF42764297329AC288827412A6D2AEE27C8269826C5265B29312AEE2BEC2B702B992C5A2ACF269E2572267E271129F5296A2866288728442A8D34FC30692D102BAA277626E9243226BA27AC273F283228212898280B281529CB2A2F2ADD28D32AC0290A2C7A2B7D2B95289E25A4286326C62722284F27F42419289D2AA42DDE2B3B273F27CD2570287F2578262D285027FF253D286929642919295D28B829F02B7929DE2A26287D26AB25DA27C8255429FA2A7B2BE92CC42BC2346331DD2D242B4C2A6E28F925AB260C2AF62B892A0F2CD02CA52A742AC62B2A2C322A432AB529C82A062A21298B270926C7261B292F282727BE273F270527A628C029A12BBA2ABF27C9267128C6285F27262834296B29942B8C2A8129422B6E2A612CE6287D2815287D270B264A26F3250E24D028A42A4D2A302B672BFA2BE332D730952DC02B642C802BD12A0629532C6E2D312C9F2BD22CA72CCB2BA42BB52C4A2BFD2A742A352D2E2B3029062A922881292A2A3A2AD02822261127FB27212A612AE32AFC29DC28DE280C2AB62BE52BF92A502B9C2BE22BA82B092BF02AD22AD02CC52A7E297B2AF5282827BC25E5258B278429572A082A75295229C929A5311E33002BA729C52BE72C1F2A572BF22A8E2BBE2ED02CE72C192B2329212AD929EF29A42C802A002D422C452AF3282429C629C82AB1292F26CC26B427AF28622B102B76282926DA2672276329842A99293B2ADF2BB42BE52A4B2C172AE82A732BD22BC12A102BD129932B9329E6277927972659291E2AE228742A1C2A682AA334E72F392B7328EC29B229FA2A8C2AD62BD62C752DCE2E852C0B2BDB2ABD285A29BC292A2CBF2CA72CAE2A1E2BDB2ADA28EC28AE2AE028EA27192AA628C72AE329862B80290228F428B227D92796281C271D27B7286B29562A6B2B512B992AD92BE02C8A2C292C402CD32C1E2CBD2AA4296B27B329812A212B612C672CDC2D9E352C2EC727CA267C2566272929D129282C7B2BB32CD92CC52C0B2BE92872289029D129BD2C452C292C9C2BBF292928B726AF2935298E285728AB2830296D29E928CA29A12AC32916285A272A2A0328ED246E26C2291A2A442C022C3F2C8B2D352D922BD22BFF2C322DEF2AEC2A20293329F2285529CC2AEB2BAC2A582D2E2EF134472D3E282A26EC2732294A2A062A062DE92C242D302B1D2CF82A2228CC275628F528282A8A2BB62A8227A12642255726E3241D28EF272728F8288B28AA265A294C292C2753279726C2269928562808256627FD28252871277929D929132BB62A172CA22BF72A132A4D29DC28752761271529722A9C2CF82B7A2CCD2B5A2B3435A02E83284727BE29B028152B332A302A512B042C0B2C362BE32A86274127B9265E282B2AB02A1F2946288225DF2674258A2595274627BE29CF29DC28DD257725D2270D2835257E254D262F27B124BE23CA264B25E826B1253B28CE29D328C6271C2A412BBE2CD929C829C1288827C62812283C2AFF2B2E2B532DCD2C152C7635692F0E2BF32AB829272B9629292B30291D278D274A2865287927F929CF2998295B29252AE82AAC2AE92873282328AF27D427C0284B29532C012C7E29EC280A2876291829D32647259F263D27B8259D24E426CE27D9264826FE263B27F02609267629912BF92A6C2AAA2AF42ABE275B28E026EB29CF2B252B662CDB2BDE2DA736A32EB32AB22B982CAE2CB62C262BD427AF2673275626EA277329052BB4298F285627D02AD02BAF2A032AA329B02D3E2A332736272F2A6F2BDF2CE12BCA2A2B2CF22A8D28A326B0258C269C26E0262E26F9282E294029DE29B4296028F4287729C62A392C2F2D6F29762A462A7E2AE22ACD27C4285A2A582B6B2B4B2CA22D7C348C30792CB22CDB2D5B2DD22CFC2C432A6D2BD329CF2778283C29FF2B312B432876276B28F22B0E2C5D2D732B3E2AD528A828E929DA2AF82DF92C282C102C002D702B9E2A9628F9251B29412AC929BE2AE82BA32C8A2AFA29362A632B4A2B4D2E5D2E3B2D972CE92BB92B562B092B332BF42805276328272A082CEB2B772C8335AF2F292C392CE52D192C802BC52C1F2DBC2B4829B6287B282529F42B012B2127FD259127292D092DC72A1A2AF129B92948288728142B852C4A2AA02AA02A772A812A34291926E7250228112C2E2BEB2A9A2B752AE428B1280A2A2D2C232F902E462D862CB82D832C0A2E2F2C522C422A732731276A28612AC52AD02C112C0D35C32E2C2C652B6B294E2AA32A632B502CA62C262C922AEF2A182AFD298B2AAA2824269828F42B2B2D462BD32C4C2A472BDE2959297B29072B342BA22ADE29A429F8294C273926EF2663278129062B542AFA2A002A7828422713293D29E22B292CAA2C912DC22C1B2C7D2D5A2F732C902A2328E428B12852296D2C292BBE2C82349A2F3B2B4C2AA5291228CD2609285527F8279B28172855284C29E9298A29ED27ED24D125E72AE42C4E2D952DBA2B68290D286D29472B452B042CD529D028AC26A7263225552538264728E129442B5129DD29BD2AA9284D26C727FA26A1276D288D2B0A2CAC2C732A1A2D412DA72CC02B0429B729842A502A722B1F2C582C81337930A72B482B3428E327BB251D25EB269025E32653267827BC278B2658252525B424A9262629F42B722B5C2C072E592A8227A0279629852A652B31294C28972894262825D524752585285F29E2293029F6295A2A0127322555241B2594260C27CC290B2B4E2B102A752B3C2C2D2EAF2CA32A3C2A652968274F298A2C782AAA33082FF7287A295F275C25F4240C250B263D276227F0253B25DF249E263626CE24DE242A260C267D286728DE29D52ACA270427C2268E27C928412AAA2B272A05297327C52424253427EC2793286F28E7284729982AD527E2247A253425A2258228112A382CD82B6C29702AC82A242C132C282C092AA128C5275928852A342C8B34352E4A291C29432775260C25CC25E426DD290329FC27F6260C26D8259E26302731261D267B25A427E4268F279D29F1284627BA27F825E4264D2A4E2AFF29F3275E279326902586261527892831270329B82BA52A302A902792276527B827C32AA92BE62BBC2BD029B52AF52BBD2AA72CDB2C152927293F277829762C3E2E5935412C6428B12500270227112543265D28E1296A2A272A90277C263925A9242F26C1253F271D289E27BF26D4267C27AE299B28F7256E253D26D028802A092BA7299A291A29B8284A28E827CF289328DA27232CBA2DF32AF7281D29F9277328CB2B182E4B2CD32B672A892B1D2BDC2BD52C972B46298728AB276E2A832C422E0836E32C1128AE279C26402932283329142BA82AAF2B762B582AC828CF25412665253B26DB27932C7E2A2329B6255527112A002A67271B261E2671266126D7265728AC28D3274A28502AA92A3829032BF02A772EA12B122BFD29032A0A2AEC2A112CE12BCE2B172D0B2BFB2C092CB52C3C2B752BE92A522CD62B262C5D2CC62EC736922D1C273526092AEB2A112BFB28442B152BB02CD12CDF2CB0295027A027E6263A261B292C29332C52298724FB263729962A2529A326CC25622698255F242726E02415263128DF271528E229F029C629012CEB29DF297D2A3F2A9029B9298F2CD62B902B2B2A412AC42BD82AB02BE52C4C288628FC2A542A6F2BB22B562E5936F42D6F272D2865288D2B3F2B152A712B552BC02A762B532A652AD4275827E427D025D727DB28022B63298E263A264526232795294329F6275B26CC25EF231F23A024FF266A25AB2679282126EB292729572B4D2A0C29E72675268727FC28722AD82A8B29072B382A262D092BFC2BE3282D28C2275D2B662B262C4E2CA22C11362F2DCA288D2668286028C02A4C29B327FB28312AD82A3628FD250127A5269426A0276527B92884284C2660269B26442791280A2BCF29CF285228C225332551245B23EA23B2239F232625D02517265927122AEB2BAD294227E8251A288429102C952BC1281528492ADF2D002D192C6F29BC27FD27912AD22B4A2CE32C2F2E84376E2E82288826C725BE27A6283D282A297028DD25EC265D258625A023D1255127882595270D270527C925BF26CC258C29912A222B5E2919293529632961295E28AB26B325CE23B023E622D925CF27B629C329C72A4328AD253E26B6269329252BDD2AC5293D2BBB2C592E272CD02A1F29CF27A627142A8F2CB62D1B2EA92CD136362E3D28222500260E273A28EF27C8268F279124FB23DE23A5230E2537286E2A032A0228EC256D259E24AB24CA262027F428612951284E260127282872287028B427CF24772329217F24DD23DD27F8285C2AA82A5B265B249024B727F128EE2BBE2BE52A142B0B2CB12B712A3B2B6D290E2839285F2A182C332D7D2C972DFC35C32F7E298228B0240925E527A228FC27FB261B25852487251124BC244829702B702B3F2A3629A42571259623AC249E272B297B2A142B56276826CD260F2627272A2778259C26A72476258125EE26DB27EC299629A42722279927D32627292A2C522EEB2DEC2A1F2C6F2DC12B2F290F28D8266129052C142C642D382DF22C91345231F428A0267D25E325F327C9282B276C26F324592314240A24422478264C28AF290E2BDB2A2B280B267324E923A3258A26882872276628A0269F25E9253325A12579256B271E281328B527EA266328C029E12866275E28AD2509260C283E29372D6B2DD92C192C662C8D2BF5281D290C287829052D112E6F2D192E7A2B6B34362F362A54286E256F25852772263527372616254B25F22348266F25D626652684285A2A0E2C312A72297926FE253828F424BC276E28F9293428CE261D26D9233E24B9262627172789288E29132975266F2898260C270826342608246725D52A5A2C0E2D0B2C962BA32C712BA729012AB0296529D12AE428EA2A142C762B9534E82F582CD02761279A25DC26D427EA25EC26782332248326BE274C26D2254227C6250428B229E42B0B2AC4283427142845265D26E72619294A2A1E29482504247D24E6263726E225E526CF283D29E42635265B277F264726DE269323B5245E263F2B0F2AB92AD429512A6828CC29E628EA2AFC2755275E292F2AD22BC42B6C331630A12D052B3829A2275427D2268C26DA26462649277228C529EA25E8256026742727288826202ABC299A29DC29232709262C25E326B726052B1E29D029B826A126842690263725DE274828BE28AD273726CE267F283B27C0275823E524922573291A2BB22C6D2B1E2C0C291B2B012B3F299D265F265627782A7D2CC52C103680312F2D252BBA2A492A772AA627DD26AF273028442A262B542A262A6A2934295229C128A4263C287A26D12612282529D42690262F2542256F27D02895271F28E327682560253C2674254A29EB29AF26B628DA294A2A492776274C253B261D2583279729632C112B682CA92B022B002A532A1B27C9265D253A289028DD2AF134EC2E482A482A3B2A9C29A4292E2AF3273129612A202B0D2CDE2B15297F2975286A28AA274227C9258F2421268D27B5273A27E12792252F246325B927AC2787265227A726CD27C62674270F293D2952272727EC286629BE2757262E25C025E62489252E29B92A712ACD2A402A7529E52AD2283D27DD2635269828C7295B2C2436E82CA027D0270428E62747293027E327E22A492B4D2CA02A282AF7278A265A271827EF26C028D3273124B923D4241D26AD26DB284B2686262F25392310262525ED24B92668287B269B2832297F27DD27FD27C4297D29E8296B291627F6244624F025062797287B282B2A492A822A542BD328F72635285E278D29B1295E2BF833352D6A27E424D8266227732692260828BA2AC2294829AF29DE267626AE250A26DF26E6265F27F6270026B5246925B525E3241B28DF27E525F8244625842550259725862569253F271B2820280228AF27882842280D289C27ED277726942441262A26B7255C26F927D629F02BF22BC72D9229DE2885283729A529632A942CF734FD2B8427B3262624BB24F024772421258C276D2832278327A024372511254427CB258E265C28292774277A259E25E0235C24192730268E27C326A024C1241524F924412389245A254D252E259325142501268425FD2426286E272625C52480258F27DF259D265D28B4296F2A292B052A1029EA28AE27642AF12BE02BED2BAF36282E9428DD247D23A323A6230E246E24DC2530268924C2242124E22479267A231C252526A826A328F9296A28672548232F24A825D826A8254C272425512634278226DD24D224A5220D2404248E2354230625C725C225D92626262225D8245826D7261D2749267A26462990281D29412A272712266E280029D42AA42E982D2238E52C9427A924A0230223B623762283256E222724CE2462252225DA26AE26E4246F245A2678256F289627FC26A7258B24312364250F267D259427772668264B26D626C52501258E23A924A3256425FC24B1243726F825E725102593256726E726A027D2263426B024C9265524A525B12445249B25F5268A288F2B402C4B2DDD351E2DEC25C2247B250323232308230B24FF243A248527EC27F1263E2891276626F926272585277029A22972285C276F26802389252726B7261827B42505261124EA258C27BC287026F726C2278328D2266C2772280E258925662847283C2AAF2AF1281D28822744263526622561239D249D2430239825DA26E2275129542BDC33672DAB282925102539255222B123D8232025D2267C261A280F2A4C29BE2692270D278227CE27712965298F284C281F27E424C8246B27BA29D328E8273625F22231262127D6268F25B428D4261B282E261828D7275A2544256427B226492B5529212BF22A7B2A69282F27E6244924892515247A228B241726B4286229462C3C36562EC1271925B8249425B0229B23DF22762414251E26CC256C27D526CF26C825602509268225C92730282828202817286326AD24A72526257928BC2607266723FC220E23B1249524CA222826B3243224F7255B26B623B424DD24B4243A2557272227F626B4286227BA2826274225BD23B4232623A224C925F6266B28182AFD35A52C342743267D2312236D2225239522A324EB23BD239B24F7241828F42898258826C325A226F7279327CE268027342626254E25CA23552411298F289B25F822CF22C123CD214B216220F1229621B823892558267223AC23C924AB23A0243E235B2634275C2731295E290C297C274F271E247523CA237B22C126E627552A3033C02BB226B424CE234923A1230C24B223BB23DC2270238424FF23B224F926D9277726BC24F2243F2642267925562521256025D1239622B42416256327CE24D1236A221722CB22E320182091212122A3220625CC24C72434248C23B4236B2280253B25D523DC25192816299E2695267C26C72440230A2405230C25A526CA276330F52B4B271923D1221222BC21C623A72238236621EF2231212622CC2299244125D525FA23C824D424E4250D235E24D826902556242B239322C324FE249726E32527231022D622DA1FCC205B21AE216A23DE245A26412733249325722370223022FB242924B22217233A25C7257F24852594237D248525192451240E26C32713304C2C13264124BF229B2346239322BE222023C821B2224C217A228321AF22DD22E422ED233F23F9238F229422B8239D24C323782256219D2275232A247525B9252D25DD23ED21A820A7214422C3225823B3239825BC243A26782579233D2118229322EF21A722D622E323C921F7220A243526D723A124A22386231B23BA26FE30F02BE72414235B23332351228122F32390211A22F621282163216E207121682352238B22C0243525532418233A253B24652376233D23B1229022B522462335260626EE25CB2154210E20AE22D72191224523A9240124E324F925FF22BB226322EC2146221F22FB21352207235F244B223124E6259C257A259325D92603286C30C52B3425AC217722F62246226F232A23CF2297234E23E7204221F6209822F722F323C9243A24E023DF240023AC259A22A823B4237D206122E9201422AA22CE25E52592247523622218203C225E2273222125CE24D8212325A424E4234423E7231622AC1FC42298211E20952106216322BA239E2582269A2363265227682A6C33D02BBA25A022CA21DB222E236722D1241B23FF2158212A21F41F0C20132297231423312450266E23232592240C2552230D2419249C217321CC20E9216821AA23D2234222842378218A20602248225521D123D222DD221B223E25912358253222BD2131215D217D209422CF1FCE215E23DA241224DA249C25E6268726192A43356A2CCB25022224239B227223DA22AF24362486224822BC224221CD1F7622B92246246424D824F2226B2531242025AB239922D6238B21B41F39216D237621D521D6217E213E23FF2165202F22B521FB20B8231A24C62132223A22D22141220D22C32282212221FE1FC12191214A22FB23BF23AD24C42374232C269E2ADB2C3637812CB32543243221FC23CE243D23EC239C249C23C1249D22D721D720AB224C215B22AB24A223A222B62324238424D723232323248C22E82018225120FC205A2175204F2144219822D8226421C621A423BF23F922EC220420301F4221B821502374228222CE21EE207722C52145220124F622B0252C24CB22952441291D2CCC34 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 403F + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 55 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 0009 + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 B027FC21BF21B01F6920CF1FD41E0F1F9F1F191F561EA41DB81D211D161FAE1D881CA21E411E151C1D1EB81D911FDF1D991EC51DFC1ECD1E401EBC1DAB1F491E231DBC1D101DFC1ABD1B751D111D5B1DEC1C7B1E461DE51CEA1D101D401C6B1DD61E541E311EDB1B9A1DFB1C381CB51EFF1C741FDC1E5F1E8B1D5620F221EC2C072B7023202003229021DA1F2A20B220FC20171F1120341F3F1EC11EA61E591E8A1DC21D4B1EC01D2A1E651FA11FB820481ECB1F621E241FB91E631FD31FDF1D171E021F551ED01E2C1D641F4C1D511DA91FFA1E641D5B1D2A1E0020051F2E1E2C1E8C1E271FE51D201F6F1F8B1EB81E531EF41FBF203820E820312187232F2D1B2B6B24DF22B021A8200A207C208721D71F6622D42153210F201F1FA91FA71F9B1F0720641F2D1E671E3E20911F731F7920B7209F1FB51FFE1F821CD31E4120C21FD620721FB51F561E6A1F0D1EF41D9F1E401FFE1DDD1E541FA31CFE1E1D1F6A1F7B1F021FB51D461F9D1FA71F761E961EED1EC121EE1FD120FA20B624892E9F2B4527B123FD207D212F211722E92025200220D61FD01F45206B1FDA20811EC41FBE1F601F3621E41E831F711E2620D31FEE20BA1F4220DA1E2A1ED21CA91E3920EA1FCD206A1E291E041F0A1E661D891EAE1F4A1FEA1F2F1F211FF31F5C200F21811FB41F2B1FC01E011EAD1D9C1E6D1FAE1F7620CD214420E321A723C92D6D2C0126C5210F21ED20B72122210D2145217520742079207D1F201FA120DB1FF81E0220A01F991FA31F581F9921FE1DB41EC921241F411C571FDB1E631E7C1E0520AC20C91FD61D111FD81ECF1DB21F9B1E8020CC1EDE1E9D1F2020BC1EDE1E01202520E420BC1EB61F071F9D1F6E1FAB1F721F4320161FC32085221E23A82C162A622526221423FC1FE5217620A520B521B8204721A821591FA41FF11F4C205520C51F5D1FD4206F1FF81E5B2096208F1EEF1F2F1F081FAB1EE61EB61EA01FA31F0F204E1F6C1DE21FE11DCE1E6A1E291F6A1FD21FCA1FEC1E531F1B1F471F8A20BA20041F841FB51F2D20D71FFD1FCE1EE11FC321D22074202D23DF233E2D732A18231822BB211921F8202D22052182209921CD2003203020B31F0D210721682118202D20BA1F6A214E21C51F21200B1E801E7D1FB81FD11F8B1FE4202E20851F0F20711FEE1E6C1E741E741E251E991F2621F91E261F9A1FD91FB21E231F9820BC1EF81F0F1FA91F441F1B1F9F1FE820181F9D207E1F422114223A229E2DEB2AF1246E22F2225521461F302015211021B721B31F72203F21632242201C205C21B8202021C9212221931F6D2011227A1F681FB01F211F1520E320D41F061FB11F26201620CC1EF31E6D20C3200B1E64210120A71E2E1E0C1FDB1DAE1F661F9C1F871FA81FB21F7020CD1F5D1F1F201320EA1FAF1F5C1F10222D223B24822E6029AD2407238F2194212A203F1F51212A223B21982001200C20C51FB820D51E461FBB201621F120C420581FF81D8620E11E5A205C1E9F1D101EEB1F571FFA1E9B1F6620CC1F5F1F8F1EE11DA41D2F1EDB1FDC1E961FE81D1F21D21F2B1F691FDE1FD81EC52091206521961F891EC71FBD20811FF41FC31F0B20482288228C2E832C8B2359211521592163204921FE209D22CF2010225F21C11FC2200D21A220E0203F20242170206020BA20DF1E6220D71E5821DE20E91E0821A91E771E981FB11FD920A31F2020661E9F1F961F781EC61F9620CC1F8F1F291FE31FFC1EA81F06233620201F3C213421042134215020D120BA21EE203F216720A222D4239830D22AEC263223B321F921E4205D215321AE219C213D23CE214221D21FC6202C214421A220DF203621C51E8B206B1F1F215A1E3D1EA11EA31E8A1F211F641EE71D6920C01FBB1F2220441E311EAF1F4E1E3C1F7C1EF51E181F87203F20EA1F0A21AD20881F6A1FC32071218C1F6A1E8420A020EC1F5720221F4121BF22462351302E2BB924B9215623D920911FD92028200522F7212921C320961FB71F0A201C2141208A2053209422E41F7721CD1ED01EEF1E0B1F951F711F021EA61DDE1E441E101E241F0D1E751E551E151E271F3B1FF61F911EA01FD31F2A206A1FD61E2D20552081209621831FA2210721CF1F4B21B82010208C1FF01FDE1FA620B824FF2E4E2C97266722C0210421D22008212720B821D2201A20F8215C20AE212B213C223C2178206D1F5F21C51F1620A71EAC210B204720A61FE31E651F16201B1F901F4E1EDE1F3120871E651D2A1F3A21871F9C1E4C201F1F821F2F21F020481F2B21911FDE1F5E1EDC1E18201A20BC1FC220E61FA91F1D2252202D211322CB24AC2CE82A1C253E225C22DB222820E520A3213B2293216C204320A91FEE1F6421871F1121DF2019204520FC1F711F6E1F4C20B3208220DE1EA11EB9207B1EFE1DD31C401F4F1F2B1F4B1EDC1ED11E8D20EE1E59217821901F4A1FEB1EF51F2D20EF201921111EDC1E701E8220101EA01D551F2B204D216C210821B12126222924692F922ADC247323B3210F236921B3228420F6223120CF213722E21F2A200A202321B11F1E22C52219202C1FB91E9C1FCE20D820701F4F1ECC1F7C1F421E9F1EB61E2B20C01FDE1F721FFB1EC61E2B1F0F20E31FF920CB203A1F4C1F2720C01ECE1E6B1F11204020791F1021F61F5F1F2120402156200922921FA9203F229123572D1F2C44265322C821142203231D227F22DD2015226E2230219D1FAC21E3219722BE2043221C21662010227022F41F4D20FA1E06205F20A81F6C1F7A1F2B1FA81EA91FF31E771FEA1E031E7E21C91FFD1FE021AC1EEF1E051FA81F411F6C1FF81FE720AA1EA920E61F90215D22991F9221C520AB211C229A22AA21AA2274241D2E202ADD25FC239B2393218E219321A221EC202421FC2209224A215C2106218F216221B520F9207E20D221E11F8B20402169200A212F205E1EEE1F7920F01FCA1FBC1FFC203E21B01F6B1E9D1DAF1EED1FF31FC81F581F262014226020781F6F2039205E1FA0204F1F282017204620781EA72151210122A422E222E522A925362FE82B9E25FD2334214F222423DF2127222F227222FE212522BA21EC215120C220F52067216A220E217F2032219320C220561F482102216A2066206B1FE4204E20E421DF20CC1FFE1EC6206820A61FF71E32201122B520FB1E3E2218205D1F3A1FA721201F7420B61E1521E71FD122402249236C22BD21692221235523F5241731242A52276C22E922E9229B224C23DA21B9211221FD226F200421F8204922AE206B21642336212F231C220D20692131225D1FE02028205220F21F5E1F0E2199201A21F720DC20771EDC206020C320521EBE20F01F2F20BA1F0D1F9C1EDA1E5A1FDB1F36210122221F26216320F91FEC1F7B215A211B214021FC2360239625EA2FAD2C1B2519223222D72138229621E0215B2065207D213122D621BE1FA622A12225228F1FF82166222C20F31FD51F0A2005215420371EA01D6022B91F5D21781FC620E02179205E20C21FE91FD51EBC1F502065206E1F281F0421561FC21FCB2000229D20A42055216622971FEB1F8F22D821CA214B22D121DF2284241225352FD92BE425EC2314235E223D22CE203221A6212B224623D3214F205A20A920C42191205E2384214620562020223421AA204C21BB1F3820BD1FF31F082135217C223022862222214F21A41F281FC2211F1F7E2176216021FE205A1F2A21E51F70200021741FDD1F7A20BD22B92125227F25A922CB22B924AD21FC235A24D3243A2F8C2B20259D243D22AD21F321F021FB21D72298227D206222E821BD220F2196216C22542005232021BF216721EE215E21F41F8E209D1E2B1F152187209422A921C82226234122CC219E2142219B2040218422242289203C21FA202D21D61F42208F20332129202821DA22D521D72213220122E123AD228724D1242B2485256C2F672BEF24702269232723FD228422A221D8221B22CA229722F2216F20F6227321A021E7206B21731FB91F4C209D207120EE1F0220181FBF1F8620861F4E21FE21A520FB21E221C221FA202C24DF2270218023CA224E221921D2218A2130210122C6219E2120239721692395246323EE22C422F521D8226B234023AF2389243D30852BC8243823AD228B22A720A9218D2233212F2030214322981F422137233B2157213622F1209B21D320FF20062278229421FE1FD81E9A200921DB1F3D2144218A21A7221321A51FE4227123C223A223F7239E220D24BA2214234D22CF2171238F2000217B214521D3229F21D122EC23F42327220623042329241E2338260D31092C96247121F2216F22D42105216C213F23CE21A92355221A210B214120E020B12041219A21F1230D218D204F221C222A21E020B22053208D1F0620B11F0521012139222F2240231D23CC228824C7227E2297226A237923A4249A22BF2278228D23B5209E206A216821C521F7214825D82492233424472403264C26FC25F9307D2B0E257B228023F1216622C0228222E22294218A22892270212322D7222E222F23D420772213228C22A8239A2182221A2234222222782020222C211A228721B222C2220524F023F8227C2433243422072456235222F3218125AE232623912423226E21CD2137203C2398227A244C26082773257325E12402263C259028C032772C04268C22A721EC220423F42227233A23AA221D2408247022752332232523BE230224B321A223C6224822EA21E7232122B721F0229F205E21A52180220921EB22AF23F6252B246B26C924F6249C24A22225247823FA236025D326F3247625A1225321302252228523D4223025092702285F281829EE28DA26DD26CE27A832572C4C2705226723CB222723BD23D524682499237C25F4237427ED243F25152502248124D3235324F92221232123B2231123C9216120DD20C8206121BF21C8224224CC25AB262F251D2792250126D523F123D3232824DC25622645279924912380242322BE217C23BC233F241126C127072ACA2A192C292BEF29F3272729B4330F2DD725962438242123202293233E248325E524C223BF25A025B9277526132721261824CB2476231A232623A0225222C721B8227220CB1F1821052233218F239B25BD274229F727FA271027D623CC234A252E25AB26F426C728A92553253225D0239A233D22E12358247B25BF264E2A842B5D2D7A2C212A9E28BE283928EA31912D992673217F21BA217424842301246725D623DC266225192677254A268E26F9253124EE237C25F021EE227021DA2313216E21FA212B21B52179214122DB23A8253E28312ABB2AF3281727B924A52490267E275C290A27AB2745261D25C52409259E250B26A325BB2540267826EA270B29632AC62BCE282A27A9270A28A233652C4825B6221A22ED230A2377233225E5242D2785248B264225BB2589245A22B022C022CD232823A2211821762325230B2253229423AC21DA22512392233724F9256829642BA12A80287B244224902457268B2AD7271927C2264226102443240C25AC28A228EA25312857285C29052A352901285E27FB25F42767287429DC33002C9B27182339249B241C2213236723AC263B28122A6729BA26342636270A26FE2230232523D12305210422CA2171217E228624702424244025612320254925EB29192BFC2BBB285E26D625EC24D8234227732A4F2976285427CD258D25C524972788298929EE29E828FD262D2771273627CE274C258E258A257B29F12BD734812C1B25CB226A224223AA23BE254C259725C027962802280D285726FA2411248E23E12145241F23DF221F233F227421CF229C25992614276A26FC258124D6263C29A32B902AE22724244425782429264629582C2E2B7B2986281126FB25D724AE28ED29C32A8E292B2A3228E9254C25CB25F02488252E246C260C29032BD434902CBE263823D523EA22CB248423D82482268326E0255E26BE259225D024B92351224B247A23C923D724E62284240923CC230326C127E625F62679279425D826062A652AD8277924CE24D32294249526FE2A292DE429092AEF28042647256526A028DF2A972B722A63283324E822A722AC249624AD22102439276829CF2AE435922CD5258B22B3230E250023A424CE2350268825AE255E2511263D2497249323082408250226C92692287726D024B425F3241B263E2976271826F225C3259A26EB2610289B249922D6239F244926F726B72AE02C7C2B242B08289426BE27BC267C2A712BE629CB27BC255D2557238223B52271238A24DA241B27772A062CA635882A6E24E32369222024E623E4238723AC249125C92638277A252B2482237E2323269D26E726BD2943285B274427602590237D259027E32482247B244223A924F92329247B255921F620042691283528EE29EC2DD32C78294D28292767262A29622A692B262B6F2A9428BC242D21B82123220A232924A324F226832AEA2BB1361F2C4F252124322454247C23B42309260C27DE28302A60280F2653230F2418250E27062ACD2A252CD62A9F296C27A0264F24E6230F27A6246223272491225824F123502548247E219C2115266728BF29162DD82CEB2B1E29B327B5235B253627C52BC229752B1C2B69285A25D423B1234123E723E625BE26CD297A2BD12B28355E2C9B2627245424B62581268A28272AB72BAE2BB72B44289F24A526E3247F253429DE2AC72B4E2B032B6427BD26DA25B92398220424A5238F2343233924E326F226AA27F8263C243C24DB25B529CC2AE62CA82B912CE828B52519253C2545260C29D52AA72AA729842AE626682575240B24D322A026BD27D8293E2CCB2C7A35022D7C2668246125472605271B2883291A2EB12A9B2ABF297827C5257226C0262B2B8C2B6A2A5F2A5C2876275227C4233224C4228F21272387222524ED26E3276C29072BF52A93262825EC268028622A2E2C622A492AEE27A925CB231225FB27E928CA289B28A7282C28CE26B9244724EB22DC22DB250125AD29442B832B7F32EC2D96272126362644268827FF28732A272C2C2CA62C432B0528F82428250D272F296D2BC429C228E0286926C425E1256723D022DE22F123022516262629AF29D62BDE2B032BBE283227C0279F28E429232CB62BAB29242652268324A225D426A827722723289D27B5270625E42339247C242524CC264827E8283029EA292E332A2EC22ABC286C29442AE42A782B592C7B2E902D602BE42974283427B0263227552AA22A11272529B927A327C22670247F23CC23DD22E424B3287C277C29772BAC2B0E2B732ADC278225B32693260529042BC52ABF2871254E24A7248A26C9270926F226FB2617279C276F263625D724E32331242625482679277427A02A4E33B42FE2290828A829B328B429922BF22C432D922C722C3E2B7527662479251526A3274D28D028A7283B2906293F288D26C92426236124AA25E7270729CE28BA290529372AF92A62259F23BA246526B4261E2714272127EC25BD259A24BD24E12461277327B926AE27F32878272A273D262025A32460270927092AA22BB22B5A326B302D2B672B0C2AC12C9028312B452D652C5B2C722BD529C626D523F82564261D285828BE290D2AD62A322A38288B2628251A25582602298529302972287D27F6272B286E299A260624E722E323CA241E2632252C2666235B243824EC24AE25CC277329092A762A872B122B692AC0297627E2262328D92AC92B192ABB2C7834A12E222A9829272A7A2A0D2B99294E2BDF2B222CD32A13287224CA241024E1253D26E528B629F729092809288C272926E1239328E1282227652811265525AA27EC27DA28F027B02306229F218723E32202246B24362480234D24FB233B25B4286028AE28DA29B82A2C2BBB2A8C294D29FC277C26CA273F27E1270F2A0D2D39352D2FF42A1F2A0A2A002BD92A0B280528992B1B2B4429B8261C2351238A269A271D275E2974297E2A332C6D2ACC287326E624BE253E274628BC269825C2259125BA26B4289027EA2237228C22102212249F2467272F26B1267C2318257C26C828E22AA42B632CB32B392C4328EA26EE268F25C025D826F52651297F2A542B6B34F12FB92B5C2A502A322A91289E278D282E29602ADD2777251F258C26272851287728B2297E2B5A2DC92A052A98289C25B1268126062716270B269E223B250126B5283429532866243C220F21B5234A24B7255B284A2758276027DE24D228AA2A892B6C2B902BE929CE2BF7280B26E32529250F25FB26B6258B27562A8D2A6F338431872E992CA22AE12AA029952812277E27B9255E2579231F24D626A629E42ADF2AD82C352A1A2C172B1B28E525E825FA24E2251E2643277A264125A8267228AD29F528BD274C25B822D3229525CB241B275828422A192AD4261628EB28AA297F29D12BCF29D229252AFF261D24F1254C248523C425A724AC256426A8274332B0324D2EDC2D572DB32A262A23275F267F26B725C6238824202562281A2A9F2B012C762BFA2BC72C7C2ADE2706272C25DA2327256F26E926D426B8275B27D227FC28C72741271024E92238245B24C825ED279C2A622A97291129FB263F278828E428DA2A322A4E28BB261F25252553245E238E24B0258925B8255726AC27FB313B33512F8E2DE32C922B342A4C27D123F9232F247225B7249E252F28AD2AAD299C2CB329C22BD32A192860273E257E264526B4258D252027B928BA26802764278127F225B7262523FA22A7223124CF24D4263D29EC27F12632271E262225BF261D271325B8251F24C824322455237825EB256F250025682597274828272A59324732BE2E1D2DCC2C642C5629DA2460247925662414243E2614273A293E2B972C612AE62AFF2A942B70293126D2240A24972651298B2A412BBF29E6280E2956285027DA25E1240524DC226E23DA236F25C7258E270C27AA24FC2537274526DC25DE233F26DE2380241E24D72472250027AB28222AEF29E127E3280229422AEE334D324B2D112CBC29E729CA280F28AC25D924A32370255E27E629062AAC2ACC288929852CA929372A3B26A6253B25F42669256527852AA22AD029FB2A412AD127DC24C824D92349233E237D22D3237925962729262127F027E828852A3E29F9285427E9265A24EE231A2607274028482AF42A582A932A60294F2BF9277E29A2317A31232D342B5429BC270B27ED252D24552527250F2775279328E32964297627C925ED25C5252528B4248B24FD2652286C2619293D2940292F2A3127FC26A02541251826AB233F236223F3243C258B2539278F287628712895297C2AEF294928D8269B256025A824B129842A932AAA2A722A282A9F2842261327E32771270A319C31CB2CCF2D4B2AE729C32791250E235325F426D127CD29A22CC72ABC2AA928C3266827122516270B278A266428D9279528CB2553273A26CD26722451255824D924BC26AC269026C3275A279928BD289E281829CF275A29BF2C872B252AD128B22573257E251A2821295B2AE42B9D2AF927A9261229F22719298C276F2744335B341A2DD92C7D2A532931268C246A2548248B243F28A229722AA22AF12AE42B8D28CB287E28A4273925AD275F29E829B2272626CE242925662417251026BA256D284729C929C529D628502994297E299A2ADE2996287A2A4C2BED2BF9284927C8265B245B253B297B2C5F2AEF2A2B2BD127BF25C3288028E8292729A72869323E32AE2D992AA52A91290C27B02495242F23342591289C2BEE28612A67293A29A528732746276426842806276629FC2889257125FF271E25F326B8270E274827AE29512B4B2BE62B092A9228EA28D028EE29AF2A13295229422ACB2B4D299827CB2692254D27362A4E2CDE2AA528EF27C32679273A2BB32A222A812955288730D6319B2BB32AE8289A25D2265C242F2523264C25C9275F29822A5D272127E725CF2880270826DC270D27B328BA276B288A260227A426A72845286929272A5A29792BC12E372C1C2DF52AC92906284428B829602AC0275D2769289A29E72853267B2582260D2AF429AF2DEC2B2929E529F9284329B62D732B5529D629832BE933962ECC28D826CD26BD24E52480240626BE23FD22602682241F267E251F27AB26B927122680240E2576270D25062865272129182A64298F27702662260B284129102BD52EC32D512DCD2B3929B02AAE298929822A1D283526052719270F262925D724A925A6281F2A0B2B082BAA2B71297D26A025992AA0295F2A4229EE2A07342A2E5226CB25DD250427CC249F265F24B8243E250825E3230F25E2258927482626263525CE2324256325AE2604278E282629152AF02A4E28EB24AA2321251527002B932DB22D742C092B4E288D281027752829282627B9239B2502247E247E256A26AF28302BFE29BA2B602A822ADB2883266E28082AF129282CBF2AC22BEB33392DB4277E27DD27ED2787287827BD268F26CB253626EA254928802747290A28092612255E25872649271D28F5292E2A9429CD29FD2A5A270926E823A225D326C628E52B2F2E4D2B792AA2292327F2248C25BC24F523F423482399235B256826BA28FB2A342D9E2BCA2CA42B752C892B5527DE286127242799297C2A4F2CBB36BF2D4129352BC82BF92B9B2B9329712803277625F5256025E8270A2AA42BCB286B271C275A26B927DC271F29FB28472AAA2C3F2A5C27F7231E24DC2455247127A428B72B082B042A05288B2672256524AE24A722D12445237723D823772499289B2AD12AF62CDB2AF62CAD2B4D298A2A732800263B250A2663272D2A762C9635B12FA62B0D2DE52D1B2DEE2C512A462A29287F26342752277B29EB29F92A1A2DEA2B1D2BB02AFF29022AF92B622B782CDF2A1B296C2774266E2452270B257A25FA27B82A592A5D28DC25E6243E24AB23F224A6257E24A825C82324245A279429202C652B402B302BE52C6E2BA52A4229C127F325F925F724B028822BAF2D6636EF2EED2C512CDA2CB92BFA2A102BB629B128E725EA267E26DE29D02B292D992BC22C402C2A2C902B5D291C2B332BBA2A6C293C283826B2262A27F727AF26E125AA269027D8286A284F253424FE25CE24AB26B72696278B27C52793250F260B28802B702C132C30298029342888281629612740265E27BD283A2B302BD02D88357F2E262CB82D792A472B582B082AF326CC25B325752620275028902B022CD22D122C552C222B7D2CCA28062AAA28E4274426A42717262B273529EB2999274E25FF23E7247F2857254625E7247226DD25ED289D29D9287A27F7293A27B826FD28F72ADB2A43286027D126FF26952885291E298E25F9292A28D829FA2DA92D4335D72FF8284929D128D029162CC8289C267E26162544271A28A128052A732C232ADE2CCA2CCA2B782A362CD42A44270C27B5269B25652855289428B8275F279225AC259326D326BD26492586250828F8277A2ADE2C6B2B662A102B442B5C297C2A9429252AF326792551276A261B28C32CB62A5E28DC286B28F528BF2C5C2BA336252E1A2982252F26D827AE26252790250E26F426F226CB276A272428F828A42A0F2AE92A052B4B2C392B612B4529A8250D25DD27A6274F28A228D9266525AC25A6264F2747270E264E2679274727092A912D3B2CB42CA62A732C702A632A0C2A462ABC26E8258F27A828D0270B28B22AAC2AE32A1D28EE27FF27ED2A372D7536E02CDB286026B62736268A2719285526EC25F424E02A6529F52B67292629CB277729A8297A2B082B8429A128B124AE26C2269B27A228A0262D27592631249B25DD27552954281F276E29B82893276928562AAB2BFC2AC029032A3D2A752AA9280729D9271827AE263D2889293729932A0528E326352781284629052A952CAA35552C83290928AC27C42ABD2AB0279C274F25A527E529812ABC2CEB2A0D2BCB29532A9B2B352BD12BA9289E25412682261026E12646265626C225F624792572273929342A492B9B2934284129A2294B28EB27A22A4E270927352AEA2A032A062936262C268527C426332B942AC72AF0290F281827F0276928AA28FC2AD22BD234E42E972B942B022C3A2C262C3E290E2944277B28642B062E422B842B652A3E2A692B642B302A8F2B1729622568271A268E272D291729B5244F24EA258626D1275C2BAE2BBA29DC2819297C2A1E2B562BEB2A622A102A9729192959283F29F926EE267C2807289828732953294C2BD728AF27BE287C2B3D2AF52896284D29CE309F302D2CA42B392A852BD22C7A2A992863286C298F2B002BD32B7E2B7A2ABC2A042A382BD229422ACF2830277227FD294D2982292728802544254825CE26AF271A2CDF2B05298928B6285C28522A7729EA2A5A2C322AC32B312AF7277829FA299929102A4C289A29DA2801293827F426F427B729492ADE2A982AB3272826A92FC12F502BC22928290D2B092C982983264629A72A0A2DEC2C592BBC2ACA2B9F287529462C622939279E26D6266F296C2B992BD72A432A142611259525FD26012AA92C9F2C7A2B40272528182A772A3C28CB29642AE0291C284829CD28B528822BF82ADD291A2A7928B9272D259525402805284E2A172A5029F2278F260427162F082FE329EE29C62AA22A7F2A81281928442A992CA12B872D64299028352A1F2A052C422ACB28CE2680266B28D129702B1E2B832AC6296A267A267425B626B627D32A442A462A6127B427122801284226092851277B27C92525282D271C29F32A052A0B28A3262825EA25BC240327C9279F285A29EF275727FF25DC25A42612311931BC293829812ACA2BE22A7B2730287B2BE82CF52BD82CBE299629222A1B2C072B852A2A29B92617283B282F2A952BAE2ACF29A6281C27BC269E275226C327BE2A8429932AAC265226E926E4252D2514258A2658247E258F2541299B28232815288624AE255A250024D52464267B298C2B432A0E29D627762876262628EF32982E33289B26E727BA293529BD2827297F2AC02BF82C8D2BC32ADC288829D22B3729582740282029F428112813279328E6276828B126282697278F27A628CB2A542A3B2ACA291827C426C8244E248D24692664253F27BA26FF27D3261728C526EA25B3243624C6241B259B26FC28562C552B232B2B2B0D2A632B362A932AD934A52E7D29E2268A29DC29522874290F2AAE2B382C702B7D2B8D2A162C8F2BF72AD8296F296829A32AA629382D8A2A122BEE284B291626B525B9271E293D2834282928EF27112A9928C22568262F269E26AC27F2281F2A4B2A3B2AE3287327DB27D727B126322727279B288928D02A8E2C572D202D202D3B2B9D2C372C3D2C1436D1306E2A542A182CFB2A8B28F5271628562B242CD22C802B132CCA2D8C2B672CA02AD2282E29072C302BEA2A5A2D0A2EC32A27289124B7245E2633255F27602715282D29322C702ADF28D82714270828BA292C2BB12A67296D2B8629E526DA274428952A052A6328192BD22ADA2DC02CF52B6E2ED62E0C2DF62C3C2C302C61340F30EE296A293329942AAB27C6240A27862A042CA22C7A2B182CA92C1A2D332B3829E328D5291A2D072B7A2BD02B942C43282827FA24D923EF261B26072706275E272829BF2A8C2B9229CC2837296A290D2AC32CE9297029EA29FE28B92849280B2A6C2A782B82293D2C702C0B2C202D072BF32CA42E0C2C692B1A2B662BF23206300B2BA027CD266F2774261D260B29542B9D2C8B2C472C6F2B912BCD291E2A79272E283729622AD12B172D952C402A262956264025CB254E271D2997272C28EA29F328512BF02AFC29B729A42A772A7E2C992CFA29AB2A5B2985271D2A592CBB2C522B232BF12C232D622C2F2BE12C7D2B1F2C822CD62DF22AF129972A6732A9307C292D27C4252D2633278D27B0275F2BB32C032EE62CC22B2A2C972ACD291A2955281D2AEC2AE32B162C422B402AE12713273D27E1264629D4293C2C3F2BE82A1A2AF429BA29CB29092B812AE5282B2B0A2B0E2A522943298A2AD82AFD2B762A952BD32D7F2B7B2D762CC42859295F287E2A292C382A452B332C202B6833B72E1029E825BA25C5265328F329F629AF29922CAE2B9A2CE42B122BEB2803285A280228C928E329762B072D492B8629F6270C287627D9268729332A572AF427752718296C2A442AA6291C2B70270929672A492AC426FA26292AD72BBA2B452BD32ABC2BFE292F2CB82B4C2B762810277626CC28D2292A2BF62C9E2C1A2C1734A82F4D28F92605260828D52AB32A062DC82CB62B672C252C1E2C092AD127842A4328B429232B1B2B4F2BCF29322A0D28D824BE267927A7295D2BDE296B2A1829B9285F29512A252B792A3C28E5265D27162887289226D728792B452E3F2EF92B6C2A552AF829612BF92C9A2A1D2AF3297928D4273C287B29822BE22CC62D8036F52E8228C124CA253827242888296D2B1A2B962C5F2C382B3E2872278526A72B122B332A61298F2941297F28C726C1271424ED279D29C02ACB2B5A2B752892281F29B42ACD2BBF2C442BD129AF281F26CD25E2263627EB291B2D582CDD2C2F2CB52A322B9729A52AA52BA7292B2B6D2957287D29712AB528D82AC92BC62EF8350830C929C525F7240E26BA272C280A2A342CD02C1F2C7D2BA02934270729082A4B292A29AC29B52A452B082A0129BB26EB27D529FB2A352C772D3D2B7B293F2BAC2A04291329322A1F2BC029A228FC243326E325EC270E297A2CB62CFB2A862C572CB12BC02ACB2A162ADF29E929122BC32A5F2A402C602CB52D1E2E402FC8370D30692A13280F2548259A27B2278329002B242C962BFE296C2870274A2A0D2BF42A1D2AE02A552B0B2C07298026712614262B294E2B7A2AE82C612B7B2CDE2998295528B3291B2A6F2A81292129EA258425D426C8274527EC2D9A2B782B222B862BA72DEC2B2E294528E1264A286F293F2AF02B362CD02C7F2C692D952E1436E230912B7527FB25512627271F29E629E12BA12CBC2AD72855273B28392AED2B81291A2AEF29842CA42C5228A02642263628C7298E2B4A29AD2A4E2B752C282BBC28EA27D828ED29492AD228B429C5260526F6273D276A2ACC2B362C5C2B472B562B502C4F2A2B287E26F026EC28062BA32B322CA62AF629502C002DDB2DC235F032E12AE6290F282D255F256D261A2AE02A512CA12A812807279429112C3B2B822ACE285B2AF12B0E2B3B279B26D926F329712A952A972702291C287B28AD281028CF276026B428DB281A296B2859297C2B7229C02A362AC72CB02B1D2AFF2A9B29162A64271626C3267D261E2A322CDA2CC82B9929F427342B7E2B642D51372531792CF428172668241F24B5248427A2290E2A2F2A7D26BD29312B202BA22C992A9A29E7285C2A81274B25DC26D727E9293B2BB32AB12A2929F92743299829BE287A287D275926A9275227D7283729FF29F22A602B9A293F2D9C2A7D2CCD2B94291B292D26DB245227B7288F2AC72C0F2C512BDE2C3229BF29FC2A002D6B341B32CC2B5229E8267326A4264C2565296A2AA02B912B762AA22AE32BDC2B8E2D422C5A291A29DE2766265226EF26F728292A622C5A2B872CD32B6A2B432B122B892B5F2B0A2BEE27A3257629CC2C592A6A2B542D7B2C202BBF2A5F297429E9298E2BE029BE2808291B2A132B372C722E902C6C2BC52B072B802B842D752DF234CF2E962A0E27F92644271928762642271E28792A342DCC2BD72BEB292E2CB42B332C772976282B262425F0241828062A782AB82BF928C829502A5D2A1D2B942ABB29632B1F2BE52A672A3D2B6E2B302B582C382C9029A32923294C29552A762A7B2C6329542A892AE62A4C2CF12A242B6D2CAB2B402B4D2A562C542C052B3C357930B2292D283628E12A0B2A9E28342655282D29FF2A6D2C6E2C1E2CC32B7B2CDB29252AAE26FF2515250C2408292B2964293C2AF629822ABB2AA32B3E290B280B292D2BA12BA82A2D2A0C2CCF2DED29502C312BDC2A2B2BD32AED28832A212ADA2B112AF42AAB2A712DD02CEA2A872A002BB72AB32A3A2A5B2AA22A3A2BA5337130ED2A2629F629D22BBF2BEB2995284F2805299129462A692BA72CBD2BD82B012A9C27B4253025D7248924D5273629572A472C55290C2A232CFF2BAC29C7280F29CC291C2A3B287D2724286029F029492AC32A902B4B280029982A462AE12BD72A6B29742A4828C22ACD2C5B2B112C8D2A7C2BA62AFE27AA28E528C5288233CD30E62B4329322A6C2C752B3328EF27F228C9279D29342AA029452A902C812A5C27DA27E2269825BF2524269328492B822A5C2C732CD92B742EF02B3E2BD52A072A5F299F28F6232A257125CF2508258727882784274427172950281E2B632BC1283827E8276329512C0B29182A542C1A2D0A2C4E2B562A7C291628902ADB357831852A372AD52A1F2D6F2CC22A7E29EF2739290C28F0277F252D2733296C28CF27682814271928D2258B24E025AF292D2B3F2B5C2BA92C102CF72CFF2A5D28AF283E27462421227F2460249E246B2549253F285A26C0250F27EC27B52AFE291A280426A5252E28452B282BA82CBC2D272E922D8B2C562A00295728E729C534B22F362AD32B5B2B782CE82A582A382AEA286A29422A4928E324DF248125B3273D287929392AD62A6E28F5256D27042B212BF92B592AE929542A19281628AB273326E524BB25B2258E26DE25D525EC26B3243629E3274426E228A82830277C2573258F27B82AD029132C562C6E2DF92CB52CA62C902B3C2A9D2A1A2B4E2D5536D62FEB298829922C262C022C532BC529E529A729892890276B250325BD2631290F2A3B2A6A2CD82A78298C264E273F28AE29EE29AC290228D527B526F8264228C326B22736280E257B288D2799288B274B2628287E28E429072B992AFD29C8269A26EF292A2C7A2C642B2F2B222B2B2C0C2B042CCE2B2229FB287A29AB2A0733DE2F02295528EA29E32B3D2BAB2CFB284C2811294028BB268724B3246A282F2A7A2ACD2BCB2AEC2BF0284A2734282E285427432884272C2931286B26C127AD283E290329292993274B2971295D2BF9297428CC2767279028262B1C29F428352731287E29042CB92AEA2C232A7B29AA2B072B1D2A0B2A69299427DE28F32A0932EE2F0C2C602B6F2CCE2CC82AB42C7E28AE28E62739242725C42669262727462A342B522CB02BB62BEC2ADB28A827CD2899282129D029392A9F29DF2A2D2A532AC32A942B092B2128D929C42A462A8528B8297729F327C927BA280F280A298227CB28EC29F12A702D482C362A0C2AE4292F2AF52AAA2A0D2911295529452A5E34AF30402D272C472B182C0A2A9E29E5277F257925922560269A271128CB28BE294B2A612A4B2D032B022CC92A2F2BEE29A9282D2B9F2879294F2BF12CF12AD02BCF2B8B2DD52B1827D4276927EC2A85270B275828E1272227AF295C2A7D28B0278127E92ACB2D3A2B3B2DB22B4F2C092C2B2D50298F2AC32B822CDB2D8F2CC93550319E2DD62A722B0B2B03290D2774277026AF243D269A2818286B296A2B632A2B289929A02ABD2AA9290E2A002A7429BA2A222D6E2BF92AE02C612D982CE72B312AD32AAA2A28284927D129892B072962288C283E28502AF2299927C2279327372BC82A162C2D2B7F2AAF29582BE92B4129442B122B9C2A492C362DF62DC0340831622D362ABB2AE42A2E2B72285A28DE260B25AB24A927D0299C2BEC2B862CF129B5291B29252AFF28E7280B2C0C2B1D2CEC2BE62B552CE52BFE2C382C7C2BEF28ED286929392AAA2A5D2B6D2C962C2A2AD728D7280C2A422AE4281F289D28D42B502CD82BF02C3F2B472A602AF72A712B8E2AE529FB29402B232CF12CE83305358D2CF8281429442BC82A752B182997263028F625B627D4287429922CE22BE72A1A2C80272227B62679278F28F829172AA72B8F2B7C29502BF72BAB2B7D2B8728F0254D26C328E729BD2BD62B082BA62A212AF8287428C52AD529712A662BB62BAA2A512B6E2A772C9C2ADF2A612C8D2A802AD728FC26522A922BCD2BA1350934BB2E92298629E7289D2B772B272B992908280B28B8276329BA2C4F2C0C2DFE2B312C83297527F2243A270929AB287E292B2C0C2B672AD02CC32AD42CB428E32744250526E9293E2912299B29A52841283A27392617275229AF2ADA2AC72B4B2CEE2A142A142BCC2BCB2A542B762C602A8F2A362898272D29C82AC42DE1355232012CA82936260C27D429D12A282B6928C9265026E4274B29D42AB52C612D6B2B5E2CD9292728F0261F27AC27A328742B132A7029D12887288229642A57290E28532657266F26DC268829FD275B256126912770253F276728CA29B12C532CA029592869281A2A4F295E284127452A9F2A3928B0265E267E25EE28DA2B01345631AD2C442991281129092AD12A0C2DA92AD528D225D127F029F0291A2BEA2ABA2A672A642ACA282126A32727287B2ABD291D2A4127B1255227C727A027022B852AF326B22637261827D72803299526752897295B271B25AF27AD28882A4A2BE52B042A04290F29A128C9275E27F128872A1D29702745265A27C9279528ED329231312CB729052AAB27922AB02B532BC52A672ADE29BA2AC92B9E285128C127F028982A7E2ADD284828EA27672BEC2B8E2B4F2B81272227642699268426BF279F2A6E2B2528FE266E28042AEA2898271D2B2A2A912A3728E129732D022DAA2C2E2EB22C722DFA2A362B84294329042C5C2B742AAB28F826F529B92B542BBD340431442DD82A96271B272F27442B592A4528C127B628512AC829F82A372AF1298229AE29132A71298928FE29E72BA12CD62C2C2C2A2AED298928C225AD26ED274C2B7F2C9D290B27A627752AEC2A8A2ACB2CAD2CA62A1729E7292E2C162D7B2B2A2E842D8D2B472BCC2B952A9C27302A1C2B0B2CC82AF7294C2C5D2CC02D6136512FD42ADD290A29BE27EB287C29C727DD261E289D28742B842CBD2BA52A382A75290D2C912AFF274D26D127682ED22C59295029D52A012A5E2A9229412899292C2B6A2B002A5B26CD25BF27312AB92A852C922A052A642AA3297229E72B792C532D4D2D972D042AB92AD6286E28672BE92B9A2D182D0B2C292C212DB42DF13417305E2AD3280B280228A628B929DC276D28AE284529232CBF2B232DCC2CF02BC12B332BC12BCE28BA28B328082A592A102A6E2ACB29F32C8E2B9A2A012A102BF22A592CC52AE3256D2686272D29662B602C772B1C29422858276F282B29682DFD2D6A2D452D792C702AB028EB27102B212DBB2C812C2B2C092CD62B2B2CBB35FA2FD1297D27A227EC2629273B283F28AE265F26B828422A5B2A732CB92C082BA82B692BDF2D652ADE27DA27BE299C2A13287F267D28E028C9261F27AE276F28022A702BF12831268B248C272C288C2A412C2F2B4A2A68295C28D928602C042DC82C4B2C482D6E2B8E2BAA283B2812291C2B1E2DDA2C862BD529F62B5E2BE234B92FAA2A6D27EA23452694272327F626FA26EF27FF28F82A192A8129712BCB2CC22B2B2D3E2D7A2AAC28E62AD52A272CC9290A275A2518267F25A525D2256B271E2AF42909291E26DA22F123DB262029492C552C882B272A7529E9278B2A852C622CBB2C0D2B1D2A672A3B2A7B2702289E29892DEA2B662A672C382B1B2D6C34C730762B6128642658253A257426B1245D25A1260328A029CF2AB12A982BF52BB32A7A2A032C742A142977294D2A6A2AE4287D27DC255E25DE268E25F825802503277B27EB2633254024C824F925CE251F28292BB42BDB29B42997276028272A322DCB2B4C2C43295F2ACF28C326D127FD28B02CBC2D6F2C3C2CD02C732DFA330832122DDF2B9828D92754252824752504245226EF27272A0D2BF2292D293F297E29CD2AA02B642A32278426132A412AF927FB257B2578251B28B62771273328CE266F2685264225FC257925D824432457263729942809281627B826AF270028042A5D2BDF2BE22AAF2AAA28FC28C329C62A752B552C962A122C412DC12BD034DA30C42BB32CFC291328A9252B24752357240D25F72523289F28012A7D29C6276927C4284E277627C7245D25DF264426BD27FD25B024BE25F627EE299429F928B027F025D625F825A8256F25FB243326C0266B281627B426C8285E287826BF26D4265729782BF72A402BA0299428C729142CE82AAE2A872BC32BD02CAF2CC83532309A2C0A2D3A2B9E2AAD270A253C23F52424247125DB27BF289D28A628FA281528ED2754267A275425DA24C32788279328492917276526EE28BE289328422735288027D924A623E123992587256C280B2B4E2938288F26032A452BCF28BB27CA25DD26FC288529C62B352B1E281C2A272DAB2A202CCB2A9E2CB62DE22D78352A2E122B032AB92BCC2AD327BB268625A32402247D256C25B026E5265627B529EB29402AA829392817266026D426EF282D298E29152A3E2A2F2B3A2BDD2AD02A342B4D2AD4275C24AF239F25BA267B27002C3B2DAC2963277729002A6F29A9295A29F6271228E8276D29A3287428502A622C612C6F2CC42BA62C7F2CE92D9335152FE02AB32B2529F0290C28EF282728FF244424A3241A256B2500263D29972A262B282A732C7C28ED276026BE274228CE276028602A552B782A2529B6281C2A312B9629EF261A26CC24DB233227A127022D6A2B8C29A4277427CB289329E329C9289928542A6D286D288C26F726A326252AEC2CD82E792D532C712B702DC0359630812BD32A162C042A962941288D290127E5257825D026BA2521274B2B132D7E2B262B02287D290429DC26B729BE29DA284C2854285229282A2B29F62788299C28C626CC25FB23F3231B266D250D26AA29BB2942295829BC286D27AB27F92AE02A692BA92A8829D3284E265426432810266928B22ADB29F82A722BAA2D03367331702C572D892A5E2B292AFC299F2A29283D244924B4241427F127942A402D682A0B2A37288828CE28D328A929E4271B2575270228C12872280D28A326A4260E27B227402412243B269724BB2729268F281228A127FC26772783272828432A722B4F2ADD2BC72A782CFC28C828252745277326A628D527282A512C122DC9364330542D4A2B0F2BDD27F6297A2AA6287727EE2593259A239B24D027B8287829CF2A5F29A528CA272F27B8294E2BA7291D283227CE260E28D4280226832600260D24B923A122DF22B2251C273C27262787287228172735262A27DC28E429FB2C5D2D6E293C281C29C02C462CEA2BDC2AB7290228FE26A4269E282F2B452E57374E31EE2B402A3B277B26C8275629C32A3E28182494249D2311241F2305265628702750298528D1270328CF2AA82A7F2C83293527F9243527A82837282E28DE26E024AB230522952362249428F0290D2BF028D928C32647251D26D625F8285E2B682B29294628C428652BEC2A5B2BE82B372BA428E426EE26CE28AC2AD32B1F36A12F882ADB27092654264127492835279127472497232A23EB222C239325AC285A292D29AE272B274A27F428CC2B712A51288C253124512407277F28BE27FF259F240021EE20392002252525BA28C729A22A512B7127E324A0248D26ED267B2A992BA8296A28AD271E27D526DC29E32B9D2B842A1929ED274928C1284A2B1C36422FAE29BF28F424C824E326402778276B27F82538259A255E237722002587266027922820294127B12720270E29E42AEB292A281F279224AD269128ED26EA25622360210D239C223E246D240A26542794298D2A7F29062A95294B2653260D29AD2BC62CE5296B2ADB2AE5285027F928F0293A2B4C2B2C2A472B052BF22B9234D32E4327FB254C25AE25DE27B128C4277C28BA2753251E2547243023A323FC23F924A8260F292F2891270B272F27362904290128DA24D525CE2569271228FC246123062280241026F02589254E25DB269C28EC28E629862C7629B227322686253F29C72A102CA92B962B202A83286229DC29BD2AE12B9A2BAF2BF52C402B4934D32B4F27EF25B624B025CF281D28F629122B332A4E290A26BB26C1258D267824BA24C725E12738285829DD273B28A12A7E28CA29342940284125D826EA27F524772340247A2582265D2720279F26F4255E28DE267D2840297B2A2E28A32633281B2779283829842A9D2CBB2BDE297F29EA297B29382A4C26B128BB2ABD2B6F34B62C2E28F0231B2647267F287A2A422B052E652AAF28CE274D2805289827B7274324FA24A026222A6E2A592ADD29002B2D2ABE2AF129072A7C299D2831267525A024EE26A2279528A628852810298B287A288F283E2765278429EE274E2858272F29E2255326A227172A112A672BB628DC2A5D295C29212AFB29FF2B3B2CA933B02C3E282C2537254C268B28F429582C902D2C2C012A452800299C260528F726CB26F426EE244329D929A82AA92B512902297628A929C2288D2B3B294A2AC2273127B1279A29C629D52BF929FC29BA2A202A1B293528F0255F27E92464272628042A7928A4271F279A29CB282B2B492B7F2A142AF72AE72A4B2CAC2CF42C1536A32ED527EC23C9240A276A2A702A732CE02C3F2B252A0328E0262E29092A44298128962738255F27BD269C280A2AAE2A1628A3284B28E927FC286D29BA27102828289126E127B929A628C029272AB727922A3A2B2F292F252A2632251B279626A028DC28BA282A2692278A27A0270E28342AA229DE2BD829D62AED297A2B8E34F82CCF26A0247724EF254529B82CD02B842C0B2AEC279D27A3284F28402AE128FD2743275826D124AC240F28A1296629C5269227BD2758272128D82891275D26F4274628242982280F290929D427D2262827612875281F2782265E259825BD241726892862299527C12640251F2525284528D028F729CB299D2BD12B6C2C29360D2C55256C247524EB251F2A622A222CB02C63297D282A27C928A329582A402AB328B0274D28A92663235924B0266727D4259F266F25F32759275F240D2646250E262928BF292427B627302747250D27AA27D728C0270329A229A9285125F0226124B3250D27C42605282226A526AB273027A227FE284F28FE290E2A222B2C337C2DC52656236825FF26FE286C2BE42C7C2C2028BC26E0289A28692B472CB82B372BF8298D29A128D42500256927AE28F726FE27EA268C258725C6257E2551254426BE2637266326662632264B271728C529082AD128D427DF28302A9327D62580241E2474245026BE27C1283428A12A48299229852880279B27B027BB290B333E2D27288D26AB23A125322818298429F72936290C279E276A27A82AAC2BDF2CA7294129122A9B28F827D625C9268826FC27412AAC28C227252670234D23DF221425F024E22567255F250B260428AC28082ABB29C227AF29082B5C2AB7296727922687247E25F2262228FD276028B3278728172A0E28AB275C271027D2276133E02FE02940260A24DA2471262F28A627B227DF26ED2425255B258B27412A5727E326B926D626A628D92913293527DF24C326B929002C3E29BE2877241A24EF240126F6261B284F251627F52716282028B329342B112AFB29192A7B2A3D2AEE29E1273B26B0258D267A2956295E2A152BA827C327ED28D527EF27072BCF2B7736022EAF281F25CD23A023DA255725F2276524DC25AA250C25C4244127F627BC257D242F26E024CE27CF276028E6278926CC242227282978298F2A0628BC255A243D253126A327B826D4271029FE285D28E0266828D927EC272428A529032B7A2A9C293827F525FB25CF291A29732AD428F825CC262227E427F02AC22BF62C9D354F2E0C275725E425FE23FE241025EE257926F525652848268F241626D5269D261527932469263028EB275F27F6282F29C2250A265926A8271729A52798267023E9234825232894261C27FE2745294828FF274229862632279D2ADB2BEB2C3D2C55291E27DA25EA256F28932A9D2A782BA2294E259E24512535262A28A4299A32C22EDC2968260D25EA25FB23A625B725A327112938289926AC266925B224172751276227E7261F281926CB25302813291B286E26F626F3271527FD2893270824792533257C24D0239C2726267B27E726F729462A96273527712A2F2AC72DFF296E2C112B1C290526462725288B2A742DC129F1254424B5239A25DF253528933222306029B42619251926BE230726FE25F527AC28C22958277425942319247624322577267D25A926492682263E28642A172B1929A6270224C8252326DE27822543245B230724202445222F2600252725C72746292227B42743284229EC2872290A296428F828A325E726ED2651285329292AF227BC255424F523AB241326BA31D92E57297028C724C3238F23D6255F2602291429F9295429F625E025B5250723B82537265027CB27862735271F296D29C229402A8E27C22409276926E52534250E268E269523292253211023E921AF23F8257227EB254126BC27AD27ED282A2732294829D32789273F265925E6259A29A22978285E2690221325AB257727C12FCE2DEC2815279C25D9239B246D268D273528CE27D529F02A2B272C2463244E2531252E2524263827CD277827802774280C2990273425DE256B247F26DB24FE257426D426A52603236A212A22092234227223D4236725E025D925EB268926782AD72AD7281928EB2792275A247924202668278A27DA2782252726F32600278D2FF12CE92848251D24BB224F2273257526B4274F268E289426FA2512246D2332236D24C7244C26E926082852258826992905296B2758258B24AD25A2252027392748263227C0284A24CB22D021AF21A0224C23602439261B25EE2673258E25BA263D2BC02AAA28B3262C271226A623B6240C24E7265C29C428722768270228D72FD82C6827F1250B247D24D6230A24F925F2273A26C4271427AC270525E123B62245229D248B25132783254625DD25E6267A263B2561243626242717275E27672654264A270D287C26DD25BC23FD22D0224F227F232323DC25BF27DE2642252A278229842A082B182A2D29362559233F23FB244823442631279E271D2608283631F62BD1257824BB240A24B922FB232C27922675274427C927A5288F26BF246E24D823A2236B273329222912274F28372747261227D92799271428CC279E261227F125BC27B726C52754256425BE22A022A8224F230A2377254529A628D728BB2860299E2A432B932A1229332740262B2218228523FF241327E928BF2AE72A7D31A52B722556223D235C236922D723492503279328F8274926D727E8273C2778256C241E2535269D2766290B28E9296627D727372889255728052792277E277527B6255F258A27BF28B926382793249522AC24DC2313219925192703295429CF2ADC298F28712BC62A1228A126A523E322412227235D243423CC260928262BE832C82B2326F2223C2242236D232923D026CE268826FF255826BB25B92685284528F1241625E327EB253E286028B6296C291B2A1F2AC8273B279D26A727842612277F25D723B527882889285129342760231D24D322E422A7222427BE26BB293A28A127B028C029F0299E2BEA26482581248224292345240F25DC258C2499272D33332CDD25E22154230D234523F422132651271F27D926D226BC258925CD2899289B27522646265C24D0265D2666288428C428B3298B275724A825F4273F26B725F3235F233C273D282228CD29992838250A254824C821E32224235F23EB233D24ED256C25832672269428A52730261B254C249825CD246624CC24692777284F34782CA125042448211D2483245223C42458272C281B297027E125D925322806275926CA277A261E25B425D124C826A827F128C42A3129422632263024DA249E2413235D232024A12719290728FD2739282E26C12356234B20E91F3B22E6225324BA233B24E42374239D25292532257F25DA23CF27DD2700268C25D5271829AD32 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 403F + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 55 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 0009 + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 80287422CB22862028217020691FE31F3120B41F0F1FC71E6E1EE41DEC1F721E4C1D7A1F081F151DC21E621E6520951E691F871EA71F811FD71E871EA2202D1FB01D871E7B1DD31B561C541EA81D301E7D1D351F061EC41DAD1ED71D231D4C1E911F381F0A1FE31C781E121EF61C891FAA1D2520911F371F7A1E6121F422D82DC22B09246520C1227522A820D5205821A421941FB120D81FF11E991F721F541F441ED71EC51E991E9F1EE71F5F206121321FBD202A1F69203D1F27207120991EC01E0420F51E771FBF1DF91FF41DE91D27204E1F421E211EBB1E9C20EF1FDD1E731F751FF41F871ED61F2720341F801FFE1EA5203D21F4207421B3214724BA2D1D2BD024512345222521BE20102115222E20BF2239220C227C20C61F2B2026201820EE20D81F981EE51EA4201020D81FED2047212F2004204B20C61C221F8520FD1FC620DD1F2820A41E6E1F751E4B1EDF1EBB1F4A1E0F1FEF1F4F1D691FC01FD01FED1F611F431E0C200C200F20BD1EDF1E961F2D2238201B215521D4248D2EE02B16283124772141227921AD2258219520552076200920E820EE1F9521C41E1720FF1F0720D1214A1F901FC41E96208220172121202121561F6E1E531D491FAF209E201D21CC1E881E4A1F331EE71DA61EC81F971F2620A11FA21F1E20CA205D21FC1F69209E1FE71E751E481EE21ED31FF41FFB2039227720E921E2232A2E9D2CF225E2212E214421E421DE20A421BD21C820D820DF200420351F06214820F21E8520A71FC21FC81F971FE821701E341F1A22691F7D1C431F2E1FBF1EA81E5420EE203C20181EC61E0E1FFD1DD01FB71ED520DB1E331F921F22201F1FE41E9E20592030212B1F2D20EB1EAB1F5C1FEE1FA21F7E20881F8D202F22F822B52C832ABD2579227B2381209222C420B22032226021A621422203200920852099207720F51FA71F3A21661F511FBB202C21FB1E46208A1F551F441F5B1F151FD41F2620C220B91F0A1E4420351E741FF61E8B1F701F2420F01F501F0B20AF1FB11FC92021218F1F0520E61F5E20BD1F1D201E1F482002221C21C62031233624682DCF2A7F23862251225C21782140223E21E220462265214D20992030207B214B21AD215A206F20F31FAB215121D31F5120561E7B1ED91FE01F11208B1F4B219F20CF1F8220C31F261FDB1EB01ECF1E7F1E0B205C214C1F391FCE1F5520191FE41FF5202B1F5320901FCF1FD81F691F981F03217E1FC120861F5F214A225622CF2DAA2BB12547230224032257201821D621C7218A22B020392151224D231021EE201922DD21DA211922DC2147204F210E232D20B21F5920E81FEB204A21922013208A20FE200D21901FE21F7A219F21C81EEF21E520831FE91ECA1FA21E93205620CA2070208320912020215F2011209D20A8203F20B0201C207822B8221425FD2E252AAC25E7238C22B5220121971FDF2100231B222B21B120F02083202C217C1FEB1F2121592181213A21DC1F5D1E5621891FCE200B1F651EA11E47209F1F671F2B2027214C20E91F591FF11E741E011F7C20681F6220AB1ED5218C20E81F35206B20251F7A210121FA21EB1FCA1E3D207821212089203020C020CD221023BC2EAD2CE8231322C0210122D820A5217821E9221D21AF22F62174207B217A211121A521CF1F2121F12000210D212B1F7D20261F98211021361F0821251F141F1D20EE1FAD2124204C20F71E65204020EC1E3020DB2008209A1F7B1FA220531F38204E2369207D1F24211B21142168217E201E21B3214A215D21A820512336249330E92A0427C523B42235239F21A8219C2115221422CF2376225122822048217F217921C22016215121101FA120A81F2B21B01EA71E141F411F35206F1FB81E9A1ECE208620A920EC20271FD61E5C20A91ED51FBA1E531F681FE52067205C207C2131211B20D81FCE203B21AA1FC61E1821E8206420B120B11FED214E23E32355302D2CE0251723A2243122CC207B216A212923A9231A23A02262215621952126222621FD20D0205823D6206F22E31F61203920362092200620241FE11E4A20BB1F381FC320062074208F20971F9C2080202421901FB420F120BF205520BA1F7E21AB219921B322AB20C522D621C9209B22CB2155214021D72102226222DF250030052D6F2784237522DC213A219E21D620AA2264229D21B923C52122235B22222314221B21D11F1822F720EC20F31FE0226B2123218120A11FDF1F14213F201921DE1F822145228620571FD420BC22B020C81F3A21CC1F37202922F0211120EE21C220CC20FA1EE01FAC20D1205D20952144216421F1232A22FF2287232426CF2DB72B4B26392369239F23E3209B21762289239F23692247229B214E21D122C5202B22DC21E2204E21E220972034212C223E2226225E20F41F2C221620C81FF81E382186218B213320D020F9203B22562021226E22AF20782005204B214E210422C822601F5620CC1F6421901FBA1ED02015226C23872325231D244124B6257030682BCC256424F122FF233C229723CD218B248122012472249E21DD21B2217722EF201023BA2368218C20BC20FF211023FD22B02155209D21F6202620D52040211E231A22FE212F21CB204F2051205F210E2133223F2279201B21CC217520132123210A22F421E7206D229921FD200D226923BD222025C822CB2307257525B52EE42C6F27BF23CC223023CA23102378235E222C24E82454234C21BB227123E523ED210D23DE21CC21B1238F247122CB225421C322B8228A215021A321B0219D2133229521A421EE20D41F2E2381217D2131230D209020E020F4218D21AE21E021EE22CE20762267213023822463222F24E623F5240E263F2748262D26FB26402FC62BCB27F72598250023E522012357230F23EB230226AA24A12369231F233A23E722EA217A22DF21B8239822D723B4247C231C24CC2279200F220B23D722572322233A24BE230B22B420A31F68206421BB217F217C21F5224F253E232B223423DD2279218A2254215B2215236C24AB23A6266B26C8270C29A628B2271A28DF30D92DC52729267023B124E9245A23CE239D24F7253726F225BA243D24AF22F7223A22C822C9233D225B225F24B224EE24D6224E24C0236F22D92246226624C9232B25E4236D22A821B7230A2396216D20A5210224402317228825FF22C2216221DA23B42183223121AB23A6226E27A728072A7D297228D028B728E226AD26E331B42C4E2A862637267725CD240725CC23C72407267628C3253725DB2464258023A5233425F42268250C25152499269F271A24CE2442230A23B422E6228D256F259225C924BD244622A32490237E234620B422422236237A232823C8218321EF21CD22142430251322EC23E422FF2368252D2840284E27D2261528D22595262E304D2F3328EE25DC25DA245A240D2316244D23052586270F281A27CF234F26A325D0246321CE23C324A6239824BD251026912600258A215220BF2467239C2691258E26AC269425F925C824EB23BC21D221632224232E2373232F259F228C2264230D25322412246B24ED24C921512241267F262527EC262B253B25BA255C25522F962E49298A2876277D2590248E22F9221E24C2258228E3269725CD244D24F824B1233C26CB23D4236525CF274527D026D3263B246D23D2213522C82496260C298029B0296C28C8271A258323E8244A21672383241D255125C223D424C2223923FE23B522DA22AF2227246E2385238B277125A9251F27D5228B2452248824622F162EAE284829B32778257E247F237D23CC24DD25C824EA262426922656258B258725D6228D25B524C12678272F280F27A1241E243A213C210023872366270E286829422AD428C62766269424392295221A24EF243D2433252E25FC24E6221123DF226623AA217C227123042354240F24AF233D25242329247B231E23E224332F852EA3291A29A629C62844265224D92320269026A0271A278726802533287426FB252024382410234E24DE258A268125C02326237721A6216E22D622A42694281F270F285A279026E5249C26C723D821C2242F253D25A324102582248D230D248923582340247B22912473264D26122660257523562337239E22142336243730AF2F9D2A152B422AF8289B24A824412516250826E0273328212576262529E326C82571252E237A24772429257C26B126ED2410235821A1220A231F236C26BF27EC277928712662242926F2240624852364240324DA25C0240B255E235E22B8230C21BD2113222222802422246926BF27F82626247923F322D123E622EA25CD30CE30722B0F2A372A392ABA276624FA23F226D526732AA82994264E257D24CE246A24AD2356242727D324CD24CE26832698257C25AE246D239822DF23F22420270E275B28C028B52839272F249A243F22D622992385245824AB243822C021E121432302211B21BB220024D925BA2643297E28B425E4242524A425B625602568305D2FCE2AD929FE2AE1284A27C62598242E253825D527F127B6257125E524E923E4247D2268246E250127B5286926D4272C274327E9269D244C26392692272A272328F127852842270F24D023EF22D2203323C822CD214021F2237221282118235E2177212222BD21F825A0262E2829295328EC24CE23EC224024EA238327D331F42FB12A5029C5274E281827182548247424E0247A274127AC24E724B72324231524A02471235C271C28FF27B127E12911298D2744271E25E726C5276A28212695263626E826A7233C24BF21FE215D2299212723C3222322B0223B24DC226C2497211C21B6226D23C2254B2674273E271626EB24AA2447246B23BF253A2762322C30612B392727289A26E12541259E254525992533286126F5280126F6243E24EF228E24E125F728FB29C72A8D2A8C2A98291827DA23B824D3251C279826F025592502255824E4219922E620F7217A219C22E12220230F240E24242553242E243F25892201226A240C26BF26E426D32504260F25172665255126CB26A92A91354631532A5329332827260924542488246B26F126F2269A28C327F4289426DF2534252A24B22673285C2A6E2BF429D828C92605268F22D9219223E3245A2315241424FC23172443217B215221C61F0121A223E12338253E2571274C257926972677242123FE21AA24F7258F269E26D627B7266D28F927CB25AB250029742AFC3452327C2B6726E2259125F2269624CA2437279227AC2B4E2B552B4F298B28CE26A0251D25EF26F42AD429ED2A0E2944290625C02375223E2168220922DB21A8228522AC22D72272224C217021E5206422AE24D92505282726CA28C72849282027F8256025CF254B26B027BF28C127B6272C27E3276C289325C125E427DF28B533A931082BE728AB27622832262D259E262C277C2B202B482DB52B8F2BD8288824E0239224AD26902726276A2621284326D823F8224423AF20D32136229D22AC229B2267239223952244229B20782197225B24AC280F277928742A762B7B288626BF248027A1274B269A2A782C372D1C2CF329C027B7260025B8260D27902752317B31732D5129BD2A7D2AEB2660253C241A282B2B622E3B2D082B5C2B962BFF28A42442243E2566263D24E1249123F021CB21AE22B321FB201C22BC207223DF22282650258F25A622D82266238D2245215324772802288B29BD2AF4299D282325C0250D2792278B29952A552A8A2B582B8A29BE28002538246923F025A9261E303831B92A8E2986298C292A28D526EA246925DF27F228E428182ABE292B28B92522245D22AF24CE2390239523BD2247210C214322FD2102225422B3224622802498257527B7269D254223BB240B23802326266D29CB2A7A2ACF29F0274E26A5237F250F273A28C028F9290C2AB029B12838270225DC24F92215242C25C9256F30BF30732BA229572BDC29F428A32402242C25EE2516267927DC278A27A326C02480223824F4227E23B6243323DC2438239722B022B72256205F226A246124972559288F282027C22547267A236E23A2232128782AA0295F2AA329AC268A2423240B255E272529A7299B290E272C252E249825A6249022572359259D26E9265F320031262B8C29C72B1B2C63270C26792376251A25B7266227AA283926E725C9234C23A3230E24E42475272F26AF253F276225DF237F24B8215422D224C12657281228272944269C2484250725B8240D249827F329512A9A2A2528352688261A247327EE27CD27F226DD262127C5244424D122AA230F2510255526A328EC280B33AF2F042B0F2CD52A492B31283E2584237C2459259627BB28692788252A249D231B258F248B24A227C1261A275728FB271A268F25932568225823F9257827FC296E290528BB27F522FD21AA259A266A257927D82ADA2A272836272027C6262328E727AF28762A102BA5295E26222232227F23ED2443264726CB276D29BE290D352031BD2B5B2C1D2C7D2A732682233024862495266F28222703262523DE23C4236224AE25422638294129AF285927AB29A9283426782711240C242627C3271F2A042ADE29B1265B22F520FF2376253427E529BB29D427E0246F25AF232E269427302B8C28D129502AD5266323A422E22373242226B7281A29102B562A1F2AA333BB31132DDF2B882B9F2A2F284F26EA2511269526F027D82549245B272B25E12362257C2551270B29162B1A28BE28962A512AFF273F261A24D824E226D4288E2BC12AA32A9D282A24CF22B122C0254627072A7E2886275F249023072570264627BF297E2ADA28572601262F23E523A624CB25BB25E62A9E2BD62B9E2BB32B46354F321B2C2A2B302B9F294E27F1248724832707250126DE260C2786270728DE25D8261A260B27E029D72A652B102CF929552C2F293A253D25F8239F26A8293E2A8F2ACE2A132ADE2407238823C424EC27332AFD2745262E24A923EC2211253C286C29E028C426CE24702302235C230425D624D62569294729E12BFA2C072DDA334531E82A362A5B2911275425A4244F24D724FC24AD26662742275F276A274C262D258526AA27D629402DD32A102B342CBC2AA82835253D24242440256B28B8275529C729A32868257C240625DF25D627442A902AAE27C223FD238122B623852555270B274526D92468246522AB2269247B25CD259C286829F12BA82CAD2D7835F52F7E2BF928402910283A264D2541256027C826B62542267728CB2A282BA6287F27AF269D25692B852BDB2B262B4B2A232A24291B24042354256923BF254A276B284428A9278B252A24EC2524267528B92ACA2AFD27E523A32271224D2451261725C4251F25D7246425FF238E23F92338243825F1267F28F029472B7A2EC035A630C1296B2759283E251B257125E026E3275128B029BC2A002A732A652B53297F27572694276C29DB2B462B542BC32AFE29D326E723472273236C248F2488260F269B27932809243F241D27EA28C828D328BD28B12768260A26CE24D9249A24512699256D245125C02694248D24822416259826282A3E2A922CB12D982DEE3393301E2A5529302799284223D3251F28872891294A2AE02A212A332ADF2BEF290628972599267E268627A027E6264C270D266324C8229623CA23A3248226C7269C260626EC26D224E624EF250E28F8271B285126A12620244C258F26472763257F251D26EB25A626F0279126F4247B25B026C829B52C972DD82CC42A742DC734B42ED828C5266126D1251C268024C72661289B29672A412AA329112B2B29E827DF24D725D7245E24D82245245A25AF25D122EB252324CD219725C325D9264D29C6286B2724254E226022F5233B273E26442640251C2428248D26882714285A2856250E25D2260A28A728D126D324E12526284D2A1F2D092B3E2A5C2B152D29358B2F4629A8267E2500261726ED238E245B286E274C272028BC27A428592A8328D0241F25A823CC246126CC256C26E2257F2447231B236024D72482269C27BF26B925EB254B2427212B22C12391236A253224F9252624D125922510293F2961288D2705278D287429412B8626EF24DC268127BF29912CC72B622CF82B742C62344930AC29992651255225CA248325F6262B277127BE25AC26DD28C72AC32A8927B824FF240B26B428F5268927FC27B827E428DA262725EA24802598224525B3246C25CC243D243B229D2188211824BA235B23A4241223D6241428C4274F2A042B8229EB2844294F28562A6628BF250228A528B429432D562C702D2E2FC42D5535A230DA2BC527C1247D2518261527D52715287A254E252D245825DA2719285B2618254527B325B928832924289827C429992A522A61283627002635248B24712547256F2374231E2342223B2331265824D523B5226C2457258525CD29832BCD2AFE280D2AF3288F284328DF25DB25C229AD2982289D2AAD2B382DA92D7E2D0D3635324F2BF22871267C24DE260E27A6294E2BF029C926E1251125D82571250B259C255F253328D82A882AD728102AC22AA82AEA2A93292027C22436257B2491247F253F25332666245E2481251D268526C9257025F7234125252868297D2B1A2C482B592CEB2ADD283327FC25B027F8284C2853280629802AA62CF12C0A2DEB348032602C3D28E525A9255927FC286E29D72AEA2A062BC0272025252467246522EA24A123852724298028DC294C29D22B482C552A9D2644246B246B2212247325AA270128B329E926D6252B25BF265626B525BB25EF23E4258D29862AFF29002CAE2B6B29CC2949275F26EB251A275F2ADF2AC028ED261A28082BFA2B4A2CC23207314E2C7428B1266727D027EB27422AA92C722B1A2B552A2B27E724E523B6249322C9243C27362A372BD82920295B28452AEB2AB927D624A62250222524FA26FF29C72BAC2BCE29E226D925FE25AF26D124D5252925C624B728F52A9D2A892A9D291A2C2E290128A025CE257527C829832B172B86297B27FD282528812839322A31AF2B5E2957266F275829D12C142D2A2C852A2C2CBE2B512A9C26CB240A2349251929AF28542BE829652B552AE92A7427BE2584255E2313233A253C26CE261A28852A5A2B082A0828AE2496244D25BB26A42440257C27D629DE2C902A8D2BD22B152D8829E1267825C925F227C32AB02BAB2A442A34290F2A6126F32784308731DC2CE02907282A270C2A222C9B2B792CBB2B712CCD2A5C2990270B259A238B2378253E27F92BDA2ADC2A292B372A4026812623242724092672240726C6264229CF2C112BF4292C2725259F237023D624FD2589253526E0272129DB28F628CF29042A0E288E2348267125DB262E29F52A6D2BDD299E2752267B26F726973142311A2BD42AEF276229232B412B4029352B472C3C2C302C2E2B0727C0254C24422435279226A12A2C2C812AB129BE267925902259241D24392689253B275726F827C82B882C842AFB28F5244024562432256E265F25EE26EC29A929B328932809282E27FA24402492229523CA265E284728F628E12A0A28C62642258E26F733AE322129AA277B261129292AB729DB2A342936286D2A322AA427B4246D247C26E8241A289C297B2B82289128B8274526A62347236A22EE2384254E27A0280728552A5D2C4E2B53297726B02440232D2426276528A32798286E276C28482731284E29242647233D23DD241B237725B428A227BD26A928D3251A26DA25F2263F31E12F93291E269927B5294F2B112A4629B9266A277229D22A7D257B254F244A25BA25C52681293E2B942C23298D29572749237D236326F2232627A129882A042A2D2BA42BEB2AEA29AE26B523D32369244C28E32A472A0C29342774285C28F9299C2BC927BD249B24D2254125F3242C268025A025B92776254D2548261526B42FFA2FC7285827D6266A26EB290E29C528FA2899272629A2294628512467258F25492899273928972B282B352CA62A862A8826C22472230626D627DD2A9F2CBF2A2F2B652D402A2F2ACA26512411233F251228BD298C28072717261A27EF28ED29BF2AA8287027F324B028C4282927DB27F525B925AE285A2624257A278E293E33622DC927F425EF27C7272729DB281F2A0028C8265C2ACA274227D125C5276928272A9A28C12676278429F127CD2AA62A0F2A8B2776241A245E260B29732B172B792A912C312BA229D426332410270D27E9286A2B7E2914288C2755276B28A22948291027E326BD273329A52A452BBD2857253D249A289B279228C028002BB9347F2D44251925F626EA29B229192BDA2860289B29202A2A27D8257925FF271D285E290328A625252654258A26F126F32822281D25E924E124952507270B294C29642A602C852BC8289225A423A82617287E2B252CAD2BCB284529D427442875296E284F27FD27EB26012BAA2AF7299327D425D028B52B2F2C182D922A102C5F34DA2B9225E2240926E727C329722A1929B628BA28E32885265E260E24CF2625278E27BE268D26EC260026A925F826B0266D254C248A250024DD264928322B1F2BAB2A882C562DAD2859263526CE267E286E2B5E2C5A2B2F2BED29CD295A2A9F28A4261D26B127E127582BFD2A3E2A4329A726D22AEE2B862B4D2CEE29392B1B36312CB825132603261D282429F427482783277A26B5261E24F723BA247F26FB253527E327CD27EA2816284E27D325852608286E25512341225E25A52895290E2CCA2A1D2DE52B9229A026DE251A279C28042B5F2A002D0E2B052B422AA6284B278725202423276E27752BA92AB427F028F028C929722AA52A0D29F328A82A6E34D02CC72693252627A328592986272F274C2771267E26C324B62431235E258229AD2A052B002B0A2B2F2B0D2C8029332888256C24FC24D924C123F9277127CB27E3284A2B852A3D285A259E24C0249B25D22790295629342B392965288427FB2551258124102655286D2BB22B032B6E2AA22AC42AD22A89274427C028DB2A72353A2CD6272C25BB27AA28AC28AC28062858297E28A427AB24F724B925CD271C28302B7A2BDA2B5A2C692B382DA62AA827EE24D4251A25062531244A25DD266128382AE32A572A5028FC2301234125F023BC257C26DB271629B029DA268824AD238325FC262E294429CC2B772B032CE12CA12B1D2BFD2A60299F28D427022B2335F22C3228E828B727132AE52AD329592846290C2B432A9A27542585271828832BB92B172C212BF22C212A432C33297227E724862790268025182573250727A829742AF329FD2A4C25B723E922812489235B266227CC263827B02A7E2730250125F9256027C628F72A1E2C462C992C322D792CD228C72B4B2781274B2B812C0A35FE2E3527C527D428A82BB52D562AE929C62BD42BD82BBD295628B8281F2B4E2A072D392D072C0D2AEC2B9C2AD626F52595250725A5271726C424EC23132707296D2A942A59276A2463220723FB2464243C27C429482A4E2A592B5F2B4428CD2665258B275B283B2A002D432B8C2BF72E722BB028882853272328AB2B032A2835812E55297E27A829CF2CA62B362B5029F32AD42BAC297928E027C8293D2C102DDA2B192C992B1D2C412A8C2AE22732245C232626D5259525A6259624AB25D42730292328DC24A7225723172467239025FD28BB29702B462A4E2C702ACC293028DC260825D027B92BFE2C412BDE2A602C222BCC2A80278D278328BB2BE82B9334402D84290E29492CF02B232D5E2CA329BF288126B9290827D629DE2A262C572AEE2B202B852C4F2B5329B7271724BC253C251A26C1276A25B625E124F623C525ED265D26F0234123DF253024E6220E2323265129F42AE4298F293A2A712AA52741271126BA2783286329632ADE297A2C9D294728F3266828832A9D2BD92C0C35AF2B852948294D2B4E2E772CAD29042A1A268B258F25E924CC28FB294A2CD7295629292AF82A3C2C572990264C2650266226DD27A528AA282F27E02468245825A1250D265A278A265324FD244D24C3225324EA29E52854291D2B432B2E2B94296C255624CE263A26232AD929862B832C852A3328B126F227132A0E2ED02DBF354E2D94299C2B342D5F2C292B7929F92994266424852511275126FB2868299628BE28F828D8288E2B9F291026DA2760269B28782B352DEC293529D527F324B323C126A527BC27E127CA271C271A26FF25C527002A492BD62AAF2960299D2A1727CA249025612525263727F827F52CFE2B642AB02853284327FA28B42B572DA333BF2EE429562BC52AD92A702BE02AAC293E282925E324AA243527C72844282328BF262B282B28682ABA290828B127802A202AE12ADE2A642B162BDF27FA24B023F7272D29AB29542B142BD02819286A253928D92B442B982CF72AE128EF295E28C525372580234526F826ED2A5E2B9B2BEE296C281D26C527D92A672B8B2B0634F02DEA296C29F6283D294B2AB02AD428432896257826F9277128052AE32BC2271027E9298C28AD26EC26F42658295E2B052C522BD22B402A522A4F284F25C925C127CE29A12B8E29322BF22BDE29C42632282C29162AB828BD2AAD2A2E2ACC2AAA27A02435253925B728012A5D2BE62B7A2885270E279728CC29DF2BFD2C6734802D0529C72882280127A3282229C32905290827E425F0297B29232B642DED2A0B2C2C2AD328D0266826DF2794292E2B9A2B3E2B492BD5294D2AE12645245F230C26A9261C295228332952293529A526AD275127D7275426F628C228822A912BAB281D2564248C24382849298A2B5A2911277726E3269A29962A8C2B682C1936D0308029E727C1273A28102A9D298B2985298E2773265F2ADA2AFB2B012D082E8D2C972BBA29D126EF27E0285F2B6F2CAC2BE72B742BF82B502AC427D3223523A426B526ED29392818283428EC261E262E269528ED25872666259A29682AD72A132B1A266D250725A72558275E274827FF26B1260428442AFF2CB92A2A2D96372A3062294C271326B928252A122CBD292D287F26F328542A152B5629F52A5C2D4C2B3B295A290C29EE27C428CC28382A042A9F2B342B342BFB2A1427B8246B268226F927A1286527EE279D25BA2439255828A9287F2AA7286027A826A029FC2A362AF426FD234924882430257B25F926BF242A263C29812A6C2CEF2B182D80377430A52A7626F727CE28C42A352EA12B2A2AFE288429442B522AC52AE929802A5A2A972940282D288C27A32CAE2BDE2BF9298E2B6129AC29F7299B286524C924562661271C29AC275726C526D825DA259D27722A532C282A0829BA279329482BEC2A1E27AE242E248A253C24BD24D6248D25E226A7283028692AAE2A802BF4355031202B2C29F3288929122B072DB52BE12B9A2A582CD12B0E2C222C9A284029DF28E626DF26E928E2285D29742CCA2D082BBB29EB26D825542683238824BF24E026ED28F22A2329D927322731260A267227E2285529A927F32832284127EB28C6273E273824992272262F250827B225C524FF27092A192AE52A262A442A1333352F0E2973265726B229E92A2A2AAB2B6D2CED2A3E2BAD2AA52B192B5B2AC728E32636263D264328B9269B27E6272B294627C527C325A423812490222A23C4236F2502281429E9285826AD25FD2617264E26B729C72829283D294429D729192929282B259424F122E4252E277A27CF28B32641299F2CE72A102B172B3C2BD932052FA5295326C625C6270029702AB62CBD2CE72BE92A1B2BE72A9B2A5928E22795258826AB267D26F026AA273427E32564266026C6253024A02210233521582310276927602AF2283C262C256526FF26CB29052CB02ACA2B252B012A952CEE2CDA2B05280D253A2674271429652A652D642B1F2BF02BF82D712CAF2BFB2BB733BE2FD528F425E6240526AD276528B428432BF32A082C542ABE29DD292529C12796265726A227B026F425E325BB251B26EC25E02781273E245D23D021A123A7234726702848290328CC252C267D25BE243829AA2AF82AA22A922A192C132C8F2C382A63292C28882402288F2AA42AED2CF8293C2B3E2B80292B2A4A2B2B2B9234842E272986269C26292730289528D727CA27412AA6292129AD28A7273C26A0253E2650269927A227D1260E28FA27F22866299A2B45297825F324782372231222932455292B2C482ADF289828B924C8265829B02B0C291C282F2A7D2CB82C552CF62A732912250D265727202AD42A3A2BB9291A2B002B3D2AFA2A382AFB2ADD34BC2E56285D28EE27152AEC2B7629FD29D129442A3A2A0C293C28232735256927BA238227BC298F29B427E1250F29342A382968297D272127F726A12546261525FE2514288429052B4A2A8A286726ED250F27C129D828C1286B2AA72D0C2E7D2BF528F826E8250027182A3E2AE72AB62ACC29242A882A4B29B32871288E2AC1358D2EEC28F9267F299F2BC12AB229E4289927CA29A42BD72A54281A289D26A82943289B2720289B27AD257326A628312C7728B3289A274D278D28D129A2271D2743271828AD2810298D289F28AE28352690259B26A7267A28552AF12AE72B252A03271F275C265D288C297529482B4E292F28DA2A392C2B2862270F27CF2B36355530422A8E286B290C2B672A1F280427C3276429152C6F2D702C3E2A372BF42984270E279D2742279C268327222AC32A122CE72947282D295B2C1D2BE128B529922A8A28B825BC24A225D426AF288926782678253D2601269F29E62A6429BF282A272027E428AD2A512A422AFE2A022C662BBF2B8F2D4A2BDA29A729212D0B37CF2F982A4B2A3A2A4B2A8A2935260225C725BC27D8291A2CE52BF72A362CB62AE3292F2814276426E527C4268D27E628B127FB279A289628CA2C812B022BA128DD29152AFE295626C1246D25A6280728C026CE258424A523F92AFE2837284F26E6255029E62A802A1E2A6429B72ADB2B212B8D2C2F2CAE2BA928C627B12A0435672E7429EC26812876295328D926B624A526A3277828012A962A032BF42AC72AF32727277F257B2727290C28B727DA26ED264E2749293D29852BE12AD52AE029522A0E2BBC2A142809265925F328D927CB251625C0227C255F26DF27492654251D26FE28822A2A2B4F2AD829A22A0E2CA02A522B4A2A5328AE27892745293B34D82F3627E2264128B627E2266525EB27A727262AB32AA8297129D92AF42B602A902820257A26492894298C2823295C287D28A9260A281C27CB28F726FC263928292AEF2B192930290E29D2290C2A052AE529BF25EA244B241A277026B724F22529268829882AE82BC62B2129BF2A942BE32B222A9C2711254727CE263F29D4340E2E1929E6262E262126DC2583259E27482A0E2BD62B8B27FE292E2BD92A852B69284C261026A9291A2941288729142A772A99298528AC270D2616256727A128F22878290829CC27AF29A42AA32B9429A227FA25B525212596290A27DC277927FA26002A282BC32AFD2B792AD029972A252AD0284C29212620263927AC29843224303C2A8B2912283E275527F225912AF82BF22B122B34295A29F52A9B2A1E2BBD28002696272B29F229012A5E29772AEA2A142CEA281628532649265327142875289127F2277527B327532BB52CAD27292672276C27BE289429D227E7258425F2275A29832BA02DC52CE32ACC29462B3129B9267B26B62634289D2BD32CC533932E072B5A29922A342998284B27C929D52B6F2B612B4C29BF299E29E12B5229C62799264829272B972BE7296B2AD92A302BBC2C45289D267F251F268728BC28812663269C266F28522A072CF829C4279B264726AE259F28DA2A3D2ABB28C125FF27D927A22BC52CAA2BD62B022A4D29012A29289226E826F82A332D8D2C003617304B2AF529472BB52B6C28F5273329752C642BE4298A29A329282A7B2AB9297126D4270228462B752C2929822A4C288528252A09299C278326DF28CC28A628072829274C264E27EA28382BA12B7225EA25B625BD273A2BFA2C0A2A7E294B261627DB273D2BDA2BC32D952CE12A032B772ABF28592749289F2A982C022E6235E02FED2A7729362A232938281528C929632CAC2B3429912762278428F1277727ED257D256F277E296B2A46294D28D1267927D829CB26E6259D27A229292AD829D3281A2680259925FF265F27D42634255B24B525762997298C2B362CFD28F727D5265A27BB2AB7299A2BA62CE22BF22C6C2A662966282D27CE2AF12CB92CF7357330C52BCF298129DC28B126EA250429DE2BFF29282A71294C28012744280626B8238126652749280A2A772A2D2AFA281126A5272C277626EE2AC32A092BA72B4E2A0028F325532364266F26F524DB22C924CD25CD271D29812BD5297F2BA229B8264E266F280D2B922D4D2AD32BE12C882C852994273C28D02A142BEB2DC9365132C22BD12A7429E72842272A2779285528EB2998286E28AD2591266C27712511254E268C25B82830297729F127982680253425DE259C284F2AC92C4A2CA42A082B1028B8234722C4250A269D242724BD23DF2718274627E0275F27A62ABA2988277B24BD248A28AF2C7E2C292D142E762D2F2B01292927982881294D2BDF34C531F12B8B2C5D2A202910263526F4277128A629AB2B4729EA26B025C325B126E326C9263F26F3285C2A0B2B842AAF2940257E257625B927812A0E2AD42B442CE82A17289B259D249D252325E423F924FA227E28D92738272728F926DC261F2659246A243A278D26422B9C2D282EF12C392C362BE5280E277A29F82BCC2DA9352932542B1B2AFB2BDB2976272F268226AA28A929BF29BA29D027D9263D2868294A2915283E28FF27512A8A2A622A73274F259524682627278B28F628C1296E2BF929A3297127BE217C24DC22AE23F422142328264727D62871284E28C8293228022652254025FF258527C229802BD72B8E2A2F2B9E2925271028AF2ACC2CB033BE318E2A042AD02AB22ABB279527D0253328CF2ACE2AFF2879260127032B502C122B802B31297C29B8288429D02A06286B2410256D2638298128CA26D6277828D2289627392602236C235322F123FE2283233C26CC268727D628D326252AAB2A1B2ADB26FB244E2307270527DD28792BC52BC52ABE295A287926BB294F2D8B33A3300B2D022C942CA42B9327B627B625FF299E2B2228BC26DA270428DD297A2CD12B642CB62ABD2948294428332787269E248A251528532956281C28E42667275D28FA2802287E23C324D22332222E21032510278F27BF27F5264226002AA52B582BE5274B24BF253525D3249E260C28BC293C2B282A6026762516265628AE320030372D962C2D2BCE2A732766259B2664284B2AD32ACB287D273028DE2ADE2C632BE729C32B2A29B129C9270827F724692380269F25C628542A082B8928A1290F2BBF2C742AB824A223B1217B245421E4223F26B227542732280529722A322CBD2AB329CC2895242E2765261E289828BF2A1D283428CF26F0254D270A271132712EDE2BAA2A302B92298726B324C227272A0C2B802C492BB427C227562B1B2C3429B0297129CF29FF275925722465247E25C0272D28AA29332C3C2C202B622B562BFC2BAC2A7A267D23DA2380243623E023952571260229A627E12554298C2BF82DAC2998286726CB267226C828F229A328192A232A6C277F26CD2612293832FC2C4F2A8A29F029CB2864273B24B227F629512BC12ABA2AE928FE27A929EC2B252A232AEA29502B48289C2559272627B6284428AC28E229562AA82B572B012C3A2B542C972B262AF12624259425FC25D7244125F2263428ED26692593266728232CA82B62298329A7282128C2285A2A042C172C512B9C29512805277B28CB3173306A287926CF2742289525AC256125B927612CEB2BF52A2A288E25AB288429922A242D802A0C2B332810265126D3288629F6293029122733298429CF291A2CC72BAE2AB129172A0C288826072638254C25D8263C288D27BA27B62424256C27D829832A8C2A3F28F828C2273C28A72AC42A802B912ABE282B2958276C27ED32722E692928265E279A26E027F8255326DF27C129992CD829AA27D427F92612293D2A2E2D662C262B9927D2268B2894289C29282BE328B527F2297F2876299C28E82A192AE829C42AE8276726DE257F24AE24BA251227C12735278B247723C125E9280C2A332ABC293F29A7284B28102965277928C2275328BE28F627F6283832762DDF26B625C024BB27EB280E27E625A125EB276929BE299527F425E2268A287328462BEB2B6A2B9429F62719270328E92A9F290E29AF28FF282A29442838279C28D3288B28ED265C260E2934266F231C24D02666266528C7262F25042682260B263C27D4287929E627FD27CE26F027A0260F255F2504271D2608287929CB311E2D3527E3247627322A5D2B07280528C126C227A027E028A9274125512601272C274128272A072B35289527172770289B25C727C327012856295E295927AC28D72790255E2646259C25E027D52710248E25D6265B256B25F226E925D2252F25A6260D27D226D62678263328B9284A283D27062565253F25D12687265327A731592E7327B524B327D927B32B602A2028212779282C29B3291F295E254E267326D127DA28E529722A312BC229432B73299F276E273527522ABA2AE0295B271126302748274924F623672492264A2528242B26652436262526F829282CEF28A8269D2706281F2AA52816281B286E295A2B67282A263324A823B8274529FC298B33CD2FE4297227AC24D926A927FD2A18291226AC269628C8295C28C228C1280C2A672A90299729D02AE52A2E2CE42BF22AA22922283927852ACA2B97297428D226F5274F27E724452305242D267C26E22596262C260D257D26332AC92BF92A5127C927D327E126A42747285C28A526A6291C28D6268424EC23D9265529502C483573302A2A09289726DA269928512AB3288427E7272528512B872CDF2AE729A12A022AA32AD1281A27D527F728692E6C2B22278F24C3258B260529B629DC28A329092996273A267F238623EF2498272727FD271425C0244D2717298629832AC429D128B427A22785252E2738268527AA2AE929232944269C246A25F228362CD4332533322CD628C62645267E27822A3F2AEC2A8D295429372CE82BDA2C8A2BE12AEF29EB27932736264A284D282E282827D726062609243327F8261E279627C9297E2A002B08296A249725B1263928DA29EA29BD27932446254426A5273B27292A712AF028A927B1260326232645278C2ADB2BEB29D726F024B2251C27192AA8348432A82C7B286A27FA2598262929932B032AAA27E527E5294D2BD52CBE2BED280D28F2259F28E0266E26FF266D27332743256124B025C1254F24C025C826E0271F2A752BA1284F25952449287E28072AB829FD26C7245B25D5269D27B129CC2AA52AA8290E2AF82727284326FC275329AE2A642B09298F26FE244F281B2A6E355631B52CF428D42571260527BD284B2B792B9629B128642A1E2BFB29792A562AF12724272B28362801294D2C572AD32AD6282C26C6240F25AB251C27D9271F29D72BD72AC12978272524A325BA284B29412AD72761258726BF287127022970290E2B742C462AE928C8288E29812740284129232C35294D265D286A28CE2B4B359731D22B6F299928D926F425E827C628FF2917298127B828282A622A932A232AF0278E257028022AEB2B6D2D2C2C7C2AE4282B28412682240226B62600296F29E32AC12A0F2AD92765253E263828C627592733278526D9266F2ABE29D72866288E2A232B842BEA28D129762943279F27B627FF291F2A1A285F28122A562C86344F32C42C712BCF29882A9127EA25DD28AA2858295328A9290D2BF22952295D295E282E28FC29CD2C932B382C522EBF2B20298327EF25FA231125D525E827182B702BE72AC8295B27FC25F524A4250A266E269B26042585277F29112B5D2A372891282B29302A3C2AE92A7B2A3C2BD5292028DE27ED279F2587272D2A632A15341032D82B142C842A6E2992272326FD260C29B8294C286B289F28CF29F6297D28D2271D299229A72CE62B212C662C49298129DF272725A223FA239B2534277B29C82AF328642738267424A3237323A324712542267925FB266A2A0B2B4F299A2886286D2A3F2B522AA72B8A2B2A2B8F2A472A812852274E27E226AE287A2AF634A431572C4E2B102AD9297E27A12555258628F8272A283829042938273626082765260128B9285A2C832B5B2B342C88295C280629CD2661240A254423AF242826152934286524272374229523CD215223C5255C24862536265B2A3E2B5C29CE29B029BB2A3F2B292AFB2BCB2C6E2A2E2B912BAA28CE289A265527B929AE2C4035E52E832AA4275A28D327EA24D624E4240126AE261A2883270D289C25E9239824D82450262329452B832BEB2A8929B22927280B287B284B273B2624257224A3259A272A2791245A227A220D24F1222E214824EE251E25C8254829A728F02641283B2AAD2A762ACC28D2297E2A402B9E2B4A2B6E2939288F25A9263028AC2B8C35962F072A80291D27E4274825DC25D3250824B3259127A728AC287B27AB2695247524B124B42A432BE52BA32982296F2974284928072AB329FA260D23472252246C2693257F23AE231D238B22F124302340260D249A24C3260E29FD284827B8260F274028382A14292B2A232A692BB62A2F2C662B5B2ABC276C2643272E2CFE35D130942A3F29002B6429AA27DE233326272448250927712AED2A452A782AA8276A247725F6259A2B392C9D28C029162AD429052AA1298C290E28E124BF228E24E224AF25A6253623A022B72455248D23C9242123682441280C2ACD281C26ED27BF2779299B294728EE273527F628582C162A202AB829C225672588276F2C193612313B2B6F2C0F2B142C9429AF26D9257424A722DE243C27E42A3A2BFD2A8C2942257925CF258A29EF2A8129AE28DC25E124F6275828672832276B253C23E9237F266A2999279C250C26072461279125E625ED23D9238A25BD274F276826A9275F293429612A6128B128882673282F281D2A9F29872A81278B26802768292335D42F7E2C072C022D772AA72B5628AF24D5247024402539250827892970292028752738269026FF26E726DE273A27CF256625F326092798278C279124DE234A253A2612284228B126D726E32650272C273C2771266C2567261B28AF284028222A392CEB29ED285728AB2A662AEC2A9A2ABB2AD229632ADD284A270427672907359730942BFD2B9F2A302A2D295A28BA270B25E62249247824CF254024DF2589267325412739262A2686250A27942582275B26F826A6268628CB28D02677267E27E928C929B6296A293B277A288F29342A03274026CF2433261729D4285B29B829AB29C1280629C42A1B2D702BB92B2F2C892B5A29FC2897298F293D28E9264632642E6D2991289628EF271B2758266025E225C6224A2397244825CF252E2777285A2749260C258F24A924E224932616254F25EB262028CF272C28B026E025122784281A27D72731269B2886262E291629EC27D0268923832473278D2A4E29A5291E28A7251D26EC277D28A1288E2B0D2C232A2228AB266827A4284627C727F031FD2EF328C228EB26B326CC271B2708270226ED24C6254928202808285C2A242B242A10291D28E2242725DB231125D7264528D629892C4A29B1286C261D256E27B527BA26B6281D2883293F299329CE28FB27982533242E27402A882ADE2964290529802886252E27F8284128A6273B28EA26E025CA2529268928D728C029B832DD2E222762263C274228B529E029B4274E26C52490249F2787291C2A092B692B882A2A2A732AE927B72546240A2408279C27B6281D27F527F425AB248325D525E8269D25422773289529612A332A572A8B29ED2566245E27CA271A29E5286726D72717282A28B8272C28CF27F1262E280026F22441262827C128792B7D2A9133D52C27288F274D27E128DF2A7428F0274B2614256C268727C32B0C2C342D6E2B0F2BB92AA02BB929002880247225EA28C5261C29FD273E2752243A24DD24EB24C825B425EF246E259E271C298E292D29442A2F26AA24F4246A286B28472873294F287228B128B128832AD72AA0290D29A2277024AA25D923F327C52A1C2CC4342C2E452AB1260B29F129D12B1E2BF6277827592347240828702BBC2B3C2B432CFD287F29C92A082C29283825CA242327DC267F2782269126F8253024B2223124A7250C271A25C224562518278828BC278827DC26C024CF24C1288F282E29BB2720295026E9261E27B1294129042B8E28D428C524FD2414283329952B392C3A34622F192BF62884297D2B8C2CD32A14299927272506258027092BCB2858282E28B328FC29C729D32C2429FC254A2525245525A324182602257727BC230326EC25FB27E527DC27D9257627E22613282028BE263C25B024B223E327C527D1296228F627B0267727DB27A52A7A29CF2BF02A9128532509264428F92B8B2C5F2C173626318A2B23293E2AA72CAF2D3F2A64297B273625F5250127E628A52A772AFE287F28B529712AF62C60298D26192511260C25D625C1243D25ED26AF25D12377267D28C1263027F12771267129C62A8F28D6294928652488219825FE27ED2A7D286126422541266726AA29782B152B6C29D029F326C52740275F29FF2886290934FB2F202AF329B029422ACF2BD82C78294D28B925F524AA27432A672A112B36287627A128212BEF2BEC297829CB27AD25D8242E26132564251B27C6279725AD24632707289928C527DC28922AA12BA12A9B2927284125A122F12332264F294E28A425C425D825EE2577287429C129D42A8A28AD2724292B29962A932A0D2BAF35382EF927E12708280628702A182924284A27BA24DF24B72543289A28B427FE268026F227562B412CF628F726FA251C257823D22457231426E626C8248A25BE236424D7268B28FC2579280C2A8E299A2A3D2A5529F02491235324E6245E26682539257524AA241225B227CD27BF274E274E25DF25D828B5281A2B1A2AA12A4B33762FE4286C26D4283129D229F82AEA29702878237E235E26DE26242963283827DF278C289B292B2B6729A0285729372885246B2595244324A52545276826EC241F251026E5257E27FE28222A5F2BA02A432BB429782637231A2321246224682695256024782433268027B727D5261D28F524AE253F27B128DF29FB2A612C1F35CC2E772AE0294D27C32711290129382832265A25A02373253E250E288D27FB28B927D1284B2A1F2916294128232A392905286C271E24CB245B25FA243A256223A823B2220025C02601280429042A3329B92841271524C4248C239422AF23F124D426B9240F2669286F2968272326B224652549265725D2271629F929B62AE0357B312A2C06293027FE26A0272729812872269224BD224623BE23FD2560289B25BD279829A92ABD2A872ABB29552AE029262B872ACA288D249025EC245B272A271925612359247223332735296129FF27DD27A2271D26E4240523C8223E23DA255C265C26C6263F28F82A8F282B2702287E252E262027E3254427502BE32B6C36AC2F852B5E2811264726A1273A27B9291025A523F0220A2312231B2676273E271628D02AA62AC82BEE28D927A128C129ED29562BD9291A264B26C6251E282328B1261324DD223322E624C2276828A82766264A2870274B25DD2227236123E4243A267F25BD25ED267A2A7E27E226B625C225C72701284327A428D328922A5C34712F52290228E127AA251327052804297D279623B0249C23BC2265259E2762294C2B2A2A722C0E2D782AC32741281F299A2795297729C426E6244B24EB265F27142820269A25BE224C239A25782794263627752AAE283E26FF25FC23B524FB24C6237523E3247726D828C728AB26C927A528022716280C286127C627E728E3314F2F4C2B27286526802738266029222A4229FC26DA230B23A324AD24EB25082B262C822B5A2B092DAA29092742270A2831271B28D0295429DC24012573253026DF296D29F02546227E251724DA252E257928362ABE299728C326FD22CB25FD22CA256925302515254528B82864296E2ABD28C326AB2877295B2B512A552B3335D62F4B29E026A1254027BD2525297029FC282526782464229B22AC22D1259D287529B8298E28B9292A295927AD261028412814289228CE24B4249F221C253725902676265A26B824F12174255B240E243C269828282766282526EA236222D9237F23CC22732465231C288729B8299628B527762777290F2BFF2AE32AF32A4B35512DD0271F27F9233E240525CA27C52878297B26EF23EE2243221A250C270127392AA0292029922966292F279E26BF25A526582828273725F7255C247A23372393245A266824FB22A4218C230F22BB2376258F2666257426FD268324F62346215C2398237323632510273C2873274428AE25A726A9286727082AC12ACC2B3633702C09273725412490230F2532279328542844252224B2235322AE227125B6288F2965288626DE26E6268C25CC2426250A26BC253D25142629230224B722A6235C236C2377245822A2215423DA23E2234D2472234F24232563250B259E2297244A23F520A82278254228CF26A826D925AF240D2540283828192A122B852A9832152C6327182395221B22AD229F250426D12624241D24C5207821CF219C2371259C270527EF26AA25EC259122F123EB263626FF2575256C247024B722EF2413260C240523F723AB20D321ED222724EF25CD2500256325B32357265F24B72251216C23CF21F5206D223E27F628DF26122697238F258128FF28AF29512B8A2B6433992C5A26E42468230D24492422243F25C0266924572478213E226F21F32281231B244D26C6250F25F7223E2263233B256225F6247D2436262B251724FB244826A0264F255523AF212423B32369252727D42642262B237B24A725CF247922A922712277217E22152470272727ED26E9267927BB2404279227E3288628032BF734072CF6246723CB237823D5225823BE252824C32452231C22A82182208C216D232C249E2329265626FE249722D624A824EB242126032755262B256A23A423282715289C276E239622F420A123BF235526F52719270424A323B225152445247623232208222422DF23212686287A29672622262527A127DD279028E829F92A6232352CAA252B226D230024ED228D24CC2470257F261725C821D6218621C4226D230624F224CD24BF243125EE22632522239D259B2620245F260424A0237223E72621283827A625EC236721B923AF24F925742AF8299E2477251E256F263B264C2640237D206923AA2366249B2782276F2717275A282529C026FD277727D9295932882BDF25A222E62179237E237223D5255324282420231E222F20662033222924DB22DC23DD25BF22A62419249D24F723F7251C274A251725BC23AA23B2227625A22606256E25B72287211C2301248024CD28C027DC2516230826BB258229DD251D231B22FC21D32137254B24EA2712292E2904283B28BE274327922496267C316E2CAF25292290232123BC239423AD2575253524FD238323B2210620A4221A237524BE24F7249522AC248823B02412246B2468278A253623B42302259E221A232524ED23C6245F22D020B422FD22C6237128D328F624C7233723B223F9245624E123C82190210321E223F62428279B294929E52901295827B52687276427EC329C2C6425BC2324212824E4249F235A245E250325F3259C2303223121E022CE21E7227A2592244B23EC23C8223024F3233724EC266326682416249421AA213E2216224D2367223922F421F420B421CF249326A526C125B02171209E223E237824CD225E227C21E72025239523E325CA299729F22BB82A772873268727A627AE30 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 403F + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 55 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 0009 + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 D32817234C233221D021412156204120D020F81F561F291F3D1FB91E7C20151FC91DC31F7D1FA31D2A1FA91E5520DD1E371FAC1EC11FF61F631F4F1FFF20A81FDC1DDE1E481E401C131DF71EA61E131F801EF81F9D1E4E1E2D1F831ED21D321F42201C209D1F911D0F1F791E4B1D1220081E02212620C41FD41EF3213723262E6E2CC324922191233823B121C521332248223D209A21A320CF1F8D203720B01F1E1F2B1F131FED1E251F47209C20DE213F1F3221B31FC1200A20F92062218E1F8F1FB720C91F3F20FD1E56217D1F6D1F6A2196200B1FFA1E931FD621C620E21F39202D20DA20681F43208320C21FE61FAB1F6C21FA21A9217F227F22BC24092E3E2CD625A02498237522D7212D223D23D2207E23E922152352217220B7201B21B82041216F20201F0D1F352165205B207E21FF21D220112170213D1E7620E6215B21F2212121B02191208F212E20E81F4F20DF20721FAF204221C91ED220112195210F217520211FA1209920F420B21F32207B207023612161225D229A25A12FE92C3D293B26F9224F23DD22B12311221121DC2002219F205E21A920CC21761FA42091206020F321921FEA1F151FF22001212022AD21702207212420CB1E77203422CF21942240205B204521FE1F541F3D202D2110218F211D2106211C224222DC22472134211220D51F541F3F1FD91FDF207F2121225423A1211E23B3246F2EDB2DD727B8239722D0222C233E22492227224D212C2150211F20C71F6121E820841FEF20242029200A2028205122D31EFF1F7C232F21861E9B2148213120502012227F22DD211420E0208120BB1F33210320DF215E20CE20D321CA22692117216722B7216722FA1FE9200120C420762034214921C2219B208B211923B923432D362C632774244A254822AE23A12187218822B5210E22A5227120BA20DF203A215321D5203920F0215920E11F24210A2245203D22B921AA21C721CF215721F221DF215222AC212B20D121F11FB9204B2004218C2134228222BF21BD22EC2113221A23C322D6201721062161214221D9210021F221A423CD22CB21172457252E2EC62CEF25B124FF23F0226E225F23F421A821AF22242228217021D9207B225522CF227621E021F2200523D1226021F92174200E218F22CF22E122F1215E239922DD215722B921E8209520BE202620D11FF12103248D22C8224E23BD235122EC2281234F210B223B2130218421A821072289230A229C23CE210E23CE236024842F692D6827AE242A25E822C92034210D22B0217922BB202F219E22D3235C217A219522F921A222FE228722052137229424B7212722A9223A22CE22EC22D72120219C212B226E22FD20F820D8228122E31FBC234223B62276225723A9212A23C422EF227022B5219621782243224F22A1237F2371232F232D22B1241B241A269130552B0A27A1242023E7221E21C01FBA21B32202226321EC201D21E320CC21D11F8F20CD212F225A223B22F7203F20F0221122C823D221D1207020BF218720A920B42191220A22A421D1201320661F8D206D220622AB230022E724ED22292230228322F720A3228C22DE2383221322F72323258723CB23F9222823F6242125DB30992D9D246322F4212222F120D7217A21D9224521B1222E2294209E2105229B2138225021372205225C2237233B2264242823AA256F247F211F238C20692084212F22D623E822382340213A22F721B720B0221524DB234123DE226423EC21A5228A255822E720CF2289239524BF2573258F2564265D25A6246923CE251827C533642B8B2716248022F0228D21E521762112223C228F2388227B229120F7214C224122C7216522CF226D21FF23DA231B26A4231A235B2289217E21E020D52060216B2424247F24A424EF2118214A22192103235923EB23F523D724E0236B2379243F23E0217121A522872402241824D4263126E72431249E22A0248D260E2867343D2CB625CB2280240E22B320B2210A211D235823EB22CB226821E821F32143238B226E225922B225A82377267E247025D8242524B9230422BC20AD200C238A2322240826C324DC2479231122F4227A232226352679276027E32609250724B724BE2357233124E6226A26CC270A2886299428E1252024F323752473260E2BBC348D2C0C273A237722F921A621E921D9209A2276221422582487227124E823FF241724122385212E2423242225CE245A277A256C240B238821DC21AF231E24B626F5254E276227B12461228922CD244224002680293C29C528E7286E271E25D8251023A5227D21DF2249266B286529C72A57286D256B262E24D9258A28DA2BD932B32B652694231F24DC240B2264225E231E24242452231024E523D023FC25F2236C25912409239723102475241525D525D925F3248822A12143245623B624F624C7279F279A26A524B123CE225D2425241329412B822A1829DD269626F1259B252625B2211C231D244F284F273B278A283628992760258724122746296F2C5D366D2B32261925C12310254E23C82497223425F622AB252027D824A625E1250727C024D025BE251623D62250237924F525FF25F9233E2246234E23AF232E25EC253928122857277B254523F6214322572405267228AA285626D425F7252B24A1231A235224D3250927E629652A81298F2961293026C8253023B725A429FF2BA834702DE027A224AF23F7248525A724942430233325FD2648277D266E285A29672A65278E2615249123C425B326AB24B2256E24EE248A24E022D7222224BD246E25692785272327A924F521A9242E237B2444276524F72448252B26BD257F25CF24AE240A239A262D28C82A052C54282628212676259B2482252926DF28CA2B8D342F2B7827162644265824CC2499245424E323E92458285E29D829802AA52A442A7B28EE247A24202471269A253B2792284927C1269A2493211923B224C2255F27AF28522AE529E425D6219E20A5222F251C26AB259A244D2699299F28E226EC254E247523D62551268827FB26EE2505230C253D242524B7255F2741295F2CA734132D1C274426F123BC25C12660253F25232690274F291C2B7D2BF82BD3295E29DF266D25B925F2246C261729AD295D2A9B27CD26BC242523C1233124A727DE28962B9D2A0F281F2436248523D7232025FC2668285426E324A629312848254523ED24BB22A8249F231E2674245027B725DF257B24AB2322259227F528972ACF353E2C062ABB2601271B277527DE275026BE26AB28332C6E2BAE2BAB2B412CF028482780275C250729FC290A2A2D2CF32B6D274A260724EA23FC2369253429162BA82BFF2A852971245024AC2367250F25A828312797259D2466246123ED22212363230F25C926192451267525E424952407250B24D523C82470287128502A6C349A2F8929A02821280D27FA26DE250C27E72649290F2C442D712C032A942C852A252820247D272F293B29F629F429602989281D25C721202164261F26C7299729A12A322B9C286D254D23F4220A238B2525279F26F0238E22E323DC216F22CC2304266C260A27B1274229A42612261328FB258B2513260B26792750290D2A443458301A2C2C2CBA2AD7283D270A253E267C280D2B272E522C592AA429F5281728B825762963294E29BF29672B252AC72885276A241624B523272597277928242A472A382A992733255E2203228825DA2301262A26DC2453230D21A7224C22C3236B26BF264C288C289F2A3D2A5629232B9327A027422AC527F428CC2865285A32B830BB2CFB2D132B2D294228CE26772681289F296029202B8D295629D7265B26B0277227242CE82A0A2BD929122AB028A62523251923E124A727332779282527D127B927EA2565249E23C323F9237025982601264D2345234322A32234223A246627F629FD29582A452BAD2910297F262025FB276327B6287C27EC25DD266C303830732C832B792C0A2C7A2AB52863265A278B276E28A5277C269624DD264A267228EB291C2CA629A3284F276527F026FE24BA240D24DA269F28F0264527AC262325C325B424B6237223AE275E276C26AF2800289426BF23A723F922FD231D27EB29F02B1F2D272A432B0F2BD3276025AF246024712561254D244524DC240630C52FE52AD02BE02B662C19295A288F278A258A242A250F2620238C249B27DE27FA29BB2C1F2B902AD62882271628ED2705267424E92488285F29FE26C0262426FB253C26832379219225D127FE29142BDF2B8F2A4A2A00277C245E23CE234F284828F129332A11291F2811256624DD248824CC22622340230324EF2295251630482F9F296929EE2A332C372A9F27F0254027A925402721259A236124BE251F29C22A572B302BD52B9B27A8250327CF25C124FB24E826F8279A27F32636255526A025BF25D224E9248B266E28562B0C2B612B9C2BAA2A8E272E25BB22B6230A26892942284326D725F923AE2206224B244E23F921A3221D231325C425B5245C2F872EA52980290E2C702A7829EF27D0262727FD254A265925DB23BF259028E12A922D9D2ABE2A2C290328C527C8245A25A72440267E286F28D429B0287C28F627D9278A26E325A12571251929C32AD8293A2C042B44281F24C2244922CE220926B725602511254122BD232822AC2297238923DA214822CA228024E824C2279C312A30942B162A4529072A2F29D92763272F27AC26F3268E2566235E2567272729CA2AB92A7F27A5270326DF24032451258824BE25D7283C27EE276F27E92804280628AC269426E823DA267C26CF27002914289028D325E1220A226E235D224C25FA23AF223E23732270224521C7218A224F22AC224924FA245B25642776280033B030E32D392ADB2AB7298B29EB2834281C27A3258C260624B8268F24A826A427C426C32742267A2690250F256424942453259C266D264D27F92670273B28BC281E28CB2607252723F924AA24E9262B26D026812571234F2239216322E521AF22F5244A23BB227B23392378221A222D2247239023F62589265128D228E62B7A35DD31342D1A2DCA2B502A1229B92876275627A025C523CD24D82354263026F62759289F26DC26E525DE257C253624A123B6242228A727C226C2271E290429122A23298E2657253123612464257E2447254926F9233A239A2136235C2137233625C024F524D5230025DA233B2321230825EB24EA27C4288B27E327642A562B56342633112E022AC82960293A2C292A94287D276D24E225352482245D243226A5272E2843275C271829DE255A25762389256D241D27E7286A28E0285F29FE297E29F426D224C8247124472493253D255B254925D92337247C21B8230F24A6254E26D726572769273E266F255E2422233224BB254F28FA2A64294A28D1286E28EC32FD31242C8E2A3229812A802A9E2A682AA4270B2727239524B7233F250025EF235225C4250B27E626A6244623CD24BC242B25C327772A4729832A2A2B5C2A1828FF24942492247F241B253424B724B1235B23FF2439218322B5240027D52632277D2625294429ED25EC2621269E26A8265A27AE28992A4B2ABF2A6C290C286F306131D32D4129092AEA2A70299D2AF928CB28E1275327AB255524202692281F288C250826932654271A24F8232923D62278253A29802A9E2A482B52293B2A1E272D271425C625F423DD2440261925842254232D24E321AF2359264128BD29B628EE283B299E286E28D9265024F024B2267B286F2BD02AAD2A2C2982291029D1301D30D6290F28DB26A12898293B2CC32A46288F267825D8246D2621279127D126BC26E8244F27DC26B1257924E4238D230126B029F02AC92B222B462A1927E3259E2455254325A2245A236D269A25532593256C253624972487276229772B1F299C292E2A122AB6280D28F725C3258727F229B02AFD2B722ACB2A73292B280631DB2FC92A502888286B28512BCD2A4F2ADE28E6259B24F3255327402995296228B7254D2704268D26022754246E261626E8277529262AD6278D28E728D22554248125402517240023E024FD23CE25FA258827832615237B25B528D0291D2BCF2995299E2A172BF129A72703244C24B0260E2A402BFC29D02AE02A0D29F026D131C2301F2B43297C2ABC2C192B502C282926293A26B426DC27EE2A392BA72BDA29A3280427572616263428B2260927E0290B2A8A29D52906271B26A7264825B4240E247426312439235225EA268F2746267E274B26BD24F5269E28212B2D2DA229162B3B2A5929E8264A253A2528256227DF2786293D2C3C2BFE296E29B427B83145308B2C092DF62BC32D822C2B2B4D288026E1263829B12C0C2D6D2CED2B2D2B172B98289525B5261C2503264E29AC2A53293529B529A026C025B425C42363243F24BF25DB27C5243F25152A992BB128D6261528552796266D2A322D612CE42BEA29C4291F2AE328DD269D24F1226025772742294D2B5A2A20296328352808347F32C12D092EBC2DB62DB42A992826276E26F928DD2C092DC62C2D2BF62BFF2BCE2BA62A39278E263C254A2508264929BA28BA27D72908287A265826D2235E251D26B828D32804272D27A52A592B912A712A46283926C026B8292929B429B328022AF72673272D275D2423243C2555272C282829C82AC22970290D2834292E345A32B82D922C4A2CE32C102BFC29D128FF27A529612DD22BBB2A632D552BDB2A7B2C4B2913276125C925EE22A02591278B26C225BC265926F425AB24F723B526DD27D229922A22298529BC29FB2AF229D3295C263C2610258225E0261726062547254B25E523C2221025562454274529592AAB28EF29F8273E262727242ADA353C32972C712B182C882B3E2A6129B527A129FB28E42A302D3D2CCD2BF72B752B1A2DA92937265225A8249626E228EB26E727082661248F258E235C23ED24C72560288729792A062819285C293829E728EA27F223E423F0239624A2235C243A25D8241B2446230B2306243E269928682AC62A8B2943293B24B225F8267F299E32B6317E2BC82A192AD829422A6729882769261327482A612C652AD828A6284029E12822285A25A12400279A27F529572B3A289B25CB23C523812290229524C724A326D526AD26D926D128B7294429222796252E244923E022C5252024F823AE2453256B257D25A6244F26D32598271029FD294028F8268A24E424EF254C28D731D1316A2D752AB72B0B2CB12BEE29C9276027F326F4261928DF28CF296A2A8D29442A28283623612624267B29482C942BEB29E3273F239822A3241A227C244B262F26C924A924A8255B279429E227BD25C825EE245A256D254926BC250527032953286628A927E7269427C826A7264D279D266025F7230E23B9237B240F2948325332B02B1E29F82A87293A29002837272B263C26A528062AAC29E728C929872960282627E8254D2547275629BC2C9A2DA72B4127E8231422F622802481258A278E25232557267A2318254B271427C7246A241E265728862ABA2BAD28DA27E928452C052BB72838275D270625E2254D26F0254B25F225CE239A250728BF29A6327F320D2CBA2BD72A782DE4277028A627DA255E261A28512A672AF0294B2BA72992283B26E425FF240C266628232AD92CDE2A52272224F42300242D251E28F428AD27B7256F26A5249624EF232424DD236325B626772AF4296F2AC9285428BD28C62A342B2E286225832509258C25372787278128592810285127E226462B5E3478303D2B442BC02C282D292D222ABB290328AA278C29B22BC62A2A2B4C28DF26382513277526BE25D924A527B42A772CE1281E294326CD2273261B27E628852B0D2B102A1B28A2230123EF222F245E238625F1278D298F2A982AE4282828BE2A3B2A612822261824482441249625F928EF2A6F2BA32B5828ED256128292C8235E42FB42A032B492CFC2DC12D992A4D29D9298A270F2801294127E825F3266E256D24EA26AD2690270C291B29BC2A452B78280025D4232C25C626C8283C2A8D291329122A59290825B92364234622722422258D295F29EC2A53284028E127C7286D29132806263D2481259C23C92595297C2A7B2BAD2B3D29C728C3283C2A9E333A2F6D296C29AB2B2C2D982C1B2C132C6F2A7F297627B4268E268126D7255524EF241B28832AB72C9929AC29372B6E2AC729EB26C524FC258E28C72667297B28F3284C2AE82AE5273924AB2183239323312541282528A329872BFB273029572A9F28FB2535256923F826AB268D26C229182B852B032D5C2AB129E32A572AF632872E012B3C2AB42A202C732CC22CF82BF62AEA270F263B244F237A240225DC246D26DF2A852A6C2B632A2E29352A672B9E28F225C025FD28312ADF294D2AE729012A7729142ABE28E92446235824EC229824E6257829292B2D29E9292629DA276A259F2517236C248B2789267C26762A732B012BB62CE429822816273C28473291300A2C122C972B98297B2A0F2984299A2A60291B26E124C52394247424AF25D8277F28302A7C2B3B2A942AC42CF92A67276026DC278129592A702A2429D1272F286428802943277C2484236F220D23C224B4273728FD285629E127562676267A25B125A3251126F2273F2703281029D729A72BD52B53291E270E265D27A332ED32882F5F2DF12B0D2ABF29A828622787280128C92702259B23F023E7258A25432998270429ED281229F22B942B802BC628A22666268F285C29D2262126B42604280C2835293026AC24E922862334246F263D2945286128A7286D2624242E2572243323B4254927A92971299D28672A592C682B7B298827C82630273D2A253495327E2F362DF92B2F2B4E29E82608285C2A4B29F2263D258523A824B82626292A288E284829782B772C1C2B8E29822609265027F927E628C5265D24E5242126FC2739286527C82509245C241325AA261C28982A3A2AC927A127F3267024AD22BD211526CB26DA29052B1C2A7729012B152CEB2B4529B4255C26BC26EB29E0354B33CD2E9A2DCC2ABF2AE12AC32C512B4B2A9E2789263E25D125FB25E5278B27362A262E602B782D8E2ABB2ABC274E2618239E23BE26DA2604260B260426FF253D268C273626752592252725CC253B27362A132A622A9B2A9C2A7E2A80265925B324E627FF28A42A0F2CD52ABC2A2A2C3E2B3729CE2781254A28ED26502B51348933782FED2CCF2A162AFA2B8C2BB8298329FE270A271C257B262B29AC2A7B2AC529482B4A2BC92D7129332721269525E3228E26242720287A280825A326DB2735297F2A6527E3256F2672286527302644278A28DC2747289629352A2528DA24AE249727F429082AB52D592B4D2ABB2A392AE9288125F22302267429B32B2A35AE31252CDC2CF229F22A4D2A7028BF2554278727B92625270E2A262BC62CA62B8E2AE72B7829B32A90283824FD23F022E024E424C8284628CA2762256C270D288028F52876270E27A5282E28B5278B253D24CA240424ED26902ACE29EB2651248223BD25E727D729E7292E29852AF52A5D28D025922699245F275929222CD93793321A2AA6297529CC2A1929E0263E2782263F265E281328A628932A0E2C122DC3294B2A062AB628D823702375234B24222597274928CF277A258C259827152835290E2808268426D226A427AA25DA230B2589240A24D6268D26342790239A22892441245326AE29312CEB29D42BEA2D3B2ABE2669263E250F29212C302E5F36A130612B5A29262C992D4F2B47281927E9253728532AC22BA228D32AF1295629F5279126F1261A265E26132323240F248D2392262F2A5B2637259925ED257E25FE259325FC24A4261C2678254F252424F825A5270126B7251325922544235A232B25152567279529752B912A842A022C192AE927EF27C4268029472DFC2C9A34E9316B2C4C2D8C2DD32B942C1429A628E2297729BB2A442BEA2A20285B274E261929CE2871289429942701260624A725692525273626B926F024C4259526B524C124A2263625B727B92798272F2578252528742919287026A124CC24D6244B24FC2496255F28DA266C2A3C2A5229B22BFB299D274528C326E3278C2BEB2D5E359730E22CEF2CC62D9D2B732B472AFF2B1F290F287B2A6827CE27B52651285228CC2AC32AFE29EC29F129B824EE25D4255429D2293A287C25C9246825D4269E259B24B026F7264C29D62A542A9D2BC4298F2AAE2C8C2AFE279826D5253A26A726E9254E25BC26CC2734281F297D2B632A82267922AD25D42488277228922A63340F327E2BA32B232BD92B4A2AB42C952A022A442A3129E9256325EE25C628A029E52A782A9B29792AFF2760255A246927AC29D5298A296A2733262A26772609255C25CE26F0274D29C22A382A292C602A602C742C722B4C27F226A625882749294028A026BB2715264C2AE32A942A0D283924B62463258C259B284B27E028D0321631FC2B202B2B2A832A112C342C612BEB29A7286E27BE25A126B32586292F2B902A372AFB2AFB2A3728822578273429592A2B2ADA2A392864295429F0287A261B2503281D2BE72A242C8A2C712B422A312B642A89285026EF240E267B2898289727E226AD27FE27802BCB2BE62A7328752355256624AF24D5271A28882AD33687310B2CD42C8A2B732C572D1F2C4F2A062964264B267F24A8252A28132B4B2BBD2B0C2C892BD92A892758268F27A12B2E2F9C2C1129F0261429E22A2229B728EA27892B452CCD2C6E2C672CEF2B222BE62A0D28E9270425F724BF2539261B28FB263025792751271C2BE7293B26D3267325EF247624D325B427CF2A212E49370432842D032DD12C0C2DBE2D622BF72A6228B9254226CB257027892717298F2CAB2C802CFB2BD929D92736285429D52CBC2C212BF0291B298427422A8E274D262F29CE2CC52CF52B9F2BFF2A822A1E2ABA29522887243125D923BC24FA26AF265326E9246C25A026CE2842272E2644263A26CE258125FF233127132B9B2E1037E2300B2EAD2B1D2C792BC22BA32C292BF129AA263126E425EB28502A4D2B81292C2B062BC32A1B291126B6281B2AA02B342B142BD6279025D5240026C9259C261A2A2C2CDE2CC22B57295E29D42BA9298F28B925112573251128DA26CD262626CE26B8264E2794252826722561267A288F277326D926F626A62889292C2D653515318A2DB52DFF290D2C632D012DB92A0C297F277327D628FD29192CB929EC2A042ABA2ACE283928A52552296D2AD62BFE29F92AED26CF23E8238D24D2247826DE273F29F82B97277C27DB28522B3429922890268D246226872BE629E829D1297629B0275525F8253B2651270729D6295429C925CD2939274A29CE2D392ECB352932412BCD2AE629B82CD62FB02DCF2B1D2AC327C728C52A942CA22B622AA627A22A612CD429F526F228D32A122A2F2B782A0029F1283A256223C522F5243D26CF277728F8264C260A260828562A5128252788274C27F728B92B982CCC2A742B962801278424E224E9284229BB2A682EA72A2D2890282028B829902E872CF236CD31412D3F29122A312D7D2D262EBF2B5D2A63292728E129FE2A482A6E29032AE92A442C7A2ABC296F291C2CF62BD828E2270F2A03293226E3245924B625D3271829ED283426A524CC2611291328A8279128CB273B2AF52ADB2CA42ACD2A852AC429FE242A241628A72A5D2B382B6E2DCE2C0A2D7E29DB289629092D1C2EAE36FB31DA2D412B542C4F2B922D1D2EC02B632914261A2A2929322BAC281A2896275D2BB82BB32B47291A28AE285F275A294A290B2A932A7B266825B9250026E028D82AE92AD0275B268929F028AA260A252D27B8296F2B1C2BE12AAF2A132B7229632957260A2522265F282D2A152B7D2D262CD12B0D2A3A2909297A2A7C2C2F354D30E42D522B9C2A162D352CA5295D2A9826B6265C286528D929ED272228C8271329F52A34295D2996275A268028E228D0271628B7261B260025A125D727F129AD2BE52BF72B532AE627472741267A240B26D82AE6289829982CAE2C9F2BDA29F525F6239024FB23D0287728572A5F2C9D2B3D2A0629F3264527372A8D2B0334B531FA2D2D2DB92CA22BC82A52280A28B92510264729D22CB72955297A286428FD29A22A6F285729A628DB25FE28F427CA280F294228C12469252E281D297729412C5B2C742BB4297628CD26F726E728A92A562B192B582B1C2BD02A722BE527FC258F2642254425C3266727AF2BB32A2A29B428FD28F7264A27482A6F2BF3313832492DFB2C032B1B2BF32A2C28B225D425C22597278E287729B3291029E92A952A6D2BB329EF2A8C29F0272E28362BFB2905299D26C0258027B1288D29CE28B62A3E2B74299B29C2273C25F0279928212BE62CCF29932BD12A2C29072BEC29A9282B27AA25DA278927412A9929BE2830272127BE26D328742B8A2B932A6E32AD309F2C5F2B332AF02AC22ACF271524BE2551256D27722989295F29FA2A4F29D72A7C2DA52AD8289F278E265C29B42B522C0F2B502AC9270229722AA12A9A2A062ABC293A29DD2434259127A929DC29672CAF2BA42AC928832AC129A929992AE9285C2769286F29EA2A9E299229D5298527BB288729112BCD2B292C922B54325130172BBB2A7E2A0E2AEF2999272D26C6265727CE275A2B2C29B2289F2A832A972C202B6C2A932896263827EA28992B822C7A2CC82B3829F62AD02AE32A0C29FC287E26E5251E23BB24C1267D29372A062C262B1F2BD4284529B0261827982793261926A6274F299A2B5E2A112B5829F1271C296B29B62AB82A152B3D2B30345E326A2AD4281A29D629982A4C270926822732282B29EC2C662A96298129322B222BC42A91296226E6252C2680292A2C712C152C082BDD2AF62A482BD5283828512900277826BC22E72459279A279D27E8271D29E9261B27AB245626EA232A241F25DF234D27FC285929FE28022833286629152AC32A9D2BA92C872AE22BA735A0305729FD267F27BF29A82AF329E927AD26C927A42BB42C0B2BA327CF27302A382A81290329162705245424C0254B295B2AF92B092B762A0E2B7529192A132B0528F1253B25ED238326E325F624C524A2260F26FF26CC251525C7221E2392225623AF23CC24C22723290129E5272C295528BC29512C932BD42CE92A102C4B363F30D82AD327C329962A342A7E2BAB285B2718292B2BAC2C4A2AA129D7289E29C72BB22BA6280C263D23AA27D626A5282428F02A642AAB2A042B182A732789264B25DD239D25DE247F25CE267825F624A625BE27EB2860272326C9240E2483242A258424B82616298E2BC729CD28832730290D2B2E2D272BEB2B282A902BE536C531832C172CF12B8D2A4D29D7284926AB278E28F42BE72B2C2A052AAC270A2AB92B1C2A3F28272779250F266D29932A48285327B626B227C227CE24E32522253625F42463275D265126C326D8250826C827652942291A2733288F26582406256B241627BA27CD28D62CC82A442B5E286F260C2A5C2DDA2BD92ABA293B2B27352831252C3D2B90290E2AEA27CE24122616282729992BC82AFB2937298D2985299A2905291C2886284F277C29C12A622A092698261B27CF26F7277D25B2254E2577254F26C727A928892643265F28CB283B29392CD429C828F528AF2880287D263126F725AD287B29B12C592CAC2A8C290326CE27232B252A9729A02A3B2D5D3691300F2CD8283127D126D925B02577281C2A8C2B9E2BAE2A4F292F292929C62A7228E927F5268927C529A72CBC2BDC270C26C7257B2781280A283A281A260327DC28DB26E028F4270E2689251A28CE296E2CD52CE529D12A9F2A212A492CCC2BB62AE828C0286A2ACA2BC22AD529EF2AA5278C268E268C289C27FC28A32CCF358E31E12A0A296C26872593250126E2277F2C8B2C2A2CD8297129442BD92C002D312BDF28B128AA293F2B3D2BDB287926C5243E277E29CE29A62A3D290C2BE02AC32A27297327312576241E26E327C327612B7F2B842A8C2A5F2B262D902CDC2CF42A192C572C56280929F428AB27D12956276126B025A323B4255A298E2B3C352230232CB72A8C292D27D42504271729552B482DB829C8280729CB2A782C442CC12AFF272628FD29FF2AFB2AF927C125AD255B281029A228DA2A5E2AB72A192972288B287E270425A124BA274C263E29402BE12B08296E29752C372DF82C0E2C7A2C7F2DCB29FE29F527EB27D9279329EA280C287C26BD25B727B129262BC133AA30082C6D2D8B2AE22847272E26322AE92CD02B0E2A4E2873283829CD29B32CF42641278A288E2955293E263226D92537241B266B26E928152AF929822B3A2A0229E32605250725C3254C2612275727852868295D29312B7A2C422D912C662A152A5D2B732AD5295D2A512811290B2B2F2A4328D4255D2507279628BF29C1334230FF2C2E2C122C88290D27722630299B2A6A2C6A2B3D2A082886282E28512BF828402728275B28A627FA256725F628462636282C28D4275D29452B7329D929D3280B2704264227E42790287E29A328982760280B297C2A662B6B29B5295C291629302BC1295C2A122BC829B42BAE2AA028F227BF26552444268E269029B4321E31612DDF2BA22B8D29A2279C258B27ED29392C092D162D112CBA29972A262906274727F927742875283A2788271A28FB2A9A2B202A322AF92BD52A7929152A542841250B25792640287F28D12909294F2A0A2ABE29E427E32719278D262C29E2298B29A529CD2A652B792C7D2C0A2CFA29DC279D272627BA289228BE29CC33D430B92CFE2C692BE32948282B24B424E226F229682C3E2C5D2B292A392B5B293B299C29F329BF2827285825DF25F828C728402A8B2AF3282C2C782B5D2BD8273B261D266B288B28CD28FF27C229592A702BF92B69291C25EA282D26AE270D289927572977298E29AC2B202C2D2CD42AD527822798274D296729482944294231D930502C6429402A632A8228E2252F246B266629292BFE2A092A902ACA2A1C2A5528382AA32A1C2B3E290D260427FE28672A972A272ABA28982B832C342C7C2991278A28F42A952B862AF927CF29832A342BEC2B3B283327B52611287B28F3275827BC27A5278E29A62BD82B572B2829C326892786287E29522CA72B922A4F321033DA2AB329E828C72766264E24692585249B28832A6F2AF0295C2B9B2B8729FC287E29632C8E2B5C29A226AE28222AE32BBF28A1271226372A6F2AAE298D289B27E1294A2AF52BD62AD0293629CF2AF42DDD2A41292E264E2847295329AA299627D5260226EB28622B502A342AB827C526272761284F29522DD62B7B2B6734A731D02C3529DA273027E526D6245324B42461252B29FC27472BB62BFF29A52AA529812ACB2AA22B7627DF254B29B72AAA2A6B28AA26D527CD27D027BE288B27A426802850291C29D42991289E285D29E22A022BFE28A625B02A152BB22D982CC328D226BB255127BD2A872A0F289F25DE2442268F2BDA2A982B592B3C2C8332F832212DFE2AAD29F22A622BA4277E274025E025E6271729F62AF92B602A4B2B952ACE29A42A96286B26CA2665281B2A5F291C29112787283628B127062711266C268D274829C727A3261E29632A41284029902BB2297229B42ABF2AA62B152B542A60276A26DB28D7292029F3269526C324F5240B28AA29442B8F2D1A2DCB334B30272CB028CC298E2B512C9329CB27A3243825FB27F428492B172BF52B3E2A332ABE29132ABE27E7256D257728E7294229422986258C26C72529255E25C324E9232F26EA278C29AF2A612A6628B3269F27D8275826AD28B32AB42B592CD62A312AE02589261927DF27F427032566241126D126ED27FC27122AD52B4D2BC2343E313C2A8B28BA28212CD22B1F2A312711263E252726B228D72A7F2CE12B2C2BAD28312AE428F127F32506243E288C2745285B281E27B2264326E0268B24D6234025F32698281E2A452B362C572BAC251727E926FA271D2BE92CD32A7A2C902AA52A762778263C267F2941291526DB2456263028192AD12A292A432AD82B49347C30B32A9628E5289B2A6D2BD829FA278C263F26FA25B82683286F2B392BD72AB3296829E3295A287E25ED233A25AE268528712A32274B2660276928C7269C269B267F263C283A29642AE5295428C925FF2574274D2A7B29402B322C722A112C1E2B0529CF277D246B27D4286B26D6262F26FC285F2B0C2AE72AFC2AF42AC2345031FB2B85293E2A092CE82A0A281027F1266425EC2605278C27D528602BFD29EE279D2A132BE027DE259824F8252D28AD27F1296F299527222ADF29302AA42A532A2B2917295727012982273A259123DD268E2858294D29332A30288729AA2AF329ED28D926D4263B28E6248225DE259127552899296A2A852A1129FE2B3D36BB32242C1E2C252C482D0B2C572A3528B925DA26AD250A2701258B27C829FD29812A152C94294F29CC25B52364246827C828E8286F27CD277728742B2C2C822AC22B3F2A74276E25F22723268424F424E625CC2A6E29EA2717273C268329162AD5299E27A32425253F27C525FC25B026FE27ED28162ACC281C28D727D6291F355D31222CE92D5E2CF32BAA2957299A29522809280F2996271C253F25B826CD29B82BA52CB62AB1291C279625AC278A2B642AB9292027CA265729D229702BF42BDB2A38298F2846271B2765246223A1254B25FB2A062A652706281A27D0260427B427CC272C28B02490261126562694259A26AA2865292329E929F82A782D73360832592B4C2AD62B1F2A99294B2AD92A2C2B182A54297929AE262B25C125F2289D2ACE2B352C3729B0278C26FE291D2C0E2CB2296128C1260D29372A552BA32B97290C2A5F29FD24EF256923202449245A25F8281B2AE22A652A462A842BF529CC2A662BEE29C827CE250C26E62536269D257E28DE29E328F328D72AE82C8534BA31502ACF289A270C2877271C2B372BBF2B7F2BF82A0C2AF426EA2365269F27A8286C2AFF28732824263D27992B6B2D6E2B632A04286328E928BC283A2A0C2A2D290E27A226C5244425E8231626852527261529B829832A002C8929A52BC02BE72C942BFA2951261F28ED26A5260D287A27422754284B28D926A029562DD133C731F32D9D2B4A2A2F299727E92B302B122D2B2BF9277F28A028BD25C624C126DB272A29F427B8263F26D326BE296C2DE02B2B2BA629F1280828042A112A9F29C0294F297B273224A226B92600272F26FF284B2A9C2A4F2A392A2F2AB82CC42C3E2D352CC129032A0D291228092825278D27C929142ADA26D525FA25DD287933FB31AD2E562C182919280027C228512A6329DA281428DA285B286A25DD2401268D26C025BB2894261C2737262729552AC329142B1726B82582274B297B28452A822BDF2B6228DA22C9240725B829D1279A27372926290628EB280A2BA72B772C522B4F2C722C0929772A1D2A3E2A57285529CA2727284726112515268326BC311A329A2E212B9629EA275F2681270C2A8B2A62281C29C22A6E28E625E7254225952468272A29462A71273325BD25572777291E2BE427A6250D2771285A29892B4A2B662BBD280B2431240427F4282C28F128C2299329A32A2C291F27112A8E2BBB2DD02A8A2B2F2BFA29BE28BD28C0279825AB28D62704252525B125A5276D301E31D62D432A6929192810282027FB29312A2928F4263C2801285226E8247E25BB248427C929CA2CFA28B125B727B627452AC429C72767258A244F2638272629CD29A72A7129DD274726CA26DA27A9283929232A552A592A1E2979270427DB27FA29012A302A7E2CEC2AAF283E263026F526D2275A27DA2557255125B0264F2FDA33C52BE228AD279928A1271D2AF0299829C42BEF2851280C27AB245C25F2236F246E284928652A20297526A1258C275828AD28C526242355244925E3254E282C288B27B4263C27EA267D2798264E268028EE2AD02A6129FF29FB2638260A273C270F26DB26052773293F28D1250A268A25A22729286526A727CF25F025AA309F317D2D242A1E2ABC28602A6B2B5D2C192CFC2AD32BBB292B291D296426A525922464271B299F2A3B282B277D27BB2612282B2A652894256B27CC25572875269B28F5275C28EA2923283027D4263E25D52616299D296029C928A3266D252426D626C82529253426FB271F28F126C126FE248D27822871290C2A2A299A2950328830E22B2B2CB829232B8B2C862BDF2C782BBC2A262A242B262B252A0729A427C224A3263928612ACF2AFC271F26AC261A2BD02A352A79283F27D2281929C1272B27E2273029B8288A274929F426C823E9258629DE29F02A2D29D8277728FE270A259B249C258027702732284026B126CA25A2254C28F0298B29592B0A2CD132A02FB42C482C312E842ED52D982B462E6B2D342C5C29EA2A2F2C0C2BC12A82286825222518288C2A9729D6280F2687270428722BB52AAD296D2AE72A9E291E2AE22716257927A027F426F62774271B24C926E228FA275F26E627FF27E2285F28A928F226A62545270E296A2AE3289C26A1265926732840293D2A0B2ABC2A2634AC2F832AC12AB12D852C062EFC2BF32B482DA92CBC2B622BA82C702A962AC42745252825F7264129C52BEC28892823277528462B8D2AC42BCC2AC02A842811268926A6262125BD25CE25932673244223EC25232541271A266628DE2AF62AFA2A902B6E296B29A0281E2BA82BC529F52817269626CB262E263C286A29892A9C341530932CDF2B892A012C582A192C192BF62AAC2B6E2B652B292A452CAE2BF4291F278525B3268F29312B502B3F29FB28852AEC2B792B6D2CCE2AA7286A270F264827A0273A27EE25D5250A261D254524F025BC26CA260D2794283D2A7B2BF229802B052ADC264527AF2AFC2B67277C26B624B0263E278226F0278E286C2BB135AB303A2D1F2DE42C602CB72BB32AD629612BB72C3F2B512C3D2C762B6A2AA52916275E2704278627EB285229782C7C29D9278028C729FC282829582862264027F5268B27CA285B27B4262826A9256D24A0269D255327FD29302B572ACD2B7D2B0A2B202A63290826F528C928B82775273E258A271029282996289E294E2CDE3306333C2F2C2EFB2DD22C7F2B842B7A2A832DA92D742B5C2B6E2AA32BB32BEA29082977263927D126E029432AEA28DC267C28212AE829312B5F2914285A267627D6270D2A182BE228D329E128E3260A2678260027BE26B1287729E62A0C2B182D382CA32AF328FD273A281A28862684264E2643270029CB2984291B29442A19356832472F8B2DB32E712C9D2A302B452C6C2CBD2A6E2AE129B929A92B942BA92992291A287B293D28262759280E28362810283D29522BF82A17287C26D72579260529B02A532A552999287C2A5128DB265526662598251128D529AF2B8F2D812C2B2B42292C292227F228BF27CA2651257725A628BE2A562B02290A2AC62A1835DB30882E022D0F2B3F2CCB2B9F2B362D892D0D2C1D2AD42ABC2A7D2A592BC92B752A9F2AFA291628F626F62ACA29C62AA42A4D2AB5299E29F827DB2655260928F12A452BF32AA529FC26DA26A62709272F2700265125F4258C289628582A4F2A7E29A328DB25A82470268C2810261C253B25DC29E52AC4297B2BD029382DEE352B31BB2CBD2BD72BFA2A232BB12CCD2BAE2B6C2A9128D8270F299129432A112AA6287827C628B127732747295D290B28B82776291329A02671268424CE256027E329F32A442AE8270C26422711280B27E526E42637258624E726AC26CC2680260F272525CB2478222F2577259224AB240F25E328C72A432AE929FA2A562D23350A32CD2DA92C552A562CB92BED2B7D2DDF2A222A05289627DB279226D925B326CE269D27FE27A02768256E268629AA280727D22786271A251825642398244628E029D82A322935261B267D268927CA27F8277E27FC23A423E62337252C26BA24F8244D2456247D235624E0235225F8243C25E62762297827AC27A6293D2A7E340D319D2C272DEC2AC32A262B2E2C792C592CC32AE0271127AE26EB278227522639277327F225F8258024F4244D2680255A28C2285F270925162452240A24CC268F29BA29EB281127CA2594265A2714298F299529FF25E1230B257225BD2448257B2429254725732442252B245C230E2449269C27F5285929D1274E286D2951341D31212E3B2E7E2CA62C562B132C9B2B502D762A7029562A202AC4288128102A8029D8287C25B225D423732318267926F827992AE728DA25C725492326234224E928B829ED274F2694254C278A261829292CC629EE27CE241427F126FD264A2869264A2636279F265527962563221724A726F126112A8D285B288829832BF333942F412DA12B112DBB2C062B292CB52C172D802B922B322A992AE5284A284E2A002B792A2029FA263424FA23E223E125D52613280829E32842281E26B22481254C2841291328D72560258926D3266B26172BCD2C5C29F626672816284F28B32A682B75291F2AB229BC291D26362469243E260B284629022876289B296C2CC234B52F262C342CA529FC2BCB2ABE2C892D902B782B762B482B192A40295D2B152B7A2B5F2A382DB62835269B22452350247824442565270528D7268A248A22F323D4258A2595241E2557248C236A269B26892B752A2A29AC2755285D29CD2A062BFF298829F92BFE2A6A2B922705251123572656292D2C452A9829B2298E2C36356E30A22B832A502BE9295C2A57298A2C862A122B6B2B272D862AD729A12C792CB92A062B6528EF29C427D221F323B4244025E1243B2464256E26E4247922272321223123F823FD225E22712441257926F329222991281029112AD52985295F2CF22A2C2BE429622AB42A1B271125E4258424D627D12A1B29D028FA28862BF233C930082C162DBD29E12951290A2A672B0C2A892846292B2A7B2BEA29832A9F2BC7284928C4274329F2279624FD23282387237A260B26F0259B259725502316223B236125D42340242F2562239D28ED28852B762AC6288327B5286029542AE72B252C232AC22B902A342CED27792611243026EC27BF2B2B2A1D2A5F29EF28C332BD2E522CBB2A3A2A04272329A829252957290529C429D427F1269C278B2652266E288B282429EF2905276C2501253725D92615299C28E327A5273B25F224E3233C22F622DE2235233325C2266F279F280A2B982B1E2975283F29B82A172BE42C522D8F2AA8296B2A6A2C7A29D02775255426FF272B2A362908282E27C9281433062FAE2A0A2BBA283527E927D329DD2BEA291C27F027B4265C26D7231C25A226C126FD2ADB2B672BC228CB2684246B286529D52A3D2AE72AAD2A60298429622866251D24B6225F244E25ED284B29F6299828C62850279027B0294F29FC290E2B5E2B822A492B1B2CC02C892932272B26FA267928112A612A7729F9282928FF321A2E5A2A0B2AD02A632908292E2AEE29022A4527CC260E27F0257725D726CD28012A9F2BD82A5B2A532865257F264F266E28BA29992AFC29462AB0291B294B2827262522A0213A21B226BB2683286827A726A827F124C325FC27CA29722837293929D128DE28602A352AEE27A327DE2653274D29E42A652AC72916291C2B59355C2F552B1C2D812AEA28FE28042AF22A922A072A6F2A6B2B4E28DF2531286829BD2ABA2BC72BCB282D274024E1248627A029062B912CEB29C529F9282E27302844269622BA223F22A2240C260C27432688262D257B245627332A8F290C291A29C229DE29A027F7284C2A2F283D25C6242D258028BC2A862ADB2ACA2A292CE0340930782A992B292BE6299729DF29F029512BC92B062BDD2A8B29D8272B28A629052BD72BB62BE1283726A32460241F2715282529EF270929F82744275128EF27DF266923D62278232C25D825C125AC2758285425A124C327D2276629692AA628DD29142A1E2ABE29DE29BB28B525C225D4246027FC2AF72B142A8A2A022A4E34A42DF42A462CC72A6529BE282B266E28892A442C082D0B2BDF2BBE29962B362B362C2E2C5A2CC8284B26B02344255C299B274129132801286C256D2661287D27CD26032592229121AD230E256325B5257229D52604251625F5276828FD28B62BCE2AFE2A6D2A3A2AA82B192A3127F724DD244B2528291727FF26BA26872731322E2EF92BAD29632B15291A281B27F9264C2B3A2A202BF02BF02BDF2AB72A832CE62AFD2BCA2B342A1D260B24B8241D289428572872260B26D4261327D926BE276327FE267F237B220E2389247C257425D827D6283B264925072889271629D4281D2B2629072A3D2A592BC128B427EA23DC252C24DE26A329FC27DB262E276730A82DF62AA42907292B279526B725D126A629CA2A842BEC2A0E2B622739288128182AD42C022A332AB3252D238624FB245827EA2692265023EF260A264D2A2A2A002AB72717266523B5249C23A524F2250A2755274C26622454274626BC285D28262AF72A832CF42B1F2D202927284B25FC23BC235C266F285129082853271732932FA42A4C2872271A2712279E247026D128EB299A2BD22ABC28A82896299229512A2D2CB82A4B2A4A250823F2231926BA267028E925162417254D27522730294E29E925AA251F26FE235A257E253F23F927F528A026F222B825AB26CD296229802AFF2A312CAB2A0F2CB52AAA2756246925C7231026C0256D2713255B262B315A2E8629CE282A273A2537252C2681255F286C292A2A042B0F2AFA2733296B287F285729422A03285C242A243C24FE24622512289A26C9247525E527A0279626B626E22590270627C1262F26682474231D2423263226C6238523BB24EF26D327D428292B7D2B0B294128D02645258225D923C923FB246124EA26CE265E27FD32E42DC129312A0929AD2721273124EB25DC282F297F2A492A962ADB28D1276628A32725288C2A402971246A220323CB23FB235627F2256127E3260926A32826271B261D272B29EE27CB282628AD2451240C2423269226E126042682240A249B24FC27C829E22A5C282C271B2634269E25142346230025DB248127E0267A276131162FE02AD629E22B3B2B2729AD276E27D529DC28AA280E2B142A4A2AD028A7272C28A1288A295629E42591239C244A244B23A42675271927E4272F2A632BC12AC429A0283327662876297A292E28C325EF2568267527452789267B259B2354259B263A28D3288C282E2863285B287B298B255D2405253D26C727C5272929E332642D902AD82B012A3C2A7F294927F225FB277F29242AC52B772A5A2A64287728EF26BB283E2B4F29782713245B24A4222C232B26B026F628DD29D6281B296C282B28E6259726E12632270B28FF2738269D250F2554256329A229962633247B246F2673267A28F3284929FD282A2900283E26F625712333268727FA268226B032D62FD32BE8297B290B299E277C2632267327FD274C272729CC297029E728AB243426AB28302A3B2B71292726E2233522AD235626FC28AA28B62911282929A02AC729A52701279724EE263B28CD27EF25FF252A2650264C28E128F1279025CB25BD25FE254F262A279C29F229FC2A782BA326BD244125E4230E253228972743339F2E2B2B842ACF29C028A027B124F8261824B325BD26CB27B428182AA228FA2473255529252A592B6427552482230523AF220326A028C128E9298E281B298F29F1290E28FC25A524A52681282328A026A724A5256F25FB25B52665273727C825E72541255B255725EB281028B829A628F925C025F7247F245326C7253227D130592EED287E29822BD629DE28EA256D2513251D24DE26D42669261E28E5260C2677272727DD2A882CE828902447241E24C32139258C27F3289C287E264D27ED26F427982700282C251326D927CF2841261B25F9252223E4237D264D266E278A263524C2238724E624E6263528BB27D0286827AE23BC235923F822562460259D2E372E802AB6287429E12B4E29EB285826A425F625E1242025F326E3259B24C52652276728C7290D2C4D28E92309242624F0221B24ED27A82A6B2998283C26D724952761277725AE2356276B263B285325B225AC24BC227F23462564231D27FC23C02664260E267424C525AF252127372960279C23E1236D237024642412261F318A2FE5297D28FA275E2AA229C72AD7271826E6243125E623DE24D8239C24C72499257E2600270529C1277224562373242524FE230726BD25E4282C2764272925982452245D25C4246323B327A526EF24A3246224852264249524E6231B231A246C24BC241A275E258A26562522258F252A26C3249324E3238223A5230525FB303C2EB229012A5727B426A827BA298429F228602580244E24EB23C125C22592236B269B26CE276529C228B124BF235622D02284240E240B259329D82913288525782522269924E6239A2350266825C8254B2519254F234E25D4260B25CD2441222525192635279329D5284326C2245B2601259224092475212D24442468267A2F1B2DD2280B28BD26B625E4266129A52A6329B125C824FF2480233A23B5240A263B2680254E266928892863252E2395225723B9229A2227254325362834273E27FC25FF24EA254A246424BD2685266A25FE24C2232325B126C827F2264E24B3258A247F234B26F8295D2BE226AF24B124052532240225F422E623C024CD25512F512D752952268F256B245624A3273B2869281F25E5241422A02252227523E523DD258D25D9263628C2293E25E02370251124BB2376238123602583250F28D528D92689258F26C2237B253226EF2533260B252A25FF261E27412A80281B254C232025562412246E26D4293E295F25F124622498264427AB24B0230925C8260330762D3D2815274C25A0251025BC2472260628B32519257722F422B621CE223523552373259E25A22700275825A8239123B9220722E0215024E324DA24F72577260D27E726A9255A257F276327C726DE25C12337243023FB267D299228B024F723A323CD22DE2484277F29F1261F242C24182728260527652418231822172632317E2C952656254B25A0244D238723CD259224A525FB231D22A2219920592171230F243C243327CF28E928BA2579250923DB21B222E9234A240C25AD2433241126C3251427FE243826DF25F127FC253E25D8230A2308223A2422282A262725F523E1225223452464261828A32769268B2220241E27C3279D26E724EB24FE25EB2ED52C6B272624F724B9244323E5233F24222598265A252222E121D421C0220C24BA24502646272328C5299E260726E321F822592385216D2462247B267426842769263F25FD25F5262826D628F827A4257A26CB240A210D25FF25D5269525B9257F233621DC24E1253E26ED261C24E423F3246327692813250726F9240B27C02FE12CB927A724CB237224FE23362384254224CB235D23D4221021E9208322432493233825B028282771291B28AC25E92256233F2478229523D3236F261C27E1287427F823542510250E26E7281E28A7248A25D9239A23AA22C426602616286B245323DB22452383233327E2244325362500269A2593265B268D257B22C9242E30962D6627C523B3240F241924372361251B251124C323F92347228020B822DF227A241925ED2545259C288D2660250623692236246C22F4204723DF26912679279226BA24CA257124F324A2279D266D2462251425932271230A244B25C8251A24CB2328223A22032203252326DE258525A824DA25F0248D23AC236C253F26DA31602DF326682592221C252725792334242625582461250E249A226721B32297214322EC245824C723E925A124B624EF22DF21FE232423F52141235122582446258C240925C0236C2490253C253925E025192550236D2322215521CD24192557253A238022FA2187212F2434251E26422656234726F82484223A228D243426A62F +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 403F + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 55 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 0009 + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 65289F229522A12011216920A51FF11F5D20DF1F431FB11E8D1E271EC71F311E1B1D631F2E1F2B1D741F251FD920721FEF1F101F38200720751F681F1E21A01F831EBE1E1D1E5E1CD01CA81EF41D691EEB1D7A1F141EC31DDC1E201EA41DDC1E5E2032202420AA1D4A1FD51EB11DB220D51EA0211C21AB20981F21223E23442EBF2BE623B8205A22522286203621CF218921CA1F11214620281FE01F661F191F8D1EB21EE61ED91E491F0A2199215D22E01FA721E01FE520D21F9820E520441F501F8520591F1E20681E68206B1E721E6120B71F791E771E141F3721A720D71F2D20742077212520612156213B20B52092200E221F23A3220723DF22D824F12D642BDD245B231E220221B2203721EB212E20FB2276226E227B20F01F532071201E20D6203A203E1FA91FE621472117211B22632243210421EF205A1D9F1F22218E206321612093202A1FFD1FF71EBC1E2A1FE41FAA1EA61F3A20E51D3420BB205421D0215321502097215921BC215E20E72045210024B9218B228F22A225782F3D2CF0278D24E6214622EA21FC22AC212A2140210C21AD205E213920CA213F1FC220B92099208E228520222180207022FB21D222A12109226D20401FF81D901F2E210321DA21791F551F3720141F621E991FD020842023218920A520EF217222B323E022E22222227E218620332038210A222922C822EA2318226D23F724F92EEF2C34267122C321AE212F220322F6213D22E321AF21D021A5204E20AF21C320841F30217920B7200A217321D7235720D820D023BF20591D2020B51F361F451FCE207E21CA20881E3A1FBE1FBC1E8520A11F7621A51F3720E920B02117212A21CE22E022BC2364212922FA206A2149218B218E210422B120C3219323D723EA2D522B272616232124BB213123CF21BD21F9220B22742201239020F520A1219821AF21E42085206A2231215121CE22EB2298208E2164200E209F1FF11F791FB520B92022217320F01E2721391F8020F21F5F20732040217421CD20E921A5214C2255237123FE2109228A212B224021AC21FB201D221D24C722F8218B24B625BA2E762B73247D233323C0226A2286238E22202202237C22CE21FF216B2186228022D8223A21E62149215D2361233022FC21E11FF71F922055209220F41F83210B214F200B21AE202020A41FBE1FE21F801F00219C229320FE20D5212323FD21CC22AB23BF21C9221E21962131214D21A0211F23AD21C42269211C23C3231B24BB2F442C60260C24ED2497237021ED21DD22B822502391219222552362240022E621D622E52182224723F4225B216722E023FA206820D5200E20FF205521AE2003209F204321492144201420DD2145225C1FEF227821C020F2204F22BA21592413241C24C7224A2201226D2258224F2281236723782350235E222C2429244C267730202BCB26E524AC233A2495222221F32208244E23862229224C22CD21BD22F42003211C2250226122A5223121A71FFB21FD1F5221BF1FC01E171FD120CB1F1220B42077214921C2201720B81F681F7120D021F1209822F3212B268A25F324BE246C242A22BF23DB223024E622B522E724E825A8249624BA230823C1240825E730DC2D5125A9237F23AD23AE228023BA22E8237C227924A723C821E022D822E72185223F21492286219121FF21D11F2821941FF0217021AA1F9B21B31F541F6420862060222621A32144200D22F8218320DD21F8224123DA23112519263425332567277223932162231C246B25F22615275327D227AB26C225A523AE25B8268933462CDF2803268224152532234A23062380230724DF25B32425242022B822A2224B226B21EB2107227D1F1D21EC1F8B21611F1D1F7C1FA31F59201920621F221FDE21BD214122A22240215921BD222E21C521062154220224D3268626A62558262225FA223B22442327257525E225D028B3286227BA263124E9248A254A26D3337E2D6B27D9249C26B823222295222C228E24F624E5248D24382396223F22C3229C21542106214F238B20D621701FC01F50205F20BE208520911F051F5F202C203C2022221422D122F922E322E2237523CF23D021D62261248825FD2418245125BF24E1232E24B82279269A27D627022A3129BE27FC25CD24702318232D270632522FB829C9264E258724CD23482326228324B424EF24022798244B25AA234524FC226A216D2032228120B520B41FDE22EC212322B2211821B521B022502119223121F92388251D2467234E250E28482565233424992220238C2508262A24452667240F24B621A0221525C72663277628E3275C27652801253224892450273B2F752EB2293627AD26E926A023E9231C244E2500266825AC25AB2496236924AF21B422232243214D21DA207820D4202A22C6224923F321C021D1239721EC20EE1F09238C24DE25DA250B2788270F291D275C27DA2580232923A5237325E425A126B426D6220B235F22EE24F0231E2481262B28B029E1286B25EE24F324B3262332FB2E9D29C2282C262127D02495253423BB25A623F325AD26F223A723D4222723B52001232023EC20F91FE51FF920DF226023122295218F23612330222C223222D924B125BF277B2827280A286B2860281F267E258224D2236A25E926B2255D26422669253024C522F224CB24DB25D427DC29F128E9285F24EF23C12412263C3065308E2BA1280B260D269F268F255B2544235D248225BB24D122D623C623DE234221B222902110218E222523022103224621E622B5236B238F23D5233A23EE22B424D6252128C0286228892B222ACF288B27A522D2221524BB26A1276A286128C427DA230224AE22BF24332767269529B9291A2AF8284A274D2468242F27E830272EFF2AE02905296F2678263A269825F223D12365258D24BA238723D02283222E2248219321F7204622E72012225D233823FD24E8244023C6245C259A24E8246A26E729492C5D2BFC29AD2834289E27E1251424B723CD251B2A422ACB29552A62283F24DA232622BE238825C1270727642A622AD629162899252E25E1271A32D82E65290A281E25A6262F289D273527BC25FC2468241B249D2351236021AF214C21D621B9220B21FF202A2242223E2355237A261A278F26DA254624B8259D259B28332AEC2A872A0B2C4F2B6929C5265125A326B9254125E1296329492870275628AC23AA237121C724C524D129972A342B982AA52899269225B1244E26E032CF2C532A4E26682604279427FA287027832512244A258322B222A0229123CF213C22A42416226024EA226921B423B3255D25F428FC28BD2828270C25FC251526A8279A29832B28299C2ACE2981292A25E8253F25B226552714271426652685265F26F2251326DC22A2258925E525C0253E27DC2620267824C725AA24322785315B2F7128532591259925772615262126C123E322A123BD23502378211B2414248323A2200A23442356215921CC215324D62751295628CD26042A2F27C5265224672638282728382828270E26FA2339247525EB26572775270B291F27FA26C0270B280B26FA2410264F288B2521251C27A625502501255123EE23AF258026E830C82F392A4829FA2799266E2643242D241924FE233F25D523CE22B5220C230E248A22AD2495229F21AF2168230223742453273E285329CF2801290429C9277B270B27AC276526B22626243C231A2517236026BB28C229702AFB273928B5261A274827942412252E26A5295B29F4271B29D4256B254A2671229624A025D6260D321B310F2C5D2C422918280827BD25BB242625B324F222A6251825CD252F245C247E24FB2160245A22602246227D23E6233624CE26BA250C27AA293329C6295F27D8263627D22540257524A02341236325B428292AAF296929A52830275125EE25CB25DA25A2244627EA2932299D28C1259F24642632241D251D25CA25F92820338031C72C8B2B402B652AF1285F266E2410259B244626EE265B26B124EC267F246D241823F422A6206020D920702139226B22142418243A26FA27C626BE27E126AF248125972543255D2302267725F4259A2A0F2C442BF328BB2754269C255626C425912581277A27EB2A5D2CD629E22741264F25852527250725EA262E291C354E31322C2E2CC72AC42909265225912517240824D72510279B24F225E6273D25E6240E25A3229922B9219F21FB22CE23EC23B3237D2386260827792526261D2548256A26FB24C7228C246E24EC255028DB2A4F2BD82B53294E283326312525264823E92372253F27112A33290429EF28FC2818276C26B72549268026452A0B354030722A0A29C2282E28062604240A2451261625D6270527EA25EB253A25C025892586241C2486250122D620C3222D2372233B255A26C426AF2582255A2430256E24DC25BA25F824F5230D23A925BD25E527AA28F9281A28C627452549242E2416253F235D245E278C27B227A2269B295E2AB1287F27C225EF26CB27442709324A2E84287326F627CD250E2504255524442589249F251726AE254727CC27B2275628172514255123AF22DB23A721C6239B24D426852889271B2880261D27DC2513263A25F925BC242C2258231624982335273F277C2544245026052413230E249F2259237E2503265E2931282C285A2A692B4428D325BE23CC246F24712772317A2E412821256024DE25AF256725B825FB25872589268C26B2251028792864283D29E7286924D524E12200224322E82498255F27032A2A2992288827EC27EB251D262425C9251C238824C322C5233125FC24DD26B625C124B824B4258523F824CC22F6228B25D6272E2950280229382A9E2AE5286626AB243123B3240F26F230512E6F295C25E52573252126002773287428C92774285C26F0296729902AC12A88297329B02627253523CE222A239B245C26A027D6276C286C270227EC26C727C227CE2620250323F92370222924FA2397258625EA246625B724B825CB232B239F24582385249A281F2A932ABE2AE22AB82A47288B26D5241E253A25452855328B2F98286127A82665268426D4287A29B22B982B57297829D228F02A942A8B2B722B47299628F624D723A02300239B23D6244E281E2832272027E226FC254D2803297328F7262923CD223B232322EC23FF256D25A025122546261824DF24F8249323A123ED231D28042A782B962B222DB22AD3299B278F242525B12713281832AB309E29102504259E25A029632A182B622DDF2BF82D512BB32AE129962A302B8D2B912A91294A29A324E8234E22FA24922312261528EF26F0258D253C26A8275228DD278C267724E22231237423F824EE26F225D6264D24752624260126E724A2240125B4266628792A312C4E2BE42A0B2925279E26E1245825D8271828CB327D2F9A28C525DA25AE284228DF28D92B8B2CA32E482BAD2B4B29E1294A29CF271F292E2AD72ACB27AC23B3218C2312230123532428267A245E25D02564262D27D32706283A26B423A22296219623CD24BE259D270324B1248A269C277E259C24812383269627C026E22A842CE22C282B2528D6245524FF2374265527C62722314A2F752AFD26AC28EA293928E5272E285B2C972EAE2EA52B55285D283A2BBE2CB32B162CE42A4C29B4237F225A2106212022A9248B254E258C261825AE27DB274D2B422A8E281323A022C824F6252625E226F0274F25FF25F92689275B27EB24BF250B27892727298E299D28E328BE282D27D12626242D24F723EB26E1277431942E9B271126AF26AC287B288828E3275928FB29062A84285C280C2814285129052BF129C02AFF26A3237222202161208521A624262638281A28F0272B277E29822A922A5627CC2397228E26372783288829592A61281D26F5257B255C257723F825832719283B28AB29D62878273F2638269F241325A6232425AE2654279931732EB128E62674286C2847296B260A263927C32650262F2723287C290A2A9329A8286429CC276D255224B421EC226A212322C124DA273427F929C02B4B2A842AF92B0E2A99267F23DD257326AC283629002CB92B9D27B926F325462494234F238224E526FD288A29EE28D125FC24A62421268E258F23FD2426288028B828E133062ED22748260629962AE827A927C925E4270426C426B527AC2AEC2A662B072A562908290F2765251C26AB2380227E234323F7243829AD29862AB32B962BAD2B112A222A002619240D275629E52A64295E2B372B5929E4286826202521257E2207255325AC251A269D265E2751267726E124EC24C326D4278B297B2C772B2735BD2C5528E429922A8B2BE12906299E27DD275327E82786290B2B1A2B2F2B0B2B172C232A212790269C230723E423262361228424B22875289129DB2A4F2AC02AC0289F26F5265123FD24A52A2E2C4F295129412C8B2B0F292E28B8269A24F6248324EC248B26B2277E27F3259823B42405257F2590268E27FB297D2CDA2B7936CB2E8B29A52BC82CE02BE5286227DC29E92AF02A612A8F2869287228A42A7C2CCA2C862C6429442821251B23EF21D923B422C123BE2887287F28C629CB288D293029F3281526B42277235B28E0280429542AB22A1D2A0D29222803244C241B249C2740256D27BA278B252E241E242525E524FE24E0264A27AB29032AC52A9634B72FB02A692A462BBB2B372AF92A7F2C372D6D2B4629DE2574256D2A402BE12B6F2DAC2B282A40282D26792155224623A72240239D26AA27D42724273A27B7292B2A0E29F82579224723CE249C27C226FB28AF2744297C279D254F251324EF2373266428CB276B26C726CC2312244B243424F622B0263826C7266627CE2875334430B92ABB2A042C252B1C2AC92A9D2BD92E492A1D28ED26A6275C297B2C712C2D2E5A2CA12AF629FD260825D12436225A246A244C25B2283F277526CC279D2857290029ED265C2268223424A6253C2739283926EB263D26C125CA23AC23C7256F27C9288C29B328A52629252C241B245E239E23BC26B624F126DE267628E4305B2F5F2AB02B6D2B592AAE2A9B2B472C2E2C492A6D29412812272D285A296E2A962A2A2C452B602AEB29182634248824FD22122485255727AE27AD264028E5278A28F326182574236523332576266B27D3287D2863276325B926E3233C23F723FB25C228962A5C2A7829B625E123FF23F8247925BD286C287B289C271228C931562E582B612B222D1D2CA72B4F2CAA2C572D2A2A83264A258227232BD02B3F2AAA2BF12BEB287A2BA1286F26FF24FD222E2389243B245D26EA28AF2551273229A12902285026A524D323AA254A26B2286B2ACF2A6729B926E324BA238C24F125D1253E286C2A5D2B7F2A5A27B72487245624FA252C28E3284C297928932ACA32AF2ED4286C27712A1928E727E428392AFC296E273C260827B127DA28CA2A652A4D2AE62A5A2BD62A0F2A31274C25122544244123E524B825232612264526AA297729F629D02A1F269C240C262928E428C428A2282628DB26B726D524DA247125ED27D727B027AB2855295526DA24C72428258827122B902A2E2CA32C182CE3314B2F3229AD287D27D4298C25D9264E27B025A324C0245B26EE27DF287D2AAF2A6F2A0F2A3B2B5B2A0F297126EF233424A8234C236324BA26D525FA24852645285829132ACE2BBE299C273E2513269626B9271626C8268823CC2466251027A52605277927DD2665270F280A26D92473258A26BA29982C1C2DCE2BD9299A2C0F34CF2EBB29E6288028C127D0279E255A265E25F5245125F4263F28AF2AD329482A37299E2A7E2AE9294727F4256F25A424E321B9258725792338262124FD2482286C29DE2AEC2AE2284B27AC255326E6245C25F324F723A9233825DE250028852905282A26F2266427F0267525A5245C262228F429502C3F2A79285E29862C693547304F2CA02B022B272BC12A7B27F4253827A8240C25A02660277228A62BA22B062A9C2BC329FD29F82AD228C726D924D72210220823CF2440259125D8265B261D26C4279A283B2712287C273725102598239C25EB23ED24322310263227E02734288F286429B42971297824D724BB278C287D29BF2A562A542A2C2AE72B43344430852C312CFD2CDF2C802B902ACC2A7B28172761257D269328662AA52B902A582A5D2B392BAC2BCF28D627E7269224972434232923D3240027662507281227BD26B82630279F26DA264125ED25BB23F222172425236424562696244727CC270528AA28AC291828AE283F2634258C287C29FF29422B5929C0294F2C102C8634902F262D5A2CC02B122D1D2DFD2C502B152AA826CF25052649279D2870297529A029162B5327112767260825E724AF245323FC228B230C273029FC29B82B152BE5287125AB255726352690262A27232390224122D1240F266A25D727832741276A265A294E2815279A269E24BD245229FF29F42712280F271E28BC288E2AC23427303A2B532BF12AB229302CD82A632BF229B9273525CA2690277C287827E9276628B62661263826C424BB2384252C240822802232248326E4287D2B602BE5297E28CC252B278A26EB26412756253523CF2278248B24A92533272C27C5276A28F028CC2A5D290526AF2409248B25D026A326DB26B126302692269627E6282233F931CB2C172A20290529A32A832B7A29E427F425C726F8258F262927F92791262D292B26A3269025FC2335252A2490253F248822462222251029E728CC29E228DB27CD26AF292A2870277D259F24E5223A233026B42542277A285428A1275F29612A1028CA261A2467247324822420277927332668258425232874299A2B13335431242D582A072A6D2A74290C28812852289E25E92446269B26EC279128222AFE28022917294F293D278824DD237D228F23482492246326472761273D28A02723281B281A2858272A2537248B23CF2374233027AD288827FC281B2975279A266725C4275924F0230323A1237B2460269E270228852678259428F528692A25349031962CEC2B312A7C2AB32A8A2CBD2AB7283E25EF2569278129E629922A6E298F2BA42E782B852B8326182655248F241721F2209E239424F125DE27B9276726DF254B278E262D25DA2388222D234B2422273C27EA29CC2B6B2BF32AA6271C273926F026D42342225623BB235825C9276C2754266E268C26622A8C28FC29E431D730B92DCD2C272CC12AD02BAA2B5929BD2839269A26BC26B228FB2AAA2B052A1429922942291D2BED2667255F26AB258E21EB2287222B243A27F924DB25EB25992792299D27B424EA22992309240B25D726D32876294629912835278325AF24F024B424D423E0207D248D23342400264E269F25AB24DB23C42557274927D830BE30FB2C5C2F9C2CA42CA02B462A442782272327A526F527022BA62BE82C942A7A28C428AE26BC284A2867273F28B3253224C7200323CC235A267725D826C82654286E2BC42A30282E26A024FD251B272F27E2278C269027DE28ED265725A6256A258B25D62324230022EA226125AD255624CA23E0252024722507255326B5327E330B2D282DCD2BA02B532A2429A529DB26922473265E26BB263D283F29AB2A1D271A27C3271128AF25D927492982285724E922BB210423D1231825A2278128F12B7B2CD52A1C29D5269F26CD256726FC271F27CC25642759263F27F4258526F827D7250424DD23F524AB236025FA2729256B2363250424D025D2253B260C309F32F02E622C672D9F2D732C2F2BA22A7D27DF26D42738295D264828D62698260725DF2468266B276F290E28CE294129CD24F823FC25022327252E27AB289329B82B222B1F2A202BD129AC2837273026D227E527F92547261C27A929DC287028252A3A29EC279326562621268826EF27BD2606268B281927952789275B260A2FAA32412E3B2E082DD02A5C2D632B822BDE2AA9282D29CF2ABC2A7527FE25CA235A25B5247B25F52874288128BD2780283D25A6242323ED2427254128C12AA42A5A2AA62B4C298C2B8A2AF5281825F124C62577253E235F2488266B29712A8529B329C429412A0F26D127F327A12799298028EE26772AFB29C628912881288130FF30A42DA82DB12D6A2B352CC22B9A2C5129D127112B0C29432AF028E0276825AF2568258326D6289E2A3A275F290C29C72878260F24F4225A24C526572A152B572A9B2BDB2A9B2B9A2A7F279D2798243F248925F723D524EF27C329B62AE9299B2939290529142708277B28982B382BA52738259A29DE293D2BBA284828A931E731AD2BFB2C6B2C092D5F2A4D2CB629A229292B6E2BAA2985296228F3279D25062643265127A22A8F2A85296028A5297629AF262025AE2391239C2400280D299F2AA72B702BA82A9C29C42626266123D924E624AD25E624F2283129D629B52A2A2A322A9629AC2573278E28A52AE72AAB281629B62A502BB82CBF28C428AF3252306C2C472D2C2D4D2C092C4D2AD229EA29662A592B8429D1291227E327AA268A26262763296A2C5C2BE029582A8C2AA32954276126952301251C25B128592AF62A3D2C212DA32A282BAD2A87270524752461244E25AD27482820295B2A312ADC290E2914287525F8274E29152C712C3D295F2A9E287227A62810276D294F36CA2F0B2C6A2ECA2DD62CB52BB7286927D827C6275C28AD26142748277E28D426D4278A297E2B212D2B2B1F2AA4280629212B0E28B7237E217D23EE2586271D2B162BEA2CF32B332CFC2B1D2CE5298D26F9248723AA275D28EB28A528A52753297429F726E426E324CF288229D728AE2A642A7B28C82546252B25D7266A29B9340230FF2BEA2CBF2CEE2B232A4026332609260C26A727EE26C927C4265A27D02AB42B212C952CC72C662BA72BEC28FD2743265524DF232723CC21EE25D12526276728042AD02AC52B3F2C642B2829AF254A248F257026A8298228D1263F28A128592930273825BF2575297F2A4B2A61292C2973287926DF22542479263629B933622EB12B8D2ABD2B152A2C281826B1253E277F260028D7264A29F42A122C312A3C2B042B802BF32B2F2A452B7B294E27CD24C42470222222352295245726D127DA28BE28342A552C2C2B052BA62BEF26FC24F5249B27AC29EF2A762726267A27232A0A2A2329CE26AD29822AB5292B2A1F29F22881287326E925142576281F32FD2EB52ABA2B32299F29AE283D2675245F26BE28812AA32A4A2A642CA12AB82B79297F29D528372A15287C2902290F29A726352699224D210C238C254827152973289027602AE828AF29982A722A70260726D725CE25A026C8292F262E255E27AB29B829AF27BA2786287A29522936293C2972273C2BF7254C257228BC298333E531322950280A279528F2291D2704267028112A7A2DCF2C1F2B742A4D2AC626F9272A293B2A8D29582B882A5129762B6B2AB1265A25F4225723B8246028F329462BC72A8E280F28BA2759284829312669252E277E271A27212759267724762619277928C925F7256528C426ED264F2AF128CF27A7282E26B8258929932997354F32652C6A2772265527D32565261E264229452C412C062CD629DC28F1277B2789265628492ADB2CDE2A532CD72B712A1229D128EC255724D724CE25D1273B2A132CD52A4A2867257D26C827C9257425B7261C26CC284C286C28DB2479246625D6276C254E25A827B6270126AA25CC280529F929A027FA25A8253C290D2CCF359231532D992A3D2A3B26DB25D626CB26B4281128E62C4C2A772B9A298E29FA268627E727A82ABB2AAB29772ADB29B32B8029EC277C268B239224BC2550260C29112B5B2BED2771256C27CA25E3234A227B23572578274528C12771268526DC25CC280F2857278326F025F8250D26EE295C291729F1274F2792276628152B41352E2F9B2C042BB52995295F26CB23E7268126AD28C3290729692BF12A052CF029682809285827C028DA276928152BB12B2B29C427D525F92424246424EF264F29682A282A2C2A4F2872259D25DC23E620C7201825A9236726FA28942807287A28F7277428E328BE26C42895269028682BE52B0F2B93296E282D28C22BF22C0D35172F942BE02B212B86280026D423D8266A270D29772BCD2C642A402B282B0D2B562AAF2806260528CD272227602A9629372A1E2BD72909251F2406265B2793275D29E7283327FA257D2637261625E9238A238E249C25CF26B7261726C02659252327A929F228532744263B25462AD52BF92B792C9D2C7C292529A92B3E2D7F33FF2F4B2BE82AAE2811287328B12600278229132B3A2CF62AB52B6D2B702B552C652A5F299B268B2850297F28E427D0296A2962290728AC258F2509263128D827BB294E28E5241E26DD26A3251E2640237B24F626DD2692290729AA25462656263927BC27F82596269524062751274F2A782C9D2C492BE92ACA2B422B762CA934E92FCE2B102ABB28AE290F2B752916273329262A232C572CFC2A1B2BFA2CA62A8F2AD72A7427F9260D291C28FA276D27E327AB2726275924032538265D28A32ABF2AB828BA26FC238B264B2881276724AA253A267427A627762A7B280426C927EF279527B3279E25942570246C26602A562B262D1F2C8B2B882A6F2BCE2C30340230432BEA2A5C2B3D2B202CD22A882A9D2A852A69292D2BF327EA270E2BAB2BA32CCA2996289028D329A52A842939289C261026512528232E2516250F2703287F296D2795268324E4262D28C028B426D526FA25EB275228A42A40280027E3271F2816289F2747258E256824D426DA279028592A6E2A232B542A7A2ACA2B423560314B2A2A297D2AF42B0D2DC22AA52ABC2ABF290D289D28EC254626A927542AD42A682AA8299D28522AB12A8E2A6A29EE269824B0231424B32333246B232F26C129E628982945268E26322805287226382520260925D127292740286F25B02530289C263E283B26382468245E251B273928FD276E282C29482A4D287229A934512F0329F926A927122AAF2B242DD32B5D2A8E294B2A0F2911274F245926E429D3296529802A062B65298A28E0268C27E625162675246D23E323A5224924D92868296C2AD029F5262427CA25AB254825DC26C825B32790272828C625ED253526BE26962699259825A425B7263428472A5C28E026A32776270929FB27D328AA33C72FF12A25270628E827A328BF2B0B2BA02A8B2ABE2AE82A6128F8278A27B729BB2BFD2BBD2A532B2529972BC9287428D4268C28EF25DB248E246924F2232E27F12864282228EC244F245D251225DB24FC25ED28152AA4299D292429C4282F29EA29F227692640265F285D28FC29D72A9D291B2842270C26C228BF28C5297135FA30102C592A792960299F29D02A6229D32ACE2A252DE02B3F2B2D2BA728BF2B8A2DC92B672B9E2CB12A7829CE2A4E2B8E2962294027BD25ED244122612559278129BB29982941265F24C424DF23342420279C29112AE027E6284A292929322B7F2A652A4E276725412A262A072C5C294326F426CA2764279929172BB82BCB340E30FB2B682AC928CA2AA82BEA29482A6E2BEE2B532D562B682B1E2A6B2A402B852CC62C222C372DE9294A293D285B295B28452A5C2AEF27B8267A24B5255027E128DF29732967281E255D244925C7240F26702A96293028E727CF284D2A5B2A642B4029D7274026A029D92AF828D7274124B725E32796269A28D32A272D1B35FF2FDC2C292A4C29402A082C562C562CC32BCF2B3C2C352B502902285B26DD283129B32B3D2C222BD42A282AED289727AD29C92A602BED29FF27EC260A25E926282A7A29902BBE29DD275B2515259824BD27E829BC29512AEB28C42636290F2B7C2B19290D272A281D296629D22716289925802503261228CB2742293B2C6C34BB31FE2B932AC9281028812AC62B652A9E2BD72AE42BD32943285427C7268127D128072A0F2D192CEB2A35298C27A727D728AB2B672C99290729AA27AE29F028C3285329712A9229F528BD272925D8229C26FC273529542AF529E1295D287328342715288C2847254A281629242790279725C727F1274B26CD261F293A2A8434DE30CD2C512A2729DE277B29AF2B2D2A63286F297528FC27BA27FE26C12599259526FB27082BC42C7B2BE92AEC2824299D2A1E2D8C2A79275328A3285D2981279526F227AB29F6296329DB294225D524CE2588279726D7282E2B2C2B1B2A0E297C2805284D24C72599267328792761276C269829712BD62AC32A042A752AFF337332282DB82C13296B29A22BD72ADC2B322AF528B0287129C52AE5290727F0278524EC28842C332DA02BA628F7289F29D629182B0D296728712886289B2A372ADE290429EF276728EC28D827CE25AD24CE247F26D0268729DF2BBE2CCE2B112AAE28DF2760269926BE28D127F8275428A3271529FA2A882B0E2BA02AE32B3536C8320D2EA32AB529F029E229932A862A9B28E528A929552B962A552BA428E3295628F828562B4A2CE0291D28E126122A86281A2AB229F727B82736289B269A28D729F32A092A2A293F28C0278727FF24DC231D2589254A285D2A7D29DA29B1290428ED27752644286829602793271226ED25002A082CD22889284927872B6F35A033E72E2E2B40299728D728612828291C2AEB29CC2A392D9B2DCE2B142C5B29D2271F29D62B202D1E2BC8289E270A277129432ADC2838287328C225E5246728BE2AF52A512955272B261826C326462403256C2432259B2505298C291629CA2A2F2A9328B327B129092B1E2AAD28BC27A528C329C02C862AB529DD28B22A2C362A33182F212D9229022833280427AD289929EB2A6E2B822CF72C6B2CE62CC4298F29152A6A2C2B2C452BA4277D2529263B252727FE27452614285E25AE26B526182A942B722C4329C62699256727C32598240D2521240223792AC729612B122B102A7D2A76291F29B92AED29CF288F273528392B222B9729A52744273429263330324C2E532A4D292928A02790281129FD2BDF2C9F2BDE2AA22A152BF32AE82988273C289728B42A972AD7268125BB254D271028BB28B5267C273A26A127D0287D2A5B2CE42C852A8A286A2622291F27C024A124E121AA2453264A290F2B432B5E2A872A152943296E293C28FB261C27E7276E2A4A2A7B27D92668274828CE315B33D22B852A01291626D825C126922A5F2B622DA42B59292D2828293F2A0D296F2729260628E4298129B1263027DB27662AC929A529DC265C276D254F267428212A5F2CEF29042A78285D28D9282F29F7287D2408249E223D2696276B28EA2AAA2AAD2A5A284728A328E2252F261D2676289929B128762605286B263728ED3228315E2C1A29F4257F2421259C26F829272C492B0F2B1326EE2753289D28442A12293727EC26292A3128A826992876297C2B922A9A2881271C26E1258528F729132AF429AE28412580267E262028AA27DC263D25EA23BE2178260C268B2AE32C572B9F2A2128C62681280A273025F9247D255127362B7E28B9260D26A92749309332082D552A8B275426F9265D27E42C2E2D1A2D8B2B8C29C428B0296D2AA42CBC2ACC276E2717282E28E728122A6A2BD22A872B4E288A279D267927A629AE2BE72B762A6D29A825D9231D28252A5327A82653276A258224E324852407278429A42C882AAF29A72AA72AAD284B26CF2639259825412822291329082A342ABC3289302A2D8C295629A8275B27E527A12A792CCA2C9E2DA62B7B2BE8293C2CCB2A8F2A2B28CA27FF2694273228442B722C732B302BC525ED24BB241F26CA29952B4D2A782A962866272028342981294E285427EB25FF22F423D4241525112782287B2B31296529C929E629B929C726B72431268726CB270728B029332A60290F342A33B52DBE2B0D2B762A7F28E028582A0B2E332DE02C942CE62B342BAD2AD62A80285029A3279927B6278827612C5E2BC82A922AD827A32530255227FA2745295D2B272C5B2A58298529602CBB2D6B283E283826E3256227DF271C25A326FD264D2A382A6B2B772ADA2CE92AE427AF258A260E28A529122ABB298A296B2BB334F4324A2E2C2B182AA228DB273E29D92BFC2D352DFA2A5D29E8289928B0278D271A28A427AA27D3264826B426DC29D82A862A322B1626BC247F25E12638279928322A8A2A512A73280E283329CF2ABE295B280C27D928E3260528C7272025F6261C282429912B4A29A82A192BF62729279B264029752B0D2A6F2A9F29CB29FA3454338A2E852AAA28C42768273528C42B792E182CDF2BD629B227A4268728D327E726AC2AD52A732811278927ED29BD2B59297F29F72759253328A827E128C72AA12B4C2B792A2F26EC26232706271A261B2819299F29C929182B5628FF27CB26A925522705290D2BB82C312838275127CB28F629F82B602C8D2B5728FA29A5354A33F42C8E2A5E28A627C2274229B02B612B802BD5287128FA2597278C293A294B2AA12C142A3B2A22273325DF250728D02771261D253A261A27162A522B5529F72A492A3F288025492715267A25742681277E2BE22988285F28D3273329F026242574246325B627302A8C284727ED277C296A2B902CA12B882A3128E22717336B32652DB22C012994274A255727022A782AB4290A2AAD28692775283D291E2B302C492DB42BCD2B54293327CE27602A4D285127E524AE2574282E28C529772AB028242730296A2A6D2AC7273425FA269C26EA2BDC29F926C8276F275F26CA23AD22DD23C527F42616294328B627CF26E1271C2AE02BAF2B942C5C2BAE2BC034E6327D2D382BD12A0528D425032707287629E028992788282D2845293C2BA52C9B2C7B2C842DB42BBF2AC228E929AE2A912A5A2847264E26C628E129C92AD72A2928AB277F290428B3298F27EB250F254E261F296728B227032785272E288C242D232F2418267E27C327C0271427CF26F125A228872A6D2AEA2A612AEF29FE31C9319D2B7A2A0A29E8271C25D2276727A229842BC62A002A1C280D28BD2B882CBD2BD42CCF2B4D2C9A29252AED2C5A2C8F2A752991275228DA280B29CA2A9E2AAD286526EC2794277D28D225DF257D245A253C276A26292665274C254327A2250125A123BE24942421299E28DF27C528CA267D260E28A92823271928C4286E2FB2302C2D872B8B2A21290626D9288028482CBF2C30294D29FA299D29722AC12C122C692D982CCF2B832A282ACC2BA82D7F2B842A6B292E292D29572BCF2A402A87289A2714284E2744297F279F243F2332271D28B0263126A9257625BE278C26C725152453236526FC2766289C28782756261927D627AD2678269B252D2610306330982CB32A502819278A257D26E328B8298F2A1C2B3B2A14291A28F3294B2CE12BD82A482D5F2A892A3C29362B782B4829082A33265126AF286D2A25291929C727DE27D126312445262724B226AC23F525AC28912711257426802725279D26C82361243625A9234328ED28E42912284227AB23F6252F2706277A276526C430E631F72DF32A7E2A7B281B267827562B182D362B772C652C3429DB289A2A7D2BE529B82B832B302B4A287326AF2722296E2A622B7128F2260129FB29772AA0294D2708269825EA241B2692275C287A27AC286A2ACD294A2A97286026BF281728C0286624432431243C2604284D2AB0293D25EF25E12564267628DF28C929F2310132C02E152C082C452AF429FF28FB2CA42DBA2BD72A9B2B122B862AB52A0D2B6D29592AC92AA02B5B284926A929FD2AB72C012B22299E27012665279B27CD27382583250C260927AD289F29792B432C6C2BC02B3E2BA02A76299028AB28CF28FD296E27422487252B26D5275529F2294A28492679266C273029D82A672BC4328E35142EDA2B8F2BF12A0628EE29432BAE2B1E2E302B032BFE297D29632B242AF628D72A7929AE29F5278B269B27962A782B132B2129702411256325AE25E526FE240523B82272256327B429982BE92A122BB92B4E2BF1298E2A5E2859293B2BB52ABD27FF24492328264B27C528902A6D2833279326B926AF2AA12B372BBB343D335A2F142CC02B262891281129C22B642D412C6D2C172AD729982B842B832C292BD42C912B7F2A2F286328ED29FF29D22A992BF7272D255A27EC2435277024E02413231223AD25B824CF261E292C29E228FE28F428CB29D6290B2974290F2CFD2C102AFA262D25B9251A275D297B2BAB28FB274026AD27812A7E2CCB2D5B3565305F2B572B4B289C277C287D28082B7A2BD52B4C2B222BA32AF52AC52C8B2DB92BDF2C002C252B792A422A682A682A822CF328D726FF25762629282D288B26FC24EF236624A42397235427D02644265F27512977284D2A302A452AA22C3C2D452BC82980289926BE249A2598267C2A8D2A7927B726F7272D28DB2BC32D4434A92F4D2B3B2A752BE52A4A2ACA293C2DE82C332D442B7E2BAB2BF92AF12CD82C6E2B022BDE2B0A2BC228FF29382A3E2B04297E297527C1264F29072B472ADE2ACD2750232625AC25CE2671285429D5272D29662AA728A52751298429C22A1D2BA62CF92A7C285426FC2477259D2662298A2B16291528B627FC29842A392A07347531E22B6C2A712B7A281B2AAC2AC62ACF2BA22CF52B6E2A612AE6281D2AE2295B29112AFE2AC42A612BDC29152C952AB0293E2A692725299B2AD22BC729D227E226EA2534254E27D228772A6E29EF27AB299E280C299D28D52A532C4F2A052AF22B152B562B5E281127E82545262B2AF42A8C29F9270627EB2AED2B812AD833BD32D92E4F2C3F281B27E425E529EE2A4A2A102BA72A4D295E26FA277B28F72872289D283F2A5C2CF72B972C322CDE2BEC2BA32AAC28E329EA2B8A2A1A2A0F28402777261A2605276729EA2B552B6229502ABC2A9629AC298D2AC62975290128572ABA2AE528232846283E2728247427C3271D29D22719279B2A482B352C0C352B327A2E162DC62A5128D726652859295C2C4F2D812B722BEC29FE27C6268D279E27CA2A312CE32B762B732BC62F232DD029AB28EE2702274E2ACB2BEA297F291127CC24002640269528A429142B7529072B202A9F2AAB2B312A4628A32830290F2AB92A032B3E28D927B42552259127BF2776290E290428EC28462AC22BC832E032EF2E6D2D032C8329862779287329F92D642EA12CC42CD6297029A528A728E529752A852C7F2BB72D622CB82B482B9E2BE52A6928D729D12A202B492A58296926A725E82595259229A02AF9291E2A6F2B6B2C642AFA296028C5268026AE29392A92295329D328B6278725C2245326BA27E5275A2754276F289D283429973261317B2DDB2B8A2C0B2A0B2841275829832B6F2BBE2B892A5B2860284828A327A429292A5B2DD82BDE2AB82ACB2A862BA72AD829862A1D2A5F28CB2860288D265B25BF24E32366257B27C42A67294C29BC2A762B672AA6296F28EA26DA28FB287C275B26B4278E269F276325222551246824F4266E276E27092759298029B73222303B2C5D2A3629E12A352AE42817290D2B822C292B792AEE273826F0279529B629A52B532C892BF22A8F2CB629732B6A2B8C2AEE295D2AD8296F297F28C226A22656248E24AE25F226A128DE29E029DB2B612C862B532A8D29B9260B289E285D284B28C726B225AE26B827DB2409249623CD26EF253326E4291F2A8F2B6933AF30642A8F289828BC29072A80298727EF284C2BB82A7729422851278729432B282AFF29F22BB62BA12B702BB3285E26F826C228E829B229302A6A28A2274E2580244723AC23832438271B2A9A2B8229702AD22C562C2C2B122B1128CD27AF28232A38290229DB26C4278726AA241A24E52236252A2630276929DA2B512C3433A331B82A092918286E2B622A57280128D527072BEB2B822B98295828B528882A962B652CE72C182DC42AB229962A8C27B52501273C28ED271F2947285B276B27242564234323C6230B277928D32828281D29822CF42A252BCC295029AE29DC28A929DC29A62AB529122A5A28822795259B23AA23B32450241D28312BBD2A8D338131BA290629802739293F2A99280D275628262A582BC02A5129B42A822B0B2BF72BEF2C522B712CD22AA82A9E29E626E8271128C12764275B28C229952997285A265D230623E424F22638283C27B927DE28AA2B402B7C2A6C2BE829B2284029A529692B312C3B2BE82B4E2A6F284A26EE2428239A231B252A263E28E52959347431292B9429B0288D2A6C2A422920275C28FC27092A342B0C29A728282A7E2C9A2B3A2B212AED2BF32AF32A8B2B7C29022A6E2BB628FF26A1282E283528A3263B261F243B2208248326BB281026B92667296729812AF0283A2ADA297729B52A6729322A762B442A422BC92A6527C1261026AC22A3248523A1255D27F2285032C52F7B2A06275028F329062AC5297B282F27E5268C285A27AC266026E027A12A802A272A022A752A1D2A842AA929AA2A652A262A7E298428C2289429D6291429C327F4245C23BB2394262029C5278224F127E829542985273328B926D7265728CF28B9274D284C270E2850268E2690266E251D2455245024E72581266028FB319530DF299C2814260E2A192B3E2CCF2A6F27922617279F269525DA253F295E2A7A2A87297E2C272A882A10288F28B4295F2AD929462A3C29742833281228B528FF27352467225E247726D326B3288D269829252941293D289B27C3263C26CF25192589252A28DB263A281A2700285426C9267F269F2786276E2854281F2B813437313D29E82612291D2AB82B7A2A1A2C5F285827D826B4276B252726172AAA2BE9297E2ABA28562B582A7726A5286F2AA42B362A14280727F32764289427E32751253A23F4224F227B239A26D0260F261A282D28D1282029F62777250724B025DB24FB251426A1269528F5273629C82A03267B25B32660269C29812B412EE73518318A29022A4129C82B1C2CF42B052CF5282E25AF25C92563279E27E929FB2BF428C228B328382B5F2B6E286E270427782796298728EC26042779270A26BC24F524A625ED21A1229824E823EF28EE27482976294229AF2756269025BF2436256F2567241427D026422A8029852AB927B62612258727B727B32A112D3B2D4136A42FF62AE128F029142A572D932B8C2996288C268F2639256E269629932A212A982AE029412A402B532AEF295729DA283D29CC2906286D27E427D6253F252624AB227122C721D7210B24AD26B32716286929032B9E2A40298A283128DC27A9283828A7251C253627D72A122B1F2A2828E12511256226B3271D2A2D2C852DDA362830F02912290B289129EF2A302B272BCA28132586252725D026BA269529BC2A20294D2A4E2AD72ADE2AA92B71294B2B772976284B26FF2752293B29022873252A2490233522B52289234228D2299C2A2928FB284529C829E52AB8297D2A372A092961272828EC298B2CBA2AD72941280726232455259127E12A592CBC2A0B354B2FC529D727D6277029C82A852A0329DA28B125CA24B424262538270E2AED2CD12BCA297F284E28E2280A2ACE2B32296227C925772529268C284329AD271426262584211821032025250F261B290A291D286F28B126E227582A9A2C1B2BAE2BE529262940299E2A082B372A152B0029E1254324C8240E27502A382B832C0835F72F0C2B8D2AA6272F279A29802A8B2AA329BE272D26EA26152586253F29102C272BDB29A329D0270F28C5277D29342B55297827BC270D26EC27DE28F326D1261925E0212922DF216B24F6252E275026B126C8259925BF28C92BB92BFF2BEB2B592B652B4929262B9B2CBB2BBC290E287324FC23D9248126172A452C962C4734D02F5229D629F828FE277A292F2B022A3D292E28C82554264525A4244F268F286029B929FE290328FA261C27C8277129C2276825CC23F6252426FE26AD278726FC248021E42198225F246225D6254F262426162481248A27A527D628B6294C285A29E329212A2C291B2A002B70292728032456236F2541271929852B4E2AEC33162D2F29032A142AE829C62A3629312AA429DD281129A8265A27C92578278D27B429862A7F2A1628DB26CD25A127932AA8263626C3243425EB237B250D278D255E24A22247214221B6239225B625E524A626F1236224A524B22635268C27DA29DF287F281928AE278429DF2AD7292628D0247F22BA237922C0266A29EF29A633122DBF290428EC2A4F2B4C2B242B66299D2A99270F27AF279227C6257325EC27D22668281228E5271825C924D525A628AD273226A0231B2379242824B9234624452311231F216121A223FC25A72607253325832510245F248B268E251B27D9264729202663261526E4274A273529FA2545264122C1229D254126C9286C29A331652CF828242716299A2ACC2A682988287828C8277327AC26CA266E2238236A24E326CD28AF2518274B24A123212570253B2729250E2463219F24E92298264E256A243722F1218F212A26C426B827202767250C246F249723BD261F258B273E27CE287F27C4274B27F128F9268728D52680247E22F92295248F27852806299833E92DF827B1252228842ADE2B4E28792711280728B7280B27C324FC24EA2436255427392921281128A024AE2381243527F6268E27712428221023F1245225712737262721D2209A229A2342287E2928279B284D27F7245222D3259A26BC28D827A128B2273A27A325D627372866277525CA252C232124AA222E253325CA272232432C832659256926A1277228F028292732280D2843274B273726E5238425212520279628AE29DC28D22509267926A526BD26D927C1254323FC2398264C27FD261C26F322B32235226924B12607286627B42644263725A623AA240726EA27B227D0276128C226D1249025BB2648270228D0242224EF239022A324382628290135EA2A0E257D2480258A2652289425A6262C285C279827CE252F2502242424752552260228C22AD32AEA26B9242725D725982568278E24212547242A23DF26BE2607256F23A42344217B24CB25ED24282625261826F123CE24872571259D2595255B27D92667251124C525A4267728D327B324A5231224452289245125CF280033A02C112668233E266F2769278427472784284E266F268A272A256D2599241325C326F9273829802A0529C92660273927A5254C27A0259B2397233625B8263E271E260024D321C122AE24D725E326F226542711266424732311240A251F243526C22699257E24A5240D265E283F29492BDD26C924EA2356233D24D425E7290D354B2B7626B4253F24CE266727232656255F26502716267C26D5233624C3233126DD24682688289E275E287626F0263125BA2518270F241D24BD236922B42309249824DA21FD2107229C220124A525F7258D262F259822F6239723AA223923F7232E26842483242E25E525D3259927E9261226352500223023B7238E24EC260F346D2D74275C248A24B2262027432740266326B526BC244624C222F2228624CF21CF230F258525FB26E127D826F825B224F825A126C725B722982353221B244F25CD24D422A8221B207E225B23B82343245A26C126FA240224B9229622E322DC235F248524FE23B723A2250225EC26F0287725EC2367237D214E228426092802352A2CA626FC239A24C3256B277E25AE27AE230E25C1242324D822D723D023582291229F24C2235D25FA232124DD241D25802495252325D6226624A5238A24892492242F23F921B720D52109237D2336247724C726FB25E5234C226422CC22F922F82364236B23C6221025D82288250D2510243F241B2342226E24A024EF278D32CC2B29242E235B256E247A25D6241C25F02485239A2518245C22682328231D234324752243245E252024E922692479253E23372453233F220E228F216F233122652329237723CB206221A622BD23C1223C249826F8230024DE243723BE237823F821482165223C237D24E22479236925412571220A224C21782170236E25882F622CD0262723D723822561237B24172494240825BF2369235824A722EB215E247524992424240425F122C6219F239E245F23EA2254234F231D2196226B224D212C242323C221DF1F8923FD219F23FF2130248D245923D723CA24FB218D244E21DA23B7233D230822CB239423722427266424AA21F6216E210323BC2341268C31352E98266423632369250923B82456234324DC233124CE22E022AE214A22882290227C2328227A23E52274220C2388247E2404235622561F7F21EE20D4222E2231226F21FE21B721212005247F2277219E2230238F2118240D24FA22B32165226322D0211B23E320D423D723CA23BB234A243B2322234122A822A623382580312C2C2A268724F52114227222D52375233C2571230F23AC22E6216B237323C520312361221F23E0232E233D22C3223D2286225B237220651F2822B821B821EB20B721ED22C220E41F2E1FD421422098215122872230207521CC239C22E522322083227D222E22FA228E22AE22F222E8248623DD225F22B01FDE22B2235D260B2FBA2B4426C323D2220D2256235924542482240B239E2341245F22FD21A02298234923C221022201237123272238229922662309221920F820B91F2222E620CF219821322123220520A61F48213F21C220F72131213D2193212D221623E021DB233222AC20D42156231A24C421CC229A23722331224B237A215A239324DE25252F892B9126F6217921B62009213A23A6227223F921F0225F200A210B21A521212110221121202236221523ED1F322122243C237F221521B51FBE2024204A22CA22DA202D2083215A1ED01F9D20762001214321C221B6229D20FA229B21392166206E222021C51F43205E22EB22EB211E237A21AF22AB231A2219221D242126592F0E2CC725B323BD21B9226322A5212F226123DB2183220E21EE217F20FE20D02009208721FA204B21AA1FA41FC3200D22D021B8207D1F6D20DD209B2070219A21C321DA203F20491FD22044213E21AD2066205221C21F89210A226E212020F62032216F202B21C921902267203921EB21BE232E21D921D620732168213B25D72F5A2B6D244E22842202228F20F3203023D820E0217E213C21F120771FEE1F5121D520FA1F0B221F223C21671FCB215C21F5207E21E821E320C320AF1F9E1FBA218521F1215A1F831FF41E2F2101204F207620A120AF1F4320F221D01F21212F21B020EF20CA20F7205821FA212D2363201E21EA21F6215F2292224E246A251B2EB42B1125912108224D221C21FA2102223B226123F322A120C12086203B21D7217C2113226621FE20E621831FD321411F9E21D921631F51219A1F3D20F51FF821FB2045207420C1200A1FA02150219B20D4224522F51E6D21BE2040215D2170222521EA1EF1212721A91F0C217720632159213722B722A5201C23FB220A26F32EAB2BA3257622912166227122A721DE2341228E2142211F21EF1FE11F6A215F22002165218C23D22002221D217B216620E921D622AE20DE200C20D220C31F4321A5206B1E7820511F121F39219321EB1F24225F211C21B71F2422C42080233221FE20F0208920B11FFA21EC1EB1203822C9222C21892123221C238A21EE23682F012CA6257621C122F5211C22B7219B233723CA21F3215C225C21961F9C219C21AE2218223422F11FCF21A2206C2161206A202522A2200F1FFA1FE9212E201720061FD41DEF1F111F641E5C201420821FBD213A22FB1F8420F81F8D1FD71F6C204F212720E21FBF1E9B203A20C5202922F8219522E1200C207921E323CF24AE30B32CCD258423E320DA230C24A222F022E5231A2377247022A3213221A322D52071215D23032243205D214020B1214B21F1201D23DF2183205621CF1FBD1FEA1FC91EC11EDA1D491F3820A01F4620F22149226F215D211B1F0F1EB41F0120C52128212821A0207A1FAF202D20D92077224B21F5239B224B209620AF238A25D92E +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 403F + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 55 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 0009 + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 AF287B22C522FE20BF213B2130207C20F8200020991FF01E1F1F561E4B20591E711DC51F501F311D041FC81E6720CF1EA41FA61ED11FD41F441F191F0F21731F431ED81E331E5D1CFD1C8B1E2A1EBC1E091EA21F6C1E0B1E091FB61E591EFE1F55211D21E320B81E0620571FF51D1D20601EA520FD1FB91FA71EDC2119233A2EC82BF923E6202B2309235521EE21542252223720A2217020841F3F20CC1F831FBE1E081F0B1FB31EF71E4620AB20D721361FFB204D1F5C20BF1F3F20EC202B1F6F1F5120751F5A20931EBF20951EB41EE2202F200B1F881E331FDA21ED20BD2054217221E421692099213221F41FA41F881F3421DC214F214C2243229F24962DB92B982525244B232522B821792295233D21D4232923D6224221B120F420F120DE20D021A720861F9F1F5821AB20BA20BD2132221421EC2066210E1E24206B212321072244216A210220CA202D20871F4F20FC20881F3C20FD20D11E3A2125228D228E22A72113203F211D21F520861F761FFB1FB622FE20CE21A222AB258C2F862C5D28DB245E220F23E222E8237E220A2259218C21AD200922D12069221E205C21FB20C9206A2223206620AE1FAE219C217D2285210222CA201720AA1E53201422A02173229F1FC21F3521D31F5D1F5B202321072153210A21042151221623ED236622752235216B20A51FF21EB41F3120A920A221B92262213C23E824D82E2F2DB926C822652279226023B322C122F822F621EA21D121FC20832016227C217520DD210721B72090208720DB224E1F34206123EE20271E3221062178206D20FF216F22BA217C1F9720F720FD1FBF2177205422662084203A21352271217B21E7227A22E522452018213B206F201C2094205B20292177209D2180233D249B2D292B45265B23BC2457225724BF2295228823752292224D238E205E210D2254225C228C21A02036227D2043203721CF210220A02144212D216721D7212C213D222322FA216D21AF1F5C223F2088214F2192218F21DC21C621F520BE21F620E321BE22B322D2200D21EE207721ED200F2173207B210E2362221422B8247C25602E852B5024AC239223262331236B2454238D2265233E228921DD215A21CE2207235323B22109226521C1223E22FB20A521D21F8920EF219C22B3224122DE232523CE212E22D921712149219C21A321D920452294233B211A219921D921AF201721522259205B21E12000211521B520C2204222CC206322672172234924A224562FCB2BD825B023D0249923F7219F223F23FE223D23EC20A7218D22F923ED2106222423842216232F238722D120C2216F233021B7217E22D72109236C2394229A21C421522297220222C622F4231A24D020BC234C22AD20E51FA3205A1F1D219B201421BA20E4202821D5215321B420BC216A21AF21EE211922F32464251E279E30A02A3826D8243124A72451232F220D24A524602344229D21BD21F121D32232218D21122367231C230B237221E11F5F222821A2231D2263211D2253238322A12133222D238C23CC2394230323FC21442227234921FF211D20D5224021A420E9206B219620B3227722C9230822EF200F22152313223823C8231025ED2619273032182D672410233A23BB23C022E0233A2350243D22882390223C21A7221723AF224A2354226D23D2227E22F022772005223B215F240F246D228B248C22DA21E7210522ED23772331252C2432253A2401227B228B22952182200D20CB20E21F9B2034249321C12048236F2365235E238622D922E72322248A25FD258728EE287E34792B8B27C324AB2398242F23B6232F234F230B237424F3222223F52131239F23BE2324231F247224E621F3224E212923A3219D226623872327240123C92128211B239522D223CA2590243024D724F02146225920472038207121A420C32046228222D521F42157232E24E222B12179230323A322F523E9246128BC29982A7A36852C2B26DF23062689236F227C231223722429245B2331239722432382236C24D3232B249A24922752240F25D021B622FC2392259726DF25B523A7226C236422A22117239F221C24D0246C24BD247023212306214A2199215221DA208D201A2295225423C924EF22672538252D248F258D24582439253F27772832290A2D3E36802DC5272D24F223F223BD23142438237B24BC23AC22CC24B823C0255F2532264E25CA242F24D3269124CE23CF218825A5256D27CC272A264325A8252A24C524C3227323AB23BA224A22F0239125F922342120227A208F206D224622C420B022EF21C222BB21DC223524E72444250526AC24FF24AD2827295D2BA42C4C2E1034382C512768246D257826A024632509260D26302528238623DC23D324B42655249F25C825B225F725112550230723C9238A257627DA261D267127C924B82489235825212462236F227D233123D023FA216B235A234621942052206521D4218E222024C8215123B823D425C8231E23D824E2251228A9298D2BDC2CF72CBD2D1237972BA826E425542571271626BD27EA240A276F235A2457250424AA259E25B925B4237426B627A5251024F3229C22CB23E324C2247524AC252C25822487259526DC27D124B723B02288228D2177212222AA219F221E2268207820F820FC1F0D217D228D24632628269F27E9250B25B025D9261627E42AA52AC52B7D2C4C2CB834352E372935277126B0275728F626D8269024FB241B251C244E23B426DA27BC27C5246026B125B125BD263C26D6224A23A8228A2428254E2483247E251B2607274D274F2459230122042143241E2216229C234E202A207920E5208420F6202422F7246F2529291D29252A3C2A5126AD26EF253E288C2ADD2CD62BC02B8E2C0334122D602A082B432B4E28B52641263C26DC247C2491256D247D2497253C265126AC25A62440250A25D0256123FA238C247D24A9253425DD228A249F25D3258C26B925AF2562246C2224211C20FF20E121012281219D2070217D2365211D216F233D250527382A0E2A4D2AD328F926C1231E26D7267C283C2AA52A902A5D2BC2339C2F5B2AF12A5E2881277927F82594268B263226E62462245024F92419243725E324852595266424C5237224DA239E243424E926DC26EB25DF259A24DC25E124EC2522247922B421A5235A234B2201216B22EE234E22EE1F5F236021C420C32120263826612988286A2ABA28CE2994288F274626B8259D267727EE26AB270D33002EB42B38287328882787266027B6265A263925C1251023E623D92476263F25C225EB27B325A727AE254F23FD24312772265E298F280B28F12683255226C4247A24D223DC23DE21802419247E2491218A23F521E62122216B20731F01209721B62395265829672759290A29D9276825A125BB24482446247326CE25802768312D30F22851268826122615264126352797257224AA24F1247C25B924BE286428F92771250828B2279224C8231224FC2510291A2A19282F26B5290B2780265423C62397243E24BA2480248324D22244234E23A0222B218B20F8212020E7208E22C324C6249625EE26E8287126B9258B27ED244A24DC244C24122512260D26C830C22EAF28692710270C278727E126EC27C0276226D3265125CE25D327D128DB297C29BD2C192A0D28FA258A26BD258A26AC2850289E2835270627E6265B25D7252E25D625B2252D26D1241D2413261E2398242124182341222A2000220221D1210423EA2260248F25BA271E278E268728132537251D2734243D26C52590251731B62DA927FE270827AE275F29C92A342B3F2A0628E5239B26E527C32A1D2B622BEB2BF1296F2C0129F9275026A72699269125BD26C42476259226BF24CC2569247025DA267F26B5265B26C425DD246C2582268125C322CA22E2211B22DF20CE212A234A2583250627282843269D26F7242724CD25E624D5262826F4244126CE30942DD527BD260229332B0B2DCF2C772B7A2A9E2790269626C7276928692B842A422BF72AE72AF127A025872599252925042451244E23FF2350249F2235248624DC23852513264926F5251629C927BF252828FD2646254223B1222622EB21AB232E25CD26B1297029552A462A2B2857262325F323C9244125BC24C2241225D7305F2E822880289B29E02BB72AE12B1F2BCB274424C1246626A8257628862B0E2ABC2A932BA6291229A6262E260F27CA260E254323302205243424AE223C24AC247D252927EE242023EE266D2863293B2A7D2AD3282328DE24DD2397225A222F256B2430272729EF29902A9D27802717280E274524E5249C24C7256824E9268C31162F2C29D7270629032A2F2ADD280A273F27A024CC268926D626FC27D427D828A7287528B3271929F5253425CF2695255C24CE232023EF22AA229F230C24DD257A26E927E3264E2686262927472A812964293729CF277525A8240B22FD210C23FC26F426F2277329D3280927792627297028C2250D25BC248326C3268D257630752F0B2AB5282929E127B327C12765268F251124B325FB265F273D29702AD82AFE2A17279726EF25E426A6288826F0268A2532259424E32244251C26462864288329E42AC82AA82814264A28BC295528D829A829E52669237224AA215921F82347243927502901284A2877262B273629B6294E272F25AD23A7243C2471279E31A830C82A802829263027E3263F26782543255F255C2844297628C329F72AE32A9D2A4E29D5258727F627D628CB28C229E52744257A25A823AD255E27A6296B29942B132C2C2DB529C72A7929072A172A9228CD28432786244D2362236E2106249F233A25E5276027F6266D25BF261D2887287C2791262A25682316253D267631C92F662B2C279B276E26822650268126DA25A4268F2A072B5A2E372BAD2BB52BA3290429D927F628322A7A2B352BDC2AAF286A254423B3240426DB279B28262A882C5E2D8D2C1E2A952A2329CE293529AC297529BF2794255923E7222721D121EF242824CE246526FA259F255D26592770284F27A32772256E25362590285D3392303D2A662A432AE62827261C268C25B526E427EC28202CA32BA22C762A6C2B7E2AF028C229A82A802BB22B692A1A296826D7258B233F24B526BC27EA265229DE2BED2C162C5D2893277927532600283A2AEE29DE28A4251A254921E021D722C62287236223A525C42533262427BB29DD29E72AE3281C25DE2460278B2827336B32712C4F29E8295429302ADE2636250827FA26F32B1E2CC82B20297A28A928FC28A5284D2A332E4E2B4A2BF528642947252E24B22417258826F825BD252E27E82751281A28E2261C2685260F261B28972A612A582A2D257C24D4228422C422C92316258726472756281A29BB2839294429C628DC27B425F825B328F529E6350D32482CC12B8C2BE42BA4282B262B26C2254A296E287F2AFC28C8275B25882327256827BD2AFB2BFD295928B328EE262324EF235E25782445261F269325E724FE24F7251526162629261C250E26E726BA27252A18266D24C82353234521092284228626E5276827322B8C2C322DB82B2A29B826C925BC250E29D02A602C5A354331C52EEF2B5A2DEA2B1A27052576249827E928D72A9B2904272326C827B127A0261E28312AB02BF927AF26DE24152349234C25FD2536261C27D3241D26E62492272026EA262C259E264828A1279B25EC26CF277D24F823BD2314233E23D021D523B825D426102AC52B2E2B7C2B632AC5280028872502277828D72CE92DE1354A31E92B802BBA2AAB296B279D26D1253B261028B227632645266D25EF25B32643286427C8296C28EE255E253624892247233C261A28D628962775260925E126DB260D273A262726E92537294628B5272028DE27FE244023E72219226522D020E32233245D264B28822B5D2B0E2A47297C289F2609276B266629692C712C27352631E52C962B872B36286B273E257A26A028BB28A8274F2786261327D32761283728972916285026DB26AB256527062500253327D32934280528E227CD266A28D82911293326F424E6273E27812862283A2AF1290C25B523352389216E2108219B210D241C27E1288229EC270B270027D72872284125CA25F428732A4A2B8F353831722BA329ED293729CD25DC273D27A62AD52A0A2B822AD12AA228DD286C28FA28A928182799269729122A812994296E278927CF2ABE28C9264A279728912A622A1A2A0C262B24DA26B628722903294D2ABC29C826F3248622D022392377201C23E823422587260A27D12709276A28EA279627152799258B262D29992983330A30FC2A932A28281828FA27A9298F2AA92B062C2F2D772D3F2B8729FC289728782AA729E4270B2941283829F02A5C296527DE28052B6328A9276A28B5285E2A8829C427922706241F25372A302CBD29372AD42C152BF7269A25EC24A923D62374231C25AD285E2A9E2A8D2878256F2722281C287726CD242A26AA2885296D354D31732A2C294B2795273A289229E12B7A2B032CCB2CBB2AF828002665270729D82A9B2BD929632AEF283C271526C827FD2680281A2CFF296529872A312A7C2B1E297527602583230E25E428E928CB280C2B222CF62A40285D269422ED225A22B22512242D286D2A5629BD278127382906291F283827B0256A275B287D2A95347B30CD292827DD251328252A812D1C2D102CF52A1A2B5A282F25E528CE28FC29002D812B832A012AFD298625F024092632268D27932924292D2955292A2A8B2B45294127A4250D250327372747286927B72AE92A132C84287A25AC241523482217241526D326D02694283427DF296C2B552B0D2894286126AF2622295A2C7536A82F5F286B251126E927DF295F2C912BE72D272A452AB7290529D528052B322C5C2E822BB429C02A9529C32878275024FE264127C926A827C3251227B42826283427C0262D279C254527352820273828C629C029352A992847265323CA22E1230225F6259927012736265F27C529602CB32B982AAB2A1D26CB278929E92CD834DD2EBA27E22589258F265B29F62B6F2C3E2CF82B4B2C832B692904281729522A1B2BEE2AA4295F2A5D2CE7283E270B27FF249125A5256F25AE2474241D26B9244425FF24A426FD275D29082AB2286D279A288329C7299C271727922387228F22CF2329266D28752737271A265228C62ABF2CE02BA92CAE295529CF298A2BB6346C2E47291B264E27C328A42A862CD62C032E7E2B1929EB27B8276328A828AC286F2A0B29D8253B2A1C2A042A5528A2259B2532263E243424032618238423BC23C423C6231E2642281529DD29C4276F27B9288329B229A5272425FC22FD22CD23F222C4243E262426DC26B926C9275329BA29FA29692A51292328C926E929D532C2308628BC24C027B627EF2A5B2CDB2CDF2B3D2A132A9A29A0274526602703276E27B42645272D28A129A0298A29282A7D2A8928DB2692251126C42509248524AE226D2456288B27F5285B2AE22AA4293429232A0A2B6A2A4D29F324A92230223C242E24CE230A250027AE26B0270128F1274C28CD2AC9296B2A692A9329843025316C29F127AC26BB2B4129AC2BF52B542A962945293129D72700276F28CD270427E2259626F22580267F2681262D29F32ABC2AC0291D2A5529EC27FC261125B023A823622700288E2884284E294629132A2729212AAD273626F423CA227F216522AC23EC2391255F28BC286D280A285A268727FA285A2A222A17271C287530982FC82877263F2744297D2BEB29202B792BFD2BB42BFD2AE729032B39294F298727FD27E226F225FC234E25F4262429F128A92C822BD228EC2AD129F028CB27A425FA2522271B264326B326C8282A272C27FA26D4253425AE2546233822E82277217522602592279329782AB0292D29D727B42685286927EF263F274428DE30482F3F290C27D026DB28F82919282E28592CE72BAE2B282BE6283F270F2A162BF329812AFC278B273E296E28A92825291929D02730273429A92AE82A072A3C274C24CA2504277B251426B0261C26DB264D25B12689243225F422D32295219521ED22A2247C272029132CA529BA2994293128CE2785295529F6296E2833271C30D32ED728A026EA273929732880288129742BFB2C9F2A0E2A1129B8281929AA29242AE52A4F2A032C5B2AF82A532B0D2A8C2A6F288F26482745297227F427BC24F5239A247A26F1254A25C524CA26DF253725F7259123F6235724002168227222A222442427270D28112C612B28296829F628BD29822B78299A29272A42287130AB2F092CBE2AD72AEC2C8B2C082C4B2AEF2A222ACE2A8128E5267D27EE28F829202B052C18283429022B8D2A442BBE2B892962287C263328532927295328EA25A3246A23B8253D27AC26E3266229E7273127C7251C2625253A223F23A522D22127212225FB253C28972A8329C42712296C2844278E28B326922699268227B0313531122DCC2C892D8E2CB12DE02A2A2AB02A252B4D2A8F2A4F29512959298E2AD92B152985287F2833283728652A6D2AD928552854284328392874298E27312459235223392728285D289728F027A5270D287528E8256324DA235E224322DA22D9224825C526DD26D127E52641272827182655263B265B25912547269D270E327A32D22E452D462D8E2DA52D232C0B29AA28FA29312DAB2B362A112986297D28482A08279226DB25C02473270228D02A822B3E2A672854286129B5262A25A2238023B5249B293E2ABC2A3229C72734260127E9272025F624E025AC249C228E2337231922C3244625E7267626382674284A28862699247124AB26E227872AEE32A330F82DC42C4D2D3B2EDB2CD729192AB52B342BD22B8E2CC22976285727EA283D27A72659267F27AD26C52574276C28892A372CB12AB8295A2767241F24B623F024772652298C2BDD2AB42986274726FB24A426C725B424E7268027712442228C202424E0236E26D726A7266A27E928FD29242AF02733253A27A527CD29AD34D42FD82B032C6C2B812C872C042E702CE52B122B7A2CDD2BEE291A279F268A259D273A2A20289C285A258B27BE28FF2A3B291928D9280427C325AF25CB241C23CD224D2525276528362893253E24672471263725C225FF26B8288B2936252E24FE22E22440249524BD269927B028132A592AB12850287327892975278D2A9A32802FD22BB22B2D2CFD2B3C2DCE2C092C262D6F2CF92C492A5B28F626F2261327B52616280C29082CA928CC27A32AC42C0429F629D32814283C29DA259125E024FA252328BD2634260A259F24612349234725C426E6255D25DC26FF2641253F2320231624D3246324E2293F2AFD29B62942297429F8287127B62731295D2AF933A72FFF295B2B302A142C472CA42B4829AB2A882B9A2AE9291A2930275429FC29FA29C42BAA2A4F2DE32B9429B32A892AEE2AE427B728F9280B2A9B27452772266A27EA29312AB9273326CE23E623DC237E247C25F1232225192877269924AC23C52273237224B026DD277328BE290A2912270727C52988280C2869271D2928351733A92A19297327AE281929FA2888299D27D325AA27C726CC257227212AA12D522BF22C4D2DB82CE328C628092A532BE4297A295C285F2923290A28DC271228372BEB2B422A802870258C24AE222023EF25CA26DC253727C62669276B246223FC2346228F22F825FE295A298C296C2A4E28AC26B428422634274527C3274A31FC32942D2B29E628CA29E529CE281828AE257F264B28B929BF276B2A692B4B2C382CA42B482B442A992A3028BB2ADB2BAA296D2A422D9C29C8297329AC286729B32B832B432AF729E92710257624E623F426382AE529BC288628D8295628EA25BD257523122497265D291F2A6B29A329B328EC28022BC8286527C5263B26E42E6A346F2E362C2F297026F628DA27FF28112A2E29A629592BCE2BE229C62A9B292A2C1D2BBE29992A3228B3283129922C6F2BF12B862A102BBB28F52728299B29C72A5C2CAF282829B3275026FA23EB24A927012AB3288427CA27BD29C429502709254C24BC25EF2362292F2BC129B62B622AA12A4A2D232ABF2708274B27ED2F5C32652DF52A7D2956276228A6298A2D382BA629032C3329942A8F293B2AB5283B29A228D026B226C9270C25C1298D2BE92DD62C9D2ABC276425DD241727D027A02755290029BA280C2848268B287B27A2281C2BC029412836282928A22720262624642393242525322760296B2B312B4C29F528772CB62A2E2A1627FA265030F332C72A8D2934282E287228E42C3E2DDE2D1D2EB32CD329B6292229F029CF270F27332635257926EA259B26AE27BB2A632CF92A3E2A3A270824CA2234244125E7266F28EF280A29AE28D227BA294E29B92B442C752BD427D127D125662561256D249A24C62585237F27F027DF282F28A1283D2C392E5C2DFB2C0D285927E6307331F12AF3280A27C426CF29D52CDE2DD62DF72C3F2CCD296A2A8D28F3291529E3268225E12550276E270127D128D029BE2A192A842A2326D224952347254325A9254028A72AF9290E2B762B002A3D29E229D629A7286A270226F624732467232223FB2303259424D927E0274D2882289227452C882C922B712BB027EC276433FF31DF2BE32AA128C928502BA62CD72CCD2C3F2B8F2AE027D3276929942BB729F328CD27502778299D29FA286327B728642C8C2B5528EB231024102586258B28E227102A562ABC2A2D2ABD2AC729CB285828A4268F284827BA27F826D4248B245C2374222125D824E4288728DC256228A6283B2A422B5B2B9D29E42849292533C032A12C722A31298E29A02CDC2BB92CBB2BCE2AA32A5C298C29E228F2295C2C632ACF29FA29762BD52B582C74294929FE27B8278A27CC255523CC26082784288929C52AB4290A29062821279A26F9263A28A2284E27B4297929FB282328BF2586243A238A241427ED2A292B022AAB281028812861298827902722285F29CE331A31EE2CE528CA271E279529AC2C9F2CAC2CE6291C2A2829B32A7F2B8F2B9028D12868287529122CAF2B662D522A3C28AB250D26412446237B2311263028CF29FC2AD029C229F62811262D25CA275B270629B8283F29442A532C0D29BB26DB2481257F25F8261427792A422BA52A5B2A8728B2278E28C427FD260725C227B9313731032C1D2B81265E273D2A1E2CBF2A5D2A702A552BF22BD22BBF2C3B2AD32AB429632A002A072C632AD32C572AC829F2262126BB221122DE233126D627F82855281A28A02A2827D625AE259127B326072916294428D328B62B15282E26AF2597255525322552285E2A1E2C432C4C2B3B299525F229EE25CB24E726582745316B329F29422805268C28B62C002C392B0B2B162A942CF82C952CAC2BCA2ADF27A92A232DF12B6129522B172B9928BF29EC28FC248A249F22F222CA23B52679277E282A295B280928FE25F32513279125522635280528E028692A5F2A4A2739267824012520244E263D2AC829C129742CE228892678274025D923022660245C316B31162B68264826E528C3296C2C3D2B612C912C2C2B4F2B6B2A0A2A7C29472A732B4F2D4E2C332B0229FE29D42A0129EE268326022454236E244C25EC26B7284A2A442A982840267127E8279525B12584271226EA270528612A64290729D227B7264D238A240729662B8F29BE2742280F28D629C527CC25A1234F259D264631412F3F2A9F27F627F926F629702CA22BEE2B6A29992C4028D4287B2656274D27452BB92C722D4B2A6928C227F4260B297B27F1255726E6236625DE2632279329CF2B802B4E2882266B29C0289D2502245B257826D926A826CE27DB288E2918286227392530261728E329BF299D26202709265327E127F02687256D24CA269631882DF0296727DE26602A8E2BD229352CEA29AF2A682A5327832783258927D1272B2A322D8C2C692CC929D7281C2AFF28332721273A270027C3250126EB278029672A372A4D2A1629BA27152888267623332460281B2581255A28CE28BD286027F1239B23EC25B526492BE928D5269C269C268B274E283D27AA2509272928D1321D30712B722AFC2A0B2C152DD52B062DD42AC22AA82B952B9A27A9269426A1279E297E2BDC2A182D612B1C29DA2A6A28AD28292BCA2BD8271926DA26892658264B28B2279B26A426C5272628482732276428A3294F2988277A25D824572598227A227124D6249A26E6279126A628D926DA269B287B2ACC286C26B2267C28F3301632522C292B262A842C722E5C2DCA2C472CD22A092BDE29C8298C280C27A8273226E22785279D29502AB228252755291C29DC290B2A34293428702626265C24A026B52590249A264D2787267227ED25F1285E2B5729FE286F26E122C5238422D622B123F5225E261D272429FE27D827282895283728D928FE28FB263E2734312D31BD2C1D2BBB2AE82C5F2E1C2DEB2A522CF62A142CE52CEF2A9F29E1298126EB25F42703263B2561268A25B326352754287929F52A402974289A278827F6274F27C026472628249D26DD284728B7268D29572AF9280326552774255D23B124EC2381233A251426E2284728D728532A94285B28C227D02788278827502846318430352CA72C0B2D0A2C752C6D2B552BBA2BDF2BBA2B5C2E8F2AAA290E2B4429492ACE28C026F9254126E32608265C269C268E27BF286C2795283527B0272027D8289F2664260B25B0278728C82853286D2AF229BC28AB25132873268A26AE2616251124DC24F32518291229032AD1285D264326D4240B264626FF27442AE634A533C32D7E2DF62DCE2D612D632A5F2AB32B882BEE2BDE2D362B2F2BB82B3A2D252C242C902A892790280B28F327B5270127EA273C28EA28A728992834274828262B5729862949275429C02A0C2A6B2AA62A3E2BF12605270E27A42A3A297927F12637243E263D27FF27AA28712771271E274F250A2500269728FE28DE2C3137D231552DB22CEA2CD72D322DDE2C3A2B782A7F2AF52C372C852BF129A52BD42D042CC92AFA2A8C291D28C527EA25CF26DB26A329732AB929F1296428EC28D92BEE2A482AF4286727A92AD62A5E2A452ACD2B6F295C291828BD29162A132B92295F28B5252D25DE26CC275828A728D6291928362634268F255428DC29572D97376E31482E1C2C412E842D1E2C802DC32BA32B412B132BB02A4229BC2AD72B722CA92CB72CFF2A222A28289C2BF928F8270727692B4B2B1F2A382A5B2AFD2898290629F326FE26EF25DD27B12A902B632AE029E329A729AC28E9297C2B4E2C2D2CD62A7E28B2279827E5280C2871291A2B8D2B912944280E25F92784293C2CA8367C32452E772E8F2E7A2C632A9B2A302A9B2C032B2F2BAA28DF279B2959293B2CC82CD52B822B562C532A7B29FC2AAA2A9328B52844281728AB28E5265128102868273926B726FB24362657299A2A832ACB29C928BE262A268B2A762C3B2BC52B392ADF2B6C2A4E28B4295528942A732B842A692BF3293A277027E3280C2BFF343731092D992C432B9E2BB428FA254328F42A5D2A372986268226AE27042A0A2BDE2B522C552CC92DAC2A362AEF28702852250B289D28DC26D92783265927EC265A260726AC2486244B241027A72AEC2A7029ED29F5262C27742A682C552CEC294F2A442AE22B5429902998287728AE2AE729E12ABF2A8C26882593275D2A48345030EA2DF92BDE2AF2296A2839277E29C62A702958275526BB266C28D928992B232BDD2C642CAA2B3B2C3E2CD2291B271C27EB279A285D276F2556262A254B27E629AC27BF261524B3232E268B29AD2A712B752A5B287D2A2F2CD12AC22BEB2A0A2BB42AE82AF62BDA2AFA28AF27B02A3F2AEA29E3270527A72428253528DE31BF30B22C5E2CF12A84298729E3296829462BE6280E2824273D287A2B3A2D9D2DAB2D442D882DD52C9D2CAC2B70290E2811278229112AF326A6259A241428BA29C32BF32A422823242A24602711291A28FE2908296828B0298D2B212C702A8D29FD27CA289E2AF828502B622B662805291F275828152732235E23372541267A31F22E302CB52B802B292A8A2AC72B212BD329D72A3128F927592A282DE92CD42C9E2CD42BB62CEC2C772C432D9B2A1329A428182ABD28DE24EB25302618282D28D2290D2C002B2D277F25D02901296C2AAA2A6529CF25A4267529B02A692963284C27E1279D257C28612A742C862A3B29FF263327B126B4258A254925292673302E30302BAC2C942B8D2C602DB02BEF2D4E2DFD2BB92A6829572BEB2CB62BF82DD528F92A352D9A2DB42C812A612A93289225792620269027502819280E2ADD2A972BE42B322AF22757260227A628322993282B286D255726B9277F29092AC92742252F2597253D283D2C562C932C992BD628402727279626FE250F2554260731F230542B392A652C162D5B2CAA2CDF2D642DDD2DEF2B0F2A8E288E2A6B2B572E3E2B082B922B322C6C2BEB29FF27FC274523002639278B285E2A292BA5289328332A122BE8299B285D260427AB28AB270E279F26372684271529362877282427A32447253E256228EF2A1E2B132DC22A50289929622A72274E266F24052756303F32802C0E2A932B272D2E2D3A2CB32DE02E2A2E762C052B202BFF2A5B2D822C9A2ABE2A0E2CEE2C332CF729A6287B25F8251B2662277E2ACC2C012A6027E127652859272626C8241325C725B12713263A27262783282B28A82A1B2BA4282028A026E42522274D296E2ADE2ABD2A212B432A9E2AD32CB82A36290727EF27AA318C31662C7E2B8C2BD22C4E2DAE2B832CFD2CD92CBC2BB72AE92AB22BA32D2A2C4E2CAA2B772CE32BC02BD3271B25C624BB2273251A281B29672CCB29B328A9253A2760279728F425DD244125072816272F2782283A29A428D92D7C2A7229A4276F266B29B32AE929332AB7282A28C1272127A629E42A592A4527DD25F426EB2FFB30A62CCA294D2B062D212DB92C212BC62C972C882B2C2AD529572BA82CD32CCC2A912B552A072C952B7D273525ED23652436268029822A6D2CE02AF32A8629C329DC2A052BB928F225E1241228A526222737293529702B432B512AD727272647272C2AE82BB12B242AC82852276727332781299F295227E5262D26A42623309233E72BB12BB32B7F2B6F2B162A392B1A2AB42BB72AB6286D275429992B052C852BE029F32A172CB52B632834278725782639264429A4297C2C802A0E2A582A342BA62B5129FD28F1260926492646276A2A232AC62BA12A282C632919268126EA264D2A5B2B1E2C1C2CF628A0287E28282A672BC729012774279625592731329E319C2D622B892A5E2A8C2A5F29E028D7289127B127D9231327E6279928772BDB2B662BD72A682D592B0A2A092AB8275D2657272829BA2B112B2E2ABF2AB52A9529412A1F29572676261B251726F6253728332A072C372A9B2DC6287028BC27FE269E29752AB22A9B2CAC2AC2296B2AF42AAA2BD02D2F28B7258325E12731316E32372D752C932BE32B2F2CC22A8D2C082A9028D6260B2669262428E028792C0A2DAE2B6F2C872C9D2CFB2B342A46287A263329142A142DBF2B462A08296A2857282528E0278325E0246F270D282925E827282C1B2D3D2CC62B3B282F265326AD281129A92AA72C432CD72A5B2A6A2CD72B0E2BFD2A6A28E8262629672AA733562F242C062A7D2BAC2A262B762AAA2B232A1E2954294C288B29B729E52BC32A2C2BE42A1A2CDD2B822BBA29D929EC2788267B297B29412B2F2AE927EC266C263C255D2604269627B02803290A27B725B427A5291A293529B828D526CD26CB26C329BD29A82B662B4B2A182A9328A028A22A272A02290427B9282D2A9C2A3E352031162C662BD62B662C0E2AFF29A72A822C792AE829222B452CF12C132C622B6D285B297429B22BE12A9D277E28A12574250F28CB29B32B992AB829C226D5258727AE28A3287F29A42A4F2CDF2B2D26B227CF27D428B9295A29812505273E27D62A4D2BF92B322AD32B002BA0291F294C295328B927EB27BC29D02B992CB934CE31AC2D0D2C142CA82BD72AFD29912A322C802B142AF0293F2B932C922BE42AF2282D2777285B2A262AD927F7264025B224FC2772262C28EE284F2844268E27CE296F2A082B932A272BE12A7F2AC0280E276428C42A5828442746277226FE29652CC42BAB2B0D27AD28162B562BE42B0329FD27A4277A27B32ADE2B012B9D34EC32DA2E622C112C8A2CD02AD9281729162A4C29002A092A9D29432AB12C192A242777284F295D2A6D2BEC29552966272724632666278E27272A5D282E272D29AF2B922CC02CD7291F2B162AB029DE273029C12A332BAC2AB82A5B2787291D2C272C0A2C412A9429742B8C29DA2B0D2C7C2A8B2750264028892A9329FD2A93340634D52DDB2C442C8A2C532B612A9B2970274627E3256227C225D027D52933293A28A42873272A2A532AE428C4254E25F8247A252527B129552A9C2B6C2A5E28F52A6C2CD02A2A29112B8D2AD129EF290B2A762D252B4A2AE0294028122BDC2C222D4A2BFD283528C52AC42BC82C1B2C3E2AB7278A268126302706276E27D331FE32452DB42D502BEC2AB529102A892AF3271E262427402615254325AA2578279C27FF27E527F029082A8F289E272927B624D9260828062AF82BF629C929A129FB29612AA32B952B032C622A0429BB290A29F62C5A2B1C293829EB27AF27ED28FF29AB2B5D2CD028352AEF2BAD2CEC29BC27E1258325D425C3275728902942338B33B82CBD2A952BB52ACB2B422C382BDD29FB275426CE275E2686251A2663279B273D281E2AE029FD291228C1271C265426292796296E2A6C2B522A6429A7298B28F929792B4828972A81295A293127E826182A452A8B2A1B290F283B296E29972AD02BF92A8E296C285E2849293F296726B7261E277526EB2787289A285330E032962BFE29132ABE2B8D2BD82D8F2A3A2AF32AFC2A772A0C28C625F527C7274928A32A052BD02B23297828DE28D827A226B728D729462C182B86299F291F2948294028B128F827D429BF28D7274326AC26412939298328B028D025B427CD27972989292129F0251528BE262C28C12A222AD0281B29FC29AC293E2BEA2BBD317B31022D792B252C6C2DAE2B902DB429732BEB2BB629CE2AE42A26284C26BD277B28522B4E2C752C8B2B0D2A1E293329B528742ABD2B222C862A262B0C2A7E29C4298D29FF286C26A32928294A26DA239027B229552A13295227A5256527FB266C28A727A725CB264826B72567281F2A7D2B722C492C752A192BCE2A812A56336A31732DE32B002B022D682BB12ADD293929BE2A142CB22B142BDE28F127B927B1278328B42CE82B0B2D0B2BC12AEC2888271C2B1E29BD29DF2AC52BB829E02A2D2BAF2B9029EB25482765259E27DD234D25E928BA298A2812289D2719263B265225F726E6260C24252730273429B02A4B2D112A932B702BC32BD52CB12A09343032D42D272BFC2B8C2C8E2B4E2A4C2B602B9E2A2D2C922C392AC0298D29D1277225DD273E2A722C1E2B6B293C276D26A928E22B012B5D2A102B2D2BB62A042BB42A4E2B132ABA270327E527BE274226FF269D280028032921278B24F8250B2691289E257E259B243C257E25B227E0280527D929342AD22896299B2B5F2C6333CE30122D482A752BDA2BE62B3E29E12AD22BFD2AD129E22AC42A522A3B29BE270225F525A5272B2B312A5828012999275E29FF2A672B042BD929442AE229FF2A332AAB2B6C2BA52A9A29F328C928D0295729DB28EF27F126E2254D25262698265E297E27DF2473250B254D2524262227FB278D28D227A1263F27DD28682A3D320435772C642A182B0C2C8A296B295C280029E82DE02BB82BBD2A002A262BE4278025D3274C26C82803297F2892273A288428132A152BA428A629C72A232AB62B852B012ADC288129CA28DF28012956292E2A192B9F2913284C28D826C628752AA72BE9283E26FB235926492795287D2A9929052AF728E225C327C728012AD9344833872E252B152C8B29072AB2275A273F293A2B902DBB2B272BA12CC12A1C2AD927C728D0281829422701286E282E27EF27B129F32854275529D62702293F274A29DE27F5262828862603272828FA27CE284E2983283128E827CA27A028C82A3B2C092AEB26F1247926ED28CA2A562C692ACE2A7D28C226C82745297C2CDC346632BC2C852CA42A772B2D2B33280428EA276C2A242C722C0E2C102C132D852C8D295A2B772B892B1A2AED279C26C426342AAF29F9289A27BC26FF26B7260F26C526E7261027E72511267B294A298D28D3298D2C6C2A932AEE286328462BD12C5C2A6C28C026712698255128B529ED2CFB2B182AB928E027EC26032BCF2DAD34E3306A2CB02BCF2DDC2EAB2D6A2A6C2B402B2F2CDD2A462C782CA52B552D7B2C872AD629D52B502CD72873271D25C2262226DE29E3290429432995287026D627F226ED24492734279B27072A1B2C652ADE2CD32DD92A4A2858273E27DE29822BDA2BD12858254E243E257D28522A502CF42C222B3E2A6029922A0D2B042B6B340030702A4E2ABD2D9B2CF72E3E2DC72B952B682C9C2C2B2CDB2CE42A652CE72AF9298229E22A9C2BB62BFC274B2787255B266C29E3297C2B812A47298326D6249126DF27882736289528EE29C929AE29E42C492BE72B77287428E2299E2A142BEE2BE228D0261324E4259C27E629392D532BA52A932939296E2BD32B682A1C338630BB2B382B602AAA2C5C2C172F142DFB2A022B552BC72B802A152DD82C232CC12947294D2A0F2C712B0B2B252976278827D528D629542C0B2B07283D2658259F270929A029A4280428B429852ADF29072C402C992A6D29AE292A2A1F2B8829212B782819241A231C25252806286D2A8629082AC929BA29502B672A5D2A0533F330DC2BFE2A882B922CAF2D042E612C4D2CC42B392A3B2CB62C882CE92B1A2B96298F2BD92A5C2A9829B329272E9C29FB251826DE283A29EA29CF28E0252C27562726287E29E527DB268027B828BC273D2ABC29DF29C82AAF2AB429502BAE2A382A4B2810265D22712475268F29482D912AD32A9F2AEE2A4A2BC12B382BFD311C33B82DFE2BA72B602BA92B312D9C2B582D7A2B6729622AA62AA42C762CE32A602A0E2ADC2ABC284929C829522A2228D626BD270928582BCB2A7328CC263D287D28EA297D2A9D272C280528E527F4274B287C28EC265527ED27F52819298D2A152ACC2758257C24D9244526E328CB2B102C562AEC29BC2AB52B462B342BA133FC32172E172CBA2C9A2AFB29172B4A2B422A3128C5273728D828792B712C2E2AB62A072A262CEF28F42669288C295529192752265029562B292905282C275F28302A942BFC2932292E282E2AE7281A28722782268326682731283129202CB52B8B29C22616263624BA262E27EF29882A642A1C2B302BD02B982AF22C282CF7345E31402E872CF12AF72BD32B332BB22A422A332A0D2A4C2BC42AAF2A952C022DC52BF22B122BC7286C27632B992ACE2AC5281127C8279029702AC729B7286729832C5C2BCF2A7B29CF27412840292B288F284128E4272E280329A727EA29322B4C2AA0280825A423ED25082AA82A632A5729552B402A9829602C402B262D34357031232D702CD82C872C4F2CAD2C392A70299D29E2292B2B002CBD2BE62C5D2DAA2B152AD22AD52816285C2A9C2AEB2811272B274D27792759295A28E5270628602A8D2A5529C027632726297F2AB928C627A629C12AA829372A0228A927DF28282AD22792259522B4252028C129EF2A14294229082AA4299A29352A612BB433F432042E742D152CBB2D472CF22BD92C362A432A142A3A2B922B822A722A752BF72BBB2B702B6B2A8C271A28FE2BF62A12280026B32533256127FE255E263D29D52AB92A67287D25DA26D427482802279927E229AF29262A822880273928742887291628D42584231F25F126F42ABD2B1729BA27C0274626B827B728E42709329532D42C022DC12A5E2AB22A552BBD2BCB2B122BAF299B292A29982A6B2BB92AC72B8A2CE329832946274F28C62AAE29CD295B273124E423D224B3258A257727DA297A29042814261D2691262626EA26B427532A752A9A29AA292928EA26272931290229542730240E254B261D2810298B294C2795262F28B227C32715286032F631432D462D272B002B5A2AB42B5B2B602CA12A362B752B342921288229FA2BE22BAD2B0029EF28C727EF28212C862BD9295629B924542355251C2441240A256A287528E325F22465256427D624E12505293129FB293128032958285A274229B3288A275E26F3231E25112671258A2884292B27072916287F289B282229A731E82F4E2C7A2A442CAF2CE92A582C252C442B492A252C8F2A6429242700273D2A202B1F2B062A21298B28452AFC2B032D6A2A84272825D2242D2664266926912769299629B4274325A926C728F3265C249C28142BFC291F28A5283C274F27B028E728EA269F25AF23CC24212496260B29102A6C297F292429F0289827C828CB319730EE2A2A2B152A162D112C872DD32C9229E229712B972B3E2A1828D8286C29FB2A062A222CE3289029DB29FF2B852CE3292927DE25F924F524D72427269128E6294028A725CD26A827B027AB29EC27912ADC298329B6283B2882282528E5268D2543258F2631243D25E9249E277D28982A162BA82B332B702A4A29832B3734A531F32A292A5F2C6D2C9A2CB52AFA2C7229B729D32A9E2D262BA029002BDD2AF0294D2AB327AF29DA2921281C2BDD2B872AF3277C24E623EF242B2538261729C028C82788265125E825CD289228BB27AA2929289A279A281329DA281728AE2858267A261F254324B0244B242B270A2B072907293D29B9279B29582B582ED9359B32422C072D3E2BF62C7B2C062C092C092ADA27D828992AB02C922A9E29192A6C285528D327EE28B5289328B929A7283B2679275925E123AC23FB245F250727F4284C2A3C27B325E0266025FC29F529692BD3288526D7252F274B28A328C82840283526F3265D24C92559246F27872743291C28C92818279829C92CA12DF3362931692D2E2BEB2AF228342BD02A6629CB2979296D2AB1297329BE29572782251C27B7271029A3287B26F0270229C428EA27C5272425B9237623FB218C23482514260627BD255B24D82418250126BE27E0291C2A4F2759256A26B82801297D2A952A2627032474233D2521259E2638273E277B265E267825F3267929F52CE5364432762C312B61283627FA273929A72BDF2AEB28512A7D2A6B2A2127E82534251424F12710298F298528F22838286F2B582A73286A25932538255824C924B525BF267B27DA2684266424D6258726582875273328DB252E2576263A263128A1293E2A75274325E424A82554243A2559267926D1240125B0247D266728F72852340231562C872AFB2957286C27A4283C293A2B9E29372A3C2B952A73299928BA27EF25AE2586269128E528A428442A9429AF28DB26AF250E252F253B241D238B23F5244824E8245B2393257623512593257126A32701244E2319244C260C26592884282D264024B9231523F92201268D2697258224DD23C623182505260229EF333732592D7A2D202A742805280229712A5E2BA42AF02B972D5E2B7A29A1291F286A25A9244A26EC26EB2999298429392A83298228432848252125BE239F2198226F227C229C249A231B24302357230C23FB244A251A246525DD25302445250C27042876277723AD230225C4243525D1259624612450244123E124F5259E288D324432442C382C7C2BDF29C229232AA229522A752A572A272C482BFD281327282508248523FF24E42537282129A828AB281526692506247B25FB23A8225F22CA205521DF20DB22C723CF2329238622B92321258E244224A0251523462337244724ED262D26BE241C23C42331253126C527B32536258C2531259124E82651271D32562F2F2C7F2CE72A232A0A2A1C286E29022A162A992B102B502D402AF7289E2593244324C924342519285928EE28D029222597252B25CC251C23C8228122FF2024215E22B122A5228D23AE236423CC22EF25E52405251824C223782291237B27DB2640260C240D23A0249C25D926F027EF278D251A26C921B823942560272E32882EA32BD828E929A02944292C29B128CD29DB26EB26D9296C2C552A1E2893264523EE2308240026D82501273927B22723255024C723F923412487224F206820C32043236F2391233623FB2332240B23182463264125EC23A22490222B240D250E2895239F22EB216623B6226B26CA25BF288F25BC24D225C824E3254C279D30AC2D3A2A2B2845272A279227452793273C28752622266327132A93272926A7246924E324492245258B2423254926A9242C24EB22CA23C8219D24AC218D239021962285222224CA23B425432400243424BB23272402252C2390242822A12446253E273A257D24042302250E23D9254B260B26B924EF241625602677260E27C4316A2F8929FA267826C726BC275C26A2271328E8260E2734260A27D4285A283A2658251F25AD238C25E223E22331254426562496243A234C22B422E5224D21FD221723012151228224C9239B252A255922B825E0251E245521C7235B23DE25B224C225B8243624A422902410252725CB24DE26F4248D250E237224532376254830092E7B282327B02697250226F327B22760290028B42583263F27B7260D286A254E24E123D4236F236F238225582661251224E024AF234922CA22B5235D2292217A22C6211E231F234E246025B824492348232E24A023ED211622B72264245124EA232025DC23F92190220B238623C425C725BB254325F8223424BF24EF257231862D85273127852761261B270325E0260629972798260D25A52590251A26BB25E7234E23772489241C23F7234825CF246123AA2490223F24F8225B206B224221E1201B228D238E210224522458224923CA23AE2419238023A723BC22B82243229D23A5230423582176221E222F234E244824D024F3259023D424E223BA258C2F852E3E281426EF28A3286E2659252926AA27AA247024B925FC2368253925C3248F244E233723E923AD233724CA25DB2447221324FF22E721722116221022B521B6217321C9207C215E229E22ED221223A1243624F922DF21DE21712298212D23F3227C227E21E2211B22DD22FE22D825BA23382484244824602429244A26E42FD42CEA284629F927662726264E24D023BB24962595240A254B235C242A24FF2571232C231324D4220D2404239624CD228222042424227E2231220620FB1F641FD220A01F6320F220AD2021210122FC21692312231421CD228322562181217F2241246922AE22AD22F022F121A0228522A3237F248F2268246F24F02329247630842E9C29F827B22661260C2510248A232B242424B622EF225A22BD22682480216B22AA2243222C23232472232123E3218222D623AE234E218F223B20052171218621A1204321051FBE203C21C620C7200D23D623882209226A218B218B21EA2216234F239F2224229123F721E3220B255B22F12294238922EA22E325B1259131822C862766250B25EE24F424BB2239259421BF22F722922253228723B52332228121D0225D210E2357219B21312282226221FE22F8226F21FB228B21FC212A21822180203F205F1FD82078212321432168219A2394227E21C0205E21C9210622F8227522E22101213023D2206922BE21A221EC223F23AD22C7248B241826E52FD62B7924AC235525BF23C32398220E23C1226821CC23E7226F21B32216238A224A230821822285233122FC208522552384209B218121BA20F620C21FEA20D41F7B20C3203F227D1FEF1FCF208A213B20742176232221D02089227F2159226022B9204620D020232154223622D0207D22A822D1209C218521C3215E236B24A22D742C21277523C8235325A7226723C0225623F62389227B2272235E22B521C6231E248623AA22AD239F2146205D22E4225C213F2106229D2236207D21D1204F1F4C22BD21AE201B1F3F22B520D021EC1F30224F22E020D22070223C20AA23B32064236922BC216420EA218B210D228623152279206E21EA216423AF23EF2597308B2D812649232B23DF2472225823132208239B2292224521EF21AB2074219321DC212C22022107224221B92084212323CB225021CA205D1E6920A71FA8218B207920FA1F2221AA20CD1E6022EF20C61F842047213F1FE3201621AE20E51F37216B21AC203F21211F6A218F212C213021DE2148213922E2211D22152382244730932C8E260025432264223D22F32268220224D122FC214A226F2139235E236C20C22205222222E1221222E920DF215B218A21DB21B01FD31EAD2148219F20F31FF1200422E21F5B1F4E1E8020161F4C203E211321C81EBE1FB521CE208F211D1FEE2132225D21D3210D21D920DB20BA220D21FC204D21571F87226423B7259E2EDE2B0B26D62315236C223823CB2381233F232622B6226B239D2129212822E422EB21FA204B21D421D121F720B3203321E721F620611F2920061F372112209D207220592003210E1F5C1ED21FC01F6C1FB3207A1F931F631F09203A211820DC227A21BA1FC020EF213E22E11F78200B214F20C11FA72173209622EB23BF24022EDB2BE326A522D3212421D82008239622B4223A21D9224B20F9209C2035211A21BA21ED2083217B2140223A1F71206C23B022BB215C204F1F3220A21FB22145227520751F0221FD1D381F991F351F1D204D201921C821C61FEA218920D11FA11F05229620281F511F41218A21E51FD720341FC12057225B21E3214224A025622EB02C36267C245B221823D522F1210722D122A321A5221B21F821A12050217D20C31F26219B200621771F2E1F3020C621022114204D1FB9208A2067201C2182219321D5201720C91E78206F2060201720CC1FB1200E1FD1205521E6207E1FC820D820E51FB3201E21EA21AE1FE11F47202222AD1F25218620282145215525D92FA82BA8249222C12215224A216421E422C82098214821BC20D2206D1FF01F0621BF20AC1FB721CC21D720091F3A219C205E20F120172173204920681F151F0F21ED206C21EC1E031F191E1220201F1A1FB51F12202B1F7A1F2A215B1F1C203920282068203E20772091200421E821281F831F98200421A9216222B1231125BC2DD12B2F259E211D221F22E6200122E92122221823A02256206520082017213B214F218E21F320A3204C21DC1E50216E1EAA2028217B1EBF20511FF31F9C1F5521A320D91F40200A20231E1E200620261F20229421B61D8D20B41F48202820DC214720151E10216720B81E4320621F0A20BE1F9E208D21C01F7B228F228A25222EC32BF4256E2274212B222B228921CB234D228E210621F7207E1FAF1F6021792296204221462337206F21EF202B213C204B21352240203820891F82204E1FCA2041204A1ED71FDA1EA71E6A205820EE1E32213E202F20D81E5521D61FB8222F20482015204F20421F4C21851E46201C21BC217420DC205D215122AE207123142F8E2CE325D32128239422A822DB211F246A2330220E2273223321761F7E21BA219A224622E921AC1F0A22AF20C521A82040203E224B20D61E1F2029222220DE1F3E1FF31D2420F71E4F1E3520D11FF01E69212022BD1F1B20AA1F261F831FF61F0321BB1FD21FBA1E932006207020D5214421EA21AD201A2091211424A624EB304E2C90257C23A620A123DE235422EE22D1232A23F523852253218120F2216120EF20F3227921DD1F0E2109203E21F6202220002215210620FA20211F361F7D1F861E6E1EC61DDC1E731FF71E731F32215821A020DC20631EF81CF21E491FBB20F91F7020CB1F981EFA1F801F032096213E201C238921A41F10202E23E324C02E +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 403F + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 55 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 0009 + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 042973239F239D21A9228121E7201321A921FE201A20341F461F141F4F20081F261E29200420F71DB61F301FE320861F3F20A11FE620E52065203820CF213520E41E661F6E1E0A1DC91D9E1FB91E471FCC1E3D20F61E851E4C1F891E271EA11FE820EE200021D41EC2203B200F1F20212C1FCD2175215D2130202E237B244B2FF52BBD2446218D232C23BC218022DB22E522A320C2215F208D1F3E20CC1FCE1FCF1E561F8E1F4F1FB11FD920EC202022961F98214A203F21C8206721C5217E1FAD1FB920AD1F7E20091F07212F1F2C1F56217E20D71EDF1E571F7221A720FA1F6C20F4208B21A22052223322F320EE20B020302259232E23D523AF23DD25D12E2D2CAC25A224C323C6224322B522C823E2213F24502307235B218720FC2023214B21F321322139205B20FA213021CF201922A9220122F3216A22C81E2921F72159211722252189212820422160200020DE2093211620BF200F21A01EE920A0214F226C223F222821D822DF2286225421432141227C25F9237C2468248227C630A22C94287E25202399235923A2245B236922E72193212A2168213A202B22EF1F842169214A214123AA200821BF1FAB219B21DD223722C7226221AB201B1FA620CD217B212C22142022204821F91FA41FFD20C9217D218C21C120D520F321E1220C244D234E23A52253225D21F120432166222923DC24A6269224A625CA26E52F102DAE260223CD22ED22D3231C2370239A2398221F22A3217A201720972169217020262298219F21672152214A23421F302087237D21891E8A21232173200F2083210D224E21701F7C2009213320DB213121DA22E520EE203921E521D120572183236E23BA247F228C2312224D221022D022F7232B268525F725AD26F925F62E742BAA267E23BC2452226F24E6221C232B24CA22E7222D23C5202B21452141226D2214229F218123B7210E21F12167222920DB212F21402140219F21CD209721A321BE21FE20841F05227720CE2194212C22B922E122BC2291210F22AD21D022EB2443259B2327240924D32304230B2396221D253F2983291528E0287928C02F062C9E24AA233C231C235523A024D423572352243A23172255226F21D122DE22BA2374223E238922B323772396217A21BB1F00208121C8212E22AC21622391224621F4219C2156213A217021BC21802126231D253023C922FA220323E521AE23ED258D24F42574247F240F24272315235B24ED237E277927BD28332885264D30AE2CB326DC238D242C23B821162381248D2400255A22F4220B24B2242F222322DE23B823232488247B23DC216822D623B820FE209D2177216F2253236422A721F121FE215222C321A52268249F24AB2129259A24EC23AC2212232E210C23112499264827E426F225BB25DD2422241F24D123A923D6248A251B2843270528CF30992B3E274F257C23E823BC228E2227258A266D259B24B62367238C222F231A21EB21CB23ED23F523B523D521DC1F07223E201E22B920A62039212C239E223722702209235B2201236A2320234622B7224E24EA232C2542236825FE224122CF23382680264A28AE266627A525812460257C258B23BC23EC23E624492628255630322EDF25F42328237323AF228A243A255A27AB25BE263626C8232B246B23F822C523F322E62331231423FB22A220942138205E239C232B22CF240A238E22E122882268236822B6238C2368258524D222AA23D724A12408241D23352383210023CB27352622258E26E52651278A2880276B26C22540245724A8238A25CE25DC316C2DBD29A727D924DA248023DB24BB250427A2273629CC288C279E24332438242724C82332241E24A121B9226521F522F2204221EF2257236A2485234E22E2215223312234229023942355243D250C23A623CA22F3231F249E24472378223D24F124CD24B224AF25E0265426A9262629CE279524AE232A22EE230F25512547313B2EA828212674270E24C7225D2433255228E828D6288928C7263F2583235D24992357241C247D265923E924E021762235233C24D42514255E2341220523332277216422E420A821C2228A2303250C2480240C234024CB240D246722CF216D23792425250326FC237126D626C426CB28D7267B241023D1229B22B0223B269530882F9F2A1A28972627259B244125B3252A28AF271B27E12859266B26B6246E253425E92408243C2682249224272353268D25D925472637255A24CB2451232024CE226A23DC224121B920152315265E24CE22832458235423832412246122B3247D249025FD230A240A255F2545251626BD242A23A424CA226F233E24C726862E2E2EFE299228D8279C2701256B267828B4299C295F277925252405234324992240251226A4252026BC255F25D125352689260726E7242B24B925DE227322D521FB234523612224216522FE22F824DF23EB25E92521244C23CA227423CD239B25CD27C02591265025AA261A24E522C7239B230C248D236C238624E624D9269F31082FF8296F2902283D28802653298F29702C472859273F260B23D622892233241F24F2276F29FA275D26F425FC26A027F326E1249523F3244424FD22A2234324BF251F245823902213236523CE24B425A4259B26AB264624FB2361240A23DE24BF27DC292E2BD729852A2828582592249B2446230B25442383243826C3262630A230162BE127FB256226A3279128462A302996283B27D22404228623352466252125F028D52855285129F52931276E26B3234D24632414240B249024B7242225D2259F230E235922B122B227F8262227142940264126C9250F257B23B5231F254F281F28522AD6299F2A202A84252C259B235A24462596261B26A8260128A330632E562AD4283928BD2646272D28C3285D27A926742750265D2509255D240E256C262C28FF29042A812A0B286E2731274F254B258F246D230E268E2723286C288F272F27752529244A247225CC27F828B629262A1429AD28612912268A2435263E272427EF28E42790275826A324C2213424B92422268F27CC27B8279A292B33A02EAF28082746255227B828AF274327E926A5269026472724278B26FE2356240725E127FA2AC7292529962859269D25572327252725D42567278B27B329262A3C2A2B27C223DF220B266E27F0275327EC285B2B772A0B280729C8251D240824D1264024AE252624D025DD23D7256F245D251325292564263E274E27A22857343D2C102AEA26BE2791286028BF28DB26A82614267A287D2718288D272E27B42428257C281628052B432A95271A27CA264223F124E324C0267027F327C429A4295E2874256824E9212325432620280D26332950293C291328042627240D2443244A24E524F62560226A24462350225C22042425245924A4245A277927042A6B34AC2EC327CB25D5266F27A327A2266B27EA25B4264628472A8F2A6727A82787266625FE227426D22756266625B024372400259E243823732345284A261828BC25AF25792569239F23F6233D2596250527432855289B26C7256426F523D024362584252124AF23CE23C024CB211A225A258224F9249425B1248C25C3274529B833692E5229D4282328C827A327D6257A264E277D281F2B9C2A06294E273F265626A3244A273E256D24FD249C2620259F24AE25AA248725CE240D2555261026E426FE250626632412259724C125B2289026D9285A2888267B259A23ED2517258D25C325892393230323A42458244825B029D227C5279D2995263C279427C9288C33632F632AB52A0F280B265D262C266A26EC27F8281C28C5299A29692969278C276228922539273C2495246C2490257625C824B6259B23BF2306251924B325E7247F251B26E625B726A7279C276B27D028E6292D291A2586243425A32686260E2761265B26DB23FA234C25772457279227F627462A592A212B742A2029EF2A3F342330E02A15294F281E27B526B426F326E528F1288329C329B428F126032A012A722ACE286426FE221622A022BF233C24F823CE24C323172466241D23D8242C256A23BC2461262328B9287D2B952AFA28042B96299E276325BF26AE278228C0298B290C28612782244926A228B028EA2845298A29E52AA12BB42A932AB32A2B354E30ED2A892A8C28A0265D248D26C9282528AD26C62667277F2502287A2B8F2A922ABC2924268B2425239B239425D82650260F2500249D25F72549244D25C7246B2465251F25E825662AC82BF42A752AB22A2029D02883278C28982892286A2A6627602649254C245E264F26202810299E298129312B252B192B0729472A7334E930282B0A2AE128D0276C260626A427592A4A28C0286427A826CB2751289E2954299B27E62516270A24502477275A2852282E28CE275B2795260F27B1257525FE23CD24C0253928022ACE2A5A2B4528FE261127B0276728862AE72915299528FF2862256723FA238E2450269B266A28AA2779273529C429AE2AF8295728BC31692F812A0E2A612B2D28A9262A27E12724288A265F27BB270827A028C029152AEB29252699254125B125D0275827C5299A2A262BAC2A3F29132B072A462986266F25D924882633270E273C297E2844258526882570256626872AEE296C28222892256924C8234F22DF25E1252E279727A12614256A258D255F2704275F298A33FA2E752904282F275228A427202708272B270827B2282729D2279A284C28D527C0275A27B2245427F82606270E28D52A162B732A262B4A29632A2F2A5E29B9255225F8244E26D12456272F26EA25B6250724D525B0257326262886290B28A327AB24EE22C3230724C125AD253426FF2517259024FA24AC257525FB279529A133BC2D0B29292543273D273D27F5266127F926AC265829D728252C6C29802832278925E6262C2715292A2932296429122AFF29BD28F0263228B228AC2805279A259725CA250826A02495253F24FE24BF238824F5247425FE26A0262E2710257F24A5254A23EC229725F1262427B22669259625CA240D26AC251427DB27602B3E35F92D4727D526522731274426A226AE269F27532773263229042A692BDF28CA276927D4267F29422AF22AB92AC42964291E282F287B267F269228F8273E2559252D26F4264228512665267B2579232E24F9254B26D9271D278C27872485250E2748261A25C423C326CF286129C52878281A27E028AB28832687266A29AC2A9E346A2F5A28A523FC242026D42868276B277128B226C629402A9E2BE5297629E1282228E9275329B52CE029782A1129C42A6A28ED27C127D326C3277626772599252F26A827F1285529C1287C281627E326B9270228D129D926B9269F25C02632283829A828A5279E277329F22A1A29C227472722297C2B88286F2607280D29C334AD2EF227CB25C4254F27572607273C2982286E298726BF29422A2B2AC727E724DA25022791286E280B271527FA29652A96299E29DF291827192732267125B9250D272629512A652A462A6528E327C5266C261B2915261626BF26DE26EF25AF27CC27E0288027D325312A7B2B532BAD29C028E828072A8A281928A127A12811337A2E672B41280E295F281425BB251C26F528DB287D2932290E280728CF29482A3D284E2898289E28B7253D27822784279F28062A592930273326822381256626652BBC2BC22B24287827A6283B2776248B257B271D264A27AE2706274227ED2599262226D42468272D291C292029D6287729572B142AD428872504284F2999337E307F2BF52A5B2A87299227DA277B27C526F927282737269127912773284329BC2AB629322B832968286A2820286C27E7275729A4286C273625BF240A24D227AF2A192D112BD928E22532287626CC258127B229472AA929C029C1281A287325A7259A24CC248B26972AA62AA8292829512A632BDD2B4228FC26BF2744294434F230372D552CE52C312A77290027E7271029E527CA2559252725C1265C288F29022AA92BCA298D29012A4F286A29F827D526252726273323E2238724F523E1262D2B7B2C1B2ADB270228D025FF25B6253729412B5329AC297F290B28B1275B26EF244624E32486260B28762625256925B0289629FE275B26FF266C27002912358B30162C952BFF2C6E2CBF289F29EA299F2C8B2A8428D0256826982529287428C5282129BD298429932B9929702802285C26DE25EA267123A52277237B245E279429792D8E2AA628F2289428E727FE257628BB292F2AC72954287C28A92A5228A027A6242623B923BB25D5261325AA2571256F26DF26A325BF2587288629A434EC2DA929AD2B8F2A272B6F29EF29122B552CD62B222A3728BB256C25D625FE26D028D627C926C2285327AE27A3281D272B2572269227E2230423C423AC239C266428EC29482C192848266C29EF29A12631264C29C5296F2789277628FC28252A6928C3259F25D4268327F225DA22EC238124802525252C244A256628722A9136472F7B294A2AB42AE029C2276527A92A762CBD2C722B4B270025B023A52522274928FF28AC27AD286727A4260F262F2884270228CC2ABC2760249524AD23662776284C2AC129AE26DB2402276A26DF252E271627BF262525CE25E7237726412783290E263627B128C327F126E725CF26B22653262D26B224CA26A2271A2A3D354330CE2AC929A829292A4129AB2ABF2C292D012C312AEE25F8229E26C426D627BA2A9B2A6E2AAE299929812563265928B2281329912A84285C25D723BD24782879296A2A1829C826F72519269C27F626C928FA262C2887252A24E424BD24192523267F26C6264428EE2AD3291B2AFD29362A5327EB27AB25C4259327802AE1353B31B52B762BFA2BC22A7329562A972B4D2E002A3D2827268325B0264929B82A582EE02C2D2B772B5A29A1284E29AF28092B8A2B92290629AB24B423D825182704292A2A5A2A79272C27FD277128A829982AB5287D286E276A268E2413259B267826F725132746288029B62AC42AC92B592B482A412A3125E6267A280B2BA233CE30FD2B172C372B822936299029BD2AC92A2629FE27DE26BF254626EB28BB2A1D2C1B2DD52B522BD82BFF2841292C2BBE2AE22A95298C279F246F23FB241825EC26F9267C27E42742293B2AC829EC288529E8298029762867294926D0259526D226C926BF273C2832295F286328922AC82C762B512BFD277627B128522BE5341B307D2C1E2B602C5E2B682AD929452A062B90281725DA24CE26DB29C52BC92BD42D0D2DE529222DA52A8B2AFC2A132A422A0D2A652791258C268C23C724252672262526AD266927EB2789294B283827EE270A28EB282829992825270128302A8529CE29422926290829AE2794273B29372AE82935281C25FE24A3266F2B2634A42F91294328782A872936297228182876275126A62654276927E627E829FB2ADF2B622CF52C552CF62BB62A822BCC2B842A5F27B325CB249525792630263F2825272128AA29BC26EC26A6289F28F426262688278D29AA2B9C2B4F28172704285F2BFA2A16293E2844288426F526BB272028E327AF2741245A25932792290932CD2FEC29C22A042B572EB028122984283126E825C7266328362875276329012A692B3D2C352DE62BBA2A182ACE29E12A162989265125E2269727FF272A29D029272A7429B62AA1287F27722768283A28DB28D427F6297A29AA29022892260F26BA270C29DB2759273928A627BB276F28F027C128ED271627D6251A257E298633462ED629682A6E2C432D302D1A2A9E293028DE2732297A2AB629D629642707288628742BD12BC42AB4277F27A02802299126CF286A273F2545295029A129F12BC62B702BD82901270D260B27FA29A129C729222967284D28F128F4263125EF2589248D24E025FB253A270128DC271D293729392864275A242323A2250E2AC134B72FFD2A892BAD2CE12D882DC9295D2888290E293B2A782BC2292528CB28A327A4269E2862278C2764287D2762284C28F126F32553258A26682758288329CD296429C92A4F2A1127EB267428D428A62A1C298F294D272428F7255B26AB242124D9244725C7265F278729EE271228F6289927C8266E26B3241C25C425E6287C331A30E22A4E2A832B5E2C582BAE2A252AEE292B2BD92A1A2B5E2B112B8229C226F12409264D263327E124BE2568275F2707284326CD24DB2466257523A22735286729602A942A0A2964271D27C6299829A927CF26AC24E5254F27B624752602260E252B2538271627942A2D2A89285E28F3260A260027BC2460250428B428A63292304E2C932A422A142C3D2CD12B1B2AAD2A4D2A4D2B9229C8280A29D5288226EC2506288C242E25D5247324F5267829D028C126DE24C62587258E24AE2690289B29AE28EA281629BF278E276F29C2272926B0240326DE26FF244D278D272F272A25F72639265C28952ABD296B286B29802805266D27D125F625A3264628383397315F2BAF29DF299A29C72BD029E629B82AE12B992A2C2A68289727B0268926B9275F27CF2748272A253F255F29F42A1529A627F02679264B26BC26AE2579259F26E6269928EE2665262C268A2560254425702645266D274D285F276C28BC295F298C29DF280F290B2AD329012ABD29F728F82898288927E427FF27452997332F32162C76287D27EF272729F928BC277A29CC2A5B2C3B29DF261F2666279626F0299128B829EF27E72478275A29512D0C2C1B297C26F726F228D026C6256A24D6244B25762888251F258F2391234823B424C127D7274F29102B712AB129042B012A47277827C5276A2AA62A74298F2A282BD4295D28BB274F2AC32BDB2C32347C30B52B2828C8277329E32882260C28452B642B0F2A0A29C92688272829682B0E2AAE2ABE2A752A92286E27D929B82A5D2BF22A402946291729652830283A26B3257525F6251F264D24AB2365239D241E25A32858298628142A022C452A9B2894259E275026FB28142A812AEC29F629AF2A142BC1296128962A572B0A2C44354330672A4D298A28D4292D2BAE2C7D2B412BA329112AB8282128A928232B922A012C2C2EDA2A922AEA265D28362A682C9428782629271B279728FE2AC72A9827642406251D2594250225E0235924E325BC28912871290F2A9F2A232C4D29F32805274727EF250F27F229332BFF2A572B7E292328BC27CD27932B8C29EC2BEA32C02F182B452A0A2B9D2B692DD02C9E2B392C522BFB2A7F28FB278F2AF42BFC2A242AEB2A6A2AE52B57273A27702A792C9628AC28EC26D827B42AAC295F2A7D2873262F27D725CA26FD26B227532747272E289029B128D626DF26F4266F26BC252F25B025D225EF25CA2B252C8B2BAC2AAF280A277725EA243127EA2811290033B82E8C296A2B412B622DAA2C272C462A722CD42C4B2B112A742A002A282C7E2B552A112C312ADC2B7729772701293229B129CD26AA277527CD296C295F2A9E27B1250A271C28CD28622A3F2ACF2AF829B4288D279525C1262A28DD2525241C24B623AE240025DD268228F729082C812A7D261324EA25652446266026D5275E340632A92922299629C82B422B982AEB2CFD2BAC2A4C2BEA28DC255D26F428292C9D2A2B2CA52C242C472794264428F229E12938295A27892705281529FD29FA27E82717271627D8283029702A95293C283428D026F625AA2837279F261323C522CA2418242F2482265D295A29FB2A672BEF260123EB2495237C2644276A284A32A7318E2CAD285D29FF2A192BC32A942CD42B302C7D2B782AD8245926A0265328AF28EA28D529092A4E2AB8265828F428A22893293F2C83283829C02AE02AF528C127FB266C27212A5F2A0E294D28AB26AC26A0270C276528B628F9289125CC249526BA2627273027BB279D27C327DE288826AE2477266A2542270429A0284A3151339B2DC92B602913272D298029472C3E2E972B102B66291528F224132614255C27D726942611294C279D2716275C291A296D2AD529352B6F2AC12B802CAF2924288828F6265D2AFA2AF029F2264025CA255826B62594276A28CE286127F42595264728C42A8427C3276C267925F72789264E25C9276F266227AB2A1A2C27334732A52DC12BA82AA32798273C29F32D862B7129982ACB268A271C277D288526E926A0251A250A26AB27FD248528EE28372B582B3F2A13292429E129122CDF2A74288428F727582A262CE729622A31271A26A1282B28662857290E29B627FC2646276C28D72AB42A9E28902607275C269F230C2214260726112A862B3F2CF1331D33E82BF92BDB29B3282A263E2A002B032BC22AE128ED25EF262E28E92936282A275026592511267925C826ED275F2A082B1E2A992AF9295E29C3288A29C128C9276E27A627F4289329242987290627BA287729852B9429932A4A28D0271E29A6291E2A3B2CDF29222B56281E27F5244E23E02499264B273E2B0B2BA22BFA333631872BD32AFE28C1253A26872718297A29952801288726F7270E2717299D280A28E42756280F28EA2629276D2A062C732B232A3B2BCA289C2A1C2A1F2B9C28AC256526A728C827BD29CF2AE7281227F527E3289429292A212909283729092A8C2A7B2A972A5829FA2A032AE229E02751234D258D24F424D7273B28C02A7D363430362A862A75288C26FC257525B426D927D226BE278D26572765289229C1285C29C82B992BDA2ACF278C2702290A2C5C2F9A2CFB284F260128DD299A295429932629276A261428FC28162A532A1329A4282A2750292E2880277027CA27FB2A162B7B284B2810263829EC281326542722252B2432241E25FA26DA29E72C7C366D2F812964285B27AC263326DE23C4254A262C276B29152ACC2A6C28D327AE2A492C312D372D4B2BC328BD284E294A2CAB2C2D2B5D2A9F28F625EA28F326882561259B263326E526FA270D28F827D127E1278627672543268D2462256128292AA72BAE2859268A25112894273527FF2506266F254325FC233028CD2B3E2E1037962D8329202737279026F725C725AF25522745273E2A382BB22C992BEB2A6529602B5A2CC52C222C6A288428E72722296329AC2A982888260E2557262427612762279E26D927D72852284D28D52953282E2805276E26F52517263E247925E027492BF32BDF2AC6274128452742275C281D283F284C28822723290F2AE72C3435DD2D4429542A5A28EE284729552707252526A728752BE22C342CF42C052AB62A462A012CCB2B2A2D52282828882597266626C4287E2623259B25B026E9278929CF28B627412AE5283A2A1F2B912BF328A8296A296127BF25732796236024A1269429EC2AE329F12944293A296129822A612BCA282C2CAF277227622A372BC4337D2F5F270128DC28B32A892C8C288026DD27AD27A42A8A2C872C922BB32A26271A2A2C2DFA2C3F2BFD2B99283224B9257126B025BD271A267025AE258F289A29E02A262AE2284B293A2A532B732C8A296A29252AA629FE27BB26D9256623EF2453254E27EA250F2620281926D1266F2BBB2A562A022C752985270C292227BD32AF2FA02AC0271F29F22ADF298D294128F129652BDB29E22ADB2A1A2B4A2A372A8B2A6F2C052D632DDF2A952A3428EB248425EC28F8283A2964292C283C28F029882B4C2A002999289C2A4D2C5E2A7E2A4F2BFB29782BAA29A029E7255524FF245B26D5235524E8264927562528254D281B298E2C5C2B2B2A3B28012976293733CD2F4A2C8C2ADD2B48297429D629652AC72B022A1F2DBF286B2AA2291E2AF228012B822BE12C7C2B5B294A28C8258627B2275C29D82B802A252B302AE427A928AF29242B1929EC28DF2BD62AEB28A92659270F29E72AAA2A48295C27F9263926DD28B0269D254225072612263224812637262D283A29C42925291B28092AFA33542FE62C722B8B2B352CA6297E27332AB42A742C932B5A28D828CF27CE29962ACC2BC52C7D2B1B2C322A8229E929CC286827C828F729842B0F2B412A3B293A299429A12A602C452B0B2A292B6329CD25F82464294B28F429982C772BC32A8F2A4229A6284F27F724FD2850278226E1260827C52757280C2887270B2A1C2C9235E0300E2D832C282C722A172917278729882A562B3F2BAD2AC326E9266E28842AA32CBE2C2C2BBF2CD02B252A432C3728BD26CD281F2BD429622A592B6729DA274529432A042B652AD12A1B2B292A79298628AC289B29A12AE9298629732AC5288D28622AB628812665268B2656298B27ED269D29AE2BFE28E1275B293C2C92337E32322D6B2C3F2AE1299B295D288F28972AEB2AE029F0265626AB26B527082AF92A9F2C762B622DCD2CA22B242B1F2B612766264B28AF29712BBA2AC629652717294C2A1D2AC02B592B53290A2A50288C29A12BA02824296428E325C028AE285029D22926285B284B2799294529B127FA27662A0C2B752AD92A462A2A2B6A34C931C62D362CC02A8D2A172A8129FB28C12B622BE32A81297D278727E429FB27DE281E2CD42A0D2A302A96299F2A6D2AEF283B27A2284229EB2B002C902AC12A412A462BD32B89287D283129DA283C271D2A3A2ACD277D24D7261B269B259D2802290F29102A3D2A3F2B872988298D2A2B28FD29892BE72A9A291F2A552B28338931412D2B2DF32CCD2AF4298D29592B542DCF2C6A2A542B15282329592C062B282C3D2A3A2ABB29D628E2289429312ABD28FD264C28AE28E62B452B702A9628B1299A29952A14287D27A026B526AE267729E228EF26AB2331261225A5262028812886286229592A6D2C782AD92AFB282D277928FE28E7295D2845284929843368346A2DD72C632D112CD92A87280E2A662D562C4D2A372B7429382BD32C8E2D142C4F2B602AE52724280828A82A292C422A9B287B277E29192B402B0C28D12630291329A02BB0286A274A27132620264B27562816240C249223F7274127BD27632A5029A12BDD2BBC2AAC2AFE284628222834285D29292A542A4327B028FC33BD314F2C342B042B9A2B80297829332A852AF7297D2A09292029F628F32AE12C4D2A0C295529A6285B26D026A827282AB7297D295D28EE274729C62735279928E626512815294328E7280B27B82513265C288F260F272425122674262E291F2ADD2A922A262A032BE62AB42A382A912AB227D327A1294D2922290427D627F2336131202D2A2AB52B002A4828C6297B2AFB2A3A2AC6281028D927962A6C2BCA2A342A5E2AC0298A2903277D2922294F2A67290A2BD9286228662985294D274F2735271926B4273127A5270629B728292866285529742959271F2766287C2A2F2DC22CCE2ADF2A1C2B9F2C412B1B2BF42989293F29402908278827DD262C28CC334E31942C282CB42BC129F8277528EB28272C172BF42A9A28D828102C762A782B462A02295C29AA2BE829A528F229B12B642AF7292C2855285529D127C2280D29C4294629542983278A27B8290F2ACF29342A842AE1297A274A286228D528D42B3D2B832BC829F328232DA12BEA2C1C2A43277928FE2845271D28A22875297B33932F832BEE2A9A293F2ABE27E3251929622C0A2CD62A77288D289F29542BC429A0282728BB29052D662A6B2AD229BA2A7828132A922A3A29B52A3529042AE22A4F2BAC2A33291728D326FB28962BD12AE729322C702AA32999293F29B92A342B9C2BB429CE29DD28C12BE22C8D2BE62AE22623278328822649280F2B0F2DFC34052F552CBD2A252A4E29D627F427B32A362C7B2B1C2AC8298729F729EF281829B026B52798286229C82A392C5B2B4E298D29642A7A2BC22ADA29DD292F28202A962D122BB82A2F270126FC27AB2AD32AD22B912C192B8F2C9F2A4A285F2AB82C2F2D882B142AD52A212C102CC72AED2A7D2781254F257A270E287E2A9D2DF434562FE52A0B2B002B1E2AB529A029DB29EC2C6C2B612B272A992A642C7D2C372BDB298B28DF29892A762B582B772AFA29EB29DF2B6E2C5B2A482A0F287B29F92A472C082C192A0827642505288E29D428B12A302BF32B3E2CC02AC4296E29432B632BD12C122DB5285D2A7B2B9D28352872242C25A2245923E726892AD62B9534252E4B2AFA29802B4F2B702B712BA72B752B852D762BA82B972C512DE12C342CBD2A0E29DF29FA2BF92B3D2D852B792B842B992D2E2C3C291B2AC9288A285C2727299C2B3A2C4C2967270329AF27F929982B902B3C29E429F32A9D29DC28B3290D2C732D302A822A9729472A2A29D4276A25DE253D25E32540297D2B332CAE34DA2E2B29F0299229C42B8D2CDC2A472DCE2D3A2DBF2C422CDF2CEA2CAF2BCB2D99285629282B552CC32BDA296E2BDA2ACB28D229012AC32945296A2738288A28DD29FC2ABF2AD229E7280827F22670284329062AF5275128462880282428682733287F29C429BA29352A8729D02A7D2AF127892502250F262328AE2AEF2CE935762F372AA528522A5F2CE12BF72BD32CC32C962EC52D962C7B2A9E2B5C2AFF2D762B342AC329302B032BE32AA02A302C8B272D297F293D289E2711272E25F727282AB72BFD2AB32AE529B729502A7A298C299429F227B9276E2756251B261426E12567285928252A032BBC298E2CDF2AA4281728FB2662247E27DF285E2DC835F0307F2BBC28EA29062C092C0F2B542BD62C732D9E2D0B2D3F2B0429C329CF29B828802880282029DA2AEE2A8A2B2F2AAF2A602A48295929D3285625B92320270429C0283A275926EA26C027552981287B29F12998294427AA26C7250624B72669278327A3286A2AEB2AE72A892B572C642B3F29D0286727F028FA2AF72D9D362031EF2BB12A572A052C682DCC2A622AA12A042C162CA52BB42900286228CB2761291D29EE28A1282E2A472985281629812726293E2AA628A629512625268C242027E626D127A325C325D8265229232892271F292F29C926642ACA26F926E0278B286A2A902AFF2849295529E62A0B2B1B290E299E272727B427EA2A7C2D25358230042C01299C29C12B262DF02C7F2ACB2A6A2B612BB42A9228D2262B27DD287129322B8729E32AF32BDA293D295F28A6289B2A142CC829AC2A0C29B6285A2809286928B2286F2724278727AF2AF328DB266F279B266F29782910291D2856292F2B582C222A6A2873279328A4298D2A8A280A28B32678250E28C92A552DA6353932492A632AAF2A3B2A892AF5292F2B1D29572AF42A012A3928AC274D28D228742AAE2A742BDF2B5A2C822AE42A8029B82A6D2AB02B8B29162B06291F285D2891297C2B07292429B628632A762B392B3D2B61271127A62624292828A426D828DE29CD2B98290A28AF2765268E29962A2D2A5F288225EB23A7272328062B4236A530FD2B132AE8296429B0299E28DE28A728DE271B2969263B2823286128852AC52AB82B3E2B912D242BB12A7B2BF7293E2AE12A7F2B312CFE2A5E2A092BBD2A702A7B2B1F2A0C2898289928632A342A30290A283C267D24DC28AD266228F4281329662AFD28BC2654275027AA288C2ACF294F28D928AE2426248A25B728FA319A31A12B982A3C2A2D2B2B2BEB28DA2A4B2AA12A372A3528C1262D28E828232C9B2B922AB52BC02B322C0F2CA72BEB2A6B29842BA22A0C2D8A2C672C422CAD2B6B2BA92A9A2A6F2829268E29CE2B8B28192832296827D725A9259524FE248B25DB27EF27F2273B281D27ED263C287F2B5A2A2B285B26E524A724D927A1292133A42EB32A2D28B629552A7B2AF52877299329782B742C6B2953282426FE28CF29052BAE29A82ADF2A012B642ACF2B992BEE29872A4128DE29362B792B822C1F2CCF293D2AA329542A952A1C2A72285027B1288828F325A825392582244C25C124B526B724C826D826D4253227DE260D28F0296B29F42678243E26D4272D291335AA30E8290E29FE29FF2B172A1729A927D529382B842B822A8028AC265D274B295E28D9298D28FC28D228EC26442AA7291C292E2831278C28532A642C8A2A462A312BA32B9F2AAD2A372ADB2AD12A89261529E0291C2AF7299528E92468263C25C52621251C266525C227DD27B227F727E7286C287A27112680264E283F2B57352131492B9B285F29342B4B2B2C2A5529462A862BA62A35291327D926ED26B9288E2961297129B928FA26BC253927DF27E4277928DB248426BA28CF29D228082801295529DC29B4288F280128E2276A276928AC2A2B2DF22802280D280727DD284B288A26C7269C239926E22824292A2AE12837286327322522274729252BB1357431112C9928B028212BCD2B0B2AAC2ABF2BCE2A222CCA2A64281927D029AB2AC9294F2C272B4729D428932710282329BE279A288928BE27F7291928802620278427A527E72766254B27CE268226F4254129352B6A2BAD2998297B278A2AE82BA92941285D26682664286F2697296E2B6B2B22298327F7262C281E280C2C8236B431F22A142A012B372D632DEF2C4C2C452A782B2F2A7B2AA6278028502B742BD82BB72CC129D52A6729F6279C2632287428A728B728682A202AD5291528FE24A726C4267025DD24D727BB273D27DC27AE289C2C402A00282427FC26762B572C4E2B21281D257C25C627E3273D2A052D572D7C2BE2299E2713271728F8291B354531D62ABE2BA12BC32C902B4C2CDF2C012B3D2BD62B312AFA27A6277429AE2B662C062D652B902BC22A552AB12A122CE1299A2AC629B82A9D2BCE282D282E27CB255F2529273C283529F728BF27AF281827062C682AA927A92713277B277327BC278C28AE291327A02785272C29692AA52B672B3B299E276328CF29412D77364C32302B0B29812A9B2B002C982CE42BD32B4A2BE129DA2924282327FF288A2B022CBD2B892C5E2B1F2BEA294F2BFC2A2B2B042A5C2A942A8F2BA02A122AEF292827E7266028DF25B92834278327662637262F285B2831285D27EA27F829BC285F28C7290A2B4629C7266A252A267D27922768292729CC26C2268A28702A9833DA32012BF128FB276E29AE2A272D542AFA2AE02B692B612A4627A825F628082A702ACB2BEE2A242CE9298929162B052BE129F5290129E42A172BD629BD2AE22979281E26A7278A273C2936285D281A271C2714289B26FA25752705263C29FC29522BC52A402BC128C7281525E024C52796274A275827CF265A25F627E32AD4327F32292EC82BEB2AB82AA529FB2C7C2AAF2C372C0B294729BC29F227B62684285029282B592BFA2B7C2B89294629432A002A4E2A362A3A2A0F2A6A2B6F2AB82992285127EC276B27E82AEB2A0929CE267C2A622BCA280A267E25AD25C429962BBA2CF82BCA2A172CB129B126EB25D526D427C5280128C925A1254D26FB2798328432EA2EC52CE12959293528BF28192A852AA92BA02B802A592A8A285C276628812890289D2BFD293C2BBA29F8294D28B126EE29C127DE27BB29B52BAC299F2A652AC52A32291F266D286C27EF298427AC28D42AC929D026952644276928712A1A2AE52BF12C462A1E2C89296F296C29302B502700272D261D261928E527D6329532062F432C592BA029D4279527B62A5F2C782BD22C9F2C8D29D128E9288F2847289E2AA32B4F2BEE28A027C326BF25A6277F2A252BE02AEE2BD62BF22A452BA12A2D2ACF28CB25362637284B283C27D928BD2A1A2A8C2A6F286025CA279828D62B442AAB2BA82B782B3C2AA72A6C2A37285A29F627852585250727F8298B33D131522EFE2A192B852A822A5B28122A212B9C2A3B2AB12A74292B287F276D28D1285F2B532C752D742A9B29042BB9285428DF28D52ACC2B452A442A002AD22ACA29992A46296E27CE266427C027152830288329CD2A752BB82AF6283A271627752A822BB22B902DC52C4A2B6F292329282A212B042A56279925F8250D288431F834BA2C9529C229EC2A6B297B2A2C2945289D2A1E2964284E2664248F269526A027EE2BE82AEA2B272B0A2B812AFF29B427B7272D2963281329A7289C27A4294E2A29290727FC26C826A027FD26FF2507276029DA2A0B2BC52C3E2A7229C828BE29B829642B1A2B6E2DF12A0A291A294728E429312A63271D274C256B261D32A532832E3C2AF42A072A3F2C082B372BB12AEA29A02A3F28E0251627CF250927F526322A832BFC2AC7299D2BA72CDB2983284228D927A028C32AFC27C5285D27522ADF2933293C296D27FD2739282D26F82531270B2894298A2B242BD4298D29552A392A2C2A742B6A2C932B702ABE29C0278A28CB27CD272C28232763289F314631DA2BAA2BD828052AEC2BD32A4C2B9E29292A2B2A432A98289F262427F227C9266829C129EB294B2A4C2A502AFF29092BAF27E626E22780287E281A28F3272F299E297B294A273B26082A5529E8262727F9289D270029D729682A3D2B852A8627BA266B28572A3D2A492AB6284929B728C1252C2653275126632860293C310F303F2C122B522C262D322D302BCB2C0C2C562C112B132C0A2B6028B8272B287328AB28B1297E2A0B2832280328BE292928B6275F268527E029872A0229382B3A2BC5283C286326BD264329002BC8284C2AFD2BC228D62543274527B227EC26D12741270627D127AA287D29D029252A512A942795266C264028F727192742316330A52A8D2AE92C232BE92DA32CC12B682C5E2DB12D3C2CF02BB328C5273A2703296D2AAA2BD42AB12A9D28F829372A742993284526F328512AF52A7729AB29B12BC12B6E28762602278929E328AC272B2B8B2A082BB9284529C32A7328932699284829362B7029D02A0E2B832BFE2C7C2A8428F2252D250029B4299E286031C0302C2CFB2AF628F829702A7C2E3F2D752B132CA72CE92BE429F42ABF299029BE29302BBD2BA32CD22AF22ABA2A3F2BE22A1429FD2675284C2AC42AE12AD32A642C242C242A94273F27D4293F2A2729D72A662CE52A0A2AAD2A652A7529132619287529EF29182A282B232C3C2A232C91299B28B72520250E28DE28CC29BD325031662CAC2BAC2B2B2B072CD32CED2B5E2C592DA52CE62C0C2C9C2A20297728DE28792CDB2CCC2BAE2A75297D2E6F2C1C2995264F26C625AC28B62A0E2B9B2C272B4E2AF629AB27D72652288E29D329A52C6E2B1B2B4A2BD02ACD29BC2A56295228F8282F2B5F290B2B552A1E2B0B2D842AAE294C27BF252827182A982B01326933D62D4A2CC52C9B2CE12B9C2CE32A362D492DFE2B492CA02A922A4C29B6274728CA29682C312B8A2B5929AA280829D6297E294D26F6268526DF278729BA2BEA29DC29DA2893250728AD281B29172ACA2B652C98296C2824277E287D28D429A429A929E2290A2AFD294D29A529982B602B65286C257B259D26D828312B1F346B32F92CCB2A242D052D932C6D2C402C0F2B312A142B5D2B4E2A9E2A172990256F262728B92C8D2B17292A2706277D280B29CE28AA280527EE23D6243E270B29262A8B29C5269225D526C02AFC290A2AA52A612A8F29E928172859295C2C0A2B5529C428DD2AD129802A3D29C52A542BB32AD5297B2787255625A9294A2C70352031602CD7298D28B42BC12DEF2C832C792B302BD62A382C872A9B28E3279627BF265529442C542B5429E5297A274A299F2A752ADA294C2856254F251C277A29482B9129DC2763269726A828272A3B2A192B5C2A9D297A29C92AD929792BEC2A51294F296829ED282029DC2AA829012B722A7A2BB227B124F227DD28D62C4A36EB31912BB4285D28E529F32B582D912B552B342BFA29DB29132AA02961290E2922289728192C912C862BB72A36296928EA28AB2A6D2B3329AE276325A5261D28F52999298E28C926C827B529C32B072A0A2AF82A6C2A102A422CDD2A892A8729AD29F4279E286F271F29AE2831287F29B429A62A3C291B27BE27D22ACA2DB6357A33ED2C8A2A9B28392B892C632CEB2DE42BDB2C792BB32A2A29D427B826E42730294C2CFC2DA92E8E2BB82A7C2C982BB32ADA2AE42A94291629F8252B26CB29D22A102AD1288027D1282529C229B229C22AAF2B7D294F2AD22A072B112B0A29F0278B2687278528FB295A29C7297F29E42800293328B725AC27DC2A172C693576328D2BEC2ACC277728712A232CED2BB02C492C4A2A9E28FD25BA26E026322778290E2C122BFE2B362A002A702A5229292BBB2AA929BD28D72730279E25A82751292A28662718287028322807289129492B572C432A18297E2ABC2AD628EE274A2684267327212730299E289626512637272626362657263F2664286C2AE034E7327B2D952B0E299529DF29352B992A3C2C2D2AD329D32820263D24A42543284E29102BF629B92A09290728C6297929422AB42B1629D627A4280C2680245324F527F627F72566266F27A628EB26FA285B2C5F2BB52A9928AF29B629BF288328C2262E26F2266E267A289C2836259B255026D0242C27BE2569265428602A0134C530412D94299D29D028B0269A289329B82A842918293B260E25A1238523A1263328282A462B742A7D28962772272929CD280728D827B427532813274725FD242227A42773266B2552260C283C274C263F2B422DA92A95287F28C326C32524283529C7274D27CE264628FB26CC252F251425DA24FD25B025BB26DB2603292F336230192CD12ABD26D826D325A928162BAE2A822A8E293827012543246C2639268427C628F42CA22A952929274B2848297728E5261527D826E625ED239422812304257124CE232E255325D6240F277926582BF22A8E2A9A29B4283C268C258E26CB2653271229A6282B2A7428C32621240C25DA254A27C426782723279A29553333317D2BF329872A26281627DC261A2C422CD42CE62A132A5726E825A029682AFD28AB299D29592C342AD526BF29CD2B9B2B572AFA277B26A6260A25C42233231722ED22B623D72237238125F924D2247C285A280B2AE22B882A0E27BA243E2738276428DC2712283A29E02711274327A6232F24E524B6237826A7282A2B1034E131E42B982CF929C0296A288A28E12B812CE02AC42AAB289427B1264829452C4B296429F228EB2A1A2A8B28B3288C283328862A6329AF277526D72514235E212822D524DF224E235B2408229325FD2493270628B9281829A128DA2610254526AE278926142866271F2A3928A1277D24DE248123ED247323FD259028BC29D133C530D92C042B9E2A73274E28E627E028B02B142CDB2B2B28F1251C27F4273F29132B482AF529392916277427DD282729C229FD2A972893267A267123EB22C421F520CC218D228F229A234423192352247F260728082821283828E327482667272428E825EB2405266E295029BE27682598248323E023C422E92367269C2992340231A92C972BAE287826A8251C27272A9A2B062AC52A5F284726EC23A925D027AB27C329522978281126EE26F0253129722956293C279A26D5257824F123F22225225E221522612391226F244524C624AB23BA252F252126052792258C254A26FC25C2240025E425A5276826E62555258324F822B622AF22EB2329252826AF312C2F722B742A522AA6286D261826BD26FA29082993282227A525FA2469268928772893278726D925E924AD245126E62464251926A7251825F5243C24002371223122D91F692031204024E9227324C623BE2318251C238D239C24002673240C2657252C24DC236324CC238C238F2665265824E0224022C121DE227C23B0269831902FD52ADA2BCD2918293A28A1267026EA272228E727FE274A25E423302625279B26E925F825BA234F240E23F4236D2588254126D9275425E82487245F22F822D921582094216021A02313244D245F23DF234523C722F42465268E2458242025052635263823FB23D2243424A323C4245523C822DE22BC210623D423A126C230D12F6629F029402A942A182A6C28A2255D2638271D2667261B25A9231024A024BC24ED243525962391233F233123EB242C2428254D246C26FE24602481242723D422D8202F222F235524AB2496242E2550252E23242328256923D723A4238C224D25412518256723B623C4237A232C25F922B422CC2364237422A824E1246130DE2C2029592A362A242A942A1F273126F3251C2610277A25A226D3246725BA24B925C0259425F823FB23A7226624F22669231125462582261E247624CC245A230C23A822FC216321FA2239246824FD231D266D233923D02299231F227222E0247B237A248423C722E523D02310238E23AA23B1215722321F5021B122B1240B30372DC529EE27192A052A7529F02815266B27C623FA2372252D26EF2442246B26092568268126D4262924BA23D923D8256D2402249D23CA24AB25882403231023D322C423B022F52148222F2358249223DE238B249923F4222D242922E1226D2294244D21E5219021CD225521782301229C24C321BD210C23F6215523DA24BD2E1E2DB72834268A271028A1277026E62557266624D723DC231625572184229923A425942712253A278D247323132454229022E821D4225521E424D02240255523F923B522CF22BF21DB23C322EB221C236B224D22DF22F121C2236F21452383227C237022D2226B22FE230521EC22BE229422D8212422AB22EB23DB23B224E42F702F972838251C2608288B289D25AF25C325FB241F25F6231823E723C223F8237C25BF268C25C826F22349239E236824CB22EE229C213B2163225F232522DC23722318213D21A822F421E623C323EF20E823CC231A221A20A022F422E2246E22FF22EF21C3229C218E233523CA2152218E2325220E232D2151226321A823A62EFC2D7D278225472552259A2693278E25A42620259F2340244A243422BF231323EC23562402250D2413235D245824EA2334224E23F1213A214222EF23CC22CF21A0229C210F22D121CB225F23B82270213A21E9219D2187202521EC212D236022A621AE22ED21EB20562113213A205A22042285229F22ED206D22D4227C243630362DE1261A25E5240125F6260B250226DA260D25C024722350238A22A3226123F82234236525DC240F221822D6221923BD212423FE20F32227227D2093229421FE208A21C5227E20C222CF22C2209F21D62179223421D5219522FF2176219520702157218021F81F2721C32034218221E8200F21BA22DC20B02291227024762EAC2D1627DA235825C525BF25E2257D26062790232A238E246A22FD228F227F228F233D237E230424B4226822A023E922CB209E222122D120AD20D4212F221422E121212113200C210F22BF2119220922AF22072225219420042188218820A821302162200E20A720DA201821FD209623FE2046217B215121E0216A221825082F5B2CB32776266B23A324FC24AF249A24CD25DD258424AD246A22DB228D228224B4226C239C241323892333227923CD21C72143234721F6211B2217201220CC1F1221501F6F20E6209B20BC209D21542140227C2105201F229921F620DD204321D222FD206C2117223322CE203821CC20942187222720A8211E22F3210823FF2F5F2E2728AB249023672383236A244E24F824F7242223F82298217621FA22F31FAA21B5225C223723CB23FE225C22EB20AB21FE225F23AB20222227202821C521D420F81FF3209A1ED120BD203D203320AE213C221721FD207A208D2054209821CA21ED218421EC20722299202F2107238920AC203A218E1F6F20C42344245A30EB2C4527122418230B2388238C228A253D2252235C2331231D22FD22B9222321D620B7224821E222FC203D21C2219121A9206A2260220F21B6227A21B921E920CA207F20CD1FBA1E3F20CF20212073208B20352255219720E91F2C208020F420B321F4201321CD1FE0216F1FD120E41FFC1F622145217E20632219220B24A72EAC2C2925AE23D824A92208237D227D23F3238222F724BF23412201239822262230236F21D122EB237A22E420EF21A32227204B21FD20A8200D212E203521831F5B20A820C021671FA81F2C202E21EB1FDE201B232C202B202E221821EB216D21C61F201F4B207920A7217521E41F12216821C51F7020FA1F1E20A621EB224D2C1F2D6D28382407248524C021DA22A522BA2387242A231423F823502216218223B4238323FA22192499211B2088213022BB203C209321ED2101205E219E207A1FB92195211A208D1E9721F21F6C21911FE421EC210D202620B321B01F1D23AB1F8C22D0212921C21F30219A20B1203C22DA201E1F3B203020B2211D222024762F602E65275B249123242529229222A1211223F02295233B226122D020A3216A21C621552216210822A321CC202A21A5224F22B5204720191E6E20B81FB8214B207B201020A62053207B1E2E229420871FAD20F220F51EBF2022219D20B31FC42096201D202321B21E0121EB20BD203A20882061203721EB202F21E9215023A82FDA2C2427ED252123D0222922AC22FD21CE2381226C22BA22AE213B2346236F20AD2222226322492356223321D22126213321C721951FF41EB2213C2100212220EE200922ED1F151F6A1E4020CD1EFB1F0721E0205B1E961F8921CC204E21F91E6B217421D82042218E205C203320E62135206A20B0209A1E0F22A9221E25D52DD82B72265224A2238E2221238323BD22EF22B1213F220A2392210821F821C82219229C200821FE217222F020C920FF20CE218620EF1EF61FF61E2921E01FD320592001200121121F281E6D1F711F9C1F7520201F421F561F29200D21E01F2422C320411F6B20AC211022ED1EBD1F84201E20591FCB20C71FE52104231C24852DCD2B38278A2282229621F320062329229B22DD209022FD1F0021D2205C214A2127224921C021CC216822931FC020FF23B8226E212420801F5B20DD1FE5216622A520D31F4821261E371F8C1F1D1FFC1F71202F219921741F902178200320721FE8218D201D1F7C1F32212621921FA820FB1E2120F421D120352151233A25182EE72BC02526246A22FD22772243219A212022E920EC2184206D210A20BD203520DA1FE1207B20D020381FBB1ED51F8021E120BF1F9C1E11206C205720DF202A214121C020831FB71E1F201720F41FFE1F941F5B20C21E2820EB205820FE1E11207C205F1F6B206D200421F51EFF1ECF1F2421BF1E6520A81F80207420EC23062F732BAB24CF22DC221822F12011219B225A203321C72066207720771FCD1FFE209520C01FC721ED21BE20041F5921C3204A20CC201C211C200A20B91F0D1F2D21DD20BF21FE1E0F1F171E5A200D1F761F9F1FE61F1E1F701F2D213E1F12203320FF1F3520182014200C20C8206221351E301FFC1F64203021B5211C237F248A2DB42B3425A6218E22B6226B213F222122B6212323C522432085204E200B215F21702123227B21F420EF213A1FFC21F21E41218621AF1EF2209F1F3620CB1F9521202176208C205820621E41208320CF1F2422AE212B1EE2202E206A20B22019228B206E1E7C215620F91E61205A1FBB1FEB1FC7208D21A61F5722C7224F25202E9B2BB2254422A4213A2270229021B223B6215221CF209D207A1F701F1F2128229720392140230020DC21AD2044210D20692153221B204520A71F3D203D1FCE200620D01DE41FFD1E8C1E9A203A20091F30215A208D20041F3F21A61FA82245202220DB1F4920ED1E1821FF1DF61F0C21A421C71F83200421402255200323AB2E3F2C8225A421D422A7228622E321842316237F21A021D621B820491F6D2155213C22F521EB21B11FEA21712036213E20E81F1C2214207A1EFD1FF121B81FDC1FE01EC91DAB1FD61EEA1DDC1F911FB21E00218B21891FFE1F2D1FBC1E331FA41F1921B61F6F1F401ED61FCA1FC91F31218D204A21DF1F801FC12064237C246F309E2C6A259123C3207823D5232322C5224923E722DD2317220E217620E2215D20F120BE22A221BF1F2421DF1F07216720F61F5522F120D61FDC20EB1EED1E6B1F2A1E8B1E801DC31E8B1FBC1E6A1F65215E215820B820D11DEF1C991EEE1E8120CC1F5420F71FD31EC31F551F911FF420A41FA7222221761F0520F3227424412E +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 403F + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 55 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 0009 + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 D228EF2224238421F82154217F20052179218E20B41F551F2A1F9C1E8120051FF91D73202E20431EA31F551F2721601F0020851F76200F20841F501F6321BC1F431ECB1E491E561C0B1D571E1D1E771ED61D941F471E071ED11EEB1D7E1D941E7B1F661F8D1F321DA41E501E481DCF1FC11D66200920C31FC51EB4210C23412E642CD924D621B0238323D3217F22EE221623E52022220B21E31F67203920DE1F8A1FE21F0D20561F0620D92069217A22D11F9B21DC1FAC2027208D205121511FA51F6720CE1F4720AD1ECF20E71EC61EF7202B20161FD11E701F33215E203D1FAC1FAD1F2120091F5620A120751FDD1F521FF720DF2147210F223222EA24402E452CAA255E2478236822FD2182229C23DC212F247D2368238721D820F2201B211D211422F3200620E61FBA210E21CE2014222F22D020B7203421C41DDC1F2E21E420FC21F9202621C41F9320AE1F571F1520C220491FFB1F7D20221E252018209A207320BB1FC41E402043205B201D1F3D1F032071228D20BE211E22BB254D2F1F2D33297E260D233623F722612443234622D721FB217121E2211B213F22EE1F5D21812157213123C42042211920FF21C1219D22AD2128228420AE1F631EBD205822ED21B6222A20DB1FE820941F371F4A205121F620A921BE20612032215E212C22AA201921F21F9E1FF01EE81E791F7120AE206A210C23972116233A25DE2ECC2D8C27732361228A226223AE22F5224723672260225122172163209B2132214D20F02152214F212C2106218423C01F5620B623CD20AC1DA82077203620692045225A229721661F20208D20851F1F21652060221E20D220452166211620D71F3021F62083216C1F9620821F0A20F11F7B20F91FDF20E11F40213F231024AA2D532CFE26DE23A324F621EF2303225B229F23A1220223D1236721C021C421F721D2219A213D215923CF214D2194221123E32092227C21E1208D20E820C920E8210F226C226A21D91F06221C207321AC202F21AA216A218121C6207D21CB20D920EB21B9216E20BB207320C5207A207B20DE1FBD20BD2234229B2163247E25522E732C2325D0233D237D2247227B23AB225E22BC231F233922A522F221F022BC2246239F2161221C22AB2388232F228222D020E0203A220A229D215721D122B522E4217722C6215521F7200C210E217A20F9214223CE209A209321D6216A2009210622002085213C207D204E20C21FEE1F5B21E01F99217A20852273239723D42E292D2F27C624F72430230921EB21D8223223FD235E22022328244B25BA227822732358222823B32394232B2252230D256A224A228C220B22A122DF2229228321802256233E23FB212122B723DD23892091233122D6200620002120200E225621B021AB2192216221E421FC20922051210721A220DE2095205323A323BA25AF2F652CD227DD2568242B243C22E52045239E244B24D1230B234C231023D923EB2180218D22A6220423532363226321B2232B22FF234F224921C5206522A321F22177238424AF23002324226721C2205E217622D720BC21F41F2123C7215D21A221F721B820D02255225D23FA20CA1FEC201E22AB203921FA20A721CD23F023B12FE62E0F2641241F24892318227D22B622C724CB234B252E249222F723362465239223B221A1222F22B122C1231E22742378222C254225CB223E24B2214221E92204248625A223502383212423B22213210422AA224421AF2081206121B9204E21C224DE21AB20A22260226F228F22522177213022B721A4210921D6238B242931E22DEA294A2740250425E122FD221F232E241B2500273A258C2416234224D7245324F1229422B5225D2192236C238B253923522329240D246B24D1224A21662261265526BF25CA24D721922122235B218C228A214E2114216822D021D521342303231322B321B1227F23B32160203622E521DD20DD20D91F4E22B72384240B31912F55291A279B278E249422DE22912207253A267B26D9250824BF2304244C256224DB23CE2211254F22DC247723C6240F254125D4257C2589232F22052384225A2455278925BE24462313225723212367248C2235233E238822932178211823B82330233D248222E7243F24C422DF23AC22712100218921B721492241264F30ED2F3D2B3F28BE25AC2443231723392272244225B625B2274A25C02547259B268C250A243622BE2370222A232523D8275A27CD26CF2592242D2461249922D0233A241628D428D8253B230F2417263C245F2304259323A7237324D92322226324452349238F219822AE23EB238323DC231E226C218B23D221F3223824A2266E2E952E2C2A4728FF26BC267A236023A9232F255B26C626152725263E254F262E24482508252823842253226222DB2375263F28DD27792500249225CC22B92175204224CF261D28992613261E2523261E246626B2261625A5246B23F623C9236624062501222F23AF222325F522DE21E6221B237123F8223E2289233D248B26AE310C2F702AB2294C274F27D924E52570238526DE243228C92AC028AE279B260E272125B72646261B231E22FF21D323F026B528B9273125242615259123FB22B7225D2421255D279A27F9265B2500258E25812571262B276F2528258F2434225D228723E7246325EB24702649259B248B24D4249622B3234C217C22D1243D26F02F8230492C67293F27B726562789267D2677247126D528222A7A29A72AEA2AD62A75281028A925E623E02448250A24A1269926A8273027DE25DD254226162554234B23B22236243B2555255B280226BA2562270324F0246425A325A223B822C1227A24B223822619269427C4274C244E25CD23D5237623E723EE224A2420274030E52E6F2C812B732AA92708288B286028D5268026A729862B002CA62B022B0E2B9E2AC9297628A32574255E238D252928792895282F27362578275E28B92767266A24BC24162501252B259F2469251C2619269E2544251C26C427AF24C822DA236224AF2448278D2638272D2645257322AC24EE23D1235D24A1245325CF286C320A319A2CC42B4A288D28842A6E2BB12BBD2A73298E29A52B802CAF2C292AC92AC02A602B042B2C2768245624D72435270227B828C927EF26EF275727A528432792267324EC220F23CE25E826E02530255026C927CB26B6242427D923F321B1211C252A24FA26CD250C288325CE272826232681248323B623A4246E252B288134FA2EA72E672BC92A302A8F2A302D692CD62BFF296A2AA428BB298D2BC12CCC2A922B082D41297829C426082317255427F125EE27B0260927D626E826562808273226B224D8238121E024A8250127E4249F27E42682266625E02316227421EC214523D425DD283F26FB27582623255C240D257E246123A4227B25D925F928B2335C31252C662B9C2A2529A829552A562CBC2A5A2930287827FC27B527602B532C0E2BB32730281427FB23B6226D2204241D2678261824B023512802262427CE242925462548230623BD222124D023DA252627282780256C24EA241E22B5212323DE244025702620283D2966267F2598270026682594244523BD23FA258E2721328A306D2DA72D5B2C402AEF294C298E2A542B4A2AB629CB265525BE26352856290028D829DE276F2542245625BD23CE23B2256A255426C525DD25CD263A26F2269726E72682251A2532234D23F1265B254C27CD26F525EF24A222CD239E22FD22D023A123C02524278D295529C0282A2B2828E3268E275723DA242B253426E431FA2F3D2C892E0F2C922A152AE729A52AFD2BA32AD826FE26CD260C284527F7271428A62539280826FF25BC245C244424972494267025392628279E250427D6257527A928F727B52671252E25902598263327E825EB2286238D23952312236F2398237C2485242127CD29A129072AEC283527FB27C22549267725A7245E266231B02F5B2B8D2BD02C232CD02A0E2AFA297F2B4B2A9729F927AC269D2549288D26B426FA25A826B1242824A0235B23582353247A266D26C2264C267E241C2603275E26442892282F27E7241E275C26392504271525E2237F2273233B245325CA26EF2597243026BA258D29702C282BA02930283426D6255B256C24592445256F3192301D2BFE2A752A452A99279927E9286C285727FE26E226C4237425FC27AC25FD257227D326AB279226A02579253D259A258726692678270927722512276F274C287E29B6272024AD2503254025D3254C260725B6253E240025F62427262C2956265B2560247E248927FB272629652939286D25AF2528250B26AE2489261731A031E62A88283627BE27D726BD2556260D2827269B27E5255124702464247F25E1253827B628B62B1B29C0262726FD249224E126BF286D2828272327B12695284428C7285127FB2538244823E124B423982439253226D625AA268A2505279D28D92909262D23C2237324E6253E26912828279125DE2539260828E627072629307930502A2728C1276A267A2600270E262F268F24CC24BE24FA23A825E826742780297128602A382B692B512B64265C259724E3268E299129262BFB29A12AC029862A9429FC284B26DA22CC235B232E226725B4255425FB249C2719260027C329EE28F726B224AA219A2462242326432713271A254925A625AB278E2624281B31E82F9A2910273A2611289C281928C3279926DB24722581256124162762285729CE2BDB2BA029D12B9B2A0329D8267226BA243C25B728FC28AC2AC62ADB2A80284E28FF27AC28582506269523B623DA233A23AE259A25F7257326C327AA2676292628EE25E5247D2328242B24B325B726BB26AD264D27002864276428E5278431E72E6F2A842717298329EC2A442BD42BB5292727E626D124A328DC27C729932BDA2AE02B6C2A3D2A1729F828F827622640251024232427279C28DF285E2878277B278927AA26A524612587237D2477230624B623F423EE24F324C92565243D2539279F2441234724D324DC25242772274D280527F2282629722A7F291B2BB433CC2F582AAC2B622CD22B322B4C2C082C432C452AF1269226B2253A29592AE62C022DD92A132B3B2958289327EF26E2250C247424E8220B248C26D2274B266D27A9275B2758274924AD247724C32283241D26AE24A6240B24AD256A2399247A250625642463223824CD25D4271A293D2B862A8B2BF42ABA29692A042CDC2AE4339B31542C6929FC2A762BD02DD72BBD2A622B7A28FD28932654253625B327482ADA2B752ACA29142B87274E278925E62626232A23952369235925DE259326F126B626D626D826892546244B24FB23CB25EA274427BD2743242E255424E0244025EB25CB2534250F2565260F295B2AD02AB129302AEF2B5C2A882AF72BF22A4934AD30082C1A2B522B502D1A2C6E2B232B162973298225D725BA2347240C244F249F265C274D28A327A1264026C0270126F223C3231B25BE23C125B9264C278527502727288727A3258E24A522E9230A25D726D42917279926E526B826592520263726FA27AA26F42384278129BF2CB52BCF29A5282E29AE29FB2BA42BD22A4C33FB2FC52D122B9F2C072D032BD72A2829272A382A7E2AC92708243F2430268327F725372789273A2883260E2805277A24DF23AC255126A8264D28B6260E293729E72BBA2A642A66269A242B252825782481268128B426D5273528AD27DD27F626DB285C294027A8267026E7267829932A032A202BB7299D2AF129A82B002B3733022F022A1F2AF529C92A302B162CDE2AD729EF2A232A062895260D25A82471253726792531287227CF275428E726632481234B25A72620293F2A352A712892291C2BE62BB529D926B623DC25A8254D274329DD296528FA2662276A27B2274D269528EB28F0273D26DB27BE279F285C29E52A522A492BE7290C2B862A70297032282E702947288029D629BC2B062A422B922CE12B912A9B2987272526DB25B7252D258F2771274C284B2A28297829ED25E6235325AE27FB26B829B52BC829F829612C8A2BB52888258C256E237625D826E02A932B4527C7256026C9252D260626DD26E32723286827D3266E25F92622281D2B452B7E29292AE02B1E2BF4286C32502D68279F250A290E2CE32A792C772BC92D452C142C162A49292A26C7261D27CC275C282F29CD2AC82DD92BEE290A29FA256D253229EA28092AE52AE62A632B3B2B5D2C21282F24FA241625D3255126DD29A72B5B2924279624B425D8273E25B226DC25022572241625EA269F272C2A8D2AA72AEF2A902A302B032C93291F32D02B1927CC271329E72CFA2CCC2CA62BC32BA52B162C5D2B5D283C26B2266C28D02B6E2B8A2A702DE12BFA2BE82B6D290126672669293829772A772B432AFE2A112AAC29A5297B242B22D4257727A126E428D62CC82BD72799267D2784273E28412692253A26A226B2262126FC25B228452A922B102BC629032BAE2B522A4B347D2D5227CF27652A2F2D032C3D2B5D2C642B722C432D342AC627582497268D29622C4F2D642B022D362C702BF829AB2A5227F625702ABC2AD72A412B6329642A0129FE285E27922323223E24B6246B266F2A692C242B58281227A9249B267E261E28F2234225DE25B0244E254027F729962BFE2B562C3D2BB82B622A2E2A2933DE2D2C2806270D297A2C842D5E2E4F2E3E2DC62C592DF029D626EA27F6263328262BCF2A4D2A542A152B7128C42880292E27D7259428C829062B262A67297A2A5529FA2826281C2530245E23BF24CE245029442A2D2C07296F262926882697259C253C258C23C5228B243224E1273F2A652C822A232DE12A7E2A1A2AB82AB834212E6927F4254A28052A562BCF2B322CE02E8E2B492C082CB3296F2720279D27DC2A102AB7286F28C8272429FA2A0528EF27FB250926BD299A29FD281C292C2852280F29D429B926AE256125892448252E27F726A028F82793267A245C257826ED257A249D239B22A822DE235926B129A22A5D2A2F2BB0270F296229632BCC33FD2DDD2671258B259125F1262C281B29992A012BD32C152C78289924892472255E266628732747272E29DC281F2A1F2B06285C251B25BA263E2790260D27CE25ED260A27CF270228AF28E0279225E4247A25DC254D2532248E256D23942326245824682451248E230824A3227A245F27C629122A782AF128DB281E291A2B1335622E742993260027B226BF26C6270129A22BDA2ABA293E294928F826C02588251B29022AC4279B2A8729862A992B702AEC28C02699237B2453279724B125A426AC2636266C2774280329482ACC2738262C26402597240E23CF22CB22E124872617257D2564255425B22595240C25B226D6271828D1270C27BA270128C52B5335CF2F51287D247E26BF2406264827BF29ED2BC22AC42A192A85277724FC247D25CF272A2AA32C0E2C252C0B2B8C2B062C7F2A93262024EA221024D624F32450272227A7276E294F270D2887294729CE264E2509251A2592247A253224FB24EC252228F226E62418251326AD24E4243D2656271F28F629D6283329F12ADE2B3533163037292A27DC24FD27D5235D27FE29BD2A95290128F126EF24D8230B2533257627B8296C2CC62BAF2A762973283429BE282E26B1244B24AF23892411276B286B28802791285427FD26D825EA252D256B25DE237A245F22B723EF24832638266527BD276526302565259A244E2484255A260629912A6C2BAF29A627C429663309306A2A56281C2704273B282227FB28B329D6290B28562668257426AB24492589256B2A452C602B6428CD27F7273E284D26092A8C28BB237B25BF24CC268A2ADE2A2A2A8328BF24852489245126F0243325A8245023B5229424932543274029BB275D275928462719266F24B4234A258126A4273A2A0629692618272E2A71348E311B2DC62A61287D28AE29482805273529B927BE26DE262726E82629290728652675294C2A9D2B192C692A082AA029CD286728EA277C279C25102544276A28FA28B229A728FD24A325A126D42542277126A527A924DC249B239826EE2743288628F3285B2A4B2A022AEA243E24D22555256B260228452794271327F22819339632892D572BD8291229EF283F29172963288C28922774286B2AAF2C9C2C522AF7283829E42A052E932B892BDF2BEA2AF02BD42AE82AE82927287D237E26EB27F3294F2B922AEF28C427AB26FB28E7286A28E828F2252B258F26F725122AF22A632979281429AE28132B22297226172890272A27D628E627FD27E129512A52346832002F0E2C29298C285B277D27A526F026B62609293929AA29542CE82C112CEB2A5E2B4028032A912B9C2A5A2B812C642B412BCF2A742B8B29F226C1266F28232A292A142BF22AA029A029C62B9729D2296B287B27CB25B923EB27EC29782A39287228CC266328712AEB280627CB2934299427BB281028BA28BE28A82990347432AA2D962C5F2A4727B7265C24F824D026952822298A2B1F2B0A2C962BF32BDD2BDD2827284F294A2A472AD32C242CA72AC32AB92A352A1129E4275026B1253F282B29782BBD2AA32A322B322AE429EA29542A5E27C02429251826D228C62A3F2AE22A7A29FC28142A35297A285028F127EB287729262A062BB72AF12A293484322B2ED42B172AB8283F27DA25FC23EA25E627632BE22AA22987293D2A0C29982AEE2659276627F2274E2A282A452C662B3629732680262528AE256A2545257427E428212CB92A532B2D2A662A8229D029832A32270525D7257D26C927D62A312BA628C0280328552949294C27FD276C29DB29AA29CF29F92B412CC62CA133F0309B2DCA2BA12B782BA5295D26A426802858288F29072A0F285127D827C52865264525C52589289829E6297529F027F6287729462885279A2615262926FA2553282D2AFA2BC22BC02A462A9629A829C4285229BA27BA2303251B27AE2756286F27382AA028A428E7276E27F82501261D28E32AB92BEA29762BC529A32A07340632522D602C172B032C2E2C142DFD2ABC298B28FB29AF2A2E2A9528E928A4276728D2290F279E28A927AB2A632AD72AEB2732271D298528AC27DE28ED27B825F1250429672AE32AA22A14294D283C28072A8C28FF268926042643287C274329FB28E32965287426EE26E7253A253B26D02655284E2AF12A612C2A289529EF319733352F432D0C2CEC2B352D5D2CBA2AD62A5B2ABF2B122B042B6E2B842B222B9F295E28B826CE292529FF29902BD82A5528A82AE42A562BBA2CBE28BF2712265927AA2AF52ACB2AFA29422AEC286F285D29CF2AB228DA254725A8256326DC2610278627C526FF2347272A26582555259A255A279D275427F227C628842803323333F02D3D2EB92B642DC32C0C2B2428B328C429512B7C2CD22D092CE12CEF2B0C2BE32AB9277029BB2A282A1A2A7F287928EB272D2B662B1D2C2129992859268D262D2A392CAF2BFC2B7A2AB82A012A192A2D2AAD27A526F2262925242412252325CF2565256625802409252627D22636251425FB277C267927B726A1271234ED34292D402C602BF52C4A2C772A322A9427EE257D28AB29342AA62A1B2BE02C4F2AE62AFE29D72903284F29042AA429EC274828542886295029A228972715261A286D29A52A332B942AE82A5F2911296F2A702ADA272C270725AB241522662344262825EF241D264E27F0250D28942ACB281226FE27D426B0285928B428C331BE32442E812B742DE52E452E1B2CE429A0261626B427AD29E927BB298229102AAC29E52849289227642A1E2A6A2C802B5028B427192A4B270F27152747268525C4268E272728CC2975291028CA2700274C298C2A00292F27A1259625642376247327142758277627D1278727AC27732964296F28F02A0C2AA72AED2A12292830ED32362DEA2CC92CBA2C2F2F0B2D342CA62AF8275828B0292F2BF128D729EB29902CDA2A5629E72AF32A502DCD2CA12D142B722A7C289D2802289A284629BE274C270829F3267D28F3275B27642553267128DA29AA2867273427C127F326BD25C6261428C72AFE27BB29C6291529072B822AF129842D232C9D2AA32B972B7532CB30312CFB2A5C2C2B2CCB2D032D6B2DED28EE252E29C427BC29232A7F2B632A462CC02A6029D1281E2B99290B2D672C8E2DAB2BEC281C2794275F28032A8029F2276A280727B527E72704274829E7270128A32A412AF529B02A2C2A5B29852759263C277729B029E6280A29F32ACF2AE4276F25222A792A812B7929B6291432AD32102B402BDA2A092D0A2C6F2EAB2B152AB529D0296229272AC129052B9829892A9F291C28B9279A27C328D029BD2B0B2C572AE829CA289F27A3274D28E9264727AA270528BE272328C7279B29B028F429412BFF2B142A632CC22A052A0429EC262C287E2AA7285329E6275528E0272726C2279E29DE2ACC2C092987287031B4318D2C5C2CB72BA52B032D7C2DC52CF52AA829222B1D2BC92BB928152983289528D1279927832755279428182BC22BF72A8729EA296A289B297C29EF29F327D6255127C5294A29922A2D2BA029FA2782286829602A1B2B8F2AEC2AAC2A7B289B261E2790294A295B2A682804285A285E2535273A262026F827D02672282734DE31E32C952E2F2D6E2D122E922C1A2CB02A1A29262AA62AF82AE329DE2981288829E92A4329B828EC2616282D298F2B552D352A5527CB259F28072BE92A012BC4289B291D2A382B972BFE2BAB2A8B2879280028092BB62ACE2A6D2AFC28EC281A271F257D27912635296727EF2400282427CA252A24F923D824E6267B29503440324E2D7B2DF72DEA2DB12DB22AE82A75298B28C629B22BAE2B71283B273A2A7E2C482D522C3E2AE6273E286C281F2A52298D271027DA270E28762C892B962A2F2A1A2BA22A092BD92BB92B0B2A2C285A28292AD1291B2B6D29E2287C29602933298326F724042571271E2716286D285C28B7264A253A222A241327102A443564309E2D1E2C972C772BAE2A4C2A3B29E428EB268F289E29F72BE52AC329A027C2296C2B2E2C472BA3278127AB265626A625EE25E5248B266B289E2A022C212C9B2C872BE82BA52B172A2B2A812B4428F027CD28D429F92AFD2BCD291029FA28862AC229BA27B923A4255B269628B62AB1292F28E626A425C525CD24DE28E4333A30772D682EB82AA22A2B2A4F287D258E25352638272F282229682A2A289227F326ED27DB272729B52553266724DC24D023602535242B257C28102AC12A292BDC295A2A452DE029282AD929982A1C272028F2284229092A562DC3290E292D29012A2F29CD25A324132524272F291A2A2D294925AD292F26C0253A283128E031ED31792BB92C0C2B562BC92BDB2781259525FA24C9269627B127F327F1274824B1269C27CD270B268B27FA254B231B252325C32333269B263628C327D5281B28D228E129322B102CD92A992ADD2AA927CC27402AA22BBE2B0B2C5D2BAE28DD28BD27B727A92417248926382691271A2B94281E26672860284C28132AF12719338331C12C9029772A812A2E28042726254A25AF259424DE258F2521265B26D626D3254327F327C028C126202753260A247F234F250A25F126BE298129E227AC267B274A285028E328822A042B42284527E7282D29D52BD82A932BE3272C262426B6267F237B23AF2564269525ED259A28EF27322906283929B629112CA52BA4342931382D722ABC2A9227292707271C2569248F221D270825FD276D2674276C262128C4272D297C284F2601262D251A272E256124D625F32504297629FA260726EB269427AE269427892A2F2989261024C32551282F2940291C28A8261F269C243B267524B82321232124AB25162608292628B1284229A32A202B422BA12C2435852FB32C212A872871283426D423F52504234F2486253126A429F429B02B882A822AF62A6B2956292727A9267C296529AB2604259D24BA26E128F7284E28C2279A27FC272729FE272E261C274725F8223023CC277D25E52617299728AB27E026D8249324A524F6226126E8250D28022BDC2BCF2BC42B5C2BF02A752DC22DA0354330D72B752A8429B727EF25642404262E242A2409263D298B29802B7E2C7E2CBB2CAA2BF4283E29BE27B926112BC629482956281227C8243A27D929C329E8271D29452A12299C267A25BE25B2256725B4255C26E1261527B626CB2622286726282785286C262025E82484253C2B602CA02C5A2D5D2ED12B5F2BE62CDC2D8E33C930E02A12292C263C265F271D27EA261627FF24FF24392576288C2A3A2C402D222B402BBD28422925290529EA297F2C9F29ED278125C324CD261D282B29DD270C2A372B3F297528E425D62373259A24EA26AB288D26E52700273D265C29FD29832A332B0F283927C9256829902A162CAC2CEE2C0A2CA02CBE2D9B2C5E2C0A344C2FF929AF279825C9264028F72867280D2AD5274826C32615271A29BE2BCB289728F62988272C260428B328652AB02A442AC128612770244A25FE263C28092AE02AFE2A542A0E26242505268F263526E6294E2A5D296A2744297A29FA29972C072C822B252BF5280529AD28B12ACF2DFC2BEB2C002C782C732CDA2CF82CC833312E07299227F026CB267828B429CE2B1D2C3B2AE1267827BB25DF27682B3E2A122A1427C8253B26C9279E29692AE4296D28832629267C23F8242A24582514262D28CE260E27EC232B247724D925D426512A872A9B2A1329862A1F292B2A2A2C182CC52AF6294128D3286729482C212CE52A402B1F2A5B2BE32AF02A142B4D342131922952287C28F629142C962A272CB62D5F2B2028BF27B9269D291E2CF52C6D2AC8281327412598275C28512AF12AF3289126CA2482245524F82301226A23B426BE255827EC23E124CC25C6255A2629270A29E0262728DB27312A5629042A952CFF29D12AE028D7266228F8292C2C292C642B4C2BEC2B782CB929EA2A01353D2FE328F52640280C2B792C0C2DA82C562CE12A5C2AFF273F27E027A52A722D902A5A280D28FC271F26E5250426AF280428D92792257C24E924C82382244E264C257626D226C225FD273027BC2654269327182696274F27B62860278F284A29092B4D2B3D29D22781269B27EC294C2D1D2B9E2BC32C382CBE2C872AD52A0335892E5629BB26B529C52BE92B722EF62C222D272C4B2AE0282C265E29822BB62C0B2C992A302934291E279A29BE27EA27412746297E262D2540268B2775268F261E2675256B281728A528B62AB12A51295D286C280A29E8288729D529B82A4F2C642D002C282B24297F288B277929502B882CB62C5A2D702BC12C662B402B8A35CA2FC6295E299D2A902B4D2C282D0D2C982D8C2C192DA9295A284E29C529832DA82C3D2A85294A2B492A4F29412A062A7B27B12605254025E32696264729A429D929CD298E2B812A9B2AE12B292BDD2A1E2A952944290C29F02B142CAF2A7C2C442C042EE52BB228272A76293E2CDE2BD82AB92D8D2EC22C202DF82B952B5234552E3029C328EA280E2BE52A012AAC2B662D192D612D052A5A282127FA286A2A7D2BEE296729152BC629152A4229AE285D2469260626FC24EF268127B629052B8A2BB62B8A2B7B2B2A2AA42AFC2B862A1029BA291D283B297C2CE12CBA2CFE2A5F2B1B2B392C4529C729ED29012BF82C832A4E2C9A2E4B2C742C992C3D2CE133BD2E792B492A272B7C2BA72ADE2A972CB32C792C782CEF2A0F287226FE25072918289B2851271D279C286F2A152A322775261527CF2762269925E4276F27D0293C2D882BDA2C732A4C293929852ACD29242A8F298C287F2BBF2C3C2B0F2D902C422CC82B842BA12B452A3B2997290F2DB72B552BBE2B8C2DF82B7C2B362C84330631062C2B2C432CCE2B6C2BC72AF02AF92B282A072BDA296B28B5273228CE29172B7429A0284B27252822284F278F261026D728B6291E27CD25AB255F29912A352C692CA92B9C293F29522ABB2A1A28D52841284429F42BAC2CA52C452BC12B0D2BBC2CD12DF929C62ABF29D127942AE229D92B922BE129402B652C3E2BFF335B30BF2C5E2C0A2D3A2C9D2BDD2BB72B822ACF2A112876275227412764278829702B67297728AC27BD271929E627B126D8261D299E287A25E625252674281C286429E62BBC2CF02A9A2AFF2C0E2A132BA12ADB290928CF29AA2CCE2BBA2AEB2A3F2C512DEB294C2AE6285528CE268428E128392B172CBE2B862CBA2B3E2B8B33C931F32CC62DE12BFC2B152C242B752E182E132C342A39288827DA264F26AC2B9B292B2BB62ABA29E828E5266D27412662244F267C264A27A526B9256D29002BE42BBA2B4C2B2F2B372BCC2A212B032B862AD3291428A629E52BA72CA22B092A9B2A8F2BF12974296C29EF26E32655282129842A472BC82ACA2A902A512BFE3455329E2D752B1B2B6D2A452AD92BF92D4F2D782D4A2C5F2A6227FE257B25D92A802BAB2ABD29BC29FE282D286F2783287C24752788283F28F527AE27A7269029BB2BBB2CF72B812BC12A3C2B182C862A6B2929293C2742280E2A312A4A2A7A298428532A5829F0289B271A255427AE263327222A522BD1279D271D27852BE8343F33702EBF2A95293829502A062BDF2C852DA12CDF2BF12B742ACC265A262B279B2703297F294F2A792B6C2B372C4A2AFE2AB72A7D2AC82B5F2CAD28CA25FF28122B112B202A8829C229E929882B342A062BF628A0279C254F28F828512892296529FC288B289E281B27DB2625284F2A652BF32A312C292A722936298F2BA33624316F2C6B2A0D28CC2630288927CA289E295D2A732955290B28C3252A26B0248D268D278328CB28F32AD02ADE2AC72B7B29352A422AC129192D452A3A29AB264F287C290F2C0E2AEF2859283D2B262B1E2B4B2A9027AB239429EC274D286028402854294628D325B9267327AB29892BFD2A062C1E2B2D2A53283028B12A7A346F30422B9227CB2608279227B627F4255727A827332739267F2509257E24D9241E24E7265C26F928472B4A2A5E2BE62B1F2C4F2BF92A9D299C2C212CE22BD2298B29902A922B6A2A8829BA28332C0A2B962ABD2A0F274D27A5271629F628F1288829722A41283026BB267328952A242C402BF42B9B2AF0283329D729D12B01353B3272296D28D726102694269126B527E525E8275427DD25C0244425E7253925B325FC2502286E28AF28F827702A7C2B352D1A2BD02ACB28842B1A2B8A2A4F2AB52A392CD229422A5929302A4B2BFB2B182D4B29B228CD26242AB82A9A293A2AB929F02AE927CC26B027B4271D2BD82B352C352BF129F627CF29B4294C2C1337B22F9A2A9527BF25DF257C272028E4288E28822786282F245D26FF25B7258A27E326E5272128E3293727F7250328AA280A2AB92AEE296F2AA62A422A252B812A6A2AEB2B032B4D2862281E2864295929F9284328E1261226E82B5D2BD62D092DAB2AC32A542889250427B2273129FB2A1E2A552A4C2DDD282F27B327692A34344530F5292C289E26AC280A2B5D2B9E2ED32CF32B6B2B0229AA27BA272B28FF295129A728562A802A6929EF27F225A1265927C0298F287F295B2AAE2A052A23297129892BB02C8D2957272429252BFA27DB265F285427F827C6299E2AF32BFD2B722DD22BF72A4E2A172861266927582A9129EC28032A1A2ABA288129A82BBA34CE2CD12888253D27DA28FC2B6B2C832DF62C1A2DF92D462BF429BC28A52AF829D1293229D42A3F2B2C2AB827E72664264126412994270D28F02745283D299F284D27CD29F82A9F2B6D2A9529C5276426F826C326FD241D271629962A262CE72B9A2D602BC52CE22AFD276726DF24E92582289928E1282328E928C6282829FC34E52E6C282A261627F229222BB02CAE2C3F2E682D9A2D0C2DE62AF629D42AAB2B22297A2A942AB52BD52AF32789287F25782509281329442AB42A612B7529FA287E29952ADF2A402AF328C328D42873247E263626C327732A292CBF29612B662ADB2C1D2C332DF32AFD2A902863259E259D27EC28F029CD2980290829B629AE33472FD82A4E280528EE28142A712B402CDD2D3D2EFF2C362BA829CB289B28CD2AA82AC129292BC42B342B9B29BE29E92776262028AC26C728452A602BD229452A412B4B2AF129D3277226622618263625612521276F2A172AD62B2C2C002AD22A952BE02BEA2CC328EF285028D125C026C326E829D02B7D2AC02A8C2AEB29DC337C31872DB02AB32AC12B222B132AB82BAB2C122B8D2C312BCE288526EC28AE291529AC2B3E2CF92AEC2B402C8F2CD52B8E28F7285428A927EE2A4329AD28292A8F2BD82AC1297C25132687264E2645241F26CD26CC28672AE32C9D2A3A2C392B4F29C029802ADE2AB52AA525C2254027B128A828372A482CC52CB52AC92CE3351233FC2D502D4E2C5C2D802DA22D852CE0299629D9273A2838250B2666288929982B2E2D3D2ACC2AAE295329BA296A2B372B282A1F28A427562716298A28DC26F52806298126932317260527692704277F25D9274326852700293629EC2B832ABD286A26FD2585270129B2261127D928852AB32A992AD729B22AB22AA22BEB34D930672D652E5D2C722C9F2BF62CB22DA52A44284D274A263525A62512279729E92B212D482B232B7D292529F429662CC929512AF427C9254326D7245726A1262426A925442600264726B525C6253827F123DC2614257624F5261E27B7269B25442547263329832851295F28C42733288429ED2A1B2A80292B2B8C2C082EA836B730342BE42A932C1C2B262B282C702C692B8D2871253026E72583264D289D2AAE2BEC2BE62C632A78296E277D282728F4270B27002764251325852492258527CC259726D226B323DF268F2544265825E923D324CC248F265027CD27C828B4260B27A828DC292B2BD1292B2876276A283C286C2A1C2C632ABB2A7F2BD02C58343230D428A928DD290C2B9429A82B332A7A2A7229CE261726BE2554263929272ACB29AE2AA729262A2D270026E42792278125922551257926A624DD2290245F2654276A2621278B266128A92704296E2763258225D724FD26112AE828D729B729142BA02A992A3928B9299C272B27452ADF2AFC29F72AE02B742A442B932C4533932F8D2BC62A0C2C872C472A4F2C022AE82BE629492592252028982732273328B827F4280F28EA270427F2259B262E28BE26412650267C26A124F22481248A253B27BC288A290B28572B102C872A3528CB28A6270F267D275029EB29282CF12B932CF02B7C2A4E2BF12820265427E828182BB02B4A2B182AF32A0B2AF9295E33D92F582CEA2BDB2A9B2B372AEB2A972BAD2A302A8F28B9260F2826288428AD28E527F327A72AB228DA28FC26E42875295A289C29012664264227962728256327E628232B2C2BF9272A29B2288D2BAE28E52710285D268F26172A882CDF2B552CDE2A782C142D7529952A48285C288229412DFE293E2B472B2F2B5F2C0F2B8E340C2FFB2B602BAC2CE62AF928F827182BC82C9E2ADE2A852AF4272A281D2A0E2AD028612AD02B0B2C892988273127AD28122B172D082BF929FB2AE42AFC28A4280729832A182B7B284E27D52835295728A8273527EB25A3288A297129FA2AA12A002DED294F2A782984282A265427CD2805280F2BB62B422AA02A442B1B2D7F343A2E272BC22A0C2DAA2BA82A7427D029762B3C2B432A212B342A0B2AF12A502CD82A0B2CC72CE52E4E2C6629412B552B5A2D142DA22C252C412BE22B932A122A8828392AAB2BF22A85293E28D32737286C27A5266726CF275629802998284528B4293529AF27752845274526B725C7266929942B812BB42AF8299029002B41337431DA2889278829542B1B28E726F9252727902B402B152B78291A28052A332BB52A8A2D562B8B2D412C212AFA28B9290E2BC12B882BD0287F2A612B012AE4297D284426A52667282227F2266C263E268C276A284E27F026AB29332820277527A627A32616263E256227C225B924E7253F2644293A2A82284C293A288F277032EE2F5F2A5B276F284C272628242651269027A5280A2B2E2A4C29532A002A8B2BD52B532D2B2D572D352B6D2BFD2AF4289F28C72AD9299129132DCB2A212C7A28A428CC2655264728C525782530266C26D8274228B62755289929C729D427632712296E29F8281F29102AFC284D27C426B725F7283329E429CA2A4C2AB52AEF32592F83284227CF256D270A288326FE2645265027D22724280E28CB27DC28732AFF29F22BC82B862C8B2C2A2B9C298D28172A8A28F527E628B229952B6A2B3A2A4B2A112AE528C925E323452621256825FB270F2AC828FB291D2B672B302CE52ADF28DF289E2A402CD12A582AC1278027DE269A279029212B012A572C942CF8329D2F1A2AA328C82AF32C6A2CAE29A02BDF2AAE2AC22733271427DA259C27832717273227E028D429132964292328D4273B254E2799260028B42A2B2CF42A622D602C712A342BCE28D225ED25AE263B26AF2A262CAD281927B3294E2B6D2C1C2B2D2CF52A9A29E0293A2A2F2B442A50290329FD28DC2A732B6E2CAF2BE22A38342F31852A8929082D5B2CE52E482D182CD92C762DC92B4C295A28BF241626BB25E82554267F27A2280D2ACC28F328C02640258B26C825FF28F72AC92B9A2AAB2A9D2CE92C7B2A232A52292B2838259925462A372AF32ABA280F2B382E9A2C582C622D012C942CEE281429A7298A2B342C7E292129AC29FF29372D2D2DC02B3734EC30222C932AE929FB2B312B532E0F2D372B1B2C062C542A6C2765270E279F27B226B2266B28AE2A712B942BE829F5278826EE25A1259B28412A552A272BF32A1C2D0C2D8E2BF129F929C229D3279226232A362CF32A5A2A322B402B902B8D29BA2B9F2B9A294428BF27D528D2273B2A9F281228DE27B028612B9B2B492C8B349B2F0D2B642AE42A9A2B272C0A2DDB2BDA2B332C972BBE2CE32B9C294128B8278C27C429A42A352B1C2BDD2AA82E3A2A2526772410268026DC291E2CCA2B982DA52C472C9E2B92292C2957287727FF26B42ABE2BC82B982C812B02298E29452A622AB129002AB026BF260326EC274E2BEC29E32987287828F329EF2B742C00336430862B262A472AB62A542BA42CDE2B932D722CA62A1E2C612B562BAC2921287D276627E829F52A4B2D6C2C872BD229D92989290D283929EE29A72B582C992DE12C452D2B2CAF2820292C281F27B4273A2A012DBB2B5D2B6A2AC229B228042BE62B1E2B4B2A3B299C27FF253926F3296D2BC329D6281D282A29072A2D2C15351A2F0B2A3428C428E027B7284E2A762CE92BEF293929D928A928322A07295A25DC2415251D291B2954293F2A2D2B2D2BE229CC298B2A1029FE25C8275B29CC2A182C6E2C072A7E28742732291D27A726B728272A4F2BFA2AAA2A782A762BF12A3D2B352B412C762A1A2BED27E126FC265F28312AAA2AC1293328CA29E32B2A368A2D86298B271C25FB2674287729052B642C7E2C0B2AE328DF260C26CE263F266D24B825E726E9268D27642B922A5E2CC02BAF2ACD29E928B926F5266728022A422C1A2BD22A56299727C7274E28F526AB28DB29EE2AA62A922A9D28392914291629D72AF62A5C2A342CD42CAC2805277F277D2B5D2B9B29692B0E2A182D4F364C2FA5290A28982797272F274028EB2796290C2B16293427C8259325E9256825AD233D2322262F272428BB29272A59291C29EA2AFD2A7828FC26E125BD27FB28AD2A812ACA2985284728112AD92A302856277A292E2B4F2B252C7F29A3285027E2279727EF285328772BF32B3D2A8E28C426042AAD2CD22B552BB52B1A2DFA348E31C12ACF29C429BF2B78299128CB29B629EF2A7C294D2824260F248223A723A02358253C27D428E0270F29592C312B8129B129112A07289526F92452260B2A482B1C2AB428502600284A29392A5729FE289F29D128372A1E2BF32AE42A11292628B5264227F2272D2AE52AE12B862A1F28A1277B29E7285829FB2AAB2AAB343631A4299029A5283329042A5B2A272B472C332CA42A2F296A27CE269525CF233124E925F42582281528F629FE2B4F2A6D2B582A7F296428762635262C25EA2644295C289D26B125CD252B27A527F7289229472B422924299C2BAB2B282AEB2AFD29BA296329F228822ADC2A452AE32976298427C02653280F288B28D629AB34B331332B56290728A9283D29C12AA32BB22D222CF82BE62B162AFF2764260E26FA249725CE2546296C29492A602D2A2CE12BC92CFF298529832AD026B9249A24E2272028AD25A5247F247026F4258A28AF2BF62AE52A9629512CF92B252BD32B662A3D2A722A9529192C5C2CDC29BD2A6D2B3228532890265228092A122C17346030932BA9288A286B28EE269329852C202D4E2C222CE429572941270025AF25BA246F25FE26CF28D9282A2AA42BE82C732B812A482AD72AFC2B852A2228B826A9275228A5276F260726D826F726CE266B2BB12DD32B3A2A2A2BEA298929B62BEB2B4C2AF52948294B2BEA2A782BFA2BCD2BAA2A0D296C279228B729C82C23351E308B2AB42AB4285329C9278629E42CEE2B1F2C762B2F2A4628A12534264B254B252225DD2829276E282E27E0290E2C0A2B11290F29E929C1291B2895269D276B27DE25A725A627762748262A28AC27552CBC2B5E2BEF2ABA2A8B29AF285928FF274A28C9291A29422BE82A2A2C842A882CCB2CEC2CFA2964290E2A602DBB3517306229B428372BEF2ADA295428F62BE62B512CCD2B092CA8286D269B27352719266227B12571287E273025E328642B7F2CD62A822878273C28BE268C255C2770262226D125F52449251E28B127CA27F02A452B2A2CA02C6A2CD629FC2798296D2A7B2B3E2A6729832A302A112CD62DD62A2E2C1F2C0D289E27FC289A2D5436D32F3B29972A3E2A9A2C492B592A9C2B4D2B23294429F828B3297827D8267C273F265828D127782831278725202712273028BA2A432949289727B8264B2420249E266129DE26902573263125342A0C2A602C1D2CC82BCC2A9C2ADB290D293D29912AEB2ADA2CD12A752C672A132CB62B132DB02B052D8B293729B9294D2B2A361F2E162A32292E2B5C2A012D2D2B5D2A3F2BBD2A472AD12782266027FA255A25BA272829BD2A3429EC255625DE252B26FA274A2A42292F28C5275225202485239023DF250A279B262826102669274A297A2C7E2DAA2C232B922A5C2A6429822AD52B3B2A002A892A8D2CBA2B912CC32CA12CFD2BC42B9229D62842285E2A8035272F162928293B298B2A732B3D2CD72D602CAD294629B6263B261F240825FF25D8257F2ABB2B1C2B52282227D524E6272F28BF28C727AD288D299228182729259824AF25712617289F260D28F627D3297C2A982CEA2B6B2B2A2B9528DE27CD2808293D29852A092C9E2C9C2A1B2B222C8F2C772B3D2BB02A0F2A48292028B332812DAA27342607283029B32AFD2BF12BDF2CA629A9279B25AB2454251F276A29E7298429F729A52A10295F26E92601269B266827AB27B3271729E6282A275A25A824112349244F2459284926A22711273B28A72ADC29CC29F529402AFC270B29AE29BE292F2A2F2B662A4129AC2B9B2CDD2BEA2A992A0D2ACF29272894289B32BC2D7727E926082527266A29542BC22B6E2BB2291728FA265F245D2412280F2AC92AE4293C2AB528BC2757252D258927E028A829822A6628F62879298C278C26CF24FD231926A425F6262227FE2671254126FF265928372BC12C6529A1288928082AC02B152A932B832C592B9D2A992B912AA02AC02AEB296C2AB329F129EB32832E6726A224E324ED251828BC29DC299029A027C72411255E24172454268328EC28AB28CA28E6272D270726F4241F2778277328B4261F285B27D9277928E1262D26102577273428C02762277E2714287E278325D6261C2BCE2A7E2AC129B127F02840293A2A902A5F2B6F2BCC2A8F2C222BA62AC02BB72BAD2A5C2B9F298A331E2DA627E625AE243E254727AF26AA28C2274A26B2258B234B25C8241B27B62736295D297B2926299B29B327C326F1282826B428C428AE28D3256A264D2731265E26442772274E27DA27B327372757274829DA263E26B226B428BD270928A12A98296F294828FC27072BD42B452BC92B392CC02A5D2B68271A2835293E2A58344D2E6029D4248D250F25EC26FE27F3266D282824EB220D24EE243E24C6249327B626C8283A29712A7B2939290E28AF283627582752279C274A27B325B0239F240D264E28D227FF27DA27BF275727462613281E29A2273426B927F225AB2648279F2A0128B2260D269A28B428522B452A3D2DBD2AAD2ADC2AA228B9287829BC322A2E8729FE25712458248825E425992638270D258F23E2224C24E4206222E42381264629A727ED297D284327C72784268126B0241B269F249B276F24392605252E27512742285E27092955273D261E26ED25A826CB270127A528FD2467267C267C296C2AD72AE228072A0D293B2B6D2B172B242AC52A062A8C2AF1282E2807336830B029BD25A0249E24A225FE23DA2554263025AE24412391228823DB230A24E5255227A5264028A225302527268D2729264F267124CE234724DC2487237325AA263825FC25DE26C125B627D5264B232E269A266F2698258028D427C2287326CC278B295C2B4E29152ABA29DE29662A5E2C9C2ABE2BA7281D2843254B26B030882E8528E22599241623602318252524F425EB244E239D23D7233722CA230B23E32363240A25DF2405249C251426C025F72432266C2452234B24B225CC2480249925A22500274B26D3265927EA251A24412350244F2593258526DA26CA276E26F52544289C286827682774263E269929902A542B272BD428342805271427E2319A2C8F26A025BF2459235F24D8212B2319252124562409232B239322942222238E22D122DB24EE245B23E523BD2508261525ED26E924FC25C12407234B255D24CC24A926E828D9268E28E028B2262726E02459253E2595270928E226A325012453257B25BA256724F925F5244E25A426BB26D3275829B227C027BD258226A62F292D2427E123CE253625FB2366231F240025A7224D233525B523FA237A23EC22E1236F239923C224DF243226EF28222992270C29BE28A7262225D8253A267D26BC26DF26D126C728C629F629792A8429C82801278726252754283B28922568269A25B824EC2310245E240025B8240A2731256426C426F425F8258B2507278830C22B2B271D269F230724C92392225722CC23DA2478243426B324FA24F12342253B2355236F2496234625C525132928294829042B6629C128F226BF230024602442262625EA26FE26C227712885295229C028DE26B52416285E293E293A28082760284F26B7257325EC246923B6237F23B424C425602347250D257124ED241F313C2D6A27AC23DA22E7229C22C02269225D2307249F23FD244F240B24C72443211722A3221122542351244D25BC2627272729062BBE2B3728EE26792392231F25A7250526F0265C241326D0262927AD269A27CF2643258C25C626222898271B284F283B288F2612259A25512318241726B823E7234624C922F922CA25CF2592317C2B17262923802248223223762107243B215B23B424A3257725DA25182567229E212B2357210D23BA21B822E0248E26C7264E296B2A9D28D127E72462248723A0243F25AD2569242F25B42505265B26F2259C2623256F24AE2430267A2765279128B0280628CA254F260623A024A724FF2417268D2537248A25D62451264C30662BF123B22241245422CF222D22FF227123D32217260F260B25F5251525ED232924F2213223C323AA22C7210624FE2571241327842726277C26E223FE239E21F0221A242326A424EE243525A325892482254F273B2433240327FF261128F32774263D260027A426FF26072682247C268E27FB252426E8241B24A1249525A32E072C9E26ED222A237324F421F422DF22E423F32476243D259A26E42489230B259E2445247623FE23F121202131239124F423142403260B27F5249C258E233A21DD239D236D239B222826782453258623FB25B6250724A12429277926282AA527FA28D9276C268324BE253925A4253827832618254A2664267426FD252B276E31862D8626E222FF22AD24042262231E226E2389238D24C523BC246E23AF236823F022CE2251217922DD216A21762285246724A3239C2382213D2442238F24BA22702211222D233423D82191259623882241231E246A229224D3258E26D126182873272726982581229A243424FD231A24AE24E024622623268C25BF250B266831CF2B05268624B421C321F3218D226822D323DF221123A9238E230D254125082263233D223F22E5229022AD2152226122EF22A423D62159211424C2233223FE21CB22BA2302222021732086228220BA21DC220E238320012230257525DC26D9242B2795263125C1240D23A022AD222224D4224D23EB2338226425AC253D27722F6C2BC62581236F222D22D522A7239723BB23AA220823982479233323FE2350241523A1215A214F2280227121AB21A722F123E7225F21A3223321582301225E222622112262237C218220DD21152167208421A5201A2186213B23EA2489241E27C92508247E24BA24A12493215D22DF227D22A0215923B5221425CA256A26F32E602BAC263922D2213C21DD2033239D22D5228F211A23DB202B224122F322512289226521C521A5219D22C21F6C21CF248F248323D222B321D622DB215023F9231A2277211A2336203C217D2165207B214B213922BE221421CA23772390234E23A7252C2499222C22AF2390230422D9226E216A22CC23F9227223AC25DA267E2F212CB02506246022F422AE22EE2128220023F121CD22832175229A211D224721A520C821F720F920B81F9B1FDB2088228822E9211F21CC22A4224E22D6225D2265224222692197202F22FF219D214121A12057210720B321C822D02243221E239923FB2274236B23BF236521D1216622DE233F217A22852155222322222672306F2B662471226B220E223E212721E022B6209C218121132128210720D9200322582110201622BD21E720211FD92179215B216122E122432212223C219C209022E421922207208620951FC02145208820A2202021BF1F55202722E4203B22702245227F22402203229222D0229023CE203E211D229522D4226E23AE24CD25612E4C2BC8242221FA211122ED2011224622E621F5229B223E20BD20AB2033219A21A521E3214521C5207721D11E8721271F40211522AB1FEE217F202521D4207F227221A320DD20E9204C1F6C2164218720FA22F721841E4F21BF206421D621C2232F22AE1F0823E32141205E21DB2050213F211A2208239F20772398232126B72E7A2BD4254E22772144224D229E219B231422682149211421C11FF71FA921DD228621C32193232C20B621E82082217020C621DE223F219721DB207321B52015225421DF1E3321D51FB01FCC21A92149202F2241212521861F3322C0201024D221AD215E21AA219B20B122B11FA121C422612360213822EA226A23C1214824B02F6B2C84258421DF2295226D22E921AC2348230722BC216C221321C31FE0211022DE22A7222B2214202D22C220C421D420B420BF22E020691FDB201B232D21DB20C91F9E1E8620DA1F251F5121D62044207722AB22BB20FF204B20F11F74201C215422DC200321E21FB32137216621AB220E22D3228821D120EA215D244A255E31892CA325C523C220A2230C247C22E322AE2337233E2475227B2108216522DD2083215B23DB2132203D213B20C021FA20DE20B1221122DA20CB21FF1F5E208920881F4C1F651EAC1FAD202520D020B622D922EB21D7214B1FC11DF41FEF1FDF2131217B21012103206B21E4206421BC2284217824DA22C320DA20F223D2251C2F +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 403F + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 55 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 0009 + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 A42936256425802333245C239422DD222223C321A920C21F7F1F241F6D2142209F1FCA21B221881F3F21F02026226320C6206D2025224D220E2231225524F1227E219F212720F11D3C1E41205E202921D3203322682031209B20BE1FBD1E4E1F59202520E01F081E38200320501FEA210420AF22B121DB207B1F53228B23BC2EA32C1026CA234A26042614246B2437253E25A52214239F213E2043219B21BB212E21BC21AE214421F9216B239C236C248E214823FE2180233723A624A225EF235224AA247323A2225C20A722F720A3216324A223AC214021842171236322B020B62081206021A120AA222D236622AA228B22F2235C247223CD233923BA251A2F072C3F26F125A3251725BC2439256F265D249E26E7243C241E221421F4215C221823F32340233E22442277245F2417248E242624E9225523052567220A25CD26C8266C2786255524D42162225021DB21642336249722DC22082383205A2237224722DF2172213E20DE22A6238F244723FF22A7231226D3235C2436242F27C0309E2CF72889266D2495258F251827C1252525B5243C24F222112377214423FE200A237823C7239E25AE237F24D3232F260425AB240623D9235323F423F92335267A289C285729FF2499225C22B6208F201C233425FA24D024DA23C623DA24D424F524BA22A122092241228A221E233324B42492241225332696240C26F8267030972ED027F02368232D244C253425BD256E26B025A9251C256723312246239722DD218B23F4235324D32402252828E52437259F26C722F71F332349248925A5267E289B297529E02598235D229E207C226822532646258E2597254D2687259A25D026EB243D24B62153233623D7241E25042584245E254124A9251B27BA264B2FC02CB327A124CE24042245245C2340243426B4252326CF2652245924A9231F23EF227422BC220C2564244525492776282E266B260424C42288223924C525072831288228C527E02414254521D121412160226A24A82684272126EB26F02606287628FB26D82314238D224923AE23E124B023EB24FD269E269A263A296A29CB300B2EE426702565245423F0227F24362497246026532693256D26C02590263925B424952259237C2360267C277C271429C2277D27A3271E263325C8243A28CF29BA299929B4281F270E2597237F227721FA22B025EC2518284B29202A2229DE29D02BC42853279223FD227822A422D123AF259E240027A526C929FB2AF32932339D2F9F2965278C263824892108227F233924B8257C242E265E27C2283C266225752592230D247A24CA2489243C27DB2ACE290E2A302A5C28D2263826CF25BE2604288E281F2834264926F726F225C7216C248A230F242C26C62879271D293829FF297D297927E724C82386221C22C323E1243B268327CE271A2B042CBC2CF2337C2E7F2A2D29AC2692253D238A2177230C253025F3256A26B027B02747284E257424DF246F2447246024F0232D24C428C128262BA52946282E26AD257224D824E126F027A2262926C7257E253324DB2357249622D5232D240A29EE28ED2736287129B4283829E426D025BD2253212C23D525A126EA2816298329492B472ABE334131FC2899277D262C269823E923532320255F249F272D295A29932A002A1528122716259E2506256B240C25A024F72763285E2B362BD528412966253623C423CB245E27102683269F251428742742253D25E2248123A0236024DD257725A9268C2B062A1A28AC277B25FA23C023FF22CA2421286D298C2A2229032A172A3B35CB2F592C782AB328AC27E3249424DD230F2429251929B72A882CDF2AFC2A9A29792808275F27EC2767252726F1248E28C2277D2722271127FF273A26BA236C2290241D253526D126512669274B29ED2700272C244C230123C2246524F624AD264827FC26CF2691269625AF220F2158236D248B25E0271F277128242808284933A130202B7E29202BF727F924E5247723CF24A125D42660290C2BD32BDE2A3F2A48287A272E28C72BA1297A2A06269925B82693272828ED271C276C26662617248322FB239A2358255A2667277A29BC295C298925A824E423F92318245624A4268D26B626E127D224E625F7238A222624F323B9240926A926D22543256828E3318731982CB629F6288B271126352532238A24B82312247A28EE28492CB32B902B5F2960286E27CB2AB32AD82966265C27AF25AE26A127DE27A828BC294C28C826C123012477247C236C237B26B4298E28BA2503264F239822B8249525742520285E26D4250C248C249C249D234D223A23C822F422CB268825EF254C264228DC2F5E2FA32AA22854288B28AB25B625CF253126BE252E24D124D6257B27012A0329652950296429902A2A2BFB2978286D261D268226F5266728112B7B29A928E2256326B324D223EB22592434258A2775262928722779244C23E022E424F1265F287128CF24F62585250727C523B62196220F23D9244A26E626F827A027E428D7337E2D1F2862275E26F327BE26552897266F29DF26F82696261E2456253426E027E826282A5A2C142BF82AD2291329FB277F26BE24D8243E286F29AE28E62880284E296B27AE2546257C254D25A22605286328B8281227C5232F2378242A24A025102679274128AB28812AE6281026D324A9246723D62658267D287429F5294933882DAC2879253E25BC265D28C828BD295129582AD329D226F42284246525FD26D1250629A529D32A7A2DAE2DDE293E28A2240C259025952584263A28D9289729462A79282B27A02673266A2ABB29282A5A2C97285E269224AE2392225223AF24E3261B264129D6294C2CEC2C9028B727CF24DC24DB253028DF288229FF2A9533022B7627712662279626C927C3283F2974294B29E5293E27AA240A24712372243D2553268D2898299F2C942A6C2A7A293827B926AC25B5237A25E1263228072A382B782BAE2A06295828A927AE28AB297D2AD22953279C25202686238B2263258D26F72615297428A4294F2ABC29E52529264025C6250828082A4D2B3A2C9534202C6E26B6254324C726E5289228BD284A28FD270427A32545249323E321782235238F258C287F28EB28BD29682860277A259227ED2738276326DE24A226AB275F2ADB29372874269128FD279727C3268C270C290C2704233825532297218622C226E925AB272D26BE28EB27F52A7D293D289A257824CF2542293E2B782BA135B62A3128E523AD25792725282C29F927B726BF25262758243D24A6231A2488228D23A4263626C629F429C827D728522961264628E228512A8929C2264326B925D926092790263123082694258D26DD230A26DC24A6243123CD21AF209F20D0211724ED26B228F625A328B1284928DE265526532431236723DE2765296D2CCC34932D1D264223DB24D525382713272B2748253C254D26B3260C26D223C0256B2584240922E72578277826E826B9274228D4284128F7263F270B2B6A277D263A234C244C25DA237F23232367232B2272233C247824EB22A3221324A8218F212C23DF249725AD26C028972B6F296329662B60286125E623DE22B724D1276329E933C92C13272025D2246725192734264D2675269326D428DB27A0264B264A2575255023E625C1248724A925B92886289E28FC28F9262D2712263A266126B9246C258324982421232223B021922141240322F72465251725132554239B249122742277234323F72532285B2BAD2B262B082EC6298627602647226824A325152793327B2DCB27572782244D241B2603275B275528492873266F29442A0C2B87282D276A268C23EF257B24F2256826C527C7279926B626FF230A2477257324C4259B2404254F25A9232B23F4224D237B2325253B277727F025ED266527AD261A243D234723D52407264829CB2C7B2BC72B882AD229B8296726C1254A2521251B287832722EDD28BE26F726FC252626FB26AC2784290A2ADD2A142B482B102AD62BC8285A27F72486247A221F23B82351240325EF242B25BF234F24B72490239C2534269224BE24D52342237F221126ED25C0256A29CE29EB292D29B22920294F27F3259524EE242A28F1287A2CB22DF22B322BEC2A98296F287926E324C0256A27E8332330C52AE02A0F2976270724E5255328BD28B228DA29732BB129E42BB62D292A4F29AD27E024E7245C2475244D250E26D5255325BA24EC260C27572539274B2770277E278A2494210A24DA24FF26C128AC2AA42A782C4F2BF42BE02A3B293029FB24B72470256B279A2AAA2A462B152C582C942A9A2A7B28FA265B25A32808348731C12BE42A0A2BE02979271325C72520295329292C8B2BE22A822BBA2AA32A9B29E3274326BE27E4249424F7250325942496253927F9274D271C279326412843285D294828C4257824DA23FD268127A4299B2B7B2C962C102D012B4C2ABB294C2A2426EE232225D126CD28D129EC2C512C4E2B5E2B2C2A0B2A7028142610314F31492C472BB02C982A8E292D2814269126A126D328762A062B7A2CA62CD22BDF2B3628B127EC261827222847257E25A024B225B427FC27232A9B29FA29BF29392B452B982BAC286D24B2243625E925C32A2C2C382B492AC22CD42A7E2A0E2C202A89280F262B233E261E27FB29E62CEC2C1A2AD6295128AC27B4253F27BB307C32592C7E2AD7292B2BE22A98293328342693256828302AE02A982C552CB02BD12B2F2BB828132AE329A828E426AC2662247C24B226D926E82836290F2A2D29212B822C6F2D3129D6288125AA24F725D726A12A812B002BEB2ADE2BFD2A1F2DA62AB828F426B224B1247D24B82668281728DE271328262774259625CB25F22FD7313F2DB22883294929442AD72A472AE5277925EF27A428902D5F2CFC2C862CD32A0A2BAB2AFA2B8F2B4C2B242934270025AB23E62290254E27B2283B299C2A3E2C842D2A2DB22A1E2A93263E25FB23B82576273029F32A462AFF2A362A632AE22B86286E25422549249C23A324DF24D325BC246F26AE25DD25F9242C27F5308D31EF2A422A522983288727B0288028A3288D26B7248C279928442C322C972DF42C822AA02B8D2B432CB02B9329C726DE23FB232C222223A1262D299F296E2BB62CA12D262E3B2B912A4528B5234D23772538262529E629FC2BEA282929842A9629C1273624562451230F235D237D2546247925A1258D2494243526EC25FC2FC330BE2AB12651266C26FB2807283727032869254C27CD25882610277729092C6D2C1A2B822BBD2D892AD42A2828E227A3230D230B2341232F266628092AB92B932B522CBB2C0B2C8A2A1F298A26F5240626D626592A7F29732B842A252AD429B8296B28E6251C24BF238023EB2212231023EE2340269625BD25BB264E26E430E12E0B29CD275528C729CB29642963293028FE29942719271B25D725E5250A26452806292A2B1D2B93297D2802294427E424F7239024E72215264F29882BEB2BF82B3B2CC72B112B002B4E2942281E261F2506285D26DD281E2B5D2C732A762A4029D52964273223462512259A25FB24D7230C24F625FD269429242A59290B32262DF129DD27182A502C7C29ED288A278229752B132DE12A0127B2257F27C2279526FF27B22844295D269F26DB25FE24FA2428261C259B23DC25B925FC29EA2A882D662BB22AD427B527C529B529F225E324862520242326F028A82A4B2C5B2AA62A4F295826AB257E24FD22E7223023F423502647260228E427FD2ABD2BDB33322D0527B625E52656294E2A522B282A7729012BCC2B4A2B622AAA278C250025A625F424BC27AE267F26C1267626CD2577260F28412785266F257925CD25E828452A8B2BB729A727B325C029202ABF284B27C92513240424122695277429D728812984298C284527D52720253523FE22F02495261D29D5285F2A182C3E2C2C35322E4B28172584263827C6296D29952A6B2B9B2A0D2A532B9E2A86291F27F324772374256925532612283A27E8288F2745279028C228192569259E2518250627052B022CB1294227BD288B28E729D129D129EC277622E1222624AB24F82541262D275328B529C22A3C299B243B220C22DE25ED271428BE29F32B772C222C6D36132FB0273524792540276226BD29082A602CCD2A5F2B862B602C54291428312597249D24EB24F225BA29B8291729502A7F2896274829D226572573252C25A827DC29B32DE52A96286E292E2B502B0329E9282227C22347232223262573272425EE2657261B269326FB262D26C423DF23F123E7262D2AEF2ADE2A3D2CC52BB235E62D0B28502663249F251E26EB276329042B6B2B4A2CE32C752BCD29F427EE259926EE24DA2338269B252127082985286826E9269D286626CF25B8256A2455269428C42A222DBB280928272CAD2C7D2860266527632580223024F0260D282729E327DF26BE27D528EB28E825D021442278233926332854284629632A7F2A9835EE2F5D290C286426AF2587245B252E29672B222D9B2D732B0C2B1129CC282128A9279D27A725AF269B252D263427B129E5285027252A0729E427B827AE242B268827DF2AC02BC3286A27DA29272AF02890281C27A22419238024AE246828B429A52C43283429EB2AFE28C526E824D12427242C250428E128972A862A592A4D332E30C42ADD286B2723278D264C27C228952A932C162D102AC928022BB329AB283A291A2856273F265F26B7231027362AF929FB28F429EE29D7296428C4257F26AB261129CA2A4C298428BC27282840264D27F9242026C323A8236C269F28D729202B642B092A9329312B8D28CB279B266B255223E3266527D228CE29132B4834FD30922B6F2AC52A2A2A68298728F526962A8429522B112CED2A7B2AEC2ABF29982B212AD2281A28A6255626F028C128A22B2A2B9F29432BE929A729FC285D26F925F027D52A37291828D2263425AE2532267624A42529251125A3248027FA2A762CCE2BD62A302AA02A972A4E2AE028B125E5232526FD245F29592B932C89336231B72C742C882BCB2A662B192B8E29F8275127AF29822BCD29A72820280C28F327C32944294528E5273725B326BB29FF29582A6F2AD82AD72A0F2AD629EC26C125DA2576274E28D8277F269624D92305258C26FB26012645279B244E257927DA297A2B362C162BF02B782A932ABD294D280425802628274F2A942C2B2D03353531C72E932C732D012DA62D782DF32B302B6F28C0250326C427D729EE2A9F2A052C182B0F28C729712725266126EC260329AA2A7A29D0298E2C6829AF2993281826EC249725F7251626B325FA228123D4258827D62899274C2683241525E7261E27A72845291A2AAA2B522BFD2AEF298C27EF246924C225A028142AFF2DFE342E33722D1E2BF42CDE2A652C262D312DD02BB729D328532742267E278A2A882BB62B4B2B6C2B642A462A35283427D52702294229BB29CA298F2A132A9A28B228B8260726BA265123A923C82403258A24E825E628D42AA02BAE2AF2267624FD23E72639276A26FB26EF289F28CC29F2292828D2253C2624251E280A2B372C93321833A12DFC2C542B0A2EB429252CE52C832B252B9D29A227E4249A258729D22AF72A812A612BF129B2291E29A4269A26A32682276A28CC2959291228BC27702707276725D6255F23D6225022C4231125B42751281A2B7129AC2822264E24C7222C245B268826BE269A274B283C29A62A962990289D269F267126AD25EE28DC31F230CA2C8D2CB82CDD2C682D032BFC2BBF2B472CB92BAC29B7268226B22636297D29962BD22BD32A6728862802293D28CB241F29CB29C62729290A2716264E28F7270227F724CF2180211622C22445254A289129212A282A222AC427CF241B257C23D124C52777287F2867280729F62AA62B0E2A03292425DF23F625EF28BD322C30132CD62B672CAD2C9C2C7029C728E12A292A442A9B29722693247927F428E629872C0C2B622B3B2C6B2B342B37294326A6240125DD26C6264826592677257D252E262725B3217A22C6233F24A727F3282C2CEA2AD22B2029EE284D2648249A242B267D28D129DC2BCD283629DB2A852A1F2A302948263A26E826D6285D32522ED729B829622B8F2BC929E92874280C286D288D2793275027BC268926D72694283D2B362CF42C702AFF2A6F2B3C29F427DF24C9237D24B025B1226225DB24C3250C26A7258D237722F22119257F268328242B062A4D2A3B2B1828222828264024E724AF277727392B502B092A822B922A1D2A402A1A2788268529A32AE433BD2D512A7F2A302B972C5C2BEA2902281A275D255E26C925F12560260826A4255E27F32A78286628F92726274928D2289626B624B3236425FD25E525432774282528A7265F27FB259B230D233625D424A626AE275E299C2971274629DC288B26542343255D258427282A912A002A902C362BB329FA2940274E260D2736298534C62E5829022A872C6B2CE32CDB29E1286C283927AF25B6279927F8275926A325662677262627CF274F260D269628F927C125D324A324ED2456267D283A295A29002A8C29AB2A88280B26D124F2226E23E1243C27192747271828382894289D272F261D27FA27F628AE2AD92AC22B842B742AE52A042A2228E626A0278A29C5340730262A09285829312C382D9E2BF7280328F72643285327A5272628C6275024D825032357255525A32410278227D72999293326C222AA23FE26D3267D28DE286F29DD29C02CC529F7275C240323B9210623E82533257A26A728FA280D28D9273B2688248A274A28E22A572B232A3B2BB92A3229E7268825F826B028D22B8E347730102BE5270028012B422C4E2ACC2A782BF32851271D284128CC29402A91292B2513246A25DE270428872749283B28D629DA292627A625CD2505276B290D2AC42A6C2BCA2BB12B5129FA26CF234623EE22CB256026DB25FC28152B5B2A7128F6249F272327EB296F2B1A2BEF2A7E2A6D2AEC29E927002553260127882AAD358C32A02CE7297B2793280B2BBB2E9F2D642C6D292129B628DD29AE2A522CD529FB287E2958270A295427E128A929A42B5C294828C927B624F3248928392AD4293428E0280D2A322B812A8627F324082493254925C1269128762A472D122B702B87296229CA278E28982AFA2A682B3E2C0B2B8B29C928FC258C2725259929F732C632E02E3F2C4629AE279D2A692D7C2D5B2EBA2C9E2B3D292929A32B3B2C822B0829652736264F29B727B2270C2A122CCB29EA2BD32974272227082649290F2A472A9B2B542AB92A002A102A8D27C424DE257327C327F22716290F2B9D2BB12AE529CA2869271B26D92A0F2BB02AED2A632B6F2B0F29BC254D258726D927AB32BF31662DA42EBC2AE229A9294E2BFC2BAD2E102F122D842BA72BEB2A2D2D2B2C362ACC29A7266B285228E3261428EE27A529B2284D2A3E28942616256D28B3294E2A102CF02B442B132CB42AF2297427C9251627C926E028762B4F2B772AC02A8F293228AB25BA263F277828882AD32A022A092ABF2B3B28BC26AA25CF27F7342533492CDF2C852BBC2AF828BD28042D662D612C2B2DF02ACE289D28662AF42DE02A572A8029172A8727EA2708286D28CA27BE280028E127C0255925F627302A9F2D142DC92BB52B0C2BF22B8F294327092703289028E92AC92A142BEE28F2280F2AFA265124EA255A29BB29012B0C2DA42AD629412CEF2992295F28DC284733AD30822CB42A002D122D5C2AE6283B2AA52ADF2B752C342C4127CB28C428762AB12A9D2875273628462BFF29962B782A0228A3289B2BB4276926BE25A526B728AB2B982CED2BBE2CAC2BAC2ADB29AB26B0266829A22A162BD92AB22B93296F2996295F269524FD2468275228F6278D2802284329872DD82BEF2AC32950285030CC316F2CA42C312C0E2AC92A70275228B82A882A172B332B712AFA274129BB296D2BF6284927F0293D2B772DD12BE42CCF2A9B2B952A4C2B6D28E72659279027692A5F2EE82B682D722C692BDE276A2530265F280C283F29D2297E2B4D2B4F2AAB285A26EB255F22E4257C2677259D270327F527722CA72BE329F629C429D931E72F2A2B912A402B7D2954280B2690277026842644296727C0288A288D2ADD2A532CBF297B27CC27442B3F2AF32CB72B322DC22C042CD32AB0295D284F27A2275C29632D772DDE2DFB2C762A8F2ADF26DC25CD2824295529F92A9C2BCC2BAF2B1E2A5D274125AF23CD230525F5265D265E249D231D29FD28F82932282728B230782FEA278A27CA275A286F259126ED2490256027462803284C29BC29932B322B642CCC2A1428F4275328162A2F2A732B732B652B742CC12BA32A5B289B260C264729A82C162DA52C912B16292A2912266027D428EE29EC286C2A6F2A1A2B152CD12A94290228EC22DD249E24B4269A266A257D272829FA291F2C01297C2849318A2C8D26AD258325122567253F252725492519269028BC29A82CDC2A8E2C902BC22BBD2A4229D12890281129BE2AC92A1F2AF229662C772AF02B182977287226CE269F2A3C2DDB2AF32AA92ADF27FF2427265827E1276F28A7273228D129B4292029C3278826862354255B255D28C729BA2701296328B42794295929472A2835E52CBA26EA265B26BD262027B725F3250E26AF254E283E29FA2BDF2CC32DE02B982C7A2C602A38296F28BA29312ADF2A732CF02A6C291729F62AA92BD02819288F277E2B0C2C1F2CCA2AC52A4229C426B0268726C92A502A0B2A102A4F29852A29291D268A26CC232126C0258D25D22AB92B932A0E2A722A882A652CC52D4E361D2D04272A260C277A27C6274C25D926D625712552274B28822A712A222BA32D4E2DB32CCF2A7B2850277F2A5B2B7E2B48299A274A2832290429B72BEE275025A226C62A6F2C112C6F2B602A742998272D26DC277328052BC529022A952B292B462AC52788250A253D270A260B277D29312C782C372C0B2AC12B6E2DF12EBF362E2DE32822266D27D6277F271328DD2792283026D7266E267629852B642C552AF82BD42B322B6529D9267D29AA2BE22B022ADC29E627A2277D283E294D288D265427BF29AC2DB52DE42B402BBE2C72292428EA264129FD2B9A2DEE2A842ABE2A8E2C162C6B2B3F288B28B326D126D929192C162D452EDC2D8D2DE32B772D2435692FD52A2F2B1A287D29BB2A382A132976292529D928DB2774283B2B342B272C4C2AC42A152A6C2A0B260727AF279B2AB02A122C80295E288028D02875273426F1245B27A02C0F2B962B8B2B4B2C98294029AD27E826C728F32C712A702A0B2BB92CCD2C9F2B632B902A6429A3288528602A522A5930E52BFD2B7A2D092C8E33AD326E2BE72A3329442B362ED02B732B962C1A2B212B012A622AD62BED2CC9299B2BD22BE12A962865283F26AA248828752A6D2ACC2B492AA129D4272F27B1255B253F278C280E2A512ACD2A652C99292429472883273729E82B4A2C332AC52A1E2A952C472B592B792C2C296B27EA291529082A4E2DEF2BA42A082CB0284633E032C52ED92A702A4B2C9A2CC12DAF2CF02D582EAA2B662AE429B52B382DF72D702CBA2C622C832CBF296A29E1277A258126FD29E129732A422BBD294E271C26222656264A2638278829CE2BBC2AB52B7C2C292968298729A12C4A2BD12A012AC92A2029732AE72D7F2DFE296F277727CC27EC2AD62A7C2AF4289029C42954339931522E992B382B7729842CBF2E822D7F2D212B4C2E9729FA2AA92AEF2C122CE42DBC2C8C2DA62B3A2AEF28FD2405264E25CD25E6271027D3282029142676257825C025C524F425342AAD2AE1299929A52AD92A7829FD28312A8F2B212BD328CB294329402AE12A1E2AC1281925E72586247F26B727DF27DD26E025B127ED315A2F7A2D152BDE29E12B302CEA2BA72E4F2CD02CD92C172A5A2BD82A112E262DF62C862D7C2C8B2DD02B592A112AD727CB247B2597262328E628862938291428602603261428C8288228722AD32A2E290A29702CF428C128D12B412DAA2C1C2B3328DA272929FE279E2A7A286D26E7251F253125FE25D9257D251D283F296933C12F052CD52BC52A9A291F2A032B9E2D6A2C882CDE2DE52D6B29D3292A2C4E2DB22DD72C842B072E5B2D852BDA2B1B2733262428CD296928D8280B2A4F2A0329E0286A27D3264528BC29562B5B2BA22B0F2C9D2C092C4C2A00297F29942B8729DC283C2A4729F92879291028D829552788242425D6264D25DB250428452AB5324530862B202BA928342881281C29702B8A2D9E2C902C7D2A382A9829062B702D082C122C2D2BB82D712E972CC32A0E2A3027E6270729BC293E2B162A4B2ABA28D729A12868263429792AE5299A2B5B2A402CFA2D7F2BC22B9A28CE252529F029142BDF2A48285A29DB299D2CA32AD82868266D25F424AE26FB29442AE12AA633162FD62B422B3B295B2852281C28CF28FC2CBF2C752DF02C5D2A5E29BD2A6C294A2B632D332B422B822CA92B672B4F291B289E286A2B7A2B7A2C562C042C4F2C8F2B482AAE299227C3293D2C3E2C062B532DD42CBD2BF1289C292328CB272F2B332C162B3B2BAA2AA62C4E2BC82B2E2C2C28B827C02658281D2A7C2C522D6334392FD82B5D2C102BBD28142885277729C42C7D2D6F2CC32D22290D28D528A328052B8F2A042B432C8E2C6D2CBB2A302993270028E92A942BD52D802C1A2CB22AB42BB6281A28D325D8287E2A112C8B2B872D5B2CF92B5929AB2AC928512A012DA62C112B0D2B4F2A4F2C162C692DEB2BBC29B1286B274129F72A6E2C1B2D57360632B52C582C002CC32A792AD328132A162D892C2E2B222CD42893278427BF29D32AAD2BFB2B462B2D2D092C6B2CB62A852839286C294E2C352DD72C352A802ACD2B9128B82869258527C02AAA2B232B692B9A2C6B29B9298C27F72855281B2ACF2D222B1F2C042B512A472B472B472CCE2BB129D4291B2BBF2C422BB72DC037892F892B5E2BFE2AAB2BDD2AB62B402C4E2C132B162B1E2924278E24D32529291D2AF62A8C2C812CD82AD72A4129BA284C278E28A428A129BC2B302AF52A702CAD29DD27EE256F2470276E282129EF28532A8B29E22AF828F7278E254E273829AE2B932B4C2A482AEA29472A432BFE2CE5291C29D529A42AF42BF32A472C3637DB2E552B592A942D7B2C4D2BAB2D412D782D252CCB2928280D25292645267528C82A132D592CF32B7129132D842AC22935271C2961284A2896291F2BE029DD2A93295126D62528244E25A0284A293529A8288629352AA2287B277A263427652AF62C0A2CF82BB22BD92BE229FB293B2AF3292B29D029EE29262C6C2B472CC7362A2F822A062C322E762D752C2E2C652BA72DCC2C732C70287426B8279D26AA2ABA2CDA2B4C2BB72B98298029C62BF02A2B2743265325D825F92628264D29CB2A1E2B11294527AD24BA256828A9289D28C428C927A3263A2551288B275C26AB29AE2BB02D0D2CBE2AFB2D3D2B2B2CF9297D2789286229242A6E2C822CDB2C2C35A62E2C29F829A62BC72EEC2CFB2AE42BF02D832DF72C6D293128E9278829582B942C202C552A792AAA2724286D28592825240E25D42409247926D525A428322A402B182AA92732263C266528912AE429B128D3280A261626FB28B929DC2936295A2B832BC62C352BFC2C562CBD2BDC2BB4289F28C229A9281C2AE52C282E8A353E2F682A5A28DE29162DF62C972C992DDA2DD42D402D1C2BB4284728C828872B802B9F2C602AA0276B26D227F3276E2695256B241625F62484241E268725D728F92B2B2AC029CC26842663285B2A052AF92A502A70289829132BFB29FD2A882A0B2C572C452CAF2C662CD22B902BE52DBD2B942ABB28FD2818292B2BAE2D30350931212AA028AF289C2AC42CAA2CDB2B0D2E6F2DF22D782B2A291F2A2D2C7C2DDE2DB82C092CD928C626FE26C826EC26CB252426CD26752575265A25F7271C28032ABE2ABC284925A725DA28D1298D28E42AFC2A532BDA2B0B2C2B2C0E2A07297C288C2B4A2E2D2BF12C9A2C5C2AC72C732A692BBE292A263E26A929C72B3635E930ED2BD328CB286E2AB62C932DA62C2A2C1F2E8D2CAF2B262A8A2A492B932CD92CED2BDB2B9F2A6D29502A4A2ADD2A0D2A5A2AC328D126D0289128912820276A28CF2A1E2BB9279B26A42A6A29442BEE2C402D9B2AC02B9C2D692D9C2BA32960297A2B212A802C9D2BB52CB22B402C4C2BD22B342AF228F3281129872AA034D731432C562BDA28902A962DEF2CBC2E7A2E972D892D812CAE2BED29FF286E2CC829242C0F2D282C782AB428482AD12BD029A0291E283E29F02ACC2A272CEF2A522BD12B2B2B4E29F727E4275829582B552C872D872BD52C8E2D922EA92D672A43288128CB29192C772DFE2BAD2C4D2D082C5A2B512AF429CE292329C2299A347031D22CDA296F29C1296F2B772DB32E9F2D7C2E092E212D122B5B2A2528FD2AE92A8A2BBF2BA32B842A58294E294C2C0E29E5290F29E928402BB42CE92A922B312C852C532C662B93297829442CC92B4F2CE82C602C502D022EF02CEC2CB72A90273B28A928122BDE2CC62A3B2D892B7B2A452C612BB328BC29D428422ACA323D313C2D3C2A2529AB280A2A312CF72D612EF52DBF2DE42D702DA72AFB29B7273526C527122A0A2CAD2B742A842AE829B92B1A2BD2287E29BE2C732B1A2A0A2C202CBE2A602A94290E2931296C2B3F2B4E2DBC2C812DF92B902D4D2DB82B732C032A4228E228B72B232CD92BDC2BE02CC62C2B2CAB2C102BE92B6B2BA02B35340E30932C702C432A2729592AFE2A6B2CD42CFB2CFD2CB72C652C8A2BFA2A6027AA262F27E328562A8D2C8F2A9C29F72928290E2A2F2A2528342B562B462C522A5F2BF82A2A2C7F2A8129A728772A422B572CB62C002CCD29C62E052BDE2BBB2BE329652A092A982A692CD42B2F2C722CDE2B5F2D4E2C032B0D2A062BA82B2133262F602B6729B52967299729D22A9A2AEA2BC32B782BC02B682BB32B132A55287C2516271227DD29802CAD2A782ACB29D82A212B772AE927182A142C992D1B2DEC2BBA2BE82B682B232AB8272629FB28F629252B1229E229402A3C2A8029EA29E429D0282A28EE28C92AF92B482C712CE02BA82D302C9029D029812A082B5133A9300729E529222A61288427FD272E2B3C2A8D2B2A2BCA2A252B892C382C2629DF270427D028B9291A2B9F293D2BC42AC42C902B332B0E28DE295F2AD22B402C3C2CD42B0F29692A212A4D293828AF28CA2BF729802ADF282B2BEB29F027462813276C27EF25FA27BB2AEC2AEE2CDB2CA02D982DFE2BB029AB2A3029BD2AD335F82E512ACC28F42631262826C3262C29DA2AB829822A8427C82A102C3D2B942BE1292E2A312ACD2CE129F428E829732AA32B5B2B1B2B292BB229092A6F2C402CC02A022ACA2834275D29F228B0287427A02715292A2A6429B32DEF2A902B462ABF288E277A25F525E029CB2B692C242DAB2C9D2C382F792A3C28C02673293E33F92FED29B828C9268F269326D625232AFA2AEC2A7A2AE229A32AFB2B062BFD2B8F2AE129F62B0C2CD52B2F2BA02954290129612AA1296F2BB22AE92A722BCF2A5F2A70289E273A266D269D29842ACC26DA266A29B02A7C2B582C852A5A29132882293A28A22641282E2A2A2CC32CBD2ED52CD22B2A2CB02AF0281929FE29AB33D62DC229EF26BA267F26BE26572557274A28DE29342C742BC82C7A2B8A2CC129DD29462AFB2CA32C642C3B2B992BF22931288F286826C62792274928982AB42A95277F26E025BA26CC27CA288B275D2610277B276A262C29CC2A332A9E29B02745288F25B8263E27B228E22B402C3B2CC62D182D2F2CE729432A7B29EC285434B22F2629972657263C2771258C253825C0275E28CD29BB2B072DBB2C792B402AD9274A2A232B4F2CC92B192AC42C06290E27542653264F2792273029AF2860282028AB27B62602262126C4276A281B247D2607262E27212AC82B9528B827FB244F26CA249C2596243628372A8D2B7E2BEA2BB32B132C3C2B0F2AE5283C291F33602F042A3A278F253D25542570253526A0270E28FC277C28132A4E2CBE2A9929AD28B727E429272B662A2E29102AB829A62819282F2434251C2762280A2829287A28FE279F27BA25DB24FC245B257325DA257A26FC284028072A842A9F26C225E62431242225ED21AF24D2274E28252A2829B229D12AD429F529F428AC27ED31CD2F032BB9275625A825B7246F247326212836273829E729B729C529F62A1C29A2269929682BBE2ACD2A6F2AB42A1E2B3B298C29A7270B2534287D279E28052A7A2A092A612A6526A22546248724C724BA27A227122886286E2AC028482885266A247C24B124DB24F5256723E425BD273228482729286C293F2A62277D27BD3144309B29B82826260F26FA25862656272C2690274A272D2A4D293C2A132B8528C327072A252A482C502A6E2947282C29B429CC29BE27B6260726D828882A322A892BC72A34297D27E527B025172663283329712C2829F1275328D827A629682877265325EB246A26C62747253726E127D228F32818295728BF27FC25FC254E30432E7E284F289225092577230325EA261C265A263928CE281729CE294129BE286628FA29ED2A3F2C082BEB29B32A952BE429D62ADE28BA2689260126A529262C162BEE29FD2A1D2BCD2ABC270726BA290C2AA92D4E2AF5272A299A288C26522439247B26012A8A28C228D126A926602665270329A6296429DF29432946290C326B2E7D27E324F1255B24E0233C25EF25D2263E26B9252C283629942A9B2AB52954287E287C2B062CA92BCB29692B432B7F2B612ACA2AC4289A276E278B29C62C3E2BBE2BB12B8E28392AB42709277E27DE28A72A3F2A162AED29FA29BA29EF253525B62798290D2AE9280427E02565267B26B329882B382AA92AB62A0A2A1C317D2DB1257D230323F523F72256253A24822539261826AB26BF260528902AFA287126FA270C29D82B102A102AE02BF82BBD2AFA2A7B2A652B3F29A5270D2ADE2B6B2C9B2A932A32292E2A0228982798265D28F5297A296B28FD28D72618287126C4268226EC275E27F12923281226AD276B27D1273429862A972A672CDB2C9F31B02C64276F243E245124EF226D254323DD256B258D22BD24892731276C278D273C264227DA275A297C2AE529632A7B2BD82A712BE22B4F2B1429F629432A992B1C2C552C812B5329E42BE42ABB279424C228102A3E294028B526262679286527C22710270428EC2A022BAE29F028AD2769279428F2287928752AE82B092C3834342C922766247F221A23DA213422E622512226230624E123122504256426A226E424C323EB26D02556280828B3291C297327B4291228D9277A28C129DD29C52BC12B4A2DDD2B5628F22997287729B8255A26EB285B285A26822787284C28FC2732261328042B2A2AD52CDA2BBD2B482AD72A46276D275528F42AC42D692C5C35902CC7272024F423F122B5218E21BD235224352362247F259F231E24B52535253E23F423B824F425F5242725A5253F26D327C12903295F288128DE289E296A2B562A782AFC291E2864282A2A792A982762273929D12831296C280C27A429E2291B2B19281329AA2A1A2BE929D22A062B4C28C0292C2995279129CA2C6C2EA7348C2B4A27BC23E5234423592341210324512428237122C22399237C23E523EF2450237423C323ED256D24C923E2265826ED272C28EA279D27F4258A2602278B28D8275C2818281E288C288F29B8293E2981271428CF28B6280D281728F6287629F72AC128A427EE29BD299A286D274D282D2AFA2BFB297728CA28232B982C9533642FDE258C22DE22AB23C521D222042289226E2557232823EF216D219423D822C222CD242F2255243B2400244724EA25C4256326112668231D259124572499268626F92468240726622678287A2838261326E827F2288F287F2904286429222BE32A17285027EF2663289626FA257B270928EA2A3D2A62272929D22AD62B1635362D50279A220C234E21B022F521AE22AE23A223B924C0221D22D02372235C242B234724B423DC23AB223424D625B2240F25F226682567243D26582380247A22A3258C24B0247A26C424EB2678286F264825C925AA26AF28F028F32753289C2A972B2F2922276127CA27F5261F276D27B8262A2AF629CF2845292C2BF32DB535EA2B2D25DA22452047214E2248210D2253217E228C220023742287220424F824462354247223DD23402404240624272424277F25E425CB258125B224E123A4225B236B244F25F423BB238F273027CF25B6250827E52522282B2888277A298F2B8C2999273326BD264F25F2256B25AB27212849282C29F5280427192B692E8135ED2BC5258A2293232024A8232A2257245F23BB23BA21C022F2222F224D24612466230E230C248F24E1225724BD230C258B233226D625A926AD285A27BA242A2670252724042642251C25F126BB2847279328BA28FF25472593275B27BF276428DA29BD285425C22480248625B4262C290D2CC02BC42B722AB92A962A2E2C4A36B12B20255D222124F2212324E8222F223423C3238F235722CE22832097227122B2222623E723A223552415232B25FB23952340256D24C327AC289A28F7253624712605281D276C26252687271027AF26D7287F263327CB25B8281B2BAA293828652969283027D923CC247C25F326CC2A512CC02C232BD429D82B812BD12ADB34512C03273124AD210722C520C923CF2261214722A7222C22E61F632285227C23FF220B23A3237924AE23572441248A24FB24CE244E24F726F42758273D27E025CE27C6290E2A8028C527A528FA288128242930289326FC263B29132B582BB527E92882279424A8231B25DB26AD2537297729332C4A2B632ACE2B5D2BA72B2934D72B4D262E2422239022502266223121DB2153231422D2226622B821B321F2217721B0239D23DE220622EF211B27F524CE218E2142231823D925EE26C82609283B270528B6294D28BB26642628289128ED298E2745269E28E129282A902BC62AC329A42811270E23ED2418263228BC2A7D29442AB92A722AF82A682B812BF731462D0B27E423502398220522D4227E21B42399230B22FD22C0219122BF22F121BB216321BE239222D8238C229C227C22CA221723042284241C24BC2442261A283127542852296D28C928B0278A275B29862A68295A267D267B27AC2827295B2CE62BAC2A3A2966271426CB26D128FF2A302AB52763265D27B4287328F9284632692CF725192302249522BE215622FA2260223A217821AA214821172316231421522102212D257323E821E321812226233422DC213E2359235C214722DC23A925A7265B2755260227CA2793291528A828A529AD28DE25AD25E2261628B22AD72B392BB229512A1E28FD27FF260429EB288127C626DA257725EF2468271328FA31A62AD02535223B202922792231225C22D12286229121F3212D21FC2014229C223021812291230E234A22F2231522A62328230422C6211022A3211622AD222D242F260025782566265326EA264C272E270829D8279625C4243D25A424562766289B284B287226A324EE252D28AB26FE25E024B22624242D22AE246D24E0273431782B0625D821AD21BD21962193223321BC21E821EB20D6203F2178213C223E22DF20A7203823312332235F230F226A216921B122B8227821862273212A222D22D1238C23EF233D24322620280C286F250E263827BA25D92304258A2353248B253C27C125CF25BE22E7245525FA241225EA233B254125D0237323DC2429271730602C7425A52211211E238D21A3207B220A210922FB20F9209920991F881FB8200A2145223423DA23A7217721E6231A223120A92016217520DE21D520512160232523B6225822E621F52469261B265F24C82401265E23032381228C22CF23882329251C255E25B2236A24802402268825EF2353245924612287238D252D2570302B2C8C245D23082125212421392185213D224922AD20FA1FA11F032107216B203421AC228B213723C7218A21832279209F21D3201C2045201C214222972114220323BD21C121EE2144238C247E246C246724B125A92388227B23C42242228D2320241D26CF26E2252226502514258225F1254C246224EC24BA24ED2564271A32142C0626CE23FA213622E0201221AE20BB227621992173213720851F7920CB216221FF21F6208122512135213A23EE21332150222720F51FF021A120892052208D222E22A520B7209121DF230922BC238D250024FA236D222724552389223824E523B825B727532742286127B1245526DF2657242926CC2416260228892A3734FC29C1240121FB21CD21991F0F20AB2043211F2189210920A21F8E1E4A1E65208A203621AA217421AB200A21E820E621C02016202920A620232202221F2152214C22372244216D202C215E23DD2214217E2471253D23CD218F2317227721BA239925A725F6275F28DA282F268025FF25F4258525B02545256326F9267F2AB534A62A17243822D11F95213A20B6214E22A720252178213C21EA1F181F9720932090208620E3234F216321951F2421A4218120B11FC0202A213921B01F0A1FA420D1217020EF1F8321AE212B21F023E922E82587231623B3220D2391222322AA220A231725AB282229E22A4E288C260725D0264427B4283B286A273727052B4D35F12ABE23FA20232235211E21A61FD32253210B22702260239E205B209C224122B12093211C2066232822E11EBC214D220D22C220D11F682030217520071F6720531F09205A20D31FC51F0522F7211322A924E62206237B243A2454224921672354235F25CD25B926F3274726F6256427B524FD25F8265725F7269127872BB8343F2BA323D522D920C1212F21D5203622C721002020210C2164220921E62178228D201621E620D12266228620F420C91F011F072167202720E11F3920F11EA21EDA1FC421991FF71FF920A61ED8222B22B3246823CC227522DB225622D421BC227D23DD226D25B8245327F2246E252F24E62588258A27AD25F72632283E29F7338929B2249321B421621F85217C20601F11217921352282203720A3217720FE1F642140210F22C0210420DF200E2136203D205F21D11FA91FF71F0A1E8B1EC81E211EEB1E071F781E731F8E1FF41F0921A823A7247023A2229822132382220A24EC23B4216C2100239A25E82475242624D5242D25DC2536257C253026CE28B033142B58243D226B201B205220EA202F221B21F51F8421F7200621131FC91F4420581F5A2217222D222E21E3213320AE22A3212D219E1F8E20CD2069208D205420E41FEC1FFC1ED61F101F6B21B321B222EB2102243223F3228A232B228C2253230A230022AD2296231B259D239B232B247B24E523B6242C253C268C265526C9310F2A2324B620D02059203C207620CE1F4D218F1F11208220AC1FE21F9C20D9214221D32045209420462077209421D01F6F1F801F401F321FFF1FCE1F021FFF1E081FFA1C891D561CB71F961E2621F62055210A23EE20E020E2212E23E021272358224E2137211122D5215D212124F3234A23B7228F23992371246C24E6268D31672BE52440235D20881FB9209220E42047212A2148217A228820B11FAF21CE21322109216B21F81FF420E51F7B209B21E92047212A22A11FED1FB01F111ECC1E451EF41CB21E711EE81FB31F5F20F91F1A214621242120231F24302233224C23B623BE23FD2087226E239A220D22EF22EB216B221623D5227D24F7241427CD30B42BA723BA21E8207820CE209C20EC1F9320B3209F1FAD203F20DC1F1A2049203D206D202321422082204B20FC1F0121FE1F0920971E6C20731F271F5D1FF81DCE1DBA1C5F1E8A1F57202C20881FD4209E216820802019236A2185218521DF2019230823BD228221EF213F22EB2132237621BE21A523E623922366255A25AB30C6293824A222DC206A204321031F5420C620DF20B92123200C22DB20C9219820F020F0206A218620E320F91F10213A232E1F69200F205920761EF11EE31F121EB31DE01D9D1D9A1D831F6B206B20A31FF22156208C2076206221CC1F0B20D822B3212122822107214322AC22DE216A221822A0206C22B81FF9218C232A254430B72AAB257E203722F72012213F21F11F60226E1FB31F3E2182224F21DB204E22F41FF4206221FC215F205420CA208B22C920F91F5D1F00205120B21FDD1DC21DD61D571F511E5A1EE21E3D201A21B51FA820E221F820C520F5219F1F95206D20AD22BB1F1820C51F2321C41F5322BD206523CB205B211123CD22E6232525E72E8B2AC425E0210E21D52020219420C520FC211D21ED20BF20D4216B1E881FEB1F3D21F821D11F8622502024200C21FF1FF91F671E8C1F741D7220A21ECB20461F961FC11EE81E351E7420611F5F2085201620F61FF92001200A22301FBB20772017221521C6216C21D4221A20E1211222C221DE20C821892268245A2449255B30562C7925692124217F213322F91F3621EA216F213C2212213220702171211F21CC215222E1208522FE1F7B1F9320D921EE1F76200C1F3B1E251FBF1FF81E6E201E20411DA51D171F521EC6203D219C1EC421AC218120F91D0121D8207E22B62082214421B22146206C221022F7209120C1226321C8221621C722C721B423B72ED32A97241122CE203820AF2025222321A422FA21EC20C221D22170200422AE2036214421B721DC20062052218B210A210E201F212620E31E9F1F30215B20241FC71F6E1E331FBE1EDC1FCE203C209E1FBC1F26202A202C1FF31F4A209821E1208F20C2213D21EF1F9F205520D11FFA217721E6216122DB20722266237F244A3087290F23B021302131209821591FAD20A3226D219821EC200C2187207A205721832097207122BA21851F971F13206020481FB520E41EC420D51F001E67204A1F641E511F9D20CB1D13207D20911EC61F1820AD20841F89206C21BD201E200E1F30207C208C20671F7920D01F4720F2207720A1203D22A520A32298221624152ED02A1524BB204822242267213B21BA21DB22422088206F22A320992127215C21132289212321FD211E21AB20F32144214E1FEF2062206E1F631F4B209D2088200B20991F951E441FE21FE81F64209A209421DF208F20AA1F47200B217F1FE920B320F71FA91F262090202521F7203F23DC203F2176210921ED2185225725692FE72941258C23DB20AE21BA219E201A206521FD210F21222235201621C9200223C6206E21B9221921BE215A20A4210D20CA1F6821651F55207120D71EA71E451E541FD61D071FEC1EE81EFC1EDD1F2920D5202A20AD1E46219920BC1F0220832033228C202F21A321902163209B2082205B213922F11F8B214C223E22F522D72FDB2BD8251A22E220FC20CE20AA20C62016214821A61F05206C1F9D1F4221951EFE1F8E208720432111220021BB203F1F33204D214421591F6420AF1E941F4120DE1FEE1E571F171DF01ECF1E6D1EC01E75202A21242005208C1FD71FD91F9C2015210E21C0205320F5215520DF20662209204D20EB20521F7720C2233E243830372AD624F721F420C520842121207D221F1F9C207A207E20EC1F56211C217E1F221F612107202F21A11FAD1F5B2039200B1FF820B820881F6121F91FA620D31F1B208C1F921E8C1DE61ED01F201F391F351F39218C20EF1F3A1FCF1F1C201E202D21AD2061208F1FCB21181FAB20AA1FB91F1B21EC204A203F224922A024982E5F2A17239A21052322217A21752029212E21F71FC72179213C2036211A2169211922032029219C225E219A1F0A21D621BD1E5120AB1F381F861FA91E0920851E941FEB1FE520601EB11E7A1F2B201E1F2A2021225D1F821F9521A32030211D21951F2B1F322043207E2147219D1FE52057219E1F352023204320CB214423B32CC52A77256D218721B2223E2025210021862122229B20BC20EC2194209A1F35224C2222228B217C223120E71EA2205221A21F261F0620DD20C01EE01F761F291E9B207C20331F871DD3201B1F3B20A31E1521D920151FAA1F3521F31E6E22321FB5214421D620401FAB20F61F902046227920C41ED91F0C206F211922E923712F772C4125BF21862159238720BE217720622108218021FE1FBC20861F45203C20B4201621D61F25217220FD1F3C2096216521DC1F511FF01C481F8A1E63203A1F4C1F1C1F2720941F911D6E21F21FAD1E8A1F2020101EFD1F0F208F1F221F5E202A20831FA020721EA72077202720C01F77206120FA20D7202E218A217623902F1F2B492589230021B920D120BE21102197224F21EF20E320EC1FCF213622581F932104216C21522235214220ED2055209820DD207E1EC81DEE205F202820081FF31F31215E1F601EE31D831F481E1B1F50203420E11DD71EEF200320AD20761E002100215720002102201A20E01F80210220FB1F7A20631ED921B2220B25EB2DD12AF824B722E521D92092215822F621F221B52049211E226020FE1F0321C5214F210E202E203D2175213620ED1F4D201C21D51F1E1E4E1F311E50201A1FD31F9A1F771F92204E1E8C1D0A1FD01EC11EAA1FCB1EDB1EDB1E8C1F9420A71FCD21A920251F202035218921FA1EB71F1120DF1F071FC420CD1FB1213F231B24882D712AB52539219620BB1FBA1FC52139211A21A81F5B21E21E7E1F8A1F0920E51F8920D61F6C20A0205A21431EAB1FA1229721C320051F4D1E5A1FCE1EBB204521581FE11EFF1F461D2E1EFA1E5B1E021F651F1820EA20BE1EE320C61F6E1F121F1F21B21F831E661E4A209420321FFE1F861ED11F3F218220D22029230725182EAE2B6E258823EA213B22AC2114212E21F7210821BD212320CF207E1F7A20C61FFD1E7820FA1F5F20D41E801E951FE620D120941F601EE41FE11FD21FA520C1201E2176208A1F8C1E1D202420F21F6A1F2F1F2820811E3120B5202C20001F38208420D41F65208B207321F81E0A1FD21F4021BA1E2C20F91FB920A720BE24742FE12AC5238121E0213E2129203920E521841F65204820EA1FAC1F5A1EED1E5120CF1FF41E28212B21E71F321EA7202620871F4D207520C91F771FD41EA51E6C200B20E720401E7F1E581DA41F641EAF1ECF1E651F6D1EF21E9F20BA1ED21F921F8B1FD91FED1FD61FCB1F34203521E11DA01EEA1F6320F12081213E237A248F2DE22A092472205B219B213420342103211C21E0219021791FBD1F2B1F2F2042205C20E52066200120AB20511E0821311E3920A620061EEE1FB51E5D1F211F05211B203E1F851F951FAB1DF01F991FDB1E7721D120611DEA1F311FCF1FD41F7521C01F8B1DBF20EF1F6E1EA81F931E241F221F422018211A1FFE214A220E25DD2DAE2AE5249421AF20872186218A200F231E218C202F202820711ECD1E5220D021F81F8A20A1226A1FBF203920C520541FA0209421821F6D1FD51EAC1FA61E23208C1F731D691F7C1E361E1320B41F881E9D20A61FCE1F961EFA20791F3322F11FB51F661FFE1FC01EA120DB1D8E1F742004216F1F2E20AE20872177202023972E0D2CC924A9201422CB21E52118213A235422F920F720872116208D1EF520BE20DF2160215F214D1F4221E91F9D20ED1F7B1FBE21C11FFC1D521F6F212F1F301F601E341D601F451EA81D911F191F6B1EA2202721271FA31FE71E571EF41E691F6E20451FF01EF11DC11F5A1FB31FEC204C20E220901F291FA7202E23112453300F2C2E25F0225F20FB224123CD21322217236D2266230022CE209620B121E91FAC206422F920681FAE20C11F262171202220DF212421A51F72207E1EFF1E241F131E161E111D7C1E361F741E2D1FF720032128206020D91D6E1C4D1EAB1E7C20B11F24208A1F4B1ED41F361FAE1FE4207B1F94224921261FA01FE322C224862E +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 403F + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 55 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 0009 + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 FB28092311230221A821FB20D51F3A20A020BD1F671FDF1EDC1E251E0220441E711D861F2D1F3D1D1B1F9D1E7620DB1EE31FD81E3C200D20B51F8C1FA6214D20081FB91FC41E1B1DAB1D7C1F591FEB1FB01F7E211A202420C120DA1F391F1D201221CC205620D71D7D1FED1ECB1D5920741E092123202E20F71EA6212523402ED42B6A242921EF22AA22ED208721E021AF21B81FDA201620DF1EB41F791F171F7E1EE11ED71E711EA91E5620CF20F021691F3E21E31F2E219D20B32127227E20B520B5219920FF202D1F6B21C51FF41F7322E62103214321E821C8237B222C2148217C21D32155206721612155206320BD1F5221F321B6215F228F22CA24142EBB2B1F25DB23BE2292210221A1218B22A7200A2371224C22AB20FA1F65205F20602016212520101F511F2D21F720AC20D121452237219D2135220D1F822157231F23FA2397228B227D205F216C20342079219922182296232F248D213A233A23212300234822F22010221622B6213A202820852085239021752204230026BA2FFF2BEB275324D521E3218E21BB227521F320922064202220AE20DF1F61211F1F302057201220F821EB1F5C20B51F8C217A2198226C217922722115212F20782235242424F2240B229A2100229420BD1F27215022FD225D24A524B32497254125D8254D2437245E236C226F217220EB203C21682143227E23EE216A2339251B2FB82C69265722A7218A215A226D21D121EA2147211D213C215A20C01F7521E620911F492191206E209920F42063230120DE204524BA21BE1E4E225D225D22FB22FD240626C9251B23F723BB232622FF2287217723142252230E258F26282692255426E5256426222414252C233D2359225022E9214922F9200E221924A0240E2E062B13268D22DB23D720D32264213021B322AC213A22E0229020BB204521A821CF214D21A9203922E620E5204E22382343218E22EE211022302214233E23BE24A02597264D26132592278225BA252D245A2382234D238923BD23FC250C27D727FA279427E8253126B2255825D523A5235022F8225D243C233822BC24E2257F2EEF2AF723CA2273228C21C921F522082285218E226C224621AA216D21EA22CE225023D8217E22BC213023D022B1215B22AD20302193222A23B5230A24D3250326B025E62620274D27CC27A827472782250D26D4264424A62318240D250F25B126F1270B262D270C2665259E24F6234823F8233B225223B121E7228723F823BB2E632B9425E522E9239D22C7208D217C224C222A2365210822462333242422582299238623F1231B243F23BA2169225024E621D821C422C0224F24912556256F258C26F326AC27DA26F827EA29352BA727B6294E29002785244F24612224240C2512269926C926F525BD2561244A23A6234E23F422852283215B234A237025852FEA2A34267F245D23DF2374223F2185235A24DE236423AF22F122E022D2231D221423BB2432250F25C2240923FC206E23B121112391217621782232258C258626EE279028D6278C2766276E276C276728492AFA29032BC227ED278F241B23AC2323251E2545277B266526F6231622F52212249722D9225422922221241F24DC2F4B2D9C240D2342238D23EA22CA236823EE246E2333258C24BD22E62346244B242325A22402268C2546251D259D2234236B21DC2371239C214E241C237624E026502889299B28832877270F291729F12709299C2A4B2A69283F250524E7218122D926EF24F3239025C3244524FE23102374237C2491238123D822EF24A025E431222C8E289B256B243525AD23A423A323FC236A244826E324A924A2226324F3246D252025DE25982643247425B6236E24582107210721EF20D921FF212A23B52430284C289328BD2977285D275828B625D02602263026BA2506251A23E721F02232230E23EF22CE238A247C229821FD230A248623D423332245245925B4256532942E7A282426D127BA247A23C623232315259E253D25CB24C1232D247624D3255C25B32554265F29C226D627362487235823B822DF22DD21CC20C420E022142458250B28AA27DE280429FD27BB2731266126B6241C259D24BA238B226F213F2288223F234C2442220625F324A724E5268B266426D3258225D2242C257929B633C030832B3E281B2605253E24052487226124AC238623C025652487264A26CE27FC267026D225AB284527822684249B26AA24302417237F219E217822D821FA23FE23F4260829D12821282529802A3A276E247A25662350235C245A2444220C2451228922EB20AB216A231A253126A02885281628A629632754265A27C42ACE32CE2FC82A90281027F126D723EC2332247D24CE23D9224A23A0234825FD27DC262728D52706276A27AA2644258C24C424E124382421222121FD22A1206320F91F4F2316256A26FD26CA28A428B82847258126E8251B2352228122C0236F24D92412255B217B213021ED230B2315241D279028AF29EB283D2638261F2763295434142FDF29D3285E265D272C253326B223BA25C0224524222532236925382778296328A829622A7C274725F023B6232424F6233922F720E6218E214820CF2040214324CD242726232726271C2645250125FE235E24F32376222C2395241524E224EB24D224C92341220724892365244326D627BF267027F32373246B2644276430EB2F602A92273E25E825DB2609261D2646246525C7254024AC22BD25FA27ED29C128B628222774252126F22557226A220D2101225B2288210A216B2142210A22B924B225FF266226B8249E26DB2365239024FB20672113220C24DC242326FE26D52737249224CB22D823D624D422522569255226E0250D26FC2484251D27CF2FE92D1E2A32296B285A268526CE2612271F266126F627F525F824EA24AE2508274C27CF25A4253F240B25E02238239123C0225923DB22F3203B22F522FA22352580279D2AD02B0B2971252522AE215F2262223B22FB21D8232C27B626E5266A2810286D250E2595229922702292229421E024052582256226482648267C28AE31092F6929E7289A25E82617280E2886283F29B7290D2936272025B1242823BD23DD238D244925E82256226023AE22EA229D21ED233A2446236D23052241243926542A3D2B31299326F225A1239E214D20B421EC2358230723B0274426E025E425FF2743248524FF21A723C5217E24E723352572244E24EB2434267D26ED2773332F2D802B7328AF28FD283B29D52ADE2A372B532AE8294726CF24F8236D24C1224A23782505239325162486222A24A2250323C52499249024D423D622EA24B326D629DA2A3429A224F524592320235920B1223522A223E22483253C256E25B42571259226452784238524CB22C0215221FF22D1224A231C24C427C227EF29F133532FA6280A28B229462AED2A3B2A172B1229A9276626D125AE240D225324CA239B23AC2067231324B822F32201233C2494252C2515233D2240261523A624122421277628DF25AA242923B62230216D22DD22B223AC232E25FF2700266A25A925A426D225B0259C258125B221E120BD23D32201232024DA24A327F429572A3933A72E5629C0299F2A2F2B622B3B29EF2760274526AE26252461220B22F721F222F421AB240F2379221523B82546256A253B26B924622569247D24D9242C24E5254426FF265925C52489229022412505237F255726C1267527ED250A276C24F323BE24D8230025ED24D625182419239226EB23A1249927DD25FE293B2B2D2ABB33172FB228BB28E4272E28C4289527412662261D25A3222B246B234A24DE222A23C523192295243923DA24E7253227E326C825F425D423AF241E2641245C25FC2321250D26EA24EF242E257A259925C126A1286428C226F2261D261725CD221C23E5239A257425D6269E274F2517254324BF2418288E275F2AFF2BE12A482BA433592FEE287826D5264A272927342673241B2524248D242824DF2336228A248F238323772233238321F92271257A27C427F926AA26E2243A25852530231624DC239D22C8235724C624C024C3282728E82612295A281827C32499245D23CD22F523FB244B26A1299529002BD52AA02729265C274028F329B82AFF29EF29C329CA33CE30E4297E28F625AA256A234924A524372385223D232324EC21D2233E26F123F7234424BB22AC231824A626242A732CAB2A5D280525D825372524235F23A0229B22C6231B2244212925F2264228482810281526A526D124662461233423E224B6235D261129732AF92B39289326C5267C28E3287C2A302A882AEE28152AE4332D32022B872765252125E923EE22292315256E23B5251A25C6235124702484241624AF233F24B42677240925E3286C2A792A5429512721256A23D722F5216B22DD218E229C22D922A0234E24642799263C26D2252626C92592268F2430248724D426DA26E228972ADA290328E424F2262B27CF27EE29ED2A352C042C672AED33FF30252B1328D7261F24B4239E238B23E2232623AF24A8256B25CD26B227E2266B272424A42517261B262E274F25182864289A283C274524C2246223BB23AB224E23B0222D23AC2260216D23B2241D24EB269C26BC2589254E283C261C25732606252727DA2807288929A6271A26B42663267A252427C0280E2BD62AB62CFB34A431122CDB2876254C2558248523D123D023DA23922646288E276B29CE291D29952895271426DA28D927D425AD24A22687259825F7251F2350230823F7235B227C23F4223E24DE2177231022632393242824B1269A27872816297C29A226E9261D25EB2554284E29752A7C281E274226D22466243426AB273A280E2AD12ACA331832E02DA228192869259724162496245B247424B927BD28C62DDE2B572BEE29F62726286328FA295C2949277D25B0242C24342376213122D9218622C022712349249F24C923FB213223E0211B232622D023EF24032724293A299528E42493245127762687273C2AC02B942A6929BF2634251B23A3245C2514277B27302A23342E333E2DF62B1E2AD426AF238724B2241E26FE25B425FB287329112C022A282AF129DE28F3295929F628AB275325D22358223423D520B4202D22EF22EC21F423E22451250525B822F8220023652141221E24462465264727DF287C255C25922643272228F427152ADD2A2D2B082A7729CE25D2245024B823AD2474279327A1310F331E2D9628BE265B258C26A624EB2461272C267228A82609275126F226C2276528B927CF2740294B26B9258C23FA24882166213A219420802193214A22462336231523E52367239522502338231F24922536259B262B24C5259C24FE2417262D280129802937299D29402A6128E9257023BE22A423A62229232F25C425D2304D31282B472976270227F924EE2411279827432A4227DB268F24CE2416247823282583253A260F2506232B2240244F239E213321F321B31F2B2104227E220C22CA218722D2225C22CD229A22DE24E72599262828A8233423CF236C241D23CC243D250E287C271E25072871281E28CC25B8223621BF21BF2133248124E924702F21305D2CAC28B028A3272924BA24A6253A2A562CFD2B8A28A8246F24552622262824632427242124E920A0215A2160209E201F2248219A20B6213E202F22C8217B2480230124AD2145230E2617272A269127F327E12322235F23CD22C8231023CB244E25FF232125FB241E24F4230B23E321092383214F22F721FE247525832FBA2E9528B7269F2501269C252E275727D128C22A612AFD2738266624D223CA239B2378214E23E7214D21AB21DF2001204520DC21D321F22148212E213A20E421D6229F24AC242E24FF23DE275D276927CC2721278C24B8225F220522962244210C2332231323D322E3240C24BE22EF2195225A22B32377228B23AB24332530301E2EDE28CA256426D525A327E92580272529F6286B27FF2571242224BF232E239821E722B2210B22E0226B214923A42140213822AB22D61F7F2153221A21C421A924B425C725712590270A2628269825662746262922362225222F21292107211321FD21F42289238A2338217C20EB207F2323244E237224E725BE258A255B31462DF9262C24F725D827C226272894262F2818269F253A247E24942238235922262279227A22BB225F253F240B24FE243323672207240D2103211022F1212B23EE233627B626DD263628F72754262B246125CD2495227822EA2097218D22E51FA521FF20C3200021F7214D2350228423A2237E25B82782271B2745271A26A430FE2B1527A526BE257827A127AA27C725EE246024C124EB24A023DB226D22AC2234248C23A0223825C1241D26E727B926112497245C257E22BA226D23AB22A723B8233825B0280E275C27252A7529D424A823942520249D21B222F7222F227C2250217221FA225C24F324B6231C223E248026F428862A7D2AB5296F286826E131192E5028BB274B27AA26A724FD232D242023A7237924B0223222CA203522B9221424142561246D262026512773277528562689259327D724C323BD24502366244824DE25D22576257326AC289C266C24E6241824DB222C21EF21CA1FD420C1204823FA1FB82296246924A224F72507288A29312BD42CCC2BE32AF5276F266030882FC02A19296927D22682256B2514258924B9235B2420220021EA24ED23F2237126F2257F2644274828CE25FF2793290E29CA277A27EF257625FA2422252027DE262027CF268025C82649260E26C423F224AF22CD23D4215421F4218E21EA20F421AD22B822872338272D27A229242CFF2CA02BF92DB42B2A2BCC299329D932BB2F242949278226E2249723EF22882231258E223323D523D9236324C325DF25662968290229C3284626322603282927362A70297C263426C2232024BD250326C8271E29F82859248723192415233F237A231721BA21CE2054213E2018215D22B722B6227623C524C026AE28D529EA2BBA2B8C2B9C2C8229F62B5A2B612B8F31682ED827A7250424652238224622622265229A227324BC24E323B5236B25E0266328B22A842AE828552749245325C9278A272327B125C3247A23BB227E242C24822679275D27DB24BD23F4220F22932132221222D220DD1F0C2229205C200521FC219023C5253F271D2939280228FB28D02A112B7D2CBB2B0D2DA72C922CA433072D98282E2599258A2412245C23452392246C23432277233E2588273928CA28F72B662CB9286B2936258224F224BF247825C2257A236923CA24CF211E23D12467262927922701265024C023F821052247239522DB21CA20D6206420ED217A237B226E246A26F028992B842AB32849276A2724284C29962A162CDA2BC62D59344F2D262685223C2401229822B12276230923732269234F2486249225CE27BC28202A892BC12AAF274F25C1231424902495248522F321EB20F82184226B22D5246725C927292A8626CF2480251C25FF2232227822E22200233B236721FD204A21F9233324D12484279D2AD1297B28012633255326A9294B2A792C192D102CD831302E5627AE2517245F269821E423EF23E6225722002305243624BB25E228E329952B7E2BC02AF9263324A4232D231D247C232122D321F322BE221023E4244D267F2705280B2A4A29A028DD2604267B243E24A622BE2340212422FA213F22DB212B237824F9243F271B2AD82AF728BD26CE244F267C28B52B542CE829B52BDE32E22D24280926BA25A22558251123C9238B23C623D923C423882379266C2666282F29222CFD29812661220F23A92328249421DC2487235821CF24AE2454266C2A542B322B552A0E284A272826E3268C24452463238D22B021D8224922C322BD244C239423242592265E28182815275226892592258C27F2272E281A29D52AFC331E2FBC29D02709264E26F925BE225822442516243B24D2236422F722022766284E29712B1A2848258225AB24A4243124EC22EB215322CA23892439261229F629B329652A362A2327E526C626CB249525012483259423F223C22126236323F4242C2645266826A726D027EF248B25DA255024D5248D2639270D2991291C2BE633C32FD929662789262A26682402247C24042598265025B1243324E3247326D3279429EB2AFF288327F72320241B253E243A253024D323A8248225FF23172819287728AE28E52855270C263E24CB25BF248924EF256A245A24C8242F225325AD276227BA26EE26C624F326B1257E24E925E924CF241D279D26AB28E62B9A2CDE341230372B022866252226452507256C248F25892581261D2401234824F925BC276C29532B0A266C256B24ED22E62346250625C3253226EB275628DF269C27AF279F26532529274A276926CA252827EF246D252025DF26D9252A23C8249F25AC264B264A280D26DA257326DC2440241C27722592248F2603261928212A392C5635BF2F4F2997272A2630246C253A232E243A26A9273626C6256724CD24A22458268E28F026E8259825F0232E2393254425D6245D261228E828D828F92876266E240E24522450273327AB27B5278E267326D026CA274B267F250D25F62399242B26552749299B2956275D26B125CB26DD262F268826AE266E260328C7296E2B8A34E82E4929752685254B25B3253C258C236D250B2788286D26A525D42404258C237C264B238C240C24F722E724FC245B274E27A126D925472739290426DD24C8230224042550296029512A2829ED28932763275B289E258125B8266F262525032654265426712811276B27DA26F72502284D2881264025B025CC28702A622C0A33E62C46288B253D262027EF2519236524792600261526722798260226A82412255322A9226A23522591255424CF2486249526942793274F28EB27B52615266425D425FD265D29772B5A2B302B742AB129CA279A272D261C241A270129DA267E24DD22FE26AF2642281C274C267E25892601270F27E2256D25CB28EB28022ACF33902DC327E426A6251827632744281F26D5259D249B2642286B298A27F2248A2285232126422443260524FF25B225B4260A245F248C26B72635285A2A4A2AF0276325C1265F283E2AC42BFB298C29C828BC28BF255925482629283A2ADE277B263525B32605263726FB27F6262226812652253C24EF249D25F82957283F2A9E32C32D3629AE27EA260E278628BF2708269F26CC251E279C27D028C427DF24D122DD21CE225F2319274E25A5251A27732746244326A525F7265E2AF829792B402A9629C3292828F927A7286729CA28C42690267C26EE24D4235D25732608260225B82412254F2592242E292928DB2527255D240E246A236423F0258F2865293834982D0D2896280627B028FD28D427B924F825C32610272B29022BE027A0254B237C226124C22228262727AF26D9270927FC262B243426B826C429302A2F2C9C2A2A2AF92A7C293828A0289A27E8276B265525D524E522C22315261E258D244425C724FA24FD23D0249C24DC240D260325D5225122A1247223882577266E29E236DD30C228B227BA26E527A3270D269826BB240524FC26C6289128F026EF248925DC228B24312508265224E7260829DC291F290D283426A0278428D529E92B212B732D7E2C0C2A042823266726AD2449244025D2244D23D92447244625C5234025EC2682240023352453266B248025AB2615243A22FA23C7229625BB27302A03359830B32B6E282F28462872276925DE24F722492437278C2A75286128B724CD23652357233925A0255C27F4257929362BCC29F729292C83289829362B8A2AA12A9D2CBB2C222BC82AA1274824CF23B4223724A6251C249C24F924AC269325822660281726CA244C2545263725C123AE24CC23F323F1254224B52551289E291B33A132822CF02A012890243E26BC2383247B256324D0269029D02AD226C824892201258525AD2667292928CF27EE26A0294B2A742B292A792B3E2A942A062B172A032CA72EFE2B4E2CD0281F2512221222AB23A1242E238324DD25C9270828C527AC27C12617272424342762264B24CF251F25DF248B27EB24BB249828CB2BE4344E312D2CB6295D288024F1231C231A25CF22452277266D25ED27D92518266A24212603274A2821292F2AAA25FC267626B5293C2BB32A8929F828DA2738296E2AA62B8C2E8D2D882CB429A325542530235E234625DD24842575289229262AFD283C271F267E262526A5262F2714280F278F241E23852690242526E326D62A36351332DA29EE286626AC2594225C24112379236E24AB244524842573259F262D2558263C27AF276729E1275226D1242D26F0260228042A352A6D289326C727D328642BB62D522DB62BBA28B624F62401232E25AE25E0268F26742A4D2AE3295629B32753289629942750297B29322AF428D0265A27D5277326F927A226122A39352530BF292B285925CF23C5239223F623F523F8239A2459245426DA246B27D9261626F02503274B2884272B2738280D27F825292682293629162AF228FB291B2A502A9C2CD72DF82AEA293028C2242A2397243525CD2667297B2AAB2A9C298B277B27C529D02B1B2AEF2B3D2BBE2B802BCB27CE2881266624CE25702520291336642F4F285B27D22508256F243E236D232B24352324244223A92487261D29B027DB26F42653269F2706278F281A29C729E12A6628212611260429302BD22A582C692BA62D9E2C672C952A7129942752257B25BB24C1290F2BA42B7A2AEB279327D0277D281E2BFF29F32BC4296927B529EF28D326FC243324432474263229CC33922E0328F42568250E258A243622972378233F2371243E243B255325F127F22ABA299228B5275027BA27552A1B2B272CDF299B270627EB278C289B2C5E2B332B352CE22DF82C502C162B06291D2748258D254F27EE274E2B962AB129E329ED2824297A29F729132A592BED280B27212613269925F324CF22A625BA273C292033A42C2F285D243824CD22FF210C231F238E241B23232406236125B227F02ADA29CC293228D2274728EB26D5297A2B0B2BDC295429B32683262928F22A7B2C542C6E2D812D442E612D442A0329BF298E273B27BF267A28DC2A3C2D6B2ABB2967292C2BF12BAC2C492AC52A7A285E26DA25CA242825A2260B271928222718298C31C12CE927E2269522B422D222AE226721D622A9233C244B240425D528FA29502C5B292228B326CE28EF2585289B28982AA92AB42B3C28E62548275529F62A702CA02B282C6A2E042AF929D429032BE628C129A728E6260228272DEB2A9C2A2C2BBF2BBB2B542BF92B332B8F29BF278525D7247923D029EA273628BE2ADA2A8132732EF825BE24D02261230625702215226923F622A424EC247125D927FA2A4229142AC829DB2831273129A8280D275D2A4C2CEB2AB32B84286C26D4254A285F29732B202CB62A252AA6289D28902A4129E129DE29FA271727F5294A2CB02A9D2B722A6C2B2B2AEA2A722B852700259227242589252429BE29152A392D912A1835A42DC327C922B922B123AD223C236822762306240023DF2383246B27322A5E2CCB2970290A29882A4A2A822B802ACF29D22A702DDC2B932A47290327E925C7272C2A0B2BBD296A287B284F296028CE285F2A09290428D2253828E8274929892A732BC0281029BE2B132B4E27572412261B278D2AE62AA12B942BFF2D2E2EA036412C9027D323B9243022292335243123D122D9204D25A9224426B926642AF82A462B0129CF29B22AB82AB12AD528602AEA2A572B8A2CDE29BE294D27F323BE25A228472A66297428052A44289925FE24E4267827EE25C024C624CD2560272427B9299C29E72969291028ED25E3239A264927B02A8B2C522D6E2D202D942E9036292A842654239F228824C5220B217723F82002229722C921232545267C2A842B7C2A132A31298E2A6B2ADA294A2A29290C27E327F4270A293C2878259824E5253828142A4B2C092AD426222604252123A3231427FD225B23C525172632268B26BB25BA26FD272A267028F525D3250E27EF28142BF02CEB2CD32C1E2F312F68365E2B1926AF242F245623D7227F212E23DE21CE21FA224B24E722DF2536285E29782ACE29B228F02AFB2AD228D929042665250427E327D8255626E9262525B3248328FB29C729D8278325F3245825E225452647269D25BA24A3234523A7242123DC24E427DC2660268D2614253328EE26BE27572A272DCC2BD72BEA2D6D2EED33122CEF25232491221323EB239B227822F42232225222862192230C255F26F82742276028D628BF2B012C0B2A6E28712898254D2593255026312860272A268A248C280D291127BF266B25F623C925C0244C27F128F6269527F2252A23BC243125C2264028D62655273E26E027822688264B27E8287A29AB2A772DFF2C102C6F33022BB0256B23B421DF2287237E22D3200723EF21ED22CB2309233324A826CC235A259E285A283629252A6D2830282F28F22729287528E9269E288C2855276C2828292929B728672460251F27E627FC26C7292C2A272A5928AE297D2804266F288529282A702A6E281128A4266A278C288F268B28D6288E2A1B2C582CE32B92329B2A522504242923B9214D22C6218722AD2316236F22B3242F227D235F2662253827C7261528CA28942860280C283729E929BE2A4D2BF5286629E4278727A9277929712862283B261A271228F129352BF92CF12BD22B962AEB2C862AF929CE2A6B2B3A2B972ADB288629C428002AB728E42675275A27462A4E2B7F2B3D2BFA33D02DC4256E235223DA22AA22C720FA21DC235323FC215A248B226E23B7240E27AC2663275C28152740288E27FE28452A8A2AFD2AC52A7D2A9428D12601255427F729DF28922A24283828D229972B172C3C2C0C2DCB29242BA92A5F2D112A7829AA2BB02ADE2B922A7929F629452A462A362981273228742A232DBC2A9B2B3135602C79256C221D2215236B22C222BD227B222122DE238C23AE2309225F24B62647251E252F278F27612641265C2680286928102AFB290D299027C2241126F0292D2A582A042AAF28D129142AB82A632B002DF62A632B372AA22BCB2AE82A34297429282A8B2AD32AE92A6A2B5F2C622D1D29C827282A192C472DB22B812C6D36CD2B2726542115231C22A921BF239C227D224C22D72274235E22E9232624FA245625D52527251726F52494299A28D928D626C52868279A2610262325D423CD2660289C278E281A262926DE28D6290D2A012A512ABA2A5529F929C32AB52AE929EF29EF28ED29AA2AA82C992BAB2C272BE5293D29DD2A242B032D292C592D8E37732C5226EB23E4238222B52117224A211D23A82263240B23CB2311257623CB25A7259C24B424722671261227262ACB2A9627C525A524252550256C22BA24AA267B29502A402AE5263A254427C727E327BA279027D026EB25D429AD2BDB296F2999276629EB29382A0B2E3D2CC52DA52A452730293D2C572C432D6A2DF02D60364D2B1F2548236D22F723F021B21FE2209922B922FB235D2251239923E02470247F249C24FD24D026E5251C288B28B728C2246426A926CA25F926D524A12564277F29FB2A21299926CE23B324582799269725C42737261327CB29F02BF42B7F2813272A27942AF22A262DE92CC42B152B92276D28082C1A2CFC2C1D2E002FA336A12ACC25B2219A217E22D721322161228D226822982268225522B02250222324DC22842447244424B826A729002BB128CF27EC2768292A29E3271B2782246227552B4C2A5E2A2426A3237A2390255526C32777289A27262A772BB72A122C792A7A29F828922A732D632DAD2C5F2BAB2C06290D2864297D2DEE2C272DB42E1836672BD4246522D3215B21A5216721E220E12266223D2330226E229C233A24E4240E2543241925C82463268D286429DE298229D12BA72C1F2A54290627B6276127012A3D2B2029A125B2231225E5259B2528299929092A0B2B822B2B2C0E2B392AD2275C296F2D342B682D862D822BB82C842868285E29F929E82BFC2D9A2D8F36112A2E25A822592281219221F82168211A211523C32175224523F3239D23A123CE2331232E24052593255628DF288D29EF2A4F2DF12BC428BD288F2721270725DF26052A942BB0282A268827B2255E29A92BBB2BE7283D29662CA02C212C9A2AD029D02B702ABF2CD92B272D6D2CA72CA229E429D92A5E2C2B2EC42DB52D1236312BAE24E3238C214322D1224521182344236E22A8227022B6237B2368225B2562219D23D224F624B0241C2424275428992769292529F1283629D127EC275B26ED26F728502A862AF2285326BC261829BE2AC32B9929192BB92CA12E1D2E0E2B8429F32AB22B772C4A2D3B2C962D922DA32A1B29F629DE2CC92DFB2D9B2E6137562B7325432267223B228121E42102237D2276230023D0228C21FB22E621E8252C24E723FC23E82342235D23D9243C290F27FD289828DD27272823280125AF25F2262A29072BAF2B532A1429C92848280929E4294E29382B742DE82C3C2D112B5529672B802B762CC52C0A2BEE2DF82B7029242A612B2E2B062E472DCF2FC336202CE1258B213421A2218621CA2002223723D62229237E231323B0211323FB22482239238023D523D323A323E725F7250E28A427A4263D270D280E25EA22DB240926C226BF27EC279F28E927DB27E3258627482749287628D12B0B2DC22ACE2A902AEE2A682BAC2B6D2A752A5F2B642CF62A5129BD2B0D2D922E792ED02FE4378A2BF9256F2388215821312283201221A32107227D22C22266220B2291237E229B23EC2327245A235924CF227923392508245425B0256424E526902478242523C32538275429C128EE288B285129C32610260127B8272827722EC72BBC2A5A295629712C7F2B2B294729D6289E2AE62B532A562AD32A9F2CBA2CE32DE02E3336EE2AC7256921D621282265223D227320C5213F2248222D22B621D5214022AF22CB218F23D322472475249C225423A0235724B924862502249A25D524DD257B262228A529912A8429F3296A29642AA426EA24D026D926782A672C772C2A2A1F29082A892C0A2B6B2910299329DC2B972CDA2AA42AF62AC52B972DF52D392E7C36652CB02355225C2280210F21A6202722CB20E4229922E92162216B22C22249227E22F0212223AB235C23EF215F236423FB2453235824EA228F25CF233E245B262429BB2B2329242939291E2A4329CF274C28ED25FD27A928262C622B7128B028212A962C812A4E2AB32A022A7B2C542CCB2B4A2A802AC82ADE2D692C1B2EA737E52AFA24D2219D209020C7205E2029219D212D2167220D1F072298213221FB2255225122D92163237221F620E322BA227B2323235E23F6242F2479232F25EF26A827AF28C827A8255127F326AA273D265D263F26492776279C2C0A2A3C2B532B9C2BAF2C112BC729732BD62B242CB32B7329D028F22C342B312BD32ACA2CE9341B2C5E25D422F620AA214822762044230C2201222322B3209D202021E220B7231E238F211F2294219921E221C4216F221322DC23AC227B24702412246F246425B926AF26B926F123AA224D26C227C2245325E8270C283829B92A8D28F9263528F12B4F2C8B2B012CD12BC82BEE2B452CE328DB265E29172B302B5B2C102DF234272A9B255621FF210322BE22AF21F9217C21F5211D23812148228E208122DC217222DC214D234E22802288213023F3224622CB232621AC2293223A22CF234124A023F32494246F24B62476259C245B24CA254B266A259D28FF2913290128D827782B9F2AE22B8A2A232ABB2B522A30291E2902282429162AC42C532C142CA136072CE524CC219F2129232422332267218A220C22AA22372343222D22DD216722B5202823C621FF2267229920C523FA200021F221E621782281226223BD21C2217F239F241B24BA23C223B0259126FE228C256C25B226A6291D2B37272927C1261F2A482AB92A0B29062B6E2BBF2940282527FB26EC285D2AD92B452CEF2D7D36D32B71253E2104215921C721CB21CD217822EA222022532142218F210421BC218321A02072219621432164200B21752086207A22BF1FF52028226A2212217D21C522942222239222322387231A242F24972496254B28CA26C0279D276B25DF2741297F2A4F2BC62750292E2B1329C3273F25A526FF277527E529CA2B3E2C8236252C292661219A203E21E32051207121C522172171225F22AB201D2056223821991FFE21CD21BD202B212E21BC21B6219C1FBA218F21E020B823B0215D21532216232F234F23A1209A22B1222F239D2245250A26D426C326FD2717252F2752289C28392A702BFF2B802C8027BC26E0260A27DC26502793285A29D328E82B7936682D9725F7229C217122E022BE227922802072214C20AF21B01F8A20C22107213E2128220D203D210520731F3A1F6C209420C120A6205F22D2213423CB221F21922353239021372046238B23BA23D6248724A2277525C9241F25EB241029C629462A9F29DC298E2BE92C3C2907282428BE29D02A9C2A072AD8291329D329F933192C81250125F321AA21C520D521B2224721B220F2216E210C20981FB11F2D21F5216622F220CC21F220822069216E224E20D3206720FE20FD21B2200422C6225E229F219422B122502313231623D32562230C2781249B22412472240B26FE261F275D280B2C3C2B932C042A07298B28012A832BD52AA42AC02CE92CCE2DB1355D2C1325E12162222521FB20AC21CD21A821EF20CF1F25215820212007203721E6200C212922102178215D208321DE20C920C71F5D20FC1FF4209C203721A722252102223422201F102212218B224C2218228923C12250236C238C2457271027122722289729C62A992912284D274029362A112CC62B132AAE2BEE2C4B2DD833032C38244D211921DA21FA202B233921DF215C22A7213021432001202C22DE21F92045221F218322B1202D216623342288202D202220E2214B2117207D21FF2177223021A8216920062245218C2218229D22BC23A622C222A42460232B275128B328D826B227CD2604293F2737284D2CA72DF12CCA2C392C122B492DDF2E3B34072BB925C02276227B22A1204623A620F0225422281F4020E821FB20C020BC210D210222A021D3214F21FA20C721B922E0208C20C1200F213C2011217920FD208A210C2214220E20F52292224921382096235E24BE228C22C8222A2305273728CB28CE2656252627B4260C26EC277C2A8C2CC02D962DAC2BDF2B8B2B232CB734942AC925C222B0205B21D61F3F20C22018207E20C0207120DD209D207F21E0219020A71F8B22512001220E2152221421411F67210E1EA71F2021F02122205321EE2161232A22921EC020AC1FAE22961FC120D222FF21DB20DA22AE24752540279426C7260227D223DA26AB269B285129F22BE7284F2A1A2C8F2BBA2C412B78341B2B37265E2203223E21FD1F0420E9214122F920EB2165224720AA20F2219221971FE9205021F2216820F41F0920F41F25219122CF2063200B227C229F212E226121CE217E21121F581F0821DC21D4204C2131220E217F225722D0215D2555276A2A16278C256F24A1240B25942708285F2504289028FE275F29B42AD02BCC328C2AE025F72164225E22D621E71F99227A22592173209F216421E1202B213922882015216B2186235B218320AC2298213722A92152217E216E20D82180212A220821DF2197215921C22004210C2277228A2131213C21CF2101224922AF2306269F29CA2789244D25AD240C25A6254026B826E62641264D261127A82773281E30582E21252321962195226D205121A020EC20C7238F21D720E51FF81E6721D1205020D3225220F6218C21DA20F120C3213421A4217021AC1E6620D6207920F8218E212B20571F4120DD1F0F2138218F2027217722C8210721EC2216229D23BF257D27EC257B24A1227F25622536267E272A26B526B125192456262E26AF26F130382CEF264621BC21A81F4D2183200E21E821CD21AA227B20F31FC3210121EF21B620E3214621EF20931FE3200C22C1201E2171225D20571F8B21581FA521511FF120DF1F681FB7208B1E4A1F6320F11F6620C4207A20F3200B22EB216022FB24C426D425E3238D2357259C26FF274E295D2777274D252A2519269A263328CE308A2B69242E22D71FD62059218620682199202921D8202721CF20CF201A22F822AE2029226C21B121A021D420CF20DF208C23F4205C20BE1F901F2720A020CD1F79207520B020EF1EEF1DDB209A1FC51E6A20A72204215922D62186227C241525BB23D8236024F02423241B260A27652ABC2A432890265D26A6240828482ADD31EE2A3725E02139234223E4223A21682349226E22632018214421842013221422132180205121F421CA1F8020D51F5A21EC1F4D21E81F6C1F4C20CF2007204122AE21841FA820781FDD1EA12058218F1F362285231121CC1F8E21A821AC22CC224124C223BC220B23C02347251D278D29392C402A282939271328A728BC29A733DC2BC4242C22A4238321D0237D22A221A9224123A42204217B212A1FC320AC20E520E5203D210F216321DE1F0622BA2028206121B71FFE2012210C210B20B61F7A210322EC1FA51FCB1F1D21691F171F3122F620F92133202422D4236C2243225724E023A6246E22DC2387243326A729582ADF291228B5250629352BB52B8734162C9F26B52315216E213B2014233022E120B9217B21EF20CF1EF7206821DF210921EE208421072208216721FC2090218B219321EF20172297211F20AA207F20DE2188221921C41FFC1F4E21C8202E206E2289221821CD20092275228822A4204D235B238B215321F4227824122372261B265D278826D425A028D829852C65359D2B1D26D623CA220422182202227C201D216822292199216721A5204320B020D11FD921CA21C0202020D21F95241322441F241F7B20E41F3F213E21762044227221F82007212B1F1D1F961F6420C61F72225E210A210522C221C220452290211E2270227622D71FDD21A3212B23A925BB24D9258B25A625A026E128F42AAE32132DBC26CD236C234A2256217722052174230D237B211522C620E421A121B2207F20082016220B2126222E21FE204620E62036210120082211219320AA2005223C21EA214221A51EAA206220FC1F21215B22CF22A920B1207620DC205F20E322A822672233220E22AA212721E7218D248C248F235C2311242725822635281832902B83252F226F23CD21192188219822A5213E208D20AA205220D321DE21B41FEC1FB51F7123C0214A20152044202D21FA1FFF1F5E212821AB1E9C1EFE1ED61FC5206521521F9D1ECC1E8F210E200D210C22BD217A20E820FB2050217B23B12226225A21C1228521302360219E223C2262224F23AC235723DE22F02500276E319B2AB1253E22F01FB2212E22F721102245226E222021622168203420AB211F2251208521BA22F3211B2182231D21AD2204220D21F520CF20E81F851F441FF81FD5217E202A208A1F171EDA1E7420EB20612257226C21E2207B215B2043227022DF217C224121C3205A2264242C2251222722A024CE22E72140243B24EC271431EF2AA324862191213F210921F621012163215C214A202B20A8207C207821A2211520C01F41221F223B224022ED202D202820932179210120B4200F1F551FF61E15208D1F851F9C1E911E2820D220591F5F207222FB21B120162220203C20BE202F22FD205A21511FE9212322AC21F3218E21C723D323292361230B25E926EC2F792C9D250A233F211623B7210A21A322B120FB211A21EE207020611F561F47209920AA21B0222F233B2120215C235A21C61F0B207F20C91F72206C1F711F1D217F20DF1F3D1FEC1D391F781F4E1F121F0D20EC212620682013203B201121F71F2421DC20202186209B21A121482317231B22D2225223F7218A2378256D253F30132C62242123202110218C2134215321102230228020961F111F19207720D11FC4203F2218215222B820EA209D21E11F87200920751F491FAB1FB920A01F22207F200F1F001F021FC61ECF1E811E7D1F36200F22B72011207321AD20C21FB4201620A121FD21D0209321782189211C22F42214228A224A2346234E24DD258830EA2BC8259D23B6211222BB20B120A0209A220C2160213821E51F3C1F2A207E21022198216020EC21C120B220A3225821C4207E214C1FBF1EC3206B1F1D1F5D1E6120DE1F971DCB1D1B1E971FEF1D6A1F002295205C21FF1F6122972129204A21F41FA620A521BD20EF2117226620B821EE220821822339228823F8248D26882FDF2933252A214022C921951F1C20E0207E211621A021F71F981F541E391E4C200120B520502130214A20AA208720C6215620411FAB1F0F201F214021EC1FC01FD7206020D21EFD1D201EAF1FE61EA91D43219C22E220D31FB5216020F41E08211B229820382175205E2122209A206A21E721B521F5218121B222FC223125182F4F2AFC234C22B41F9E2121204C210222832029210B217020741F9D1E7720DE1F3A200C20B623FB20DF20E31E3920DA20E81FDD1EC71F3B201520EE1E361E0E1F1220D01E001E5B1FD71E3F1E8E20561FD0223621B3204920A3208C2010201D20B51F9D207422D72054229B201A210B200722432364242523EA221E226F25522FCD2A53232B209721C6209620201FC722C52080219721A6226E20C61F2B22EA212020DD205B1FCE223E213B1E00218A2155211820241F431F1F204C1F251E561F1E1E1A1E961EB31D361D0D1F931EC61E6021DE1F4520BF21AD21CE1FB11E37218A209521F22097204421FF1F8F2069220120A621A3228E209221D9220426292F292B7623E222AB20CD21FB209420C3216121EE1FD420FA201822C520BD212122D31FFE20B9208522DD2138209620311F881E8120BB1F4D1F281F2F1F2F1E881D041FC7203E1E6D1EF91EA41C56206E1F1922D4201720A11F2420DA1F871F742007212C203522A620662222200B212E209A215021002311216B229F23CB24512FC629CE248C2193212F1FB121A020B61F2D210F21E9217C20322059218220D51F2F212B219C21792112209F2075202020EA1FC220671F241F5D1F961DEC1D171E8B1D6C1E491E691D3E1E6E1E5F1E771FBC21B822572133200520F6208D20E1217822E61F1C1F102028228421812103217321992105221E216221EA224C252C30012B55242F222620EC1F33209420F621FB20A41F5121B220D220C21E7C1FDA1F041F0622E921B42190207E21DA1F35222A218A206C1F5D2058207F1FB11F801F1E1F211F961EB91EE11D1F202720FE20252023221B2194200B21A11F9A2088214A212C20BD208B218022E920E52020219421DC20162179217522FD22A123712F812A5F249A213321E8208E20D92039201E22162090209E20E11F6520342156224421F220B120C520762063209F211320791FAD1F741FEF1EB91FD51F361F9B1EC91EB41C371D511CB91F3A1E6A204C20AA203422D41F8D1F5820CE2172200E227E2174201420D7206E20991F34224B229C211B210721D2201F223E22A2242330562BEA2484235220D61FAA20C220EE204721012112213D228E20AE1FC7212D22082197205321C81FE120981F2B205121A820CF20DD216A1F971F9E1FD61D841EB11D881C571EEC1D2A1FF71E891FAF1EFA1F6F20AE1F9C218F22C820A12091218522A322C21FA620F42100214820E1203020482013217220F921E5222D252E2FF42BB723CC21DD2076201721CC20D71FD220E0208A1F7A202D208B1F572044200F20F91FFA20E91F3620F21F8B1FAB20531FB71F801EF91F4B1F941E0A1F8E1D881D0C1CE81D3E1FDA1FEB1F1E1F0520DF20A61FA41F2D22282037204220701F2422DB218C218A20FE20EE208220C32111201320C321F7216C21602390236A2FD3294A24D922042170206E21421F5520CD20E220B72150203F2210211B22C6203F212D218F21D1201221B51F352133230D1F6520FB1F8220921E0A1F7D1FDC1D801DC51DE01DAA1D661F2F200F20681F8F21E01F3D20FA1FAE201E1FA71F5E223021A221C1200D20D1219021F4205421EE20BB1F2E211D1E8D200F2222245F2F882AF325C3200F220B21022155210B202422961FDF1F07210C223A2189203822AD1FA9203121122251200C2098205C224620A11F991EA01F0020581F731D6A1D6A1DA41EE71DC71D701E8C1F4B20631F5A2014215120C61FFB20BE1EC61F431FE121C81EEC1E5B1F5120191F3221BF1F6A22871FE41FD4211B2192221724032E6B2ACC258F212F21E5201A21A520F6200422F620C9201521EC21EC1EAB1FF41F33211822F91F78223820B41FF120C11FE31F841E2F1F831DE120951E8320051F5A1F9D1EC21EFE1D1C20471F012040207E1F6E1F6720A51F6A21CC1E28201B20562186204F21A72043228B1F3A214721E6201D20AF207F21BD2391235024CF2F642CF625BC2188219421A7222C209D212822FD212C22262157206F217621422106226222B420B2224720B21F922085213920C920D61E091E391FE21F7C1E3C2045207B1DB81D481F251EB2202A219B1E91217B212620F61DAA208D2047223620EB2056202F21EC1FD42189218F20282032228320F2212B20CC21D22049233D2EFF2A77242822F5201D20F32006221721A72208223721D421E5210B202922AB2044210B216321FB2040206521F9212C21F21F2121CF1FCA1E5A1FE9200620F31E9D1FAE1E4B1FC81EB01F7C204A20401F791F0320B41FCD1E861FF61F31216220EE1F7321F020B61F0C200920521FC721DF201D219A213E20EF21A32244240B30C9290623E4211A218A2097218A1F2121A9227621B9217720E220592053201721BF207B202622D821941F9C1F7A2079204E1FB320251F1521C21FD51D3A20E91E8F1E001F4B20DD1DB41F36203C1E871F801F6A206B1F5220DA206720BF1FBA1EEF1F13203C20ED1E1420631F1920A420A51F4220D721FF1F2722FA210424172ED72A1C2478207822E82167210A21DE210D2346203C203F228B20492100212D21C9211C217221F021B120CF20B3216421F11EAE208820721F951F65203020E51FF81F221F4F1E3D1FCB1FB21F4E2027206A2106212220591FE41F6F209A1FBD201620CF1F381FD31F5D20C0209E20DB224620AC201421C220AE213B220A25F52EB72907256423AF203E216C219320662072210922F820B3210A20D82054208A22A92043215A22D5206A2143207D21971F881F1521491F1920B71F3E1E561E071E041FA11D4D1E6A1E841EC51E901F871F5F20A01F4B1EA2204120741F571F14208B21F41F3E20F6206521B61F4920E61FD4209821731F0721A5218A2180224A2F092CE1250422F920E920B820F420B12055214521002018205E1FF51F55217E1EFD1FCD209C205A2102221821C520531F1E2028214D21481FAF208D1E741FEB1F831FCC1E4A1F431DA41EEE1E671E801E3D20D420E31FC91F8A1F881F361F99207F20412193202420B921F91FAC20792209206120DB20391F2F2099232224F62F8D2A0F25F42134213621E421352098220C1F66208F20A12013208B217021A41F6C1F282106204321AF1FFC1F62202C20491F02210321B41F3C2133209F20FD1F6220641FB61E8C1DFB1E9D1F5A1F891F971F20216720C81F3F1FA01F0920F31FED2039207C20411FB121041F6520AC1FC11F1B2106216C206D2220226A24CF2EA52A14238921052300214A218F2049211821D91F1722632147206721752100210322FD1F94218E221021B41F12218921C21E3020BC1F9B1FB61FD11E02208D1E8D1FD21FCA209E1EC91E951F3120F21E182005229F1F491F50219F207321EF205B1F151FDD1FE11F1F210521701FC820ED20371F1320AD1F122082213823CB2CEC2A34257421BA21D72264202D21AA207C210622A6209A20FB21C5208B1F41225D225A22B921A5222F20F71E68207021C51F321F4820E4200C1F1B203F1F071EE02094201D1F951D8720171F62209F1EB22000212A1F691FE420CD1EE721091F792126215C200A1F312002204820BD215020A61ECF1F22204C21C1210C244D2FC22C4325F72173216223D220E021792080218921FC217F207F209F1F8A206A20D3202821DC1FF42053200C20362006229521FF1F951FE31C6D1F8F1E9F20801FB31F2C1F1B208B1FA71D8521FF1F951EC91F2F20251E30201320D21F2E1F19206C20B41FA5203A1ED02099205720E41F5E2085201721E6202E21CD219723852FD42AF02450238120512040201621AB200422F72099207220BA1F7321AE21CD1E1A216F20EC20B521F720B21F9E20C21FEC1F5120361E441D70201A209E1F8F1EB91F0321991E031E301D321FC21DE41EDB1FE31F6A1D731E5E20E11F4D203B1EE320BB20FC1F9820A91F901F661F4721881F6E1FF61FF31D462133228A245F2DC32AFA24CA229D21D320B721712213220922AC2044211B225520DA1FEA20BA212721E61F1D20EB202C215420D91F2D20F220DE1F3B1E0A1FFD1D4520ED1EE21F761F4A1F2920721E9B1DF81E101F4A1E7A1FA71EA21ED41E441F5120561FA4216F20B51EE21F0F2179218F1E6A1FC61F581F1B1F4820A51FB72126234924432D912AD2250B219A20F11FD41F102204216D21F11F6421D21E931F5B1FF61F0320CD20C11FA820C4205C214E1E9F1F91226621BD20BB1F881E951FD51EAD204021971F0C1F4320231D721EEC1E881E2E1F761F3420DF20DC1ECD20B31F841FFB1E5E21E31F631EBD1E85206820E81ED11F551E9E1F442192200D211223F124072E2F2BB024F422F920A321332191206820862115202E21731F6220031F0920831FC21E0D20811F04204D1E191E0B1FA8201820081F2D1E671F4A1F821F3B20432091202B20C91EF71D861F541F581F051F991E991F271EAD1F7B20BF1F781E701FC31FEC1E551F3120A920581E491E231FDE20621E9D1F671F292006201E24382FDA2AC5239E21AF21342157203120C321B81F8A206020FF1FB31FAB1E041F70201820F41E2C21162129201C1E89205120CF1F54208E20B41F8C1FD11EB41E362037202221681E8E1EBC1DDE1F871EDB1EC81E6E1F521ECA1EAB20921E901FCF1FA81FE21FFB1FA61FDB1F46201721131E921EBF1F1520FE209B21F82263241B2D1C2B8824D42059219921F01F2221422121213022C321511FD51FD51F2C2084206D20E220A8202820CA206A1EFA206C1E1E20BF20D71D1B20AB1E3F1FD21ECF20F01F641FAC1FA41F921DEA1FE51F021FB721E920541D22206B1FBA1FB51F8521D41F8E1D91200A20211E681FD41E401F101FF41F4621331FE2211422BE24F62D102B2125E821FA20D921A221E820EC22E22096206F207220FF1ED71EB320C3211320CD20A8229D1F02211720D220A11FBD20E921B41FF01F381FDD1FDA1E4D20FA1F8B1D641F9B1EE81DD81F0F20841E1B21D81FD31F731EE920441F8322EE1FD81F8A1FCF1FB41EDF20C61DA11FCC2027219E1F29200421FC216320F622C02EE32BE724A62024229E217121E320BF225322D620E220262115207F1EB020832094215B210821FF1E182100207A20DC1F6C1F6C21AB1FC31D031F67212A1FDC1E4B1EFA1CF11E221E491D3A1FD11E5B1E81200721B01E711F861E3B1E871E041F5420121FCB1EA11D8A1FF51E971F9C201720E4206B1FE81E3A20E82217241230252C1125F522502020233E23CA214822192366225C23C521D320F21FA42106208A204F224021721FA320581FF12086201E20E321FC20CB1F78207C1EE81EE81EFE1DF41D0C1D501E251F4D1E0F1F1721FD2007207D20E11DC01CAA1E9E1EB120D51F33205D1F941EAF1F4B1F7D1FE820C91F7A223E21041F591FC5229524832E +# Note that this results in a zero byte read +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 403F + USBDEVFS_REAPURBNDELAY 0 3 131 -2 0 1 0 0 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 000B + + diff --git a/tests/elan/capture.ioctl-recording b/tests/elan/capture.ioctl-recording new file mode 100644 index 00000000..45c4ca0a --- /dev/null +++ b/tests/elan/capture.ioctl-recording @@ -0,0 +1,47 @@ +@DEV /dev/bus/usb/001/094 +USBDEVFS_GET_CAPABILITIES 0 FD000000 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 4019 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 2 2 0 0140 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 000C + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 4 4 0 40009000 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 0009 + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 771E7618FD18BB16711785165415F3156116D7150B15BD14DB144A142D160014DC133616D0148213471539148716D31470159D14EE154B15C71415158316E114A413F614D913A112DE12CE140414E414D313F215F9136F14C3142D147E130215A9151F15E7140713FC144414A0132616A313E716A01514154514F017B1182C2410227A19FD16D0189D18DF16F916A417C9171A16FF16FE152F15E615A8154B1542144615D314DC14FB143816BE16CB170215C1162915301693153E168716F7141F158116101590151414DB161F14C714BD16E8155F14DF14F214381723164D153C156415DF1597145A164B1674157D154315D9165A170017BB17F117531AC02391215D1ABD1971186417E4160B175A187616F918371821188216FF157F1660166A162B174C16131515151617421607162317631747164D165416F11221169B162A162F172E166E163815EB15B814EF144B15D01591140816F61585133916BA1504161B16921544146A1633167E1636151D15B3157F1875166117C7173E1B0C255E227B1D841AA6172F186717A3187117AD16BB16BD162A16B216D2158C17B91471160D161E16CA17B915DC153115B5166A165E1783160317AE15DD148813ED15B216FB168617EC14E114C01583141514D5153F160B168E16BD1595158816E2164C1726168616D31577159F14B714C71526163616D8169D1897169A18941A8A242323BD1B5518911782171C187D17CC17E7175017EC16151701166E15231748166815D3160016E815FE15C71520187F14C4155218A515B81254168515BF14031575161D1782163614E11560154214BA16E414FD165E159B150816761674152115E0166A162E17091578164B15FF1596153116F915D2169915D61602195319232318213D1B8418B719DF16CB18E1160C177218A81792172318F115FC158416F016D4166016F8155817CB15B81562163A17FA14BB16A21541157D15C6151F1525165F16EC16F815471409176914F3151415BE152F166B162E1681151516B315FE15F4163F17C21552165B16981607169E16A7159E1642186017D0169719BB1ADC230A21F2188F18061841177C17561855170B17281857177416DD160A1691174117A8176B16FC16FD15A317A317001657167C143315B815F415FD15641501170017DE153816CC152515D7145E15FF149C14671680173D156715D1157616AF141516D816F91446167D152816E7159D15211625178A15CD16FA15A8179D18FD1819249D21A61A10196C199F17F015AE1685172A173D181F16C016C717C7187C16811698172117561706186F17E915ED16CD18D4159C15371690159116011727168415D7159C166D1614156C15DF1620178E14E61779161215B614AB153814F716DB151E16C41500164616F4165016FF15E2169B1644165A16A5154818E318AF1A0D251920BB1A7A19D5172B18DC166E159D1765187717FA16B6166F160016C4164E15DF15CF163E172D171D17EB15431462173315BD16A314AF149014CE168E153515AC15C3162416AD15C614AD14A41417159D160C150C16441458182016AF15CD15291640152C17FE161D18EC15EA1414167A17E1154C160D168116D3180A1914256C22D8186217591751176516771718179018DD1624185C17CB152417D716301603171C16F316B216A216A616E2143C16AE14CB17D816F714E916BF14E9149915AF151517C915F41565145416EA15AD14381657160E166415391536164C15C4154919791650153E174A176C17A417841612178B17D2163317A8160619D519A626D220271C6619FA17A518EF164D172917A3170018E918F2176D179315E91653171A17B91600172117EA147C164815EC1680148314FB14E914F01519153C149214A9160216EB15FD154014E714F7155F14CB1577148A154E15921646164C1650174E171216EB15001773179E15A2142917A716181662166B15B117B318B4199B266721191AFC17041A3C17AF15A216101605182B187F17BC16E1150C160F162017781686162116CE183216A917E014471515158915FC15981580144214BF158D149B1499157D146715B8147214F41576155016D2144F1689166C16B8156115BB16CF16DD16D5170D16181826170516A31704177B160F167516A3161D17061B5A25A122271CA018FC176717C2160B171816BC170E176F1632186E16C91706171D188017E0167C15D217301605169C140B1887165816ED154315A91574165B15DE15BB143A169216F31403140016C0171E16F414AB16821539169017891794157117FB157516C61498158F16941630163D17821624168418A016A3178E18671B62237621411BDF18DF185E19B6165117F717A7184718B816BE160B163516C417AF151E173A179716A0165F16E1151516FC163117251772151B154F174115C314E013A2160616D9154B15BD15B01533177C15E7175F182E16BF158C15D216CC1669179417C5141F161315D916D31446146116E116CF17A21716171918B418A31A0F26E1206D1A9819EE17471953179E18C516271968160B184A18D1151A163A164517B2152818AE18801670150315221653175517D215D9143116BF1594146C150715F81663165216C815A3150E155E15191641167B1753179715E2159C1608157815E615E9166816D015471746167F15801688174E164218B615011769189419C3235D22681B8E185217C21782181A183C18A2169B173D181F177A152F178E173D18A616D817C516BB1658186818B115681630153416A416D915BC15DC154F15021507162D15AE153D1555140218081633166E18DA1435151815CA1551156F15EA151317EC149716E2159C175F18A9157017BC167E17CF17321896170A189E1A13249E20AA1B1A1AD8199617C6178A179717F6167B17F6180A183E177E1740179417A717CC166417B316DB173A16D8168D17A7162A177F1605152A169E162B16741620166617BB174016CA14F81390151D164B16F5155F1530163F185916B915FC1698168815CB1676156F16F21512162D1438182F17CD1760187F18EB18541B4525A121F21AE519EF162818AE18D117C817F3177C18E117E117641781170116981699165D176218AE1649166D17E61604175B157C1775173416AC165715B5166A16C2171117DB156E15CD1684169415BE146D1601186716C8144718E1151F150A15BE1715153516CA1446175B156618A3176318AE17C0169B17851850181C1AE926F61F571C2B1897182E181418C9188B175817DC16B5184916A216DC16F817A016D1163619EB164B19BE17B815771763187B15F0165816C516F81586150C176D1621172417CB16321490170F16B616031420178615EF15691502153B14ED145815DF15C716A6178D14CB16E5150E15CD1467160D16DC15F715CA184F18AD1A1C25C322131A6E17A817251777172317B8172D1613163E17AA176917651557185618B0174015E7174D18FF151016B9155816421743163B14B614AD18F81504173115DF16CD174E1637168F1596158A148E15AD15F2158E14E3148C16DE1465153F169C1747165E1683168A178514FB143E17F415FD154116EE1507171E19EF199B242122361BE719C718F0172718A71633178317FA176B19A6175F169F169C16F417F916DB197F17CA16B21639183017D8169617F61535169B15F7152F17D31652180B18C918A917441764150A154917A514E916C8164E165416C014FC169F15D9159A162515CF15DB159B1797168C168019CC16AA16811887151118B118BB198E244D21441A3D1AAD171117A717C01759175618EC17FD151F182B171A1885165B1786183A16BD18F9166F174D179417FC16C0155D16F4133215AF16D61508182C17E617DA1892171317A41612164715F4154017CA16D014BA15B9150016C7146715A9156816221543164717E6159E16A6155D150F17B715BE170918D017EB194B24D220CE190118BC18A6187B18E517131769187717351810188217F8154718C2163117C116FD16E11402158B15EE15E115121560154114A715A215B014F31601170616FE161317AF16B7157D18D216FF14901700176F162615DD15B615BF15941678166216E617E2159617341806172E16091640150B16C116F816AD17E618F8242F21A1190819851860183D168F175A18DC161216CA1606185A15B816E5189B16F616E0176F16DD161D162F167417BA17AC166C1544147716DA15F7144D162F168216A317C315B71383172F173A173517D317A616C8174016E716BD158E154217B514D6150516BF1510177D15AF166B175217AA157F16AC16FF171C17611A22251A220C1ABA171618A2189E170E172317D7183317E5181618EF16BB16D11557164716951615176619491679155E17FF16F315E2150B164115B014C9154A154F1608162B17FB16C916B216E815EC1730165C16C41697172F17601791156E15E6153B172C15E914F3157415F51599154B18C0177316F01670177C19EC191A1936244A21D21912180C196217AD17E9178A177617FE15FA163D172516C2163017FA160D18881501178316D816F4179415A81601168816AA16FD149116E815021744162917AE1601177116E51446164C16171517179716501574142F17BC14CB145B1600154B1572150414F316B21569160518DC17A215C1157A15A51775175B1AB1242222261B4018531792185418DC170818BB17ED16F317D017131620171617211709181D1897157417C216ED15DD15B0177616181647177C156B1657162E17B515241728175C1872155C171216BB16CF161F166D17051637156015761641155417A015BC140416E7156D1692156316EB16B7165916F5167417BF1627188119A724D221241C9F17D918CB17DF171E18C6182618EF169B18D816DF193D1797176F17D2168D17011759179C167916A9163B170417DF158514E1153615DB153416E9169A171218AA17C0151B178E15DE16F7155C16101620155415F914B41583148F150C177E15A3149C165C1643164E16F2155D163715E41639172C181718461B9A2510227D1AA0190B19E417A016B317D617B518B1173216B317E216AF18511731186518D416A217B016AA16FA16CD167E16BD15D516A4147414CE152F1615151717E31717185A180216491612166614F815D316DF15BA151E15A9165F14EF15BF16D71585152B14411632167416B616D817641674176F178D1608176319CA19A8241D23921B87160A17CF16C5187D17DA17DE180C178219B217D7177D168B172C18D418D2172418AB19B3164617F5155C189315F2154A162A158415A2150716CB162F1714179B17F61646169C16DF15A61654171D173718FD14C516E3153A1661160A172317E5167B160617771747165D1678153F16F817C41675175419CC1966258521C619FE17E2165E18C516D6163818651746191616A817FC159116CC15A1143D16A416971717175615FB1446171A17ED159D158416F8132D161D168D160316D815BD165D16E215B3154814EC155F15D6150D18C8144E155D16BE164215E1151F156C176B168F14FE17EC17CA1845179315DE1442168E16BA180619A21909245921111C46181319011946167916FF15E518D419591A92184716F516E218FB18C1161C175717AF179714A915391595147F157A1610164F159716031542174716A5182A178F174F1480152816E81511142D16CC162B15C3158616BE16D71675155016541605152F16A816E81570163916B21548171216EA1692165C19591A4224DF21251A1A18AB17E21799179018D9177717AE185D1890171318E0177D17D317BB170616A517781618161B164F1582145515AC167816E21698167C16331511171417F917E716BC1535148417DB15D415DB16961778162816A8167E16E916161553161F160816C515561873179D16D5158F169916DA1776168E17D61883198F24F7214A1B5D18AB189B176D18C21650173118B817E41648171F178C17F5174A172C16621705160216C1162615B7165215FD14E815A916F7138316DA169F15D815EB17E017FE152E1490163814B315C2146A17C6178B14131653169815E3154B150E15EB15021772174E17ED148714F01440175B1784155616FF177918EF18B3240122481AA417C8186D19E8167A188E16FF188117B3175A178B1820174A181D173917831746174117621974178A16BB1758161816011832152D1522168416F416DC16CD1854167F14FE15FB15DD157E140D171017CF154B167B159816BB170915C416DE15681566153D161417E01589160016F9164E183517EE174C194F198D243020A4191C199F17B518E71796176F16E01625172618FA18EF172D171B173217C4188B1762160718CA16E5162018581772158316D417FE1453159D16B0157F16F31515169217D51336149F17E516741491150D18651765159216A017CE163317CE15E215F316041831188316BC13E81578166017C217A8169917F1182B197E25F221161A56193819D718AE1620166E173B176918371995177E17D415EC16871785180B19DC170E19A317C91677169D18B7164B160219DA162D164C17E215F3160C163D17D7156E13D113D916E11585156C179217D8163E16F6162E153616E415A01831151E173418D216F31523161C172517C617EC180118F318E518D4199C24EA21E81A8018EB17F9180D18B018E9187C1835186C181016EA145F183117A4167B189917541794171D18A91448167017AF16B215DD162F1614160B16E3156717AE169216DF15FD13ED148E140816EA1434171416CD173C16B11535167C166B158716CF16FC156D154B179615EB163E175617F215B8186E17EA170219911AA62559223A1A421837185418701767172D17DB19C3160017921715171217E517561754198217A91681172D16E9164C18F815D8178E161115A416CE148215CD168F16DB162D17FE16E413BC14DF14B814B7159F164E153616C91528161715CA150E17D41639160D16CD15A715051642166E175116DD1582170B152318DD18271BE923EA217A1AE418D717BB1613178E1738171B172C177318D7179D16CB154216A816AA168E17A516A0166318AB162517581885163E16B415D2153C153715AE162916DA167C16EC154715C315AE153715B31457168E168A15CE14F4162F156915A3153516DD1670176C16E61641152516DC163B17BB16111820175B1808191E1B4A25AA213C1C2319A119581966181218E9174419DE17ED158C1588168617A7172117B7189A17DA145A18411656178C17B7164716A1169F1467153C176014361665162C165F15C2156515F014B5155814BE15E616A91644161F151515EB1485166917EF154D16C416B6167717E0169C160E171D16EC150E161D16F116E617F41B56258322411A2817CA18FC165917BD175618D417F6167517D81694155515FD16E71634175E1793172917A717DB16C9173918F2178615DA14A2147A155E15D2140C16D7148F1566172214EB140D1632167B15B915DB1661171E177217BC159015AE15851791161E15BB1527171016F916BF160F16B6156017EB15F417B519351B2B232623691BA8190518401AEE15CD174C184917BB1674169316881571159517071770172017AA178B169816A81635169A172517CD15BA154F16AD150C150B166615F114F6142117F715B4151D15A615B415E41679169A17551561161416E8153F151D16631629156E155816E016BC16C31669161917EA17D9187D180517951A4F247D21D11AA3188B184E18B118AA16D11779177C1731179916AE15CC1697153C16A415C9178A174F1631147B164217CA179815C118621782145117B314D214DE163416FF15DB153E14A514D1142117A91592167016F5154C153816291525159316E5146414CD151015E415B615A2153D167B16941692176916DD1563179D1A992419226E1B1A195A184E191B1918161516C118BA1698167316A7141F155C17AC16FF15B2177616A016DC174F170518D1178A16B115D2155E166C16E7150F163C15FC142F1600168C13C0153B1667158C16BB15D3170C162117D9141216351531151E164916901631164C177314C7156916D115B5160E1872172418FD176319B62377218E1A13185E188718991753170718C117271892169116E316A517A41760161B1642179217C718DF16FC16FA17541743180F17031638165F1641137516F814F21519166816A91529152E14F41672153F15D41695154A164017AE1403175D160E16CA152A16B514D01790163815DE166116CD160919B1178718B21A511A57245321981B4419BD17E3189F1821186B177A17CB15A916A4157A15DC167117C416F316FC18DF154417C216991598161F18F7169316EE156E171817861548166A16611673143816F2151015FF14C31675141D15A51465164B169314B1165E16E0153C1401173414D415B31678151C15DF172C166C15191807171418DF18481A3C25E021A81A5819C11832177B186616F01669178E1730161B17F81617170917B016AF17E2162517D317A616FA155018A917391686165A166A163D16471755164715A6159414651661156115BE15C614F214CF14901678158D152C166015F6159A163F162D1732162915F615BF15B8165D16671583164D179217BF176618261A4C256F22021CE11846181F183D185917D915A216D0168B18D11676160616FA162415A517F714C716851670156E17AE1745193118381653149D15B616B414CA1528154315EC145B173B15F015ED1467157C146D15E61669158515941617165915A41654165B14D01596147E165216AC153717EF163916D315A7167D18BE18681B39242521F41BAF187A184319B117561593164D18581711178A172A16BE16BF16E4177C15F9158E165218BB177D16041727167617C717B2166016FC1429142616E1150917CE160417B016BC1590154115F6151915DE16541672142B16C516AD15E4140314681729154516F315F515CA156216C7168017FF162D160D18C317D4192A25FE214E1BAE1997171F182D18A4199F18CC17F816B117561750178016271777152417CD19A317F618B3156017C0160918961415155716FE14161519169B16B4154E154E164F162A1684153D14AE15F415931739166916B516AF16C817B51572167C16FC171B163615431618161C16FD166B16AC15E016D816FE19C5163E1A2B237E22A41C511AA1188917B3189418FA17E8185F18C118811620160817E2166316A315A616C2166419AA1639166017941769140B17831505163217981441160116DF164D18041632158114E715081567155B166E17FA152E159215E715C81578150316E7167716A3143618D11622163116211650165D1656158816621871195524D921E81ABA1B7C18DC187E181C1813172019CD199318131867189A16E017E416A5164A184B16DE1869188016F2166F1537163F1405171116C616F2148316F61569162618731747166F1682152D16E615B315F41533140B16891765164D15BC153B151D16BB152F164C15AA156917C7163815621518186E1603180518A319F4269B234A1A0F19DD178A18EB177217FE1853182D1797182417A515C415A716E618D61647189518A218AF159B16C416D0168F15F0155715CF1547154D158C1672166818E917E916CB151A1501161C15F0142516D915D81475168C1554160A14FD14461660146A144B1662173C15EB16A3183B16D514911727166318F0184C1AF6240B226A1B31185A193B1A0319FD172E18D016AB17EA1868195B15F2164316D4168016331649177A179818FD15A91716174A153816B61885152116CA164016541682177117BD161B1716163C157C159314101607172316DC15AB152D17561505164B1709153A151116D1166C168D1583160E164E16471966178A1890198219C0222023861B9E1A4C1950174C198A1779186519C9177018661816188D155916E5149617EA16E3163C19A617AE180817C217F3159116EB159A17771640172D18F11638172F1981161918ED166A161B14781598153F166F145E150D157416B31623166C167816C217B714B817A0161F153117E516C316291A201892177A19041B6924DD20771ADA184A191A178317481712192417B215D5174B15DB16DB158D16F3158E1780162C162B179C18361561172C16C2172A17151623153515B0151617461791169918DC17BB172017A415CA1776151515B1166D15E814EE15391654169516F415741567163A16AD15E21558174617CC143A14801886167218F817FE19FE234722FA18A8184418A418D416ED189F17EC1779188D17D215231611164017DC1539171017931681179E166716A2156A169D162016381791167115EF14EE152D167017481828185817911672156317AF1579178617351721155817DE156716171726178D171818561568173B162817CB169E15A017CD189718991A7218411A9C24CE2059198E181E186617FF17FD17B218A8181F1828182617ED1795153717DB16C216FC16AF171218211775163217BF161916FF15D317FA154A17D0155B177F166E16CF17EC18CD16EB16B517F1160F16F316BF16FC157116BE15FC15A416EE157D163717C717DB15CB170017CD170118AB158B18D917DE168F18D417A21A082779216F19301AB318A118B01804186C184019C31724189E16AC161F17C5173B164417E917E8176B18C0169E163C169517AD19C517EC15781479166C17B016F917EA16B518751795171217B617AD17DD16131783158817411610174E1669151217A71666154D170215B51785164014ED177B174B17FF160D172E173219AB1B4B267221511A2B191019EE1863190C17DD18BA183818F018BE1785179115F315A118AC18FC18EF186D18841770187C174718721792160817E5165F15751824169315D6168318D0170517F9162317B1161B165116D616C61540179F158515F316B7163217E915771565158D17A2168416DE1603174A17701783155E17BA19DF1B3C263520B31AEA171A181C170A17A51720185F19F5178B18A1169D176317D917851560175817D5171F18AE166C188117C41663162417A61547153D157C16971679169E177A175B1835182F16FA15F417DD153A1615169C161C17B217E11516156F158C177617B71749158316E215AD167817C01623173A18F91754186E17901A84241D211C1BBC1A17174917D517A01744175518DC1858194B188217D81828175518F01647178E16D41831169C18A11632175216D417C315EA14FE158F164416FE169916861690191E169B16AF160B18B6159C177817B215A115D418C01570156C1698177A17A3160917C7161D17BF17C5171D174E15BF1AF916B617B91A081BA324CB229E19F4186B172518071A0A18DC17F1181C18A61943195818FD170D185315E3175118B01719164318B217701567177017DF158417C9157F150B157D16B116FB16C4170017B0169F1520160918C515A8166A17A1164D1651174D172315D71646166F17F8150016EC1739166A16FB19EC16BE1547189D177D17391AE31884253A22A61B77178917D318C0177218D417DE186219E8173A1849174417AD170C187B17C9170F18A7186C17A5181A180116C41546177E166016F316FD15C015A316D1173417CC15A6145F164C170B160C176218AC164E17491696176E159C1515165B1794140F16F6172B186116901583176F17D41812176E1737173219961A5C252821421B9B187D194A1791187B195B18F51718161E1ABC169A18E316B51724162818C117AB187317BF169B168E15AA177F1670165C17AE1562168816C914701674174C17E4148714E0176B167C15CA144A16A316A9164C162C16FA156D166F15C81744161416EA1566166116F214CD171316D5166F17C9178417B017581AEF24821FBF1A3D18E41793198018A616E018C51666178A17141655182617A718961785174618971781180C178816F51797171616F815D0159E162116A615E715C8162317001762172716B5149616411618146714B4175E145D164C181718A81739175E156F15831604159418A0169B1686173C173017411719170D17C119361B6A256F20881AEE19F819D5184E180F17EE185E174F17F317F8189616BA17DC170E186418D717A1168B18061835164318EF15201643179E177715C615F916DF158D1574170E17DD154A15AA1522169816B316BD16AE16B3167416DE15D115BC16CE14551532176D16F71543165E15B6183617BE167F17BA18BC168916C218421BCD239D21741A3419E317441846191618DD1716180F170B17D7150A17F417FF179D1808179D17FC16F41879186D17EB162118DF15E715C315C915BB1620164216DF1484175D170F153B162D160E15C4166115DD166A181E164D1788167D148416C4158B1617172D150717FE15FD17A816CA166E178817A1165117CF1866187F19D0239A20CF1AD6186C173E18C818FF17531640182717D0174118F116AA17851913169216A618AF163116E6165F163E17321714176B1610178E15A0163217BD167317F817F9178A17521422161A172A179715E11725176216B414D1171B1611153E17051789167F1709176B176416DE167E18141763188E178817CD17A718331AAC236E20F4193719C11816177E176C171918FB185F18D8161519491611177519A11794181817EB16A8165A16E716BA16EC169F166E16F0169F1595175216EE154E15BB176716BA166C14CE153716EE166716B81790160D16DD14B917BE1576165817C516641625176D166D175216DC176B17C11681178516A817FE161018011AFA24BC236E1B75193119A5185518A116D717461980183E17F418EC16AB17FC17551935184018FA179516B017AB16A11719181117EB1683163717D716CB164714BF153418DF16E817A7141B161017C31666169E167A17BF1416162015D417FC1525162A181F16111873173E1667163A1691174018A017BD172718E618BE17E11AFB2636229A1A331887177E185B18E3186D18D7178617B418D3176A17A215FB1636198B17AE1649186B181E169915BE14DD16AD151417BE1669160F1714158315A117FE16681735174D1507172716B415CA15941778167B170F16C516B3157516731607178F165116E9168E167916BF16B218F3161B1708186217FC184C189B1ADC26E9216A1B6317CF180D185D177E193A185818E5178C17BD1722168F17931714183218A218DB17EC179215F81898162F17B5159817E7151F16F7168016FA14D41511169E15FA163F15A1151D17C416FB154316A3174218D916E716DF161817CB173D18C816DE163B172F184616CA162217A017E917F81792160C185C183D1AAF266D226D1BDD19E619B3186C17F7173417C518F4178A19BF17B417C418FE162F193C192918CB1795187A1605168F1814197616B715BE14EE1553164B14A9162416F51639175218F0151B162F174E16AD153E16BC166816D614CF1750174A16BD1726173218F116AD15191905178F18FC161016631839198C170318CC18811AF6243621AE1A17193B18D119ED17AD15A41689185718BF1806178217B617A3187018751878181E1824190F16E916D616E01796148C16E01527150A17801568167716CE160217EE16AB165315B3158A17151668156C17CA15C015A2171E189C18CC167C173C167D171D1632186C17FE16DA17D9159817D41938176D171E19451BED24F520641B82185B18F718D3173917AB188E185E18F0177C17D5160117A51684184517A718FD17DB1673177E1823183B16A416FA157C16AD16F115BA16EC14CF16E718B516EC17ED1543157715C61634168717F9177C1669184018DF16E6182519DB1873174E17881846186C17B116DF185E1743172717E518F0176C18C21AD024A8217E1AC6180F188C17D717D217DC161819031883184617E8160F1897180E1919192618A6189917701776170F17F0161F1675171D184616A5165915C51712174B18FC170B1751151315631631168014CD1733175E173418E7175A18841749181D173A18B5196C168D183A18F6151F18FF15FF1783179315E9161D19651A08252020811A751816185917BA1708183317CB16D218E4164817D61706186A1771170418FE161C174F173717AB18511772176617C3187817B814A716031641167614E9158E17EB171C165C15AA17E2149716B41705186A1522161618CC177E17A517E9176B188C151417ED16ED17C71664177E16E9179E175A17C418CB18EE19592482216B1A171A14188D18D31826174519241999180B1880175F180D18C51684193A158E1760183A187E17F315A7173A173E156D16E0159816F916EF155D178516E8163117A7163A162D167F15D315691622174718AD164B17C517EF181B19A117C816F6168616C4167B187417FD17B518751767177517A217BA18F8189E1ABB259321191B2C187E182B186D17D417EF18061817197B180118461663175716061A3018C5179817E4164616B415AB155E184F142A176E1629161F178517381561162D178317F41628179116AE1657178316CA16C917EC16D2178618CE161218471719162317D115041792173116CD17C716F21514182F1801169817A617911BF024BE22F01B3E18AD17BE17E81741174E181F19EF18DF180E19BB18C616BC17F0171817CB17FC1714189517D9169117C716C4171F1740164217DA1869164E1518173F17F5154F155615FE15E6153317CE15CD179917CD1759160E18E4176116CE175F17C4160517C5171017E71644171818CD175C17E1181D18F5181419B31BB626B321D31BE019BA1757173F187D16EF167C171518061863189E17EF167D18221703182C18C018B217EF17CD15CA154C16F4142216A616AC15A118A6161B17501589161F16FA16A815271533151F174516D4168917B3164C14B21A0B175D170717C61685189C1765161E176D16291764179416D517D6170718D7178418A31A9C2429212A1BC417221836189518451895168717E5179617A5170717FF162017B5175216481827177D18D71810161E16DB15201692161417E11518187217F2176117F11645179B1767169F15B8147E1744156415DF163915CF169A169C17FE16E0160617D5170A179F16EE16F11638175D1796161918BC176F16C5176D18661ABC24D822A4191E19D41819189F17CB166F18DB1607198018B3171817CD174818EE17C317CB164A187A18461859164017EA16021871161E17C3153D18901693161017D0173C1827162E177216EB16BF164A17AF188816E2168515F9177A178F16E617CD16AC17631625171418F715AA179D175A180D189217F615E8187F18A21A1526D420FC1A14181717DC16F9166E1603179917FF1633189A148A17311792166A18981756177D16A1181A167215901713171617731687167017B316FA1502170817B91644176A160D158B16B315F7162C16AE16C316DF1603155819D7164D192519781763173A16BC15A317FE16F5166217D716DF16F019E116A0161217C6198B23EA228A1B2019AE177B181A199017E619671832180F18DE16BA168517F9164B199F180117C8175417F0160D17E31622179916F7179A161318A9176417471731178D17A317C41795150514FD17F018F715EB16E31823186117A917731614174A178918BD1745177E1816187E178917FE1890179F165E17EA162317E819331BB924E520581B8F176B1852183019FB177718AA17FB172B19C617FA1784163C1874171918371740186E176B178D161318AF17AF16B417E81469161316B2150617BC165B159116CB16021724176D178316151674178E176F151116E616C4169617FF1659182816C417B517F3169A178A1619179E18B617531725167A185A19101AEF259722021B6618E017A619A1187218AD170C191918B61811196C180F18C1172F188716B1184B1794180518F415A5182216F715E9165516A616B9168917CB157915E916EB17271710177D160D1829191C15AC17D21621170D183118DE159B1771165218E9160318D216B01839180A176C17011847185F18D8175D185019801B83257022A61B0E18BB179D1776185C18531835191419341898179717DF17FD1691173D175C1616176D17D0161516B716D315A4159C17F5145915B516F3164915A4158816AF16C316E015DE15C11582166116E8168717EB18E415D7169B172B16C617BB1794177518E314DD1655180E17F317E316B1171C18D11668185B1974198C25F5226A1C4C187A178318DE17D6164E185B19C817EB18C0185A17931681188117C91547189F179116F8160B17C217901771156B170517FC15A818581632161317B81797177C176014D7157B159E151115AE17EF1780171B171118DA15D1172F181D1778178717401822198415C516F617FA171E1707172B18DB18AA179C1A1526CA23A31B7719D717EF18D918E418A118BA169D174716CA174D154816A9174F1703170518DB15DF16C515FF14DC141516E315D115DA15D6167416BD1761174715581736174A153F1384166815C5157C162716201957161D15AF157715A8180A1861171B166D159616D8185317C9175D18A4189F17B217E3161E170B17A4182824B622431B201B52187818DC1624184719AB17E0160C1883172316B215BA151417A2171118E7168317A5161116B4162418CC1571164D15071607175115D8160217A7169115391613167316C715691536173815D31854162E14AF1679153A15BB147C1565168A18C016AD186018A618901706173D170417EA16D7188E198A1BA925A922DF1A8218D318A6174C1751185B185518601702162E17A4161C16331635173F17F9161918B5163817D3159B17F2164E16A2150116A7150716D815141661171B1672163316281383165E1409164915F4149C163E15DA15C215FE15DD16F714D015F216CD17C01730174317FF1662170B16171774176A1686179A18231A22238E22E419D617301727180E1727196B175118B91882171717E115EC15E0179717B616FB17E616C1173416AB16FB18EB17F515BF154C15FC163D1641154116B116B916A71566154E145316EC148616AA156E155C165515AA159E174215F21672167D178A166D17D315C918D816B416C818FA17E2162917AA17DF16C1182D1BE422E321A31B69190A19DF185817AA1939177A198C186C15771635181D17C516B21753172C187C178D178A171617DE17AE18EC16CE16EB161B17D9158216C2153F16D616CB16A5168414A3173317C0158C1407180318E6164F164F16EC151918961740185417C5166D1804187716C516FD16AE17C4186F187916FF169A175119122479219D1B1F195C170518BE167F163E174F16D81634170C176617AA16AB172C18EB16D3157218A216F71719177718711779154817BB13A315AF164B173615C4160C176E1838175C1301163D14261848142816A117661610150517E3177B1786173B169417EF18B01648193518301870179A18A1156017A517B3176A197019D4248121281CB2189918AB179D162C166718811848176218DB18BE16D1164218DF17DE150C1767171418E416FA151F162016F3167618A6161416CF17021805171117A216191742161214FE140116C816F9155B16F016B115531748160F151917BC162819A9163B176A17811765174618F317FD146317421750169417EC18521B0624B220761B4F1874185C185C18F9159318B0186F17B91698177117E5160F176018A61656174B17601954174F169A182117FA179D170217DD1617160F17D9163F17F615DA166E16331688150316DC1637177116701631164416EC159A156015B6152218081724164E18CC173A17ED16741604171617AE164B16C9160F18AF19EB22A624D51ABF17E917FB18EB16D4171F171417251AF01769174C164A15BE17F8163016BF1848161D185B17EA16EF16F81727177E17E5161A149B16791643166417C01645158114E0150C153A16A216281671167817D116CE15AB17A6151F16B5164017281652167D150A18DF16D816D5172A164F178116DC1454172F1793181D24EB228A1C59186C189516E31712179C175F186F1849194E17AC162A189D17551843176118D417BD1708164317A0180D171D17A71880164015C51751156617FA14B21651151E15701654144B15FE153415C315FC15F6154116DF1669160B1634177918BC17AB166B16B817AB17E0170A18E51548172F16F71517173718011B8F2418222D1ADC181B163817E117DE16A917AE1674175C175B17231724175518DC1825176A187F17E717AC17A616CB161917A9192217131653157D15201661166515DD1514166D169714B613EE161B1553145416D6175A167417B41610178A184518E91686162617E01766167D16B415A017E916B81521169516A815B818581B5F24AA21DF1A5E186B19B6194A1993177219B018E218AF1669179817AF163A1862186A17841671171F18D915CB160B169B17DF156D17C315101554169316961521180F17E71433163315751485169716C814AB1793184716C71425162716F616C4161E186117531647163C16971686162817EE1745175E17DC1674178717EE1895244A22CF1AE218321A1018721A271955184619AF19E118CB17E41772156517121742179F17D817AD172C184D163718FF168B16B7179D152917241705170916AD156817E717CE15AB157C157C164215C814731834169C1785155417FE187917D916E8185B18F218BA168A178017B2175919EE1700185A17FA154C18F7187619C7246922621C1A1A4A170B18B916AC1973185B17FB172F18281726156117B1173718AC17F716A3175D188417AF1769179417FC179617BF16DA1790176716D51680160C183618FD1640159D1517177B16D5159E17E1179816B915BF16551778176615ED17EC170916E4152B17CB17AA157C1763169917C816421607184E18351AFE24F321B81BDA194B196F1821180F181A17BF17B7183717C3178017D5166E16C816FD152018F817FB164B162F16AE1AED17401506158E16D21545172C175E16BC1707178216BB16B1140715541513168F150818BF16A4163217F516CA150B17C516F7166817B9174F14E016C0156F1642188916771743179C16F6164918561A15238023BE1C2A1AF319281903181E1982178B197819D7178E187A173418F0173117F3167E1666186617A218991745178F16341735173A16521881178E169316E3172417C9174917B514C4163616C715E116ED175E1872165A16AB153B16011644181618811775174E178E161A16F315A61784174C161916A8163517AB17E519C0247B22D21B1C19E8197318CA17781844198E18FA16721785172E17B31858184C16B3165E163F1AA518CA16C7160F17A117DC168716C6177E17D914631571150816EE166A17A815F014FB14B717D715C516F8178517AB1682168616EF16081974186C17D1162E1816178718E4161C17CC1675166E177417D316DF15C618291A892538217F1BD81867167E1806197418AF18C818C118A2172418EE168516FA175C18DE16E7170B193F18851792197C170F194E183817D916E616AC15BF1564155216021891160C1694150E14E0155D1676163B185D180B1739161417C5159E17A1177317BE178F160E1684173C191917AE1606165118CA167715A2178317391B7225C121911A2C18E917F217D617D118721708184F182C17E4161E172F170E1849189F162D168E189618C018D118811787168416CD1779176616EC168515D41526157416FE15BC15EC1432155216E116841594163318C3178E169317EF1577168316C017B916F8162A1568174C17C41653174A165B186218DC167917F2185D1BDF24D6225E1B541993175919FB177117FA184A175E1860173417BA16A615B215A1161117381846197F198F175D1732195617EC151A16B8167A15BD16821585153517A416DB152D15ED13DD152E156D15F514EE15B91719163416E1150516C716F61590165B169916E5151617B3164218DC17EE167517F117D815621749196619A8243B221C1A641951177A17A7176917BC176C18A718BC1617167F15B41608174E162A172D183C175C180D175A17BB17E3153117641682157815FA15E716FD151816C3165B15E214E9140115C5148714DC151016E1175616BD151817B7167D154516EA152B17E117EB150517F616CE162A179D17BB161917ED179717C6184F1A34257422791B001A63184A180B172C17F816D318C2179817811760168615FE157E175417D417B3166718BA16B016E6188417A416C4176A1555154917991522150E158A16DB15D413B01445146616A113FC15AA179416E516D51517183017F915EB16AA15411619176316DE17A2172D15FC164418FD15D917BF16E11774194D1BCE247F20D61ABB170C1989184A1603175017C417EF171D1861162116FB14FD14DA16C9162D17DF17B617E0162D1713170F18DB16E21517166D16C71791177F168F163A17CE163A153714311548168915E9134A18E4186117E815F1176816A71537174818DE1657177116A617061655165617871734179A17DF16E7174318B61AA2240421061A2519A1167D18A2164618D6183A17CD1723187C173416B415F416AC168A1691160F1A941790177D1517178617901661157516F81698163D1581147616DF165115561423165515AC142617BA156A194C173F1798160D17E4164B1641161916A116A818F2163B18BA16C816D715AF17E1181B1AB9180818E4174E1BEF2431213919E6167E184B174217C215091988170B1857182119AB164916BF1854188D167817FD152E19AB178214EC170A1898178D166D15D6158D16B5156514EA156814EC14F514E5131B1444162D156715AC17EB159D16FC17C917B515FF143C17A7167B171B179E167417AD1565163C18DE158F172D18001658177C18E61B5D25AC218D198A1928175A1889173617AB181018A516621768177D182F174918F818C01665172917D7186D18D916CF16EA1523154E173A16D115A0150816B7149614F51556170A1534158915DC124F17E71513182F174A1604164B16C8158C1568163D174B164618EC1679180B161B170716D117FF16F21828176E18AE19A51A96254F209C1AE2171818D8151718D716FF15A817D7176918E1169D16FB17DB1635169F174B17371805188B162E172C178F166E1626175816E815C7150514401553147A144F1599144F142B15A114B7141C161918B4186517C9168916F81696160E186B1804164815D9153918511792171E1774173A17F817E0162C178818321B432687212F1AC918EB16A416C2165517C318AB17931615181917B9178E158416CC169A158E1884186A183F1705186916D818AA17E816F115EA16C516FF1553161B16C1150716BB149E157A140317B6169E179D166A188117C4163F1719160417B817C3178B160117BF17BA18E6160C177B17D917ED16281783178B183819AB196725CA20BE199A17C6172C1754171617CA1678188516A616E9169916BD167B175C18B6173917A41611170217F416531887160916F015A0153E15251613161A1511150815301306146013DB16BA14F5167216EF1652180516A2152916A817871647182F1747161716111762165E15211848184217D616B616CC16DC174518231BC925B4218D1AC019CD161F16111728175117EA1722177417BB18D716AC15F01722187A170517DA1700164917F815A9168917431762176318B115F815FB15FD133E15F6139A1319150E141D164715DB153E1583164E16BC15B5170919BE16F416CB17C8189D18E21500172418FE165E16BB16FD154316E8164816F817C118301B4925C62279195B18D5178517AE176F1793165C177217B3168017DA160E16E8161417C016FB169D17C916EB16B8164A16561768167A164F15CC16F0158615E8156F148E144E133415A71595167216DB15A116801727166516D018BE16CC16F116B715A1188B180618FD1638176A17051744189616611648184F180618DE19231AC1254720311AF1188117C916D7175015D3161D1757170B18CA163D1851175B1803175617B617EA17F41663175A1685178119B0158116291615175E157F15AF1538146214BE1427145A1427167C164C16A915EF1702169416F71508177915961548180917C717F116A316AA17B517001787172417B51512171C1454170718051A6425F7203F1B26177F1809173817A11746168B189E15061671177018A11743177618E6152B17B417461865166016FE16B518CE161316BA14E4153D16B515B41374145C1465150E149314C4140516E216BD1529166B17651656163917F714F115A7150318FD146115151546160F157017B315B01895151516D317201762180F1AE523DB20BC1B2B188F175317B0170D177E178A188C1766172F176E1812150316701669176E182716C918B7164216641767165716D314A315A013C317E814541759150616E51409154E141D17E81566168616EC15B415A0169415A217FB14A9164816AF17B0167617ED16A11890154817691709172316F716CA17A21985196D1A9A25CD22BB1B5A180B18301815198C16A417AC1854189818B217D016F117FF177F170D18C6185D1727198D162A16D91647186B16EE1615154414F81519161715871692168613D014D91586148E176A17E614C8179F1782160E144917BA165D188B162A17FF167217281616189E178A166516B018ED16DC1735160F18331736193A249A214B1AC318A317C7166E17A31822184E19B118F417621874180C17B5189217F417B017FC1766177716E3174318EC179D1680176E1642151B165717C0168B153916F214041620154216D116AC16B715A815781647162615001694168C17C3166316B7173417EB1545160B16A815CB1729178E172E1898162E18DB18A71A55262020DF185618CA17CE1628180F168D176019541825183217D517EE162F17D11732173517A71866182F16FA15CC161217E81587178B157C176F165B1445174D1512157315E2168F14CE166116AC1451163916D216E015921651170417451630153916B4164E1606159816AE156A16CC160D167A1633185D1648184B18451A19248521EF190D170419AF18FC17C6173E185D19C6164817E618EB16BC177817A71799189117D7176C184D17F8164618D8177C155817BE16E2151716A816ED16A3167F16BB15B314E0151E1612166B16D416AD1755178B16D7152F1627176C15DC168D16F4159A154F16E516F616D01641198E1604175B171417FA176E18101B3825C620E51AF0199117251860184917D2160A18C1188E17DD18D916A91792175C19B317DF173419A517401823173418B9166516DE171A16F616ED165C154215D0140C167114DF15821519156C155D162C16481784162215721739171216591670160C1879160B17C217DE17D016B616A6165B1736182716D1176318531820191626B722DC1BB118C517DC17C8176C173B17D71706187816B61621167D1604180515E71688171C17DF179418C6174117E915EC16D1174418D8151417F5143916CE1641167915E9159F13221681152415DD145217751786165C160416E71506165C178717C517F3169A163A1869163117C5188116D7163F17F3158B16161A6D1ACB26CC20B51A6418DF179B17201893160E198415EA16FC161E177616B917CF172C160016CC178016FD17151663160417AD16E4155E1762172216BE17B0164A172C168C16D7155115E513F015F515AC15A815AB15B217CE1602169E157116A416BC167417F416C716BA15E4175515C416581619166F175817D216C518BE188C1AE824FF20B518F41745195217A5170C17A6178F177216AC18CD1776169D179D179F1712186B16F01701198B17E3154617EB173615AF163A16E115F31526154C16BA141216F5151417B7142D157F155216461536163818B015A415771793168A1741175F1522150A164A166A173C17CA15E7162E17AF1533163C162316C6174A19FC2278217F1B2318301842190517EC173317311887184E1745176B1853175416AB18E418BE1823188218DE1670156617BF1741168515D816BB173515A716C8159A14CF17131767150614FD17AC15C216FB1416172317F115C01537176C15841869152D18551717175C15F61651169C163A189F16151523164616C21751187C1AA7252F23261B5F18E617C61943175218F416BB17E0170518CB16EE16F515C716B2163A17B91724165617B4165116B9164418F6175416F515581373160115E616FD15D4158F153C16CB15E913371811160215F8158416531409178F16F4158015B41686160616F216AD142317C4164B163016A81673161B1712176517EF178719CB258821B11AE6191D175717ED16B0174517A018B217231721175D16341823188515C41746176B175518B917531624179316D4160217C9142B14C117AB163A16271531166B178E159514CD1394164014FF157D167916BD13DA151D176216E5169914BF17751786160E173A162A16BD158B1704161F1648168D144118CB18FD1A4124B7207E1A41190F181417C217B51851183518D5168117F9179C165D163C17151832175C165A1643179B177816D515A1166417E2154A14C51538141F179D150716CA158B1552164C1423148B15F214D914B915C514F014F514F015C916B815F517C3161D1501165E178917C814C6153316B215F514BE16F415C1172A19351A6723A821ED1B58187C17F3168816D91823184018F5162518DF15B21692160917E416951771165217491776182515F216B619721883170E1667154716DA159E1700187316D915C916D813AB15DA153715D4154516F916B5178715B817B916E9159C150818DC1638157A155C175717AE159316D71495160B183417D517CD19B51BDC240422A41AB519B0176118BC17741760172618F116A6174D160017C2153F16D2151C15A01632166216EE14D9141B165617F416B81597141A161B1613168716CF16BC168616AB159E1455163316FF15FF155A154716EA144C16EA165716EB14141653168F155B16A8162B170F152415FB153B1705157716B6159616BB16B21AAE257221A919661843189517BF1600176F1858160517AB1672168D161B15A915A116BE167015AA17AE17B9161115131781163E16DD16131732162A16771513151317E416B417D81429151A140317F9144A159715F715DC14911539173C15F21554160D1641162A168616551687169217851495151A169216271717185B19001BE823B121701A4C1717182E18D7167E17B917E317CC18881831167D165416B6161F173C17CC172917BF1658171C158717D2142D175517A41441175E152416A6153D17A3161C163F162416771451174116D91529188E17DA137B1708165516A116081892165D14C917611605153E165115A715D7158616BC17E3159E18B118851B7A243E21891AEA17EE16FB171B18421731199117FD1690168516F0140915B516D2172816BF16F618E2152A17B1160017F1151417F117EC1504161E15D6151D156D16DE15D8136916D0147114BA1606168B148817F11502167B14B51791157F186216FB15B9151116DF14D816FD136C16AA166117C9154616F316011861163D19D8243922351B9317FF1871185D18BD17D6192819C31799172C18E71695153617A61787182018F517DD151418BC169F1791163F161E184F168E146B166818F915BC152015CC13DA162A152F14F816CA15E014AA17DB178B153916B315E9147D15D41521170516B115C014C7162D1645166517C616AC176316E4155117D619BD1AF1269422061B7619AE167319A719FF17A7187619B718C419081851178316DF1767161717C31894170516D616C6158917CC16991641185017ED15E416CC14671546157014A414A1138E157D15DD14A2154E176517851695162814B1136C150A15D9162116A416C815DE1442168C15DC153C17CD15F01871176F153A161D19BF1A9F24 + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 F327E021F221D31F8520F31FE21E3F1FC01F4D1F951E0F1E021E421DD41E9F1DA01CC71E681E971C4C1ED41D741FD91D901EF81DDF1EBC1E4A1E061E0A208B1EF61CBA1DEA1C2D1BE31B891D071D981D261DBC1E6B1D121D9D1D251D741CD71DCB1E7B1E801E301CDE1D6F1D551CC11E171DA31F051F801EAD1DD8202F22752DEC2A2023E11F6A217321A11F0E208020AE208D1EAA1FE61EF01DC71E231EF71D0A1D671D031E951DC11DE61E521F4520D11D9F1F251E111F2D1EDA1EE41F8B1DF71DBF1EFE1DA21EB31C091FD11C1C1DA91FAD1EFE1C251DAD1D8B1FFF1EBE1D0B1E361E031F7C1D9F1EED1E321E741E241EA41F4920D01F7320CC203223A92CF72A6824F422CB21BC2013204520A521601F3922CD215521E91F1E1F8D1FAB1F6B1FEC1F4F1F451E4F1E3120A81F321F3D20A920A31F981FCC1F7A1CB61ED71F891F8B20861FB31F9E1E571F191ED51D761E441F1E1EC11E5D1FE51CCD1E131F611F8E1FE71EDB1D7B1F911FB01F4C1EA61E321F8F21B41FE92045217D24CF2EAA2B19277E23DF203F21BA20EE217B20FC1F8D1FBA1F821FCF1FF41EA9200D1E651F421F5A1F3521881E041F361E1F20C61F70208B1F3520AB1EFA1D9D1C711E4520941F8B203F1EE51DE51E741DEB1C9D1E521F291FA51FE51E7E1EB71FF51FC2202F1F5C1FC91EBC1ECE1DD41D561EF81E3F1F4E20C021D11FAF217C23BD2DAF2B752534216F2085201B213B2043209A2045200020B21F181F5D1E2F20AF1F1C1E981FEE1EFF1EDE1EB91E0D21981D2E1E8021D71E8D1BC41E8D1ED61DFD1D8B1F2220321F671D2E1E5C1E741D2A1F121EBA1FFE1D7B1EFA1ED91F5D1E0B1EB01FAB1F8020151E591F481ECC1E8D1E321FCF1EC81FB31EF21FEC217E22562CF629C6248A21B422C31F9C210620BC1F242152209020F720921E341F581F7D1F521F3B1FA61E2120CC1E791EB81F3820EC1D671F911E3F1E301E761EBC1DE81E321FBA1FD11EF11C1C1F4B1D4E1EF91DA61EC61E0D1F2A1F5D1EEE1E781E091FCB1F0B20791E3E1F2B1F551F0F1F2F1F511E9B1F5B216620F61FBA22B723AB2CE129F922AB2115216A202F206E2143200220442133204D1FD31FF41E9B205220E820551FBE1F2A1FCB209620F71E3B1F671D4F1DE21E1A1F431FAC1E4720E21FFA1E9A1FCC1EE91DB51DB01DC91D911D0D1F9820A71E721ECE1E6C1F641EEA1EDF1F281E731FA61E431F151F6D1E0A1F5620B61EFC1FDB1E11216F21ED21152D502A4A2471214322C320C61E651F832077200021E11E981F9F208F21741F4D1FB620FF1F8420C4204B20CF1EC71F6821A41E5A1EE11EA31E501FBC1FFC1E951EDA1E5C1F6E1F361E0A1EC61F6020241D8220871F111E701D9B1EE51C381FD21E181FF01E291F201FE41F621FE91EC51F881F621F671FFA1E70218221E623F42DBB2861231E22ED202221CB1F711EB7209021B520FF1F531F371F111F0420431ECB1E022076204D201B20C11E361DC01F261E7E1F0A1E141D681D0A1F6E1E371EC31EAC1F2E1FD21ED61D7A1DD31CE61D341F281E021F771D7E20451FD91EE61E3B1FF81D5320E01F1A21051F1D1E4D1F5820EA1E651F1E1F821FCA21FE21E62D972BB82255203F209320A61F2B203C209521052020219820E31EFB1F4B20631FF81F041FEE1FC61F9D1FD91FE91D5A1FD51D6C20F81FD41DF71FCC1DB81D8C1EA21E2520B51E111FA81DE41EC71E951DF41E841F1F1FD91E571E711F2A1EFE1EF621491F531E9E2079201D206A20AA1F4620B62053202C20881FE9212E23E92FFD29BC251122C1204E21C21F2C202020AD20A120042279202020591EC61FE61FDB1F511FB71F2F20B01D471F4A1EDD1F3B1D4B1D891DDC1D971E961D3D1DC41C331F7B1E8F1ED41E091D141D831E1B1D571E741DDE1D3D1E961F3D1F2E1FFE1FD21FC31EA41EB61F8420821E911DAB1FA31FE71E171F2B1E73207E2175226F2F132B5C2473211C2352200C1FF31FAC1FA1217621A62055202B1F991F671F38205F1F701F9F1F17223F1FBB20F21D5C1E971E771E061FA11E731D211D481EB31D7A1DAA1EC91D0E1E101E851D971EB51EA81F2E1E341FA31FC21FF61E661EA71F112021203E21401F65214720701FBF202820951F611FC61FE61F2C204124DB2EFF2BE22518224421712026202420E21EF62034209D1F7021A41FF92067207B214620D61FAF1E5D20611F741F041E0C21931F9B1F191F481EEE1ED71F351E511F181E531FC71F131EF51CA11EBB203B1F6B1EED1F921E011FB1209C20F01EC020551F721FF01D9A1E821FBC1F371F63206E1F421FB621B71F9D20AE219A24982C732ADD2495219C2111223A1F0C20AE2079210E21491F6F1FC81EDD1E5920771E0D2006208C1F961F2D1F911EEB1E8D1F2A20F21F5A1E071EFB1FE31D931D851CC31EDB1E871E931D6E1E921E3620971EA020E8201E1FDF1E4D1E881FDE1F49206F20921D8B1ED61DF31FB01DDA1CB91E951FA82090200C20C52070215D23A72EF22926246E22F6207B229F20C621CD1F4B22B51F41217421191F741F671F3F20EE1E7F21C821A11FA71E3D1E041F56209320021F0D1E641FE81EFC1D011E231EFB1F461F481FD81EAD1E1C1EB91E9D1F531F7F205220DD1ED61E931F121E7F1EE41EE31FA41FCC1E6720851FDA1E6B1F80208A1F4E21C31E29206721DB22BA2CB52BD525EE21C02056210F2217217621E71F6821F02195208C1E4A203721A021D71F59215020CC1F8721B421531FE31F491E7F1F13205E1FDA1EFA1EBB1E691E431FB61EF31E491E9A1DCD20821FE81F9C21F31D641E671E491F5A1E9B1E291F4E20121E02205C1F0B21C4211D1FB120B51F9E205D21A9212F21A321B8237F2DB92950250523AF229620C520AA20B42002209D202A2258216B2071208720A420B2205420AA20DF1F4721A11F6520D620E01F9620C81F031E871FC91F601F9F1F711FD920D9205D1F421E1F1DFA1D661F7E1F751FCE1E2E1F5821B21FD91E5620D81FB81EDA1F791E901F531F721FC11DC3208820FE20AD21C5211822D524942E2F2B86247122C71FEF20C321B620F220BD2009218C20C4206F207320E41EA21F571F5A209321711F5E1F5420971FC31F5E1E572005206A1F851F711ED11F4E1FDB20F11F061F3A1EC91F6D1F5A1ED51DDB1ED420311F7A1D0721DE1E201EEE1D7620F71D351F7A1DE91F3E1E5421AD203721AA20EB1F5B206321802155237D2F2529FA25F920AC218A214D210A22962085206220C021611F9F1F25203821D01F5120B7226F2075226521371F9A208121C31E5320891FD11F371FB01E2620F81FA2202F204920FB1DC31F1D1FF81F651DBD1F961EB21EAA1E0E1E8C1DD31D6C1E1E1F42202521F91DAE1FCB1E731E2F1E3E1F8C1F2B1F471F2A228D2112249B2EBB2B0A24C8209E202720B720162070203C1F5C1F1520A6209120761E43215E210B213E1ECD206F21401FBA1E9B1E6D1F3120371F511DA01C2A21011F6020731EA21F9B20491F381F741E661E5C1D501ED91EAF1ECB1D6B1D361F921D501E9E1FB720781F5E1FB51F4C207B1D871D3820191FE01E731FE91E02202222ED226D2D362B1025D522E121242146219B1F58209E204A215922C9209F1F8B1F1D20F3200C20CF22C620C61FDF1F76214D20F01FCB202D1F491FBF1E191F64202520752144210122AD204E20A81E211E5E20BA1DD71F0020BB1F551F1A1EBA1FF11E341FFF1F6D1ECB1E0C1FBC20B11F921FEE22A31FF61FAD21B01E0821CE216B22BD2D702A4024702310216320D820C920E6207F217E21331F7A21CE2062210120AD209E215F1FE1210B20CC206820B7204620131F821F781D0E1EF91F5D1F3F2162206F213222C820732016204C1F921E361F72202220361EE01EC31E7E1F311E831E0C1FE81F701E531F68204A1FC11FE31E451E4A20371FE9208221592127239A2D232AF0234821E721EE21F6214A2177209921F32082218D21A7203D1FB6213920AF20192025203A1E701EF91E461F4A1F2F1E7F1E751D671E121F0E1E14208020531F7E206B20C91FBB1EB12148205C1ED7203620971F391E061FD91ECC1EF41F821FCA1F3921381FD520A9218B20BD1F6D1F561E691FE51FE61FC620F321C22DE82AF7235822D1219D21E31FFC20A72164207A1F8A209821BA1E4D204D22762099207D21F61F3B20BB1FD11FD72043211D20CB1E6D1D721F971F4A1EEC1FD91F312006213B1F481D5F20AC20C020C72062213A20612103204620361F9F1ED9203B1E301F931F011F2920B21E1E2017210821211FCA1F27205921AF20AA23A42E1B2BE92307211D21A021EE20F91F6820D2213A2042221E21A41F621FF41E7B1F961F711F1D205622651F5D1E6B201C205D1F5D1F161FC71EF11D781E391E5C1F321F2920CE1FED1FF91F581F0B213D1F671FD41F9A2059208D209D1E7E1ED21E70203D1E221ED31E791EBD1EDC1EBC21BB205B1F1920A020C122F2226122512D8A2A3B248B21A122562148216C211B210921B01F8820BB20941F7320D0207E207621271F5B2013200F207721051F1A206F1FDE1F3620531E04205E1F5120E61FD4203520E820FF1F501EB71F00209B1EA5203220121F2D1E47204F1E031E0C20861EC81E451FB01D1720081FF71F36217A212F1F501F431F3A21FF20C123032E622BB92452215B209A217121E9202B21D2200A203421E7202B1F43200520DD1FE1205421F31EB920BD1FF91EFE1E9E20811F2B1F6F20631E141F6A1F7120DD1E342030202721E81EA6200E1FA21FFF1FC91E3820161F7B1E391EB31F3F1E4D206A1E0E1EEF1E931E9F1FB31E881F15208B1F8D1FEA1F5E207F1F9721B022A42D702B2D26FE201E2268217221762164227B21672009222E2034233E20BB20DE20E61F032151209220FA1F02200F2085201420EB1ED01D791EB31E191F9C1F6F2020214621E620031F7A20321F27201B1F9F1F651F811EEE1E3C1E2A1FFB1D871EE820D21E341E7D1F7D1F621FD21F461F761FCF1E8B208B20802162213624032FB22BB3242C2375224C2111202A214221D0210B21B71F3C218A200322D120B321D9217B20502124208D208820FA1F951F081F0A20DE1D911DB91E991F981E8D200F216821CC21401FCE1F751F011ECA1E57205C1F6F1FB51E2C20F21DC41E4A208F1FF91E881D601FA41FEC1F44205F21CC1F1521F520EE1FEE200D236023FE2D0C2CEC24C91F1A20F61FFF21A020C020FA21482069227920DB20C31F80205821CA2106214521D122A61FB620101F69219D1E021FED1E081EAB1EAF1EE61E1420342094206C2020206D1FBA1FBC1E741FB720EE1F0F21FF1D941F231F721FA51F0D200020C11FD21FDF1FB320901F471FB71E551FF02029205A207922C6228D2EDF2AE92311214820B72123200D204621B8209122891FE320731FC21FF61EE31D721FDE1FEE2079209B1E101EB4202D20F41EF21EA21F4D1DE61E8A1F851F3E1FFC1E0620C21F0A1FAD1E781D6D1EB31E0E1F4F21EC1DAA1EB71F0420701EE11E551E6820E61FB31DFF20862199217D20F21E1F1E511F7E1F12222E229D22502DB72A3E2666216E227A22861F0620931F4822EF22C1230A22991F7720542266224C2076209220BB20DF1DE41E891E0B1E781E15203E1FB61EC61F691E0B20581FF221C220EA20C11D171E921F151F9E1DC31E6D201F1EEE1E0D20961F3920FA1E971F7A1F3A1EB31F0220621FF31FB31F0D1FB5205A1F1520E91FF3228F23A52D232B08248321AE203021CC20D721E220CA202922AD21A5205421BE2013211521DA201C1F0621671FBF1E3F1F971E761D031E9C1F7F1F27208A1F821F431EE31F1B2073214420C51E0C1DBA1FE31ECD1EEA1F7020A41F1C1F9B1FBF1FE21F151E371F2C1F1A1F3E1F99218E20B11FE71EE41FA71FD620781FD7200522E322032E6D2B5125BB215C22E020ED2109209420D32124214220CA20A62028216421D520971F2321CB1F991F4720C41E0F20A61E771E5E1FEB1F881D5C1F7B20F11E4C1F63214221761FD61D2F1FCD1D911E841EC7201721291EF31EC51F0D1F2F1F5E1EB61E611F3A20CC20D520A21E391E001E4A20A820FD1EAD1FC12106225B22772E5B2B7E240521F121C82262207821EB1F26221C21EF20CE20CD2189209B2170205B20872065202E2067228D20991F1621AA1F8D1F16217E1E711E751FB71F7A206D20E221401F7A1DDC1E491F021FB41DDA1F2A200A1FBE1FA31EB21FDA20121EC51FE81E6C1E991E401F8A20441FCF1F1C1F3B2085218E20B62095227722D92D48294E238E22EB2028220C21A920AE1F1A201F2086215F2238219D2052208F200622C820C11F92210920F61F4121B120C71ED91F6121821EB21E801FA31EB31F1F1F751FA520F21C4B1C21209A20B31D311E8221D820AF1E1620DC201F2092203B1F311F0F20CA216721CF1F421D3F1E881F8D20D3202320A32026229A220C2FF92AF52358224222E021FD1FDB1EFA1F3E2087218722C7206C20D91EC11FB32096211C229A20FA21BF209C1F3C1F5E21DB1F561F4F223620191F1920DC1ED31F141F1D20061F731C301CCE1E981E9E1E51208F201820281FD11FF01D931F241F9B211E1E312059210820E41E201F2A207720D520C921D6203822A921D522AB2D752B1125132294212B2275211A22312243229D21FF217F1F771EBE2186200F20D821C520DB20F920B821031E781FFB20C11F101F6720A41F771F6C1F681FFB2029200020901F941DBE1DDE1D5E1F821ECF209F1F85216D1F1C1FEF1FA61F241FFC1F15207E1F161FDC20381F9F20D9201E21501F2F22D62029214C22DF23492F782B3B24B421A4215421C1209B209B206E232D206C20B5205420732080217E209622BE20F01F8620951F69209D21471FF420C21F541EB11F0D1EB31E6720D31F4E208F205F20431D8A1D351EB41DEE1E2F204D1E321F2B1F941F6C1E2F1F59206320691F141FCD1EB31E2A1FA01F8F20991F461F1B215F1E2D2126226E245E2D712B6A242E2267213F2037209B20AB20B120AC20B0215A210D20DA1E961F3D2009202E212A20D21F5B21FA1F2A206221B61F851F231F181F8E1EAD1EF51F131FE71F9A1F5E1FA71EC71E0D1F731E141E9B1FD11FF81E1F1E18207C1EF41EE81E8F1F06209020C91F0820B51E3E1F2C20D020042078218A20CB21C5224524832E202B062637225123A922D8218E216321D6221321601FED1EA81F002122218B2009220B21341EAA21B01F99202321D31FDE1F0F20101E9F1E81200B1E1C1FAE1F841FB01EFD1EAD1E421E381FA91D941E4B2019208E1F521E1F1E1C1E871FC8204C1F2E203F201D20DF203820F61F35205E1F151F781F7A1F53200C213225842E992B3424852033222E20CC20BE206121372154207C203420F11ED81E1E2035208A206B20B52045200B213820BC201D21D020E11E271EB71D951ECC1EEA1D1F1FD91DAD1E6920441DCF1D521F7D1FE31E0F1F20209720A020D1203C1FEC1EF11EE220941F3F1E941E6B20681FED1FE71F501F1C1FC220431F372126235C249D2C0A2CE224CC2213218D239D1EED20D7215820B91FA21F711FA91E6F1E2F20E61F4D2030200C21DD1F9E1F8F1F2F1F05214C201F1FB31E6C1FC81E3B1E9D1E7F1E261ED21DCC1FF41EB11E5E1EF91ECE1EFB1F111FAC20C51E3B1F0A1FD91E1D1ED11E7F1F281EF31D5A1FFD1FD71FD01F5F1F1620C120DD217621FF1F9C23672DE12AA5249B21AC213621A821E31FD720442093203B20561F7E1ED61F6B1E021F6C1EDF207B20901F6E1DBA1E5520C820701ECF212820931D1D20FD1DB31D9C1FF81E351FD31ED51C921DC21DBC1F851E6D1F731F0B1F931E341F8B1E731E8D1F0C1E6C1D1A1E191E9A1ED31EBE1E8A1FB41F8C1F1A218D1F9F1EA3208623912D3D2B30253522792194227C226F1F501FF62155201220CE1F2A1E661E9720DD1F3F1F2721721FD51F4221DB2028211421FD1F1A1F0F1F0D20A71F491F551FA51E461E801F381F321D8D1EB11FE71E0B20181F28216C1F8720FE1DF81E8F1E831E6A1F681F1C20BF1FBD20FB1D2F1EA61F041FD81F05217B204E21162170221C2DF42A5D2449215721A1218F206520FC2019216821A21FC91FE51FA0209E20AC1F4F1F6C20A2201922E01F582016218520A321FF1F4F1F221F8E1F5B1C1B1F111E0A1F311FAB1F931EF21D511D6B1FB41EAB1EF51F901E471F8B20E41DA71FB11F021F061F621FBF1DBE207B1F6A1E11204C1F0A2031222A21B821EC23B3237B2DF42A9925582230213222DA21C921CD201821B41F4220011F1C1F7320F120512058207C22651FC6208820571F1320662190200E20AB1FC8205C203C1FC71F3720DD1F271E171F541FC01EAB1E2E20E51D361E451ED71F1220151EFD1F0520601FE61D801FC11D9C1EEE1FF61EBE1E2021C31F1E1F99217920C5210222D023202F252BD8246222CB216620CC21B31F2F20F8209C20731F6720CF1F7820392042201421E31F8720E420C31F371FA221EF20711FB51FC41FF91F971F9720BF1F801E8B1EC01D3A1F621EBC1E211F0F1E901D761EBE1F151F8E1E321F781EF71EDC1F3F1F19201D1F491E561F021FD01FCD1FB21EC21F872099201521C8215123882EB72B8E251422822142217A21A4200D1FE81F4420C2212720451F501F2720861E9F200C1E2E20B21FBE1E65207F208E228321A11FF91D541ED51F931D871EA81E981EF01D8A20B21EF61ED81D411EC61D291E1A20541EE01EED1F501F761ECF1F531F6D1D4E1EFD1D531F521F1E1F5E2064205F1F091FBD1F9121132270248B2DB72A8D25FA21C4217022E420891E941F9F217720F41FBF20671FB11FF71FEC20A61EC31ED51F6221FE20D81FED1F291F99200821C71F591F151E441D551EE71E442004201D20BC1FA71EBB1E351EE81E471E5B20A31F711DDC1EF21FCC1EF21DE81C0920191E281F061F6D1FD61E3A1FC61F9C2031203A1F4F21C9200F23292EE22AFE242B22702000219A20632226213221671F672029203B20231FA31F7A1E3020C5223A208821C11E30207C1FB920AC1D411DDC1EF31D091EF81E271F821EC11D3D1F2D1F881E591E0E1D801D791E4820EB1E321F751FDF1F9820881E801F4C1F9520D51E3E1E2F1FB01EE31E2B203E1F701ECE1F8A1F7C22A91F0123422CCE2BA026C023FD21CF2021220D225521B522A721E0210E2005205B204E20A91F3C1F2A206A20B422FC1FB91FE420FE20EE1D1E208E1E6C1FB620091E951F2E1F3A209421801FC41EF01DC41EAC1E751EBE1FC8207B1F9A1E1A1F691F631F261F851F7A207520111EA0216A20921F981FA41F1D20C11FEC1E3A20D521FA22B82D082BE7246724A121072245211921E21F6B22F422C22122219C21F51FB920F41FE51F5421391FD0215821901F03209C1E331F3C1D8F1FD51EFE1F121E761FFC1E9F1F0F219E207B1FD21F641E521F361FE71EEB1E4D1D741EAF20291F4F1ED71E761EFF1EE01E1C1F581EB41E7E20D51F241E851E2B21B81F0F21C020C722D42FE02C28243F223721D521F520A7208F22C52164209F215D20CD1EE21EA21F3D220D205721C1210222E91EBA1F59201E20D81E571F911E131F601EA61EB11F8E1FCE2171212D20371F8B1E401F901E1B1E761F051F0A1EAD1FE21E6D1F301DD31DA81FBB1D841DC41EB420E61E5F200E22A71F7D1EDD207C1FA1214A226523152E482B76257921932236239F22682173211D20F420132278223B1EBD1F371FFD1FB61F741F75203020D021991F062125204E1E6A1F1D22811E701F1F205D1F8A1FCE20A720BF1F52204B1FA11EC91EA71D141F19200E1F1D1FC01E0620701E141F5B20B41E9A1E181F5820A31FBD1EA71F591FBB1F6922A120EC21A822CB223C2C442C5225462342229120C222E520B621B52239217F218D215E21AE1E5E1F651EA120AA1FC31F3522EC206921D41FCE20371FC01F151FBB20781F56202021E71F58200422AF1FC7200320591FA71DB61DB41E6D1F871DFD1D6B1E691F8C1FA41FA81F821F8F20D91DB320D21F301E7A20D31F132043234A21AC20B7220524612DAE2A38259322D8221C214321A720CE22D0205F1F0122E11EB520C21FA7206E1F13213320DD1F86208922E91E242136206B21F420E21FA51EDD1E5F1F2221CF201E20692206215F21F420851F6F210D1F031F9320181FC31EF31FF91FB61FD41F9C1F9F1F4220E01FDF1FDA1F4521DD20371E931D56213720D3218221CF23CE2D422BEE22FA214F21022227201922FD20602175218220E71EB31F301F7120591F7B204920ED1FBF20D31F721F581EAE1F11205B1F3320861FA31E111E071F8A1FBC207F214C217720DE1F8C1E9020241FC62098208320701E5420211FA81F3E20DA1FA5204521341E6B205D1F2D20C91FFF1EC5201C227321BB238D211723DF2DFB298A23A821102167200E21F820B421EC215F215521FC1FF7209E1E6420941FCE1FE01F802034211C20101F3720BD1F401F301F1621FE1E1C20951E5F20901FEE1E9F20DA21A71F1720C020FA1FD11EF61FB61FE51E761F061F361FCD1F101FA11F26206A20F31EA320CC1F8F20C720B71EA4216F200720A1211721C7234F306B2A86231B23C821F92130226C21C4211822272170218A1F04205A203821901F612094211121C3213420EC1F511FAA20CF221F21391FC41D501F3520921F352119200D22992085205E2033211C211C203420CD1ECE20771F0120C91FC91E0820D51FDB1E8B20321ECC208F1F7C1D9920C120AA20E31FD51F5F204F221225D92FEA2A342453224922182226224F20FE2118226521EC21F7209820A41E211FBE21AA21D521EF2158215B208E21962092217120B31F7820CF1F2A1E5821551F021FCE1F6C2103215220022039208E1F011F681FF91FD21E8620E81EF41E622004207B20CE1E671EB31EB220FD1F961F961F5620B920BA20BF1E9520AC220925922F9729A8240E21A2217B204C20262186210C2336218C213120BE2076201C21EF1EC520A6202C21A3211420CA210E212520BC1F1B20B41E9F1E6C1E931FC61FE31FA920CB20BD216B21871F741F2B21EE1E861F231F0E205B20F920061F851E941E3C20E720E2207F1EFF1F841F9F1F8F200A20472071210E21AC21A620E423F32D462AE6241924F01F69202B210E2132202C2118225222A721C220B121FF1F6821372075209C1FE221641F41210220D9208A1F2421BE1E2D1E2E1FD41F9A1FF11F6F1FD01FAC22941FCD1F0D202521091F92202B20DE1E0E1F0C22A81E741EAB1F0C21C920AB1FFC1F4420B320B720CE208220391EFB235320EC20C0232C24182EC22B7F230622A7203D2159231521D720FA212F2179226C224F21F0200D21431ECF209321C220F81E76219E20CA1ECF20A320E21E7D20D41E641E001EA71FCF1F4220E420BD1FDD1FCB1E511F1821081FC91FB820F91F511F5B20B520491E06204E1F9720C81E0E1F4421451F6A1FCF22A31FD81E5621B620382047230922BD2E342B9125B820EF20DD21D020F221F5201E223D2205213B214B208220DD20642149205321252181213420DC212C213B1F7C1EA220951F421FB61F3A1F051FD91F1B217E20151FB81DF11E8F20471F35209921A71F8F20551FE520A61EDB1E481F87200E1EC01E35218921DB1FEC1EB320BD2001226D205120DE1F4022B523762E2B2A4D25F021AA22C0209E21A622B721BA215C1F77231020022289202821A81F8921FE2031220521AF1FED1FBF1EFA201A20D71FCC20EE1EC91F901F2E1E4B1F7720A220541EEA1DD120E51FCA1E511E2E1F9F1F2E208E1F791F791FE71F101FE420CB1F881F441FCB1F9B1F981EED206B1F4820BB2017211721592188234D2E5E28D62359210E21FC229521B91F0622E51F39209C203C1F44217F20BD21A2207C205A21B420B221E21F7E1F1B21C120EF1E1F1FF31EBA1F4C1FEF1EFF1EAF1FC61F212093206A1FF41D1E1F171F101DF71C4120531DA91E8A216221D4205220891E6B1E7F1F201E47218B1FC11FA3207920422084203E206720CE220124422EE3297B24C122B122E2218B211E20E2219720DC1F2B21C5217A1FBB201A211F218C210521BA1F0C22C0201E1F7C21FD1E231F8820C9207E1EEE1E0020F91EA21EB620EE1FDF1E431ED61E721F9A1FE51FE41FEC1FC91FB11FF71E991ED21FB41D6C1E3C206E1F1D1F351F661EA621492024209C20B221801F861FBD21BF23C52CB82A512462229520B7215B222221E6203C218F206820061F4B2013212E217D211520E720E21FFF21C021CA204B207921C91EDE1ED31E041F68208A1F7D1F301E9C203520691E7F1F5F1F4E1EC71F6A1E3A20D9215C1F9220781F841D591FA91E641FFB1F641EDB1F041F3521CE1FF51FBB20BF20CE1F522019228E21AA22282D8F297824CB216C203E2106221A218E1F912177201C2163215320D92088224F1FA41FC721A41F4F1F2120711F502066201220921F0420C41EE51F0220C01FAF200E21FA209220441DAA1E40202820CC1EF6207620BA1FD11D5820821F6B1E6E202520CF1FB920E91FAF203B1FF21FCB212B207821C9207120B420B5219323D32C6229DD231F2251215120C92013200C21A5217721FD1F7322881FD71F3422C92021224020B31FB41F8F1F2A20A91FEC1FBE1F651FF21FB91EA420091FFB1E921E8C204E1FD81F3E1D721E4D1F0920581FCF209A1F5A1FDD1D7220941EAA1F7220D21F611F2520501F6C207E1F0C219B201220CB20BB1F5E2039200D2106231B2ECB2CEF2418220F225B214921791FC320492246212F20E121A41F5520E120592220212A21F2208A1F4620541F8220E8200220CF1F9A1F3E20FC1F711F241D031ED520B31FC820921DCA1EFD1F781F4D1FA91F7820381D641EDE1DBC20BB1E271FCE20B71EDE2017202C1F201FDA1E1820362183207820BE201C22AA203523972F382B252423213E206621E1202D211A21E82059208E2157201F205E1EC21FD0213A208A1F48216021131F461EAA1DB71FF11EF01F511F141FC51FF81D771E7F20B11F1D20F81F2B1EFF1F1D1FA01EA31E9720531F3920491F0320541E5E1F791FAD1FAB1FFA1EB61F761F611FC01F7B21D11FEA1F15214520CB2144217523E62FC22A19252E20C821E9208B2093225A211121FE20B920E120871FAA20872036215F218D21CE20F720901EE121CB1F7E20AA1E84201F1FF51EC31FBE1F061EED1E141FDE1EE71F651E641E6720A91FE81E2A1FA220272113201E20FB1F432000219C211520062013202521761FE51F4020E920CA201E218E1F0D21A6214C23D62FD12B65254B231923D821E1209B21542032229121EF223E21E220482229208A22862265210A21C321A81F711FF0215022FA1F0E1F151EBC1E8C1F931D661F9C1F692046208F21681F6A1F8C20E41F371F5B1F0E209B1F471EEE20C320B31F0F218D20F82187200B1F74222620D1217020201FC621E622ED208E211222A5235D2EB82A942488229C212B233B21D01E28200722A62147226920F4207F213322C621CF210B228B215B22791F2A207E204A21F91D761F291F741E9220C71EA41F951F0C209D2006202720C11E201FD820671F7E1EF920191F2A1FD4206021E0217220B320B41FAF205A1F84216121392015213A1F622122238520D420AA22B7249A2E1C2AE32440214821E62115215C20A02152216D21FC209620CF1F3720EF1F92217B20B921D320CC1F5D207121EE20681F751F011FBD1F511FE41ECD1FE51D631FAC21D81F6621EF1E3B1E811EC81F5C1F8820C820771F40210521971F30221A22BB21A4202D20742152215220C71FC32158206B204E20E121DC2081214524B22DB82A12248F21F9208420C2204A201320C82100215621FC1F9E1F012183210A221922FE206D216A204120B520E81FDE1F411F7220DC20BD1E221F341E7120ED1FE520D320DD1F281ED21D531F131F591D2B20FF1F0720D520E5202C218020FD20CA1FFB206D220A1F6C217621211FB320DA1EA9207520081EBB1FF4210823CD2DAE29A12499217621C220B7204421B6202720CC2166205120F1205821E4200921232149202621F220AC20EC213121EB2076205522AC20C11DE01F3E1F5B1FF11DF71EDA207721631F671EC720691EED1F1D215921A21E02206D213421DB2027215A210B22F31EAF2043205B212520AB200C2067213C2125213F224F227623792DBA2A3F247A23FD208A21E32164208022A5229B2149219B209B215421EC1FA122DA1EF320C5215A216020591FA32096206F1E8C1FDA1EC01F17206D1FC220AF1FE31F4820CE1F611F601F551EEB1EA71F8A207821CC1F8C202E21F3213C229E20DE1FF11F761F07207A216D205521A2217E20402080202E21A9213422C223732EB12A90244C21522140213420EF20DD2110210F228221D3202F1FE21F5C1FFE224721D820452041201D1FA71EAF1E6D218A1D901F391F071FE91F8B207E1E441FEA1F53201D203C20861F681F3320791FC51FBB20D31FD02055212820E3207020011F0420D91EEB1F6420E31E0421B71FB51EC72014210D1F88205B204C24F92DD32BCE2554218D20EE20C3200E206521C621C521D821D1215521C01FBF20E22018208420182195207B20A81F5A207C1FDB20FB1F591F4A209021211F021ED41FF01FF81E1F1EB41DF31E101F2620C51ED2205D20C620581FF820DE20A11FF2204B20941F851FA4202B2002202120FA20D420A320EC21F420FB21FE21DC24612F0C2B3E25D0220E21B9204321A91F4E20872001212E212821DE2000207D212A201E2152218F21F220EE200A1FD31EB91FC31D1D1FB61FD51EB221AA1F3020321E8E1FF21E1A207A1EF71D5B1E3220041F0420B5201D207B1D3A231E207B200A206A1F6A21A620331F23209A1F4A206420771FF520B120182185209821DD23252EBC2AF624D520B321CA21DA217921C51FE3202C21FF20CD203E20392085203E21E81F8C219820E6213322931F7E1F501F2D1FDB1F7E20761FB221D020DA211A216A20D020C920F01FF91E141E9920B21EE41E6120991E34205B2018215A205B2087201E2187207A20422051208020F820E01F7B21E120D51F0B215122C223EA2D212C5223352213221521BC20D61F83215F200D228821F4202B202A218021AB20E520F81F3321A6216721B21F4B20CB1F5D215A1F1D20091F8B21971F7E1FF71FB0204221421F6020531F1820F91F72200F22861F2B20DA1E7721A5200220E220BB1FDD20631F862037212A1FCB20DF20D7216E2170203B1F23225A21D5236A2F2C2ABA2456216320DC1F0A20A51F33200521F41F5921E41DBC209820D51F7F21CA208120531F5D216A1F041F7120E31F1020C91F941FC720C61F601F6D206320BB1F4820BD1F4E1EA41FFB1EF41F051FD41F5520FB1F101EA622502052222722C1208B20281FC31ECC204F2049207220502063201D23E41FC91F2D202023C02CAB2B16253622B1201621CF21DE1F6722FF20F620D720DA1F671F2C20072032225D21B01F6B20CF1FAB1FDF1F991FF11F2E1FCF20921FF3204D20F71FE01FC71F44201B2090204E1EE21C0F2098218E1EB51FAD210D217B205C20551FBA1F4420BF219E2076206921FF204A20512026224B206D1F5120C91F2420A022D123442D9D29EC24942081212521D62114218A219520082123228E20FA20491F16215D20142137207C21CD2075206C1FF820D420D31F1021201E4D1F351FD91EF81F751F721EA81FA81FC31FE31F5820761F471F48208220351E501FEA1FA31F692036205321391FE3208F20E01FC6207C1F18208721CC205A204A1F5E215322D322BD2EBB2B9F24B5217421DC22DB21DF21E5205622842117225222A7212021E5206121F71FC8219F20802120214F1FF1217A1F7A1FFC1FAD1FDB1FB71F5520081F121F1220E020AE2052200E201F215422541EFB2002205720812190214B1FA020841FA621762039211F209C21572150208B205221642158213421A8215A22A2249B2E9D2B8D257C21A920E4206B217521AB2134221A223C217220522008212B207E205A20781F6F206520D81FF51EDE1F191F041F9220C61D9C1ED91F1620831EAC1E9A1F8D1F0F20DF1EE11EF71E751FA31FE81F80201B22F01EEB1F8E20041FA6201421B2208D21FF1D811F2A2125204821CF1F11216F21C51F7E21F8210023652E3B2C40264A2153207721C8200D20AE217522CA205222BE214E20811FA221E420E81E2121D420AC1FEE1F3320AF20AF20861E49201520221FA521911F091FDA1F67206B208D207B1DE21EFC1D7E1E181E9920D520AE2013200B21071FDB203B2108208E2099200E211B22851EF11FD92024211B20F91F32211622B420DC23172FE52C94257D226321F62138225522DE213420BD20941F1B21C61EDC1F5A216C209D203821131F2C20201F331E181E151F5E1F241F0E1F2F20CB1F54217A20E11E99203620561EDF1C381FBB1EA11ED01F851F4422D11F8A1EE91EF71ED5215621B920821F8E1ED41F242289200821BD2195210D21F020AC2091202F20F221512D802BF02449246F214D2125203D214922B120E11F01219B20281F741E9E1E5220BC201D2119209E20F51F1B1FCB1F3121051FA01F7A1E161F1A207D1EF21F6D20A01F751EC81E171FAF1F941E471E57204B1E0622481F571D021F761E481EDF1D341E6D1FC021D41FF621B421A421C42048205120D91F4520BD21A022A024D32E032CF024E8215222D920D720532196217D217B20631F9220E91F941FC01F872097209720652150209720851FB8206320F11FCC1E2D1FE21E721F371F961FC7200F1F6F1F7E1F581C4D1FAB1DBF1EA21E221E8A1F801E581F531F691F56206E1E011F6C20572117215D2098208520DF20581FB520C620BD1FB820012248238E2C9B2B6A23E22061206421342046224B200821502197203920691F101FFA20DF20E71FE5201E20FD203E1FDE1F2A220B211D1FBB1E9E1E4620621FF91D791FEA1FF11F6D1ED11E8B1DF41EB41D741FBA1E9C1E861FA51EA31EBE20591EFC1FA41F4620D91F8B20FC1E96210520F41FF221E720FB1F7A20D020B71FFA21FB23F52BD62AA4257A222E2283225B20BE222F20BE22A521921ECB1F76217B201420F22046206B21F020C320B4202F20AE20DD21EE1FA11F06200620F81E9C1FD31E521FC21F0E20721F731D67203220121FAE1DF2204321F31FAD1F901FFA1E3421D82051217B20601F7B21F420941F0820182092206721C121E91F372069204722262D712A852510224720F720A71FB81F4320981F03202520E71F6020DB1F83201F211E20D21E4C21811F1B21352054213D20731E3A20EC1C331EBA1F8B20551E7B1FEB1F6F21E91F8F1C691E481D7120581D931E5220611FEF1DB51FD32079206720321FDF20DA21831F482222210821AE209A216B1E1E20AD20DE2085224022962DA52AD425C0214421BB208E1F2B1F21214B2137207821CE21991FEC1F4121A620B01E29203020F220C51F131FF01EF71E1B2066219D1F311FDB20E9202D2037208F1FA91F7E1F131D6F1D2E1FC11FCF1E3C1FDE1FB51E3620411F051E1120BE1FEF21781F7E2037204C200C203921D620DF1D0420E71F761FC5209C21EE23042D6A2A1126C2213422F421B521B21F34223C22F72008203121F720A820F0208A213720D120B3203523FC202320D221EB20C3210D2192208820AC1F8C205920B020781F89205C20BD1F1B1F871F5B20E120C01FB41F651FCA1FA41F0E1FED1E0B1FC0217B20B51F622129219B2061204A208A20E2208220DC1F302083210A23712C3E2ED7244F21832166220A2010213E206F20B0236A21F220981FBE1E04212A20C31F1F22FF1F8621E620792051203721C820F920AA20BD1D601FA41F541FB7201420E61E171EF91E6F1EC81FB81F481FA71FC2201F20461F0A214A1F7A1F1D20BB20DE1FB41FCD1E6721842031202321CF1FC020D91F501EEA20CE202522BE2D252C76265821B721DB1F3E215320F6208D216D218D22CC20E51F5721BE20442127205121DE20C6203B1FA120B7216B207520A421B71FB61EF3207D1EBB207A1EEF1FAC1E731EB81FB71D581E4B1F8A1E161F7B1F001F571F2C20C01F411F80208221DD20FD1FF11FD120FB20192138210D1F7B203B1F591FB0208C21CB23012E682B3724F4216C1F5A2020213F2011212520F920B220E1207B205A209A21132231209021ED2029210521352037205B2092224420541FD21E861E671FB71F331F6A1F461FB41FC91DBB1C7F1F431E791D111F2121361F72203B20FC1F9B218A21DD1FE91F7A200521AD1F1620611FC4204B201F1F8D1FB21F9D1EFE21C924AA2DAF2A0D25B32190220C239F22D520E822F621D321F81FAD20B020C81F9121B5215920F71F10210C21EE1ED31F5E1FCA20221FC920EE1E5B1EC21FEC1FE31E2A2162204F1E8B1F791E811D281F19204F1EA720FC218A1F341E951F431F3B200D208421BB20851FAB1F871F35201820712034217B20D620CF1FA0209F203222402E582B9724D4217B238F21652328227E212D22FE223B22C620CE20D81E4E202C206A204B20DA20CF202221601F50213A20FC1FD520061F702005205C202E1F8F1EA120DE20981EF81EA91EBF1F661EB41D0321201F5C20B11E3220DA216E20F41FBC211821FE21D71FF2207420DB2072220B2128214220371F85212D226D22D62DC92B84266723CF2050210220F8229B215520F8205D21AB206E1E7920E620842157200820C620AC21A9204821D720CE20FC20A5204E203B21E120461FAA1F9A1F3E217E21C61F711EDA1E2520AF1FE01E9520F420B11FE81EF51FE1209720871E2521F720F71E151F5D205921FB1EBC209C1FB9202920411F1D2157214F23892E332BDE257D23D222AE219421AB216C20FD20EC21E8204821F7202420F51F3720921F922166213820841F6D1FEC239521AD1E611EF41F881FA9206D20BD1F3821902029202020281E1F1E721E741FD41E5B213820CA1F86205A204D1F7120FF1F7F20A420E820BE1D7D1F2E1FD01F9B21B91F04218320C81F8B20B821A523A12CD12CD926AE23262324220A212022D7200823912206211C227A207B2152217B20F31F851FB421C620EB2188206D209D1F5920CE20831F4F219C20EE1FFA1F4221622053218F20F11DB41F691F481F0420BC21B5219C1F6D1F371F881F321F8F213D21CA20E720A020A41F281F8A1F1721C320CE1F741FF31F97204221EE22012E9C2B78253C2224239421FB2071212F229F21E61F512050200420C2216321491F8F1F5D1F2A234E21F21FAE1F0D20F820E71F851F99208620EE1D2C1E6E1EF21E45208C20C51E391E2B1EA9203F1FED1F0D218B20BC1F8E1FCE1FF81F392290214520B61F122101209021F01F5920A41F681F6720612022200B1FD4212723782E4D2A6025ED21C71FA4211B229D2121224A224922332160213C20FE1F2321A4210E2057215722B521DC20E12296204E2265217D2032205320551FE51EDF1EAC1F4C210720731FCF1E751D2D1ED91FC91FAE2152216B20C01F3020F51E30211B21D4201021FE1FB61FD020792245200220A31F92210E20B21E3921BD209224FC2EDA2AB8245E2130210721EA2023228E204B214B210A200A2063206C205B213F210B20B51FEE210122C9210D22C220C71FAE1F29219120B81F3520C71E2B1F4D1E751F541F081FDC1D0E1E801FFC1FD01EEE1F8C21C52000201F210C1F491F9A1F1321AF1FD31F271E5120B020FE1F5220E51FAB21772185209320E5214D24082E1B2C4B259E22B020A0221A2182203D226720B52143205E20EE1FE51E071FE81F582092217122BA22902063208E229C201B1F8D1FAF1FCD1ECE1FB61E091F6920A71FF11E771E531D431E4F1E951ECE1D251FF920421F421F351F6B1FF81FCE1EB21F951FEB1F2F1FF61FDB1F812125212E205A20C020071F8C207E229322D42DDB2BE023B522B420A120E620B020ED20B021C9210A20A01FD41E06202720461F9620C321932092215020A0204D21441F6220951FEC1ED31E201F1D20251F9E1F27205F1EEF1D491E0E1E4E1EE51D7E1E9C1F2321C91F191F4D20BE1FEF1E841F171F4E20CF209B1FB7200F20032063200B2134204C2007219D20F721C823EB2ECF2B27251A234D21BC2143206420DA1F0422E820B320D8208A1F9B1E521FD5203E20D720BE1F722122201920F621B120CC1F1E218D1E571E1420C41E631EA91DD01F2D1F0F1DDD1C451D841ECB1C5B1EDC20B71F2B20EF1E07214C202C1FE81F011F5F1F40205F1FE0208720B01E4A20F720FA1E472104201A2192225124DB2DDB29BD24EB20DB21BD21611FD21F95203921C9204921AE1F791F141EC21DFD1FD91F65208720C420B51F1B2018202D21F11F0D1FF01E571FBA20A4208F1F7E1F5520F41F701EB31DB31D391F8B1E3B1DF0200A224C20231FBF20B11FA71E5B201B21F31F4520731F7E20321FB41F2420E2205A208920232005217521D823152E742AF9231522CB1F8621082072212B229F20472132218E20B21FF21E7E20CD1F2820EC1F8023E120DC20D01E3E20DB20D71F011FE11F0420C11FA81EE01D231F2C20A11EE11DE21E8D1EE61D3E20F11EC222AB204320E31F32203420721FB61F611FDB1FFC2151205821C01F5B201B1F0A2117226A23C9215721432183247A2EC92A36237D20C621B820C820351F6322F120B921BA216B223220CC1F1D22C3210B20F9209C1F9722FA20191E2621BA2141212920E71E321F06203A1FC11D0A1FEA1DD61D661E3C1D161DC41E781E851E0D21811FD41F3C211E21631F3D1E79200A20EA2092203D20C620431FA01FA321451FAF20A821911FAE200F226F257F2EE12A55239A2257207B21AE2014209521F520CD1FC8208720D8218020492104229E1F8E202C20FC218321DD1F1C20FF1E2B1E2620711FFC1E041F0C1FD31D1F1D1C1E4820C61DCB1D7E1ED51BCE1F1E1F96210720681FEA1E7F1FF11EC31EDE1F6C208C1F2521961FC6211E1F1C201C1F7020FB1F1E2242205521A422E523FD2EBD29A72481215221271F83213B20861F95203E21C3211C202A202D214920A31F0F21BC2076216621C51F65208320FA1FD81FBF20741F211F081F681DBF1DDE1D321DE61DC01D661DF41DF41DDB1DF91E6B210822B7200020D81F452031202D21C6217C1FA71E471FEF21C920DE208620AE208A204A216820BE20F6216B24B02F1E2BF2230F22ED1FCF1FE91F6B20DD21F920791FF7203920CF207F1E321FB21FAA1EC1215C2157213F202721D21F0A2291201220AB1EF61FC91F1B1F871F031FDD1E911E981D611E241DA11FC91F6420691F6B214820BD1F7B20F11EFE1FF520C020B81FDD1FD220AE21E11F20207520ED20C51F47208D20972147229F22472EB729FE233C21CA20572036205F20D01F6021881FE11FF21F9F1FD71FC220EE21B82057204A204820152013206021A61F111FF91EEB1E771E491F281F631E471E521EDF1BB71C001C181F8C1D1320A81F22206F212A1FE11E911F1D216E1F0D21A720921F4E1FF81F801FAB1E35214D218A200F20F51F032015215521DF23F82ED82A5E24F0221E20661F7B2073206720CF208720C220D5211120281F3721242170204420F820701F6820411FB81F022175204D204F21E41E491FC71E091DE11D041D021CBB1D511DC61E921E081F671E591F931FFE1E0D2165220B20F61FF920DB21EA21071F6B203D213A208E1F35206B1F6F1F5420CB1F3D21F1217824B52EF92B6F235821F3209720C8208020CA1FAA20A320AF1F87203F20451FC21FE51FFB1FF71FD620C61F0520D91FA41F9D20301F531F361EF21FAE1E861EA81E701D641D211CA51DE91E811FA81F071FD31F85201F1F3F1FB3210B20B51FF51F4E1F9321822107210B2042205A20C51F24214F1FD21F202173211A210B235F23BA2E87292B248022E02065203121B41E05209F204C202C21ED1FC6216C20A5214320D320D0205221DE1F8A208A1FB520D922CC1E4C208F1F6320241EC11E1D1F611D031D281D6B1D471DF81ED11FC11FBA1E4321B91FD51F431F05204B1EE41EC3216020E12028209E1F0121D6201E209D205420BF1E9820921DA91F2F215D23D22EE1295D25612097217D20D520F020881F2522091F441FC9209721FE208920D8214B1F2320B620AF21731F741F2E20BD21FF1F611F381E281F861FB71E1A1D041DF51C3F1E2A1D671DD81D081F2A20121FB21FB820C31F6E1FAC20761EFC1EBE1E8821F81DAA1E581E7A1F331E66202C1FB721DD1E4B1F17211A20AE210F23192DF12969255F2176204520B720532048207C21642025203F207A211A1E051FA21F9B205D21F01EAD21DC1F321FFE1F1A1F321FEF1DCE1EA11CFE1FD11DFB1F701EDB1E2E1E2C1E5E1DA31FE01E361F8F1FD41ED21EA41F931E9320D31D9A1F1C1FD02007206D20DD1F8221D31E63201C20C91F2A1FFE1FD220C0228B22B423D62ECF2B64250F212C214A21C721D61FE7206E210B21EA21BB20E81FD420ED2096200B21D0216520D921B01F271FED1F4621AD1FCB1F771EB41D7C1E601F1D1EA51F811FDE1C441D7C1E7C1DFA1FBA200D1EB720F720711F3E1D3620D61F8521C01F4C20E81F9D203B1F2721DB20DC1F0F1F2121BE1F1721651F332184207E227E2DE22A99240E22CC2019208520E5210021C522B221AB205D21ED215720E021A8200321B4203921CB20B31FE7205B210621D01FBE20B61FA31E4B1F9C20AF1F8C1E201F691EDD1E491E3E1F2420ED1FCB1EB91E0420AB1F3A1E0D1FA91FC420DB1F931FCA2063200E1F9D1F5D1FC31EE1205520DB202C21A11F61210D22B423722F7B29BE22CC21EB202E206621201FCC204222382178218720A02012206420012142205820D3216721221F491F31204420B51E7E208C1E51206D1FB11DE71F941E331EDC1E0020591D2A1FCA1F251E2C1F591F1820F41E0920B520E41F761F6D1E841FB01FC11F6F1E9F1F1E1F8C1F1320411FD61F76218E1F712158216723742D802AB4235D202D22C42104219C207521972201200920C7212020DF208620FC20CC21E420EF209E218E2014208D21EB20B31EF81FB61FFB1E041FC51FD91FCD1F7D1FF71ED81D8A1E141F041FC41F0020E5206820CA1F111F8A1F2A207E1E0D20CE1F3C1FDB1E571FC21F6F2010207F22E31F522089203320F5207E213924682ED8297924F8227520F42010210020C81FED209821C720AB21A21FAA204B205E22A920DB20FE219520402111206121741F811FED20DF1E4620E61F051E561EA11D9F1E401D371E5C1E381E2F1E6F1F2E1F08204A1F201E1120BA1F321F551F9F1FF620B21FEF1F9820CA20781FA11F701F36201821F31EB020332124212622F42EDE2BAF251222A520E1209520D120702025214321A81FE31F351F8A1F56216B1EA91F712069203321E62106216620BA1E1320F7200B21CD1E4B20581E691FD71F521F5E1E291FB41C891E9E1E341E1B1E1720D620B71FAF1F5B1F6F1F271F5C20572081201C2019206521A71F35201922A91FCC1F7F20FA1EA21FEE228823D32F3E2AA924C521D020CB205D21B01F2022C21E23204C206120B21F26212E21271F261FF020C01F12214D1F641F3720CF1FFF1E4F209920741FCF20DF1F6820C71F931FC91E681E621D611E4F1FF51EB81EC91ECD2020202C1FFC1E6E1F871FE51F8620E81FF61F301F1921881EC71F7C1F4C1FBC205E20A21FB421D921D823612E672AA1223021AF22E32025214920FA20E520A91FEF215F21A41F1621FE20DE201722BE1F6F211E22D320481FD7203321B01EF51F731F6D1F4A1F511EB41F411E3F1FA81F9520101E611E121FBA1F901E851FA021311F0B1FE6203020F320E320D51E9D1E631FD01FB420C020E61E5320A320261FD51F911F9E1F1521D322672CFC2A6D253E217E21E2220C200421B1205321EA21A8208D20D8218A209D1FF3214422F52166215C222E20A21E68201021991F051F30208520401E8E1F641F071E8A203720251F6D1D7920F51E4C203F1E88206D200C1F221FE520AB1ED121B21E4F2103210B20DB1E2D20D31F1D207621F31F381E861FB41F3C21A121ED23202F862CCB249F21282102234A204C2141200721DA20F920E41F1E20E31E881FF81F6120C1204C1F8720E81F651FAB1F82210B21821FF91E8A1CF31E331E1620051F301F831E8F1FF31E301DF8205A1FEE1D021FC41FAD1DA31F861F6E1F9B1EBB1F8B1F091FEC1FA31D2420E31FC31F421F8E1F831F3B201F2060201C21BE22052FDD2AB02428233D203620302026218F201022AB2084200B20B71F49219621AF1ECA206B20A420AA21C220AE1F7820A11FAF1F5220EE1D081D1E20A31F2C1F841E5D1FCC20B71EC61DEF1C0D1F891D7A1E791FA91F1E1D251E4A209D1F1D20CC1D4A206E20AA1F28208B1F651F371F13211E1F471FC81FC21D3F21FE214024532DA92AAF24B722512191203A214622E321972148201F219E214120DF1F84202C21A320DD1FC51FE0202521DD1FB61F0220C1205B1F0D1E0B1F861D3120F21E941FFC1E081FEE1F151E561D991E7B1E411E721F5A1E7D1E551E431F5420051F4C212F20431E491FD7200B211F1E181FD41F3D1F541E3A207D1F5521B422D723172D8A2A6A251B21A120FB1FAD1FED2102214521E71F0D21C01E991F411FFC1FDD1FBD20AD1FA92080204121581EAD1F91227F21AD206D1F501E5C1FE11EFE2023214C1F351FEA1F251D1D1EBC1E601E141F461F3420C320CB1EB1208E1F7F1FC71E0521DD1FB21E661E6D203F20D21EC01F5A1E7F1F36212D20BC20BE22AE24E02DEC2A86247C22BA20522103211D2050200821C81FF5200A1F1D20FC1E6F1F271F701EAB1F561F981F0D1EA81DD41E6B204820C41EB51D251F0D1F181FB91FE61F3E20E61F6A1E9B1D2C1F351FE41EAA1E411E2F1FD41D871F0220311F001E2E1F631F9B1E211F0C205D20541EFB1DC21E6720FC1D8C1FAE1E831FB81F9723BE2E882A8123DB21BF212921292048209C219B1FA8206020F41FB21F421EE51E2420E51F0A1F05210D210320361E90200F20B01F2B206E207D1F8B1FE91E3C1E55206620EE20061E881E841DBE1F551EAC1EBC1E3A1FE91DDD1E9920631E4E1F831F801FB21F671F651FC01F5520EB20B61D421E6D1F1F20F32086210523F423252D902A1524522034213421D31FD720BC20C120F7215221201F961F6A1F282079206B20CE203520D51F69202F1E9E20F21DD21F4720D11DCB1F511EF51ECB1EB820DC1FF11E811F891F571D9D1F661FB61E3D216D20E41CA41FBB1E0E1F421F13218B1F741D78208C1F351E491F661EBA1EED1EFC1F6020CE1EB321E021AA249D2DB12AD0244F219D2057212B21CC208E2221216120D11FA11F711E611E18209621D71F53207A225E1FA220D91F9F20731FA620A121691F801F021F761FA11E0320A71F681D471F3D1EE21D7A1F6A1F221EA120801F631F231E6920EA1EFC21A01F461F4A1F5E1F691E8120891D771F0E20C020F01EED1F7B207821F01FAF22682ED82BDC2411215322B321B121F520FC227C22FF20DC2078212E20981E9A20D620DD21A1215421191F2821EE1FBF200120A71F8321A21FD61D3C1F9B21481FD31E601E181DFC1E291E861D571F081F591EB520EC20DC1E861FB41EE51D921E061F8E20F21EFF1EC21DB71F351F981F8D204F20DA20681F081F3C2030234F244C30EA2BD524D122E21F9F22D8224A21D2217F22EB2113237C216120C51F4B219C1F61204A22EB20161F0C20041F95204A207F1F8B21BC20581F30206A1EC01EB51EC31D8F1D951C0C1EE71E341E9F1E6120A520991FDA1F3D1D7F1CFC1D461E26203E1F9E1F1E1FF61D551FA51E471F8A201F1F2822C920C51E4E1F75222724152E + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 042817222D2226209D202A20071F4A1FBC1F031F561EC31DDD1D7F1D211F861DBF1CAE1E6B1EA01C5A1EB81D891FD81DD01E2C1E011FDE1E4E1E1E1E0420BB1E141DEB1D3D1D531BFA1B911D091DC41D421DB41E501D621DF81D5C1D411CA81DBE1EA11EB91E2E1CDA1D411D7B1C021F211DCA1F1F1FCA1E0F1EBE205322902DDD2A3A23DA1F7F218521A61F4F20B820BF20AD1EDB1F021F0A1EE11E891E1E1E6D1DD01DC41D7F1DF91D201F681F5D20FA1D921F1C1E271FA71E2E1F9A1F231E031E051FFD1DB21ED41C1F1F561D7F1D5B1F911E6A1D521DDD1DFA1F1B1FFB1D4F1EAD1E281F181E071F381F6A1EAB1E801EE31FCA20CE1FB320BC20B3230B2D932AF7238D228D216B20AA1F6F2034211D1FF52154213321EB1FC81E691F681F001FCA1F271F0C1EE01DB81FFD1EFA1EF61F7420521F2B1F691F631C4D1E931F2B1F1620181F251F3D1ECB1E951D601D321E161FAF1D7C1E051F7B1CBD1EEE1E0B1FD11E981E7E1D241F061F311FD41DF41DAE1E3F214C1F8120BF201B24342EE52B7A2799232C219F21D220D721BA202C20DA1FB41FB01F1D20FE1ECE204C1E891F991F841F2521E91E351F711E1820F61FBF20DA1F5120CF1E2A1EA51C8B1E3020BF1F9420461E051EFE1EB21D371D7E1E541F461FAE1F0D1FCF1ED21F1020F420741F8B1F991E8A1E2B1EEE1D301E221F8E1F3020AD214420AB218523DF2D082CA3256621B5207B201F219C20C020202152201E200B205B1FB51E4520E91F911E03200F1F531F2F1F261F8421BD1D5E1E6B21F31EE81BEA1E881E521E3D1EE81F8C20A81F701D841ECE1EE01D2F1F0D1E67203D1EE71E331FDE1F701E801EE91FC01F8D20941E791F9E1E5C1FFA1E621F321FFD1FCB1E502022229422A72C3A2A5525EA21EC22F61F152243207B20EA21B120F3205D21361F8A1FC41F2E20FF1F911F0C1FCE20F41E141F1D205C208D1EBE1FEA1EA01E6C1EFB1E711E8E1F7B1F13200B1F571D7B1FB11DF31E4D1E161F3A1F841F921F941E371FAC1E861F1F206520021FBF1F771FFA1F6C1FB51FA91EDB1F87216F20F71FF0222424C42C7F2A3823072278219A2096209C21B82028207821AC20911F1C204A1FC020A8202321981F3F20801F4721DA20631F8F1FD41DD21D191F291F7F1FEC1E8620D71FF11EED1F511F691E0D1E421E281EF51D3D1FC920B01E971E851FC41F971E091F4520861ED61F0D1F5B1F541F041F3E1F6120D31E4920371F3C21FD21ED21752DBF2AB324052289229520E41EEF1FC5209C201F21301FAF1FDD20D621D61FAE1F03217320DC204B217420ED1EE61F8B21FF1EF31E621FC71EB61F4220761FD91E061F641FC81F681E821E03205620AF1DA2209E1F551EAA1DB11E9C1D5F1FE81EBF1F691F5A1F7D1F1F20AF1FEC1EFA1FBD1FB51F711F0F1F8721D0210F242A2E7029932484221B215F21C21F811EA820AA21CC2045209C1FA01F051F21205B1EC11E4720902071203920061F661DEF1F541E1920D21D1F1DFD1D971FB81E7C1E2E1FF01F761FE11E1F1EE41D161D321E9F1FA11E3A1FB91DE0204D1FE11E361F951F731E8A2047202721891F251EC31F7F20111F6F1F7C1FFB1FEC21F421592EC82B822291208720E7207A1F9120FB1FD721022062216D20CF1EEA1F1920961F3920301F2B20DE1FEF1FF01F4C1E8D1FD61D772021207A1E31202B1EE11DD71E121F5520131F5D1FB21DFE1EDF1E701D141FC21F551FD31E761EB71F901EEF1E5F22951F6C1E42205A206F20A020681F1C20D72047207920A91F2B224623F32FF0291926AC2288209821FC1FB9203B20CC20D8202B22CF208720C61EE81F0F204520F51F05204620131E801F921E2F20AD1D911DD71DB41DF61E281EB01D781D871FFC1EEB1E301F731D401DE51E721DCC1EC81D441E8B1EFC1F3D1F411F7B2027203E1FFC1EF11FD520E81E1F1EDE1FAE1F381F991F821E8720CD216E22D52F672B8524AA213623C520421F2E20D81FA6215421C0208820931F901F681F6420D61FF41F7B1F13224A1FDA20361EBF1EBA1EC61E291FCC1EC71D581DD71E0C1E681D2C1F011E5F1E5D1ED61D0D1FEC1ECC1F761EA11FBE1FE51F381FE71E37204C2045207F217A1F9021A120651F13213A20C71F8C1FE91FEB1F94209024B12E1F2C1F263E224221C22032206120951FF42091209B1FA1219D1F29218620A621D620EE1FDC1EC520E21FAD1F5D1E2521B81FEB1F3F1FB71EC41EB21F951E521F1A1E911F0920671E4A1DD01EFB20521F841E4220F51E571F1B21B620DE1EFB20B91FCD1F241EB61EF51F941F901F7C20F31F9A1FA6210720E020FE21ED249D2CEE2A592529222F228522D41F74205C212F22862106200920761F431F2F21171F8D207D20E31FEC1F9E1F281F581F24204C204D20E11E921EC220551E171E441D5E1F521F431F1D1EFB1EEF1EAC20321F31216221F41F361F0F1F26201820CA205721301E081F621E6C201D1E5E1D261F3C203B21232173209821ED21D923802F182A5D24AD22DA20802296200522E31F9E22C71F1D219C21FC1E821F391F3C20F71E85211522C31FE11E701E401F93206C20191F3D1E861F611FD11D311E391EDC1F641F6D1F2F1FAD1E401EB81E9B1FA41F9F20B920A91E071F8B1F321EC51E331F1D200720551F9F20D01FEC1EE31FC220B61F5721151F4420A421C922402D852B9425BB218D203F21A32132217621EE1F4721D22162209D1E8D201721C321FD1F44213A20D81F6421CE21551FC51F561EAD1F3220221FDA1EC71E801E311E621F7D1E3E1F471EA61DF2206B1FA91F8C211B1E551E4E1E561FEC1ED71E751F5D20741E0A20621FF820AB211D1FE020CD1FF9203021B321D12097210324492D5E2966251223C422AA207220D5208F2015205020F921302170207B20482096209C202120A320F51F4321651F4A206C20D21F9820CD1F0B1E661FE21F7E1F851F4E1FBE20CE20591FE81DDD1C291E8C1F971F181F941E4C1F7B217E1FD41E2220F41FCB1EC81FD01EAB1F1D1F8A1FAB1D80206D20FB209421AE21FC2180241B2E0C2BB424C82275206C21FE21192104212E217D21F0200D21EB20CF20191FEC1FCE1FBE20A1211220DC1FA92015204C20AD1EC42070209D1FF31FE51E27208F1F73213620231F8B1E2B20C11F1E1F0D1E481F2721B81FEC1D2E21241F711E6F1E2421821E971FDD1D5820F11EAE211321AC21F9202B20E420A1219A217223E12F90296B26A821F721F62185215622ED20AB204F20DA219D1F68206C205C210520AB201323A920FC22B821C11F6B210422E91EA32031207420EA1F1B1FC62064202121AD20C420171EAB20CB1F8020A81D4020181F2A1FE91E781E0B1E411E121F971F7B209A21451EB020481FE31E911E0620B61F6B1FC51F352207224924C52ED82B07240921F120A82005216E201F21751FC81F7B20EE20C520F51E7A217D213721E51E22216221A91F9A1FFC1EB31F9A20E61FAE1D201DC6211B1FAF20EE1E762032210620C71F2E1FE41EE51DB01E681F6A1F1A1EE11DC51F321E1E1F0E2064213E20182048204A21331E2A1EAC20971F6E1FCB1F2F1FB02088227823D62D4A2B0225EF2202228B213E2112209320F82064218522FA200A20CA1F232047214820502326216B202820EA21B5209F203721851FF51F1B1F961FBD20B2201D22F1216922F320D520031FEC1E0021211E2D2066201F20F91F411E6620431F9F1F6B204E1F741F921F63218C2058205E232F209E2068225A1FC2216D222523382EA32A4124AF230921832047212921D420BC219C218F1F7321E12093217620F8207921A91F81222D20D0209D2000216A204D1FD11F9D1D951E5F209A1F8521AA20BE217C226B2106216C20D11F091FA51FE1205B20701E111F9E1FC91F9E1E1D1FBA1F6020FC1E12200A21E01FA4208C1F1B1F29218F1F8221CD21AD219023AD2D9A2A4C24FA2178228A220F22D32131212B22AC214B22132298210920662215218A219B20C920FB1E3F1FCF1FD41F22200C1F711F6E1E471FDA1FEC1EB8207221FF1F60211921E620D21FAB22ED20031FAC21DE208920581F1820E21FAE1FF820212100212D226620F2211E235721D4205720801F5820F420EF20DE219F22972E3A2B1B24CA2238223422352049213322F820F71FC220D021681FC920FF22EE201721132252200F215C207D208F21C821CA20441F231E1F207B20031FD720BF202F212922ED1F2F1E1121A52178218221F421F820D721FC2029212420B21FA921621F30209520F41F6A21D21F1521AE21B6210820B020D3203C2272210724032F7B2B782477219D2125227E219D20F2209A22E720F822A021842054209F1F1F2056204F20F62009231E20991F2D21E0201420F01FC91F2B1FE41E651FFF1E672021203521DD20D120D1201920E12151204220C02076210121B9219D1F741FFD1FE0215A1F291F3320861FCD1F1420C2220322DB207A2165216F23D423EB22D72D1F2BED24FE210E237321B92125227A21D72150203E21982171203B21862132215622F71F3221A320F320F021EF1FA8202F20B3201D21551F1E2171207F210221AD219421FD215821781F01212221BC1F8B211A210B20351FAA21591F401F2A21FC1F51206F20AD1E502181209E21A222A42268205A2053203522C8218E24AF2E8E2B1225BC21F5204E22E221A421DA219221A620DB21CA21D41FEC20C520EE200A22ED215E1F44214F200020BC1F822191203A206921BA1F60209A20C6214D204C214421B9222F201E22682029215D21582082212E20701FC21F0221CE1FEA216320B61FF820732003213A203A2195215C21EA203E218D21F02044227B23952ED92BD1267B21922219223C226322DB2242225421A922412127245821AB219821D1202B2255218D21D220A5201A2186213D216820321F02201D20AE20A020962172225C225222D3205222FB20E421912019218D20B01F3E20601FCB201320B820E322E2203F20A7217F217D21A52173214C217A200B221E22DC2291222825DB2F332C33253624C0231C220521F021162227233D224D201D224221DB227C21A52282226A215B220C21132159211821D120BC20A3219C1FE91E33201E2145200A22E12243237B231B21A521C721AE1F3920C421B12009213A20EA21CB1F4721D1221322BF210320A421A921F32137228B23CA21E922DB22912170226B248124DD2E032DF725F5203521F3202C23BA21F521222325214E23A321A321A4207C217222DE221E228222CA23FE20BC21AA201F234A20F020F820F01FB1202920D120D121422287221623AF22C821EE21BF206D21B022EE210823F01FCF218521FC21B72246231023AA221722AC2272235A2262228921E12166234222A12261247424D92FBF2BF4247122D32106238F216F2193221D22D623D120F221CF202D217720451FE920B021A422BE2106200220AC221B2259215721552232202221E62144220F222E22E1220423F1219A210920EF207D21EC212324A3201021F321F022AB214722F521B5232223DC203524E424B9258A245622572190227E22C424AC24E524B32E172BFB26A6227D234A23B020AC207E2027231724BC243123D9208521CB23B123A021FF2147229C227F1F6A201A201F20EC207C22F821F7203F229F20CF226A227E25122403247720B5201D2228229A2049227B2313219E2135226A224A23BA2189226A224521182300245623F423CC232323F323BF226F23F8227625A325EE2EDC2B2525B0220D22B822F521E4223F22CF2148231E232F22BF2217224A22BC221D2335212423AA211F218B21102172204A21D622CF22B1227822842227224F24EA24F32596248E2265207023FB22E323D624E92460232B229A227B221A231C2180221E2230220D234826ED259924FE2340241024152574234224DE24F924662FBB2CCB263C239223692284238E21E12125238322072238223F22F422402326232622D92393224222142388216D2326221D22EF2273238F2094221624B423F12468272D27F024832255232A22A1239E242D27632614223722A422BA211D228721712163220224C5258126A524F423C923E025E425CB23E72360253E25B5240C30242C562515220A23BC234721B222112130231A2274225E22812305224723B2222423A023F022DD220025BE23C122C62393224A22E4237121DA21C7233625EB263327CF28122572222923152495252325CA26A025FC226F222821E62123235820F5218621F221B2230226082756258D25BA24012580251824CF23CE241924A22EBD2ABA240A24B0229B23C5229522A7214122C2222D2489241C23C722B622102377259F244F23CD244D236B23C12419241922C122B62433225823C2259C26A92805287C27A927AD22DB21CB2615292D28DE2780280126BA22ED226F2347221D231C226722E624CD271929C1273424FF2412255F2525254E23B823632459245A303D2C69250C24FC2355235C213621F122AB22F62321257423BA220F215E227A2392241F25F1235725842311237C22AC24E9224722B2252124ED246C281B28492AD8295F29DD25AA219321FF251F288A29FB2A0E292A26322333238320B02160211524AB214F2533289C275D26A325D4253125AC2410253E232024672338241F2FA42C9E262F247523C0239D238E2451252625D5242A2590221021F923862282224D24AA23B2236023F2233421DC22C224892394222D24B624D326EE28582AAB2C622B64293E26FA217D228324C2285E29C42B1429AF2865242F2256228F211121D2220B241E2578265A28C325E425132586242E226B24AF22C522902354259730A52DEE2684242E243B248024092580253E28BA24B724D9249D232523EA232E235925D023A922D32314236E24D326E524CB266125B6234D261C278929432C542C0B2C312A7B27422296220B25F3264529182BF728B227022545234D218321FC2214245B25CF2634279D269C25CD24BC243A235822A523BC206823B5241A279830A22E08280B26D624CE23E3248726ED267926EE25BD2676252D23E1212E227F22A92204244E232B2467260D260128282A9528B626D9254F279C28282A902C9C2BD42BEF28A925E723A9240926A226D8266B28D628CA268C2365246B217521B921C123C4250D2895272E274F24D3238824E124BC23FC24932309253E261729B233CD2E822A8D276027CC266E27F827BF2739285D2623241D230F23D823982329230B25F124072399272327BA28492ADF2AFF291928D5247A26EA2A232A832BC82B592A2C27B4243D24E924582621257025F92692266425B222AD219120F7214F234F2327253A263B2670263C25AB246425CA249B24FF23572356242226EB2AD03472311E2A7226D22607250127BD2736286727052609263625F422D2214C23E3232F25EC26D628082A9C2B4C2B502CE02CE72A8B269C245A255C28DB29EC29772A97273326D526312425257726B726A925ED25F726A626962511255222AA21ED2115252F25FA248925F626E925FE265227F82669261527CD244927C629792B013351321A2B2C295426BC282B25C62778280227F02693261126D923B0228E2400254E272729F12BE92B772B612BC12A002C4529C025DC23F725DD2656273C2848276D2542243C269425D6251B259C2684276B298129722AD2262D258C23DC220A2228247C268D263C2797288F28F628252A0E2A482939287328D128FB26CA29F4327F30F9290827F72597260C2832266327102801292D29732767244924472382254227E12AFB2BA42A4628B7282E2A722BC1261E27A3244D227925CE24AD242126042573240724CC22CE239025A8281529D12A092CF12A1D298A276A2428236224B1232425A9278828F9283C29B629D32A8D2A40289B27E825AC2501266A288431672F032985268F253C27E027BC257026B629AF2924296C271723B0223B264627BC27DE2910291128AC288328D4290D2AF226B5237D22F0224C2371230B242A23A122D0239F233C22E924A7271229DF2B842B932D422BC32A422623257123E722442480260529052AB22B2A299529A02ADC27D3254A268F251426FB24B2257E2F0F2EFF276E257226932705270327CB28582AD52BBA29C3279025D3252C27E827222867289F27AF28A826D427BE29032AB528B9248C22EB226E23E1204B24C723A124A124D924C82458267627092B7E2B502B222C9F29D628472792238224FB23D72349255B27DC26732AF62AE529C929AA26AA242326D8243025C026F225A12F462D4729AC274627382937299F29252AB42BF32A2C2AAF263124B825B727C028DB28692982253F260C2769273B293B2AF6260424DD223F248F24A8247D268327F3267B2441259926B527E129082D092BCB2A64291829132789231A2587241223D621ED237623342544270927282667278524A2225224692314246B24A4259130FE2D8C28DF285F2A082A802AE4282B2A832CF02BF928A126932441257326CF277B283D26C725A1267F260928112BAA2995257923BB23D623B8245E273428D12733272D25F525EC25D627F729942A152AD729B3297F262624ED23FC22D9226423A422BC23ED22592200240224BB24B023E12100220023CE221923C72392253A306D2ECC2A962AA02BFF2A3D2BB02AEC295F2BC02A392A1926BF23F62325263525B2270C24AC2597258C256E29A62AC12A652769239221EF2255256125CA274B28A427CB253827112539266E265427AD26B5262A27E423242394230E2310225623E522D120DD21382169234E2377225023C222B5214D21F321FA2306256D277030742ED52B622B4B2C0C2D902B5F29582B062D2E2A60272625872327248525912702265626592637286028BB2789278B255025F224DA2365241524CE24F5267F27DD27872643265A256024EC249D24F824BF230925A42336210B234024F7228E226721AB24CE22E323A0231F2346225D228F225B23BC23C5224A252825B227A6321F30632CB12C002C8D2C4E2C222EAA2C602B26285426A4243824E02397262727C229DF2CBA29452A4F2650261C251A2566210D2123238B2258231C25C1250B2540243125162587241F244B2238229A229124F12282234C242A25F42660251B2638259626BD24B7235C244C23A2225623BC22CE22B6244D256D282F260F2A13330132292FB72D272C092BE02BC82B512A6C2A5F28BB262224252431264528AF29B62A672B302A582BF1268C24E124792412210B235F226623F424EE22B724C324692660283C266F25E223C82350221622AD232225CB2401258D26322867282127F9250026D1253623CD25EA239F22EF22DE23D225E026C7261D28322A282C7635E531102D472D89297E298F28A227022675273C27F925D125A926632623296B2A912AA12BBD285329F5277E24DD23E2219422D12025233423A82417236825BA25C0276C2AE62AF2284027B32388237423B0238B2530255127862AF7297128B42612250E250124DE2332227E22212435241A24FF25E72A322AD12A2D2AF62B74370934532CB92A7D2865275A2685252927E425E7241D27E1264A25AA2522277D2994271E289127FE269423DF23CC23C5238D229F239D23B22421244425D326E227D12B1B2DEC2B0D2A552743253B238223B0262628FC27B729F5282E280C25F62464261D24942240235C2464221D24072737266B264D2A4129F9299D29762A8833F3324C2EF62A392BFD2A7D29BB27C0268525AC27292AA82B9626B326E1245B2589245D2450252F25C8260F241326DD2514247A2509295B265E27C22791276A28232BA92C622CC52BF327882469240725A928292BA02A1E29492742273D2525260028D725AA248B24DA249724D6233E254A258726992919283F2787271B275A2FE332962D9D2C262B8F289929A227652792282F28F829FE2A4F2986242B24E1225F2535253D25CA27D6266D288F27CA2815270427C226BC299129DD298B29BE287B2AD22D182BD22AA127DF24A8234D2632298C2A6F289F266E251D26D7264F270928C026F526F523C926DC25762312253924BE245628D725BE24A326B6270430F92F5F2B6B2A522A352884277226DA274F26F22526296426E6266B2525260D25FA26DA26D4260028102B6629472C0D2B7C2BA729F6287D281D296A2888284C28D728B52BF32A5D2921279D24FF274328E229AA2CBF298627A926EB26112872298629CC27B6265726CC263C262126CD242222C0214326B12524273126DA272A31D72EEE26A3267D26EA2660242C26CB245D25D326C2268C2558263D26B527F625122789273D271D29322AA52B2F2B172C042B1B29C928CE28F7271D26B1254225B726F9279E27D325172451232527DF27432B422C1E2BAE270E27DF25C0278529002AE829862AD42717286725B8248B2311230026B3282B29632B2929FC298733D92CE72614268425BC24D32480245F252726B026B8277127532971286E290B2852262A2664277E29562BBB2B742D622C042B5A298D29972726285F25D2256924CE2387258B26E023A024EF25BF2641274A29962AAA29602899255325C52613271B288A29082BFF28872898251E258F258324C22834291F2A202CF62AE32C6D37DF2D33287B28C826D1258C2551243825D226B6265128C227CA28D029A62A2727E42506266326CC285529CD2A2D2B482C592D8029CF2517231124E72490237E24802335257224E12477259627AA28B5287729A228BF2A9B28342601252F241A26EF2673274F290D276327F92493223926BF275029C12A722B622B4C2CDD2DAF36C52FBF2A542963280827C526472451261A2782273A29F7288329262768266927FC25F2251526D2269D270E2AFE29812A4F287E259224FB23C521C424BD220922D722B524F024B625AE276829592AD029222A022B2E2AD8299B25CA23D424192585267F26A9260F268127D1257D256C262F292D2B9E2B4B28FB28982A852CE635CE2F6F2CF729AF29532889261B2774279A293A299C2A4F29532914281D2757243725B2245B25FB25892510288427112673246E24892269227922B123BC2372236A24AE2489268F287F295B2B542DCD2AEB2AF729EB297A28A82667236C226123AB2639288F285A268E2728266E26D228422ABC2B7F2B052A2C298C27202A5A345F307E2DFF2D422ABF2AF32AF52900285F28EB29682CCA2B9E29242927265C27A1256E25FA23A62598234C26A824DC2436232324DB21DA218623B324A724D124E82303240E28DF261329152AC12B2229A929C628EE25D0249626672246229724B92773299129002A5029F727CE274F29202BB028102C832756275F2AB92BD334DA32932CCB2DAB2C712D0C2F1E2C722A9C2A612A782C492CE8299B286B28CF26AD294D299A2665236B252125FD227D245D24C4226D2402241E25622522278B26CD260A2786268E2722276827D228D326CA2643273826A025C72504251F2380258327262B0C2B702AF12ABF2748273B2C412B94292E2BC2298D29D52C362BDF364C31E32D8C2B7E2CA22D372C2A2D962B6F2BCF2AF3281D288226C6269C28A82AB92ADE295A27A725DC2352250F255C23CD22EB24C12484268A286D28FA27D2282229A528FC2637251826C626B624A925D626A9257527932676271724D02336257B283C28CD296C2C9D2A9627FF26952AD42B372D132B302B1B2B642DF32DC036D62E012CB62A162C032A862B2E2D1B2CD32AB726B2281B250F270A2616288F276829D227E6269D2467238F23FE22422516243924ED269727762A102BCF283929C12AEE2A9D27E624BF261D256B23F722BC246F2667278F279A261325E62471240228AD28BB297529862776266B26572AEE29782A502B282C902C0C2CE92CD834722CCE2961280A28492A2A2A40290C2BDF274426A325FB23A7263126392862270E27D2264525162639243224C9263C26AB241E25D026162AAE2BCB2A252AC52AA52A022A7A299326A323A424CF24092312248F28EB252226822766268C259F25472539276E29E32782284C2645274829EF287328C728372AEB2B552DFB2BDA33292E5E29FF28CC292F2A1A2A81289028C92566243B257026C924FC2535269E26DA264126DC24352704278A2585287926ED266128202AA529A92A742ABB28EF26A028BD2764251024582434252A269B270329B7298328DC254B24C623F12428233D2487270D27672682256D242F2806271226F6268D29FE295D2A492B942A03316930402A262AAA29442B292CB029FD277F26DB24452573243526B826B2267B2772268B275F27342A222B072AC929122C612A432A4A2AB42A702BA029BE287626F0287C28B0255D261D26A3251928F227B92A4B2CB42709278925AD23BB25DC24C22498254D24BC25C1243C276626B4265727DF27F727232A512CA6299F288E30CC2E792A7A293429252A052B6F28AE244B265125ED260E28E026AB26C9277D24D6259329472A8F2A342B30294929732AE62B602B6D2B8229612ACB2A492A8E2AE22AB42A342A61265926FF27CD28A627632997283526F023E52695265825CE2550246D2357242C241226C1258D27F9292E297029072904291F284927CA271C30B72EEE29952AF42A8A29142980267426F327BB285D28092B9328F7269327812564271128532A882BF62A78292C288C28A3293D2A8D2A1829DA2A532AF729B128A72A832A5A2BA627352724275227F22511274F25BF248F230827CC257A256C25EE2306238D23FA23DD2668273C2AB82A49295728E12510264625A6251027DD31B830332A652AD02BA32AB8282E25BF26202A4F2BB32A402C58291A282E268B263126AD28722A4C2A7F2A27287B274F287F27F5268A26D22703288F27F324CD255529D2298C2BC226C12501268424F1239A235A245022ED239123FF25AE231C234224ED21142424255326F327DC28262A272AA8276C25F724722623253528CA33292F372A442947291C294D27D227FD28E129A02A282CBE2A4129AA25422521273726AC26D829932A86271825FA23A625B32420253324CC24A8252D248B2435278127E6282B28EA241A255723B6229E227624BC23BD25EB244B257B236124EE23952318237A23B225CA27B629F12A642CB0282126DF25032522278827742A3736CB2E482A762767282027B026B229D429592A1C2A8C295C29FD26C2269126AF27E3285D2A722921288A249B27E9247A24CC229824212397230925ED24AD2304257526DC253B2610239D222B245223DB22F723802603284B272D27812682260527E126B4243625A127862AE42AC22B2C2B79296F270E2655240427CE28852B9337D62F382A2D29E7286D287F28192A5429532A7F29862A5928F226C0277126562ADF2BB32A5C293A29B726D72494267D26AD23392353228123C2246623A32578252826B825062670234D234E24C3232A24EA25B027A928AA27762AAA2A1229A72912284B27E12574269C2B2C2BAD2C282A1727F5263227FA252F28B129992C9135F82E4F291728F6263A2A262BD3291A2A3C2BE62A872ABB270A26DE2529288329662AD329EE286029962755270B26E5251E235F252926B825DE2799264627D725602587255D2571254A24EC249926AC26A027692B9A2AC72A782C222DD32C0A2ACE28F0257626F0251A290E2A3A2ADC297226A926FB28A127E528192B7E2D3835A52EF42945273327092A432C092CF92CB92C132CB72A3728FC241F254E25D4275E26AA27292787277F29D92BEB29BF2640261627ED285729D6287C29B726C2254A27F92560282727022629264228FC28752B2F2DFC2BE42DB22DC82BBA2C1A2BF02833260225D4262B28C328BB28E9290528A3271D29DB2BFD2A7F2ABF2BDC336F2F94289D260E276C292C2C962CB32BC32DF32B162B5827C2246626292733282328D926DC277F288B2A042CB52A2E294D287B2A7E2CB22A062A8727AD272D263027782845295A2871270329F3290D29D42BB82B722C4A2D392D352D3F2A9F2837257025EB26A4249B287E2AFA28152A3C28662A0D2CE429572A872AF7298633832DB128562605286B29C02B912CF22BF02AED2B42282326FA25BE27E8288B298829A82741277428F929712D592C352BC62AE22C592B2E288127C325A5250224C6250C29972A75293D29542C2329D029A32AAA2AB3289229E42C282C182A4D2866261A263A235B26EE28842BE12ABB2A9F2AF82CE22C102CF82B1D2A7B29DF32252E48281D288127B0293B2C0B2BD52C972B35295027D7258D27F928E5293A2D16285B284A28832821298329372B4B2AB2274D283F27E3260E261825C2265F26ED26A72794275828572964295328D62693266D27022610285229592B5A2B9028C125FD24FA243227EE2A772BE12B622CFA2B4A2C152CB12B742B212A512A0134AE2F9E2A7128C028332A412B402C7B2C9E29F428A92768272E271F2ABA2AEE2DA22A5A29792804296C296E29B528BC296A25252743265A254126B427D026E527C727D427F62781294F2A172AC4299827B7254726C4250D270928A1272229FF28B32636272D265128F8291F29422AF529EB291A2D222DA42A3E2C0B2AE02BB3337831212C7F28A82723294D2A3D2ABE2914295B27C1279629FB2A052A2E2B9A29D4278527BC284B2A822B982A54295E26F62506251624412532282528DF27A62959297B270726FD258927C52738286625AF26FF25BA25A824BB267D2726279B28C1272B278F27BA28272837275F26882766284029642C5C2CBD2C982BBF2B7E3490309D2B2A2ADE288429412B8D28D1261126D526F228702B0F2C2A2BFD2BA029072AB929EE2A202C272D1B2A8C2757258D22432309243024DE287929982BA729C9296C29EF298F2711267A2632288F267126A22649260024932A75296B2A4929F027CC28DB28BD276B27F125E92541261E265F282929082B172B232BB92A72324B2F672A8F27C628892AD92BCA2943268E2666284B2A7C2C762B5D2B882B5C2B0629042AC4295A2C3B2D8E297827BC24B123B523B924A024B528182ACD2B452B212BD02B0A2C5429E7260E253627AA25C825A327CF255228F629A52CCC2B552A9A28D72792263F26D225F925892627275527FB2866283828F7295A2ACD290E32853034281D28A929C82A612A6A271D2720260A2A182C7A2BDA29972ACF2BDE2A8F291527D1278C29F92A7A283527C32429255D239F24BB24E8285128B2287F29522B662CA229082925264D255A250227642A3F290B290C28FB2A022C462B622BB628CF261924DE24C62521250D284D296A2B4C2B7629842758299E2708286A32092F3C2A5D298229D129C72810260326E327B1285E2A2C2771283A28F427F7299A28252616250A288426ED25A9266A2580253125FC2568282F294229A72ADE2ABE29E82942289224B22448246B268A27BF29FD2AB52990275C2BBD2A032E692D272A0C28F323A323A026CC274229FD2A552B9F2BCB2D86297A28EE262E2875305030BD2AA12A582A722A7729A3266729322AC42B172BA428BF26EE26C92678297228A425072612265D267E265E268726352641287327232A6F2B6A2B8F2A7F29E5285D273227BC23FF213B267F293C292B2B3D2DC42A9F29812A362AC62B162C6B2CC8281926B826C527F128F929082CF12A322B772CB02BAC2A3B2C922B2A33692EC42AF8279A28D227CE2753270729DF2AC22CD32DA02AC329AE27D52805285B286027B427FD260227672665288228E32790293D28B928672819283B285E27E825CE26FB2508269F260C280029762AB32C602C25294E29A22A452B2A2CAF2B542CED27B8264B26D92662291D29BC28DC2A6C2C8B2C952B122DEC2C1C2CCA358E309F299027D826222834278828E129712D1F2D4D2D3D2D8F2C922B612AA02A4029522B5129E5291D293627B02AC429C229962A4E2A912994283A28AA26C126C3278328B327CD2746289A2A0E2DDB29882C732B402B722CB62C162A8B2A00290B2A1A283C2758252D283729E4285D287329B22BA22C192CA22B902BE42C9A35F72FAB29B825B7244A251A274529692BFD2C862CE52B652B4D2BC32B192A662ABC2A9A2ACC2A372A5A295C291C2BE22A562A132C4F2839275B2725285F27AB285F29E4282729AF2831292D295C2918290D299A29702BF8285829B629EF26CE270628532725281C24C225DB27692775282F27C8282F2AF6287C292D2AF22A7C35532F2129AB2478235525AB260B28D22A3C2CF829912BB32B462A7829462ACA285E27A12A9A2AC8284A29222A7C2B422B7A28F82801282026E0280A2809282D29E7299828D628E7269528342792266425A42782275027612649273325D42623270327DA271128AD27FB277D246126E8276A28A4272727EB27BF28C2273E2BD535F02F4228D925BE24D4269728F52A312C332AEB2AEE29E52BB829392AC929EF276C279228E725EA261226B2257A255026D3254225DE2421267E269D2887283626562765260E25AF248127CE26AE26B8278627A029492607247424BF24BC281229372A0A2ACF286D285A29A0278928952ADC2BC12A982911288C277A277029E3333D2F6529542901274027A827CA2AB42DFC2BC52B3B2DE12C102BB629C127D5269B26FE262E25CF254925AB242C2575263624F724E823C724D726A025BB261F261525FA2368254A2661272A270628F22A4B29532B70273E235A254C26D0277928C1293E2C792E572B7A2B4E2A442BF62B532C202CF72A7629052AD729702B9A3472307A2A6728ED2713277F28922B312D502D242C302BB72BEF295C282C26D72518258A24B625E624372501246A25FA2408256D2446250925222687256B251A263A243B25FD2508232E272D2753297A2AAC29AF29C926A12534263D289E2AA42A5E2B3B2DCE2D942CDD2979284628472AD92A312CDE2B4A29192894274928D5303131962A2D28FC25C12667278B2B7D2B052CD82B512AED28C22571240C2670257524EB2598253127D22569260D28F8269425CC26DF26B628EE264424F724F4258E26FA25B8261A2584276127D129632A5129692970265E255C274C26F428CA298C2C372C6F2CC529022A6D26D5254A29F02952298F29CA289226D527EC29E8313C31B82CE229552876271A26192A5629482B0A2AF3250F26F92683256F25CE269A2684288B28B229D12ADA29A829D629C1281329AD2AEB2A11282D261F25D2269028D5296D2A49277A287928CB27A6263C2929290F279525D22502269A28F728E52A5B2B902A6A2BC228B624A92434255326452814296327DA273928FD2971346430972CB029A926D22528254626BC27EC265827C2271E27382765268227FF28C2286A27102A7A29EE2BE62A4B2B5B284F2546280C26B5279527DE26AD248E27D229BD2C782BD526F2265D254C28F8241326C1270C27A025BE27212901292129BD270C29482AA9277C28DE256425F524FD263C259F28FD29462BA12C6A2BC1340731FA2CAD29A428E826B82577265229D42A182AA12B122CA0293629442A052B2929A9280C2842291E294228952680258F26AA28AE27CB260C2793264A261C28F728862A2B2A2F2709262928BA282C276327BB283D280F2A672AF0285E2A502AF32B56280E276C263A268F244725FD2419237427FA2818294D2AE22A5D2B9032D730082DBB2A6F2B792A932A3D29DC2BB32C482B5D2B9D2C012C0B2BFE2A7B2B6F2944290C298B2B092A3A2897282B277F28C8285D28D7263F25662654272C29DB284729F028172859287F294F2B842B372AFC2A082B892B252B9B2A932A4E2A1A2C522A6B28C4284F27882562243424C22591278D282D28FA27C5276A28973031330A2BEC29972B362CBA29742ACC29FA2A922E862C8E2CA02AA728F62805283B27302A9929582C4C2BAF29CE27CF275128EA28DD273E240926D52623287E2A8A2AE22763254F26D526EE287329BE28BB28792AF22A152AAA2BC8293B2AF12A132BCE29312AEF287429C5277C25C625D2244A27F32749275A290829A628FD320930A62B4729D82A2329E129B329DA2A502CDE2C812E5B2CA12A4B2A2A28C727C527422A1E2C312C0A2A432AEC290F2891275A295E277126792972283F2A44293D2B4A29D527AE28C4264627C8270C268C269D2798287A29202BD12A5E2A482B0F2C072CC42BC22BF02B6B2AD4281F274D255428F528092AD42BF92B042DFE34792E5B28F22738268627E128EA28662B3A2BAA2CB22CAE2C602A3E28B4278F280228E42ACA2BEF2BE92A1E2955272726F528BF273827E5273428F328BB284E284429ED291B2940275126E428B126082478257728A628CE2A592B6F2BD62C772CBC2A462B962C9C2CD9296829E4260B27C526AC27F629D92B212A232D5A2EE1341E2D482843262B288129FA2AAE2AA12DC62C9A2CCC2A142BD829CC26FE26B2270D28CA28EC29292AC226E5258324BE252D24B226C3262A270A28A427F6255728F327EF2561267B258D250527D726E42376263C2884263A260128EE271529E128972AC52A292A18293B2790268B258C2515279A28E92AB02B262C542BC92AB634542E6C284427A72956299D2B9C2BEF2A9E2B252C352BBD290E29F6255B266B26C8272929B529FC27F4267324E425D424AC24B4261F26B8282228942795250425F526372777245F2433250126D0230023F0259B24F425032519276C280327DE262A29EE29942BA92853273126C0251427C226CB28F7297D2AA42C532C982B3C35352FDE2A602A41298C2A1B29822A1E2977276D27C72755271E26FC28CB2878284C281F29872906297E2742277B266226F72696272A287B2ABD2A2D284427CC26EA2734275025FE23BD24C325AD24E3232626AF26DB2556250426312673268D243328CF295D2940287E2798276224DE25E224AB276D298729E92AE22AEB2C1336D62E392B952B9B2C9C2CEE2B432A9E27B826722743265128F729D42A2629552898278F2A0C2BF429DF28A4278B2BF228D425BE266629FC2A5B2C922B7C2A6A2BB52987271B266824182580253A2690258128F827F927D2282D28E92612284228F128642A902BC6274C284C275527F327AC253627C7287C29C029E22AD92C5234AB306E2C742C6E2D412D912C042C3429352A7029D2273129B72A7F2CBD2A67287C27D9272B2B9D2BC22C572ADA28B02698270E298229622DC92CFA2BCA2B992CF02A6D29AB27B524AD27B928AF285E295C2B102C092AF7288C28B0292B2A622C6F2BDA2ABD2ADB2ABA2A3D2A6E292E295C27622585269128B329312A0E2B9E340A30442CF12B842D9C2C402C832C9E2CF62AAA28DB27A3289A290B2C7E2A4626D725FD26882C752C712A2E29E927D12786262827322AEA2B212A502A6D2A242ACC295F28162536252627442BA12AC02A7F2B9E2A002910280329172BB52D162C542A8C294B2C752B932DC92BA32B87286B25E525FB2633294729382BC52A5534D22E1E2C242B37298A2A812BB62B052C8F2BAB2A23291F2A002A5729F1284D273C2502285B2B4F2C362B412C9D295C2A38291A286A285B2AE22A552AFF283F285528F82592253D26DF26A4284A2A282A452B452A8A28FA2680283A28B629FF294E2ADB2BB82B7B2B7D2D092FCA2B53297B2695272227A627332B572A782BB133782F3E2A9828C1279826BA260128E4267F276C278626C22684278327412713266C244C250F2AEB2B302CA42CA62A87281627CB285D2AA32A632BD4289427B025DB259724DE24A52584278129832AF328D828062A5728E8258C27F5254526972660295B2A922B392A772CCD2CEC2B8A2A8D27EA270B286A28FA29312B3F2BB732C62F232A4D28D3245026AF2449241426DF24C4254A2563252725032460237E23EE23D5257528162AC529E52ADE2C77298A26F1260F297B29A22A7528F126A3277B2570247524E724FC27B728BF281728A7280A29D2255A2413248C2485255E25DD27942877298C296D2A3D2B292DA92B1D29B727C8266F250B284A2A8B29A832302F5028262777240D2444242B24EE24C125C825312406247823B0246F246C230724ED25B6258B2748279C283829C226C726E526AD27AE287229492B8A29292852268024CD24F5260E288D283728792821291F2A702769246025F724FC249B27DD28C42A372B3029A529962AAF2BE82B422BB728D826D8266727D329542B1C34242E7228C126C024B224C52359246A24D8268225572549254C24A723E323E7244D24EB248F24DD261A269026B1285E27C026C927DE2557263D295129DC28B026AB26DD25D2243D260B27622839263828DD2A6729AD28E125F526C5260A27012AE62A3A2B362BF928E529BB2B732AFC2B0C2CB8277827F1252328582BA62D6C34142CA9270C24C1257125A52307253D2644277C26E326EB24AE243723AA227A247C24A6254427DC27B1269B2643272729D927AB252525C2253A2862296C2987280728EB27CE278A27B1278A282F2857276D2BC12C602A2928222869272F28A62BC52DF32B5D2BBE29E52A712A842B352CB32A6328E826AC265829472BC62D7335BB2C28279726CC2439270A26B927CC28DC272628E92737278725C323AA244524A625D726F12B2C2A3A295926EC27B02922291D2611262D2682262A267C268F27F6277A27F427292A442AD528512A2B2A8B2DEF2A8A2AB029BF29622AF22AEC2B982B7B2B6D2CA32AC62C902B422C972AC12A2C2A762B112B6F2B942B8B2E46369A2D612702262D299F299D290E28102BAA2A522BCD2AC22AD226492451268826142653292D29AD2BB0282824D8279C29592A2F2836264026B926ED25E3243C264D259A269D28A6282C289A2932299128172AA92970296C2A4A2A8429D329472C352B6F2B9829A729D82A6E2A652B222CC2279527D4298E296D2A142BD72D0836D52D97270A295A29192C242B9C2A522CF22B592A1C2A3C282F27DB24F92559276226EB28F028C02914286725242673260027932927298F28E226D725FF2384232D25F22744263527B028EA25E1287527502A502A5A29A727BF2667278528DB29622AA928052AB6296D2C422A432B5A288A278D26912A7C2AA32BA02BF82BFC35702C5E285027B32999296C2BCD298D28AE29322AAD29402665244F259B24A224AE26C227582879275F256A254626EA268128972AAE2A8B292929F225F8249024D52386244824F1230C255925BA251E27692A082CFA297827CC257F27CA28262AAD2AFB2776279229EE2C1A2CB02A35282D26AE265729A52A9C2BFC2B6A2D2537DA2DF82752276C27A5284A28F32744299428B8252526A924A424CB22492412251B244B27F02610279D25A526B725AD29032B572BC929632A992AE129AA29D52806273F26262405247F237D260028ED29082A342BB228F3259B265F26E828492A6C2A5829C22A8D2C7B2EDB2B182A3928D626F026C429702CA22DC32D9E2C6136E02C7927A025CF26BA27B7275A2701260E272824A4239823562385245426F22734277A264E255D25C324E424DC26CB26ED278A287D27A026B927BF28E228CE2825287B24FF223D21B7254E25B928DC29892AD32A7D262824E924FE27C5284C2BE82B582AB52AD52BA42B112A5A2AC428D8261D28C629FA2B3C2D7A2C7C2D2C36892D8F27AF26CE245B252F2798261B266E25FF23D12331252A24E824B228E529D629A7288E2788247F244323C52479274928AC28F628D125D825B926F525F1266B267E242825662488265227C528CD28682917289D258F26852797260529652B842D262D132AA82BDE2C9A2AF427C626382633286B2A952BFE2C4F2C512CEF335D2E6326352565254126C727DE26A724AF24D823A4221F24C7244525D7274B29892A9E2AA529C126F8242B249224C52657268A27FF251E277A253325C325AA245925AA246626CF272429002A7629432A902A0D28B126F2275425CB25AA273E29592D1B2DA82CA72BBA2B7F2A41284D2845271029432C902DAB2CE52C592AE933A12BD9264925AC24CB257B27872464241224082492248F236926E02553288128702A8B2B172B7E28FB26E7242726FF28DA250E28DA276728EC255C252F25B32350242A267A268A26D9284C2AC929A127B2284A264426902585259F235B25202A292B522C972BD62ABF2B5E2AA5282428F3281528CA29F127FA28CA29192A9D33532C1628DC231A265526CE26FB25D1233625CB228A234E25BA26232623268A287D27E529AF2BB62B90286026FB260429DD289D2833280F2986295227D423722339241B27CF2593250A27A0289228EC250D264C272F267D25C925D62208244C25192AF628702917291429DE26FE282028442A522785261328C927F0289B295432B42CD628A3256225FA257D26F8244B249F254B25D3250126482778247025E5254D2799291929282CAA29E8270A28492737289E27A1288227A22AE827B62790254B263326E2259D2476274B27FE264126AC254126BA27A82508266E22EC23B6240C286429452B862A9A2B6428EB29432AE828D3259F25242616298B2A642B4535EE2E6D2986263D261E2729272324F1246226FE264528612736268D27EA277D276A273E289927DE29D32763264B2749283C27DD277A267426E227A628192640273D27A6249024A625F924ED27C127A524FF270429BA271B2411251724A325A224EF26EE28492B482A922B712B472AA628542842250926FB24AB27B027242A79341C2D87276326AB250D250F25C425012583278927182785279427022638287F266226D325242675258024D5255F265A263D254B2608252D24C12506282A27E125A8267C260527D125A026F926632622256125CB268D265124EB23A623132506251D2684295F2B9E29252AC4299F281C2911279F255226832521288329712B89358F2B7525232517252224352501232225832707272C279D257225FF24432517264A25C224FE254325A3229E22D2236824BB2344255523DF24A22439234D262425662519271729822678270427B9241C258D256B26DD242A2558254B2439244524172687275E2865278F283928AB280C2851250E25B326CE2557285528AE2918334D2C0126CD22DB24D024F6232F246C251F2792247A24F225F323D6249C24022542267B25F7245A2588236B23C024EE24F8224B2596248423092476258226CC264E279B274B2737282A2814273426832533269825F22449249E24F824592434262426B125C325C9261E287F29492A052BC1269A254326B02664283529AB2BC0341C2B2D2636259C226723A623FC223023B6242A252424C72404230024DD233326AE24732570267B24A224122345247023D623FB255424BE25B325FA240126CE25E0268525F726BF26F225D02402250C24B72434244523F4258225EB231F2450250B276325042681279028BE27012864269925F825DB24C427DC29492B732BBA36F12CE0267123AA2274227622FD22B2228323BA238F22DE22152268228224FE2196239124D324462569252B24D0236122E0239A251327CC251F275C25D6260C271D26F8247C25D122D423C523A8228222BE24D0259E25C725C1243E24232490252C267F2618261E26DF27F725EE251227BF231624D12520263828472CF62C8D37412BBA25A022222245223C23AE215E2418218B22EE225823F42258246124F3221F2370256924F525CF236B23E32308248123DF25B7267B260E283E269E26D325CC25B2240824E42204247D242D245224B124E326C0275A277B25D5255C26A326B42702277C26A4246E264423F4235A23A822902497257E26F529E12AB32CFE35C62A6623B621B223F4215922EC218722EE22ED214A24C22388222024012442248E25B823D7252E272D252D238224682511231C25BA25CE2590259B23DC245423F4247C25C3269324EB243A25362611251626C5280227A82742292029792A9D2AAA281C27E8265C2517253A24F3214C23AF2380224C2431251B268D28DD292333622B0B262A225F22F523D221BC229C22A5236C2430237723A1245523B9223725E3253B264D26B327E6240A23BE24C1253B248A24B226B227AE24FA24EC23F7225E26EE2634266B243B275E25E825A8246127102705261B273429CF27A52B11296C2B712B0D2AA7266525AC236723C02462238522D3246B26C928BE29432C8B36E72C742520223B22DF237F21EF22D3211623D0228A2342229C2280217722FB22D123CB245A241D26CA248723B72371259B256524E324AF22AB24B02206243C23AD231824E7245F241822BE2571244423B9244B25592301253B253C25EB248A260F279627C82925279D262825D9231823932322245B2649287229252B3E2C9F368F2B8B25F72365215F214B21AE220722C02379229322D222D721C423D92388210B24792457252E269625E82310244C236A236D2483222F22442527240023F02104236324322257214B20B32245215023CA24762484211122E5232223212488220926D227B028FA298228E625B424E1255F23E223EC257225D929362BC92CAA33652BA2259623A822CE217822BD2366232423F821EC2287239122182210233224F4230E237E23A624AF24792309235123C12363223C21AE2248213E23B2212B22FA21E721AD22D3200420B42110226922A5237322D321AB212B22E72208221E252E25B0241527362950299125CC24AB24B923D822FD24282521273728B928D530012B5E26ED21342194203920E322AB211822C0205F225B2030213B21C421C721B122FC21DD223923CD23E420292212259F239022A121B0209B21BD20BD2223237921DE205D22161F0F205C2168215822032394238C231B21BE22C121A8216B21BA24F223222392237425D524FF22A223CE21FB22BE2484239C23802507270430962B6F258A238D214C22F4214F213B215C2240210222BF209C2199206021C8204C20CA2180210E22A0202C200E218D221322C820AE1FD620FE201021A3210A22F221DF21A2200520CB21FC2148222E22C9219E22C920E021332246213320A8216122DA21B622B622A7234E2179212D22D0232F2154228521C921BD219A2528304B2B102434224A22C021A620CC20B7225B2090212B21D520C720CA1F73208D2170219220D522FB2217222A206122C6213F21D421BB21142140211E20FE1F2422E8218922E51F4E20841F45228021B021E3211E2291209C20072231200E213121632196217B219D21AA214C225C239D20172128226922E7221D2333244E25522E9A2BD1244421FA214422F6203322CF21C1212A238F222020F2208B209421D6210B2296227222C921D4225C20D622BE1FA3211A22791F50211120D1209C204C221922172141217721F71F492286221922F123FA22801F0222D12035217321BA2287211D1F30229D21EE1F8621DB208A21B9216722FC22F2209723F1236D264B2F302B4C250422E7202F22E421E02062237E21F320F220CE20401F741F3321E022F420B721C723BD206522A921D621D320D22190223C20B720CA1F9520E71F7221EB20A51EE720F81FB51F0B2203229C20A0228B212021A51F33222E203A23D1209A206720AB20E61F06224E1F1221332230233321DD2167225E2306228A24FF2F072C2D255D21D0221D2218224D215B23B722B9219F21FC21C820981F6A219921FA22B022B82299208C223921E721DB209E2058227E20F61E7020AD2286203B207A1F771E62206B1FF71E8C2148214F20A022B9224720B920FC1F3A1F911F4320802130202C20381F2B212C217921BB226522C42262214721E0229325772682321B2CEB243B23752009239523F321D0224823A522C023012235219F202A2290203D210D233B225C209C21592074211021E420A5228C2183201E212D1FDA1FEC1FD61EF51E421EB61F9B20CE1FBA2075223B221E212C216E1E211D2F1F961F25217420F4205520661FF520A6201B21B0222E211524BA220221CD213B25E8268330 + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 F227FD21CD211F2083200420111F211FCD1F3C1FBD1E401EE31D511DE91EA81DA11CBA1E711E841C791EEB1D9A1FD51DC21E161E061FF81E8E1E2F1EA91F491E231DFF1D151D5D1BC61B891D0C1DA31D2B1DB11E751D351DED1D391D561C9E1DF01E9F1EB61E761CFB1D491D711C181F1B1DCE1F301FDF1E9A1DDA206222822D492BB72353205222DE212C2087203121FD20101FFF1F261F591E391FB21EAB1EA31D0A1E481EB51D381E1D1FFC1FC320801E1D20A81EA41FE51EAA1F1E20231E521EB01F8A1EB61E321D841F931D891D6D1F071FF81D891D341E1520901F641E721EC51E3C1F0B1E641F451F6B1E911E601E2F20C720192001215121A323392DDF2A5624E522DA2162203020A42095217E1F3E22DA21C6211020561F9D1FF61FA61F24204A1F7E1E4F1E6420A81F4F1FC020B920BC1F971FCE1F8B1C841E02208E1F7F209B1FFA1F7E1E231F0F1EEF1D8B1E7E1F0F1ED31E661FDF1C0B1F2B1F6F1F9A1F231FA11D5D1F731F971F3A1EAD1E1E1F9B21B81FAC20FC20B8247B2EE12BA02702248C2185210D2137220B21502008202920B21F6C20BD1F1E21751EC21FA71FA71F7821E81E451FA21E2A20EC1FCC200F208820211F5D1ED91C9C1E0F201520BF20391E0F1E491F1D1E561DA91ED91F811FF61F6B1F171F06201A20F5208B1FAC1F181FDC1EEF1DF51D921E4C1FE71FBD2002225920C221E123F12D0B2C81259621D820A4202321A1202521D9204E2049205D205A1FBF1E5020E21F951EE01F481F1A1F341F421F8C21F51D591E8F21041FD41BBD1E9D1E201E531ED81F8D20BB1FAA1D671EAE1E8A1D4C1F2A1E1F20931EBF1E3B1F12207A1E841E0020FF1F9F20931EB51FAE1E661F121F4D1F401F3F20DE1EF31FF9219B229D2CF0292F25112227234B202D2232203E20A021B2201521AE21511FA71FBD1F25202320B81F381FDE200A1FD11E352099205C1EF01FE61ED71EB71E1B1F931E9A1FBC1F07203B1F861DE31FBA1DD61E6F1E4B1F491F891FA01F091F471FD11E591F582082201B1FC21F9F1FDC1F421FB91FF11ED01FDA21CD207120FB222A24152D392A9C22CB2110218F207320C7215720FF1F4D215820A41F0520101FAB208E20FA20B61F1A204A1FF220C0203C1F5F1F861DBE1DF01E261F751F1D1F8020B51FF31E901FF91E661EF31DF11D421EAA1D281FF220901E741E5C1F741F2F1E261FF81F501ECC1FF61E191F171FD31E021F3F20931E3920061FC8207E21F221982D092BCC24F521A722FC20311FCF1FD12089208A21701F062029213522062019204D217220D1202F21D620871F1620E5216F1FF31E6C1FD51EB21F4820D51FBD1E531FBD1FCF1F8B1EBB1E6F209B20C81DC320B01F9A1EF81D9E1E8D1D741F301FB61F601F741F7A1F4420DD1FDD1EBE1FF01FA41FB11F2F1FAC2138223924F62DA329BF24C5226621AE21F61FE61E0621D3211821AF20E51F9A1F811F4D20AD1ED11E77209520D420BD20461F091E4620AF1E08205C1EBF1DD21D651FE51E7E1E7C1F1020A41F3A1F5C1ED41D491D441ECF1F741E861FC41DE9208C1FFF1E061F631FA31E992060209A21A01F551E871F7B20341F9D1F501F242005224F226D2E7C2B9F225A206720A7204D1F99205420E3211E2065218220EC1E15201420A41F3120461F4820E71FE41F2220001E571FE51D4A20F41F1E1E2720091EE21DBC1EE51E3320AF1E171F971D2B1FE21E9A1D001FC41F121FAE1E681EB41F6D1ECC1E4722781F511E4B202E203F20BC20E21F5320D020FB1F3920B21F4822E0223530142A2B26ED22832105224720F320BA2033215C21C0226321BD20101F4A207420A820022079209E204D1EEC1FB61E7F20051EA01D1D1E6F1E411F7B1EC91D851DB71F2B1FFB1E491FDB1D881D4D1FC11DD61EF61D6A1EE71E1A20921FA21F9E208820491F221F2C200721661F281E4A200F207D1FC71FB61E10213022E7221F30272BAF240122AA23F0209D1F4E201320FC21D321F1209F20941FE41FD31FC3202620062003207722C41F1821651E091FCA1EAC1E631F341FF71D971DD91E301ED81DFF1E561EC11E561EFC1D321FFE1EE41FAF1E7E1F2B201C203E1FE91E47205A2071207A218C1FC421EA20DC1F4821A4200920CA1FC41F2B208320BC24092FAA2B0926E2213021D0201C204420321F1C219620B71F5721AF1F522153205F21C920F31FC71EBB20831F2B1F481E1921FF1FC11F071F371ECD1E8C1F821E391F0D1E341FDC1F181EE41C601EF5202E1F5D1EE01FCE1EEE1EF920C020E31E0921611F931FFC1DDA1EC51F9B1FA71FCA208A1F4D1F9B213620FD20A8213A24BA2CE02A93254B226D22ED223520992046213B22B9213A200620B81FBF1F1521451FA920C620EE1F1520A21F4E1F981F1020B02084208D1E781EBB20871EEB1DBF1C661FAE1F561F541E071F2A1F9A20071F33215521C21F311F091F1C202F20082152214A1EC41E751E9D206A1EB41D2A1F7F207C215C21A920B821052226247C2F4A2A7624F6228621F422FC2028224A209122AB1F6521F5218F1FB01F8C1F5B200D1FEC2128220820301F961E5C1F9F20D220221F4E1E961F711F0C1E611E4C1E1B208D1FA61F331FD41E9B1EDF1ED81FCE1F90200521C81E591F3D20BF1E391F891F83204D20441FF9200B202B1F24202821F41F8F21661F46200222F122362D6E2B9A25BE21FD206121F5217121E22130206F21D3218C20BE1EA02041219E21072070217C200920A621F821631FDB1F691E7C1FD91F361FFD1E481FC61E441E2E1FBD1E391F8E1E7B1DE920691FB41F7021121E8C1EAA1E941FC51EF91E6A1F59208E1E80207F1F4A21FD214D1FF1202F2004218921FB210D21BE21F623772DBE299525A6233C23A820FB20FE203C219120C72074228721B720E4208820FB20F32022208B2032206D21D21F8A203B21F71FC02031203A1ED11F24207E1F991FB01FF5200621AC1F171E491DA71EE31FE01FB91FFA1ECB1F01221420411F6E207F20721F8820611F1320062017205C1EDD20F7205E2103222C2269220925F32E692B4E259123D52077218E227B217D2163210422662181211A21EE20811F43202B201E210A2250201D20F12090207A20481F2921BB20132055200E1F4F200720A721A720661F0C1FA420F21F3C1F971EEF1F90214820A21EEB21A41F421FF11ECA213D1F65209C1E2F21B91F9D228F218C22B221BA207F21642275223524C430F929F226EC215E225B2228222E237C215B213421622211206F20CA20DF2187203A213423D1204C23C5211A206E212522621F2121552088202520821F2321AA206D2148210E21A81EF4204120FB202B1EA620EC1FC11F541FDF1E9D1EE31EB61F43209F216C22411F4A214920C71F641FE02082207220A7207223B1221A255B2F432C7C247A2175211A214221C7205B21C71FE21FF8203821F120501FDF21D821AA21D11E54211622D91FB91F6C1FE51FE920F71FE51D9E1D0022831FFC20401FBF20A8216C204820A91F701F541E7F1F9A1FA81F6B1E6A1E6020C81E381FB1202C225621912002210822361FE81E82218E207120D0205420B421632324247F2E322BEF24FE221A22A8215321E11F8420D02081218122D720A11FC41F582019216220F12207210F203F206C21BB202E20CA20861FD61F291F781FD1208920462244225E224C21F020551FC51E2121401E1B207620E81FC81F701EBF20CC1F74201421951F3D2085202122FE20A920D823C82050210B23F21F4722ED228523812ED62A8524D7236A21C7203E213C214521DE21E421AE1F9F210321E4216120F620C4219F1F8F229A200721E1206521B0205E1F0C20F01D9F1E5120881FE12125212022F622E7215821E4201F20541FC21F2821DE20FA1EA81FFB1F5D20741F172070204121142030214B2200217F21372023205322E3209222CE2281222C24022E9F2A4724F721612254225122C42128216D22B1213122E7219D2136205322F8204D21B32013210D1F551FE31F34201E20511FA01FAE1E651F2520501F4421CC218120FB21F2213A213F2033238A21971F25222B21C520481F722059209B20A221D8210E224C237D211223A9239022FD217F21D520B7212B220422EC225823272F462B812437238D226D226920DF21AB222221062030213022D51F15213223112155212722BF206121B120B920FE2162221F21C21FBB1EB32001219B1F6C215D21EB21CC222B212D1F192244224822EF218C2260219F228221BA213521D520C7223E207A21A72146216E22FE2070227C23C423CA2196228B22E123CE227225CD2FC82BD024B721F2219922B7211E21F620E622252121231222B520C5200120AF20A520CB203B21AF236B20DD1FA5214121A1208B20B2200E20E71F7A20382059211A21522210221B22B8211F21C9222A212021662148222B225C22B820A2206A21DF22C620AB2046219A203D21BB21C0243F24FC22612399233F2551250E24AE2E532B1E255622B82337222F22A422FB213522CC20A321F821EC2096216722F421C2226E20AF215621B821B92252206121E820B921D521CC208222A9217F22D3217F225F22EB225C2200219D226B22CD20C122D621CE203A20D722C620B220B922CD2132229B22AE201B235622E1232326B9255523DF22CE22F6234923D325492F632CAE256922A5210823A522632267221E226021B72265227020BB217B21B821A2221F23622000223821BD2035204A22442157217C22E520F4211722FB22FE2033228A229D23FC21DF238222B922C322392121223D21B1200221DA221422B4242E23E422D02306238323E922C024D225C424BC239E2389234122A92389243D2F3D2C102752228C239322C022DE22B023AF2200227F23BE218624BB21632257226D218B22B92119224C2159217E2123222022C02176203C2182212D220E22C3223E239E23C4235522F823BF228223A921DF214D218F2032213F210223C12242245F279D258B244825A924512450251725BB24EA221D2462231424A1235A26B030B82C45261625B6241D23F7211B232D23FD23F6227221032335222E247F22722386232722E022EA21F5213422B021B921B4211423402112212222A5227D212B23C923BB249825BF23F82394238221B221A222BC211A22AE21D32327220D252C2849285628E825DB25FF242225D42597279125A4252025EC231F24612650269630952DAE26F82178226C227F243023D22227241A22112472229C226A216E2236235923B7223723762467211C22522113243222FA22E8222622C222402265222E239C2383243C25EB24172407243B22D2228D235F233E2432214B2309242826A2287A2A572A0A298C26D02527260826BC26EE258F255E26DF24B9254D27E82696312C2C592541230123DE23A0225C227223802288243F2193220821FB210521D61F67213722662386220021B520A423FD2378231D24DC2456223C2349233A237C2330244A25EF244424592397214F22A4227823E5253422902205249625DE250828C027F828E6260E249A27C928D52AF02A0F2970265C26702628297329A9282D31382CF7278323D7249D24EC218B216121CA239B247125F023622128227224B524852227239323F023E220D7211D229122292439264425D4230A243722D2242A25B0288C27A726F6227A22CB23C72306233C25F126A9232124842448251027962606271D2641240326D827B428202A602B602A862A6028712880288A2A0B2AC331042D0A26C82333237723D522A423B9228122D123D623E1227B2341236A231E249224F0221225F0239A2248234B23C923602510273D26BB255D244224D623F626E728DB29F8277724CA21D024B524AD26E828FE2880267A248A24D924C6254E244125592410240226D02AF42B3D2B8F2A0F2BC729F22A9829AE2A2D2AA92825312A2DA1278224B5245123F623FE219B22D123B923AD223023422303245924C3243E243B262D25EE246925EC23DC268826D0261E27AC26DA222A24EA25C0253828632BD52BA9288124DB2481230626E127CA2AAB2A9225F2235324D923EA2369231F239A238D258A28D42A932A012AA9290B2CEE2BBA29B329862A55291F2701313F2DA92664233324162547229023DB216924B023FE23A32393246523D924E0242F2642275727F526DD289927BD276C29E4272A26E3268A233D23C125F027B52A192C9A2DFA28F924D024A7257E27F827322A9629E6251324CB2266238424C32176230A2388235026A9293C2C2A2BB82B132B9E2B692B002990278927E4251430E32A6525B7240F231E242B23F1222322CC22C9232025B8259B24BD23CF232925EB27BF27F32655282B26982640285D279924CF24E3252D2351243D275B28FD2A662B172B982AD02491226427282A77297229B72AED273F236A23EC23192377233F22DA2252251929682CD02B07299329362AF5296128962506259C2501258C30E32C3926C8249124212408229B218E23842425263527C52575246922E62345253A2781280A279F27F2250C25B8240C261F2425235526BD24E0256329572A992C872B912B40280E232B22A026DF28652A052C632ACB272F24C12322214722D121D82423225026FA29202BA92AB529632AAF2876271327C8241B251C24DD24732F732D7C278424EF236B2404246E25982681273F2773271225EA2269256723C923E3252B25F524D024E024D0217423F924FA231D2357241A251E2782293B2B0B2DDA2B222A4927CF220423842472282A29A02B8B2930292925E122CD2200223521D4227B241026AF27B92AE4285728492713263F2329255723EE22A6237025F0300B2F302886253625E3241225772602289C2BD12814286027AA258524BC241D243126E324E6239224CC23EC241E277B257F27A1250924D5267F27322A942C3E2C412CF62A1A28BB22C8220325BC269729D72BA32917293B264924F2210F2285238A24CF25EE27BD28D128F7275D26B425BC23DB221A24FC20DC23F3245A27B530652FA4287F2654253C24C7256328EA29712A7429462944280C2596228E229122F8222824C2237024C7264B261528402A6129AE27A325FB269A28B229292C032B6A2B2829E9251C245924B625622607278D28E3287F2710247D24C2211521E921B5230D2663287528E127A324ED2330247B247423B02448239724FF25AE2873331730C22BD8280928BC27BD28EE2A8C2BFC2BA3292C27D0249324EC2431249D239A251B25A3232D28BE27A4299B2B742B012B1829AD25E326372B3C2AA12BF62BB42AEC275525862468252727A3254026D12776278326F423782264217122A3236B23F3255E2720272827B125FC247C25DF24462402248623C0248226722B3E352232522BB927CE27CF251D287E29F62A1D2A722881270C274B24CE22CD2399232025FE2632292B2B752C242C342DB72DDD2BAF27322569256A28742A782AD62A61283B260A272D24B925C126E62604264A2637271D2716265125B622E2219722D825F8258625082673271626B826AA273E275A260A270F258927FA291B2C743346338F2CEA2908278729A9261629B4292229412819286627FF247A23E1246C253D27ED280E2C482C522CE52B742BED2C682AD8266E2425265C27EC27B428F82753267F247A269B25FB254425902690279A29CC29AB2A2127C12526246723A322F0241A27E027242857297829C829E82A152AD329C528A828A9292C28B82AB7337C31162B20288226BA261C28F2264728E9289F29112AD4289525E324572383251E27202B1E2C3F2B8728FC28052BE42BF8271C284D25E02224261E25E624D3267A2590244F24BC22C5237225E3284F29462BD72B0C2BB72990286A25FA23A4246324C8254A28E828CB291F2A6E2A9B2BEE2AE12802288A262E260227E4289A320431562A79271E26CB278C289026BA26B12A9E2A562A2629372547234C269B277B28B02AC6297E29032AF329762BBD2B8A28BD243223D3230D242B24F524D62356236324EC238722A7259228E7296D2C272C752E0A2C172C27286D268A240C246F259F27092A6E2B352C6F29082A942BDA281027F4269A260827E22545265030C62E14286B2522269A2736276427FC28552A312C652A9A281A261526EE26AF279628E92850284F298A272128372AF92AD229E5251923BF2282232F216324E523AA249F24A324DA248726FE271E2BA02BD42BBF2C6C2A0D2A4B2936255425D124C2240D2676280228812B402B432AB22ACC2779259A265425862537274726DD2F532DC6287F2687262E2935294E293729DC2AA92A542AFB26FA234A2500274B287B281B2930253726E22640277329BA2ABC275324C322D32346244324A8267527FC2671240A2568269727A129022D8F2ABD2AA029E62936285F245625C6248A23CE2138249E235925B7274027B926DB27D3247D2260244C23BD230C247E2559302D2E5D286F28F6290B2A302B3829772A8D2CCA2C1E2A162807259325A226362818296E27732648278927E728B72C492B04278F24E3234B243725F1277F28992821283226EB26D426BB28C02AE02AC52AC02AD52AB027662527251D240724602476234624E4234D23DF24F7241626EE24F8220F2374236E23CC235E24F125F230492E332AB729F62A662B842BE02A142A882B512BAC2AAD260324C823F2251F25F1279E2410262726F3269E2A8D2B302CA7284924F421552365260F26F12895294D294227FB27002627273227532864273C273D2890249023EF23AA23A622362495238721DB224822072403242123DE234823E321B7212D225B247025DD27BF301A2E0F2BFF2A922C212DBD2B9029B62B312DE52A3128C8257A23F3238825F4276226EB266E27DF28A029D629BD2959270A268A255F24EC24C924B2250628A3281229A827A126462655257D256F252626BB24C9257D24EC215023B02414248D237D228E2545247825372562242623D9225323202410245023A825F62598282433622FE22B912C962BA22C752C472EE52CAC2B9F28A326E22454241924C6261B27F429BC2DA42A4B2B73279C2711262C26F42155219023DA22D223B42586260526BA2486252F25FD246924B5229B226323E1246D23F8233E246125C92735267B27C926F6277A260326E4251B247F23D32331238023AD2565264F299B27402B80334731882E422D0D2CEC2A382CCE2B932AD82AB0289B26FA23FE23E625C927082A222BF42B2D2BFB2C112887253B25E124172173239E225F236A25642323255D25AE26AD28A626FC257524E823A1223722A523592539255725E926B528D528582869276A27772719256727A22462235B2332245926A5278027E0282C2BC22C9D358F31552D9E2D2E2A212A6429C728B926C427032868260C263827FF26F829822B7C2B552C9C29F02A3D298A258F246C2246233C211924C423992515240126CB2628283E2B8F2BBB29B928DF241B24AD235124EB25DD25FC271A2BF72A6829D3288C26B226CF2548259123EA22A5240C2512256627712BF52AB92B512B8C2CF137EC33D62C492BD228DE278326C9257F27FE2527255C2722275326E3260F28502ADC281329C328D027EC234B2448243524B323AC249D249D254B250E26C027CE281C2C652D072CA42A2A2894257D237523CC2686285728E129A7295E291926AF25A627D8251E24FE23402537232A25AB2722278D273C2BBB29BF2A892A162B3834C832622E102B7F2BE42A3C299727C226FE243827C8292A2C71277927AC25E6253E25F124A8259625FB269C24C526A0268F25B4264F2A11286B28002965280A29C12BC22C482C3B2CC6281325B1240E25FA28BD2BAD2A85292B289327102610274929F627FC25FA24BF254425A324E7250B261027E1295928F827FB276027A32F8933572EB62DF42B5B29F02A7428D52705298E28712AA82B222A0626EB24502329260A264D26D628F1276B299529802B4F29282960281E2B3B2BF72A9D2A9529A32B692ED52BF82B9528D02547246E26E629A12BFF284A27EE25F0265E27F728B1293D297D288024412873279E24E8254E259D2509291D27C6257427112882306E30BE2BAD2AD42A11296B28B12612283226CE2542298F2601276A2559266B25B227D027AE27DB289F2BE129142DB62B242C292A44295F29FD299F29DC297429B629652C902B392A8F27B7241928A128712AD72CF729EF2734270B277E28442A0B2A2A28532744279B272E27C6264325AC2220221B273D26DF27DD2642284231682FFE27942774275128902531273825F22549272F277225CD261D274B28E7263828E828C7282C2A2B2B342C962BBB2C9D2BFD29752A762AB9292A28F1268A26FB2744290C29122701252D24BB27A328502CF82C4A2C11283C2810279128C82AFE2A8F2A2F2B57285B29DD2676253E24F12354278B291C2BE42C712A532B2534842DA4271D27E726F025A4253D25AC257E2632276728E127B0294F299B2ADF2870272B275528272AD72B0E2C6C2D192DA42B2A2AB32A9228F928F926EB261E2592241C2632278B240A256C2667277B287C2A5F2B0E2A2229DB264F26E227FA27BA282A2ADA2BE2291E2AE726E0256C261425EF29412BB02B412DB02B872DF237832E0829B8291D287026C925A5243725EA266A27EC28E3274229EB2A602B952789269126C7260629FF29EE2BE52BE52C542E4A2AA1267D23AE2439250224D424CC23C325B524B724DC2537289C298D2A2D2BB829EC2BFA28C026B025B524D52673272C28752A2D2834284F25EF22E2266928132A7D2B022CC82BDD2CF12D01377130CD2BE02AC529312892266E246A2675276428142AB629082AD127DD26DA274E26E22573266C279928742B802BF72BCE291727F22411242322C424D7223B22FF22CE24A124F7253928EA291D2B952AB02A8D2B6D2A482A142607240425A8257D2795272327EC2604281926E3255027F229022CB82BEA284829442BC82CD635FE30D12D2F2B792AC62812276A27D3270F2A3E2ABF2BEA29062AAC287627A02477251925DA25F6263C27352A0A2A2A28B42517250B238D22C7220624ED23092475249E24C6262929522A992BD02D272B622B052BE92A4D299E276223EA224324A0274329092AF1272E288826A6269729FD2ADB2BE62B372A8E2944289D2AAF344031372E422FF52A2B2BB42B972A0329C029B12B432D5D2C492AD12954272F28DD2673261D25162773257328CB267E2678247125D222E922192528260926EB25BE24552539299628572AC42BC52C452A302B102AD8273A26DF2778238923C5252129BD2AED2A1A2B1C2A21291229BF2AFD2BE129A92D5128DB27502B8F2C9835F932EB2CE22DE12CB42DAD2F4D2CD12A2A2BB02AFF2CCA2C542AA528B7288627E42AC52ADF2720246A2634263724F1254E257F235C2524251B26C1267928D0276F2749271C2761286728E228E529E1271D27D12709279126EF26B225EC239A268E28C42B962BE12A812B44281528E42C882B362A752B002A162A692DB62B0837F931C92ED12B052D142EB72C842DDA2B1C2C742B7429D828532732272829F82BB42B1A2B9028B126B524A926AD267024FD23D525C8258627E029BB2A1B2A582AF72AFF299128CE265D27D427CB2528267C2782269F2861282F295C25EC248826BC29B729B02AC82C4B2B1E289827262B9D2CC72D802BE92B922BD82D7F2E2437632F2D2C862BB72C9B2AEF2B862DAC2C452B25271B29AE257927C726EC28E528DC2A7C298E28B8253824AB2436249D266E2567253728FB28C02BEC2BD729BD2AF92B182C862844262F283D26212467238F2510277F287828A6271626D325C42579298F2A4C2B272A51284A273527F02A3E2A132BB92BD32C0F2D3B2C6B2DC8350A2DF929DE282528A62A962A8E29FE2B83288926DB257A2428270C27EC280F289627A72711268A26262573259B274427AC25AF25AB277D2A0C2CF82A192A062BA82BDA2A212A4C278624DD258125CF23B024E3285126B5267D285B27AF260F27C2266228322A54282F29DF26DA2776292A29A12868294D2B122CCC2D952C47349E2E9A29C229A42ABE2AC92A7829DE290327C524E6255827B92572261A277127B027C626B12550280D28C6261E2A5428E7270629F82A0B2ACD2AE12A36295A2887292829FC26F12421251B26212763289229852A6B292627D4248624C6256324C1252D29BD2856271D260625FE28BB27B6264727182A7F2A142BA82B512B3931C730952A822A1E2A982B952C4C2A33286D26FA243B25D224F526FE26BA26CA27D826EF27F2273B2B3C2CA52A972AB52CCF2A102A612AA72A552BDC29172904273629D728BE26EA268A263F26C0289D28702B8A2C2F28B4278F26ED24E926DE25B9254826C62442263025C327D1267127CF275428DF28892A702C562A5C29AE31C82FD12AD829EC29732BBF2B6E29C2259F26CD254D27AE28EC2765277028D5243826802AF72A672BDE2B98293F2A912B3F2C592BD82B182A0F2B572BDA2A662B402B632BEE2AD1264B27E728F6290929AB2A9129802738259C2893280E2721274B253124F7249D249A26B22681280C2BC529AF2A292A0A2A5A296B285928FF304E2F7B2A292B9B2B592AA729BD27DA2695286B29B9298A2CA929B6282728F5250F28C3282C2B552C692B242A1029D629FD2A022BF02AD529062CFD2A4F2A4A297D2B4D2BC22B572809286A288528FE26AB271026C1254C25DD285D275C27D826CC248B2325249824B22775285A2B5C2BF329AB29C2271327E5254F26F327E032D131272B342B7F2C8B2B6F299D26CA27FF2A532C942B282DCE2A522949278D2742278129EF2BF82A7C2B392944290B2AC529A628BE27EE282729E7280326C226512AFE2ADC2CEF2739270D27D625F024C624AD25B523D025842598270B259E245D25F62218253A2640274F29682A8A2B6A2B0129DA26EC2531278926BE29A235CF2F872AED29362AFA2970281F286D29032B502BA22C8D2B352AC026F0258D27E2268127582A4B2BFC27EF2522255926F524BD25F7245D2571268C241E2518287228482A252A8626EF25F5232B232C23652517252327AA26852674245225DE249924AA235324912695284A2AE82B1C2DE3291F289726C425CF273129F72BFE36BB2F432B7528432902283E276D2ABF2A182BC72A822A6C2A2D28D1273827B1289229922B6E2AA529652685287E257B259E236C25142407248925E1256D243F26B427F726122714245123DF247F244924C725A028982A4E2A48297428CE272528B4279A2557267328672BB02B542CDA2BD12AA728E526362576284E2A492D28381C316D2B5E2A4F2A6D2984293D2BF929362B772A4F2B5F291A28AA287B27642BF32C882B372A6D2AF527FD2559272F277524372445235E24F0259424DA26B42698270427EF2649242F246B25E824DD2526286E2A502B222AB62CE62B8B2A722B72294C29A527E927ED2C322CC02D702B002828286728452784295A2CFA2D9C36962F2D2A94288C27B32AA72BC4298F2AF12B682BC12BB02881268826C4283A2A0D2B582A5E29D62AA9281B28E426A626AC230A26D4265D268828CC274428D126452636262D2673263B25DF25BF27F927D4285D2C7F2BC12B252DCC2DD22D2E2B092A9A271A279E260C2A3F2B2B2B122B1E278727C2293029692AD32C712EF935782FAF2A7D2730276D2AAD2C4F2C1E2D042DC92CC42BEA2890256725C9258F28D926082892274628122A582C3B2A2F27CA26CA278729712AB429862A9C276A26CE2756262F2982283A274127AE288129A02C8C2D332C012E9F2DEC2B672DC72B0D2A55279F257827E128E4294E2A6C2BE328BF28112AEA2C872B2B2BAA2C7034FE2F22297327B827CC29842CC52C2D2C522E942CCD2BD4274B25C426F227C928B82893278D2806295A2B9B2C0A2BB229E2282A2B0D2DFB2A8D2A7828C128EA26E42748295D2AD4282128022ACD2A2529132C542CCE2CE32D852D932DE12A7129B52644265F275B259D29BC2B9B29952A5D28DD2ABC2C572A8A2A012B652ACD33D72D1B2937274128F2292C2CB32C552C622BFB2B902824268826EF273F297A2A492A9828BF271029CA2AA02DA42C752B082B6D2D1E2CA6280F2847264426CA24E026AE29A52B74298D29792CD229C42AEF2AF32A70291A2B9D2D072D3B2B54299227C3260C2449276A29A62C532BF32AE12A3C2D562DB32CA22CD12A0C2A6133242EDD28A128BD276C2A932C422B442D2F2C4629A727412616288729C02A0D2EB128D72804291B29DA290C2AA02B922A7628F6282C289B27CF26BB25AA276827EF276828012865289329DA29EE28D92719270128D926B428A52A2A2DDC2C3E2AF5274D26D125BD27B12BE12B0B2C742C232CA12C502C3A2CD52B992A882A8134412F902ADA28EB28F629422B992C922C9C29AE286D276027C126052AB02AF82DE22A7C29D8285629B829802995285B298B258527A5267425A1264B28942741286F283A28F8275929702A8B2A2B2AB1270B26B82637266D27BF288B286E2A552B1029B928FF26B7286C2A4F290F2AF729CE29902C132DB82A2C2C362AD62B9B337931B92C47298128A3297A2B732A7F2A6F29ED272328222A962B892A8B2B402A8328A728FE29A82B252C272BFB29FF26EC265226D7242526F9281F298529002B3E2A50289826C6266328D9280C2A03283628E026DE26A025B627AE28D328292BF02A002A0A2ABE2ACB299828FD260A285129602A482DE42C6B2D1E2C532CD334C430FC2B892A2329EB29DE2B1729592736265A274C29E12B322CAF2B412C3C2AD42AA52ADF2B542C662DA92A1428EC25E122CC23A2249024F329F82A7D2C3A2ACF2A592A082B5128052771278C29AD28802731282227F3245B2B6C2AEB2B412B012ACC2BC12A5B296829102755265B267726EF288429AF2B892BC62B542BF632A32FC02AA227B4286A2AAF2B042A8F26DC266E286D2A502C702B712B8E2BDE2B9A29612A302AE72CC82D1F2A6028D7242A24F6231525082531291D2B7B2C6D2BA42B2B2C4C2CE1299527AD25CA273126B0266B28C22605294D2A022D9F2C4F2BF829F629C1284F27CF2637269C2697275A27F72838290129082B072B682AF432F430C22879281E2A332BE82AE0276F278926742A842C832B7C2A042B182C892B382A092880289E2A302CAA295D29A226F7255024BE259725B9299B29092A082BE32BBA2C692AB7296127132604264D27EF2AA0290A2A8C28C62B972C802BCB2B9729BF284325872574267A253C28A7296D2C642B7129F0270D2AC128A0283C336F2FAC2AC7293B2A962AAB29EE26B12635288729892BC3271C29DB281329A62A1829B126FA255A29B6281F286C280927DA269F2646278F29652A4A2A532B6A2B8B2ABD2A1529792525259A244D274128792A452B362AEB27DA2B742B802E232E162B15294A25CF231E279728E229992BB12B392C812E212A35296E281A2964319630E72A142BDD2AD72AC529D52663298C2AB32B5E2B9A287F2657272227A029DB28F3253F26BB2675279F27AD272628C127BE299B29ED2BC32B982BD82AF7296F29D4284227EF232322502669298929712B722D092B4E29482A552ABB2B6D2CF42C3F29BA26F926FC278329982A3B2CF92AEC2A562CAE2B282BD72C2E2C6833DF2E442BF928E22948291E290D28AE298E2B102DB32DE22A452A4D286D291D29F6280A287128D827A0272F27BF29242B1B2AA12B8C29862A95292E292E291D289626FF2630264826CE2669284529FC2A242DAF2C8729F529632B9A2B872C0F2C3E2DC228A02706276F27092AB12971294B2B632CAD2CB02B862D342D992C54362331672A6E288C278B284F27BB28162AD42D3F2DA52D6B2DC92CD62B242B442B9929FB2BE3295A2AD329B4280C2CE72A8F2A352BE92A492A342926299327352774285B29CC284828C928CE2A432D022ADC2CBC2B9F2B6F2C182D7D2A5C2BCB29B82B7129E72882262929692A7C295129082AC92B002D792C152C3A2C5B2D12369830202A69260525C4259E27B629612CF42CAA2C152CBD2BC72B2C2C5D2AFE2A2E2BDF2A2F2BBA2A4E2AFD29262C752B0E2B792CAC28B9270028A428D727FC28032A8729A729BE29812AEE290A2A262AFB29862A242C3929B4290B2AD7272D280A2929299F298025C3268F28E927D428D2279329232B53294D2AFE2A2D2B0636E32FAD29962599239E25F62694283C2B7A2C5A2AB82BD82B982ADE29E22A66295428562B222BA329A32A142B0E2CC72B6629752A87296F27D5297E29F929C62A382A9229FE29652823291328E726B4252E2823289527DD26F72785255227B927952712299F29CF28012956252927B1289E29C628FE27832811298728C62B9236153095280D262D25EE264629342CC92C742A452B352A512C262A872A2F2A6B28F127352923277B27BC26AA262C26E0267B261F26C92516277E27E5293A2AC527BF27F726E1257625482852279F27A9287528982A1C279A242D2583255229792AE02AAF2ADD29AF29BC2A90281E296D2BAC2C792BC02AEE28AF289728162AD734582F86299C29582742276628922BF42DFC2BEA2B452DB62C862B9A29B22765272E27B6265A25DF257B2505258725AE260D24F0241624662595279A269A27D526552552249125912604286A271A285A2B242ACE2C62281724D425BF261E28CC28442AF92B4A2E842BC52B8F2AC02B722C812C882CB62B7A2AEB2ACA2A1B2C1235BF30F82A0429A3286E2716293F2C512D4E2D6D2CD92AC32B672AE228D226F12554259D24D325F72493251D24A025E72430257E2481259925FD2612261A269A26E424A9256426C023B02781278229922A8F2AFF2A9228CC261427C028AE2BAB2A8D2B3B2DB12DE42CAF2A56294E29E82B842B9F2C712C0A2A40295628A5283831B131342B2429102733272C286F2C0C2CA52C5D2CD02A75290F2723258626CD251B257D26E32593277226B8264A28502722263A275C27AA29EC285C252F26E1268827D3268827022616280528502ABE2A602AA72A312869269E284D27BB29012BE12C8E2CF52C9B2A592B9227D726672A162B2D2A862AA9297E273D28782A67324C31252D912AE028F8276E266B2AD029F12B5C2A752645263B27D625A225FD261927DC283E29532A052B202AFB29252A10297929FB2A722BA528FF26C225A727A4294F2BE32AC82758298529E628CC27502AA42AF9278E269B268D265529DB29F12BF32B362B7E2CA2298C26A22517266227E628912994287F28BB28662ABC34A030A22CEA29E026F9251E258826A12751275727C02724277227DD26F82750292E2909284E2AC829282C032B212B7228BA25992861267927D227D126EC243628652A692DD62B0A2700273E253E284325942651284A270F260D286B293B292C291A28D6299F2B0229E7297027E42536253A27C025FA28B62A782BA42C4F2BF334C631872D5F2A99291928A1262D27262ACD2BB52A062C182D622A572A172C202CD929072A9D29DB2A562A5B29BB27F9253E276829CD285827BD27432729272129912ABF2BFE2A23283B272529E2296C286028C929F029DE2B422BE329D42BF12A9A2C02298128C1274327CE2553261D268024DA289E2A602A7A2BC12B592C46333031C62D692B072C472B4F2B032A142D5E2D162CCD2BF72CCA2CCA2B9A2B682C8E2A212A3A2AE22CF92ACF283B2957285329392AB6295C2835263F272428302A422AE62A092A0E29F4284E2AEC2BF72BC12A492BAC2B0B2CAB2B0B2BEB2AF22ABD2CC72A0D29FF299D28FC26AF259A253B271A29432AF629C929AD295A2AC231CA33BD2B872A0E2CD92C742A572BC02A872BFB2EDC2CE82C1C2B5029692AA129F328A62B162AD72CB82B182ACC28E7280829352A4029D125C626CE27FE28B12BF62AD528C72616276427A129FB2AF229532AEF2BC62BC72A572CA12ADF2A8F2BE42BB42A0D2BBA296A2B7729A42741278D261B29012AE5282D2AFF298C2AAC349330092C9229972BAB29942A052A232BAB2C0C2DA02E9C2CBF2AD12AA828A628A828142B432C7D2CA12AEE2A512A73287D28EF295E289426D329F028B82AC529B22B692959280829BF271A286D28A5263927C12877297D2A8B2B092BCC2A802BBF2C312CF22B8F2C922CF32BA42A3329FD268F29BE2AF92A132C8B2CAE2D7B35A32EB9281B28CF26C4274A29C129D42B592BB02CFE2CBF2CAE2A6528A627BC28AB28A92B072C212C602BA0290828AA267E296228C6274C28B5288D294329E2281F2A632A8F29142858270E2A602843259826DC296C2A3F2CFF2B262C802D4C2D142BFF2B242D092DF32ADC2ACF28AD282E280A29B82A2E2C8C2A5A2D7B2E1B35DE2D11294727C428DA297A2B4D2B022E692D732D502B922B892A5527A6276128C4287229162B992A4B278726EA2402267B2476279A278927E1287028CC26F828EC28A626492768267B2678281428FD2445273729382875275A29E629EF2A4C2ACC2BA42BED2AFE290C2986285E270F27B828F729B42CD72BAE2C902B8B2B7C35D52E4B292A287A2A9429642C9F2B492B4D2CD32CD02B982AC429AB26F326AC26772898294B2A9628622758253B262D252C254827E026F3287828F327CC257725B2274927392572251E264027E224AD23ED26782526272F26F127D9292829F5279C29132B892CDE2984296A28F62643283C28612AD52B0C2B212DF52C122C983525304B2C742B6B2AD42B172ADA2BD629A4288C2855285D289B27232A1C2A162A592ACD2A7D2A3C2A99281B28A6279D27FC27A12861290D2CA82B5329C62855284F290B29CE2647258D266327F825CE24FA262728252789266027A6274B27C5256B29662BA72A572A912AAF2A6827F627D326C029D12B6A2B6A2C522CFE2DED368B2F1A2CA82C442D212D452CB92AF327F226A227DA26F128C82A782B352A8629F628B92B382B3A2A88291629CE2C0C2AF6264F277B2A642BA02CD32BBA2A372CBC2A6228EB26A9255726AC26E8261C26E12813295A29F0298829F427D0286F294B2AEF2BE92C1829CC299229092A8E2A9327B128672A272B5C2B4C2C9A2DD4348C31AF2D9F2D462E032E172D882C8329E52AE229C228572A792B402DAB2B5729212949298B2C1B2C352D3F2BF629C3285828E429EE2A092E292DAE2C4E2C772DA42B8F2AD428E0253C294F2A522A302B562CF72CDB2A5E2A312A1C2BE82A442D112D782C832CFD2BC62B712B102B1C2BC128EA262828582A142CDD2BC62C8C35AB306E2DAA2C2E2EE22C562CDD2CDE2C742B1F291229322A382B092DEF2AD32660267A270F2DC92CF32A85290429D0288C27C927952A282C3D2A962A902AF12A662A19290D26ED251128CE2B182B362B062C472BA229A0296E2A032C5C2EEC2C342C4D2B712D722CFB2D282C662C052A402722277228382A762A9B2C092C1935A92FD32C8F2B8229342B0D2C352C962C882CD12BAB2AC22B4D2B8B2A492A7828DB25A828082CB02C3E2BE72C0B2A9A2A84290A2953290F2BE82A762A962965294629E826D425852654276F290A2B7F2AD52BDD2ADF28DD275B297929362B242B4F2BCB2C402C1E2C7C2D2A2F1A2C502AA62758282A28B328142CFB2AA62C4434A430EE2BC929D0284C275527E72801287728152913289C28B929EC294E29132708255E26252BDC2CBA2C112D362BEA287C270629C62A132BD22B4F2959285326462614256D2553260428D529352B3629092A3C2B2829A7265A2800272227A627332A302B5D2C702A302D132D642C6D2BDA2839298629E729462B4C2C1F2C47337A31182C262A29277027F02597252E27F5256227BB26D827792726262125E82417254627FA298B2C132B052CD52D202AAA274E28712AC82AAD2BA929D628D728EA268E255525F425B828FC296A2AA829922A3F2BB027CF2521258625D826F5268329602A422BD52AF12B482C022EEF2CCF2AFA299629812774299E2CEF2AAB33E62F99292B29D7259F24AC24CE24AE25C3261E277F250C25EA241A26902551240E256C2626265228FE2723290E2A5D27532792275B280F29342A8A2B212AFD28FB26CC247E256527A5286D29D7286129C229FE2ADC27F624C5258D25B7253D288529862B612B8229EE29142BF32B192C422CEF290A28942720286D2A212C7E34F62EF329E428FF25C7258324F9256526B2285D270C27E1264A26DC25F42512265D25D1257525C027F02674278229C72878276528F6265B27462A692AC829B12768278C26C625F6268827CB28FD2602298F2B542AE5294C27BA278D270128142BA72B3E2CF52BA229A42AE52BAF2A762CDB2CD3288428DE264729972C692E4C35F92C092916260827E72625251427782845290A2929299E273627F62591249E25AF25F4264E28BB287A2784271228142A2E29C0260A26BD264629082BC02A9229602975296E290929D928AA29A829F828A52CC72D4C2B1D299529FB288729B02C382E692C2F2C662A7B2B0E2B202C952CBD2B60299128BE27662A6F2C4F2E16365D2DC128192886264428632717294B2A092A252A632AED292F287A25BA2538258626FA27EB2CCE2A87298926FF27E8296B2908271B264026CB26E1260327EC276828A7274D28F629532AE928C12A472AD62D582BE92A5C2A7F2A8B2A3E2BE32BC52B892B7E2C132BCF2C9C2B7A2CB72A312B9A2AB52B7F2BD62B012C9C2E73361F2F6F294529D32BAA2B312B4629682C862B902C472C2A2C4B28AF253027582745277C2AA92AA52C6E2934256828792A472B2529BD26CE2637277B268425A526B1250B273529F728E3287C2AE2294629342BB929792AAF2B222B2C2A272A9E2CCE2BD22B5D2A292AB42B4A2BDB2BCB2C52287228092B752A8D2BC42B932E4536DA2F8D29DC2B6A2B3F2D442C372B9A2C902C592B472B75292329182680261C284427112AEC2A692B2629BF26F6267627D727D229CC29FF283527F2258524E42360256B28F226BA27E92853264A2970283F2AC62AFE294C284D27CE27FB287E2AB82A5F296F2A542ADF2CF92AD12B45299B28AD27802B332B1F2C302C952C1D36152FDC2AE329E52B8C2A6C2C852A8C29CE2B9F2BEB2AA927AE253626E3253E261E28F6285D2AAA2929271727862759289229D92BB12A152A8D29B42694254A2569240825B7248524EA2508263B268927522AB42C972A33288A2633287F29BA2B572BDC284028F129CA2DF62CFF2B4629D427AF27632A902B702CCE2C152EA3379D30002AEE298729C92A7F2A112A742B802ADB2762275B254C2566230125E025062533281028F227F126CD27F926A52AF22BAE2B162A602AD52A542A662A88298528992669246C24D123FC266828362AAA2AEA2B3E29FA2607273227E1297F2BDF2AFF29862B032D672E2B2C152B33292028FE27AD2AAB2CE32D332EC32CB0362330A22914288F288C29662AF2299A28AB2857257224762425245A2562270E297828552785265326C325B2253F280A282C2977299B28AC265E287829F329FE299E29E025B823E121D525AF259129CA2A2A2B232B1C27DC24AC25EE28B429AE2C032C402B912B8B2CF82BC12A7E2BA82943287028652A2E2C532DB62CF62D8636C430E52955290B276D273929D02812283A27D724B024D025ED242226C029AC2BF42A8D296629422671250224B72578285E290E2A122A00276E269227C726DB27DF262225E925E424C726F327FC281B29152A9E296527DA27432833274C295E2CF32DFA2D082B312C3C2D7B2BC928B327DE2608294C2BEC2B502DD22C012D513409315828B726D3264528172A0C2A86279A25BF244B23A9245F257226E328892A872B252BF82ADF27DC25B62413259927E927BF28122705286B2619267F265425D92563253A2769289F298A2ACA29D72A442B142937289A2910276D2696289029F82D872D202D232C332C082BF228D128EF278329DC2CA32D3D2D922DE12A26345A2EDD28A1273826EF27A22A3B2860262525D0247B25B0240127DF26F42871298B2B662C1E2CAD298028CB25D526612A1A289829FE290D2BF627A226EF253F243D25E7261227002782296D2BFB293F289B29F42702283D271027C3243626162BC42BD62C082C3C2B9D2CFE2A48296C290D2A6129362BCC28DD29162BBE2A0F34462FBC2AA726C6272328EF297729D425B52616241C25D226E82760272C275329F728DB2A302CDE2C882960288727162ADF29C0296A29642AF82AF528242560244125B4275B261826AB27AB29F8294B27DB260F28CD2732271E279A23F6243626E62AAB296A2AB729292A3328A0294329732B59286B27EA28AE28272AF92A39338030932C9C29E028842869290C283526052799279128E2289729F6266427B527D328DA2A662A5C2DE42A5529EC29D92800290428252937288B2B4E29A529AC2696270B27BD2659255628A128EF28C1276D26A427FF289D2775273823A7244F25F3285D2A312C1F2BCC2BAF28DF2A7B2BC1295A273F2647270F2AF42B512CB8351233B52D7D2A4F2AD12AC62A08278D266128EF296A2CFB2A66299B2A6B2B912AE129392A1D29AD2AAE28B32750286A292328F3279426A5268B284B29B827F82744289A25E3241F26DD255629F229E3251D29DB2A202A6E264326C1245626762577271A29522B522AC22B9E2BD92AAB29282AB726FD26E325EB280629122BB334B231152D872C642B4C2A8C296C294028562BD52C4B2C192C192BF0296A2C2C2B472A8E289227902664254727EA27D027C2269027EC25262502275B2985290D282228B227DF27F0261C28A72971299A27BC278C295F29C32680253825A726A3268B27E02A0B2CC02A062B922AF429BB2B20296E27FA27A827DB2A5D2CE22C27363630EC2AA72BE12A23295A29C226C328F02C8E2C4A2C612A3F291E288B28372AA5298C283B28B426B123EE23A125BF2639266E27DB24752699256C244A2771267C26C927A1293B284429F029AE279727EC272C2992270828C3275F26BB26A8264328FA285D2A6A29632A1F2A002B2E2B5928E32612296A28C32AEA2A8D2BB9331C30B32A5A280C2971285227F627272A722D5B2A4D29062A5F273F2724273B28F829CD29322991271B25C7248C261927A12672289F273A257225DA264727A327D52716282A286429652A212A472931281829A428E2276327EC26F1260527C229EC29D0283F288229AB2A6B2C842C472E89299528C228CC29062B602B522D9C35CD2DAF295029BD258B25222612266D27912859292D28C727A825A82691260C29642808298B2A5528E1268F2426264325A4264229F627EF27B8275F26232773266827FB256127A5274B279B2665266C258B26FC258325582841278525AE269828772A35293C29DE2A202C5E2BB52B2C2A072965282227142A3B2C2D2C352CFC36A42E592931263324222424241425A525A926AB2624257E254825E8256227A724B3264728FB285E297428DE2538255E240926162896297F283929812761282728B3265625EB256D23C824642472234D238825C32638275E27E1255925DB251928F928DC29152ADE29BA2BC829BE295D2A602639262328E328BF2A702EA12D92373C2DCC27AB24A7236123A624ED230027E22394258A2628277527D32883285E26712668299529D82A88276325962521268725F6270A292329662A59280B284A27822628255D248123CC2430250F254D2546250A28BB28E2270B265A261F27B2273329CA296429C627B9281A251C26D724282429269C273D29952C4E2C662D0E36F22D562653242625B62399241B2557265427D726BB2A702BC529832A142A2629B52952299C2BEF2CFE2983264C27A62844263F28D8282A298128F1256426762598269727BD285626AD2651275A2832273028562AEA28B8282B2A842A642B8E2B8E2936298928F02677267B25B823BB2464256224942628288528342AD52B0234BF2E65298F25EB237C25A523C825BA2656281D2A1C2B3B2B712B0029E6260129EE295B2BAB2B322DEC29E72675278628182816287A2AEC2BB1284A275C259624D028A829C429F027D1295F28A4284727E429AA2A97287928F629F0284A2CD5299F2C232C132B8F274D265F244324F7259024D7239D268B28692A412B1C2DDA36B2307529D625462420262224412665262F28B1285E2AD629B4295227A8262427A228802A0F2B422CD82AC0286F270E299629BA29AE2A8128E8285726C5266025712609273C285F27DD24482822271F275C295F2A3C271E27FD26CA26C726BD280B299B29B02B63283028BF26A225D9243825F725D428A32AE72B9D2C6A2D5037E12E27292C27972331237523D1242025972795263E275F28892885297029F126102A9D2B1C2CDD2CD62BE3292A2935271227AD28D9271A289D2ABF28CB25F423EE247F269E2493237F221625DE246E28592A362BE7263525E92532257E263825F328AA2A6B2B492C642AE628BB273F284C25ED255628C627182C062D6E2E2E35CD2D212896252B245C2313242B25612594258824CA25B027F0276028A82833298A29F1288B2888299B2A792995280B27F3260026A325D52748264F272D243A24BF23832382247A220022DC246726E727232A8329682865259224E624B124902842299B28212A4E2C152CBA28C52773274426F924822784279229E82A312BEA32B42C5628DC2308232022B921F8233523CA238C225A24C2222C255E26F926CE26A527BF2604271B27F627CA243F26B428F126E42513250A25DC25DA238B257A25BC2345234F245621FA227B2522278429042B542BDB2AAB26F425CE2327244025382905280D261826B0277D27E125BF2672248B25DD26B725842534278328E1301E2D10278A258423712496238922B12271238C22C323B922A9248A24ED2570255224E625E925CA2656258F24102562266125A423B3228D24D52485240625B525DE2513256E2382221E2566277529CC2A8A2A3D2BC528AD27D225F623D922F02403261825E224FF248425A7236E24F9254D285C25C625B22368230C23B626DB30312C6625DF235C242A24F3225222CD23AC21B42288228522F422AF22D6237C25C7246124C9271129BE283D267527F425D324C0249424C7230024AC234824CB261F263026B5220B2360230B288F283C29EF29A82AEA2875274D26BA223823A023C92315246F2336238923CA24A7265625F0264127E325FE24A824F225212701307C2CE925AB221F24B12456239D2327233523732407240422DF225223BF2493250F26022750284B29552BA52847297325BE25F224BB21D623C022A424CB25EF27472614249E23F5239B2313286B29F728312B3A2BAF278D28DF253A246523BD246F231F21302402231122112421247026C227D527C8269423FF25A926AF297432362C892652232123B024BD245023FA24262375227C22A4224C21552245259527BD26C027302B372A1D2C5E2AA6290627C0256C25C0220D23C82226253225B526322502222D2398225F23322782286D263728312754277925F826FB23BC2510232023A622E722E8215B243C22FA24A927BE290028F9252E26C6272A27DD290E35F72C72268F2267244D24AC248A230A251E24B822C32287236422CB2169252227DB286729082B202A812CED294829D325E723B6248D223F218523D22618259F2487238F2119236E22B72268263227E825FA26CA267824A524BE23A322A02229235B241B2306232B225A24D724B32606294E29DE282726CC258F284C2C342DBC37CB2C1C26B0244B223B25A625E82319248D24262403254423E922BA221F2593247D261E299D297F29D42AD0281C287025AF239B249F2316238C24042357239123BF22B02295215A23FF245D25CB26D228C327D0242A249B216F206722CB225725D9249E2551257E246226B92679283B2AD828CC2909289E265429492D2B2ECA35 + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 4F282D223122112002210A201C1FC41FCF1F541FCE1E2A1E3B1E8D1D3A1FDF1DCE1CE51E6F1EA11C7C1EC51DC91F521ED71E221E501F3A1F781E8E1E3F20AA1E4B1D0C1E251DAA1B221CC51D461DAA1D431DC61E8B1D8B1D1D1E2B1DE61CF71D3D1FAF1EA21E831C2A1E931DA01CF11E571DE61F4A1F001F541ECB208522B22D4D2B892337205122B8211E20BD2018214621E91E59207D1F841E301FD11E851EB31D5E1E2A1E381E4A1E6B1FBB1F0621661E0A20AF1EA61FE01E361F0520651E7E1E821F701E341F7C1DB11FA41DD31DA21FDC1ED11D9A1D501E3920541F571E971EAA1E371F3D1E861F781FD41E021F9D1E2120F620592017210321C123612D362B8124EC22062290206B209220AA21C11F7D22AE21AE21D31F601F931FE71FCB1F70205D1F3A1E4F1E4A20AF1F2B1F7420BB20AA1F8C1FD31FBE1CDF1E1420C31F7020951FDC1F931E1A1F431E0E1EAA1E541F111E901E341F061DF41E441F561F671FD81E981D601F8D1FAD1F971E8C1E1A1F1122BF1FB5202121A624AA2E9E2B6427EA23FF209F21F920E92196203E201A20D61F771F6B20791FD120591EA81F8D1FA41F7121DD1E471F631E112002200D21F21F73201F1FDA1DCD1CBC1E1620AC1F1021651E231E371F111E601D7E1E771F581FCA1F061FEC1EC91F0320E2208D1FA01F031F1E1F211ED41D831E2C1F751F8320CE214420CA21CD23ED2D882CC725D321D520F520AF210F211E2140219E20AF208420A71F3B1F94200020041F5920791F6C1FAD1FC41FC621221EA51E04227D1F491C571F081FAD1E691E4D20E8203120F91DB91EFA1E0A1EBB1F9D1EC120D41E2B1F961F3220F31EDF1E49201E20AC20E71E1220C11E5E1F801F9B1F9F1F6320491F522096224E23B72C5B2A802543220823632025225B208920E521C3205F21A9212E1FF41F21209120B020DA1F891F3521AF1F261F5820B020B91E2720311FF51ED21E411F991EBB1FD41F2D20891F871D0820F71D221FAB1E201FAC1FDB1FFE1FE41ED71F281F7D1F9420A720691FDE1F991FFF1F681FEC1F0D1FDC1FC221D720B3204E234124F62CA42A57231522A421ED20C6201522F620A020B7210721CB1F3620AD1F10211721812125208020D81F7D215221AC1F1D205D1E1E1EBE1FC91FBF1F2F1FB42063206A1FFF1F751FC71E321E551E961E221EA91F29211B1F041FA81FEC1FCB1E581F9020B91E2620451F981FA01F451F671FCD202A1F8B20871F4521F8219322A12D9C2AAF240622B32215212E1FE41F8E208320F020F61E27204F210622E81FC61FF2206920E7205921ED20331F2920CA21EA1EB81E6A1FC01EBA1F3E205B1FB71E151F9B1FD61F951E861E3D2067208B1DE320971F741EB91D821E531D741F011F561F271F4E1F8E1F3B20711FDD1EF41FA91F5C1F721FF51E9321E021EB23462E57297B24CA2265217E210920E31ED320B22101215820DF1F9F1F571FAD20B31EFE1EA220D1209420AF20151FBE1D3E209C1E1C20361E891DA01D8D1FE51EAB1E671FD81F531F2A1F4E1EED1D791D281EBD1F951E841FEC1DE5206C1FF01E3C1F981F911E7C207B205621511F201EA31FA220241FC11F481F0C202C225222542E0D2C0623BB20B9200421B91FE8206F20132272207A21D820111F8120A4202420D320B41FB8204120262081205C1E8C1F8B1EB4206A20891E9220891E621EF81E1C1F9C206F1FB81FD61D6E1F201FE61D851F23206E1F281FC01EB81FB11E761FB222AF1FE41E9220A420672004215B20602006215F208620CE1F762275230530622A9626A92282210D228F200921E72035216B21AC227821F9204B1F5D20BA209920412080208E20331EED1FAD1E8E20F81DFA1D2E1E4C1E461F9A1EE81DA71DEF1F1D1F331FAF1FDE1DBD1D401FA81DEC1E021E8C1EBB1E1D20BE1FD01FCA206820611F571F5B201A21531F371E3520FA1F781FC01FA11ECE203A223223E52F832BB024C6217A23D8209A1F842053200322C7215821D020B11FC81F751F6920C91F0020CD1F7922B91F42217B1EDF1EF01E121F8B1FF11E1B1E931DEE1E331ECC1D561F0A1E841E851EF71D261FE71EEA1F9C1EB01FF61F26207E1FEF1E7B2061209D209221691FBF21B520DB1F4821C5200020D21F2620EC1F6120B024D02E412C4D266922CD21C1205720BE20681F3321C1200E208221DE1F6B21B020CF21D4203220F21E59218C1FC91F6C1E85210E20C81F971F7A1EDF1EE31F181F851F7A1E6C1F1220BF1E421DBE1E2821971FA31E4F20CD1E361F2F21D220311F0121AC1FDC1F631EE71ED01FE51FE61FDD20C11FEB1FD42148200621F3217F249D2C1F2B422593226422BF223620C320B9211622BF2144204E208E1F891F7621551FC620CF200F203720CF1F2A1F8C1F5C20C520B720EB1E771E8A208B1E211E1C1D651F8F1F5D1F541EE91E1F1FAA200D1F7221B421C21F8F1F1F1F4A205B20D6202421601E3E1FCD1E9A20841ED31D381F632081216821AB20A1212D22FE23312F6B2AB72423238921EB222C2119225F201B237320BD211422AA1F0520C51FE4205F1FDF21B322EA1FDF1ECA1ED31FC420FE20771F871E811F931FA61E821EAF1E3E20BA1FCA1F721F0F1FC51EFE1E5020B31F0D210721191F961FFB1FBD1E1E1F531F7A206620B31FDB20F21F681F4A2039212320E221521F7720582272233D2DDA2BD5252422C9204C212222B421CE21FA1F8F211522C220D61ED520622114221020822199202220F321FC21411FF51F731EE21F3A204E1F121F5E1F9E1E9F1E151F831E5F1FBF1EE01D3E21A21FDE1F8C216E1E8C1E8D1E7F1FDB1E201F541F7820C41E9D20901F4D210022251F2021522030217921E9215D21002224246A2DF129CF25D0236D2326214C217621B3218A201421A9229E21D7200921FA20382118218620D2205420C6211520C220A820192011215620601ECA1F3220671F9B1FB91FEC20FC208B1F551EB41D851EAA1FBF1F5D1F0F1FA31F0E220F20191FAF207D208F1F9B20601F3A20F41F0020491E5D21262168215222832289222F251D2FAC2B31255923C420E5219F229A215C21B6210F22AB219221482152219C1F6C203D20082126224620282014217120AA201F1F2E21E6202A204620011FC220F61F7F21A020C31F031FAC207520651F731EEB1FAF2137205D1EF321A31F0D1F221FC4210F1F8820E31E2821B71F7C22CB21C222A621042169219A2293227C24D630E929F2262D22AE228B224F220823082246210C218622F71FA020C020ED215720F1200523D4203C231222EC1F90216322401FDA207020AF20F21F401FBC208A203521F320B320761EFC203220CB20541EB620A81FA71FA51FF51E511E881EE81F60209E217E22231F3F2158200920781FFD20CE205D2082204923CD222425842FCB2CB82418220C22772104222321FE217220612059212B22A021A91F5522952214226A1FC02134223620F81FB31F5A20152177203C1E0E1E3D220F207321281F1421D421AD20B8202A203620E01EB61F54201220021FD71EBA203D1F15200F21772262216621B4219922981F8B1F3822FA20AB2055210F2141221C24A124482FA42B6125A0238D22092212225620012151219221F8226921212036209820B321842057234E21782087200022D820AA20452100200A203F1FEF1FE820DE2069224F22B122AE215521931F591F8B21A21EAB20B82047202E20D71EC220EC1FAD2095211420B2200721E022A621B42198248621B5217B239020332304245C24292FD92A6824D9236D21E42066214D2119211C22DF21751F8E210D21F121A1201C21F621D11F12227320412128213421CA20751FED1F3A1EB81E5720A31FCB211C216E220623CD214621D8203E208A1F37203C21DA20C81E721FDC1F8220251F0C20CE20C92193209E21DD228D210522112180204E222B214A23C12382233225AF2EE22AD924CC219E22BE22802220226C219422C3216B224622BF2152203B22E4204721C0203D210D1F581FC61F5A204F20A81F731FC31E9D1FFE1FE81E3A21E321D320FE21E021AC219F20A523A021F81F41224921EB20AA1F92208020D1204C223C222522F1237022AB23982420235C223D2247219F22AF2365234524C4243230542B96247B236322AC227F20062258225D217F2025210122A51F702145233C2157211A228D207421CE20D120C52120223321D01FC81E8920D920D41F5A21B321E021F1226321611F1F226D22C2228922F522F4211D238B21D32115210321D422A120D2213B2212226923A921BF22A123F323CE22E223FB23512534246826DB30A42B8424D121E72182225121D220B820DF22E720B8228821B92062209D1F572060209620262185232D20A91F5C210E211D202D202820D71F751F24200320602120215022D121C0219C21562134236821422155211922092260228F206D20FE20E022F6209E207B214E217721D9217A249024F523BA24E624D826B52649251D2F1E2BB22440228823E32106229E221C22152297203D217C219A209821E321C221C7222D20C22148216021862254204521822034216A2105201322CA21D1224722C52241229D221A2285209C22CC221E21E5226322CD20CE1FA7229E208220A6229B214A228B22CB2058236922E9235F260C2761257425A2240126D124AC2666300A2CD1255D22B5210F230D238D226A2234227621A6228222ED20FD21C3212022032334237D204C226E21A4204C201A22E320E2205022EB202422B622E723EA214223B122A523BA211724E4227F23D5232E22D02275218120DA209922B52161243423352364246A24BB240923D024F626AC27D12719273C264C2411254025B62FFD2B072703227B2319230F232623D223F8222022C423EB21AF241822A3229322A421A922E0212522782180215E21D821C221FC20242066210622D22239238123B623E62380236E2248245F239B24F322D022D0210E212021DF2096220922B9236527382605267F27A82690257625D7266528A726B226482592258224DF26DF30E42C0C263C25C624C22362223923BE2375249123A3214A239E223A24A922E823DF235F221423DB21FA211D22ED215D213C21C0228D21B5212723F523D122152482249C246425A42332256F25F7220A23BD2315223A22812170238C21FC23EE2637284529BD28B2299D289526E92504284A27CA2780267A2451243B268326B530B62D29276622CF22C4224125FC23FF23DA24AB22C92439232723DF218F22BB23D823EA22302394247B215B222A218A237521C522D62387234924AA23D423722406248724DE25F125FA25EE2568241E244D24B0230A25FA21CD23C52349259427342A272C9C2B3E2AE82808278025DA25F925DE2664276A255725FF26B427BE32DB2CCC25D5236123CB24D223C423FB2498238525FD2124235D2122222B213E20702112223723BD22102182202F23F622E422FD23EB258D2456253625A32409240424CC25DC264326C225AB23B22335235623FD25E622C3234C242D25C024102750285F2B9D2AC026EF276A27EF28C428F927462777273B263F28D628A9299E335E2C2628EE23C225F42505236523A222C4244325DA25F2239A219C22D324B324552202234923DF232B21BE217F213D21062353263E278226C42602242425A2246E28592868293F26BB24552536241F227E2439277C2536266626D325C8267D26A4289E284C261326EF25852659285F29F729B42B162950288A27E92A2F2C2534EC2C65268224C624F12475245225D12317230E24F02345232B2462238523EF2300248E220325B923052314238E226E225124AA2792287628C4260C256623DF256E289C2B4E2B8C283F249A25DD23AE248A27022AEA295728D2260E26A92665252327D02565244924C227FA280F2A6B2A4F2BDD2A012B1B29FD29822B142BCF33C52DA828E7256B260C250D262C232C23FD235523E422BC23D0234F24A4242324A1239C25BD241525A72596235925F22442264428D828662595256C2582248B261F2B6B2D952B5028D3269B239C23F0242B2A702CCB289927B226FC24CB2482245A242D24A42489257527F927B328B7287F2BF52BE429372A6B2B282B842921336F2D3627442486255A26A623532440221624FF2221242A248725F523B9244024F7245526C526B326C928D2269D25E327AB2781276B288324952381248125F128C22A762E5D2B2F287F263C25FE244825012AF92B92290A2871244C2437258822CC23A9225C2283236926BE29B02AA72BE82A3D2CAB2C062BD729F6292928A230E32B8C2603269C24952549248D235A220F232E244C26B627422655256F24FC243E274627B726D728932602262F272E27C2250D26B52656239D234E25CA26FB299E2B682C2B2DAD275524962669274B2793296F2D472BE1267625422531245524DA2268224E24FF268229022BB2294D2B662C142D052C1829E12711271B267531AC2D1B274C26F8254625132315227D2331248D2601290D287427E224EF2424252B260128922771283326952433246C26C524DB237F260E24682420271228362BDD2B022DC62A552655234F2519267428032C9B2C402A152790252322042336228F24792163249827B1286E2ACC2B8F2C052C1E2B632AAC275126F8244F25932F822E2D290427D425E625F62431251C265927D0284D2AC5280D27502984264F24DF251A25AE257E257E259B2156232E254E247223A924DF2321255C27DB291B2D782CFF2BDA29E4258A230C235B261E28C92BCF2ACA2BD927E6244324EE228621B7225A232F2461262A2A6E2AA12BAF2AC8294A262227B924CC231C249825B1304C30E7299A2847272226CF2432256126602BCF2AFF2AC92AF9290F297B278B24D925B724872321242123FF23EE259F2429275F263524AC2547250A28702B1A2C782CFB2B472AFF24AE222423EE246C28DC2A2D2A562AED280E279523A02240237A23BA23C825D5275229102A4A2959287325BA235124F720BC2340248D26B02F3831692B1F2AB128722506255F26B628D52ADB2B6C2C3E2B7328C8257B246923F622032444239A23D5253425DA263C290329F22819272C26E426B528072B0A2BC22BAA2AA128B7241424DA24922528273329F9299729FF27802780231622AF211223B6249E27EC28F629F827F02586252925C323DA241A231B248C25F427A132CE31F02D7B2B632B32290F288028F72AF92D322C5D29E3273A276027E9256A247925BE24B5225F27D626C428242AA12A522B562BD32759264B2934292B2B6C2CF52BA329C927F7249824BB267C267727B128872861287B26D12401234A2370246823562557275128DF280727AF257725AD241A24F22352234D24F325B22A3E35EC322E2D732AE32AAB264E263528612A722B032AAF29BC285D261625D424AC23FC238B257C27E728E62A512BB92BB82C412CC6292B2756247F268728CE28352A1429A0274527502363248326FA26CC25C925F626862706272626602386228C2209252B257F256E26BA27B725E6254326BE259C25B4268F248526BF29BA2B19330C34402EF72C43290E2A0225CE27A929A2294329F328F828692744254D2536241A2595269C29152BC72B132B522AF22B7E2B0229D4252525DB241C26B727A7270D263824942549247824332455250226D82741284F29B0260026B524F9239D22BA231D26ED265027BE282328AE27632852288F283328D427B8274527652A013396325B2D1C2B26297627512783268D28EF28102AE22A8C2AAB281E27B423F923C82453295F2B732B1329D728BD29172BAB29AD2B7E27C222DD24C42362245A2630255B249A23C4218022B5232E2797279029702B9D2AA529BE290C27F0246C25D8231D259F27A528702974292A29192A602B092A5D29D526CA252A27F828E5318832DA2CBB29412702278A270D26A6266C2A8E2A1D2B062BE4271725A7251D252626EB29D929F329962A59299429AE2A382AB227592491231523E522EA2308236A229A23562369218E236A268627C32A5A2BA22DED2BFF2BB2284B273B252324BC243626DA28062A402CEF281C290B2B752A1829B3279F257C26AD25F625A42F6431972A3B270826E7261027A927ED281B2AAE2C6F2BD12AE228BB26BD25732546273D29CA292B2A362783277829D92AD82B34281C24E422FC22FF1F592350236924D9247024C323B0243E26372AD02A442B0E2DD12AEE2A3A2A9925CD25252523242D2590279027572B0F2B4D2AE52B352AB6274527D2249825F0261926842FFC2F552A6C27952542289329DA2AD929092BE42A252C8229F426AE25B8258526D927572A46277F26A72643262A29D92B692A66272124F8230C247923A925702770271B25772591256226CE28C22B3C2AF52AD42A812BA629F625C825FD24B123F7215A24A2237625062887277627FD296928CD2427259F230E247824AA25B030DB2E7728A026B5275628152B6B2ACA2A0F2CA82CF72A642A61276F25E824DA2517286D271427592769265C277E2B322C252963266A24B123FF239E26E1274B28C6285B2733272F26CB27FD29C32A972A162BD12B7C2921275425AB23DE232E246B23B22403241F23D324F82449260C26F723BE23B8230523D623802435261331532E8C2815260328F429162C472B9229D32A402B7D2CAC28F52469232E2496233027F224A7267C26BD259229502B172D0A2A0726E221AA22ED24CC24E8272529012A6528ED288A264027582747281828CF288329C726CC2421246B2381221E24872379219F2235225B24A924BB23CF24A5236A22D0216622AA24B9254428F430CC2C03297A27B829AC2CBD2B8F29392B262D832BCC2918283B2496237C24A826EC2581273F28A129202A482AD02ABA28AD27FC25E6230324B823E424A12767299C2A9F293228DD26C82533266A26F3261B26102740251B2219239D24B32385237522922528242326AF254525E723382344236A242324D3237826FD2651294B34A72D0D296729D529E02B872CB12D9D2CF82B89299228DB25A72450230C250226F7292B2E942B2B2CC328B028EC26B62638226321F6226A221323B5259E2661265025C225A72583252225E92305244424B42598239E2329241B255327172653270027E927592698262B2718259423E9230723F822BB25D926702AEC28D02BD533412F542C2A2C342C182B162CBF2B472BCA2B88293028CF243024BA2547273229AC2A642C1F2C172E6929A2278926A325C7215423F922CA23F0251B247825C025CB26FC286C272A276F265426C424212315247F256C257225F326E0285A295E2993288B2823280F268B280126F02393235924862637288B281F2AB72C162DFC356C2F8A2B592E4F2B912A38297428AB265D2833289826F225F3269E267529ED2A432BA82CE329772BCB29A426EF24B62211236F216F2488243526C12456265C26D227E82AD62BC62A922AA3278C25E12318247B258625F527D52AAF2A7D29D128E126EF26D4259325E5234C23B924D124D2242827BF2B352B122C5A2B632CF4372732022C792C8F2AAA2919283B26B1272926A824FC26E12696269527E3289D2ABE28C3286328FD273A24C0249424A424BD23F6246C25FD26AF26D7266B28C128A22B402D932CA72B8729F3277824A423502610283D28812AE229B0298226B3255C277D25512494246A2528230325F22743278E27CC2AF529212BE32A412B593400312B2E712CDA2C572C472A86284E2758258526D628742B8028482987275B267225F4247925BB252F27AF24FE26F3264526D427BD2A1729002AF32A142A142AF92BF32CC52C1B2D442A2827FF24B4245E283A2B3F2B522AF6283328F325BF26FE2881270B264C2524269E254F25562628268227332AA22830287928D32712309D32182E202ECD2CF929472BC7288928B5281D2772292D2CB72B2427A72529248B26252642260B2931287D299929C72B8B2AB42A6A29782B8D2B5A2CC72B142A882B732E422C0A2DE02941277B2492269129FB2B7329A0281A274527912769289A2973292E295425A6287328DE252C27B7252326A7296928CB260D28E8286531982F772BA52A402BEF288328BC278A28DC25CD24E828D726E0273E269E269F253127792766273B28682A2C292D2C5D2C3E2D3B2BAE2909292B2A092BD32AE829232A1E2D142CC12A53284925FE273928522AE72C9B2AB928D8273E2721289429AC2918285D273427CC27FF27A327CC255023F2222E28A327DE28F927D3282E322C2FA9279327AA27D128E72635288C25B725F0263927B0259526DE2657287C27DE27F12721277428D228372A352B982C4E2CE42A8C2A132AEC2947299B286727D9272C29F3283A2701250F24A227B4281D2C0D2D102C70281F28B2263628102A5A2A9F29432A132864294B27992573247B24E527D42A332C002ECE2A4A2B18348C2D0428C3270D287F27582737261A2688266327BF28402895298F28892A3B2A86287F26DE263A28A529402B452DD42C122C102BEE2BE52898290328C227ED25F7243B262B277124A4250A27F4276529E52B032C5E2A7D2963277F26B627902709283729F92A97292F2A2927EE258B26E4258E2A7E2C362C902D262CAC2D1D38832E76295B2BDA2ABA296028B225F3255F271E28732A0E2A5F2ABA2A3D2C79291628A2262526CA278128442AC22BAE2D2D2FB32BE72740244725CE25F324A4253124C325EC2463257B262D29472BF82B922C532A6E2CED2959283E261F2515276627D727602A46287D28DA255B236A279129242C662C7A2C262C092D332E05378F2FBD2B502CAA2B342AEF282D25A5261B28A229132C422B6C2A2F28EE272E29A4276B260426B4265F28632B312C172DA02ADD27BB2584244F224C25FF227122FA22E924152511263F28732AD82B522BA72B1D2CBA2A032B0D27F0246B25D025412779278727D52624288E26172659270C2A432C8A2C50293A2A9E2BFA2C3D36402F5D2DDE2BCA2BB8297F28DE2700286B2AF62A9E2CBF2A3C2B6F29D02848255F26D325FC25EC266127442B8E2BF3297627B8252F23C722D5223D24262421249224D124DA26E128DC29F02BDC2D472BC02BF12A1E2B202AE4287E2476238C241128A529CF2AFB276C28B526E7266F292B2B442C942CF22AFF29BB28112BC434EE2F7B2D8E2F842B8A2B662BBE2ACE28D6297E2BAF2C592CA72A3F2A8427D227E0266527E5253027C12501290D28CA2715259225A12275227424D925922581255124C3248928DF275B2ABC2BB22CFA29EF2A0F2A3928BA269628CB23B623EB25AD29982BEE2A252B392A0A296F28192ABA2B9D29A02DB42893289B2BC82C4F3503326E2C2E2EDF2C292EB02FC12CAB2BD12BEA2A8D2C592C922A23296B28EC26F42A492C4729AD25E826C026812514270826C8235C2515252D26C9261F282D275327D52789270F29EB29302AD72A042862276428E127E227CA27E026E3247D274129922C7F2B282B5E2B3F28AB27CB2CCD2BB32A402C792A6D2A392E692C01378230952D6D2B032D1A2E0E2C0A2D3C2C912CF22B4C2939286D26C0263B28C02A7C2BE62BCE29F22733250F2702278B257424D7255C252E27C1295D2A8C2968294E2AC629C9286827842890281326702689276526CA28D5287F2974263525DD262D2AF629242B8C2CDE2AC2271127B82A262CAF2D3D2B9F2B9F2BE22D382EE836A82ECB2B232BB52C942AA92BFB2CB42C612CAF28552A04263E27822676286E288B2AFC29D5290F27D2247625DC2521282526A825A4273F28E02AA62B6829FB29512BF02B5F299927B5281027C724BC2345250F27B0282229822859265D26F825D929BF2AF62A2A2A6E2815271127D82A412AB92A5A2B762CE02C302C8C2DE5351A2DE72992283B28A22A2D2A2A29972CBF299328DB266E245E273B271029B728C628632878260E274326BF267529C629102725268127762A012C912AF429802A8B2B402BEC2A322858258A26C925C8239324F62897265327A7288A27E526AF27FA26C028522A7A28E028EB262328032AAC29DE281329CB2A1B2CCC2D162D4F34DA2FBA2A442AD12AF52A5F2B052AC72BF428B1267A26BB2760260428F9271628452886272A262229CA29E428F52B9B2946292A2A8F2B102ACD2A5F2B48294828052AFA295528FD25E325CE26BD276528AA29202BF429CD279A253125AC26382552266B29FD288527A3269425D9293129B7270A285A2AE72A2C2B032CC12B9131E2316D2BB02A442A8C2BFE2CCE2B4E2A7B28CF25E825D32505285D285D27CA27AB26B5271728AA2BE22CB02BE22AEA2CB12A492ADC29822A8D2BFE294B297F27CB29E429EC27742700273726C0282728CE2AB52C5D28B22789267525B527F52680269926A3240B263E2510288427CD273D28B4280729762AA22C872A2F29B4317630892B94292629A02AE22B502A2F275F272226E32783299529BF295B291225CA255A29FD293A2BD72B002A442AF82A282C632B5E2B8929D42A0E2B8F2A342B5A2BB12B5E2B69276C276629632AB228E22940293627E124F728022964286A28AD2536242125CE241E270C27F228452B362AE62A2C2A162A262933288528D430D62FB42AB42A5D2BA62A822A81286A27BA288B29192A272DB02A2E2ACD29B5269327CF275E2A552C672B552A8229772A1B2BD42ADA2A6C29E62BDF2A802A86295A2B432BDE2BA82852282628B528F626CF271326D7255B254E295A28D62795275B258F236F24B424F9272F292F2CFC2B312AA2296127782720264126F927DE32EF31F12AF42A7D2CD62B852A08275E273D2ADE2B562B252DC12ADB2935286E2795268F28082B362B9A2B4E29EA29A52A272A3129C927B828FD280D29642690260F2AE72ABA2CE12726272527C725C1248824CD251E2484261D2628286425CB247D250B23412599269D27B029082BA02BA42B1529DC2634266F278726D229DE352730A42A2B2ADE2A522B3129F7286D29AE2A632BCC2CFB2B662A84279926E2274C27E227C52A8E2B99280C27E6257627022666263D257725D4261A25B0254828AA28BC2A4F2AD6260F264D24AC234E230D26C6256828E627C3274325BE253025F2244C249424002746291F2B152CB02DD02ACA285F2708265D285829B32C4D372A30B12BF728402A0F297427042AB22A182B942AB42A7E2A5D28C32717276928D929852BF32A512A30272529F026412676235A2518242724BC254426F9248C262228AD274527E7236F23A62448241724CC25AF286B2BAC2A99298D2852286E28DF277C257526B628072C7D2BFE2BFD2B0A2B8928BB2633259128C52AB62DF7376E32B72C442BE02A912988297C2B022B632C682B302C412A3A298829D027702B772DE22BA32A072B3F2929287528DB271025CC2446245425A72691250828DC27C228F027AD271D25DD2428265C252A266C28ED2AD22B9C2AFB2C6E2C212B192CF329FB2963282128382D8D2C062EE02BAE286028C02845289D2AC02C3D2EBA360B31582B5B29CE27AC29682AC329092B712C512C442C6A29A727E6266F28DC29F62A852A7029C32A4E2980290C28FD269823AE266D27D626E12858282B298527562676262F26D6269A2531269D27E727D528C92C682B5D2B4A2DCA2DB82D812B2E2A6227E9269326CA29862B4A2B632B65279827092A9529EF2A2E2D712ECB3524307A2BF327E9267329A52B532C432D972DEC2C2D2C472926267E25ED256D2857274528CD270128522AE32C392BA827FC26D127EE29EF2AFD29BC2AEE27C426082886267429C628C5271F27CE288029B02C942D162CF22DD92D4F2C0C2DE32BF2295A279B255B27F828A029822AB82B30299F281E2A092DB12B732BE32C8E34BF301A2A1F284C274229542C0F2D382C572E0D2D202C7A28C42522278528BA294D2AFE28F5281629712BE82C992BFB290A29D72A1A2D4A2BE22A99288628252739286F29702A5129E228262ADE2A5E29382C132CBC2CC82DC52D732DCC2A7E29CD26622680276D25A329112CF5290C2BE328202B5D2C7A2A0D2B3E2BBA2A4A348F2E7529EC276528D5297B2C222DB32CBB2B662CD628BC26FC268428F929EA2B722B6529E2286D29552BD42DD32C9E2B962B6C2D392C2429E8280A27B226A0257D27B22A442C272A002AC62CB029DB2A392B7F2B9F295F2BD02D712D8F2BDA297F282B275D24AF27142A5F2D952B552B402B892D9D2DF82C242D7A2B892A8033122F0D2921297E28CA2A332D112CD22DB92C082A45287A263828E929402BAD2E9129782A472A0A2AAC2A752AD72B662A96285E2900296328FA263F2607290E297629A7291C297B29982A5C2AB229E228AD2775286927B9290E2CA02D8A2DE82A9828A22621267228FB2B182C742CBD2CC02CC42CE52C722C782C342B5B2BE734262FDC2A5C29FC29EE2A4A2B372CE22CDD294029D52772273D27192ACE2AFC2DF32AC1290A296E29D4291B2A0E29D829EF256B27A526C525C726A0284F286B29B12981294C292A2A202BE62A7C2A0D282B26B22637269F2729294229A12B982B42292629A7277729252B6829A32A0B2A392A1B2D5E2DDF2A352C3F2ACA2B04349B312C2D822ABA29AA2A932B5C2BCD2AE729A128B228862A562C452B542CD22A14293029282AE82B7F2C962B7A2AC927C3277E267625E826EC29BA2A2C2A192C542B4729A4271027AE28E129522BB428EA280828C427982668286C29772AB82CDA2BDA2AAA2A7E2B7F2A3F291128D028E129822BC42D452DBC2DB02CCA2C1835A630602C9F2AD4280F294F2B5629BD277B2636274329B92B512CAC2BAF2C6C2AEA2A902A802B672C682D1A2BA72863263E231424CC241925792A862B5E2C482AEF2A812A472B93284B272328D3291229C6284F29F527A5250A2CAF2AFD2BAD2BD02A732C862BDE29BB29D4277826A226A126F828012A042C942BA72B8A2B533310306B2B24288B28792A252C7D2A08271127A828902A9B2CD62B882B412C6C2C162A0A2B0A2AB12C2A2E4C2B58296426932436247225AD252F2A2B2CE92C3E2CC42BB82CC12C0F2AFB27D025422859260A27E9285F278129402B122DD82CBF2BD52AF32A442931283527C126EC26BF27E42745298A298629752B4C2BBF2A09334031E0289628ED29C72AB62A402875274926392A312CC22B292AF92A052C752B902A0C287228A92A9C2C6C2A0D2A442784267824AE250F26502A252A992A022BFD2BFD2CC92A032A0D27002617269B275C2B212A482A27298D2C6A2CB82B192CF0294229EB25BF257226AF25A528E329432C512B9E2915283F2A2E2940299C333730CE2A7A29112ADC2A0F2A8A27E62652283E29ED2B1C2853294B29AB29D92BB6292A283E26092AED29B229262AC8286F275F27F0272B2A592B0D2BE92B1C2C312B5C2BA2299E26B725DE241F27D128CB2AB32B9D2A2E28922C0A2CB12E522E9B2B39296F255C2468279328112AAF2BCD2B962CA32E1C2A59297D284F29223246311F2B782A432A172B272AFC2669295D2A1A2C1E2C5C29A827CA27CC27712ACD296B27DC267127C0281F29C228E22888281F2A012A3C2C102C012C2E2B802A022A95296B282E248922B426422AB829F12BE12D582BE629C72AAA2A052CBB2C632D8D29472745274A28B529062B462C1D2B4F2B922C262CB22B072DBE2CE433712FFF2A3028532929291329B0279C29232B802D5C2E172C412B2229E42A4A2ACA2A812987292A2924298328CA2AEA2BBD2A402C052AD42AD3298E29BD2958299D278B279D267B262227B228FD29852B512DC22C7D293C2ACB2B672C102D972CCD2D3A2987282B270928862AE929F129FF2BAF2CE82CF42BA52D802DB62C9436A031522A23280527FC275427BF28E129872D132D722DD02D642D892C852BFF2B872A8C2C6D2A142B372A6E29062D262B872A642B142B932A202957293228BB271E29DA291329AE28E7281D2B5D2DE429D52CC92B682BDD2C0B2D762A832B3A2A302CA2297229262725295F2AD32965299A2AEE2BE22CA32C3D2CE92B592D36367031CB2ABE26F824452526278729F12B282DFC2CDF2BAF2BFC2B462CDE2A472B762B4D2BA12B542BE32A602A4B2CB92BDE2A9F2C4B29B9288B285D2900298F29AB2A7F2A2E2A1B2A9F2AFE292C2A192A462A052B402C84299D2AA42A4F282F29D829B329792AE32516272029DC28CA29BF28EB297D2BD229F72A452B812B1D36E130A52A19263B24BF25FD26EE28DA2B3D2D172BCC2C482C6F2B982A562BCF29FE28DB2B8E2B0A2A692BB82B8B2C342C7929172B672AEF28372B002A842A222BF42A1E2AB02AE228EC2932292128B926D3282729C828E827B4287B263B28AC28C428102A942A112A4A2A5726ED277429262A242931299729832A8F29C52C173795301D29BD264425AC268F28C62BE72CDA2ABC2BC62AA12C582A4E2BE92AB0283428A12915279E27FE26D02655264427DA269D261D269C273328A32AF42A242839286A276C26DC25B428442800285A295A295A2B7027AF244B25B325E329F22A882BD22A3C2A862A242B1D29D2294C2CF42C802BBB2A7D291C29F928AB2A0535402FBA29FC29072799262F27842AAE2DD32BCF2B3A2D902CFA2AF42994285C270127D0260125AA254B252C25492575267D24752545246025D2273827DA27102781257E24CB259F2648287A274028802B492ADC2C282833245626F8265B28C628FF29BF2B592E402B422CD82AA32B522CB52C772C662B8D2A242B022B422CFB34F630AF2B862905292C274B288A2BA02DC72DA02C902BFF2B9E2A4929D8279C26D42563253D262A25FB25E6240F2656259725232551268326D5276027A72620276A256F26CB263A244A283528202AE92A042B772BFA289727A7278E29822CE62AFF2B582DF22D4A2D022BBE29812A652C752B022DE22CA82AA629F828A429AF319031CC2BAD29B727F4263327C12B442CE22C8E2CDC2A7F299727D32520277C26BF25E1265B26FE27DD268327D5289727C626EB2759285A2A712980268526762780289F27CC277A2696288528162B442BF82AF02A662826275A294028392A6F2B112D9E2C122DE92AEB2B2D286727D12AC32BC82AD62A4A2A1528BA280B2BCF323D31662D212B0329B027FC25F129DA29412CA62A36265C26932759267026FA271F288F290B2AC32A6F2B532A0A2A1B2ADA2868292A2B972B17292F27BE25EA27B329422B002BF42764297329AC288827412A6D2AEE27C8269826C5265B29312AEE2BEC2B702B992C5A2ACF269E2572267E271129F5296A2866288728442A8D34FC30692D102BAA277626E9243226BA27AC273F283228212898280B281529CB2A2F2ADD28D32AC0290A2C7A2B7D2B95289E25A4286326C62722284F27F42419289D2AA42DDE2B3B273F27CD2570287F2578262D285027FF253D286929642919295D28B829F02B7929DE2A26287D26AB25DA27C8255429FA2A7B2BE92CC42BC2346331DD2D242B4C2A6E28F925AB260C2AF62B892A0F2CD02CA52A742AC62B2A2C322A432AB529C82A062A21298B270926C7261B292F282727BE273F270527A628C029A12BBA2ABF27C9267128C6285F27262834296B29942B8C2A8129422B6E2A612CE6287D2815287D270B264A26F3250E24D028A42A4D2A302B672BFA2BE332D730952DC02B642C802BD12A0629532C6E2D312C9F2BD22CA72CCB2BA42BB52C4A2BFD2A742A352D2E2B3029062A922881292A2A3A2AD02822261127FB27212A612AE32AFC29DC28DE280C2AB62BE52BF92A502B9C2BE22BA82B092BF02AD22AD02CC52A7E297B2AF5282827BC25E5258B278429572A082A75295229C929A5311E33002BA729C52BE72C1F2A572BF22A8E2BBE2ED02CE72C192B2329212AD929EF29A42C802A002D422C452AF3282429C629C82AB1292F26CC26B427AF28622B102B76282926DA2672276329842A99293B2ADF2BB42BE52A4B2C172AE82A732BD22BC12A102BD129932B9329E6277927972659291E2AE228742A1C2A682AA334E72F392B7328EC29B229FA2A8C2AD62BD62C752DCE2E852C0B2BDB2ABD285A29BC292A2CBF2CA72CAE2A1E2BDB2ADA28EC28AE2AE028EA27192AA628C72AE329862B80290228F428B227D92796281C271D27B7286B29562A6B2B512B992AD92BE02C8A2C292C402CD32C1E2CBD2AA4296B27B329812A212B612C672CDC2D9E352C2EC727CA267C2566272929D129282C7B2BB32CD92CC52C0B2BE92872289029D129BD2C452C292C9C2BBF292928B726AF2935298E285728AB2830296D29E928CA29A12AC32916285A272A2A0328ED246E26C2291A2A442C022C3F2C8B2D352D922BD22BFF2C322DEF2AEC2A20293329F2285529CC2AEB2BAC2A582D2E2EF134472D3E282A26EC2732294A2A062A062DE92C242D302B1D2CF82A2228CC275628F528282A8A2BB62A8227A12642255726E3241D28EF272728F8288B28AA265A294C292C2753279726C2269928562808256627FD28252871277929D929132BB62A172CA22BF72A132A4D29DC28752761271529722A9C2CF82B7A2CCD2B5A2B3435A02E83284727BE29B028152B332A302A512B042C0B2C362BE32A86274127B9265E282B2AB02A1F2946288225DF2674258A2595274627BE29CF29DC28DD257725D2270D2835257E254D262F27B124BE23CA264B25E826B1253B28CE29D328C6271C2A412BBE2CD929C829C1288827C62812283C2AFF2B2E2B532DCD2C152C7635692F0E2BF32AB829272B9629292B30291D278D274A2865287927F929CF2998295B29252AE82AAC2AE92873282328AF27D427C0284B29532C012C7E29EC280A2876291829D32647259F263D27B8259D24E426CE27D9264826FE263B27F02609267629912BF92A6C2AAA2AF42ABE275B28E026EB29CF2B252B662CDB2BDE2DA736A32EB32AB22B982CAE2CB62C262BD427AF2673275626EA277329052BB4298F285627D02AD02BAF2A032AA329B02D3E2A332736272F2A6F2BDF2CE12BCA2A2B2CF22A8D28A326B0258C269C26E0262E26F9282E294029DE29B4296028F4287729C62A392C2F2D6F29762A462A7E2AE22ACD27C4285A2A582B6B2B4B2CA22D7C348C30792CB22CDB2D5B2DD22CFC2C432A6D2BD329CF2778283C29FF2B312B432876276B28F22B0E2C5D2D732B3E2AD528A828E929DA2AF82DF92C282C102C002D702B9E2A9628F9251B29412AC929BE2AE82BA32C8A2AFA29362A632B4A2B4D2E5D2E3B2D972CE92BB92B562B092B332BF42805276328272A082CEB2B772C8335AF2F292C392CE52D192C802BC52C1F2DBC2B4829B6287B282529F42B012B2127FD259127292D092DC72A1A2AF129B92948288728142B852C4A2AA02AA02A772A812A34291926E7250228112C2E2BEB2A9A2B752AE428B1280A2A2D2C232F902E462D862CB82D832C0A2E2F2C522C422A732731276A28612AC52AD02C112C0D35C32E2C2C652B6B294E2AA32A632B502CA62C262C922AEF2A182AFD298B2AAA2824269828F42B2B2D462BD32C4C2A472BDE2959297B29072B342BA22ADE29A429F8294C273926EF2663278129062B542AFA2A002A7828422713293D29E22B292CAA2C912DC22C1B2C7D2D5A2F732C902A2328E428B12852296D2C292BBE2C82349A2F3B2B4C2AA5291228CD2609285527F8279B28172855284C29E9298A29ED27ED24D125E72AE42C4E2D952DBA2B68290D286D29472B452B042CD529D028AC26A7263225552538264728E129442B5129DD29BD2AA9284D26C727FA26A1276D288D2B0A2CAC2C732A1A2D412DA72CC02B0429B729842A502A722B1F2C582C81337930A72B482B3428E327BB251D25EB269025E32653267827BC278B2658252525B424A9262629F42B722B5C2C072E592A8227A0279629852A652B31294C28972894262825D524752585285F29E2293029F6295A2A0127322555241B2594260C27CC290B2B4E2B102A752B3C2C2D2EAF2CA32A3C2A652968274F298A2C782AAA33082FF7287A295F275C25F4240C250B263D276227F0253B25DF249E263626CE24DE242A260C267D286728DE29D52ACA270427C2268E27C928412AAA2B272A05297327C52424253427EC2793286F28E7284729982AD527E2247A253425A2258228112A382CD82B6C29702AC82A242C132C282C092AA128C5275928852A342C8B34352E4A291C29432775260C25CC25E426DD290329FC27F6260C26D8259E26302731261D267B25A427E4268F279D29F1284627BA27F825E4264D2A4E2AFF29F3275E279326902586261527892831270329B82BA52A302A902792276527B827C32AA92BE62BBC2BD029B52AF52BBD2AA72CDB2C152927293F277829762C3E2E5935412C6428B12500270227112543265D28E1296A2A272A90277C263925A9242F26C1253F271D289E27BF26D4267C27AE299B28F7256E253D26D028802A092BA7299A291A29B8284A28E827CF289328DA27232CBA2DF32AF7281D29F9277328CB2B182E4B2CD32B672A892B1D2BDC2BD52C972B46298728AB276E2A832C422E0836E32C1128AE279C26402932283329142BA82AAF2B762B582AC828CF25412665253B26DB27932C7E2A2329B6255527112A002A67271B261E2671266126D7265728AC28D3274A28502AA92A3829032BF02A772EA12B122BFD29032A0A2AEC2A112CE12BCE2B172D0B2BFB2C092CB52C3C2B752BE92A522CD62B262C5D2CC62EC736922D1C273526092AEB2A112BFB28442B152BB02CD12CDF2CB0295027A027E6263A261B292C29332C52298724FB263729962A2529A326CC25622698255F242726E02415263128DF271528E229F029C629012CEB29DF297D2A3F2A9029B9298F2CD62B902B2B2A412AC42BD82AB02BE52C4C288628FC2A542A6F2BB22B562E5936F42D6F272D2865288D2B3F2B152A712B552BC02A762B532A652AD4275827E427D025D727DB28022B63298E263A264526232795294329F6275B26CC25EF231F23A024FF266A25AB2679282126EB292729572B4D2A0C29E72675268727FC28722AD82A8B29072B382A262D092BFC2BE3282D28C2275D2B662B262C4E2CA22C11362F2DCA288D2668286028C02A4C29B327FB28312AD82A3628FD250127A5269426A0276527B92884284C2660269B26442791280A2BCF29CF285228C225332551245B23EA23B2239F232625D02517265927122AEB2BAD294227E8251A288429102C952BC1281528492ADF2D002D192C6F29BC27FD27912AD22B4A2CE32C2F2E84376E2E82288826C725BE27A6283D282A297028DD25EC265D258625A023D1255127882595270D270527C925BF26CC258C29912A222B5E2919293529632961295E28AB26B325CE23B023E622D925CF27B629C329C72A4328AD253E26B6269329252BDD2AC5293D2BBB2C592E272CD02A1F29CF27A627142A8F2CB62D1B2EA92CD136362E3D28222500260E273A28EF27C8268F279124FB23DE23A5230E2537286E2A032A0228EC256D259E24AB24CA262027F428612951284E260127282872287028B427CF24772329217F24DD23DD27F8285C2AA82A5B265B249024B727F128EE2BBE2BE52A142B0B2CB12B712A3B2B6D290E2839285F2A182C332D7D2C972DFC35C32F7E298228B0240925E527A228FC27FB261B25852487251124BC244829702B702B3F2A3629A42571259623AC249E272B297B2A142B56276826CD260F2627272A2778259C26A72476258125EE26DB27EC299629A42722279927D32627292A2C522EEB2DEC2A1F2C6F2DC12B2F290F28D8266129052C142C642D382DF22C91345231F428A0267D25E325F327C9282B276C26F324592314240A24422478264C28AF290E2BDB2A2B280B267324E923A3258A26882872276628A0269F25E9253325A12579256B271E281328B527EA266328C029E12866275E28AD2509260C283E29372D6B2DD92C192C662C8D2BF5281D290C287829052D112E6F2D192E7A2B6B34362F362A54286E256F25852772263527372616254B25F22348266F25D626652684285A2A0E2C312A72297926FE253828F424BC276E28F9293428CE261D26D9233E24B9262627172789288E29132975266F2898260C270826342608246725D52A5A2C0E2D0B2C962BA32C712BA729012AB0296529D12AE428EA2A142C762B9534E82F582CD02761279A25DC26D427EA25EC26782332248326BE274C26D2254227C6250428B229E42B0B2AC4283427142845265D26E72619294A2A1E29482504247D24E6263726E225E526CF283D29E42635265B277F264726DE269323B5245E263F2B0F2AB92AD429512A6828CC29E628EA2AFC2755275E292F2AD22BC42B6C331630A12D052B3829A2275427D2268C26DA26462649277228C529EA25E8256026742727288826202ABC299A29DC29232709262C25E326B726052B1E29D029B826A126842690263725DE274828BE28AD273726CE267F283B27C0275823E524922573291A2BB22C6D2B1E2C0C291B2B012B3F299D265F265627782A7D2CC52C103680312F2D252BBA2A492A772AA627DD26AF273028442A262B542A262A6A2934295229C128A4263C287A26D12612282529D42690262F2542256F27D02895271F28E327682560253C2674254A29EB29AF26B628DA294A2A492776274C253B261D2583279729632C112B682CA92B022B002A532A1B27C9265D253A289028DD2AF134EC2E482A482A3B2A9C29A4292E2AF3273129612A202B0D2CDE2B15297F2975286A28AA274227C9258F2421268D27B5273A27E12792252F246325B927AC2787265227A726CD27C62674270F293D2952272727EC286629BE2757262E25C025E62489252E29B92A712ACD2A402A7529E52AD2283D27DD2635269828C7295B2C2436E82CA027D0270428E62747293027E327E22A492B4D2CA02A282AF7278A265A271827EF26C028D3273124B923D4241D26AD26DB284B2686262F25392310262525ED24B92668287B269B2832297F27DD27FD27C4297D29E8296B291627F6244624F025062797287B282B2A492A822A542BD328F72635285E278D29B1295E2BF833352D6A27E424D8266227732692260828BA2AC2294829AF29DE267626AE250A26DF26E6265F27F6270026B5246925B525E3241B28DF27E525F8244625842550259725862569253F271B2820280228AF27882842280D289C27ED277726942441262A26B7255C26F927D629F02BF22BC72D9229DE2885283729A529632A942CF734FD2B8427B3262624BB24F024772421258C276D2832278327A024372511254427CB258E265C28292774277A259E25E0235C24192730268E27C326A024C1241524F924412389245A254D252E259325142501268425FD2426286E272625C52480258F27DF259D265D28B4296F2A292B052A1029EA28AE27642AF12BE02BED2BAF36282E9428DD247D23A323A6230E246E24DC2530268924C2242124E22479267A231C252526A826A328F9296A28672548232F24A825D826A8254C272425512634278226DD24D224A5220D2404248E2354230625C725C225D92626262225D8245826D7261D2749267A26462990281D29412A272712266E280029D42AA42E982D2238E52C9427A924A0230223B623762283256E222724CE2462252225DA26AE26E4246F245A2678256F289627FC26A7258B24312364250F267D259427772668264B26D626C52501258E23A924A3256425FC24B1243726F825E725102593256726E726A027D2263426B024C9265524A525B12445249B25F5268A288F2B402C4B2DDD351E2DEC25C2247B250323232308230B24FF243A248527EC27F1263E2891276626F926272585277029A22972285C276F26802389252726B7261827B42505261124EA258C27BC287026F726C2278328D2266C2772280E258925662847283C2AAF2AF1281D28822744263526622561239D249D2430239825DA26E2275129542BDC33672DAB282925102539255222B123D8232025D2267C261A280F2A4C29BE2692270D278227CE27712965298F284C281F27E424C8246B27BA29D328E8273625F22231262127D6268F25B428D4261B282E261828D7275A2544256427B226492B5529212BF22A7B2A69282F27E6244924892515247A228B241726B4286229462C3C36562EC1271925B8249425B0229B23DF22762414251E26CC256C27D526CF26C825602509268225C92730282828202817286326AD24A72526257928BC2607266723FC220E23B1249524CA222826B3243224F7255B26B623B424DD24B4243A2557272227F626B4286227BA2826274225BD23B4232623A224C925F6266B28182AFD35A52C342743267D2312236D2225239522A324EB23BD239B24F7241828F42898258826C325A226F7279327CE268027342626254E25CA23552411298F289B25F822CF22C123CD214B216220F1229621B823892558267223AC23C924AB23A0243E235B2634275C2731295E290C297C274F271E247523CA237B22C126E627552A3033C02BB226B424CE234923A1230C24B223BB23DC2270238424FF23B224F926D9277726BC24F2243F2642267925562521256025D1239622B42416256327CE24D1236A221722CB22E320182091212122A3220625CC24C72434248C23B4236B2280253B25D523DC25192816299E2695267C26C72440230A2405230C25A526CA276330F52B4B271923D1221222BC21C623A72238236621EF2231212622CC2299244125D525FA23C824D424E4250D235E24D826902556242B239322C324FE249726E32527231022D622DA1FCC205B21AE216A23DE245A26412733249325722370223022FB242924B22217233A25C7257F24852594237D248525192451240E26C32713304C2C13264124BF229B2346239322BE222023C821B2224C217A228321AF22DD22E422ED233F23F9238F229422B8239D24C323782256219D2275232A247525B9252D25DD23ED21A820A7214422C3225823B3239825BC243A26782579233D2118229322EF21A722D622E323C921F7220A243526D723A124A22386231B23BA26FE30F02BE72414235B23332351228122F32390211A22F621282163216E207121682352238B22C0243525532418233A253B24652376233D23B1229022B522462335260626EE25CB2154210E20AE22D72191224523A9240124E324F925FF22BB226322EC2146221F22FB21352207235F244B223124E6259C257A259325D92603286C30C52B3425AC217722F62246226F232A23CF2297234E23E7204221F6209822F722F323C9243A24E023DF240023AC259A22A823B4237D206122E9201422AA22CE25E52592247523622218203C225E2273222125CE24D8212325A424E4234423E7231622AC1FC42298211E20952106216322BA239E2582269A2363265227682A6C33D02BBA25A022CA21DB222E236722D1241B23FF2158212A21F41F0C20132297231423312450266E23232592240C2552230D2419249C217321CC20E9216821AA23D2234222842378218A20602248225521D123D222DD221B223E25912358253222BD2131215D217D209422CF1FCE215E23DA241224DA249C25E6268726192A43356A2CCB25022224239B227223DA22AF24362486224822BC224221CD1F7622B92246246424D824F2226B2531242025AB239922D6238B21B41F39216D237621D521D6217E213E23FF2165202F22B521FB20B8231A24C62132223A22D22141220D22C32282212221FE1FC12191214A22FB23BF23AD24C42374232C269E2ADB2C3637812CB32543243221FC23CE243D23EC239C249C23C1249D22D721D720AB224C215B22AB24A223A222B62324238424D723232323248C22E82018225120FC205A2175204F2144219822D8226421C621A423BF23F922EC220420301F4221B821502374228222CE21EE207722C52145220124F622B0252C24CB22952441291D2CCC34 + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 B027FC21BF21B01F6920CF1FD41E0F1F9F1F191F561EA41DB81D211D161FAE1D881CA21E411E151C1D1EB81D911FDF1D991EC51DFC1ECD1E401EBC1DAB1F491E231DBC1D101DFC1ABD1B751D111D5B1DEC1C7B1E461DE51CEA1D101D401C6B1DD61E541E311EDB1B9A1DFB1C381CB51EFF1C741FDC1E5F1E8B1D5620F221EC2C072B7023202003229021DA1F2A20B220FC20171F1120341F3F1EC11EA61E591E8A1DC21D4B1EC01D2A1E651FA11FB820481ECB1F621E241FB91E631FD31FDF1D171E021F551ED01E2C1D641F4C1D511DA91FFA1E641D5B1D2A1E0020051F2E1E2C1E8C1E271FE51D201F6F1F8B1EB81E531EF41FBF203820E820312187232F2D1B2B6B24DF22B021A8200A207C208721D71F6622D42153210F201F1FA91FA71F9B1F0720641F2D1E671E3E20911F731F7920B7209F1FB51FFE1F821CD31E4120C21FD620721FB51F561E6A1F0D1EF41D9F1E401FFE1DDD1E541FA31CFE1E1D1F6A1F7B1F021FB51D461F9D1FA71F761E961EED1EC121EE1FD120FA20B624892E9F2B4527B123FD207D212F211722E92025200220D61FD01F45206B1FDA20811EC41FBE1F601F3621E41E831F711E2620D31FEE20BA1F4220DA1E2A1ED21CA91E3920EA1FCD206A1E291E041F0A1E661D891EAE1F4A1FEA1F2F1F211FF31F5C200F21811FB41F2B1FC01E011EAD1D9C1E6D1FAE1F7620CD214420E321A723C92D6D2C0126C5210F21ED20B72122210D2145217520742079207D1F201FA120DB1FF81E0220A01F991FA31F581F9921FE1DB41EC921241F411C571FDB1E631E7C1E0520AC20C91FD61D111FD81ECF1DB21F9B1E8020CC1EDE1E9D1F2020BC1EDE1E01202520E420BC1EB61F071F9D1F6E1FAB1F721F4320161FC32085221E23A82C162A622526221423FC1FE5217620A520B521B8204721A821591FA41FF11F4C205520C51F5D1FD4206F1FF81E5B2096208F1EEF1F2F1F081FAB1EE61EB61EA01FA31F0F204E1F6C1DE21FE11DCE1E6A1E291F6A1FD21FCA1FEC1E531F1B1F471F8A20BA20041F841FB51F2D20D71FFD1FCE1EE11FC321D22074202D23DF233E2D732A18231822BB211921F8202D22052182209921CD2003203020B31F0D210721682118202D20BA1F6A214E21C51F21200B1E801E7D1FB81FD11F8B1FE4202E20851F0F20711FEE1E6C1E741E741E251E991F2621F91E261F9A1FD91FB21E231F9820BC1EF81F0F1FA91F441F1B1F9F1FE820181F9D207E1F422114223A229E2DEB2AF1246E22F2225521461F302015211021B721B31F72203F21632242201C205C21B8202021C9212221931F6D2011227A1F681FB01F211F1520E320D41F061FB11F26201620CC1EF31E6D20C3200B1E64210120A71E2E1E0C1FDB1DAE1F661F9C1F871FA81FB21F7020CD1F5D1F1F201320EA1FAF1F5C1F10222D223B24822E6029AD2407238F2194212A203F1F51212A223B21982001200C20C51FB820D51E461FBB201621F120C420581FF81D8620E11E5A205C1E9F1D101EEB1F571FFA1E9B1F6620CC1F5F1F8F1EE11DA41D2F1EDB1FDC1E961FE81D1F21D21F2B1F691FDE1FD81EC52091206521961F891EC71FBD20811FF41FC31F0B20482288228C2E832C8B2359211521592163204921FE209D22CF2010225F21C11FC2200D21A220E0203F20242170206020BA20DF1E6220D71E5821DE20E91E0821A91E771E981FB11FD920A31F2020661E9F1F961F781EC61F9620CC1F8F1F291FE31FFC1EA81F06233620201F3C213421042134215020D120BA21EE203F216720A222D4239830D22AEC263223B321F921E4205D215321AE219C213D23CE214221D21FC6202C214421A220DF203621C51E8B206B1F1F215A1E3D1EA11EA31E8A1F211F641EE71D6920C01FBB1F2220441E311EAF1F4E1E3C1F7C1EF51E181F87203F20EA1F0A21AD20881F6A1FC32071218C1F6A1E8420A020EC1F5720221F4121BF22462351302E2BB924B9215623D920911FD92028200522F7212921C320961FB71F0A201C2141208A2053209422E41F7721CD1ED01EEF1E0B1F951F711F021EA61DDE1E441E101E241F0D1E751E551E151E271F3B1FF61F911EA01FD31F2A206A1FD61E2D20552081209621831FA2210721CF1F4B21B82010208C1FF01FDE1FA620B824FF2E4E2C97266722C0210421D22008212720B821D2201A20F8215C20AE212B213C223C2178206D1F5F21C51F1620A71EAC210B204720A61FE31E651F16201B1F901F4E1EDE1F3120871E651D2A1F3A21871F9C1E4C201F1F821F2F21F020481F2B21911FDE1F5E1EDC1E18201A20BC1FC220E61FA91F1D2252202D211322CB24AC2CE82A1C253E225C22DB222820E520A3213B2293216C204320A91FEE1F6421871F1121DF2019204520FC1F711F6E1F4C20B3208220DE1EA11EB9207B1EFE1DD31C401F4F1F2B1F4B1EDC1ED11E8D20EE1E59217821901F4A1FEB1EF51F2D20EF201921111EDC1E701E8220101EA01D551F2B204D216C210821B12126222924692F922ADC247323B3210F236921B3228420F6223120CF213722E21F2A200A202321B11F1E22C52219202C1FB91E9C1FCE20D820701F4F1ECC1F7C1F421E9F1EB61E2B20C01FDE1F721FFB1EC61E2B1F0F20E31FF920CB203A1F4C1F2720C01ECE1E6B1F11204020791F1021F61F5F1F2120402156200922921FA9203F229123572D1F2C44265322C821142203231D227F22DD2015226E2230219D1FAC21E3219722BE2043221C21662010227022F41F4D20FA1E06205F20A81F6C1F7A1F2B1FA81EA91FF31E771FEA1E031E7E21C91FFD1FE021AC1EEF1E051FA81F411F6C1FF81FE720AA1EA920E61F90215D22991F9221C520AB211C229A22AA21AA2274241D2E202ADD25FC239B2393218E219321A221EC202421FC2209224A215C2106218F216221B520F9207E20D221E11F8B20402169200A212F205E1EEE1F7920F01FCA1FBC1FFC203E21B01F6B1E9D1DAF1EED1FF31FC81F581F262014226020781F6F2039205E1FA0204F1F282017204620781EA72151210122A422E222E522A925362FE82B9E25FD2334214F222423DF2127222F227222FE212522BA21EC215120C220F52067216A220E217F2032219320C220561F482102216A2066206B1FE4204E20E421DF20CC1FFE1EC6206820A61FF71E32201122B520FB1E3E2218205D1F3A1FA721201F7420B61E1521E71FD122402249236C22BD21692221235523F5241731242A52276C22E922E9229B224C23DA21B9211221FD226F200421F8204922AE206B21642336212F231C220D20692131225D1FE02028205220F21F5E1F0E2199201A21F720DC20771EDC206020C320521EBE20F01F2F20BA1F0D1F9C1EDA1E5A1FDB1F36210122221F26216320F91FEC1F7B215A211B214021FC2360239625EA2FAD2C1B2519223222D72138229621E0215B2065207D213122D621BE1FA622A12225228F1FF82166222C20F31FD51F0A2005215420371EA01D6022B91F5D21781FC620E02179205E20C21FE91FD51EBC1F502065206E1F281F0421561FC21FCB2000229D20A42055216622971FEB1F8F22D821CA214B22D121DF2284241225352FD92BE425EC2314235E223D22CE203221A6212B224623D3214F205A20A920C42191205E2384214620562020223421AA204C21BB1F3820BD1FF31F082135217C223022862222214F21A41F281FC2211F1F7E2176216021FE205A1F2A21E51F70200021741FDD1F7A20BD22B92125227F25A922CB22B924AD21FC235A24D3243A2F8C2B20259D243D22AD21F321F021FB21D72298227D206222E821BD220F2196216C22542005232021BF216721EE215E21F41F8E209D1E2B1F152187209422A921C82226234122CC219E2142219B2040218422242289203C21FA202D21D61F42208F20332129202821DA22D521D72213220122E123AD228724D1242B2485256C2F672BEF24702269232723FD228422A221D8221B22CA229722F2216F20F6227321A021E7206B21731FB91F4C209D207120EE1F0220181FBF1F8620861F4E21FE21A520FB21E221C221FA202C24DF2270218023CA224E221921D2218A2130210122C6219E2120239721692395246323EE22C422F521D8226B234023AF2389243D30852BC8243823AD228B22A720A9218D2233212F2030214322981F422137233B2157213622F1209B21D320FF20062278229421FE1FD81E9A200921DB1F3D2144218A21A7221321A51FE4227123C223A223F7239E220D24BA2214234D22CF2171238F2000217B214521D3229F21D122EC23F42327220623042329241E2338260D31092C96247121F2216F22D42105216C213F23CE21A92355221A210B214120E020B12041219A21F1230D218D204F221C222A21E020B22053208D1F0620B11F0521012139222F2240231D23CC228824C7227E2297226A237923A4249A22BF2278228D23B5209E206A216821C521F7214825D82492233424472403264C26FC25F9307D2B0E257B228023F1216622C0228222E22294218A22892270212322D7222E222F23D420772213228C22A8239A2182221A2234222222782020222C211A228721B222C2220524F023F8227C2433243422072456235222F3218125AE232623912423226E21CD2137203C2398227A244C26082773257325E12402263C259028C032772C04268C22A721EC220423F42227233A23AA221D2408247022752332232523BE230224B321A223C6224822EA21E7232122B721F0229F205E21A52180220921EB22AF23F6252B246B26C924F6249C24A22225247823FA236025D326F3247625A1225321302252228523D4223025092702285F281829EE28DA26DD26CE27A832572C4C2705226723CB222723BD23D524682499237C25F4237427ED243F25152502248124D3235324F92221232123B2231123C9216120DD20C8206121BF21C8224224CC25AB262F251D2792250126D523F123D3232824DC25622645279924912380242322BE217C23BC233F241126C127072ACA2A192C292BEF29F3272729B4330F2DD725962438242123202293233E248325E524C223BF25A025B9277526132721261824CB2476231A232623A0225222C721B8227220CB1F1821052233218F239B25BD274229F727FA271027D623CC234A252E25AB26F426C728A92553253225D0239A233D22E12358247B25BF264E2A842B5D2D7A2C212A9E28BE283928EA31912D992673217F21BA217424842301246725D623DC266225192677254A268E26F9253124EE237C25F021EE227021DA2313216E21FA212B21B52179214122DB23A8253E28312ABB2AF3281727B924A52490267E275C290A27AB2745261D25C52409259E250B26A325BB2540267826EA270B29632AC62BCE282A27A9270A28A233652C4825B6221A22ED230A2377233225E5242D2785248B264225BB2589245A22B022C022CD232823A2211821762325230B2253229423AC21DA22512392233724F9256829642BA12A80287B244224902457268B2AD7271927C2264226102443240C25AC28A228EA25312857285C29052A352901285E27FB25F42767287429DC33002C9B27182339249B241C2213236723AC263B28122A6729BA26342636270A26FE2230232523D12305210422CA2171217E228624702424244025612320254925EB29192BFC2BBB285E26D625EC24D8234227732A4F2976285427CD258D25C524972788298929EE29E828FD262D2771273627CE274C258E258A257B29F12BD734812C1B25CB226A224223AA23BE254C259725C027962802280D285726FA2411248E23E12145241F23DF221F233F227421CF229C25992614276A26FC258124D6263C29A32B902AE22724244425782429264629582C2E2B7B2986281126FB25D724AE28ED29C32A8E292B2A3228E9254C25CB25F02488252E246C260C29032BD434902CBE263823D523EA22CB248423D82482268326E0255E26BE259225D024B92351224B247A23C923D724E62284240923CC230326C127E625F62679279425D826062A652AD8277924CE24D32294249526FE2A292DE429092AEF28042647256526A028DF2A972B722A63283324E822A722AC249624AD22102439276829CF2AE435922CD5258B22B3230E250023A424CE2350268825AE255E2511263D2497249323082408250226C92692287726D024B425F3241B263E2976271826F225C3259A26EB2610289B249922D6239F244926F726B72AE02C7C2B242B08289426BE27BC267C2A712BE629CB27BC255D2557238223B52271238A24DA241B27772A062CA635882A6E24E32369222024E623E4238723AC249125C92638277A252B2482237E2323269D26E726BD2943285B274427602590237D259027E32482247B244223A924F92329247B255921F620042691283528EE29EC2DD32C78294D28292767262A29622A692B262B6F2A9428BC242D21B82123220A232924A324F226832AEA2BB1361F2C4F252124322454247C23B42309260C27DE28302A60280F2653230F2418250E27062ACD2A252CD62A9F296C27A0264F24E6230F27A6246223272491225824F123502548247E219C2115266728BF29162DD82CEB2B1E29B327B5235B253627C52BC229752B1C2B69285A25D423B1234123E723E625BE26CD297A2BD12B28355E2C9B2627245424B62581268A28272AB72BAE2BB72B44289F24A526E3247F253429DE2AC72B4E2B032B6427BD26DA25B92398220424A5238F2343233924E326F226AA27F8263C243C24DB25B529CC2AE62CA82B912CE828B52519253C2545260C29D52AA72AA729842AE626682575240B24D322A026BD27D8293E2CCB2C7A35022D7C2668246125472605271B2883291A2EB12A9B2ABF297827C5257226C0262B2B8C2B6A2A5F2A5C2876275227C4233224C4228F21272387222524ED26E3276C29072BF52A93262825EC268028622A2E2C622A492AEE27A925CB231225FB27E928CA289B28A7282C28CE26B9244724EB22DC22DB250125AD29442B832B7F32EC2D96272126362644268827FF28732A272C2C2CA62C432B0528F82428250D272F296D2BC429C228E0286926C425E1256723D022DE22F123022516262629AF29D62BDE2B032BBE283227C0279F28E429232CB62BAB29242652268324A225D426A827722723289D27B5270625E42339247C242524CC264827E8283029EA292E332A2EC22ABC286C29442AE42A782B592C7B2E902D602BE42974283427B0263227552AA22A11272529B927A327C22670247F23CC23DD22E424B3287C277C29772BAC2B0E2B732ADC278225B32693260529042BC52ABF2871254E24A7248A26C9270926F226FB2617279C276F263625D724E32331242625482679277427A02A4E33B42FE2290828A829B328B429922BF22C432D922C722C3E2B7527662479251526A3274D28D028A7283B2906293F288D26C92426236124AA25E7270729CE28BA290529372AF92A62259F23BA246526B4261E2714272127EC25BD259A24BD24E12461277327B926AE27F32878272A273D262025A32460270927092AA22BB22B5A326B302D2B672B0C2AC12C9028312B452D652C5B2C722BD529C626D523F82564261D285828BE290D2AD62A322A38288B2628251A25582602298529302972287D27F6272B286E299A260624E722E323CA241E2632252C2666235B243824EC24AE25CC277329092A762A872B122B692AC0297627E2262328D92AC92B192ABB2C7834A12E222A9829272A7A2A0D2B99294E2BDF2B222CD32A13287224CA241024E1253D26E528B629F729092809288C272926E1239328E1282227652811265525AA27EC27DA28F027B02306229F218723E32202246B24362480234D24FB233B25B4286028AE28DA29B82A2C2BBB2A8C294D29FC277C26CA273F27E1270F2A0D2D39352D2FF42A1F2A0A2A002BD92A0B280528992B1B2B4429B8261C2351238A269A271D275E2974297E2A332C6D2ACC287326E624BE253E274628BC269825C2259125BA26B4289027EA2237228C22102212249F2467272F26B1267C2318257C26C828E22AA42B632CB32B392C4328EA26EE268F25C025D826F52651297F2A542B6B34F12FB92B5C2A502A322A91289E278D282E29602ADD2777251F258C26272851287728B2297E2B5A2DC92A052A98289C25B1268126062716270B269E223B250126B5283429532866243C220F21B5234A24B7255B284A2758276027DE24D228AA2A892B6C2B902BE929CE2BF7280B26E32529250F25FB26B6258B27562A8D2A6F338431872E992CA22AE12AA029952812277E27B9255E2579231F24D626A629E42ADF2AD82C352A1A2C172B1B28E525E825FA24E2251E2643277A264125A8267228AD29F528BD274C25B822D3229525CB241B275828422A192AD4261628EB28AA297F29D12BCF29D229252AFF261D24F1254C248523C425A724AC256426A8274332B0324D2EDC2D572DB32A262A23275F267F26B725C6238824202562281A2A9F2B012C762BFA2BC72C7C2ADE2706272C25DA2327256F26E926D426B8275B27D227FC28C72741271024E92238245B24C825ED279C2A622A97291129FB263F278828E428DA2A322A4E28BB261F25252553245E238E24B0258925B8255726AC27FB313B33512F8E2DE32C922B342A4C27D123F9232F247225B7249E252F28AD2AAD299C2CB329C22BD32A192860273E257E264526B4258D252027B928BA26802764278127F225B7262523FA22A7223124CF24D4263D29EC27F12632271E262225BF261D271325B8251F24C824322455237825EB256F250025682597274828272A59324732BE2E1D2DCC2C642C5629DA2460247925662414243E2614273A293E2B972C612AE62AFF2A942B70293126D2240A24972651298B2A412BBF29E6280E2956285027DA25E1240524DC226E23DA236F25C7258E270C27AA24FC2537274526DC25DE233F26DE2380241E24D72472250027AB28222AEF29E127E3280229422AEE334D324B2D112CBC29E729CA280F28AC25D924A32370255E27E629062AAC2ACC288929852CA929372A3B26A6253B25F42669256527852AA22AD029FB2A412AD127DC24C824D92349233E237D22D3237925962729262127F027E828852A3E29F9285427E9265A24EE231A2607274028482AF42A582A932A60294F2BF9277E29A2317A31232D342B5429BC270B27ED252D24552527250F2775279328E32964297627C925ED25C5252528B4248B24FD2652286C2619293D2940292F2A3127FC26A02541251826AB233F236223F3243C258B2539278F287628712895297C2AEF294928D8269B256025A824B129842A932AAA2A722A282A9F2842261327E32771270A319C31CB2CCF2D4B2AE729C32791250E235325F426D127CD29A22CC72ABC2AA928C3266827122516270B278A266428D9279528CB2553273A26CD26722451255824D924BC26AC269026C3275A279928BD289E281829CF275A29BF2C872B252AD128B22573257E251A2821295B2AE42B9D2AF927A9261229F22719298C276F2744335B341A2DD92C7D2A532931268C246A2548248B243F28A229722AA22AF12AE42B8D28CB287E28A4273925AD275F29E829B2272626CE242925662417251026BA256D284729C929C529D628502994297E299A2ADE2996287A2A4C2BED2BF9284927C8265B245B253B297B2C5F2AEF2A2B2BD127BF25C3288028E8292729A72869323E32AE2D992AA52A91290C27B02495242F23342591289C2BEE28612A67293A29A528732746276426842806276629FC2889257125FF271E25F326B8270E274827AE29512B4B2BE62B092A9228EA28D028EE29AF2A13295229422ACB2B4D299827CB2692254D27362A4E2CDE2AA528EF27C32679273A2BB32A222A812955288730D6319B2BB32AE8289A25D2265C242F2523264C25C9275F29822A5D272127E725CF2880270826DC270D27B328BA276B288A260227A426A72845286929272A5A29792BC12E372C1C2DF52AC92906284428B829602AC0275D2769289A29E72853267B2582260D2AF429AF2DEC2B2929E529F9284329B62D732B5529D629832BE933962ECC28D826CD26BD24E52480240626BE23FD22602682241F267E251F27AB26B927122680240E2576270D25062865272129182A64298F27702662260B284129102BD52EC32D512DCD2B3929B02AAE298929822A1D283526052719270F262925D724A925A6281F2A0B2B082BAA2B71297D26A025992AA0295F2A4229EE2A07342A2E5226CB25DD250427CC249F265F24B8243E250825E3230F25E2258927482626263525CE2324256325AE2604278E282629152AF02A4E28EB24AA2321251527002B932DB22D742C092B4E288D281027752829282627B9239B2502247E247E256A26AF28302BFE29BA2B602A822ADB2883266E28082AF129282CBF2AC22BEB33392DB4277E27DD27ED2787287827BD268F26CB253626EA254928802747290A28092612255E25872649271D28F5292E2A9429CD29FD2A5A270926E823A225D326C628E52B2F2E4D2B792AA2292327F2248C25BC24F523F423482399235B256826BA28FB2A342D9E2BCA2CA42B752C892B5527DE286127242799297C2A4F2CBB36BF2D4129352BC82BF92B9B2B9329712803277625F5256025E8270A2AA42BCB286B271C275A26B927DC271F29FB28472AAA2C3F2A5C27F7231E24DC2455247127A428B72B082B042A05288B2672256524AE24A722D12445237723D823772499289B2AD12AF62CDB2AF62CAD2B4D298A2A732800263B250A2663272D2A762C9635B12FA62B0D2DE52D1B2DEE2C512A462A29287F26342752277B29EB29F92A1A2DEA2B1D2BB02AFF29022AF92B622B782CDF2A1B296C2774266E2452270B257A25FA27B82A592A5D28DC25E6243E24AB23F224A6257E24A825C82324245A279429202C652B402B302BE52C6E2BA52A4229C127F325F925F724B028822BAF2D6636EF2EED2C512CDA2CB92BFA2A102BB629B128E725EA267E26DE29D02B292D992BC22C402C2A2C902B5D291C2B332BBA2A6C293C283826B2262A27F727AF26E125AA269027D8286A284F253424FE25CE24AB26B72696278B27C52793250F260B28802B702C132C30298029342888281629612740265E27BD283A2B302BD02D88357F2E262CB82D792A472B582B082AF326CC25B325752620275028902B022CD22D122C552C222B7D2CCA28062AAA28E4274426A42717262B273529EB2999274E25FF23E7247F2857254625E7247226DD25ED289D29D9287A27F7293A27B826FD28F72ADB2A43286027D126FF26952885291E298E25F9292A28D829FA2DA92D4335D72FF8284929D128D029162CC8289C267E26162544271A28A128052A732C232ADE2CCA2CCA2B782A362CD42A44270C27B5269B25652855289428B8275F279225AC259326D326BD26492586250828F8277A2ADE2C6B2B662A102B442B5C297C2A9429252AF326792551276A261B28C32CB62A5E28DC286B28F528BF2C5C2BA336252E1A2982252F26D827AE26252790250E26F426F226CB276A272428F828A42A0F2AE92A052B4B2C392B612B4529A8250D25DD27A6274F28A228D9266525AC25A6264F2747270E264E2679274727092A912D3B2CB42CA62A732C702A632A0C2A462ABC26E8258F27A828D0270B28B22AAC2AE32A1D28EE27FF27ED2A372D7536E02CDB286026B62736268A2719285526EC25F424E02A6529F52B67292629CB277729A8297A2B082B8429A128B124AE26C2269B27A228A0262D27592631249B25DD27552954281F276E29B82893276928562AAB2BFC2AC029032A3D2A752AA9280729D9271827AE263D2889293729932A0528E326352781284629052A952CAA35552C83290928AC27C42ABD2AB0279C274F25A527E529812ABC2CEB2A0D2BCB29532A9B2B352BD12BA9289E25412682261026E12646265626C225F624792572273929342A492B9B2934284129A2294B28EB27A22A4E270927352AEA2A032A062936262C268527C426332B942AC72AF0290F281827F0276928AA28FC2AD22BD234E42E972B942B022C3A2C262C3E290E2944277B28642B062E422B842B652A3E2A692B642B302A8F2B1729622568271A268E272D291729B5244F24EA258626D1275C2BAE2BBA29DC2819297C2A1E2B562BEB2A622A102A9729192959283F29F926EE267C2807289828732953294C2BD728AF27BE287C2B3D2AF52896284D29CE309F302D2CA42B392A852BD22C7A2A992863286C298F2B002BD32B7E2B7A2ABC2A042A382BD229422ACF2830277227FD294D2982292728802544254825CE26AF271A2CDF2B05298928B6285C28522A7729EA2A5A2C322AC32B312AF7277829FA299929102A4C289A29DA2801293827F426F427B729492ADE2A982AB3272826A92FC12F502BC22928290D2B092C982983264629A72A0A2DEC2C592BBC2ACA2B9F287529462C622939279E26D6266F296C2B992BD72A432A142611259525FD26012AA92C9F2C7A2B40272528182A772A3C28CB29642AE0291C284829CD28B528822BF82ADD291A2A7928B9272D259525402805284E2A172A5029F2278F260427162F082FE329EE29C62AA22A7F2A81281928442A992CA12B872D64299028352A1F2A052C422ACB28CE2680266B28D129702B1E2B832AC6296A267A267425B626B627D32A442A462A6127B427122801284226092851277B27C92525282D271C29F32A052A0B28A3262825EA25BC240327C9279F285A29EF275727FF25DC25A42612311931BC293829812ACA2BE22A7B2730287B2BE82CF52BD82CBE299629222A1B2C072B852A2A29B92617283B282F2A952BAE2ACF29A6281C27BC269E275226C327BE2A8429932AAC265226E926E4252D2514258A2658247E258F2541299B28232815288624AE255A250024D52464267B298C2B432A0E29D627762876262628EF32982E33289B26E727BA293529BD2827297F2AC02BF82C8D2BC32ADC288829D22B3729582740282029F428112813279328E6276828B126282697278F27A628CB2A542A3B2ACA291827C426C8244E248D24692664253F27BA26FF27D3261728C526EA25B3243624C6241B259B26FC28562C552B232B2B2B0D2A632B362A932AD934A52E7D29E2268A29DC29522874290F2AAE2B382C702B7D2B8D2A162C8F2BF72AD8296F296829A32AA629382D8A2A122BEE284B291626B525B9271E293D2834282928EF27112A9928C22568262F269E26AC27F2281F2A4B2A3B2AE3287327DB27D727B126322727279B288928D02A8E2C572D202D202D3B2B9D2C372C3D2C1436D1306E2A542A182CFB2A8B28F5271628562B242CD22C802B132CCA2D8C2B672CA02AD2282E29072C302BEA2A5A2D0A2EC32A27289124B7245E2633255F27602715282D29322C702ADF28D82714270828BA292C2BB12A67296D2B8629E526DA274428952A052A6328192BD22ADA2DC02CF52B6E2ED62E0C2DF62C3C2C302C61340F30EE296A293329942AAB27C6240A27862A042CA22C7A2B182CA92C1A2D332B3829E328D5291A2D072B7A2BD02B942C43282827FA24D923EF261B26072706275E272829BF2A8C2B9229CC2837296A290D2AC32CE9297029EA29FE28B92849280B2A6C2A782B82293D2C702C0B2C202D072BF32CA42E0C2C692B1A2B662BF23206300B2BA027CD266F2774261D260B29542B9D2C8B2C472C6F2B912BCD291E2A79272E283729622AD12B172D952C402A262956264025CB254E271D2997272C28EA29F328512BF02AFC29B729A42A772A7E2C992CFA29AB2A5B2985271D2A592CBB2C522B232BF12C232D622C2F2BE12C7D2B1F2C822CD62DF22AF129972A6732A9307C292D27C4252D2633278D27B0275F2BB32C032EE62CC22B2A2C972ACD291A2955281D2AEC2AE32B162C422B402AE12713273D27E1264629D4293C2C3F2BE82A1A2AF429BA29CB29092B812AE5282B2B0A2B0E2A522943298A2AD82AFD2B762A952BD32D7F2B7B2D762CC42859295F287E2A292C382A452B332C202B6833B72E1029E825BA25C5265328F329F629AF29922CAE2B9A2CE42B122BEB2803285A280228C928E329762B072D492B8629F6270C287627D9268729332A572AF427752718296C2A442AA6291C2B70270929672A492AC426FA26292AD72BBA2B452BD32ABC2BFE292F2CB82B4C2B762810277626CC28D2292A2BF62C9E2C1A2C1734A82F4D28F92605260828D52AB32A062DC82CB62B672C252C1E2C092AD127842A4328B429232B1B2B4F2BCF29322A0D28D824BE267927A7295D2BDE296B2A1829B9285F29512A252B792A3C28E5265D27162887289226D728792B452E3F2EF92B6C2A552AF829612BF92C9A2A1D2AF3297928D4273C287B29822BE22CC62D8036F52E8228C124CA253827242888296D2B1A2B962C5F2C382B3E2872278526A72B122B332A61298F2941297F28C726C1271424ED279D29C02ACB2B5A2B752892281F29B42ACD2BBF2C442BD129AF281F26CD25E2263627EB291B2D582CDD2C2F2CB52A322B9729A52AA52BA7292B2B6D2957287D29712AB528D82AC92BC62EF8350830C929C525F7240E26BA272C280A2A342CD02C1F2C7D2BA02934270729082A4B292A29AC29B52A452B082A0129BB26EB27D529FB2A352C772D3D2B7B293F2BAC2A04291329322A1F2BC029A228FC243326E325EC270E297A2CB62CFB2A862C572CB12BC02ACB2A162ADF29E929122BC32A5F2A402C602CB52D1E2E402FC8370D30692A13280F2548259A27B2278329002B242C962BFE296C2870274A2A0D2BF42A1D2AE02A552B0B2C07298026712614262B294E2B7A2AE82C612B7B2CDE2998295528B3291B2A6F2A81292129EA258425D426C8274527EC2D9A2B782B222B862BA72DEC2B2E294528E1264A286F293F2AF02B362CD02C7F2C692D952E1436E230912B7527FB25512627271F29E629E12BA12CBC2AD72855273B28392AED2B81291A2AEF29842CA42C5228A02642263628C7298E2B4A29AD2A4E2B752C282BBC28EA27D828ED29492AD228B429C5260526F6273D276A2ACC2B362C5C2B472B562B502C4F2A2B287E26F026EC28062BA32B322CA62AF629502C002DDB2DC235F032E12AE6290F282D255F256D261A2AE02A512CA12A812807279429112C3B2B822ACE285B2AF12B0E2B3B279B26D926F329712A952A972702291C287B28AD281028CF276026B428DB281A296B2859297C2B7229C02A362AC72CB02B1D2AFF2A9B29162A64271626C3267D261E2A322CDA2CC82B9929F427342B7E2B642D51372531792CF428172668241F24B5248427A2290E2A2F2A7D26BD29312B202BA22C992A9A29E7285C2A81274B25DC26D727E9293B2BB32AB12A2929F92743299829BE287A287D275926A9275227D7283729FF29F22A602B9A293F2D9C2A7D2CCD2B94291B292D26DB245227B7288F2AC72C0F2C512BDE2C3229BF29FC2A002D6B341B32CC2B5229E8267326A4264C2565296A2AA02B912B762AA22AE32BDC2B8E2D422C5A291A29DE2766265226EF26F728292A622C5A2B872CD32B6A2B432B122B892B5F2B0A2BEE27A3257629CC2C592A6A2B542D7B2C202BBF2A5F297429E9298E2BE029BE2808291B2A132B372C722E902C6C2BC52B072B802B842D752DF234CF2E962A0E27F92644271928762642271E28792A342DCC2BD72BEB292E2CB42B332C772976282B262425F0241828062A782AB82BF928C829502A5D2A1D2B942ABB29632B1F2BE52A672A3D2B6E2B302B582C382C9029A32923294C29552A762A7B2C6329542A892AE62A4C2CF12A242B6D2CAB2B402B4D2A562C542C052B3C357930B2292D283628E12A0B2A9E28342655282D29FF2A6D2C6E2C1E2CC32B7B2CDB29252AAE26FF2515250C2408292B2964293C2AF629822ABB2AA32B3E290B280B292D2BA12BA82A2D2A0C2CCF2DED29502C312BDC2A2B2BD32AED28832A212ADA2B112AF42AAB2A712DD02CEA2A872A002BB72AB32A3A2A5B2AA22A3A2BA5337130ED2A2629F629D22BBF2BEB2995284F2805299129462A692BA72CBD2BD82B012A9C27B4253025D7248924D5273629572A472C55290C2A232CFF2BAC29C7280F29CC291C2A3B287D2724286029F029492AC32A902B4B280029982A462AE12BD72A6B29742A4828C22ACD2C5B2B112C8D2A7C2BA62AFE27AA28E528C5288233CD30E62B4329322A6C2C752B3328EF27F228C9279D29342AA029452A902C812A5C27DA27E2269825BF2524269328492B822A5C2C732CD92B742EF02B3E2BD52A072A5F299F28F6232A257125CF2508258727882784274427172950281E2B632BC1283827E8276329512C0B29182A542C1A2D0A2C4E2B562A7C291628902ADB357831852A372AD52A1F2D6F2CC22A7E29EF2739290C28F0277F252D2733296C28CF27682814271928D2258B24E025AF292D2B3F2B5C2BA92C102CF72CFF2A5D28AF283E27462421227F2460249E246B2549253F285A26C0250F27EC27B52AFE291A280426A5252E28452B282BA82CBC2D272E922D8B2C562A00295728E729C534B22F362AD32B5B2B782CE82A582A382AEA286A29422A4928E324DF248125B3273D287929392AD62A6E28F5256D27042B212BF92B592AE929542A19281628AB273326E524BB25B2258E26DE25D525EC26B3243629E3274426E228A82830277C2573258F27B82AD029132C562C6E2DF92CB52CA62C902B3C2A9D2A1A2B4E2D5536D62FEB298829922C262C022C532BC529E529A729892890276B250325BD2631290F2A3B2A6A2CD82A78298C264E273F28AE29EE29AC290228D527B526F8264228C326B22736280E257B288D2799288B274B2628287E28E429072B992AFD29C8269A26EF292A2C7A2C642B2F2B222B2B2C0C2B042CCE2B2229FB287A29AB2A0733DE2F02295528EA29E32B3D2BAB2CFB284C2811294028BB268724B3246A282F2A7A2ACD2BCB2AEC2BF0284A2734282E285427432884272C2931286B26C127AD283E290329292993274B2971295D2BF9297428CC2767279028262B1C29F428352731287E29042CB92AEA2C232A7B29AA2B072B1D2A0B2A69299427DE28F32A0932EE2F0C2C602B6F2CCE2CC82AB42C7E28AE28E62739242725C42669262727462A342B522CB02BB62BEC2ADB28A827CD2899282129D029392A9F29DF2A2D2A532AC32A942B092B2128D929C42A462A8528B8297729F327C927BA280F280A298227CB28EC29F12A702D482C362A0C2AE4292F2AF52AAA2A0D2911295529452A5E34AF30402D272C472B182C0A2A9E29E5277F257925922560269A271128CB28BE294B2A612A4B2D032B022CC92A2F2BEE29A9282D2B9F2879294F2BF12CF12AD02BCF2B8B2DD52B1827D4276927EC2A85270B275828E1272227AF295C2A7D28B0278127E92ACB2D3A2B3B2DB22B4F2C092C2B2D50298F2AC32B822CDB2D8F2CC93550319E2DD62A722B0B2B03290D2774277026AF243D269A2818286B296A2B632A2B289929A02ABD2AA9290E2A002A7429BA2A222D6E2BF92AE02C612D982CE72B312AD32AAA2A28284927D129892B072962288C283E28502AF2299927C2279327372BC82A162C2D2B7F2AAF29582BE92B4129442B122B9C2A492C362DF62DC0340831622D362ABB2AE42A2E2B72285A28DE260B25AB24A927D0299C2BEC2B862CF129B5291B29252AFF28E7280B2C0C2B1D2CEC2BE62B552CE52BFE2C382C7C2BEF28ED286929392AAA2A5D2B6D2C962C2A2AD728D7280C2A422AE4281F289D28D42B502CD82BF02C3F2B472A602AF72A712B8E2AE529FB29402B232CF12CE83305358D2CF8281429442BC82A752B182997263028F625B627D4287429922CE22BE72A1A2C80272227B62679278F28F829172AA72B8F2B7C29502BF72BAB2B7D2B8728F0254D26C328E729BD2BD62B082BA62A212AF8287428C52AD529712A662BB62BAA2A512B6E2A772C9C2ADF2A612C8D2A802AD728FC26522A922BCD2BA1350934BB2E92298629E7289D2B772B272B992908280B28B8276329BA2C4F2C0C2DFE2B312C83297527F2243A270929AB287E292B2C0C2B672AD02CC32AD42CB428E32744250526E9293E2912299B29A52841283A27392617275229AF2ADA2AC72B4B2CEE2A142A142BCC2BCB2A542B762C602A8F2A362898272D29C82AC42DE1355232012CA82936260C27D429D12A282B6928C9265026E4274B29D42AB52C612D6B2B5E2CD9292728F0261F27AC27A328742B132A7029D12887288229642A57290E28532657266F26DC268829FD275B256126912770253F276728CA29B12C532CA029592869281A2A4F295E284127452A9F2A3928B0265E267E25EE28DA2B01345631AD2C442991281129092AD12A0C2DA92AD528D225D127F029F0291A2BEA2ABA2A672A642ACA282126A32727287B2ABD291D2A4127B1255227C727A027022B852AF326B22637261827D72803299526752897295B271B25AF27AD28882A4A2BE52B042A04290F29A128C9275E27F128872A1D29702745265A27C9279528ED329231312CB729052AAB27922AB02B532BC52A672ADE29BA2AC92B9E285128C127F028982A7E2ADD284828EA27672BEC2B8E2B4F2B81272227642699268426BF279F2A6E2B2528FE266E28042AEA2898271D2B2A2A912A3728E129732D022DAA2C2E2EB22C722DFA2A362B84294329042C5C2B742AAB28F826F529B92B542BBD340431442DD82A96271B272F27442B592A4528C127B628512AC829F82A372AF1298229AE29132A71298928FE29E72BA12CD62C2C2C2A2AED298928C225AD26ED274C2B7F2C9D290B27A627752AEC2A8A2ACB2CAD2CA62A1729E7292E2C162D7B2B2A2E842D8D2B472BCC2B952A9C27302A1C2B0B2CC82AF7294C2C5D2CC02D6136512FD42ADD290A29BE27EB287C29C727DD261E289D28742B842CBD2BA52A382A75290D2C912AFF274D26D127682ED22C59295029D52A012A5E2A9229412899292C2B6A2B002A5B26CD25BF27312AB92A852C922A052A642AA3297229E72B792C532D4D2D972D042AB92AD6286E28672BE92B9A2D182D0B2C292C212DB42DF13417305E2AD3280B280228A628B929DC276D28AE284529232CBF2B232DCC2CF02BC12B332BC12BCE28BA28B328082A592A102A6E2ACB29F32C8E2B9A2A012A102BF22A592CC52AE3256D2686272D29662B602C772B1C29422858276F282B29682DFD2D6A2D452D792C702AB028EB27102B212DBB2C812C2B2C092CD62B2B2CBB35FA2FD1297D27A227EC2629273B283F28AE265F26B828422A5B2A732CB92C082BA82B692BDF2D652ADE27DA27BE299C2A13287F267D28E028C9261F27AE276F28022A702BF12831268B248C272C288C2A412C2F2B4A2A68295C28D928602C042DC82C4B2C482D6E2B8E2BAA283B2812291C2B1E2DDA2C862BD529F62B5E2BE234B92FAA2A6D27EA23452694272327F626FA26EF27FF28F82A192A8129712BCB2CC22B2B2D3E2D7A2AAC28E62AD52A272CC9290A275A2518267F25A525D2256B271E2AF42909291E26DA22F123DB262029492C552C882B272A7529E9278B2A852C622CBB2C0D2B1D2A672A3B2A7B2702289E29892DEA2B662A672C382B1B2D6C34C730762B6128642658253A257426B1245D25A1260328A029CF2AB12A982BF52BB32A7A2A032C742A142977294D2A6A2AE4287D27DC255E25DE268E25F825802503277B27EB2633254024C824F925CE251F28292BB42BDB29B42997276028272A322DCB2B4C2C43295F2ACF28C326D127FD28B02CBC2D6F2C3C2CD02C732DFA330832122DDF2B9828D92754252824752504245226EF27272A0D2BF2292D293F297E29CD2AA02B642A32278426132A412AF927FB257B2578251B28B62771273328CE266F2685264225FC257925D824432457263729942809281627B826AF270028042A5D2BDF2BE22AAF2AAA28FC28C329C62A752B552C962A122C412DC12BD034DA30C42BB32CFC291328A9252B24752357240D25F72523289F28012A7D29C6276927C4284E277627C7245D25DF264426BD27FD25B024BE25F627EE299429F928B027F025D625F825A8256F25FB243326C0266B281627B426C8285E287826BF26D4265729782BF72A402BA0299428C729142CE82AAE2A872BC32BD02CAF2CC83532309A2C0A2D3A2B9E2AAD270A253C23F52424247125DB27BF289D28A628FA281528ED2754267A275425DA24C32788279328492917276526EE28BE289328422735288027D924A623E123992587256C280B2B4E2938288F26032A452BCF28BB27CA25DD26FC288529C62B352B1E281C2A272DAB2A202CCB2A9E2CB62DE22D78352A2E122B032AB92BCC2AD327BB268625A32402247D256C25B026E5265627B529EB29402AA829392817266026D426EF282D298E29152A3E2A2F2B3A2BDD2AD02A342B4D2AD4275C24AF239F25BA267B27002C3B2DAC2963277729002A6F29A9295A29F6271228E8276D29A3287428502A622C612C6F2CC42BA62C7F2CE92D9335152FE02AB32B2529F0290C28EF282728FF244424A3241A256B2500263D29972A262B282A732C7C28ED276026BE274228CE276028602A552B782A2529B6281C2A312B9629EF261A26CC24DB233227A127022D6A2B8C29A4277427CB289329E329C9289928542A6D286D288C26F726A326252AEC2CD82E792D532C712B702DC0359630812BD32A162C042A962941288D290127E5257825D026BA2521274B2B132D7E2B262B02287D290429DC26B729BE29DA284C2854285229282A2B29F62788299C28C626CC25FB23F3231B266D250D26AA29BB2942295829BC286D27AB27F92AE02A692BA92A8829D3284E265426432810266928B22ADB29F82A722BAA2D03367331702C572D892A5E2B292AFC299F2A29283D244924B4241427F127942A402D682A0B2A37288828CE28D328A929E4271B2575270228C12872280D28A326A4260E27B227402412243B269724BB2729268F281228A127FC26772783272828432A722B4F2ADD2BC72A782CFC28C828252745277326A628D527282A512C122DC9364330542D4A2B0F2BDD27F6297A2AA6287727EE2593259A239B24D027B8287829CF2A5F29A528CA272F27B8294E2BA7291D283227CE260E28D4280226832600260D24B923A122DF22B2251C273C27262787287228172735262A27DC28E429FB2C5D2D6E293C281C29C02C462CEA2BDC2AB7290228FE26A4269E282F2B452E57374E31EE2B402A3B277B26C8275629C32A3E28182494249D2311241F2305265628702750298528D1270328CF2AA82A7F2C83293527F9243527A82837282E28DE26E024AB230522952362249428F0290D2BF028D928C32647251D26D625F8285E2B682B29294628C428652BEC2A5B2BE82B372BA428E426EE26CE28AC2AD32B1F36A12F882ADB27092654264127492835279127472497232A23EB222C239325AC285A292D29AE272B274A27F428CC2B712A51288C253124512407277F28BE27FF259F240021EE20392002252525BA28C729A22A512B7127E324A0248D26ED267B2A992BA8296A28AD271E27D526DC29E32B9D2B842A1929ED274928C1284A2B1C36422FAE29BF28F424C824E326402778276B27F82538259A255E237722002587266027922820294127B12720270E29E42AEB292A281F279224AD269128ED26EA25622360210D239C223E246D240A26542794298D2A7F29062A95294B2653260D29AD2BC62CE5296B2ADB2AE5285027F928F0293A2B4C2B2C2A472B052BF22B9234D32E4327FB254C25AE25DE27B128C4277C28BA2753251E2547243023A323FC23F924A8260F292F2891270B272F27362904290128DA24D525CE2569271228FC246123062280241026F02589254E25DB269C28EC28E629862C7629B227322686253F29C72A102CA92B962B202A83286229DC29BD2AE12B9A2BAF2BF52C402B4934D32B4F27EF25B624B025CF281D28F629122B332A4E290A26BB26C1258D267824BA24C725E12738285829DD273B28A12A7E28CA29342940284125D826EA27F524772340247A2582265D2720279F26F4255E28DE267D2840297B2A2E28A32633281B2779283829842A9D2CBB2BDE297F29EA297B29382A4C26B128BB2ABD2B6F34B62C2E28F0231B2647267F287A2A422B052E652AAF28CE274D2805289827B7274324FA24A026222A6E2A592ADD29002B2D2ABE2AF129072A7C299D2831267525A024EE26A2279528A628852810298B287A288F283E2765278429EE274E2858272F29E2255326A227172A112A672BB628DC2A5D295C29212AFB29FF2B3B2CA933B02C3E282C2537254C268B28F429582C902D2C2C012A452800299C260528F726CB26F426EE244329D929A82AA92B512902297628A929C2288D2B3B294A2AC2273127B1279A29C629D52BF929FC29BA2A202A1B293528F0255F27E92464272628042A7928A4271F279A29CB282B2B492B7F2A142AF72AE72A4B2CAC2CF42C1536A32ED527EC23C9240A276A2A702A732CE02C3F2B252A0328E0262E29092A44298128962738255F27BD269C280A2AAE2A1628A3284B28E927FC286D29BA27102828289126E127B929A628C029272AB727922A3A2B2F292F252A2632251B279626A028DC28BA282A2692278A27A0270E28342AA229DE2BD829D62AED297A2B8E34F82CCF26A0247724EF254529B82CD02B842C0B2AEC279D27A3284F28402AE128FD2743275826D124AC240F28A1296629C5269227BD2758272128D82891275D26F4274628242982280F290929D427D2262827612875281F2782265E259825BD241726892862299527C12640251F2525284528D028F729CB299D2BD12B6C2C29360D2C55256C247524EB251F2A622A222CB02C63297D282A27C928A329582A402AB328B0274D28A92663235924B0266727D4259F266F25F32759275F240D2646250E262928BF292427B627302747250D27AA27D728C0270329A229A9285125F0226124B3250D27C42605282226A526AB273027A227FE284F28FE290E2A222B2C337C2DC52656236825FF26FE286C2BE42C7C2C2028BC26E0289A28692B472CB82B372BF8298D29A128D42500256927AE28F726FE27EA268C258725C6257E2551254426BE2637266326662632264B271728C529082AD128D427DF28302A9327D62580241E2474245026BE27C1283428A12A48299229852880279B27B027BB290B333E2D27288D26AB23A125322818298429F72936290C279E276A27A82AAC2BDF2CA7294129122A9B28F827D625C9268826FC27412AAC28C227252670234D23DF221425F024E22567255F250B260428AC28082ABB29C227AF29082B5C2AB7296727922687247E25F2262228FD276028B3278728172A0E28AB275C271027D2276133E02FE02940260A24DA2471262F28A627B227DF26ED2425255B258B27412A5727E326B926D626A628D92913293527DF24C326B929002C3E29BE2877241A24EF240126F6261B284F251627F52716282028B329342B112AFB29192A7B2A3D2AEE29E1273B26B0258D267A2956295E2A152BA827C327ED28D527EF27072BCF2B7736022EAF281F25CD23A023DA255725F2276524DC25AA250C25C4244127F627BC257D242F26E024CE27CF276028E6278926CC242227282978298F2A0628BC255A243D253126A327B826D4271029FE285D28E0266828D927EC272428A529032B7A2A9C293827F525FB25CF291A29732AD428F825CC262227E427F02AC22BF62C9D354F2E0C275725E425FE23FE241025EE257926F525652848268F241626D5269D261527932469263028EB275F27F6282F29C2250A265926A8271729A52798267023E9234825232894261C27FE2745294828FF274229862632279D2ADB2BEB2C3D2C55291E27DA25EA256F28932A9D2A782BA2294E259E24512535262A28A4299A32C22EDC2968260D25EA25FB23A625B725A327112938289926AC266925B224172751276227E7261F281926CB25302813291B286E26F626F3271527FD2893270824792533257C24D0239C2726267B27E726F729462A96273527712A2F2AC72DFF296E2C112B1C290526462725288B2A742DC129F1254424B5239A25DF253528933222306029B42619251926BE230726FE25F527AC28C22958277425942319247624322577267D25A926492682263E28642A172B1929A6270224C8252326DE27822543245B230724202445222F2600252725C72746292227B42743284229EC2872290A296428F828A325E726ED2651285329292AF227BC255424F523AB241326BA31D92E57297028C724C3238F23D6255F2602291429F9295429F625E025B5250723B82537265027CB27862735271F296D29C229402A8E27C22409276926E52534250E268E269523292253211023E921AF23F8257227EB254126BC27AD27ED282A2732294829D32789273F265925E6259A29A22978285E2690221325AB257727C12FCE2DEC2815279C25D9239B246D268D273528CE27D529F02A2B272C2463244E2531252E2524263827CD277827802774280C2990273425DE256B247F26DB24FE257426D426A52603236A212A22092234227223D4236725E025D925EB268926782AD72AD7281928EB2792275A247924202668278A27DA2782252726F32600278D2FF12CE92848251D24BB224F2273257526B4274F268E289426FA2512246D2332236D24C7244C26E926082852258826992905296B2758258B24AD25A2252027392748263227C0284A24CB22D021AF21A0224C23602439261B25EE2673258E25BA263D2BC02AAA28B3262C271226A623B6240C24E7265C29C428722768270228D72FD82C6827F1250B247D24D6230A24F925F2273A26C4271427AC270525E123B62245229D248B25132783254625DD25E6267A263B2561243626242717275E27672654264A270D287C26DD25BC23FD22D0224F227F232323DC25BF27DE2642252A278229842A082B182A2D29362559233F23FB244823442631279E271D2608283631F62BD1257824BB240A24B922FB232C27922675274427C927A5288F26BF246E24D823A2236B273329222912274F28372747261227D92799271428CC279E261227F125BC27B726C52754256425BE22A022A8224F230A2377254529A628D728BB2860299E2A432B932A1229332740262B2218228523FF241327E928BF2AE72A7D31A52B722556223D235C236922D723492503279328F8274926D727E8273C2778256C241E2535269D2766290B28E9296627D727372889255728052792277E277527B6255F258A27BF28B926382793249522AC24DC2313219925192703295429CF2ADC298F28712BC62A1228A126A523E322412227235D243423CC260928262BE832C82B2326F2223C2242236D232923D026CE268826FF255826BB25B92685284528F1241625E327EB253E286028B6296C291B2A1F2AC8273B279D26A727842612277F25D723B527882889285129342760231D24D322E422A7222427BE26BB293A28A127B028C029F0299E2BEA26482581248224292345240F25DC258C2499272D33332CDD25E22154230D234523F422132651271F27D926D226BC258925CD2899289B27522646265C24D0265D2666288428C428B3298B275724A825F4273F26B725F3235F233C273D282228CD29992838250A254824C821E32224235F23EB233D24ED256C25832672269428A52730261B254C249825CD246624CC24692777284F34782CA125042448211D2483245223C42458272C281B297027E125D925322806275926CA277A261E25B425D124C826A827F128C42A3129422632263024DA249E2413235D232024A12719290728FD2739282E26C12356234B20E91F3B22E6225324BA233B24E42374239D25292532257F25DA23CF27DD2700268C25D5271829AD32 + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 80287422CB22862028217020691FE31F3120B41F0F1FC71E6E1EE41DEC1F721E4C1D7A1F081F151DC21E621E6520951E691F871EA71F811FD71E871EA2202D1FB01D871E7B1DD31B561C541EA81D301E7D1D351F061EC41DAD1ED71D231D4C1E911F381F0A1FE31C781E121EF61C891FAA1D2520911F371F7A1E6121F422D82DC22B09246520C1227522A820D5205821A421941FB120D81FF11E991F721F541F441ED71EC51E991E9F1EE71F5F206121321FBD202A1F69203D1F27207120991EC01E0420F51E771FBF1DF91FF41DE91D27204E1F421E211EBB1E9C20EF1FDD1E731F751FF41F871ED61F2720341F801FFE1EA5203D21F4207421B3214724BA2D1D2BD024512345222521BE20102115222E20BF2239220C227C20C61F2B2026201820EE20D81F981EE51EA4201020D81FED2047212F2004204B20C61C221F8520FD1FC620DD1F2820A41E6E1F751E4B1EDF1EBB1F4A1E0F1FEF1F4F1D691FC01FD01FED1F611F431E0C200C200F20BD1EDF1E961F2D2238201B215521D4248D2EE02B16283124772141227921AD2258219520552076200920E820EE1F9521C41E1720FF1F0720D1214A1F901FC41E96208220172121202121561F6E1E531D491FAF209E201D21CC1E881E4A1F331EE71DA61EC81F971F2620A11FA21F1E20CA205D21FC1F69209E1FE71E751E481EE21ED31FF41FFB2039227720E921E2232A2E9D2CF225E2212E214421E421DE20A421BD21C820D820DF200420351F06214820F21E8520A71FC21FC81F971FE821701E341F1A22691F7D1C431F2E1FBF1EA81E5420EE203C20181EC61E0E1FFD1DD01FB71ED520DB1E331F921F22201F1FE41E9E20592030212B1F2D20EB1EAB1F5C1FEE1FA21F7E20881F8D202F22F822B52C832ABD2579227B2381209222C420B22032226021A621422203200920852099207720F51FA71F3A21661F511FBB202C21FB1E46208A1F551F441F5B1F151FD41F2620C220B91F0A1E4420351E741FF61E8B1F701F2420F01F501F0B20AF1FB11FC92021218F1F0520E61F5E20BD1F1D201E1F482002221C21C62031233624682DCF2A7F23862251225C21782140223E21E220462265214D20992030207B214B21AD215A206F20F31FAB215121D31F5120561E7B1ED91FE01F11208B1F4B219F20CF1F8220C31F261FDB1EB01ECF1E7F1E0B205C214C1F391FCE1F5520191FE41FF5202B1F5320901FCF1FD81F691F981F03217E1FC120861F5F214A225622CF2DAA2BB12547230224032257201821D621C7218A22B020392151224D231021EE201922DD21DA211922DC2147204F210E232D20B21F5920E81FEB204A21922013208A20FE200D21901FE21F7A219F21C81EEF21E520831FE91ECA1FA21E93205620CA2070208320912020215F2011209D20A8203F20B0201C207822B8221425FD2E252AAC25E7238C22B5220121971FDF2100231B222B21B120F02083202C217C1FEB1F2121592181213A21DC1F5D1E5621891FCE200B1F651EA11E47209F1F671F2B2027214C20E91F591FF11E741E011F7C20681F6220AB1ED5218C20E81F35206B20251F7A210121FA21EB1FCA1E3D207821212089203020C020CD221023BC2EAD2CE8231322C0210122D820A5217821E9221D21AF22F62174207B217A211121A521CF1F2121F12000210D212B1F7D20261F98211021361F0821251F141F1D20EE1FAD2124204C20F71E65204020EC1E3020DB2008209A1F7B1FA220531F38204E2369207D1F24211B21142168217E201E21B3214A215D21A820512336249330E92A0427C523B42235239F21A8219C2115221422CF2376225122822048217F217921C22016215121101FA120A81F2B21B01EA71E141F411F35206F1FB81E9A1ECE208620A920EC20271FD61E5C20A91ED51FBA1E531F681FE52067205C207C2131211B20D81FCE203B21AA1FC61E1821E8206420B120B11FED214E23E32355302D2CE0251723A2243122CC207B216A212923A9231A23A02262215621952126222621FD20D0205823D6206F22E31F61203920362092200620241FE11E4A20BB1F381FC320062074208F20971F9C2080202421901FB420F120BF205520BA1F7E21AB219921B322AB20C522D621C9209B22CB2155214021D72102226222DF250030052D6F2784237522DC213A219E21D620AA2264229D21B923C52122235B22222314221B21D11F1822F720EC20F31FE0226B2123218120A11FDF1F14213F201921DE1F822145228620571FD420BC22B020C81F3A21CC1F37202922F0211120EE21C220CC20FA1EE01FAC20D1205D20952144216421F1232A22FF2287232426CF2DB72B4B26392369239F23E3209B21762289239F23692247229B214E21D122C5202B22DC21E2204E21E220972034212C223E2226225E20F41F2C221620C81FF81E382186218B213320D020F9203B22562021226E22AF20782005204B214E210422C822601F5620CC1F6421901FBA1ED02015226C23872325231D244124B6257030682BCC256424F122FF233C229723CD218B248122012472249E21DD21B2217722EF201023BA2368218C20BC20FF211023FD22B02155209D21F6202620D52040211E231A22FE212F21CB204F2051205F210E2133223F2279201B21CC217520132123210A22F421E7206D229921FD200D226923BD222025C822CB2307257525B52EE42C6F27BF23CC223023CA23102378235E222C24E82454234C21BB227123E523ED210D23DE21CC21B1238F247122CB225421C322B8228A215021A321B0219D2133229521A421EE20D41F2E2381217D2131230D209020E020F4218D21AE21E021EE22CE20762267213023822463222F24E623F5240E263F2748262D26FB26402FC62BCB27F72598250023E522012357230F23EB230226AA24A12369231F233A23E722EA217A22DF21B8239822D723B4247C231C24CC2279200F220B23D722572322233A24BE230B22B420A31F68206421BB217F217C21F5224F253E232B223423DD2279218A2254215B2215236C24AB23A6266B26C8270C29A628B2271A28DF30D92DC52729267023B124E9245A23CE239D24F7253726F225BA243D24AF22F7223A22C822C9233D225B225F24B224EE24D6224E24C0236F22D92246226624C9232B25E4236D22A821B7230A2396216D20A5210224402317228825FF22C2216221DA23B42183223121AB23A6226E27A728072A7D297228D028B728E226AD26E331B42C4E2A862637267725CD240725CC23C72407267628C3253725DB2464258023A5233425F42268250C25152499269F271A24CE2442230A23B422E6228D256F259225C924BD244622A32490237E234620B422422236237A232823C8218321EF21CD22142430251322EC23E422FF2368252D2840284E27D2261528D22595262E304D2F3328EE25DC25DA245A240D2316244D23052586270F281A27CF234F26A325D0246321CE23C324A6239824BD251026912600258A215220BF2467239C2691258E26AC269425F925C824EB23BC21D221632224232E2373232F259F228C2264230D25322412246B24ED24C921512241267F262527EC262B253B25BA255C25522F962E49298A2876277D2590248E22F9221E24C2258228E3269725CD244D24F824B1233C26CB23D4236525CF274527D026D3263B246D23D2213522C82496260C298029B0296C28C8271A258323E8244A21672383241D255125C223D424C2223923FE23B522DA22AF2227246E2385238B277125A9251F27D5228B2452248824622F162EAE284829B32778257E247F237D23CC24DD25C824EA262426922656258B258725D6228D25B524C12678272F280F27A1241E243A213C210023872366270E286829422AD428C62766269424392295221A24EF243D2433252E25FC24E6221123DF226623AA217C227123042354240F24AF233D25242329247B231E23E224332F852EA3291A29A629C62844265224D92320269026A0271A278726802533287426FB252024382410234E24DE258A268125C02326237721A6216E22D622A42694281F270F285A279026E5249C26C723D821C2242F253D25A324102582248D230D248923582340247B22912473264D26122660257523562337239E22142336243730AF2F9D2A152B422AF8289B24A824412516250826E0273328212576262529E326C82571252E237A24772429257C26B126ED2410235821A1220A231F236C26BF27EC277928712662242926F2240624852364240324DA25C0240B255E235E22B8230C21BD2113222222802422246926BF27F82626247923F322D123E622EA25CD30CE30722B0F2A372A392ABA276624FA23F226D526732AA82994264E257D24CE246A24AD2356242727D324CD24CE26832698257C25AE246D239822DF23F22420270E275B28C028B52839272F249A243F22D622992385245824AB243822C021E121432302211B21BB220024D925BA2643297E28B425E4242524A425B625602568305D2FCE2AD929FE2AE1284A27C62598242E253825D527F127B6257125E524E923E4247D2268246E250127B5286926D4272C274327E9269D244C26392692272A272328F127852842270F24D023EF22D2203323C822CD214021F2237221282118235E2177212222BD21F825A0262E2829295328EC24CE23EC224024EA238327D331F42FB12A5029C5274E281827182548247424E0247A274127AC24E724B72324231524A02471235C271C28FF27B127E12911298D2744271E25E726C5276A28212695263626E826A7233C24BF21FE215D2299212723C3222322B0223B24DC226C2497211C21B6226D23C2254B2674273E271626EB24AA2447246B23BF253A2762322C30612B392727289A26E12541259E254525992533286126F5280126F6243E24EF228E24E125F728FB29C72A8D2A8C2A98291827DA23B824D3251C279826F025592502255824E4219922E620F7217A219C22E12220230F240E24242553242E243F25892201226A240C26BF26E426D32504260F25172665255126CB26A92A91354631532A5329332827260924542488246B26F126F2269A28C327F4289426DF2534252A24B22673285C2A6E2BF429D828C92605268F22D9219223E3245A2315241424FC23172443217B215221C61F0121A223E12338253E2571274C257926972677242123FE21AA24F7258F269E26D627B7266D28F927CB25AB250029742AFC3452327C2B6726E2259125F2269624CA2437279227AC2B4E2B552B4F298B28CE26A0251D25EF26F42AD429ED2A0E2944290625C02375223E2168220922DB21A8228522AC22D72272224C217021E5206422AE24D92505282726CA28C72849282027F8256025CF254B26B027BF28C127B6272C27E3276C289325C125E427DF28B533A931082BE728AB27622832262D259E262C277C2B202B482DB52B8F2BD8288824E0239224AD26902726276A2621284326D823F8224423AF20D32136229D22AC229B2267239223952244229B20782197225B24AC280F277928742A762B7B288626BF248027A1274B269A2A782C372D1C2CF329C027B7260025B8260D27902752317B31732D5129BD2A7D2AEB2660253C241A282B2B622E3B2D082B5C2B962BFF28A42442243E2566263D24E1249123F021CB21AE22B321FB201C22BC207223DF22282650258F25A622D82266238D2245215324772802288B29BD2AF4299D282325C0250D2792278B29952A552A8A2B582B8A29BE28002538246923F025A9261E303831B92A8E2986298C292A28D526EA246925DF27F228E428182ABE292B28B92522245D22AF24CE2390239523BD2247210C214322FD2102225422B3224622802498257527B7269D254223BB240B23802326266D29CB2A7A2ACF29F0274E26A5237F250F273A28C028F9290C2AB029B12838270225DC24F92215242C25C9256F30BF30732BA229572BDC29F428A32402242C25EE2516267927DC278A27A326C02480223824F4227E23B6243323DC2438239722B022B72256205F226A246124972559288F282027C22547267A236E23A2232128782AA0295F2AA329AC268A2423240B255E272529A7299B290E272C252E249825A6249022572359259D26E9265F320031262B8C29C72B1B2C63270C26792376251A25B7266227AA283926E725C9234C23A3230E24E42475272F26AF253F276225DF237F24B8215422D224C12657281228272944269C2484250725B8240D249827F329512A9A2A2528352688261A247327EE27CD27F226DD262127C5244424D122AA230F2510255526A328EC280B33AF2F042B0F2CD52A492B31283E2584237C2459259627BB28692788252A249D231B258F248B24A227C1261A275728FB271A268F25932568225823F9257827FC296E290528BB27F522FD21AA259A266A257927D82ADA2A272836272027C6262328E727AF28762A102BA5295E26222232227F23ED2443264726CB276D29BE290D352031BD2B5B2C1D2C7D2A732682233024862495266F28222703262523DE23C4236224AE25422638294129AF285927AB29A9283426782711240C242627C3271F2A042ADE29B1265B22F520FF2376253427E529BB29D427E0246F25AF232E269427302B8C28D129502AD5266323A422E22373242226B7281A29102B562A1F2AA333BB31132DDF2B882B9F2A2F284F26EA2511269526F027D82549245B272B25E12362257C2551270B29162B1A28BE28962A512AFF273F261A24D824E226D4288E2BC12AA32A9D282A24CF22B122C0254627072A7E2886275F249023072570264627BF297E2ADA28572601262F23E523A624CB25BB25E62A9E2BD62B9E2BB32B46354F321B2C2A2B302B9F294E27F1248724832707250126DE260C2786270728DE25D8261A260B27E029D72A652B102CF929552C2F293A253D25F8239F26A8293E2A8F2ACE2A132ADE2407238823C424EC27332AFD2745262E24A923EC2211253C286C29E028C426CE24702302235C230425D624D62569294729E12BFA2C072DDA334531E82A362A5B2911275425A4244F24D724FC24AD26662742275F276A274C262D258526AA27D629402DD32A102B342CBC2AA82835253D24242440256B28B8275529C729A32868257C240625DF25D627442A902AAE27C223FD238122B623852555270B274526D92468246522AB2269247B25CD259C286829F12BA82CAD2D7835F52F7E2BF928402910283A264D2541256027C826B62542267728CB2A282BA6287F27AF269D25692B852BDB2B262B4B2A232A24291B24042354256923BF254A276B284428A9278B252A24EC2524267528B92ACA2AFD27E523A32271224D2451261725C4251F25D7246425FF238E23F92338243825F1267F28F029472B7A2EC035A630C1296B2759283E251B257125E026E3275128B029BC2A002A732A652B53297F27572694276C29DB2B462B542BC32AFE29D326E723472273236C248F2488260F269B27932809243F241D27EA28C828D328BD28B12768260A26CE24D9249A24512699256D245125C02694248D24822416259826282A3E2A922CB12D982DEE3393301E2A5529302799284223D3251F28872891294A2AE02A212A332ADF2BEF290628972599267E268627A027E6264C270D266324C8229623CA23A3248226C7269C260626EC26D224E624EF250E28F8271B285126A12620244C258F26472763257F251D26EB25A626F0279126F4247B25B026C829B52C972DD82CC42A742DC734B42ED828C5266126D1251C268024C72661289B29672A412AA329112B2B29E827DF24D725D7245E24D82245245A25AF25D122EB252324CD219725C325D9264D29C6286B2724254E226022F5233B273E26442640251C2428248D26882714285A2856250E25D2260A28A728D126D324E12526284D2A1F2D092B3E2A5C2B152D29358B2F4629A8267E2500261726ED238E245B286E274C272028BC27A428592A8328D0241F25A823CC246126CC256C26E2257F2447231B236024D72482269C27BF26B925EB254B2427212B22C12391236A253224F9252624D125922510293F2961288D2705278D287429412B8626EF24DC268127BF29912CC72B622CF82B742C62344930AC29992651255225CA248325F6262B277127BE25AC26DD28C72AC32A8927B824FF240B26B428F5268927FC27B827E428DA262725EA24802598224525B3246C25CC243D243B229D2188211824BA235B23A4241223D6241428C4274F2A042B8229EB2844294F28562A6628BF250228A528B429432D562C702D2E2FC42D5535A230DA2BC527C1247D2518261527D52715287A254E252D245825DA2719285B2618254527B325B928832924289827C429992A522A61283627002635248B24712547256F2374231E2342223B2331265824D523B5226C2457258525CD29832BCD2AFE280D2AF3288F284328DF25DB25C229AD2982289D2AAD2B382DA92D7E2D0D3635324F2BF22871267C24DE260E27A6294E2BF029C926E1251125D82571250B259C255F253328D82A882AD728102AC22AA82AEA2A93292027C22436257B2491247F253F25332666245E2481251D268526C9257025F7234125252868297D2B1A2C482B592CEB2ADD283327FC25B027F8284C2853280629802AA62CF12C0A2DEB348032602C3D28E525A9255927FC286E29D72AEA2A062BC0272025252467246522EA24A123852724298028DC294C29D22B482C552A9D2644246B246B2212247325AA270128B329E926D6252B25BF265626B525BB25EF23E4258D29862AFF29002CAE2B6B29CC2949275F26EB251A275F2ADF2AC028ED261A28082BFA2B4A2CC23207314E2C7428B1266727D027EB27422AA92C722B1A2B552A2B27E724E523B6249322C9243C27362A372BD82920295B28452AEB2AB927D624A62250222524FA26FF29C72BAC2BCE29E226D925FE25AF26D124D5252925C624B728F52A9D2A892A9D291A2C2E290128A025CE257527C829832B172B86297B27FD282528812839322A31AF2B5E2957266F275829D12C142D2A2C852A2C2CBE2B512A9C26CB240A2349251929AF28542BE829652B552AE92A7427BE2584255E2313233A253C26CE261A28852A5A2B082A0828AE2496244D25BB26A42440257C27D629DE2C902A8D2BD22B152D8829E1267825C925F227C32AB02BAB2A442A34290F2A6126F32784308731DC2CE02907282A270C2A222C9B2B792CBB2B712CCD2A5C2990270B259A238B2378253E27F92BDA2ADC2A292B372A4026812623242724092672240726C6264229CF2C112BF4292C2725259F237023D624FD2589253526E0272129DB28F628CF29042A0E288E2348267125DB262E29F52A6D2BDD299E2752267B26F726973142311A2BD42AEF276229232B412B4029352B472C3C2C302C2E2B0727C0254C24422435279226A12A2C2C812AB129BE267925902259241D24392689253B275726F827C82B882C842AFB28F5244024562432256E265F25EE26EC29A929B328932809282E27FA24402492229523CA265E284728F628E12A0A28C62642258E26F733AE322129AA277B261129292AB729DB2A342936286D2A322AA427B4246D247C26E8241A289C297B2B82289128B8274526A62347236A22EE2384254E27A0280728552A5D2C4E2B53297726B02440232D2426276528A32798286E276C28482731284E29242647233D23DD241B237725B428A227BD26A928D3251A26DA25F2263F31E12F93291E269927B5294F2B112A4629B9266A277229D22A7D257B254F244A25BA25C52681293E2B942C23298D29572749237D236326F2232627A129882A042A2D2BA42BEB2AEA29AE26B523D32369244C28E32A472A0C29342774285C28F9299C2BC927BD249B24D2254125F3242C268025A025B92776254D2548261526B42FFA2FC7285827D6266A26EB290E29C528FA2899272629A2294628512467258F25492899273928972B282B352CA62A862A8826C22472230626D627DD2A9F2CBF2A2F2B652D402A2F2ACA26512411233F251228BD298C28072717261A27EF28ED29BF2AA8287027F324B028C4282927DB27F525B925AE285A2624257A278E293E33622DC927F425EF27C7272729DB281F2A0028C8265C2ACA274227D125C5276928272A9A28C12676278429F127CD2AA62A0F2A8B2776241A245E260B29732B172B792A912C312BA229D426332410270D27E9286A2B7E2914288C2755276B28A22948291027E326BD273329A52A452BBD2857253D249A289B279228C028002BB9347F2D44251925F626EA29B229192BDA2860289B29202A2A27D8257925FF271D285E290328A625252654258A26F126F32822281D25E924E124952507270B294C29642A602C852BC8289225A423A82617287E2B252CAD2BCB284529D427442875296E284F27FD27EB26012BAA2AF7299327D425D028B52B2F2C182D922A102C5F34DA2B9225E2240926E727C329722A1929B628BA28E32885265E260E24CF2625278E27BE268D26EC260026A925F826B0266D254C248A250024DD264928322B1F2BAB2A882C562DAD2859263526CE267E286E2B5E2C5A2B2F2BED29CD295A2A9F28A4261D26B127E127582BFD2A3E2A4329A726D22AEE2B862B4D2CEE29392B1B36312CB825132603261D282429F427482783277A26B5261E24F723BA247F26FB253527E327CD27EA2816284E27D325852608286E25512341225E25A52895290E2CCA2A1D2DE52B9229A026DE251A279C28042B5F2A002D0E2B052B422AA6284B278725202423276E27752BA92AB427F028F028C929722AA52A0D29F328A82A6E34D02CC72693252627A328592986272F274C2771267E26C324B62431235E258229AD2A052B002B0A2B2F2B0D2C8029332888256C24FC24D924C123F9277127CB27E3284A2B852A3D285A259E24C0249B25D22790295629342B392965288427FB2551258124102655286D2BB22B032B6E2AA22AC42AD22A89274427C028DB2A72353A2CD6272C25BB27AA28AC28AC28062858297E28A427AB24F724B925CD271C28302B7A2BDA2B5A2C692B382DA62AA827EE24D4251A25062531244A25DD266128382AE32A572A5028FC2301234125F023BC257C26DB271629B029DA268824AD238325FC262E294429CC2B772B032CE12CA12B1D2BFD2A60299F28D427022B2335F22C3228E828B727132AE52AD329592846290C2B432A9A27542585271828832BB92B172C212BF22C212A432C33297227E724862790268025182573250727A829742AF329FD2A4C25B723E922812489235B266227CC263827B02A7E2730250125F9256027C628F72A1E2C462C992C322D792CD228C72B4B2781274B2B812C0A35FE2E3527C527D428A82BB52D562AE929C62BD42BD82BBD295628B8281F2B4E2A072D392D072C0D2AEC2B9C2AD626F52595250725A5271726C424EC23132707296D2A942A59276A2463220723FB2464243C27C429482A4E2A592B5F2B4428CD2665258B275B283B2A002D432B8C2BF72E722BB028882853272328AB2B032A2835812E55297E27A829CF2CA62B362B5029F32AD42BAC297928E027C8293D2C102DDA2B192C992B1D2C412A8C2AE22732245C232626D5259525A6259624AB25D42730292328DC24A7225723172467239025FD28BB29702B462A4E2C702ACC293028DC260825D027B92BFE2C412BDE2A602C222BCC2A80278D278328BB2BE82B9334402D84290E29492CF02B232D5E2CA329BF288126B9290827D629DE2A262C572AEE2B202B852C4F2B5329B7271724BC253C251A26C1276A25B625E124F623C525ED265D26F0234123DF253024E6220E2323265129F42AE4298F293A2A712AA52741271126BA2783286329632ADE297A2C9D294728F3266828832A9D2BD92C0C35AF2B852948294D2B4E2E772CAD29042A1A268B258F25E924CC28FB294A2CD7295629292AF82A3C2C572990264C2650266226DD27A528AA282F27E02468245825A1250D265A278A265324FD244D24C3225324EA29E52854291D2B432B2E2B94296C255624CE263A26232AD929862B832C852A3328B126F227132A0E2ED02DBF354E2D94299C2B342D5F2C292B7929F92994266424852511275126FB2868299628BE28F828D8288E2B9F291026DA2760269B28782B352DEC293529D527F324B323C126A527BC27E127CA271C271A26FF25C527002A492BD62AAF2960299D2A1727CA249025612525263727F827F52CFE2B642AB02853284327FA28B42B572DA333BF2EE429562BC52AD92A702BE02AAC293E282925E324AA243527C72844282328BF262B282B28682ABA290828B127802A202AE12ADE2A642B162BDF27FA24B023F7272D29AB29542B142BD02819286A253928D92B442B982CF72AE128EF295E28C525372580234526F826ED2A5E2B9B2BEE296C281D26C527D92A672B8B2B0634F02DEA296C29F6283D294B2AB02AD428432896257826F9277128052AE32BC2271027E9298C28AD26EC26F42658295E2B052C522BD22B402A522A4F284F25C925C127CE29A12B8E29322BF22BDE29C42632282C29162AB828BD2AAD2A2E2ACC2AAA27A02435253925B728012A5D2BE62B7A2885270E279728CC29DF2BFD2C6734802D0529C72882280127A3282229C32905290827E425F0297B29232B642DED2A0B2C2C2AD328D0266826DF2794292E2B9A2B3E2B492BD5294D2AE12645245F230C26A9261C295228332952293529A526AD275127D7275426F628C228822A912BAB281D2564248C24382849298A2B5A2911277726E3269A29962A8C2B682C1936D0308029E727C1273A28102A9D298B2985298E2773265F2ADA2AFB2B012D082E8D2C972BBA29D126EF27E0285F2B6F2CAC2BE72B742BF82B502AC427D3223523A426B526ED29392818283428EC261E262E269528ED25872666259A29682AD72A132B1A266D250725A72558275E274827FF26B1260428442AFF2CB92A2A2D96372A3062294C271326B928252A122CBD292D287F26F328542A152B5629F52A5C2D4C2B3B295A290C29EE27C428CC28382A042A9F2B342B342BFB2A1427B8246B268226F927A1286527EE279D25BA2439255828A9287F2AA7286027A826A029FC2A362AF426FD234924882430257B25F926BF242A263C29812A6C2CEF2B182D80377430A52A7626F727CE28C42A352EA12B2A2AFE288429442B522AC52AE929802A5A2A972940282D288C27A32CAE2BDE2BF9298E2B6129AC29F7299B286524C924562661271C29AC275726C526D825DA259D27722A532C282A0829BA279329482BEC2A1E27AE242E248A253C24BD24D6248D25E226A7283028692AAE2A802BF4355031202B2C29F3288929122B072DB52BE12B9A2A582CD12B0E2C222C9A284029DF28E626DF26E928E2285D29742CCA2D082BBB29EB26D825542683238824BF24E026ED28F22A2329D927322731260A267227E2285529A927F32832284127EB28C6273E273824992272262F250827B225C524FF27092A192AE52A262A442A1333352F0E2973265726B229E92A2A2AAB2B6D2CED2A3E2BAD2AA52B192B5B2AC728E32636263D264328B9269B27E6272B294627C527C325A423812490222A23C4236F2502281429E9285826AD25FD2617264E26B729C72829283D294429D729192929282B259424F122E4252E277A27CF28B32641299F2CE72A102B172B3C2BD932052FA5295326C625C6270029702AB62CBD2CE72BE92A1B2BE72A9B2A5928E22795258826AB267D26F026AA273427E32564266026C6253024A02210233521582310276927602AF2283C262C256526FF26CB29052CB02ACA2B252B012A952CEE2CDA2B05280D253A2674271429652A652D642B1F2BF02BF82D712CAF2BFB2BB733BE2FD528F425E6240526AD276528B428432BF32A082C542ABE29DD292529C12796265726A227B026F425E325BB251B26EC25E02781273E245D23D021A123A7234726702848290328CC252C267D25BE243829AA2AF82AA22A922A192C132C8F2C382A63292C28882402288F2AA42AED2CF8293C2B3E2B80292B2A4A2B2B2B9234842E272986269C26292730289528D727CA27412AA6292129AD28A7273C26A0253E2650269927A227D1260E28FA27F22866299A2B45297825F324782372231222932455292B2C482ADF289828B924C8265829B02B0C291C282F2A7D2CB82C552CF62A732912250D265727202AD42A3A2BB9291A2B002B3D2AFA2A382AFB2ADD34BC2E56285D28EE27152AEC2B7629FD29D129442A3A2A0C293C28232735256927BA238227BC298F29B427E1250F29342A382968297D272127F726A12546261525FE2514288429052B4A2A8A286726ED250F27C129D828C1286B2AA72D0C2E7D2BF528F826E8250027182A3E2AE72AB62ACC29242A882A4B29B32871288E2AC1358D2EEC28F9267F299F2BC12AB229E4289927CA29A42BD72A54281A289D26A82943289B2720289B27AD257326A628312C7728B3289A274D278D28D129A2271D2743271828AD2810298D289F28AE28352690259B26A7267A28552AF12AE72B252A03271F275C265D288C297529482B4E292F28DA2A392C2B2862270F27CF2B36355530422A8E286B290C2B672A1F280427C3276429152C6F2D702C3E2A372BF42984270E279D2742279C268327222AC32A122CE72947282D295B2C1D2BE128B529922A8A28B825BC24A225D426AF288926782678253D2601269F29E62A6429BF282A272027E428AD2A512A422AFE2A022C662BBF2B8F2D4A2BDA29A729212D0B37CF2F982A4B2A3A2A4B2A8A2935260225C725BC27D8291A2CE52BF72A362CB62AE3292F2814276426E527C4268D27E628B127FB279A289628CA2C812B022BA128DD29152AFE295626C1246D25A6280728C026CE258424A523F92AFE2837284F26E6255029E62A802A1E2A6429B72ADB2B212B8D2C2F2CAE2BA928C627B12A0435672E7429EC26812876295328D926B624A526A3277828012A962A032BF42AC72AF32727277F257B2727290C28B727DA26ED264E2749293D29852BE12AD52AE029522A0E2BBC2A142809265925F328D927CB251625C0227C255F26DF27492654251D26FE28822A2A2B4F2AD829A22A0E2CA02A522B4A2A5328AE27892745293B34D82F3627E2264128B627E2266525EB27A727262AB32AA8297129D92AF42B602A902820257A26492894298C2823295C287D28A9260A281C27CB28F726FC263928292AEF2B192930290E29D2290C2A052AE529BF25EA244B241A277026B724F22529268829882AE82BC62B2129BF2A942BE32B222A9C2711254727CE263F29D4340E2E1929E6262E262126DC2583259E27482A0E2BD62B8B27FE292E2BD92A852B69284C261026A9291A2941288729142A772A99298528AC270D2616256727A128F22878290829CC27AF29A42AA32B9429A227FA25B525212596290A27DC277927FA26002A282BC32AFD2B792AD029972A252AD0284C29212620263927AC29843224303C2A8B2912283E275527F225912AF82BF22B122B34295A29F52A9B2A1E2BBD28002696272B29F229012A5E29772AEA2A142CEA281628532649265327142875289127F2277527B327532BB52CAD27292672276C27BE289429D227E7258425F2275A29832BA02DC52CE32ACC29462B3129B9267B26B62634289D2BD32CC533932E072B5A29922A342998284B27C929D52B6F2B612B4C29BF299E29E12B5229C62799264829272B972BE7296B2AD92A302BBC2C45289D267F251F268728BC28812663269C266F28522A072CF829C4279B264726AE259F28DA2A3D2ABB28C125FF27D927A22BC52CAA2BD62B022A4D29012A29289226E826F82A332D8D2C003617304B2AF529472BB52B6C28F5273329752C642BE4298A29A329282A7B2AB9297126D4270228462B752C2929822A4C288528252A09299C278326DF28CC28A628072829274C264E27EA28382BA12B7225EA25B625BD273A2BFA2C0A2A7E294B261627DB273D2BDA2BC32D952CE12A032B772ABF28592749289F2A982C022E6235E02FED2A7729362A232938281528C929632CAC2B3429912762278428F1277727ED257D256F277E296B2A46294D28D1267927D829CB26E6259D27A229292AD829D3281A2680259925FF265F27D42634255B24B525762997298C2B362CFD28F727D5265A27BB2AB7299A2BA62CE22BF22C6C2A662966282D27CE2AF12CB92CF7357330C52BCF298129DC28B126EA250429DE2BFF29282A71294C28012744280626B8238126652749280A2A772A2D2AFA281126A5272C277626EE2AC32A092BA72B4E2A0028F325532364266F26F524DB22C924CD25CD271D29812BD5297F2BA229B8264E266F280D2B922D4D2AD32BE12C882C852994273C28D02A142BEB2DC9365132C22BD12A7429E72842272A2779285528EB2998286E28AD2591266C27712511254E268C25B82830297729F127982680253425DE259C284F2AC92C4A2CA42A082B1028B8234722C4250A269D242724BD23DF2718274627E0275F27A62ABA2988277B24BD248A28AF2C7E2C292D142E762D2F2B01292927982881294D2BDF34C531F12B8B2C5D2A202910263526F4277128A629AB2B4729EA26B025C325B126E326C9263F26F3285C2A0B2B842AAF2940257E257625B927812A0E2AD42B442CE82A17289B259D249D252325E423F924FA227E28D92738272728F926DC261F2659246A243A278D26422B9C2D282EF12C392C362BE5280E277A29F82BCC2DA9352932542B1B2AFB2BDB2976272F268226AA28A929BF29BA29D027D9263D2868294A2915283E28FF27512A8A2A622A73274F259524682627278B28F628C1296E2BF929A3297127BE217C24DC22AE23F422142328264727D62871284E28C8293228022652254025FF258527C229802BD72B8E2A2F2B9E2925271028AF2ACC2CB033BE318E2A042AD02AB22ABB279527D0253328CF2ACE2AFF2879260127032B502C122B802B31297C29B8288429D02A06286B2410256D2638298128CA26D6277828D2289627392602236C235322F123FE2283233C26CC268727D628D326252AAB2A1B2ADB26FB244E2307270527DD28792BC52BC52ABE295A287926BB294F2D8B33A3300B2D022C942CA42B9327B627B625FF299E2B2228BC26DA270428DD297A2CD12B642CB62ABD2948294428332787269E248A251528532956281C28E42667275D28FA2802287E23C324D22332222E21032510278F27BF27F5264226002AA52B582BE5274B24BF253525D3249E260C28BC293C2B282A6026762516265628AE320030372D962C2D2BCE2A732766259B2664284B2AD32ACB287D273028DE2ADE2C632BE729C32B2A29B129C9270827F724692380269F25C628542A082B8928A1290F2BBF2C742AB824A223B1217B245421E4223F26B227542732280529722A322CBD2AB329CC2895242E2765261E289828BF2A1D283428CF26F0254D270A271132712EDE2BAA2A302B92298726B324C227272A0C2B802C492BB427C227562B1B2C3429B0297129CF29FF275925722465247E25C0272D28AA29332C3C2C202B622B562BFC2BAC2A7A267D23DA2380243623E023952571260229A627E12554298C2BF82DAC2998286726CB267226C828F229A328192A232A6C277F26CD2612293832FC2C4F2A8A29F029CB2864273B24B227F629512BC12ABA2AE928FE27A929EC2B252A232AEA29502B48289C2559272627B6284428AC28E229562AA82B572B012C3A2B542C972B262AF12624259425FC25D7244125F2263428ED26692593266728232CA82B62298329A7282128C2285A2A042C172C512B9C29512805277B28CB3173306A287926CF2742289525AC256125B927612CEB2BF52A2A288E25AB288429922A242D802A0C2B332810265126D3288629F6293029122733298429CF291A2CC72BAE2AB129172A0C288826072638254C25D8263C288D27BA27B62424256C27D829832A8C2A3F28F828C2273C28A72AC42A802B912ABE282B2958276C27ED32722E692928265E279A26E027F8255326DF27C129992CD829AA27D427F92612293D2A2E2D662C262B9927D2268B2894289C29282BE328B527F2297F2876299C28E82A192AE829C42AE8276726DE257F24AE24BA251227C12735278B247723C125E9280C2A332ABC293F29A7284B28102965277928C2275328BE28F627F6283832762DDF26B625C024BB27EB280E27E625A125EB276929BE299527F425E2268A287328462BEB2B6A2B9429F62719270328E92A9F290E29AF28FF282A29442838279C28D3288B28ED265C260E2934266F231C24D02666266528C7262F25042682260B263C27D4287929E627FD27CE26F027A0260F255F2504271D2608287929CB311E2D3527E3247627322A5D2B07280528C126C227A027E028A9274125512601272C274128272A072B35289527172770289B25C727C327012856295E295927AC28D72790255E2646259C25E027D52710248E25D6265B256B25F226E925D2252F25A6260D27D226D62678263328B9284A283D27062565253F25D12687265327A731592E7327B524B327D927B32B602A2028212779282C29B3291F295E254E267326D127DA28E529722A312BC229432B73299F276E273527522ABA2AE0295B271126302748274924F623672492264A2528242B26652436262526F829282CEF28A8269D2706281F2AA52816281B286E295A2B67282A263324A823B8274529FC298B33CD2FE4297227AC24D926A927FD2A18291226AC269628C8295C28C228C1280C2A672A90299729D02AE52A2E2CE42BF22AA22922283927852ACA2B97297428D226F5274F27E724452305242D267C26E22596262C260D257D26332AC92BF92A5127C927D327E126A42747285C28A526A6291C28D6268424EC23D9265529502C483573302A2A09289726DA269928512AB3288427E7272528512B872CDF2AE729A12A022AA32AD1281A27D527F728692E6C2B22278F24C3258B260529B629DC28A329092996273A267F238623EF2498272727FD271425C0244D2717298629832AC429D128B427A22785252E2738268527AA2AE929232944269C246A25F228362CD4332533322CD628C62645267E27822A3F2AEC2A8D295429372CE82BDA2C8A2BE12AEF29EB27932736264A284D282E282827D726062609243327F8261E279627C9297E2A002B08296A249725B1263928DA29EA29BD27932446254426A5273B27292A712AF028A927B1260326232645278C2ADB2BEB29D726F024B2251C27192AA8348432A82C7B286A27FA2598262929932B032AAA27E527E5294D2BD52CBE2BED280D28F2259F28E0266E26FF266D27332743256124B025C1254F24C025C826E0271F2A752BA1284F25952449287E28072AB829FD26C7245B25D5269D27B129CC2AA52AA8290E2AF82727284326FC275329AE2A642B09298F26FE244F281B2A6E355631B52CF428D42571260527BD284B2B792B9629B128642A1E2BFB29792A562AF12724272B28362801294D2C572AD32AD6282C26C6240F25AB251C27D9271F29D72BD72AC12978272524A325BA284B29412AD72761258726BF287127022970290E2B742C462AE928C8288E29812740284129232C35294D265D286A28CE2B4B359731D22B6F299928D926F425E827C628FF2917298127B828282A622A932A232AF0278E257028022AEB2B6D2D2C2C7C2AE4282B28412682240226B62600296F29E32AC12A0F2AD92765253E263828C627592733278526D9266F2ABE29D72866288E2A232B842BEA28D129762943279F27B627FF291F2A1A285F28122A562C86344F32C42C712BCF29882A9127EA25DD28AA2858295328A9290D2BF22952295D295E282E28FC29CD2C932B382C522EBF2B20298327EF25FA231125D525E827182B702BE72AC8295B27FC25F524A4250A266E269B26042585277F29112B5D2A372891282B29302A3C2AE92A7B2A3C2BD5292028DE27ED279F2587272D2A632A15341032D82B142C842A6E2992272326FD260C29B8294C286B289F28CF29F6297D28D2271D299229A72CE62B212C662C49298129DF272725A223FA239B2534277B29C82AF328642738267424A3237323A324712542267925FB266A2A0B2B4F299A2886286D2A3F2B522AA72B8A2B2A2B8F2A472A812852274E27E226AE287A2AF634A431572C4E2B102AD9297E27A12555258628F8272A283829042938273626082765260128B9285A2C832B5B2B342C88295C280629CD2661240A254423AF242826152934286524272374229523CD215223C5255C24862536265B2A3E2B5C29CE29B029BB2A3F2B292AFB2BCB2C6E2A2E2B912BAA28CE289A265527B929AE2C4035E52E832AA4275A28D327EA24D624E4240126AE261A2883270D289C25E9239824D82450262329452B832BEB2A8929B22927280B287B284B273B2624257224A3259A272A2791245A227A220D24F1222E214824EE251E25C8254829A728F02641283B2AAD2A762ACC28D2297E2A402B9E2B4A2B6E2939288F25A9263028AC2B8C35962F072A80291D27E4274825DC25D3250824B3259127A728AC287B27AB2695247524B124B42A432BE52BA32982296F2974284928072AB329FA260D23472252246C2693257F23AE231D238B22F124302340260D249A24C3260E29FD284827B8260F274028382A14292B2A232A692BB62A2F2C662B5B2ABC276C2643272E2CFE35D130942A3F29002B6429AA27DE233326272448250927712AED2A452A782AA8276A247725F6259A2B392C9D28C029162AD429052AA1298C290E28E124BF228E24E224AF25A6253623A022B72455248D23C9242123682441280C2ACD281C26ED27BF2779299B294728EE273527F628582C162A202AB829C225672588276F2C193612313B2B6F2C0F2B142C9429AF26D9257424A722DE243C27E42A3A2BFD2A8C2942257925CF258A29EF2A8129AE28DC25E124F6275828672832276B253C23E9237F266A2999279C250C26072461279125E625ED23D9238A25BD274F276826A9275F293429612A6128B128882673282F281D2A9F29872A81278B26802768292335D42F7E2C072C022D772AA72B5628AF24D5247024402539250827892970292028752738269026FF26E726DE273A27CF256625F326092798278C279124DE234A253A2612284228B126D726E32650272C273C2771266C2567261B28AF284028222A392CEB29ED285728AB2A662AEC2A9A2ABB2AD229632ADD284A270427672907359730942BFD2B9F2A302A2D295A28BA270B25E62249247824CF254024DF2589267325412739262A2686250A27942582275B26F826A6268628CB28D02677267E27E928C929B6296A293B277A288F29342A03274026CF2433261729D4285B29B829AB29C1280629C42A1B2D702BB92B2F2C892B5A29FC2897298F293D28E9264632642E6D2991289628EF271B2758266025E225C6224A2397244825CF252E2777285A2749260C258F24A924E224932616254F25EB262028CF272C28B026E025122784281A27D72731269B2886262E291629EC27D0268923832473278D2A4E29A5291E28A7251D26EC277D28A1288E2B0D2C232A2228AB266827A4284627C727F031FD2EF328C228EB26B326CC271B2708270226ED24C6254928202808285C2A242B242A10291D28E2242725DB231125D7264528D629892C4A29B1286C261D256E27B527BA26B6281D2883293F299329CE28FB27982533242E27402A882ADE2964290529802886252E27F8284128A6273B28EA26E025CA2529268928D728C029B832DD2E222762263C274228B529E029B4274E26C52490249F2787291C2A092B692B882A2A2A732AE927B72546240A2408279C27B6281D27F527F425AB248325D525E8269D25422773289529612A332A572A8B29ED2566245E27CA271A29E5286726D72717282A28B8272C28CF27F1262E280026F22441262827C128792B7D2A9133D52C27288F274D27E128DF2A7428F0274B2614256C268727C32B0C2C342D6E2B0F2BB92AA02BB929002880247225EA28C5261C29FD273E2752243A24DD24EB24C825B425EF246E259E271C298E292D29442A2F26AA24F4246A286B28472873294F287228B128B128832AD72AA0290D29A2277024AA25D923F327C52A1C2CC4342C2E452AB1260B29F129D12B1E2BF6277827592347240828702BBC2B3C2B432CFD287F29C92A082C29283825CA242327DC267F2782269126F8253024B2223124A7250C271A25C224562518278828BC278827DC26C024CF24C1288F282E29BB2720295026E9261E27B1294129042B8E28D428C524FD2414283329952B392C3A34622F192BF62884297D2B8C2CD32A14299927272506258027092BCB2858282E28B328FC29C729D32C2429FC254A2525245525A324182602257727BC230326EC25FB27E527DC27D9257627E22613282028BE263C25B024B223E327C527D1296228F627B0267727DB27A52A7A29CF2BF02A9128532509264428F92B8B2C5F2C173626318A2B23293E2AA72CAF2D3F2A64297B273625F5250127E628A52A772AFE287F28B529712AF62C60298D26192511260C25D625C1243D25ED26AF25D12377267D28C1263027F12771267129C62A8F28D6294928652488219825FE27ED2A7D286126422541266726AA29782B152B6C29D029F326C52740275F29FF2886290934FB2F202AF329B029422ACF2BD82C78294D28B925F524AA27432A672A112B36287627A128212BEF2BEC297829CB27AD25D8242E26132564251B27C6279725AD24632707289928C527DC28922AA12BA12A9B2927284125A122F12332264F294E28A425C425D825EE2577287429C129D42A8A28AD2724292B29962A932A0D2BAF35382EF927E12708280628702A182924284A27BA24DF24B72543289A28B427FE268026F227562B412CF628F726FA251C257823D22457231426E626C8248A25BE236424D7268B28FC2579280C2A8E299A2A3D2A5529F02491235324E6245E26682539257524AA241225B227CD27BF274E274E25DF25D828B5281A2B1A2AA12A4B33762FE4286C26D4283129D229F82AEA29702878237E235E26DE26242963283827DF278C289B292B2B6729A0285729372885246B2595244324A52545276826EC241F251026E5257E27FE28222A5F2BA02A432BB429782637231A2321246224682695256024782433268027B727D5261D28F524AE253F27B128DF29FB2A612C1F35CC2E772AE0294D27C32711290129382832265A25A02373253E250E288D27FB28B927D1284B2A1F2916294128232A392905286C271E24CB245B25FA243A256223A823B2220025C02601280429042A3329B92841271524C4248C239422AF23F124D426B9240F2669286F2968272326B224652549265725D2271629F929B62AE0357B312A2C06293027FE26A0272729812872269224BD224623BE23FD2560289B25BD279829A92ABD2A872ABB29552AE029262B872ACA288D249025EC245B272A271925612359247223332735296129FF27DD27A2271D26E4240523C8223E23DA255C265C26C6263F28F82A8F282B2702287E252E262027E3254427502BE32B6C36AC2F852B5E2811264726A1273A27B9291025A523F0220A2312231B2676273E271628D02AA62AC82BEE28D927A128C129ED29562BD9291A264B26C6251E282328B1261324DD223322E624C2276828A82766264A2870274B25DD2227236123E4243A267F25BD25ED267A2A7E27E226B625C225C72701284327A428D328922A5C34712F52290228E127AA251327052804297D279623B0249C23BC2265259E2762294C2B2A2A722C0E2D782AC32741281F299A2795297729C426E6244B24EB265F27142820269A25BE224C239A25782794263627752AAE283E26FF25FC23B524FB24C6237523E3247726D828C728AB26C927A528022716280C286127C627E728E3314F2F4C2B27286526802738266029222A4229FC26DA230B23A324AD24EB25082B262C822B5A2B092DAA29092742270A2831271B28D0295429DC24012573253026DF296D29F02546227E251724DA252E257928362ABE299728C326FD22CB25FD22CA256925302515254528B82864296E2ABD28C326AB2877295B2B512A552B3335D62F4B29E026A1254027BD2525297029FC282526782464229B22AC22D1259D287529B8298E28B9292A295927AD261028412814289228CE24B4249F221C253725902676265A26B824F12174255B240E243C269828282766282526EA236222D9237F23CC22732465231C288729B8299628B527762777290F2BFF2AE32AF32A4B35512DD0271F27F9233E240525CA27C52878297B26EF23EE2243221A250C270127392AA0292029922966292F279E26BF25A526582828273725F7255C247A23372393245A266824FB22A4218C230F22BB2376258F2666257426FD268324F62346215C2398237323632510273C2873274428AE25A726A9286727082AC12ACC2B3633702C09273725412490230F2532279328542844252224B2235322AE227125B6288F2965288626DE26E6268C25CC2426250A26BC253D25142629230224B722A6235C236C2377245822A2215423DA23E2234D2472234F24232563250B259E2297244A23F520A82278254228CF26A826D925AF240D2540283828192A122B852A9832152C6327182395221B22AD229F250426D12624241D24C5207821CF219C2371259C270527EF26AA25EC259122F123EB263626FF2575256C247024B722EF2413260C240523F723AB20D321ED222724EF25CD2500256325B32357265F24B72251216C23CF21F5206D223E27F628DF26122697238F258128FF28AF29512B8A2B6433992C5A26E42468230D24492422243F25C0266924572478213E226F21F32281231B244D26C6250F25F7223E2263233B256225F6247D2436262B251724FB244826A0264F255523AF212423B32369252727D42642262B237B24A725CF247922A922712277217E22152470272727ED26E9267927BB2404279227E3288628032BF734072CF6246723CB237823D5225823BE252824C32452231C22A82182208C216D232C249E2329265626FE249722D624A824EB242126032755262B256A23A423282715289C276E239622F420A123BF235526F52719270424A323B225152445247623232208222422DF23212686287A29672622262527A127DD279028E829F92A6232352CAA252B226D230024ED228D24CC2470257F261725C821D6218621C4226D230624F224CD24BF243125EE22632522239D259B2620245F260424A0237223E72621283827A625EC236721B923AF24F925742AF8299E2477251E256F263B264C2640237D206923AA2366249B2782276F2717275A282529C026FD277727D9295932882BDF25A222E62179237E237223D5255324282420231E222F20662033222924DB22DC23DD25BF22A62419249D24F723F7251C274A251725BC23AA23B2227625A22606256E25B72287211C2301248024CD28C027DC2516230826BB258229DD251D231B22FC21D32137254B24EA2712292E2904283B28BE274327922496267C316E2CAF25292290232123BC239423AD2575253524FD238323B2210620A4221A237524BE24F7249522AC248823B02412246B2468278A253623B42302259E221A232524ED23C6245F22D020B422FD22C6237128D328F624C7233723B223F9245624E123C82190210321E223F62428279B294929E52901295827B52687276427EC329C2C6425BC2324212824E4249F235A245E250325F3259C2303223121E022CE21E7227A2592244B23EC23C8223024F3233724EC266326682416249421AA213E2216224D2367223922F421F420B421CF249326A526C125B02171209E223E237824CD225E227C21E72025239523E325CA299729F22BB82A772873268727A627AE30 + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 D32817234C233221D021412156204120D020F81F561F291F3D1FB91E7C20151FC91DC31F7D1FA31D2A1FA91E5520DD1E371FAC1EC11FF61F631F4F1FFF20A81FDC1DDE1E481E401C131DF71EA61E131F801EF81F9D1E4E1E2D1F831ED21D321F42201C209D1F911D0F1F791E4B1D1220081E02212620C41FD41EF3213723262E6E2CC324922191233823B121C521332248223D209A21A320CF1F8D203720B01F1E1F2B1F131FED1E251F47209C20DE213F1F3221B31FC1200A20F92062218E1F8F1FB720C91F3F20FD1E56217D1F6D1F6A2196200B1FFA1E931FD621C620E21F39202D20DA20681F43208320C21FE61FAB1F6C21FA21A9217F227F22BC24092E3E2CD625A02498237522D7212D223D23D2207E23E922152352217220B7201B21B82041216F20201F0D1F352165205B207E21FF21D220112170213D1E7620E6215B21F2212121B02191208F212E20E81F4F20DF20721FAF204221C91ED220112195210F217520211FA1209920F420B21F32207B207023612161225D229A25A12FE92C3D293B26F9224F23DD22B12311221121DC2002219F205E21A920CC21761FA42091206020F321921FEA1F151FF22001212022AD21702207212420CB1E77203422CF21942240205B204521FE1F541F3D202D2110218F211D2106211C224222DC22472134211220D51F541F3F1FD91FDF207F2121225423A1211E23B3246F2EDB2DD727B8239722D0222C233E22492227224D212C2150211F20C71F6121E820841FEF20242029200A2028205122D31EFF1F7C232F21861E9B2148213120502012227F22DD211420E0208120BB1F33210320DF215E20CE20D321CA22692117216722B7216722FA1FE9200120C420762034214921C2219B208B211923B923432D362C632774244A254822AE23A12187218822B5210E22A5227120BA20DF203A215321D5203920F0215920E11F24210A2245203D22B921AA21C721CF215721F221DF215222AC212B20D121F11FB9204B2004218C2134228222BF21BD22EC2113221A23C322D6201721062161214221D9210021F221A423CD22CB21172457252E2EC62CEF25B124FF23F0226E225F23F421A821AF22242228217021D9207B225522CF227621E021F2200523D1226021F92174200E218F22CF22E122F1215E239922DD215722B921E8209520BE202620D11FF12103248D22C8224E23BD235122EC2281234F210B223B2130218421A821072289230A229C23CE210E23CE236024842F692D6827AE242A25E822C92034210D22B0217922BB202F219E22D3235C217A219522F921A222FE228722052137229424B7212722A9223A22CE22EC22D72120219C212B226E22FD20F820D8228122E31FBC234223B62276225723A9212A23C422EF227022B5219621782243224F22A1237F2371232F232D22B1241B241A269130552B0A27A1242023E7221E21C01FBA21B32202226321EC201D21E320CC21D11F8F20CD212F225A223B22F7203F20F0221122C823D221D1207020BF218720A920B42191220A22A421D1201320661F8D206D220622AB230022E724ED22292230228322F720A3228C22DE2383221322F72323258723CB23F9222823F6242125DB30992D9D246322F4212222F120D7217A21D9224521B1222E2294209E2105229B2138225021372205225C2237233B2264242823AA256F247F211F238C20692084212F22D623E822382340213A22F721B720B0221524DB234123DE226423EC21A5228A255822E720CF2289239524BF2573258F2564265D25A6246923CE251827C533642B8B2716248022F0228D21E521762112223C228F2388227B229120F7214C224122C7216522CF226D21FF23DA231B26A4231A235B2289217E21E020D52060216B2424247F24A424EF2118214A22192103235923EB23F523D724E0236B2379243F23E0217121A522872402241824D4263126E72431249E22A0248D260E2867343D2CB625CB2280240E22B320B2210A211D235823EB22CB226821E821F32143238B226E225922B225A82377267E247025D8242524B9230422BC20AD200C238A2322240826C324DC2479231122F4227A232226352679276027E32609250724B724BE2357233124E6226A26CC270A2886299428E1252024F323752473260E2BBC348D2C0C273A237722F921A621E921D9209A2276221422582487227124E823FF241724122385212E2423242225CE245A277A256C240B238821DC21AF231E24B626F5254E276227B12461228922CD244224002680293C29C528E7286E271E25D8251023A5227D21DF2249266B286529C72A57286D256B262E24D9258A28DA2BD932B32B652694231F24DC240B2264225E231E24242452231024E523D023FC25F2236C25912409239723102475241525D525D925F3248822A12143245623B624F624C7279F279A26A524B123CE225D2425241329412B822A1829DD269626F1259B252625B2211C231D244F284F273B278A283628992760258724122746296F2C5D366D2B32261925C12310254E23C82497223425F622AB252027D824A625E1250727C024D025BE251623D62250237924F525FF25F9233E2246234E23AF232E25EC253928122857277B254523F6214322572405267228AA285626D425F7252B24A1231A235224D3250927E629652A81298F2961293026C8253023B725A429FF2BA834702DE027A224AF23F7248525A724942430233325FD2648277D266E285A29672A65278E2615249123C425B326AB24B2256E24EE248A24E022D7222224BD246E25692785272327A924F521A9242E237B2444276524F72448252B26BD257F25CF24AE240A239A262D28C82A052C54282628212676259B2482252926DF28CA2B8D342F2B7827162644265824CC2499245424E323E92458285E29D829802AA52A442A7B28EE247A24202471269A253B2792284927C1269A2493211923B224C2255F27AF28522AE529E425D6219E20A5222F251C26AB259A244D2699299F28E226EC254E247523D62551268827FB26EE2505230C253D242524B7255F2741295F2CA734132D1C274426F123BC25C12660253F25232690274F291C2B7D2BF82BD3295E29DF266D25B925F2246C261729AD295D2A9B27CD26BC242523C1233124A727DE28962B9D2A0F281F2436248523D7232025FC2668285426E324A629312848254523ED24BB22A8249F231E2674245027B725DF257B24AB2322259227F528972ACF353E2C062ABB2601271B277527DE275026BE26AB28332C6E2BAE2BAB2B412CF028482780275C250729FC290A2A2D2CF32B6D274A260724EA23FC2369253429162BA82BFF2A852971245024AC2367250F25A828312797259D2466246123ED22212363230F25C926192451267525E424952407250B24D523C82470287128502A6C349A2F8929A02821280D27FA26DE250C27E72649290F2C442D712C032A942C852A252820247D272F293B29F629F429602989281D25C721202164261F26C7299729A12A322B9C286D254D23F4220A238B2525279F26F0238E22E323DC216F22CC2304266C260A27B1274229A42612261328FB258B2513260B26792750290D2A443458301A2C2C2CBA2AD7283D270A253E267C280D2B272E522C592AA429F5281728B825762963294E29BF29672B252AC72885276A241624B523272597277928242A472A382A992733255E2203228825DA2301262A26DC2453230D21A7224C22C3236B26BF264C288C289F2A3D2A5629232B9327A027422AC527F428CC2865285A32B830BB2CFB2D132B2D294228CE26772681289F296029202B8D295629D7265B26B0277227242CE82A0A2BD929122AB028A62523251923E124A727332779282527D127B927EA2565249E23C323F9237025982601264D2345234322A32234223A246627F629FD29582A452BAD2910297F262025FB276327B6287C27EC25DD266C303830732C832B792C0A2C7A2AB52863265A278B276E28A5277C269624DD264A267228EB291C2CA629A3284F276527F026FE24BA240D24DA269F28F0264527AC262325C325B424B6237223AE275E276C26AF2800289426BF23A723F922FD231D27EB29F02B1F2D272A432B0F2BD3276025AF246024712561254D244524DC240630C52FE52AD02BE02B662C19295A288F278A258A242A250F2620238C249B27DE27FA29BB2C1F2B902AD62882271628ED2705267424E92488285F29FE26C0262426FB253C26832379219225D127FE29142BDF2B8F2A4A2A00277C245E23CE234F284828F129332A11291F2811256624DD248824CC22622340230324EF2295251630482F9F296929EE2A332C372A9F27F0254027A925402721259A236124BE251F29C22A572B302BD52B9B27A8250327CF25C124FB24E826F8279A27F32636255526A025BF25D224E9248B266E28562B0C2B612B9C2BAA2A8E272E25BB22B6230A26892942284326D725F923AE2206224B244E23F921A3221D231325C425B5245C2F872EA52980290E2C702A7829EF27D0262727FD254A265925DB23BF259028E12A922D9D2ABE2A2C290328C527C8245A25A72440267E286F28D429B0287C28F627D9278A26E325A12571251929C32AD8293A2C042B44281F24C2244922CE220926B725602511254122BD232822AC2297238923DA214822CA228024E824C2279C312A30942B162A4529072A2F29D92763272F27AC26F3268E2566235E2567272729CA2AB92A7F27A5270326DF24032451258824BE25D7283C27EE276F27E92804280628AC269426E823DA267C26CF27002914289028D325E1220A226E235D224C25FA23AF223E23732270224521C7218A224F22AC224924FA245B25642776280033B030E32D392ADB2AB7298B29EB2834281C27A3258C260624B8268F24A826A427C426C32742267A2690250F256424942453259C266D264D27F92670273B28BC281E28CB2607252723F924AA24E9262B26D026812571234F2239216322E521AF22F5244A23BB227B23392378221A222D2247239023F62589265128D228E62B7A35DD31342D1A2DCA2B502A1229B92876275627A025C523CD24D82354263026F62759289F26DC26E525DE257C253624A123B6242228A727C226C2271E290429122A23298E2657253123612464257E2447254926F9233A239A2136235C2137233625C024F524D5230025DA233B2321230825EB24EA27C4288B27E327642A562B56342633112E022AC82960293A2C292A94287D276D24E225352482245D243226A5272E2843275C271829DE255A25762389256D241D27E7286A28E0285F29FE297E29F426D224C8247124472493253D255B254925D92337247C21B8230F24A6254E26D726572769273E266F255E2422233224BB254F28FA2A64294A28D1286E28EC32FD31242C8E2A3229812A802A9E2A682AA4270B2727239524B7233F250025EF235225C4250B27E626A6244623CD24BC242B25C327772A4729832A2A2B5C2A1828FF24942492247F241B253424B724B1235B23FF2439218322B5240027D52632277D2625294429ED25EC2621269E26A8265A27AE28992A4B2ABF2A6C290C286F306131D32D4129092AEA2A70299D2AF928CB28E1275327AB255524202692281F288C250826932654271A24F8232923D62278253A29802A9E2A482B52293B2A1E272D271425C625F423DD2440261925842254232D24E321AF2359264128BD29B628EE283B299E286E28D9265024F024B2267B286F2BD02AAD2A2C2982291029D1301D30D6290F28DB26A12898293B2CC32A46288F267825D8246D2621279127D126BC26E8244F27DC26B1257924E4238D230126B029F02AC92B222B462A1927E3259E2455254325A2245A236D269A25532593256C253624972487276229772B1F299C292E2A122AB6280D28F725C3258727F229B02AFD2B722ACB2A73292B280631DB2FC92A502888286B28512BCD2A4F2ADE28E6259B24F3255327402995296228B7254D2704268D26022754246E261626E8277529262AD6278D28E728D22554248125402517240023E024FD23CE25FA258827832615237B25B528D0291D2BCF2995299E2A172BF129A72703244C24B0260E2A402BFC29D02AE02A0D29F026D131C2301F2B43297C2ABC2C192B502C282926293A26B426DC27EE2A392BA72BDA29A3280427572616263428B2260927E0290B2A8A29D52906271B26A7264825B4240E247426312439235225EA268F2746267E274B26BD24F5269E28212B2D2DA229162B3B2A5929E8264A253A2528256227DF2786293D2C3C2BFE296E29B427B83145308B2C092DF62BC32D822C2B2B4D288026E1263829B12C0C2D6D2CED2B2D2B172B98289525B5261C2503264E29AC2A53293529B529A026C025B425C42363243F24BF25DB27C5243F25152A992BB128D6261528552796266D2A322D612CE42BEA29C4291F2AE328DD269D24F1226025772742294D2B5A2A20296328352808347F32C12D092EBC2DB62DB42A992826276E26F928DD2C092DC62C2D2BF62BFF2BCE2BA62A39278E263C254A2508264929BA28BA27D72908287A265826D2235E251D26B828D32804272D27A52A592B912A712A46283926C026B8292929B429B328022AF72673272D275D2423243C2555272C282829C82AC22970290D2834292E345A32B82D922C4A2CE32C102BFC29D128FF27A529612DD22BBB2A632D552BDB2A7B2C4B2913276125C925EE22A02591278B26C225BC265926F425AB24F723B526DD27D229922A22298529BC29FB2AF229D3295C263C2610258225E0261726062547254B25E523C2221025562454274529592AAB28EF29F8273E262727242ADA353C32972C712B182C882B3E2A6129B527A129FB28E42A302D3D2CCD2BF72B752B1A2DA92937265225A8249626E228EB26E727082661248F258E235C23ED24C72560288729792A062819285C293829E728EA27F223E423F0239624A2235C243A25D8241B2446230B2306243E269928682AC62A8B2943293B24B225F8267F299E32B6317E2BC82A192AD829422A6729882769261327482A612C652AD828A6284029E12822285A25A12400279A27F529572B3A289B25CB23C523812290229524C724A326D526AD26D926D128B7294429222796252E244923E022C5252024F823AE2453256B257D25A6244F26D32598271029FD294028F8268A24E424EF254C28D731D1316A2D752AB72B0B2CB12BEE29C9276027F326F4261928DF28CF296A2A8D29442A28283623612624267B29482C942BEB29E3273F239822A3241A227C244B262F26C924A924A8255B279429E227BD25C825EE245A256D254926BC250527032953286628A927E7269427C826A7264D279D266025F7230E23B9237B240F2948325332B02B1E29F82A87293A29002837272B263C26A528062AAC29E728C929872960282627E8254D2547275629BC2C9A2DA72B4127E8231422F622802481258A278E25232557267A2318254B271427C7246A241E265728862ABA2BAD28DA27E928452C052BB72838275D270625E2254D26F0254B25F225CE239A250728BF29A6327F320D2CBA2BD72A782DE4277028A627DA255E261A28512A672AF0294B2BA72992283B26E425FF240C266628232AD92CDE2A52272224F42300242D251E28F428AD27B7256F26A5249624EF232424DD236325B626772AF4296F2AC9285428BD28C62A342B2E286225832509258C25372787278128592810285127E226462B5E3478303D2B442BC02C282D292D222ABB290328AA278C29B22BC62A2A2B4C28DF26382513277526BE25D924A527B42A772CE1281E294326CD2273261B27E628852B0D2B102A1B28A2230123EF222F245E238625F1278D298F2A982AE4282828BE2A3B2A612822261824482441249625F928EF2A6F2BA32B5828ED256128292C8235E42FB42A032B492CFC2DC12D992A4D29D9298A270F2801294127E825F3266E256D24EA26AD2690270C291B29BC2A452B78280025D4232C25C626C8283C2A8D291329122A59290825B92364234622722422258D295F29EC2A53284028E127C7286D29132806263D2481259C23C92595297C2A7B2BAD2B3D29C728C3283C2A9E333A2F6D296C29AB2B2C2D982C1B2C132C6F2A7F297627B4268E268126D7255524EF241B28832AB72C9929AC29372B6E2AC729EB26C524FC258E28C72667297B28F3284C2AE82AE5273924AB2183239323312541282528A329872BFB273029572A9F28FB2535256923F826AB268D26C229182B852B032D5C2AB129E32A572AF632872E012B3C2AB42A202C732CC22CF82BF62AEA270F263B244F237A240225DC246D26DF2A852A6C2B632A2E29352A672B9E28F225C025FD28312ADF294D2AE729012A7729142ABE28E92446235824EC229824E6257829292B2D29E9292629DA276A259F2517236C248B2789267C26762A732B012BB62CE429822816273C28473291300A2C122C972B98297B2A0F2984299A2A60291B26E124C52394247424AF25D8277F28302A7C2B3B2A942AC42CF92A67276026DC278129592A702A2429D1272F286428802943277C2484236F220D23C224B4273728FD285629E127562676267A25B125A3251126F2273F2703281029D729A72BD52B53291E270E265D27A332ED32882F5F2DF12B0D2ABF29A828622787280128C92702259B23F023E7258A25432998270429ED281229F22B942B802BC628A22666268F285C29D2262126B42604280C2835293026AC24E922862334246F263D2945286128A7286D2624242E2572243323B4254927A92971299D28672A592C682B7B298827C82630273D2A253495327E2F362DF92B2F2B4E29E82608285C2A4B29F2263D258523A824B82626292A288E284829782B772C1C2B8E29822609265027F927E628C5265D24E5242126FC2739286527C82509245C241325AA261C28982A3A2AC927A127F3267024AD22BD211526CB26DA29052B1C2A7729012B152CEB2B4529B4255C26BC26EB29E0354B33CD2E9A2DCC2ABF2AE12AC32C512B4B2A9E2789263E25D125FB25E5278B27362A262E602B782D8E2ABB2ABC274E2618239E23BE26DA2604260B260426FF253D268C273626752592252725CC253B27362A132A622A9B2A9C2A7E2A80265925B324E627FF28A42A0F2CD52ABC2A2A2C3E2B3729CE2781254A28ED26502B51348933782FED2CCF2A162AFA2B8C2BB8298329FE270A271C257B262B29AC2A7B2AC529482B4A2BC92D7129332721269525E3228E26242720287A280825A326DB2735297F2A6527E3256F2672286527302644278A28DC2747289629352A2528DA24AE249727F429082AB52D592B4D2ABB2A392AE9288125F22302267429B32B2A35AE31252CDC2CF229F22A4D2A7028BF2554278727B92625270E2A262BC62CA62B8E2AE72B7829B32A90283824FD23F022E024E424C8284628CA2762256C270D288028F52876270E27A5282E28B5278B253D24CA240424ED26902ACE29EB2651248223BD25E727D729E7292E29852AF52A5D28D025922699245F275929222CD93793321A2AA6297529CC2A1929E0263E2782263F265E281328A628932A0E2C122DC3294B2A062AB628D823702375234B24222597274928CF277A258C259827152835290E2808268426D226A427AA25DA230B2589240A24D6268D26342790239A22892441245326AE29312CEB29D42BEA2D3B2ABE2669263E250F29212C302E5F36A130612B5A29262C992D4F2B47281927E9253728532AC22BA228D32AF1295629F5279126F1261A265E26132323240F248D2392262F2A5B2637259925ED257E25FE259325FC24A4261C2678254F252424F825A5270126B7251325922544235A232B25152567279529752B912A842A022C192AE927EF27C4268029472DFC2C9A34E9316B2C4C2D8C2DD32B942C1429A628E2297729BB2A442BEA2A20285B274E261929CE2871289429942701260624A725692525273626B926F024C4259526B524C124A2263625B727B92798272F2578252528742919287026A124CC24D6244B24FC2496255F28DA266C2A3C2A5229B22BFB299D274528C326E3278C2BEB2D5E359730E22CEF2CC62D9D2B732B472AFF2B1F290F287B2A6827CE27B52651285228CC2AC32AFE29EC29F129B824EE25D4255429D2293A287C25C9246825D4269E259B24B026F7264C29D62A542A9D2BC4298F2AAE2C8C2AFE279826D5253A26A726E9254E25BC26CC2734281F297D2B632A82267922AD25D42488277228922A63340F327E2BA32B232BD92B4A2AB42C952A022A442A3129E9256325EE25C628A029E52A782A9B29792AFF2760255A246927AC29D5298A296A2733262A26772609255C25CE26F0274D29C22A382A292C602A602C742C722B4C27F226A625882749294028A026BB2715264C2AE32A942A0D283924B62463258C259B284B27E028D0321631FC2B202B2B2A832A112C342C612BEB29A7286E27BE25A126B32586292F2B902A372AFB2AFB2A3728822578273429592A2B2ADA2A392864295429F0287A261B2503281D2BE72A242C8A2C712B422A312B642A89285026EF240E267B2898289727E226AD27FE27802BCB2BE62A7328752355256624AF24D5271A28882AD33687310B2CD42C8A2B732C572D1F2C4F2A062964264B267F24A8252A28132B4B2BBD2B0C2C892BD92A892758268F27A12B2E2F9C2C1129F0261429E22A2229B728EA27892B452CCD2C6E2C672CEF2B222BE62A0D28E9270425F724BF2539261B28FB263025792751271C2BE7293B26D3267325EF247624D325B427CF2A212E49370432842D032DD12C0C2DBE2D622BF72A6228B9254226CB257027892717298F2CAB2C802CFB2BD929D92736285429D52CBC2C212BF0291B298427422A8E274D262F29CE2CC52CF52B9F2BFF2A822A1E2ABA29522887243125D923BC24FA26AF265326E9246C25A026CE2842272E2644263A26CE258125FF233127132B9B2E1037E2300B2EAD2B1D2C792BC22BA32C292BF129AA263126E425EB28502A4D2B81292C2B062BC32A1B291126B6281B2AA02B342B142BD6279025D5240026C9259C261A2A2C2CDE2CC22B57295E29D42BA9298F28B925112573251128DA26CD262626CE26B8264E2794252826722561267A288F277326D926F626A62889292C2D653515318A2DB52DFF290D2C632D012DB92A0C297F277327D628FD29192CB929EC2A042ABA2ACE283928A52552296D2AD62BFE29F92AED26CF23E8238D24D2247826DE273F29F82B97277C27DB28522B3429922890268D246226872BE629E829D1297629B0275525F8253B2651270729D6295429C925CD2939274A29CE2D392ECB352932412BCD2AE629B82CD62FB02DCF2B1D2AC327C728C52A942CA22B622AA627A22A612CD429F526F228D32A122A2F2B782A0029F1283A256223C522F5243D26CF277728F8264C260A260828562A5128252788274C27F728B92B982CCC2A742B962801278424E224E9284229BB2A682EA72A2D2890282028B829902E872CF236CD31412D3F29122A312D7D2D262EBF2B5D2A63292728E129FE2A482A6E29032AE92A442C7A2ABC296F291C2CF62BD828E2270F2A03293226E3245924B625D3271829ED283426A524CC2611291328A8279128CB273B2AF52ADB2CA42ACD2A852AC429FE242A241628A72A5D2B382B6E2DCE2C0A2D7E29DB289629092D1C2EAE36FB31DA2D412B542C4F2B922D1D2EC02B632914261A2A2929322BAC281A2896275D2BB82BB32B47291A28AE285F275A294A290B2A932A7B266825B9250026E028D82AE92AD0275B268929F028AA260A252D27B8296F2B1C2BE12AAF2A132B7229632957260A2522265F282D2A152B7D2D262CD12B0D2A3A2909297A2A7C2C2F354D30E42D522B9C2A162D352CA5295D2A9826B6265C286528D929ED272228C8271329F52A34295D2996275A268028E228D0271628B7261B260025A125D727F129AD2BE52BF72B532AE627472741267A240B26D82AE6289829982CAE2C9F2BDA29F525F6239024FB23D0287728572A5F2C9D2B3D2A0629F3264527372A8D2B0334B531FA2D2D2DB92CA22BC82A52280A28B92510264729D22CB72955297A286428FD29A22A6F285729A628DB25FE28F427CA280F294228C12469252E281D297729412C5B2C742BB4297628CD26F726E728A92A562B192B582B1C2BD02A722BE527FC258F2642254425C3266727AF2BB32A2A29B428FD28F7264A27482A6F2BF3313832492DFB2C032B1B2BF32A2C28B225D425C22597278E287729B3291029E92A952A6D2BB329EF2A8C29F0272E28362BFB2905299D26C0258027B1288D29CE28B62A3E2B74299B29C2273C25F0279928212BE62CCF29932BD12A2C29072BEC29A9282B27AA25DA278927412A9929BE2830272127BE26D328742B8A2B932A6E32AD309F2C5F2B332AF02AC22ACF271524BE2551256D27722989295F29FA2A4F29D72A7C2DA52AD8289F278E265C29B42B522C0F2B502AC9270229722AA12A9A2A062ABC293A29DD2434259127A929DC29672CAF2BA42AC928832AC129A929992AE9285C2769286F29EA2A9E299229D5298527BB288729112BCD2B292C922B54325130172BBB2A7E2A0E2AEF2999272D26C6265727CE275A2B2C29B2289F2A832A972C202B6C2A932896263827EA28992B822C7A2CC82B3829F62AD02AE32A0C29FC287E26E5251E23BB24C1267D29372A062C262B1F2BD4284529B0261827982793261926A6274F299A2B5E2A112B5829F1271C296B29B62AB82A152B3D2B30345E326A2AD4281A29D629982A4C270926822732282B29EC2C662A96298129322B222BC42A91296226E6252C2680292A2C712C152C082BDD2AF62A482BD5283828512900277826BC22E72459279A279D27E8271D29E9261B27AB245626EA232A241F25DF234D27FC285929FE28022833286629152AC32A9D2BA92C872AE22BA735A0305729FD267F27BF29A82AF329E927AD26C927A42BB42C0B2BA327CF27302A382A81290329162705245424C0254B295B2AF92B092B762A0E2B7529192A132B0528F1253B25ED238326E325F624C524A2260F26FF26CC251525C7221E2392225623AF23CC24C22723290129E5272C295528BC29512C932BD42CE92A102C4B363F30D82AD327C329962A342A7E2BAB285B2718292B2BAC2C4A2AA129D7289E29C72BB22BA6280C263D23AA27D626A5282428F02A642AAB2A042B182A732789264B25DD239D25DE247F25CE267825F624A625BE27EB2860272326C9240E2483242A258424B82616298E2BC729CD28832730290D2B2E2D272BEB2B282A902BE536C531832C172CF12B8D2A4D29D7284926AB278E28F42BE72B2C2A052AAC270A2AB92B1C2A3F28272779250F266D29932A48285327B626B227C227CE24E32522253625F42463275D265126C326D8250826C827652942291A2733288F26582406256B241627BA27CD28D62CC82A442B5E286F260C2A5C2DDA2BD92ABA293B2B27352831252C3D2B90290E2AEA27CE24122616282729992BC82AFB2937298D2985299A2905291C2886284F277C29C12A622A092698261B27CF26F7277D25B2254E2577254F26C727A928892643265F28CB283B29392CD429C828F528AF2880287D263126F725AD287B29B12C592CAC2A8C290326CE27232B252A9729A02A3B2D5D3691300F2CD8283127D126D925B02577281C2A8C2B9E2BAE2A4F292F292929C62A7228E927F5268927C529A72CBC2BDC270C26C7257B2781280A283A281A260327DC28DB26E028F4270E2689251A28CE296E2CD52CE529D12A9F2A212A492CCC2BB62AE828C0286A2ACA2BC22AD529EF2AA5278C268E268C289C27FC28A32CCF358E31E12A0A296C26872593250126E2277F2C8B2C2A2CD8297129442BD92C002D312BDF28B128AA293F2B3D2BDB287926C5243E277E29CE29A62A3D290C2BE02AC32A27297327312576241E26E327C327612B7F2B842A8C2A5F2B262D902CDC2CF42A192C572C56280929F428AB27D12956276126B025A323B4255A298E2B3C352230232CB72A8C292D27D42504271729552B482DB829C8280729CB2A782C442CC12AFF272628FD29FF2AFB2AF927C125AD255B281029A228DA2A5E2AB72A192972288B287E270425A124BA274C263E29402BE12B08296E29752C372DF82C0E2C7A2C7F2DCB29FE29F527EB27D9279329EA280C287C26BD25B727B129262BC133AA30082C6D2D8B2AE22847272E26322AE92CD02B0E2A4E2873283829CD29B32CF42641278A288E2955293E263226D92537241B266B26E928152AF929822B3A2A0229E32605250725C3254C2612275727852868295D29312B7A2C422D912C662A152A5D2B732AD5295D2A512811290B2B2F2A4328D4255D2507279628BF29C1334230FF2C2E2C122C88290D27722630299B2A6A2C6A2B3D2A082886282E28512BF828402728275B28A627FA256725F628462636282C28D4275D29452B7329D929D3280B2704264227E42790287E29A328982760280B297C2A662B6B29B5295C291629302BC1295C2A122BC829B42BAE2AA028F227BF26552444268E269029B4321E31612DDF2BA22B8D29A2279C258B27ED29392C092D162D112CBA29972A262906274727F927742875283A2788271A28FB2A9A2B202A322AF92BD52A7929152A542841250B25792640287F28D12909294F2A0A2ABE29E427E32719278D262C29E2298B29A529CD2A652B792C7D2C0A2CFA29DC279D272627BA289228BE29CC33D430B92CFE2C692BE32948282B24B424E226F229682C3E2C5D2B292A392B5B293B299C29F329BF2827285825DF25F828C728402A8B2AF3282C2C782B5D2BD8273B261D266B288B28CD28FF27C229592A702BF92B69291C25EA282D26AE270D289927572977298E29AC2B202C2D2CD42AD527822798274D296729482944294231D930502C6429402A632A8228E2252F246B266629292BFE2A092A902ACA2A1C2A5528382AA32A1C2B3E290D260427FE28672A972A272ABA28982B832C342C7C2991278A28F42A952B862AF927CF29832A342BEC2B3B283327B52611287B28F3275827BC27A5278E29A62BD82B572B2829C326892786287E29522CA72B922A4F321033DA2AB329E828C72766264E24692585249B28832A6F2AF0295C2B9B2B8729FC287E29632C8E2B5C29A226AE28222AE32BBF28A1271226372A6F2AAE298D289B27E1294A2AF52BD62AD0293629CF2AF42DDD2A41292E264E2847295329AA299627D5260226EB28622B502A342AB827C526272761284F29522DD62B7B2B6734A731D02C3529DA273027E526D6245324B42461252B29FC27472BB62BFF29A52AA529812ACB2AA22B7627DF254B29B72AAA2A6B28AA26D527CD27D027BE288B27A426802850291C29D42991289E285D29E22A022BFE28A625B02A152BB22D982CC328D226BB255127BD2A872A0F289F25DE2442268F2BDA2A982B592B3C2C8332F832212DFE2AAD29F22A622BA4277E274025E025E6271729F62AF92B602A4B2B952ACE29A42A96286B26CA2665281B2A5F291C29112787283628B127062711266C268D274829C727A3261E29632A41284029902BB2297229B42ABF2AA62B152B542A60276A26DB28D7292029F3269526C324F5240B28AA29442B8F2D1A2DCB334B30272CB028CC298E2B512C9329CB27A3243825FB27F428492B172BF52B3E2A332ABE29132ABE27E7256D257728E7294229422986258C26C72529255E25C324E9232F26EA278C29AF2A612A6628B3269F27D8275826AD28B32AB42B592CD62A312AE02589261927DF27F427032566241126D126ED27FC27122AD52B4D2BC2343E313C2A8B28BA28212CD22B1F2A312711263E252726B228D72A7F2CE12B2C2BAD28312AE428F127F32506243E288C2745285B281E27B2264326E0268B24D6234025F32698281E2A452B362C572BAC251727E926FA271D2BE92CD32A7A2C902AA52A762778263C267F2941291526DB2456263028192AD12A292A432AD82B49347C30B32A9628E5289B2A6D2BD829FA278C263F26FA25B82683286F2B392BD72AB3296829E3295A287E25ED233A25AE268528712A32274B2660276928C7269C269B267F263C283A29642AE5295428C925FF2574274D2A7B29402B322C722A112C1E2B0529CF277D246B27D4286B26D6262F26FC285F2B0C2AE72AFC2AF42AC2345031FB2B85293E2A092CE82A0A281027F1266425EC2605278C27D528602BFD29EE279D2A132BE027DE259824F8252D28AD27F1296F299527222ADF29302AA42A532A2B2917295727012982273A259123DD268E2858294D29332A30288729AA2AF329ED28D926D4263B28E6248225DE259127552899296A2A852A1129FE2B3D36BB32242C1E2C252C482D0B2C572A3528B925DA26AD250A2701258B27C829FD29812A152C94294F29CC25B52364246827C828E8286F27CD277728742B2C2C822AC22B3F2A74276E25F22723268424F424E625CC2A6E29EA2717273C268329162AD5299E27A32425253F27C525FC25B026FE27ED28162ACC281C28D727D6291F355D31222CE92D5E2CF32BAA2957299A29522809280F2996271C253F25B826CD29B82BA52CB62AB1291C279625AC278A2B642AB9292027CA265729D229702BF42BDB2A38298F2846271B2765246223A1254B25FB2A062A652706281A27D0260427B427CC272C28B02490261126562694259A26AA2865292329E929F82A782D73360832592B4C2AD62B1F2A99294B2AD92A2C2B182A54297929AE262B25C125F2289D2ACE2B352C3729B0278C26FE291D2C0E2CB2296128C1260D29372A552BA32B97290C2A5F29FD24EF256923202449245A25F8281B2AE22A652A462A842BF529CC2A662BEE29C827CE250C26E62536269D257E28DE29E328F328D72AE82C8534BA31502ACF289A270C2877271C2B372BBF2B7F2BF82A0C2AF426EA2365269F27A8286C2AFF28732824263D27992B6B2D6E2B632A04286328E928BC283A2A0C2A2D290E27A226C5244425E8231626852527261529B829832A002C8929A52BC02BE72C942BFA2951261F28ED26A5260D287A27422754284B28D926A029562DD133C731F32D9D2B4A2A2F299727E92B302B122D2B2BF9277F28A028BD25C624C126DB272A29F427B8263F26D326BE296C2DE02B2B2BA629F1280828042A112A9F29C0294F297B273224A226B92600272F26FF284B2A9C2A4F2A392A2F2AB82CC42C3E2D352CC129032A0D291228092825278D27C929142ADA26D525FA25DD287933FB31AD2E562C182919280027C228512A6329DA281428DA285B286A25DD2401268D26C025BB2894261C2737262729552AC329142B1726B82582274B297B28452A822BDF2B6228DA22C9240725B829D1279A27372926290628EB280A2BA72B772C522B4F2C722C0929772A1D2A3E2A57285529CA2727284726112515268326BC311A329A2E212B9629EA275F2681270C2A8B2A62281C29C22A6E28E625E7254225952468272A29462A71273325BD25572777291E2BE427A6250D2771285A29892B4A2B662BBD280B2431240427F4282C28F128C2299329A32A2C291F27112A8E2BBB2DD02A8A2B2F2BFA29BE28BD28C0279825AB28D62704252525B125A5276D301E31D62D432A6929192810282027FB29312A2928F4263C2801285226E8247E25BB248427C929CA2CFA28B125B727B627452AC429C72767258A244F2638272629CD29A72A7129DD274726CA26DA27A9283929232A552A592A1E2979270427DB27FA29012A302A7E2CEC2AAF283E263026F526D2275A27DA2557255125B0264F2FDA33C52BE228AD279928A1271D2AF0299829C42BEF2851280C27AB245C25F2236F246E284928652A20297526A1258C275828AD28C526242355244925E3254E282C288B27B4263C27EA267D2798264E268028EE2AD02A6129FF29FB2638260A273C270F26DB26052773293F28D1250A268A25A22729286526A727CF25F025AA309F317D2D242A1E2ABC28602A6B2B5D2C192CFC2AD32BBB292B291D296426A525922464271B299F2A3B282B277D27BB2612282B2A652894256B27CC25572875269B28F5275C28EA2923283027D4263E25D52616299D296029C928A3266D252426D626C82529253426FB271F28F126C126FE248D27822871290C2A2A299A2950328830E22B2B2CB829232B8B2C862BDF2C782BBC2A262A242B262B252A0729A427C224A3263928612ACF2AFC271F26AC261A2BD02A352A79283F27D2281929C1272B27E2273029B8288A274929F426C823E9258629DE29F02A2D29D8277728FE270A259B249C258027702732284026B126CA25A2254C28F0298B29592B0A2CD132A02FB42C482C312E842ED52D982B462E6B2D342C5C29EA2A2F2C0C2BC12A82286825222518288C2A9729D6280F2687270428722BB52AAD296D2AE72A9E291E2AE22716257927A027F426F62774271B24C926E228FA275F26E627FF27E2285F28A928F226A62545270E296A2AE3289C26A1265926732840293D2A0B2ABC2A2634AC2F832AC12AB12D852C062EFC2BF32B482DA92CBC2B622BA82C702A962AC42745252825F7264129C52BEC28892823277528462B8D2AC42BCC2AC02A842811268926A6262125BD25CE25932673244223EC25232541271A266628DE2AF62AFA2A902B6E296B29A0281E2BA82BC529F52817269626CB262E263C286A29892A9C341530932CDF2B892A012C582A192C192BF62AAC2B6E2B652B292A452CAE2BF4291F278525B3268F29312B502B3F29FB28852AEC2B792B6D2CCE2AA7286A270F264827A0273A27EE25D5250A261D254524F025BC26CA260D2794283D2A7B2BF229802B052ADC264527AF2AFC2B67277C26B624B0263E278226F0278E286C2BB135AB303A2D1F2DE42C602CB72BB32AD629612BB72C3F2B512C3D2C762B6A2AA52916275E2704278627EB285229782C7C29D9278028C729FC282829582862264027F5268B27CA285B27B4262826A9256D24A0269D255327FD29302B572ACD2B7D2B0A2B202A63290826F528C928B82775273E258A271029282996289E294E2CDE3306333C2F2C2EFB2DD22C7F2B842B7A2A832DA92D742B5C2B6E2AA32BB32BEA29082977263927D126E029432AEA28DC267C28212AE829312B5F2914285A267627D6270D2A182BE228D329E128E3260A2678260027BE26B1287729E62A0C2B182D382CA32AF328FD273A281A28862684264E2643270029CB2984291B29442A19356832472F8B2DB32E712C9D2A302B452C6C2CBD2A6E2AE129B929A92B942BA92992291A287B293D28262759280E28362810283D29522BF82A17287C26D72579260529B02A532A552999287C2A5128DB265526662598251128D529AF2B8F2D812C2B2B42292C292227F228BF27CA2651257725A628BE2A562B02290A2AC62A1835DB30882E022D0F2B3F2CCB2B9F2B362D892D0D2C1D2AD42ABC2A7D2A592BC92B752A9F2AFA291628F626F62ACA29C62AA42A4D2AB5299E29F827DB2655260928F12A452BF32AA529FC26DA26A62709272F2700265125F4258C289628582A4F2A7E29A328DB25A82470268C2810261C253B25DC29E52AC4297B2BD029382DEE352B31BB2CBD2BD72BFA2A232BB12CCD2BAE2B6C2A9128D8270F299129432A112AA6287827C628B127732747295D290B28B82776291329A02671268424CE256027E329F32A442AE8270C26422711280B27E526E42637258624E726AC26CC2680260F272525CB2478222F2577259224AB240F25E328C72A432AE929FA2A562D23350A32CD2DA92C552A562CB92BED2B7D2DDF2A222A05289627DB279226D925B326CE269D27FE27A02768256E268629AA280727D22786271A251825642398244628E029D82A322935261B267D268927CA27F8277E27FC23A423E62337252C26BA24F8244D2456247D235624E0235225F8243C25E62762297827AC27A6293D2A7E340D319D2C272DEC2AC32A262B2E2C792C592CC32AE0271127AE26EB278227522639277327F225F8258024F4244D2680255A28C2285F270925162452240A24CC268F29BA29EB281127CA2594265A2714298F299529FF25E1230B257225BD2448257B2429254725732442252B245C230E2449269C27F5285929D1274E286D2951341D31212E3B2E7E2CA62C562B132C9B2B502D762A7029562A202AC4288128102A8029D8287C25B225D423732318267926F827992AE728DA25C725492326234224E928B829ED274F2694254C278A261829292CC629EE27CE241427F126FD264A2869264A2636279F265527962563221724A726F126112A8D285B288829832BF333942F412DA12B112DBB2C062B292CB52C172D802B922B322A992AE5284A284E2A002B792A2029FA263424FA23E223E125D52613280829E32842281E26B22481254C2841291328D72560258926D3266B26172BCD2C5C29F626672816284F28B32A682B75291F2AB229BC291D26362469243E260B284629022876289B296C2CC234B52F262C342CA529FC2BCB2ABE2C892D902B782B762B482B192A40295D2B152B7A2B5F2A382DB62835269B22452350247824442565270528D7268A248A22F323D4258A2595241E2557248C236A269B26892B752A2A29AC2755285D29CD2A062BFF298829F92BFE2A6A2B922705251123572656292D2C452A9829B2298E2C36356E30A22B832A502BE9295C2A57298A2C862A122B6B2B272D862AD729A12C792CB92A062B6528EF29C427D221F323B4244025E1243B2464256E26E4247922272321223123F823FD225E22712441257926F329222991281029112AD52985295F2CF22A2C2BE429622AB42A1B271125E4258424D627D12A1B29D028FA28862BF233C930082C162DBD29E12951290A2A672B0C2A892846292B2A7B2BEA29832A9F2BC7284928C4274329F2279624FD23282387237A260B26F0259B259725502316223B236125D42340242F2562239D28ED28852B762AC6288327B5286029542AE72B252C232AC22B902A342CED27792611243026EC27BF2B2B2A1D2A5F29EF28C332BD2E522CBB2A3A2A04272329A829252957290529C429D427F1269C278B2652266E288B282429EF2905276C2501253725D92615299C28E327A5273B25F224E3233C22F622DE2235233325C2266F279F280A2B982B1E2975283F29B82A172BE42C522D8F2AA8296B2A6A2C7A29D02775255426FF272B2A362908282E27C9281433062FAE2A0A2BBA283527E927D329DD2BEA291C27F027B4265C26D7231C25A226C126FD2ADB2B672BC228CB2684246B286529D52A3D2AE72AAD2A60298429622866251D24B6225F244E25ED284B29F6299828C62850279027B0294F29FC290E2B5E2B822A492B1B2CC02C892932272B26FA267928112A612A7729F9282928FF321A2E5A2A0B2AD02A632908292E2AEE29022A4527CC260E27F0257725D726CD28012A9F2BD82A5B2A532865257F264F266E28BA29992AFC29462AB0291B294B2827262522A0213A21B226BB2683286827A726A827F124C325FC27CA29722837293929D128DE28602A352AEE27A327DE2653274D29E42A652AC72916291C2B59355C2F552B1C2D812AEA28FE28042AF22A922A072A6F2A6B2B4E28DF2531286829BD2ABA2BC72BCB282D274024E1248627A029062B912CEB29C529F9282E27302844269622BA223F22A2240C260C27432688262D257B245627332A8F290C291A29C229DE29A027F7284C2A2F283D25C6242D258028BC2A862ADB2ACA2A292CE0340930782A992B292BE6299729DF29F029512BC92B062BDD2A8B29D8272B28A629052BD72BB62BE1283726A32460241F2715282529EF270929F82744275128EF27DF266923D62278232C25D825C125AC2758285425A124C327D2276629692AA628DD29142A1E2ABE29DE29BB28B525C225D4246027FC2AF72B142A8A2A022A4E34A42DF42A462CC72A6529BE282B266E28892A442C082D0B2BDF2BBE29962B362B362C2E2C5A2CC8284B26B02344255C299B274129132801286C256D2661287D27CD26032592229121AD230E256325B5257229D52604251625F5276828FD28B62BCE2AFE2A6D2A3A2AA82B192A3127F724DD244B2528291727FF26BA26872731322E2EF92BAD29632B15291A281B27F9264C2B3A2A202BF02BF02BDF2AB72A832CE62AFD2BCA2B342A1D260B24B8241D289428572872260B26D4261327D926BE276327FE267F237B220E2389247C257425D827D6283B264925072889271629D4281D2B2629072A3D2A592BC128B427EA23DC252C24DE26A329FC27DB262E276730A82DF62AA42907292B279526B725D126A629CA2A842BEC2A0E2B622739288128182AD42C022A332AB3252D238624FB245827EA2692265023EF260A264D2A2A2A002AB72717266523B5249C23A524F2250A2755274C26622454274626BC285D28262AF72A832CF42B1F2D202927284B25FC23BC235C266F285129082853271732932FA42A4C2872271A2712279E247026D128EB299A2BD22ABC28A82896299229512A2D2CB82A4B2A4A250823F2231926BA267028E925162417254D27522730294E29E925AA251F26FE235A257E253F23F927F528A026F222B825AB26CD296229802AFF2A312CAB2A0F2CB52AAA2756246925C7231026C0256D2713255B262B315A2E8629CE282A273A2537252C2681255F286C292A2A042B0F2AFA2733296B287F285729422A03285C242A243C24FE24622512289A26C9247525E527A0279626B626E22590270627C1262F26682474231D2423263226C6238523BB24EF26D327D428292B7D2B0B294128D02645258225D923C923FB246124EA26CE265E27FD32E42DC129312A0929AD2721273124EB25DC282F297F2A492A962ADB28D1276628A32725288C2A402971246A220323CB23FB235627F2256127E3260926A32826271B261D272B29EE27CB282628AD2451240C2423269226E126042682240A249B24FC27C829E22A5C282C271B2634269E25142346230025DB248127E0267A276131162FE02AD629E22B3B2B2729AD276E27D529DC28AA280E2B142A4A2AD028A7272C28A1288A295629E42591239C244A244B23A42675271927E4272F2A632BC12AC429A0283327662876297A292E28C325EF2568267527452789267B259B2354259B263A28D3288C282E2863285B287B298B255D2405253D26C727C5272929E332642D902AD82B012A3C2A7F294927F225FB277F29242AC52B772A5A2A64287728EF26BB283E2B4F29782713245B24A4222C232B26B026F628DD29D6281B296C282B28E6259726E12632270B28FF2738269D250F2554256329A229962633247B246F2673267A28F3284929FD282A2900283E26F625712333268727FA268226B032D62FD32BE8297B290B299E277C2632267327FD274C272729CC297029E728AB243426AB28302A3B2B71292726E2233522AD235626FC28AA28B62911282929A02AC729A52701279724EE263B28CD27EF25FF252A2650264C28E128F1279025CB25BD25FE254F262A279C29F229FC2A782BA326BD244125E4230E253228972743339F2E2B2B842ACF29C028A027B124F8261824B325BD26CB27B428182AA228FA2473255529252A592B6427552482230523AF220326A028C128E9298E281B298F29F1290E28FC25A524A52681282328A026A724A5256F25FB25B52665273727C825E72541255B255725EB281028B829A628F925C025F7247F245326C7253227D130592EED287E29822BD629DE28EA256D2513251D24DE26D42669261E28E5260C2677272727DD2A882CE828902447241E24C32139258C27F3289C287E264D27ED26F427982700282C251326D927CF2841261B25F9252223E4237D264D266E278A263524C2238724E624E6263528BB27D0286827AE23BC235923F822562460259D2E372E802AB6287429E12B4E29EB285826A425F625E1242025F326E3259B24C52652276728C7290D2C4D28E92309242624F0221B24ED27A82A6B2998283C26D724952761277725AE2356276B263B285325B225AC24BC227F23462564231D27FC23C02664260E267424C525AF252127372960279C23E1236D237024642412261F318A2FE5297D28FA275E2AA229C72AD7271826E6243125E623DE24D8239C24C72499257E2600270529C1277224562373242524FE230726BD25E4282C2764272925982452245D25C4246323B327A526EF24A3246224852264249524E6231B231A246C24BC241A275E258A26562522258F252A26C3249324E3238223A5230525FB303C2EB229012A5727B426A827BA298429F228602580244E24EB23C125C22592236B269B26CE276529C228B124BF235622D02284240E240B259329D82913288525782522269924E6239A2350266825C8254B2519254F234E25D4260B25CD2441222525192635279329D5284326C2245B2601259224092475212D24442468267A2F1B2DD2280B28BD26B625E4266129A52A6329B125C824FF2480233A23B5240A263B2680254E266928892863252E2395225723B9229A2227254325362834273E27FC25FF24EA254A246424BD2685266A25FE24C2232325B126C827F2264E24B3258A247F234B26F8295D2BE226AF24B124052532240225F422E623C024CD25512F512D752952268F256B245624A3273B2869281F25E5241422A02252227523E523DD258D25D9263628C2293E25E02370251124BB2376238123602583250F28D528D92689258F26C2237B253226EF2533260B252A25FF261E27412A80281B254C232025562412246E26D4293E295F25F124622498264427AB24B0230925C8260330762D3D2815274C25A0251025BC2472260628B32519257722F422B621CE223523552373259E25A22700275825A8239123B9220722E0215024E324DA24F72577260D27E726A9255A257F276327C726DE25C12337243023FB267D299228B024F723A323CD22DE2484277F29F1261F242C24182728260527652418231822172632317E2C952656254B25A0244D238723CD259224A525FB231D22A2219920592171230F243C243327CF28E928BA2579250923DB21B222E9234A240C25AD2433241126C3251427FE243826DF25F127FC253E25D8230A2308223A2422282A262725F523E1225223452464261828A32769268B2220241E27C3279D26E724EB24FE25EB2ED52C6B272624F724B9244323E5233F24222598265A252222E121D421C0220C24BA24502646272328C5299E260726E321F822592385216D2462247B267426842769263F25FD25F5262826D628F827A4257A26CB240A210D25FF25D5269525B9257F233621DC24E1253E26ED261C24E423F3246327692813250726F9240B27C02FE12CB927A724CB237224FE23362384254224CB235D23D4221021E9208322432493233825B028282771291B28AC25E92256233F2478229523D3236F261C27E1287427F823542510250E26E7281E28A7248A25D9239A23AA22C426602616286B245323DB22452383233327E2244325362500269A2593265B268D257B22C9242E30962D6627C523B3240F241924372361251B251124C323F92347228020B822DF227A241925ED2545259C288D2660250623692236246C22F4204723DF26912679279226BA24CA257124F324A2279D266D2462251425932271230A244B25C8251A24CB2328223A22032203252326DE258525A824DA25F0248D23AC236C253F26DA31602DF326682592221C252725792334242625582461250E249A226721B32297214322EC245824C723E925A124B624EF22DF21FE232423F52141235122582446258C240925C0236C2490253C253925E025192550236D2322215521CD24192557253A238022FA2187212F2434251E26422656234726F82484223A228D243426A62F + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 65289F229522A12011216920A51FF11F5D20DF1F431FB11E8D1E271EC71F311E1B1D631F2E1F2B1D741F251FD920721FEF1F101F38200720751F681F1E21A01F831EBE1E1D1E5E1CD01CA81EF41D691EEB1D7A1F141EC31DDC1E201EA41DDC1E5E2032202420AA1D4A1FD51EB11DB220D51EA0211C21AB20981F21223E23442EBF2BE623B8205A22522286203621CF218921CA1F11214620281FE01F661F191F8D1EB21EE61ED91E491F0A2199215D22E01FA721E01FE520D21F9820E520441F501F8520591F1E20681E68206B1E721E6120B71F791E771E141F3721A720D71F2D20742077212520612156213B20B52092200E221F23A3220723DF22D824F12D642BDD245B231E220221B2203721EB212E20FB2276226E227B20F01F532071201E20D6203A203E1FA91FE621472117211B22632243210421EF205A1D9F1F22218E206321612093202A1FFD1FF71EBC1E2A1FE41FAA1EA61F3A20E51D3420BB205421D0215321502097215921BC215E20E72045210024B9218B228F22A225782F3D2CF0278D24E6214622EA21FC22AC212A2140210C21AD205E213920CA213F1FC220B92099208E228520222180207022FB21D222A12109226D20401FF81D901F2E210321DA21791F551F3720141F621E991FD020842023218920A520EF217222B323E022E22222227E218620332038210A222922C822EA2318226D23F724F92EEF2C34267122C321AE212F220322F6213D22E321AF21D021A5204E20AF21C320841F30217920B7200A217321D7235720D820D023BF20591D2020B51F361F451FCE207E21CA20881E3A1FBE1FBC1E8520A11F7621A51F3720E920B02117212A21CE22E022BC2364212922FA206A2149218B218E210422B120C3219323D723EA2D522B272616232124BB213123CF21BD21F9220B22742201239020F520A1219821AF21E42085206A2231215121CE22EB2298208E2164200E209F1FF11F791FB520B92022217320F01E2721391F8020F21F5F20732040217421CD20E921A5214C2255237123FE2109228A212B224021AC21FB201D221D24C722F8218B24B625BA2E762B73247D233323C0226A2286238E22202202237C22CE21FF216B2186228022D8223A21E62149215D2361233022FC21E11FF71F922055209220F41F83210B214F200B21AE202020A41FBE1FE21F801F00219C229320FE20D5212323FD21CC22AB23BF21C9221E21962131214D21A0211F23AD21C42269211C23C3231B24BB2F442C60260C24ED2497237021ED21DD22B822502391219222552362240022E621D622E52182224723F4225B216722E023FA206820D5200E20FF205521AE2003209F204321492144201420DD2145225C1FEF227821C020F2204F22BA21592413241C24C7224A2201226D2258224F2281236723782350235E222C2429244C267730202BCB26E524AC233A2495222221F32208244E23862229224C22CD21BD22F42003211C2250226122A5223121A71FFB21FD1F5221BF1FC01E171FD120CB1F1220B42077214921C2201720B81F681F7120D021F1209822F3212B268A25F324BE246C242A22BF23DB223024E622B522E724E825A8249624BA230823C1240825E730DC2D5125A9237F23AD23AE228023BA22E8237C227924A723C821E022D822E72185223F21492286219121FF21D11F2821941FF0217021AA1F9B21B31F541F6420862060222621A32144200D22F8218320DD21F8224123DA23112519263425332567277223932162231C246B25F22615275327D227AB26C225A523AE25B8268933462CDF2803268224152532234A23062380230724DF25B32425242022B822A2224B226B21EB2107227D1F1D21EC1F8B21611F1D1F7C1FA31F59201920621F221FDE21BD214122A22240215921BD222E21C521062154220224D3268626A62558262225FA223B22442327257525E225D028B3286227BA263124E9248A254A26D3337E2D6B27D9249C26B823222295222C228E24F624E5248D24382396223F22C3229C21542106214F238B20D621701FC01F50205F20BE208520911F051F5F202C203C2022221422D122F922E322E2237523CF23D021D62261248825FD2418245125BF24E1232E24B82279269A27D627022A3129BE27FC25CD24702318232D270632522FB829C9264E258724CD23482326228324B424EF24022798244B25AA234524FC226A216D2032228120B520B41FDE22EC212322B2211821B521B022502119223121F92388251D2467234E250E28482565233424992220238C2508262A24452667240F24B621A0221525C72663277628E3275C27652801253224892450273B2F752EB2293627AD26E926A023E9231C244E2500266825AC25AB2496236924AF21B422232243214D21DA207820D4202A22C6224923F321C021D1239721EC20EE1F09238C24DE25DA250B2788270F291D275C27DA2580232923A5237325E425A126B426D6220B235F22EE24F0231E2481262B28B029E1286B25EE24F324B3262332FB2E9D29C2282C262127D02495253423BB25A623F325AD26F223A723D4222723B52001232023EC20F91FE51FF920DF226023122295218F23612330222C223222D924B125BF277B2827280A286B2860281F267E258224D2236A25E926B2255D26422669253024C522F224CB24DB25D427DC29F128E9285F24EF23C12412263C3065308E2BA1280B260D269F268F255B2544235D248225BB24D122D623C623DE234221B222902110218E222523022103224621E622B5236B238F23D5233A23EE22B424D6252128C0286228892B222ACF288B27A522D2221524BB26A1276A286128C427DA230224AE22BF24332767269529B9291A2AF8284A274D2468242F27E830272EFF2AE02905296F2678263A269825F223D12365258D24BA238723D02283222E2248219321F7204622E72012225D233823FD24E8244023C6245C259A24E8246A26E729492C5D2BFC29AD2834289E27E1251424B723CD251B2A422ACB29552A62283F24DA232622BE238825C1270727642A622AD629162899252E25E1271A32D82E65290A281E25A6262F289D273527BC25FC2468241B249D2351236021AF214C21D621B9220B21FF202A2242223E2355237A261A278F26DA254624B8259D259B28332AEC2A872A0B2C4F2B6929C5265125A326B9254125E1296329492870275628AC23AA237121C724C524D129972A342B982AA52899269225B1244E26E032CF2C532A4E26682604279427FA287027832512244A258322B222A0229123CF213C22A42416226024EA226921B423B3255D25F428FC28BD2828270C25FC251526A8279A29832B28299C2ACE2981292A25E8253F25B226552714271426652685265F26F2251326DC22A2258925E525C0253E27DC2620267824C725AA24322785315B2F7128532591259925772615262126C123E322A123BD23502378211B2414248323A2200A23442356215921CC215324D62751295628CD26042A2F27C5265224672638282728382828270E26FA2339247525EB26572775270B291F27FA26C0270B280B26FA2410264F288B2521251C27A625502501255123EE23AF258026E830C82F392A4829FA2799266E2643242D241924FE233F25D523CE22B5220C230E248A22AD2495229F21AF2168230223742453273E285329CF2801290429C9277B270B27AC276526B22626243C231A2517236026BB28C229702AFB273928B5261A274827942412252E26A5295B29F4271B29D4256B254A2671229624A025D6260D321B310F2C5D2C422918280827BD25BB242625B324F222A6251825CD252F245C247E24FB2160245A22602246227D23E6233624CE26BA250C27AA293329C6295F27D8263627D22540257524A02341236325B428292AAF296929A52830275125EE25CB25DA25A2244627EA2932299D28C1259F24642632241D251D25CA25F92820338031C72C8B2B402B652AF1285F266E2410259B244626EE265B26B124EC267F246D241823F422A6206020D920702139226B22142418243A26FA27C626BE27E126AF248125972543255D2302267725F4259A2A0F2C442BF328BB2754269C255626C425912581277A27EB2A5D2CD629E22741264F25852527250725EA262E291C354E31322C2E2CC72AC42909265225912517240824D72510279B24F225E6273D25E6240E25A3229922B9219F21FB22CE23EC23B3237D2386260827792526261D2548256A26FB24C7228C246E24EC255028DB2A4F2BD82B53294E283326312525264823E92372253F27112A33290429EF28FC2818276C26B72549268026452A0B354030722A0A29C2282E28062604240A2451261625D6270527EA25EB253A25C025892586241C2486250122D620C3222D2372233B255A26C426AF2582255A2430256E24DC25BA25F824F5230D23A925BD25E527AA28F9281A28C627452549242E2416253F235D245E278C27B227A2269B295E2AB1287F27C225EF26CB27442709324A2E84287326F627CD250E2504255524442589249F251726AE254727CC27B2275628172514255123AF22DB23A721C6239B24D426852889271B2880261D27DC2513263A25F925BC242C2258231624982335273F277C2544245026052413230E249F2259237E2503265E2931282C285A2A692B4428D325BE23CC246F24712772317A2E412821256024DE25AF256725B825FB25872589268C26B2251028792864283D29E7286924D524E12200224322E82498255F27032A2A2992288827EC27EB251D262425C9251C238824C322C5233125FC24DD26B625C124B824B4258523F824CC22F6228B25D6272E2950280229382A9E2AE5286626AB243123B3240F26F230512E6F295C25E52573252126002773287428C92774285C26F0296729902AC12A88297329B02627253523CE222A239B245C26A027D6276C286C270227EC26C727C227CE2620250323F92370222924FA2397258625EA246625B724B825CB232B239F24582385249A281F2A932ABE2AE22AB82A47288B26D5241E253A25452855328B2F98286127A82665268426D4287A29B22B982B57297829D228F02A942A8B2B722B47299628F624D723A02300239B23D6244E281E2832272027E226FC254D2803297328F7262923CD223B232322EC23FF256D25A025122546261824DF24F8249323A123ED231D28042A782B962B222DB22AD3299B278F242525B12713281832AB309E29102504259E25A029632A182B622DDF2BF82D512BB32AE129962A302B8D2B912A91294A29A324E8234E22FA24922312261528EF26F0258D253C26A8275228DD278C267724E22231237423F824EE26F225D6264D24752624260126E724A2240125B4266628792A312C4E2BE42A0B2925279E26E1245825D8271828CB327D2F9A28C525DA25AE284228DF28D92B8B2CA32E482BAD2B4B29E1294A29CF271F292E2AD72ACB27AC23B3218C2312230123532428267A245E25D02564262D27D32706283A26B423A22296219623CD24BE259D270324B1248A269C277E259C24812383269627C026E22A842CE22C282B2528D6245524FF2374265527C62722314A2F752AFD26AC28EA293928E5272E285B2C972EAE2EA52B55285D283A2BBE2CB32B162CE42A4C29B4237F225A2106212022A9248B254E258C261825AE27DB274D2B422A8E281323A022C824F6252625E226F0274F25FF25F92689275B27EB24BF250B27892727298E299D28E328BE282D27D12626242D24F723EB26E1277431942E9B271126AF26AC287B288828E3275928FB29062A84285C280C2814285129052BF129C02AFF26A3237222202161208521A624262638281A28F0272B277E29822A922A5627CC2397228E26372783288829592A61281D26F5257B255C257723F825832719283B28AB29D62878273F2638269F241325A6232425AE2654279931732EB128E62674286C2847296B260A263927C32650262F2723287C290A2A9329A8286429CC276D255224B421EC226A212322C124DA273427F929C02B4B2A842AF92B0E2A99267F23DD257326AC283629002CB92B9D27B926F325462494234F238224E526FD288A29EE28D125FC24A62421268E258F23FD2426288028B828E133062ED22748260629962AE827A927C925E4270426C426B527AC2AEC2A662B072A562908290F2765251C26AB2380227E234323F7243829AD29862AB32B962BAD2B112A222A002619240D275629E52A64295E2B372B5929E4286826202521257E2207255325AC251A269D265E2751267726E124EC24C326D4278B297B2C772B2735BD2C5528E429922A8B2BE12906299E27DD275327E82786290B2B1A2B2F2B0B2B172C232A212790269C230723E423262361228424B22875289129DB2A4F2AC02AC0289F26F5265123FD24A52A2E2C4F295129412C8B2B0F292E28B8269A24F6248324EC248B26B2277E27F3259823B42405257F2590268E27FB297D2CDA2B7936CB2E8B29A52BC82CE02BE5286227DC29E92AF02A612A8F2869287228A42A7C2CCA2C862C6429442821251B23EF21D923B422C123BE2887287F28C629CB288D293029F3281526B42277235B28E0280429542AB22A1D2A0D29222803244C241B249C2740256D27BA278B252E241E242525E524FE24E0264A27AB29032AC52A9634B72FB02A692A462BBB2B372AF92A7F2C372D6D2B4629DE2574256D2A402BE12B6F2DAC2B282A40282D26792155224623A72240239D26AA27D42724273A27B7292B2A0E29F82579224723CE249C27C226FB28AF2744297C279D254F251324EF2373266428CB276B26C726CC2312244B243424F622B0263826C7266627CE2875334430B92ABB2A042C252B1C2AC92A9D2BD92E492A1D28ED26A6275C297B2C712C2D2E5A2CA12AF629FD260825D12436225A246A244C25B2283F277526CC279D2857290029ED265C2268223424A6253C2739283926EB263D26C125CA23AC23C7256F27C9288C29B328A52629252C241B245E239E23BC26B624F126DE267628E4305B2F5F2AB02B6D2B592AAE2A9B2B472C2E2C492A6D29412812272D285A296E2A962A2A2C452B602AEB29182634248824FD22122485255727AE27AD264028E5278A28F326182574236523332576266B27D3287D2863276325B926E3233C23F723FB25C228962A5C2A7829B625E123FF23F8247925BD286C287B289C271228C931562E582B612B222D1D2CA72B4F2CAA2C572D2A2A83264A258227232BD02B3F2AAA2BF12BEB287A2BA1286F26FF24FD222E2389243B245D26EA28AF2551273229A12902285026A524D323AA254A26B2286B2ACF2A6729B926E324BA238C24F125D1253E286C2A5D2B7F2A5A27B72487245624FA252C28E3284C297928932ACA32AF2ED4286C27712A1928E727E428392AFC296E273C260827B127DA28CA2A652A4D2AE62A5A2BD62A0F2A31274C25122544244123E524B825232612264526AA297729F629D02A1F269C240C262928E428C428A2282628DB26B726D524DA247125ED27D727B027AB2855295526DA24C72428258827122B902A2E2CA32C182CE3314B2F3229AD287D27D4298C25D9264E27B025A324C0245B26EE27DF287D2AAF2A6F2A0F2A3B2B5B2A0F297126EF233424A8234C236324BA26D525FA24852645285829132ACE2BBE299C273E2513269626B9271626C8268823CC2466251027A52605277927DD2665270F280A26D92473258A26BA29982C1C2DCE2BD9299A2C0F34CF2EBB29E6288028C127D0279E255A265E25F5245125F4263F28AF2AD329482A37299E2A7E2AE9294727F4256F25A424E321B9258725792338262124FD2482286C29DE2AEC2AE2284B27AC255326E6245C25F324F723A9233825DE250028852905282A26F2266427F0267525A5245C262228F429502C3F2A79285E29862C693547304F2CA02B022B272BC12A7B27F4253827A8240C25A02660277228A62BA22B062A9C2BC329FD29F82AD228C726D924D72210220823CF2440259125D8265B261D26C4279A283B2712287C273725102598239C25EB23ED24322310263227E02734288F286429B42971297824D724BB278C287D29BF2A562A542A2C2AE72B43344430852C312CFD2CDF2C802B902ACC2A7B28172761257D269328662AA52B902A582A5D2B392BAC2BCF28D627E7269224972434232923D3240027662507281227BD26B82630279F26DA264125ED25BB23F222172425236424562696244727CC270528AA28AC291828AE283F2634258C287C29FF29422B5929C0294F2C102C8634902F262D5A2CC02B122D1D2DFD2C502B152AA826CF25052649279D2870297529A029162B5327112767260825E724AF245323FC228B230C273029FC29B82B152BE5287125AB255726352690262A27232390224122D1240F266A25D727832741276A265A294E2815279A269E24BD245229FF29F42712280F271E28BC288E2AC23427303A2B532BF12AB229302CD82A632BF229B9273525CA2690277C287827E9276628B62661263826C424BB2384252C240822802232248326E4287D2B602BE5297E28CC252B278A26EB26412756253523CF2278248B24A92533272C27C5276A28F028CC2A5D290526AF2409248B25D026A326DB26B126302692269627E6282233F931CB2C172A20290529A32A832B7A29E427F425C726F8258F262927F92791262D292B26A3269025FC2335252A2490253F248822462222251029E728CC29E228DB27CD26AF292A2870277D259F24E5223A233026B42542277A285428A1275F29612A1028CA261A2467247324822420277927332668258425232874299A2B13335431242D582A072A6D2A74290C28812852289E25E92446269B26EC279128222AFE28022917294F293D278824DD237D228F23482492246326472761273D28A02723281B281A2858272A2537248B23CF2374233027AD288827FC281B2975279A266725C4275924F0230323A1237B2460269E270228852678259428F528692A25349031962CEC2B312A7C2AB32A8A2CBD2AB7283E25EF2569278129E629922A6E298F2BA42E782B852B8326182655248F241721F2209E239424F125DE27B9276726DF254B278E262D25DA2388222D234B2422273C27EA29CC2B6B2BF32AA6271C273926F026D42342225623BB235825C9276C2754266E268C26622A8C28FC29E431D730B92DCD2C272CC12AD02BAA2B5929BD2839269A26BC26B228FB2AAA2B052A1429922942291D2BED2667255F26AB258E21EB2287222B243A27F924DB25EB25992792299D27B424EA22992309240B25D726D32876294629912835278325AF24F024B424D423E0207D248D23342400264E269F25AB24DB23C42557274927D830BE30FB2C5C2F9C2CA42CA02B462A442782272327A526F527022BA62BE82C942A7A28C428AE26BC284A2867273F28B3253224C7200323CC235A267725D826C82654286E2BC42A30282E26A024FD251B272F27E2278C269027DE28ED265725A6256A258B25D62324230022EA226125AD255624CA23E0252024722507255326B5327E330B2D282DCD2BA02B532A2429A529DB26922473265E26BB263D283F29AB2A1D271A27C3271128AF25D927492982285724E922BB210423D1231825A2278128F12B7B2CD52A1C29D5269F26CD256726FC271F27CC25642759263F27F4258526F827D7250424DD23F524AB236025FA2729256B2363250424D025D2253B260C309F32F02E622C672D9F2D732C2F2BA22A7D27DF26D42738295D264828D62698260725DF2468266B276F290E28CE294129CD24F823FC25022327252E27AB289329B82B222B1F2A202BD129AC2837273026D227E527F92547261C27A929DC287028252A3A29EC279326562621268826EF27BD2606268B281927952789275B260A2FAA32412E3B2E082DD02A5C2D632B822BDE2AA9282D29CF2ABC2A7527FE25CA235A25B5247B25F52874288128BD2780283D25A6242323ED2427254128C12AA42A5A2AA62B4C298C2B8A2AF5281825F124C62577253E235F2488266B29712A8529B329C429412A0F26D127F327A12799298028EE26772AFB29C628912881288130FF30A42DA82DB12D6A2B352CC22B9A2C5129D127112B0C29432AF028E0276825AF2568258326D6289E2A3A275F290C29C72878260F24F4225A24C526572A152B572A9B2BDB2A9B2B9A2A7F279D2798243F248925F723D524EF27C329B62AE9299B2939290529142708277B28982B382BA52738259A29DE293D2BBA284828A931E731AD2BFB2C6B2C092D5F2A4D2CB629A229292B6E2BAA2985296228F3279D25062643265127A22A8F2A85296028A5297629AF262025AE2391239C2400280D299F2AA72B702BA82A9C29C42626266123D924E624AD25E624F2283129D629B52A2A2A322A9629AC2573278E28A52AE72AAB281629B62A502BB82CBF28C428AF3252306C2C472D2C2D4D2C092C4D2AD229EA29662A592B8429D1291227E327AA268A26262763296A2C5C2BE029582A8C2AA32954276126952301251C25B128592AF62A3D2C212DA32A282BAD2A87270524752461244E25AD27482820295B2A312ADC290E2914287525F8274E29152C712C3D295F2A9E287227A62810276D294F36CA2F0B2C6A2ECA2DD62CB52BB7286927D827C6275C28AD26142748277E28D426D4278A297E2B212D2B2B1F2AA4280629212B0E28B7237E217D23EE2586271D2B162BEA2CF32B332CFC2B1D2CE5298D26F9248723AA275D28EB28A528A52753297429F726E426E324CF288229D728AE2A642A7B28C82546252B25D7266A29B9340230FF2BEA2CBF2CEE2B232A4026332609260C26A727EE26C927C4265A27D02AB42B212C952CC72C662BA72BEC28FD2743265524DF232723CC21EE25D12526276728042AD02AC52B3F2C642B2829AF254A248F257026A8298228D1263F28A128592930273825BF2575297F2A4B2A61292C2973287926DF22542479263629B933622EB12B8D2ABD2B152A2C281826B1253E277F260028D7264A29F42A122C312A3C2B042B802BF32B2F2A452B7B294E27CD24C42470222222352295245726D127DA28BE28342A552C2C2B052BA62BEF26FC24F5249B27AC29EF2A762726267A27232A0A2A2329CE26AD29822AB5292B2A1F29F22881287326E925142576281F32FD2EB52ABA2B32299F29AE283D2675245F26BE28812AA32A4A2A642CA12AB82B79297F29D528372A15287C2902290F29A726352699224D210C238C254827152973289027602AE828AF29982A722A70260726D725CE25A026C8292F262E255E27AB29B829AF27BA2786287A29522936293C2972273C2BF7254C257228BC298333E531322950280A279528F2291D2704267028112A7A2DCF2C1F2B742A4D2AC626F9272A293B2A8D29582B882A5129762B6B2AB1265A25F4225723B8246028F329462BC72A8E280F28BA2759284829312669252E277E271A27212759267724762619277928C925F7256528C426ED264F2AF128CF27A7282E26B8258929932997354F32652C6A2772265527D32565261E264229452C412C062CD629DC28F1277B2789265628492ADB2CDE2A532CD72B712A1229D128EC255724D724CE25D1273B2A132CD52A4A2867257D26C827C9257425B7261C26CC284C286C28DB2479246625D6276C254E25A827B6270126AA25CC280529F929A027FA25A8253C290D2CCF359231532D992A3D2A3B26DB25D626CB26B4281128E62C4C2A772B9A298E29FA268627E727A82ABB2AAB29772ADB29B32B8029EC277C268B239224BC2550260C29112B5B2BED2771256C27CA25E3234A227B23572578274528C12771268526DC25CC280F2857278326F025F8250D26EE295C291729F1274F2792276628152B41352E2F9B2C042BB52995295F26CB23E7268126AD28C3290729692BF12A052CF029682809285827C028DA276928152BB12B2B29C427D525F92424246424EF264F29682A282A2C2A4F2872259D25DC23E620C7201825A9236726FA28942807287A28F7277428E328BE26C42895269028682BE52B0F2B93296E282D28C22BF22C0D35172F942BE02B212B86280026D423D8266A270D29772BCD2C642A402B282B0D2B562AAF2806260528CD272227602A9629372A1E2BD72909251F2406265B2793275D29E7283327FA257D2637261625E9238A238E249C25CF26B7261726C02659252327A929F228532744263B25462AD52BF92B792C9D2C7C292529A92B3E2D7F33FF2F4B2BE82AAE2811287328B12600278229132B3A2CF62AB52B6D2B702B552C652A5F299B268B2850297F28E427D0296A2962290728AC258F2509263128D827BB294E28E5241E26DD26A3251E2640237B24F626DD2692290729AA25462656263927BC27F82596269524062751274F2A782C9D2C492BE92ACA2B422B762CA934E92FCE2B102ABB28AE290F2B752916273329262A232C572CFC2A1B2BFA2CA62A8F2AD72A7427F9260D291C28FA276D27E327AB2726275924032538265D28A32ABF2AB828BA26FC238B264B2881276724AA253A267427A627762A7B280426C927EF279527B3279E25942570246C26602A562B262D1F2C8B2B882A6F2BCE2C30340230432BEA2A5C2B3D2B202CD22A882A9D2A852A69292D2BF327EA270E2BAB2BA32CCA2996289028D329A52A842939289C261026512528232E2516250F2703287F296D2795268324E4262D28C028B426D526FA25EB275228A42A40280027E3271F2816289F2747258E256824D426DA279028592A6E2A232B542A7A2ACA2B423560314B2A2A297D2AF42B0D2DC22AA52ABC2ABF290D289D28EC254626A927542AD42A682AA8299D28522AB12A8E2A6A29EE269824B0231424B32333246B232F26C129E628982945268E26322805287226382520260925D127292740286F25B02530289C263E283B26382468245E251B273928FD276E282C29482A4D287229A934512F0329F926A927122AAF2B242DD32B5D2A8E294B2A0F2911274F245926E429D3296529802A062B65298A28E0268C27E625162675246D23E323A5224924D92868296C2AD029F5262427CA25AB254825DC26C825B32790272828C625ED253526BE26962699259825A425B7263428472A5C28E026A32776270929FB27D328AA33C72FF12A25270628E827A328BF2B0B2BA02A8B2ABE2AE82A6128F8278A27B729BB2BFD2BBD2A532B2529972BC9287428D4268C28EF25DB248E246924F2232E27F12864282228EC244F245D251225DB24FC25ED28152AA4299D292429C4282F29EA29F227692640265F285D28FC29D72A9D291B2842270C26C228BF28C5297135FA30102C592A792960299F29D02A6229D32ACE2A252DE02B3F2B2D2BA728BF2B8A2DC92B672B9E2CB12A7829CE2A4E2B8E2962294027BD25ED244122612559278129BB29982941265F24C424DF23342420279C29112AE027E6284A292929322B7F2A652A4E276725412A262A072C5C294326F426CA2764279929172BB82BCB340E30FB2B682AC928CA2AA82BEA29482A6E2BEE2B532D562B682B1E2A6B2A402B852CC62C222C372DE9294A293D285B295B28452A5C2AEF27B8267A24B5255027E128DF29732967281E255D244925C7240F26702A96293028E727CF284D2A5B2A642B4029D7274026A029D92AF828D7274124B725E32796269A28D32A272D1B35FF2FDC2C292A4C29402A082C562C562CC32BCF2B3C2C352B502902285B26DD283129B32B3D2C222BD42A282AED289727AD29C92A602BED29FF27EC260A25E926282A7A29902BBE29DD275B2515259824BD27E829BC29512AEB28C42636290F2B7C2B19290D272A281D296629D22716289925802503261228CB2742293B2C6C34BB31FE2B932AC9281028812AC62B652A9E2BD72AE42BD32943285427C7268127D128072A0F2D192CEB2A35298C27A727D728AB2B672C99290729AA27AE29F028C3285329712A9229F528BD272925D8229C26FC273529542AF529E1295D287328342715288C2847254A281629242790279725C727F1274B26CD261F293A2A8434DE30CD2C512A2729DE277B29AF2B2D2A63286F297528FC27BA27FE26C12599259526FB27082BC42C7B2BE92AEC2824299D2A1E2D8C2A79275328A3285D2981279526F227AB29F6296329DB294225D524CE2588279726D7282E2B2C2B1B2A0E297C2805284D24C72599267328792761276C269829712BD62AC32A042A752AFF337332282DB82C13296B29A22BD72ADC2B322AF528B0287129C52AE5290727F0278524EC28842C332DA02BA628F7289F29D629182B0D296728712886289B2A372ADE290429EF276728EC28D827CE25AD24CE247F26D0268729DF2BBE2CCE2B112AAE28DF2760269926BE28D127F8275428A3271529FA2A882B0E2BA02AE32B3536C8320D2EA32AB529F029E229932A862A9B28E528A929552B962A552BA428E3295628F828562B4A2CE0291D28E126122A86281A2AB229F727B82736289B269A28D729F32A092A2A293F28C0278727FF24DC231D2589254A285D2A7D29DA29B1290428ED27752644286829602793271226ED25002A082CD22889284927872B6F35A033E72E2E2B40299728D728612828291C2AEB29CC2A392D9B2DCE2B142C5B29D2271F29D62B202D1E2BC8289E270A277129432ADC2838287328C225E5246728BE2AF52A512955272B261826C326462403256C2432259B2505298C291629CA2A2F2A9328B327B129092B1E2AAD28BC27A528C329C02C862AB529DD28B22A2C362A33182F212D9229022833280427AD289929EB2A6E2B822CF72C6B2CE62CC4298F29152A6A2C2B2C452BA4277D2529263B252727FE27452614285E25AE26B526182A942B722C4329C62699256727C32598240D2521240223792AC729612B122B102A7D2A76291F29B92AED29CF288F273528392B222B9729A52744273429263330324C2E532A4D292928A02790281129FD2BDF2C9F2BDE2AA22A152BF32AE82988273C289728B42A972AD7268125BB254D271028BB28B5267C273A26A127D0287D2A5B2CE42C852A8A286A2622291F27C024A124E121AA2453264A290F2B432B5E2A872A152943296E293C28FB261C27E7276E2A4A2A7B27D92668274828CE315B33D22B852A01291626D825C126922A5F2B622DA42B59292D2828293F2A0D296F2729260628E4298129B1263027DB27662AC929A529DC265C276D254F267428212A5F2CEF29042A78285D28D9282F29F7287D2408249E223D2696276B28EA2AAA2AAD2A5A284728A328E2252F261D2676289929B128762605286B263728ED3228315E2C1A29F4257F2421259C26F829272C492B0F2B1326EE2753289D28442A12293727EC26292A3128A826992876297C2B922A9A2881271C26E1258528F729132AF429AE28412580267E262028AA27DC263D25EA23BE2178260C268B2AE32C572B9F2A2128C62681280A273025F9247D255127362B7E28B9260D26A92749309332082D552A8B275426F9265D27E42C2E2D1A2D8B2B8C29C428B0296D2AA42CBC2ACC276E2717282E28E728122A6A2BD22A872B4E288A279D267927A629AE2BE72B762A6D29A825D9231D28252A5327A82653276A258224E324852407278429A42C882AAF29A72AA72AAD284B26CF2639259825412822291329082A342ABC3289302A2D8C295629A8275B27E527A12A792CCA2C9E2DA62B7B2BE8293C2CCB2A8F2A2B28CA27FF2694273228442B722C732B302BC525ED24BB241F26CA29952B4D2A782A962866272028342981294E285427EB25FF22F423D4241525112782287B2B31296529C929E629B929C726B72431268726CB270728B029332A60290F342A33B52DBE2B0D2B762A7F28E028582A0B2E332DE02C942CE62B342BAD2AD62A80285029A3279927B6278827612C5E2BC82A922AD827A32530255227FA2745295D2B272C5B2A58298529602CBB2D6B283E283826E3256227DF271C25A326FD264D2A382A6B2B772ADA2CE92AE427AF258A260E28A529122ABB298A296B2BB334F4324A2E2C2B182AA228DB273E29D92BFC2D352DFA2A5D29E8289928B0278D271A28A427AA27D3264826B426DC29D82A862A322B1626BC247F25E12638279928322A8A2A512A73280E283329CF2ABE295B280C27D928E3260528C7272025F6261C282429912B4A29A82A192BF62729279B264029752B0D2A6F2A9F29CB29FA3454338A2E852AAA28C42768273528C42B792E182CDF2BD629B227A4268728D327E726AC2AD52A732811278927ED29BD2B59297F29F72759253328A827E128C72AA12B4C2B792A2F26EC26232706271A261B2819299F29C929182B5628FF27CB26A925522705290D2BB82C312838275127CB28F629F82B602C8D2B5728FA29A5354A33F42C8E2A5E28A627C2274229B02B612B802BD5287128FA2597278C293A294B2AA12C142A3B2A22273325DF250728D02771261D253A261A27162A522B5529F72A492A3F288025492715267A25742681277E2BE22988285F28D3273329F026242574246325B627302A8C284727ED277C296A2B902CA12B882A3128E22717336B32652DB22C012994274A255727022A782AB4290A2AAD28692775283D291E2B302C492DB42BCD2B54293327CE27602A4D285127E524AE2574282E28C529772AB028242730296A2A6D2AC7273425FA269C26EA2BDC29F926C8276F275F26CA23AD22DD23C527F42616294328B627CF26E1271C2AE02BAF2B942C5C2BAE2BC034E6327D2D382BD12A0528D425032707287629E028992788282D2845293C2BA52C9B2C7B2C842DB42BBF2AC228E929AE2A912A5A2847264E26C628E129C92AD72A2928AB277F290428B3298F27EB250F254E261F296728B227032785272E288C242D232F2418267E27C327C0271427CF26F125A228872A6D2AEA2A612AEF29FE31C9319D2B7A2A0A29E8271C25D2276727A229842BC62A002A1C280D28BD2B882CBD2BD42CCF2B4D2C9A29252AED2C5A2C8F2A752991275228DA280B29CA2A9E2AAD286526EC2794277D28D225DF257D245A253C276A26292665274C254327A2250125A123BE24942421299E28DF27C528CA267D260E28A92823271928C4286E2FB2302C2D872B8B2A21290626D9288028482CBF2C30294D29FA299D29722AC12C122C692D982CCF2B832A282ACC2BA82D7F2B842A6B292E292D29572BCF2A402A87289A2714284E2744297F279F243F2332271D28B0263126A9257625BE278C26C725152453236526FC2766289C28782756261927D627AD2678269B252D2610306330982CB32A502819278A257D26E328B8298F2A1C2B3B2A14291A28F3294B2CE12BD82A482D5F2A892A3C29362B782B4829082A33265126AF286D2A25291929C727DE27D126312445262724B226AC23F525AC28912711257426802725279D26C82361243625A9234328ED28E42912284227AB23F6252F2706277A276526C430E631F72DF32A7E2A7B281B267827562B182D362B772C652C3429DB289A2A7D2BE529B82B832B302B4A287326AF2722296E2A622B7128F2260129FB29772AA0294D2708269825EA241B2692275C287A27AC286A2ACD294A2A97286026BF281728C0286624432431243C2604284D2AB0293D25EF25E12564267628DF28C929F2310132C02E152C082C452AF429FF28FB2CA42DBA2BD72A9B2B122B862AB52A0D2B6D29592AC92AA02B5B284926A929FD2AB72C012B22299E27012665279B27CD27382583250C260927AD289F29792B432C6C2BC02B3E2BA02A76299028AB28CF28FD296E27422487252B26D5275529F2294A28492679266C273029D82A672BC4328E35142EDA2B8F2BF12A0628EE29432BAE2B1E2E302B032BFE297D29632B242AF628D72A7929AE29F5278B269B27962A782B132B2129702411256325AE25E526FE240523B82272256327B429982BE92A122BB92B4E2BF1298E2A5E2859293B2BB52ABD27FF24492328264B27C528902A6D2833279326B926AF2AA12B372BBB343D335A2F142CC02B262891281129C22B642D412C6D2C172AD729982B842B832C292BD42C912B7F2A2F286328ED29FF29D22A992BF7272D255A27EC2435277024E02413231223AD25B824CF261E292C29E228FE28F428CB29D6290B2974290F2CFD2C102AFA262D25B9251A275D297B2BAB28FB274026AD27812A7E2CCB2D5B3565305F2B572B4B289C277C287D28082B7A2BD52B4C2B222BA32AF52AC52C8B2DB92BDF2C002C252B792A422A682A682A822CF328D726FF25762629282D288B26FC24EF236624A42397235427D02644265F27512977284D2A302A452AA22C3C2D452BC82980289926BE249A2598267C2A8D2A7927B726F7272D28DB2BC32D4434A92F4D2B3B2A752BE52A4A2ACA293C2DE82C332D442B7E2BAB2BF92AF12CD82C6E2B022BDE2B0A2BC228FF29382A3E2B04297E297527C1264F29072B472ADE2ACD2750232625AC25CE2671285429D5272D29662AA728A52751298429C22A1D2BA62CF92A7C285426FC2477259D2662298A2B16291528B627FC29842A392A07347531E22B6C2A712B7A281B2AAC2AC62ACF2BA22CF52B6E2A612AE6281D2AE2295B29112AFE2AC42A612BDC29152C952AB0293E2A692725299B2AD22BC729D227E226EA2534254E27D228772A6E29EF27AB299E280C299D28D52A532C4F2A052AF22B152B562B5E281127E82545262B2AF42A8C29F9270627EB2AED2B812AD833BD32D92E4F2C3F281B27E425E529EE2A4A2A102BA72A4D295E26FA277B28F72872289D283F2A5C2CF72B972C322CDE2BEC2BA32AAC28E329EA2B8A2A1A2A0F28402777261A2605276729EA2B552B6229502ABC2A9629AC298D2AC62975290128572ABA2AE528232846283E2728247427C3271D29D22719279B2A482B352C0C352B327A2E162DC62A5128D726652859295C2C4F2D812B722BEC29FE27C6268D279E27CA2A312CE32B762B732BC62F232DD029AB28EE2702274E2ACB2BEA297F291127CC24002640269528A429142B7529072B202A9F2AAB2B312A4628A32830290F2AB92A032B3E28D927B42552259127BF2776290E290428EC28462AC22BC832E032EF2E6D2D032C8329862779287329F92D642EA12CC42CD6297029A528A728E529752A852C7F2BB72D622CB82B482B9E2BE52A6928D729D12A202B492A58296926A725E82595259229A02AF9291E2A6F2B6B2C642AFA296028C5268026AE29392A92295329D328B6278725C2245326BA27E5275A2754276F289D283429973261317B2DDB2B8A2C0B2A0B2841275829832B6F2BBE2B892A5B2860284828A327A429292A5B2DD82BDE2AB82ACB2A862BA72AD829862A1D2A5F28CB2860288D265B25BF24E32366257B27C42A67294C29BC2A762B672AA6296F28EA26DA28FB287C275B26B4278E269F276325222551246824F4266E276E27092759298029B73222303B2C5D2A3629E12A352AE42817290D2B822C292B792AEE273826F0279529B629A52B532C892BF22A8F2CB629732B6A2B8C2AEE295D2AD8296F297F28C226A22656248E24AE25F226A128DE29E029DB2B612C862B532A8D29B9260B289E285D284B28C726B225AE26B827DB2409249623CD26EF253326E4291F2A8F2B6933AF30642A8F289828BC29072A80298727EF284C2BB82A7729422851278729432B282AFF29F22BB62BA12B702BB3285E26F826C228E829B229302A6A28A2274E2580244723AC23832438271B2A9A2B8229702AD22C562C2C2B122B1128CD27AF28232A38290229DB26C4278726AA241A24E52236252A2630276929DA2B512C3433A331B82A092918286E2B622A57280128D527072BEB2B822B98295828B528882A962B652CE72C182DC42AB229962A8C27B52501273C28ED271F2947285B276B27242564234323C6230B277928D32828281D29822CF42A252BCC295029AE29DC28A929DC29A62AB529122A5A28822795259B23AA23B32450241D28312BBD2A8D338131BA290629802739293F2A99280D275628262A582BC02A5129B42A822B0B2BF72BEF2C522B712CD22AA82A9E29E626E8271128C12764275B28C229952997285A265D230623E424F22638283C27B927DE28AA2B402B7C2A6C2BE829B2284029A529692B312C3B2BE82B4E2A6F284A26EE2428239A231B252A263E28E52959347431292B9429B0288D2A6C2A422920275C28FC27092A342B0C29A728282A7E2C9A2B3A2B212AED2BF32AF32A8B2B7C29022A6E2BB628FF26A1282E283528A3263B261F243B2208248326BB281026B92667296729812AF0283A2ADA297729B52A6729322A762B442A422BC92A6527C1261026AC22A3248523A1255D27F2285032C52F7B2A06275028F329062AC5297B282F27E5268C285A27AC266026E027A12A802A272A022A752A1D2A842AA929AA2A652A262A7E298428C2289429D6291429C327F4245C23BB2394262029C5278224F127E829542985273328B926D7265728CF28B9274D284C270E2850268E2690266E251D2455245024E72581266028FB319530DF299C2814260E2A192B3E2CCF2A6F27922617279F269525DA253F295E2A7A2A87297E2C272A882A10288F28B4295F2AD929462A3C29742833281228B528FF27352467225E247726D326B3288D269829252941293D289B27C3263C26CF25192589252A28DB263A281A2700285426C9267F269F2786276E2854281F2B813437313D29E82612291D2AB82B7A2A1A2C5F285827D826B4276B252726172AAA2BE9297E2ABA28562B582A7726A5286F2AA42B362A14280727F32764289427E32751253A23F4224F227B239A26D0260F261A282D28D1282029F62777250724B025DB24FB251426A1269528F5273629C82A03267B25B32660269C29812B412EE73518318A29022A4129C82B1C2CF42B052CF5282E25AF25C92563279E27E929FB2BF428C228B328382B5F2B6E286E270427782796298728EC26042779270A26BC24F524A625ED21A1229824E823EF28EE27482976294229AF2756269025BF2436256F2567241427D026422A8029852AB927B62612258727B727B32A112D3B2D4136A42FF62AE128F029142A572D932B8C2996288C268F2639256E269629932A212A982AE029412A402B532AEF295729DA283D29CC2906286D27E427D6253F252624AB227122C721D7210B24AD26B32716286929032B9E2A40298A283128DC27A9283828A7251C253627D72A122B1F2A2828E12511256226B3271D2A2D2C852DDA362830F02912290B289129EF2A302B272BCA28132586252725D026BA269529BC2A20294D2A4E2AD72ADE2AA92B71294B2B772976284B26FF2752293B29022873252A2490233522B52289234228D2299C2A2928FB284529C829E52AB8297D2A372A092961272828EC298B2CBA2AD72941280726232455259127E12A592CBC2A0B354B2FC529D727D6277029C82A852A0329DA28B125CA24B424262538270E2AED2CD12BCA297F284E28E2280A2ACE2B32296227C925772529268C284329AD271426262584211821032025250F261B290A291D286F28B126E227582A9A2C1B2BAE2BE529262940299E2A082B372A152B0029E1254324C8240E27502A382B832C0835F72F0C2B8D2AA6272F279A29802A8B2AA329BE272D26EA26152586253F29102C272BDB29A329D0270F28C5277D29342B55297827BC270D26EC27DE28F326D1261925E0212922DF216B24F6252E275026B126C8259925BF28C92BB92BFF2BEB2B592B652B4929262B9B2CBB2BBC290E287324FC23D9248126172A452C962C4734D02F5229D629F828FE277A292F2B022A3D292E28C82554264525A4244F268F286029B929FE290328FA261C27C8277129C2276825CC23F6252426FE26AD278726FC248021E42198225F246225D6254F262426162481248A27A527D628B6294C285A29E329212A2C291B2A002B70292728032456236F2541271929852B4E2AEC33162D2F29032A142AE829C62A3629312AA429DD281129A8265A27C92578278D27B429862A7F2A1628DB26CD25A127932AA8263626C3243425EB237B250D278D255E24A22247214221B6239225B625E524A626F1236224A524B22635268C27DA29DF287F281928AE278429DF2AD7292628D0247F22BA237922C0266A29EF29A633122DBF290428EC2A4F2B4C2B242B66299D2A99270F27AF279227C6257325EC27D22668281228E5271825C924D525A628AD273226A0231B2379242824B9234624452311231F216121A223FC25A72607253325832510245F248B268E251B27D9264729202663261526E4274A273529FA2545264122C1229D254126C9286C29A331652CF828242716299A2ACC2A682988287828C8277327AC26CA266E2238236A24E326CD28AF2518274B24A123212570253B2729250E2463219F24E92298264E256A243722F1218F212A26C426B827202767250C246F249723BD261F258B273E27CE287F27C4274B27F128F9268728D52680247E22F92295248F27852806299833E92DF827B1252228842ADE2B4E28792711280728B7280B27C324FC24EA2436255427392921281128A024AE2381243527F6268E27712428221023F1245225712737262721D2209A229A2342287E2928279B284D27F7245222D3259A26BC28D827A128B2273A27A325D627372866277525CA252C232124AA222E253325CA272232432C832659256926A1277228F028292732280D2843274B273726E5238425212520279628AE29DC28D22509267926A526BD26D927C1254323FC2398264C27FD261C26F322B32235226924B12607286627B42644263725A623AA240726EA27B227D0276128C226D1249025BB2648270228D0242224EF239022A324382628290135EA2A0E257D2480258A2652289425A6262C285C279827CE252F2502242424752552260228C22AD32AEA26B9242725D725982568278E24212547242A23DF26BE2607256F23A42344217B24CB25ED24282625261826F123CE24872571259D2595255B27D92667251124C525A4267728D327B324A5231224452289245125CF280033A02C112668233E266F2769278427472784284E266F268A272A256D2599241325C326F9273829802A0529C92660273927A5254C27A0259B2397233625B8263E271E260024D321C122AE24D725E326F226542711266424732311240A251F243526C22699257E24A5240D265E283F29492BDD26C924EA2356233D24D425E7290D354B2B7626B4253F24CE266727232656255F26502716267C26D5233624C3233126DD24682688289E275E287626F0263125BA2518270F241D24BD236922B42309249824DA21FD2107229C220124A525F7258D262F259822F6239723AA223923F7232E26842483242E25E525D3259927E9261226352500223023B7238E24EC260F346D2D74275C248A24B2262027432740266326B526BC244624C222F2228624CF21CF230F258525FB26E127D826F825B224F825A126C725B722982353221B244F25CD24D422A8221B207E225B23B82343245A26C126FA240224B9229622E322DC235F248524FE23B723A2250225EC26F0287725EC2367237D214E228426092802352A2CA626FC239A24C3256B277E25AE27AE230E25C1242324D822D723D023582291229F24C2235D25FA232124DD241D25802495252325D6226624A5238A24892492242F23F921B720D52109237D2336247724C726FB25E5234C226422CC22F922F82364236B23C6221025D82288250D2510243F241B2342226E24A024EF278D32CC2B29242E235B256E247A25D6241C25F02485239A2518245C22682328231D234324752243245E252024E922692479253E23372453233F220E228F216F233122652329237723CB206221A622BD23C1223C249826F8230024DE243723BE237823F821482165223C237D24E22479236925412571220A224C21782170236E25882F622CD0262723D723822561237B24172494240825BF2369235824A722EB215E247524992424240425F122C6219F239E245F23EA2254234F231D2196226B224D212C242323C221DF1F8923FD219F23FF2130248D245923D723CA24FB218D244E21DA23B7233D230822CB239423722427266424AA21F6216E210323BC2341268C31352E98266423632369250923B82456234324DC233124CE22E022AE214A22882290227C2328227A23E52274220C2388247E2404235622561F7F21EE20D4222E2231226F21FE21B721212005247F2277219E2230238F2118240D24FA22B32165226322D0211B23E320D423D723CA23BB234A243B2322234122A822A623382580312C2C2A268724F52114227222D52375233C2571230F23AC22E6216B237323C520312361221F23E0232E233D22C3223D2286225B237220651F2822B821B821EB20B721ED22C220E41F2E1FD421422098215122872230207521CC239C22E522322083227D222E22FA228E22AE22F222E8248623DD225F22B01FDE22B2235D260B2FBA2B4426C323D2220D2256235924542482240B239E2341245F22FD21A02298234923C221022201237123272238229922662309221920F820B91F2222E620CF219821322123220520A61F48213F21C220F72131213D2193212D221623E021DB233222AC20D42156231A24C421CC229A23722331224B237A215A239324DE25252F892B9126F6217921B62009213A23A6227223F921F0225F200A210B21A521212110221121202236221523ED1F322122243C237F221521B51FBE2024204A22CA22DA202D2083215A1ED01F9D20762001214321C221B6229D20FA229B21392166206E222021C51F43205E22EB22EB211E237A21AF22AB231A2219221D242126592F0E2CC725B323BD21B9226322A5212F226123DB2183220E21EE217F20FE20D02009208721FA204B21AA1FA41FC3200D22D021B8207D1F6D20DD209B2070219A21C321DA203F20491FD22044213E21AD2066205221C21F89210A226E212020F62032216F202B21C921902267203921EB21BE232E21D921D620732168213B25D72F5A2B6D244E22842202228F20F3203023D820E0217E213C21F120771FEE1F5121D520FA1F0B221F223C21671FCB215C21F5207E21E821E320C320AF1F9E1FBA218521F1215A1F831FF41E2F2101204F207620A120AF1F4320F221D01F21212F21B020EF20CA20F7205821FA212D2363201E21EA21F6215F2292224E246A251B2EB42B1125912108224D221C21FA2102223B226123F322A120C12086203B21D7217C2113226621FE20E621831FD321411F9E21D921631F51219A1F3D20F51FF821FB2045207420C1200A1FA02150219B20D4224522F51E6D21BE2040215D2170222521EA1EF1212721A91F0C217720632159213722B722A5201C23FB220A26F32EAB2BA3257622912166227122A721DE2341228E2142211F21EF1FE11F6A215F22002165218C23D22002221D217B216620E921D622AE20DE200C20D220C31F4321A5206B1E7820511F121F39219321EB1F24225F211C21B71F2422C42080233221FE20F0208920B11FFA21EC1EB1203822C9222C21892123221C238A21EE23682F012CA6257621C122F5211C22B7219B233723CA21F3215C225C21961F9C219C21AE2218223422F11FCF21A2206C2161206A202522A2200F1FFA1FE9212E201720061FD41DEF1F111F641E5C201420821FBD213A22FB1F8420F81F8D1FD71F6C204F212720E21FBF1E9B203A20C5202922F8219522E1200C207921E323CF24AE30B32CCD258423E320DA230C24A222F022E5231A2377247022A3213221A322D52071215D23032243205D214020B1214B21F1201D23DF2183205621CF1FBD1FEA1FC91EC11EDA1D491F3820A01F4620F22149226F215D211B1F0F1EB41F0120C52128212821A0207A1FAF202D20D92077224B21F5239B224B209620AF238A25D92E + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 AF287B22C522FE20BF213B2130207C20F8200020991FF01E1F1F561E4B20591E711DC51F501F311D041FC81E6720CF1EA41FA61ED11FD41F441F191F0F21731F431ED81E331E5D1CFD1C8B1E2A1EBC1E091EA21F6C1E0B1E091FB61E591EFE1F55211D21E320B81E0620571FF51D1D20601EA520FD1FB91FA71EDC2119233A2EC82BF923E6202B2309235521EE21542252223720A2217020841F3F20CC1F831FBE1E081F0B1FB31EF71E4620AB20D721361FFB204D1F5C20BF1F3F20EC202B1F6F1F5120751F5A20931EBF20951EB41EE2202F200B1F881E331FDA21ED20BD2054217221E421692099213221F41FA41F881F3421DC214F214C2243229F24962DB92B982525244B232522B821792295233D21D4232923D6224221B120F420F120DE20D021A720861F9F1F5821AB20BA20BD2132221421EC2066210E1E24206B212321072244216A210220CA202D20871F4F20FC20881F3C20FD20D11E3A2125228D228E22A72113203F211D21F520861F761FFB1FB622FE20CE21A222AB258C2F862C5D28DB245E220F23E222E8237E220A2259218C21AD200922D12069221E205C21FB20C9206A2223206620AE1FAE219C217D2285210222CA201720AA1E53201422A02173229F1FC21F3521D31F5D1F5B202321072153210A21042151221623ED236622752235216B20A51FF21EB41F3120A920A221B92262213C23E824D82E2F2DB926C822652279226023B322C122F822F621EA21D121FC20832016227C217520DD210721B72090208720DB224E1F34206123EE20271E3221062178206D20FF216F22BA217C1F9720F720FD1FBF2177205422662084203A21352271217B21E7227A22E522452018213B206F201C2094205B20292177209D2180233D249B2D292B45265B23BC2457225724BF2295228823752292224D238E205E210D2254225C228C21A02036227D2043203721CF210220A02144212D216721D7212C213D222322FA216D21AF1F5C223F2088214F2192218F21DC21C621F520BE21F620E321BE22B322D2200D21EE207721ED200F2173207B210E2362221422B8247C25602E852B5024AC239223262331236B2454238D2265233E228921DD215A21CE2207235323B22109226521C1223E22FB20A521D21F8920EF219C22B3224122DE232523CE212E22D921712149219C21A321D920452294233B211A219921D921AF201721522259205B21E12000211521B520C2204222CC206322672172234924A224562FCB2BD825B023D0249923F7219F223F23FE223D23EC20A7218D22F923ED2106222423842216232F238722D120C2216F233021B7217E22D72109236C2394229A21C421522297220222C622F4231A24D020BC234C22AD20E51FA3205A1F1D219B201421BA20E4202821D5215321B420BC216A21AF21EE211922F32464251E279E30A02A3826D8243124A72451232F220D24A524602344229D21BD21F121D32232218D21122367231C230B237221E11F5F222821A2231D2263211D2253238322A12133222D238C23CC2394230323FC21442227234921FF211D20D5224021A420E9206B219620B3227722C9230822EF200F22152313223823C8231025ED2619273032182D672410233A23BB23C022E0233A2350243D22882390223C21A7221723AF224A2354226D23D2227E22F022772005223B215F240F246D228B248C22DA21E7210522ED23772331252C2432253A2401227B228B22952182200D20CB20E21F9B2034249321C12048236F2365235E238622D922E72322248A25FD258728EE287E34792B8B27C324AB2398242F23B6232F234F230B237424F3222223F52131239F23BE2324231F247224E621F3224E212923A3219D226623872327240123C92128211B239522D223CA2590243024D724F02146225920472038207121A420C32046228222D521F42157232E24E222B12179230323A322F523E9246128BC29982A7A36852C2B26DF23062689236F227C231223722429245B2331239722432382236C24D3232B249A24922752240F25D021B622FC2392259726DF25B523A7226C236422A22117239F221C24D0246C24BD247023212306214A2199215221DA208D201A2295225423C924EF22672538252D248F258D24582439253F27772832290A2D3E36802DC5272D24F223F223BD23142438237B24BC23AC22CC24B823C0255F2532264E25CA242F24D3269124CE23CF218825A5256D27CC272A264325A8252A24C524C3227323AB23BA224A22F0239125F922342120227A208F206D224622C420B022EF21C222BB21DC223524E72444250526AC24FF24AD2827295D2BA42C4C2E1034382C512768246D257826A024632509260D26302528238623DC23D324B42655249F25C825B225F725112550230723C9238A257627DA261D267127C924B82489235825212462236F227D233123D023FA216B235A234621942052206521D4218E222024C8215123B823D425C8231E23D824E2251228A9298D2BDC2CF72CBD2D1237972BA826E425542571271626BD27EA240A276F235A2457250424AA259E25B925B4237426B627A5251024F3229C22CB23E324C2247524AC252C25822487259526DC27D124B723B02288228D2177212222AA219F221E2268207820F820FC1F0D217D228D24632628269F27E9250B25B025D9261627E42AA52AC52B7D2C4C2CB834352E372935277126B0275728F626D8269024FB241B251C244E23B426DA27BC27C5246026B125B125BD263C26D6224A23A8228A2428254E2483247E251B2607274D274F2459230122042143241E2216229C234E202A207920E5208420F6202422F7246F2529291D29252A3C2A5126AD26EF253E288C2ADD2CD62BC02B8E2C0334122D602A082B432B4E28B52641263C26DC247C2491256D247D2497253C265126AC25A62440250A25D0256123FA238C247D24A9253425DD228A249F25D3258C26B925AF2562246C2224211C20FF20E121012281219D2070217D2365211D216F233D250527382A0E2A4D2AD328F926C1231E26D7267C283C2AA52A902A5D2BC2339C2F5B2AF12A5E2881277927F82594268B263226E62462245024F92419243725E324852595266424C5237224DA239E243424E926DC26EB25DF259A24DC25E124EC2522247922B421A5235A234B2201216B22EE234E22EE1F5F236021C420C32120263826612988286A2ABA28CE2994288F274626B8259D267727EE26AB270D33002EB42B38287328882787266027B6265A263925C1251023E623D92476263F25C225EB27B325A727AE254F23FD24312772265E298F280B28F12683255226C4247A24D223DC23DE21802419247E2491218A23F521E62122216B20731F01209721B62395265829672759290A29D9276825A125BB24482446247326CE25802768312D30F22851268826122615264126352797257224AA24F1247C25B924BE286428F92771250828B2279224C8231224FC2510291A2A19282F26B5290B2780265423C62397243E24BA2480248324D22244234E23A0222B218B20F8212020E7208E22C324C6249625EE26E8287126B9258B27ED244A24DC244C24122512260D26C830C22EAF28692710270C278727E126EC27C0276226D3265125CE25D327D128DB297C29BD2C192A0D28FA258A26BD258A26AC2850289E2835270627E6265B25D7252E25D625B2252D26D1241D2413261E2398242124182341222A2000220221D1210423EA2260248F25BA271E278E268728132537251D2734243D26C52590251731B62DA927FE270827AE275F29C92A342B3F2A0628E5239B26E527C32A1D2B622BEB2BF1296F2C0129F9275026A72699269125BD26C42476259226BF24CC2569247025DA267F26B5265B26C425DD246C2582268125C322CA22E2211B22DF20CE212A234A2583250627282843269D26F7242724CD25E624D5262826F4244126CE30942DD527BD260229332B0B2DCF2C772B7A2A9E2790269626C7276928692B842A422BF72AE72AF127A025872599252925042451244E23FF2350249F2235248624DC23852513264926F5251629C927BF252828FD2646254223B1222622EB21AB232E25CD26B1297029552A462A2B2857262325F323C9244125BC24C2241225D7305F2E822880289B29E02BB72AE12B1F2BCB274424C1246626A8257628862B0E2ABC2A932BA6291229A6262E260F27CA260E254323302205243424AE223C24AC247D252927EE242023EE266D2863293B2A7D2AD3282328DE24DD2397225A222F256B2430272729EF29902A9D27802717280E274524E5249C24C7256824E9268C31162F2C29D7270629032A2F2ADD280A273F27A024CC268926D626FC27D427D828A7287528B3271929F5253425CF2695255C24CE232023EF22AA229F230C24DD257A26E927E3264E2686262927472A812964293729CF277525A8240B22FD210C23FC26F426F2277329D3280927792627297028C2250D25BC248326C3268D257630752F0B2AB5282929E127B327C12765268F251124B325FB265F273D29702AD82AFE2A17279726EF25E426A6288826F0268A2532259424E32244251C26462864288329E42AC82AA82814264A28BC295528D829A829E52669237224AA215921F82347243927502901284A2877262B273629B6294E272F25AD23A7243C2471279E31A830C82A802829263027E3263F26782543255F255C2844297628C329F72AE32A9D2A4E29D5258727F627D628CB28C229E52744257A25A823AD255E27A6296B29942B132C2C2DB529C72A7929072A172A9228CD28432786244D2362236E2106249F233A25E5276027F6266D25BF261D2887287C2791262A25682316253D267631C92F662B2C279B276E26822650268126DA25A4268F2A072B5A2E372BAD2BB52BA3290429D927F628322A7A2B352BDC2AAF286A254423B3240426DB279B28262A882C5E2D8D2C1E2A952A2329CE293529AC297529BF2794255923E7222721D121EF242824CE246526FA259F255D26592770284F27A32772256E25362590285D3392303D2A662A432AE62827261C268C25B526E427EC28202CA32BA22C762A6C2B7E2AF028C229A82A802BB22B692A1A296826D7258B233F24B526BC27EA265229DE2BED2C162C5D2893277927532600283A2AEE29DE28A4251A254921E021D722C62287236223A525C42533262427BB29DD29E72AE3281C25DE2460278B2827336B32712C4F29E8295429302ADE2636250827FA26F32B1E2CC82B20297A28A928FC28A5284D2A332E4E2B4A2BF528642947252E24B22417258826F825BD252E27E82751281A28E2261C2685260F261B28972A612A582A2D257C24D4228422C422C92316258726472756281A29BB2839294429C628DC27B425F825B328F529E6350D32482CC12B8C2BE42BA4282B262B26C2254A296E287F2AFC28C8275B25882327256827BD2AFB2BFD295928B328EE262324EF235E25782445261F269325E724FE24F7251526162629261C250E26E726BA27252A18266D24C82353234521092284228626E5276827322B8C2C322DB82B2A29B826C925BC250E29D02A602C5A354331C52EEF2B5A2DEA2B1A27052576249827E928D72A9B2904272326C827B127A0261E28312AB02BF927AF26DE24152349234C25FD2536261C27D3241D26E62492272026EA262C259E264828A1279B25EC26CF277D24F823BD2314233E23D021D523B825D426102AC52B2E2B7C2B632AC5280028872502277828D72CE92DE1354A31E92B802BBA2AAB296B279D26D1253B261028B227632645266D25EF25B32643286427C8296C28EE255E253624892247233C261A28D628962775260925E126DB260D273A262726E92537294628B5272028DE27FE244023E72219226522D020E32233245D264B28822B5D2B0E2A47297C289F2609276B266629692C712C27352631E52C962B872B36286B273E257A26A028BB28A8274F2786261327D32761283728972916285026DB26AB256527062500253327D32934280528E227CD266A28D82911293326F424E6273E27812862283A2AF1290C25B523352389216E2108219B210D241C27E1288229EC270B270027D72872284125CA25F428732A4A2B8F353831722BA329ED293729CD25DC273D27A62AD52A0A2B822AD12AA228DD286C28FA28A928182799269729122A812994296E278927CF2ABE28C9264A279728912A622A1A2A0C262B24DA26B628722903294D2ABC29C826F3248622D022392377201C23E823422587260A27D12709276A28EA279627152799258B262D29992983330A30FC2A932A28281828FA27A9298F2AA92B062C2F2D772D3F2B8729FC289728782AA729E4270B2941283829F02A5C296527DE28052B6328A9276A28B5285E2A8829C427922706241F25372A302CBD29372AD42C152BF7269A25EC24A923D62374231C25AD285E2A9E2A8D2878256F2722281C287726CD242A26AA2885296D354D31732A2C294B2795273A289229E12B7A2B032CCB2CBB2AF828002665270729D82A9B2BD929632AEF283C271526C827FD2680281A2CFF296529872A312A7C2B1E297527602583230E25E428E928CB280C2B222CF62A40285D269422ED225A22B22512242D286D2A5629BD278127382906291F283827B0256A275B287D2A95347B30CD292827DD251328252A812D1C2D102CF52A1A2B5A282F25E528CE28FC29002D812B832A012AFD298625F024092632268D27932924292D2955292A2A8B2B45294127A4250D250327372747286927B72AE92A132C84287A25AC241523482217241526D326D02694283427DF296C2B552B0D2894286126AF2622295A2C7536A82F5F286B251126E927DF295F2C912BE72D272A452AB7290529D528052B322C5C2E822BB429C02A9529C32878275024FE264127C926A827C3251227B42826283427C0262D279C254527352820273828C629C029352A992847265323CA22E1230225F6259927012736265F27C529602CB32B982AAB2A1D26CB278929E92CD834DD2EBA27E22589258F265B29F62B6F2C3E2CF82B4B2C832B692904281729522A1B2BEE2AA4295F2A5D2CE7283E270B27FF249125A5256F25AE2474241D26B9244425FF24A426FD275D29082AB2286D279A288329C7299C271727922387228F22CF2329266D28752737271A265228C62ABF2CE02BA92CAE295529CF298A2BB6346C2E47291B264E27C328A42A862CD62C032E7E2B1929EB27B8276328A828AC286F2A0B29D8253B2A1C2A042A5528A2259B2532263E243424032618238423BC23C423C6231E2642281529DD29C4276F27B9288329B229A5272425FC22FD22CD23F222C4243E262426DC26B926C9275329BA29FA29692A51292328C926E929D532C2308628BC24C027B627EF2A5B2CDB2CDF2B3D2A132A9A29A0274526602703276E27B42645272D28A129A0298A29282A7D2A8928DB2692251126C42509248524AE226D2456288B27F5285B2AE22AA4293429232A0A2B6A2A4D29F324A92230223C242E24CE230A250027AE26B0270128F1274C28CD2AC9296B2A692A9329843025316C29F127AC26BB2B4129AC2BF52B542A962945293129D72700276F28CD270427E2259626F22580267F2681262D29F32ABC2AC0291D2A5529EC27FC261125B023A823622700288E2884284E294629132A2729212AAD273626F423CA227F216522AC23EC2391255F28BC286D280A285A268727FA285A2A222A17271C287530982FC82877263F2744297D2BEB29202B792BFD2BB42BFD2AE729032B39294F298727FD27E226F225FC234E25F4262429F128A92C822BD228EC2AD129F028CB27A425FA2522271B264326B326C8282A272C27FA26D4253425AE2546233822E82277217522602592279329782AB0292D29D727B42685286927EF263F274428DE30482F3F290C27D026DB28F82919282E28592CE72BAE2B282BE6283F270F2A162BF329812AFC278B273E296E28A92825291929D02730273429A92AE82A072A3C274C24CA2504277B251426B0261C26DB264D25B12689243225F422D32295219521ED22A2247C272029132CA529BA2994293128CE2785295529F6296E2833271C30D32ED728A026EA273929732880288129742BFB2C9F2A0E2A1129B8281929AA29242AE52A4F2A032C5B2AF82A532B0D2A8C2A6F288F26482745297227F427BC24F5239A247A26F1254A25C524CA26DF253725F7259123F6235724002168227222A222442427270D28112C612B28296829F628BD29822B78299A29272A42287130AB2F092CBE2AD72AEC2C8B2C082C4B2AEF2A222ACE2A8128E5267D27EE28F829202B052C18283429022B8D2A442BBE2B892962287C263328532927295328EA25A3246A23B8253D27AC26E3266229E7273127C7251C2625253A223F23A522D22127212225FB253C28972A8329C42712296C2844278E28B326922699268227B0313531122DCC2C892D8E2CB12DE02A2A2AB02A252B4D2A8F2A4F29512959298E2AD92B152985287F2833283728652A6D2AD928552854284328392874298E27312459235223392728285D289728F027A5270D287528E8256324DA235E224322DA22D9224825C526DD26D127E52641272827182655263B265B25912547269D270E327A32D22E452D462D8E2DA52D232C0B29AA28FA29312DAB2B362A112986297D28482A08279226DB25C02473270228D02A822B3E2A672854286129B5262A25A2238023B5249B293E2ABC2A3229C72734260127E9272025F624E025AC249C228E2337231922C3244625E7267626382674284A28862699247124AB26E227872AEE32A330F82DC42C4D2D3B2EDB2CD729192AB52B342BD22B8E2CC22976285727EA283D27A72659267F27AD26C52574276C28892A372CB12AB8295A2767241F24B623F024772652298C2BDD2AB42986274726FB24A426C725B424E7268027712442228C202424E0236E26D726A7266A27E928FD29242AF02733253A27A527CD29AD34D42FD82B032C6C2B812C872C042E702CE52B122B7A2CDD2BEE291A279F268A259D273A2A20289C285A258B27BE28FF2A3B291928D9280427C325AF25CB241C23CD224D2525276528362893253E24672471263725C225FF26B8288B2936252E24FE22E22440249524BD269927B028132A592AB12850287327892975278D2A9A32802FD22BB22B2D2CFD2B3C2DCE2C092C262D6F2CF92C492A5B28F626F2261327B52616280C29082CA928CC27A32AC42C0429F629D32814283C29DA259125E024FA252328BD2634260A259F24612349234725C426E6255D25DC26FF2641253F2320231624D3246324E2293F2AFD29B62942297429F8287127B62731295D2AF933A72FFF295B2B302A142C472CA42B4829AB2A882B9A2AE9291A2930275429FC29FA29C42BAA2A4F2DE32B9429B32A892AEE2AE427B728F9280B2A9B27452772266A27EA29312AB9273326CE23E623DC237E247C25F1232225192877269924AC23C52273237224B026DD277328BE290A2912270727C52988280C2869271D2928351733A92A19297327AE281929FA2888299D27D325AA27C726CC257227212AA12D522BF22C4D2DB82CE328C628092A532BE4297A295C285F2923290A28DC271228372BEB2B422A802870258C24AE222023EF25CA26DC253727C62669276B246223FC2346228F22F825FE295A298C296C2A4E28AC26B428422634274527C3274A31FC32942D2B29E628CA29E529CE281828AE257F264B28B929BF276B2A692B4B2C382CA42B482B442A992A3028BB2ADB2BAA296D2A422D9C29C8297329AC286729B32B832B432AF729E92710257624E623F426382AE529BC288628D8295628EA25BD257523122497265D291F2A6B29A329B328EC28022BC8286527C5263B26E42E6A346F2E362C2F297026F628DA27FF28112A2E29A629592BCE2BE229C62A9B292A2C1D2BBE29992A3228B3283129922C6F2BF12B862A102BBB28F52728299B29C72A5C2CAF282829B3275026FA23EB24A927012AB3288427CA27BD29C429502709254C24BC25EF2362292F2BC129B62B622AA12A4A2D232ABF2708274B27ED2F5C32652DF52A7D2956276228A6298A2D382BA629032C3329942A8F293B2AB5283B29A228D026B226C9270C25C1298D2BE92DD62C9D2ABC276425DD241727D027A02755290029BA280C2848268B287B27A2281C2BC029412836282928A22720262624642393242525322760296B2B312B4C29F528772CB62A2E2A1627FA265030F332C72A8D2934282E287228E42C3E2DDE2D1D2EB32CD329B6292229F029CF270F27332635257926EA259B26AE27BB2A632CF92A3E2A3A270824CA2234244125E7266F28EF280A29AE28D227BA294E29B92B442C752BD427D127D125662561256D249A24C62585237F27F027DF282F28A1283D2C392E5C2DFB2C0D285927E6307331F12AF3280A27C426CF29D52CDE2DD62DF72C3F2CCD296A2A8D28F3291529E3268225E12550276E270127D128D029BE2A192A842A2326D224952347254325A9254028A72AF9290E2B762B002A3D29E229D629A7286A270226F624732467232223FB2303259424D927E0274D2882289227452C882C922B712BB027EC276433FF31DF2BE32AA128C928502BA62CD72CCD2C3F2B8F2AE027D3276929942BB729F328CD27502778299D29FA286327B728642C8C2B5528EB231024102586258B28E227102A562ABC2A2D2ABD2AC729CB285828A4268F284827BA27F826D4248B245C2374222125D824E4288728DC256228A6283B2A422B5B2B9D29E42849292533C032A12C722A31298E29A02CDC2BB92CBB2BCE2AA32A5C298C29E228F2295C2C632ACF29FA29762BD52B582C74294929FE27B8278A27CC255523CC26082784288929C52AB4290A29062821279A26F9263A28A2284E27B4297929FB282328BF2586243A238A241427ED2A292B022AAB281028812861298827902722285F29CE331A31EE2CE528CA271E279529AC2C9F2CAC2CE6291C2A2829B32A7F2B8F2B9028D12868287529122CAF2B662D522A3C28AB250D26412446237B2311263028CF29FC2AD029C229F62811262D25CA275B270629B8283F29442A532C0D29BB26DB2481257F25F8261427792A422BA52A5B2A8728B2278E28C427FD260725C227B9313731032C1D2B81265E273D2A1E2CBF2A5D2A702A552BF22BD22BBF2C3B2AD32AB429632A002A072C632AD32C572AC829F2262126BB221122DE233126D627F82855281A28A02A2827D625AE259127B326072916294428D328B62B15282E26AF2597255525322552285E2A1E2C432C4C2B3B299525F229EE25CB24E726582745316B329F29422805268C28B62C002C392B0B2B162A942CF82C952CAC2BCA2ADF27A92A232DF12B6129522B172B9928BF29EC28FC248A249F22F222CA23B52679277E282A295B280928FE25F32513279125522635280528E028692A5F2A4A2739267824012520244E263D2AC829C129742CE228892678274025D923022660245C316B31162B68264826E528C3296C2C3D2B612C912C2C2B4F2B6B2A0A2A7C29472A732B4F2D4E2C332B0229FE29D42A0129EE268326022454236E244C25EC26B7284A2A442A982840267127E8279525B12584271226EA270528612A64290729D227B7264D238A240729662B8F29BE2742280F28D629C527CC25A1234F259D264631412F3F2A9F27F627F926F629702CA22BEE2B6A29992C4028D4287B2656274D27452BB92C722D4B2A6928C227F4260B297B27F1255726E6236625DE2632279329CF2B802B4E2882266B29C0289D2502245B257826D926A826CE27DB288E2918286227392530261728E329BF299D26202709265327E127F02687256D24CA269631882DF0296727DE26602A8E2BD229352CEA29AF2A682A5327832783258927D1272B2A322D8C2C692CC929D7281C2AFF28332721273A270027C3250126EB278029672A372A4D2A1629BA27152888267623332460281B2581255A28CE28BD286027F1239B23EC25B526492BE928D5269C269C268B274E283D27AA2509272928D1321D30712B722AFC2A0B2C152DD52B062DD42AC22AA82B952B9A27A9269426A1279E297E2BDC2A182D612B1C29DA2A6A28AD28292BCA2BD8271926DA26892658264B28B2279B26A426C5272628482732276428A3294F2988277A25D824572598227A227124D6249A26E6279126A628D926DA269B287B2ACC286C26B2267C28F3301632522C292B262A842C722E5C2DCA2C472CD22A092BDE29C8298C280C27A8273226E22785279D29502AB228252755291C29DC290B2A34293428702626265C24A026B52590249A264D2787267227ED25F1285E2B5729FE286F26E122C5238422D622B123F5225E261D272429FE27D827282895283728D928FE28FB263E2734312D31BD2C1D2BBB2AE82C5F2E1C2DEB2A522CF62A142CE52CEF2A9F29E1298126EB25F42703263B2561268A25B326352754287929F52A402974289A278827F6274F27C026472628249D26DD284728B7268D29572AF9280326552774255D23B124EC2381233A251426E2284728D728532A94285B28C227D02788278827502846318430352CA72C0B2D0A2C752C6D2B552BBA2BDF2BBA2B5C2E8F2AAA290E2B4429492ACE28C026F9254126E32608265C269C268E27BF286C2795283527B0272027D8289F2664260B25B0278728C82853286D2AF229BC28AB25132873268A26AE2616251124DC24F32518291229032AD1285D264326D4240B264626FF27442AE634A533C32D7E2DF62DCE2D612D632A5F2AB32B882BEE2BDE2D362B2F2BB82B3A2D252C242C902A892790280B28F327B5270127EA273C28EA28A728992834274828262B5729862949275429C02A0C2A6B2AA62A3E2BF12605270E27A42A3A297927F12637243E263D27FF27AA28712771271E274F250A2500269728FE28DE2C3137D231552DB22CEA2CD72D322DDE2C3A2B782A7F2AF52C372C852BF129A52BD42D042CC92AFA2A8C291D28C527EA25CF26DB26A329732AB929F1296428EC28D92BEE2A482AF4286727A92AD62A5E2A452ACD2B6F295C291828BD29162A132B92295F28B5252D25DE26CC275828A728D6291928362634268F255428DC29572D97376E31482E1C2C412E842D1E2C802DC32BA32B412B132BB02A4229BC2AD72B722CA92CB72CFF2A222A28289C2BF928F8270727692B4B2B1F2A382A5B2AFD2898290629F326FE26EF25DD27B12A902B632AE029E329A729AC28E9297C2B4E2C2D2CD62A7E28B2279827E5280C2871291A2B8D2B912944280E25F92784293C2CA8367C32452E772E8F2E7A2C632A9B2A302A9B2C032B2F2BAA28DF279B2959293B2CC82CD52B822B562C532A7B29FC2AAA2A9328B52844281728AB28E5265128102868273926B726FB24362657299A2A832ACB29C928BE262A268B2A762C3B2BC52B392ADF2B6C2A4E28B4295528942A732B842A692BF3293A277027E3280C2BFF343731092D992C432B9E2BB428FA254328F42A5D2A372986268226AE27042A0A2BDE2B522C552CC92DAC2A362AEF28702852250B289D28DC26D92783265927EC265A260726AC2486244B241027A72AEC2A7029ED29F5262C27742A682C552CEC294F2A442AE22B5429902998287728AE2AE729E12ABF2A8C26882593275D2A48345030EA2DF92BDE2AF2296A2839277E29C62A702958275526BB266C28D928992B232BDD2C642CAA2B3B2C3E2CD2291B271C27EB279A285D276F2556262A254B27E629AC27BF261524B3232E268B29AD2A712B752A5B287D2A2F2CD12AC22BEB2A0A2BB42AE82AF62BDA2AFA28AF27B02A3F2AEA29E3270527A72428253528DE31BF30B22C5E2CF12A84298729E3296829462BE6280E2824273D287A2B3A2D9D2DAB2D442D882DD52C9D2CAC2B70290E2811278229112AF326A6259A241428BA29C32BF32A422823242A24602711291A28FE2908296828B0298D2B212C702A8D29FD27CA289E2AF828502B622B662805291F275828152732235E23372541267A31F22E302CB52B802B292A8A2AC72B212BD329D72A3128F927592A282DE92CD42C9E2CD42BB62CEC2C772C432D9B2A1329A428182ABD28DE24EB25302618282D28D2290D2C002B2D277F25D02901296C2AAA2A6529CF25A4267529B02A692963284C27E1279D257C28612A742C862A3B29FF263327B126B4258A254925292673302E30302BAC2C942B8D2C602DB02BEF2D4E2DFD2BB92A6829572BEB2CB62BF82DD528F92A352D9A2DB42C812A612A93289225792620269027502819280E2ADD2A972BE42B322AF22757260227A628322993282B286D255726B9277F29092AC92742252F2597253D283D2C562C932C992BD628402727279626FE250F2554260731F230542B392A652C162D5B2CAA2CDF2D642DDD2DEF2B0F2A8E288E2A6B2B572E3E2B082B922B322C6C2BEB29FF27FC274523002639278B285E2A292BA5289328332A122BE8299B285D260427AB28AB270E279F26372684271529362877282427A32447253E256228EF2A1E2B132DC22A50289929622A72274E266F24052756303F32802C0E2A932B272D2E2D3A2CB32DE02E2A2E762C052B202BFF2A5B2D822C9A2ABE2A0E2CEE2C332CF729A6287B25F8251B2662277E2ACC2C012A6027E127652859272626C8241325C725B12713263A27262783282B28A82A1B2BA4282028A026E42522274D296E2ADE2ABD2A212B432A9E2AD32CB82A36290727EF27AA318C31662C7E2B8C2BD22C4E2DAE2B832CFD2CD92CBC2BB72AE92AB22BA32D2A2C4E2CAA2B772CE32BC02BD3271B25C624BB2273251A281B29672CCB29B328A9253A2760279728F425DD244125072816272F2782283A29A428D92D7C2A7229A4276F266B29B32AE929332AB7282A28C1272127A629E42A592A4527DD25F426EB2FFB30A62CCA294D2B062D212DB92C212BC62C972C882B2C2AD529572BA82CD32CCC2A912B552A072C952B7D273525ED23652436268029822A6D2CE02AF32A8629C329DC2A052BB928F225E1241228A526222737293529702B432B512AD727272647272C2AE82BB12B242AC82852276727332781299F295227E5262D26A42623309233E72BB12BB32B7F2B6F2B162A392B1A2AB42BB72AB6286D275429992B052C852BE029F32A172CB52B632834278725782639264429A4297C2C802A0E2A582A342BA62B5129FD28F1260926492646276A2A232AC62BA12A282C632919268126EA264D2A5B2B1E2C1C2CF628A0287E28282A672BC729012774279625592731329E319C2D622B892A5E2A8C2A5F29E028D7289127B127D9231327E6279928772BDB2B662BD72A682D592B0A2A092AB8275D2657272829BA2B112B2E2ABF2AB52A9529412A1F29572676261B251726F6253728332A072C372A9B2DC6287028BC27FE269E29752AB22A9B2CAC2AC2296B2AF42AAA2BD02D2F28B7258325E12731316E32372D752C932BE32B2F2CC22A8D2C082A9028D6260B2669262428E028792C0A2DAE2B6F2C872C9D2CFB2B342A46287A263329142A142DBF2B462A08296A2857282528E0278325E0246F270D282925E827282C1B2D3D2CC62B3B282F265326AD281129A92AA72C432CD72A5B2A6A2CD72B0E2BFD2A6A28E8262629672AA733562F242C062A7D2BAC2A262B762AAA2B232A1E2954294C288B29B729E52BC32A2C2BE42A1A2CDD2B822BBA29D929EC2788267B297B29412B2F2AE927EC266C263C255D2604269627B02803290A27B725B427A5291A293529B828D526CD26CB26C329BD29A82B662B4B2A182A9328A028A22A272A02290427B9282D2A9C2A3E352031162C662BD62B662C0E2AFF29A72A822C792AE829222B452CF12C132C622B6D285B297429B22BE12A9D277E28A12574250F28CB29B32B992AB829C226D5258727AE28A3287F29A42A4F2CDF2B2D26B227CF27D428B9295A29812505273E27D62A4D2BF92B322AD32B002BA0291F294C295328B927EB27BC29D02B992CB934CE31AC2D0D2C142CA82BD72AFD29912A322C802B142AF0293F2B932C922BE42AF2282D2777285B2A262AD927F7264025B224FC2772262C28EE284F2844268E27CE296F2A082B932A272BE12A7F2AC0280E276428C42A5828442746277226FE29652CC42BAB2B0D27AD28162B562BE42B0329FD27A4277A27B32ADE2B012B9D34EC32DA2E622C112C8A2CD02AD9281729162A4C29002A092A9D29432AB12C192A242777284F295D2A6D2BEC29552966272724632666278E27272A5D282E272D29AF2B922CC02CD7291F2B162AB029DE273029C12A332BAC2AB82A5B2787291D2C272C0A2C412A9429742B8C29DA2B0D2C7C2A8B2750264028892A9329FD2A93340634D52DDB2C442C8A2C532B612A9B2970274627E3256227C225D027D52933293A28A42873272A2A532AE428C4254E25F8247A252527B129552A9C2B6C2A5E28F52A6C2CD02A2A29112B8D2AD129EF290B2A762D252B4A2AE0294028122BDC2C222D4A2BFD283528C52AC42BC82C1B2C3E2AB7278A268126302706276E27D331FE32452DB42D502BEC2AB529102A892AF3271E262427402615254325AA2578279C27FF27E527F029082A8F289E272927B624D9260828062AF82BF629C929A129FB29612AA32B952B032C622A0429BB290A29F62C5A2B1C293829EB27AF27ED28FF29AB2B5D2CD028352AEF2BAD2CEC29BC27E1258325D425C3275728902942338B33B82CBD2A952BB52ACB2B422C382BDD29FB275426CE275E2686251A2663279B273D281E2AE029FD291228C1271C265426292796296E2A6C2B522A6429A7298B28F929792B4828972A81295A293127E826182A452A8B2A1B290F283B296E29972AD02BF92A8E296C285E2849293F296726B7261E277526EB2787289A285330E032962BFE29132ABE2B8D2BD82D8F2A3A2AF32AFC2A772A0C28C625F527C7274928A32A052BD02B23297828DE28D827A226B728D729462C182B86299F291F2948294028B128F827D429BF28D7274326AC26412939298328B028D025B427CD27972989292129F0251528BE262C28C12A222AD0281B29FC29AC293E2BEA2BBD317B31022D792B252C6C2DAE2B902DB429732BEB2BB629CE2AE42A26284C26BD277B28522B4E2C752C8B2B0D2A1E293329B528742ABD2B222C862A262B0C2A7E29C4298D29FF286C26A32928294A26DA239027B229552A13295227A5256527FB266C28A727A725CB264826B72567281F2A7D2B722C492C752A192BCE2A812A56336A31732DE32B002B022D682BB12ADD293929BE2A142CB22B142BDE28F127B927B1278328B42CE82B0B2D0B2BC12AEC2888271C2B1E29BD29DF2AC52BB829E02A2D2BAF2B9029EB25482765259E27DD234D25E928BA298A2812289D2719263B265225F726E6260C24252730273429B02A4B2D112A932B702BC32BD52CB12A09343032D42D272BFC2B8C2C8E2B4E2A4C2B602B9E2A2D2C922C392AC0298D29D1277225DD273E2A722C1E2B6B293C276D26A928E22B012B5D2A102B2D2BB62A042BB42A4E2B132ABA270327E527BE274226FF269D280028032921278B24F8250B2691289E257E259B243C257E25B227E0280527D929342AD22896299B2B5F2C6333CE30122D482A752BDA2BE62B3E29E12AD22BFD2AD129E22AC42A522A3B29BE270225F525A5272B2B312A5828012999275E29FF2A672B042BD929442AE229FF2A332AAB2B6C2BA52A9A29F328C928D0295729DB28EF27F126E2254D25262698265E297E27DF2473250B254D2524262227FB278D28D227A1263F27DD28682A3D320435772C642A182B0C2C8A296B295C280029E82DE02BB82BBD2A002A262BE4278025D3274C26C82803297F2892273A288428132A152BA428A629C72A232AB62B852B012ADC288129CA28DF28012956292E2A192B9F2913284C28D826C628752AA72BE9283E26FB235926492795287D2A9929052AF728E225C327C728012AD9344833872E252B152C8B29072AB2275A273F293A2B902DBB2B272BA12CC12A1C2AD927C728D0281829422701286E282E27EF27B129F32854275529D62702293F274A29DE27F5262828862603272828FA27CE284E2983283128E827CA27A028C82A3B2C092AEB26F1247926ED28CA2A562C692ACE2A7D28C226C82745297C2CDC346632BC2C852CA42A772B2D2B33280428EA276C2A242C722C0E2C102C132D852C8D295A2B772B892B1A2AED279C26C426342AAF29F9289A27BC26FF26B7260F26C526E7261027E72511267B294A298D28D3298D2C6C2A932AEE286328462BD12C5C2A6C28C026712698255128B529ED2CFB2B182AB928E027EC26032BCF2DAD34E3306A2CB02BCF2DDC2EAB2D6A2A6C2B402B2F2CDD2A462C782CA52B552D7B2C872AD629D52B502CD72873271D25C2262226DE29E3290429432995287026D627F226ED24492734279B27072A1B2C652ADE2CD32DD92A4A2858273E27DE29822BDA2BD12858254E243E257D28522A502CF42C222B3E2A6029922A0D2B042B6B340030702A4E2ABD2D9B2CF72E3E2DC72B952B682C9C2C2B2CDB2CE42A652CE72AF9298229E22A9C2BB62BFC274B2787255B266C29E3297C2B812A47298326D6249126DF27882736289528EE29C929AE29E42C492BE72B77287428E2299E2A142BEE2BE228D0261324E4259C27E629392D532BA52A932939296E2BD32B682A1C338630BB2B382B602AAA2C5C2C172F142DFB2A022B552BC72B802A152DD82C232CC12947294D2A0F2C712B0B2B252976278827D528D629542C0B2B07283D2658259F270929A029A4280428B429852ADF29072C402C992A6D29AE292A2A1F2B8829212B782819241A231C25252806286D2A8629082AC929BA29502B672A5D2A0533F330DC2BFE2A882B922CAF2D042E612C4D2CC42B392A3B2CB62C882CE92B1A2B96298F2BD92A5C2A9829B329272E9C29FB251826DE283A29EA29CF28E0252C27562726287E29E527DB268027B828BC273D2ABC29DF29C82AAF2AB429502BAE2A382A4B2810265D22712475268F29482D912AD32A9F2AEE2A4A2BC12B382BFD311C33B82DFE2BA72B602BA92B312D9C2B582D7A2B6729622AA62AA42C762CE32A602A0E2ADC2ABC284929C829522A2228D626BD270928582BCB2A7328CC263D287D28EA297D2A9D272C280528E527F4274B287C28EC265527ED27F52819298D2A152ACC2758257C24D9244526E328CB2B102C562AEC29BC2AB52B462B342BA133FC32172E172CBA2C9A2AFB29172B4A2B422A3128C5273728D828792B712C2E2AB62A072A262CEF28F42669288C295529192752265029562B292905282C275F28302A942BFC2932292E282E2AE7281A28722782268326682731283129202CB52B8B29C22616263624BA262E27EF29882A642A1C2B302BD02B982AF22C282CF7345E31402E872CF12AF72BD32B332BB22A422A332A0D2A4C2BC42AAF2A952C022DC52BF22B122BC7286C27632B992ACE2AC5281127C8279029702AC729B7286729832C5C2BCF2A7B29CF27412840292B288F284128E4272E280329A727EA29322B4C2AA0280825A423ED25082AA82A632A5729552B402A9829602C402B262D34357031232D702CD82C872C4F2CAD2C392A70299D29E2292B2B002CBD2BE62C5D2DAA2B152AD22AD52816285C2A9C2AEB2811272B274D27792759295A28E5270628602A8D2A5529C027632726297F2AB928C627A629C12AA829372A0228A927DF28282AD22792259522B4252028C129EF2A14294229082AA4299A29352A612BB433F432042E742D152CBB2D472CF22BD92C362A432A142A3A2B922B822A722A752BF72BBB2B702B6B2A8C271A28FE2BF62A12280026B32533256127FE255E263D29D52AB92A67287D25DA26D427482802279927E229AF29262A822880273928742887291628D42584231F25F126F42ABD2B1729BA27C0274626B827B728E42709329532D42C022DC12A5E2AB22A552BBD2BCB2B122BAF299B292A29982A6B2BB92AC72B8A2CE329832946274F28C62AAE29CD295B273124E423D224B3258A257727DA297A29042814261D2691262626EA26B427532A752A9A29AA292928EA26272931290229542730240E254B261D2810298B294C2795262F28B227C32715286032F631432D462D272B002B5A2AB42B5B2B602CA12A362B752B342921288229FA2BE22BAD2B0029EF28C727EF28212C862BD9295629B924542355251C2441240A256A287528E325F22465256427D624E12505293129FB293128032958285A274229B3288A275E26F3231E25112671258A2884292B27072916287F289B282229A731E82F4E2C7A2A442CAF2CE92A582C252C442B492A252C8F2A6429242700273D2A202B1F2B062A21298B28452AFC2B032D6A2A84272825D2242D2664266926912769299629B4274325A926C728F3265C249C28142BFC291F28A5283C274F27B028E728EA269F25AF23CC24212496260B29102A6C297F292429F0289827C828CB319730EE2A2A2B152A162D112C872DD32C9229E229712B972B3E2A1828D8286C29FB2A062A222CE3289029DB29FF2B852CE3292927DE25F924F524D72427269128E6294028A725CD26A827B027AB29EC27912ADC298329B6283B2882282528E5268D2543258F2631243D25E9249E277D28982A162BA82B332B702A4A29832B3734A531F32A292A5F2C6D2C9A2CB52AFA2C7229B729D32A9E2D262BA029002BDD2AF0294D2AB327AF29DA2921281C2BDD2B872AF3277C24E623EF242B2538261729C028C82788265125E825CD289228BB27AA2929289A279A281329DA281728AE2858267A261F254324B0244B242B270A2B072907293D29B9279B29582B582ED9359B32422C072D3E2BF62C7B2C062C092C092ADA27D828992AB02C922A9E29192A6C285528D327EE28B5289328B929A7283B2679275925E123AC23FB245F250727F4284C2A3C27B325E0266025FC29F529692BD3288526D7252F274B28A328C82840283526F3265D24C92559246F27872743291C28C92818279829C92CA12DF3362931692D2E2BEB2AF228342BD02A6629CB2979296D2AB1297329BE29572782251C27B7271029A3287B26F0270229C428EA27C5272425B9237623FB218C23482514260627BD255B24D82418250126BE27E0291C2A4F2759256A26B82801297D2A952A2627032474233D2521259E2638273E277B265E267825F3267929F52CE5364432762C312B61283627FA273929A72BDF2AEB28512A7D2A6B2A2127E82534251424F12710298F298528F22838286F2B582A73286A25932538255824C924B525BF267B27DA2684266424D6258726582875273328DB252E2576263A263128A1293E2A75274325E424A82554243A2559267926D1240125B0247D266728F72852340231562C872AFB2957286C27A4283C293A2B9E29372A3C2B952A73299928BA27EF25AE2586269128E528A428442A9429AF28DB26AF250E252F253B241D238B23F5244824E8245B2393257623512593257126A32701244E2319244C260C26592884282D264024B9231523F92201268D2697258224DD23C623182505260229EF333732592D7A2D202A742805280229712A5E2BA42AF02B972D5E2B7A29A1291F286A25A9244A26EC26EB2999298429392A83298228432848252125BE239F2198226F227C229C249A231B24302357230C23FB244A251A246525DD25302445250C27042876277723AD230225C4243525D1259624612450244123E124F5259E288D324432442C382C7C2BDF29C229232AA229522A752A572A272C482BFD281327282508248523FF24E42537282129A828AB281526692506247B25FB23A8225F22CA205521DF20DB22C723CF2329238622B92321258E244224A0251523462337244724ED262D26BE241C23C42331253126C527B32536258C2531259124E82651271D32562F2F2C7F2CE72A232A0A2A1C286E29022A162A992B102B502D402AF7289E2593244324C924342519285928EE28D029222597252B25CC251C23C8228122FF2024215E22B122A5228D23AE236423CC22EF25E52405251824C223782291237B27DB2640260C240D23A0249C25D926F027EF278D251A26C921B823942560272E32882EA32BD828E929A02944292C29B128CD29DB26EB26D9296C2C552A1E2893264523EE2308240026D82501273927B22723255024C723F923412487224F206820C32043236F2391233623FB2332240B23182463264125EC23A22490222B240D250E2895239F22EB216623B6226B26CA25BF288F25BC24D225C824E3254C279D30AC2D3A2A2B2845272A279227452793273C28752622266327132A93272926A7246924E324492245258B2423254926A9242C24EB22CA23C8219D24AC218D239021962285222224CA23B425432400243424BB23272402252C2390242822A12446253E273A257D24042302250E23D9254B260B26B924EF241625602677260E27C4316A2F8929FA267826C726BC275C26A2271328E8260E2734260A27D4285A283A2658251F25AD238C25E223E22331254426562496243A234C22B422E5224D21FD221723012151228224C9239B252A255922B825E0251E245521C7235B23DE25B224C225B8243624A422902410252725CB24DE26F4248D250E237224532376254830092E7B282327B02697250226F327B22760290028B42583263F27B7260D286A254E24E123D4236F236F238225582661251224E024AF234922CA22B5235D2292217A22C6211E231F234E246025B824492348232E24A023ED211622B72264245124EA232025DC23F92190220B238623C425C725BB254325F8223424BF24EF257231862D85273127852761261B270325E0260629972798260D25A52590251A26BB25E7234E23772489241C23F7234825CF246123AA2490223F24F8225B206B224221E1201B228D238E210224522458224923CA23AE2419238023A723BC22B82243229D23A5230423582176221E222F234E244824D024F3259023D424E223BA258C2F852E3E281426EF28A3286E2659252926AA27AA247024B925FC2368253925C3248F244E233723E923AD233724CA25DB2447221324FF22E721722116221022B521B6217321C9207C215E229E22ED221223A1243624F922DF21DE21712298212D23F3227C227E21E2211B22DD22FE22D825BA23382484244824602429244A26E42FD42CEA284629F927662726264E24D023BB24962595240A254B235C242A24FF2571232C231324D4220D2404239624CD228222042424227E2231220620FB1F641FD220A01F6320F220AD2021210122FC21692312231421CD228322562181217F2241246922AE22AD22F022F121A0228522A3237F248F2268246F24F02329247630842E9C29F827B22661260C2510248A232B242424B622EF225A22BD22682480216B22AA2243222C23232472232123E3218222D623AE234E218F223B20052171218621A1204321051FBE203C21C620C7200D23D623882209226A218B218B21EA2216234F239F2224229123F721E3220B255B22F12294238922EA22E325B1259131822C862766250B25EE24F424BB2239259421BF22F722922253228723B52332228121D0225D210E2357219B21312282226221FE22F8226F21FB228B21FC212A21822180203F205F1FD82078212321432168219A2394227E21C0205E21C9210622F8227522E22101213023D2206922BE21A221EC223F23AD22C7248B241826E52FD62B7924AC235525BF23C32398220E23C1226821CC23E7226F21B32216238A224A230821822285233122FC208522552384209B218121BA20F620C21FEA20D41F7B20C3203F227D1FEF1FCF208A213B20742176232221D02089227F2159226022B9204620D020232154223622D0207D22A822D1209C218521C3215E236B24A22D742C21277523C8235325A7226723C0225623F62389227B2272235E22B521C6231E248623AA22AD239F2146205D22E4225C213F2106229D2236207D21D1204F1F4C22BD21AE201B1F3F22B520D021EC1F30224F22E020D22070223C20AA23B32064236922BC216420EA218B210D228623152279206E21EA216423AF23EF2597308B2D812649232B23DF2472225823132208239B2292224521EF21AB2074219321DC212C22022107224221B92084212323CB225021CA205D1E6920A71FA8218B207920FA1F2221AA20CD1E6022EF20C61F842047213F1FE3201621AE20E51F37216B21AC203F21211F6A218F212C213021DE2148213922E2211D22152382244730932C8E260025432264223D22F32268220224D122FC214A226F2139235E236C20C22205222222E1221222E920DF215B218A21DB21B01FD31EAD2148219F20F31FF1200422E21F5B1F4E1E8020161F4C203E211321C81EBE1FB521CE208F211D1FEE2132225D21D3210D21D920DB20BA220D21FC204D21571F87226423B7259E2EDE2B0B26D62315236C223823CB2381233F232622B6226B239D2129212822E422EB21FA204B21D421D121F720B3203321E721F620611F2920061F372112209D207220592003210E1F5C1ED21FC01F6C1FB3207A1F931F631F09203A211820DC227A21BA1FC020EF213E22E11F78200B214F20C11FA72173209622EB23BF24022EDB2BE326A522D3212421D82008239622B4223A21D9224B20F9209C2035211A21BA21ED2083217B2140223A1F71206C23B022BB215C204F1F3220A21FB22145227520751F0221FD1D381F991F351F1D204D201921C821C61FEA218920D11FA11F05229620281F511F41218A21E51FD720341FC12057225B21E3214224A025622EB02C36267C245B221823D522F1210722D122A321A5221B21F821A12050217D20C31F26219B200621771F2E1F3020C621022114204D1FB9208A2067201C2182219321D5201720C91E78206F2060201720CC1FB1200E1FD1205521E6207E1FC820D820E51FB3201E21EA21AE1FE11F47202222AD1F25218620282145215525D92FA82BA8249222C12215224A216421E422C82098214821BC20D2206D1FF01F0621BF20AC1FB721CC21D720091F3A219C205E20F120172173204920681F151F0F21ED206C21EC1E031F191E1220201F1A1FB51F12202B1F7A1F2A215B1F1C203920282068203E20772091200421E821281F831F98200421A9216222B1231125BC2DD12B2F259E211D221F22E6200122E92122221823A02256206520082017213B214F218E21F320A3204C21DC1E50216E1EAA2028217B1EBF20511FF31F9C1F5521A320D91F40200A20231E1E200620261F20229421B61D8D20B41F48202820DC214720151E10216720B81E4320621F0A20BE1F9E208D21C01F7B228F228A25222EC32BF4256E2274212B222B228921CB234D228E210621F7207E1FAF1F6021792296204221462337206F21EF202B213C204B21352240203820891F82204E1FCA2041204A1ED71FDA1EA71E6A205820EE1E32213E202F20D81E5521D61FB8222F20482015204F20421F4C21851E46201C21BC217420DC205D215122AE207123142F8E2CE325D32128239422A822DB211F246A2330220E2273223321761F7E21BA219A224622E921AC1F0A22AF20C521A82040203E224B20D61E1F2029222220DE1F3E1FF31D2420F71E4F1E3520D11FF01E69212022BD1F1B20AA1F261F831FF61F0321BB1FD21FBA1E932006207020D5214421EA21AD201A2091211424A624EB304E2C90257C23A620A123DE235422EE22D1232A23F523852253218120F2216120EF20F3227921DD1F0E2109203E21F6202220002215210620FA20211F361F7D1F861E6E1EC61DDC1E731FF71E731F32215821A020DC20631EF81CF21E491FBB20F91F7020CB1F981EFA1F801F032096213E201C238921A41F10202E23E324C02E + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 042973239F239D21A9228121E7201321A921FE201A20341F461F141F4F20081F261E29200420F71DB61F301FE320861F3F20A11FE620E52065203820CF213520E41E661F6E1E0A1DC91D9E1FB91E471FCC1E3D20F61E851E4C1F891E271EA11FE820EE200021D41EC2203B200F1F20212C1FCD2175215D2130202E237B244B2FF52BBD2446218D232C23BC218022DB22E522A320C2215F208D1F3E20CC1FCE1FCF1E561F8E1F4F1FB11FD920EC202022961F98214A203F21C8206721C5217E1FAD1FB920AD1F7E20091F07212F1F2C1F56217E20D71EDF1E571F7221A720FA1F6C20F4208B21A22052223322F320EE20B020302259232E23D523AF23DD25D12E2D2CAC25A224C323C6224322B522C823E2213F24502307235B218720FC2023214B21F321322139205B20FA213021CF201922A9220122F3216A22C81E2921F72159211722252189212820422160200020DE2093211620BF200F21A01EE920A0214F226C223F222821D822DF2286225421432141227C25F9237C2468248227C630A22C94287E25202399235923A2245B236922E72193212A2168213A202B22EF1F842169214A214123AA200821BF1FAB219B21DD223722C7226221AB201B1FA620CD217B212C22142022204821F91FA41FFD20C9217D218C21C120D520F321E1220C244D234E23A52253225D21F120432166222923DC24A6269224A625CA26E52F102DAE260223CD22ED22D3231C2370239A2398221F22A3217A201720972169217020262298219F21672152214A23421F302087237D21891E8A21232173200F2083210D224E21701F7C2009213320DB213121DA22E520EE203921E521D120572183236E23BA247F228C2312224D221022D022F7232B268525F725AD26F925F62E742BAA267E23BC2452226F24E6221C232B24CA22E7222D23C5202B21452141226D2214229F218123B7210E21F12167222920DB212F21402140219F21CD209721A321BE21FE20841F05227720CE2194212C22B922E122BC2291210F22AD21D022EB2443259B2327240924D32304230B2396221D253F2983291528E0287928C02F062C9E24AA233C231C235523A024D423572352243A23172255226F21D122DE22BA2374223E238922B323772396217A21BB1F00208121C8212E22AC21622391224621F4219C2156213A217021BC21802126231D253023C922FA220323E521AE23ED258D24F42574247F240F24272315235B24ED237E277927BD28332885264D30AE2CB326DC238D242C23B821162381248D2400255A22F4220B24B2242F222322DE23B823232488247B23DC216822D623B820FE209D2177216F2253236422A721F121FE215222C321A52268249F24AB2129259A24EC23AC2212232E210C23112499264827E426F225BB25DD2422241F24D123A923D6248A251B2843270528CF30992B3E274F257C23E823BC228E2227258A266D259B24B62367238C222F231A21EB21CB23ED23F523B523D521DC1F07223E201E22B920A62039212C239E223722702209235B2201236A2320234622B7224E24EA232C2542236825FE224122CF23382680264A28AE266627A525812460257C258B23BC23EC23E624492628255630322EDF25F42328237323AF228A243A255A27AB25BE263626C8232B246B23F822C523F322E62331231423FB22A220942138205E239C232B22CF240A238E22E122882268236822B6238C2368258524D222AA23D724A12408241D23352383210023CB27352622258E26E52651278A2880276B26C22540245724A8238A25CE25DC316C2DBD29A727D924DA248023DB24BB250427A2273629CC288C279E24332438242724C82332241E24A121B9226521F522F2204221EF2257236A2485234E22E2215223312234229023942355243D250C23A623CA22F3231F249E24472378223D24F124CD24B224AF25E0265426A9262629CE279524AE232A22EE230F25512547313B2EA828212674270E24C7225D2433255228E828D6288928C7263F2583235D24992357241C247D265923E924E021762235233C24D42514255E2341220523332277216422E420A821C2228A2303250C2480240C234024CB240D246722CF216D23792425250326FC237126D626C426CB28D7267B241023D1229B22B0223B269530882F9F2A1A28972627259B244125B3252A28AF271B27E12859266B26B6246E253425E92408243C2682249224272353268D25D925472637255A24CB2451232024CE226A23DC224121B920152315265E24CE22832458235423832412246122B3247D249025FD230A240A255F2545251626BD242A23A424CA226F233E24C726862E2E2EFE299228D8279C2701256B267828B4299C295F277925252405234324992240251226A4252026BC255F25D125352689260726E7242B24B925DE227322D521FB234523612224216522FE22F824DF23EB25E92521244C23CA227423CD239B25CD27C02591265025AA261A24E522C7239B230C248D236C238624E624D9269F31082FF8296F2902283D28802653298F29702C472859273F260B23D622892233241F24F2276F29FA275D26F425FC26A027F326E1249523F3244424FD22A2234324BF251F245823902213236523CE24B425A4259B26AB264624FB2361240A23DE24BF27DC292E2BD729852A2828582592249B2446230B25442383243826C3262630A230162BE127FB256226A3279128462A302996283B27D22404228623352466252125F028D52855285129F52931276E26B3234D24632414240B249024B7242225D2259F230E235922B122B227F8262227142940264126C9250F257B23B5231F254F281F28522AD6299F2A202A84252C259B235A24462596261B26A8260128A330632E562AD4283928BD2646272D28C3285D27A926742750265D2509255D240E256C262C28FF29042A812A0B286E2731274F254B258F246D230E268E2723286C288F272F27752529244A247225CC27F828B629262A1429AD28612912268A2435263E272427EF28E42790275826A324C2213424B92422268F27CC27B8279A292B33A02EAF28082746255227B828AF274327E926A5269026472724278B26FE2356240725E127FA2AC7292529962859269D25572327252725D42567278B27B329262A3C2A2B27C223DF220B266E27F0275327EC285B2B772A0B280729C8251D240824D1264024AE252624D025DD23D7256F245D251325292564263E274E27A22857343D2C102AEA26BE2791286028BF28DB26A82614267A287D2718288D272E27B42428257C281628052B432A95271A27CA264223F124E324C0267027F327C429A4295E2874256824E9212325432620280D26332950293C291328042627240D2443244A24E524F62560226A24462350225C22042425245924A4245A277927042A6B34AC2EC327CB25D5266F27A327A2266B27EA25B4264628472A8F2A6727A82787266625FE227426D22756266625B024372400259E243823732345284A261828BC25AF25792569239F23F6233D2596250527432855289B26C7256426F523D024362584252124AF23CE23C024CB211A225A258224F9249425B1248C25C3274529B833692E5229D4282328C827A327D6257A264E277D281F2B9C2A06294E273F265626A3244A273E256D24FD249C2620259F24AE25AA248725CE240D2555261026E426FE250626632412259724C125B2289026D9285A2888267B259A23ED2517258D25C325892393230323A42458244825B029D227C5279D2995263C279427C9288C33632F632AB52A0F280B265D262C266A26EC27F8281C28C5299A29692969278C276228922539273C2495246C2490257625C824B6259B23BF2306251924B325E7247F251B26E625B726A7279C276B27D028E6292D291A2586243425A32686260E2761265B26DB23FA234C25772457279227F627462A592A212B742A2029EF2A3F342330E02A15294F281E27B526B426F326E528F1288329C329B428F126032A012A722ACE286426FE221622A022BF233C24F823CE24C323172466241D23D8242C256A23BC2461262328B9287D2B952AFA28042B96299E276325BF26AE278228C0298B290C28612782244926A228B028EA2845298A29E52AA12BB42A932AB32A2B354E30ED2A892A8C28A0265D248D26C9282528AD26C62667277F2502287A2B8F2A922ABC2924268B2425239B239425D82650260F2500249D25F72549244D25C7246B2465251F25E825662AC82BF42A752AB22A2029D02883278C28982892286A2A6627602649254C245E264F26202810299E298129312B252B192B0729472A7334E930282B0A2AE128D0276C260626A427592A4A28C0286427A826CB2751289E2954299B27E62516270A24502477275A2852282E28CE275B2795260F27B1257525FE23CD24C0253928022ACE2A5A2B4528FE261127B0276728862AE72915299528FF2862256723FA238E2450269B266A28AA2779273529C429AE2AF8295728BC31692F812A0E2A612B2D28A9262A27E12724288A265F27BB270827A028C029152AEB29252699254125B125D0275827C5299A2A262BAC2A3F29132B072A462986266F25D924882633270E273C297E2844258526882570256626872AEE296C28222892256924C8234F22DF25E1252E279727A12614256A258D255F2704275F298A33FA2E752904282F275228A427202708272B270827B2282729D2279A284C28D527C0275A27B2245427F82606270E28D52A162B732A262B4A29632A2F2A5E29B9255225F8244E26D12456272F26EA25B6250724D525B0257326262886290B28A327AB24EE22C3230724C125AD253426FF2517259024FA24AC257525FB279529A133BC2D0B29292543273D273D27F5266127F926AC265829D728252C6C29802832278925E6262C2715292A2932296429122AFF29BD28F0263228B228AC2805279A259725CA250826A02495253F24FE24BF238824F5247425FE26A0262E2710257F24A5254A23EC229725F1262427B22669259625CA240D26AC251427DB27602B3E35F92D4727D526522731274426A226AE269F27532773263229042A692BDF28CA276927D4267F29422AF22AB92AC42964291E282F287B267F269228F8273E2559252D26F4264228512665267B2579232E24F9254B26D9271D278C27872485250E2748261A25C423C326CF286129C52878281A27E028AB28832687266A29AC2A9E346A2F5A28A523FC242026D42868276B277128B226C629402A9E2BE5297629E1282228E9275329B52CE029782A1129C42A6A28ED27C127D326C3277626772599252F26A827F1285529C1287C281627E326B9270228D129D926B9269F25C02632283829A828A5279E277329F22A1A29C227472722297C2B88286F2607280D29C334AD2EF227CB25C4254F27572607273C2982286E298726BF29422A2B2AC727E724DA25022791286E280B271527FA29652A96299E29DF291827192732267125B9250D272629512A652A462A6528E327C5266C261B2915261626BF26DE26EF25AF27CC27E0288027D325312A7B2B532BAD29C028E828072A8A281928A127A12811337A2E672B41280E295F281425BB251C26F528DB287D2932290E280728CF29482A3D284E2898289E28B7253D27822784279F28062A592930273326822381256626652BBC2BC22B24287827A6283B2776248B257B271D264A27AE2706274227ED2599262226D42468272D291C292029D6287729572B142AD428872504284F2999337E307F2BF52A5B2A87299227DA277B27C526F927282737269127912773284329BC2AB629322B832968286A2820286C27E7275729A4286C273625BF240A24D227AF2A192D112BD928E22532287626CC258127B229472AA929C029C1281A287325A7259A24CC248B26972AA62AA8292829512A632BDD2B4228FC26BF2744294434F230372D552CE52C312A77290027E7271029E527CA2559252725C1265C288F29022AA92BCA298D29012A4F286A29F827D526252726273323E2238724F523E1262D2B7B2C1B2ADB270228D025FF25B6253729412B5329AC297F290B28B1275B26EF244624E32486260B28762625256925B0289629FE275B26FF266C27002912358B30162C952BFF2C6E2CBF289F29EA299F2C8B2A8428D0256826982529287428C5282129BD298429932B9929702802285C26DE25EA267123A52277237B245E279429792D8E2AA628F2289428E727FE257628BB292F2AC72954287C28A92A5228A027A6242623B923BB25D5261325AA2571256F26DF26A325BF2587288629A434EC2DA929AD2B8F2A272B6F29EF29122B552CD62B222A3728BB256C25D625FE26D028D627C926C2285327AE27A3281D272B2572269227E2230423C423AC239C266428EC29482C192848266C29EF29A12631264C29C5296F2789277628FC28252A6928C3259F25D4268327F225DA22EC238124802525252C244A256628722A9136472F7B294A2AB42AE029C2276527A92A762CBD2C722B4B270025B023A52522274928FF28AC27AD286727A4260F262F2884270228CC2ABC2760249524AD23662776284C2AC129AE26DB2402276A26DF252E271627BF262525CE25E7237726412783290E263627B128C327F126E725CF26B22653262D26B224CA26A2271A2A3D354330CE2AC929A829292A4129AB2ABF2C292D012C312AEE25F8229E26C426D627BA2A9B2A6E2AAE299929812563265928B2281329912A84285C25D723BD24782879296A2A1829C826F72519269C27F626C928FA262C2887252A24E424BD24192523267F26C6264428EE2AD3291B2AFD29362A5327EB27AB25C4259327802AE1353B31B52B762BFA2BC22A7329562A972B4D2E002A3D2827268325B0264929B82A582EE02C2D2B772B5A29A1284E29AF28092B8A2B92290629AB24B423D825182704292A2A5A2A79272C27FD277128A829982AB5287D286E276A268E2413259B267826F725132746288029B62AC42AC92B592B482A412A3125E6267A280B2BA233CE30FD2B172C372B822936299029BD2AC92A2629FE27DE26BF254626EB28BB2A1D2C1B2DD52B522BD82BFF2841292C2BBE2AE22A95298C279F246F23FB241825EC26F9267C27E42742293B2AC829EC288529E8298029762867294926D0259526D226C926BF273C2832295F286328922AC82C762B512BFD277627B128522BE5341B307D2C1E2B602C5E2B682AD929452A062B90281725DA24CE26DB29C52BC92BD42D0D2DE529222DA52A8B2AFC2A132A422A0D2A652791258C268C23C724252672262526AD266927EB2789294B283827EE270A28EB282829992825270128302A8529CE29422926290829AE2794273B29372AE82935281C25FE24A3266F2B2634A42F91294328782A872936297228182876275126A62654276927E627E829FB2ADF2B622CF52C552CF62BB62A822BCC2B842A5F27B325CB249525792630263F2825272128AA29BC26EC26A6289F28F426262688278D29AA2B9C2B4F28172704285F2BFA2A16293E2844288426F526BB272028E327AF2741245A25932792290932CD2FEC29C22A042B572EB028122984283126E825C7266328362875276329012A692B3D2C352DE62BBA2A182ACE29E12A162989265125E2269727FF272A29D029272A7429B62AA1287F27722768283A28DB28D427F6297A29AA29022892260F26BA270C29DB2759273928A627BB276F28F027C128ED271627D6251A257E298633462ED629682A6E2C432D302D1A2A9E293028DE2732297A2AB629D629642707288628742BD12BC42AB4277F27A02802299126CF286A273F2545295029A129F12BC62B702BD82901270D260B27FA29A129C729222967284D28F128F4263125EF2589248D24E025FB253A270128DC271D293729392864275A242323A2250E2AC134B72FFD2A892BAD2CE12D882DC9295D2888290E293B2A782BC2292528CB28A327A4269E2862278C2764287D2762284C28F126F32553258A26682758288329CD296429C92A4F2A1127EB267428D428A62A1C298F294D272428F7255B26AB242124D9244725C7265F278729EE271228F6289927C8266E26B3241C25C425E6287C331A30E22A4E2A832B5E2C582BAE2A252AEE292B2BD92A1A2B5E2B112B8229C226F12409264D263327E124BE2568275F2707284326CD24DB2466257523A22735286729602A942A0A2964271D27C6299829A927CF26AC24E5254F27B624752602260E252B2538271627942A2D2A89285E28F3260A260027BC2460250428B428A63292304E2C932A422A142C3D2CD12B1B2AAD2A4D2A4D2B9229C8280A29D5288226EC2506288C242E25D5247324F5267829D028C126DE24C62587258E24AE2690289B29AE28EA281629BF278E276F29C2272926B0240326DE26FF244D278D272F272A25F72639265C28952ABD296B286B29802805266D27D125F625A3264628383397315F2BAF29DF299A29C72BD029E629B82AE12B992A2C2A68289727B0268926B9275F27CF2748272A253F255F29F42A1529A627F02679264B26BC26AE2579259F26E6269928EE2665262C268A2560254425702645266D274D285F276C28BC295F298C29DF280F290B2AD329012ABD29F728F82898288927E427FF27452997332F32162C76287D27EF272729F928BC277A29CC2A5B2C3B29DF261F2666279626F0299128B829EF27E72478275A29512D0C2C1B297C26F726F228D026C6256A24D6244B25762888251F258F2391234823B424C127D7274F29102B712AB129042B012A47277827C5276A2AA62A74298F2A282BD4295D28BB274F2AC32BDB2C32347C30B52B2828C8277329E32882260C28452B642B0F2A0A29C92688272829682B0E2AAE2ABE2A752A92286E27D929B82A5D2BF22A402946291729652830283A26B3257525F6251F264D24AB2365239D241E25A32858298628142A022C452A9B2894259E275026FB28142A812AEC29F629AF2A142BC1296128962A572B0A2C44354330672A4D298A28D4292D2BAE2C7D2B412BA329112AB8282128A928232B922A012C2C2EDA2A922AEA265D28362A682C9428782629271B279728FE2AC72A9827642406251D2594250225E0235924E325BC28912871290F2A9F2A232C4D29F32805274727EF250F27F229332BFF2A572B7E292328BC27CD27932B8C29EC2BEA32C02F182B452A0A2B9D2B692DD02C9E2B392C522BFB2A7F28FB278F2AF42BFC2A242AEB2A6A2AE52B57273A27702A792C9628AC28EC26D827B42AAC295F2A7D2873262F27D725CA26FD26B227532747272E289029B128D626DF26F4266F26BC252F25B025D225EF25CA2B252C8B2BAC2AAF280A277725EA243127EA2811290033B82E8C296A2B412B622DAA2C272C462A722CD42C4B2B112A742A002A282C7E2B552A112C312ADC2B7729772701293229B129CD26AA277527CD296C295F2A9E27B1250A271C28CD28622A3F2ACF2AF829B4288D279525C1262A28DD2525241C24B623AE240025DD268228F729082C812A7D261324EA25652446266026D5275E340632A92922299629C82B422B982AEB2CFD2BAC2A4C2BEA28DC255D26F428292C9D2A2B2CA52C242C472794264428F229E12938295A27892705281529FD29FA27E82717271627D8283029702A95293C283428D026F625AA2837279F261323C522CA2418242F2482265D295A29FB2A672BEF260123EB2495237C2644276A284A32A7318E2CAD285D29FF2A192BC32A942CD42B302C7D2B782AD8245926A0265328AF28EA28D529092A4E2AB8265828F428A22893293F2C83283829C02AE02AF528C127FB266C27212A5F2A0E294D28AB26AC26A0270C276528B628F9289125CC249526BA2627273027BB279D27C327DE288826AE2477266A2542270429A0284A3151339B2DC92B602913272D298029472C3E2E972B102B66291528F224132614255C27D726942611294C279D2716275C291A296D2AD529352B6F2AC12B802CAF2924288828F6265D2AFA2AF029F2264025CA255826B62594276A28CE286127F42595264728C42A8427C3276C267925F72789264E25C9276F266227AB2A1A2C27334732A52DC12BA82AA32798273C29F32D862B7129982ACB268A271C277D288526E926A0251A250A26AB27FD248528EE28372B582B3F2A13292429E129122CDF2A74288428F727582A262CE729622A31271A26A1282B28662857290E29B627FC2646276C28D72AB42A9E28902607275C269F230C2214260726112A862B3F2CF1331D33E82BF92BDB29B3282A263E2A002B032BC22AE128ED25EF262E28E92936282A275026592511267925C826ED275F2A082B1E2A992AF9295E29C3288A29C128C9276E27A627F4289329242987290627BA287729852B9429932A4A28D0271E29A6291E2A3B2CDF29222B56281E27F5244E23E02499264B273E2B0B2BA22BFA333631872BD32AFE28C1253A26872718297A29952801288726F7270E2717299D280A28E42756280F28EA2629276D2A062C732B232A3B2BCA289C2A1C2A1F2B9C28AC256526A728C827BD29CF2AE7281227F527E3289429292A212909283729092A8C2A7B2A972A5829FA2A032AE229E02751234D258D24F424D7273B28C02A7D363430362A862A75288C26FC257525B426D927D226BE278D26572765289229C1285C29C82B992BDA2ACF278C2702290A2C5C2F9A2CFB284F260128DD299A295429932629276A261428FC28162A532A1329A4282A2750292E2880277027CA27FB2A162B7B284B2810263829EC281326542722252B2432241E25FA26DA29E72C7C366D2F812964285B27AC263326DE23C4254A262C276B29152ACC2A6C28D327AE2A492C312D372D4B2BC328BD284E294A2CAB2C2D2B5D2A9F28F625EA28F326882561259B263326E526FA270D28F827D127E1278627672543268D2462256128292AA72BAE2859268A25112894273527FF2506266F254325FC233028CD2B3E2E1037962D8329202737279026F725C725AF25522745273E2A382BB22C992BEB2A6529602B5A2CC52C222C6A288428E72722296329AC2A982888260E2557262427612762279E26D927D72852284D28D52953282E2805276E26F52517263E247925E027492BF32BDF2AC6274128452742275C281D283F284C28822723290F2AE72C3435DD2D4429542A5A28EE284729552707252526A728752BE22C342CF42C052AB62A462A012CCB2B2A2D52282828882597266626C4287E2623259B25B026E9278929CF28B627412AE5283A2A1F2B912BF328A8296A296127BF25732796236024A1269429EC2AE329F12944293A296129822A612BCA282C2CAF277227622A372BC4337D2F5F270128DC28B32A892C8C288026DD27AD27A42A8A2C872C922BB32A26271A2A2C2DFA2C3F2BFD2B99283224B9257126B025BD271A267025AE258F289A29E02A262AE2284B293A2A532B732C8A296A29252AA629FE27BB26D9256623EF2453254E27EA250F2620281926D1266F2BBB2A562A022C752985270C292227BD32AF2FA02AC0271F29F22ADF298D294128F129652BDB29E22ADB2A1A2B4A2A372A8B2A6F2C052D632DDF2A952A3428EB248425EC28F8283A2964292C283C28F029882B4C2A002999289C2A4D2C5E2A7E2A4F2BFB29782BAA29A029E7255524FF245B26D5235524E8264927562528254D281B298E2C5C2B2B2A3B28012976293733CD2F4A2C8C2ADD2B48297429D629652AC72B022A1F2DBF286B2AA2291E2AF228012B822BE12C7C2B5B294A28C8258627B2275C29D82B802A252B302AE427A928AF29242B1929EC28DF2BD62AEB28A92659270F29E72AAA2A48295C27F9263926DD28B0269D254225072612263224812637262D283A29C42925291B28092AFA33542FE62C722B8B2B352CA6297E27332AB42A742C932B5A28D828CF27CE29962ACC2BC52C7D2B1B2C322A8229E929CC286827C828F729842B0F2B412A3B293A299429A12A602C452B0B2A292B6329CD25F82464294B28F429982C772BC32A8F2A4229A6284F27F724FD2850278226E1260827C52757280C2887270B2A1C2C9235E0300E2D832C282C722A172917278729882A562B3F2BAD2AC326E9266E28842AA32CBE2C2C2BBF2CD02B252A432C3728BD26CD281F2BD429622A592B6729DA274529432A042B652AD12A1B2B292A79298628AC289B29A12AE9298629732AC5288D28622AB628812665268B2656298B27ED269D29AE2BFE28E1275B293C2C92337E32322D6B2C3F2AE1299B295D288F28972AEB2AE029F0265626AB26B527082AF92A9F2C762B622DCD2CA22B242B1F2B612766264B28AF29712BBA2AC629652717294C2A1D2AC02B592B53290A2A50288C29A12BA02824296428E325C028AE285029D22926285B284B2799294529B127FA27662A0C2B752AD92A462A2A2B6A34C931C62D362CC02A8D2A172A8129FB28C12B622BE32A81297D278727E429FB27DE281E2CD42A0D2A302A96299F2A6D2AEF283B27A2284229EB2B002C902AC12A412A462BD32B89287D283129DA283C271D2A3A2ACD277D24D7261B269B259D2802290F29102A3D2A3F2B872988298D2A2B28FD29892BE72A9A291F2A552B28338931412D2B2DF32CCD2AF4298D29592B542DCF2C6A2A542B15282329592C062B282C3D2A3A2ABB29D628E2289429312ABD28FD264C28AE28E62B452B702A9628B1299A29952A14287D27A026B526AE267729E228EF26AB2331261225A5262028812886286229592A6D2C782AD92AFB282D277928FE28E7295D2845284929843368346A2DD72C632D112CD92A87280E2A662D562C4D2A372B7429382BD32C8E2D142C4F2B602AE52724280828A82A292C422A9B287B277E29192B402B0C28D12630291329A02BB0286A274A27132620264B27562816240C249223F7274127BD27632A5029A12BDD2BBC2AAC2AFE284628222834285D29292A542A4327B028FC33BD314F2C342B042B9A2B80297829332A852AF7297D2A09292029F628F32AE12C4D2A0C295529A6285B26D026A827282AB7297D295D28EE274729C62735279928E626512815294328E7280B27B82513265C288F260F272425122674262E291F2ADD2A922A262A032BE62AB42A382A912AB227D327A1294D2922290427D627F2336131202D2A2AB52B002A4828C6297B2AFB2A3A2AC6281028D927962A6C2BCA2A342A5E2AC0298A2903277D2922294F2A67290A2BD9286228662985294D274F2735271926B4273127A5270629B728292866285529742959271F2766287C2A2F2DC22CCE2ADF2A1C2B9F2C412B1B2BF42989293F29402908278827DD262C28CC334E31942C282CB42BC129F8277528EB28272C172BF42A9A28D828102C762A782B462A02295C29AA2BE829A528F229B12B642AF7292C2855285529D127C2280D29C4294629542983278A27B8290F2ACF29342A842AE1297A274A286228D528D42B3D2B832BC829F328232DA12BEA2C1C2A43277928FE2845271D28A22875297B33932F832BEE2A9A293F2ABE27E3251929622C0A2CD62A77288D289F29542BC429A0282728BB29052D662A6B2AD229BA2A7828132A922A3A29B52A3529042AE22A4F2BAC2A33291728D326FB28962BD12AE729322C702AA32999293F29B92A342B9C2BB429CE29DD28C12BE22C8D2BE62AE22623278328822649280F2B0F2DFC34052F552CBD2A252A4E29D627F427B32A362C7B2B1C2AC8298729F729EF281829B026B52798286229C82A392C5B2B4E298D29642A7A2BC22ADA29DD292F28202A962D122BB82A2F270126FC27AB2AD32AD22B912C192B8F2C9F2A4A285F2AB82C2F2D882B142AD52A212C102CC72AED2A7D2781254F257A270E287E2A9D2DF434562FE52A0B2B002B1E2AB529A029DB29EC2C6C2B612B272A992A642C7D2C372BDB298B28DF29892A762B582B772AFA29EB29DF2B6E2C5B2A482A0F287B29F92A472C082C192A0827642505288E29D428B12A302BF32B3E2CC02AC4296E29432B632BD12C122DB5285D2A7B2B9D28352872242C25A2245923E726892AD62B9534252E4B2AFA29802B4F2B702B712BA72B752B852D762BA82B972C512DE12C342CBD2A0E29DF29FA2BF92B3D2D852B792B842B992D2E2C3C291B2AC9288A285C2727299C2B3A2C4C2967270329AF27F929982B902B3C29E429F32A9D29DC28B3290D2C732D302A822A9729472A2A29D4276A25DE253D25E32540297D2B332CAE34DA2E2B29F0299229C42B8D2CDC2A472DCE2D3A2DBF2C422CDF2CEA2CAF2BCB2D99285629282B552CC32BDA296E2BDA2ACB28D229012AC32945296A2738288A28DD29FC2ABF2AD229E7280827F22670284329062AF5275128462880282428682733287F29C429BA29352A8729D02A7D2AF127892502250F262328AE2AEF2CE935762F372AA528522A5F2CE12BF72BD32CC32C962EC52D962C7B2A9E2B5C2AFF2D762B342AC329302B032BE32AA02A302C8B272D297F293D289E2711272E25F727282AB72BFD2AB32AE529B729502A7A298C299429F227B9276E2756251B261426E12567285928252A032BBC298E2CDF2AA4281728FB2662247E27DF285E2DC835F0307F2BBC28EA29062C092C0F2B542BD62C732D9E2D0B2D3F2B0429C329CF29B828802880282029DA2AEE2A8A2B2F2AAF2A602A48295929D3285625B92320270429C0283A275926EA26C027552981287B29F12998294427AA26C7250624B72669278327A3286A2AEB2AE72A892B572C642B3F29D0286727F028FA2AF72D9D362031EF2BB12A572A052C682DCC2A622AA12A042C162CA52BB42900286228CB2761291D29EE28A1282E2A472985281629812726293E2AA628A629512625268C242027E626D127A325C325D8265229232892271F292F29C926642ACA26F926E0278B286A2A902AFF2849295529E62A0B2B1B290E299E272727B427EA2A7C2D25358230042C01299C29C12B262DF02C7F2ACB2A6A2B612BB42A9228D2262B27DD287129322B8729E32AF32BDA293D295F28A6289B2A142CC829AC2A0C29B6285A2809286928B2286F2724278727AF2AF328DB266F279B266F29782910291D2856292F2B582C222A6A2873279328A4298D2A8A280A28B32678250E28C92A552DA6353932492A632AAF2A3B2A892AF5292F2B1D29572AF42A012A3928AC274D28D228742AAE2A742BDF2B5A2C822AE42A8029B82A6D2AB02B8B29162B06291F285D2891297C2B07292429B628632A762B392B3D2B61271127A62624292828A426D828DE29CD2B98290A28AF2765268E29962A2D2A5F288225EB23A7272328062B4236A530FD2B132AE8296429B0299E28DE28A728DE271B2969263B2823286128852AC52AB82B3E2B912D242BB12A7B2BF7293E2AE12A7F2B312CFE2A5E2A092BBD2A702A7B2B1F2A0C2898289928632A342A30290A283C267D24DC28AD266228F4281329662AFD28BC2654275027AA288C2ACF294F28D928AE2426248A25B728FA319A31A12B982A3C2A2D2B2B2BEB28DA2A4B2AA12A372A3528C1262D28E828232C9B2B922AB52BC02B322C0F2CA72BEB2A6B29842BA22A0C2D8A2C672C422CAD2B6B2BA92A9A2A6F2829268E29CE2B8B28192832296827D725A9259524FE248B25DB27EF27F2273B281D27ED263C287F2B5A2A2B285B26E524A724D927A1292133A42EB32A2D28B629552A7B2AF52877299329782B742C6B2953282426FE28CF29052BAE29A82ADF2A012B642ACF2B992BEE29872A4128DE29362B792B822C1F2CCF293D2AA329542A952A1C2A72285027B1288828F325A825392582244C25C124B526B724C826D826D4253227DE260D28F0296B29F42678243E26D4272D291335AA30E8290E29FE29FF2B172A1729A927D529382B842B822A8028AC265D274B295E28D9298D28FC28D228EC26442AA7291C292E2831278C28532A642C8A2A462A312BA32B9F2AAD2A372ADB2AD12A89261529E0291C2AF7299528E92468263C25C52621251C266525C227DD27B227F727E7286C287A27112680264E283F2B57352131492B9B285F29342B4B2B2C2A5529462A862BA62A35291327D926ED26B9288E2961297129B928FA26BC253927DF27E4277928DB248426BA28CF29D228082801295529DC29B4288F280128E2276A276928AC2A2B2DF22802280D280727DD284B288A26C7269C239926E22824292A2AE12837286327322522274729252BB1357431112C9928B028212BCD2B0B2AAC2ABF2BCE2A222CCA2A64281927D029AB2AC9294F2C272B4729D428932710282329BE279A288928BE27F7291928802620278427A527E72766254B27CE268226F4254129352B6A2BAD2998297B278A2AE82BA92941285D26682664286F2697296E2B6B2B22298327F7262C281E280C2C8236B431F22A142A012B372D632DEF2C4C2C452A782B2F2A7B2AA6278028502B742BD82BB72CC129D52A6729F6279C2632287428A728B728682A202AD5291528FE24A726C4267025DD24D727BB273D27DC27AE289C2C402A00282427FC26762B572C4E2B21281D257C25C627E3273D2A052D572D7C2BE2299E2713271728F8291B354531D62ABE2BA12BC32C902B4C2CDF2C012B3D2BD62B312AFA27A6277429AE2B662C062D652B902BC22A552AB12A122CE1299A2AC629B82A9D2BCE282D282E27CB255F2529273C283529F728BF27AF281827062C682AA927A92713277B277327BC278C28AE291327A02785272C29692AA52B672B3B299E276328CF29412D77364C32302B0B29812A9B2B002C982CE42BD32B4A2BE129DA2924282327FF288A2B022CBD2B892C5E2B1F2BEA294F2BFC2A2B2B042A5C2A942A8F2BA02A122AEF292827E7266028DF25B92834278327662637262F285B2831285D27EA27F829BC285F28C7290A2B4629C7266A252A267D27922768292729CC26C2268A28702A9833DA32012BF128FB276E29AE2A272D542AFA2AE02B692B612A4627A825F628082A702ACB2BEE2A242CE9298929162B052BE129F5290129E42A172BD629BD2AE22979281E26A7278A273C2936285D281A271C2714289B26FA25752705263C29FC29522BC52A402BC128C7281525E024C52796274A275827CF265A25F627E32AD4327F32292EC82BEB2AB82AA529FB2C7C2AAF2C372C0B294729BC29F227B62684285029282B592BFA2B7C2B89294629432A002A4E2A362A3A2A0F2A6A2B6F2AB82992285127EC276B27E82AEB2A0929CE267C2A622BCA280A267E25AD25C429962BBA2CF82BCA2A172CB129B126EB25D526D427C5280128C925A1254D26FB2798328432EA2EC52CE12959293528BF28192A852AA92BA02B802A592A8A285C276628812890289D2BFD293C2BBA29F8294D28B126EE29C127DE27BB29B52BAC299F2A652AC52A32291F266D286C27EF298427AC28D42AC929D026952644276928712A1A2AE52BF12C462A1E2C89296F296C29302B502700272D261D261928E527D6329532062F432C592BA029D4279527B62A5F2C782BD22C9F2C8D29D128E9288F2847289E2AA32B4F2BEE28A027C326BF25A6277F2A252BE02AEE2BD62BF22A452BA12A2D2ACF28CB25362637284B283C27D928BD2A1A2A8C2A6F286025CA279828D62B442AAB2BA82B782B3C2AA72A6C2A37285A29F627852585250727F8298B33D131522EFE2A192B852A822A5B28122A212B9C2A3B2AB12A74292B287F276D28D1285F2B532C752D742A9B29042BB9285428DF28D52ACC2B452A442A002AD22ACA29992A46296E27CE266427C027152830288329CD2A752BB82AF6283A271627752A822BB22B902DC52C4A2B6F292329282A212B042A56279925F8250D288431F834BA2C9529C229EC2A6B297B2A2C2945289D2A1E2964284E2664248F269526A027EE2BE82AEA2B272B0A2B812AFF29B427B7272D2963281329A7289C27A4294E2A29290727FC26C826A027FD26FF2507276029DA2A0B2BC52C3E2A7229C828BE29B829642B1A2B6E2DF12A0A291A294728E429312A63271D274C256B261D32A532832E3C2AF42A072A3F2C082B372BB12AEA29A02A3F28E0251627CF250927F526322A832BFC2AC7299D2BA72CDB2983284228D927A028C32AFC27C5285D27522ADF2933293C296D27FD2739282D26F82531270B2894298A2B242BD4298D29552A392A2C2A742B6A2C932B702ABE29C0278A28CB27CD272C28232763289F314631DA2BAA2BD828052AEC2BD32A4C2B9E29292A2B2A432A98289F262427F227C9266829C129EB294B2A4C2A502AFF29092BAF27E626E22780287E281A28F3272F299E297B294A273B26082A5529E8262727F9289D270029D729682A3D2B852A8627BA266B28572A3D2A492AB6284929B728C1252C2653275126632860293C310F303F2C122B522C262D322D302BCB2C0C2C562C112B132C0A2B6028B8272B287328AB28B1297E2A0B2832280328BE292928B6275F268527E029872A0229382B3A2BC5283C286326BD264329002BC8284C2AFD2BC228D62543274527B227EC26D12741270627D127AA287D29D029252A512A942795266C264028F727192742316330A52A8D2AE92C232BE92DA32CC12B682C5E2DB12D3C2CF02BB328C5273A2703296D2AAA2BD42AB12A9D28F829372A742993284526F328512AF52A7729AB29B12BC12B6E28762602278929E328AC272B2B8B2A082BB9284529C32A7328932699284829362B7029D02A0E2B832BFE2C7C2A8428F2252D250029B4299E286031C0302C2CFB2AF628F829702A7C2E3F2D752B132CA72CE92BE429F42ABF299029BE29302BBD2BA32CD22AF22ABA2A3F2BE22A1429FD2675284C2AC42AE12AD32A642C242C242A94273F27D4293F2A2729D72A662CE52A0A2AAD2A652A7529132619287529EF29182A282B232C3C2A232C91299B28B72520250E28DE28CC29BD325031662CAC2BAC2B2B2B072CD32CED2B5E2C592DA52CE62C0C2C9C2A20297728DE28792CDB2CCC2BAE2A75297D2E6F2C1C2995264F26C625AC28B62A0E2B9B2C272B4E2AF629AB27D72652288E29D329A52C6E2B1B2B4A2BD02ACD29BC2A56295228F8282F2B5F290B2B552A1E2B0B2D842AAE294C27BF252827182A982B01326933D62D4A2CC52C9B2CE12B9C2CE32A362D492DFE2B492CA02A922A4C29B6274728CA29682C312B8A2B5929AA280829D6297E294D26F6268526DF278729BA2BEA29DC29DA2893250728AD281B29172ACA2B652C98296C2824277E287D28D429A429A929E2290A2AFD294D29A529982B602B65286C257B259D26D828312B1F346B32F92CCB2A242D052D932C6D2C402C0F2B312A142B5D2B4E2A9E2A172990256F262728B92C8D2B17292A2706277D280B29CE28AA280527EE23D6243E270B29262A8B29C5269225D526C02AFC290A2AA52A612A8F29E928172859295C2C0A2B5529C428DD2AD129802A3D29C52A542BB32AD5297B2787255625A9294A2C70352031602CD7298D28B42BC12DEF2C832C792B302BD62A382C872A9B28E3279627BF265529442C542B5429E5297A274A299F2A752ADA294C2856254F251C277A29482B9129DC2763269726A828272A3B2A192B5C2A9D297A29C92AD929792BEC2A51294F296829ED282029DC2AA829012B722A7A2BB227B124F227DD28D62C4A36EB31912BB4285D28E529F32B582D912B552B342BFA29DB29132AA02961290E2922289728192C912C862BB72A36296928EA28AB2A6D2B3329AE276325A5261D28F52999298E28C926C827B529C32B072A0A2AF82A6C2A102A422CDD2A892A8729AD29F4279E286F271F29AE2831287F29B429A62A3C291B27BE27D22ACA2DB6357A33ED2C8A2A9B28392B892C632CEB2DE42BDB2C792BB32A2A29D427B826E42730294C2CFC2DA92E8E2BB82A7C2C982BB32ADA2AE42A94291629F8252B26CB29D22A102AD1288027D1282529C229B229C22AAF2B7D294F2AD22A072B112B0A29F0278B2687278528FB295A29C7297F29E42800293328B725AC27DC2A172C693576328D2BEC2ACC277728712A232CED2BB02C492C4A2A9E28FD25BA26E026322778290E2C122BFE2B362A002A702A5229292BBB2AA929BD28D72730279E25A82751292A28662718287028322807289129492B572C432A18297E2ABC2AD628EE274A2684267327212730299E289626512637272626362657263F2664286C2AE034E7327B2D952B0E299529DF29352B992A3C2C2D2AD329D32820263D24A42543284E29102BF629B92A09290728C6297929422AB42B1629D627A4280C2680245324F527F627F72566266F27A628EB26FA285B2C5F2BB52A9928AF29B629BF288328C2262E26F2266E267A289C2836259B255026D0242C27BE2569265428602A0134C530412D94299D29D028B0269A289329B82A842918293B260E25A1238523A1263328282A462B742A7D28962772272929CD280728D827B427532813274725FD242227A42773266B2552260C283C274C263F2B422DA92A95287F28C326C32524283529C7274D27CE264628FB26CC252F251425DA24FD25B025BB26DB2603292F336230192CD12ABD26D826D325A928162BAE2A822A8E293827012543246C2639268427C628F42CA22A952929274B2848297728E5261527D826E625ED239422812304257124CE232E255325D6240F277926582BF22A8E2A9A29B4283C268C258E26CB2653271229A6282B2A7428C32621240C25DA254A27C426782723279A29553333317D2BF329872A26281627DC261A2C422CD42CE62A132A5726E825A029682AFD28AB299D29592C342AD526BF29CD2B9B2B572AFA277B26A6260A25C42233231722ED22B623D72237238125F924D2247C285A280B2AE22B882A0E27BA243E2738276428DC2712283A29E02711274327A6232F24E524B6237826A7282A2B1034E131E42B982CF929C0296A288A28E12B812CE02AC42AAB289427B1264829452C4B296429F228EB2A1A2A8B28B3288C283328862A6329AF277526D72514235E212822D524DF224E235B2408229325FD2493270628B9281829A128DA2610254526AE278926142866271F2A3928A1277D24DE248123ED247323FD259028BC29D133C530D92C042B9E2A73274E28E627E028B02B142CDB2B2B28F1251C27F4273F29132B482AF529392916277427DD282729C229FD2A972893267A267123EB22C421F520CC218D228F229A234423192352247F260728082821283828E327482667272428E825EB2405266E295029BE27682598248323E023C422E92367269C2992340231A92C972BAE287826A8251C27272A9A2B062AC52A5F284726EC23A925D027AB27C329522978281126EE26F0253129722956293C279A26D5257824F123F22225225E221522612391226F244524C624AB23BA252F252126052792258C254A26FC25C2240025E425A5276826E62555258324F822B622AF22EB2329252826AF312C2F722B742A522AA6286D261826BD26FA29082993282227A525FA2469268928772893278726D925E924AD245126E62464251926A7251825F5243C24002371223122D91F692031204024E9227324C623BE2318251C238D239C24002673240C2657252C24DC236324CC238C238F2665265824E0224022C121DE227C23B0269831902FD52ADA2BCD2918293A28A1267026EA272228E727FE274A25E423302625279B26E925F825BA234F240E23F4236D2588254126D9275425E82487245F22F822D921582094216021A02313244D245F23DF234523C722F42465268E2458242025052635263823FB23D2243424A323C4245523C822DE22BC210623D423A126C230D12F6629F029402A942A182A6C28A2255D2638271D2667261B25A9231024A024BC24ED243525962391233F233123EB242C2428254D246C26FE24602481242723D422D8202F222F235524AB2496242E2550252E23242328256923D723A4238C224D25412518256723B623C4237A232C25F922B422CC2364237422A824E1246130DE2C2029592A362A242A942A1F273126F3251C2610277A25A226D3246725BA24B925C0259425F823FB23A7226624F22669231125462582261E247624CC245A230C23A822FC216321FA2239246824FD231D266D233923D02299231F227222E0247B237A248423C722E523D02310238E23AA23B1215722321F5021B122B1240B30372DC529EE27192A052A7529F02815266B27C623FA2372252D26EF2442246B26092568268126D4262924BA23D923D8256D2402249D23CA24AB25882403231023D322C423B022F52148222F2358249223DE238B249923F4222D242922E1226D2294244D21E5219021CD225521782301229C24C321BD210C23F6215523DA24BD2E1E2DB72834268A271028A1277026E62557266624D723DC231625572184229923A425942712253A278D247323132454229022E821D4225521E424D02240255523F923B522CF22BF21DB23C322EB221C236B224D22DF22F121C2236F21452383227C237022D2226B22FE230521EC22BE229422D8212422AB22EB23DB23B224E42F702F972838251C2608288B289D25AF25C325FB241F25F6231823E723C223F8237C25BF268C25C826F22349239E236824CB22EE229C213B2163225F232522DC23722318213D21A822F421E623C323EF20E823CC231A221A20A022F422E2246E22FF22EF21C3229C218E233523CA2152218E2325220E232D2151226321A823A62EFC2D7D278225472552259A2693278E25A42620259F2340244A243422BF231323EC23562402250D2413235D245824EA2334224E23F1213A214222EF23CC22CF21A0229C210F22D121CB225F23B82270213A21E9219D2187202521EC212D236022A621AE22ED21EB20562113213A205A22042285229F22ED206D22D4227C243630362DE1261A25E5240125F6260B250226DA260D25C024722350238A22A3226123F82234236525DC240F221822D6221923BD212423FE20F32227227D2093229421FE208A21C5227E20C222CF22C2209F21D62179223421D5219522FF2176219520702157218021F81F2721C32034218221E8200F21BA22DC20B02291227024762EAC2D1627DA235825C525BF25E2257D26062790232A238E246A22FD228F227F228F233D237E230424B4226822A023E922CB209E222122D120AD20D4212F221422E121212113200C210F22BF2119220922AF22072225219420042188218820A821302162200E20A720DA201821FD209623FE2046217B215121E0216A221825082F5B2CB32776266B23A324FC24AF249A24CD25DD258424AD246A22DB228D228224B4226C239C241323892333227923CD21C72143234721F6211B2217201220CC1F1221501F6F20E6209B20BC209D21542140227C2105201F229921F620DD204321D222FD206C2117223322CE203821CC20942187222720A8211E22F3210823FF2F5F2E2728AB249023672383236A244E24F824F7242223F82298217621FA22F31FAA21B5225C223723CB23FE225C22EB20AB21FE225F23AB20222227202821C521D420F81FF3209A1ED120BD203D203320AE213C221721FD207A208D2054209821CA21ED218421EC20722299202F2107238920AC203A218E1F6F20C42344245A30EB2C4527122418230B2388238C228A253D2252235C2331231D22FD22B9222321D620B7224821E222FC203D21C2219121A9206A2260220F21B6227A21B921E920CA207F20CD1FBA1E3F20CF20212073208B20352255219720E91F2C208020F420B321F4201321CD1FE0216F1FD120E41FFC1F622145217E20632219220B24A72EAC2C2925AE23D824A92208237D227D23F3238222F724BF23412201239822262230236F21D122EB237A22E420EF21A32227204B21FD20A8200D212E203521831F5B20A820C021671FA81F2C202E21EB1FDE201B232C202B202E221821EB216D21C61F201F4B207920A7217521E41F12216821C51F7020FA1F1E20A621EB224D2C1F2D6D28382407248524C021DA22A522BA2387242A231423F823502216218223B4238323FA22192499211B2088213022BB203C209321ED2101205E219E207A1FB92195211A208D1E9721F21F6C21911FE421EC210D202620B321B01F1D23AB1F8C22D0212921C21F30219A20B1203C22DA201E1F3B203020B2211D222024762F602E65275B249123242529229222A1211223F02295233B226122D020A3216A21C621552216210822A321CC202A21A5224F22B5204720191E6E20B81FB8214B207B201020A62053207B1E2E229420871FAD20F220F51EBF2022219D20B31FC42096201D202321B21E0121EB20BD203A20882061203721EB202F21E9215023A82FDA2C2427ED252123D0222922AC22FD21CE2381226C22BA22AE213B2346236F20AD2222226322492356223321D22126213321C721951FF41EB2213C2100212220EE200922ED1F151F6A1E4020CD1EFB1F0721E0205B1E961F8921CC204E21F91E6B217421D82042218E205C203320E62135206A20B0209A1E0F22A9221E25D52DD82B72265224A2238E2221238323BD22EF22B1213F220A2392210821F821C82219229C200821FE217222F020C920FF20CE218620EF1EF61FF61E2921E01FD320592001200121121F281E6D1F711F9C1F7520201F421F561F29200D21E01F2422C320411F6B20AC211022ED1EBD1F84201E20591FCB20C71FE52104231C24852DCD2B38278A2282229621F320062329229B22DD209022FD1F0021D2205C214A2127224921C021CC216822931FC020FF23B8226E212420801F5B20DD1FE5216622A520D31F4821261E371F8C1F1D1FFC1F71202F219921741F902178200320721FE8218D201D1F7C1F32212621921FA820FB1E2120F421D120352151233A25182EE72BC02526246A22FD22772243219A212022E920EC2184206D210A20BD203520DA1FE1207B20D020381FBB1ED51F8021E120BF1F9C1E11206C205720DF202A214121C020831FB71E1F201720F41FFE1F941F5B20C21E2820EB205820FE1E11207C205F1F6B206D200421F51EFF1ECF1F2421BF1E6520A81F80207420EC23062F732BAB24CF22DC221822F12011219B225A203321C72066207720771FCD1FFE209520C01FC721ED21BE20041F5921C3204A20CC201C211C200A20B91F0D1F2D21DD20BF21FE1E0F1F171E5A200D1F761F9F1FE61F1E1F701F2D213E1F12203320FF1F3520182014200C20C8206221351E301FFC1F64203021B5211C237F248A2DB42B3425A6218E22B6226B213F222122B6212323C522432085204E200B215F21702123227B21F420EF213A1FFC21F21E41218621AF1EF2209F1F3620CB1F9521202176208C205820621E41208320CF1F2422AE212B1EE2202E206A20B22019228B206E1E7C215620F91E61205A1FBB1FEB1FC7208D21A61F5722C7224F25202E9B2BB2254422A4213A2270229021B223B6215221CF209D207A1F701F1F2128229720392140230020DC21AD2044210D20692153221B204520A71F3D203D1FCE200620D01DE41FFD1E8C1E9A203A20091F30215A208D20041F3F21A61FA82245202220DB1F4920ED1E1821FF1DF61F0C21A421C71F83200421402255200323AB2E3F2C8225A421D422A7228622E321842316237F21A021D621B820491F6D2155213C22F521EB21B11FEA21712036213E20E81F1C2214207A1EFD1FF121B81FDC1FE01EC91DAB1FD61EEA1DDC1F911FB21E00218B21891FFE1F2D1FBC1E331FA41F1921B61F6F1F401ED61FCA1FC91F31218D204A21DF1F801FC12064237C246F309E2C6A259123C3207823D5232322C5224923E722DD2317220E217620E2215D20F120BE22A221BF1F2421DF1F07216720F61F5522F120D61FDC20EB1EED1E6B1F2A1E8B1E801DC31E8B1FBC1E6A1F65215E215820B820D11DEF1C991EEE1E8120CC1F5420F71FD31EC31F551F911FF420A41FA7222221761F0520F3227424412E + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 D228EF2224238421F82154217F20052179218E20B41F551F2A1F9C1E8120051FF91D73202E20431EA31F551F2721601F0020851F76200F20841F501F6321BC1F431ECB1E491E561C0B1D571E1D1E771ED61D941F471E071ED11EEB1D7E1D941E7B1F661F8D1F321DA41E501E481DCF1FC11D66200920C31FC51EB4210C23412E642CD924D621B0238323D3217F22EE221623E52022220B21E31F67203920DE1F8A1FE21F0D20561F0620D92069217A22D11F9B21DC1FAC2027208D205121511FA51F6720CE1F4720AD1ECF20E71EC61EF7202B20161FD11E701F33215E203D1FAC1FAD1F2120091F5620A120751FDD1F521FF720DF2147210F223222EA24402E452CAA255E2478236822FD2182229C23DC212F247D2368238721D820F2201B211D211422F3200620E61FBA210E21CE2014222F22D020B7203421C41DDC1F2E21E420FC21F9202621C41F9320AE1F571F1520C220491FFB1F7D20221E252018209A207320BB1FC41E402043205B201D1F3D1F032071228D20BE211E22BB254D2F1F2D33297E260D233623F722612443234622D721FB217121E2211B213F22EE1F5D21812157213123C42042211920FF21C1219D22AD2128228420AE1F631EBD205822ED21B6222A20DB1FE820941F371F4A205121F620A921BE20612032215E212C22AA201921F21F9E1FF01EE81E791F7120AE206A210C23972116233A25DE2ECC2D8C27732361228A226223AE22F5224723672260225122172163209B2132214D20F02152214F212C2106218423C01F5620B623CD20AC1DA82077203620692045225A229721661F20208D20851F1F21652060221E20D220452166211620D71F3021F62083216C1F9620821F0A20F11F7B20F91FDF20E11F40213F231024AA2D532CFE26DE23A324F621EF2303225B229F23A1220223D1236721C021C421F721D2219A213D215923CF214D2194221123E32092227C21E1208D20E820C920E8210F226C226A21D91F06221C207321AC202F21AA216A218121C6207D21CB20D920EB21B9216E20BB207320C5207A207B20DE1FBD20BD2234229B2163247E25522E732C2325D0233D237D2247227B23AB225E22BC231F233922A522F221F022BC2246239F2161221C22AB2388232F228222D020E0203A220A229D215721D122B522E4217722C6215521F7200C210E217A20F9214223CE209A209321D6216A2009210622002085213C207D204E20C21FEE1F5B21E01F99217A20852273239723D42E292D2F27C624F72430230921EB21D8223223FD235E22022328244B25BA227822732358222823B32394232B2252230D256A224A228C220B22A122DF2229228321802256233E23FB212122B723DD23892091233122D6200620002120200E225621B021AB2192216221E421FC20922051210721A220DE2095205323A323BA25AF2F652CD227DD2568242B243C22E52045239E244B24D1230B234C231023D923EB2180218D22A6220423532363226321B2232B22FF234F224921C5206522A321F22177238424AF23002324226721C2205E217622D720BC21F41F2123C7215D21A221F721B820D02255225D23FA20CA1FEC201E22AB203921FA20A721CD23F023B12FE62E0F2641241F24892318227D22B622C724CB234B252E249222F723362465239223B221A1222F22B122C1231E22742378222C254225CB223E24B2214221E92204248625A223502383212423B22213210422AA224421AF2081206121B9204E21C224DE21AB20A22260226F228F22522177213022B721A4210921D6238B242931E22DEA294A2740250425E122FD221F232E241B2500273A258C2416234224D7245324F1229422B5225D2192236C238B253923522329240D246B24D1224A21662261265526BF25CA24D721922122235B218C228A214E2114216822D021D521342303231322B321B1227F23B32160203622E521DD20DD20D91F4E22B72384240B31912F55291A279B278E249422DE22912207253A267B26D9250824BF2304244C256224DB23CE2211254F22DC247723C6240F254125D4257C2589232F22052384225A2455278925BE24462313225723212367248C2235233E238822932178211823B82330233D248222E7243F24C422DF23AC22712100218921B721492241264F30ED2F3D2B3F28BE25AC2443231723392272244225B625B2274A25C02547259B268C250A243622BE2370222A232523D8275A27CD26CF2592242D2461249922D0233A241628D428D8253B230F2417263C245F2304259323A7237324D92322226324452349238F219822AE23EB238323DC231E226C218B23D221F3223824A2266E2E952E2C2A4728FF26BC267A236023A9232F255B26C626152725263E254F262E24482508252823842253226222DB2375263F28DD27792500249225CC22B92175204224CF261D28992613261E2523261E246626B2261625A5246B23F623C9236624062501222F23AF222325F522DE21E6221B237123F8223E2289233D248B26AE310C2F702AB2294C274F27D924E52570238526DE243228C92AC028AE279B260E272125B72646261B231E22FF21D323F026B528B9273125242615259123FB22B7225D2421255D279A27F9265B2500258E25812571262B276F2528258F2434225D228723E7246325EB24702649259B248B24D4249622B3234C217C22D1243D26F02F8230492C67293F27B726562789267D2677247126D528222A7A29A72AEA2AD62A75281028A925E623E02448250A24A1269926A8273027DE25DD254226162554234B23B22236243B2555255B280226BA2562270324F0246425A325A223B822C1227A24B223822619269427C4274C244E25CD23D5237623E723EE224A2420274030E52E6F2C812B732AA92708288B286028D5268026A729862B002CA62B022B0E2B9E2AC9297628A32574255E238D252928792895282F27362578275E28B92767266A24BC24162501252B259F2469251C2619269E2544251C26C427AF24C822DA236224AF2448278D2638272D2645257322AC24EE23D1235D24A1245325CF286C320A319A2CC42B4A288D28842A6E2BB12BBD2A73298E29A52B802CAF2C292AC92AC02A602B042B2C2768245624D72435270227B828C927EF26EF275727A528432792267324EC220F23CE25E826E02530255026C927CB26B6242427D923F321B1211C252A24FA26CD250C288325CE272826232681248323B623A4246E252B288134FA2EA72E672BC92A302A8F2A302D692CD62BFF296A2AA428BB298D2BC12CCC2A922B082D41297829C426082317255427F125EE27B0260927D626E826562808273226B224D8238121E024A8250127E4249F27E42682266625E02316227421EC214523D425DD283F26FB27582623255C240D257E246123A4227B25D925F928B2335C31252C662B9C2A2529A829552A562CBC2A5A2930287827FC27B527602B532C0E2BB32730281427FB23B6226D2204241D2678261824B023512802262427CE242925462548230623BD222124D023DA252627282780256C24EA241E22B5212323DE244025702620283D2966267F2598270026682594244523BD23FA258E2721328A306D2DA72D5B2C402AEF294C298E2A542B4A2AB629CB265525BE26352856290028D829DE276F2542245625BD23CE23B2256A255426C525DD25CD263A26F2269726E72682251A2532234D23F1265B254C27CD26F525EF24A222CD239E22FD22D023A123C02524278D295529C0282A2B2828E3268E275723DA242B253426E431FA2F3D2C892E0F2C922A152AE729A52AFD2BA32AD826FE26CD260C284527F7271428A62539280826FF25BC245C244424972494267025392628279E250427D6257527A928F727B52671252E25902598263327E825EB2286238D23952312236F2398237C2485242127CD29A129072AEC283527FB27C22549267725A7245E266231B02F5B2B8D2BD02C232CD02A0E2AFA297F2B4B2A9729F927AC269D2549288D26B426FA25A826B1242824A0235B23582353247A266D26C2264C267E241C2603275E26442892282F27E7241E275C26392504271525E2237F2273233B245325CA26EF2597243026BA258D29702C282BA02930283426D6255B256C24592445256F3192301D2BFE2A752A452A99279927E9286C285727FE26E226C4237425FC27AC25FD257227D326AB279226A02579253D259A258726692678270927722512276F274C287E29B6272024AD2503254025D3254C260725B6253E240025F62427262C2956265B2560247E248927FB272629652939286D25AF2528250B26AE2489261731A031E62A88283627BE27D726BD2556260D2827269B27E5255124702464247F25E1253827B628B62B1B29C0262726FD249224E126BF286D2828272327B12695284428C7285127FB2538244823E124B423982439253226D625AA268A2505279D28D92909262D23C2237324E6253E26912828279125DE2539260828E627072629307930502A2728C1276A267A2600270E262F268F24CC24BE24FA23A825E826742780297128602A382B692B512B64265C259724E3268E299129262BFB29A12AC029862A9429FC284B26DA22CC235B232E226725B4255425FB249C2719260027C329EE28F726B224AA219A2462242326432713271A254925A625AB278E2624281B31E82F9A2910273A2611289C281928C3279926DB24722581256124162762285729CE2BDB2BA029D12B9B2A0329D8267226BA243C25B728FC28AC2AC62ADB2A80284E28FF27AC28582506269523B623DA233A23AE259A25F7257326C327AA2676292628EE25E5247D2328242B24B325B726BB26AD264D27002864276428E5278431E72E6F2A842717298329EC2A442BD42BB5292727E626D124A328DC27C729932BDA2AE02B6C2A3D2A1729F828F827622640251024232427279C28DF285E2878277B278927AA26A524612587237D2477230624B623F423EE24F324C92565243D2539279F2441234724D324DC25242772274D280527F2282629722A7F291B2BB433CC2F582AAC2B622CD22B322B4C2C082C432C452AF1269226B2253A29592AE62C022DD92A132B3B2958289327EF26E2250C247424E8220B248C26D2274B266D27A9275B2758274924AD247724C32283241D26AE24A6240B24AD256A2399247A250625642463223824CD25D4271A293D2B862A8B2BF42ABA29692A042CDC2AE4339B31542C6929FC2A762BD02DD72BBD2A622B7A28FD28932654253625B327482ADA2B752ACA29142B87274E278925E62626232A23952369235925DE259326F126B626D626D826892546244B24FB23CB25EA274427BD2743242E255424E0244025EB25CB2534250F2565260F295B2AD02AB129302AEF2B5C2A882AF72BF22A4934AD30082C1A2B522B502D1A2C6E2B232B162973298225D725BA2347240C244F249F265C274D28A327A1264026C0270126F223C3231B25BE23C125B9264C278527502727288727A3258E24A522E9230A25D726D42917279926E526B826592520263726FA27AA26F42384278129BF2CB52BCF29A5282E29AE29FB2BA42BD22A4C33FB2FC52D122B9F2C072D032BD72A2829272A382A7E2AC92708243F2430268327F725372789273A2883260E2805277A24DF23AC255126A8264D28B6260E293729E72BBA2A642A66269A242B252825782481268128B426D5273528AD27DD27F626DB285C294027A8267026E7267829932A032A202BB7299D2AF129A82B002B3733022F022A1F2AF529C92A302B162CDE2AD729EF2A232A062895260D25A82471253726792531287227CF275428E726632481234B25A72620293F2A352A712892291C2BE62BB529D926B623DC25A8254D274329DD296528FA2662276A27B2274D269528EB28F0273D26DB27BE279F285C29E52A522A492BE7290C2B862A70297032282E702947288029D629BC2B062A422B922CE12B912A9B2987272526DB25B7252D258F2771274C284B2A28297829ED25E6235325AE27FB26B829B52BC829F829612C8A2BB52888258C256E237625D826E02A932B4527C7256026C9252D260626DD26E32723286827D3266E25F92622281D2B452B7E29292AE02B1E2BF4286C32502D68279F250A290E2CE32A792C772BC92D452C142C162A49292A26C7261D27CC275C282F29CD2AC82DD92BEE290A29FA256D253229EA28092AE52AE62A632B3B2B5D2C21282F24FA241625D3255126DD29A72B5B2924279624B425D8273E25B226DC25022572241625EA269F272C2A8D2AA72AEF2A902A302B032C93291F32D02B1927CC271329E72CFA2CCC2CA62BC32BA52B162C5D2B5D283C26B2266C28D02B6E2B8A2A702DE12BFA2BE82B6D290126672669293829772A772B432AFE2A112AAC29A5297B242B22D4257727A126E428D62CC82BD72799267D2784273E28412692253A26A226B2262126FC25B228452A922B102BC629032BAE2B522A4B347D2D5227CF27652A2F2D032C3D2B5D2C642B722C432D342AC627582497268D29622C4F2D642B022D362C702BF829AB2A5227F625702ABC2AD72A412B6329642A0129FE285E27922323223E24B6246B266F2A692C242B58281227A9249B267E261E28F2234225DE25B0244E254027F729962BFE2B562C3D2BB82B622A2E2A2933DE2D2C2806270D297A2C842D5E2E4F2E3E2DC62C592DF029D626EA27F6263328262BCF2A4D2A542A152B7128C42880292E27D7259428C829062B262A67297A2A5529FA2826281C2530245E23BF24CE245029442A2D2C07296F262926882697259C253C258C23C5228B243224E1273F2A652C822A232DE12A7E2A1A2AB82AB834212E6927F4254A28052A562BCF2B322CE02E8E2B492C082CB3296F2720279D27DC2A102AB7286F28C8272429FA2A0528EF27FB250926BD299A29FD281C292C2852280F29D429B926AE256125892448252E27F726A028F82793267A245C257826ED257A249D239B22A822DE235926B129A22A5D2A2F2BB0270F296229632BCC33FD2DDD2671258B259125F1262C281B29992A012BD32C152C78289924892472255E266628732747272E29DC281F2A1F2B06285C251B25BA263E2790260D27CE25ED260A27CF270228AF28E0279225E4247A25DC254D2532248E256D23942326245824682451248E230824A3227A245F27C629122A782AF128DB281E291A2B1335622E742993260027B226BF26C6270129A22BDA2ABA293E294928F826C02588251B29022AC4279B2A8729862A992B702AEC28C02699237B2453279724B125A426AC2636266C2774280329482ACC2738262C26402597240E23CF22CB22E124872617257D2564255425B22595240C25B226D6271828D1270C27BA270128C52B5335CF2F51287D247E26BF2406264827BF29ED2BC22AC42A192A85277724FC247D25CF272A2AA32C0E2C252C0B2B8C2B062C7F2A93262024EA221024D624F32450272227A7276E294F270D2887294729CE264E2509251A2592247A253224FB24EC252228F226E62418251326AD24E4243D2656271F28F629D6283329F12ADE2B3533163037292A27DC24FD27D5235D27FE29BD2A95290128F126EF24D8230B2533257627B8296C2CC62BAF2A762973283429BE282E26B1244B24AF23892411276B286B28802791285427FD26D825EA252D256B25DE237A245F22B723EF24832638266527BD276526302565259A244E2484255A260629912A6C2BAF29A627C429663309306A2A56281C2704273B282227FB28B329D6290B28562668257426AB24492589256B2A452C602B6428CD27F7273E284D26092A8C28BB237B25BF24CC268A2ADE2A2A2A8328BF24852489245126F0243325A8245023B5229424932543274029BB275D275928462719266F24B4234A258126A4273A2A0629692618272E2A71348E311B2DC62A61287D28AE29482805273529B927BE26DE262726E82629290728652675294C2A9D2B192C692A082AA029CD286728EA277C279C25102544276A28FA28B229A728FD24A325A126D42542277126A527A924DC249B239826EE2743288628F3285B2A4B2A022AEA243E24D22555256B260228452794271327F22819339632892D572BD8291229EF283F29172963288C28922774286B2AAF2C9C2C522AF7283829E42A052E932B892BDF2BEA2AF02BD42AE82AE82927287D237E26EB27F3294F2B922AEF28C427AB26FB28E7286A28E828F2252B258F26F725122AF22A632979281429AE28132B22297226172890272A27D628E627FD27E129512A52346832002F0E2C29298C285B277D27A526F026B62609293929AA29542CE82C112CEB2A5E2B4028032A912B9C2A5A2B812C642B412BCF2A742B8B29F226C1266F28232A292A142BF22AA029A029C62B9729D2296B287B27CB25B923EB27EC29782A39287228CC266328712AEB280627CB2934299427BB281028BA28BE28A82990347432AA2D962C5F2A4727B7265C24F824D026952822298A2B1F2B0A2C962BF32BDD2BDD2827284F294A2A472AD32C242CA72AC32AB92A352A1129E4275026B1253F282B29782BBD2AA32A322B322AE429EA29542A5E27C02429251826D228C62A3F2AE22A7A29FC28142A35297A285028F127EB287729262A062BB72AF12A293484322B2ED42B172AB8283F27DA25FC23EA25E627632BE22AA22987293D2A0C29982AEE2659276627F2274E2A282A452C662B3629732680262528AE256A2545257427E428212CB92A532B2D2A662A8229D029832A32270525D7257D26C927D62A312BA628C0280328552949294C27FD276C29DB29AA29CF29F92B412CC62CA133F0309B2DCA2BA12B782BA5295D26A426802858288F29072A0F285127D827C52865264525C52589289829E6297529F027F6287729462885279A2615262926FA2553282D2AFA2BC22BC02A462A9629A829C4285229BA27BA2303251B27AE2756286F27382AA028A428E7276E27F82501261D28E32AB92BEA29762BC529A32A07340632522D602C172B032C2E2C142DFD2ABC298B28FB29AF2A2E2A9528E928A4276728D2290F279E28A927AB2A632AD72AEB2732271D298528AC27DE28ED27B825F1250429672AE32AA22A14294D283C28072A8C28FF268926042643287C274329FB28E32965287426EE26E7253A253B26D02655284E2AF12A612C2A289529EF319733352F432D0C2CEC2B352D5D2CBA2AD62A5B2ABF2B122B042B6E2B842B222B9F295E28B826CE292529FF29902BD82A5528A82AE42A562BBA2CBE28BF2712265927AA2AF52ACB2AFA29422AEC286F285D29CF2AB228DA254725A8256326DC2610278627C526FF2347272A26582555259A255A279D275427F227C628842803323333F02D3D2EB92B642DC32C0C2B2428B328C429512B7C2CD22D092CE12CEF2B0C2BE32AB9277029BB2A282A1A2A7F287928EB272D2B662B1D2C2129992859268D262D2A392CAF2BFC2B7A2AB82A012A192A2D2AAD27A526F2262925242412252325CF2565256625802409252627D22636251425FB277C267927B726A1271234ED34292D402C602BF52C4A2C772A322A9427EE257D28AB29342AA62A1B2BE02C4F2AE62AFE29D72903284F29042AA429EC274828542886295029A228972715261A286D29A52A332B942AE82A5F2911296F2A702ADA272C270725AB241522662344262825EF241D264E27F0250D28942ACB281226FE27D426B0285928B428C331BE32442E812B742DE52E452E1B2CE429A0261626B427AD29E927BB298229102AAC29E52849289227642A1E2A6A2C802B5028B427192A4B270F27152747268525C4268E272728CC2975291028CA2700274C298C2A00292F27A1259625642376247327142758277627D1278727AC27732964296F28F02A0C2AA72AED2A12292830ED32362DEA2CC92CBA2C2F2F0B2D342CA62AF8275828B0292F2BF128D729EB29902CDA2A5629E72AF32A502DCD2CA12D142B722A7C289D2802289A284629BE274C270829F3267D28F3275B27642553267128DA29AA2867273427C127F326BD25C6261428C72AFE27BB29C6291529072B822AF129842D232C9D2AA32B972B7532CB30312CFB2A5C2C2B2CCB2D032D6B2DED28EE252E29C427BC29232A7F2B632A462CC02A6029D1281E2B99290B2D672C8E2DAB2BEC281C2794275F28032A8029F2276A280727B527E72704274829E7270128A32A412AF529B02A2C2A5B29852759263C277729B029E6280A29F32ACF2AE4276F25222A792A812B7929B6291432AD32102B402BDA2A092D0A2C6F2EAB2B152AB529D0296229272AC129052B9829892A9F291C28B9279A27C328D029BD2B0B2C572AE829CA289F27A3274D28E9264727AA270528BE272328C7279B29B028F429412BFF2B142A632CC22A052A0429EC262C287E2AA7285329E6275528E0272726C2279E29DE2ACC2C092987287031B4318D2C5C2CB72BA52B032D7C2DC52CF52AA829222B1D2BC92BB928152983289528D1279927832755279428182BC22BF72A8729EA296A289B297C29EF29F327D6255127C5294A29922A2D2BA029FA2782286829602A1B2B8F2AEC2AAC2A7B289B261E2790294A295B2A682804285A285E2535273A262026F827D02672282734DE31E32C952E2F2D6E2D122E922C1A2CB02A1A29262AA62AF82AE329DE2981288829E92A4329B828EC2616282D298F2B552D352A5527CB259F28072BE92A012BC4289B291D2A382B972BFE2BAB2A8B2879280028092BB62ACE2A6D2AFC28EC281A271F257D27912635296727EF2400282427CA252A24F923D824E6267B29503440324E2D7B2DF72DEA2DB12DB22AE82A75298B28C629B22BAE2B71283B273A2A7E2C482D522C3E2AE6273E286C281F2A52298D271027DA270E28762C892B962A2F2A1A2BA22A092BD92BB92B0B2A2C285A28292AD1291B2B6D29E2287C29602933298326F724042571271E2716286D285C28B7264A253A222A241327102A443564309E2D1E2C972C772BAE2A4C2A3B29E428EB268F289E29F72BE52AC329A027C2296C2B2E2C472BA3278127AB265626A625EE25E5248B266B289E2A022C212C9B2C872BE82BA52B172A2B2A812B4428F027CD28D429F92AFD2BCD291029FA28862AC229BA27B923A4255B269628B62AB1292F28E626A425C525CD24DE28E4333A30772D682EB82AA22A2B2A4F287D258E25352638272F282229682A2A289227F326ED27DB272729B52553266724DC24D023602535242B257C28102AC12A292BDC295A2A452DE029282AD929982A1C272028F2284229092A562DC3290E292D29012A2F29CD25A324132524272F291A2A2D294925AD292F26C0253A283128E031ED31792BB92C0C2B562BC92BDB2781259525FA24C9269627B127F327F1274824B1269C27CD270B268B27FA254B231B252325C32333269B263628C327D5281B28D228E129322B102CD92A992ADD2AA927CC27402AA22BBE2B0B2C5D2BAE28DD28BD27B727A92417248926382691271A2B94281E26672860284C28132AF12719338331C12C9029772A812A2E28042726254A25AF259424DE258F2521265B26D626D3254327F327C028C126202753260A247F234F250A25F126BE298129E227AC267B274A285028E328822A042B42284527E7282D29D52BD82A932BE3272C262426B6267F237B23AF2564269525ED259A28EF27322906283929B629112CA52BA4342931382D722ABC2A9227292707271C2569248F221D270825FD276D2674276C262128C4272D297C284F2601262D251A272E256124D625F32504297629FA260726EB269427AE269427892A2F2989261024C32551282F2940291C28A8261F269C243B267524B82321232124AB25162608292628B1284229A32A202B422BA12C2435852FB32C212A872871283426D423F52504234F2486253126A429F429B02B882A822AF62A6B2956292727A9267C296529AB2604259D24BA26E128F7284E28C2279A27FC272729FE272E261C274725F8223023CC277D25E52617299728AB27E026D8249324A524F6226126E8250D28022BDC2BCF2BC42B5C2BF02A752DC22DA0354330D72B752A8429B727EF25642404262E242A2409263D298B29802B7E2C7E2CBB2CAA2BF4283E29BE27B926112BC629482956281227C8243A27D929C329E8271D29452A12299C267A25BE25B2256725B4255C26E1261527B626CB2622286726282785286C262025E82484253C2B602CA02C5A2D5D2ED12B5F2BE62CDC2D8E33C930E02A12292C263C265F271D27EA261627FF24FF24392576288C2A3A2C402D222B402BBD28422925290529EA297F2C9F29ED278125C324CD261D282B29DD270C2A372B3F297528E425D62373259A24EA26AB288D26E52700273D265C29FD29832A332B0F283927C9256829902A162CAC2CEE2C0A2CA02CBE2D9B2C5E2C0A344C2FF929AF279825C9264028F72867280D2AD5274826C32615271A29BE2BCB289728F62988272C260428B328652AB02A442AC128612770244A25FE263C28092AE02AFE2A542A0E26242505268F263526E6294E2A5D296A2744297A29FA29972C072C822B252BF5280529AD28B12ACF2DFC2BEB2C002C782C732CDA2CF82CC833312E07299227F026CB267828B429CE2B1D2C3B2AE1267827BB25DF27682B3E2A122A1427C8253B26C9279E29692AE4296D28832629267C23F8242A24582514262D28CE260E27EC232B247724D925D426512A872A9B2A1329862A1F292B2A2A2C182CC52AF6294128D3286729482C212CE52A402B1F2A5B2BE32AF02A142B4D342131922952287C28F629142C962A272CB62D5F2B2028BF27B9269D291E2CF52C6D2AC8281327412598275C28512AF12AF3289126CA2482245524F82301226A23B426BE255827EC23E124CC25C6255A2629270A29E0262728DB27312A5629042A952CFF29D12AE028D7266228F8292C2C292C642B4C2BEC2B782CB929EA2A01353D2FE328F52640280C2B792C0C2DA82C562CE12A5C2AFF273F27E027A52A722D902A5A280D28FC271F26E5250426AF280428D92792257C24E924C82382244E264C257626D226C225FD273027BC2654269327182696274F27B62860278F284A29092B4D2B3D29D22781269B27EC294C2D1D2B9E2BC32C382CBE2C872AD52A0335892E5629BB26B529C52BE92B722EF62C222D272C4B2AE0282C265E29822BB62C0B2C992A302934291E279A29BE27EA27412746297E262D2540268B2775268F261E2675256B281728A528B62AB12A51295D286C280A29E8288729D529B82A4F2C642D002C282B24297F288B277929502B882CB62C5A2D702BC12C662B402B8A35CA2FC6295E299D2A902B4D2C282D0D2C982D8C2C192DA9295A284E29C529832DA82C3D2A85294A2B492A4F29412A062A7B27B12605254025E32696264729A429D929CD298E2B812A9B2AE12B292BDD2A1E2A952944290C29F02B142CAF2A7C2C442C042EE52BB228272A76293E2CDE2BD82AB92D8D2EC22C202DF82B952B5234552E3029C328EA280E2BE52A012AAC2B662D192D612D052A5A282127FA286A2A7D2BEE296729152BC629152A4229AE285D2469260626FC24EF268127B629052B8A2BB62B8A2B7B2B2A2AA42AFC2B862A1029BA291D283B297C2CE12CBA2CFE2A5F2B1B2B392C4529C729ED29012BF82C832A4E2C9A2E4B2C742C992C3D2CE133BD2E792B492A272B7C2BA72ADE2A972CB32C792C782CEF2A0F287226FE25072918289B2851271D279C286F2A152A322775261527CF2762269925E4276F27D0293C2D882BDA2C732A4C293929852ACD29242A8F298C287F2BBF2C3C2B0F2D902C422CC82B842BA12B452A3B2997290F2DB72B552BBE2B8C2DF82B7C2B362C84330631062C2B2C432CCE2B6C2BC72AF02AF92B282A072BDA296B28B5273228CE29172B7429A0284B27252822284F278F261026D728B6291E27CD25AB255F29912A352C692CA92B9C293F29522ABB2A1A28D52841284429F42BAC2CA52C452BC12B0D2BBC2CD12DF929C62ABF29D127942AE229D92B922BE129402B652C3E2BFF335B30BF2C5E2C0A2D3A2C9D2BDD2BB72B822ACF2A112876275227412764278829702B67297728AC27BD271929E627B126D8261D299E287A25E625252674281C286429E62BBC2CF02A9A2AFF2C0E2A132BA12ADB290928CF29AA2CCE2BBA2AEB2A3F2C512DEB294C2AE6285528CE268428E128392B172CBE2B862CBA2B3E2B8B33C931F32CC62DE12BFC2B152C242B752E182E132C342A39288827DA264F26AC2B9B292B2BB62ABA29E828E5266D27412662244F267C264A27A526B9256D29002BE42BBA2B4C2B2F2B372BCC2A212B032B862AD3291428A629E52BA72CA22B092A9B2A8F2BF12974296C29EF26E32655282129842A472BC82ACA2A902A512BFE3455329E2D752B1B2B6D2A452AD92BF92D4F2D782D4A2C5F2A6227FE257B25D92A802BAB2ABD29BC29FE282D286F2783287C24752788283F28F527AE27A7269029BB2BBB2CF72B812BC12A3C2B182C862A6B2929293C2742280E2A312A4A2A7A298428532A5829F0289B271A255427AE263327222A522BD1279D271D27852BE8343F33702EBF2A95293829502A062BDF2C852DA12CDF2BF12B742ACC265A262B279B2703297F294F2A792B6C2B372C4A2AFE2AB72A7D2AC82B5F2CAD28CA25FF28122B112B202A8829C229E929882B342A062BF628A0279C254F28F828512892296529FC288B289E281B27DB2625284F2A652BF32A312C292A722936298F2BA33624316F2C6B2A0D28CC2630288927CA289E295D2A732955290B28C3252A26B0248D268D278328CB28F32AD02ADE2AC72B7B29352A422AC129192D452A3A29AB264F287C290F2C0E2AEF2859283D2B262B1E2B4B2A9027AB239429EC274D286028402854294628D325B9267327AB29892BFD2A062C1E2B2D2A53283028B12A7A346F30422B9227CB2608279227B627F4255727A827332739267F2509257E24D9241E24E7265C26F928472B4A2A5E2BE62B1F2C4F2BF92A9D299C2C212CE22BD2298B29902A922B6A2A8829BA28332C0A2B962ABD2A0F274D27A5271629F628F1288829722A41283026BB267328952A242C402BF42B9B2AF0283329D729D12B01353B3272296D28D726102694269126B527E525E8275427DD25C0244425E7253925B325FC2502286E28AF28F827702A7C2B352D1A2BD02ACB28842B1A2B8A2A4F2AB52A392CD229422A5929302A4B2BFB2B182D4B29B228CD26242AB82A9A293A2AB929F02AE927CC26B027B4271D2BD82B352C352BF129F627CF29B4294C2C1337B22F9A2A9527BF25DF257C272028E4288E28822786282F245D26FF25B7258A27E326E5272128E3293727F7250328AA280A2AB92AEE296F2AA62A422A252B812A6A2AEB2B032B4D2862281E2864295929F9284328E1261226E82B5D2BD62D092DAB2AC32A542889250427B2273129FB2A1E2A552A4C2DDD282F27B327692A34344530F5292C289E26AC280A2B5D2B9E2ED32CF32B6B2B0229AA27BA272B28FF295129A728562A802A6929EF27F225A1265927C0298F287F295B2AAE2A052A23297129892BB02C8D2957272429252BFA27DB265F285427F827C6299E2AF32BFD2B722DD22BF72A4E2A172861266927582A9129EC28032A1A2ABA288129A82BBA34CE2CD12888253D27DA28FC2B6B2C832DF62C1A2DF92D462BF429BC28A52AF829D1293229D42A3F2B2C2AB827E72664264126412994270D28F02745283D299F284D27CD29F82A9F2B6D2A9529C5276426F826C326FD241D271629962A262CE72B9A2D602BC52CE22AFD276726DF24E92582289928E1282328E928C6282829FC34E52E6C282A261627F229222BB02CAE2C3F2E682D9A2D0C2DE62AF629D42AAB2B22297A2A942AB52BD52AF32789287F25782509281329442AB42A612B7529FA287E29952ADF2A402AF328C328D42873247E263626C327732A292CBF29612B662ADB2C1D2C332DF32AFD2A902863259E259D27EC28F029CD2980290829B629AE33472FD82A4E280528EE28142A712B402CDD2D3D2EFF2C362BA829CB289B28CD2AA82AC129292BC42B342B9B29BE29E92776262028AC26C728452A602BD229452A412B4B2AF129D3277226622618263625612521276F2A172AD62B2C2C002AD22A952BE02BEA2CC328EF285028D125C026C326E829D02B7D2AC02A8C2AEB29DC337C31872DB02AB32AC12B222B132AB82BAB2C122B8D2C312BCE288526EC28AE291529AC2B3E2CF92AEC2B402C8F2CD52B8E28F7285428A927EE2A4329AD28292A8F2BD82AC1297C25132687264E2645241F26CD26CC28672AE32C9D2A3A2C392B4F29C029802ADE2AB52AA525C2254027B128A828372A482CC52CB52AC92CE3351233FC2D502D4E2C5C2D802DA22D852CE0299629D9273A2838250B2666288929982B2E2D3D2ACC2AAE295329BA296A2B372B282A1F28A427562716298A28DC26F52806298126932317260527692704277F25D9274326852700293629EC2B832ABD286A26FD2585270129B2261127D928852AB32A992AD729B22AB22AA22BEB34D930672D652E5D2C722C9F2BF62CB22DA52A44284D274A263525A62512279729E92B212D482B232B7D292529F429662CC929512AF427C9254326D7245726A1262426A925442600264726B525C6253827F123DC2614257624F5261E27B7269B25442547263329832851295F28C42733288429ED2A1B2A80292B2B8C2C082EA836B730342BE42A932C1C2B262B282C702C692B8D2871253026E72583264D289D2AAE2BEC2BE62C632A78296E277D282728F4270B27002764251325852492258527CC259726D226B323DF268F2544265825E923D324CC248F265027CD27C828B4260B27A828DC292B2BD1292B2876276A283C286C2A1C2C632ABB2A7F2BD02C58343230D428A928DD290C2B9429A82B332A7A2A7229CE261726BE2554263929272ACB29AE2AA729262A2D270026E42792278125922551257926A624DD2290245F2654276A2621278B266128A92704296E2763258225D724FD26112AE828D729B729142BA02A992A3928B9299C272B27452ADF2AFC29F72AE02B742A442B932C4533932F8D2BC62A0C2C872C472A4F2C022AE82BE629492592252028982732273328B827F4280F28EA270427F2259B262E28BE26412650267C26A124F22481248A253B27BC288A290B28572B102C872A3528CB28A6270F267D275029EB29282CF12B932CF02B7C2A4E2BF12820265427E828182BB02B4A2B182AF32A0B2AF9295E33D92F582CEA2BDB2A9B2B372AEB2A972BAD2A302A8F28B9260F2826288428AD28E527F327A72AB228DA28FC26E42875295A289C29012664264227962728256327E628232B2C2BF9272A29B2288D2BAE28E52710285D268F26172A882CDF2B552CDE2A782C142D7529952A48285C288229412DFE293E2B472B2F2B5F2C0F2B8E340C2FFB2B602BAC2CE62AF928F827182BC82C9E2ADE2A852AF4272A281D2A0E2AD028612AD02B0B2C892988273127AD28122B172D082BF929FB2AE42AFC28A4280729832A182B7B284E27D52835295728A8273527EB25A3288A297129FA2AA12A002DED294F2A782984282A265427CD2805280F2BB62B422AA02A442B1B2D7F343A2E272BC22A0C2DAA2BA82A7427D029762B3C2B432A212B342A0B2AF12A502CD82A0B2CC72CE52E4E2C6629412B552B5A2D142DA22C252C412BE22B932A122A8828392AAB2BF22A85293E28D32737286C27A5266726CF275629802998284528B4293529AF27752845274526B725C7266929942B812BB42AF8299029002B41337431DA2889278829542B1B28E726F9252727902B402B152B78291A28052A332BB52A8A2D562B8B2D412C212AFA28B9290E2BC12B882BD0287F2A612B012AE4297D284426A52667282227F2266C263E268C276A284E27F026AB29332820277527A627A32616263E256227C225B924E7253F2644293A2A82284C293A288F277032EE2F5F2A5B276F284C272628242651269027A5280A2B2E2A4C29532A002A8B2BD52B532D2B2D572D352B6D2BFD2AF4289F28C72AD9299129132DCB2A212C7A28A428CC2655264728C525782530266C26D8274228B62755289929C729D427632712296E29F8281F29102AFC284D27C426B725F7283329E429CA2A4C2AB52AEF32592F83284227CF256D270A288326FE2645265027D22724280E28CB27DC28732AFF29F22BC82B862C8B2C2A2B9C298D28172A8A28F527E628B229952B6A2B3A2A4B2A112AE528C925E323452621256825FB270F2AC828FB291D2B672B302CE52ADF28DF289E2A402CD12A582AC1278027DE269A279029212B012A572C942CF8329D2F1A2AA328C82AF32C6A2CAE29A02BDF2AAE2AC22733271427DA259C27832717273227E028D429132964292328D4273B254E2799260028B42A2B2CF42A622D602C712A342BCE28D225ED25AE263B26AF2A262CAD281927B3294E2B6D2C1C2B2D2CF52A9A29E0293A2A2F2B442A50290329FD28DC2A732B6E2CAF2BE22A38342F31852A8929082D5B2CE52E482D182CD92C762DC92B4C295A28BF241626BB25E82554267F27A2280D2ACC28F328C02640258B26C825FF28F72AC92B9A2AAB2A9D2CE92C7B2A232A52292B2838259925462A372AF32ABA280F2B382E9A2C582C622D012C942CEE281429A7298A2B342C7E292129AC29FF29372D2D2DC02B3734EC30222C932AE929FB2B312B532E0F2D372B1B2C062C542A6C2765270E279F27B226B2266B28AE2A712B942BE829F5278826EE25A1259B28412A552A272BF32A1C2D0C2D8E2BF129F929C229D3279226232A362CF32A5A2A322B402B902B8D29BA2B9F2B9A294428BF27D528D2273B2A9F281228DE27B028612B9B2B492C8B349B2F0D2B642AE42A9A2B272C0A2DDB2BDA2B332C972BBE2CE32B9C294128B8278C27C429A42A352B1C2BDD2AA82E3A2A2526772410268026DC291E2CCA2B982DA52C472C9E2B92292C2957287727FF26B42ABE2BC82B982C812B02298E29452A622AB129002AB026BF260326EC274E2BEC29E32987287828F329EF2B742C00336430862B262A472AB62A542BA42CDE2B932D722CA62A1E2C612B562BAC2921287D276627E829F52A4B2D6C2C872BD229D92989290D283929EE29A72B582C992DE12C452D2B2CAF2820292C281F27B4273A2A012DBB2B5D2B6A2AC229B228042BE62B1E2B4B2A3B299C27FF253926F3296D2BC329D6281D282A29072A2D2C15351A2F0B2A3428C428E027B7284E2A762CE92BEF293929D928A928322A07295A25DC2415251D291B2954293F2A2D2B2D2BE229CC298B2A1029FE25C8275B29CC2A182C6E2C072A7E28742732291D27A726B728272A4F2BFA2AAA2A782A762BF12A3D2B352B412C762A1A2BED27E126FC265F28312AAA2AC1293328CA29E32B2A368A2D86298B271C25FB2674287729052B642C7E2C0B2AE328DF260C26CE263F266D24B825E726E9268D27642B922A5E2CC02BAF2ACD29E928B926F5266728022A422C1A2BD22A56299727C7274E28F526AB28DB29EE2AA62A922A9D28392914291629D72AF62A5C2A342CD42CAC2805277F277D2B5D2B9B29692B0E2A182D4F364C2FA5290A28982797272F274028EB2796290C2B16293427C8259325E9256825AD233D2322262F272428BB29272A59291C29EA2AFD2A7828FC26E125BD27FB28AD2A812ACA2985284728112AD92A302856277A292E2B4F2B252C7F29A3285027E2279727EF285328772BF32B3D2A8E28C426042AAD2CD22B552BB52B1A2DFA348E31C12ACF29C429BF2B78299128CB29B629EF2A7C294D2824260F248223A723A02358253C27D428E0270F29592C312B8129B129112A07289526F92452260B2A482B1C2AB428502600284A29392A5729FE289F29D128372A1E2BF32AE42A11292628B5264227F2272D2AE52AE12B862A1F28A1277B29E7285829FB2AAB2AAB343631A4299029A5283329042A5B2A272B472C332CA42A2F296A27CE269525CF233124E925F42582281528F629FE2B4F2A6D2B582A7F296428762635262C25EA2644295C289D26B125CD252B27A527F7289229472B422924299C2BAB2B282AEB2AFD29BA296329F228822ADC2A452AE32976298427C02653280F288B28D629AB34B331332B56290728A9283D29C12AA32BB22D222CF82BE62B162AFF2764260E26FA249725CE2546296C29492A602D2A2CE12BC92CFF298529832AD026B9249A24E2272028AD25A5247F247026F4258A28AF2BF62AE52A9629512CF92B252BD32B662A3D2A722A9529192C5C2CDC29BD2A6D2B3228532890265228092A122C17346030932BA9288A286B28EE269329852C202D4E2C222CE429572941270025AF25BA246F25FE26CF28D9282A2AA42BE82C732B812A482AD72AFC2B852A2228B826A9275228A5276F260726D826F726CE266B2BB12DD32B3A2A2A2BEA298929B62BEB2B4C2AF52948294B2BEA2A782BFA2BCD2BAA2A0D296C279228B729C82C23351E308B2AB42AB4285329C9278629E42CEE2B1F2C762B2F2A4628A12534264B254B252225DD2829276E282E27E0290E2C0A2B11290F29E929C1291B2895269D276B27DE25A725A627762748262A28AC27552CBC2B5E2BEF2ABA2A8B29AF285928FF274A28C9291A29422BE82A2A2C842A882CCB2CEC2CFA2964290E2A602DBB3517306229B428372BEF2ADA295428F62BE62B512CCD2B092CA8286D269B27352719266227B12571287E273025E328642B7F2CD62A822878273C28BE268C255C2770262226D125F52449251E28B127CA27F02A452B2A2CA02C6A2CD629FC2798296D2A7B2B3E2A6729832A302A112CD62DD62A2E2C1F2C0D289E27FC289A2D5436D32F3B29972A3E2A9A2C492B592A9C2B4D2B23294429F828B3297827D8267C273F265828D127782831278725202712273028BA2A432949289727B8264B2420249E266129DE26902573263125342A0C2A602C1D2CC82BCC2A9C2ADB290D293D29912AEB2ADA2CD12A752C672A132CB62B132DB02B052D8B293729B9294D2B2A361F2E162A32292E2B5C2A012D2D2B5D2A3F2BBD2A472AD12782266027FA255A25BA272829BD2A3429EC255625DE252B26FA274A2A42292F28C5275225202485239023DF250A279B262826102669274A297A2C7E2DAA2C232B922A5C2A6429822AD52B3B2A002A892A8D2CBA2B912CC32CA12CFD2BC42B9229D62842285E2A8035272F162928293B298B2A732B3D2CD72D602CAD294629B6263B261F240825FF25D8257F2ABB2B1C2B52282227D524E6272F28BF28C727AD288D299228182729259824AF25712617289F260D28F627D3297C2A982CEA2B6B2B2A2B9528DE27CD2808293D29852A092C9E2C9C2A1B2B222C8F2C772B3D2BB02A0F2A48292028B332812DAA27342607283029B32AFD2BF12BDF2CA629A9279B25AB2454251F276A29E7298429F729A52A10295F26E92601269B266827AB27B3271729E6282A275A25A824112349244F2459284926A22711273B28A72ADC29CC29F529402AFC270B29AE29BE292F2A2F2B662A4129AC2B9B2CDD2BEA2A992A0D2ACF29272894289B32BC2D7727E926082527266A29542BC22B6E2BB2291728FA265F245D2412280F2AC92AE4293C2AB528BC2757252D258927E028A829822A6628F62879298C278C26CF24FD231926A425F6262227FE2671254126FF265928372BC12C6529A1288928082AC02B152A932B832C592B9D2A992B912AA02AC02AEB296C2AB329F129EB32832E6726A224E324ED251828BC29DC299029A027C72411255E24172454268328EC28AB28CA28E6272D270726F4241F2778277328B4261F285B27D9277928E1262D26102577273428C02762277E2714287E278325D6261C2BCE2A7E2AC129B127F02840293A2A902A5F2B6F2BCC2A8F2C222BA62AC02BB72BAD2A5C2B9F298A331E2DA627E625AE243E254727AF26AA28C2274A26B2258B234B25C8241B27B62736295D297B2926299B29B327C326F1282826B428C428AE28D3256A264D2731265E26442772274E27DA27B327372757274829DA263E26B226B428BD270928A12A98296F294828FC27072BD42B452BC92B392CC02A5D2B68271A2835293E2A58344D2E6029D4248D250F25EC26FE27F3266D282824EB220D24EE243E24C6249327B626C8283A29712A7B2939290E28AF283627582752279C274A27B325B0239F240D264E28D227FF27DA27BF275727462613281E29A2273426B927F225AB2648279F2A0128B2260D269A28B428522B452A3D2DBD2AAD2ADC2AA228B9287829BC322A2E8729FE25712458248825E425992638270D258F23E2224C24E4206222E42381264629A727ED297D284327C72784268126B0241B269F249B276F24392605252E27512742285E27092955273D261E26ED25A826CB270127A528FD2467267C267C296C2AD72AE228072A0D293B2B6D2B172B242AC52A062A8C2AF1282E2807336830B029BD25A0249E24A225FE23DA2554263025AE24412391228823DB230A24E5255227A5264028A225302527268D2729264F267124CE234724DC2487237325AA263825FC25DE26C125B627D5264B232E269A266F2698258028D427C2287326CC278B295C2B4E29152ABA29DE29662A5E2C9C2ABE2BA7281D2843254B26B030882E8528E22599241623602318252524F425EB244E239D23D7233722CA230B23E32363240A25DF2405249C251426C025F72432266C2452234B24B225CC2480249925A22500274B26D3265927EA251A24412350244F2593258526DA26CA276E26F52544289C286827682774263E269929902A542B272BD428342805271427E2319A2C8F26A025BF2459235F24D8212B2319252124562409232B239322942222238E22D122DB24EE245B23E523BD2508261525ED26E924FC25C12407234B255D24CC24A926E828D9268E28E028B2262726E02459253E2595270928E226A325012453257B25BA256724F925F5244E25A426BB26D3275829B227C027BD258226A62F292D2427E123CE253625FB2366231F240025A7224D233525B523FA237A23EC22E1236F239923C224DF243226EF28222992270C29BE28A7262225D8253A267D26BC26DF26D126C728C629F629792A8429C82801278726252754283B28922568269A25B824EC2310245E240025B8240A2731256426C426F425F8258B2507278830C22B2B271D269F230724C92392225722CC23DA2478243426B324FA24F12342253B2355236F2496234625C525132928294829042B6629C128F226BF230024602442262625EA26FE26C227712885295229C028DE26B52416285E293E293A28082760284F26B7257325EC246923B6237F23B424C425602347250D257124ED241F313C2D6A27AC23DA22E7229C22C02269225D2307249F23FD244F240B24C72443211722A3221122542351244D25BC2627272729062BBE2B3728EE26792392231F25A7250526F0265C241326D0262927AD269A27CF2643258C25C626222898271B284F283B288F2612259A25512318241726B823E7234624C922F922CA25CF2592317C2B17262923802248223223762107243B215B23B424A3257725DA25182567229E212B2357210D23BA21B822E0248E26C7264E296B2A9D28D127E72462248723A0243F25AD2569242F25B42505265B26F2259C2623256F24AE2430267A2765279128B0280628CA254F260623A024A724FF2417268D2537248A25D62451264C30662BF123B22241245422CF222D22FF227123D32217260F260B25F5251525ED232924F2213223C323AA22C7210624FE2571241327842726277C26E223FE239E21F0221A242326A424EE243525A325892482254F273B2433240327FF261128F32774263D260027A426FF26072682247C268E27FB252426E8241B24A1249525A32E072C9E26ED222A237324F421F422DF22E423F32476243D259A26E42489230B259E2445247623FE23F121202131239124F423142403260B27F5249C258E233A21DD239D236D239B222826782453258623FB25B6250724A12429277926282AA527FA28D9276C268324BE253925A4253827832618254A2664267426FD252B276E31862D8626E222FF22AD24042262231E226E2389238D24C523BC246E23AF236823F022CE2251217922DD216A21762285246724A3239C2382213D2442238F24BA22702211222D233423D82191259623882241231E246A229224D3258E26D126182873272726982581229A243424FD231A24AE24E024622623268C25BF250B266831CF2B05268624B421C321F3218D226822D323DF221123A9238E230D254125082263233D223F22E5229022AD2152226122EF22A423D62159211424C2233223FE21CB22BA2302222021732086228220BA21DC220E238320012230257525DC26D9242B2795263125C1240D23A022AD222224D4224D23EB2338226425AC253D27722F6C2BC62581236F222D22D522A7239723BB23AA220823982479233323FE2350241523A1215A214F2280227121AB21A722F123E7225F21A3223321582301225E222622112262237C218220DD21152167208421A5201A2186213B23EA2489241E27C92508247E24BA24A12493215D22DF227D22A0215923B5221425CA256A26F32E602BAC263922D2213C21DD2033239D22D5228F211A23DB202B224122F322512289226521C521A5219D22C21F6C21CF248F248323D222B321D622DB215023F9231A2277211A2336203C217D2165207B214B213922BE221421CA23772390234E23A7252C2499222C22AF2390230422D9226E216A22CC23F9227223AC25DA267E2F212CB02506246022F422AE22EE2128220023F121CD22832175229A211D224721A520C821F720F920B81F9B1FDB2088228822E9211F21CC22A4224E22D6225D2265224222692197202F22FF219D214121A12057210720B321C822D02243221E239923FB2274236B23BF236521D1216622DE233F217A22852155222322222672306F2B662471226B220E223E212721E022B6209C218121132128210720D9200322582110201622BD21E720211FD92179215B216122E122432212223C219C209022E421922207208620951FC02145208820A2202021BF1F55202722E4203B22702245227F22402203229222D0229023CE203E211D229522D4226E23AE24CD25612E4C2BC8242221FA211122ED2011224622E621F5229B223E20BD20AB2033219A21A521E3214521C5207721D11E8721271F40211522AB1FEE217F202521D4207F227221A320DD20E9204C1F6C2164218720FA22F721841E4F21BF206421D621C2232F22AE1F0823E32141205E21DB2050213F211A2208239F20772398232126B72E7A2BD4254E22772144224D229E219B231422682149211421C11FF71FA921DD228621C32193232C20B621E82082217020C621DE223F219721DB207321B52015225421DF1E3321D51FB01FCC21A92149202F2241212521861F3322C0201024D221AD215E21AA219B20B122B11FA121C422612360213822EA226A23C1214824B02F6B2C84258421DF2295226D22E921AC2348230722BC216C221321C31FE0211022DE22A7222B2214202D22C220C421D420B420BF22E020691FDB201B232D21DB20C91F9E1E8620DA1F251F5121D62044207722AB22BB20FF204B20F11F74201C215422DC200321E21FB32137216621AB220E22D3228821D120EA215D244A255E31892CA325C523C220A2230C247C22E322AE2337233E2475227B2108216522DD2083215B23DB2132203D213B20C021FA20DE20B1221122DA20CB21FF1F5E208920881F4C1F651EAC1FAD202520D020B622D922EB21D7214B1FC11DF41FEF1FDF2131217B21012103206B21E4206421BC2284217824DA22C320DA20F223D2251C2F + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 A42936256425802333245C239422DD222223C321A920C21F7F1F241F6D2142209F1FCA21B221881F3F21F02026226320C6206D2025224D220E2231225524F1227E219F212720F11D3C1E41205E202921D3203322682031209B20BE1FBD1E4E1F59202520E01F081E38200320501FEA210420AF22B121DB207B1F53228B23BC2EA32C1026CA234A26042614246B2437253E25A52214239F213E2043219B21BB212E21BC21AE214421F9216B239C236C248E214823FE2180233723A624A225EF235224AA247323A2225C20A722F720A3216324A223AC214021842171236322B020B62081206021A120AA222D236622AA228B22F2235C247223CD233923BA251A2F072C3F26F125A3251725BC2439256F265D249E26E7243C241E221421F4215C221823F32340233E22442277245F2417248E242624E9225523052567220A25CD26C8266C2786255524D42162225021DB21642336249722DC22082383205A2237224722DF2172213E20DE22A6238F244723FF22A7231226D3235C2436242F27C0309E2CF72889266D2495258F251827C1252525B5243C24F222112377214423FE200A237823C7239E25AE237F24D3232F260425AB240623D9235323F423F92335267A289C285729FF2499225C22B6208F201C233425FA24D024DA23C623DA24D424F524BA22A122092241228A221E233324B42492241225332696240C26F8267030972ED027F02368232D244C253425BD256E26B025A9251C256723312246239722DD218B23F4235324D32402252828E52437259F26C722F71F332349248925A5267E289B297529E02598235D229E207C226822532646258E2597254D2687259A25D026EB243D24B62153233623D7241E25042584245E254124A9251B27BA264B2FC02CB327A124CE24042245245C2340243426B4252326CF2652245924A9231F23EF227422BC220C2564244525492776282E266B260424C42288223924C525072831288228C527E02414254521D121412160226A24A82684272126EB26F02606287628FB26D82314238D224923AE23E124B023EB24FD269E269A263A296A29CB300B2EE426702565245423F0227F24362497246026532693256D26C02590263925B424952259237C2360267C277C271429C2277D27A3271E263325C8243A28CF29BA299929B4281F270E2597237F227721FA22B025EC2518284B29202A2229DE29D02BC42853279223FD227822A422D123AF259E240027A526C929FB2AF32932339D2F9F2965278C263824892108227F233924B8257C242E265E27C2283C266225752592230D247A24CA2489243C27DB2ACE290E2A302A5C28D2263826CF25BE2604288E281F2834264926F726F225C7216C248A230F242C26C62879271D293829FF297D297927E724C82386221C22C323E1243B268327CE271A2B042CBC2CF2337C2E7F2A2D29AC2692253D238A2177230C253025F3256A26B027B02747284E257424DF246F2447246024F0232D24C428C128262BA52946282E26AD257224D824E126F027A2262926C7257E253324DB2357249622D5232D240A29EE28ED2736287129B4283829E426D025BD2253212C23D525A126EA2816298329492B472ABE334131FC2899277D262C269823E923532320255F249F272D295A29932A002A1528122716259E2506256B240C25A024F72763285E2B362BD528412966253623C423CB245E27102683269F251428742742253D25E2248123A0236024DD257725A9268C2B062A1A28AC277B25FA23C023FF22CA2421286D298C2A2229032A172A3B35CB2F592C782AB328AC27E3249424DD230F2429251929B72A882CDF2AFC2A9A29792808275F27EC2767252726F1248E28C2277D2722271127FF273A26BA236C2290241D253526D126512669274B29ED2700272C244C230123C2246524F624AD264827FC26CF2691269625AF220F2158236D248B25E0271F277128242808284933A130202B7E29202BF727F924E5247723CF24A125D42660290C2BD32BDE2A3F2A48287A272E28C72BA1297A2A06269925B82693272828ED271C276C26662617248322FB239A2358255A2667277A29BC295C298925A824E423F92318245624A4268D26B626E127D224E625F7238A222624F323B9240926A926D22543256828E3318731982CB629F6288B271126352532238A24B82312247A28EE28492CB32B902B5F2960286E27CB2AB32AD82966265C27AF25AE26A127DE27A828BC294C28C826C123012477247C236C237B26B4298E28BA2503264F239822B8249525742520285E26D4250C248C249C249D234D223A23C822F422CB268825EF254C264228DC2F5E2FA32AA22854288B28AB25B625CF253126BE252E24D124D6257B27012A0329652950296429902A2A2BFB2978286D261D268226F5266728112B7B29A928E2256326B324D223EB22592434258A2775262928722779244C23E022E424F1265F287128CF24F62585250727C523B62196220F23D9244A26E626F827A027E428D7337E2D1F2862275E26F327BE26552897266F29DF26F82696261E2456253426E027E826282A5A2C142BF82AD2291329FB277F26BE24D8243E286F29AE28E62880284E296B27AE2546257C254D25A22605286328B8281227C5232F2378242A24A025102679274128AB28812AE6281026D324A9246723D62658267D287429F5294933882DAC2879253E25BC265D28C828BD295129582AD329D226F42284246525FD26D1250629A529D32A7A2DAE2DDE293E28A2240C259025952584263A28D9289729462A79282B27A02673266A2ABB29282A5A2C97285E269224AE2392225223AF24E3261B264129D6294C2CEC2C9028B727CF24DC24DB253028DF288229FF2A9533022B7627712662279626C927C3283F2974294B29E5293E27AA240A24712372243D2553268D2898299F2C942A6C2A7A293827B926AC25B5237A25E1263228072A382B782BAE2A06295828A927AE28AB297D2AD22953279C25202686238B2263258D26F72615297428A4294F2ABC29E52529264025C6250828082A4D2B3A2C9534202C6E26B6254324C726E5289228BD284A28FD270427A32545249323E321782235238F258C287F28EB28BD29682860277A259227ED2738276326DE24A226AB275F2ADB29372874269128FD279727C3268C270C290C2704233825532297218622C226E925AB272D26BE28EB27F52A7D293D289A257824CF2542293E2B782BA135B62A3128E523AD25792725282C29F927B726BF25262758243D24A6231A2488228D23A4263626C629F429C827D728522961264628E228512A8929C2264326B925D926092790263123082694258D26DD230A26DC24A6243123CD21AF209F20D0211724ED26B228F625A328B1284928DE265526532431236723DE2765296D2CCC34932D1D264223DB24D525382713272B2748253C254D26B3260C26D223C0256B2584240922E72578277826E826B9274228D4284128F7263F270B2B6A277D263A234C244C25DA237F23232367232B2272233C247824EB22A3221324A8218F212C23DF249725AD26C028972B6F296329662B60286125E623DE22B724D1276329E933C92C13272025D2246725192734264D2675269326D428DB27A0264B264A2575255023E625C1248724A925B92886289E28FC28F9262D2712263A266126B9246C258324982421232223B021922141240322F72465251725132554239B249122742277234323F72532285B2BAD2B262B082EC6298627602647226824A325152793327B2DCB27572782244D241B2603275B275528492873266F29442A0C2B87282D276A268C23EF257B24F2256826C527C7279926B626FF230A2477257324C4259B2404254F25A9232B23F4224D237B2325253B277727F025ED266527AD261A243D234723D52407264829CB2C7B2BC72B882AD229B8296726C1254A2521251B287832722EDD28BE26F726FC252626FB26AC2784290A2ADD2A142B482B102AD62BC8285A27F72486247A221F23B82351240325EF242B25BF234F24B72490239C2534269224BE24D52342237F221126ED25C0256A29CE29EB292D29B22920294F27F3259524EE242A28F1287A2CB22DF22B322BEC2A98296F287926E324C0256A27E8332330C52AE02A0F2976270724E5255328BD28B228DA29732BB129E42BB62D292A4F29AD27E024E7245C2475244D250E26D5255325BA24EC260C27572539274B2770277E278A2494210A24DA24FF26C128AC2AA42A782C4F2BF42BE02A3B293029FB24B72470256B279A2AAA2A462B152C582C942A9A2A7B28FA265B25A32808348731C12BE42A0A2BE02979271325C72520295329292C8B2BE22A822BBA2AA32A9B29E3274326BE27E4249424F7250325942496253927F9274D271C279326412843285D294828C4257824DA23FD268127A4299B2B7B2C962C102D012B4C2ABB294C2A2426EE232225D126CD28D129EC2C512C4E2B5E2B2C2A0B2A7028142610314F31492C472BB02C982A8E292D2814269126A126D328762A062B7A2CA62CD22BDF2B3628B127EC261827222847257E25A024B225B427FC27232A9B29FA29BF29392B452B982BAC286D24B2243625E925C32A2C2C382B492AC22CD42A7E2A0E2C202A89280F262B233E261E27FB29E62CEC2C1A2AD6295128AC27B4253F27BB307C32592C7E2AD7292B2BE22A98293328342693256828302AE02A982C552CB02BD12B2F2BB828132AE329A828E426AC2662247C24B226D926E82836290F2A2D29212B822C6F2D3129D6288125AA24F725D726A12A812B002BEB2ADE2BFD2A1F2DA62AB828F426B224B1247D24B82668281728DE271328262774259625CB25F22FD7313F2DB22883294929442AD72A472AE5277925EF27A428902D5F2CFC2C862CD32A0A2BAB2AFA2B8F2B4C2B242934270025AB23E62290254E27B2283B299C2A3E2C842D2A2DB22A1E2A93263E25FB23B82576273029F32A462AFF2A362A632AE22B86286E25422549249C23A324DF24D325BC246F26AE25DD25F9242C27F5308D31EF2A422A522983288727B0288028A3288D26B7248C279928442C322C972DF42C822AA02B8D2B432CB02B9329C726DE23FB232C222223A1262D299F296E2BB62CA12D262E3B2B912A4528B5234D23772538262529E629FC2BEA282929842A9629C1273624562451230F235D237D2546247925A1258D2494243526EC25FC2FC330BE2AB12651266C26FB2807283727032869254C27CD25882610277729092C6D2C1A2B822BBD2D892AD42A2828E227A3230D230B2341232F266628092AB92B932B522CBB2C0B2C8A2A1F298A26F5240626D626592A7F29732B842A252AD429B8296B28E6251C24BF238023EB2212231023EE2340269625BD25BB264E26E430E12E0B29CD275528C729CB29642963293028FE29942719271B25D725E5250A26452806292A2B1D2B93297D2802294427E424F7239024E72215264F29882BEB2BF82B3B2CC72B112B002B4E2942281E261F2506285D26DD281E2B5D2C732A762A4029D52964273223462512259A25FB24D7230C24F625FD269429242A59290B32262DF129DD27182A502C7C29ED288A278229752B132DE12A0127B2257F27C2279526FF27B22844295D269F26DB25FE24FA2428261C259B23DC25B925FC29EA2A882D662BB22AD427B527C529B529F225E324862520242326F028A82A4B2C5B2AA62A4F295826AB257E24FD22E7223023F423502647260228E427FD2ABD2BDB33322D0527B625E52656294E2A522B282A7729012BCC2B4A2B622AAA278C250025A625F424BC27AE267F26C1267626CD2577260F28412785266F257925CD25E828452A8B2BB729A727B325C029202ABF284B27C92513240424122695277429D728812984298C284527D52720253523FE22F02495261D29D5285F2A182C3E2C2C35322E4B28172584263827C6296D29952A6B2B9B2A0D2A532B9E2A86291F27F324772374256925532612283A27E8288F2745279028C228192569259E2518250627052B022CB1294227BD288B28E729D129D129EC277622E1222624AB24F82541262D275328B529C22A3C299B243B220C22DE25ED271428BE29F32B772C222C6D36132FB0273524792540276226BD29082A602CCD2A5F2B862B602C54291428312597249D24EB24F225BA29B8291729502A7F2896274829D226572573252C25A827DC29B32DE52A96286E292E2B502B0329E9282227C22347232223262573272425EE2657261B269326FB262D26C423DF23F123E7262D2AEF2ADE2A3D2CC52BB235E62D0B28502663249F251E26EB276329042B6B2B4A2CE32C752BCD29F427EE259926EE24DA2338269B252127082985286826E9269D286626CF25B8256A2455269428C42A222DBB280928272CAD2C7D2860266527632580223024F0260D282729E327DF26BE27D528EB28E825D021442278233926332854284629632A7F2A9835EE2F5D290C286426AF2587245B252E29672B222D9B2D732B0C2B1129CC282128A9279D27A725AF269B252D263427B129E5285027252A0729E427B827AE242B268827DF2AC02BC3286A27DA29272AF02890281C27A22419238024AE246828B429A52C43283429EB2AFE28C526E824D12427242C250428E128972A862A592A4D332E30C42ADD286B2723278D264C27C228952A932C162D102AC928022BB329AB283A291A2856273F265F26B7231027362AF929FB28F429EE29D7296428C4257F26AB261129CA2A4C298428BC27282840264D27F9242026C323A8236C269F28D729202B642B092A9329312B8D28CB279B266B255223E3266527D228CE29132B4834FD30922B6F2AC52A2A2A68298728F526962A8429522B112CED2A7B2AEC2ABF29982B212AD2281A28A6255626F028C128A22B2A2B9F29432BE929A729FC285D26F925F027D52A37291828D2263425AE2532267624A42529251125A3248027FA2A762CCE2BD62A302AA02A972A4E2AE028B125E5232526FD245F29592B932C89336231B72C742C882BCB2A662B192B8E29F8275127AF29822BCD29A72820280C28F327C32944294528E5273725B326BB29FF29582A6F2AD82AD72A0F2AD629EC26C125DA2576274E28D8277F269624D92305258C26FB26012645279B244E257927DA297A2B362C162BF02B782A932ABD294D280425802628274F2A942C2B2D03353531C72E932C732D012DA62D782DF32B302B6F28C0250326C427D729EE2A9F2A052C182B0F28C729712725266126EC260329AA2A7A29D0298E2C6829AF2993281826EC249725F7251626B325FA228123D4258827D62899274C2683241525E7261E27A72845291A2AAA2B522BFD2AEF298C27EF246924C225A028142AFF2DFE342E33722D1E2BF42CDE2A652C262D312DD02BB729D328532742267E278A2A882BB62B4B2B6C2B642A462A35283427D52702294229BB29CA298F2A132A9A28B228B8260726BA265123A923C82403258A24E825E628D42AA02BAE2AF2267624FD23E72639276A26FB26EF289F28CC29F2292828D2253C2624251E280A2B372C93321833A12DFC2C542B0A2EB429252CE52C832B252B9D29A227E4249A258729D22AF72A812A612BF129B2291E29A4269A26A32682276A28CC2959291228BC27702707276725D6255F23D6225022C4231125B42751281A2B7129AC2822264E24C7222C245B268826BE269A274B283C29A62A962990289D269F267126AD25EE28DC31F230CA2C8D2CB82CDD2C682D032BFC2BBF2B472CB92BAC29B7268226B22636297D29962BD22BD32A6728862802293D28CB241F29CB29C62729290A2716264E28F7270227F724CF2180211622C22445254A289129212A282A222AC427CF241B257C23D124C52777287F2867280729F62AA62B0E2A03292425DF23F625EF28BD322C30132CD62B672CAD2C9C2C7029C728E12A292A442A9B29722693247927F428E629872C0C2B622B3B2C6B2B342B37294326A6240125DD26C6264826592677257D252E262725B3217A22C6233F24A727F3282C2CEA2AD22B2029EE284D2648249A242B267D28D129DC2BCD283629DB2A852A1F2A302948263A26E826D6285D32522ED729B829622B8F2BC929E92874280C286D288D2793275027BC268926D72694283D2B362CF42C702AFF2A6F2B3C29F427DF24C9237D24B025B1226225DB24C3250C26A7258D237722F22119257F268328242B062A4D2A3B2B1828222828264024E724AF277727392B502B092A822B922A1D2A402A1A2788268529A32AE433BD2D512A7F2A302B972C5C2BEA2902281A275D255E26C925F12560260826A4255E27F32A78286628F92726274928D2289626B624B3236425FD25E525432774282528A7265F27FB259B230D233625D424A626AE275E299C2971274629DC288B26542343255D258427282A912A002A902C362BB329FA2940274E260D2736298534C62E5829022A872C6B2CE32CDB29E1286C283927AF25B6279927F8275926A325662677262627CF274F260D269628F927C125D324A324ED2456267D283A295A29002A8C29AB2A88280B26D124F2226E23E1243C27192747271828382894289D272F261D27FA27F628AE2AD92AC22B842B742AE52A042A2228E626A0278A29C5340730262A09285829312C382D9E2BF7280328F72643285327A5272628C6275024D825032357255525A32410278227D72999293326C222AA23FE26D3267D28DE286F29DD29C02CC529F7275C240323B9210623E82533257A26A728FA280D28D9273B2688248A274A28E22A572B232A3B2BB92A3229E7268825F826B028D22B8E347730102BE5270028012B422C4E2ACC2A782BF32851271D284128CC29402A91292B2513246A25DE270428872749283B28D629DA292627A625CD2505276B290D2AC42A6C2BCA2BB12B5129FA26CF234623EE22CB256026DB25FC28152B5B2A7128F6249F272327EB296F2B1A2BEF2A7E2A6D2AEC29E927002553260127882AAD358C32A02CE7297B2793280B2BBB2E9F2D642C6D292129B628DD29AE2A522CD529FB287E2958270A295427E128A929A42B5C294828C927B624F3248928392AD4293428E0280D2A322B812A8627F324082493254925C1269128762A472D122B702B87296229CA278E28982AFA2A682B3E2C0B2B8B29C928FC258C2725259929F732C632E02E3F2C4629AE279D2A692D7C2D5B2EBA2C9E2B3D292929A32B3B2C822B0829652736264F29B727B2270C2A122CCB29EA2BD32974272227082649290F2A472A9B2B542AB92A002A102A8D27C424DE257327C327F22716290F2B9D2BB12AE529CA2869271B26D92A0F2BB02AED2A632B6F2B0F29BC254D258726D927AB32BF31662DA42EBC2AE229A9294E2BFC2BAD2E102F122D842BA72BEB2A2D2D2B2C362ACC29A7266B285228E3261428EE27A529B2284D2A3E28942616256D28B3294E2A102CF02B442B132CB42AF2297427C9251627C926E028762B4F2B772AC02A8F293228AB25BA263F277828882AD32A022A092ABF2B3B28BC26AA25CF27F7342533492CDF2C852BBC2AF828BD28042D662D612C2B2DF02ACE289D28662AF42DE02A572A8029172A8727EA2708286D28CA27BE280028E127C0255925F627302A9F2D142DC92BB52B0C2BF22B8F294327092703289028E92AC92A142BEE28F2280F2AFA265124EA255A29BB29012B0C2DA42AD629412CEF2992295F28DC284733AD30822CB42A002D122D5C2AE6283B2AA52ADF2B752C342C4127CB28C428762AB12A9D2875273628462BFF29962B782A0228A3289B2BB4276926BE25A526B728AB2B982CED2BBE2CAC2BAC2ADB29AB26B0266829A22A162BD92AB22B93296F2996295F269524FD2468275228F6278D2802284329872DD82BEF2AC32950285030CC316F2CA42C312C0E2AC92A70275228B82A882A172B332B712AFA274129BB296D2BF6284927F0293D2B772DD12BE42CCF2A9B2B952A4C2B6D28E72659279027692A5F2EE82B682D722C692BDE276A2530265F280C283F29D2297E2B4D2B4F2AAB285A26EB255F22E4257C2677259D270327F527722CA72BE329F629C429D931E72F2A2B912A402B7D2954280B2690277026842644296727C0288A288D2ADD2A532CBF297B27CC27442B3F2AF32CB72B322DC22C042CD32AB0295D284F27A2275C29632D772DDE2DFB2C762A8F2ADF26DC25CD2824295529F92A9C2BCC2BAF2B1E2A5D274125AF23CD230525F5265D265E249D231D29FD28F82932282728B230782FEA278A27CA275A286F259126ED2490256027462803284C29BC29932B322B642CCC2A1428F4275328162A2F2A732B732B652B742CC12BA32A5B289B260C264729A82C162DA52C912B16292A2912266027D428EE29EC286C2A6F2A1A2B152CD12A94290228EC22DD249E24B4269A266A257D272829FA291F2C01297C2849318A2C8D26AD258325122567253F252725492519269028BC29A82CDC2A8E2C902BC22BBD2A4229D12890281129BE2AC92A1F2AF229662C772AF02B182977287226CE269F2A3C2DDB2AF32AA92ADF27FF2427265827E1276F28A7273228D129B4292029C3278826862354255B255D28C729BA2701296328B42794295929472A2835E52CBA26EA265B26BD262027B725F3250E26AF254E283E29FA2BDF2CC32DE02B982C7A2C602A38296F28BA29312ADF2A732CF02A6C291729F62AA92BD02819288F277E2B0C2C1F2CCA2AC52A4229C426B0268726C92A502A0B2A102A4F29852A29291D268A26CC232126C0258D25D22AB92B932A0E2A722A882A652CC52D4E361D2D04272A260C277A27C6274C25D926D625712552274B28822A712A222BA32D4E2DB32CCF2A7B2850277F2A5B2B7E2B48299A274A2832290429B72BEE275025A226C62A6F2C112C6F2B602A742998272D26DC277328052BC529022A952B292B462AC52788250A253D270A260B277D29312C782C372C0B2AC12B6E2DF12EBF362E2DE32822266D27D6277F271328DD2792283026D7266E267629852B642C552AF82BD42B322B6529D9267D29AA2BE22B022ADC29E627A2277D283E294D288D265427BF29AC2DB52DE42B402BBE2C72292428EA264129FD2B9A2DEE2A842ABE2A8E2C162C6B2B3F288B28B326D126D929192C162D452EDC2D8D2DE32B772D2435692FD52A2F2B1A287D29BB2A382A132976292529D928DB2774283B2B342B272C4C2AC42A152A6C2A0B260727AF279B2AB02A122C80295E288028D02875273426F1245B27A02C0F2B962B8B2B4B2C98294029AD27E826C728F32C712A702A0B2BB92CCD2C9F2B632B902A6429A3288528602A522A5930E52BFD2B7A2D092C8E33AD326E2BE72A3329442B362ED02B732B962C1A2B212B012A622AD62BED2CC9299B2BD22BE12A962865283F26AA248828752A6D2ACC2B492AA129D4272F27B1255B253F278C280E2A512ACD2A652C99292429472883273729E82B4A2C332AC52A1E2A952C472B592B792C2C296B27EA291529082A4E2DEF2BA42A082CB0284633E032C52ED92A702A4B2C9A2CC12DAF2CF02D582EAA2B662AE429B52B382DF72D702CBA2C622C832CBF296A29E1277A258126FD29E129732A422BBD294E271C26222656264A2638278829CE2BBC2AB52B7C2C292968298729A12C4A2BD12A012AC92A2029732AE72D7F2DFE296F277727CC27EC2AD62A7C2AF4289029C42954339931522E992B382B7729842CBF2E822D7F2D212B4C2E9729FA2AA92AEF2C122CE42DBC2C8C2DA62B3A2AEF28FD2405264E25CD25E6271027D3282029142676257825C025C524F425342AAD2AE1299929A52AD92A7829FD28312A8F2B212BD328CB294329402AE12A1E2AC1281925E72586247F26B727DF27DD26E025B127ED315A2F7A2D152BDE29E12B302CEA2BA72E4F2CD02CD92C172A5A2BD82A112E262DF62C862D7C2C8B2DD02B592A112AD727CB247B2597262328E628862938291428602603261428C8288228722AD32A2E290A29702CF428C128D12B412DAA2C1C2B3328DA272929FE279E2A7A286D26E7251F253125FE25D9257D251D283F296933C12F052CD52BC52A9A291F2A032B9E2D6A2C882CDE2DE52D6B29D3292A2C4E2DB22DD72C842B072E5B2D852BDA2B1B2733262428CD296928D8280B2A4F2A0329E0286A27D3264528BC29562B5B2BA22B0F2C9D2C092C4C2A00297F29942B8729DC283C2A4729F92879291028D829552788242425D6264D25DB250428452AB5324530862B202BA928342881281C29702B8A2D9E2C902C7D2A382A9829062B702D082C122C2D2BB82D712E972CC32A0E2A3027E6270729BC293E2B162A4B2ABA28D729A12868263429792AE5299A2B5B2A402CFA2D7F2BC22B9A28CE252529F029142BDF2A48285A29DB299D2CA32AD82868266D25F424AE26FB29442AE12AA633162FD62B422B3B295B2852281C28CF28FC2CBF2C752DF02C5D2A5E29BD2A6C294A2B632D332B422B822CA92B672B4F291B289E286A2B7A2B7A2C562C042C4F2C8F2B482AAE299227C3293D2C3E2C062B532DD42CBD2BF1289C292328CB272F2B332C162B3B2BAA2AA62C4E2BC82B2E2C2C28B827C02658281D2A7C2C522D6334392FD82B5D2C102BBD28142885277729C42C7D2D6F2CC32D22290D28D528A328052B8F2A042B432C8E2C6D2CBB2A302993270028E92A942BD52D802C1A2CB22AB42BB6281A28D325D8287E2A112C8B2B872D5B2CF92B5929AB2AC928512A012DA62C112B0D2B4F2A4F2C162C692DEB2BBC29B1286B274129F72A6E2C1B2D57360632B52C582C002CC32A792AD328132A162D892C2E2B222CD42893278427BF29D32AAD2BFB2B462B2D2D092C6B2CB62A852839286C294E2C352DD72C352A802ACD2B9128B82869258527C02AAA2B232B692B9A2C6B29B9298C27F72855281B2ACF2D222B1F2C042B512A472B472B472CCE2BB129D4291B2BBF2C422BB72DC037892F892B5E2BFE2AAB2BDD2AB62B402C4E2C132B162B1E2924278E24D32529291D2AF62A8C2C812CD82AD72A4129BA284C278E28A428A129BC2B302AF52A702CAD29DD27EE256F2470276E282129EF28532A8B29E22AF828F7278E254E273829AE2B932B4C2A482AEA29472A432BFE2CE5291C29D529A42AF42BF32A472C3637DB2E552B592A942D7B2C4D2BAB2D412D782D252CCB2928280D25292645267528C82A132D592CF32B7129132D842AC22935271C2961284A2896291F2BE029DD2A93295126D62528244E25A0284A293529A8288629352AA2287B277A263427652AF62C0A2CF82BB22BD92BE229FB293B2AF3292B29D029EE29262C6C2B472CC7362A2F822A062C322E762D752C2E2C652BA72DCC2C732C70287426B8279D26AA2ABA2CDA2B4C2BB72B98298029C62BF02A2B2743265325D825F92628264D29CB2A1E2B11294527AD24BA256828A9289D28C428C927A3263A2551288B275C26AB29AE2BB02D0D2CBE2AFB2D3D2B2B2CF9297D2789286229242A6E2C822CDB2C2C35A62E2C29F829A62BC72EEC2CFB2AE42BF02D832DF72C6D293128E9278829582B942C202C552A792AAA2724286D28592825240E25D42409247926D525A428322A402B182AA92732263C266528912AE429B128D3280A261626FB28B929DC2936295A2B832BC62C352BFC2C562CBD2BDC2BB4289F28C229A9281C2AE52C282E8A353E2F682A5A28DE29162DF62C972C992DDA2DD42D402D1C2BB4284728C828872B802B9F2C602AA0276B26D227F3276E2695256B241625F62484241E268725D728F92B2B2AC029CC26842663285B2A052AF92A502A70289829132BFB29FD2A882A0B2C572C452CAF2C662CD22B902BE52DBD2B942ABB28FD2818292B2BAE2D30350931212AA028AF289C2AC42CAA2CDB2B0D2E6F2DF22D782B2A291F2A2D2C7C2DDE2DB82C092CD928C626FE26C826EC26CB252426CD26752575265A25F7271C28032ABE2ABC284925A725DA28D1298D28E42AFC2A532BDA2B0B2C2B2C0E2A07297C288C2B4A2E2D2BF12C9A2C5C2AC72C732A692BBE292A263E26A929C72B3635E930ED2BD328CB286E2AB62C932DA62C2A2C1F2E8D2CAF2B262A8A2A492B932CD92CED2BDB2B9F2A6D29502A4A2ADD2A0D2A5A2AC328D126D0289128912820276A28CF2A1E2BB9279B26A42A6A29442BEE2C402D9B2AC02B9C2D692D9C2BA32960297A2B212A802C9D2BB52CB22B402C4C2BD22B342AF228F3281129872AA034D731432C562BDA28902A962DEF2CBC2E7A2E972D892D812CAE2BED29FF286E2CC829242C0F2D282C782AB428482AD12BD029A0291E283E29F02ACC2A272CEF2A522BD12B2B2B4E29F727E4275829582B552C872D872BD52C8E2D922EA92D672A43288128CB29192C772DFE2BAD2C4D2D082C5A2B512AF429CE292329C2299A347031D22CDA296F29C1296F2B772DB32E9F2D7C2E092E212D122B5B2A2528FD2AE92A8A2BBF2BA32B842A58294E294C2C0E29E5290F29E928402BB42CE92A922B312C852C532C662B93297829442CC92B4F2CE82C602C502D022EF02CEC2CB72A90273B28A928122BDE2CC62A3B2D892B7B2A452C612BB328BC29D428422ACA323D313C2D3C2A2529AB280A2A312CF72D612EF52DBF2DE42D702DA72AFB29B7273526C527122A0A2CAD2B742A842AE829B92B1A2BD2287E29BE2C732B1A2A0A2C202CBE2A602A94290E2931296C2B3F2B4E2DBC2C812DF92B902D4D2DB82B732C032A4228E228B72B232CD92BDC2BE02CC62C2B2CAB2C102BE92B6B2BA02B35340E30932C702C432A2729592AFE2A6B2CD42CFB2CFD2CB72C652C8A2BFA2A6027AA262F27E328562A8D2C8F2A9C29F72928290E2A2F2A2528342B562B462C522A5F2BF82A2A2C7F2A8129A728772A422B572CB62C002CCD29C62E052BDE2BBB2BE329652A092A982A692CD42B2F2C722CDE2B5F2D4E2C032B0D2A062BA82B2133262F602B6729B52967299729D22A9A2AEA2BC32B782BC02B682BB32B132A55287C2516271227DD29802CAD2A782ACB29D82A212B772AE927182A142C992D1B2DEC2BBA2BE82B682B232AB8272629FB28F629252B1229E229402A3C2A8029EA29E429D0282A28EE28C92AF92B482C712CE02BA82D302C9029D029812A082B5133A9300729E529222A61288427FD272E2B3C2A8D2B2A2BCA2A252B892C382C2629DF270427D028B9291A2B9F293D2BC42AC42C902B332B0E28DE295F2AD22B402C3C2CD42B0F29692A212A4D293828AF28CA2BF729802ADF282B2BEB29F027462813276C27EF25FA27BB2AEC2AEE2CDB2CA02D982DFE2BB029AB2A3029BD2AD335F82E512ACC28F42631262826C3262C29DA2AB829822A8427C82A102C3D2B942BE1292E2A312ACD2CE129F428E829732AA32B5B2B1B2B292BB229092A6F2C402CC02A022ACA2834275D29F228B0287427A02715292A2A6429B32DEF2A902B462ABF288E277A25F525E029CB2B692C242DAB2C9D2C382F792A3C28C02673293E33F92FED29B828C9268F269326D625232AFA2AEC2A7A2AE229A32AFB2B062BFD2B8F2AE129F62B0C2CD52B2F2BA02954290129612AA1296F2BB22AE92A722BCF2A5F2A70289E273A266D269D29842ACC26DA266A29B02A7C2B582C852A5A29132882293A28A22641282E2A2A2CC32CBD2ED52CD22B2A2CB02AF0281929FE29AB33D62DC229EF26BA267F26BE26572557274A28DE29342C742BC82C7A2B8A2CC129DD29462AFB2CA32C642C3B2B992BF22931288F286826C62792274928982AB42A95277F26E025BA26CC27CA288B275D2610277B276A262C29CC2A332A9E29B02745288F25B8263E27B228E22B402C3B2CC62D182D2F2CE729432A7B29EC285434B22F2629972657263C2771258C253825C0275E28CD29BB2B072DBB2C792B402AD9274A2A232B4F2CC92B192AC42C06290E27542653264F2792273029AF2860282028AB27B62602262126C4276A281B247D2607262E27212AC82B9528B827FB244F26CA249C2596243628372A8D2B7E2BEA2BB32B132C3C2B0F2AE5283C291F33602F042A3A278F253D25542570253526A0270E28FC277C28132A4E2CBE2A9929AD28B727E429272B662A2E29102AB829A62819282F2434251C2762280A2829287A28FE279F27BA25DB24FC245B257325DA257A26FC284028072A842A9F26C225E62431242225ED21AF24D2274E28252A2829B229D12AD429F529F428AC27ED31CD2F032BB9275625A825B7246F247326212836273829E729B729C529F62A1C29A2269929682BBE2ACD2A6F2AB42A1E2B3B298C29A7270B2534287D279E28052A7A2A092A612A6526A22546248724C724BA27A227122886286E2AC028482885266A247C24B124DB24F5256723E425BD273228482729286C293F2A62277D27BD3144309B29B82826260F26FA25862656272C2690274A272D2A4D293C2A132B8528C327072A252A482C502A6E2947282C29B429CC29BE27B6260726D828882A322A892BC72A34297D27E527B025172663283329712C2829F1275328D827A629682877265325EB246A26C62747253726E127D228F32818295728BF27FC25FC254E30432E7E284F289225092577230325EA261C265A263928CE281729CE294129BE286628FA29ED2A3F2C082BEB29B32A952BE429D62ADE28BA2689260126A529262C162BEE29FD2A1D2BCD2ABC270726BA290C2AA92D4E2AF5272A299A288C26522439247B26012A8A28C228D126A926602665270329A6296429DF29432946290C326B2E7D27E324F1255B24E0233C25EF25D2263E26B9252C283629942A9B2AB52954287E287C2B062CA92BCB29692B432B7F2B612ACA2AC4289A276E278B29C62C3E2BBE2BB12B8E28392AB42709277E27DE28A72A3F2A162AED29FA29BA29EF253525B62798290D2AE9280427E02565267B26B329882B382AA92AB62A0A2A1C317D2DB1257D230323F523F72256253A24822539261826AB26BF260528902AFA287126FA270C29D82B102A102AE02BF82BBD2AFA2A7B2A652B3F29A5270D2ADE2B6B2C9B2A932A32292E2A0228982798265D28F5297A296B28FD28D72618287126C4268226EC275E27F12923281226AD276B27D1273429862A972A672CDB2C9F31B02C64276F243E245124EF226D254323DD256B258D22BD24892731276C278D273C264227DA275A297C2AE529632A7B2BD82A712BE22B4F2B1429F629432A992B1C2C552C812B5329E42BE42ABB279424C228102A3E294028B526262679286527C22710270428EC2A022BAE29F028AD2769279428F2287928752AE82B092C3834342C922766247F221A23DA213422E622512226230624E123122504256426A226E424C323EB26D02556280828B3291C297327B4291228D9277A28C129DD29C52BC12B4A2DDD2B5628F22997287729B8255A26EB285B285A26822787284C28FC2732261328042B2A2AD52CDA2BBD2B482AD72A46276D275528F42AC42D692C5C35902CC7272024F423F122B5218E21BD235224352362247F259F231E24B52535253E23F423B824F425F5242725A5253F26D327C12903295F288128DE289E296A2B562A782AFC291E2864282A2A792A982762273929D12831296C280C27A429E2291B2B19281329AA2A1A2BE929D22A062B4C28C0292C2995279129CA2C6C2EA7348C2B4A27BC23E5234423592341210324512428237122C22399237C23E523EF2450237423C323ED256D24C923E2265826ED272C28EA279D27F4258A2602278B28D8275C2818281E288C288F29B8293E2981271428CF28B6280D281728F6287629F72AC128A427EE29BD299A286D274D282D2AFA2BFB297728CA28232B982C9533642FDE258C22DE22AB23C521D222042289226E2557232823EF216D219423D822C222CD242F2255243B2400244724EA25C4256326112668231D259124572499268626F92468240726622678287A2838261326E827F2288F287F2904286429222BE32A17285027EF2663289626FA257B270928EA2A3D2A62272929D22AD62B1635362D50279A220C234E21B022F521AE22AE23A223B924C0221D22D02372235C242B234724B423DC23AB223424D625B2240F25F226682567243D26582380247A22A3258C24B0247A26C424EB2678286F264825C925AA26AF28F028F32753289C2A972B2F2922276127CA27F5261F276D27B8262A2AF629CF2845292C2BF32DB535EA2B2D25DA22452047214E2248210D2253217E228C220023742287220424F824462354247223DD23402404240624272424277F25E425CB258125B224E123A4225B236B244F25F423BB238F273027CF25B6250827E52522282B2888277A298F2B8C2999273326BD264F25F2256B25AB27212849282C29F5280427192B692E8135ED2BC5258A2293232024A8232A2257245F23BB23BA21C022F2222F224D24612466230E230C248F24E1225724BD230C258B233226D625A926AD285A27BA242A2670252724042642251C25F126BB2847279328BA28FF25472593275B27BF276428DA29BD285425C22480248625B4262C290D2CC02BC42B722AB92A962A2E2C4A36B12B20255D222124F2212324E8222F223423C3238F235722CE22832097227122B2222623E723A223552415232B25FB23952340256D24C327AC289A28F7253624712605281D276C26252687271027AF26D7287F263327CB25B8281B2BAA293828652969283027D923CC247C25F326CC2A512CC02C232BD429D82B812BD12ADB34512C03273124AD210722C520C923CF2261214722A7222C22E61F632285227C23FF220B23A3237924AE23572441248A24FB24CE244E24F726F42758273D27E025CE27C6290E2A8028C527A528FA288128242930289326FC263B29132B582BB527E92882279424A8231B25DB26AD2537297729332C4A2B632ACE2B5D2BA72B2934D72B4D262E2422239022502266223121DB2153231422D2226622B821B321F2217721B0239D23DE220622EF211B27F524CE218E2142231823D925EE26C82609283B270528B6294D28BB26642628289128ED298E2745269E28E129282A902BC62AC329A42811270E23ED2418263228BC2A7D29442AB92A722AF82A682B812BF731462D0B27E423502398220522D4227E21B42399230B22FD22C0219122BF22F121BB216321BE239222D8238C229C227C22CA221723042284241C24BC2442261A283127542852296D28C928B0278A275B29862A68295A267D267B27AC2827295B2CE62BAC2A3A2966271426CB26D128FF2A302AB52763265D27B4287328F9284632692CF725192302249522BE215622FA2260223A217821AA214821172316231421522102212D257323E821E321812226233422DC213E2359235C214722DC23A925A7265B2755260227CA2793291528A828A529AD28DE25AD25E2261628B22AD72B392BB229512A1E28FD27FF260429EB288127C626DA257725EF2468271328FA31A62AD02535223B202922792231225C22D12286229121F3212D21FC2014229C223021812291230E234A22F2231522A62328230422C6211022A3211622AD222D242F260025782566265326EA264C272E270829D8279625C4243D25A424562766289B284B287226A324EE252D28AB26FE25E024B22624242D22AE246D24E0273431782B0625D821AD21BD21962193223321BC21E821EB20D6203F2178213C223E22DF20A7203823312332235F230F226A216921B122B8227821862273212A222D22D1238C23EF233D24322620280C286F250E263827BA25D92304258A2353248B253C27C125CF25BE22E7245525FA241225EA233B254125D0237323DC2429271730602C7425A52211211E238D21A3207B220A210922FB20F9209920991F881FB8200A2145223423DA23A7217721E6231A223120A92016217520DE21D520512160232523B6225822E621F52469261B265F24C82401265E23032381228C22CF23882329251C255E25B2236A24802402268825EF2353245924612287238D252D2570302B2C8C245D23082125212421392185213D224922AD20FA1FA11F032107216B203421AC228B213723C7218A21832279209F21D3201C2045201C214222972114220323BD21C121EE2144238C247E246C246724B125A92388227B23C42242228D2320241D26CF26E2252226502514258225F1254C246224EC24BA24ED2564271A32142C0626CE23FA213622E0201221AE20BB227621992173213720851F7920CB216221FF21F6208122512135213A23EE21332150222720F51FF021A120892052208D222E22A520B7209121DF230922BC238D250024FA236D222724552389223824E523B825B727532742286127B1245526DF2657242926CC2416260228892A3734FC29C1240121FB21CD21991F0F20AB2043211F2189210920A21F8E1E4A1E65208A203621AA217421AB200A21E820E621C02016202920A620232202221F2152214C22372244216D202C215E23DD2214217E2471253D23CD218F2317227721BA239925A725F6275F28DA282F268025FF25F4258525B02545256326F9267F2AB534A62A17243822D11F95213A20B6214E22A720252178213C21EA1F181F9720932090208620E3234F216321951F2421A4218120B11FC0202A213921B01F0A1FA420D1217020EF1F8321AE212B21F023E922E82587231623B3220D2391222322AA220A231725AB282229E22A4E288C260725D0264427B4283B286A273727052B4D35F12ABE23FA20232235211E21A61FD32253210B22702260239E205B209C224122B12093211C2066232822E11EBC214D220D22C220D11F682030217520071F6720531F09205A20D31FC51F0522F7211322A924E62206237B243A2454224921672354235F25CD25B926F3274726F6256427B524FD25F8265725F7269127872BB8343F2BA323D522D920C1212F21D5203622C721002020210C2164220921E62178228D201621E620D12266228620F420C91F011F072167202720E11F3920F11EA21EDA1FC421991FF71FF920A61ED8222B22B3246823CC227522DB225622D421BC227D23DD226D25B8245327F2246E252F24E62588258A27AD25F72632283E29F7338929B2249321B421621F85217C20601F11217921352282203720A3217720FE1F642140210F22C0210420DF200E2136203D205F21D11FA91FF71F0A1E8B1EC81E211EEB1E071F781E731F8E1FF41F0921A823A7247023A2229822132382220A24EC23B4216C2100239A25E82475242624D5242D25DC2536257C253026CE28B033142B58243D226B201B205220EA202F221B21F51F8421F7200621131FC91F4420581F5A2217222D222E21E3213320AE22A3212D219E1F8E20CD2069208D205420E41FEC1FFC1ED61F101F6B21B321B222EB2102243223F3228A232B228C2253230A230022AD2296231B259D239B232B247B24E523B6242C253C268C265526C9310F2A2324B620D02059203C207620CE1F4D218F1F11208220AC1FE21F9C20D9214221D32045209420462077209421D01F6F1F801F401F321FFF1FCE1F021FFF1E081FFA1C891D561CB71F961E2621F62055210A23EE20E020E2212E23E021272358224E2137211122D5215D212124F3234A23B7228F23992371246C24E6268D31672BE52440235D20881FB9209220E42047212A2148217A228820B11FAF21CE21322109216B21F81FF420E51F7B209B21E92047212A22A11FED1FB01F111ECC1E451EF41CB21E711EE81FB31F5F20F91F1A214621242120231F24302233224C23B623BE23FD2087226E239A220D22EF22EB216B221623D5227D24F7241427CD30B42BA723BA21E8207820CE209C20EC1F9320B3209F1FAD203F20DC1F1A2049203D206D202321422082204B20FC1F0121FE1F0920971E6C20731F271F5D1FF81DCE1DBA1C5F1E8A1F57202C20881FD4209E216820802019236A2185218521DF2019230823BD228221EF213F22EB2132237621BE21A523E623922366255A25AB30C6293824A222DC206A204321031F5420C620DF20B92123200C22DB20C9219820F020F0206A218620E320F91F10213A232E1F69200F205920761EF11EE31F121EB31DE01D9D1D9A1D831F6B206B20A31FF22156208C2076206221CC1F0B20D822B3212122822107214322AC22DE216A221822A0206C22B81FF9218C232A254430B72AAB257E203722F72012213F21F11F60226E1FB31F3E2182224F21DB204E22F41FF4206221FC215F205420CA208B22C920F91F5D1F00205120B21FDD1DC21DD61D571F511E5A1EE21E3D201A21B51FA820E221F820C520F5219F1F95206D20AD22BB1F1820C51F2321C41F5322BD206523CB205B211123CD22E6232525E72E8B2AC425E0210E21D52020219420C520FC211D21ED20BF20D4216B1E881FEB1F3D21F821D11F8622502024200C21FF1FF91F671E8C1F741D7220A21ECB20461F961FC11EE81E351E7420611F5F2085201620F61FF92001200A22301FBB20772017221521C6216C21D4221A20E1211222C221DE20C821892268245A2449255B30562C7925692124217F213322F91F3621EA216F213C2212213220702171211F21CC215222E1208522FE1F7B1F9320D921EE1F76200C1F3B1E251FBF1FF81E6E201E20411DA51D171F521EC6203D219C1EC421AC218120F91D0121D8207E22B62082214421B22146206C221022F7209120C1226321C8221621C722C721B423B72ED32A97241122CE203820AF2025222321A422FA21EC20C221D22170200422AE2036214421B721DC20062052218B210A210E201F212620E31E9F1F30215B20241FC71F6E1E331FBE1EDC1FCE203C209E1FBC1F26202A202C1FF31F4A209821E1208F20C2213D21EF1F9F205520D11FFA217721E6216122DB20722266237F244A3087290F23B021302131209821591FAD20A3226D219821EC200C2187207A205721832097207122BA21851F971F13206020481FB520E41EC420D51F001E67204A1F641E511F9D20CB1D13207D20911EC61F1820AD20841F89206C21BD201E200E1F30207C208C20671F7920D01F4720F2207720A1203D22A520A32298221624152ED02A1524BB204822242267213B21BA21DB22422088206F22A320992127215C21132289212321FD211E21AB20F32144214E1FEF2062206E1F631F4B209D2088200B20991F951E441FE21FE81F64209A209421DF208F20AA1F47200B217F1FE920B320F71FA91F262090202521F7203F23DC203F2176210921ED2185225725692FE72941258C23DB20AE21BA219E201A206521FD210F21222235201621C9200223C6206E21B9221921BE215A20A4210D20CA1F6821651F55207120D71EA71E451E541FD61D071FEC1EE81EFC1EDD1F2920D5202A20AD1E46219920BC1F0220832033228C202F21A321902163209B2082205B213922F11F8B214C223E22F522D72FDB2BD8251A22E220FC20CE20AA20C62016214821A61F05206C1F9D1F4221951EFE1F8E208720432111220021BB203F1F33204D214421591F6420AF1E941F4120DE1FEE1E571F171DF01ECF1E6D1EC01E75202A21242005208C1FD71FD91F9C2015210E21C0205320F5215520DF20662209204D20EB20521F7720C2233E243830372AD624F721F420C520842121207D221F1F9C207A207E20EC1F56211C217E1F221F612107202F21A11FAD1F5B2039200B1FF820B820881F6121F91FA620D31F1B208C1F921E8C1DE61ED01F201F391F351F39218C20EF1F3A1FCF1F1C201E202D21AD2061208F1FCB21181FAB20AA1FB91F1B21EC204A203F224922A024982E5F2A17239A21052322217A21752029212E21F71FC72179213C2036211A2169211922032029219C225E219A1F0A21D621BD1E5120AB1F381F861FA91E0920851E941FEB1FE520601EB11E7A1F2B201E1F2A2021225D1F821F9521A32030211D21951F2B1F322043207E2147219D1FE52057219E1F352023204320CB214423B32CC52A77256D218721B2223E2025210021862122229B20BC20EC2194209A1F35224C2222228B217C223120E71EA2205221A21F261F0620DD20C01EE01F761F291E9B207C20331F871DD3201B1F3B20A31E1521D920151FAA1F3521F31E6E22321FB5214421D620401FAB20F61F902046227920C41ED91F0C206F211922E923712F772C4125BF21862159238720BE217720622108218021FE1FBC20861F45203C20B4201621D61F25217220FD1F3C2096216521DC1F511FF01C481F8A1E63203A1F4C1F1C1F2720941F911D6E21F21FAD1E8A1F2020101EFD1F0F208F1F221F5E202A20831FA020721EA72077202720C01F77206120FA20D7202E218A217623902F1F2B492589230021B920D120BE21102197224F21EF20E320EC1FCF213622581F932104216C21522235214220ED2055209820DD207E1EC81DEE205F202820081FF31F31215E1F601EE31D831F481E1B1F50203420E11DD71EEF200320AD20761E002100215720002102201A20E01F80210220FB1F7A20631ED921B2220B25EB2DD12AF824B722E521D92092215822F621F221B52049211E226020FE1F0321C5214F210E202E203D2175213620ED1F4D201C21D51F1E1E4E1F311E50201A1FD31F9A1F771F92204E1E8C1D0A1FD01EC11EAA1FCB1EDB1EDB1E8C1F9420A71FCD21A920251F202035218921FA1EB71F1120DF1F071FC420CD1FB1213F231B24882D712AB52539219620BB1FBA1FC52139211A21A81F5B21E21E7E1F8A1F0920E51F8920D61F6C20A0205A21431EAB1FA1229721C320051F4D1E5A1FCE1EBB204521581FE11EFF1F461D2E1EFA1E5B1E021F651F1820EA20BE1EE320C61F6E1F121F1F21B21F831E661E4A209420321FFE1F861ED11F3F218220D22029230725182EAE2B6E258823EA213B22AC2114212E21F7210821BD212320CF207E1F7A20C61FFD1E7820FA1F5F20D41E801E951FE620D120941F601EE41FE11FD21FA520C1201E2176208A1F8C1E1D202420F21F6A1F2F1F2820811E3120B5202C20001F38208420D41F65208B207321F81E0A1FD21F4021BA1E2C20F91FB920A720BE24742FE12AC5238121E0213E2129203920E521841F65204820EA1FAC1F5A1EED1E5120CF1FF41E28212B21E71F321EA7202620871F4D207520C91F771FD41EA51E6C200B20E720401E7F1E581DA41F641EAF1ECF1E651F6D1EF21E9F20BA1ED21F921F8B1FD91FED1FD61FCB1F34203521E11DA01EEA1F6320F12081213E237A248F2DE22A092472205B219B213420342103211C21E0219021791FBD1F2B1F2F2042205C20E52066200120AB20511E0821311E3920A620061EEE1FB51E5D1F211F05211B203E1F851F951FAB1DF01F991FDB1E7721D120611DEA1F311FCF1FD41F7521C01F8B1DBF20EF1F6E1EA81F931E241F221F422018211A1FFE214A220E25DD2DAE2AE5249421AF20872186218A200F231E218C202F202820711ECD1E5220D021F81F8A20A1226A1FBF203920C520541FA0209421821F6D1FD51EAC1FA61E23208C1F731D691F7C1E361E1320B41F881E9D20A61FCE1F961EFA20791F3322F11FB51F661FFE1FC01EA120DB1D8E1F742004216F1F2E20AE20872177202023972E0D2CC924A9201422CB21E52118213A235422F920F720872116208D1EF520BE20DF2160215F214D1F4221E91F9D20ED1F7B1FBE21C11FFC1D521F6F212F1F301F601E341D601F451EA81D911F191F6B1EA2202721271FA31FE71E571EF41E691F6E20451FF01EF11DC11F5A1FB31FEC204C20E220901F291FA7202E23112453300F2C2E25F0225F20FB224123CD21322217236D2266230022CE209620B121E91FAC206422F920681FAE20C11F262171202220DF212421A51F72207E1EFF1E241F131E161E111D7C1E361F741E2D1FF720032128206020D91D6E1C4D1EAB1E7C20B11F24208A1F4B1ED41F361FAE1FE4207B1F94224921261FA01FE322C224862E + USBDEVFS_REAPURBNDELAY 0 3 130 0 0 18432 18432 0 FB28092311230221A821FB20D51F3A20A020BD1F671FDF1EDC1E251E0220441E711D861F2D1F3D1D1B1F9D1E7620DB1EE31FD81E3C200D20B51F8C1FA6214D20081FB91FC41E1B1DAB1D7C1F591FEB1FB01F7E211A202420C120DA1F391F1D201221CC205620D71D7D1FED1ECB1D5920741E092123202E20F71EA6212523402ED42B6A242921EF22AA22ED208721E021AF21B81FDA201620DF1EB41F791F171F7E1EE11ED71E711EA91E5620CF20F021691F3E21E31F2E219D20B32127227E20B520B5219920FF202D1F6B21C51FF41F7322E62103214321E821C8237B222C2148217C21D32155206721612155206320BD1F5221F321B6215F228F22CA24142EBB2B1F25DB23BE2292210221A1218B22A7200A2371224C22AB20FA1F65205F20602016212520101F511F2D21F720AC20D121452237219D2135220D1F822157231F23FA2397228B227D205F216C20342079219922182296232F248D213A233A23212300234822F22010221622B6213A202820852085239021752204230026BA2FFF2BEB275324D521E3218E21BB227521F320922064202220AE20DF1F61211F1F302057201220F821EB1F5C20B51F8C217A2198226C217922722115212F20782235242424F2240B229A2100229420BD1F27215022FD225D24A524B32497254125D8254D2437245E236C226F217220EB203C21682143227E23EE216A2339251B2FB82C69265722A7218A215A226D21D121EA2147211D213C215A20C01F7521E620911F492191206E209920F42063230120DE204524BA21BE1E4E225D225D22FB22FD240626C9251B23F723BB232622FF2287217723142252230E258F26282692255426E5256426222414252C233D2359225022E9214922F9200E221924A0240E2E062B13268D22DB23D720D32264213021B322AC213A22E0229020BB204521A821CF214D21A9203922E620E5204E22382343218E22EE211022302214233E23BE24A02597264D26132592278225BA252D245A2382234D238923BD23FC250C27D727FA279427E8253126B2255825D523A5235022F8225D243C233822BC24E2257F2EEF2AF723CA2273228C21C921F522082285218E226C224621AA216D21EA22CE225023D8217E22BC213023D022B1215B22AD20302193222A23B5230A24D3250326B025E62620274D27CC27A827472782250D26D4264424A62318240D250F25B126F1270B262D270C2665259E24F6234823F8233B225223B121E7228723F823BB2E632B9425E522E9239D22C7208D217C224C222A2365210822462333242422582299238623F1231B243F23BA2169225024E621D821C422C0224F24912556256F258C26F326AC27DA26F827EA29352BA727B6294E29002785244F24612224240C2512269926C926F525BD2561244A23A6234E23F422852283215B234A237025852FEA2A34267F245D23DF2374223F2185235A24DE236423AF22F122E022D2231D221423BB2432250F25C2240923FC206E23B121112391217621782232258C258626EE279028D6278C2766276E276C276728492AFA29032BC227ED278F241B23AC2323251E2545277B266526F6231622F52212249722D9225422922221241F24DC2F4B2D9C240D2342238D23EA22CA236823EE246E2333258C24BD22E62346244B242325A22402268C2546251D259D2234236B21DC2371239C214E241C237624E026502889299B28832877270F291729F12709299C2A4B2A69283F250524E7218122D926EF24F3239025C3244524FE23102374237C2491238123D822EF24A025E431222C8E289B256B243525AD23A423A323FC236A244826E324A924A2226324F3246D252025DE25982643247425B6236E24582107210721EF20D921FF212A23B52430284C289328BD2977285D275828B625D02602263026BA2506251A23E721F02232230E23EF22CE238A247C229821FD230A248623D423332245245925B4256532942E7A282426D127BA247A23C623232315259E253D25CB24C1232D247624D3255C25B32554265F29C226D627362487235823B822DF22DD21CC20C420E022142458250B28AA27DE280429FD27BB2731266126B6241C259D24BA238B226F213F2288223F234C2442220625F324A724E5268B266426D3258225D2242C257929B633C030832B3E281B2605253E24052487226124AC238623C025652487264A26CE27FC267026D225AB284527822684249B26AA24302417237F219E217822D821FA23FE23F4260829D12821282529802A3A276E247A25662350235C245A2444220C2451228922EB20AB216A231A253126A02885281628A629632754265A27C42ACE32CE2FC82A90281027F126D723EC2332247D24CE23D9224A23A0234825FD27DC262728D52706276A27AA2644258C24C424E124382421222121FD22A1206320F91F4F2316256A26FD26CA28A428B82847258126E8251B2352228122C0236F24D92412255B217B213021ED230B2315241D279028AF29EB283D2638261F2763295434142FDF29D3285E265D272C253326B223BA25C0224524222532236925382778296328A829622A7C274725F023B6232424F6233922F720E6218E214820CF2040214324CD242726232726271C2645250125FE235E24F32376222C2395241524E224EB24D224C92341220724892365244326D627BF267027F32373246B2644276430EB2F602A92273E25E825DB2609261D2646246525C7254024AC22BD25FA27ED29C128B628222774252126F22557226A220D2101225B2288210A216B2142210A22B924B225FF266226B8249E26DB2365239024FB20672113220C24DC242326FE26D52737249224CB22D823D624D422522569255226E0250D26FC2484251D27CF2FE92D1E2A32296B285A268526CE2612271F266126F627F525F824EA24AE2508274C27CF25A4253F240B25E02238239123C0225923DB22F3203B22F522FA22352580279D2AD02B0B2971252522AE215F2262223B22FB21D8232C27B626E5266A2810286D250E2595229922702292229421E024052582256226482648267C28AE31092F6929E7289A25E82617280E2886283F29B7290D2936272025B1242823BD23DD238D244925E82256226023AE22EA229D21ED233A2446236D23052241243926542A3D2B31299326F225A1239E214D20B421EC2358230723B0274426E025E425FF2743248524FF21A723C5217E24E723352572244E24EB2434267D26ED2773332F2D802B7328AF28FD283B29D52ADE2A372B532AE8294726CF24F8236D24C1224A23782505239325162486222A24A2250323C52499249024D423D622EA24B326D629DA2A3429A224F524592320235920B1223522A223E22483253C256E25B42571259226452784238524CB22C0215221FF22D1224A231C24C427C227EF29F133532FA6280A28B229462AED2A3B2A172B1229A9276626D125AE240D225324CA239B23AC2067231324B822F32201233C2494252C2515233D2240261523A624122421277628DF25AA242923B62230216D22DD22B223AC232E25FF2700266A25A925A426D225B0259C258125B221E120BD23D32201232024DA24A327F429572A3933A72E5629C0299F2A2F2B622B3B29EF2760274526AE26252461220B22F721F222F421AB240F2379221523B82546256A253B26B924622569247D24D9242C24E5254426FF265925C52489229022412505237F255726C1267527ED250A276C24F323BE24D8230025ED24D625182419239226EB23A1249927DD25FE293B2B2D2ABB33172FB228BB28E4272E28C4289527412662261D25A3222B246B234A24DE222A23C523192295243923DA24E7253227E326C825F425D423AF241E2641245C25FC2321250D26EA24EF242E257A259925C126A1286428C226F2261D261725CD221C23E5239A257425D6269E274F2517254324BF2418288E275F2AFF2BE12A482BA433592FEE287826D5264A272927342673241B2524248D242824DF2336228A248F238323772233238321F92271257A27C427F926AA26E2243A25852530231624DC239D22C8235724C624C024C3282728E82612295A281827C32499245D23CD22F523FB244B26A1299529002BD52AA02729265C274028F329B82AFF29EF29C329CA33CE30E4297E28F625AA256A234924A524372385223D232324EC21D2233E26F123F7234424BB22AC231824A626242A732CAB2A5D280525D825372524235F23A0229B22C6231B2244212925F2264228482810281526A526D124662461233423E224B6235D261129732AF92B39289326C5267C28E3287C2A302A882AEE28152AE4332D32022B872765252125E923EE22292315256E23B5251A25C6235124702484241624AF233F24B42677240925E3286C2A792A5429512721256A23D722F5216B22DD218E229C22D922A0234E24642799263C26D2252626C92592268F2430248724D426DA26E228972ADA290328E424F2262B27CF27EE29ED2A352C042C672AED33FF30252B1328D7261F24B4239E238B23E2232623AF24A8256B25CD26B227E2266B272424A42517261B262E274F25182864289A283C274524C2246223BB23AB224E23B0222D23AC2260216D23B2241D24EB269C26BC2589254E283C261C25732606252727DA2807288929A6271A26B42663267A252427C0280E2BD62AB62CFB34A431122CDB2876254C2558248523D123D023DA23922646288E276B29CE291D29952895271426DA28D927D425AD24A22687259825F7251F2350230823F7235B227C23F4223E24DE2177231022632393242824B1269A27872816297C29A226E9261D25EB2554284E29752A7C281E274226D22466243426AB273A280E2AD12ACA331832E02DA228192869259724162496245B247424B927BD28C62DDE2B572BEE29F62726286328FA295C2949277D25B0242C24342376213122D9218622C022712349249F24C923FB213223E0211B232622D023EF24032724293A299528E42493245127762687273C2AC02B942A6929BF2634251B23A3245C2514277B27302A23342E333E2DF62B1E2AD426AF238724B2241E26FE25B425FB287329112C022A282AF129DE28F3295929F628AB275325D22358223423D520B4202D22EF22EC21F423E22451250525B822F8220023652141221E24462465264727DF287C255C25922643272228F427152ADD2A2D2B082A7729CE25D2245024B823AD2474279327A1310F331E2D9628BE265B258C26A624EB2461272C267228A82609275126F226C2276528B927CF2740294B26B9258C23FA24882166213A219420802193214A22462336231523E52367239522502338231F24922536259B262B24C5259C24FE2417262D280129802937299D29402A6128E9257023BE22A423A62229232F25C425D2304D31282B472976270227F924EE2411279827432A4227DB268F24CE2416247823282583253A260F2506232B2240244F239E213321F321B31F2B2104227E220C22CA218722D2225C22CD229A22DE24E72599262828A8233423CF236C241D23CC243D250E287C271E25072871281E28CC25B8223621BF21BF2133248124E924702F21305D2CAC28B028A3272924BA24A6253A2A562CFD2B8A28A8246F24552622262824632427242124E920A0215A2160209E201F2248219A20B6213E202F22C8217B2480230124AD2145230E2617272A269127F327E12322235F23CD22C8231023CB244E25FF232125FB241E24F4230B23E321092383214F22F721FE247525832FBA2E9528B7269F2501269C252E275727D128C22A612AFD2738266624D223CA239B2378214E23E7214D21AB21DF2001204520DC21D321F22148212E213A20E421D6229F24AC242E24FF23DE275D276927CC2721278C24B8225F220522962244210C2332231323D322E3240C24BE22EF2195225A22B32377228B23AB24332530301E2EDE28CA256426D525A327E92580272529F6286B27FF2571242224BF232E239821E722B2210B22E0226B214923A42140213822AB22D61F7F2153221A21C421A924B425C725712590270A2628269825662746262922362225222F21292107211321FD21F42289238A2338217C20EB207F2323244E237224E725BE258A255B31462DF9262C24F725D827C226272894262F2818269F253A247E24942238235922262279227A22BB225F253F240B24FE243323672207240D2103211022F1212B23EE233627B626DD263628F72754262B246125CD2495227822EA2097218D22E51FA521FF20C3200021F7214D2350228423A2237E25B82782271B2745271A26A430FE2B1527A526BE257827A127AA27C725EE246024C124EB24A023DB226D22AC2234248C23A0223825C1241D26E727B926112497245C257E22BA226D23AB22A723B8233825B0280E275C27252A7529D424A823942520249D21B222F7222F227C2250217221FA225C24F324B6231C223E248026F428862A7D2AB5296F286826E131192E5028BB274B27AA26A724FD232D242023A7237924B0223222CA203522B9221424142561246D262026512773277528562689259327D724C323BD24502366244824DE25D22576257326AC289C266C24E6241824DB222C21EF21CA1FD420C1204823FA1FB82296246924A224F72507288A29312BD42CCC2BE32AF5276F266030882FC02A19296927D22682256B2514258924B9235B2420220021EA24ED23F2237126F2257F2644274828CE25FF2793290E29CA277A27EF257625FA2422252027DE262027CF268025C82649260E26C423F224AF22CD23D4215421F4218E21EA20F421AD22B822872338272D27A229242CFF2CA02BF92DB42B2A2BCC299329D932BB2F242949278226E2249723EF22882231258E223323D523D9236324C325DF25662968290229C3284626322603282927362A70297C263426C2232024BD250326C8271E29F82859248723192415233F237A231721BA21CE2054213E2018215D22B722B6227623C524C026AE28D529EA2BBA2B8C2B9C2C8229F62B5A2B612B8F31682ED827A7250424652238224622622265229A227324BC24E323B5236B25E0266328B22A842AE828552749245325C9278A272327B125C3247A23BB227E242C24822679275D27DB24BD23F4220F22932132221222D220DD1F0C2229205C200521FC219023C5253F271D2939280228FB28D02A112B7D2CBB2B0D2DA72C922CA433072D98282E2599258A2412245C23452392246C23432277233E2588273928CA28F72B662CB9286B2936258224F224BF247825C2257A236923CA24CF211E23D12467262927922701265024C023F821052247239522DB21CA20D6206420ED217A237B226E246A26F028992B842AB32849276A2724284C29962A162CDA2BC62D59344F2D262685223C2401229822B12276230923732269234F2486249225CE27BC28202A892BC12AAF274F25C1231424902495248522F321EB20F82184226B22D5246725C927292A8626CF2480251C25FF2232227822E22200233B236721FD204A21F9233324D12484279D2AD1297B28012633255326A9294B2A792C192D102CD831302E5627AE2517245F269821E423EF23E6225722002305243624BB25E228E329952B7E2BC02AF9263324A4232D231D247C232122D321F322BE221023E4244D267F2705280B2A4A29A028DD2604267B243E24A622BE2340212422FA213F22DB212B237824F9243F271B2AD82AF728BD26CE244F267C28B52B542CE829B52BDE32E22D24280926BA25A22558251123C9238B23C623D923C423882379266C2666282F29222CFD29812661220F23A92328249421DC2487235821CF24AE2454266C2A542B322B552A0E284A272826E3268C24452463238D22B021D8224922C322BD244C239423242592265E28182815275226892592258C27F2272E281A29D52AFC331E2FBC29D02709264E26F925BE225822442516243B24D2236422F722022766284E29712B1A2848258225AB24A4243124EC22EB215322CA23892439261229F629B329652A362A2327E526C626CB249525012483259423F223C22126236323F4242C2645266826A726D027EF248B25DA255024D5248D2639270D2991291C2BE633C32FD929662789262A26682402247C24042598265025B1243324E3247326D3279429EB2AFF288327F72320241B253E243A253024D323A8248225FF23172819287728AE28E52855270C263E24CB25BF248924EF256A245A24C8242F225325AD276227BA26EE26C624F326B1257E24E925E924CF241D279D26AB28E62B9A2CDE341230372B022866252226452507256C248F25892581261D2401234824F925BC276C29532B0A266C256B24ED22E62346250625C3253226EB275628DF269C27AF279F26532529274A276926CA252827EF246D252025DF26D9252A23C8249F25AC264B264A280D26DA257326DC2440241C27722592248F2603261928212A392C5635BF2F4F2997272A2630246C253A232E243A26A9273626C6256724CD24A22458268E28F026E8259825F0232E2393254425D6245D261228E828D828F92876266E240E24522450273327AB27B5278E267326D026CA274B267F250D25F62399242B26552749299B2956275D26B125CB26DD262F268826AE266E260328C7296E2B8A34E82E4929752685254B25B3253C258C236D250B2788286D26A525D42404258C237C264B238C240C24F722E724FC245B274E27A126D925472739290426DD24C8230224042550296029512A2829ED28932763275B289E258125B8266F262525032654265426712811276B27DA26F72502284D2881264025B025CC28702A622C0A33E62C46288B253D262027EF2519236524792600261526722798260226A82412255322A9226A23522591255424CF2486249526942793274F28EB27B52615266425D425FD265D29772B5A2B302B742AB129CA279A272D261C241A270129DA267E24DD22FE26AF2642281C274C267E25892601270F27E2256D25CB28EB28022ACF33902DC327E426A6251827632744281F26D5259D249B2642286B298A27F2248A2285232126422443260524FF25B225B4260A245F248C26B72635285A2A4A2AF0276325C1265F283E2AC42BFB298C29C828BC28BF255925482629283A2ADE277B263525B32605263726FB27F6262226812652253C24EF249D25F82957283F2A9E32C32D3629AE27EA260E278628BF2708269F26CC251E279C27D028C427DF24D122DD21CE225F2319274E25A5251A27732746244326A525F7265E2AF829792B402A9629C3292828F927A7286729CA28C42690267C26EE24D4235D25732608260225B82412254F2592242E292928DB2527255D240E246A236423F0258F2865293834982D0D2896280627B028FD28D427B924F825C32610272B29022BE027A0254B237C226124C22228262727AF26D9270927FC262B243426B826C429302A2F2C9C2A2A2AF92A7C293828A0289A27E8276B265525D524E522C22315261E258D244425C724FA24FD23D0249C24DC240D260325D5225122A1247223882577266E29E236DD30C228B227BA26E527A3270D269826BB240524FC26C6289128F026EF248925DC228B24312508265224E7260829DC291F290D283426A0278428D529E92B212B732D7E2C0C2A042823266726AD2449244025D2244D23D92447244625C5234025EC2682240023352453266B248025AB2615243A22FA23C7229625BB27302A03359830B32B6E282F28462872276925DE24F722492437278C2A75286128B724CD23652357233925A0255C27F4257929362BCC29F729292C83289829362B8A2AA12A9D2CBB2C222BC82AA1274824CF23B4223724A6251C249C24F924AC269325822660281726CA244C2545263725C123AE24CC23F323F1254224B52551289E291B33A132822CF02A012890243E26BC2383247B256324D0269029D02AD226C824892201258525AD2667292928CF27EE26A0294B2A742B292A792B3E2A942A062B172A032CA72EFE2B4E2CD0281F2512221222AB23A1242E238324DD25C9270828C527AC27C12617272424342762264B24CF251F25DF248B27EB24BB249828CB2BE4344E312D2CB6295D288024F1231C231A25CF22452277266D25ED27D92518266A24212603274A2821292F2AAA25FC267626B5293C2BB32A8929F828DA2738296E2AA62B8C2E8D2D882CB429A325542530235E234625DD24842575289229262AFD283C271F267E262526A5262F2714280F278F241E23852690242526E326D62A36351332DA29EE286626AC2594225C24112379236E24AB244524842573259F262D2558263C27AF276729E1275226D1242D26F0260228042A352A6D289326C727D328642BB62D522DB62BBA28B624F62401232E25AE25E0268F26742A4D2AE3295629B32753289629942750297B29322AF428D0265A27D5277326F927A226122A39352530BF292B285925CF23C5239223F623F523F8239A2459245426DA246B27D9261626F02503274B2884272B2738280D27F825292682293629162AF228FB291B2A502A9C2CD72DF82AEA293028C2242A2397243525CD2667297B2AAB2A9C298B277B27C529D02B1B2AEF2B3D2BBE2B802BCB27CE2881266624CE25702520291336642F4F285B27D22508256F243E236D232B24352324244223A92487261D29B027DB26F42653269F2706278F281A29C729E12A6628212611260429302BD22A582C692BA62D9E2C672C952A7129942752257B25BB24C1290F2BA42B7A2AEB279327D0277D281E2BFF29F32BC4296927B529EF28D326FC243324432474263229CC33922E0328F42568250E258A243622972378233F2371243E243B255325F127F22ABA299228B5275027BA27552A1B2B272CDF299B270627EB278C289B2C5E2B332B352CE22DF82C502C162B06291D2748258D254F27EE274E2B962AB129E329ED2824297A29F729132A592BED280B27212613269925F324CF22A625BA273C292033A42C2F285D243824CD22FF210C231F238E241B23232406236125B227F02ADA29CC293228D2274728EB26D5297A2B0B2BDC295429B32683262928F22A7B2C542C6E2D812D442E612D442A0329BF298E273B27BF267A28DC2A3C2D6B2ABB2967292C2BF12BAC2C492AC52A7A285E26DA25CA242825A2260B271928222718298C31C12CE927E2269522B422D222AE226721D622A9233C244B240425D528FA29502C5B292228B326CE28EF2585289B28982AA92AB42B3C28E62548275529F62A702CA02B282C6A2E042AF929D429032BE628C129A728E6260228272DEB2A9C2A2C2BBF2BBB2B542BF92B332B8F29BF278525D7247923D029EA273628BE2ADA2A8132732EF825BE24D02261230625702215226923F622A424EC247125D927FA2A4229142AC829DB2831273129A8280D275D2A4C2CEB2AB32B84286C26D4254A285F29732B202CB62A252AA6289D28902A4129E129DE29FA271727F5294A2CB02A9D2B722A6C2B2B2AEA2A722B852700259227242589252429BE29152A392D912A1835A42DC327C922B922B123AD223C236822762306240023DF2383246B27322A5E2CCB2970290A29882A4A2A822B802ACF29D22A702DDC2B932A47290327E925C7272C2A0B2BBD296A287B284F296028CE285F2A09290428D2253828E8274929892A732BC0281029BE2B132B4E27572412261B278D2AE62AA12B942BFF2D2E2EA036412C9027D323B9243022292335243123D122D9204D25A9224426B926642AF82A462B0129CF29B22AB82AB12AD528602AEA2A572B8A2CDE29BE294D27F323BE25A228472A66297428052A44289925FE24E4267827EE25C024C624CD2560272427B9299C29E72969291028ED25E3239A264927B02A8B2C522D6E2D202D942E9036292A842654239F228824C5220B217723F82002229722C921232545267C2A842B7C2A132A31298E2A6B2ADA294A2A29290C27E327F4270A293C2878259824E5253828142A4B2C092AD426222604252123A3231427FD225B23C525172632268B26BB25BA26FD272A267028F525D3250E27EF28142BF02CEB2CD32C1E2F312F68365E2B1926AF242F245623D7227F212E23DE21CE21FA224B24E722DF2536285E29782ACE29B228F02AFB2AD228D929042665250427E327D8255626E9262525B3248328FB29C729D8278325F3245825E225452647269D25BA24A3234523A7242123DC24E427DC2660268D2614253328EE26BE27572A272DCC2BD72BEA2D6D2EED33122CEF25232491221323EB239B227822F42232225222862192230C255F26F82742276028D628BF2B012C0B2A6E28712898254D2593255026312860272A268A248C280D291127BF266B25F623C925C0244C27F128F6269527F2252A23BC243125C2264028D62655273E26E027822688264B27E8287A29AB2A772DFF2C102C6F33022BB0256B23B421DF2287237E22D3200723EF21ED22CB2309233324A826CC235A259E285A283629252A6D2830282F28F22729287528E9269E288C2855276C2828292929B728672460251F27E627FC26C7292C2A272A5928AE297D2804266F288529282A702A6E281128A4266A278C288F268B28D6288E2A1B2C582CE32B92329B2A522504242923B9214D22C6218722AD2316236F22B3242F227D235F2662253827C7261528CA28942860280C283729E929BE2A4D2BF5286629E4278727A9277929712862283B261A271228F129352BF92CF12BD22B962AEB2C862AF929CE2A6B2B3A2B972ADB288629C428002AB728E42675275A27462A4E2B7F2B3D2BFA33D02DC4256E235223DA22AA22C720FA21DC235323FC215A248B226E23B7240E27AC2663275C28152740288E27FE28452A8A2AFD2AC52A7D2A9428D12601255427F729DF28922A24283828D229972B172C3C2C0C2DCB29242BA92A5F2D112A7829AA2BB02ADE2B922A7929F629452A462A362981273228742A232DBC2A9B2B3135602C79256C221D2215236B22C222BD227B222122DE238C23AE2309225F24B62647251E252F278F27612641265C2680286928102AFB290D299027C2241126F0292D2A582A042AAF28D129142AB82A632B002DF62A632B372AA22BCB2AE82A34297429282A8B2AD32AE92A6A2B5F2C622D1D29C827282A192C472DB22B812C6D36CD2B2726542115231C22A921BF239C227D224C22D72274235E22E9232624FA245625D52527251726F52494299A28D928D626C52868279A2610262325D423CD2660289C278E281A262926DE28D6290D2A012A512ABA2A5529F929C32AB52AE929EF29EF28ED29AA2AA82C992BAB2C272BE5293D29DD2A242B032D292C592D8E37732C5226EB23E4238222B52117224A211D23A82263240B23CB2311257623CB25A7259C24B424722671261227262ACB2A9627C525A524252550256C22BA24AA267B29502A402AE5263A254427C727E327BA279027D026EB25D429AD2BDB296F2999276629EB29382A0B2E3D2CC52DA52A452730293D2C572C432D6A2DF02D60364D2B1F2548236D22F723F021B21FE2209922B922FB235D2251239923E02470247F249C24FD24D026E5251C288B28B728C2246426A926CA25F926D524A12564277F29FB2A21299926CE23B324582799269725C42737261327CB29F02BF42B7F2813272A27942AF22A262DE92CC42B152B92276D28082C1A2CFC2C1D2E002FA336A12ACC25B2219A217E22D721322161228D226822982268225522B02250222324DC22842447244424B826A729002BB128CF27EC2768292A29E3271B2782246227552B4C2A5E2A2426A3237A2390255526C32777289A27262A772BB72A122C792A7A29F828922A732D632DAD2C5F2BAB2C06290D2864297D2DEE2C272DB42E1836672BD4246522D3215B21A5216721E220E12266223D2330226E229C233A24E4240E2543241925C82463268D286429DE298229D12BA72C1F2A54290627B6276127012A3D2B2029A125B2231225E5259B2528299929092A0B2B822B2B2C0E2B392AD2275C296F2D342B682D862D822BB82C842868285E29F929E82BFC2D9A2D8F36112A2E25A822592281219221F82168211A211523C32175224523F3239D23A123CE2331232E24052593255628DF288D29EF2A4F2DF12BC428BD288F2721270725DF26052A942BB0282A268827B2255E29A92BBB2BE7283D29662CA02C212C9A2AD029D02B702ABF2CD92B272D6D2CA72CA229E429D92A5E2C2B2EC42DB52D1236312BAE24E3238C214322D1224521182344236E22A8227022B6237B2368225B2562219D23D224F624B0241C2424275428992769292529F1283629D127EC275B26ED26F728502A862AF2285326BC261829BE2AC32B9929192BB92CA12E1D2E0E2B8429F32AB22B772C4A2D3B2C962D922DA32A1B29F629DE2CC92DFB2D9B2E6137562B7325432267223B228121E42102237D2276230023D0228C21FB22E621E8252C24E723FC23E82342235D23D9243C290F27FD289828DD27272823280125AF25F2262A29072BAF2B532A1429C92848280929E4294E29382B742DE82C3C2D112B5529672B802B762CC52C0A2BEE2DF82B7029242A612B2E2B062E472DCF2FC336202CE1258B213421A2218621CA2002223723D62229237E231323B0211323FB22482239238023D523D323A323E725F7250E28A427A4263D270D280E25EA22DB240926C226BF27EC279F28E927DB27E3258627482749287628D12B0B2DC22ACE2A902AEE2A682BAC2B6D2A752A5F2B642CF62A5129BD2B0D2D922E792ED02FE4378A2BF9256F2388215821312283201221A32107227D22C22266220B2291237E229B23EC2327245A235924CF227923392508245425B0256424E526902478242523C32538275429C128EE288B285129C32610260127B8272827722EC72BBC2A5A295629712C7F2B2B294729D6289E2AE62B532A562AD32A9F2CBA2CE32DE02E3336EE2AC7256921D621282265223D227320C5213F2248222D22B621D5214022AF22CB218F23D322472475249C225423A0235724B924862502249A25D524DD257B262228A529912A8429F3296A29642AA426EA24D026D926782A672C772C2A2A1F29082A892C0A2B6B2910299329DC2B972CDA2AA42AF62AC52B972DF52D392E7C36652CB02355225C2280210F21A6202722CB20E4229922E92162216B22C22249227E22F0212223AB235C23EF215F236423FB2453235824EA228F25CF233E245B262429BB2B2329242939291E2A4329CF274C28ED25FD27A928262C622B7128B028212A962C812A4E2AB32A022A7B2C542CCB2B4A2A802AC82ADE2D692C1B2EA737E52AFA24D2219D209020C7205E2029219D212D2167220D1F072298213221FB2255225122D92163237221F620E322BA227B2323235E23F6242F2479232F25EF26A827AF28C827A8255127F326AA273D265D263F26492776279C2C0A2A3C2B532B9C2BAF2C112BC729732BD62B242CB32B7329D028F22C342B312BD32ACA2CE9341B2C5E25D422F620AA214822762044230C2201222322B3209D202021E220B7231E238F211F2294219921E221C4216F221322DC23AC227B24702412246F246425B926AF26B926F123AA224D26C227C2245325E8270C283829B92A8D28F9263528F12B4F2C8B2B012CD12BC82BEE2B452CE328DB265E29172B302B5B2C102DF234272A9B255621FF210322BE22AF21F9217C21F5211D23812148228E208122DC217222DC214D234E22802288213023F3224622CB232621AC2293223A22CF234124A023F32494246F24B62476259C245B24CA254B266A259D28FF2913290128D827782B9F2AE22B8A2A232ABB2B522A30291E2902282429162AC42C532C142CA136072CE524CC219F2129232422332267218A220C22AA22372343222D22DD216722B5202823C621FF2267229920C523FA200021F221E621782281226223BD21C2217F239F241B24BA23C223B0259126FE228C256C25B226A6291D2B37272927C1261F2A482AB92A0B29062B6E2BBF2940282527FB26EC285D2AD92B452CEF2D7D36D32B71253E2104215921C721CB21CD217822EA222022532142218F210421BC218321A02072219621432164200B21752086207A22BF1FF52028226A2212217D21C522942222239222322387231A242F24972496254B28CA26C0279D276B25DF2741297F2A4F2BC62750292E2B1329C3273F25A526FF277527E529CA2B3E2C8236252C292661219A203E21E32051207121C522172171225F22AB201D2056223821991FFE21CD21BD202B212E21BC21B6219C1FBA218F21E020B823B0215D21532216232F234F23A1209A22B1222F239D2245250A26D426C326FD2717252F2752289C28392A702BFF2B802C8027BC26E0260A27DC26502793285A29D328E82B7936682D9725F7229C217122E022BE227922802072214C20AF21B01F8A20C22107213E2128220D203D210520731F3A1F6C209420C120A6205F22D2213423CB221F21922353239021372046238B23BA23D6248724A2277525C9241F25EB241029C629462A9F29DC298E2BE92C3C2907282428BE29D02A9C2A072AD8291329D329F933192C81250125F321AA21C520D521B2224721B220F2216E210C20981FB11F2D21F5216622F220CC21F220822069216E224E20D3206720FE20FD21B2200422C6225E229F219422B122502313231623D32562230C2781249B22412472240B26FE261F275D280B2C3C2B932C042A07298B28012A832BD52AA42AC02CE92CCE2DB1355D2C1325E12162222521FB20AC21CD21A821EF20CF1F25215820212007203721E6200C212922102178215D208321DE20C920C71F5D20FC1FF4209C203721A722252102223422201F102212218B224C2218228923C12250236C238C2457271027122722289729C62A992912284D274029362A112CC62B132AAE2BEE2C4B2DD833032C38244D211921DA21FA202B233921DF215C22A7213021432001202C22DE21F92045221F218322B1202D216623342288202D202220E2214B2117207D21FF2177223021A8216920062245218C2218229D22BC23A622C222A42460232B275128B328D826B227CD2604293F2737284D2CA72DF12CCA2C392C122B492DDF2E3B34072BB925C02276227B22A1204623A620F0225422281F4020E821FB20C020BC210D210222A021D3214F21FA20C721B922E0208C20C1200F213C2011217920FD208A210C2214220E20F52292224921382096235E24BE228C22C8222A2305273728CB28CE2656252627B4260C26EC277C2A8C2CC02D962DAC2BDF2B8B2B232CB734942AC925C222B0205B21D61F3F20C22018207E20C0207120DD209D207F21E0219020A71F8B22512001220E2152221421411F67210E1EA71F2021F02122205321EE2161232A22921EC020AC1FAE22961FC120D222FF21DB20DA22AE24752540279426C7260227D223DA26AB269B285129F22BE7284F2A1A2C8F2BBA2C412B78341B2B37265E2203223E21FD1F0420E9214122F920EB2165224720AA20F2219221971FE9205021F2216820F41F0920F41F25219122CF2063200B227C229F212E226121CE217E21121F581F0821DC21D4204C2131220E217F225722D0215D2555276A2A16278C256F24A1240B25942708285F2504289028FE275F29B42AD02BCC328C2AE025F72164225E22D621E71F99227A22592173209F216421E1202B213922882015216B2186235B218320AC2298213722A92152217E216E20D82180212A220821DF2197215921C22004210C2277228A2131213C21CF2101224922AF2306269F29CA2789244D25AD240C25A6254026B826E62641264D261127A82773281E30582E21252321962195226D205121A020EC20C7238F21D720E51FF81E6721D1205020D3225220F6218C21DA20F120C3213421A4217021AC1E6620D6207920F8218E212B20571F4120DD1F0F2138218F2027217722C8210721EC2216229D23BF257D27EC257B24A1227F25622536267E272A26B526B125192456262E26AF26F130382CEF264621BC21A81F4D2183200E21E821CD21AA227B20F31FC3210121EF21B620E3214621EF20931FE3200C22C1201E2171225D20571F8B21581FA521511FF120DF1F681FB7208B1E4A1F6320F11F6620C4207A20F3200B22EB216022FB24C426D425E3238D2357259C26FF274E295D2777274D252A2519269A263328CE308A2B69242E22D71FD62059218620682199202921D8202721CF20CF201A22F822AE2029226C21B121A021D420CF20DF208C23F4205C20BE1F901F2720A020CD1F79207520B020EF1EEF1DDB209A1FC51E6A20A72204215922D62186227C241525BB23D8236024F02423241B260A27652ABC2A432890265D26A6240828482ADD31EE2A3725E02139234223E4223A21682349226E22632018214421842013221422132180205121F421CA1F8020D51F5A21EC1F4D21E81F6C1F4C20CF2007204122AE21841FA820781FDD1EA12058218F1F362285231121CC1F8E21A821AC22CC224124C223BC220B23C02347251D278D29392C402A282939271328A728BC29A733DC2BC4242C22A4238321D0237D22A221A9224123A42204217B212A1FC320AC20E520E5203D210F216321DE1F0622BA2028206121B71FFE2012210C210B20B61F7A210322EC1FA51FCB1F1D21691F171F3122F620F92133202422D4236C2243225724E023A6246E22DC2387243326A729582ADF291228B5250629352BB52B8734162C9F26B52315216E213B2014233022E120B9217B21EF20CF1EF7206821DF210921EE208421072208216721FC2090218B219321EF20172297211F20AA207F20DE2188221921C41FFC1F4E21C8202E206E2289221821CD20092275228822A4204D235B238B215321F4227824122372261B265D278826D425A028D829852C65359D2B1D26D623CA220422182202227C201D216822292199216721A5204320B020D11FD921CA21C0202020D21F95241322441F241F7B20E41F3F213E21762044227221F82007212B1F1D1F961F6420C61F72225E210A210522C221C220452290211E2270227622D71FDD21A3212B23A925BB24D9258B25A625A026E128F42AAE32132DBC26CD236C234A2256217722052174230D237B211522C620E421A121B2207F20082016220B2126222E21FE204620E62036210120082211219320AA2005223C21EA214221A51EAA206220FC1F21215B22CF22A920B1207620DC205F20E322A822672233220E22AA212721E7218D248C248F235C2311242725822635281832902B83252F226F23CD21192188219822A5213E208D20AA205220D321DE21B41FEC1FB51F7123C0214A20152044202D21FA1FFF1F5E212821AB1E9C1EFE1ED61FC5206521521F9D1ECC1E8F210E200D210C22BD217A20E820FB2050217B23B12226225A21C1228521302360219E223C2262224F23AC235723DE22F02500276E319B2AB1253E22F01FB2212E22F721102245226E222021622168203420AB211F2251208521BA22F3211B2182231D21AD2204220D21F520CF20E81F851F441FF81FD5217E202A208A1F171EDA1E7420EB20612257226C21E2207B215B2043227022DF217C224121C3205A2264242C2251222722A024CE22E72140243B24EC271431EF2AA324862191213F210921F621012163215C214A202B20A8207C207821A2211520C01F41221F223B224022ED202D202820932179210120B4200F1F551FF61E15208D1F851F9C1E911E2820D220591F5F207222FB21B120162220203C20BE202F22FD205A21511FE9212322AC21F3218E21C723D323292361230B25E926EC2F792C9D250A233F211623B7210A21A322B120FB211A21EE207020611F561F47209920AA21B0222F233B2120215C235A21C61F0B207F20C91F72206C1F711F1D217F20DF1F3D1FEC1D391F781F4E1F121F0D20EC212620682013203B201121F71F2421DC20202186209B21A121482317231B22D2225223F7218A2378256D253F30132C62242123202110218C2134215321102230228020961F111F19207720D11FC4203F2218215222B820EA209D21E11F87200920751F491FAB1FB920A01F22207F200F1F001F021FC61ECF1E811E7D1F36200F22B72011207321AD20C21FB4201620A121FD21D0209321782189211C22F42214228A224A2346234E24DD258830EA2BC8259D23B6211222BB20B120A0209A220C2160213821E51F3C1F2A207E21022198216020EC21C120B220A3225821C4207E214C1FBF1EC3206B1F1D1F5D1E6120DE1F971DCB1D1B1E971FEF1D6A1F002295205C21FF1F6122972129204A21F41FA620A521BD20EF2117226620B821EE220821822339228823F8248D26882FDF2933252A214022C921951F1C20E0207E211621A021F71F981F541E391E4C200120B520502130214A20AA208720C6215620411FAB1F0F201F214021EC1FC01FD7206020D21EFD1D201EAF1FE61EA91D43219C22E220D31FB5216020F41E08211B229820382175205E2122209A206A21E721B521F5218121B222FC223125182F4F2AFC234C22B41F9E2121204C210222832029210B217020741F9D1E7720DE1F3A200C20B623FB20DF20E31E3920DA20E81FDD1EC71F3B201520EE1E361E0E1F1220D01E001E5B1FD71E3F1E8E20561FD0223621B3204920A3208C2010201D20B51F9D207422D72054229B201A210B200722432364242523EA221E226F25522FCD2A53232B209721C6209620201FC722C52080219721A6226E20C61F2B22EA212020DD205B1FCE223E213B1E00218A2155211820241F431F1F204C1F251E561F1E1E1A1E961EB31D361D0D1F931EC61E6021DE1F4520BF21AD21CE1FB11E37218A209521F22097204421FF1F8F2069220120A621A3228E209221D9220426292F292B7623E222AB20CD21FB209420C3216121EE1FD420FA201822C520BD212122D31FFE20B9208522DD2138209620311F881E8120BB1F4D1F281F2F1F2F1E881D041FC7203E1E6D1EF91EA41C56206E1F1922D4201720A11F2420DA1F871F742007212C203522A620662222200B212E209A215021002311216B229F23CB24512FC629CE248C2193212F1FB121A020B61F2D210F21E9217C20322059218220D51F2F212B219C21792112209F2075202020EA1FC220671F241F5D1F961DEC1D171E8B1D6C1E491E691D3E1E6E1E5F1E771FBC21B822572133200520F6208D20E1217822E61F1C1F102028228421812103217321992105221E216221EA224C252C30012B55242F222620EC1F33209420F621FB20A41F5121B220D220C21E7C1FDA1F041F0622E921B42190207E21DA1F35222A218A206C1F5D2058207F1FB11F801F1E1F211F961EB91EE11D1F202720FE20252023221B2194200B21A11F9A2088214A212C20BD208B218022E920E52020219421DC20162179217522FD22A123712F812A5F249A213321E8208E20D92039201E22162090209E20E11F6520342156224421F220B120C520762063209F211320791FAD1F741FEF1EB91FD51F361F9B1EC91EB41C371D511CB91F3A1E6A204C20AA203422D41F8D1F5820CE2172200E227E2174201420D7206E20991F34224B229C211B210721D2201F223E22A2242330562BEA2484235220D61FAA20C220EE204721012112213D228E20AE1FC7212D22082197205321C81FE120981F2B205121A820CF20DD216A1F971F9E1FD61D841EB11D881C571EEC1D2A1FF71E891FAF1EFA1F6F20AE1F9C218F22C820A12091218522A322C21FA620F42100214820E1203020482013217220F921E5222D252E2FF42BB723CC21DD2076201721CC20D71FD220E0208A1F7A202D208B1F572044200F20F91FFA20E91F3620F21F8B1FAB20531FB71F801EF91F4B1F941E0A1F8E1D881D0C1CE81D3E1FDA1FEB1F1E1F0520DF20A61FA41F2D22282037204220701F2422DB218C218A20FE20EE208220C32111201320C321F7216C21602390236A2FD3294A24D922042170206E21421F5520CD20E220B72150203F2210211B22C6203F212D218F21D1201221B51F352133230D1F6520FB1F8220921E0A1F7D1FDC1D801DC51DE01DAA1D661F2F200F20681F8F21E01F3D20FA1FAE201E1FA71F5E223021A221C1200D20D1219021F4205421EE20BB1F2E211D1E8D200F2222245F2F882AF325C3200F220B21022155210B202422961FDF1F07210C223A2189203822AD1FA9203121122251200C2098205C224620A11F991EA01F0020581F731D6A1D6A1DA41EE71DC71D701E8C1F4B20631F5A2014215120C61FFB20BE1EC61F431FE121C81EEC1E5B1F5120191F3221BF1F6A22871FE41FD4211B2192221724032E6B2ACC258F212F21E5201A21A520F6200422F620C9201521EC21EC1EAB1FF41F33211822F91F78223820B41FF120C11FE31F841E2F1F831DE120951E8320051F5A1F9D1EC21EFE1D1C20471F012040207E1F6E1F6720A51F6A21CC1E28201B20562186204F21A72043228B1F3A214721E6201D20AF207F21BD2391235024CF2F642CF625BC2188219421A7222C209D212822FD212C22262157206F217621422106226222B420B2224720B21F922085213920C920D61E091E391FE21F7C1E3C2045207B1DB81D481F251EB2202A219B1E91217B212620F61DAA208D2047223620EB2056202F21EC1FD42189218F20282032228320F2212B20CC21D22049233D2EFF2A77242822F5201D20F32006221721A72208223721D421E5210B202922AB2044210B216321FB2040206521F9212C21F21F2121CF1FCA1E5A1FE9200620F31E9D1FAE1E4B1FC81EB01F7C204A20401F791F0320B41FCD1E861FF61F31216220EE1F7321F020B61F0C200920521FC721DF201D219A213E20EF21A32244240B30C9290623E4211A218A2097218A1F2121A9227621B9217720E220592053201721BF207B202622D821941F9C1F7A2079204E1FB320251F1521C21FD51D3A20E91E8F1E001F4B20DD1DB41F36203C1E871F801F6A206B1F5220DA206720BF1FBA1EEF1F13203C20ED1E1420631F1920A420A51F4220D721FF1F2722FA210424172ED72A1C2478207822E82167210A21DE210D2346203C203F228B20492100212D21C9211C217221F021B120CF20B3216421F11EAE208820721F951F65203020E51FF81F221F4F1E3D1FCB1FB21F4E2027206A2106212220591FE41F6F209A1FBD201620CF1F381FD31F5D20C0209E20DB224620AC201421C220AE213B220A25F52EB72907256423AF203E216C219320662072210922F820B3210A20D82054208A22A92043215A22D5206A2143207D21971F881F1521491F1920B71F3E1E561E071E041FA11D4D1E6A1E841EC51E901F871F5F20A01F4B1EA2204120741F571F14208B21F41F3E20F6206521B61F4920E61FD4209821731F0721A5218A2180224A2F092CE1250422F920E920B820F420B12055214521002018205E1FF51F55217E1EFD1FCD209C205A2102221821C520531F1E2028214D21481FAF208D1E741FEB1F831FCC1E4A1F431DA41EEE1E671E801E3D20D420E31FC91F8A1F881F361F99207F20412193202420B921F91FAC20792209206120DB20391F2F2099232224F62F8D2A0F25F42134213621E421352098220C1F66208F20A12013208B217021A41F6C1F282106204321AF1FFC1F62202C20491F02210321B41F3C2133209F20FD1F6220641FB61E8C1DFB1E9D1F5A1F891F971F20216720C81F3F1FA01F0920F31FED2039207C20411FB121041F6520AC1FC11F1B2106216C206D2220226A24CF2EA52A14238921052300214A218F2049211821D91F1722632147206721752100210322FD1F94218E221021B41F12218921C21E3020BC1F9B1FB61FD11E02208D1E8D1FD21FCA209E1EC91E951F3120F21E182005229F1F491F50219F207321EF205B1F151FDD1FE11F1F210521701FC820ED20371F1320AD1F122082213823CB2CEC2A34257421BA21D72264202D21AA207C210622A6209A20FB21C5208B1F41225D225A22B921A5222F20F71E68207021C51F321F4820E4200C1F1B203F1F071EE02094201D1F951D8720171F62209F1EB22000212A1F691FE420CD1EE721091F792126215C200A1F312002204820BD215020A61ECF1F22204C21C1210C244D2FC22C4325F72173216223D220E021792080218921FC217F207F209F1F8A206A20D3202821DC1FF42053200C20362006229521FF1F951FE31C6D1F8F1E9F20801FB31F2C1F1B208B1FA71D8521FF1F951EC91F2F20251E30201320D21F2E1F19206C20B41FA5203A1ED02099205720E41F5E2085201721E6202E21CD219723852FD42AF02450238120512040201621AB200422F72099207220BA1F7321AE21CD1E1A216F20EC20B521F720B21F9E20C21FEC1F5120361E441D70201A209E1F8F1EB91F0321991E031E301D321FC21DE41EDB1FE31F6A1D731E5E20E11F4D203B1EE320BB20FC1F9820A91F901F661F4721881F6E1FF61FF31D462133228A245F2DC32AFA24CA229D21D320B721712213220922AC2044211B225520DA1FEA20BA212721E61F1D20EB202C215420D91F2D20F220DE1F3B1E0A1FFD1D4520ED1EE21F761F4A1F2920721E9B1DF81E101F4A1E7A1FA71EA21ED41E441F5120561FA4216F20B51EE21F0F2179218F1E6A1FC61F581F1B1F4820A51FB72126234924432D912AD2250B219A20F11FD41F102204216D21F11F6421D21E931F5B1FF61F0320CD20C11FA820C4205C214E1E9F1F91226621BD20BB1F881E951FD51EAD204021971F0C1F4320231D721EEC1E881E2E1F761F3420DF20DC1ECD20B31F841FFB1E5E21E31F631EBD1E85206820E81ED11F551E9E1F442192200D211223F124072E2F2BB024F422F920A321332191206820862115202E21731F6220031F0920831FC21E0D20811F04204D1E191E0B1FA8201820081F2D1E671F4A1F821F3B20432091202B20C91EF71D861F541F581F051F991E991F271EAD1F7B20BF1F781E701FC31FEC1E551F3120A920581E491E231FDE20621E9D1F671F292006201E24382FDA2AC5239E21AF21342157203120C321B81F8A206020FF1FB31FAB1E041F70201820F41E2C21162129201C1E89205120CF1F54208E20B41F8C1FD11EB41E362037202221681E8E1EBC1DDE1F871EDB1EC81E6E1F521ECA1EAB20921E901FCF1FA81FE21FFB1FA61FDB1F46201721131E921EBF1F1520FE209B21F82263241B2D1C2B8824D42059219921F01F2221422121213022C321511FD51FD51F2C2084206D20E220A8202820CA206A1EFA206C1E1E20BF20D71D1B20AB1E3F1FD21ECF20F01F641FAC1FA41F921DEA1FE51F021FB721E920541D22206B1FBA1FB51F8521D41F8E1D91200A20211E681FD41E401F101FF41F4621331FE2211422BE24F62D102B2125E821FA20D921A221E820EC22E22096206F207220FF1ED71EB320C3211320CD20A8229D1F02211720D220A11FBD20E921B41FF01F381FDD1FDA1E4D20FA1F8B1D641F9B1EE81DD81F0F20841E1B21D81FD31F731EE920441F8322EE1FD81F8A1FCF1FB41EDF20C61DA11FCC2027219E1F29200421FC216320F622C02EE32BE724A62024229E217121E320BF225322D620E220262115207F1EB020832094215B210821FF1E182100207A20DC1F6C1F6C21AB1FC31D031F67212A1FDC1E4B1EFA1CF11E221E491D3A1FD11E5B1E81200721B01E711F861E3B1E871E041F5420121FCB1EA11D8A1FF51E971F9C201720E4206B1FE81E3A20E82217241230252C1125F522502020233E23CA214822192366225C23C521D320F21FA42106208A204F224021721FA320581FF12086201E20E321FC20CB1F78207C1EE81EE81EFE1DF41D0C1D501E251F4D1E0F1F1721FD2007207D20E11DC01CAA1E9E1EB120D51F33205D1F941EAF1F4B1F7D1FE820C91F7A223E21041F591FC5229524832E +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 4024 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 2 2 0 2106 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 2 2 0 20B3 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 4023 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 01 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 01 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 01 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 01 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 01 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 03 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 4031 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 403F + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 55 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 55 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 55 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 55 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 55 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 55 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 55 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 55 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 55 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 55 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 55 + USBDEVFS_REAPURBNDELAY 0 3 131 0 0 1 1 0 55 + USBDEVFS_REAPURBNDELAY 0 3 131 -2 0 1 0 0 +USBDEVFS_REAPURBNDELAY 0 3 1 0 0 2 2 0 000B diff --git a/tests/elan/capture.png b/tests/elan/capture.png new file mode 100644 index 0000000000000000000000000000000000000000..6295abf9b8702f87a90f9fa4762c558c8eed707d GIT binary patch literal 47670 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 literal 0 HcmV?d00001 diff --git a/tests/elan/device b/tests/elan/device new file mode 100644 index 00000000..7374dc24 --- /dev/null +++ b/tests/elan/device @@ -0,0 +1,284 @@ +P: /devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.4 +N: bus/usb/001/094=1201000200000008F304260C40010102000109023E0001010080320904000005FF0000000921100100012215000705810240000107050102400001070582024000010705830240000107050302400001 +E: DEVNAME=/dev/bus/usb/001/094 +E: DEVTYPE=usb_device +E: DRIVER=usb +E: PRODUCT=4f3/c26/140 +E: TYPE=0/0/0 +E: BUSNUM=001 +E: DEVNUM=094 +E: MAJOR=189 +E: MINOR=93 +E: SUBSYSTEM=usb +E: ID_VENDOR=ELAN +E: ID_VENDOR_ENC=ELAN +E: ID_VENDOR_ID=04f3 +E: ID_MODEL=ELAN:Fingerprint +E: ID_MODEL_ENC=ELAN:Fingerprint +E: ID_MODEL_ID=0c26 +E: ID_REVISION=0140 +E: ID_SERIAL=ELAN_ELAN:Fingerprint +E: ID_BUS=usb +E: ID_USB_INTERFACES=:ff0000: +E: ID_VENDOR_FROM_DATABASE=Elan Microelectronics Corp. +E: ID_PATH=pci-0000:00:14.0-usb-0:4.4 +E: ID_PATH_TAG=pci-0000_00_14_0-usb-0_4_4 +E: LIBFPRINT_DRIVER=ElanTech Fingerprint Sensor +A: authorized=1 +A: avoid_reset_quirk=0 +A: bConfigurationValue=1 +A: bDeviceClass=00 +A: bDeviceProtocol=00 +A: bDeviceSubClass=00 +A: bMaxPacketSize0=8 +A: bMaxPower=100mA +A: bNumConfigurations=1 +A: bNumInterfaces= 1 +A: bcdDevice=0140 +A: bmAttributes=80 +A: busnum=1 +A: configuration= +H: descriptors=1201000200000008F304260C40010102000109023E0001010080320904000005FF0000000921100100012215000705810240000107050102400001070582024000010705830240000107050302400001 +A: dev=189:93 +A: devnum=94 +A: devpath=4.4 +L: driver=../../../../../../bus/usb/drivers/usb +A: idProduct=0c26 +A: idVendor=04f3 +A: ltm_capable=no +A: manufacturer=ELAN +A: maxchild=0 +L: port=../1-4:1.0/1-4-port4 +A: power/active_duration=4747 +A: power/autosuspend=2 +A: power/autosuspend_delay_ms=2000 +A: power/connected_duration=54012 +A: power/control=auto +A: power/level=auto +A: power/persist=1 +A: power/runtime_active_time=4721 +A: power/runtime_status=active +A: power/runtime_suspended_time=49114 +A: product=ELAN:Fingerprint +A: quirks=0x0 +A: removable=removable +A: rx_lanes=1 +A: speed=12 +A: tx_lanes=1 +A: urbnum=13 +A: version= 2.00 + +P: /devices/pci0000:00/0000:00:14.0/usb1/1-4 +N: bus/usb/001/083=1201100209000140EF17181084520102000109021900010100E0000904000001090000000705810301000C +E: DEVNAME=/dev/bus/usb/001/083 +E: DEVTYPE=usb_device +E: DRIVER=usb +E: PRODUCT=17ef/1018/5284 +E: TYPE=9/0/1 +E: BUSNUM=001 +E: DEVNUM=083 +E: MAJOR=189 +E: MINOR=82 +E: SUBSYSTEM=usb +E: ID_VENDOR=VIA_Labs__Inc. +E: ID_VENDOR_ENC=VIA\x20Labs\x2c\x20Inc.\x20\x20\x20\x20\x20\x20\x20\x20\x20 +E: ID_VENDOR_ID=17ef +E: ID_MODEL=USB2.0_Hub +E: ID_MODEL_ENC=USB2.0\x20Hub\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20 +E: ID_MODEL_ID=1018 +E: ID_REVISION=5284 +E: ID_SERIAL=VIA_Labs__Inc._USB2.0_Hub +E: ID_BUS=usb +E: ID_USB_INTERFACES=:090000: +E: ID_VENDOR_FROM_DATABASE=Lenovo +E: ID_PATH=pci-0000:00:14.0-usb-0:4 +E: ID_PATH_TAG=pci-0000_00_14_0-usb-0_4 +E: ID_FOR_SEAT=usb-pci-0000_00_14_0-usb-0_4 +E: TAGS=:seat: +A: authorized=1 +A: avoid_reset_quirk=0 +A: bConfigurationValue=1 +A: bDeviceClass=09 +A: bDeviceProtocol=01 +A: bDeviceSubClass=00 +A: bMaxPacketSize0=64 +A: bMaxPower=0mA +A: bNumConfigurations=1 +A: bNumInterfaces= 1 +A: bcdDevice=5284 +A: bmAttributes=e0 +A: busnum=1 +A: configuration= +H: descriptors=1201100209000140EF17181084520102000109021900010100E0000904000001090000000705810301000C +A: dev=189:82 +A: devnum=83 +A: devpath=4 +L: driver=../../../../../bus/usb/drivers/usb +A: idProduct=1018 +A: idVendor=17ef +A: ltm_capable=no +A: manufacturer=VIA Labs, Inc. +A: maxchild=4 +L: port=../1-0:1.0/usb1-port4 +A: power/active_duration=11223581 +A: power/autosuspend=0 +A: power/autosuspend_delay_ms=0 +A: power/connected_duration=11223581 +A: power/control=auto +A: power/level=auto +A: power/runtime_active_time=11223333 +A: power/runtime_status=active +A: power/runtime_suspended_time=0 +A: power/wakeup=disabled +A: power/wakeup_abort_count= +A: power/wakeup_active= +A: power/wakeup_active_count= +A: power/wakeup_count= +A: power/wakeup_expire_count= +A: power/wakeup_last_time_ms= +A: power/wakeup_max_time_ms= +A: power/wakeup_total_time_ms= +A: product=USB2.0 Hub +A: quirks=0x0 +A: removable=removable +A: rx_lanes=1 +A: speed=480 +A: tx_lanes=1 +A: urbnum=106 +A: version= 2.10 + +P: /devices/pci0000:00/0000:00:14.0/usb1 +N: bus/usb/001/001=12010002090001406B1D020003050302010109021900010100E0000904000001090000000705810304000C +E: DEVNAME=/dev/bus/usb/001/001 +E: DEVTYPE=usb_device +E: DRIVER=usb +E: PRODUCT=1d6b/2/503 +E: TYPE=9/0/1 +E: BUSNUM=001 +E: DEVNUM=001 +E: MAJOR=189 +E: MINOR=0 +E: SUBSYSTEM=usb +E: ID_VENDOR=Linux_5.3.8-300.fc31.x86_64_xhci-hcd +E: ID_VENDOR_ENC=Linux\x205.3.8-300.fc31.x86_64\x20xhci-hcd +E: ID_VENDOR_ID=1d6b +E: ID_MODEL=xHCI_Host_Controller +E: ID_MODEL_ENC=xHCI\x20Host\x20Controller +E: ID_MODEL_ID=0002 +E: ID_REVISION=0503 +E: ID_SERIAL=Linux_5.3.8-300.fc31.x86_64_xhci-hcd_xHCI_Host_Controller_0000:00:14.0 +E: ID_SERIAL_SHORT=0000:00:14.0 +E: ID_BUS=usb +E: ID_USB_INTERFACES=:090000: +E: ID_VENDOR_FROM_DATABASE=Linux Foundation +E: ID_MODEL_FROM_DATABASE=2.0 root hub +E: ID_PATH=pci-0000:00:14.0 +E: ID_PATH_TAG=pci-0000_00_14_0 +E: ID_FOR_SEAT=usb-pci-0000_00_14_0 +E: TAGS=:seat: +A: authorized=1 +A: authorized_default=1 +A: avoid_reset_quirk=0 +A: bConfigurationValue=1 +A: bDeviceClass=09 +A: bDeviceProtocol=01 +A: bDeviceSubClass=00 +A: bMaxPacketSize0=64 +A: bMaxPower=0mA +A: bNumConfigurations=1 +A: bNumInterfaces= 1 +A: bcdDevice=0503 +A: bmAttributes=e0 +A: busnum=1 +A: configuration= +H: descriptors=12010002090001406B1D020003050302010109021900010100E0000904000001090000000705810304000C +A: dev=189:0 +A: devnum=1 +A: devpath=0 +L: driver=../../../../bus/usb/drivers/usb +A: idProduct=0002 +A: idVendor=1d6b +A: interface_authorized_default=1 +A: ltm_capable=no +A: manufacturer=Linux 5.3.8-300.fc31.x86_64 xhci-hcd +A: maxchild=12 +A: power/active_duration=2372569822 +A: power/autosuspend=0 +A: power/autosuspend_delay_ms=0 +A: power/connected_duration=2405642105 +A: power/control=auto +A: power/level=auto +A: power/runtime_active_time=2372599414 +A: power/runtime_status=active +A: power/runtime_suspended_time=33016992 +A: power/wakeup=disabled +A: power/wakeup_abort_count= +A: power/wakeup_active= +A: power/wakeup_active_count= +A: power/wakeup_count= +A: power/wakeup_expire_count= +A: power/wakeup_last_time_ms= +A: power/wakeup_max_time_ms= +A: power/wakeup_total_time_ms= +A: product=xHCI Host Controller +A: quirks=0x0 +A: removable=unknown +A: rx_lanes=1 +A: serial=0000:00:14.0 +A: speed=480 +A: tx_lanes=1 +A: urbnum=19225 +A: version= 2.00 + +P: /devices/pci0000:00/0000:00:14.0 +E: DRIVER=xhci_hcd +E: PCI_CLASS=C0330 +E: PCI_ID=8086:9D2F +E: PCI_SUBSYS_ID=17AA:2238 +E: PCI_SLOT_NAME=0000:00:14.0 +E: MODALIAS=pci:v00008086d00009D2Fsv000017AAsd00002238bc0Csc03i30 +E: SUBSYSTEM=pci +E: ID_PCI_CLASS_FROM_DATABASE=Serial bus controller +E: ID_PCI_SUBCLASS_FROM_DATABASE=USB controller +E: ID_PCI_INTERFACE_FROM_DATABASE=XHCI +E: ID_VENDOR_FROM_DATABASE=Intel Corporation +E: ID_MODEL_FROM_DATABASE=Sunrise Point-LP USB 3.0 xHCI Controller +A: ari_enabled=0 +A: broken_parity_status=0 +A: class=0x0c0330 +H: config=86802F9D060490022130030C00008000040022E1000000000000000000000000000000000000000000000000AA1738220000000070000000000000000B010000 +A: consistent_dma_mask_bits=64 +A: d3cold_allowed=1 +A: dbc=disabled +A: device=0x9d2f +A: dma_mask_bits=64 +L: driver=../../../bus/pci/drivers/xhci_hcd +A: driver_override=(null) +A: enable=1 +A: irq=125 +A: local_cpulist=0-3 +A: local_cpus=f +A: modalias=pci:v00008086d00009D2Fsv000017AAsd00002238bc0Csc03i30 +A: msi_bus=1 +A: msi_irqs/125=msi +A: numa_node=-1 +A: pools=poolinfo - 0.1\nbuffer-2048 0 0 2048 0\nbuffer-512 0 0 512 0\nbuffer-128 0 0 128 0\nbuffer-32 0 0 32 0\nxHCI 1KB stream ctx arrays 0 0 1024 0\nxHCI 256 byte stream ctx arrays 0 0 256 0\nxHCI input/output contexts 37 38 2112 38\nxHCI ring segments 116 120 4096 120\nbuffer-2048 3 6 2048 3\nbuffer-512 0 0 512 0\nbuffer-128 30 32 128 1\nbuffer-32 0 0 32 0 +A: power/control=on +A: power/runtime_active_time=2405617003 +A: power/runtime_status=active +A: power/runtime_suspended_time=0 +A: power/wakeup=enabled +A: power/wakeup_abort_count=0 +A: power/wakeup_active=0 +A: power/wakeup_active_count=0 +A: power/wakeup_count=0 +A: power/wakeup_expire_count=0 +A: power/wakeup_last_time_ms=0 +A: power/wakeup_max_time_ms=0 +A: power/wakeup_total_time_ms=0 +A: resource=0x00000000e1220000 0x00000000e122ffff 0x0000000000140204\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000 +A: revision=0x21 +A: subsystem_device=0x2238 +A: subsystem_vendor=0x17aa +A: vendor=0x8086 + diff --git a/tests/meson.build b/tests/meson.build index 29ff6afe..d46d1c8c 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -29,6 +29,7 @@ if get_option('introspection') endif drivers_tests = [ + 'elan', 'vfs5011', 'synaptics', ] From 6716359fe8b2bca84713f60a33b565c614db5396 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 7 Jan 2020 13:45:33 +0100 Subject: [PATCH 183/237] tests: Add more notes about umockdev recording creation umockdev recordings are usually not usable as is. Add some notes to the README to summarise what kind of changes may be required. --- tests/README-umockdev | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/tests/README-umockdev b/tests/README-umockdev index cabbace1..eec3598c 100644 --- a/tests/README-umockdev +++ b/tests/README-umockdev @@ -21,4 +21,33 @@ To create a new umockdev test, you should: Please note, there is no need to use a real finger print in this case. If you would like to avoid submitting your own fingerprint then please just use e.g. the side of your finger, arm, or anything else that will produce -an image with the device. \ No newline at end of file +an image with the device. + + +Note that umockdev-record groups URBs aggressively. In most cases, manual +intervention is unfortunately required. In most cases, drivers do a chain +of commands like e.g. A then B each with a different reply. Umockdev will +create a file like: + +A + reply 1 + reply 2 +B + reply 1 + reply 2 + +which then needs to be re-ordered to be: + +A + reply 1 +B + reply 1 +A + reply 2 +B + reply 2 + +Other changes may be needed to get everything working. For example the elan +driver relies on a timeout that is not reported correctly. In this case the +driver works around it by interpreting the protocol error differently in +the virtual environment. \ No newline at end of file From ba07c74006687f46f5a69a23c21bd024a1b43f12 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Wed, 8 Jan 2020 18:40:41 +0100 Subject: [PATCH 184/237] tests: Always add dummy skipping tests Just hiding tests that cannot be run does not seem like the best idea. So add dummy tests that are skipped, to denote that we could test more than we actually do (even if it is just for drivers that are not enabled). --- tests/meson.build | 49 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/tests/meson.build b/tests/meson.build index d46d1c8c..912b5001 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -16,6 +16,12 @@ envs.set('FP_DRIVERS_WHITELIST', 'virtual_image') envs.set('NO_AT_BRIDGE', '1') +drivers_tests = [ + 'elan', + 'vfs5011', + 'synaptics', +] + if get_option('introspection') envs.prepend('GI_TYPELIB_PATH', join_paths(meson.build_root(), 'libfprint')) @@ -26,25 +32,44 @@ if get_option('introspection') env: envs, depends: libfprint_typelib, ) + else + test('virtual-image', + find_program('sh'), + args: ['-c', 'exit 77'] + ) endif - drivers_tests = [ - 'elan', - 'vfs5011', - 'synaptics', - ] - foreach driver_test: drivers_tests driver_envs = envs driver_envs.set('FP_DRIVERS_WHITELIST', driver_test) + if driver_test in drivers + test(driver_test, + find_program('umockdev-test.py'), + args: join_paths(meson.current_source_dir(), driver_test), + env: driver_envs, + suite: ['drivers'], + timeout: 10, + depends: libfprint_typelib, + ) + else + test(driver_test, + find_program('sh'), + args: ['-c', 'exit 77'] + ) + endif + endforeach +else + warning('Skipping all driver tests as introspection bindings are missing') + test('virtual-image', + find_program('sh'), + args: ['-c', 'exit 77'] + ) + + foreach driver_test: drivers_tests test(driver_test, - find_program('umockdev-test.py'), - args: join_paths(meson.current_source_dir(), driver_test), - env: driver_envs, - suite: ['drivers'], - timeout: 10, - depends: libfprint_typelib, + find_program('sh'), + args: ['-c', 'exit 77'] ) endforeach endif From fcdf1a1ff156b6855f68bc738e746f4b7825c020 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 23 Dec 2019 23:55:55 +0100 Subject: [PATCH 185/237] device: Fix potential memory leak of progress_cb user data The progress report user data free func was not assigned and therefore never called. Add the missing assign, potentially fixing memory leaks (mostly relevant for bindings). --- libfprint/fp-device.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libfprint/fp-device.c b/libfprint/fp-device.c index 116f9f87..634c2cc1 100644 --- a/libfprint/fp-device.c +++ b/libfprint/fp-device.c @@ -775,6 +775,7 @@ fp_device_enroll (FpDevice *device, data->print = g_object_ref_sink (template_print); data->enroll_progress_cb = progress_cb; data->enroll_progress_data = progress_data; + data->enroll_progress_destroy = progress_destroy; // Attach the progress data as task data so that it is destroyed g_task_set_task_data (priv->current_task, data, (GDestroyNotify) enroll_data_free); From 3f3d4559b40e8dc0f4e7c972180644e735c14fbc Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 24 Dec 2019 00:03:14 +0100 Subject: [PATCH 186/237] upekts: Remove unused argument from deinitsm_new --- libfprint/drivers/upekts.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libfprint/drivers/upekts.c b/libfprint/drivers/upekts.c index 16534d39..965b3b29 100644 --- a/libfprint/drivers/upekts.c +++ b/libfprint/drivers/upekts.c @@ -832,7 +832,7 @@ initsm_done (FpiSsm *ssm, FpDevice *dev, GError *error) } static FpiSsm * -deinitsm_new (FpDevice *dev, void *user_data) +deinitsm_new (FpDevice *dev) { return fpi_ssm_new (dev, deinitsm_state_handler, DEINITSM_NUM_STATES); } @@ -988,7 +988,7 @@ static void do_enroll_stop (FpDevice *dev, FpPrint *print, GError *error) { EnrollStopData *data = g_new0 (EnrollStopData, 1); - FpiSsm *ssm = deinitsm_new (dev, data); + FpiSsm *ssm = deinitsm_new (dev); data->print = g_object_ref (print); data->error = error; @@ -1251,7 +1251,7 @@ static void do_verify_stop (FpDevice *dev, FpiMatchResult res, GError *error) { VerifyStopData *data = g_new0 (VerifyStopData, 1); - FpiSsm *ssm = deinitsm_new (dev, data); + FpiSsm *ssm = deinitsm_new (dev); data->res = res; data->error = error; From 8292c449f777fa99988757f769ab4f1b4247ba80 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 13 Jan 2020 13:25:48 +0100 Subject: [PATCH 187/237] device: Better define ownership passing for results Some things were odd with regard to the ownership of passed objects. Try to make things sane overall, in particular with the possible floating FpPrint reference. --- libfprint/fpi-device.c | 20 ++++++++++++++++---- libfprint/fpi-image-device.c | 2 +- tests/test-fpi-device.c | 24 ++++++++++++------------ 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/libfprint/fpi-device.c b/libfprint/fpi-device.c index 51dbee17..8b2ef9d8 100644 --- a/libfprint/fpi-device.c +++ b/libfprint/fpi-device.c @@ -908,7 +908,7 @@ fpi_device_enroll_complete (FpDevice *device, FpPrint *print, GError *error) * fpi_device_verify_complete: * @device: The #FpDevice * @result: The #FpiMatchResult of the operation - * @print: The scanned #FpPrint + * @print: (transfer floating) The scanned #FpPrint * @error: A #GError if result is %FPI_MATCH_ERROR * * Finish an ongoing verify operation. The returned print should be @@ -929,6 +929,9 @@ fpi_device_verify_complete (FpDevice *device, clear_device_cancel_action (device); + if (print) + g_object_ref_sink (print); + g_object_set_data_full (G_OBJECT (priv->current_task), "print", print, @@ -963,8 +966,8 @@ fpi_device_verify_complete (FpDevice *device, /** * fpi_device_identify_complete: * @device: The #FpDevice - * @match: The matching #FpPrint from the passed gallery, or %NULL if none matched - * @print: The scanned #FpPrint, may be %NULL + * @match: (transfer none): The matching #FpPrint from the passed gallery, or %NULL if none matched + * @print: (transfer floating): The scanned #FpPrint, may be %NULL * @error: The #GError or %NULL on success * * Finish an ongoing identify operation. The match that was identified is @@ -986,6 +989,12 @@ fpi_device_identify_complete (FpDevice *device, clear_device_cancel_action (device); + if (match) + g_object_ref (match); + + if (print) + g_object_ref_sink (print); + g_object_set_data_full (G_OBJECT (priv->current_task), "print", print, @@ -1134,7 +1143,7 @@ fpi_device_list_complete (FpDevice *device, * fpi_device_enroll_progress: * @device: The #FpDevice * @completed_stages: The number of stages that are completed at this point - * @print: (transfer full): The #FpPrint for the newly completed stage or %NULL on failure + * @print: (transfer floating): The #FpPrint for the newly completed stage or %NULL on failure * @error: (transfer full): The #GError or %NULL on success * * Notify about the progress of the enroll operation. This is important for UI interaction. @@ -1155,6 +1164,9 @@ fpi_device_enroll_progress (FpDevice *device, g_debug ("Device reported enroll progress, reported %i of %i have been completed", completed_stages, priv->nr_enroll_stages); + if (print) + g_object_ref_sink (print); + if (error && print) { g_warning ("Driver passed an error and also provided a print, returning error!"); diff --git a/libfprint/fpi-image-device.c b/libfprint/fpi-image-device.c index efdbb532..f962b8ae 100644 --- a/libfprint/fpi-image-device.c +++ b/libfprint/fpi-image-device.c @@ -226,7 +226,7 @@ fpi_image_device_minutiae_detected (GObject *source_object, GAsyncResult *res, g if (fpi_print_bz3_match (template, print, priv->bz3_threshold, &error) == FPI_MATCH_SUCCESS) { - result = g_object_ref (template); + result = template; break; } } diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c index 3fa800c9..3d1e81c4 100644 --- a/tests/test-fpi-device.c +++ b/tests/test-fpi-device.c @@ -548,10 +548,10 @@ test_driver_verify (void) { g_autoptr(GError) error = NULL; g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); - g_autoptr(FpPrint) enrolled_print = fp_print_new (device); + g_autoptr(FpPrint) enrolled_print = g_object_ref_sink (fp_print_new (device)); + g_autoptr(FpPrint) out_print = NULL; FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - FpPrint *out_print = NULL; gboolean match; fake_dev->ret_result = FPI_MATCH_SUCCESS; @@ -570,10 +570,10 @@ test_driver_verify_fail (void) { g_autoptr(GError) error = NULL; g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); - g_autoptr(FpPrint) enrolled_print = fp_print_new (device); + g_autoptr(FpPrint) enrolled_print = g_object_ref_sink (fp_print_new (device)); + g_autoptr(FpPrint) out_print = NULL; FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - FpPrint *out_print = NULL; gboolean match; fake_dev->ret_result = FPI_MATCH_FAIL; @@ -591,10 +591,10 @@ test_driver_verify_error (void) { g_autoptr(GError) error = NULL; g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); - g_autoptr(FpPrint) enrolled_print = fp_print_new (device); + g_autoptr(FpPrint) enrolled_print = g_object_ref_sink (fp_print_new (device)); + g_autoptr(FpPrint) out_print = NULL; FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - FpPrint *out_print = NULL; gboolean match; fake_dev->ret_result = FPI_MATCH_ERROR; @@ -641,16 +641,16 @@ test_driver_identify (void) { g_autoptr(GError) error = NULL; g_autoptr(FpPrint) print = NULL; + g_autoptr(FpPrint) matched_print = NULL; g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); g_autoptr(GPtrArray) prints = g_ptr_array_new_with_free_func (g_object_unref); FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - FpPrint *matched_print; FpPrint *expected_matched; unsigned int i; for (i = 0; i < 500; ++i) - g_ptr_array_add (prints, fp_print_new (device)); + g_ptr_array_add (prints, g_object_ref_sink (fp_print_new (device))); expected_matched = g_ptr_array_index (prints, g_random_int_range (0, 499)); fp_print_set_description (expected_matched, "fake-verified"); @@ -673,15 +673,15 @@ test_driver_identify_fail (void) { g_autoptr(GError) error = NULL; g_autoptr(FpPrint) print = NULL; + g_autoptr(FpPrint) matched_print = NULL; g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); g_autoptr(GPtrArray) prints = g_ptr_array_new_with_free_func (g_object_unref); FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - FpPrint *matched_print; unsigned int i; for (i = 0; i < 500; ++i) - g_ptr_array_add (prints, fp_print_new (device)); + g_ptr_array_add (prints, g_object_ref_sink (fp_print_new (device))); g_assert_true (fp_device_supports_identify (device)); @@ -700,16 +700,16 @@ test_driver_identify_error (void) { g_autoptr(GError) error = NULL; g_autoptr(FpPrint) print = NULL; + g_autoptr(FpPrint) matched_print = NULL; g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); g_autoptr(GPtrArray) prints = g_ptr_array_new_with_free_func (g_object_unref); FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - FpPrint *matched_print; FpPrint *expected_matched; unsigned int i; for (i = 0; i < 500; ++i) - g_ptr_array_add (prints, fp_print_new (device)); + g_ptr_array_add (prints, g_object_ref_sink (fp_print_new (device))); expected_matched = g_ptr_array_index (prints, g_random_int_range (0, 499)); fp_print_set_description (expected_matched, "fake-verified"); From 4d5c34e11a4b72b964dbee48e426c982894f46fe Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 24 Dec 2019 01:01:04 +0100 Subject: [PATCH 188/237] Introduce an early reporting mechanism for verify and match It is a good idea to report match results early, to e.g. log in a user immediately even if more device interaction is needed. Add new _full variants for the verify/identify functions, with a corresponding callback. Also move driver result reporting into new fpi_device_{identify,verify}_report functions and remove the reporting from the fpi_device_{identify,verify}_complete calls. Basic updates to code is done in places. Only the upekts driver is actually modified from a behaviour point of view. The image driver code should be restructured quite a bit to split the reporting and only report completion after device deactivation. This should simplifiy the code quite a bit again. --- doc/libfprint-sections.txt | 3 + examples/verify.c | 2 + libfprint/drivers/synaptics/synaptics.c | 17 +- libfprint/drivers/upekts.c | 23 +- libfprint/fp-device-private.h | 17 ++ libfprint/fp-device.c | 54 ++++- libfprint/fp-device.h | 30 +++ libfprint/fpi-device.c | 278 +++++++++++++++++++----- libfprint/fpi-device.h | 16 +- libfprint/fpi-image-device.c | 8 +- tests/test-device-fake.c | 23 +- tests/test-fpi-device.c | 4 +- tests/virtual-image.py | 12 +- 13 files changed, 385 insertions(+), 102 deletions(-) diff --git a/doc/libfprint-sections.txt b/doc/libfprint-sections.txt index 30a4e9bf..ca92190c 100644 --- a/doc/libfprint-sections.txt +++ b/doc/libfprint-sections.txt @@ -26,6 +26,7 @@ FpDeviceError fp_device_retry_quark fp_device_error_quark FpEnrollProgress +FpMatchCb fp_device_get_driver fp_device_get_device_id fp_device_get_name @@ -159,6 +160,8 @@ fpi_device_identify_complete fpi_device_capture_complete fpi_device_delete_complete fpi_device_enroll_progress +fpi_device_verify_report +fpi_device_identify_report
diff --git a/examples/verify.c b/examples/verify.c index 7fcc64cf..ffbf1132 100644 --- a/examples/verify.c +++ b/examples/verify.c @@ -152,6 +152,7 @@ on_list_completed (FpDevice *dev, GAsyncResult *res, gpointer user_data) g_print ("Print loaded. Time to verify!\n"); fp_device_verify (dev, verify_print, NULL, + NULL, NULL, NULL, (GAsyncReadyCallback) on_verify_completed, verify_data); } @@ -205,6 +206,7 @@ start_verification (FpDevice *dev, VerifyData *verify_data) g_print ("Print loaded. Time to verify!\n"); fp_device_verify (dev, verify_print, NULL, + NULL, NULL, NULL, (GAsyncReadyCallback) on_verify_completed, verify_data); } diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index 2aac75e8..2470ba9b 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -592,16 +592,13 @@ verify_msg_cb (FpiDeviceSynaptics *self, if (error) { - fpi_device_verify_complete (device, FPI_MATCH_ERROR, NULL, error); + fpi_device_verify_complete (device, error); return; } if (resp == NULL && self->cmd_complete_on_removal) { - fpi_device_verify_complete (device, - GPOINTER_TO_INT (self->cmd_complete_data), - NULL, - error); + fpi_device_verify_complete (device, NULL); return; } @@ -638,21 +635,21 @@ verify_msg_cb (FpiDeviceSynaptics *self, { fp_info ("Print is not in database"); fpi_device_verify_complete (device, - FPI_MATCH_ERROR, - NULL, fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND)); } else { fp_warn ("Verify has failed: %d", resp->result); - fpi_device_verify_complete (device, FPI_MATCH_FAIL, NULL, NULL); + fpi_device_verify_report (device, FPI_MATCH_FAIL, NULL, NULL); + fpi_device_verify_complete (device, NULL); } break; case BMKT_RSP_VERIFY_OK: fp_info ("Verify was successful! for user: %s finger: %d score: %f", verify_resp->user_id, verify_resp->finger_id, verify_resp->match_result); - fpi_device_verify_complete (device, FPI_MATCH_SUCCESS, NULL, NULL); + fpi_device_verify_report (device, FPI_MATCH_SUCCESS, NULL, NULL); + fpi_device_verify_complete (device, NULL); break; } } @@ -675,8 +672,6 @@ verify (FpDevice *device) if (!parse_print_data (data, &finger, &user_id, &user_id_len)) { fpi_device_verify_complete (device, - FPI_MATCH_ERROR, - NULL, fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID)); return; } diff --git a/libfprint/drivers/upekts.c b/libfprint/drivers/upekts.c index 965b3b29..47903ef9 100644 --- a/libfprint/drivers/upekts.c +++ b/libfprint/drivers/upekts.c @@ -832,7 +832,7 @@ initsm_done (FpiSsm *ssm, FpDevice *dev, GError *error) } static FpiSsm * -deinitsm_new (FpDevice *dev) +deinitsm_new (FpDevice *dev, void *user_data) { return fpi_ssm_new (dev, deinitsm_state_handler, DEINITSM_NUM_STATES); } @@ -988,7 +988,7 @@ static void do_enroll_stop (FpDevice *dev, FpPrint *print, GError *error) { EnrollStopData *data = g_new0 (EnrollStopData, 1); - FpiSsm *ssm = deinitsm_new (dev); + FpiSsm *ssm = deinitsm_new (dev, data); data->print = g_object_ref (print); data->error = error; @@ -1225,8 +1225,7 @@ enroll (FpDevice *dev) typedef struct { - FpiMatchResult res; - GError *error; + GError *error; } VerifyStopData; static void @@ -1244,17 +1243,25 @@ verify_stop_deinit_cb (FpiSsm *ssm, FpDevice *dev, GError *error) if (error) fp_warn ("Error deinitializing: %s", error->message); - fpi_device_verify_complete (dev, data->res, NULL, data->error); + if (data->error) + fpi_device_verify_complete (dev, data->error); + else + fpi_device_verify_complete (dev, g_steal_pointer (&error)); + + g_error_free (error); } static void do_verify_stop (FpDevice *dev, FpiMatchResult res, GError *error) { VerifyStopData *data = g_new0 (VerifyStopData, 1); - FpiSsm *ssm = deinitsm_new (dev); + FpiSsm *ssm = deinitsm_new (dev, data); - data->res = res; - data->error = error; + /* Report the error immediately if possible, otherwise delay it. */ + if (!error && error->domain != FP_DEVICE_RETRY) + fpi_device_verify_report (dev, res, NULL, error); + else + data->error = error; fpi_ssm_start (ssm, verify_stop_deinit_cb); fpi_ssm_set_data (ssm, data, (GDestroyNotify) verify_stop_data_free); diff --git a/libfprint/fp-device-private.h b/libfprint/fp-device-private.h index 1a350fe5..5bf5954a 100644 --- a/libfprint/fp-device-private.h +++ b/libfprint/fp-device-private.h @@ -63,3 +63,20 @@ typedef struct } FpEnrollData; void enroll_data_free (FpEnrollData *enroll_data); + +typedef struct +{ + FpPrint *enrolled_print; /* verify */ + GPtrArray *gallery; /* identify */ + + gboolean result_reported; + FpPrint *match; + FpPrint *print; + GError *error; + + FpMatchCb match_cb; + gpointer match_data; + GDestroyNotify match_destroy; +} FpMatchData; + +void match_data_free (FpMatchData *match_data); diff --git a/libfprint/fp-device.c b/libfprint/fp-device.c index 634c2cc1..3b36ae6c 100644 --- a/libfprint/fp-device.c +++ b/libfprint/fp-device.c @@ -809,10 +809,13 @@ fp_device_enroll_finish (FpDevice *device, * @device: a #FpDevice * @enrolled_print: a #FpPrint to verify * @cancellable: (nullable): a #GCancellable, or %NULL + * @match_cb: (nullable) (scope notified): match reporting callback + * @match_data: (closure match_cb): user data for @match_cb + * @match_destroy: (destroy match_data): Destroy notify for @match_data * @callback: the function to call on completion * @user_data: the data to pass to @callback * - * Start an asynchronous operation to close the device. The callback will + * Start an asynchronous operation to verify a print. The callback will * be called once the operation has finished. Retrieve the result with * fp_device_verify_finish(). */ @@ -820,11 +823,15 @@ void fp_device_verify (FpDevice *device, FpPrint *enrolled_print, GCancellable *cancellable, + FpMatchCb match_cb, + gpointer match_data, + GDestroyNotify match_destroy, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(GTask) task = NULL; FpDevicePrivate *priv = fp_device_get_instance_private (device); + FpMatchData *data; task = g_task_new (device, cancellable, callback, user_data); if (g_task_return_error_if_cancelled (task)) @@ -848,9 +855,14 @@ fp_device_verify (FpDevice *device, priv->current_task = g_steal_pointer (&task); maybe_cancel_on_cancelled (device, cancellable); - g_task_set_task_data (priv->current_task, - g_object_ref (enrolled_print), - g_object_unref); + data = g_new0 (FpMatchData, 1); + data->enrolled_print = g_object_ref (enrolled_print); + data->match_cb = match_cb; + data->match_data = match_data; + data->match_destroy = match_destroy; + + // Attach the match data as task data so that it is destroyed + g_task_set_task_data (priv->current_task, data, (GDestroyNotify) match_data_free); FP_DEVICE_GET_CLASS (device)->verify (device); } @@ -886,7 +898,11 @@ fp_device_verify_finish (FpDevice *device, if (print) { - *print = g_object_get_data (G_OBJECT (result), "print"); + FpMatchData *data; + + data = g_task_get_task_data (G_TASK (result)); + + *print = data->print; if (*print) g_object_ref (*print); } @@ -902,6 +918,9 @@ fp_device_verify_finish (FpDevice *device, * @device: a #FpDevice * @prints: (element-type FpPrint) (transfer none): #GPtrArray of #FpPrint * @cancellable: (nullable): a #GCancellable, or %NULL + * @match_cb: (nullable) (scope notified): match reporting callback + * @match_data: (closure match_cb): user data for @match_cb + * @match_destroy: (destroy match_data): Destroy notify for @match_data * @callback: the function to call on completion * @user_data: the data to pass to @callback * @@ -913,11 +932,15 @@ void fp_device_identify (FpDevice *device, GPtrArray *prints, GCancellable *cancellable, + FpMatchCb match_cb, + gpointer match_data, + GDestroyNotify match_destroy, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(GTask) task = NULL; FpDevicePrivate *priv = fp_device_get_instance_private (device); + FpMatchData *data; task = g_task_new (device, cancellable, callback, user_data); if (g_task_return_error_if_cancelled (task)) @@ -941,9 +964,14 @@ fp_device_identify (FpDevice *device, priv->current_task = g_steal_pointer (&task); maybe_cancel_on_cancelled (device, cancellable); - g_task_set_task_data (priv->current_task, - g_ptr_array_ref (prints), - (GDestroyNotify) g_ptr_array_unref); + data = g_new0 (FpMatchData, 1); + data->gallery = g_ptr_array_ref (prints); + data->match_cb = match_cb; + data->match_data = match_data; + data->match_destroy = match_destroy; + + // Attach the match data as task data so that it is destroyed + g_task_set_task_data (priv->current_task, data, (GDestroyNotify) match_data_free); FP_DEVICE_GET_CLASS (device)->identify (device); } @@ -974,15 +1002,19 @@ fp_device_identify_finish (FpDevice *device, FpPrint **print, GError **error) { + FpMatchData *data; + + data = g_task_get_task_data (G_TASK (result)); + if (print) { - *print = g_object_get_data (G_OBJECT (result), "print"); + *print = data->print; if (*print) g_object_ref (*print); } if (match) { - *match = g_object_get_data (G_OBJECT (result), "match"); + *match = data->match; if (*match) g_object_ref (*match); } @@ -1332,6 +1364,7 @@ fp_device_verify_sync (FpDevice *device, fp_device_verify (device, enrolled_print, cancellable, + NULL, NULL, NULL, async_result_ready, &task); while (!task) g_main_context_iteration (NULL, TRUE); @@ -1367,6 +1400,7 @@ fp_device_identify_sync (FpDevice *device, fp_device_identify (device, prints, cancellable, + NULL, NULL, NULL, async_result_ready, &task); while (!task) g_main_context_iteration (NULL, TRUE); diff --git a/libfprint/fp-device.h b/libfprint/fp-device.h index 4f7acaca..5b7cf86d 100644 --- a/libfprint/fp-device.h +++ b/libfprint/fp-device.h @@ -125,6 +125,30 @@ typedef void (*FpEnrollProgress) (FpDevice *device, gpointer user_data, GError *error); +/** + * FpMatchCb: + * @device: a #FpDevice + * @success: Whether a print was retrieved, %FALSE means @error is set + * @match: (nullable) (transfer none): The matching print + * @print: (nullable) (transfer none): The newly scanned print + * @user_data: (nullable) (transfer none): User provided data + * @error: (nullable) (transfer none): #GError or %NULL + * + * Report the result of a match (identify or verify) operation. This callback + * because it makes sense for drivers to wait e.g. on finger removal before + * finishing the operation. However, the success/failure can often be reported + * at an earlier time, and there is no need to make the user wait. + * + * The passed error is guaranteed to be of type %FP_DEVICE_RETRY if set. Actual + * error conditions will not be reported using this function. Such an error may + * still happen even if this callback has been called. + */ +typedef void (*FpMatchCb) (FpDevice *device, + gboolean success, + FpPrint *match, + FpPrint *print, + gpointer user_data, + GError *error); const gchar *fp_device_get_driver (FpDevice *device); const gchar *fp_device_get_device_id (FpDevice *device); @@ -160,12 +184,18 @@ void fp_device_enroll (FpDevice *device, void fp_device_verify (FpDevice *device, FpPrint *enrolled_print, GCancellable *cancellable, + FpMatchCb match_cb, + gpointer match_data, + GDestroyNotify match_destroy, GAsyncReadyCallback callback, gpointer user_data); void fp_device_identify (FpDevice *device, GPtrArray *prints, GCancellable *cancellable, + FpMatchCb match_cb, + gpointer match_data, + GDestroyNotify match_destroy, GAsyncReadyCallback callback, gpointer user_data); diff --git a/libfprint/fpi-device.c b/libfprint/fpi-device.c index 8b2ef9d8..629fd2ef 100644 --- a/libfprint/fpi-device.c +++ b/libfprint/fpi-device.c @@ -420,6 +420,23 @@ enroll_data_free (FpEnrollData *data) g_free (data); } +void +match_data_free (FpMatchData *data) +{ + g_clear_object (&data->print); + g_clear_object (&data->match); + g_clear_error (&data->error); + + if (data->match_destroy) + data->match_destroy (data->match_data); + data->match_data = NULL; + + g_clear_object (&data->enrolled_print); + g_clear_pointer (&data->gallery, g_ptr_array_unref); + + g_free (data); +} + /** * fpi_device_get_enroll_data: * @device: The #FpDevice @@ -476,12 +493,16 @@ fpi_device_get_verify_data (FpDevice *device, FpPrint **print) { FpDevicePrivate *priv = fp_device_get_instance_private (device); + FpMatchData *data; g_return_if_fail (FP_IS_DEVICE (device)); g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_VERIFY); + data = g_task_get_task_data (priv->current_task); + g_assert (data); + if (print) - *print = g_task_get_task_data (priv->current_task); + *print = data->enrolled_print; } /** @@ -496,12 +517,16 @@ fpi_device_get_identify_data (FpDevice *device, GPtrArray **prints) { FpDevicePrivate *priv = fp_device_get_instance_private (device); + FpMatchData *data; g_return_if_fail (FP_IS_DEVICE (device)); g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_IDENTIFY); + data = g_task_get_task_data (priv->current_task); + g_assert (data); + if (prints) - *prints = g_task_get_task_data (priv->current_task); + *prints = data->gallery; } /** @@ -596,11 +621,11 @@ fpi_device_action_error (FpDevice *device, break; case FPI_DEVICE_ACTION_VERIFY: - fpi_device_verify_complete (device, FPI_MATCH_ERROR, NULL, error); + fpi_device_verify_complete (device, error); break; case FPI_DEVICE_ACTION_IDENTIFY: - fpi_device_identify_complete (device, NULL, NULL, error); + fpi_device_identify_complete (device, error); break; case FPI_DEVICE_ACTION_CAPTURE: @@ -907,67 +932,65 @@ fpi_device_enroll_complete (FpDevice *device, FpPrint *print, GError *error) /** * fpi_device_verify_complete: * @device: The #FpDevice - * @result: The #FpiMatchResult of the operation - * @print: (transfer floating) The scanned #FpPrint * @error: A #GError if result is %FPI_MATCH_ERROR * * Finish an ongoing verify operation. The returned print should be * representing the new scan and not the one passed for verification. + * + * Note that @error should only be set for actual errors. In the case + * of retry errors, report these using fpi_device_verify_report() + * and then call this function without any error argument. */ void -fpi_device_verify_complete (FpDevice *device, - FpiMatchResult result, - FpPrint *print, - GError *error) +fpi_device_verify_complete (FpDevice *device, + GError *error) { FpDevicePrivate *priv = fp_device_get_instance_private (device); + FpMatchData *data; g_return_if_fail (FP_IS_DEVICE (device)); g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_VERIFY); g_debug ("Device reported verify completion"); + data = g_task_get_task_data (priv->current_task); + clear_device_cancel_action (device); - if (print) - g_object_ref_sink (print); - - g_object_set_data_full (G_OBJECT (priv->current_task), - "print", - print, - g_object_unref); - if (!error) { - if (result != FPI_MATCH_ERROR) + if (!data->result_reported) { - fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_INT, - GINT_TO_POINTER (result)); + g_warning ("Driver reported successful verify complete but did not report the result earlier. Reporting error instead"); + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, + fpi_device_error_new (FP_DEVICE_ERROR_GENERAL)); + } + else if (data->error) + { + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, g_steal_pointer (&data->error)); } else { - g_warning ("Driver did not provide an error for a failed verify operation!"); - error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, - "Driver failed to provide an error!"); - fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_INT, + GINT_TO_POINTER (data->match != NULL ? FPI_MATCH_SUCCESS : FPI_MATCH_FAIL)); } } else { - fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); - if (result != FPI_MATCH_ERROR) + /* Replace a retry error with a general error, this is a driver bug. */ + if (error->domain == FP_DEVICE_RETRY) { - g_warning ("Driver passed an error but also provided a match result, returning error!"); - g_object_unref (print); + g_warning ("Driver reported a retry error to fpi_device_verify_complete; reporting operation failure instead!"); + g_clear_error (&error); + error = fpi_device_error_new (FP_DEVICE_ERROR_GENERAL); } + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); } } /** * fpi_device_identify_complete: * @device: The #FpDevice - * @match: (transfer none): The matching #FpPrint from the passed gallery, or %NULL if none matched - * @print: (transfer floating): The scanned #FpPrint, may be %NULL * @error: The #GError or %NULL on success * * Finish an ongoing identify operation. The match that was identified is @@ -976,46 +999,47 @@ fpi_device_verify_complete (FpDevice *device, */ void fpi_device_identify_complete (FpDevice *device, - FpPrint *match, - FpPrint *print, GError *error) { FpDevicePrivate *priv = fp_device_get_instance_private (device); + FpMatchData *data; g_return_if_fail (FP_IS_DEVICE (device)); g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_IDENTIFY); g_debug ("Device reported identify completion"); + data = g_task_get_task_data (priv->current_task); + clear_device_cancel_action (device); - if (match) - g_object_ref (match); - - if (print) - g_object_ref_sink (print); - - g_object_set_data_full (G_OBJECT (priv->current_task), - "print", - print, - g_object_unref); - g_object_set_data_full (G_OBJECT (priv->current_task), - "match", - match, - g_object_unref); if (!error) { - fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL, - GUINT_TO_POINTER (TRUE)); + if (!data->result_reported) + { + g_warning ("Driver reported successful identify complete but did not report the result earlier. Reporting error instead"); + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, + fpi_device_error_new (FP_DEVICE_ERROR_GENERAL)); + } + else if (data->error) + { + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, g_steal_pointer (&data->error)); + } + else + { + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_INT, GINT_TO_POINTER (TRUE)); + } } else { - fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); - if (match) + /* Replace a retry error with a general error, this is a driver bug. */ + if (error->domain == FP_DEVICE_RETRY) { - g_warning ("Driver passed an error but also provided a match result, returning error!"); - g_clear_object (&match); + g_warning ("Driver reported a retry error to fpi_device_identify_complete; reporting operation failure instead!"); + g_clear_error (&error); + error = fpi_device_error_new (FP_DEVICE_ERROR_GENERAL); } + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); } } @@ -1187,3 +1211,151 @@ fpi_device_enroll_progress (FpDevice *device, g_clear_error (&error); g_clear_object (&print); } + +/** + * fpi_device_verify_report: + * @device: The #FpDevice + * @result: The #FpiMatchResult of the operation + * @print: (transfer floating) The scanned #FpPrint + * @error: A #GError if result is %FPI_MATCH_ERROR + * + * Report the result of a verify operation. Note that the passed @error must be + * a retry error with the %FP_DEVICE_RETRY domain. For all other error cases, + * the error should passed to fpi_device_verify_complete(). + */ +void +fpi_device_verify_report (FpDevice *device, + FpiMatchResult result, + FpPrint *print, + GError *error) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + FpMatchData *data = g_task_get_task_data (priv->current_task); + gboolean call_cb = TRUE; + + g_return_if_fail (FP_IS_DEVICE (device)); + g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_VERIFY); + g_return_if_fail (data->result_reported == FALSE); + + data->result_reported = TRUE; + + g_debug ("Device reported verify result"); + + if (print) + print = g_object_ref_sink (print); + + if (error || result == FPI_MATCH_ERROR) + { + if (result != FPI_MATCH_ERROR) + g_warning ("Driver reported an error code without setting match result to error!"); + + if (error == NULL) + { + g_warning ("Driver reported an error without specifying a retry code, assuming general retry error!"); + error = fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL); + } + + if (print) + { + g_warning ("Driver reported a print together with an error!"); + g_clear_object (&print); + } + + data->error = error; + + if (error->domain != FP_DEVICE_RETRY) + { + g_warning ("Driver reported a verify error that was not in the retry domain, delaying report!"); + call_cb = FALSE; + } + } + else + { + if (result == FPI_MATCH_SUCCESS) + { + fpi_device_get_verify_data (device, &data->match); + g_object_ref (data->match); + } + + data->print = g_steal_pointer (&print); + } + + if (call_cb && data->match_cb) + data->match_cb (device, data->error == NULL, data->match, data->print, data->match_data, data->error); +} + +/** + * fpi_device_identify_report: + * @device: The #FpDevice + * @match: (transfer none): The #FpPrint from the gallery that matched + * @print: (transfer floating): The scanned #FpPrint + * @error: A #GError if result is %FPI_MATCH_ERROR + * + * Report the result of a identify operation. Note that the passed @error must be + * a retry error with the %FP_DEVICE_RETRY domain. For all other error cases, + * the error should passed to fpi_device_identify_complete(). + */ +void +fpi_device_identify_report (FpDevice *device, + FpPrint *match, + FpPrint *print, + GError *error) +{ + FpDevicePrivate *priv = fp_device_get_instance_private (device); + FpMatchData *data = g_task_get_task_data (priv->current_task); + gboolean call_cb = TRUE; + + g_return_if_fail (FP_IS_DEVICE (device)); + g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_IDENTIFY); + g_return_if_fail (data->result_reported == FALSE); + + data->result_reported = TRUE; + + if (match) + g_object_ref (match); + + if (print) + print = g_object_ref_sink (print); + + if (match && !g_ptr_array_find (data->gallery, match, NULL)) + { + g_warning ("Driver reported a match to a print that was not in the gallery, ignoring match."); + g_clear_object (&match); + } + + g_debug ("Device reported identify result"); + + if (error) + { + if (match != NULL) + { + g_warning ("Driver reported an error code but also provided a match!"); + g_clear_object (&match); + } + + if (print) + { + g_warning ("Driver reported a print together with an error!"); + g_clear_object (&print); + } + + data->error = error; + + if (error->domain != FP_DEVICE_RETRY) + { + g_warning ("Driver reported a verify error that was not in the retry domain, delaying report!"); + call_cb = FALSE; + } + } + else + { + if (match) + data->match = g_steal_pointer (&match); + + if (print) + data->print = g_steal_pointer (&print); + } + + if (call_cb && data->match_cb) + data->match_cb (device, data->error == NULL, data->match, data->print, data->match_data, data->error); +} diff --git a/libfprint/fpi-device.h b/libfprint/fpi-device.h index 3d66ee58..1f53eaf9 100644 --- a/libfprint/fpi-device.h +++ b/libfprint/fpi-device.h @@ -229,13 +229,9 @@ void fpi_device_close_complete (FpDevice *device, void fpi_device_enroll_complete (FpDevice *device, FpPrint *print, GError *error); -void fpi_device_verify_complete (FpDevice *device, - FpiMatchResult result, - FpPrint *print, - GError *error); +void fpi_device_verify_complete (FpDevice *device, + GError *error); void fpi_device_identify_complete (FpDevice *device, - FpPrint *match, - FpPrint *print, GError *error); void fpi_device_capture_complete (FpDevice *device, FpImage *image, @@ -250,5 +246,13 @@ void fpi_device_enroll_progress (FpDevice *device, gint completed_stages, FpPrint *print, GError *error); +void fpi_device_verify_report (FpDevice *device, + FpiMatchResult result, + FpPrint *print, + GError *error); +void fpi_device_identify_report (FpDevice *device, + FpPrint *match, + FpPrint *print, + GError *error); G_END_DECLS diff --git a/libfprint/fpi-image-device.c b/libfprint/fpi-image-device.c index f962b8ae..0af70ba1 100644 --- a/libfprint/fpi-image-device.c +++ b/libfprint/fpi-image-device.c @@ -210,7 +210,9 @@ fpi_image_device_minutiae_detected (GObject *source_object, GAsyncResult *res, g else result = FPI_MATCH_ERROR; - fpi_device_verify_complete (device, result, g_steal_pointer (&print), error); + if (!error || error->domain == FP_DEVICE_RETRY) + fpi_device_verify_report (device, result, g_steal_pointer (&print), g_steal_pointer (&error)); + fpi_device_verify_complete (device, error); fpi_image_device_deactivate (self); } else if (action == FPI_DEVICE_ACTION_IDENTIFY) @@ -231,7 +233,9 @@ fpi_image_device_minutiae_detected (GObject *source_object, GAsyncResult *res, g } } - fpi_device_identify_complete (device, result, g_steal_pointer (&print), error); + if (!error || error->domain == FP_DEVICE_RETRY) + fpi_device_identify_report (device, result, g_steal_pointer (&print), g_steal_pointer (&error)); + fpi_device_identify_complete (device, error); fpi_image_device_deactivate (self); } else diff --git a/tests/test-device-fake.c b/tests/test-device-fake.c index eaa1fa63..84e5e202 100644 --- a/tests/test-device-fake.c +++ b/tests/test-device-fake.c @@ -95,8 +95,16 @@ fpi_device_fake_verify (FpDevice *device) fpi_device_get_verify_data (device, &print); fake_dev->last_called_function = fpi_device_fake_verify; - fpi_device_verify_complete (device, fake_dev->ret_result, print, - fake_dev->ret_error); + + if (!fake_dev->ret_error || fake_dev->ret_error->domain == FP_DEVICE_RETRY) + { + fpi_device_verify_report (device, fake_dev->ret_result, print, fake_dev->ret_error); + fpi_device_verify_complete (device, NULL); + } + else + { + fpi_device_verify_complete (device, fake_dev->ret_error); + } } static void @@ -128,8 +136,15 @@ fpi_device_fake_identify (FpDevice *device) } fake_dev->last_called_function = fpi_device_fake_identify; - fpi_device_identify_complete (device, match, fake_dev->ret_print, - fake_dev->ret_error); + if (!fake_dev->ret_error || fake_dev->ret_error->domain == FP_DEVICE_RETRY) + { + fpi_device_identify_report (device, match, fake_dev->ret_print, fake_dev->ret_error); + fpi_device_identify_complete (device, NULL); + } + else + { + fpi_device_identify_complete (device, fake_dev->ret_error); + } } static void diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c index 3d1e81c4..6af8eb93 100644 --- a/tests/test-fpi-device.c +++ b/tests/test-fpi-device.c @@ -1166,12 +1166,12 @@ test_driver_complete_actions_errors (void) g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*assertion*current_action*failed"); - fpi_device_verify_complete (device, FPI_MATCH_FAIL, NULL, NULL); + fpi_device_verify_complete (device, NULL); g_test_assert_expected_messages (); g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*assertion*current_action*failed"); - fpi_device_identify_complete (device, NULL, NULL, NULL); + fpi_device_identify_complete (device, NULL); g_test_assert_expected_messages (); g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, diff --git a/tests/virtual-image.py b/tests/virtual-image.py index 11ec8ae8..1fcb30a2 100755 --- a/tests/virtual-image.py +++ b/tests/virtual-image.py @@ -220,7 +220,7 @@ class VirtualImage(unittest.TestCase): self._verify_match = None self._verify_fp = None - self.dev.verify(fp_whorl, None, verify_cb) + self.dev.verify(fp_whorl, callback=verify_cb) self.send_image('whorl') while self._verify_match is None: ctx.iteration(True) @@ -228,7 +228,7 @@ class VirtualImage(unittest.TestCase): self._verify_match = None self._verify_fp = None - self.dev.verify(fp_whorl, None, verify_cb) + self.dev.verify(fp_whorl, callback=verify_cb) self.send_image('tented_arch') while self._verify_match is None: ctx.iteration(True) @@ -250,14 +250,14 @@ class VirtualImage(unittest.TestCase): self._identify_match, self._identify_fp = self.dev.identify_finish(res) self._identify_fp = None - self.dev.identify([fp_whorl, fp_tented_arch], None, identify_cb) + self.dev.identify([fp_whorl, fp_tented_arch], callback=identify_cb) self.send_image('tented_arch') while self._identify_fp is None: ctx.iteration(True) assert(self._identify_match is fp_tented_arch) self._identify_fp = None - self.dev.identify([fp_whorl, fp_tented_arch], None, identify_cb) + self.dev.identify([fp_whorl, fp_tented_arch], callback=identify_cb) self.send_image('whorl') while self._identify_fp is None: ctx.iteration(True) @@ -290,7 +290,7 @@ class VirtualImage(unittest.TestCase): self._verify_match = None self._verify_fp = None - self.dev.verify(fp_whorl_new, None, verify_cb) + self.dev.verify(fp_whorl_new, callback=verify_cb) self.send_image('whorl') while self._verify_match is None: ctx.iteration(True) @@ -298,7 +298,7 @@ class VirtualImage(unittest.TestCase): self._verify_match = None self._verify_fp = None - self.dev.verify(fp_whorl_new, None, verify_cb) + self.dev.verify(fp_whorl_new, callback=verify_cb) self.send_image('tented_arch') while self._verify_match is None: ctx.iteration(True) From 829fb9f873a54fdb0caaf9126eb09f0e27e3e2bf Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Fri, 10 Jan 2020 17:10:44 +0100 Subject: [PATCH 189/237] device: Add early match reporting to sync API This makes the sync/async APIs more similar again. It also simplifies testing as otherwise we would need the async methods for some tests. --- libfprint/fp-device.c | 12 ++++++++++-- libfprint/fp-device.h | 4 ++++ tests/test-fpi-device.c | 12 ++++++------ 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/libfprint/fp-device.c b/libfprint/fp-device.c index 3b36ae6c..40b3efa2 100644 --- a/libfprint/fp-device.c +++ b/libfprint/fp-device.c @@ -1341,6 +1341,8 @@ fp_device_enroll_sync (FpDevice *device, * @device: a #FpDevice * @enrolled_print: a #FpPrint to verify * @cancellable: (nullable): a #GCancellable, or %NULL + * @match_cb: (nullable) (scope call): match reporting callback + * @match_data: (closure match_cb): user data for @match_cb * @match: (out): Whether the user presented the correct finger * @print: (out) (transfer full) (nullable): Location to store the scanned print, or %NULL to ignore * @error: Return location for errors, or %NULL to ignore @@ -1353,6 +1355,8 @@ gboolean fp_device_verify_sync (FpDevice *device, FpPrint *enrolled_print, GCancellable *cancellable, + FpMatchCb match_cb, + gpointer match_data, gboolean *match, FpPrint **print, GError **error) @@ -1364,7 +1368,7 @@ fp_device_verify_sync (FpDevice *device, fp_device_verify (device, enrolled_print, cancellable, - NULL, NULL, NULL, + match_cb, match_data, NULL, async_result_ready, &task); while (!task) g_main_context_iteration (NULL, TRUE); @@ -1377,6 +1381,8 @@ fp_device_verify_sync (FpDevice *device, * @device: a #FpDevice * @prints: (element-type FpPrint) (transfer none): #GPtrArray of #FpPrint * @cancellable: (nullable): a #GCancellable, or %NULL + * @match_cb: (nullable) (scope call): match reporting callback + * @match_data: (closure match_cb): user data for @match_cb * @match: (out) (transfer full) (nullable): Location for the matched #FpPrint, or %NULL * @print: (out) (transfer full) (nullable): Location for the new #FpPrint, or %NULL * @error: Return location for errors, or %NULL to ignore @@ -1389,6 +1395,8 @@ gboolean fp_device_identify_sync (FpDevice *device, GPtrArray *prints, GCancellable *cancellable, + FpMatchCb match_cb, + gpointer match_data, FpPrint **match, FpPrint **print, GError **error) @@ -1400,7 +1408,7 @@ fp_device_identify_sync (FpDevice *device, fp_device_identify (device, prints, cancellable, - NULL, NULL, NULL, + match_cb, match_data, NULL, async_result_ready, &task); while (!task) g_main_context_iteration (NULL, TRUE); diff --git a/libfprint/fp-device.h b/libfprint/fp-device.h index 5b7cf86d..69ac5c79 100644 --- a/libfprint/fp-device.h +++ b/libfprint/fp-device.h @@ -261,12 +261,16 @@ FpPrint * fp_device_enroll_sync (FpDevice *device, gboolean fp_device_verify_sync (FpDevice *device, FpPrint *enrolled_print, GCancellable *cancellable, + FpMatchCb match_cb, + gpointer match_data, gboolean *match, FpPrint **print, GError **error); gboolean fp_device_identify_sync (FpDevice *device, GPtrArray *prints, GCancellable *cancellable, + FpMatchCb match_cb, + gpointer match_data, FpPrint **match, FpPrint **print, GError **error); diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c index 6af8eb93..8d4c116a 100644 --- a/tests/test-fpi-device.c +++ b/tests/test-fpi-device.c @@ -555,7 +555,7 @@ test_driver_verify (void) gboolean match; fake_dev->ret_result = FPI_MATCH_SUCCESS; - fp_device_verify_sync (device, enrolled_print, NULL, &match, &out_print, &error); + fp_device_verify_sync (device, enrolled_print, NULL, NULL, NULL, &match, &out_print, &error); g_assert (fake_dev->last_called_function == dev_class->verify); g_assert (fake_dev->action_data == enrolled_print); @@ -577,7 +577,7 @@ test_driver_verify_fail (void) gboolean match; fake_dev->ret_result = FPI_MATCH_FAIL; - fp_device_verify_sync (device, enrolled_print, NULL, &match, &out_print, &error); + fp_device_verify_sync (device, enrolled_print, NULL, NULL, NULL, &match, &out_print, &error); g_assert (fake_dev->last_called_function == dev_class->verify); g_assert_no_error (error); @@ -599,7 +599,7 @@ test_driver_verify_error (void) fake_dev->ret_result = FPI_MATCH_ERROR; fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_GENERAL); - fp_device_verify_sync (device, enrolled_print, NULL, &match, &out_print, &error); + fp_device_verify_sync (device, enrolled_print, NULL, NULL, NULL, &match, &out_print, &error); g_assert (fake_dev->last_called_function == dev_class->verify); g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); @@ -658,7 +658,7 @@ test_driver_identify (void) g_assert_true (fp_device_supports_identify (device)); fake_dev->ret_print = fp_print_new (device); - fp_device_identify_sync (device, prints, NULL, &matched_print, &print, &error); + fp_device_identify_sync (device, prints, NULL, NULL, NULL, &matched_print, &print, &error); g_assert (fake_dev->last_called_function == dev_class->identify); g_assert (fake_dev->action_data == prints); @@ -686,7 +686,7 @@ test_driver_identify_fail (void) g_assert_true (fp_device_supports_identify (device)); fake_dev->ret_print = fp_print_new (device); - fp_device_identify_sync (device, prints, NULL, &matched_print, &print, &error); + fp_device_identify_sync (device, prints, NULL, NULL, NULL, &matched_print, &print, &error); g_assert (fake_dev->last_called_function == dev_class->identify); g_assert_no_error (error); @@ -717,7 +717,7 @@ test_driver_identify_error (void) g_assert_true (fp_device_supports_identify (device)); fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_GENERAL); - fp_device_identify_sync (device, prints, NULL, &matched_print, &print, &error); + fp_device_identify_sync (device, prints, NULL, NULL, NULL, &matched_print, &print, &error); g_assert (fake_dev->last_called_function == dev_class->identify); g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); From 0c582230f3cebcae699ac4ce21ade6724ce577e8 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 13 Jan 2020 13:30:39 +0100 Subject: [PATCH 190/237] tests: Add tests for the early reporting mechanism This adds tests to check whether the verify/identify early reporting mechanism is behaving correctly. --- tests/test-fpi-device.c | 117 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 111 insertions(+), 6 deletions(-) diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c index 8d4c116a..3e2851c8 100644 --- a/tests/test-fpi-device.c +++ b/tests/test-fpi-device.c @@ -543,6 +543,57 @@ test_driver_enroll_progress (void) g_assert (fake_dev->last_called_function == test_driver_enroll_progress_vfunc); } +typedef struct +{ + gboolean called; + gboolean success; + FpPrint *match; + FpPrint *print; + GError *error; +} MatchCbData; + +static void +test_driver_match_data_clear (MatchCbData *data) +{ + data->called = FALSE; + data->success = FALSE; + g_clear_object (&data->match); + g_clear_object (&data->print); + g_clear_error (&data->error); +} + +static void +test_driver_match_cb (FpDevice *device, + gboolean success, + FpPrint *match, + FpPrint *print, + gpointer user_data, + GError *error) +{ + MatchCbData *data = user_data; + + g_assert (data->called == FALSE); + data->called = TRUE; + data->success = TRUE; + if (match) + data->match = g_object_ref (match); + if (print) + data->print = g_object_ref (print); + if (error) + data->error = g_error_copy (error); + + if (success) + { + g_assert_null (error); + } + else + { + g_assert_nonnull (error); + g_assert_null (match); + g_assert_null (print); + } +} + static void test_driver_verify (void) { @@ -552,17 +603,27 @@ test_driver_verify (void) g_autoptr(FpPrint) out_print = NULL; FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + MatchCbData match_data = { 0, }; gboolean match; fake_dev->ret_result = FPI_MATCH_SUCCESS; - fp_device_verify_sync (device, enrolled_print, NULL, NULL, NULL, &match, &out_print, &error); + fp_device_verify_sync (device, enrolled_print, NULL, + test_driver_match_cb, &match_data, + &match, &out_print, &error); g_assert (fake_dev->last_called_function == dev_class->verify); g_assert (fake_dev->action_data == enrolled_print); g_assert_no_error (error); + g_assert_true (match_data.called); + g_assert_true (match_data.success); + g_assert_true (match_data.print == out_print); + g_assert_true (match_data.match == enrolled_print); + g_assert (out_print == enrolled_print); g_assert_true (match); + + test_driver_match_data_clear (&match_data); } static void @@ -574,16 +635,26 @@ test_driver_verify_fail (void) g_autoptr(FpPrint) out_print = NULL; FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + MatchCbData match_data = { 0, }; gboolean match; fake_dev->ret_result = FPI_MATCH_FAIL; - fp_device_verify_sync (device, enrolled_print, NULL, NULL, NULL, &match, &out_print, &error); + fp_device_verify_sync (device, enrolled_print, NULL, + test_driver_match_cb, &match_data, + &match, &out_print, &error); g_assert (fake_dev->last_called_function == dev_class->verify); g_assert_no_error (error); + g_assert_true (match_data.called); + g_assert_true (match_data.success); + g_assert_true (match_data.print == out_print); + g_assert_null (match_data.match); + g_assert (out_print == enrolled_print); g_assert_false (match); + + test_driver_match_data_clear (&match_data); } static void @@ -595,16 +666,23 @@ test_driver_verify_error (void) g_autoptr(FpPrint) out_print = NULL; FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + MatchCbData match_data = { 0, }; gboolean match; fake_dev->ret_result = FPI_MATCH_ERROR; fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_GENERAL); - fp_device_verify_sync (device, enrolled_print, NULL, NULL, NULL, &match, &out_print, &error); + fp_device_verify_sync (device, enrolled_print, NULL, + test_driver_match_cb, &match_data, + &match, &out_print, &error); + + g_assert_false (match_data.called); g_assert (fake_dev->last_called_function == dev_class->verify); g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); g_assert (error == g_steal_pointer (&fake_dev->ret_error)); g_assert_false (match); + + test_driver_match_data_clear (&match_data); } static void @@ -647,6 +725,7 @@ test_driver_identify (void) FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); FpPrint *expected_matched; + MatchCbData match_data = { 0, }; unsigned int i; for (i = 0; i < 500; ++i) @@ -658,7 +737,14 @@ test_driver_identify (void) g_assert_true (fp_device_supports_identify (device)); fake_dev->ret_print = fp_print_new (device); - fp_device_identify_sync (device, prints, NULL, NULL, NULL, &matched_print, &print, &error); + fp_device_identify_sync (device, prints, NULL, + test_driver_match_cb, &match_data, + &matched_print, &print, &error); + + g_assert_true (match_data.called); + g_assert_true (match_data.success); + g_assert_true (match_data.match == matched_print); + g_assert_true (match_data.print == print); g_assert (fake_dev->last_called_function == dev_class->identify); g_assert (fake_dev->action_data == prints); @@ -666,6 +752,8 @@ test_driver_identify (void) g_assert (print != NULL && print == fake_dev->ret_print); g_assert (expected_matched == matched_print); + + test_driver_match_data_clear (&match_data); } static void @@ -678,6 +766,7 @@ test_driver_identify_fail (void) g_autoptr(GPtrArray) prints = g_ptr_array_new_with_free_func (g_object_unref); FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + MatchCbData match_data = { 0, }; unsigned int i; for (i = 0; i < 500; ++i) @@ -686,13 +775,22 @@ test_driver_identify_fail (void) g_assert_true (fp_device_supports_identify (device)); fake_dev->ret_print = fp_print_new (device); - fp_device_identify_sync (device, prints, NULL, NULL, NULL, &matched_print, &print, &error); + fp_device_identify_sync (device, prints, NULL, + test_driver_match_cb, &match_data, + &matched_print, &print, &error); + + g_assert_true (match_data.called); + g_assert_true (match_data.success); + g_assert_true (match_data.match == matched_print); + g_assert_true (match_data.print == print); g_assert (fake_dev->last_called_function == dev_class->identify); g_assert_no_error (error); g_assert (print != NULL && print == fake_dev->ret_print); g_assert_null (matched_print); + + test_driver_match_data_clear (&match_data); } static void @@ -706,6 +804,7 @@ test_driver_identify_error (void) FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); FpPrint *expected_matched; + MatchCbData match_data = { 0, }; unsigned int i; for (i = 0; i < 500; ++i) @@ -717,13 +816,19 @@ test_driver_identify_error (void) g_assert_true (fp_device_supports_identify (device)); fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_GENERAL); - fp_device_identify_sync (device, prints, NULL, NULL, NULL, &matched_print, &print, &error); + fp_device_identify_sync (device, prints, NULL, + test_driver_match_cb, &match_data, + &matched_print, &print, &error); + + g_assert_false (match_data.called); g_assert (fake_dev->last_called_function == dev_class->identify); g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); g_assert (error == g_steal_pointer (&fake_dev->ret_error)); g_assert_null (matched_print); g_assert_null (print); + + test_driver_match_data_clear (&match_data); } static void From 0b8e2d607403dcc978c725f90be49acf73e96e8f Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 13 Jan 2020 13:34:34 +0100 Subject: [PATCH 191/237] tests: Add tests for the verify/identify retry cases --- tests/test-fpi-device.c | 70 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c index 3e2851c8..bac0a51a 100644 --- a/tests/test-fpi-device.c +++ b/tests/test-fpi-device.c @@ -657,6 +657,35 @@ test_driver_verify_fail (void) test_driver_match_data_clear (&match_data); } +static void +test_driver_verify_retry (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); + g_autoptr(FpPrint) enrolled_print = g_object_ref_sink (fp_print_new (device)); + g_autoptr(FpPrint) out_print = NULL; + FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + MatchCbData match_data = { 0, }; + gboolean match; + + fake_dev->ret_result = FPI_MATCH_ERROR; + fake_dev->ret_error = fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL); + fp_device_verify_sync (device, enrolled_print, NULL, + test_driver_match_cb, &match_data, + &match, &out_print, &error); + + g_assert_true (match_data.called); + g_assert_error (match_data.error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_GENERAL); + + g_assert (fake_dev->last_called_function == dev_class->verify); + g_assert_error (error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_GENERAL); + g_assert (error == g_steal_pointer (&fake_dev->ret_error)); + g_assert_false (match); + + test_driver_match_data_clear (&match_data); +} + static void test_driver_verify_error (void) { @@ -793,6 +822,45 @@ test_driver_identify_fail (void) test_driver_match_data_clear (&match_data); } +static void +test_driver_identify_retry (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpPrint) print = NULL; + g_autoptr(FpPrint) matched_print = NULL; + g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); + g_autoptr(GPtrArray) prints = g_ptr_array_new_with_free_func (g_object_unref); + FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + FpPrint *expected_matched; + MatchCbData match_data = { 0, }; + unsigned int i; + + for (i = 0; i < 500; ++i) + g_ptr_array_add (prints, g_object_ref_sink (fp_print_new (device))); + + expected_matched = g_ptr_array_index (prints, g_random_int_range (0, 499)); + fp_print_set_description (expected_matched, "fake-verified"); + + g_assert_true (fp_device_supports_identify (device)); + + fake_dev->ret_error = fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL); + fp_device_identify_sync (device, prints, NULL, + test_driver_match_cb, &match_data, + &matched_print, &print, &error); + + g_assert_true (match_data.called); + g_assert_error (match_data.error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_GENERAL); + + g_assert (fake_dev->last_called_function == dev_class->identify); + g_assert_error (error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_GENERAL); + g_assert (error == g_steal_pointer (&fake_dev->ret_error)); + g_assert_null (matched_print); + g_assert_null (print); + + test_driver_match_data_clear (&match_data); +} + static void test_driver_identify_error (void) { @@ -1483,9 +1551,11 @@ main (int argc, char *argv[]) g_test_add_func ("/driver/enroll/progress", test_driver_enroll_progress); g_test_add_func ("/driver/verify", test_driver_verify); g_test_add_func ("/driver/verify/fail", test_driver_verify_fail); + g_test_add_func ("/driver/verify/retry", test_driver_verify_retry); g_test_add_func ("/driver/verify/error", test_driver_verify_error); g_test_add_func ("/driver/identify", test_driver_identify); g_test_add_func ("/driver/identify/fail", test_driver_identify_fail); + g_test_add_func ("/driver/identify/retry", test_driver_identify_retry); g_test_add_func ("/driver/identify/error", test_driver_identify_error); g_test_add_func ("/driver/capture", test_driver_capture); g_test_add_func ("/driver/capture/error", test_driver_capture_error); From 7aaeec3d6a47ffe9f39f61a04ef68f36b3dfdc03 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 13 Jan 2020 14:36:17 +0100 Subject: [PATCH 192/237] tests: Check that missing identify/verify result returns error --- tests/test-fpi-device.c | 70 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c index bac0a51a..537bad95 100644 --- a/tests/test-fpi-device.c +++ b/tests/test-fpi-device.c @@ -714,6 +714,38 @@ test_driver_verify_error (void) test_driver_match_data_clear (&match_data); } +static void +fake_device_verify_immediate_complete (FpDevice *device) +{ + fpi_device_verify_complete (device, NULL); +} + +static void +test_driver_verify_not_reported (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(FpPrint) enrolled_print = NULL; + g_autoptr(GError) error = NULL; + + dev_class->verify = fake_device_verify_immediate_complete; + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + enrolled_print = g_object_ref_sink (fp_print_new (device)); + + g_assert_true (fp_device_open_sync (device, NULL, NULL)); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*reported successful verify complete*not report*result*"); + + fp_device_verify_sync (device, enrolled_print, NULL, + NULL, NULL, + NULL, NULL, &error); + + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + + g_test_assert_expected_messages (); +} + static void fake_device_stub_identify (FpDevice *device) { @@ -899,6 +931,42 @@ test_driver_identify_error (void) test_driver_match_data_clear (&match_data); } +static void +fake_device_identify_immediate_complete (FpDevice *device) +{ + fpi_device_identify_complete (device, NULL); +} + +static void +test_driver_identify_not_reported (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(GPtrArray) prints = g_ptr_array_new_with_free_func (g_object_unref); + g_autoptr(FpPrint) out_print = NULL; + g_autoptr(GError) error = NULL; + unsigned int i; + + dev_class->identify = fake_device_identify_immediate_complete; + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + + for (i = 0; i < 500; ++i) + g_ptr_array_add (prints, g_object_ref_sink (fp_print_new (device))); + + g_assert_true (fp_device_open_sync (device, NULL, NULL)); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*reported successful identify complete*not report*result*"); + + fp_device_identify_sync (device, prints, NULL, + NULL, NULL, + NULL, NULL, &error); + + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + + g_test_assert_expected_messages (); +} + static void fake_device_stub_capture (FpDevice *device) { @@ -1553,10 +1621,12 @@ main (int argc, char *argv[]) g_test_add_func ("/driver/verify/fail", test_driver_verify_fail); g_test_add_func ("/driver/verify/retry", test_driver_verify_retry); g_test_add_func ("/driver/verify/error", test_driver_verify_error); + g_test_add_func ("/driver/verify/not_reported", test_driver_verify_not_reported); g_test_add_func ("/driver/identify", test_driver_identify); g_test_add_func ("/driver/identify/fail", test_driver_identify_fail); g_test_add_func ("/driver/identify/retry", test_driver_identify_retry); g_test_add_func ("/driver/identify/error", test_driver_identify_error); + g_test_add_func ("/driver/identify/not_reported", test_driver_identify_not_reported); g_test_add_func ("/driver/capture", test_driver_capture); g_test_add_func ("/driver/capture/error", test_driver_capture_error); g_test_add_func ("/driver/list", test_driver_list); From db14995c319119cca2c453b037ac69add8a871b6 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 13 Jan 2020 17:56:53 +0100 Subject: [PATCH 193/237] image-device: Set cancelling when errors are reported Allow the AWAIT_FINGER_ON to deactivation state transition after errors are reported. --- libfprint/fpi-image-device.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libfprint/fpi-image-device.c b/libfprint/fpi-image-device.c index 0af70ba1..cb0c40c8 100644 --- a/libfprint/fpi-image-device.c +++ b/libfprint/fpi-image-device.c @@ -419,7 +419,9 @@ fpi_image_device_retry_scan (FpImageDevice *self, FpDeviceRetry retry) /* We abort the operation and let the surrounding code retry in the * non-enroll case (this is identical to a session error). */ g_debug ("Abort current operation due to retry (non-enroll case)"); + priv->cancelling = TRUE; fpi_image_device_deactivate (self); + priv->cancelling = FALSE; fpi_device_action_error (FP_DEVICE (self), error); } } @@ -467,7 +469,9 @@ fpi_image_device_session_error (FpImageDevice *self, GError *error) if (error->domain == FP_DEVICE_RETRY) g_warning ("Driver should report retries using fpi_image_device_retry_scan!"); + priv->cancelling = TRUE; fpi_image_device_deactivate (self); + priv->cancelling = FALSE; fpi_device_action_error (FP_DEVICE (self), error); } From 54286c7603d61c32eb51fcb0654a406371b5060b Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 13 Jan 2020 17:24:19 +0100 Subject: [PATCH 194/237] image-device: Handle retry error propagation correctly With the early reporting mechanism the retry error propagation needs to be adjusted. Do the appropriate changes. --- libfprint/fpi-image-device.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/libfprint/fpi-image-device.c b/libfprint/fpi-image-device.c index cb0c40c8..6fc82802 100644 --- a/libfprint/fpi-image-device.c +++ b/libfprint/fpi-image-device.c @@ -414,6 +414,22 @@ fpi_image_device_retry_scan (FpImageDevice *self, FpDeviceRetry retry) priv->enroll_await_on_pending = TRUE; fp_image_device_change_state (self, FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF); } + else if (action == FPI_DEVICE_ACTION_VERIFY) + { + fpi_device_verify_report (FP_DEVICE (self), FPI_MATCH_ERROR, NULL, error); + priv->cancelling = TRUE; + fpi_image_device_deactivate (self); + priv->cancelling = FALSE; + fpi_device_verify_complete (FP_DEVICE (self), NULL); + } + else if (action == FPI_DEVICE_ACTION_IDENTIFY) + { + fpi_device_identify_report (FP_DEVICE (self), NULL, NULL, error); + priv->cancelling = TRUE; + fpi_image_device_deactivate (self); + priv->cancelling = FALSE; + fpi_device_identify_complete (FP_DEVICE (self), NULL); + } else { /* We abort the operation and let the surrounding code retry in the From 29a13a9b4a5812685e46f6961a833d7e925648ef Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 13 Jan 2020 17:57:31 +0100 Subject: [PATCH 195/237] tests: Add error reporting tests based on virtual driver We were not testing the image device error reporting functions yet inside libfprint (fprintd already had such tests). Add tests to make sure we catch errors earlier. --- tests/virtual-image.py | 61 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 11 deletions(-) diff --git a/tests/virtual-image.py b/tests/virtual-image.py index 1fcb30a2..c30fad5e 100755 --- a/tests/virtual-image.py +++ b/tests/virtual-image.py @@ -99,13 +99,13 @@ class VirtualImage(unittest.TestCase): def send_retry(self, retry_error=1, iterate=True): # The default (1) is too-short - self.sendall(struct.pack('ii', -1, retry_error)) + self.con.sendall(struct.pack('ii', -1, retry_error)) while iterate and ctx.pending(): ctx.iteration(False) def send_error(self, device_error=0, iterate=True): # The default (0) is a generic error - self.sendall(struct.pack('ii', -1, retry_error)) + self.con.sendall(struct.pack('ii', -2, device_error)) while iterate and ctx.pending(): ctx.iteration(False) @@ -212,9 +212,10 @@ class VirtualImage(unittest.TestCase): done = False def verify_cb(dev, res): - match, fp = dev.verify_finish(res) - self._verify_match = match - self._verify_fp = fp + try: + self._verify_match, self._verify_fp = dev.verify_finish(res) + except gi.repository.GLib.Error as e: + self._verify_error = e fp_whorl = self.enroll_print('whorl') @@ -234,20 +235,39 @@ class VirtualImage(unittest.TestCase): ctx.iteration(True) assert(not self._verify_match) + # Test verify error cases + self._verify_fp = None + self._verify_error = None + self.dev.verify(fp_whorl, callback=verify_cb) + self.send_retry() + while self._verify_fp is None and self._verify_error is None: + ctx.iteration(True) + assert(self._verify_error is not None) + assert(self._verify_error.matches(FPrint.device_retry_quark(), FPrint.DeviceRetry.TOO_SHORT)) + + self._verify_fp = None + self._verify_error = None + self.dev.verify(fp_whorl, callback=verify_cb) + self.send_error() + while self._verify_fp is None and self._verify_error is None: + ctx.iteration(True) + assert(self._verify_error is not None) + print(self._verify_error) + assert(self._verify_error.matches(FPrint.device_error_quark(), FPrint.DeviceError.GENERAL)) + def test_identify(self): done = False - def verify_cb(dev, res): - r, fp = dev.verify_finish(res) - self._verify_match = r - self._verify_fp = fp - fp_whorl = self.enroll_print('whorl') fp_tented_arch = self.enroll_print('tented_arch') def identify_cb(dev, res): print('Identify finished') - self._identify_match, self._identify_fp = self.dev.identify_finish(res) + try: + self._identify_match, self._identify_fp = self.dev.identify_finish(res) + except gi.repository.GLib.Error as e: + print(e) + self._identify_error = e self._identify_fp = None self.dev.identify([fp_whorl, fp_tented_arch], callback=identify_cb) @@ -263,6 +283,25 @@ class VirtualImage(unittest.TestCase): ctx.iteration(True) assert(self._identify_match is fp_whorl) + # Test error cases + self._identify_fp = None + self._identify_error = None + self.dev.identify([fp_whorl, fp_tented_arch], callback=identify_cb) + self.send_retry() + while self._identify_fp is None and self._identify_error is None: + ctx.iteration(True) + assert(self._identify_error is not None) + assert(self._identify_error.matches(FPrint.device_retry_quark(), FPrint.DeviceRetry.TOO_SHORT)) + + self._identify_fp = None + self._identify_error = None + self.dev.identify([fp_whorl, fp_tented_arch], callback=identify_cb) + self.send_error() + while self._identify_fp is None and self._identify_error is None: + ctx.iteration(True) + assert(self._identify_error is not None) + assert(self._identify_error.matches(FPrint.device_error_quark(), FPrint.DeviceError.GENERAL)) + def test_verify_serialized(self): done = False From 05bc2e1c807a1611b8727f7b0e87bcedc9985947 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Wed, 15 Jan 2020 14:48:06 +0100 Subject: [PATCH 196/237] image-device: Avoid invalid state transition on cancellation Fixes: #226 --- libfprint/drivers/elan.c | 2 -- libfprint/fpi-image-device.c | 18 +++++++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/libfprint/drivers/elan.c b/libfprint/drivers/elan.c index 1c2a7a37..084e4bd1 100644 --- a/libfprint/drivers/elan.c +++ b/libfprint/drivers/elan.c @@ -585,8 +585,6 @@ capture_complete (FpiSsm *ssm, FpDevice *_dev, GError *error) G_DEBUG_HERE (); - /* XXX: cancellation was specially handled by doing nothing! */ - /* either max frames captured or timed out waiting for the next frame */ if (!error || (g_error_matches (error, G_USB_DEVICE_ERROR, G_USB_DEVICE_ERROR_TIMED_OUT) && diff --git a/libfprint/fpi-image-device.c b/libfprint/fpi-image-device.c index 6fc82802..55b64886 100644 --- a/libfprint/fpi-image-device.c +++ b/libfprint/fpi-image-device.c @@ -448,7 +448,9 @@ fpi_image_device_retry_scan (FpImageDevice *self, FpDeviceRetry retry) * @error: The #GError to report * * Report an error while interacting with the device. This effectively - * aborts the current ongoing action. + * aborts the current ongoing action. Note that doing so will result in + * the deactivation handler to be called and this function must not be + * used to report an error during deactivation. */ void fpi_image_device_session_error (FpImageDevice *self, GError *error) @@ -475,10 +477,20 @@ fpi_image_device_session_error (FpImageDevice *self, GError *error) return; } } + else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) && + fpi_device_action_is_cancelled (FP_DEVICE (self))) + { + /* Ignore cancellation errors here, as we will explicitly deactivate + * anyway (or, may already have done so at this point). + */ + g_debug ("Driver reported a cancellation error, this is expected but not required. Ignoring."); + g_clear_error (&error); + return; + } else if (priv->state == FPI_IMAGE_DEVICE_STATE_INACTIVE) { - g_warning ("Driver reported session error; translating to deactivation failure."); - fpi_image_device_deactivate_complete (self, error); + g_warning ("Driver reported session error while deactivating already, ignoring. This indicates a driver bug."); + g_clear_error (&error); return; } From bc8a5859e319ae7a36268f2324fa6260b9f92d66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 16 Jan 2020 14:19:08 +0100 Subject: [PATCH 197/237] verify: Always close an open device before quitting the loop --- examples/verify.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/verify.c b/examples/verify.c index ffbf1132..1acf4044 100644 --- a/examples/verify.c +++ b/examples/verify.c @@ -143,7 +143,8 @@ on_list_completed (FpDevice *dev, GAsyncResult *res, gpointer user_data) { g_warning ("Did you remember to enroll your %s finger first?", finger_to_string (verify_data->finger)); - g_main_loop_quit (verify_data->loop); + fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed, + verify_data); return; } @@ -176,7 +177,8 @@ start_verification (FpDevice *dev, VerifyData *verify_data) { g_warning ("Unknown finger selected"); verify_data->ret_value = EXIT_FAILURE; - g_main_loop_quit (verify_data->loop); + fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed, + verify_data); return; } @@ -200,7 +202,8 @@ start_verification (FpDevice *dev, VerifyData *verify_data) g_warning ("Failed to load fingerprint data"); g_warning ("Did you remember to enroll your %s finger first?", finger_to_string (verify_data->finger)); - g_main_loop_quit (verify_data->loop); + fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed, + verify_data); return; } From 078cea17092c5ea216d0d8784026398f68637534 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 16 Jan 2020 14:36:41 +0100 Subject: [PATCH 198/237] verify: Add a verify_quit convenience function It ends the loop if the device is not open, otherwise closes it and ends the loop once done. --- examples/verify.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/examples/verify.c b/examples/verify.c index 1acf4044..ae5342cf 100644 --- a/examples/verify.c +++ b/examples/verify.c @@ -55,6 +55,19 @@ on_device_closed (FpDevice *dev, GAsyncResult *res, void *user_data) g_main_loop_quit (verify_data->loop); } +static void +verify_quit (FpDevice *dev, + VerifyData *verify_data) +{ + if (!fp_device_is_open (dev)) + { + g_main_loop_quit (verify_data->loop); + return; + } + + fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed, verify_data); +} + static void start_verification (FpDevice *dev, VerifyData *verify_data); @@ -71,7 +84,7 @@ on_verify_completed (FpDevice *dev, GAsyncResult *res, void *user_data) if (!fp_device_verify_finish (dev, res, &match, &print, &error)) { g_warning ("Failed to verify print: %s", error->message); - g_main_loop_quit (verify_data->loop); + verify_quit (dev, verify_data); return; } @@ -98,8 +111,7 @@ on_verify_completed (FpDevice *dev, GAsyncResult *res, void *user_data) return; } - fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed, - verify_data); + verify_quit (dev, verify_data); } static void @@ -143,8 +155,7 @@ on_list_completed (FpDevice *dev, GAsyncResult *res, gpointer user_data) { g_warning ("Did you remember to enroll your %s finger first?", finger_to_string (verify_data->finger)); - fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed, - verify_data); + verify_quit (dev, verify_data); return; } @@ -160,7 +171,7 @@ on_list_completed (FpDevice *dev, GAsyncResult *res, gpointer user_data) else { g_warning ("Loading prints failed with error %s", error->message); - g_main_loop_quit (verify_data->loop); + verify_quit (dev, verify_data); } } @@ -177,8 +188,7 @@ start_verification (FpDevice *dev, VerifyData *verify_data) { g_warning ("Unknown finger selected"); verify_data->ret_value = EXIT_FAILURE; - fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed, - verify_data); + verify_quit (dev, verify_data); return; } @@ -202,8 +212,7 @@ start_verification (FpDevice *dev, VerifyData *verify_data) g_warning ("Failed to load fingerprint data"); g_warning ("Did you remember to enroll your %s finger first?", finger_to_string (verify_data->finger)); - fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed, - verify_data); + verify_quit (dev, verify_data); return; } @@ -225,7 +234,7 @@ on_device_opened (FpDevice *dev, GAsyncResult *res, void *user_data) if (!fp_device_open_finish (dev, res, &error)) { g_warning ("Failed to open device: %s", error->message); - g_main_loop_quit (verify_data->loop); + verify_quit (dev, verify_data); return; } From 30c783cbebfd701152cc6c2242765df8c72e5ce4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 16 Jan 2020 14:37:07 +0100 Subject: [PATCH 199/237] examples: add FP_COMPONENT definitions --- examples/enroll.c | 2 ++ examples/manage-prints.c | 2 ++ examples/storage.c | 2 ++ examples/utilities.c | 2 ++ examples/verify.c | 2 ++ 5 files changed, 10 insertions(+) diff --git a/examples/enroll.c b/examples/enroll.c index 159ffbc0..c6572860 100644 --- a/examples/enroll.c +++ b/examples/enroll.c @@ -19,6 +19,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#define FP_COMPONENT "example-enroll" + #include #include diff --git a/examples/manage-prints.c b/examples/manage-prints.c index d64b5fa6..36e4046d 100644 --- a/examples/manage-prints.c +++ b/examples/manage-prints.c @@ -18,6 +18,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#define FP_COMPONENT "example-mange-prints" + #include #include diff --git a/examples/storage.c b/examples/storage.c index 0ab49468..53a61be0 100644 --- a/examples/storage.c +++ b/examples/storage.c @@ -19,6 +19,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#define FP_COMPONENT "example-storage" + #include #include "storage.h" diff --git a/examples/utilities.c b/examples/utilities.c index eb18600d..d68d8c18 100644 --- a/examples/utilities.c +++ b/examples/utilities.c @@ -18,6 +18,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#define FP_COMPONENT "example-utilities" + #include #include diff --git a/examples/verify.c b/examples/verify.c index ae5342cf..21a537f9 100644 --- a/examples/verify.c +++ b/examples/verify.c @@ -19,6 +19,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#define FP_COMPONENT "example-verify" + #include #include From 0889ec20a815f3cd846c18fe7e6a4f93a252172c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 16 Jan 2020 16:43:56 +0100 Subject: [PATCH 200/237] fp-device: Remove confusing success parameter on FpMatchCb This was added as alias to the error check, but given we're passing to the callback both the error and the match itself, we can just avoid adding an extra parameter that could be confusing (as may imply that the matching happened). Also clarify the documentation and ensure that the match value is properly set in tests. --- libfprint/fp-device.h | 33 +++++++++++++++++++++++---------- libfprint/fpi-device.c | 4 ++-- tests/test-fpi-device.c | 32 +++++++++++++++----------------- 3 files changed, 40 insertions(+), 29 deletions(-) diff --git a/libfprint/fp-device.h b/libfprint/fp-device.h index 69ac5c79..b0b690cc 100644 --- a/libfprint/fp-device.h +++ b/libfprint/fp-device.h @@ -128,23 +128,36 @@ typedef void (*FpEnrollProgress) (FpDevice *device, /** * FpMatchCb: * @device: a #FpDevice - * @success: Whether a print was retrieved, %FALSE means @error is set - * @match: (nullable) (transfer none): The matching print + * @match: (nullable) (transfer none): The matching print if any matched @print * @print: (nullable) (transfer none): The newly scanned print * @user_data: (nullable) (transfer none): User provided data * @error: (nullable) (transfer none): #GError or %NULL * - * Report the result of a match (identify or verify) operation. This callback - * because it makes sense for drivers to wait e.g. on finger removal before - * finishing the operation. However, the success/failure can often be reported - * at an earlier time, and there is no need to make the user wait. + * Report the result of a match (identify or verify) operation. * - * The passed error is guaranteed to be of type %FP_DEVICE_RETRY if set. Actual - * error conditions will not be reported using this function. Such an error may - * still happen even if this callback has been called. + * If @match is non-%NULL, then it is set to the matching #FpPrint as passed + * to the match operation. In this case @error will always be %NULL. + * + * If @error is not %NULL then its domain is guaranteed to be + * %FP_DEVICE_RETRY. All other error conditions will not be reported using + * this callback. If such an error occurs before a match/no-match decision + * can be made, then this callback will not be called. Should an error + * happen afterwards, then you will get a match report through this callback + * and an error when the operation finishes. + * + * If @match and @error are %NULL, then a finger was presented but it did not + * match any known print. + * + * @print represents the newly scanned print. The driver may or may not + * provide this information. Image based devices will provide it and it + * allows access to the raw data. + * + * This callback exists because it makes sense for drivers to wait e.g. on + * finger removal before completing the match operation. However, the + * success/failure can often be reported at an earlier time, and there is + * no need to make the user wait. */ typedef void (*FpMatchCb) (FpDevice *device, - gboolean success, FpPrint *match, FpPrint *print, gpointer user_data, diff --git a/libfprint/fpi-device.c b/libfprint/fpi-device.c index 629fd2ef..e6db5921 100644 --- a/libfprint/fpi-device.c +++ b/libfprint/fpi-device.c @@ -1281,7 +1281,7 @@ fpi_device_verify_report (FpDevice *device, } if (call_cb && data->match_cb) - data->match_cb (device, data->error == NULL, data->match, data->print, data->match_data, data->error); + data->match_cb (device, data->match, data->print, data->match_data, data->error); } /** @@ -1357,5 +1357,5 @@ fpi_device_identify_report (FpDevice *device, } if (call_cb && data->match_cb) - data->match_cb (device, data->error == NULL, data->match, data->print, data->match_data, data->error); + data->match_cb (device, data->match, data->print, data->match_data, data->error); } diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c index 537bad95..580effe0 100644 --- a/tests/test-fpi-device.c +++ b/tests/test-fpi-device.c @@ -546,7 +546,6 @@ test_driver_enroll_progress (void) typedef struct { gboolean called; - gboolean success; FpPrint *match; FpPrint *print; GError *error; @@ -556,7 +555,6 @@ static void test_driver_match_data_clear (MatchCbData *data) { data->called = FALSE; - data->success = FALSE; g_clear_object (&data->match); g_clear_object (&data->print); g_clear_error (&data->error); @@ -564,7 +562,6 @@ test_driver_match_data_clear (MatchCbData *data) static void test_driver_match_cb (FpDevice *device, - gboolean success, FpPrint *match, FpPrint *print, gpointer user_data, @@ -574,24 +571,18 @@ test_driver_match_cb (FpDevice *device, g_assert (data->called == FALSE); data->called = TRUE; - data->success = TRUE; if (match) data->match = g_object_ref (match); if (print) data->print = g_object_ref (print); if (error) - data->error = g_error_copy (error); - - if (success) { - g_assert_null (error); - } - else - { - g_assert_nonnull (error); + data->error = g_error_copy (error); g_assert_null (match); - g_assert_null (print); } + + if (match) + g_assert_no_error (error); } static void @@ -616,7 +607,7 @@ test_driver_verify (void) g_assert_no_error (error); g_assert_true (match_data.called); - g_assert_true (match_data.success); + g_assert_nonnull (match_data.match); g_assert_true (match_data.print == out_print); g_assert_true (match_data.match == enrolled_print); @@ -647,7 +638,7 @@ test_driver_verify_fail (void) g_assert_no_error (error); g_assert_true (match_data.called); - g_assert_true (match_data.success); + g_assert_no_error (match_data.error); g_assert_true (match_data.print == out_print); g_assert_null (match_data.match); @@ -676,6 +667,7 @@ test_driver_verify_retry (void) &match, &out_print, &error); g_assert_true (match_data.called); + g_assert_null (match_data.match); g_assert_error (match_data.error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_GENERAL); g_assert (fake_dev->last_called_function == dev_class->verify); @@ -705,6 +697,8 @@ test_driver_verify_error (void) &match, &out_print, &error); g_assert_false (match_data.called); + g_assert_null (match_data.match); + g_assert_no_error (match_data.error); g_assert (fake_dev->last_called_function == dev_class->verify); g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); @@ -803,7 +797,7 @@ test_driver_identify (void) &matched_print, &print, &error); g_assert_true (match_data.called); - g_assert_true (match_data.success); + g_assert_nonnull (match_data.match); g_assert_true (match_data.match == matched_print); g_assert_true (match_data.print == print); @@ -841,7 +835,8 @@ test_driver_identify_fail (void) &matched_print, &print, &error); g_assert_true (match_data.called); - g_assert_true (match_data.success); + g_assert_null (match_data.match); + g_assert_no_error (match_data.error); g_assert_true (match_data.match == matched_print); g_assert_true (match_data.print == print); @@ -882,6 +877,7 @@ test_driver_identify_retry (void) &matched_print, &print, &error); g_assert_true (match_data.called); + g_assert_null (match_data.match); g_assert_error (match_data.error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_GENERAL); g_assert (fake_dev->last_called_function == dev_class->identify); @@ -921,6 +917,8 @@ test_driver_identify_error (void) &matched_print, &print, &error); g_assert_false (match_data.called); + g_assert_null (match_data.match); + g_assert_no_error (match_data.error); g_assert (fake_dev->last_called_function == dev_class->identify); g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); From 456522397a3a87a7f408220a5e38d1cdecc28377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 16 Jan 2020 17:00:37 +0100 Subject: [PATCH 201/237] verify: Add match callback report --- examples/verify.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/examples/verify.c b/examples/verify.c index 21a537f9..545539aa 100644 --- a/examples/verify.c +++ b/examples/verify.c @@ -116,6 +116,36 @@ on_verify_completed (FpDevice *dev, GAsyncResult *res, void *user_data) verify_quit (dev, verify_data); } +static void +on_match_cb (FpDevice *dev, FpPrint *match, FpPrint *print, + gpointer user_data, GError *error) +{ + if (error) + { + g_warning ("Match report: Finger not matched, retry error reported: %s", + error->message); + return; + } + + if (match) + { + char date_str[128]; + + g_date_strftime (date_str, G_N_ELEMENTS (date_str), "%Y-%m-%d\0", + fp_print_get_enroll_date (match)); + g_debug ("Match report: device %s matched finger %s successifully " + "with print %s, enrolled on date %s by user %s", + fp_device_get_name (dev), + finger_to_string (fp_print_get_finger (match)), + fp_print_get_description (match), date_str, + fp_print_get_username (match)); + } + else + { + g_debug ("Match report: Finger not matched"); + } +} + static void on_list_completed (FpDevice *dev, GAsyncResult *res, gpointer user_data) { @@ -166,7 +196,7 @@ on_list_completed (FpDevice *dev, GAsyncResult *res, gpointer user_data) g_print ("Print loaded. Time to verify!\n"); fp_device_verify (dev, verify_print, NULL, - NULL, NULL, NULL, + on_match_cb, verify_data, NULL, (GAsyncReadyCallback) on_verify_completed, verify_data); } From 9f3272f296ddb3d7f0ed533278047a745db4338b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 17 Jan 2020 17:51:30 +0100 Subject: [PATCH 202/237] test-fpi-device: Use smart pointer to handle CB data --- tests/test-fpi-device.c | 114 +++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 61 deletions(-) diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c index 580effe0..54e56414 100644 --- a/tests/test-fpi-device.c +++ b/tests/test-fpi-device.c @@ -560,6 +560,14 @@ test_driver_match_data_clear (MatchCbData *data) g_clear_error (&data->error); } +static void +test_driver_match_data_free (MatchCbData *data) +{ + test_driver_match_data_clear (data); + g_free (data); +} +G_DEFINE_AUTOPTR_CLEANUP_FUNC (MatchCbData, test_driver_match_data_free); + static void test_driver_match_cb (FpDevice *device, FpPrint *match, @@ -592,29 +600,27 @@ test_driver_verify (void) g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); g_autoptr(FpPrint) enrolled_print = g_object_ref_sink (fp_print_new (device)); g_autoptr(FpPrint) out_print = NULL; + g_autoptr(MatchCbData) match_data = g_new0 (MatchCbData, 1); FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - MatchCbData match_data = { 0, }; gboolean match; fake_dev->ret_result = FPI_MATCH_SUCCESS; fp_device_verify_sync (device, enrolled_print, NULL, - test_driver_match_cb, &match_data, + test_driver_match_cb, match_data, &match, &out_print, &error); g_assert (fake_dev->last_called_function == dev_class->verify); g_assert (fake_dev->action_data == enrolled_print); g_assert_no_error (error); - g_assert_true (match_data.called); - g_assert_nonnull (match_data.match); - g_assert_true (match_data.print == out_print); - g_assert_true (match_data.match == enrolled_print); + g_assert_true (match_data->called); + g_assert_nonnull (match_data->match); + g_assert_true (match_data->print == out_print); + g_assert_true (match_data->match == enrolled_print); g_assert (out_print == enrolled_print); g_assert_true (match); - - test_driver_match_data_clear (&match_data); } static void @@ -624,28 +630,26 @@ test_driver_verify_fail (void) g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); g_autoptr(FpPrint) enrolled_print = g_object_ref_sink (fp_print_new (device)); g_autoptr(FpPrint) out_print = NULL; + g_autoptr(MatchCbData) match_data = g_new0 (MatchCbData, 1); FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - MatchCbData match_data = { 0, }; gboolean match; fake_dev->ret_result = FPI_MATCH_FAIL; fp_device_verify_sync (device, enrolled_print, NULL, - test_driver_match_cb, &match_data, + test_driver_match_cb, match_data, &match, &out_print, &error); g_assert (fake_dev->last_called_function == dev_class->verify); g_assert_no_error (error); - g_assert_true (match_data.called); - g_assert_no_error (match_data.error); - g_assert_true (match_data.print == out_print); - g_assert_null (match_data.match); + g_assert_true (match_data->called); + g_assert_no_error (match_data->error); + g_assert_true (match_data->print == out_print); + g_assert_null (match_data->match); g_assert (out_print == enrolled_print); g_assert_false (match); - - test_driver_match_data_clear (&match_data); } static void @@ -655,27 +659,25 @@ test_driver_verify_retry (void) g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); g_autoptr(FpPrint) enrolled_print = g_object_ref_sink (fp_print_new (device)); g_autoptr(FpPrint) out_print = NULL; + g_autoptr(MatchCbData) match_data = g_new0 (MatchCbData, 1); FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - MatchCbData match_data = { 0, }; gboolean match; fake_dev->ret_result = FPI_MATCH_ERROR; fake_dev->ret_error = fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL); fp_device_verify_sync (device, enrolled_print, NULL, - test_driver_match_cb, &match_data, + test_driver_match_cb, match_data, &match, &out_print, &error); - g_assert_true (match_data.called); - g_assert_null (match_data.match); - g_assert_error (match_data.error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_GENERAL); + g_assert_true (match_data->called); + g_assert_null (match_data->match); + g_assert_error (match_data->error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_GENERAL); g_assert (fake_dev->last_called_function == dev_class->verify); g_assert_error (error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_GENERAL); g_assert (error == g_steal_pointer (&fake_dev->ret_error)); g_assert_false (match); - - test_driver_match_data_clear (&match_data); } static void @@ -685,27 +687,25 @@ test_driver_verify_error (void) g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); g_autoptr(FpPrint) enrolled_print = g_object_ref_sink (fp_print_new (device)); g_autoptr(FpPrint) out_print = NULL; + g_autoptr(MatchCbData) match_data = g_new0 (MatchCbData, 1); FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - MatchCbData match_data = { 0, }; gboolean match; fake_dev->ret_result = FPI_MATCH_ERROR; fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_GENERAL); fp_device_verify_sync (device, enrolled_print, NULL, - test_driver_match_cb, &match_data, + test_driver_match_cb, match_data, &match, &out_print, &error); - g_assert_false (match_data.called); - g_assert_null (match_data.match); - g_assert_no_error (match_data.error); + g_assert_false (match_data->called); + g_assert_null (match_data->match); + g_assert_no_error (match_data->error); g_assert (fake_dev->last_called_function == dev_class->verify); g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); g_assert (error == g_steal_pointer (&fake_dev->ret_error)); g_assert_false (match); - - test_driver_match_data_clear (&match_data); } static void @@ -777,10 +777,10 @@ test_driver_identify (void) g_autoptr(FpPrint) matched_print = NULL; g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); g_autoptr(GPtrArray) prints = g_ptr_array_new_with_free_func (g_object_unref); + g_autoptr(MatchCbData) match_data = g_new0 (MatchCbData, 1); FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); FpPrint *expected_matched; - MatchCbData match_data = { 0, }; unsigned int i; for (i = 0; i < 500; ++i) @@ -793,13 +793,13 @@ test_driver_identify (void) fake_dev->ret_print = fp_print_new (device); fp_device_identify_sync (device, prints, NULL, - test_driver_match_cb, &match_data, + test_driver_match_cb, match_data, &matched_print, &print, &error); - g_assert_true (match_data.called); - g_assert_nonnull (match_data.match); - g_assert_true (match_data.match == matched_print); - g_assert_true (match_data.print == print); + g_assert_true (match_data->called); + g_assert_nonnull (match_data->match); + g_assert_true (match_data->match == matched_print); + g_assert_true (match_data->print == print); g_assert (fake_dev->last_called_function == dev_class->identify); g_assert (fake_dev->action_data == prints); @@ -807,8 +807,6 @@ test_driver_identify (void) g_assert (print != NULL && print == fake_dev->ret_print); g_assert (expected_matched == matched_print); - - test_driver_match_data_clear (&match_data); } static void @@ -819,9 +817,9 @@ test_driver_identify_fail (void) g_autoptr(FpPrint) matched_print = NULL; g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); g_autoptr(GPtrArray) prints = g_ptr_array_new_with_free_func (g_object_unref); + g_autoptr(MatchCbData) match_data = g_new0 (MatchCbData, 1); FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - MatchCbData match_data = { 0, }; unsigned int i; for (i = 0; i < 500; ++i) @@ -831,22 +829,20 @@ test_driver_identify_fail (void) fake_dev->ret_print = fp_print_new (device); fp_device_identify_sync (device, prints, NULL, - test_driver_match_cb, &match_data, + test_driver_match_cb, match_data, &matched_print, &print, &error); - g_assert_true (match_data.called); - g_assert_null (match_data.match); - g_assert_no_error (match_data.error); - g_assert_true (match_data.match == matched_print); - g_assert_true (match_data.print == print); + g_assert_true (match_data->called); + g_assert_null (match_data->match); + g_assert_no_error (match_data->error); + g_assert_true (match_data->match == matched_print); + g_assert_true (match_data->print == print); g_assert (fake_dev->last_called_function == dev_class->identify); g_assert_no_error (error); g_assert (print != NULL && print == fake_dev->ret_print); g_assert_null (matched_print); - - test_driver_match_data_clear (&match_data); } static void @@ -857,10 +853,10 @@ test_driver_identify_retry (void) g_autoptr(FpPrint) matched_print = NULL; g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); g_autoptr(GPtrArray) prints = g_ptr_array_new_with_free_func (g_object_unref); + g_autoptr(MatchCbData) match_data = g_new0 (MatchCbData, 1); FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); FpPrint *expected_matched; - MatchCbData match_data = { 0, }; unsigned int i; for (i = 0; i < 500; ++i) @@ -873,20 +869,18 @@ test_driver_identify_retry (void) fake_dev->ret_error = fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL); fp_device_identify_sync (device, prints, NULL, - test_driver_match_cb, &match_data, + test_driver_match_cb, match_data, &matched_print, &print, &error); - g_assert_true (match_data.called); - g_assert_null (match_data.match); - g_assert_error (match_data.error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_GENERAL); + g_assert_true (match_data->called); + g_assert_null (match_data->match); + g_assert_error (match_data->error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_GENERAL); g_assert (fake_dev->last_called_function == dev_class->identify); g_assert_error (error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_GENERAL); g_assert (error == g_steal_pointer (&fake_dev->ret_error)); g_assert_null (matched_print); g_assert_null (print); - - test_driver_match_data_clear (&match_data); } static void @@ -897,10 +891,10 @@ test_driver_identify_error (void) g_autoptr(FpPrint) matched_print = NULL; g_autoptr(FpAutoCloseDevice) device = auto_close_fake_device_new (); g_autoptr(GPtrArray) prints = g_ptr_array_new_with_free_func (g_object_unref); + g_autoptr(MatchCbData) match_data = g_new0 (MatchCbData, 1); FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); FpPrint *expected_matched; - MatchCbData match_data = { 0, }; unsigned int i; for (i = 0; i < 500; ++i) @@ -913,20 +907,18 @@ test_driver_identify_error (void) fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_GENERAL); fp_device_identify_sync (device, prints, NULL, - test_driver_match_cb, &match_data, + test_driver_match_cb, match_data, &matched_print, &print, &error); - g_assert_false (match_data.called); - g_assert_null (match_data.match); - g_assert_no_error (match_data.error); + g_assert_false (match_data->called); + g_assert_null (match_data->match); + g_assert_no_error (match_data->error); g_assert (fake_dev->last_called_function == dev_class->identify); g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); g_assert (error == g_steal_pointer (&fake_dev->ret_error)); g_assert_null (matched_print); g_assert_null (print); - - test_driver_match_data_clear (&match_data); } static void From b3a4c2cf9aa8d1103285dec205b2d8b902a79dbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 17 Jan 2020 13:15:47 +0100 Subject: [PATCH 203/237] fpi-device: Improve logging and testing on report/complete --- libfprint/fpi-device.c | 10 ++- tests/test-fpi-device.c | 149 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+), 2 deletions(-) diff --git a/libfprint/fpi-device.c b/libfprint/fpi-device.c index e6db5921..6e2f1c2a 100644 --- a/libfprint/fpi-device.c +++ b/libfprint/fpi-device.c @@ -980,7 +980,10 @@ fpi_device_verify_complete (FpDevice *device, /* Replace a retry error with a general error, this is a driver bug. */ if (error->domain == FP_DEVICE_RETRY) { - g_warning ("Driver reported a retry error to fpi_device_verify_complete; reporting operation failure instead!"); + g_warning ("Driver reported a retry error to fpi_device_verify_complete. " + "This is not permissible and needs to be reported using " + "fpi_device_verify_report, reporting general verification " + "failure instead."); g_clear_error (&error); error = fpi_device_error_new (FP_DEVICE_ERROR_GENERAL); } @@ -1035,7 +1038,10 @@ fpi_device_identify_complete (FpDevice *device, /* Replace a retry error with a general error, this is a driver bug. */ if (error->domain == FP_DEVICE_RETRY) { - g_warning ("Driver reported a retry error to fpi_device_identify_complete; reporting operation failure instead!"); + g_warning ("Driver reported a retry error to fpi_device_identify_complete. " + "This is not permissible and needs to be reported using " + "fpi_device_identify_report, reporting general identification " + "failure instead."); g_clear_error (&error); error = fpi_device_error_new (FP_DEVICE_ERROR_GENERAL); } diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c index 54e56414..cd1cda67 100644 --- a/tests/test-fpi-device.c +++ b/tests/test-fpi-device.c @@ -740,6 +740,100 @@ test_driver_verify_not_reported (void) g_test_assert_expected_messages (); } +static void +fake_device_verify_complete_error (FpDevice *device) +{ + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + GError *complete_error = fake_dev->user_data; + + fake_dev->last_called_function = fake_device_verify_complete_error; + + fpi_device_verify_report (device, fake_dev->ret_result, fake_dev->ret_print, fake_dev->ret_error); + fpi_device_verify_complete (device, complete_error); +} + +static void +test_driver_verify_complete_retry (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(MatchCbData) match_data = g_new0 (MatchCbData, 1); + g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(FpPrint) enrolled_print = NULL; + g_autoptr(FpPrint) print = NULL; + g_autoptr(GError) error = NULL; + FpiDeviceFake *fake_dev; + gboolean match; + + dev_class->verify = fake_device_verify_complete_error; + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + fake_dev = FPI_DEVICE_FAKE (device); + enrolled_print = g_object_ref_sink (fp_print_new (device)); + + g_assert_true (fp_device_open_sync (device, NULL, NULL)); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Driver reported an error code without setting match result to error*"); + + test_driver_match_data_clear (match_data); + fake_dev->ret_result = FPI_MATCH_FAIL; + fake_dev->ret_error = fpi_device_retry_new (FP_DEVICE_RETRY_TOO_SHORT); + fp_device_verify_sync (device, enrolled_print, NULL, test_driver_match_cb, + match_data, &match, &print, &error); + g_test_assert_expected_messages (); + + g_assert_true (error == g_steal_pointer (&fake_dev->ret_error)); + g_assert_error (error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_TOO_SHORT); + g_assert_false (match); + g_assert_true (match_data->called); + g_assert_error (match_data->error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_TOO_SHORT); + g_assert_null (print); + g_clear_error (&error); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Driver reported an error code without setting match result to error*"); + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Driver reported a retry error to fpi_device_verify_complete" + "*reporting general verification failure*"); + + test_driver_match_data_clear (match_data); + fake_dev->ret_result = FPI_MATCH_FAIL; + fake_dev->ret_error = fpi_device_retry_new (FP_DEVICE_RETRY_TOO_SHORT); + fake_dev->user_data = g_error_copy (fake_dev->ret_error); + fp_device_verify_sync (device, enrolled_print, NULL, test_driver_match_cb, + match_data, &match, &print, &error); + + g_test_assert_expected_messages (); + g_assert_true (error != g_steal_pointer (&fake_dev->ret_error)); + g_assert_true (error != g_steal_pointer (&fake_dev->user_data)); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + g_assert_true (match_data->called); + g_assert_error (match_data->error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_TOO_SHORT); + g_assert_false (match); + g_assert_null (print); + g_clear_error (&error); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Driver reported a retry error to fpi_device_verify_complete" + "*reporting general verification failure*"); + + test_driver_match_data_clear (match_data); + fake_dev->ret_result = FPI_MATCH_ERROR; + fake_dev->ret_error = fpi_device_retry_new (FP_DEVICE_RETRY_TOO_SHORT); + fake_dev->user_data = g_error_copy (fake_dev->ret_error); + + fp_device_verify_sync (device, enrolled_print, NULL, test_driver_match_cb, + match_data, &match, &print, &error); + g_test_assert_expected_messages (); + + g_assert_true (error != g_steal_pointer (&fake_dev->ret_error)); + g_assert_true (error != g_steal_pointer (&fake_dev->user_data)); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + g_assert_true (match_data->called); + g_assert_error (match_data->error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_TOO_SHORT); + g_assert_false (match); + g_assert_null (print); +} + static void fake_device_stub_identify (FpDevice *device) { @@ -957,6 +1051,59 @@ test_driver_identify_not_reported (void) g_test_assert_expected_messages (); } +static void +fake_device_identify_complete_error (FpDevice *device) +{ + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + GError *complete_error = fake_dev->user_data; + + fpi_device_identify_report (device, fake_dev->ret_match, fake_dev->ret_print, fake_dev->ret_error); + fpi_device_identify_complete (device, complete_error); +} + +static void +test_driver_identify_complete_retry (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(GPtrArray) prints = g_ptr_array_new_with_free_func (g_object_unref); + g_autoptr(MatchCbData) match_data = g_new0 (MatchCbData, 1); + g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(FpPrint) print = NULL; + g_autoptr(FpPrint) match = NULL; + g_autoptr(GError) error = NULL; + FpiDeviceFake *fake_dev; + int i; + + dev_class->identify = fake_device_identify_complete_error; + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + fake_dev = FPI_DEVICE_FAKE (device); + + for (i = 0; i < 500; ++i) + g_ptr_array_add (prints, g_object_ref_sink (fp_print_new (device))); + + g_assert_true (fp_device_open_sync (device, NULL, NULL)); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Driver reported a retry error to fpi_device_identify_complete" + "*reporting general identification failure*"); + + test_driver_match_data_clear (match_data); + fake_dev->ret_error = fpi_device_retry_new (FP_DEVICE_RETRY_TOO_SHORT); + fake_dev->user_data = g_error_copy (fake_dev->ret_error); + + fp_device_identify_sync (device, prints, NULL, test_driver_match_cb, match_data, + &match, &print, &error); + g_test_assert_expected_messages (); + + g_assert_true (error != g_steal_pointer (&fake_dev->ret_error)); + g_assert_true (error != g_steal_pointer (&fake_dev->user_data)); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + g_assert_true (match_data->called); + g_assert_error (match_data->error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_TOO_SHORT); + g_assert_null (match); + g_assert_null (print); +} + static void fake_device_stub_capture (FpDevice *device) { @@ -1612,11 +1759,13 @@ main (int argc, char *argv[]) g_test_add_func ("/driver/verify/retry", test_driver_verify_retry); g_test_add_func ("/driver/verify/error", test_driver_verify_error); g_test_add_func ("/driver/verify/not_reported", test_driver_verify_not_reported); + g_test_add_func ("/driver/verify/complete_retry", test_driver_verify_complete_retry); g_test_add_func ("/driver/identify", test_driver_identify); g_test_add_func ("/driver/identify/fail", test_driver_identify_fail); g_test_add_func ("/driver/identify/retry", test_driver_identify_retry); g_test_add_func ("/driver/identify/error", test_driver_identify_error); g_test_add_func ("/driver/identify/not_reported", test_driver_identify_not_reported); + g_test_add_func ("/driver/identify/complete_retry", test_driver_identify_complete_retry); g_test_add_func ("/driver/capture", test_driver_capture); g_test_add_func ("/driver/capture/error", test_driver_capture_error); g_test_add_func ("/driver/list", test_driver_list); From 027ac8d843ca258f19363d4640080707ab5cd0f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 17 Jan 2020 16:51:48 +0100 Subject: [PATCH 204/237] fpi-device: Only mark a device as closed if the operation succeeded We may fail during the close phase, in such case the device should not be marked as closed. --- libfprint/fpi-device.c | 14 +++++++++----- tests/test-fpi-device.c | 1 + 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/libfprint/fpi-device.c b/libfprint/fpi-device.c index 6e2f1c2a..88d752a9 100644 --- a/libfprint/fpi-device.c +++ b/libfprint/fpi-device.c @@ -851,8 +851,6 @@ fpi_device_close_complete (FpDevice *device, GError *error) g_debug ("Device reported close completion"); clear_device_cancel_action (device); - priv->is_open = FALSE; - g_object_notify (G_OBJECT (device), "open"); switch (priv->type) { @@ -877,10 +875,16 @@ fpi_device_close_complete (FpDevice *device, GError *error) } if (!error) - fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL, - GUINT_TO_POINTER (TRUE)); + { + priv->is_open = FALSE; + g_object_notify (G_OBJECT (device), "open"); + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL, + GUINT_TO_POINTER (TRUE)); + } else - fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); + { + fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error); + } } /** diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c index cd1cda67..f234e508 100644 --- a/tests/test-fpi-device.c +++ b/tests/test-fpi-device.c @@ -386,6 +386,7 @@ test_driver_close_error (void) g_assert (fake_dev->last_called_function == dev_class->close); g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); g_assert (error == g_steal_pointer (&fake_dev->ret_error)); + g_assert_true (fp_device_is_open (device)); } static void From b09df0e40ade1a4025f972bbd6a08baca60a8550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 17 Jan 2020 15:55:39 +0100 Subject: [PATCH 205/237] test-fpi-device: Add tests for error message creation --- tests/test-fpi-device.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c index f234e508..8df45a5b 100644 --- a/tests/test-fpi-device.c +++ b/tests/test-fpi-device.c @@ -1691,9 +1691,18 @@ test_driver_error_types (void) for (i = 0; g_enum_get_value (errors_enum, i); ++i) { - error = fpi_device_error_new (i); - g_assert_error (error, FP_DEVICE_ERROR, i); - g_clear_error (&error); + g_autoptr(GError) e = NULL; + g_autoptr(GError) msg_e = NULL; + g_autofree char *expected_msg = NULL; + g_autofree char *enum_string = g_enum_to_string (FP_TYPE_DEVICE_ERROR, i); + + e = fpi_device_error_new (i); + g_assert_error (e, FP_DEVICE_ERROR, i); + + expected_msg = g_strdup_printf ("Error message %s", enum_string); + msg_e = fpi_device_error_new_msg (i, "Error message %s", enum_string); + g_assert_error (msg_e, FP_DEVICE_ERROR, i); + g_assert_cmpstr (msg_e->message, ==, expected_msg); } g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "*Unsupported error*"); @@ -1711,9 +1720,18 @@ test_driver_retry_error_types (void) for (i = 0; g_enum_get_value (errors_enum, i); ++i) { - error = fpi_device_retry_new (i); - g_assert_error (error, FP_DEVICE_RETRY, i); - g_clear_error (&error); + g_autoptr(GError) e = NULL; + g_autoptr(GError) msg_e = NULL; + g_autofree char *expected_msg = NULL; + g_autofree char *enum_string = g_enum_to_string (FP_TYPE_DEVICE_RETRY, i); + + e = fpi_device_retry_new (i); + g_assert_error (e, FP_DEVICE_RETRY, i); + + expected_msg = g_strdup_printf ("Retry error message %s", enum_string); + msg_e = fpi_device_retry_new_msg (i, "Retry error message %s", enum_string); + g_assert_error (msg_e, FP_DEVICE_RETRY, i); + g_assert_cmpstr (msg_e->message, ==, expected_msg); } g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "*Unsupported error*"); From 3c5b7f8ea6aba9c159fa0041e49c9aa328c46ce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 17 Jan 2020 15:57:37 +0100 Subject: [PATCH 206/237] test-fpi-device: Add more probe tests with errors --- tests/test-fpi-device.c | 65 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c index 8df45a5b..ad873888 100644 --- a/tests/test-fpi-device.c +++ b/tests/test-fpi-device.c @@ -322,6 +322,69 @@ test_driver_probe (void) g_assert_cmpstr (fp_device_get_name (device), ==, "Probed device name"); } +static void +fake_device_probe_error (FpDevice *device) +{ + FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); + + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_PROBE); + + fpi_device_probe_complete (device, dev_class->id, dev_class->full_name, + fpi_device_error_new (FP_DEVICE_ERROR_NOT_SUPPORTED)); +} + +static void +fake_device_probe_action_error (FpDevice *device) +{ + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_PROBE); + + fpi_device_action_error (device, fpi_device_error_new (FP_DEVICE_ERROR_NOT_SUPPORTED)); +} + +static void +on_driver_probe_error_async (GObject *initable, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GError) error = NULL; + gboolean *out_done = user_data; + FpDevice *device; + + device = FP_DEVICE (g_async_initable_new_finish (G_ASYNC_INITABLE (initable), res, &error)); + g_assert_null (device); + + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_NOT_SUPPORTED); + *out_done = TRUE; +} + +static void +test_driver_probe_error (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + gboolean done = FALSE; + + dev_class->id = "Error device ID"; + dev_class->probe = fake_device_probe_error; + g_async_initable_new_async (FPI_TYPE_DEVICE_FAKE, G_PRIORITY_DEFAULT, NULL, + on_driver_probe_error_async, &done, NULL); + + while (!done) + g_main_context_iteration (NULL, TRUE); +} + +static void +test_driver_probe_action_error (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + gboolean done = FALSE; + + dev_class->id = "Error device ID"; + dev_class->probe = fake_device_probe_action_error; + g_async_initable_new_async (FPI_TYPE_DEVICE_FAKE, G_PRIORITY_DEFAULT, NULL, + on_driver_probe_error_async, &done, NULL); + + while (!done) + g_main_context_iteration (NULL, TRUE); +} + static void test_driver_open (void) { @@ -1766,6 +1829,8 @@ main (int argc, char *argv[]) g_test_add_func ("/driver/get_driver_data", test_driver_get_driver_data); g_test_add_func ("/driver/probe", test_driver_probe); + g_test_add_func ("/driver/probe/error", test_driver_probe_error); + g_test_add_func ("/driver/probe/action_error", test_driver_probe_action_error); g_test_add_func ("/driver/open", test_driver_open); g_test_add_func ("/driver/open/error", test_driver_open_error); g_test_add_func ("/driver/close", test_driver_close); From ad514c37759845e51e01a1fcbb4095eeda437596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 17 Jan 2020 16:52:05 +0100 Subject: [PATCH 207/237] test-fpi-device: Fix file description --- tests/test-fpi-device.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c index ad873888..091f3b4c 100644 --- a/tests/test-fpi-device.c +++ b/tests/test-fpi-device.c @@ -1,6 +1,5 @@ /* - * Example fingerprint device prints listing and deletion - * Enrolls your right index finger and saves the print to disk + * Unit tests for the internal fingerprint drivers API * Copyright (C) 2019 Marco Trevisan * * This library is free software; you can redistribute it and/or From a87e9c546f44b0369c4855ca728d6e324ae9e505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 17 Jan 2020 16:53:18 +0100 Subject: [PATCH 208/237] test-fpi-device: Verify device action error operations --- tests/test-device-fake.c | 87 ++++++++++++++---- tests/test-device-fake.h | 1 + tests/test-fpi-device.c | 189 ++++++++++++++++++++++++++++++++------- 3 files changed, 231 insertions(+), 46 deletions(-) diff --git a/tests/test-device-fake.c b/tests/test-device-fake.c index 84e5e202..cd085374 100644 --- a/tests/test-device-fake.c +++ b/tests/test-device-fake.c @@ -35,9 +35,15 @@ fpi_device_fake_probe (FpDevice *device) FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + fake_dev->last_called_function = fpi_device_fake_probe; g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_PROBE); - fake_dev->last_called_function = fpi_device_fake_probe; + if (fake_dev->return_action_error) + { + fpi_device_action_error (device, fake_dev->ret_error); + return; + } + fpi_device_probe_complete (device, dev_class->id, dev_class->full_name, fake_dev->ret_error); } @@ -47,9 +53,15 @@ fpi_device_fake_open (FpDevice *device) { FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + fake_dev->last_called_function = fpi_device_fake_open; g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_OPEN); - fake_dev->last_called_function = fpi_device_fake_open; + if (fake_dev->return_action_error) + { + fpi_device_action_error (device, fake_dev->ret_error); + return; + } + fpi_device_open_complete (device, fake_dev->ret_error); } @@ -58,9 +70,15 @@ fpi_device_fake_close (FpDevice *device) { FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + fake_dev->last_called_function = fpi_device_fake_close; g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_CLOSE); - fake_dev->last_called_function = fpi_device_fake_close; + if (fake_dev->return_action_error) + { + fpi_device_action_error (device, fake_dev->ret_error); + return; + } + fpi_device_close_complete (device, fake_dev->ret_error); } @@ -70,13 +88,20 @@ fpi_device_fake_enroll (FpDevice *device) FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); FpPrint *print = fake_dev->ret_print; + fake_dev->last_called_function = fpi_device_fake_enroll; g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_ENROLL); + + if (fake_dev->return_action_error) + { + fpi_device_action_error (device, fake_dev->ret_error); + return; + } + fpi_device_get_enroll_data (device, (FpPrint **) &fake_dev->action_data); if (!print && !fake_dev->ret_error) fpi_device_get_enroll_data (device, &print); - fake_dev->last_called_function = fpi_device_fake_enroll; fpi_device_enroll_complete (device, print ? g_object_ref (print) : NULL, fake_dev->ret_error); @@ -88,14 +113,20 @@ fpi_device_fake_verify (FpDevice *device) FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); FpPrint *print = fake_dev->ret_print; + fake_dev->last_called_function = fpi_device_fake_verify; g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_VERIFY); + + if (fake_dev->return_action_error) + { + fpi_device_action_error (device, fake_dev->ret_error); + return; + } + fpi_device_get_verify_data (device, (FpPrint **) &fake_dev->action_data); if (!print && !fake_dev->ret_error) fpi_device_get_verify_data (device, &print); - fake_dev->last_called_function = fpi_device_fake_verify; - if (!fake_dev->ret_error || fake_dev->ret_error->domain == FP_DEVICE_RETRY) { fpi_device_verify_report (device, fake_dev->ret_result, print, fake_dev->ret_error); @@ -113,7 +144,15 @@ fpi_device_fake_identify (FpDevice *device) FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); FpPrint *match = fake_dev->ret_match; + fake_dev->last_called_function = fpi_device_fake_identify; g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_IDENTIFY); + + if (fake_dev->return_action_error) + { + fpi_device_action_error (device, fake_dev->ret_error); + return; + } + fpi_device_get_identify_data (device, (GPtrArray **) &fake_dev->action_data); if (!match && !fake_dev->ret_error) @@ -135,7 +174,6 @@ fpi_device_fake_identify (FpDevice *device) } } - fake_dev->last_called_function = fpi_device_fake_identify; if (!fake_dev->ret_error || fake_dev->ret_error->domain == FP_DEVICE_RETRY) { fpi_device_identify_report (device, match, fake_dev->ret_print, fake_dev->ret_error); @@ -152,10 +190,16 @@ fpi_device_fake_capture (FpDevice *device) { FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_CAPTURE); - fpi_device_get_capture_data (device, (gboolean *) &fake_dev->action_data); - fake_dev->last_called_function = fpi_device_fake_capture; + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_CAPTURE); + + if (fake_dev->return_action_error) + { + fpi_device_action_error (device, fake_dev->ret_error); + return; + } + + fpi_device_get_capture_data (device, (gboolean *) &fake_dev->action_data); fpi_device_capture_complete (device, fake_dev->ret_image, fake_dev->ret_error); } @@ -164,9 +208,15 @@ fpi_device_fake_list (FpDevice *device) { FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + fake_dev->last_called_function = fpi_device_fake_list; g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_LIST); - fake_dev->last_called_function = fpi_device_fake_list; + if (fake_dev->return_action_error) + { + fpi_device_action_error (device, fake_dev->ret_error); + return; + } + fpi_device_list_complete (device, fake_dev->ret_list, fake_dev->ret_error); } @@ -175,10 +225,16 @@ fpi_device_fake_delete (FpDevice *device) { FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_DELETE); - fpi_device_get_delete_data (device, (gpointer) & fake_dev->action_data); - fake_dev->last_called_function = fpi_device_fake_delete; + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_DELETE); + + if (fake_dev->return_action_error) + { + fpi_device_action_error (device, fake_dev->ret_error); + return; + } + + fpi_device_get_delete_data (device, (gpointer) (&fake_dev->action_data)); fpi_device_delete_complete (device, fake_dev->ret_error); } @@ -187,9 +243,8 @@ fpi_device_fake_cancel (FpDevice *device) { FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - g_assert_cmpuint (fpi_device_get_current_action (device), !=, FPI_DEVICE_ACTION_NONE); - fake_dev->last_called_function = fpi_device_fake_cancel; + g_assert_cmpuint (fpi_device_get_current_action (device), !=, FPI_DEVICE_ACTION_NONE); } static void diff --git a/tests/test-device-fake.h b/tests/test-device-fake.h index e8a09193..e828b55c 100644 --- a/tests/test-device-fake.h +++ b/tests/test-device-fake.h @@ -30,6 +30,7 @@ struct _FpiDeviceFake FpDevice parent; gpointer last_called_function; + gboolean return_action_error; GError *ret_error; FpPrint *ret_print; diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c index 091f3b4c..7163b810 100644 --- a/tests/test-fpi-device.c +++ b/tests/test-fpi-device.c @@ -42,6 +42,14 @@ auto_close_fake_device_new (void) static void auto_close_fake_device_free (FpAutoCloseDevice *device) { + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + + if (fake_dev->return_action_error) + { + fake_dev->return_action_error = FALSE; + fake_dev->ret_error = NULL; + } + if (fp_device_is_open (device)) g_assert_true (fp_device_close_sync (device, NULL, NULL)); @@ -89,16 +97,6 @@ on_device_notify (FpDevice *device, GParamSpec *spec, gpointer user_data) fake_dev->user_data = g_param_spec_ref (spec); } -static void -test_driver_action_error_vfunc (FpDevice *device) -{ - FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - - fake_dev->last_called_function = test_driver_action_error_vfunc; - - fpi_device_action_error (device, fake_dev->user_data); -} - /* Tests */ static void @@ -1421,7 +1419,7 @@ test_driver_cancel_fail (void) g_cancellable_cancel (cancellable); while (g_main_context_iteration (NULL, FALSE)) - ; + continue; g_assert (fake_dev->last_called_function == dev_class->delete); g_assert_no_error (error); @@ -1643,45 +1641,176 @@ test_driver_action_error_error (void) } static void -test_driver_action_error_open (void) +test_driver_action_error_all (void) { - g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); - g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(FpAutoCloseDevice) device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + g_autoptr(FpPrint) enrolled_print = g_object_ref_sink (fp_print_new (device)); + g_autoptr(GPtrArray) prints = g_ptr_array_new_with_free_func (g_object_unref); g_autoptr(GError) error = NULL; - FpiDeviceFake *fake_dev; + FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - dev_class->open = test_driver_action_error_vfunc; - device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); fake_dev = FPI_DEVICE_FAKE (device); - fake_dev->user_data = fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID); + fake_dev->return_action_error = TRUE; + fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID); g_assert_false (fp_device_open_sync (device, NULL, &error)); + g_assert_true (fake_dev->last_called_function == dev_class->open); g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_DATA_INVALID); + g_clear_error (&error); - g_assert (fake_dev->last_called_function == test_driver_action_error_vfunc); + fake_dev->return_action_error = FALSE; + fake_dev->ret_error = NULL; + g_assert_true (fp_device_open_sync (device, NULL, NULL)); + + fake_dev->return_action_error = TRUE; + + fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID); + g_assert_false (fp_device_close_sync (device, NULL, &error)); + g_assert_true (fake_dev->last_called_function == dev_class->close); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_DATA_INVALID); + g_clear_error (&error); + + fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID); + g_assert_null (fp_device_enroll_sync (device, fp_print_new (device), NULL, + NULL, NULL, &error)); + g_assert_true (fake_dev->last_called_function == dev_class->enroll); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_DATA_INVALID); + g_clear_error (&error); + + fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID); + g_assert_false (fp_device_verify_sync (device, enrolled_print, NULL, + NULL, NULL, NULL, NULL, &error)); + g_assert_true (fake_dev->last_called_function == dev_class->verify); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_DATA_INVALID); + g_clear_error (&error); + + fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID); + g_assert_false (fp_device_identify_sync (device, prints, NULL, + NULL, NULL, NULL, NULL, &error)); + g_assert_true (fake_dev->last_called_function == dev_class->identify); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_DATA_INVALID); + g_clear_error (&error); + + fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID); + g_assert_null (fp_device_capture_sync (device, TRUE, NULL, &error)); + g_assert_true (fake_dev->last_called_function == dev_class->capture); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_DATA_INVALID); + g_clear_error (&error); + + fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID); + g_assert_null (fp_device_list_prints_sync (device, NULL, &error)); + g_assert_true (fake_dev->last_called_function == dev_class->list); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_DATA_INVALID); + g_clear_error (&error); + + fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID); + g_assert_false (fp_device_delete_print_sync (device, enrolled_print, NULL, &error)); + g_assert_true (fake_dev->last_called_function == dev_class->delete); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_DATA_INVALID); + g_clear_error (&error); } static void -test_driver_action_error_fallback_open (void) +test_driver_action_error_fallback_all (void) { - g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); - g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(FpAutoCloseDevice) device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + g_autoptr(FpPrint) enrolled_print = g_object_ref_sink (fp_print_new (device)); + g_autoptr(GPtrArray) prints = g_ptr_array_new_with_free_func (g_object_unref); g_autoptr(GError) error = NULL; - FpiDeviceFake *fake_dev; + FpDeviceClass *dev_class = FP_DEVICE_GET_CLASS (device); + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); - dev_class->open = test_driver_action_error_vfunc; - device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); fake_dev = FPI_DEVICE_FAKE (device); + fake_dev->return_action_error = TRUE; g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "*Device failed to pass an error to generic action " "error function*"); g_assert_false (fp_device_open_sync (device, NULL, &error)); - g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); - - g_assert (fake_dev->last_called_function == test_driver_action_error_vfunc); g_test_assert_expected_messages (); + g_assert_true (fake_dev->last_called_function == dev_class->open); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + g_clear_error (&error); + + fake_dev->return_action_error = FALSE; + g_assert_true (fp_device_open_sync (device, NULL, NULL)); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Device failed to pass an error to generic action " + "error function*"); + + fake_dev->return_action_error = TRUE; + g_assert_false (fp_device_close_sync (device, NULL, &error)); + g_test_assert_expected_messages (); + g_assert_true (fake_dev->last_called_function == dev_class->close); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + g_clear_error (&error); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Device failed to pass an error to generic action " + "error function*"); + + g_assert_null (fp_device_enroll_sync (device, fp_print_new (device), NULL, + NULL, NULL, &error)); + g_test_assert_expected_messages (); + g_test_assert_expected_messages (); + g_assert_true (fake_dev->last_called_function == dev_class->enroll); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + g_clear_error (&error); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Device failed to pass an error to generic action " + "error function*"); + + g_assert_false (fp_device_verify_sync (device, enrolled_print, NULL, + NULL, NULL, NULL, NULL, &error)); + g_test_assert_expected_messages (); + g_assert_true (fake_dev->last_called_function == dev_class->verify); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + g_clear_error (&error); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Device failed to pass an error to generic action " + "error function*"); + + g_assert_false (fp_device_identify_sync (device, prints, NULL, + NULL, NULL, NULL, NULL, &error)); + g_test_assert_expected_messages (); + g_assert_true (fake_dev->last_called_function == dev_class->identify); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + g_clear_error (&error); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Device failed to pass an error to generic action " + "error function*"); + + g_assert_null (fp_device_capture_sync (device, TRUE, NULL, &error)); + g_test_assert_expected_messages (); + g_assert_true (fake_dev->last_called_function == dev_class->capture); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + g_clear_error (&error); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Device failed to pass an error to generic action " + "error function*"); + + g_assert_null (fp_device_list_prints_sync (device, NULL, &error)); + g_test_assert_expected_messages (); + g_assert_true (fake_dev->last_called_function == dev_class->list); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + g_clear_error (&error); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Device failed to pass an error to generic action " + "error function*"); + + g_assert_false (fp_device_delete_print_sync (device, enrolled_print, NULL, &error)); + g_test_assert_expected_messages (); + g_assert_true (fake_dev->last_called_function == dev_class->delete); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + g_clear_error (&error); } static void @@ -1867,8 +1996,8 @@ main (int argc, char *argv[]) g_test_add_func ("/driver/action_is_cancelled/error", test_driver_action_is_cancelled_error); g_test_add_func ("/driver/complete_action/all/error", test_driver_complete_actions_errors); g_test_add_func ("/driver/action_error/error", test_driver_action_error_error); - g_test_add_func ("/driver/action_error/open", test_driver_action_error_open); - g_test_add_func ("/driver/action_error/fail/open", test_driver_action_error_fallback_open); + g_test_add_func ("/driver/action_error/all", test_driver_action_error_all); + g_test_add_func ("/driver/action_error/fail", test_driver_action_error_fallback_all); g_test_add_func ("/driver/timeout", test_driver_add_timeout); g_test_add_func ("/driver/timeout/cancelled", test_driver_add_timeout_cancelled); From cdcc4763250807b63a3af1f8fefb80dd74ea1d74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 17 Jan 2020 17:22:15 +0100 Subject: [PATCH 209/237] examples/verify: Prompt match/no-match report in callback Promptly show the match/no-match result in the match callback instead of waiting the verification process to be finished. Also exit in case of an hard error, while permit to try again in case of a retry error. --- examples/verify.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/examples/verify.c b/examples/verify.c index 545539aa..acfc9eff 100644 --- a/examples/verify.c +++ b/examples/verify.c @@ -86,23 +86,13 @@ on_verify_completed (FpDevice *dev, GAsyncResult *res, void *user_data) if (!fp_device_verify_finish (dev, res, &match, &print, &error)) { g_warning ("Failed to verify print: %s", error->message); - verify_quit (dev, verify_data); - return; - } - - if (print && fp_device_supports_capture (dev) && - print_image_save (print, "verify.pgm")) - g_print ("Print image saved as verify.pgm\n"); - - if (match) - { - g_print ("MATCH!\n"); - verify_data->ret_value = EXIT_SUCCESS; - } - else - { - g_print ("NO MATCH!\n"); verify_data->ret_value = EXIT_FAILURE; + + if (error->domain != FP_DEVICE_RETRY) + { + verify_quit (dev, verify_data); + return; + } } g_print ("Verify again? [Y/n]? "); @@ -120,6 +110,8 @@ static void on_match_cb (FpDevice *dev, FpPrint *match, FpPrint *print, gpointer user_data, GError *error) { + VerifyData *verify_data = user_data; + if (error) { g_warning ("Match report: Finger not matched, retry error reported: %s", @@ -127,10 +119,16 @@ on_match_cb (FpDevice *dev, FpPrint *match, FpPrint *print, return; } + if (print && fp_device_supports_capture (dev) && + print_image_save (print, "verify.pgm")) + g_print ("Print image saved as verify.pgm\n"); + if (match) { char date_str[128]; + verify_data->ret_value = EXIT_SUCCESS; + g_date_strftime (date_str, G_N_ELEMENTS (date_str), "%Y-%m-%d\0", fp_print_get_enroll_date (match)); g_debug ("Match report: device %s matched finger %s successifully " @@ -139,10 +137,13 @@ on_match_cb (FpDevice *dev, FpPrint *match, FpPrint *print, finger_to_string (fp_print_get_finger (match)), fp_print_get_description (match), date_str, fp_print_get_username (match)); + + g_print ("MATCH!\n"); } else { g_debug ("Match report: Finger not matched"); + g_print ("NO MATCH!\n"); } } From 58a9214610b08d401761f69fb4cd7e43fadd1d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 17 Jan 2020 19:22:54 +0100 Subject: [PATCH 210/237] test-fpi-device: Add tests for verify/identify warnings --- tests/test-fpi-device.c | 175 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 174 insertions(+), 1 deletion(-) diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c index 7163b810..6bd117ec 100644 --- a/tests/test-fpi-device.c +++ b/tests/test-fpi-device.c @@ -813,6 +813,46 @@ fake_device_verify_complete_error (FpDevice *device) fpi_device_verify_complete (device, complete_error); } +static void +test_driver_verify_report_no_callback (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(MatchCbData) match_data = g_new0 (MatchCbData, 1); + g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(FpPrint) enrolled_print = NULL; + g_autoptr(FpPrint) print = NULL; + g_autoptr(GError) error = NULL; + FpiDeviceFake *fake_dev; + gboolean match; + + dev_class->verify = fake_device_verify_complete_error; + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + fake_dev = FPI_DEVICE_FAKE (device); + enrolled_print = g_object_ref_sink (fp_print_new (device)); + + g_assert_true (fp_device_open_sync (device, NULL, NULL)); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Driver reported a verify error that was not in the retry domain*"); + + fake_dev->ret_result = FPI_MATCH_ERROR; + fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_NOT_SUPPORTED); + fp_device_verify_sync (device, enrolled_print, NULL, + test_driver_match_cb, match_data, + &match, &print, &error); + + g_test_assert_expected_messages (); + + g_assert_false (match_data->called); + g_assert_null (match_data->match); + g_assert_no_error (match_data->error); + + g_assert (fake_dev->last_called_function == dev_class->verify); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_NOT_SUPPORTED); + g_assert (error == g_steal_pointer (&fake_dev->ret_error)); + g_assert_false (match); +} + static void test_driver_verify_complete_retry (void) { @@ -893,6 +933,50 @@ test_driver_verify_complete_retry (void) g_assert_error (match_data->error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_TOO_SHORT); g_assert_false (match); g_assert_null (print); + g_clear_error (&error); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Driver reported an error without specifying a retry " + "code, assuming general retry error*"); + + test_driver_match_data_clear (match_data); + fake_dev->ret_result = FPI_MATCH_ERROR; + + fp_device_verify_sync (device, enrolled_print, NULL, test_driver_match_cb, + match_data, &match, &print, &error); + g_test_assert_expected_messages (); + + g_assert_true (error != g_steal_pointer (&fake_dev->ret_error)); + g_assert_true (error != g_steal_pointer (&fake_dev->user_data)); + g_assert_error (error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_GENERAL); + g_assert_true (match_data->called); + g_assert_error (match_data->error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_GENERAL); + g_assert_false (match); + g_assert_null (print); + g_clear_error (&error); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Driver reported a print together with an error*"); + + test_driver_match_data_clear (match_data); + fake_dev->ret_result = FPI_MATCH_ERROR; + fake_dev->ret_error = fpi_device_retry_new (FP_DEVICE_RETRY_TOO_SHORT); + fake_dev->ret_print = fp_print_new (device); + g_object_add_weak_pointer (G_OBJECT (fake_dev->ret_print), + (gpointer) (&fake_dev->ret_print)); + + fp_device_verify_sync (device, enrolled_print, NULL, test_driver_match_cb, + match_data, &match, &print, &error); + g_test_assert_expected_messages (); + + g_assert_error (error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_TOO_SHORT); + g_assert_true (error == g_steal_pointer (&fake_dev->ret_error)); + g_assert_true (match_data->called); + g_assert_error (match_data->error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_TOO_SHORT); + g_assert_null (fake_dev->ret_print); + g_assert_false (match); + g_assert_null (print); + g_clear_error (&error); } static void @@ -1088,7 +1172,6 @@ test_driver_identify_not_reported (void) g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); g_autoptr(FpAutoCloseDevice) device = NULL; g_autoptr(GPtrArray) prints = g_ptr_array_new_with_free_func (g_object_unref); - g_autoptr(FpPrint) out_print = NULL; g_autoptr(GError) error = NULL; unsigned int i; @@ -1118,6 +1201,8 @@ fake_device_identify_complete_error (FpDevice *device) FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); GError *complete_error = fake_dev->user_data; + fake_dev->last_called_function = fake_device_identify_complete_error; + fpi_device_identify_report (device, fake_dev->ret_match, fake_dev->ret_print, fake_dev->ret_error); fpi_device_identify_complete (device, complete_error); } @@ -1163,6 +1248,92 @@ test_driver_identify_complete_retry (void) g_assert_error (match_data->error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_TOO_SHORT); g_assert_null (match); g_assert_null (print); + g_clear_error (&error); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Driver reported a match to a print that was not in the gallery*"); + + test_driver_match_data_clear (match_data); + fake_dev->ret_match = fp_print_new (device); + g_object_add_weak_pointer (G_OBJECT (fake_dev->ret_match), + (gpointer) (&fake_dev->ret_match)); + fp_device_identify_sync (device, prints, NULL, test_driver_match_cb, match_data, + &match, &print, &error); + g_test_assert_expected_messages (); + + g_object_unref (fake_dev->ret_match); + g_assert_null (fake_dev->ret_match); + g_assert_true (match_data->called); + g_assert_no_error (match_data->error); + g_assert_no_error (error); + g_assert_false (match); + g_assert_null (print); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Driver reported an error code but also provided a match*"); + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Driver reported a print together with an error*"); + + test_driver_match_data_clear (match_data); + fake_dev->ret_error = fpi_device_retry_new (FP_DEVICE_RETRY_REMOVE_FINGER); + fake_dev->ret_match = prints->pdata[0]; + fake_dev->ret_print = fp_print_new (device); + g_object_add_weak_pointer (G_OBJECT (fake_dev->ret_print), + (gpointer) (&fake_dev->ret_print)); + fp_device_identify_sync (device, prints, NULL, test_driver_match_cb, match_data, + &match, &print, &error); + g_test_assert_expected_messages (); + + g_assert_error (error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_REMOVE_FINGER); + g_assert_true (error == g_steal_pointer (&fake_dev->ret_error)); + g_assert_null (fake_dev->ret_print); + g_assert_true (match_data->called); + g_assert_error (match_data->error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_REMOVE_FINGER); + g_assert_false (match); + g_assert_null (print); + g_clear_error (&error); +} + +static void +test_driver_identify_report_no_callback (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(MatchCbData) match_data = g_new0 (MatchCbData, 1); + g_autoptr(GPtrArray) prints = g_ptr_array_new_with_free_func (g_object_unref); + g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(FpPrint) enrolled_print = NULL; + g_autoptr(FpPrint) print = NULL; + g_autoptr(FpPrint) match = NULL; + g_autoptr(GError) error = NULL; + FpiDeviceFake *fake_dev; + + dev_class->identify = fake_device_identify_complete_error; + device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL); + fake_dev = FPI_DEVICE_FAKE (device); + enrolled_print = g_object_ref_sink (fp_print_new (device)); + + g_assert_true (fp_device_open_sync (device, NULL, NULL)); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Driver reported a verify error that was not in the retry domain*"); + + fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_NOT_SUPPORTED); + fp_device_identify_sync (device, prints, NULL, + test_driver_match_cb, match_data, + &match, &print, &error); + + g_test_assert_expected_messages (); + + g_assert_null (match); + g_assert_null (print); + g_assert_false (match_data->called); + g_assert_null (match_data->match); + g_assert_no_error (match_data->error); + + g_assert (fake_dev->last_called_function == dev_class->identify); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_NOT_SUPPORTED); + g_assert (error == g_steal_pointer (&fake_dev->ret_error)); + g_assert_false (match); } static void @@ -1970,6 +2141,7 @@ main (int argc, char *argv[]) g_test_add_func ("/driver/verify/fail", test_driver_verify_fail); g_test_add_func ("/driver/verify/retry", test_driver_verify_retry); g_test_add_func ("/driver/verify/error", test_driver_verify_error); + g_test_add_func ("/driver/verify/report_no_cb", test_driver_verify_report_no_callback); g_test_add_func ("/driver/verify/not_reported", test_driver_verify_not_reported); g_test_add_func ("/driver/verify/complete_retry", test_driver_verify_complete_retry); g_test_add_func ("/driver/identify", test_driver_identify); @@ -1978,6 +2150,7 @@ main (int argc, char *argv[]) g_test_add_func ("/driver/identify/error", test_driver_identify_error); g_test_add_func ("/driver/identify/not_reported", test_driver_identify_not_reported); g_test_add_func ("/driver/identify/complete_retry", test_driver_identify_complete_retry); + g_test_add_func ("/driver/identify/report_no_cb", test_driver_identify_report_no_callback); g_test_add_func ("/driver/capture", test_driver_capture); g_test_add_func ("/driver/capture/error", test_driver_capture_error); g_test_add_func ("/driver/list", test_driver_list); From 05df5e2822c34d3dd66580729e0ef51a5bde6fa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 17 Jan 2020 19:41:28 +0100 Subject: [PATCH 211/237] test-fpi-device: Verify driver enroll errors --- tests/test-fpi-device.c | 54 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c index 6bd117ec..58ebe22f 100644 --- a/tests/test-fpi-device.c +++ b/tests/test-fpi-device.c @@ -489,6 +489,59 @@ test_driver_enroll_error (void) g_assert_null (out_print); } +static void +test_driver_enroll_complete_simple (FpDevice *device) +{ + FpiDeviceFake *fake_dev = FPI_DEVICE_FAKE (device); + + fake_dev->last_called_function = test_driver_enroll_complete_simple; + g_assert_cmpuint (fpi_device_get_current_action (device), ==, FPI_DEVICE_ACTION_ENROLL); + + fpi_device_enroll_complete (device, fake_dev->ret_print, fake_dev->ret_error); +} + +static void +test_driver_enroll_error_no_print (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(FpPrint) out_print = NULL; + FpiDeviceFake *fake_dev; + + dev_class->enroll = test_driver_enroll_complete_simple; + device = auto_close_fake_device_new (); + fake_dev = FPI_DEVICE_FAKE (device); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Driver did not provide a valid print and failed to provide an error*"); + out_print = + fp_device_enroll_sync (device, fp_print_new (device), NULL, NULL, NULL, &error); + + g_test_assert_expected_messages (); + g_assert (fake_dev->last_called_function == dev_class->enroll); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + g_assert_null (out_print); + g_clear_error (&error); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*Driver passed an error but also provided a print, returning error*"); + + fake_dev->ret_error = fpi_device_error_new (FP_DEVICE_ERROR_GENERAL); + fake_dev->ret_print = fp_print_new (device); + g_object_add_weak_pointer (G_OBJECT (fake_dev->ret_print), + (gpointer) (&fake_dev->ret_print)); + out_print = + fp_device_enroll_sync (device, fp_print_new (device), NULL, NULL, NULL, &error); + + g_test_assert_expected_messages (); + g_assert (fake_dev->last_called_function == dev_class->enroll); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); + g_assert_true (error == g_steal_pointer (&fake_dev->ret_error)); + g_assert_null (out_print); + g_assert_null (fake_dev->ret_print); +} + typedef struct { gint completed_stages; @@ -2136,6 +2189,7 @@ main (int argc, char *argv[]) g_test_add_func ("/driver/close/error", test_driver_close_error); g_test_add_func ("/driver/enroll", test_driver_enroll); g_test_add_func ("/driver/enroll/error", test_driver_enroll_error); + g_test_add_func ("/driver/enroll/error/no_print", test_driver_enroll_error_no_print); g_test_add_func ("/driver/enroll/progress", test_driver_enroll_progress); g_test_add_func ("/driver/verify", test_driver_verify); g_test_add_func ("/driver/verify/fail", test_driver_verify_fail); From b9e546f05b29a20006d7c573cb9ec257e2050104 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 20 Jan 2020 13:30:33 +0100 Subject: [PATCH 212/237] tests: Add missing NULL terminator to g_object_new The g_object_new call had a NULL argument for a property. This meant that the compiler could not warn about the lack of NULL termination for the argument list. Add the missing NULL termination. --- tests/test-fpi-device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c index 58ebe22f..826c6cdd 100644 --- a/tests/test-fpi-device.c +++ b/tests/test-fpi-device.c @@ -237,7 +237,7 @@ test_driver_get_usb_device (void) g_autoptr(FpDevice) device = NULL; dev_class->type = FP_DEVICE_TYPE_USB; - device = g_object_new (FPI_TYPE_DEVICE_FAKE, "fpi-usb-device", NULL); + device = g_object_new (FPI_TYPE_DEVICE_FAKE, "fpi-usb-device", NULL, NULL); g_assert_null (fpi_device_get_usb_device (device)); g_clear_object (&device); From 4d6a7ec09df1e10b8424d356e5be0aa5c8b88bd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 16 Jan 2020 19:09:20 +0100 Subject: [PATCH 213/237] synaptics: Really check if a print is device database Fix a typo causing the not-in-database print error to be fired, actually checking the response result. --- libfprint/drivers/synaptics/synaptics.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index 2470ba9b..4544d605 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -631,7 +631,7 @@ verify_msg_cb (FpiDeviceSynaptics *self, self->cmd_complete_data = GINT_TO_POINTER (FPI_MATCH_FAIL); self->cmd_complete_error = NULL; } - else if (BMKT_FP_DATABASE_NO_RECORD_EXISTS) + else if (resp->result == BMKT_FP_DATABASE_NO_RECORD_EXISTS) { fp_info ("Print is not in database"); fpi_device_verify_complete (device, From 8893840ffaf88f4acc8ed1acf3445be0fd74fd34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 16 Jan 2020 19:04:09 +0100 Subject: [PATCH 214/237] synaptics: Always report verify state early on non-match In some cases we want to complete the verification after that the finger has been removed, but we still need to promptly report the match state otherwise fpi-device will complain about, and will eventually cause a match error instead that reporting a non-match: synaptics: Finger is now on the sensor synaptics: Received message with 0 sequence number 0x91, ignoring! synaptics: interrupt transfer done synaptics: delaying match failure until after finger removal! synaptics: interrupt transfer done device: Driver reported successful verify complete but did not report the result earlier. Reporting error instead libfprint: Failed to verify print: An unspecified error occured! Fixes #227 --- libfprint/drivers/synaptics/synaptics.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index 4544d605..e216985d 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -623,6 +623,8 @@ verify_msg_cb (FpiDeviceSynaptics *self, self->cmd_complete_on_removal = TRUE; self->cmd_complete_data = GINT_TO_POINTER (FPI_MATCH_ERROR); self->cmd_complete_error = fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL); + fpi_device_verify_report (device, FPI_MATCH_ERROR, NULL, + fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL)); } else if (resp->result == BMKT_FP_NO_MATCH) { @@ -630,6 +632,7 @@ verify_msg_cb (FpiDeviceSynaptics *self, self->cmd_complete_on_removal = TRUE; self->cmd_complete_data = GINT_TO_POINTER (FPI_MATCH_FAIL); self->cmd_complete_error = NULL; + fpi_device_verify_report (device, FPI_MATCH_FAIL, NULL, NULL); } else if (resp->result == BMKT_FP_DATABASE_NO_RECORD_EXISTS) { From 8be861b876f9b2036534537fd378645812b4069e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 16 Jan 2020 19:08:10 +0100 Subject: [PATCH 215/237] synaptics: Remove unneeded complete error/data parameters Remove the never-used cmd_complete_data value and the repetition of cmd_complete_error. In fact now as per the fpi_device_verify_report() usage, we already pass the match information to the device, such as the error and it will be they will be used on completion if needed. This allows to simplify cmd_ssm_done() as well. --- libfprint/drivers/synaptics/synaptics.c | 17 +++-------------- libfprint/drivers/synaptics/synaptics.h | 2 -- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index e216985d..46641a3b 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -279,17 +279,10 @@ cmd_ssm_done (FpiSsm *ssm, FpDevice *dev, GError *error) self->cmd_ssm = NULL; /* Notify about the SSM failure from here instead. */ - if (error) - { - callback (self, NULL, error); - } - else if (self->cmd_complete_on_removal) - { - callback (self, NULL, self->cmd_complete_error); - self->cmd_complete_error = NULL; - } + if (error || self->cmd_complete_on_removal) + callback (self, NULL, error); + self->cmd_complete_on_removal = FALSE; - g_clear_pointer (&self->cmd_complete_error, g_error_free); } static void @@ -621,8 +614,6 @@ verify_msg_cb (FpiDeviceSynaptics *self, { fp_dbg ("delaying retry error until after finger removal!"); self->cmd_complete_on_removal = TRUE; - self->cmd_complete_data = GINT_TO_POINTER (FPI_MATCH_ERROR); - self->cmd_complete_error = fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL); fpi_device_verify_report (device, FPI_MATCH_ERROR, NULL, fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL)); } @@ -630,8 +621,6 @@ verify_msg_cb (FpiDeviceSynaptics *self, { fp_dbg ("delaying match failure until after finger removal!"); self->cmd_complete_on_removal = TRUE; - self->cmd_complete_data = GINT_TO_POINTER (FPI_MATCH_FAIL); - self->cmd_complete_error = NULL; fpi_device_verify_report (device, FPI_MATCH_FAIL, NULL, NULL); } else if (resp->result == BMKT_FP_DATABASE_NO_RECORD_EXISTS) diff --git a/libfprint/drivers/synaptics/synaptics.h b/libfprint/drivers/synaptics/synaptics.h index 37eb362e..ac50171b 100644 --- a/libfprint/drivers/synaptics/synaptics.h +++ b/libfprint/drivers/synaptics/synaptics.h @@ -110,8 +110,6 @@ struct _FpiDeviceSynaptics FpiSsm *cmd_ssm; FpiUsbTransfer *cmd_pending_transfer; gboolean cmd_complete_on_removal; - GError *cmd_complete_error; - void *cmd_complete_data; bmkt_sensor_version_t mis_version; From 7a7bec5a8059f73e985d183818606cd3976e3e96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 16 Jan 2020 19:47:45 +0100 Subject: [PATCH 216/237] synaptics: Report a verify complete error on unexpected result Also remove the unneeded verify report with match failure --- libfprint/drivers/synaptics/synaptics.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index 46641a3b..c7fbe35b 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -632,8 +632,10 @@ verify_msg_cb (FpiDeviceSynaptics *self, else { fp_warn ("Verify has failed: %d", resp->result); - fpi_device_verify_report (device, FPI_MATCH_FAIL, NULL, NULL); - fpi_device_verify_complete (device, NULL); + fpi_device_verify_complete (device, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Unexpected result from device %d", + resp->result)); } break; From 3b4711312219c205542a1cf5b1f0fef6e3d1828c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 16 Jan 2020 20:00:15 +0100 Subject: [PATCH 217/237] synaptics: Immediately complete verification if finger removed When quickly scanning a finger with the synaptic driver, it may wait forever for finger removal even if this has already happened. In fact we don't take care of the finger status when reporting the verification. To avoid this, add a function that delays the completion of the verification until the finger removal if the finger is on sensor, otherwise it just performs it. Fixes #228 --- libfprint/drivers/synaptics/synaptics.c | 28 +++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index c7fbe35b..49bdfec2 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -575,6 +575,22 @@ list (FpDevice *device) synaptics_sensor_cmd (self, 0, BMKT_CMD_GET_TEMPLATE_RECORDS, NULL, 0, list_msg_cb); } +static void +verify_complete_after_finger_removal (FpiDeviceSynaptics *self) +{ + FpDevice *device = FP_DEVICE (self); + + if (self->finger_on_sensor) + { + fp_dbg ("delaying verify report until after finger removal!"); + self->cmd_complete_on_removal = TRUE; + } + else + { + fpi_device_verify_complete (device, NULL); + } +} + static void verify_msg_cb (FpiDeviceSynaptics *self, bmkt_response_t *resp, @@ -610,18 +626,18 @@ verify_msg_cb (FpiDeviceSynaptics *self, break; case BMKT_RSP_VERIFY_FAIL: - if(resp->result == BMKT_SENSOR_STIMULUS_ERROR) + if (resp->result == BMKT_SENSOR_STIMULUS_ERROR) { - fp_dbg ("delaying retry error until after finger removal!"); - self->cmd_complete_on_removal = TRUE; + fp_info ("Match error occurred"); fpi_device_verify_report (device, FPI_MATCH_ERROR, NULL, fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL)); + verify_complete_after_finger_removal (self); } else if (resp->result == BMKT_FP_NO_MATCH) { - fp_dbg ("delaying match failure until after finger removal!"); - self->cmd_complete_on_removal = TRUE; - fpi_device_verify_report (device, FPI_MATCH_FAIL, NULL, NULL); + fp_info ("Print didn't match"); + fpi_device_verify_report (device, FPI_MATCH_FAIL, NULL, error); + verify_complete_after_finger_removal (self); } else if (resp->result == BMKT_FP_DATABASE_NO_RECORD_EXISTS) { From 88461d53ec146e4531ce40293c38355549532723 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 21 Jan 2020 12:34:40 +0100 Subject: [PATCH 218/237] upekts: Fix use-after-free in an error condition The callback function would continue processing even after having failed the SSM already. This causes further invalid operations on the SSM. This error was found using a coverity scan. --- libfprint/drivers/upekts.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libfprint/drivers/upekts.c b/libfprint/drivers/upekts.c index 47903ef9..08e98c68 100644 --- a/libfprint/drivers/upekts.c +++ b/libfprint/drivers/upekts.c @@ -902,8 +902,10 @@ enroll_start_sm_cb_msg28 (FpDevice *dev, FpiSsm *ssm = user_data; if (error) - fpi_ssm_mark_failed (ssm, error); - if (type != READ_MSG_RESPONSE) + { + fpi_ssm_mark_failed (ssm, error); + } + else if (type != READ_MSG_RESPONSE) { fp_err ("expected response, got %d seq=%x", type, seq); fpi_ssm_mark_failed (ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, From a12d316aa4cf609abc68af2652ac4d0d9b75c902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 22 Jan 2020 21:03:40 +0100 Subject: [PATCH 219/237] meson: Use project name for log domain --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 96700f6b..db2a804d 100644 --- a/meson.build +++ b/meson.build @@ -53,7 +53,7 @@ common_cflags = cc.get_supported_arguments([ '-DGLIB_VERSION_MIN_REQUIRED=' + glib_version_def, '-DGLIB_VERSION_MAX_ALLOWED=' + glib_version_def, '-D_GNU_SOURCE', - '-DG_LOG_DOMAIN="libfprint"', + '-DG_LOG_DOMAIN="@0@"'.format(meson.project_name()), ]) c_cflags = cc.get_supported_arguments([ '-std=gnu99', From 24e9363a46fdddddc1df0fd64368413d02c84a1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 22 Jan 2020 21:03:28 +0100 Subject: [PATCH 220/237] doc/meson: Use ignore_headers instead of scan_args --- doc/meson.build | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/doc/meson.build b/doc/meson.build index e138ea29..55065edd 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -29,10 +29,7 @@ gnome.gtkdoc(meson.project_name(), dependencies: libfprint_dep, content_files: content_files, expand_content_files: expand_content_files, - scan_args: [ - #'--rebuild-sections', - '--ignore-headers=' + ' '.join(private_headers), - ], + ignore_headers: private_headers, fixxref_args: [ '--html-dir=@0@'.format(docpath), '--extra-dir=@0@'.format(join_paths(glib_docpath, 'glib')), From 23fab3a20a66d4e6a58a11d1541267690237e25d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 23 Jan 2020 17:08:41 +0100 Subject: [PATCH 221/237] Change SONAME and all the library paths to libfprint-2 To avoid conflicts with previous libfprint version and make sure that the target version is the correct one (plus to allow parallel install in some distros), let's use a versioned naming for the library keeping the abi version in sync. Fixes #223 --- doc/meson.build | 2 +- doc/xml/meson.build | 6 +++--- libfprint/meson.build | 6 +++--- meson.build | 7 ++++--- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/doc/meson.build b/doc/meson.build index 55065edd..1da63fbf 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -23,7 +23,7 @@ glib_prefix = dependency('glib-2.0').get_pkgconfig_variable('prefix') glib_docpath = join_paths(glib_prefix, 'share', 'gtk-doc', 'html') docpath = join_paths(get_option('datadir'), 'gtk-doc', 'html') -gnome.gtkdoc(meson.project_name(), +gnome.gtkdoc(versioned_libname, main_xml: 'libfprint-docs.xml', src_dir: join_paths(meson.source_root(), 'libfprint'), dependencies: libfprint_dep, diff --git a/doc/xml/meson.build b/doc/xml/meson.build index 5e56bb40..44c6e4aa 100644 --- a/doc/xml/meson.build +++ b/doc/xml/meson.build @@ -1,8 +1,8 @@ ent_conf = configuration_data() -ent_conf.set('PACKAGE', meson.project_name()) +ent_conf.set('PACKAGE', versioned_libname) ent_conf.set('PACKAGE_BUGREPORT', 'https://gitlab.freedesktop.org/libfprint/libfprint/issues') -ent_conf.set('PACKAGE_NAME', meson.project_name()) -ent_conf.set('PACKAGE_STRING', meson.project_name()) +ent_conf.set('PACKAGE_NAME', versioned_libname) +ent_conf.set('PACKAGE_STRING', versioned_libname) ent_conf.set('PACKAGE_TARNAME', 'libfprint-' + meson.project_version()) ent_conf.set('PACKAGE_URL', 'https://fprint.freedesktop.org/') ent_conf.set('PACKAGE_VERSION', meson.project_version()) diff --git a/libfprint/meson.build b/libfprint/meson.build index 50df8a0c..3d0752e9 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -177,7 +177,7 @@ other_sources = [] fp_enums = gnome.mkenums_simple('fp-enums', sources: libfprint_public_headers, install_header: true, - install_dir: get_option('includedir') / meson.project_name(), + install_dir: get_option('includedir') / versioned_libname, ) fp_enums_h = fp_enums[1] @@ -244,7 +244,7 @@ libfprint_drivers = static_library('fprint-drivers', mapfile = files('libfprint.ver') vflag = '-Wl,--version-script,@0@/@1@'.format(meson.source_root(), mapfile[0]) -libfprint = library('fprint', +libfprint = library(versioned_libname.split('lib')[1], sources: [ fp_enums, libfprint_sources, @@ -268,7 +268,7 @@ libfprint_dep = declare_dependency(link_with: libfprint, ]) install_headers(['fprint.h'] + libfprint_public_headers, - subdir: meson.project_name() + subdir: versioned_libname ) libfprint_private_dep = declare_dependency( diff --git a/meson.build b/meson.build index db2a804d..1f83e96f 100644 --- a/meson.build +++ b/meson.build @@ -75,6 +75,7 @@ soversion = 2 current = 0 revision = 0 libversion = '@0@.@1@.@2@'.format(soversion, current, revision) +versioned_libname = meson.project_name() + '-' + soversion.to_string() # Dependencies glib_dep = dependency('glib-2.0', version: '>=' + glib_min_version) @@ -206,10 +207,10 @@ subdir('tests') pkgconfig = import('pkgconfig') pkgconfig.generate( - name: meson.project_name(), + name: versioned_libname, description: 'Generic C API for fingerprint reader access', version: meson.project_version(), libraries: libfprint, - subdirs: meson.project_name(), - filebase: meson.project_name() + '@0@'.format(soversion), + subdirs: versioned_libname, + filebase: versioned_libname, ) From 2c9e252ca4d60c4b91dc9b94374ef81a3d863102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 23 Jan 2020 17:25:21 +0100 Subject: [PATCH 222/237] meson: Use versioned name for autosuspend udev rules Given distros may have the previous value around, let's rename this too --- libfprint/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfprint/meson.build b/libfprint/meson.build index 3d0752e9..4bd617fd 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -288,7 +288,7 @@ udev_rules = executable('fprint-list-udev-rules', if get_option('udev_rules') custom_target('udev-rules', - output: '60-fprint-autosuspend.rules', + output: '60-@0@-autosuspend.rules'.format(versioned_libname), capture: true, command: [ udev_rules ], install: true, From 7eb10178b87d557d024ad47ee1edb08aa6a3f9cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 5 Feb 2020 18:04:00 +0100 Subject: [PATCH 223/237] ci: Use a template to define libfprint dependencies This allows to share the configuration with fprintd --- .gitlab-ci.yml | 27 ++------------------------- .gitlab-ci/libfprint-templates.yaml | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 25 deletions(-) create mode 100644 .gitlab-ci/libfprint-templates.yaml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 270aec2c..7edb83e9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,6 +6,7 @@ variables: LAST_ABI_BREAK: "056ea541ddc97f5806cffbd99a12dc87e4da3546" include: + - local: '.gitlab-ci/libfprint-templates.yaml' - project: 'wayland/ci-templates' ref: master file: '/templates/fedora.yml' @@ -140,28 +141,4 @@ container_fedora_build: variables: GIT_STRATEGY: none # no need to pull the whole tree for rebuilding the image # a list of packages to install - FEDORA_RPMS: - doxygen - flatpak-builder - gcc - gcc-c++ - gcovr - git - glib2-devel - glibc-devel - gobject-introspection-devel - gtk-doc - gtk3-devel - libabigail - libgusb-devel - libX11-devel - libXv-devel - meson - nss-devel - pixman-devel - python3-cairo - python3-gobject - systemd - umockdev - uncrustify - valgrind + FEDORA_RPMS: $LIBFPRINT_DEPENDENCIES diff --git a/.gitlab-ci/libfprint-templates.yaml b/.gitlab-ci/libfprint-templates.yaml new file mode 100644 index 00000000..a1baa0a2 --- /dev/null +++ b/.gitlab-ci/libfprint-templates.yaml @@ -0,0 +1,26 @@ +variables: + LIBFPRINT_DEPENDENCIES: + doxygen + flatpak-builder + gcc + gcc-c++ + gcovr + git + glib2-devel + glibc-devel + gobject-introspection-devel + gtk-doc + gtk3-devel + libabigail + libgusb-devel + libX11-devel + libXv-devel + meson + nss-devel + pixman-devel + python3-cairo + python3-gobject + systemd + umockdev + uncrustify + valgrind From ca5143ffa5e293c69d22e4a7caa452d1d5e45499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 5 Feb 2020 19:31:38 +0100 Subject: [PATCH 224/237] ci: Exclude flatpak job from the schedules --- .gitlab-ci.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7edb83e9..10a8c3f1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -107,9 +107,6 @@ test_indent: .flatpak_master_template: &flatpak_master image: registry.gitlab.gnome.org/gnome/gnome-runtime-images/gnome:3.32 stage: flatpack - except: - variables: - - $CI_PIPELINE_SOURCE == "schedule" variables: MANIFEST_PATH: "demo/org.freedesktop.libfprint.Demo.json" # From demo/org.freedesktop.libfprint.Demo.json @@ -129,8 +126,11 @@ flatpak-manual master: <<: *flatpak_master when: manual except: - - tags - - master + refs: + - tags + - master + variables: + - $CI_PIPELINE_SOURCE == "schedule" # CONTAINERS creation stage container_fedora_build: From bb08d2e3c2216ebdbe71d1de09322cdeafcc5290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 5 Feb 2020 19:03:25 +0100 Subject: [PATCH 225/237] ci: Use extends to define extra libfprint variables This allows to merge the values when included instead of replacing the whole variables stanza. --- .gitlab-ci.yml | 15 +++++---- .gitlab-ci/libfprint-templates.yaml | 50 ++++++++++++++--------------- 2 files changed, 33 insertions(+), 32 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 10a8c3f1..d7a4a246 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,16 +1,17 @@ -variables: - FEDORA_TAG: rawhide - FEDORA_VERSION: rawhide - FEDORA_IMAGE: "$CI_REGISTRY/libfprint/$CI_PROJECT_NAME/fedora/$FEDORA_VERSION:$FEDORA_TAG" - BUNDLE: "org.freedesktop.libfprint.Demo.flatpak" - LAST_ABI_BREAK: "056ea541ddc97f5806cffbd99a12dc87e4da3546" - include: - local: '.gitlab-ci/libfprint-templates.yaml' - project: 'wayland/ci-templates' ref: master file: '/templates/fedora.yml' +variables: + extends: .libfprint_common_variables + FEDORA_TAG: rawhide + FEDORA_VERSION: rawhide + FEDORA_IMAGE: "$CI_REGISTRY/libfprint/$CI_PROJECT_NAME/fedora/$FEDORA_VERSION:$FEDORA_TAG" + BUNDLE: "org.freedesktop.libfprint.Demo.flatpak" + LAST_ABI_BREAK: "056ea541ddc97f5806cffbd99a12dc87e4da3546" + stages: - check-source - build diff --git a/.gitlab-ci/libfprint-templates.yaml b/.gitlab-ci/libfprint-templates.yaml index a1baa0a2..e8a510f8 100644 --- a/.gitlab-ci/libfprint-templates.yaml +++ b/.gitlab-ci/libfprint-templates.yaml @@ -1,26 +1,26 @@ -variables: +.libfprint_common_variables: LIBFPRINT_DEPENDENCIES: - doxygen - flatpak-builder - gcc - gcc-c++ - gcovr - git - glib2-devel - glibc-devel - gobject-introspection-devel - gtk-doc - gtk3-devel - libabigail - libgusb-devel - libX11-devel - libXv-devel - meson - nss-devel - pixman-devel - python3-cairo - python3-gobject - systemd - umockdev - uncrustify - valgrind + doxygen + flatpak-builder + gcc + gcc-c++ + gcovr + git + glib2-devel + glibc-devel + gobject-introspection-devel + gtk-doc + gtk3-devel + libabigail + libgusb-devel + libX11-devel + libXv-devel + meson + nss-devel + pixman-devel + python3-cairo + python3-gobject + systemd + umockdev + uncrustify + valgrind From 169ca1ba77bec76ac55333197ec47f875bd91ea9 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 13 Jan 2020 15:16:04 +0100 Subject: [PATCH 226/237] compat: Add compatibility defines for older GLib We are already using a number of defines and autoptrs from newer GLib releases. Add the appropriate compatibility defines rather than removing the corresponding code. Closes: #222 --- libfprint/drivers_api.h | 1 + libfprint/fp-print.c | 1 + libfprint/fpi-compat.h | 33 +++++++++++++++++++++++++++++++++ libfprint/fpi-context.h | 1 + tests/test-fpi-device.c | 1 + 5 files changed, 37 insertions(+) create mode 100644 libfprint/fpi-compat.h diff --git a/libfprint/drivers_api.h b/libfprint/drivers_api.h index 7476ba73..aef8c9dc 100644 --- a/libfprint/drivers_api.h +++ b/libfprint/drivers_api.h @@ -21,6 +21,7 @@ #pragma once +#include "fpi-compat.h" #include "fpi-assembling.h" #include "fpi-device.h" #include "fpi-image-device.h" diff --git a/libfprint/fp-print.c b/libfprint/fp-print.c index 34139ce8..b17b2034 100644 --- a/libfprint/fp-print.c +++ b/libfprint/fp-print.c @@ -21,6 +21,7 @@ #define FP_COMPONENT "print" #include "fp-print-private.h" +#include "fpi-compat.h" #include "fpi-log.h" /** diff --git a/libfprint/fpi-compat.h b/libfprint/fpi-compat.h new file mode 100644 index 00000000..fcf1600a --- /dev/null +++ b/libfprint/fpi-compat.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2020 Benjamin Berg + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +#include + +#if !GLIB_CHECK_VERSION (2, 57, 0) +G_DEFINE_AUTOPTR_CLEANUP_FUNC (GTypeClass, g_type_class_unref); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (GEnumClass, g_type_class_unref); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (GParamSpec, g_param_spec_unref); +#else +/* Re-define G_SOURCE_FUNC as we are technically not allowed to use it with + * the version we depend on currently. */ +#undef G_SOURCE_FUNC +#endif + +#define G_SOURCE_FUNC(f) ((GSourceFunc) (void (*)(void))(f)) diff --git a/libfprint/fpi-context.h b/libfprint/fpi-context.h index 48fecb48..ec98675b 100644 --- a/libfprint/fpi-context.h +++ b/libfprint/fpi-context.h @@ -20,6 +20,7 @@ #include #include "fp-context.h" +#include "fpi-compat.h" /** * fpi_get_driver_types: diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c index 826c6cdd..9f1da2ea 100644 --- a/tests/test-fpi-device.c +++ b/tests/test-fpi-device.c @@ -22,6 +22,7 @@ #define FP_COMPONENT "device" #include "fpi-device.h" +#include "fpi-compat.h" #include "fpi-log.h" #include "test-device-fake.h" From cfbd5d27b78fec0df60fe2248044b3f4c80a0bce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 30 Jan 2020 14:47:12 +0100 Subject: [PATCH 227/237] comapt: Add FpDeviceClass compatibility autoptr and use it --- libfprint/fp-context.c | 9 +++------ libfprint/fpi-compat.h | 5 +++++ libfprint/fprint-list-supported-devices.c | 3 +-- libfprint/fprint-list-udev-rules.c | 3 +-- tests/test-fpi-device.c | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/libfprint/fp-context.c b/libfprint/fp-context.c index 0e7c17f7..ffd38a4a 100644 --- a/libfprint/fp-context.c +++ b/libfprint/fp-context.c @@ -131,8 +131,7 @@ usb_device_added_cb (FpContext *self, GUsbDevice *device, GUsbContext *usb_ctx) for (i = 0; i < priv->drivers->len; i++) { GType driver = g_array_index (priv->drivers, GType, i); - g_autoptr(GTypeClass) type_class = g_type_class_ref (driver); - FpDeviceClass *cls = FP_DEVICE_CLASS (type_class); + g_autoptr(FpDeviceClass) cls = g_type_class_ref (driver); const FpIdEntry *entry; if (cls->type != FP_DEVICE_TYPE_USB) @@ -276,8 +275,7 @@ fp_context_init (FpContext *self) for (i = 0; i < priv->drivers->len;) { GType driver = g_array_index (priv->drivers, GType, i); - g_autoptr(GTypeClass) type_class = g_type_class_ref (driver); - FpDeviceClass *cls = FP_DEVICE_CLASS (type_class); + g_autoptr(FpDeviceClass) cls = g_type_class_ref (driver); if (!is_driver_allowed (cls->id)) g_array_remove_index (priv->drivers, i); @@ -351,8 +349,7 @@ fp_context_enumerate (FpContext *context) for (i = 0; i < priv->drivers->len; i++) { GType driver = g_array_index (priv->drivers, GType, i); - g_autoptr(GTypeClass) type_class = g_type_class_ref (driver); - FpDeviceClass *cls = FP_DEVICE_CLASS (type_class); + g_autoptr(FpDeviceClass) cls = g_type_class_ref (driver); const FpIdEntry *entry; if (cls->type != FP_DEVICE_TYPE_VIRTUAL) diff --git a/libfprint/fpi-compat.h b/libfprint/fpi-compat.h index fcf1600a..0904ab2c 100644 --- a/libfprint/fpi-compat.h +++ b/libfprint/fpi-compat.h @@ -31,3 +31,8 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (GParamSpec, g_param_spec_unref); #endif #define G_SOURCE_FUNC(f) ((GSourceFunc) (void (*)(void))(f)) + +#if !GLIB_CHECK_VERSION (2, 63, 3) +typedef struct _FpDeviceClass FpDeviceClass; +G_DEFINE_AUTOPTR_CLEANUP_FUNC (FpDeviceClass, g_type_class_unref); +#endif diff --git a/libfprint/fprint-list-supported-devices.c b/libfprint/fprint-list-supported-devices.c index cb2803f1..ae8cc113 100644 --- a/libfprint/fprint-list-supported-devices.c +++ b/libfprint/fprint-list-supported-devices.c @@ -38,8 +38,7 @@ insert_drivers (GList *list) for (i = 0; i < drivers->len; i++) { GType driver = g_array_index (drivers, GType, i); - g_autoptr(GTypeClass) type_class = g_type_class_ref (driver); - FpDeviceClass *cls = FP_DEVICE_CLASS (type_class); + g_autoptr(FpDeviceClass) cls = g_type_class_ref (driver); const FpIdEntry *entry; if (cls->type != FP_DEVICE_TYPE_USB) diff --git a/libfprint/fprint-list-udev-rules.c b/libfprint/fprint-list-udev-rules.c index ac507971..bd7c8a89 100644 --- a/libfprint/fprint-list-udev-rules.c +++ b/libfprint/fprint-list-udev-rules.c @@ -104,8 +104,7 @@ main (int argc, char **argv) for (i = 0; i < drivers->len; i++) { GType driver = g_array_index (drivers, GType, i); - g_autoptr(GTypeClass) type_class = g_type_class_ref (driver); - FpDeviceClass *cls = FP_DEVICE_CLASS (type_class); + g_autoptr(FpDeviceClass) cls = g_type_class_ref (driver); if (cls->type != FP_DEVICE_TYPE_USB) continue; diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c index 9f1da2ea..bfe383ba 100644 --- a/tests/test-fpi-device.c +++ b/tests/test-fpi-device.c @@ -64,13 +64,13 @@ static FpAutoResetClass default_fake_dev_class = {0}; static FpAutoResetClass * auto_reset_device_class (void) { - g_autoptr(GTypeClass) type_class = NULL; + g_autoptr(FpDeviceClass) type_class = NULL; FpDeviceClass *dev_class = g_type_class_peek_static (FPI_TYPE_DEVICE_FAKE); if (!dev_class) { type_class = g_type_class_ref (FPI_TYPE_DEVICE_FAKE); - dev_class = (FpDeviceClass *) type_class; + dev_class = type_class; g_assert_nonnull (dev_class); } From 5faf8498d96e58434ba0872510bce9289dae9d20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 30 Jan 2020 14:59:59 +0100 Subject: [PATCH 228/237] compat: Use new GDate autoptr and define if needed --- examples/storage.c | 4 ++-- libfprint/drivers/synaptics/synaptics.c | 3 +-- libfprint/fp-print.c | 4 +--- libfprint/fpi-compat.h | 1 + 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/examples/storage.c b/examples/storage.c index 53a61be0..ebffb33f 100644 --- a/examples/storage.c +++ b/examples/storage.c @@ -22,6 +22,7 @@ #define FP_COMPONENT "example-storage" #include +#include #include "storage.h" #include @@ -163,8 +164,8 @@ FpPrint * print_create_template (FpDevice *dev, FpFinger finger) { g_autoptr(GDateTime) datetime = NULL; + g_autoptr(GDate) date = NULL; FpPrint *template = NULL; - GDate *date = NULL; gint year, month, day; template = fp_print_new (dev); @@ -174,7 +175,6 @@ print_create_template (FpDevice *dev, FpFinger finger) g_date_time_get_ymd (datetime, &year, &month, &day); date = g_date_new_dmy (day, month, year); fp_print_set_enroll_date (template, date); - g_date_free (date); return template; } diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index 49bdfec2..8333fdae 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -520,8 +520,8 @@ list_msg_cb (FpiDeviceSynaptics *self, userid[12] == '-' && userid[14] == '-' && userid[23] == '-') { g_autofree gchar *copy = g_strdup (userid); + g_autoptr(GDate) date = NULL; gint32 date_ymd; - GDate *date = NULL; gint32 finger; gchar *username; /* Try to parse information from the string. */ @@ -536,7 +536,6 @@ list_msg_cb (FpiDeviceSynaptics *self, date = g_date_new (); fp_print_set_enroll_date (print, date); - g_date_free (date); copy[14] = '\0'; finger = g_ascii_strtoll (copy + 13, NULL, 16); diff --git a/libfprint/fp-print.c b/libfprint/fp-print.c index b17b2034..d02bf0a3 100644 --- a/libfprint/fp-print.c +++ b/libfprint/fp-print.c @@ -753,8 +753,8 @@ fp_print_deserialize (const guchar *data, g_autoptr(GVariant) raw_value = NULL; g_autoptr(GVariant) value = NULL; g_autoptr(GVariant) print_data = NULL; + g_autoptr(GDate) date = NULL; guchar *aligned_data = NULL; - GDate *date = NULL; guint8 finger_int8; FpFinger finger; g_autofree gchar *username = NULL; @@ -882,8 +882,6 @@ fp_print_deserialize (const guchar *data, "enroll_date", date, NULL); - g_date_free (date); - return g_steal_pointer (&result); invalid_format: diff --git a/libfprint/fpi-compat.h b/libfprint/fpi-compat.h index 0904ab2c..59025e14 100644 --- a/libfprint/fpi-compat.h +++ b/libfprint/fpi-compat.h @@ -35,4 +35,5 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (GParamSpec, g_param_spec_unref); #if !GLIB_CHECK_VERSION (2, 63, 3) typedef struct _FpDeviceClass FpDeviceClass; G_DEFINE_AUTOPTR_CLEANUP_FUNC (FpDeviceClass, g_type_class_unref); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (GDate, g_date_free); #endif From 5ac770c614659a13c846ee14ec48c679962e5762 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Wed, 15 Jan 2020 18:42:54 +0100 Subject: [PATCH 229/237] tests: Return skip error if import fails Rather than backtracing, just print the exception and return a skip error if the import fails. --- tests/virtual-image.py | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/tests/virtual-image.py b/tests/virtual-image.py index c30fad5e..9abca0bf 100755 --- a/tests/virtual-image.py +++ b/tests/virtual-image.py @@ -1,20 +1,24 @@ #!/usr/bin/env python3 - -import gi -gi.require_version('FPrint', '2.0') -from gi.repository import FPrint, GLib, Gio - -import os import sys -import unittest -import socket -import struct -import subprocess -import shutil -import glob -import cairo -import tempfile +try: + import gi + gi.require_version('FPrint', '2.0') + from gi.repository import FPrint, GLib, Gio + + import os + import sys + import unittest + import socket + import struct + import subprocess + import shutil + import glob + import cairo + import tempfile +except Exception as e: + print("Missing dependencies: %s" % str(e)) + sys.exit(77) # Re-run the test with the passed wrapper if set wrapper = os.getenv('LIBFPRINT_TEST_WRAPPER') From e19a1a655000f92ea419842ad2bb77fad22fcdaf Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Wed, 29 Jan 2020 15:34:40 +0100 Subject: [PATCH 230/237] meson: Create include directories in build tree Without this we get warnings like the following: cc1: warning: .../_build/libfprint/nbis/libfprint-include: No such file or directory [-Wmissing-include-dirs] --- libfprint/meson.build | 4 ++++ libfprint/nbis/include/meson.build | 0 libfprint/nbis/libfprint-include/meson.build | 0 3 files changed, 4 insertions(+) create mode 100644 libfprint/nbis/include/meson.build create mode 100644 libfprint/nbis/libfprint-include/meson.build diff --git a/libfprint/meson.build b/libfprint/meson.build index 4bd617fd..acb97c4d 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -209,6 +209,10 @@ deps = [ nss_dep, ] +# These are empty and only exist so that the include directories are created +# in the build tree. This silences a build time warning. +subdir('nbis/include') +subdir('nbis/libfprint-include') deps += declare_dependency(include_directories: [ root_inc, include_directories('nbis/include'), diff --git a/libfprint/nbis/include/meson.build b/libfprint/nbis/include/meson.build new file mode 100644 index 00000000..e69de29b diff --git a/libfprint/nbis/libfprint-include/meson.build b/libfprint/nbis/libfprint-include/meson.build new file mode 100644 index 00000000..e69de29b From ccd42bdecedf8b886fbee1d6b43b8d0bd4200eea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 30 Jan 2020 14:26:36 +0100 Subject: [PATCH 231/237] gitignore: Remove autotools ignores, add _build --- .gitignore | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index 20dcfdfc..07d73995 100644 --- a/.gitignore +++ b/.gitignore @@ -1,24 +1,3 @@ -ltmain.sh -missing -stamp-h1 -libtool -*.la -*.lo *.o *.swp -Makefile -Makefile.in -config.h* -aclocal.m4 -autom4te.cache -config.guess -config.log -config.status -config.sub -configure -depcomp -install-sh -.deps -.libs -compile -ChangeLog +_build From 82ba69b1df4121a1e3fc400e126d13baf6c14c2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 30 Jan 2020 15:28:27 +0100 Subject: [PATCH 232/237] meson: Depends on gusb 0.2.0, but only enable tests on 0.3.0 --- meson.build | 2 +- tests/meson.build | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 1f83e96f..303ab5fa 100644 --- a/meson.build +++ b/meson.build @@ -80,7 +80,7 @@ versioned_libname = meson.project_name() + '-' + soversion.to_string() # Dependencies glib_dep = dependency('glib-2.0', version: '>=' + glib_min_version) gio_dep = dependency('gio-unix-2.0', version: '>=' + glib_min_version) -gusb_dep = dependency('gusb', version: '>= 0.3.0') +gusb_dep = dependency('gusb', version: '>= 0.2.0') mathlib_dep = cc.find_library('m', required: false) # The following dependencies are only used for tests diff --git a/tests/meson.build b/tests/meson.build index 912b5001..99b9bcb5 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -43,7 +43,7 @@ if get_option('introspection') driver_envs = envs driver_envs.set('FP_DRIVERS_WHITELIST', driver_test) - if driver_test in drivers + if driver_test in drivers and gusb_dep.version().version_compare('>= 0.3.0') test(driver_test, find_program('umockdev-test.py'), args: join_paths(meson.current_source_dir(), driver_test), From 15a90eb451249e550f2ee35fe4f61f3cc375651c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 30 Jan 2020 15:53:40 +0100 Subject: [PATCH 233/237] meson: Use gnu99 as default C standard --- meson.build | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 303ab5fa..4d2ac25a 100644 --- a/meson.build +++ b/meson.build @@ -4,7 +4,7 @@ project('libfprint', [ 'c', 'cpp' ], default_options: [ 'buildtype=debugoptimized', 'warning_level=1', - 'c_std=c99', + 'c_std=gnu99', ], meson_version: '>= 0.49.0') @@ -56,7 +56,6 @@ common_cflags = cc.get_supported_arguments([ '-DG_LOG_DOMAIN="@0@"'.format(meson.project_name()), ]) c_cflags = cc.get_supported_arguments([ - '-std=gnu99', '-Wimplicit-function-declaration', '-Wmissing-prototypes', '-Wnested-externs', From 355cae1bbdbf958d82ad6788f037ade224bd1270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Sat, 8 Feb 2020 13:38:18 +0100 Subject: [PATCH 234/237] fp-device: Return error if trying to list a storage-less device Devices with no storage don't allow listing prints, and if we try to do that, we'd end up in trying to call a NULL function pointer, causing a crash So always check if the device has storage before calling the list vfunc, and if we fail, return an error. Include an unit-test to verify this situation --- libfprint/fp-device.c | 8 ++++++++ tests/test-fpi-device.c | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/libfprint/fp-device.c b/libfprint/fp-device.c index 40b3efa2..fe36eae3 100644 --- a/libfprint/fp-device.c +++ b/libfprint/fp-device.c @@ -1213,6 +1213,14 @@ fp_device_list_prints (FpDevice *device, return; } + if (!fp_device_has_storage (device)) + { + g_task_return_error (task, + fpi_device_error_new_msg (FP_DEVICE_ERROR_NOT_SUPPORTED, + "Device has no storage")); + return; + } + priv->current_action = FPI_DEVICE_ACTION_LIST; priv->current_task = g_steal_pointer (&task); maybe_cancel_on_cancelled (device, cancellable); diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c index bfe383ba..0e66c2ef 100644 --- a/tests/test-fpi-device.c +++ b/tests/test-fpi-device.c @@ -1527,6 +1527,24 @@ test_driver_list_error (void) g_assert_null (prints); } +static void +test_driver_list_no_storage (void) +{ + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(GPtrArray) prints = NULL; + g_autoptr(GError) error = NULL; + + dev_class->list = NULL; + + device = auto_close_fake_device_new (); + g_assert_false (fp_device_has_storage (device)); + + prints = fp_device_list_prints_sync (device, NULL, &error); + g_assert_null (prints); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_NOT_SUPPORTED); +} + static void test_driver_delete (void) { @@ -2210,6 +2228,7 @@ main (int argc, char *argv[]) g_test_add_func ("/driver/capture/error", test_driver_capture_error); g_test_add_func ("/driver/list", test_driver_list); g_test_add_func ("/driver/list/error", test_driver_list_error); + g_test_add_func ("/driver/list/no_storage", test_driver_list_no_storage); g_test_add_func ("/driver/delete", test_driver_delete); g_test_add_func ("/driver/delete/error", test_driver_delete_error); g_test_add_func ("/driver/cancel", test_driver_cancel); From 6eb06697e9dd26f4f37900251a7a6933b775a922 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Sat, 8 Feb 2020 12:42:17 +0100 Subject: [PATCH 235/237] tests/virtual-image: Use introspection names for errors --- tests/virtual-image.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/virtual-image.py b/tests/virtual-image.py index 9abca0bf..2440c6dd 100755 --- a/tests/virtual-image.py +++ b/tests/virtual-image.py @@ -101,14 +101,12 @@ class VirtualImage(unittest.TestCase): del self.con self.dev.close_sync() - def send_retry(self, retry_error=1, iterate=True): - # The default (1) is too-short + def send_retry(self, retry_error=FPrint.DeviceRetry.TOO_SHORT, iterate=True): self.con.sendall(struct.pack('ii', -1, retry_error)) while iterate and ctx.pending(): ctx.iteration(False) - def send_error(self, device_error=0, iterate=True): - # The default (0) is a generic error + def send_error(self, device_error=FPrint.DeviceError.GENERAL, iterate=True): self.con.sendall(struct.pack('ii', -2, device_error)) while iterate and ctx.pending(): ctx.iteration(False) From 0bb8ad1313e6d04a130b53fa20e5ba3961e2f6fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Sat, 8 Feb 2020 12:57:07 +0100 Subject: [PATCH 236/237] tests: Make meson be aware of the single python unit tests Scan for the unit tests in virtual-image suite and handle them individually --- tests/meson.build | 35 +++++++++++++++++++++++----- tests/unittest_inspector.py | 46 +++++++++++++++++++++++++++++++++++++ tests/virtual-image.py | 6 ++--- 3 files changed, 78 insertions(+), 9 deletions(-) create mode 100755 tests/unittest_inspector.py diff --git a/tests/meson.build b/tests/meson.build index 99b9bcb5..cce5c041 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -26,12 +26,35 @@ if get_option('introspection') envs.prepend('GI_TYPELIB_PATH', join_paths(meson.build_root(), 'libfprint')) if 'virtual_image' in drivers - test('virtual-image', - find_program('virtual-image.py'), - args: '--verbose', - env: envs, - depends: libfprint_typelib, - ) + python3 = find_program('python3') + unittest_inspector = find_program('unittest_inspector.py') + base_args = files('virtual-image.py') + suite = [] + + r = run_command(unittest_inspector, files('virtual-image.py')) + unit_tests = r.stdout().strip().split('\n') + + if r.returncode() == 0 and unit_tests.length() > 0 + suite += 'virtual-image' + else + unit_tests = ['virtual-image'] + endif + + foreach ut: unit_tests + ut_suite = suite + ut_args = base_args + if unit_tests.length() > 1 + ut_args += ut + ut_suite += ut.split('.')[0] + endif + test(ut, + python3, + args: ut_args, + suite: ut_suite, + depends: libfprint_typelib, + env: envs, + ) + endforeach else test('virtual-image', find_program('sh'), diff --git a/tests/unittest_inspector.py b/tests/unittest_inspector.py new file mode 100755 index 00000000..0d5d3a6f --- /dev/null +++ b/tests/unittest_inspector.py @@ -0,0 +1,46 @@ +#! /usr/bin/env python3 +# Copyright © 2020, Canonical Ltd +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see . +# Authors: +# Marco Trevisan + +import argparse +import importlib.util +import inspect +import os +import unittest + +def list_tests(module): + tests = [] + for name, obj in inspect.getmembers(module): + if inspect.isclass(obj) and issubclass(obj, unittest.TestCase): + cases = unittest.defaultTestLoader.getTestCaseNames(obj) + tests += [ (obj, '{}.{}'.format(name, t)) for t in cases ] + return tests + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('unittest_source', type=argparse.FileType('r')) + + args = parser.parse_args() + source_path = args.unittest_source.name + spec = importlib.util.spec_from_file_location( + os.path.basename(source_path), source_path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + + for machine, human in list_tests(module): + print(human) diff --git a/tests/virtual-image.py b/tests/virtual-image.py index 2440c6dd..b6cad95a 100755 --- a/tests/virtual-image.py +++ b/tests/virtual-image.py @@ -345,6 +345,6 @@ class VirtualImage(unittest.TestCase): ctx.iteration(True) assert(not self._verify_match) -# avoid writing to stderr -unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2)) - +if __name__ == '__main__': + # avoid writing to stderr + unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2)) From 66c9e4a829a06a25d8b6160cdfbad1d47ef5b81a Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 13 Jan 2020 15:50:16 +0100 Subject: [PATCH 237/237] Update for 1.90.1 release --- NEWS | 35 +++++++++++++++++++++++++++++++++++ meson.build | 2 +- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index f3fa3547..e7a2b668 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,41 @@ This file lists notable changes in each release. For the full history of all changes, see ChangeLog. +2019-11-20: v1.90.1 release + +This release fixes a lot of the regressions introduced in 1.90.0. Please note +that both the driver and external APIs have changed, as both the verify and +the identify functions now have early reporting mechanisms. +The soname for the library, as well as a number of file locations have also +changed. While this allows installation in parallel with the 1.0 version of +libfprint, we recommend installing only one, and migrating from version 1.0 to +version 2.0 alongside its main consumer (fprintd). + +Only major changes are listed below. A lot of other cleanup work and small +fixes have also been merged. + +* Library: + - Add support to run tests in gdb/valgrind + - Allow testing on all architectures + - Avoid image device AWAIT_FINGER_ON to deactivate state transitions + - Fix verify/identify error propagation to library user + - Correctly read image device information from class data + - Continue enroll after an image driver reported a retry error + - Change external API to allow reporting match results early + - A lot of new unit tests and integration tests have been added + +* Drivers API + - Support variadic arguments in error functions + - Various re-definitions of ownership handling + - Add convenience API to change state after a timeout + - Add unit tests for all the drivers API + +* Drivers: + - elan: Ensure correct deactivation of device + - uru4000: Fix IRQ handler registration and internal state handling + - uru4000: Fix control transfer request type + - synaptics: Ensure errors are only reported after finger removal + 2019-11-20: v1.90.0 release This release updates the core of the library to use GLib routines and Gio diff --git a/meson.build b/meson.build index 4d2ac25a..d4248d80 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('libfprint', [ 'c', 'cpp' ], - version: '1.90.0', + version: '1.90.1', license: 'LGPLv2.1+', default_options: [ 'buildtype=debugoptimized',