From 7172076650b639a471c97632f83c3e6431c3b2bd Mon Sep 17 00:00:00 2001 From: Indy970 Date: Sun, 10 Feb 2019 15:32:15 +0200 Subject: [PATCH] First Commit --- QtHPConnect.pro | 34 ++ data.cpp | 6 + data.h | 11 + datamodel.cpp | 6 + datamodel.h | 11 + errorhandler.cpp | 6 + errorhandler.h | 12 + getnumber.cpp | 14 + getnumber.h | 22 + getnumber.ui | 67 +++ global.h | 4 + hidapi.h/hidapi.h | 391 ++++++++++++++ hidapi/hidapi.h | 391 ++++++++++++++ hp_infodialog.cpp | 14 + hp_infodialog.h | 22 + hp_infodialog.ui | 17 + hp_mditexteditor.cpp | 6 + hp_mditexteditor.h | 13 + hp_mdivariableedit.cpp | 6 + hp_mdivariableedit.h | 18 + hp_mdiwindow.cpp | 6 + hp_mdiwindow.h | 13 + hp_mdiwindow.ui | 25 + hpdata.cpp | 6 + hpdata.h | 12 + hpinterface.h | 4 + hptoolbox.cpp | 14 + hptoolbox.h | 22 + hptreeitem.cpp | 6 + hptreeitem.h | 11 + hpusb.cpp | 6 + hpusb.h | 11 + icons/1_Prime_compact.primeskin | 246 +++++++++ icons/2_Prime_compact_L.primeskin | 246 +++++++++ icons/3_Prime_small.primeskin | 246 +++++++++ icons/4_Prime_med.primeskin | 246 +++++++++ icons/5_Prime_large_L.primeskin | 246 +++++++++ icons/about_16x16.png | Bin 0 -> 659 bytes icons/about_32x32.png | Bin 0 -> 1576 bytes icons/accessories-calculator.png | Bin 0 -> 1212 bytes icons/add_background_16x16.png | Bin 0 -> 790 bytes icons/add_background_22x22.png | Bin 0 -> 1135 bytes icons/add_background_32x32.png | Bin 0 -> 2004 bytes icons/add_icon_16x16.png | Bin 0 -> 707 bytes icons/add_icon_22x22.png | Bin 0 -> 838 bytes icons/add_icon_32x32.png | Bin 0 -> 1385 bytes icons/add_new_16x16.png | Bin 0 -> 487 bytes icons/add_new_22x22.png | Bin 0 -> 781 bytes icons/add_new_32x32.png | Bin 0 -> 1320 bytes icons/apps_16x16.png | Bin 0 -> 188 bytes icons/apps_32x32.png | Bin 0 -> 393 bytes icons/backup_16x16.png | Bin 0 -> 419 bytes icons/backup_32x32.png | Bin 0 -> 1651 bytes icons/calc_tree_16x16.png | Bin 0 -> 676 bytes icons/calc_tree_22x22.png | Bin 0 -> 888 bytes icons/calc_tree_32x32.png | Bin 0 -> 1082 bytes icons/casFolder_16x16.png | Bin 0 -> 449 bytes icons/casFolder_32x32.png | Bin 0 -> 1076 bytes icons/casVars_16x16.png | Bin 0 -> 561 bytes icons/casVars_32x32.png | Bin 0 -> 1150 bytes icons/chars_16x16.png | Bin 0 -> 262 bytes icons/chars_32x32.png | Bin 0 -> 1291 bytes icons/chat_16x16.png | Bin 0 -> 707 bytes icons/chat_22x22.png | Bin 0 -> 988 bytes icons/chat_32x32.png | Bin 0 -> 1616 bytes icons/check_update_16x16.png | Bin 0 -> 731 bytes icons/check_update_32x32.png | Bin 0 -> 2234 bytes icons/clear_16x16.png | Bin 0 -> 694 bytes icons/clear_32x32.png | Bin 0 -> 2002 bytes icons/clone_from_16x16.png | Bin 0 -> 694 bytes icons/clone_from_32x32.png | Bin 0 -> 1919 bytes icons/clone_to_16x16.png | Bin 0 -> 660 bytes icons/clone_to_32x32.png | Bin 0 -> 1775 bytes icons/complex_16x16.png | Bin 0 -> 151 bytes icons/complex_32x32.png | Bin 0 -> 326 bytes icons/content_16x16.png | Bin 0 -> 662 bytes icons/content_22x22.png | Bin 0 -> 962 bytes icons/content_32x32.png | Bin 0 -> 1472 bytes icons/copy_16x16.png | Bin 0 -> 409 bytes icons/copy_32x32.png | Bin 0 -> 816 bytes icons/cut_16x16.png | Bin 0 -> 282 bytes icons/cut_32x32.png | Bin 0 -> 781 bytes icons/delete_16x16.png | Bin 0 -> 509 bytes icons/delete_32x32.png | Bin 0 -> 1266 bytes icons/delete_col_16x16.png | Bin 0 -> 636 bytes icons/delete_col_32x32.png | Bin 0 -> 1487 bytes icons/delete_row_16x16.png | Bin 0 -> 550 bytes icons/delete_row_32x32.png | Bin 0 -> 1360 bytes icons/exam_mode_16x16.png | Bin 0 -> 445 bytes icons/exam_mode_32x32.png | Bin 0 -> 978 bytes icons/fileFolder_16x16.png | Bin 0 -> 455 bytes icons/fileFolder_32x32.png | Bin 0 -> 1191 bytes icons/file_16x16.png | Bin 0 -> 478 bytes icons/file_32x32.png | Bin 0 -> 1189 bytes icons/firmware_16x16.png | Bin 0 -> 818 bytes icons/firmware_32x32.png | Bin 0 -> 2440 bytes icons/help_16x16.png | Bin 0 -> 761 bytes icons/help_32x32.png | Bin 0 -> 1999 bytes icons/insert_col_16x16.png | Bin 0 -> 745 bytes icons/insert_col_32x32.png | Bin 0 -> 1702 bytes icons/insert_row_16x16.png | Bin 0 -> 682 bytes icons/insert_row_32x32.png | Bin 0 -> 1498 bytes icons/internet_16x16.png | Bin 0 -> 859 bytes icons/internet_32x32.png | Bin 0 -> 2605 bytes icons/language_16x16.png | Bin 0 -> 710 bytes icons/language_32x32.png | Bin 0 -> 2333 bytes icons/list_16x16.png | Bin 0 -> 161 bytes icons/list_32x32.png | Bin 0 -> 350 bytes icons/monitor_16x16.png | Bin 0 -> 452 bytes icons/monitor_22x22.png | Bin 0 -> 710 bytes icons/monitor_32x32.png | Bin 0 -> 922 bytes icons/new_folder_16x16.png | Bin 0 -> 651 bytes icons/new_folder_22x22.png | Bin 0 -> 957 bytes icons/new_folder_32x32.png | Bin 0 -> 1624 bytes icons/note_16x16.png | Bin 0 -> 231 bytes icons/note_32x32.png | Bin 0 -> 271 bytes icons/open_16x16.png | Bin 0 -> 620 bytes icons/open_32x32.png | Bin 0 -> 1665 bytes icons/paste_16x16.png | Bin 0 -> 541 bytes icons/paste_32x32.png | Bin 0 -> 1211 bytes icons/poll_16x16.png | Bin 0 -> 641 bytes icons/poll_32x32.png | Bin 0 -> 1701 bytes icons/preferences_16x16.png | Bin 0 -> 684 bytes icons/preferences_32x32.png | Bin 0 -> 2038 bytes icons/proctor_16x16.png | Bin 0 -> 762 bytes icons/proctor_22x22.png | Bin 0 -> 1111 bytes icons/proctor_32x32.png | Bin 0 -> 1823 bytes icons/program_16x16.png | Bin 0 -> 224 bytes icons/program_32x32.png | Bin 0 -> 281 bytes icons/project_16x16.png | Bin 0 -> 632 bytes icons/project_32x32.png | Bin 0 -> 1538 bytes icons/properties_16x16.png | Bin 0 -> 186 bytes icons/properties_32x32.png | Bin 0 -> 366 bytes icons/real_16x16.png | Bin 0 -> 131 bytes icons/real_32x32.png | Bin 0 -> 256 bytes icons/refresh_16x16.png | Bin 0 -> 822 bytes icons/refresh_32x32.png | Bin 0 -> 2035 bytes icons/rename_16x16.png | Bin 0 -> 165 bytes icons/rename_32x32.png | Bin 0 -> 329 bytes icons/reset_16x16.png | Bin 0 -> 690 bytes icons/reset_32x32.png | Bin 0 -> 1582 bytes icons/restore_16x16.png | Bin 0 -> 692 bytes icons/restore_32x32.png | Bin 0 -> 1854 bytes icons/results_16x16.png | Bin 0 -> 623 bytes icons/results_32x32.png | Bin 0 -> 1597 bytes icons/save_16x16.png | Bin 0 -> 482 bytes icons/save_22x22.png | Bin 0 -> 710 bytes icons/save_32x32.png | Bin 0 -> 1187 bytes icons/save_all_16x16.png | Bin 0 -> 536 bytes icons/save_all_22x22.png | Bin 0 -> 834 bytes icons/save_all_32x32.png | Bin 0 -> 1270 bytes icons/save_as_16x16.png | Bin 0 -> 747 bytes icons/save_as_32x32.png | Bin 0 -> 2056 bytes icons/screenshot_16x16.png | Bin 0 -> 290 bytes icons/screenshot_32x32.png | Bin 0 -> 1099 bytes icons/send_16x16.png | Bin 0 -> 599 bytes icons/send_32x32.png | Bin 0 -> 1218 bytes icons/start_16x16.png | Bin 0 -> 1863 bytes icons/start_32x32.png | Bin 0 -> 6491 bytes icons/stop_16x16.png | Bin 0 -> 585 bytes icons/stop_32x32.png | Bin 0 -> 1618 bytes icons/table_16x16.png | Bin 0 -> 231 bytes icons/table_32x32.png | Bin 0 -> 336 bytes icons/varFolder_16x16.png | Bin 0 -> 485 bytes icons/varFolder_32x32.png | Bin 0 -> 1405 bytes icons/vars_16x16.png | Bin 0 -> 559 bytes icons/vars_32x32.png | Bin 0 -> 1138 bytes input.txt | 1 + libhpcalcs/AUTHORS | 8 + libhpcalcs/COPYING | 340 ++++++++++++ libhpcalcs/ChangeLog | 3 + libhpcalcs/NEWS | 0 libhpcalcs/README | 315 +++++++++++ libhpcalcs/include/error.h | 71 +++ libhpcalcs/include/export.h | 91 ++++ libhpcalcs/include/filetypes.h | 35 ++ libhpcalcs/include/gettext.h | 60 +++ libhpcalcs/include/hpcables.h | 249 +++++++++ libhpcalcs/include/hpcalcs.h | 424 +++++++++++++++ libhpcalcs/include/hpfiles.h | 283 ++++++++++ libhpcalcs/include/hplibs.h | 93 ++++ libhpcalcs/include/hpopers.h | 180 +++++++ libhpcalcs/include/internal.h | 32 ++ libhpcalcs/include/logging.h | 56 ++ libhpcalcs/include/prime_cmd.h | 73 +++ libhpcalcs/include/typesprime.h | 69 +++ libhpcalcs/include/utils.h | Bin 0 -> 3014 bytes libhpcalcs/src/calc_none.c | 95 ++++ libhpcalcs/src/calc_prime.c | 226 ++++++++ libhpcalcs/src/error.c | 256 +++++++++ libhpcalcs/src/error.h | 71 +++ libhpcalcs/src/export.h | 91 ++++ libhpcalcs/src/filetypes.c | 112 ++++ libhpcalcs/src/filetypes.h | 35 ++ libhpcalcs/src/gettext.h | 60 +++ libhpcalcs/src/hpcables.c | 537 +++++++++++++++++++ libhpcalcs/src/hpcables.h | 249 +++++++++ libhpcalcs/src/hpcalcs.c | 674 ++++++++++++++++++++++++ libhpcalcs/src/hpcalcs.h | 424 +++++++++++++++ libhpcalcs/src/hpfiles.c | 378 +++++++++++++ libhpcalcs/src/hpfiles.h | 283 ++++++++++ libhpcalcs/src/hplibs.h | 93 ++++ libhpcalcs/src/hpopers.c | 126 +++++ libhpcalcs/src/hpopers.h | 180 +++++++ libhpcalcs/src/internal.h | 32 ++ libhpcalcs/src/link_nul.c | 78 +++ libhpcalcs/src/link_prime_hid.c | 240 +++++++++ libhpcalcs/src/logging.c | 179 +++++++ libhpcalcs/src/logging.h | 56 ++ libhpcalcs/src/prime_cmd.c | 787 ++++++++++++++++++++++++++++ libhpcalcs/src/prime_cmd.h | 73 +++ libhpcalcs/src/prime_rpkt.c | 103 ++++ libhpcalcs/src/prime_vpkt.c | 278 ++++++++++ libhpcalcs/src/type2str.c | 83 +++ libhpcalcs/src/typesprime.c | 192 +++++++ libhpcalcs/src/typesprime.h | 69 +++ libhpcalcs/src/utils.c | 114 ++++ libhpcalcs/src/utils.h | 38 ++ libhpcalcs/tests/test_hpcalcs.c | 816 +++++++++++++++++++++++++++++ libhpcalcs/tests/torture_hpcalcs.c | 41 ++ main.cpp | 6 + main.h | 11 + mainwindow.cpp | 14 + mainwindow.h | 22 + mainwindow.ui | 24 + model.qmodel | 55 ++ qthpconnect.qrc | 2 + texteditor.cpp | 14 + texteditor.h | 22 + treemodel.cpp | 6 + treemodel.h | 12 + variableview.cpp | 14 + variableview.h | 22 + variableview.ui | 22 + vartablemodel.cpp | 6 + vartablemodel.h | 11 + 236 files changed, 12152 insertions(+) create mode 100644 QtHPConnect.pro create mode 100644 data.cpp create mode 100644 data.h create mode 100644 datamodel.cpp create mode 100644 datamodel.h create mode 100644 errorhandler.cpp create mode 100644 errorhandler.h create mode 100644 getnumber.cpp create mode 100644 getnumber.h create mode 100644 getnumber.ui create mode 100644 global.h create mode 100644 hidapi.h/hidapi.h create mode 100644 hidapi/hidapi.h create mode 100644 hp_infodialog.cpp create mode 100644 hp_infodialog.h create mode 100644 hp_infodialog.ui create mode 100644 hp_mditexteditor.cpp create mode 100644 hp_mditexteditor.h create mode 100644 hp_mdivariableedit.cpp create mode 100644 hp_mdivariableedit.h create mode 100644 hp_mdiwindow.cpp create mode 100644 hp_mdiwindow.h create mode 100644 hp_mdiwindow.ui create mode 100644 hpdata.cpp create mode 100644 hpdata.h create mode 100644 hpinterface.h create mode 100644 hptoolbox.cpp create mode 100644 hptoolbox.h create mode 100644 hptreeitem.cpp create mode 100644 hptreeitem.h create mode 100644 hpusb.cpp create mode 100644 hpusb.h create mode 100644 icons/1_Prime_compact.primeskin create mode 100644 icons/2_Prime_compact_L.primeskin create mode 100644 icons/3_Prime_small.primeskin create mode 100644 icons/4_Prime_med.primeskin create mode 100644 icons/5_Prime_large_L.primeskin create mode 100644 icons/about_16x16.png create mode 100644 icons/about_32x32.png create mode 100644 icons/accessories-calculator.png create mode 100644 icons/add_background_16x16.png create mode 100644 icons/add_background_22x22.png create mode 100644 icons/add_background_32x32.png create mode 100644 icons/add_icon_16x16.png create mode 100644 icons/add_icon_22x22.png create mode 100644 icons/add_icon_32x32.png create mode 100644 icons/add_new_16x16.png create mode 100644 icons/add_new_22x22.png create mode 100644 icons/add_new_32x32.png create mode 100644 icons/apps_16x16.png create mode 100644 icons/apps_32x32.png create mode 100644 icons/backup_16x16.png create mode 100644 icons/backup_32x32.png create mode 100644 icons/calc_tree_16x16.png create mode 100644 icons/calc_tree_22x22.png create mode 100644 icons/calc_tree_32x32.png create mode 100644 icons/casFolder_16x16.png create mode 100644 icons/casFolder_32x32.png create mode 100644 icons/casVars_16x16.png create mode 100644 icons/casVars_32x32.png create mode 100644 icons/chars_16x16.png create mode 100644 icons/chars_32x32.png create mode 100644 icons/chat_16x16.png create mode 100644 icons/chat_22x22.png create mode 100644 icons/chat_32x32.png create mode 100644 icons/check_update_16x16.png create mode 100644 icons/check_update_32x32.png create mode 100644 icons/clear_16x16.png create mode 100644 icons/clear_32x32.png create mode 100644 icons/clone_from_16x16.png create mode 100644 icons/clone_from_32x32.png create mode 100644 icons/clone_to_16x16.png create mode 100644 icons/clone_to_32x32.png create mode 100644 icons/complex_16x16.png create mode 100644 icons/complex_32x32.png create mode 100644 icons/content_16x16.png create mode 100644 icons/content_22x22.png create mode 100644 icons/content_32x32.png create mode 100644 icons/copy_16x16.png create mode 100644 icons/copy_32x32.png create mode 100644 icons/cut_16x16.png create mode 100644 icons/cut_32x32.png create mode 100644 icons/delete_16x16.png create mode 100644 icons/delete_32x32.png create mode 100644 icons/delete_col_16x16.png create mode 100644 icons/delete_col_32x32.png create mode 100644 icons/delete_row_16x16.png create mode 100644 icons/delete_row_32x32.png create mode 100644 icons/exam_mode_16x16.png create mode 100644 icons/exam_mode_32x32.png create mode 100644 icons/fileFolder_16x16.png create mode 100644 icons/fileFolder_32x32.png create mode 100644 icons/file_16x16.png create mode 100644 icons/file_32x32.png create mode 100644 icons/firmware_16x16.png create mode 100644 icons/firmware_32x32.png create mode 100644 icons/help_16x16.png create mode 100644 icons/help_32x32.png create mode 100644 icons/insert_col_16x16.png create mode 100644 icons/insert_col_32x32.png create mode 100644 icons/insert_row_16x16.png create mode 100644 icons/insert_row_32x32.png create mode 100644 icons/internet_16x16.png create mode 100644 icons/internet_32x32.png create mode 100644 icons/language_16x16.png create mode 100644 icons/language_32x32.png create mode 100644 icons/list_16x16.png create mode 100644 icons/list_32x32.png create mode 100644 icons/monitor_16x16.png create mode 100644 icons/monitor_22x22.png create mode 100644 icons/monitor_32x32.png create mode 100644 icons/new_folder_16x16.png create mode 100644 icons/new_folder_22x22.png create mode 100644 icons/new_folder_32x32.png create mode 100644 icons/note_16x16.png create mode 100644 icons/note_32x32.png create mode 100644 icons/open_16x16.png create mode 100644 icons/open_32x32.png create mode 100644 icons/paste_16x16.png create mode 100644 icons/paste_32x32.png create mode 100644 icons/poll_16x16.png create mode 100644 icons/poll_32x32.png create mode 100644 icons/preferences_16x16.png create mode 100644 icons/preferences_32x32.png create mode 100644 icons/proctor_16x16.png create mode 100644 icons/proctor_22x22.png create mode 100644 icons/proctor_32x32.png create mode 100644 icons/program_16x16.png create mode 100644 icons/program_32x32.png create mode 100644 icons/project_16x16.png create mode 100644 icons/project_32x32.png create mode 100644 icons/properties_16x16.png create mode 100644 icons/properties_32x32.png create mode 100644 icons/real_16x16.png create mode 100644 icons/real_32x32.png create mode 100644 icons/refresh_16x16.png create mode 100644 icons/refresh_32x32.png create mode 100644 icons/rename_16x16.png create mode 100644 icons/rename_32x32.png create mode 100644 icons/reset_16x16.png create mode 100644 icons/reset_32x32.png create mode 100644 icons/restore_16x16.png create mode 100644 icons/restore_32x32.png create mode 100644 icons/results_16x16.png create mode 100644 icons/results_32x32.png create mode 100644 icons/save_16x16.png create mode 100644 icons/save_22x22.png create mode 100644 icons/save_32x32.png create mode 100644 icons/save_all_16x16.png create mode 100644 icons/save_all_22x22.png create mode 100644 icons/save_all_32x32.png create mode 100644 icons/save_as_16x16.png create mode 100644 icons/save_as_32x32.png create mode 100644 icons/screenshot_16x16.png create mode 100644 icons/screenshot_32x32.png create mode 100644 icons/send_16x16.png create mode 100644 icons/send_32x32.png create mode 100644 icons/start_16x16.png create mode 100644 icons/start_32x32.png create mode 100644 icons/stop_16x16.png create mode 100644 icons/stop_32x32.png create mode 100644 icons/table_16x16.png create mode 100644 icons/table_32x32.png create mode 100644 icons/varFolder_16x16.png create mode 100644 icons/varFolder_32x32.png create mode 100644 icons/vars_16x16.png create mode 100644 icons/vars_32x32.png create mode 100644 input.txt create mode 100644 libhpcalcs/AUTHORS create mode 100644 libhpcalcs/COPYING create mode 100644 libhpcalcs/ChangeLog create mode 100644 libhpcalcs/NEWS create mode 100644 libhpcalcs/README create mode 100644 libhpcalcs/include/error.h create mode 100644 libhpcalcs/include/export.h create mode 100644 libhpcalcs/include/filetypes.h create mode 100644 libhpcalcs/include/gettext.h create mode 100644 libhpcalcs/include/hpcables.h create mode 100644 libhpcalcs/include/hpcalcs.h create mode 100644 libhpcalcs/include/hpfiles.h create mode 100644 libhpcalcs/include/hplibs.h create mode 100644 libhpcalcs/include/hpopers.h create mode 100644 libhpcalcs/include/internal.h create mode 100644 libhpcalcs/include/logging.h create mode 100644 libhpcalcs/include/prime_cmd.h create mode 100644 libhpcalcs/include/typesprime.h create mode 100644 libhpcalcs/include/utils.h create mode 100644 libhpcalcs/src/calc_none.c create mode 100644 libhpcalcs/src/calc_prime.c create mode 100644 libhpcalcs/src/error.c create mode 100644 libhpcalcs/src/error.h create mode 100644 libhpcalcs/src/export.h create mode 100644 libhpcalcs/src/filetypes.c create mode 100644 libhpcalcs/src/filetypes.h create mode 100644 libhpcalcs/src/gettext.h create mode 100644 libhpcalcs/src/hpcables.c create mode 100644 libhpcalcs/src/hpcables.h create mode 100644 libhpcalcs/src/hpcalcs.c create mode 100644 libhpcalcs/src/hpcalcs.h create mode 100644 libhpcalcs/src/hpfiles.c create mode 100644 libhpcalcs/src/hpfiles.h create mode 100644 libhpcalcs/src/hplibs.h create mode 100644 libhpcalcs/src/hpopers.c create mode 100644 libhpcalcs/src/hpopers.h create mode 100644 libhpcalcs/src/internal.h create mode 100644 libhpcalcs/src/link_nul.c create mode 100644 libhpcalcs/src/link_prime_hid.c create mode 100644 libhpcalcs/src/logging.c create mode 100644 libhpcalcs/src/logging.h create mode 100644 libhpcalcs/src/prime_cmd.c create mode 100644 libhpcalcs/src/prime_cmd.h create mode 100644 libhpcalcs/src/prime_rpkt.c create mode 100644 libhpcalcs/src/prime_vpkt.c create mode 100644 libhpcalcs/src/type2str.c create mode 100644 libhpcalcs/src/typesprime.c create mode 100644 libhpcalcs/src/typesprime.h create mode 100644 libhpcalcs/src/utils.c create mode 100644 libhpcalcs/src/utils.h create mode 100644 libhpcalcs/tests/test_hpcalcs.c create mode 100644 libhpcalcs/tests/torture_hpcalcs.c create mode 100644 main.cpp create mode 100644 main.h create mode 100644 mainwindow.cpp create mode 100644 mainwindow.h create mode 100644 mainwindow.ui create mode 100644 model.qmodel create mode 100644 qthpconnect.qrc create mode 100644 texteditor.cpp create mode 100644 texteditor.h create mode 100644 treemodel.cpp create mode 100644 treemodel.h create mode 100644 variableview.cpp create mode 100644 variableview.h create mode 100644 variableview.ui create mode 100644 vartablemodel.cpp create mode 100644 vartablemodel.h diff --git a/QtHPConnect.pro b/QtHPConnect.pro new file mode 100644 index 0000000..a98fcd0 --- /dev/null +++ b/QtHPConnect.pro @@ -0,0 +1,34 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2019-01-21T20:42:03 +# +#------------------------------------------------- + +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = QtHPConnect +TEMPLATE = app + +# The following define makes your compiler emit warnings if you use +# any feature of Qt which has been marked as deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if you use deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + + +SOURCES += \ + main.cpp \ + mainwindow.cpp + +HEADERS += \ + mainwindow.h + +FORMS += \ + mainwindow.ui diff --git a/data.cpp b/data.cpp new file mode 100644 index 0000000..02f9465 --- /dev/null +++ b/data.cpp @@ -0,0 +1,6 @@ +#include "data.h" + +data::data() +{ + +} diff --git a/data.h b/data.h new file mode 100644 index 0000000..63f2ed6 --- /dev/null +++ b/data.h @@ -0,0 +1,11 @@ +#ifndef DATA_H +#define DATA_H + + +class data +{ +public: + data(); +}; + +#endif // DATA_H \ No newline at end of file diff --git a/datamodel.cpp b/datamodel.cpp new file mode 100644 index 0000000..1a9dd2c --- /dev/null +++ b/datamodel.cpp @@ -0,0 +1,6 @@ +#include "datamodel.h" + +dataModel::dataModel() +{ + +} diff --git a/datamodel.h b/datamodel.h new file mode 100644 index 0000000..c783851 --- /dev/null +++ b/datamodel.h @@ -0,0 +1,11 @@ +#ifndef DATAMODEL_H +#define DATAMODEL_H + + +class dataModel +{ +public: + dataModel(); +}; + +#endif // DATAMODEL_H \ No newline at end of file diff --git a/errorhandler.cpp b/errorhandler.cpp new file mode 100644 index 0000000..88f5800 --- /dev/null +++ b/errorhandler.cpp @@ -0,0 +1,6 @@ +#include "errorhandler.h" + +errorHandler::errorHandler() +{ + +} diff --git a/errorhandler.h b/errorhandler.h new file mode 100644 index 0000000..33d3edd --- /dev/null +++ b/errorhandler.h @@ -0,0 +1,12 @@ +#ifndef ERRORHANDLER_H +#define ERRORHANDLER_H + +#include + +class errorHandler +{ +public: + errorHandler(); +}; + +#endif // ERRORHANDLER_H \ No newline at end of file diff --git a/getnumber.cpp b/getnumber.cpp new file mode 100644 index 0000000..041c613 --- /dev/null +++ b/getnumber.cpp @@ -0,0 +1,14 @@ +#include "getnumber.h" +#include "ui_getnumber.h" + +getNumber::getNumber(QWidget *parent) : + QDialog(parent), + ui(new Ui::getNumber) +{ + ui->setupUi(this); +} + +getNumber::~getNumber() +{ + delete ui; +} diff --git a/getnumber.h b/getnumber.h new file mode 100644 index 0000000..2b45769 --- /dev/null +++ b/getnumber.h @@ -0,0 +1,22 @@ +#ifndef GETNUMBER_H +#define GETNUMBER_H + +#include + +namespace Ui { +class getNumber; +} + +class getNumber : public QDialog +{ + Q_OBJECT + +public: + explicit getNumber(QWidget *parent = 0); + ~getNumber(); + +private: + Ui::getNumber *ui; +}; + +#endif // GETNUMBER_H diff --git a/getnumber.ui b/getnumber.ui new file mode 100644 index 0000000..18aa312 --- /dev/null +++ b/getnumber.ui @@ -0,0 +1,67 @@ + + getNumber + + + + 0 + 0 + 640 + 480 + + + + Dialog + + + + + 10 + 440 + 621 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + buttonBox + accepted() + getNumber + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + getNumber + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/global.h b/global.h new file mode 100644 index 0000000..e84f8a8 --- /dev/null +++ b/global.h @@ -0,0 +1,4 @@ +#ifndef GLOBAL_H +#define GLOBAL_H + +#endif // GLOBAL_H diff --git a/hidapi.h/hidapi.h b/hidapi.h/hidapi.h new file mode 100644 index 0000000..e5bc2dc --- /dev/null +++ b/hidapi.h/hidapi.h @@ -0,0 +1,391 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +/** @file + * @defgroup API hidapi API + */ + +#ifndef HIDAPI_H__ +#define HIDAPI_H__ + +#include + +#ifdef _WIN32 + #define HID_API_EXPORT __declspec(dllexport) + #define HID_API_CALL +#else + #define HID_API_EXPORT /**< API export macro */ + #define HID_API_CALL /**< API call macro */ +#endif + +#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/ + +#ifdef __cplusplus +extern "C" { +#endif + struct hid_device_; + typedef struct hid_device_ hid_device; /**< opaque hidapi structure */ + + /** hidapi info structure */ + struct hid_device_info { + /** Platform-specific device path */ + char *path; + /** Device Vendor ID */ + unsigned short vendor_id; + /** Device Product ID */ + unsigned short product_id; + /** Serial Number */ + wchar_t *serial_number; + /** Device Release Number in binary-coded decimal, + also known as Device Version Number */ + unsigned short release_number; + /** Manufacturer String */ + wchar_t *manufacturer_string; + /** Product string */ + wchar_t *product_string; + /** Usage Page for this Device/Interface + (Windows/Mac only). */ + unsigned short usage_page; + /** Usage for this Device/Interface + (Windows/Mac only).*/ + unsigned short usage; + /** The USB interface which this logical device + represents. Valid on both Linux implementations + in all cases, and valid on the Windows implementation + only if the device contains more than one interface. */ + int interface_number; + + /** Pointer to the next device */ + struct hid_device_info *next; + }; + + + /** @brief Initialize the HIDAPI library. + + This function initializes the HIDAPI library. Calling it is not + strictly necessary, as it will be called automatically by + hid_enumerate() and any of the hid_open_*() functions if it is + needed. This function should be called at the beginning of + execution however, if there is a chance of HIDAPI handles + being opened by different threads simultaneously. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_init(void); + + /** @brief Finalize the HIDAPI library. + + This function frees all of the static data associated with + HIDAPI. It should be called at the end of execution to avoid + memory leaks. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_exit(void); + + /** @brief Enumerate the HID Devices. + + This function returns a linked list of all the HID devices + attached to the system which match vendor_id and product_id. + If @p vendor_id is set to 0 then any vendor matches. + If @p product_id is set to 0 then any product matches. + If @p vendor_id and @p product_id are both set to 0, then + all HID devices will be returned. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the types of device + to open. + @param product_id The Product ID (PID) of the types of + device to open. + + @returns + This function returns a pointer to a linked list of type + struct #hid_device, containing information about the HID devices + attached to the system, or NULL in the case of failure. Free + this linked list by calling hid_free_enumeration(). + */ + struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id); + + /** @brief Free an enumeration Linked List + + This function frees a linked list created by hid_enumerate(). + + @ingroup API + @param devs Pointer to a list of struct_device returned from + hid_enumerate(). + */ + void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs); + + /** @brief Open a HID device using a Vendor ID (VID), Product ID + (PID) and optionally a serial number. + + If @p serial_number is NULL, the first device with the + specified VID and PID is opened. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the device to open. + @param product_id The Product ID (PID) of the device to open. + @param serial_number The Serial Number of the device to open + (Optionally NULL). + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number); + + /** @brief Open a HID device by its path name. + + The path name be determined by calling hid_enumerate(), or a + platform-specific path name can be used (eg: /dev/hidraw0 on + Linux). + + @ingroup API + @param path The path name of the device to open + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path); + + /** @brief Write an Output report to a HID device. + + The first byte of @p data[] must contain the Report ID. For + devices which only support a single report, this must be set + to 0x0. The remaining bytes contain the report data. Since + the Report ID is mandatory, calls to hid_write() will always + contain one more byte than the report contains. For example, + if a hid report is 16 bytes long, 17 bytes must be passed to + hid_write(), the Report ID (or 0x0, for devices with a + single report), followed by the report data (16 bytes). In + this example, the length passed in would be 17. + + hid_write() will send the data on the first OUT endpoint, if + one exists. If it does not, it will send the data through + the Control Endpoint (Endpoint 0). + + @ingroup API + @param device A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send. + + @returns + This function returns the actual number of bytes written and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length); + + /** @brief Read an Input report from a HID device with timeout. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + @param milliseconds timeout in milliseconds or -1 for blocking wait. + + @returns + This function returns the actual number of bytes read and + -1 on error. If no packet was available to be read within + the timeout period, this function returns 0. + */ + int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds); + + /** @brief Read an Input report from a HID device. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + + @returns + This function returns the actual number of bytes read and + -1 on error. If no packet was available to be read and + the handle is in non-blocking mode, this function returns 0. + */ + int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length); + + /** @brief Set the device handle to be non-blocking. + + In non-blocking mode calls to hid_read() will return + immediately with a value of 0 if there is no data to be + read. In blocking mode, hid_read() will wait (block) until + there is data to read before returning. + + Nonblocking can be turned on and off at any time. + + @ingroup API + @param device A device handle returned from hid_open(). + @param nonblock enable or not the nonblocking reads + - 1 to enable nonblocking + - 0 to disable nonblocking. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock); + + /** @brief Send a Feature report to the device. + + Feature reports are sent over the Control endpoint as a + Set_Report transfer. The first byte of @p data[] must + contain the Report ID. For devices which only support a + single report, this must be set to 0x0. The remaining bytes + contain the report data. Since the Report ID is mandatory, + calls to hid_send_feature_report() will always contain one + more byte than the report contains. For example, if a hid + report is 16 bytes long, 17 bytes must be passed to + hid_send_feature_report(): the Report ID (or 0x0, for + devices which do not use numbered reports), followed by the + report data (16 bytes). In this example, the length passed + in would be 17. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send, including + the report number. + + @returns + This function returns the actual number of bytes written and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length); + + /** @brief Get a feature report from a HID device. + + Set the first byte of @p data[] to the Report ID of the + report to be read. Make sure to allow space for this + extra byte in @p data[]. Upon return, the first byte will + still contain the Report ID, and the report data will + start in data[1]. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into, including + the Report ID. Set the first byte of @p data[] to the + Report ID of the report to be read, or set it to zero + if your device does not use numbered reports. + @param length The number of bytes to read, including an + extra byte for the report ID. The buffer can be longer + than the actual report. + + @returns + This function returns the number of bytes read plus + one for the report ID (which is still in the first + byte), or -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length); + + /** @brief Close a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + */ + void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device); + + /** @brief Get The Manufacturer String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen); + + /** @brief Get The Product String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen); + + /** @brief Get The Serial Number String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen); + + /** @brief Get a string from a HID device, based on its string index. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string_index The index of the string to get. + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen); + + /** @brief Get a string describing the last error which occurred. + + @ingroup API + @param device A device handle returned from hid_open(). + + @returns + This function returns a string containing the last error + which occurred or NULL if none has occurred. + */ + HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/hidapi/hidapi.h b/hidapi/hidapi.h new file mode 100644 index 0000000..e5bc2dc --- /dev/null +++ b/hidapi/hidapi.h @@ -0,0 +1,391 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +/** @file + * @defgroup API hidapi API + */ + +#ifndef HIDAPI_H__ +#define HIDAPI_H__ + +#include + +#ifdef _WIN32 + #define HID_API_EXPORT __declspec(dllexport) + #define HID_API_CALL +#else + #define HID_API_EXPORT /**< API export macro */ + #define HID_API_CALL /**< API call macro */ +#endif + +#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/ + +#ifdef __cplusplus +extern "C" { +#endif + struct hid_device_; + typedef struct hid_device_ hid_device; /**< opaque hidapi structure */ + + /** hidapi info structure */ + struct hid_device_info { + /** Platform-specific device path */ + char *path; + /** Device Vendor ID */ + unsigned short vendor_id; + /** Device Product ID */ + unsigned short product_id; + /** Serial Number */ + wchar_t *serial_number; + /** Device Release Number in binary-coded decimal, + also known as Device Version Number */ + unsigned short release_number; + /** Manufacturer String */ + wchar_t *manufacturer_string; + /** Product string */ + wchar_t *product_string; + /** Usage Page for this Device/Interface + (Windows/Mac only). */ + unsigned short usage_page; + /** Usage for this Device/Interface + (Windows/Mac only).*/ + unsigned short usage; + /** The USB interface which this logical device + represents. Valid on both Linux implementations + in all cases, and valid on the Windows implementation + only if the device contains more than one interface. */ + int interface_number; + + /** Pointer to the next device */ + struct hid_device_info *next; + }; + + + /** @brief Initialize the HIDAPI library. + + This function initializes the HIDAPI library. Calling it is not + strictly necessary, as it will be called automatically by + hid_enumerate() and any of the hid_open_*() functions if it is + needed. This function should be called at the beginning of + execution however, if there is a chance of HIDAPI handles + being opened by different threads simultaneously. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_init(void); + + /** @brief Finalize the HIDAPI library. + + This function frees all of the static data associated with + HIDAPI. It should be called at the end of execution to avoid + memory leaks. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_exit(void); + + /** @brief Enumerate the HID Devices. + + This function returns a linked list of all the HID devices + attached to the system which match vendor_id and product_id. + If @p vendor_id is set to 0 then any vendor matches. + If @p product_id is set to 0 then any product matches. + If @p vendor_id and @p product_id are both set to 0, then + all HID devices will be returned. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the types of device + to open. + @param product_id The Product ID (PID) of the types of + device to open. + + @returns + This function returns a pointer to a linked list of type + struct #hid_device, containing information about the HID devices + attached to the system, or NULL in the case of failure. Free + this linked list by calling hid_free_enumeration(). + */ + struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id); + + /** @brief Free an enumeration Linked List + + This function frees a linked list created by hid_enumerate(). + + @ingroup API + @param devs Pointer to a list of struct_device returned from + hid_enumerate(). + */ + void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs); + + /** @brief Open a HID device using a Vendor ID (VID), Product ID + (PID) and optionally a serial number. + + If @p serial_number is NULL, the first device with the + specified VID and PID is opened. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the device to open. + @param product_id The Product ID (PID) of the device to open. + @param serial_number The Serial Number of the device to open + (Optionally NULL). + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number); + + /** @brief Open a HID device by its path name. + + The path name be determined by calling hid_enumerate(), or a + platform-specific path name can be used (eg: /dev/hidraw0 on + Linux). + + @ingroup API + @param path The path name of the device to open + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path); + + /** @brief Write an Output report to a HID device. + + The first byte of @p data[] must contain the Report ID. For + devices which only support a single report, this must be set + to 0x0. The remaining bytes contain the report data. Since + the Report ID is mandatory, calls to hid_write() will always + contain one more byte than the report contains. For example, + if a hid report is 16 bytes long, 17 bytes must be passed to + hid_write(), the Report ID (or 0x0, for devices with a + single report), followed by the report data (16 bytes). In + this example, the length passed in would be 17. + + hid_write() will send the data on the first OUT endpoint, if + one exists. If it does not, it will send the data through + the Control Endpoint (Endpoint 0). + + @ingroup API + @param device A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send. + + @returns + This function returns the actual number of bytes written and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length); + + /** @brief Read an Input report from a HID device with timeout. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + @param milliseconds timeout in milliseconds or -1 for blocking wait. + + @returns + This function returns the actual number of bytes read and + -1 on error. If no packet was available to be read within + the timeout period, this function returns 0. + */ + int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds); + + /** @brief Read an Input report from a HID device. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + + @returns + This function returns the actual number of bytes read and + -1 on error. If no packet was available to be read and + the handle is in non-blocking mode, this function returns 0. + */ + int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length); + + /** @brief Set the device handle to be non-blocking. + + In non-blocking mode calls to hid_read() will return + immediately with a value of 0 if there is no data to be + read. In blocking mode, hid_read() will wait (block) until + there is data to read before returning. + + Nonblocking can be turned on and off at any time. + + @ingroup API + @param device A device handle returned from hid_open(). + @param nonblock enable or not the nonblocking reads + - 1 to enable nonblocking + - 0 to disable nonblocking. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock); + + /** @brief Send a Feature report to the device. + + Feature reports are sent over the Control endpoint as a + Set_Report transfer. The first byte of @p data[] must + contain the Report ID. For devices which only support a + single report, this must be set to 0x0. The remaining bytes + contain the report data. Since the Report ID is mandatory, + calls to hid_send_feature_report() will always contain one + more byte than the report contains. For example, if a hid + report is 16 bytes long, 17 bytes must be passed to + hid_send_feature_report(): the Report ID (or 0x0, for + devices which do not use numbered reports), followed by the + report data (16 bytes). In this example, the length passed + in would be 17. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send, including + the report number. + + @returns + This function returns the actual number of bytes written and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length); + + /** @brief Get a feature report from a HID device. + + Set the first byte of @p data[] to the Report ID of the + report to be read. Make sure to allow space for this + extra byte in @p data[]. Upon return, the first byte will + still contain the Report ID, and the report data will + start in data[1]. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into, including + the Report ID. Set the first byte of @p data[] to the + Report ID of the report to be read, or set it to zero + if your device does not use numbered reports. + @param length The number of bytes to read, including an + extra byte for the report ID. The buffer can be longer + than the actual report. + + @returns + This function returns the number of bytes read plus + one for the report ID (which is still in the first + byte), or -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length); + + /** @brief Close a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + */ + void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device); + + /** @brief Get The Manufacturer String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen); + + /** @brief Get The Product String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen); + + /** @brief Get The Serial Number String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen); + + /** @brief Get a string from a HID device, based on its string index. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string_index The index of the string to get. + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen); + + /** @brief Get a string describing the last error which occurred. + + @ingroup API + @param device A device handle returned from hid_open(). + + @returns + This function returns a string containing the last error + which occurred or NULL if none has occurred. + */ + HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/hp_infodialog.cpp b/hp_infodialog.cpp new file mode 100644 index 0000000..a8ab594 --- /dev/null +++ b/hp_infodialog.cpp @@ -0,0 +1,14 @@ +#include "hp_infodialog.h" +#include "ui_hp_infodialog.h" + +hp_infoDialog::hp_infoDialog(QWidget *parent) : + QFrame(parent), + ui(new Ui::hp_infoDialog) +{ + ui->setupUi(this); +} + +hp_infoDialog::~hp_infoDialog() +{ + delete ui; +} diff --git a/hp_infodialog.h b/hp_infodialog.h new file mode 100644 index 0000000..8f323e3 --- /dev/null +++ b/hp_infodialog.h @@ -0,0 +1,22 @@ +#ifndef HP_INFODIALOG_H +#define HP_INFODIALOG_H + +#include + +namespace Ui { +class hp_infoDialog; +} + +class hp_infoDialog : public QFrame +{ + Q_OBJECT + +public: + explicit hp_infoDialog(QWidget *parent = 0); + ~hp_infoDialog(); + +private: + Ui::hp_infoDialog *ui; +}; + +#endif // HP_INFODIALOG_H diff --git a/hp_infodialog.ui b/hp_infodialog.ui new file mode 100644 index 0000000..dbe10ff --- /dev/null +++ b/hp_infodialog.ui @@ -0,0 +1,17 @@ + + + hp_infoDialog + + + + 0 + 0 + 640 + 480 + + + + Frame + + + diff --git a/hp_mditexteditor.cpp b/hp_mditexteditor.cpp new file mode 100644 index 0000000..6549179 --- /dev/null +++ b/hp_mditexteditor.cpp @@ -0,0 +1,6 @@ +#include "hp_mditexteditor.h" + +hp_mdiTextEditor::hp_mdiTextEditor() +{ + +} diff --git a/hp_mditexteditor.h b/hp_mditexteditor.h new file mode 100644 index 0000000..ab64948 --- /dev/null +++ b/hp_mditexteditor.h @@ -0,0 +1,13 @@ +#ifndef HP_MDITEXTEDITOR_H +#define HP_MDITEXTEDITOR_H + +#include +#include + +class hp_mdiTextEditor +{ +public: + hp_mdiTextEditor(); +}; + +#endif // HP_MDITEXTEDITOR_H \ No newline at end of file diff --git a/hp_mdivariableedit.cpp b/hp_mdivariableedit.cpp new file mode 100644 index 0000000..8da3d64 --- /dev/null +++ b/hp_mdivariableedit.cpp @@ -0,0 +1,6 @@ +#include "hp_mdivariableedit.h" + +hp_mdiVariableEdit::hp_mdiVariableEdit(QWidget *parent) : QWidget(parent) +{ + +} diff --git a/hp_mdivariableedit.h b/hp_mdivariableedit.h new file mode 100644 index 0000000..a6e8b4b --- /dev/null +++ b/hp_mdivariableedit.h @@ -0,0 +1,18 @@ +#ifndef HP_MDIVARIABLEEDIT_H +#define HP_MDIVARIABLEEDIT_H + +#include +#include + +class hp_mdiVariableEdit : public QWidget +{ + Q_OBJECT +public: + explicit hp_mdiVariableEdit(QWidget *parent = nullptr); + +signals: + +public slots: +}; + +#endif // HP_MDIVARIABLEEDIT_H \ No newline at end of file diff --git a/hp_mdiwindow.cpp b/hp_mdiwindow.cpp new file mode 100644 index 0000000..ff37632 --- /dev/null +++ b/hp_mdiwindow.cpp @@ -0,0 +1,6 @@ +#include "hp_mdiwindow.h" + +hp_MDIWindow::hp_MDIWindow() +{ + +} diff --git a/hp_mdiwindow.h b/hp_mdiwindow.h new file mode 100644 index 0000000..c6d226a --- /dev/null +++ b/hp_mdiwindow.h @@ -0,0 +1,13 @@ +#ifndef HP_MDIWINDOW_H +#define HP_MDIWINDOW_H + +#include +#include + +class hp_MDIWindow +{ +public: + hp_MDIWindow(); +}; + +#endif // HP_MDIWINDOW_H \ No newline at end of file diff --git a/hp_mdiwindow.ui b/hp_mdiwindow.ui new file mode 100644 index 0000000..0439df4 --- /dev/null +++ b/hp_mdiwindow.ui @@ -0,0 +1,25 @@ + + + + + + MainWindow + + + + 0 + 0 + 640 + 480 + + + + MainWindow + + + + + + + + diff --git a/hpdata.cpp b/hpdata.cpp new file mode 100644 index 0000000..ce1a962 --- /dev/null +++ b/hpdata.cpp @@ -0,0 +1,6 @@ +#include "hpdata.h" + +hpData::hpData() +{ + +} diff --git a/hpdata.h b/hpdata.h new file mode 100644 index 0000000..d9da67f --- /dev/null +++ b/hpdata.h @@ -0,0 +1,12 @@ +#ifndef HPDATA_H +#define HPDATA_H + +#include + +class hpData +{ +public: + hpData(); +}; + +#endif // HPDATA_H \ No newline at end of file diff --git a/hpinterface.h b/hpinterface.h new file mode 100644 index 0000000..52f030a --- /dev/null +++ b/hpinterface.h @@ -0,0 +1,4 @@ +#ifndef HPINTERFACE_H +#define HPINTERFACE_H + +#endif // HPINTERFACE_H diff --git a/hptoolbox.cpp b/hptoolbox.cpp new file mode 100644 index 0000000..e9645a5 --- /dev/null +++ b/hptoolbox.cpp @@ -0,0 +1,14 @@ +#include "hptoolbox.h" +#include "ui_hptoolbox.h" + +hpToolBox::hpToolBox(QWidget *parent) : + QToolBox(parent), + ui(new Ui::hpToolBox) +{ + ui->setupUi(this); +} + +hpToolBox::~hpToolBox() +{ + delete ui; +} diff --git a/hptoolbox.h b/hptoolbox.h new file mode 100644 index 0000000..b8c1ec0 --- /dev/null +++ b/hptoolbox.h @@ -0,0 +1,22 @@ +#ifndef HPTOOLBOX_H +#define HPTOOLBOX_H + +#include + +namespace Ui { +class hpToolBox; +} + +class hpToolBox : public QToolBox +{ + Q_OBJECT + +public: + explicit hpToolBox(QWidget *parent = 0); + ~hpToolBox(); + +private: + Ui::hpToolBox *ui; +}; + +#endif // HPTOOLBOX_H diff --git a/hptreeitem.cpp b/hptreeitem.cpp new file mode 100644 index 0000000..18eb6e2 --- /dev/null +++ b/hptreeitem.cpp @@ -0,0 +1,6 @@ +#include "hptreeitem.h" + +hptreeitem::hptreeitem() +{ + +} diff --git a/hptreeitem.h b/hptreeitem.h new file mode 100644 index 0000000..6cd090a --- /dev/null +++ b/hptreeitem.h @@ -0,0 +1,11 @@ +#ifndef HPTREEITEM_H +#define HPTREEITEM_H + + +class hptreeitem +{ +public: + hptreeitem(); +}; + +#endif // HPTREEITEM_H \ No newline at end of file diff --git a/hpusb.cpp b/hpusb.cpp new file mode 100644 index 0000000..171e00b --- /dev/null +++ b/hpusb.cpp @@ -0,0 +1,6 @@ +#include "hpusb.h" + +hpusb::hpusb() +{ + +} diff --git a/hpusb.h b/hpusb.h new file mode 100644 index 0000000..afad63f --- /dev/null +++ b/hpusb.h @@ -0,0 +1,11 @@ +#ifndef HPUSB_H +#define HPUSB_H + + +class hpusb +{ +public: + hpusb(); +}; + +#endif // HPUSB_H \ No newline at end of file diff --git a/icons/1_Prime_compact.primeskin b/icons/1_Prime_compact.primeskin new file mode 100644 index 0000000..7d9e56d --- /dev/null +++ b/icons/1_Prime_compact.primeskin @@ -0,0 +1,246 @@ + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+ + + +
+
+ + +
+ + + +
+
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+
+ + + + + + + + +
+ + + + +
+
+
+
+ + + + + + +
+ + + + +
+ + + + +
+ + +
+
+
+
+ + + + +
+ + +
+ + + + +
+ + + + +
+ + + + +
+
+
+
+ + + + +
+
+ + +
+ + + + +
+ + + + +
+ + + + +
+ + + + + + + +
+ + + + +
+ + + + +
+ + + + +
+
+ + + + + \ No newline at end of file diff --git a/icons/2_Prime_compact_L.primeskin b/icons/2_Prime_compact_L.primeskin new file mode 100644 index 0000000..abc15f2 --- /dev/null +++ b/icons/2_Prime_compact_L.primeskin @@ -0,0 +1,246 @@ + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+ + + +
+
+ + +
+ + + +
+
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+
+ + + + + + + + +
+ + + + +
+
+
+
+ + + + + + +
+ + + + +
+ + + + +
+ + +
+
+
+
+ + + + +
+ + +
+ + + + +
+ + + + +
+ + + + +
+
+
+
+ + + + +
+
+ + +
+ + + + +
+ + + + +
+ + + + +
+ + + + + + + +
+ + + + +
+ + + + +
+ + + + +
+
+ + + + + \ No newline at end of file diff --git a/icons/3_Prime_small.primeskin b/icons/3_Prime_small.primeskin new file mode 100644 index 0000000..470eabd --- /dev/null +++ b/icons/3_Prime_small.primeskin @@ -0,0 +1,246 @@ + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+ + + +
+
+ + +
+ + + +
+
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+
+ + + + + + + + +
+ + + + +
+
+
+
+ + + + + + +
+ + + + +
+ + + + +
+ + +
+
+
+
+ + + + +
+ + +
+ + + + +
+ + + + +
+ + + + +
+
+
+
+ + + + +
+
+ + +
+ + + + +
+ + + + +
+ + + + +
+ + + + + + + +
+ + + + +
+ + + + +
+ + + + +
+
+ + + + + \ No newline at end of file diff --git a/icons/4_Prime_med.primeskin b/icons/4_Prime_med.primeskin new file mode 100644 index 0000000..8a24caa --- /dev/null +++ b/icons/4_Prime_med.primeskin @@ -0,0 +1,246 @@ + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+ + + +
+
+ + +
+ + + +
+
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+
+ + + + + + + + +
+ + + + +
+
+
+
+ + + + + + +
+ + + + +
+ + + + +
+ + +
+
+
+
+ + + + +
+ + +
+ + + + +
+ + + + +
+ + + + +
+
+
+
+ + + + +
+
+ + +
+ + + + +
+ + + + +
+ + + + +
+ + + + + + + +
+ + + + +
+ + + + +
+ + + + +
+
+ + + + + \ No newline at end of file diff --git a/icons/5_Prime_large_L.primeskin b/icons/5_Prime_large_L.primeskin new file mode 100644 index 0000000..48e8ae4 --- /dev/null +++ b/icons/5_Prime_large_L.primeskin @@ -0,0 +1,246 @@ + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+ + + +
+
+ + +
+ + + +
+
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+
+ + + + + + + + +
+ + + + +
+
+
+
+ + + + + + +
+ + + + +
+ + + + +
+ + +
+
+
+
+ + + + +
+ + +
+ + + + +
+ + + + +
+ + + + +
+
+
+
+ + + + +
+
+ + +
+ + + + +
+ + + + +
+ + + + +
+ + + + + + + +
+ + + + +
+ + + + +
+ + + + +
+
+ + + + + \ No newline at end of file diff --git a/icons/about_16x16.png b/icons/about_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..23270c00eb60b717b2014da599950b923b40a73a GIT binary patch literal 659 zcmV;E0&M+>P)Zhr0AxD2XL}&Uw+X3ru;$}(< zjz`qP-M0#dLTb5^_tCI=_++$U+P(w~Sa;O}fE0<0+dWWr@_{!lcS~V?y#!X*O5o;G zfe)VqE_+L2Jm=+C35B`m-l|OV9!w&-w!!9(Vc2voAM4NLacx-fXQ6}3-ePEZyQT)iY7_Tc&s= zvskj2o3mdsV81}aw`&m%vDPMCYhi*_B=qg0I(15bo@%IcDI5yrGShAAgyOFu$tSv_P30+GFLFPYgRCI{2--g3u4My tI)7BMaBi2;x3!@EL2CHc9whM(d;zaB!xrOJ3tj*K002ovPDHLkV1jwBHPZk9 literal 0 HcmV?d00001 diff --git a/icons/about_32x32.png b/icons/about_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..0ff137ceb449a9917c665f74172cd82521a67726 GIT binary patch literal 1576 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 USP)anTmS$707*qoM6N<$g3QothX4Qo literal 0 HcmV?d00001 diff --git a/icons/add_background_22x22.png b/icons/add_background_22x22.png new file mode 100644 index 0000000000000000000000000000000000000000..65e0dcb1b8be545d4d7bcce14b981f2816d7f1e2 GIT binary patch literal 1135 zcmV-#1d#iQP)s&k@ zh8aR2fVc=j0A;}gut2;CLWl=o!Ar1W&8x6y$pRKAh;R#tNrEyn9^2>A-9=#!A_5_? zp^rXSb)Vx>x!XGYUqfzv_&EYkI=qooi=~7m!z}Uly!k8NGkY}mh>gG<;I840!@DYc ztaw&16-W6ep(3s!a)MXXHF&65s+fy>D)51cCU8%K3smt&)vX3iG@dZRE*gy(CXs98 z5lh69c3q1l_0GU5Dgu^Q4-jj4gNK}SNtji%Dw>s)B5FhtG$fUXS98_1?CKSCSn*ve#A_j3mW+s>w!;}*El=9 zg9!-{g^K|mp5tZ2TZb-#Pp}?g7+{|ErVzy#|B(N(ozT*uslnPFOUUlh`WvJf={E4p z3^ztZ2K;mlcOSwgVjhrbgfXNUmkhPQ2uu@Fp1s;C!&HQT1ZwCUUMW$$r`8s^Hp4}Q zvjOHUH3}1mj%C+v#Yf-W6B~DRQc9Sq?k9@@7BjWKKpS8>M%x}g1UVR>uD~})+Cm#7 z=)QBY3VlX7*>@bQn4l{BkWP6#&)oZYgZ?_vcFyxJFF0z}_%Ell^^Daxa5v7P3q>O;@WE%NeE+`0-3pOb<;j_~%UvD$p^}u>{$#%0PPXl8qlzC#FD-SN62P+u1 z&M-LC1{I@G@F~+ik$L+aXcCyF2GPXr+uw3|IHOI6GLkftb>Jd8Jde&c+nH(DP=*!@k-znD2xF+Ix%=Y_fNkKKbG?FGngvQeD zd$m>$mrFDN2!ieZ!?>zL9%`bkZ7L+6Ig3L)J2OmMlnTtWslh$(ic==2ia40o8Z{6k zJ_J+iKpROXFQHwp-kF}3fp$>Z$DGA8!m?@cV+Yar*$3n~lS{$OSnRe#7`K<&96g6x zBW=IL=#RnucDeQb_XS^hvh5mZp z<8b$XzZWE2gdaoYYP2UWG-z4?N8wty9-ex{^{e$K!tvE}hv6Xfp$#T#dh4C{6hN+Y zs4Z|`fVV&j5&-I-1gQvRKbImRg&?9uG?mnJC6Aj@=Ar%$^sM9?LOJ=l0000YdQ@0+ zQ*UN;cVTj6004N}D=#nC%goCzPEIUH)ypqR2LLwM23QbN%3J^d002ovPDHLkV1h4G B04V?f literal 0 HcmV?d00001 diff --git a/icons/add_background_32x32.png b/icons/add_background_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..2d955f7d83ca21d4afdc678d18d91cfb4d1d09af GIT binary patch literal 2004 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 LNkvXXu0mjfd7^Z; literal 0 HcmV?d00001 diff --git a/icons/add_new_32x32.png b/icons/add_new_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..845bf020455c048424eb01b85a9d1459b012d442 GIT binary patch literal 1320 zcmV+@1=sqCP)+YXyCzaivn?Bz?$*-Jx zzW-k_@~`GA4z=2QG(AMiS~&v}yH%ZKy-(vd86rPNmlrymQ2ziGiT8NBRUZD(V;`D- z%ps3))as*%xMS`Pr)@jUxp$nKs`;~vJqvA!JO^EV zv_0XJC%A0!GHQS(7xy#Egc&Q7PF7SfmU!YVf+1A`_ZhE)R-sh{}omafp`oWC`DkaZ4_E~;$ z<1ZeP&Za-}de2L|k}OdqBael7SjWC?nPuEOdy7A8{e!VX$0Vx~P#X^^7rfr+*(!LjVM4|AFIcDrppo+q5|_SEjT*Yx6?H_gei z=5rB8VvkB9lBFwHIRvDsBu(fb(l@yXc~q1V6{v0}MM8ON~kn~}i+gGE{s zT=bR{Jnl=8)niOHn68sM9#!NlC31g%2SV8w;Dq7{rkm5`iH@dHVW>;XoA!^ zzMVLy&kpnmRxB1h%be`I2w5Ip ztya&f*Xu#hSA}CQxcCCEo`1DFSKmpEc!o+u1uOkOtj2bCc)6EqxhU3ye&tXo z6x{b-_w|M33+}P^9#rTIGoLU~WM`SF0*yhEH3`?a&8xjh%jIYs*nwK@tVW|D2;vpw z-LPqzT3lRo)m2wH?Yh&PF?fbshHp{t)a7$|g;v4g(}#Qd!qdI)74Lf!zK?mN2Lz~v z0L^ChX6q^M6(R)RvuBUH?z+q7&6|Z#p+n_z+34t~6Hh$R@bK_saXL~+^dQQV9`Y>N zK;cU`GBRRtaL_&Pc~9A6a=Dy*J`c^V9FiVqBrD}_O84Iu-VH$(M&1v}a-#>mTfbt6 zENPl%ldl{=R?LdhtUN32bULzgh~iv-0kYy=GL{b(zK}s0Zpdb{DXfQr#YSrZ)qXqB zx3o(T1!;IWg(qNkcJ{CHC8EgcwdD)e-02aIc(``EC4~9dXFmO- z?|ti=Z`@xFNk1oFsT)DwZU64wdlU*qUHNCe^u<4X`>S7jF;YE<^|tGm?p+_+zp$`i zZf?%-@Q|+jtDpbEv$FEOJD~4Nk-Y>B9pnr7^rN5t)Q`UNwXbJKuI~;+`ltIH2o>5s zI6T_=$R|JbmEQ6`IuKr?vj2aX`EoCvD2|B}&A71e*-wA`i$D2(xZ8Wk`xxxuLjB_` e;WDKDU-%2+U>rZQiGOVX0000)_{hIgxt=f8j)ejucYe+bD^?%b%{sSh8b|M!Hc`h)#Ol)BD;A3uLlL!)E lYZH-nsLW%LauH->*m~o1@-)w+$w22Yc)I$ztaD0e0s!ajMeP6p literal 0 HcmV?d00001 diff --git a/icons/apps_32x32.png b/icons/apps_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..ae14849fba48cb6bcd8799678708265e1ba31079 GIT binary patch literal 393 zcmV;40e1e0P)uz+>FMh0>+J3A?(p#OD0kb;%+Aoz($mw`ID_p%gUj69+*y#%k+$8Dx8Rev-s0lq zlepou%jmkgy2aAr8f?oVa@70#`~Cg>|NsA1iijux000JbQchC<2{AG=`_jsVegFUf zt4TybRCwCGlh+EvKnw)slYQ>JrT3ov|Mx`@1kTyWfshP{MKBMm2f-*7iddkWyMUtr z+r2=GfOXc~nyd@$JT-z{#mOp(yU@;4*wDXiC6mx-sgnTBDlaNjYQW72_ zFJa?{TZ)|@9^Qk6<9WRt3>Gfe#bhv8aJ6Jp=GQy{*fMVHo{BA$00000NkvXXu0mjfZZgKv literal 0 HcmV?d00001 diff --git a/icons/backup_16x16.png b/icons/backup_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..0cb9097b51c81040bffe37427b42ec6aea500410 GIT binary patch literal 419 zcmV;U0bKrxP)4B{vG+$_R-yt9gsL*hhW)Y{m~M*NlsJ!{ zTmAh{fjGzKcIaJ=q>d@ zJS5vJr{U*%zISyL4N%YmGInSH+bVDj)4T<;uhyA}>gxkKLJmQzkBOVe6GP^@# z!aoIWdUt54D`ld;p8T{hxo?;e4+;1i-J~Y^mxAKpuO!6=fm%&sOrYleWy6ekNWfd~ z8s(v16-3)TrKPP2Kz6xrm=O;NcxYdyDBy#FVDtMT;^^ooyWBU-h=&AR{+uVr`?Z2- z+ef6v`U1!<_YE`RApwVC3E=cm%? zyHlz36_#ar90!J@fJq?0fXdTic92tgQzz43VXHQ<$QtJOf&(!boi z?MUvXtIs1AXBMPXbowJSa+wt6VkQw2m|g3-y11^{(z=3(j@$*{x~V*KrJTK{lH%pl-Dc9x$_HO+%qtbP>yQ68B7o1t+`peFw(dx6KnWEVlAZCb{PQ8RR z=iiEwg8f_nNbBG>BQBOiWW`))Ac_>ljKf*wc+#d{6O}LLNP8i3Az~^)t(@hY@`y~@ zXHvL=9kDY?s>Jg6^RsVY?L~Jm>bKap;g_-Sb}eioN}4tiEzs2u6?dbQBIP-h(t<7P z|IE2(t|JUb+V2HA5!!m+b#TXq34dn6}&RUZpkNyGsx02@H5vEGfD z9JAYN(e1S850Z>E6L(}n5$Xt{RV`m4K6OAQ;s!QAq?EbyQd1!&eKv0R56wgSlf{!A zO&5K5dElMdXt`n23uHiyWY(=R=Y=z}XQ!B++Iv}J< zCK-;phvI4xi~@7NHPu25&2^eOvr=3hRot>HG;RGrh9=vD%Fg4b!SXX^_ofmhx5rp6 zjpgQd!H64W()l8`?K?4X^+N$d2|y7gfAQytv7%H+AtwTeNN5%f)UsXcu@PSk$Ms_R zddGA-y~beDdnSNe?==|&!L!%Zs$UdgnAeLCSvWon$3qYiwx?}mO27(>sXg+VLJ}aGPqQ4P`rijamce?}_1Ci;r#<;M zyV`iv8_W6!;$Op8`nD4r6N_5QoA9W&I}E$crQ)%0Afz0P)#n;V#;^Ox54HtKXe(eb z1MhklHoNZe)0zIKYsKvId|#j3SlHF#HlE{gq!~BRZzsYal!^wUU?UUx7+>AI7f{TA ziC|_Y*n{m?XKFe3^VMSR>2xaXc%F;vx*Tq_2*v|sq!^FG-Y`(Fsc+1F!`M9=CW4tT z(PRdfOPdPx#24YJ`L;(K){{a7>y{D4^000SaNLh0L00VXa00VXbebs`@00007bV*G`2iXP= z5h@JrLoAR000JaQL_t(|+KtamNK{c6#_|8T_s(cmWQJc!7D5sk71F{mf|^COXyc-7 zpwI`nkTzii3W-7xg#-qXn?5#LtAa{&5{raHltxW3a~x~@oE#lz=FZ%EPiGQ$!psjI z&gPuweUBIlyt?&y`f1zbqSuWg1QhNnk5wMH(tNq1W#ICPT#N>+Yx9&fQqgOR+b%Nx#oe`Th!`(S+L8W?@5+?Hkn$m&l80GO;6R z*+}>w&83r@vlo~_$pLDkq512>6H6o#=H?yUjTH`I6y=uQ4Xs2UOwa#fOF`~FN()PF zvsoZfvJD6IF*Hq*lGH7Vce~?)^qv8Am)f2;&CD${Px*tS6c^-i>e%5XGAuw!DcFWa zhJ+B3Z5U+8{PVJL^=D^me6f!dJ*$X4#~-!}((43jr(SzxSAfb&wXWY!~66jX{QzRz8uXRfH6P zp^6-EAB^iC?wsYtfS+`()l}O*`bG8dnPx#24YJ`L;(K){{a7>y{D4^000SaNLh0L00hDS00hDTyw8vr00007bV*G`2iXP= z5(G872NUf800Q_)L_t(|+MSb6XdGo6hM#YCvb#yz-8M;Mme|-93+h2CEqJLYN}(P^ zsRz#{mmY+o2!Xa>szFhxS3zhLgr3?Q3bhAC)+!#NRfIsKgl@Msw(B;@{+s#c&o|%a zkWSc*h++4Em*4G~mzQtELiyr5*Ox#4XsJ?6(}4iSatGy+i8p3mdwTX0xvwyj23# zB+w~9eFkKbFHFT!uKZq^{qbhy%r}b_EDPXafhP(%jvgF7Ck~%ZnI|95pfyR`2!Vwq zL>#zTin#G%w7KydFbVMXR|{tUv(FGBP|BbpL#RP3P3F#$wjI~7RtqtP1S`3Q*o`9% z9XN^HzxG?1Bac3X^n5}EN*e-Y@UU6EzG&I~=^;6qv-%%2Bo=K9uzP1ePJVztxJ|;~ zX5*l_cJe_@B4t1rnOJUUdJ$`$Mc#9<_YOaHwo;YntMwK5m$=y+q%br-OTY z%eu?PZOi>%FSGa90hH%rbbvs`*QYNVaQCNqt6Q8sAQLc~CaiS(dq%c{5tQl;2#Hkz9`aMF=Bnw%9L1=?hi+UH^{d>^n z5mI)QPINTFz#bXe^Kb6s#22^emh8yH+2WVMOugi6DK{`WpmO75@BIT3qmBs@sC1P8 O0000; literal 0 HcmV?d00001 diff --git a/icons/calc_tree_32x32.png b/icons/calc_tree_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..96651c1ddece4408218bfe56d79ddb562e5ce915 GIT binary patch literal 1082 zcmV-A1jYM_P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01ejw01ejxLMWSf00007bV*G`2iXP= z5ivG`c|N!R00X*7L_t(|+U=H0h+9<{$N%RhndHu7OwEW)5iL$Z?V?C2$WRc4(&cnO zu?rDFyBVQS+)KAUmNKZDx|6O1r%_y#(OHR)iul+RM(W^$iFG=e+{byJ&vP$ndc&j& za)Su|$UonK+~oV6$4!EN8wSsJ+aH|TiMQ6?`}%xXe1#P*G7fm^=mmaX`{?qqgU?(k zRbc(vYWm2bg+{$rXO20*SvXjLTwci;I6oGO({3-6k3DcEL`hWPS=~4nzVX~U_&x1` zvZjUhB0f_S(mA)W7T>s_s@J<5GIW(sp(y+at$C$x6XRfrYqja6`=3rXCL$2ukv7NYZU6uP07*qoM6N<$f*D-x Aa{vGU literal 0 HcmV?d00001 diff --git a/icons/casFolder_16x16.png b/icons/casFolder_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..46ee194514a1a5f62decdff31127f5a0b7c26d02 GIT binary patch literal 449 zcmV;y0Y3hTP)m%fPFxXSjiZCh*KcQt2i@BYUIT_xv^T)j)e z3hJ?*eR=Tg&Uv(hoH=+!=Wdz_>Oh2)H9X09rJiS{990aBl^^nI|LLuB%S(^qf+8|M zQDuBT9KflOh84+`j9^dz(y{jD7S=?;!;e?;J-@~ClUWLGbt!3 z233SQPL;u<2N32ZRFLEAPO8_>lJhGUF@_2yzR#Mi%h1H4net-X&Vfzm9 zIeOw4>Ebl?j|#(xzn}h-#7YASVg)NH9^SgK%_d=*Z_ob{mY4Mjf|#mWV;Vcuvlua0 z1q2YK5k-@#vN=wwKGovD#fnSl5M#h8SX9wO(OBsS#Zh$wt$U}?eu97s)-Ou)_iZLo z6wnc=lt*D^F}DA(0gRYFoVddqShD;9SUBZgM-Q}-%XEr9B1zGS3S#TL~hYi+uK{njo9Ugt{`t50hJQ@1rb1&|Y zrEShHY4S_b!+%}`;Er=1esMBNClf#r00=+<1ws&Fr}HLoEWfkJ9p^sE!iFV1^_){! z|HUS9n?XTDKp<}3(1ygbYhTdNG$K-2mnsbfur>IzVFeUWl$0iGfVHvT>(QMI`1si` z=&u_Z~qU^BeHXL)@u_6uTP!K?x0J0LS2uKhl2w=KUV+B!=C^WIr7oL9k{2v0? zdb@2HY?`CHu1_u*TJW;?*?MxyjM@?7;e?Vih+-LfaV*$AudJG3$K7|3PrmtbGl2Nr z1`%PI~`mZg*#)1_Dsq=KqKnKHXg#R{O=WlnVtt`V~ZuHdm6SM*U3 zLTCsbh3q<;>MXWbHzne-!j&&LCIhwvM1;8U!v)@d@-1@x6i=npITXi&`j^xDSRF`^ zz|!(J#JJNo?6m(Lv^fLf%HU=f_qaqik^qgSZQD%#lk~kDYX0uN`S#uAA=!-mAd-)ugxXA}RH$7+kHbr4}iZ4?p5 zfwcyfEVR)uhM-B~lk4K#^>cXdeE^hsqUvSq^btTr3RrF6up1+8Lbt}Uc+K&vx36*c z`9m21@-bv#n%AzU+mCIE|6$L}gP;sq(kpz$)VWhIcKldmEtCbdTh#)Sj70^?=0}2T zxwC=XkFJle=+?eFMh+W^(j`jc)Y(&5x_n787G#~k`;-ED_2MnQCS!(-@e4;mSi%L? zZ(T3N!enWX!2RHXQd}25qk5(4$tijd>=h;t=r<5w!_$LE6Mn|6J2&z2)hh^6NRq$D zb5lSJzDK&Y>d`uROA<@L?~zPB1*xOC(SVpdXTZ zvux&jO1OZ^ae{fGPKg0LzUb6?k!aRQb)Vp=!o+Yh7MkD4F zuC*p8j+H{<9ZGfvtT|0x(&S2tj<9K00000NkvXXu0mjfK*kbz literal 0 HcmV?d00001 diff --git a/icons/casVars_32x32.png b/icons/casVars_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..60f7137a6aa2da5efaa50921e79233b8dcb022b6 GIT binary patch literal 1150 zcmV-^1cCdBP)Gx~1 ztCL3iZufM*jQ<<8?~=zaz45whUs0}>=7v(?01v`b%>h9vXm5=Z3B@ZO;`mPj?GFIX z$7xc}#z&JnJY!=?|0B;nbFn&l`Lkae7#UiBtd_uN4Wo4MMO#3jPzc^y3v0Yt6U-=Y z-cyV+-ZP2Th@>$k92GbhM0Ixd;I&U)#XPetgx--(%vG5i^UOBS=W45Y_xlyV{P1WI zpp^-+Wvpp3Zj1<&amI$8=f+!fO)JKsYCrw<3%>3C4l6IbB;Nl1Gt67AqI0AV)zW;> zh;Ys!b0G?r$vouXbs3SrVGI&AMxERTjEs%p<8Qyjul-#p*(%=q>04Af=0K0{LZvhh zNT@*SviZ|A0nyq>6=W7t5ygstRnSn16;Ts2R$^>Jh=Jar0sKDHMNKWy+M-&l!p_=J zRlD!9HP+g7H5jX*j0(v#eJF3Z+mCU)(ccgwTCP&L{5*a2vv~KtFJP;svRZZ9m9gN; z3t%SM`jjp4&Y_<}!;{uC%rt45f!J_TOOb~cUw#1(Jp2G|z4KOVy76Xs?Ec3<`uh6( z*exJe-*9y}Z?NjhtNQyM#~gFaff(lnIG0nvHU6Qu-f=6w`RW(s?rxI}DX(#aEOx{u$$Cq4uNep+5;HNKt#$@ig^=`BxlePx|_3`!s z4m}EqVhdW4r74^f0sX%Zq8)s|ZaeSRh(pO^xE@$$ zR)YPHlXIQ4L0TXJwAIMVVFw-F+*6tPfrlMHA-R%Iym<~59-|fV!sMr)bb7q#qBvi2 z;<9X_1uCjQ-YP^)J2`P6PFkRXn^qOLE@Jp5FnM0IQqdYP3b~Pm#|+P&B^SwmLVQTOzLOiTz6rg4<`l|Tk%A68=rH^h@_i`+&=Srd_rAbQ zXgavK1^hUeXU%dGZ>S>zrv;1rTS29f_sny|^5Q^T6HYgSfC|H^w_vgm&I=w~^se^ASuyfuR z(Wee1%{ZQg`;%HN`=$H08`YkR9gY6c!MOm;hM8r^TNf*U2G4Zf%PJJatau}_l~_mC zJZ&J0EcI3wV_cKCCYHvVAib6(#V9$gDAv)!^SX)>B;LlwlGvieNhBcq1L=y_n=DRj Qi2wiq07*qoM6N<$f&eW!6aWAK literal 0 HcmV?d00001 diff --git a/icons/chars_16x16.png b/icons/chars_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..084fdc986de082ef7d83981fc3249db78023edd7 GIT binary patch literal 262 zcmV+h0r~!kP)zu@sVXWGP2hrM zZ=F+AO+{$vD5$P@B`dNSLKVKfsETj-G*-Waj+0A^lb;E2YRUf{08gPwJQOuA5C8xG M07*qoM6N<$g83(FH~;_u literal 0 HcmV?d00001 diff --git a/icons/chars_32x32.png b/icons/chars_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..10ee8a4cc4c845f60e72f60a8b9675f71d1f7016 GIT binary patch literal 1291 zcmV+m1@!ufP)6#nj=pF1qDP>7gTj0R|YP?}oWs#IS62Y4;e#zw7;PYv|RKcL36KGY|hG_^)~ z^nu2jRD&_5<*}OB!h`Yh&T)OhulYBcfH|L)7%{kw_cNWiW zH#~Xrq}_F0tx~B7G->hNa=9#*mmjwli$x1rLnipcNAs^gdiduTxm<3*Gz}LJ0hvcw z55s6(smKq*(C_Hz2sBLtnG$3xPoK&tiY&Udq?CDG*V|3Q$mzPKd!FCoZn$n|XV=ub zr-r}ZmB7!lzn*p7(&vW`zR))K#t~GjJ}4O|FqBe^#DX9m4-pRu0T7v*4>G&mWP0NC@r4WKgWv!7bmY`qZJHF6xJn4q0Nc$l3;>)g_9H}0 zC?QPGgn+>0d91su3xEB+_{j$!e)JW92XzEyW@eltM~9SvfRqx6B}sO%q%u4D2q73` zaZJ9?x8^Z7H`kWD3B;;L$d41vsmkXu{8_nznB`t|ErTwH`%tx3#s6$FE$u ziXf;q)gmEe!xk`vtS68Nb9{UZBO@bNUtfo5+D#DPY9`hA?-r0l+5*!wasU1tE?%6( zojZ4M{`>{pyZ0N7TX;50mtq4E3bECRH?7^ zlwxZIcsDzI_%-zO^w0)!kI5}9ErAZ0y{?KnK?_me0zE6jx-xO8Z{ME5^z<|lIgB6( zI1q`ksTKgDkqFZY@P4qiwu+lKzsA|KXD~H&31`kspgCi46GS5s);T20lFGi!`@pqp zUt(otg%2I}+q-dcY^$)I6=D4z(B59axpVL1)~#=F_wHRBKR$%f(NXqONa3t)2spd;z{PF;*tbN0^(%ccW$NQ0R_{tph+F8Yei-ZX6#ZBE*;#mSJMd3s8vcOXMXLf5Jn+_Fk;fwE}qwY-~f@( zAY_(63Y!S5uCC#qm8Tpmpa2m=aaF>Ik$i;I5b{_Ma#FC?C08IO%OvO*$B!L>lXF0p zC7^(m*{0_)(};s5>sX~iQ8h#{q8b!~$5CqqRgJGoO8CCVFP6`@?6M3MxL4I`mDjpJ zLrZ(lK!ROQLndHZ7JFq0K=Cs1{R)?UpN)lAD!JTFDYFDvRHkkimSv))#er?xeD+gS zl0$^C#h;+IA@c>q+cmv4$Fyuac{!2M04*zT`nzvuJ39;SS*9szqRcstPDD)Gbc6!Z zz5^mn7^w(h5Fv;{x&|ytk%&*xc%@R^C~s6e&r{_vRI{_Q^N*L8moxth#>U3vjT<-K z1aOpK6D%^PP$)QbwKYvQgcMQ=A?YzDW>~gm(6jJ7ze*wtC`mOW28uG%m2y}7QmItl zAOqkLs)Tv^Pw>ma!ontYvcbVYV_;w)H_+do@9phv2XKJULpVryiSRPv6~e28J_g_O z*vmDaLxdLzUEST?h5r8j);J~|X6E?#c%4q4+rO%Cb}lznOH2R&002ovPDHLkV1o1R BW4r(W literal 0 HcmV?d00001 diff --git a/icons/chat_16x16.png b/icons/chat_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..4c2b8e43acc01a54bd2825edc71d656f3535005f GIT binary patch literal 707 zcmV;!0zCbRP)Z1ZJoBe%Buy&phN!ZaDF;9|z_$-f$( z%pc?UFy~*pOJSAVDc(7%d>Bk%5yd_AFEjjYO*q!f_QkS^p7`thFkE%)7o5C48;J_~ zAJCtmBC1vTe^!_`!5MursH0qCgj&ItW-_{KaWD(SyNUm|O@A7_tq zL{YL@81%t32uOd-jr%%xdnF2TU>blTo}_-AV^U6hAw;o4AyyT;bwo`uO$@4EH?!R|2skl zPsNys#~PE*L%V5Fs&&ynn^y0gPEPhtqfBD_k>e8I?PMAH;0o@9yemk8Mew%2; z!l}9vLC`F?f_2feh${{#KAQ-OE9MCHw p$>MJ;=_fE!HDm~Dph7Nm(FoW_=<-3ExTOF9002ovPDHLkV1i=wOyd9m literal 0 HcmV?d00001 diff --git a/icons/chat_22x22.png b/icons/chat_22x22.png new file mode 100644 index 0000000000000000000000000000000000000000..401301c4aafd2dce50c873c1f1e9d7904095ce0d GIT binary patch literal 988 zcmV<210(#2P)zVeJ6o?Tcx3#-to;?^Dg_oIq(sQA+U@=9(3Q!w^VTAE^uBs_@nhX|&C_Fa^9wuJ zP0wwoL)X{dspo<&uihqr#E$}lj%Toi7Sf6{t7lC_=;CkHS7F#vr0pJ{lS^R-XDcPV|s9%q-_ zGxQQ>lhSG3t2q=%%_qai+|{rLGYE`xIPlhYchJk9Uq_w$qKhz-tRa^#Y7Pal*c#g> zFb5ltSHAbuhnKP=H#R(n>9~gI+vacbB+Vgzat-2TVh%EElx_FFd4$g0+fD#hVk*If z(zx7>aw^-QCIB+bflCP2|GH;3(M1n+lAVu^J*MK5B~H&C_n^Wp3Ibq+Iq<2n^1txK zQQG%NGr1~aIeNm@i-?gggvlcIUvDBT0tT+!tTUfQ(lWD`Q zEVjYs9ZbUmusGvuO!ueSvkOyAX?T|(*$Z*B%y`w)52xAD-6?idX0&Dm{}tJ6PKmt^ zsL;M4`BA{JP);g7)+ej7Mr)P-_4$*E|Ynw9SIp^6hh)Mm$_BZ~Eu z=c#iy03B#KAs5gtR}nb@f%u&w&+uFKd=3TFNXe&AO0E&SMk!Vs^w?_daOZX^GT*o?;p>Gb8{T;GZM#a8ETPYd^jFxzd#y03uAo z4X7Nq;?QZR;H-Myy7-=6e8YECD+W zf8|d#0v%S%*P(D7jAN;AmLoKO-3rQDvwY5f%$%754JzOmwEPO)C3gy_#2Cx~0000< KMNUMnLSTX$+}{!a literal 0 HcmV?d00001 diff --git a/icons/chat_32x32.png b/icons/chat_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..bb8edb1e0def48da5e1d84157bff27f4e121881e GIT binary patch literal 1616 zcmV-W2Cw;vP)F34V}J z8`A`xNfVs5m>M(5WP&8drcE8&XvQ=(F)5_Q5`xiKK>P^cIs2Tw_j-Gbf1)Mw(a}t= znKk>z+B56kvuCZDh5u(9?xxZj?}`oka+<1MS?v3H;|luakrI0DtrFVZxawb3zge`i z)tlFNuZS45CKRVrRmt>J(k>t>jKrD!$?t{QHSY-vOq#`zY66ViYR`S?}%FdFl z8FBw}K=Ib3ZSL6UDp^q>NFoA$9}d6tUf+}JUuvw#e}2b(*>#l-T2SriMIv;`OC$O-&}Vj^cL$2*bc;%_~U$ z^wgIJ=z?~Vb!mTPzkO%ZS&Y=Gn$+%!`X8~!4lHCPJJL-6c^I5LBWL`Ztv!e7yzhPb zthpwi|!*DI% zob=}h_sn7s@0-VR?~iZBFmV#*9y$FCeR%F59ckW8i`OM&VK`Q9PocGSSycW)Hcg!! zQH~L){?$+aeWHCI{pm~{mG8=~#chcAme?t3953=9M)3OsXnya?+ZchKo{Jx7hA&?W zYOpBsgdqpG$E)$+xNib>CFd2gp8obCOM8F8h zQTFIwL!(Q*efX;P+E+sk==iv|lbI%%X#hNjl$@|63{#m6m8riwoj2yudM@+XH}KH$ z3&qc;MxI>3VZopQeOFkk?`tj8U;?~=iPPL=7@`covpd<@Q?_#2QwuZVESWRM!bQzNiUO3O(~TvmIRZTADIE;7z^qh1g>9JV+|bw1%}R5-E}a(ab}rIknU0G-0f2c^K+bCSGA(FA z5CL#RaWh<7L|K6#2}F_vSyHA;;o@{&;34oF1X+UbbDbCkp#=?m?NM;@jZdx~Yxp7; zKnrg6Nmqa1a-?i^s*(T~;JpZZdQ$)sXn8;jl4KD%0362v#s$G3aWYQ;L@<-#n*c$8 zz!*$0?)u`qB%t$XN9(aRBO5>$24LYJY5@~L!^Aa!|F2)5laU|Li311lJP$?TaIGtV zFWPa>giA!#&oCs!bAkf#bD)!|=8D^(XqdaNZ&!%_@5UW5C5LqL3s@mQnto ziOc;#_<+yxZ9gb--jiNBie|12r;K7JCC+xV+9JeS0Ml;SuHgX20hk291;A4gsn9O3 zi|t8s)2pf63%JG0$#B%CsBO=sdDzpT_m-&q$GBDIlJKqZGi{$K4!#<|R18Cgg#AxW z45!+pNUBSTq}S5b^T)G&1{ z0nix_7=G-#puO11PzFH99U=lKEQ-LH$D?huG1@^J#yDuB$3}&AoWNZaQxe8sDRU}p zeS}KoVG=ojJlthjKT$bzwJNb(5v}+>sKEWj07U_-@qKXuj{uPI9sUDFMsb$@JVv4b O0000q*8(xhDjXq(psuaiNj;*pV2g*977Pio(*vGbp9z({xX@7K5a;)c!j zL|0cqJ$my6?YlKadcl3vy!wc|n&*g3t%SaTYLZmF{}0ch6>`ZETFgLGUroJuPf)ME z3iqGBM%l{`cttZ{6&zlD{EC&EN1#bF-5UH3E0c>`2gOyA_WfE@)h|DSAMp6ECMHK?r~4Yg7XXr)J7=>xaz*M+9P z{U>|J5F+6u&q2PpQTUC$JwzIQMg6Ag&QIQaqUMchbuTtA`qPWI&*paQ*b2N{4FGud z^eM8^lJMsBn*;baxK)ip7r&V zL-ALEvL%r;A1TLw!D`x1uG73HsoSzAVO4FW;{RZk&2O=aW(N3`ZvYap`e?62PW}J@ N002ovPDHLkV1hS@boBrL literal 0 HcmV?d00001 diff --git a/icons/check_update_32x32.png b/icons/check_update_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..b871852fb47edfda5c9b148a5c9954b9f8f9ac6f GIT binary patch literal 2234 zcmV;r2u1gaP)2VdW~i$gLt;FI=?M($-eNgjNAHjig%g`$Y|HiPlu8 z8dB31V{6*jC|a~)(YjDBt%w!H&AJQh!n!QN!d}>&o!z;dnKS3@l-NWfmuRBxC;8_6 z<9(9%bDs0$J%`VTj=Fpbp1Wqk_a_4eUo@(@a_scWA78if{w*DA*6xsxJh4kY{Mb&p zdhIT`^oN_}qB}Mo9bfnJb#VJQJ_ictVBDnDIx0UsAltf0N=D7q>LMl5P3RS^vP+5JW%n|{A-elho(`p?yY z;iDhCWAUxCUmrOtBo7}sg%$Ks77J22b{NU5gJPK&rjPRKVT7tHC`jn(NpYmqnS%}O zd}ZEbdFTHvdcz<2(TdM35QyJ5XT|clPpi5j`v!7iR84}6T`a|+`l1T1o;R7B7hOy^ zpc3;bOc)bp+=L2Zi4fh#dPpYI=&~q=RYv68`BOJ&<`3p_0aQNbx*JxTlO{}PDYad+ zA4`ekkj;V{rUDYbM`!!rTX_1pcaa`1Z@<@p<@d66_lGnz9VeBw>F6B93R~pR7+qnB zP{fj{f!xx*zR0?w{eg2q0kiD-$F9A0{H!8EbaW2zjhikZ9`Vzkap+3rvC%l$muBI- zDX6+eW++WI?|`BZ4g2X$X6WopQZYP0Q&T7PTN=fbNy9~|k8twT&etv=aHBP6?({z! zrXd}Pm^ouK<4djW9?auNfvPD8LucfO2vQNCsqEZ)6c>r6YaHty!W9ad3h{UugQ+Z5 zz(+71Bp$J*x3-3!kk0$|IcK1%w(h$=%OI7`i`sEbS(x~dS1TG)=u zmYpq_WhO%fNZLg-pN{6$5FUdAhr39O2y@+y)2OK}XV{o>YHO+&pLYlqRlRk`?si#K z73RIB6Lg=-fDq(di4bZjg0i3o+qT)WuZ^@T8O#?^ygC><=sHSi6^6!8+Q#GaVCS;v zhKH)L<#HgMz4`nCihAkP$rWP5v-{}mO_R#lD5}7tt5|-Wc+`h5m!lRvD6+;6}!$A@V>Lj8j6RP|;d0Uhxth)1FL=)@QN-J*Bl`eqC;EKzudElOF zSYMj@c*w)p>I4m~Cr~sEDL_>Ot(`-(98aORE^fhrQtWh&pWbm58+J*0QU#=RQG`G! z3O+;O$z>y&uc*}~^9i)F&>XdLWQg|<_Yz118O*!55^}afSAUvtN#JOA3d{0xa6)fVoC(@M^37ttute&*CUW^A zWl;-VS6T5$Ju4Sq213$$JjwC2%l_^heQBFW&>-UghIU#2S0bfE_v&O*CuuL$4QBFu zl7Qc!vE>M#5&$R`-FA za}$@2OE5bgAYNwD-k0TQGJ{=k5k&=50SF337pc0Z59#UYBa=O|19sM+a4vyxP(0t; zSy-m(8sWHwFXE@6y`OK*uHxn99{$#PipQ7Kp{Xh}Y9O62vi)!`2RjGIx*%1FgW&3m zhw<$0dQSEBe@Y&Xp8In=VcMfz{b=#h;lhYm_+kNSYT^jfAP^3)WL6bJc}ZSM zTvt+%f|jm9noAOW{b??V=)BW(g2Qj^W%;)+!p^$@NF*Tec{HB-(|&rkKPb*B5KP?r zoy3??8X}Uem-? zO%_b-$8iM$fdoLJsV?>VV^WnF;S}=3Lq1{w<-Rj)#@9XamK}=B_QoxWx`r0^llPl^ zeRd_$Xc;pqLllLe=t>+HG)>{Tr#6vK4`66Hq;wG|*p7mtYNaTEz;#_5r+_Ht5CRlM zVcgj9GUxhtt9*jq6UuXSQzx(Jb1;SF6F!fQrm3_h^GvD?5DR(sJ35GZ9@%!9u_WMOiSat2n2ZTBDQ1!pDZsk`#Q&68%;&MT`am!7aG#*Ye9 z5%rS@S|AG?Pua9}4PxN3y(vlk`qd0eBxL!hDZ&btVOo9+-2;li$rrHgEJLXjeJ2me z1ADg}ce)!Y&+}K@Ywz22@BDwf^5!+KJ+rI%wT^ip5?)p`mXT4Cp z!W)^xmx=I%?;msJP4d!P?v`Mz=fALHPyhe` literal 0 HcmV?d00001 diff --git a/icons/clear_16x16.png b/icons/clear_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..096cf90bd3dd041b08ca7bf624fca0a8295a5406 GIT binary patch literal 694 zcmV;n0!jUeP)#k;tnlJKzT7|E*VkY)c^5#B3p5jMqM%5e0RlKR)q(dQ-g(6hoF;h5<6wX+pzW1{@ zVO%2SYzF5QYF3@cHhnz6DFCb*QKyO1W|%}3f?~b^la4{d6zB~aa45=klv-)Tr`i#4 zy>;}QP&QB!aX;uo(W|eVO-Hx8^D3i3GvM8i<2>xMCDgc22p6Y*;>D6dK>TV8Lh6}Hnl~o)g$YUJv^i3ka>d} c+=RdJ4f!qVlY^k8fB*mh07*qoM6N<$f=NklD%-fNLRHP`#2+5B$8qHtN zGn!ZLr(r~d`{nepy_2HhzIi=2JKulwsn^~6_(nO?(AM7Z-iQad9QOHL*DisW@b%osUph0Q0UOt? zvKhnoutngYYV1Q8jA+2D8MQy|@4lAq_4?HF-?WW)d5cUrA4-*>={KW@s4#UkGiWalGx=tOqWv2cCf+=W#2w9a0bmyxoS7HHCd^H^=%3 zi`NrwsDuCdy z172DB(5p)ynfVe2a$XNg5S*F7*I&wETLfnEf^J9fgLW)_?s5B}tslHPbJ=_daURiF z4{if{Ne(_~T%{_q+PIA9i(v(AgoQE9chP zSj;{YK}Li%V#Ci~Tn4GF4~fAp7{KgufzL4E=;pUo)sU|s+Bd8jc={JJ9;_`L_m9GY zJgcJW0M8@TaUEux8Ev;h2Thw>*7E|lebSt2o8Chpop2j4jzd~$DN;NS&aLD4wjHWw z$X6QjVeJ8CFfP-(#ph19>Y4^sQ=uv%Km^DlIF2_r5`8YK`r;G&PKtbv05Y3F4hP`z zfP5Kf-_wHdgf}*0_QrQ#$_-Drg0o% zQ;XsH>=JRZ22R(BGn){aT7qMT&qhJ?=o+H)-x<)nV%D}X+1^GC0&;S55$W#-V`NBU z(239$lE6&%0n?xNchpQN%gnxVlj%~5F$NGAu1qeYO(!qWi&Ya)arhK*Nm1t%Bf?_B zt@;`qKJZyoHzL*<5#I_}ySRQuS#jnY?VS;OO=Tg%;TRNAg{Y|zQW9(~7dm?*a1fIU zLrNmoSV)MA#58^AO*^V5(%WC0p(2j$KO2{3KRcy{RU9}M*9VMNkvIW&gY8>X(TmIFmwlA1R%Ljm!mgn-8hIIS1s zEOq$;2lP&^!avVl)XQ@`+Y1`XTZtEvW}QWJCiGYQa>kx%F82$&K0TpcSuhu$7_n{t zxo}tqmaij936{(>w`_dM^05KlA_(TvTt7q*kwy?02US&JweWzdKn9Q(a6u)GR5f+Y zKXJ^>@K;xrQzvhY&&~4e$gC?m$dlfyDsrF1OEKg?S@isFotm2d{`+^|)mnD7ye=rp ziF8B$j3I{s`}p~VHa_2--WLo_NF);5$9dg3qDf^QSom|jL1}!x&l>PL5sxI0)PQ^= z;Ce8T0I@f8qBwvEBkE(k&3eIO?^K6x+VyKX{eKrm=(V*5PQ>aEVyWrvLx|07*qoM6N<$g2C*q8vpYN+g5DbJS%6PLG0SrUsdbde|*>0*ljWm zuFm-5cTIVi%v^V=zVWext?m%R3;9s*d!79_Y?1*O9` zz83xo^>nMI_S$Eh0`r_`L+mxQO0J{7fIES^tGr9|$_&&Htb;C5c3@jr# zIz|_$YuB#f`t|Fw_ki*7ag+J^d7NHcfFLd#++Y>DXoVGQ*5V{~)`PCOAJIvydh>oE?x#No`Tc?=H^VQ_E| z0|NsnD=T~b9w3!U?bK>DT3TAr*4BpZ?r!8%)WgbK3>%&t?655K^>m}FtBX0Wt*wQx zuP@#MQd3hc-QC?G5{U>I3kwTOTU=bkDY}vQsZ*ygIXMY|KmeIcM(%!yK%r3B&r8jKtG*Ec6K(ZtEE-?)GOehnQRoh;hJJ)%cxX(`cwM8>2cm&>P)9zE*y$4}adl`B{7TDEMN#)=gy cHh$~UI|$XwRgJG^-2eap07*qoM6N<$f)W);KL7v# literal 0 HcmV?d00001 diff --git a/icons/clone_from_32x32.png b/icons/clone_from_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..2415da41ae1a50c7fc203a9f179368d4a48888f5 GIT binary patch literal 1919 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 literal 0 HcmV?d00001 diff --git a/icons/complex_32x32.png b/icons/complex_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..6993e35e63567b18ad4680b92f546ec671bde3cb GIT binary patch literal 326 zcmV-M0lEH(P)Bfb`2wQQ zxetI3%3`s--McEkz6ux!0RV=2I`3?_eQZ3{LsvSY;Qi)F0L=oVgaKMx7GR8L0oK9* z?`IY;&kGG$S-`sPEMVVZz;T=`;5;J>h!F;)lvzN|g$2}FSwQdoVZihlU_1ht_&5Cf YZkVS=0X#bQC;$Ke07*qoM6N<$g8OHTs{jB1 literal 0 HcmV?d00001 diff --git a/icons/content_16x16.png b/icons/content_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..0d8efd8089542a3c074d31b2a849337f11cdf04b GIT binary patch literal 662 zcmV;H0%`q;P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01ejw01ejxLMWSf00007bV*G`2ipY+ z01P?ubwRQK00I_CL_t(I%axKjYgADb$A9PE>x`KqqK#9Th=pJz>4G+jppjHIDg6Y3 zjfEfx`3iQHb|O|OOpqvXDa-_tsGt$ifQgw*oXo;(NoL-A_gKuEC55Q}=D>mfau)al zrlz0qFOaF}*`-L#I0^!wf=k%XPw!tl|2Oi~^ei%Y=dn&)pG2boV+3m*&llcmB9Rcp zVgL^q11N}eOpGlL3`D!TPyki7a>C2yWxjlTKQA}#KGNH_ zZn9e_qkxEDjesiO*2_degpbKK-T^=|nc~gNL!8(Yi%T0cTdl5Br&?=Jkwd-iWdy`n zoQRM-;yMAbKm@qhP5az?eZ^RdAh?}kg}KZp)Q5+EJSIw4`Y>E#fURsEHl`as%qjK9zj9c;Y1WgIhxA+29^o*7~FG&MgRZ+07*qoM6N<$g4`h;fB*mh literal 0 HcmV?d00001 diff --git a/icons/content_22x22.png b/icons/content_22x22.png new file mode 100644 index 0000000000000000000000000000000000000000..ed0da54e62940edd81cef71727d140d77bf01471 GIT binary patch literal 962 zcmV;z13mnSP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L011Qu011Qvs^VjL00007bV*G`2ipY+ z01qc4S1%F(00TlvL_t(I%hi<2Yh6_o#(!(?eUem5EmmnYabgFGQ70B{%(M;!aUeqJ zv|~pOLqT!i-ylVBYKQfKIwN2bti9oX_nm(8qrcrh^VY}z3fBNmzkTV0YA|?7l8R6Q_xr6xL5>jPs}vKHIarV(2RwsEAq6>-gSopD0!Ua<6pI(moq9e2^t#)4 zF8OKgHZg)S96xq1^CzCdWl;cvECJvSxe@0AB;3J5;=6CJ&7FPc>-!SGZnwkUUZ4K% z4$%w;uK(5}U>M+lnUD-YtqPEVyq+WR;E{v$x?AjScD!c1y-u&UM^*O8A?_b`Y5=VE zASKAT0jfnHYU^@#%6>twvrd{lnG=AjAsZVT{BZSi-0OoasG5XZ=Iw&D5gSNgh~Ez# zqUXUGf?8cE3QA#njXRtt>CC*ur~PR-b!zc4jyjnyU_>1~FoabUHmcTLX-S8KVN@ z8Vn-?DJ6dV@)OeH(v3$BA3jD^Rr`elFa&~wV?-NuNo)YyaR!3Gtu7p7xQrx0cF1mo z=Aey9G+-!*s>hWkMm>>lpg+MGs>9w@f069>!;0^mU7M`dr zQGlUCL=XeXAgyohaQT~`_q#!Y!eAgcU{L<0o!Xn{f{Dhib}tKxSg^7B2kFu2`*~va zaYC+;?oK{h9b6k`!N^l*L?E<vMI5ZjQY0d;KH(C*MiGXMYp07*qoM6N<$f<{rS@c;k- literal 0 HcmV?d00001 diff --git a/icons/content_32x32.png b/icons/content_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..00452216d0048a03edb542d33b6892843588e67e GIT binary patch literal 1472 zcmV;x1wZPx#24YJ`L;(K){{a7>y{D4^000SaNLh0L01ejw01ejxLMWSf00007bV*G`2ipY+ z01_!XgKh}`00lrvL_t(o!|j(#i(Ew%$A9P4?U@eo5k(vbf-YS6T1njbKu~lkD8BIv zxKwoGGT=r(f#_DUaVruO9~+G#8VwPWFg~JA#?162lb-3mx2ledI=64lfZ#$`+R)st zx~J;=&+C6q!+-ws^wAIgw(K)UkN@NP1>mWpA0D62XAdEu)e8eM&`2PR!e_@`d-(o; z1;C>(y=%`t{s@0eX0*{zDQ^T}JW|?r%BP=yPiPucgiHZyfEkFC%gn#QpICnnBrE&T zK*c)gW)Hvj`s1H80Q2c3zWZ^T_01hrhf6TX1~;#ax$~~O7>Qyk1OXFJ5!4l^78F51 zwAvRz1cHpMQ1gWuO2^?Tqz}E08M`E8w&At zkfN*(fl4{MIfd=5HCq5MGiL3K*v+|cewEmEm<~yz9=k>$_KD+GH=!s@$vqOvzQ~Yh zea>K+FdA{wEw>S8v$OyZ+m0ApR!^Pa+hec(>!p9>`8Pmp0FYBl>RNWMYymLY+(bl* zrEW@$iOJKwojk+ovp+H2nR$l&g^4!6vMNE1%%N+qW_^8) z7w8`3j3MA>s@OVdnT(AFb|ZR zdR8@P%u1b$4F|-VPLScoz9&HOOqch{E_+Prm3Xpc=l}i_fdc}am9RI05e#;-(YS>G zOFFopG?ZKRzF;2D&huoJhkoDeB}EcLB{VL(=r75jwL+J*%VMH5q&(~;|1R9@@-Yqd zu5RTB=-8WZc+)BgTy!=pve9T6MzUC0t`)f(G?qHfCOCI~W*Y_@$dhHkIs`-Td(~px-&|*~H z*H=UvRu68`tZhy?{o5vu7@1+R#S^V3pQS3Xq{;{L62oW0z6{MRRT~WGWrd<^ZR0YH zif&z;Y#)*^xV;PkFUV0+{0000w*1Lw8xA&i&o!r^t2`qkR zDFV&_=Kzv7-LHO2O_colzf2z{HWInf`C8-$x6{ue^`zmKaQL02`ph4 zqJmzxOSxPIB_~MJ6yO{%$sOtQN}#J}l1in*=H@29!x(eJJJMx;2Oxq{Z#1mb{#M7r za^%PnfWQfKl9h<$cVtT!*78qx5acGhc3$Exl|Kr+NlqAgVpsYCf4~WR@t+0$pm^c= zd``VyCrM%sA3nkoF~D2}%WTlZaV&0SwDeZhaG_8@84d?r-`{@&lvVI43vUB4mb1o~ zqQB)Z3Q-1w0lRyxewG;PzPSI;u28w&#h0a;hQ&a)Uxm$pbQY<;f|CfiGXFc zSTZ-l{Rj6ej~+eV1>WhrKMB5ON80+$n6az!-G!B(@rz$;lkIiUw{4j_51hlKY#xG{rmSb5PSiH zuV2@%U;p(R7<>nTpFe+o`1tYTClCP9pFe;9@nge=4L^ST*tBWW>o*|q{{8!Z|NedW z@Zr&;M^BzSdHM3?+O=!n0>Qg?eG?|UdJO`TrcIeSd-j{RZ)eV#wP4}GcR=v|{ksnz zckSE%{=o4*tv7(%U7>nzIuJ|;K4(O4gu{veB|(n6DLleK7ID= z*>mU40o{M?+O-=uZrlcfyZ7$gx&83r!==lXJ%0T7=g%K2SFZf^>*uP~tAXBIy?XVF zmoIxK^sZaC?lyl$G%#F5OM?7@85o(ERjpmUeJUC!%~-Z>+mY|T{{6c+@k=K#cxQOJ zIEGZj^-jK8cE>=1ZToHCw|Og642*P=XIQ4D9s6G2`s#ZAaj%+&}H;?!Hb`c_Q4qL#h*CRH6>ek__2M{ zHLV0~G9%4CHo676Xg;;?77-=iQh<-^id!)2ILToLa3} ho`1gLaPijc_x7J}q`y99ZO#o!^q#JMF6*2UngA69!GZt) literal 0 HcmV?d00001 diff --git a/icons/cut_16x16.png b/icons/cut_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..79d228e678e5a59d58d1ef6bc6ffa88de02671b6 GIT binary patch literal 282 zcmV+#0p=6L;YJu8~;=fx(EG{PCfWRM>PQ* zYqq1YW^@pwKW_cqXMa%l@y+QC-Got`OkMdapG;9qP;SjuHP(zAf0U7Dh}AJ0bc1fp zSk)`9=!Yg2*urZ}BieOmOVK7kCK#cBj14M5Ds{^g8BqpgmKJK*BjQt}SB;{xpFdo4 gNk2dJD{`$2FFyKT5j)F%EdT%j07*qoM6N<$f=pO;@c;k- literal 0 HcmV?d00001 diff --git a/icons/cut_32x32.png b/icons/cut_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..4f5ca567216b65cced5761f03ebc416c6be70f94 GIT binary patch literal 781 zcmV+o1M>WdP)kdg0008iNkl2fA>uxvAM(0r~Jy9*CoY*%s*^Z5Y%SR&Jhz#fq~8unbhbnhnYxV69ZG3=R; z+S;CzfWKJot8Pi!1-rZ-ysmo(cMR&qlR6drVBgZcMoZyg4C2B%tv>G>L~vkAgZf9? zJUIr47^!M%(~g=0hlUoE7+6&a5&F4bXk>8(yx^ETt=-ULJhK5d4$kiE)LE0W;S_1< zD?OW=#h&@(Wkh;C}@~}18#7V{~MK#8clv8dTsP;Y0>jj zhjQB^VADguYg3X|@y}ySS;xpkq^q*F3nyZRP1s8ra@m zrZ%#R2TI@p7e!U8G&J)r8>#B>TnLv9TXwF4J=F3n_m#j4{~)qPTg?xwvUsM!bsoXH z%AZ*)_?Z@7sPI_~7r94Pl+(mgcGAFlA@Li$;EI5lHRftIbRRl&eCzu!U%8zj`{0UM zAJ_8|r(~m-f52Hw8dJ3sJ>jg7ZQGc{(I3%Ac0Zmw81)d21dj_lKlOH4PNq&tf0J}J z`p7)SbR~1M5OONwa(GdA3HgYgJIz1~>dc`Zk_1f}aEku1aBO6m0n`QyEeY1g)KE4YW!& zVGxoDWAzFK76DO#Yd0M`=<}T#3IB7laF|tZ^^n8n;sZ2)2AiOwym= zNh;}G2|L6EF~U)v;Y?xtBJ?MpD{mhCLvkGsNV8>X`FGzu{4c)%y7E^5s#DGP00000 LNkvXXu0mjf5vXk4 literal 0 HcmV?d00001 diff --git a/icons/delete_16x16.png b/icons/delete_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..9d0da2e25c7078b24eaf22756bb89b3f761b5ec9 GIT binary patch literal 509 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 literal 0 HcmV?d00001 diff --git a/icons/delete_row_32x32.png b/icons/delete_row_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..f0cff02debc09b39d17b348950ae55700daa4813 GIT binary patch literal 1360 zcmV-W1+V&vP)YA(amWTO6gL-W zCkkEaK-dCqx=jdM?1GueoET&@1OyR5U>)tXy|#DPYwz*BBZJj-iCuy*h9~*u?fX9e z_MW@{zb*5>S9{8vry8FgqVGNX_Sran+w7!M%%fJ*i_f3&8cNW;qY%}D8KW|*eu6PN zcNfE3T!^pZpJ{lc0gjIt{^@ny2H2OU;ZnW^y5WO=xuny=p0)zNW~Sk2+|d2Zqt>Az zgDUOX75I6{GT7Iop>68xIG;KjpAQ{W$^2U~j~Zq_J8m@2j2Mab*T%s-`YC(Fg68IC zG&MCvWguQdLjx{!cH(qvEC1Qjx46!!<8h8*aB1QTYVDjh9(&@3J$wRnb#>@ZM~)mB zkY_vlJ$}&d7Ozi9z#Y11d(u=)8$0H=_M|DeOZ^|y6JQuS#-s+Qsi}b?DHMu8k;P&e zkk{PWiiY>rN$krn;Y!kUXx~U~0g#ydMj&!Gi|} zESvmlGt;9=s}+ibNK_fk=9s-OQQO!EnF2__ znGjGe0vf6_nP|e!oe=UiLRhbbpd~hKqSxyo64<&G;>KLHSJ}K-U88#2wn5CxgOHO0 zA(z(ZbjljlhfXI8g@rP&*L!$#2w)oJb7Fg@QD%vnktIG z0^n?|2z*=yj}(T77a>!K?FNJDd0M^$zj`%T0N&qk0+S}8>ck1~z+GSIZo9Y2dW;o9 z@46k%87#xZ^$EZ!0eBt{WU-(}IeYd17cM}NeDx|qhYzDi8z=zi(W|F;Gr~z_6*Z?jNMAtocZ)hfA?blZZvF&uph*3++7TH11? zrY`FZ%~Fw_tyOAF-m-U=!fZCnt5&T-LBVEUNlEFIvhwo!Ll%pUZZ^J8fTE%zh?LsR z2@!;3VCj;#smA8B~))6?Ub$9k;)lKuk2e~(Uu Sj1%tw0000&tif}lL_wV0Bp-@oPTNQ12YIb&Z z%k$^Y!LI({%9ShkFJ8QOPb3n(aJgJ)Zf<_a&qM;Ob~>HEbvhk4fByX4gT;CC=53Kk zB;7uruZy3F1UNt`<%)}oKd^Ypi24+PKwz}n?HoT732>MJAd|`H*hX4fnnJ79f}e>B z$j!|Sk8Pxi6J__Xe8e=P)Ut zU49Kc-Yxk4cq-hbQ((-R0(a3A&}xEj%BkRfTtI{DOe_EbU&U1D(x>(TUepo%(h}fj zTmS+?CSd@QIKYz2eCdA$82cF;Q#PVGbpxKJZ-cvG55D;$f5w>uzw8pGYnX0kx`XLX nrrVir8u$+uGhM)RI#bdMj8!Elicyl`F`!< zZ%aT0wP(+s_O9y=R#ml|VHnK?L4ajh*WbK(b3)hk2!L}H0w^vnMi`A7H%{EYfB#Zh zmgnZ@=PS`@6s4u5NG6l0TD2vw05ab~Mibt?eG5sFxbYnT3+UbR zR1U=B@pg27c}`AF6EerZfde_vRsa(N0gfFzhIFD2b){$zkdbDZCNxdkIDGiKCYl&#(XPNYpO3F(&a`wJ;L?fv`rJjE}*W6s59&YU@efbS73 ze@_9h5CM`AAgX(9UTfZTRjO2(7>PvE)r`?|Gbm%bJkJAvH}LyZ#~^@O6b<;IF*}9; zQIDLYp5Ubel_MB{gmj|x=TbC$+}N#Kw@d`fqzuGqM3u^wE9V3P5HW&8CE(46u%c%< zFv3Vi2=Qj5U%meefJX3_0#v931pD&<`TlBFxk$ykk`&CS0?UEmx-rHZ(x>HlF8DE@ zB5FE5A<&HsaC0?7L!%rK`Sv*!G-$x?-MI7JKR!A6eh0p(Y#=MY1WNoWs%E`H%um9! z1lX1g*OB2zC8U@DQ4nD0NoXpAt|pP0lR!oRLsmfwQstpwc};;EpNc1wBRxYHh(0xZ zFfk zWDcM$h=~99fV63COq_YsjN8aaBrtQX=18J7VU`nEWX7AB@VJR~7)kg6m3FF7f&sh)|Ja zGFci(PA!TVP<_WR+!ukW|Eo421v!xxdqJN7Wkj1$ZGq~Z$Z|h!LHa};*cl<9|xp~%cm0CX) z-Vp`w{pAHyHg@3;$g)k-bm)MQ1XaM|tY!DXZR(>)j1h#73}1@8zcP2?`p(YyKq`~r zY5WQpLyP(y=-XqcAfOshly~F1rYHeKxq0tWCjb|uLMjs@m(OFw@+5bSa-~A-Wt>bp z4a!#(rB#!5BnvMAaIc@gBJ;Y$%fv&BF#r^EMMid>$e7Wy(GURnDu8nl#Y&c0{T}Uf zzE+2 z5+TOoP#_pYg>NZUenzzl8gQTlt-Wi1qoxKW;FR!B0fHKWDozoMRjd>KhP5CPaIsgf zIlAvCm1-Ff{*2-y$HV)Nac$akpx@8|6pLm4MihAx@w2#0KCv^Ii;b<6|MCYlPrj7e xA`M#5E0?7wyd!`CK~ literal 0 HcmV?d00001 diff --git a/icons/fileFolder_32x32.png b/icons/fileFolder_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..7bb7ca21908e38a4efe0dd4cdf9f40f8beefed99 GIT binary patch literal 1191 zcmV;Y1X%ltP)2vat}66c&PF;fsZ-M9iOHp@o$e>9t4-!P4rj zECelLnt&LDgpjW*$-OuCCO7w-v-h5vB@COzVn|^l7CJD@p2J$tn&+8k*5O}21mLr? zC(o~k@nu1P0(I?x1PCHRzlgtn`oT9Z{4EFg=!Awf{B005{e zM<02HmtK5MfDRh%w{l?W>fM7>B|z4)wcX-!KIT;1wXD1hixxf9xr_c6Fx*hyWpER?8JR%|N1{ z9Z)I*)y%M-*xuRU*47T=IDvqe&~AtzvvPHl~m6p)J@HEk=SSRzk-g*YCU%PoF(?`NRLct*dD?e?Mrb!g^YA>+Wxe zdbU2REh^p*o*V+~?cGIAeg1}?{P-=_%L9-FP?}^lfSO)oG7y2>3Twq1H5!8|p$l9% ze~GxiwG4x^|*Q6(avWR&DtJLU=Kff0xeP-zMW z%W<95`6Jt2HK0IL6je~f70pe7wFh*drSUO96;K7TF;*t)h6di6 ziwsoXR1B%#$uqSaf1QdV+M`;Cg3fYGj@<}^xs)EHk1qK#j!3P7V{pR@PN5B@tBt%}~7KCm`6#x4XdR0JhhGI6($}#QC-L6*{sI4TIvoXgcM<5YG&&9)6iMO|Ja&NW6dbx5k_wKzH zZzv~guB$P#0}qWsli~Uo7P_9@{Y@e$<2sYa3}QVVVeSpkCeKBp?-K|SWpuI&fFq)& zmS`Z!+=@32^_#o9>!;IH5b0bG-v_!GIL}3UgoL$clX|RE?9Hn;9jLf#2q*(u;NeG) zT$m~I9&p6HLuqDcrU0FbDQ>DWhq_gp{e9p&{^{}G_!F7lejb_Oq%;5k002ovPDHLk FV1kkxDcAr2 literal 0 HcmV?d00001 diff --git a/icons/file_16x16.png b/icons/file_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..3b565ea950ec384060616f778fced0a1b3d51486 GIT binary patch literal 478 zcmV<40U`d0P)4jFk~NK|FyNWDs*&om~a^YcEt~Xet4@8{v+VQ0Vv1Ox@Y$M5P}PX69r$Ov`JBJe^Owle!MsDO~Buu-9|W zz@2;dJ{zY`pFXUYrxY*#hj{ADnKSTgJD!vOVJOe@I|+D=3Cl}Ml3iYw9LQ!RpUcV8 z;-aLINf{p-ld;iJ;hIb&WMO_@iiN@^3i2m+$86-Gey12sS-#?&}o7Vt&^k%4y*AZC1g zoOeVoXRF0oBP0Ji|C2VjM#d7>f#OvZ9Y6pR?sO{}i20*VWv z%$1p$8N@L+C1t>N)Hn>MDH9{H?y`ump zihy9wgWhw+`*b=jxm>PVjQPA@sC{R4q6skNW-urKOioV9tQH;QCH0(QvpLKU3g8-5 z0N1S|l$>gxo|+;>RPAsdC6)ZQgl!@Go3Y;Z4zzhxzNl$mTH!?CZtvnY( zMZgI(BP%9gV(^-S%7hYic6Lg0bF(xyHcC@dleD(9NPBCmbaiz}d}6}W=(tCZ9{up# zxpVjLDF8rj*Wh|a1Yw`oNW@|>X=rGW9R_XN(@3lxI(YD-vuDpfXk@!?Mn-J#=X*2f zTGi|WgM)+e>#x5G!{)LlEULI$b$O3G^2o=hPMvxPlF$NzQJ|nGB*1Bjf@3e} z>FJT%w{MF@4}nyEI+^rFHTSQ*_S&aL9$5ed=hg&J`F7-@$`L?Q>hA8A8#itUqgdO* zGpUpp({tk6Z@>NJkt0V!kOvMVkhqqL8Zb#YR9fZH4;RD_0O882Y586 zC!ToX=ZhCF8u`8jRDt&f-v)yK(!hCObYw&t>gzq;UAc1Q$4i$k?VFtOyhIz+2Ayt6 z8_bvH`M!s))g$~vR=m0kUh#qFmlW4doH+5oetG`{T1jBg%8sSm00000NkvXXu0mjf DDW@4U literal 0 HcmV?d00001 diff --git a/icons/firmware_16x16.png b/icons/firmware_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..28846502bf3ee29c3eb92ae0ae4938eb720f150c GIT binary patch literal 818 zcmV-21I_%2P)07WylZQHhO+qP}nw&&SqF{(SL-P9cOAw9o$t#9u*Is4p*`Zwa^(-X)-{a+qG zj_Dv0z4qL?^=Mk~u%Jd`$|K7P)`+qn7Rz6CFHHOb85vLAE|(t$gDv0p?mhaYb;B6Z zV2l+HMtyc2G-TIBT~1AmlJv!X;|d(Nti^7_B1{TxQ-mVSp1pX5NF-z2?sr%jwHz(k z&CrU|9F^kAGwqHweKv67Ku^yVT>>4G>tQ6X3)aY|V})!2=94@4nSv~q;G4zbLDvWE zQ9Y?fUdhywnxtJqd+n>pdqVb$HBa26({pL6wYR`wHlo%5E`BZk|hrH3sSVUh;PLC#V@dX_h~vLBm*HKNeq`O zg-&mQ)9+=>b{lLCH{QMb%$Q7e@OUyrM`tjxv25($e~ul_YZr88hcCTEyY6f{pJRa{0S;+p{wQFbG wyZ6Aoc=57s)PGRmmz`tBj>VAW@2>sAPY~wU4a*tswg3PC07*qoM6N<$f-%09O8@`> literal 0 HcmV?d00001 diff --git a/icons/firmware_32x32.png b/icons/firmware_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..caa02203481e01db51cceea574342cec83ac1d84 GIT binary patch literal 2440 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 literal 0 HcmV?d00001 diff --git a/icons/help_32x32.png b/icons/help_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..14efe93a7de0738bd2f0fa2e2e1b1ca7f54694e0 GIT binary patch literal 1999 zcmV;=2Qc`FP)M3^g6onc5BabzSVG)&Ie)tTy|C-#vc(xPtc6IXX{z-s#q2vA9EWa&pYmr%$bq9zDXHJ9lv7#toF0 zm!qt#3?(HcC@wB0&}Uks?`RM0B{EhzPkP?Q8?>p~s8OT7G%#VtK{va0?Hb5{#bNwLZS+7zH;TtHRei?YcF5EOiD=+&ZQM%3yY_T*GR~3< zbQ4lopfO?c_@D5_TNJ?Fy~|m7Disi{O~f&JYqyxk`^zM$Yc%1ZdPH!_8g4bkOfEQ)6nS6 zAOfV5bYEmb_H2!RM$LojoXUsXMM*SsrxK(0nK9|uWlT6)hH-~W*xx2NAIwHpVJX&x zT|m(BER@&sy92csrKHB6hM zGPQ6D$;ZI`r5L!c41ew^#&5eU_;tGxKW)KErjf9fM>r@`T*lfYqJIe6o))F{v zvcP_W5%%l!=og|!55Iq+qgM_@?in~NF90vm89H=W7LN`tNw8ZU5AhNO+RlxF>+U3& z&1R5pvY}F`vdJz_pegloNT<%eXRvO*O#DMs0Ez@jS&0k(AKLEEj{wld}R)r zxT^4LU@D$kt>9&-zXEk<$x(MU!z?N4Opig0iKp@N%F|#rgfM6{no_dM6KGBk3Y7`9 zRAOoGorVrUW^@QChFzc;B0mG#_-N5u!V~x#t(;R~D!U0@f=_0l=JZ(9oGC}O=~7gg z5`)hsp2w$SA|RT2gjzP~CL4siWS1wG9zkdBG*?rCf9D&z*psBMGN_81{8SJM(?YRl4 zH79`xywC%-1OyM*=74PoMsHH0L+~pIxMbq;jeFolv`CIR^OIgifaSoMN+BpLEEIA; zr~=z0l&~u35NLWKfmWXRXyL9xcV{VjEIx+bGSJ#370s8Y zpwXg4)SDZJ>N8~jCNSYFJ{^7pQEBQ*Wq9n^F`*9#oscFOz7bKZCT&wf?4v>Z02Yb> zBf5rY(RZx|1J)P7DKs0U*Y0xf05E>55`FyA(Or@Tk!unf%wv@>l@S;lfgxT|eEdl_ zCE#j;>YJk&L9lqVJ!FSsVFn#X@*vih>eAox3!yzyeeY{iA z&V^OOv}m-Mb{qzC2^qF^zwj13_Zrd|#Xm;GI5Lg7eYFZ*0@ZNZqQiGPwD_J8_%b++ z-T>hx9385FLr?|=1ZJSOR~p(ZlcCO}FoY)*Q0#@Hm9SFYdXZ97a3G&w=x6m+)~hHc@kQ=B%{gV1lEkA zFd|S2J##zV+;$*gAr>a0*Up4A!`X~vf(xlSJdR~!XmA30Ime>etaIqGEDD4DGtnV-MnlMR#Pjfyw<$As!UE-udS#~*)q z(s@pgh|^o+bvLih^=Lcppd-UC3=_I{@BWvUmzPW`l@_zH?lw!XN6a%7^qUPBx7lrr zB@&5@_R)UsoFk`4%ut_eK5rzSFnrE2uE)@fBh`z6;`uYfK!za{&5{0j(0br?$I1sVVV002ovPDHLkV1n}8u;2gy literal 0 HcmV?d00001 diff --git a/icons/insert_col_16x16.png b/icons/insert_col_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..73c13e0d71f87528c724f49b17ee667107eaa5ae GIT binary patch literal 745 zcmV(x|^f}+i4OdoO!AGfA6u?Ivuc%62zU=EEgJsL!S+Kt2)visGNhmx44lHWDicf-nIg zmwslE2_iPx5ktFS|C#%;-EYD^UkX4{OK*R_DaRmHRaGb|2Sf>oIrK%Hft`w`bj$`r zapls9$P{6xegeRb{Uqt%I%_ntaN$DQ+S<`t*CjT0YsiBpohgkOjcrV+ZzJE`$$>wQ z;-Kn%kU_#ei}z*thTph

jr+F$3H6DH~RT*g|eD@>-QgzHIT@VwbgPhg}{qKmm5L z1m)XY_JxH^nm7^1i!dw)y%)+wZYUCkE_Y7}*j#XVrz%8lD}wUSO(#L*a>T0!GIs1( z8k<_Et8YwbE9#ItEWR4wK>NxD-WuLaQ)3-+y@9s8?mz2xe$+sQ4jn?iWRuR7*w)Ll zipWi6ey!4ZXQs}kbb;@yT;{b#=(^6f%wOwD$UVLK_3KBbptE^fK4I%Q?zAVea~mh zocEEbF8tKw+HY;{7~t~QC=c+v3aFAOeu_SZ<*k=c1+DrX<-da~4 z-5AI-J4IH#FOobYviqb6ukysy=BcuxV~H&J>%(GL@|XET_lsrv2h)Xh)ZbbMo=ZPw zI&j}l(_dyZws5-i?N~T_SDDOdL=3ZEN9GLOm%Z(Tyh$s!%p=$9zSNLzVKjE&CO>GD z(MYy53Ondk0L{(KlFQ`~k%(S}0r}~VlM5gW!Pk(}U;|zbY)cr-YC-`&r_iFvYsiOC zYFqr;uchOkSE;hW{TBp{LcCs4{srg@5oT}-!=62RJt?#{~1Sg<)OTYHkiUSx6;GG#MjFxM$f2=5`h!;3Kh^Xm<{yyixn81#F>Ly3^7 zak%Qit{%m6(=?z+c~a2jN9bZ)cSD|@RL{>VHgdM_EO@Z-$VM)@m>d-U;3<9;*+3YE zjO1!MWJWn$PQ&(2iyz*hQNX)hii|G>oiLJACImDsQ}*%wo<%f$zM1;IdP=61a97)1 zv@UD?I*^NOz;PT#(w_=K*2>N-5T_U(ZM!vgo?y>`tzw%u0}K0ZV}e?88Sp%V=D`mViV= zm$0&g&T=+ycnwdNfC7a=5c;}%sUBZVgk&-)h9IXSA);meI#0A{kuWct&xq{5XqL4c zzb5j|0z*3kKKAm@Z-v5#p8Qj5jLH&m|3Zr zTdO(H1!u0n!aH+ZFKl3>&VVOST#JVBi*z6e0!m68B&3J{c8QSoeeRwR=llH(9$}8$jZooX#rp|CpQ{VU)zsL-j zmbjS=Y0o9?ejKjnlJz{uhwM9-;rUr9T-W70y8@oSC&li2`H`b&AhC*Z4&K zg5rlriUTx^xh1e!`IUg{E-c&~=e0)yCX@+>E?8h=KpP#ZkPC+4;Ut3bF@BK_nAb1n z#BriQ%y9rkfWwo&E$0{CP)x3J*xymg`{zmvo)90_m>dHNqaUSa-1tR1aNoW6@<#HP zAkpoXJ3fs4vHWIBgIb^xG`#~QF+ot=ZTnExAnSg%LgnnSAX1z%M-M{9tFmQNgG%R0 zh@dRvM}*a@SNUz7z1vT;_3XMay1OrKa~7ra-oqdL{`9%-om=-`ZhE}2@ATi=yLTQv z)AKvN*XxVOm1BSJDr!w`)_A%%F#3@-jgRq*456je;cSpS?fYIavW0wdrEZ^9JMgE& z9r=$Aw(I^i4_(=Dsn2gu{t`VVxJr&rH+4_z9F zTYJuwjep^5gGZEQOLbY;yW^;y`TD`zHym4e;lYz5kiRT*x~wz?eKRzpyL zAs;>DSILd6S+iy`pMo)qQ{S4pv|ncZBt4H$=B9q?R?Y>El{+$_Ov1XVm{nWpa9fo_ wZ3Wa7;>lS`@!1nS85Pe3|Mj!sr{w?q2jEA!!@Ok~9RL6T07*qoM6N<$f`to0X8-^I literal 0 HcmV?d00001 diff --git a/icons/insert_row_16x16.png b/icons/insert_row_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..f6facb4ae76c85be6aa932dd73bff6e0b653d0e5 GIT binary patch literal 682 zcmV;b0#*HqP)=H?&W)zxL({A>JJ`(qND5Z643)U_gpA8lgIH1!U%X3du0 zGk(IvKg5$KOqKI~ntGB&~DHF$vNF<`YA3bX5V`BI<9{qDXGc&fZ z>6$8+kx0c#l{x!^lkXLg26fZ4P`=59q%6V1qP8Z9TlCN(q+%BFswCR=2?Fc8#(4ad zrW7w{i@fo@lJ~H@*EnxzNknYvdpnS?O1=#5Z;`H7MDn&RSI!HhyQ4)0Hi|s9y6|l- zZ?D?BIsLe8+c&)WasJTAw>Py)j%MuVm0YuBi6QSKcs*@#)K>7&h8XP!Yo7kX;?jK} zZ413?HOsDzbM^Rut!p7$)L38H!tt9qOiEXB@2*8WzeHsMF5ESs??uItTW7do0o-hs z)g&}eZipCjk>Z_iLTZwlco+#g+$w;}>V1;jbIXIY&vKdg9Mo3?#H-ry*V}Bj+CnKs zDgm8K1&bKfDIxyJ_d>eIL;DG=${71S{4E>rZDP>bE*i~)_uat5UA;Jk0*XK4g%D2{ z1wt{y)tvdbN6xP>cA0iV6{UO$-}gWwM2GOF6-i` zcCV)+JD85k1JLz#Kn+j{B!M`Vt0*HP_pg3Cc-eUVk=?eGZQ_@VAK*Xu0a&i3Bz0wd Q#Q*>R07*qoM6N<$g2~QN!vFvP literal 0 HcmV?d00001 diff --git a/icons/insert_row_32x32.png b/icons/insert_row_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..00b81dfd8f17e1241d29c1416b065208367a970d GIT binary patch literal 1498 zcmV<01tt24P)yGSu>BbG^I(FR2Fa{hR0E(w(#ufiEild=l zs`?TZ-kgL<2mb?6lMnn4i*8QDH|xI%il(W=?d7wLj(i~NUB<6-K7wB*5k&o#=WlaA zgb!uC$Kd=VaeMh>lQW_w0#8+mbxEKsl8BqBwzd`(6%{BiFMk~I?1f}?Wp(&=*|$L( z37?K2d~g~;)Q5R~Hj+T=$e?^WN8C(>g@t&!+OcED3(BTcPXUz>zF0%}N-{yz*S7Lr zN1!s6M@xvCDJLffq5{F-J5hP&Ds_tekEU(R(Od z+1c3;<@X03N7UA>TVGICSy2hgO`H9XDSxuPt>|rj692q}bXrbLMD!#2BE5~cnbMLs zqpbo@03^mmyr8VSIA09Q-J922lh(u*kNn@zj6Q$vv4n`eM)dZ#Ga}K1=tf{gk&MNK zifL7ej3q^iX=6RGyo8^(-)qZw4`&U)s?xT51;1uZdnP_(T@8$=TECIp>i6Ux4W6$! z9muUaADkW2j88EVD7-8#3L*1B2mzmF%DEB1fd@W_ijP=}?H2$9H0ZjH*$p~wJ2i+( zj9iO3wILv+;g(%P=dE6TjmCEfEhqiTIu6K)~`~J=-tYev5;D=k) z5O*aARaW8GRAgNAJSDjYy~{@=dzQR+ zf*T*6B6Vj(XYK|MeR7%`UzzJMK0d{bsxyB4Cq}`~mrMBJGC{_lVt}8oknzK%ZO?wS zQo;{-4`)AZ&wj+&Z`a72an9LqH^KPhI?3pjD7nXGc+X7PAys{DrUzf0=Lvm%ffwIe z;QcNQNmks4t_M!?L!7%8#IR%qPplaeM}*hsm2FShWIXmBm!)FQC12}LR!I28O8Ixc zS?z5h0E^g;U+}*Y6$4CC(_*o^>MS-_R7i7; z0Bqm?U#td3g4qDXcZyRW>VeG#3x7bE#2-Fl#Bedi{hTwmq#iolROgK`owi;k0m<>Q;e6s-`Mg4qE4`{2G7xVmrJI(7P=Iqy7=%<7b|c=1BmoU)bgCYH!1-m^IW&M2&7898#K`KDXyNM;TiTA9ozcC8ySOyOs4AlqNUCQI=0pPEH{R_|Mm&uxe z*|TRs(G4r#O)Qa3bb9r_F$(Kg1`Qg7PXGTu{?9sfGIi{I`t;%X{IdW4_utsKaif?% zzK`(pR5qh?=g$A=+qds$7zv`is^0nmZ~cHr0OTW3qk!dm=l}o!07*qoM6N<$f@#s! APyhe` literal 0 HcmV?d00001 diff --git a/icons/internet_16x16.png b/icons/internet_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..9d81ef3ced5dd9143260fdb83cf227de22506c8d GIT binary patch literal 859 zcmV-h1ElhcU9Ziw|)E8#&*txkYd6>U_vFv}^Y7 zU931xgc&P9mP({)hFl5^4O-lF{HIvr-!d}tSONg-4`aQt^@|ltI*(j@(H6FHT|Y)A zJ)D~g(7Ut?icU~ARkU?AQYq;s+vDdome)V+x}R_g0Hvn-J*!vrtXRFKn{@WJKs7X| z)e?Hqq)7dIHsj10D#u&cPyuj-LP_1oh(>VN3F4hh)dWp z$Kn+|aMKh{njs_1T4+6D8PGy#f5i#w1CfP-M$mU%_w z2xC46*EBe}05yv}5?HWy9y;c=GEEb=6*Xbccv0)Q2@ighAr}&+p+Xc2k_1vykZCH2 zX+oL?aw3WnDroI#0b^{7lBJ>-D!~tDW^iW2#)O-qt*wmFxd`=o4J(*}ABW~3!VHQ6 zl$efEt%@{EkY#99Ac+i%$VSD)hu@81>&D^+tOzIPVpvIzI1?x$gMa~)AWSoGkt3H9 zq>w@=8O$@;m~e@)xe%{@GKhHtt*|)|P=-up(5n=Z5(vpr)+l~BI)P+t27-%mg@C@p zBfh4pF*s3+S0NaE~cmPEUTAmznI zi=oAgqTE!$>LqPh+1Cs(hL)Ov)|!sKW)ovm9vnMFZU?ZAT4`j{3u%_!@l%$`hk~(C z=n8}9$2d9RW5kWnR8r8qi`tz*+$O1i2x zH=1Z^FJri<^}FvT@b$Z=aqNX}5^umji~?t literal 0 HcmV?d00001 diff --git a/icons/internet_32x32.png b/icons/internet_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..7bf0446816737c6b98256dde7e0646c15be5ed64 GIT binary patch literal 2605 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 P00000NkvXXu0mjfUXmQ3 literal 0 HcmV?d00001 diff --git a/icons/language_16x16.png b/icons/language_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..dba77a6680844053d82c53469f38bf989767836c GIT binary patch literal 710 zcmV;%0y+JOP)2d0tH6*8#chXJRZGLI@Wl_VfAU0mVO2qmR+%j2VsDw*_~GsJL`%TNi}1*X24TidSabOIyUS$NaT+&I_78< zijmY|jAnK3QJhfVSd<=aT1kO!9w3OT2&-8b@u`J|eI1WW&sgBR6Q>zZ9NwUM`vFa_ zFxWx^Vorup((uCwDJn;6e#xp_WdzpL~97k?mCOc2HNwM|te zi7KlHngo7Kqy!)QnRDHl6)rut&cXSR@kY#{o}lfI^2F2cA%suqe2j~6($-X7*hHam zrH6JN#uU8v$)EhVrpokKgZ+KQP1l^w%ddXe-T33Na&)`h+rgCXRf(0YhqX5Cz0LOSANG`cHm`mB**kvgw+?O$joDe2B?BHZ#{A7%oOj-NQ(Eic sfzzyb0$PPItKI(HcmMzZ07*qoM6N<$f;G!snE(I) literal 0 HcmV?d00001 diff --git a/icons/language_32x32.png b/icons/language_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..3823f90d06333da1dc32a869f89e67535b4be6b2 GIT binary patch literal 2333 zcmV+&3F7vNP)I_p}NA1X$wC#A#l9 z*5Yv3Fs?*#QZXKk!U4oxbWp-3C8|>;aF#fVne-AC?kX@pYtX)DAO+unLA`T;t%;z~U zsYsPDl7O=}`mAnrIXEp8Bq1CPdGiiU&N!--JXtb$T2P`g0HuI*HUoi_G_pIb@(7BP z=*m1<8|T3DdS}=*1eBt~JCfyQP$dNuTTNC-8-*>l>#A_>6+maAKS-9C*niy z+r{bSI(w!HoVn3ub!$kY+2c=NZE)sVmoI$Z1zNo^<(x-7yZ`pPiwwGbqQsIq2f(%g z)O-f=@WT&_)_S%W7_2BP4x=?r8I*%lM<*DE32!?z!G)VW9y&40%FPZRcWN&=8*wS? zNTHO&I>paFehtwcH`_6(_&j@VlU+4K7{+KVhzOvzaaD*q#clQ9_(LW~R!#ywc)B?%m1H|H;|Rag_Zv1rE&^X3K^HS^MJJkUf(* zP(rO%%Dyi%>i1|gJ8TP43K7`}XsK4KVv}fc*y|ulFh(Orp-5R=DDn7f8zk1THgNPJ z%dwp$o?g1fM?Scl-eAl}o!ZUEKD?iget3aO-Y}6@q-lx+t~WxAF?b~OdqZq$(Mn+f z>tq5DF}d{2pQ{F`Owri$#FEAfuE+(oTYs6qNwCFxq{-%Y7?U@ zgJFmSMJ$521e>Hv9r&;r(fR2)QEq!+C~Vjx8OIdM6ZFR^ViP=Z%vUrYeE)7Hi<(;5 zN8>nsJ>H@mDr&~DqdQ7?`=J?dik0h4L}{!O#93?-13+Q-yr^7h zPKn$JE=n>SqwXfYF+$RvAb zO0v38iBSX))FNOr#0X9ac|;gJE9qRJp0;p&R)6+Vo01uYYk|O@8i4zX(~%bi|n*1 z4C9pA^c=lboqTPYFqy&yns5HiD?EB)n!^iwc_bTTKlk_zWHexHtwGrDXQR9Y%A-&! za)0*xCmz2=>Xo^$e3R4J*g;8#CPgz&G)4rc46zG9(s}W;p5HlJL`3KhBXZ>_v=;J# zN8ZyYgfLOub9k2NTAp`5IZeC1#p0nF55IXoU-FS}=eNG?{dnSgK7bEuE6XAPpKwRVUoaEdgwXoONApg?KE-<)ak{KW>3ot)&+ zx9sM}zvLbK`HNR67XofJ20VLqjjzeZz&(q{Nm9j2uf5L2E9>L~pEOaVXiyn!+wH3O zzCUA>=&ERk0JspWrZ-3s1!Pz>S|yD8EnYa=<@}YkJ6rBofBgM?-Os*&bsq11-+n&! zJx{RG(LDR&1)h855-7tk2B$Rq6C46)wmUFo>WzM--s~eb0RVwq;A6C56sM?Y1UABn zrZtFZ_7dLz)Iomik1io5ho?QRb%n{c=9!ny+ov9XdZ5F0SB;w;HQZ>ss5^GnIeX{F z;)3a-tsa{6rw)~KvkOWse$k&Lo&Hd7*PDNws0pMNQG)S3eBa<30VQ}^xmr=eN-pZy z-mT`X<)y~z*_WGxjms^YCL`xuGf9$qW*ZrywU%upyPnAGTt*>lKdV0WD}LywY_59G zSZcHFv8fKmsZmNO6?3kT^V~#PM_$rvC!HJ3_S#Bo^U9gV`s-&}NgQ=<2gm|#0`)Xa z>q@DuFbq4M=Y`|(IMrIGBI4LqtJMO~fZ4XGx%k&V^TU~=zsQ)k*3M+Mlv(Lp>oVC) zgN#PT?TqSKz}vwZx9|JDpJexv(P)$w3WX#)+@yd79RIl#fV%I#`~1v-r<5|8Q0UYnr|AGDvYz0)>N7KfN00000NkvXXu0mjf DO^A*W literal 0 HcmV?d00001 diff --git a/icons/list_16x16.png b/icons/list_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..cf141700fa21a1c8e2dac722fac45db3cd82dfd0 GIT binary patch literal 161 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`8J;eVAr*|t3{0E~W@_xsUz;T6 z+t>f}=VOy*QhIl{`onyl9)kyees=#qd4PlY-(>a`IS1Co?%t53aNyXoM(%6tV!N9r zGBouExHF5NnQt$z&Bm0IBef&p+#JgYLxF_k;Pb3gp1XN5G8`*gefWoW;cK9s44$rj JF6*2UngF{rINSgL literal 0 HcmV?d00001 diff --git a/icons/list_32x32.png b/icons/list_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..74beba9bc087ad4485602d078ac7f5291f43ff1d GIT binary patch literal 350 zcmV-k0iphhP)7==nJEHiXBXJq+{V<@H2V7bIC5Pw_WnX^W{SYc$thM>*BynK zB5-(kjK#%eM`5N2?C&37c6Q!Tm?;8qsUm?CfrW(yjE|3(1$*r^J3GsLj(PT@C-9oU z=;$aqJ3GsQz4jU$9OOR7Jp0iTcukFURN!JSboo1~g48 w3-;Pe*LCi5%(ovsfe5HB3aIY?8-~8V0XdNcE1&FaWB>pF07*qoM6N<$f(C>k!pwVbRsiPnnM@TTG#EURs^%21EAjRqdgTs2g-g*VyZWsR7&g?*ntwT_{S&3D~ zLJfTc85y-Qpp^mQY4pkkNx2@ZmGhdtVjn?lUzH2;mOgk3$0L4!1@5~(j%?ZKxy54aQ|Kl4j|2Z!~DfGA%8Pp*tQeMG5_$49zSjK`t@s* u&*x2{P%xiAe>T}{)}&G?FX-d?I-CO%TBblYZ0000X7;l;UvReVyWkM#?AT|sh*7sugSw4Vo3R_z?%BS-Ju~m!Oy1qIjVF1t z!{4*}tcaQMM@T{RKbY>ghLJ_DnK#HLRP928UBxVsF$3cj))@8Af zk%oX7O37v=aTvs-Rss~G>qB!Q9i>x36@e9G{XRKMG$YEIw-<+4&yka;4J};Iq z)Qcc8keCX^3O+AcbSDZ)SBya*S*{F7=8&A3LWQ*yWATn5ViWO1Xf)d>RKgki2s5D) zmX_v+c_UGVCeXP-MlN_7!YtZ#pyj4=`1 zYPIk!d1h|M^1iLKE)+ih?A0Iq&`+H?C%}6{z4!K;3pQ@ta616pe)}C8Pdw$chXFJ~ zO@8@>HoiyS0bh01Rl`u`<$;0WQ$PCf{qx>?@9k64v2<*Bcvuqwg+kGN^2sNkS?k^$ z9UXmTEKOfO?bMTJZ`rb?{DWX3N&pzCWPE(QTWdXf--8dHb@NTPT;e=<`;ILK)~s0Z sM&U~V2)Q@_9RM~IGlw?*dH=jG0C2<`7zhp_@c;k-07*qoM6N<$g1%5p0ssI2 literal 0 HcmV?d00001 diff --git a/icons/monitor_32x32.png b/icons/monitor_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..ca1a115fa2994145656998e5a3abcc85f8588630 GIT binary patch literal 922 zcmV;L17-Y)P)YGx%Ts>pw4o(*+k?z@LBqIhxHTwAT1N#mq=$jKSfDAC9@XIXF?~ibE8V zhn#p?VvRr)0FNXM20IWKUM#=dn0d%#{N>wkAXSE(PQ3D2CukjFFSU91J+vf`@Rsw1Ot|!97#b)fTOYm@|3w_{2!Fm6y%OzV*|aO6e9qh z24Nl_E+iu)S(pDfN>Ctps|IAT*SAoi8V8aNtxt-FE;vSGkuj%Ia}?LXVUrsgh#jD0 zrxeZ~QdrhB1MzMM&BOkM$Nh&~*mV3#X(5sVOOvwp}{Jfyz*l8|e5sSMFN(G>pDHJk5`JlBg z92AEnlSeQnw6KsacvC{MjJ&e^TdUKx?ic{2>~KdK(3+stkffxToGB#*gmMSc5YrYL z8c2));jS2lame_RtpOY3)oEMa#6a-@qIw&op&DIe&-!lO5>NR(P+4bma(+7gkLIa zjR$3}IIg??mV0WAI-Dxxg2`53-I%Ci-CPl>%%}hopvqv-n??M_fU~UlgF_oY2qLtZ z9f20J%PTi#)n-{=*|Qg3bjMxyi1OkqK{+AyzdjJ3c07*qoM6N<$f{9tGK>z>% literal 0 HcmV?d00001 diff --git a/icons/new_folder_16x16.png b/icons/new_folder_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..13699fc8dde32b09dbcaf7e6d2e9b18060bab13d GIT binary patch literal 651 zcmV;60(AX}P)&{!_Y<}kEIEtQI{aXI>YnA`pS{1nO*>x)EwedrK&j@b5;^iyU_lw7^vh@A( zaqH#`qL6DIoTH)Z>osIem4>aW*2sNja=3JJ zrtm2j+&e;jmVT#_L2s#S&^vl!=BFC4^jr1IuJz4+!e)mHZp?G1g;qvq!J*@1T{0*8PAZpvZf$+7vx8RS(@?PIFr)UQPg#p|i7# z&%dfCjKr?&&f9w~XO4Z8@Rt@chwsUqyN#rJ+BcZOr|0YJ)6XR8>cSxsJY$d#d4Mn& z0(R401Z0Tl#O@liami=`$&f3YR`-FpTV%m$14Nj6>PQfIb`KX$0Ff236$Xd^&q)Nv zfFY46T^A%5t%xKi5H=Q3_A?|X{oLG=^1F3M6kB9PV2r`@e0G!Sve$D<)oY`eJ zUaoqTpsB5ELsw|8j6z`ySV08jftx1?!c-LGY$7J6E!0xO!Dn1f6kY%rBoC8I19<70 lGo=mW(p9nuvFq+H{tCY&U%fQJFy{aO002ovPDHLkV1i^mOuqmC literal 0 HcmV?d00001 diff --git a/icons/new_folder_22x22.png b/icons/new_folder_22x22.png new file mode 100644 index 0000000000000000000000000000000000000000..498e5c90b6e9cf06734458231679598a23dec919 GIT binary patch literal 957 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 b|3AM1EjE=gR{(_r00000NkvXXu0mjfb=Wyu literal 0 HcmV?d00001 diff --git a/icons/poll_32x32.png b/icons/poll_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..56890fcf6d5ba145895e672a08db157a6eee7307 GIT binary patch literal 1701 zcmV;W23q-vP)=%g=W{WS1OUyv(|N$-a9;kmN`=0+EtQ9p{nxkd zjFMR6JRJd3mYCuQ;*@kaT#I##j$#rWp|v84lg4Xe9km1$LaZg>(0~*0ppF%9THruJ zQXr%TOQR@V!d5ftI_e12rjmSTNzB=^&l3fpC=x)WL)1Y9B_xyzwivemG*at=g}%i! z-dWp%H&zF49Z@=fs>W1P05pKHUZk_u-~!_yne~{!T+GOVwY3;)FeW^UZ_V0v&KY@E zytT4UmS(bMYgT}Dl42blfx0U2D#DqvW!@M#e2B=%!q}&aTLHjZ*Z3MU9~1IILdXLU zsYnRo!C6TKgg}ZMkLn0ifq0!>pc;UNF@TW zoc7sRU*+BooFA_8=p+u*Ewc z{aqHkeBmO)Ww?ERpGQYKeDLwCncw7>?`;d=@4fsYSqwMt=RDZC&oi2U3kg$Emy^cV zi>IJQ#Y;GjM6%8G4Gzb!bMyp{=nJ@1$oAGG!K=8d192Q-$_dVt!n7?sPHcY0v1VRy zOfb7>i`7y3ZEbj38K0KpFz#@s#?{j>-zlK9ZY;vqc~C$i_sZiMKU!Jm!kI5~^W&R% zz$pMn*F19paNWJoS*L(_6=#@gf?|U~p%t<&&$6_il<(TBT=ZNS|0cqC9lvDX&Ru*Wv@f2$-r+MWZ{Oray$KxZk?((*H z8@i4RJdK)80UL(fLL>o}(*u_JD;)t`P24+0G~LBd#!qG{T1RPq`i*b#``hsDUv99p z(gRg?(48$P0I-+>K*$6Vo}M#%ajZ_`E!bjCfS|O+)-xQ6H@>~QuHzm_!*K1TLq2@> zZQgusLX?Dert)JVPM2}sK*(v7%KkCM2}B7Bu$BCFBuYZQXYD({?Bwz3gWU?B#H4AP z{TN6?zQ^Me23f>({{cEuocZc&+`D&=zutO_;e|8UGM5^jWCPjdwwe%UE9kUGJvnT_ zrP58%Nsl<3^a*v544grnr2){w>s95T&v#!PF)P zMhZ9&Tmt%l0=Rzt`u8_CH-B{H%9USiZ*TvOe|_`76!<{Rfk!|(C&0C9*S>b{+__h; vUcLGTVE;`7DqvsDK_l`1+2#Su|9txgbidYW?N;g800000NkvXXu0mjftb{L3 literal 0 HcmV?d00001 diff --git a/icons/preferences_16x16.png b/icons/preferences_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..d00968e48883e486dd984ad1d2478385f04b2f04 GIT binary patch literal 684 zcmV;d0#p5oP)))*tu)FXd^y;{MZvdoAn5)cs3K=pXArlw{I z0E~q{xOo7s#acjbf$N}N-Xk~#A{Q=PFuZf;j&Sni$NYl-6oYX7JvTe+8u$DD;JtU> z`H%i{ANz(&K&VEjO{hiqPIc|AOSu2gp^@~vrs|C^l(($R3|&K+qP8R=DqrBW%6K&7Up=6bp}kkFKq{O@}?n~rDBo=tN1@KE{(1VApA zW5&!`;dH&uCy)?mYiqZ0a=KT^2^1F>L!po(B_*YVzn@U;eedEoyN(<+>hL^c6Y0Z; z4IRHYT2@7y^kcJK6Bq}3?>|y1;7-4-E7S>% zH8eD)ju<)W;nSy2t3o(#;w0aJ0|#n-1PRekKmBys)5`-+o_P2@EpGFFEkvlx^Ci~Pk4{cW!K+uVFn69w__S%$8hxNJb&ec8 z^4dSZpNAbGp&@wu_%WV5d5nNSf9%=2tK7}qtuQDk2s?IcSM=}Se=e_wx8_nM9QtCh zUJ!XeVa$j5_dg{rTehtEJ4gs;&6+j)=B*nzdi(?=rzNo4YKLt*b|5A;nhHA{5=RNR z@hexZP?4LZ)1Ef(b4Z{PrGAEnMxJ?j`N*IyS+myS*_t(L+P!6ColToI`X(gAW3ibT zEUZ=|A}S092KrRWnBa_K0?mu_{()Z#G8#bJYkB?W{ z+uLEt(7~{@w7`J_2Vh}gA)PmG-lHBpdK{sBvk7{H)`SMC_WP{GS$CK}fBsRL8|88F z@tg!~?QAi4@F2{XF&*d6p9dKZrROFP+7onC(@^8XZT#_(=(=dpq6b`LqM~D{=cBRA zY#C_M!Q9+Dk?xCCM%BK>d>1)5INbE|@}fM?0-qn@5s_6Swr$&1;O98g{jN>R^?&L8 z*EC*_77ZCI3Qz>wp{;If6?`*|4a z%p>u+MAU{4AKsb{6OY{7+-NdRhJm3T+O%yAYCoJfaY8{vR*dMu%BR3LAPTQX0~k}d*-uEP*E62BvKhF(uGi@PQ)`uhI=cRGY$26A4@_=bGqTU8_;^zFbjKSV-ELSqyW5mC5z-(HNLIF72lKN{&a!uavy;pF6`By#8Z zBzudZ@&8*p%x2+9l#T*Fs)wW+D9fD(S)3+PU8ycsu$0NYneMwZLP)wkl63u*t*tG_ zO&E**1Nv4OH!?DUtE;PW$&w|`T$10@V%2r(vZ0Pv??S#I*9&~iHK_^-8B3!w0WiX$NglZoUtGYF- zv5Ci*u>4%_KS<>J2vL^R53-p0$o6IMv}7-ACa^Alk`Od$(xfSQF0-?vbI$ucg{}=#>AT<)WV;Sw$lm!ZIm?Ym-2DUP z>6&;ISPg||nL;(JSPlAUSm)v4A#c^HRRz77?Z@={D79M7{{8!l>eQ(- zk3aPJlkKnbrFhF!7MHk}A@w*@hVNw=bgd7!{pUm4`uh6YPM$pZ`p}_6a>{wCuCDG< z9-_}R{+6R%w|=eI%Gm~N3;o?)b?vXH3RRy?qY2Xq0|*Vj;!GE)TDP7Gf6a&A0k`}a UHI>{)-~a#s07*qoM6N<$f}HO6jQ{`u literal 0 HcmV?d00001 diff --git a/icons/proctor_16x16.png b/icons/proctor_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..6114a19ecd30e2b9aecc50b67867442a39e24786 GIT binary patch literal 762 zcmV0R$&+{SN>D0*6UN zK~#9!jgiYw6Hyq&&)j)1Go4bHQc5WZXi*_5L{0I5e}RS#Ap}JvQ5U*~uxDdj=+a=K zMi;t}or$7|CYT5b7$ZV3rl5vLkRjMoBGZ}a%yoD~r3B@-`R?M&x#v5FK?YqN&4OiF zmvdErbxy8-m?6yF(^ooeYy*c3Ow)YCvRu95&4wBXTw2uTYXDrtHW2*9t+u9;;^J_9 zwDuHuNde(_r1X{6+a2d~cjUl!95oUtLrKK}R328aiXvx`;kR!B0OJ1@I9^k=JCL{W z8FfGb1OqCn4wmoC0mrg07$-e}v>u@>12`0fT0@eWKh)dXi@CWuAHy)omX;QZ4Ysr} z-`{nC7nS?0sKBu;EGK?6$7p0k*Y(Ga<4Cq`o8fS{F&d42SPwCSuSx!ywwS<5Lc`4T zG$t0z!KJ08JIQ3Sy{@jVOw+Whu9F9UTF7A6{-Ykbpd15ZQ=pz8B9%w7-D=C@@%U4? z^!4?V?jrR&o$40t30uSY~6KLknS;SmYkGe7}wjzb%Ho)c|% z*|Kb~t_Uc$!mJlYflZV9t|elGFk_&!g7Xp9-uRtnZmyoRszW#T&()uazP!=(yox0# z4CVXw$#Pam*G;dUGC7W8( sC@TUd$gH6+Q$zo0X$uRf@sZJ{U&K)k7>fI$-v9sr07*qoM6N<$f<_u!&;S4c literal 0 HcmV?d00001 diff --git a/icons/proctor_22x22.png b/icons/proctor_22x22.png new file mode 100644 index 0000000000000000000000000000000000000000..9976e342ff568fe56d4e5f1611fa44146bd79164 GIT binary patch literal 1111 zcmV-d1gQIoP)04;pRR3ZQX1LR3W zK~#9!os?f_97P<*zq32Dd%MZq>HW#2Y0MuJn#2^L=tJU*!TQo7Qm`S!N>LCEEmVR9 z`zBG)J`|-$HPMGci3rvgp|%gnlMfoLEl3+%Ok&y+ThbVF|8H++cGlTz^bB4k%xI!CvRbk)HFW#?g+`I(;rGt9*e~jB-PXe z!wk8Fd~Rg_$ks+K6W#X$R2__>!Y(M+#q`|1!QU~B!b?a&hG71LBJnvZ?>R%1p>)RTKEEx9$K0k7Pn>GyZ@-V zG4DNiE+8CKR!^;>44)SSW5%g%|J+u8nAD_Ox=!tAj~=R1Rp^ky1uv$*O7tBboSK?S zP)gr+9A`k+^}Jbs@0C1$Mt81dazyHnZVD|7cNitp?|EEd;Q+Q;+x{C5Wr z9t=z?2q7-1VNmJ2H%A zj4`R8|LOS0tMidi$apE?T*_H{$XyB!^@ovBP=qNfn zT9HUJ!m5jaG80k?xK0uITpEkNE#S(}v!A~|KK+t5Iyws5GTurhBW(tw%2Ws7qw?~R za)W$dR!krrSf-{1BvEf$(7_>iQFZLVUoU|UzBpp5w8KF}q9mmcwZmrkejtCN5FBCy zn&XJQjM2ux&=mq)c)@G3rYck_0yK?0P!OobKZ4#~TAA~t{3(@;?xDJ-GhK%V#d^Qh zDAkY3YQu5mHI(;n1N;bnY?{OEHqmm;fXU{XS60{gKY(V}_ZAD|O@@ zz$xaC&#WSy&LCGPK?qrGW-C0^f#|NsddF7QK0;U5*kVyhmc8Qr+%g5dENIp?0Ynmm{CY# ze|5fUj*2$ zbS7hDvK_E&+u>Y$6_5uW`PL7rw~fF{&kXm5Vfbe$VJ6i+}v>bb?g52f$xem4Fl?Q_SbUxDKwF6d67?xDia#m3SibuzAa; zF;yzz^7M2PWUESmG3KU{HVB=ZD^R*fZw`<%9!Cx9*7rh0esR?aeCFOy?di&Ow(Fvr zkI4w27~A@u0&LCS2H>?-odUzq-pQns3RHnLqa<*l2ngkt5v!pcUG*_!V6Re#hfKs^ z0XSm75KIK*J`i7z{J2;Cn#dK7+?GZ3;l#^N>Zbjop3Fi^q`@>p^#N4!0fI&uky{%s zZr%GEH0{{2V|;FIuGe*4PRr}k?%lhi6%&{{`Rfb;UDT~qC%0^H-HPQ_eINn{p;MdE zOnV?V^rc{Yd|VSkJY|~ZcW67VWkl4r?fFKd@e&bvZu|D_{)(Oj)8jvT)-d!j0#XX7 zi4cO+cQG|H_uQ7pp8o#vguAM5CN)Hi%QIIX0K>wY0U0R6Jn6CwB_z~(2%6#?;W*r@$+^QI+mjeSbrv55N!-zYClZ#; znglkJ-19`@6xwWJV&WVrbe@#>_>m(=__7IT*>#_%CPKce3v0W(v8GT!M@JT1)1YU% zcQ&_X%CanH|Ni}Gx5I}IgZ|zjB{&f^Z)p=wPR;SC5j;rC=xd>=KNff)Jl91Sgz?{ihMzvMZ}0nY<}IVqWI~7=?}0Z?oO!y~ zm3cPTnM&CS0|Xv76$k(fN@q3ys&)nlmDJcyz<&fPrtgoCf?aY86V2$GX%=1W{QA1M}am0Td|dkk}!Mh$jq1`BhSQM&jc7AsXq$ZI9|*iK15*ZyGRJ7LJ&kMvzxiHR)oCvl1)OSs%m0J zj+kj1foi#i`770`uBjUpQ`hrV45JY8CahI$kU=$aPNmBdMWQK{B5*?+!@-09HB1qf z*tS6=A9(m-W3f_Kza1J*fP`JAMbv<0SJba4&#fzW9hV5yQK>nq!7pDtp8??NSh1;^ zr%$}`<}-qEJCe+d1T!K56$O|s$vC9cxfGls&^SEKg{MI}q3>HD`w#N@cNq{7pwa*U N002ovPDHLkV1m+9S(yL; literal 0 HcmV?d00001 diff --git a/icons/program_16x16.png b/icons/program_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..57e30535a70d579545d4dd2d8e958d186050a206 GIT binary patch literal 224 zcmV<603ZK}P)p4U^J;z|p&4>O zPJ}2iA_TE8%2Rz+PneocA>*NWeE5A_MpxCt@2(Td*=d6 ak4U4?JBQLn;{GPTm^Y<|x9N$(zkk z-p-=QQ2w38x3Iil;rPOd{+ncD>MNe7y_o%B-rNkcSO53Djy*2VD!OM`&)Ww|abhxT z;_v2&>sdTLGq+{af(~Z0n^h^zreQZ0a6|@F=trdYri#r>|28eqtHe=nw$2@+<2;jl zF8d4Xio9ydX_H(sU2oRPrG0JDvrb-QeQ9#XzQ$Mi|fUY{XoAm Nc)I$ztaD0e0svy&fHnXC literal 0 HcmV?d00001 diff --git a/icons/project_16x16.png b/icons/project_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..6f12a59e44824a1cd9e99eaed5fbd753989b5235 GIT binary patch literal 632 zcmV-;0*C#HP)flt4g7Pc@DbJ3 z)%@|}$7YQkJ9fTWt*PQTZZwe5rPFDoQmIHHkrES>qEiKt?G#ICfqKGfZ zAd_Jw{W+{krGg*`FdB^$3kwVPfxrYrv&FK6Eiy1iMr4H?S$t*gYp}f*i+M5Qa`WcR zEgE69PQX|AK*}gbA$%768WxMCdG+d5<-)r4Yc_}kwjmG*!sqiV&XGt2ZnqnfB*AL6 zfaiH~=I97K)M|;w0>5ne(v8B>dxrP6_sGl5Lvcwl67dA`^73FZ znb>g@gEZaE@Q~anbar;~p-@O6$K!GI_V&WzbfC7j7TsNaxN`YFgO;nPA zkteW4pU;Qx?hf>LX;U#Y( literal 0 HcmV?d00001 diff --git a/icons/project_32x32.png b/icons/project_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..f5672ea474f64521236c2188ac3dcf004bdbbd32 GIT binary patch literal 1538 zcmV+d2L1VoP)3X;%FN8nELJO4xfPT_R1#yEnc}xn-~MV-e&==3-p3Qq zNg+T{6hxySaIjH6G68uc7%&V01cHHXugAM_aM1nHBab}#`TF(iCwb9w1hlu`dF#TO z>gs27-C1h0+hDiF!D_Y4cPg(5(m6k40^<1`B9TZKet)3#$dMxtUUJE0AFwL3C6;D> z`IVQhtE;Vj&*5;ClGLzh7Le4xvQ!kXVw5guniUR5TtRhp)f;cT@!A~#)WrxS=I7B+njEsyLlgZ3^o)b0acD5PD zl(v&qyyXD9A684kQ8dEw|i!GRbaJ7KS47C`||gkz)bQFV!a# z_*rSuR876g(&yT25@~fK$RM0ZJ??mGAwcI;pIxJQILEn?MHT=6Ge9 znZg7l3F>!t9;*}KTz10Gs+pB5R~%!v+p~Eg?_j>^rW;P7A(M(_j5n5;dykJZ>c|w* zVM(*w?5;cSx`S0PGXcl3#~pJ?bcr^S!$%Gpst1l#9%IhLdom3%b?~UN}E^@E~^X+zI!fTM$i8PZvti z2uGOaLSthi3JVJt45HZ`C6!eP90@yT*8JjvA+%}JCZP^}9kF}&ZcI*ikeHBx!h$mN z_xIw5Ro@|wa(w#frz0*d4t;%nNJ~rO{4Y8Y*%$j{@~Vt*e0+S4Oj0)9u)VzHUhfd9t84N6^G{>lx^+0`+;hanUw-+eSX)|JD%9aXSoVg7 z2C#8@1picw&;aS^gyvL0GmT7ak-6;-FAn@EHD-yaw!*@4uw40Lz3!{_z!KD5Mm<`>Og|VOxxbnxw#<+bb;vn23gHSqqq3RTg5r%&v z1gp&s>TCYmVaPqiDlA#ld|xVHqC)Y|Ac;`EB2>juCc!T-YX&ndDJc;P($doq35U?v z-GW|fz5b~rxCyGsKz>d-8jfmUnVBf(oY(VSGGY#pRW`_Hm{d2&JZ5?@(cIkJNW;ZT z6HcK{un-|D5mGoM3{Fu}QW8c-M#bi%!-E*<+K-8j&A`A`ATSL2#^bEB&Z4g9M_Ou{ zXi3^b4m9z<*4x`VzHi^YR%+o@^ndsg75{4a=V2x|2uXw-T2Mk?$1<99g_OrkUDs2H zgifikQj1xLq(-K4Wb7y-LIgvB67hIEK^it5vJpZL1=iZp(Q$yzj{xW*j1YVT zdVirwLK?vyBc>DLXCa$Zs-)SFOfm*Yy(S{l1YnZjB}~NNiCz2U@A>k7#mk#A>!=c} oF_Ke+K+Hij2FT<8_P4hm0sO-rVk-dOK>z>%07*qoM6N<$g171;|F+Bf$xa7@$FJ%SzF|+0V&RjwFlHfYA!}WkLk_7Cew1}9yxAtG39%(cfi>OucjidDMC+#6{bI6+}rdvbx-rbhkGJV z7X{sGeqFP*Mc*Y-Y^AuEJmZJm+W*20_BE+C%;4cW^WpxVTf%knMOvz@hnzq`=IQF^ Jvd$@?2>>B`xVQiS literal 0 HcmV?d00001 diff --git a/icons/real_16x16.png b/icons/real_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..c23cf4854138aa77bcf27602a35a54fa83d414f1 GIT binary patch literal 131 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`UY;(FAr*|t3{0E~W@_xsUz;TU zH$J#;KgmF%ftOM6{QUiY>fhgEo@5Zff2bk*dfp%V;%7FV5-Zpf57d8R_{t+;ns9~T eRI&0>1_twWm+iOj37iZxmBG{1&t;ucLK6UR-YX#h literal 0 HcmV?d00001 diff --git a/icons/real_32x32.png b/icons/real_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..545aa06ab835d5987f98be536bddedd01731b60a GIT binary patch literal 256 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJ#Ar-gYPCv-i6d>UGx9y+H z?<)I+6M#SF=s9o8mACXq`bHS)9YQNjsZ`>l zZ$9Fdr*CP@d^U3~IWKgKwBOV$H5>B#W97*stFN}&>gzARp;4~lzsTZ@r(b_EdF_i= zW$%slbuPQ^QUpLIId1u-m%d<~)iyMl%@Bm?>1n?D<}1Vs3yv*F&=~-rL_rQpE3Uf! zax~*C+BeX7{Yp!%?!{?Ltybg7r=R5UXCC9b&%S2nyB~S}t>^gYyN_wd z9)9dT94|EM+YFhRo1t7P^VOGM5QZiyN6lrqgAW;Jc{Cf_r9}qj?Wf{Yh^i5#*%Fdt z9pkCx8d*YGMEd(5zei_^TAV!a%VVH5@Ow?W3QSi(`dyRHN*`1Cu7a)Qq>b!e33oMd zUeg8#5--6dLL6uJ=eOET|B_-K=rCwOY?c=-vo_&&DmdGzC;d$v%g$JiztkANbI}G4pxg`to(}Nb{$E_*8W5|15nSX<@C7I5) z*9gI&lh!4jw~?e9)|I^D#?EJvB=-Sm-si`3!Mxew`0FzyI`j;!zKb6?c!Q4E5hM$( z4lvysQ4WrseJAg1S1y|Vw*GME$9}U@1^C7wEke1!!R&Cv+)#r`Uz2vn zTQGVvUavkEzYRS0&tE7-JG^aAzuJFxYqtD#!*=mk`t3ZQciQ=%?zU~06mi+vR2k=# zqPhGpF83*ahW)ZDO|yPYo3|f40H2y?AW)1 zumAn)%|aLNV}KU|b&hj`O*dG6gB3Se0{l*%_p|45DzE(d%N{Jbe$Cy_yvN85&b$Gs zPRU))=>#gkr=G9=@~ixK;zv~=aF7~SFTLm1dp)m7vkDB!9lvx3j`!t1eMN-} z`W@?rH$3t_*S{YsQdn=fGz4Ma>^{Eyn=dh)?tNccwvUd)ylnI6wr4)<8V~1bKYtQ{ zNc3jI(UK<=QhrSEoolSQ_RVg5jhmABjLRdCfZzV(SNv$_4=&y`zIn|Z&bbSL33%=A zUj`sD@aCAK*?+kAZ681Lp*OnT%}H&_=|nmrFLIo6c<(ue9Cy1kqZEd8iVkrQ^X`AV z89-#@Ej=8P|Iwv?5t zS5mH&IVpc+p-)Y|A3$X3+xia-|3|Ho&qX_Kck{ar1(g!VQ(p1AKmLwC|Le~jqELv) zfzOAAZgC~%S{Gc4)yr2OH~dH?RpQ%wKMNqT`fWo1wEvvEePw0iV>X_%6_4j=Nh9I= zKmR`URvm>9Wd@ZA;w8c3z4dWX@nk~Se3BV9NyrIsgwi4j%_T;OBGS1Q4Uq zO#FC8kGS#!mTp@~J85$?vBGzL`W;%G7LgdDJjhDJ`n_7~4*-7v5Rr{}Ry;PJ(T{8| z&%RGdY@8n!o-1Z%pl( zB9?&OFq|q6nCk+&ISHrK*7nZym(_DSeRjZsgEPm^WLe)bD1aO&4m_z{*C+wM#mHY91n(WZ7y0Z?`UreD9R=;Ws!zF^?j=TaQLZjUP1KJ8MkWX<6EBnSa?@aeUy!DJ0 zLxaN%sI5pMz;Xo>9szxO!rg6~FD;A%4U`WSAE@u5kWz1O zF7PKVq4q!12#r&rvMLlOApoA^nEvSqYusGPsilRu14GNlE+M1fQuk2c@mvCK-o?>+ zJ=)lsrLJD&RLNCCS(Q-4I&fmJfCn%thQjifAkk+M7e@wHjx4}BkiW~L!m-kufC)|` z-|rQ*^nAGn&Qx^U#;#M0x+KqSLYp*g+kB)^E;3bQfH!u);am3OK`p#5F%SXqkpivA zl%;=DX!1SzPt^eD0{c%od{3bKbbh~q3!|Zh!L%A!RZ~J;gl2(j*|Kj}Owh^p+7x6l z(BURS^P~BM{wPlk2TdvKwNRlOMyc)s7_E_tAff7w8sU?bRy&morj$3yVyHxw?VP`f znf31)JEyX(wLb4xB9+AsyoMqnQ1U`B*ojQ5ktbI9)?#SR6U>(snYblGl&{$sNudJT z2!YlFYB8fQ-Q$X4a7m={)ZTH-#LsjK{u!mFdUn4GU!0fR-N&w9>F*MDT#~9`C&O<3*4E-@<&$kNFk{X?tUj4LuA{ zNx(ScrSo?U_hE>CloLk!l1EDbo3Kr`Q*P1-Gpm7tX%M##ehgS!iK>%IzJJi1W0L;(ku>GOu!iCSgf&icUq`PTb@FK`kD%gL2^ z+Qg3P&-N~~-k=6qbG>VLR zI0fO}oL)gdz+)6zQsjh-=Ks_3@^1*y_gk^Y8ZUarh?z zpXKxr?#U(dX@t9SK91Ytxd35lPZTWcWL8_}_5s~~1$>85ehw6u^xyDm^?%<@l776e Rl^6g3002ovPDHLkV1m^$^hE#w literal 0 HcmV?d00001 diff --git a/icons/rename_16x16.png b/icons/rename_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..e092b5c74ca815aec1d26391253088f432500f3e GIT binary patch literal 165 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ii4<#Ar-goPB!FgP!M4GIbD8F z(BIyWhzrTbmHGB<*r0YTX`Nw?5$DmfVh1c4)Y3ErCN7<9{ljC~>xvFTr_jJ`*{01U zFE%kO6)||obK{%nORoxU*v;{d Om%-E3&t;ucLK6Un7d@N+ literal 0 HcmV?d00001 diff --git a/icons/rename_32x32.png b/icons/rename_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..96159358da027a7904e2f5cedb5793819bdd5f2f GIT binary patch literal 329 zcmV-P0k-~$P)Y;AIQZ%a`Jp zZ$_`!j^Fb{BmyGfWL@PvB|!`T01WNgwr$&1d;beHbvHBbKTh}%MjROwP(cH2^f15( zW1A5M=%I}UDkva>IKuG3`8PlgxZpzs31m@116@q8!U0#jA?dz&;*2d87@>n2infU$ bJjR3!*3E0yA={Ha00000NkvXXu0mjfyTXBB literal 0 HcmV?d00001 diff --git a/icons/reset_16x16.png b/icons/reset_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..61daac286db05cb7a932277580613173129dca2f GIT binary patch literal 690 zcmV;j0!{siP)U+5`n(FyFVU_tsN|&eBnRo78+_%4Hm#KbpOCU2YR@R zUXea0=Be9`Yd!CwKQeu`e);MhzJBj>sy2@>c%){kB{7(2U7Gi`Pt{sWVQvDN4OiCiho?uRs2qKXc>Q<(O+QObewH zmSwF|R~u?p}v& zF+5LUSUv!L&Be)PaSDH6#+wMXk4Fa#0P@KS_ul(EM_%$mw}9#Uf?IbG4ZaW6zrUvV z^B4U5i%%vC%^{Z=V--#FHV|yJ4WnnD z!RmvaqA}wrp89?|f4u)HjZ_l1Tw}qUu{?X{*#Kg?b_1uHK}W+Fu@K>46B8y>7&~Pi zljp2t%G_1d$|vyDWxLY)_wR(drXjw~xXxDKch}4n?DXBYe$ZhAOw6FAlm-!#)T0+N zdG<=8iI(ozI%{iE)wgNb>xg&EpxplI=kUqL0P3KFmRYfoxv7xPO1V@J+qT7V9C2M& zJkJx~_vOteZk literal 0 HcmV?d00001 diff --git a/icons/reset_32x32.png b/icons/reset_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..2304d95b9786955d6ac6f387148972d9bb077ada GIT binary patch literal 1582 zcmV+}2GRM6P)*dxA|iIa(h3w{6?DZQHhO+qP}n_P$5m)H32<9qq-yASE@>B6g&!<4jk zXvpSi9Xl&i=G~B%Ny`Dz)(mzFUVie}b^rM8^&f#Z*V*A=1ghX~Ke&l^p1G27<64u<`f#m%D!-t7A)>(_5&R^F(=TNWd$1fh?$y<(Ki6s|i`g~0&UBpye z29`&v!DH1;7vbyof9Ia_w_>UF4`SBEck16j7)wxOsg>u(c`C8wJCB~pMw_ogTdzoig69rdMvj5Y|&et1T%vzU@aKI@9xAe z6p=)NL~aB+n*~#*uhQu^i?02YGMOy?q1x!r$L`0_m} zfBcBJF5(A7LZx$LCd@;2@@$9-m{|>JSaIcfxc%aNIQWv4NTxDz17I;;fK`AH!EJTv z#h*W-@XsQ!RJ7vHQieY|6@JAsD&x_T?ZtfdBBk#?BscrABu9<_Yly^@=`FOsGm^V6 z*_}=Iy_R?a76RV~B@mj3I+m{QU%>1A14#>CeChD&M_F=BV`(2hD=5v&AHBoxANZaWJ!K-XAR|kbJo+sb1I^t*|kx9Tjv$gQlU1!D( zV5|hbi)aBrlweGN@BfO*fM`gjU`+G0T6okGsSLXwb3aeqas=r%c5(zcz-;?)3@wV1CM%G zD{Xcnx!E=#Xj=i5YQlONNMQrPcs^}SY5x4}R{&d+fa?aOo}WK#a5_NehU4T%l4u-( zmIhR^k#u%;to+VWoBzOTciqFEKW<0!xP{{@QJU#n($j%4D5ATe60EfdAWDTR zjlm-+okXXHVH^+7E8`c+xNZe;8c2;_)4YEFJr@FR#%{*~mSeGi5v3t8Sg|2kqeEy2 zs;_XB-GBc?LqnFX-flWeNwOox(KLNt{PY;kJLlx9uDJ5%0|3+i14;G3A}nhRqE-L* z_sBq@rwgqe{%~_FvGLIafzL0${KC2CoP5){7hJn*UpRoGjs(COtnmqauU_m`c*k#_ z;b@`aThb#YNAO=c|AMm~IQQJkw~E30H4p-9Xh878y|>m?o}o_X5EmtB1M zKK0@C4WPB|=V{j>)`9}80ETetq!1@dkQiA!=ll!Lx%%>Jj_w~`-@tcT>tz7pUsEse gW*lB$!v9(S04iT#m&UL=YXATM07*qoM6N<$g3dS(p#T5? literal 0 HcmV?d00001 diff --git a/icons/restore_16x16.png b/icons/restore_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..bfbc8fad2dee3b51e96696f6114ba7b2626da2f4 GIT binary patch literal 692 zcmV;l0!#ggP)Lkslo@5xGcfb>)UDu9;f1WhFLTYE3NA zKG{$ESP!2Kv^O!F8<=QJDfNKBsC;|uipc9N`NF2hZFfLCn@I9&Cdy?V{ovWKRIi$& zx7s)Mz`IY^zk6`)!c5`f!*<*|ZcKzA%m!748)GP?Sv9pXF#!=l5HJdBEqA>1NZp~x zW=hlro3Fc3e55{!1=^}TOjRb3S!)w+g7$eGg5$D&ZVj?d1^{g}01j~SQkjLxlB$6R z{f!~!x~ryEYj_CHmT9JCjO~|Nluy1Lexf$atb?mvVpd}TN09B~S=Y{ZWxr{>nqQeK zSL=HOr#EmF++o2*yR4@*I4jEYSk5AEG)nNt4p6n{ph7*pFmyK;WZH&@7Q{`&H%-QHeJAN1b3!e6!JTPvuwURkJ){A5Gn a|L_G=a|^S)uUCu!0000P) z6@G88u8446VGsbuxrEN{Q*ZYX$ol-DtBpC6w_9>nue_L9U0BGhPEA-vYJ`>Me(>MD zR}y~sDh5~!w&8v(Ttafnl)$O5#DU)6qwX7}G^B0LVjyKz8PSkF8II^^L^PSlpRHHJ^EfN+s$1jVfUwLF@BMx{OKR>vXonTM>)Ish{Ul2GJ84+I8_&;Rsa8OM_^+qlFgDnsFeC;htpd;G5b zN2CZMjLCd2q!!lT#cfo(W&F9F!Nm$kFyl_2=>vif;+YwWd@voz3>I;<{oL)f-PsB? zz?yMJGz7k1{VqDXgK0eh{#}IWAn9J%k&b2DNoTB`1FRJXK706c=g+7Ib*yDqA*DQ1 zxQ2nFBMc@__xi~d@QxS#P%a&ws$cAe7cX>V7iYvG3qNT73^7M={wXl?w=rL2^lKbP zy&J%9kYG7tjkUO^qnyWOw%lyfY4!rg>@h4j%e{C5B*oCoC|Ct3{y!uT-Rq~9$7etP z+Z(PN819E_m_#fOhpI>*#i^C#Tex!$*R;m~($Z=997aiaw4!DLJ{~qQbGRlDHZcO} zst1=Qi?fjkkzoa52}yu4F*F;*NogcyLWG_{iV8ah9PzfvljoaI0|)^G0J`O{+y&s5 zf^4@t?!7dE8W~>7yN@pxCLb$^A&f+UN)W{B`xwFHR=1X+!xvdGAXtG&_CFsO&_oEdL2N>t&>e)( z$jJgCkIOxQWP!ZppoY$Vpo3r_VA%Lcd2`N$-}zs^^|#+t>lnc`*0*LlC%E)XT0V4jlmVEo3fQ~c(Q}g1kXp0~qkWWNOxb^|~f;cdY4z@9p zH~#h;KLs$3Vd$U)&I|*H2M_r$F#EglH)Jnsjr|>!!32VX@{{QV$GUStOL=bt(8Sqf z{9lej$ZV2JFLDHQnU^o(50&zhwyrWi_Y?ltU;_?%udbav8vbvq`Xd0p#@Qgh?~3)6 zlJ&PtMca{N3W?Kh#|pzTYHBSHYL$PV@s$GyA<(aZh$0bo952i}nYGyU|J1+z)Qla^ zIR8>L^Ve<#F&mDIW=Nzp4AeKBzrd38Guf7_2Lch$?`gPAGWuh?9A0zgaQNRwZHuH% zr=T$c_YXO>xIKZd1%DI{DI=g^OF8-!6u+23%EV7{cXltQ@VjU)0uW13XbTlNLPl01 zQM9s=FzBwj#YQXe%O>LROyjJA#^Nk3ADX0Z`+pWO8xf^P&pJ*gFlrwt0wHBDZvEow zox|(DbM|5aVoS(44NfYylXYm~bnRubcGL{L%2e>iv4GC%YG z$&OG)CIW&r$N&%+3C(@E&=>e#TddoqD^6cNPd^X-6+1%34ym;F1RRn&kqT^*Cltwa zI(oS zLYz$EJcXywoqv2ujNy&<8M@Q s^SHvy3WkHvD|ENtT?Q8=0v9p=4@iugE?AZX?*IS*07*qoM6N<$f~@6xtN;K2 literal 0 HcmV?d00001 diff --git a/icons/results_16x16.png b/icons/results_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..8776eaba55a4db8a7d824af7df2ecf8a2e0c279f GIT binary patch literal 623 zcmV-#0+9WQP)5Ty*Hn>WkWi;ofoVAO7Xn-^nR~ z2;_)}U^Rj?xK~74Ja1SE6NZtUvBP~--K>7A!!+^KhhJyyx=vLIA)wmQw&A3pyW$wt z!a}8Nw)vspU8yY^<3^^Pfva!6dNFsXD%!L(3nBtIW=5%Xp-o2cR5qPb5bmA*W^;0CdSm{dCgLip&uE-#O>GI zLFjsRhdp){!{rB8l1pNLXP;J-QZs4(jhtrDInE#l?2dbU`pqY-=06FlC#HA5e1|FQ z;4ai!*~BeZ9$vv`-+h94WWD+$Yp1lOWRgUteqs~XeEH*-%sH_)9Ps_G-{O9{hqphu zM5jtFnW~UuYED>I+{^vTKM+68HZZ-4p<5P;JHZYM>yX?v2!OQx8$Dvgj+BozTN z=NdwPR={!Ryub<7D&`d|y7ry6j9Z-w8&}v}v*LbZCCn?-xVDC_~Olg{0siS{sKegb+~&z)V}}#002ov JPDHLkV1gZ#H5LE> literal 0 HcmV?d00001 diff --git a/icons/results_32x32.png b/icons/results_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..59c86823b6eecf4aed1667f50fc24d4f709f2bbe GIT binary patch literal 1597 zcmV-D2EzG?P)IHa z)*74z0ttu^R_72Rpd}6G*J&)m1g^{}hP3A3bVkwN5&&q;Dw-Pr4nf#~^>eHJl*R@^ z072B@wQ+iMaAy0n^$-Bk;p4SXXtcV^#1q046OPXChBTHCQH$26v~kRWoSG7WCGrSh zp-fhhM_pJwU;%=N4UPSW0K8g}|M5^H?R62vke)mU5U7CSQbphqcB|?M@WtMSj)%_wg^sD&DOW{n9mHU~|7knw zM_~Y(&IRbAfX)wT79(0&UYCJHASFUiPVi%new{^t5X?k4*5RI#Tg%Zp590-JsjMS( zlD>@FFl7d%#r2*9CJt!LSj+{eGKUiqRu`U_ z0nBP==ESx^?pTm+3V|gwGw&o_Y;A7M9>dBV5LBLWbJhY%FF=<>Rw-}A9c-V!g4a*I z!K#h1HMyTJcUE!vukT^CyV`KTHXZQgqTjrW?|!-q&CZ(20hJ+;5z2hrC;^dcRlul> z>-fsXE4cOjw^-|~@f-x|114{UExf#b0*{7|@Y2fhy8g4q^W;2#_4sK#KG;PZB{0s^ z&Z>OG=Y?rHU?3rcgH<{A!Op{bxcKUuxbwqp&glH<3%EoXulLrGW+VLf!98pq+r)SG z?=s=L=ibNm)`xg$^#qeRO9D=u#=Qss!G+UrLO|34j4q*y2}RZD0h)-g4GYWa|I$B+ zaW=+qH1zjK$lr1Mf48tp8TtDkY;EKA&Mg$GU;*hgg{C;oQzl6$>lWdyv+tn4+()n7 zoA-bhQ)Vdh3FaqZnRAVVh}g!nCtt&je}2uILx7c!^dW>0>_R~s9Hc|uAP!>uJ==+#lPT_vXj1y#iJ4&jmzr!m140XG!rA-hS;8vSLa^``8=p z`E1$B!|@>sUE+I5xyZ@hMhGswbGBDvC5*G&2JHWjV_?{@nu&!|s1y1oPX!Q`K zj&belF8~38K;WYje$Ea|$hw>VxHwsI!lc>@UcD(9Ry_Dfi4Gg0dN|& zoB%T9rYoIevVdUu^sB`1pFrKg6<9&;TLj)(nc@u`Agf1q05ShlYIU94d z_35{WUppO}$M9?)tzgxZo3?g9@R=`RR7xam&fJO0h2|v#*?bQ)-UaoD#?J#0`)Dmh zJAo#*1)2h!hV!HdI8(>AJ3MoRRS=5;nx$AMgIy?vF2;yfS0T}O3gVUi9qA2RO z-r}XT!90JlD1a~%@H`RLaU#mPulIO7LN}3^?*E8DK5M^q z0iWSf(yh9@oFGh=5X3F0$w5W#95+}HwMb-#M2%@2vqh>L!>SUU_A>H}l87PzU>SX- zUE1l&CI0Y~lC4{O&Xw|+xA@Iz{W$4C(B?(@gt85}j9TUV=`Kk?nH{kM&RH5)Z@{Rv z#7-qi2cxM@J+}aGig20Gd0O7<*ROwk=FFK-u3o+R`OePHH+b=vBTNam#EiI4c=#I$ v&b}Z4C1F1VkP&|V9qRADKo`uP|D65;jA-a*UU)c800000NkvXXu0mjfK}Qmn literal 0 HcmV?d00001 diff --git a/icons/save_16x16.png b/icons/save_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..ae37f2ad341827f4238256cdbcf2f02d1844ebf5 GIT binary patch literal 482 zcmV<80UiE{P)BD@7sgQ%8JhyE?%r90Jdeo;jTV$4S^nLgugP#_|IGOAk^BNHrXv=2$XE;0{4R4)T2hpeC;pA- zN%uhzfZDNVH>#?t;`p2c6k{)(H=kGZ_w}XwAW+pk-v@Q~?p+)?cnG#*W7C!`NUF81 z4KH84hevyBn>t!$cSon}?e6|W?|(1n YFXaO#?)qk8R{#J207*qoM6N<$g7NF#QUCw| literal 0 HcmV?d00001 diff --git a/icons/save_22x22.png b/icons/save_22x22.png new file mode 100644 index 0000000000000000000000000000000000000000..ca89b5e9347f12759167f28f93400996fcca2a51 GIT binary patch literal 710 zcmV;%0y+JOP)F?u`@Qt z;FC{3!Me5UAeBNOgabRc4|LE#08$y$)YLH87=i;65tLH)0*01K0lxjY7EhBy+tz?U zwwH^@z%yRIZarThf-!JG;PCb}9)Ji8;F)-aA>!9pYs0o|tc_>Pa3KUAic*Cie`~fAf&#{+n-L>eQ)EG&^?m7)C~hxp(Q}C9u&t z2i96cO6i*V`}=YG<}IXYiU|`Y;Lg3f@U4hpixw_KcXttk0|TyKYvqHLlwM;LhPIj4 zIhY05nM>OqA`poC3xxu+s$iobmGX_W<`QX&N~PjMvr43Nzi769;i5%oZf&W8NgESK zQBW-M7DE-6oy;Sr7KsF z%9P)v;o)H)n%P%IL&Q-GnWm8CGTPhQeQ-@f?dL0&Ekpm{z$YLC|NQe0%H?wP`)`b^ seA>o9X`1jliXt$uR1EF>%Kl9M1Ejl9mvq3E5dZ)H07*qoM6N<$g4WegasU7T literal 0 HcmV?d00001 diff --git a/icons/save_32x32.png b/icons/save_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..8e8699e002741f92fbb314f42c7fd837617a8990 GIT binary patch literal 1187 zcmV;U1YG-xP)?4(KBWJ}gEXc1kJCicz?5>MlezX3d>y-Y}yC)lLH|3Lx?geHx*AvVeo zRANY_A?=c8BynQ<`f$#bedO3)Qz})v$;mnQIQO3Ke)sdW6(YjRY*ymc%L3pe9&deo z>zf$$&Gf?C@4O4!JJ+FN+WjPJ zN*Tz_j|6}aItq#uh!evEI2T0vq0sart+~H2zt(y=fEZ)SA^iI1F_bmT^$;_8#KZ$^ zT&jbP3tO-p0Cz(gxw*c&C^9tP0-fy801-Bf%>&IVKAQ%zO-C_AY<3n{5s0ilEo!{P z5a}2+;{Vq`6l(}6XNCI3Tad0^!aM_DG@xTV#}*($7-{Gok0rNM3y5i7SZS|%#HL;@ zLd*fs)Mxry&8pm2g+jSvCMyh>fal=i3-eAI$chGQJBI^ZDj?X{_!NQf#qB9^!MM`vQNM=UobpH@se92!izQ$VYvn_ z-pVzA7t6r;x8Q@1K7=>6x6AhOh5I}h4B(U7x52V3nitJ2z25DfF2X2^3I)8XXH+~M zk41PnfpIQ1$?=O#wOR$+wt=zD=kwzH0-z`ivFr(U=kqW4SWREw;LhEI8 zj{#*d-^7IC{5b$5lX81@d@T9l$q5%>0L&c6JYEb@j>FWQ0YC{w!EDBb3ILf}&NH>_ z!!YFQ3oK9|V32_+1+`iY>Wu~r2ZIs~lpwSnrxZXKh2Z9B$bi8p z0G}2WM$y?A0M|7|W;7^@LJ=T&5f-gJ-2y4j*TaC=F(O3sKvLu#%ZBHp5j0m<%K+#e zDA>rKSpL+9N~Hp0^3%!0=%UqXkr1a+G@)O75CjB_^RWojN^Z|irvv?d9~3pD#sEy` z`n7BCS8MgJm4Z&@eigd!eR)qnvGJ{mhQtQl z!^6WlBC1Rcrdw0+fTB6)6k?iJ482CyAmakKwY3%R?Cb<|s>#_&ECB&!R*--p?`xyc zP;~k!YH&hl#NOUsd~|f=P9_ruYQbW|r1*1Y;y*WY#vx$vpgaHo002ovPDHLkV1i-R BEdT%j literal 0 HcmV?d00001 diff --git a/icons/save_all_16x16.png b/icons/save_all_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..5a9d1744b84771d67abc6c99a238538686c0bb77 GIT binary patch literal 536 zcmV+z0_XjSP)og3GYlapf&GH_hSm{4Ahg^QLz4lvN5-Xld5 z)t)@KkMBRe8-onuIDzYW$jiyX-TR;V4A_B2z`l+15k(PrcaTAn#Nc5V#i&?6x9f$J ztVmSHX1W1*hj&FU^RbiYl?J?f_n{xNRLnP!S72b)#{Jl~d`4e_UgpK4F+U(PR?nLR z)A73!#45pfnXjHdh9r?)Ya)!7*)~meCYcZk5$MXpcH2kg%g^YQWTg=XFn|5_O_?Sr zD=AfbAp|*i*rB-A%%9x%Ub0EH0nEk4MQCVfK!IVPwyq9>96Y>u`Uukxl_APwc%JMhS31#KY#v!>#C|W_bfpU-}iCz&TTX`HKCxO0Qvd(y$?5U+yo{l zEiNH5{#I9~`sWit5W@33q|<5KxpfQEXUxE~>C@4p;*w%C)Yrr4V(xv|BM~cG%QW@o z=4QB#gZB1zw6wIKN1;j=DnufXIa5N#+|}ILQdwPHtuI)-7`m?G@87@Z(H}J@3KNnU a8Tt=g4{@1nMf-OE0000*<*HRz{)*UWbKcE2-CU^r{4>rt>s<8bvq(a<`6Yo6dkRfIR99Sn`N4(=n>TMR z{-$EBLo8Xc1gD;I3Uc{83WNPvx93Cr9L*S3u%v+9d>Qm$hz7xJ+GryRuCG4`EzDrwzW9+gcF%m!de#-lcDo(yY05ZPe1&K zv(CE+gSi}H#IlQ*KI`H73yuXtA%=BMtQw*ytem%Gqi{m(ct1(( ztiy>Xo+yWO6n<#1e$U7FJzRR~VbI3Pd9m-hz2k9GalD}oN&PN3=@1C`u=Ie1<0)Fh z8lj<{ecx}%m_d{z?qrmDX^W=Pc>SaA@OwDnuzf^8(!M1^+x@Q<%cB$;E%iJ4pmJBR zz;)Cz5i7QqdM>SVGVX{>62}{(AG;5K7i1_=3UWPS7hCy9aF*Y=5DZT6s~Z1Pa4B^} zyzhSd$+I03lkH_1H(@-Wlstk1g9BVtnRijCRPf;a45(1SnN%8|efnw7{;!BX{_K-`4m;#f z^X+%v-MD+#u0iE_ib)gUB7nhazy9{yCXXAnx)YLg`YnvW|MqA43y=MJpn{&U9RL6T M07*qoM6N<$g0uaW?f?J) literal 0 HcmV?d00001 diff --git a/icons/save_all_32x32.png b/icons/save_all_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..17c94b8b2a7f3d2f3753861322efc3687b327e56 GIT binary patch literal 1270 zcmV_7O!f$7KL0+t>%^l+yV8 zvybu48?S-r_xnfE2kyHcAtom$H-7rrr&zy!J=U#Vhrw`&Rjb#a*Xdv!qVj{SB(L`w zBsiBB7!F66nVA8hl-iI^O-&($wH6Z-Lu6?N_PIe4N6^NMPhJsV!rEApmt^8NlKrbT zl|e{g`SRuHcDw9zFTXg(u$O^wt>jECU1>STRwBa8LV*abNuWRIH~qfwV(i{~u;us3 z$w{307k;lbqrT)8jXbn47h>GuZj_gW0RN2^z_MYogv*M37A3u`I`V6349 z^n1elor3)s4)K5Cw~tV6V|=*UoGEKy#qt%E-$ar`nCSjHzh~$3R>3Jt1x&LB=)-6< zsyyLhR=f%oXqJy8x+c5Jmf^RQB48qla$d-3*LR{ zh2~Bq-~s~1k116mms(II5`-i`2odhXqz%8Dhn2sCtr~DFLTD@kNfeM`$PExx0>2qS z!H=nd58rzWM~)o9{M;Ot<`5KSU{8NAz|Y?`{hmB_jHkx>b?X1oW(&4~&P zA|%1Y^1G4^`H}@4x?ES(lncEhtcQ zipB|4O#ueKs~06nX+A_~g|(sI{JpFb0xH#uDNuL?@~lZfE7m~WcSgekl4Q&I(C^<5 zA9|j7%k`d@{*?+G0k8o9SbW^<*zrmBDJ!oXEvQZ#f^&r`keHYRT7I8CbLM^_P_}tG zH9d9Bpg(xYGg=egf}-2S-S^xh70<*Z!m<@Izxn2n zFO;XYZQk;L-(XNb4L76;0mOIto%Go@Zb|4jfR#Szp+$+*$F!H`&4oN)+3R*U^0%8d zZPMR-{ms7Kpx^U1p_`>yCr#5%lEj%$nEFiA-&kX`wYD(EYOU15e7>OKH2Ln(p~Ja@ gi$cFCxy%Xv00q-cABL75^8f$<07*qoM6N<$f`15YU;qFB literal 0 HcmV?d00001 diff --git a/icons/save_as_16x16.png b/icons/save_as_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..172e365614b4338cdd5a63139079aaaa040a0cb7 GIT binary patch literal 747 zcmVxkj<^{SCMNp zVKN$%8~|~=2OeLLsqHq-oHoKkukPgMrEX4~?Po?$4sR@-LrX&~Jp)q{{}W?;2r8#9(!pmJ(asSewNIsw~4s?-%wLi!{(KLk>ZRcQH?9A5iUJh<-0H6 z=JprOR9g3Q(^(Q{+>y=R5uNI)LS#uwhMM94$f+EhK6@L^eIwlXvWWtNhsQ61yWXot z$hC3lWrs6$>Qs(8{D@?zHPzJHt9KUn!F(!UpXXF02f>=x<3l}cL=kt+5qV@@5Fc3&49Y9XY z%)R`|n;&CdpFw%$17iP)` zym#k3&-JCBiZh2!zln5Ku0Ay!GZk`OEe{6Cj;VZ<{QD@xu6my>ITNsi~Q1jg8b#o5rkJ zrxOm@R6|9Bj=5*IF#O96gyyu<__e!lkUW0l2G7vDcA&EfgJtuNi6;NAK zOE?@3K8$G!+Go@OpeB1(fLyyyU& zT7XiDWHK2%BZR;-1R=|$8VcE7A_s0Em5A}w3n3ogbC@MUa78XhJ{02ZZ{Ex0*IkEH zAdz~{30&1XFcAWUX@F%3Cbz&TW^wm^f%@7oukKPj@vKWGlVQ!^AX`4)O74|D#`1aG zvPaqTQ1XLE;7RY)0;G~isn~aDkcm*zCGp-uTIyVO?{itXUNAN`rUk6MYc}Ux(o6A* zRz~M-rsv2I&FMIvoOA@9oPq!et|yt*ltKssaBRmiW~3bsA9Gp0PLMB@H2IS2o4LU! zPuA9<<~>16{T!0X1SQAA^Sr5fU?RM-ql%g{)TP3B<*6LOBmth3x_u${5N+ZqLh)99 zuwf57-YDpAHK#brmWNZ!tRE#~U(Hb#kxnH^CR5bZ)uN%$d-#N?516pp6#Rz=6!}~q zv%O8k(sOzCg+1&(P=}QLjE-j5xGKfm=@|;4dF;tAC6P2SOp8c7hUe+IM*%3MJuq>9 zITa6d?Rb?-(u35VaUoq@f93m6y^G^i=6#XLCpt#R%O-yMawp2#Ni-VM)rZ0nKvCf) z8jErGK+i`z0)gU-7wo0w%th?)-orD0s@3HFxpUL}0$;jX;t^-frY1)6`>^d0kw}C{ zIE<>|_{mVGGU38c4RY~S_b`+#^40I{;8$*$@p8PK!xM)s4H-7nka@js^TfWgJFX%6l)`k(*-K9t+!mQbR8VC9!*tWnh48oBx zDyVh}kWy7Yf|=<7R^4}(u4L(o4ZL@B9Dt-f%+FSonU=Qs@pBFA>WO08U?7M^qX-2` zNv4SEM^GCb;-NeG5Rq0^-n){2?APQNGRvm>6)n?kx(;86Xr9lli<0`6v%S5YbUHn` za)yV8S+jbz?v9j7k91RGH$6UD{hD-F%Lbi9e0>dcj@xZMkTzrR5I$T$thRYsYWjI1-C=>+48UhP2l)E+f}$JZ(dRo+p%^G#ls4UX zv8YM?*6Qe+RufF@0yP{G3ve7qcfl8-SFu>s@0^M#{~cv1(KjQ37Vke} zp{sBDEJnf3XAe=x9%M${Ad9bWra#lm+Rcjnyz>TZz@QkF@oZ>lU|?VX3*hyxx92|h z(oXKYafYf-#{d5FXNJ!h%w*~&&TG)_bw64M5ExD01FM-Zcy=8}k6|FdGz^_TcI=p* z9&6>&&)spnuU~Y`igdrW{lv{rzx2j%O)B-3nbW5~Xc}e=z;PW-WLZ`~5>y&*4Tr>T0%+y0000}cL2&L;lG7k3xpay9Pdu~lF^JYYmC+>Q zsWlMh(ktt(6u4)?s7LO5?a-|&>an|J=|!JdEi6QEyF4KoK|iU0rr07*qoM6N<$g3F$T^#A|> literal 0 HcmV?d00001 diff --git a/icons/screenshot_32x32.png b/icons/screenshot_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..9faae661c00308bd33c83dd6d63e62acf69b0e89 GIT binary patch literal 1099 zcmV-R1ho5!P)L~UsE(a%tRl9{I2)U_?FMVxw!M3QN$2L}d$l#m&38F-<_z9$ zXmxc}f2D+l{Fedz4cXwqD`AN-8_Bz^sddDH><=Y!KZoK@b0EkAT zFKyhoQK3*snM_8zckfnTU*D6Ges=(rq1xKoHhuNgSAEXwL^vGQ?^&8S5tOB+B~4FH zPd@U!BiHQk~j*e<} zc2;$Db?YqzY+jw8pND2`-@ZL)D930}78e)Q+S;n+24l#83l}>LFO(Ia~P?uttC%%{P=ODQYj@82|U!?+?=|)x&Wh-Cr_e> zb968Vh%ZABZsH5dv9U1$hFriHUz^+4#;2cts%_i03D!`Ea)kG+?d8@?7XL^2Zy{K^!pr$IpM&lwNf!!?XpQ(S(#8mM(^6S zOMEd{U0p3gNDn!8?wq!6-Ku0V$sRUDzFfF)!K%hHK;lHqJM)k*U>G(Gm<_x)iZPDI zufF=KHf-47l5R@RKmUTBe)?(7G0vC!bkyuz@(d7yForGL0YEM=w|x_fF_mKX@TjAs zL%qEdiX~3CY~QEh;ciV$O_@Fb@Ghsi0DzY(5hr$;LG!Q#3<{nSTh5>swov%VC+}$I z&R#w6K%r|iRp=GW^5qWY8WaFP(Xs)sBtkJcU}#NPA3S)FJOBg?Q0sc~$tUH0tTZ?{ zsP*epdg6&EU1 zZ{I#x!x0Qyr}O}zs^1;#!XQey%wxnrn6S27xclzAscn7x?YDq{b__GOu#gwTo;}MY zYhWG#h+7@Hi~M+)_vUeMnAn_lOf%xw5V}!L7)icEhYkS(V-|Y@V2K$u2HNog)co~h z-doeKd5{4VvgW&I&mIPbJXmrDowkQq0s_FvSpew$_uqfrb;BZ;#Su%Upttt@DGB~@ zV&CjJ036$kw3N_y5B5qbbmIsYXbJk`gb;wF#f{GlK-lu(PnMeVmqtw+2$tBGV9p3ld-3Jrl!Y#ZarJyIp68hf2*pySvj;w zN=k}MPEL-ab}O>_G-=YL;h;f-wj!VLV_D)~#DdyS;t{fS}1@MRBQS5XA5iBd&?-kYmMFg>!#Y z%}7bhz+aUXM!g!VBXro2YQ@S>4U86tC@_%`Gi0X=7MqOsAHU*{N`s$&RrvPvH@^S+ zi`Q@7Qy~jnwDY9B_p$iYQ%v3W046Jk$TDKSF92M+9E!Nuctk`-;o9|E_@~lCrP0A) zGQ()H!lO9ReP#*a+oLtG%rR2VSi`53R(31D$P8pZdpCT;# z87`zffyjs~Bi4H8d)w?%o{|JkMI79ftBcxBdJ65b44IT@jV};@$TDIxeX7Fm?(zB% z78Z`gq+}!}CnGd8EVmsK8wb6?Shy|_1Vok*yFfUdJXg*+{8X{D0ZPzm3fBolmJxeU lOGK8@-?W|@2Wq3p{s-D?JW3E}sAT{E002ovPDHLkV1n$j894v| literal 0 HcmV?d00001 diff --git a/icons/send_32x32.png b/icons/send_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..5f0d720adf49a4b8def0ddccce497cd2f888422f GIT binary patch literal 1218 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 BsTu$P literal 0 HcmV?d00001 diff --git a/icons/start_32x32.png b/icons/start_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..b26aa47fe06ef0965d1b77ed59f8112157874191 GIT binary patch literal 6491 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

G*6%3 z?A?^o?5+K`y39Gcku3n@H?zTRXUn8I-Qb&)h!MlR*pQ3S|{FK0XqSXqJOlwWAo7R%771vgatyWkq z1{fGkW(j76W(k0*xYm!>&(x0|GfiimTzIisFjYiYO@KJMhW8?@+dN{Z^H&;~?B;x%b`PS0ilP zw``v)gxk(<>)6%+rIHJhUP%Xp9jfhL+F!ua!WdzoFkWW3=iZ+9JE#v7)_4zpOJD(w4DI6mhc?Fj`i%*M%;;T}Xev7`>{Z7*5 zgSLGST_A%fvV9H(%Q9vH2o|5m}Et`8Zn0>d@ zXukb??tFXUK8r*PZ;Qmi!&V_yZdM`vX=ZoKu9)3vZ8cbD5Nfb4#YWpgJ4V}bDXRD+ zH{c8$fV0L0G9-T@!wH#YOAr76002ovPDHLkV1iDy BjeY#4S5*LG0bwr$(CZM(s?UJOxcQk5!t zX6x1twO>CsugMiEWboTB;fQkO9vO!Z!F}}#JlC$lbK?fwS1!Xmb`1Q+ogcyvLV_P@ zJ9omdZY}JqSHZq~8O$Y%q0gTOW$qlv>(}E!aF8YW0=^Sa=CQVC7tEzg;1sZH#d0`i z&VXauRM-V<%$W^o%xI{aHsEeRz~e6@cXH-TlecVyA&QR0i}1?j!YhXz?&(wEo;(pR z4MUtAFvgBXX8*oWHm?UWmf$jxme8*!^l@Y1oH7}&1i*P^8UohAd8sO}_vrR9w2IHl!;Zs=Roj4x;nbYAFBdvcAFk=Z$5*hb`3L(8* z5VV?AVYX_97m92NUdR%(rVSw#FNBO-gs{Q{W-P&BakyKQkS-pxf=c0eQX)9d#43ih z&kQbwOwoxbMmYSr;1E&Pl@ebHcrcJ2SOU-D`S1^*7|Ar}v5z z7a)lu4=oQt7efjx4k@Gzq}+u2qQhNgnEgVsI=7GrI~Qd!Q~|SuS3W-|oekXi0{&XE zLKM#DBt#W0fcStCND@EudOM&bR{R+LQnH0e*;%HH^I*-I?!)!#yLsJPvZQ9g|1<9Z XbH$^JSjSQ)~O4 z^CcQ_zxWHulg)Do4}pI|r6cmU^5c&`Hrdd?|5H~-vbL63c{#a(0VY~nn8;+9ICO}C zTW+~?^w6QRfj%At`6mxbrGlo~FMVmvf|F0i8iNI_)>vyXUaT?xb#QMCEt@yf{>C?M z(#AAKB_3IUN()UhKJt+@3)ip5WV84JUv5Hacpcn((O!tfXy3Dk?vH(JlR$onhgzT= zQsqKZ-P_*2a^VRl_;O#6pQs2XxQ@y=4sF}F)AjYQZ5^;Q0^QsP@&oQo4rY{i@`l&G zdg-E*PQ~VO_%dILF?r{3B2(knVG-@$i_p1aD{Vjg@y_9(L_6do-yxr2m{l$Ocg~BR zzi`p%XW`4y8hjZbu8UY3m9WwHv#tyN?*N_CgNd8%-L}H+$D|Y;fo~{6)ajr`6;K+_N!l^I@(dz1_ippYOLFmIs?I(e0AF_C zTi;?@PdAPT{`h-`hnf41calt{02ph@aO88J1Ep|{!PSPWwRG>?wv&KyaB>Q?H%ecQHJ~+P&CTe^Nt|So5JV}K zzV)qa`p}11^uG5+@(B8FyA3ryjuH@K!QWJ2lAHuCLSo|t6oF_73SfiL<0a-T(){5M zG5>wA$|*3D6Bw_A z^>&Rv4KL;6W9)d#n{Y=*c|fdX*Ehd`^S3`!l*{3S|F6Qe91(IVaCyN?bddl@i(gOi60PB^>4h@iLYC=ok z@`9gOf&dN5DA?mIQ0k4kG?}EhFiDYj?GzM%pT2&ET3e}FxDXK`0<_lj@7P8a7Ckmb zZlIrRYYPMI9Sj4*7Dg?YioiukOcRg*$qDrD-HR5`fPZg*Nr4toq`vYEV(B!4gM$E5 zC9JgoEPcz{xbfl(SbAU|F~Ekqj1xw{mB2+nGyxYJqrf3ZEKD5e6h+OKzRIjCuD}?> z{gFteIN|qy;*Lu$Wd5N8;r~Z#Ie=Axi;$R0Kpd17P=G^p9#V?b=f6biiYxQvCY$9C zuX`;gzUH;0SFR+4cufuKe)mUqUUo6HhYz9z92>2tC2$cEi|4-w5CSBOI{Fx@F3;b& z|3>}O3tzyp8?WIX*IvWfzx*}nRjZ;B=`=In@;0)se>F*>wE&FDe-9`P_yKx)yUC8! z0)QnTdD|^y-umXL$F2W8vC)&S^W=2`myHf^l0^PH~ zcla(sWF--c;f%^S|5`f}XHE;0iWk5Y7`<`>wIBHyn_m7>W?g>`6%u_r8{pQ$64w0u zm)Ox!x?l1FW{i&mU@Qy)J7>XdZRkFivw@9-$W`QHps^~>KaZ)!S)75A@ZFmQ`cgGi z_H;3AYOE0&I6SSG0;8!3B@30@Nez|xsdxu!8w%){&vP3uf%?c2~h#=jU_s7o(umvi0R96KJZT>9?}FN!V-=1 zP%E1uE)QN{*NQ^^ynXy$@<&<#fpaSo{MD9LtFm;O(o~9~vN8&aicpS&(VEH8QAP&` z8S3uF?aG+8?x72SfAAp4B|J1TQ0hVoAP&O);DRlIPGE$GfSkaSi{eS$*|;zDFD%;kpMk_oGXy~_grP#`F{27F6b~YN;muf(Bq#WODDd!u)??=Xg60Kj*L%Z)H8IKd hM@-3523bzTcmd0ucC`+KGk^d9002ovPDHLkV1hX5VkQ6p literal 0 HcmV?d00001 diff --git a/icons/table_32x32.png b/icons/table_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..3814a4dab990c8ad256d1d1571e06721e4703d18 GIT binary patch literal 336 zcmV-W0k8gvP)J_n7{nW|MCY$D-}@(X_YM2I%QPJ)Iy+;r8qDVj3%eL@TPa!rWb8M>9VPY}# iQp{cqf&gr*WOWB&_bx{%E3upa0000 literal 0 HcmV?d00001 diff --git a/icons/varFolder_16x16.png b/icons/varFolder_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..f4179e850f52f13026b3119f2bb2fa0cd6d31861 GIT binary patch literal 485 zcmVeUOM?B70l5Y-?v*6!5VvlpT+K_G&$A;->K)cW-shy($qyhUqOseno|WwxuO`n2diF*V~8C!Vq9`ptk-1a z$WbE_gIBPEV8jxLrzQepK?VCQHI37E@1s5fp?VZ-{lrApa)-lOG2}0Yy*{pNy;0 bu-St@HG#(^4ebK900000NkvXXu0mjflgZ~I literal 0 HcmV?d00001 diff --git a/icons/varFolder_32x32.png b/icons/varFolder_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..5a8f6d7c35620d74fa31bc4ff53b0c7683bc0c3f GIT binary patch literal 1405 zcmV-@1%mpCP)H@7;U5u)@i3>7?a5 zE$397YX9>s;NDMt&nLIL&7C4c4*~;bgw)z#y&AuW+xR=TxX;tx?fluF=ffU!Pe1*| z&b-i*u^~7%hU|yl{=U!VFpeQYAd!h6k_>b@0ul5i(6Cewq|EKz@z!y(xoEq+;1gdu z=jZ2+j7UU~N#Z(JTf6mbZWrSaQW}B`em#fQ!VpA+RU;w7&}c#U?Km+smT38*kAC5C zc4pE*Ww%_YgrMkof*jHl}&8><(?V8+}=X00{^Q7U;A__N37;Cqu9- z;Tl)HT>SJW-(AD_tAT8T!|frxD_J#kwYhim=91ID_@UE3{juF{t3i;6B!z&0S_dQ~ zpz)LMUdDy}A5JiS=Nn&g<*Qu9{^=jd>;`m{3MBgC(#7+B{Jn2_-K(Fgtwv`r63t(4 z2>^&cdr(KZ`F?iZ{Q5V)_^nsH`Sm$XLikJV0@WWJ@VT#cS{jp#x-b}$ zDagVvK{-VwAjv8KF*tYjXU1=S_reWN?r|`8t01sR_OL*)$fO6yhcstcH+nC7CA;cj zu5rU0L}zR)$&z%eusb?1&eM^lu^#BjjCBA@jh94_m@+hM76L#$0Aoqs(1Pp;b_ z$2RHNjSb7fyfD8$GVp0rL& zHc88hdq=V>hP|^(jzO*K%yp)UeI`pMlQFC0m*;JIf{L%Pk^N7tlS$OeLtzs*)i~@x zpyODr1z?vfINpkArcpA@=p;JUSdyu-x56QisLRbbjvic`i6Bby0lQA&QI85XGs9S_ z2DXXvXq4P@UEY&rp(Rl)lqJ`Nol2=8nHQq4OJOr;NfvfSbXtYL$;Q#dluQYX$W&FV z*d@mYrnwYK5>bxXCIbXjfb>qE>6kELU5@CTmdP>`Np_AQ6WDb28Y7lUu4$$vOJU1F z4m%@OfjWFQ7dBaAl`J^wv|x>~MKcCD4k~6Y*O;)UWDm+hB&(s~Tk36bSsW?nG(fT! zq+W)@s zT35KNz02Ohn6pHIB-A021Y`h;X?79-L(r4J5``;w=yU{n49;zSX6(J2wp+VgBp}hy zQN6@UDWRl}>i+^;3It5T9u6su!WoL-8Rd75w=nPfR4a7og z44Bb-F+^Z$g>6J0hHcf4)iC<;&n_7=+2&b?%VKQw+O*f$b!JG`HE)eeOJ#Q> z8)Ii!b2L$x>sT6_i3o~-VWF?sOik*>v8{fzyKv@OLv%S*8C`{QyYgydUO2#d#Z*6I z)E9kh&EbxYab($+{K4LciPgY1St7yifYe>~^4I*_cxt%PwO{v=yF5Z0PN0z?#c%uG zfDAO54GkfSe`bi*g-)cf5FII{h@_=WF literal 0 HcmV?d00001 diff --git a/icons/vars_16x16.png b/icons/vars_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..5870ae5ea08137ef94ea2cac0786f1f1c4263282 GIT binary patch literal 559 zcmV+~0?_@5P)a$t2Sfh;_>JH1Kf?DV zTL@&D<#m(pZHyH|+MpmTJDUtDQ?i&s8aA$7g;k3epk9Si7}UNMn3T2HtXdKhRH;}7 zTAl>}19AEdAB7U7O5ys|%Xo7C9`6Pba#eyPb%wC) zEFrn<6y+g`i)L}ZUW1ee>9acHgM0Al2Qsqz++}2k{`~2ief9ZAQxm};1#J*aBCZNr z1wF`f;sd^q057OMK7RY<$UC1PDS!8HZ6#pgVfAKs%^GRy> x=kHSrNWo$Yr7^^6mROgy*%%vTBWzfD{{c41(nkO}L$?3`002ovPDHLkV1nEX{XPHy literal 0 HcmV?d00001 diff --git a/icons/vars_32x32.png b/icons/vars_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..fffa2718eeb3afcd884d1ea5e85a053cca7f0683 GIT binary patch literal 1138 zcmV-&1daQNP)}a0?3Q(H=YZw5jf-(qD3JN+4!B9L=+B;}JL^K4Y zYPBdSRcaAirD}EX#Rs20!FKfYT=nJ)Z!Z&7s!&=((~O2G6kyscY+K!1Q#YG=W855T z3n1F^Gs>A7A~Z#cx=&LlrT32_zJKW@v`;xbssn$Zeex;LGsRSMtIk#S`Bgzpld0EY zCi5l}qm+0_4mU@!FEja!mOj4_wwb-}r{6I8hwm}XoU`KB_di3&WZfwD7hxuu8X6%~ zn~DdGVL~U}Da}t7$9~_IahX#omGSeZUt{baz0e(9`0dkgVR|M(Wo!g9(Uc%=2M{OI zq)`ooD}fvXSLt^IvMTr;;h0}758m$*O{ExD9F0?g&cZb?=(K)gBj8kDGIcmLS zu*C;oWecdr2}YV)lG3#%;Q42s#$o&IgXy|Eu*JG-!!uYMz=da@>1@s!rgP)o)>w9F zH$I#=O~6yLPR(Bk;Dc|x`YL?)%{N2DoOax?IOdd7Q5-0`xDiY~$)uBEI8p508rgv; zMiaPVKAg!L3S<8)v(1KI#+LEvPrqO|m)~#`8WAPQ_#>!HK)`l8@4|<;1dqM=azhcG z8|u3ha@&IsWB!E}ZZ04d0lyB8K{NP(^)}wb$8O6*cr@H*?!<*>oi!x+@++;J?-E+A zkr(cWKhtpKyKlD*h2%<3B6si7>=K$51Tgu&ha3`KIv?jw?h9AykH8q`TH1w>R+Ezi zLYuBxt;_c;Pkr$NDpfb0i| zEkGA6bUilwbySaSo3v@x)MG7TGVk%f4^kh;09$Fo{sc;EM3q`H_}lLnxF4s_e}%~_ zm1JU!AO)1htT=afuM^;T@RD;hfHpS{Sn)<;D>3INiH9y5h{Y!oM`Da?jKx>$c{v9Z zz{HBNBy97^#kaFB^fJ~Vo+SRk04idPL#v7TIHrg{0cEn(n(ukwJpcdz07*qoM6N<$ Eg654IqyPW_ literal 0 HcmV?d00001 diff --git a/input.txt b/input.txt new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/input.txt @@ -0,0 +1 @@ + diff --git a/libhpcalcs/AUTHORS b/libhpcalcs/AUTHORS new file mode 100644 index 0000000..b9447cf --- /dev/null +++ b/libhpcalcs/AUTHORS @@ -0,0 +1,8 @@ +hplibs +====== +Lionel Debroux + +Code patterns and snippets borrowed from libti*/tilp: +Romain Liévin +Benjamin Moody +... and other contributors listed in the AUTHORS files of libti* & tilp. diff --git a/libhpcalcs/COPYING b/libhpcalcs/COPYING new file mode 100644 index 0000000..f90922e --- /dev/null +++ b/libhpcalcs/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/libhpcalcs/ChangeLog b/libhpcalcs/ChangeLog new file mode 100644 index 0000000..5a76bbd --- /dev/null +++ b/libhpcalcs/ChangeLog @@ -0,0 +1,3 @@ +Log of changes (libhpcalcs): + + - Initial version. diff --git a/libhpcalcs/NEWS b/libhpcalcs/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/libhpcalcs/README b/libhpcalcs/README new file mode 100644 index 0000000..0971758 --- /dev/null +++ b/libhpcalcs/README @@ -0,0 +1,315 @@ +hplp: code for communicating with recent HP calculators +======================================================= + +hplp (still entirely in libhpcalcs for now) is the beginning of a toolkit for +two-way communications with HP Prime calculators. + +See below for detailed build instructions. Note that +the install_hplp.sh script provided alongside this README +( https://raw.github.com/debrouxl/hplp/master/install_hplp.sh ) +automates part of the build process. + + +Status (2014/06/03) +------------------- +The code base does: +* work on Windows, Linux and MacOS X; +* support communication with a single Prime calculator connected to the + computer. The communication is known to work with the following firmware + versions, but might not work with other older or newer versions, if HP + performs backwards-incompatible changes in the protocol (like TI does): + * "SDKV0.30" firmware version from mid-August 2013; + * "SDKV0.32" build 5447 firmware version from late November 2013; + * partial success with the V0.031.6030 / V0.032.6030 build 6030 firmware + versions published late May 2013. +* implement 10 operations (without _known_ bugs): + * ready check; + * get calculator information; + * set date and time; + * receive screenshot; + * send file (for now, the leading metadata of *.hpprgm, if any, needs to be + stripped manually); + * receive file; + * receive backup; + * send key(s) (remote control); + * send chat; + * receive chat; +* provide a terminal-based UI: the test program "test_hpcalcs". + +The code base doesn't: +* perform color conversion on PNG screenshots (libhpcalcs should use libpng to + uncompress the files, mangle pixels, and recompress to PNG); +* provide a user-friendly GUI for the above, but a GUI can definitely build + upon the library easily enough; +* implement _proper_ conversion from UTF-16 LE to other charsets on its own: + that's a task for libicu, Qt or other libraries. + + +Goals +----- +Laying the groundwork for GUIs dealing with two-way communication with HP +Prime calculators, and possibly 39gII (Prime and 39gII are similar), using +USB HID class, and possibly later fake MSD for firmware upgrades. + + +Non-goals +--------- +As far as I'm concerned, working _by myself_ on: +* duplicating / replacing code targeting older, non-USB HID-based HP + calculators; +* a GUI comparable to HP's Connectivity Kit. However, other people should + obviously do it, in order to make the software much more usable ! + + +Build process summary +--------------------- +As mentioned above, the repository contains an script for automating the +part of the build strictly related to libhpcalcs / hplp: install_hplp.sh . + +The build process is that of pretty much any autotools-based piece of +software: +$ autoreconf -i -f +$ ./configure +$ ./make check +# make install + +The terminal-based test program is in tests/test_hpcalcs. + +For now, the main external dependency of libhpcalcs is HIDAPI: +https://github.com/signal11/hidapi.git +Unless your Linux distro packages it (few do), you need to compile and +install it yourself (it is also an autotools-based project: configure, +make, make install). + + +Detailed build & configuration process for Debian, Ubuntu, Mint and derivatives +(courtesy of Jonathan Cameron) +------------------------------------------------------------------------------- + +1. Install build dependencies (packages required for building libhpcalcs). +As root (with root shell, or sudo) +# (sudo) apt-get install build-essential git autoconf automake autopoint libtool gettext xdg-utils libpng-dev libudev-dev libusb-1.0-0-dev autotools-dev + +2. Install hidapi: + +$ mkdir -p $HOME/lpg # or anywhere else you see fit +$ cd $HOME/lpg +$ git clone https://github.com/signal11/hidapi.git hidapi +$ cd hidapi + +Follow the directions from the README.txt file: + +$ ./bootstrap +$ ./configure --prefix=/usr +$ make + +As root (with root shell, or sudo): +# (sudo) make install <----- as root, or using sudo + +3. Install libhpcalcs + +Download +https://raw.github.com/debrouxl/hplp/master/install_hplp.sh +and copy it to, for instance, $HOME/hplp + +$ cd $HOME/lpg +$ chmod +x install_hplp.sh +# (sudo) ./install_hplp.sh + +4. Test hplp + +As root, verify that you can run test_hpcalcs. +Plug in your HP Prime to the computer and turn it on. +Wait about 10 seconds and then run: + +$ $HOME/lpg/hplp/libhpcalcs/tests/test_hpcalcs + +You should see the menu of possible operations. +If you do, hplp is working and you are successfully communicating with your Prime. + +5. Set up for normal users to access the Prime with the test software. + + a. If you are on your own isolated computer (e.g, laptop), + then add the following udev rules to the new file + /etc/udev/rules.d/82-hp-prime.rules : + + # HP Prime Calculator + SUBSYSTEM=="usb", ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="0441", MODE="0666" + KERNEL=="hidraw*", ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="0441", MODE="0666" + + Note that anyone on your system will be able to access your Prime with + the hplp software. + + If you are on a shared system and you want to avoid anyone accessing the + device but you, do this instead: + + # HP Prime Calculator + SUBSYSTEM=="usb", ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="0441", ACTION=="add", GROUP="dialout", MODE="0664" + KERNEL=="hidraw*", ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="0441", ACTION=="add", GROUP="dialout", MODE="0664" + + and add yourself to the 'dialout' group. You may need to switch to + the new group for this to work (eg: newgrp dialout). + + To activate the new udev rule, turn off your HP Prime, turn it back on, + and wait for about 10 seconds. Then, check the permissions for the device + (in /dev/hidraw? and /dev/usb/hiddev?). + + +NOTES: + * technically, compilation doesn't require being root, only the 'make install' + commands for hidapi and libhpcalcs (embedded into install_hplp.sh) require + root (and even then, only if installing to /usr or some root-only location). + install_hplp.sh cannot use sudo on its own, as it's not necessarily + configured by default. + * in the above directions, the source location to be $HOME/lpg. You may prefer + some other location, just replace $HOME/lpg by your location. + * the install prefix is forced to /usr, so that you don't have to adjust the + LD_LIBRARY_PATH environment variable, or add a line in /etc/ld.so.conf, or + add a file in /etc/ld.so.conf.d. + The install prefix is controlled by using the --prefix option to configure, + for hidapi and within install_hplp.sh. + + +Technical notes +--------------- +The code leverages HIDAPI, and it was strongly inspired by the time-proven +concepts and APIs of the libti* family, which I (Lionel Debroux) am the +current maintainer of. +The GPL'ed libti* family ( https://github.com/debrouxl/tilibs ) forms the +back-end for TILP, TIEmu, TilEm and titools (mainly), and contains the only +third-party portable FLOSS code with two-way communication support for all +three series of TI-Z80, TI-68k and TI-Nspire graphing calculators. +Putting together framework strongly inspired by libti* clearly took some work, +but having such a foundation saves time in the longer term. IOW, it's worth +the apparent complexity. + +Obviously, until the calculator's protocol is better documented, the API +will change in backwards-incompatible manners (for instance, the output +parameters of check ready and get infos operations). + + +TODO list +--------- +* ... hopefully attract _code_ contributors who have more free time than I do; +* a GUI, but I won't be the one doing it. + Nowadays, Qt is the main choice for a portable, good-looking, fast UI + toolkit (for native code programs). GTK+ used to be a better choice wrt. + portability, but that was years ago, and in 2013-2015, a number of + applications and desktop environments are switching from GTK+ to Qt. + UIs based on Web technologies are another option, Qt provides support for + those (QtWebkit / QtWebEngine) as well. +* strip out leading program metadata, if any, from .hpprgm files, like the + UTF-16LE BOM is already stripped. + Implementation notes: + * in a loop, attempt to read 4 bytes containing size, and seek forward of + that many bytes. If this leads the offset beyond EOF, break out. +* add some form of packet parsing + validation API, which can be used for + both internal sanity checking and packet dissection; +* add 39gII support, probably, as the Prime is similar to the 39gII. That's + where the usefulness of the cables / calcs separation and cables generic + API will become most obvious. + Implementation notes: + * move the upper layer from prime_cmd into a new file called by both + prime_cmd and the new file (like e.g. dusb_cmd in libticalcs). + * the 39gII has 00 00 CRC. +* modify the way recv_backup works for more reliability. + Implementation notes: + * recv_backup should attempt to receive as much data as the calculator sends, + and then split the received data. The main heuristic would be to assume + that some packets were lost, and traverse backwards the contents of the + current file to find a (00) F7 01 xx xx xx xx packet ? While this could + fail in some circumstances, it should usually salvage more data. + * recv_file shall probably be refactored as well; + * Marcus Von Cube reported that when receiving a backup fails, the test + program crashes: +" +hpcalcs INFO: prime_send_data: send remaining failed +hpcalcs ERROR: calc_prime_recv_backup: s_recv_backup failed +hpcalcs ERROR: hpcalcs_calc_recv_backup: recv_backup failed +hpfiles INFO: Displaying var entry 83EC8B55 +hpfiles INFO: Name: +" +* implement screenshot color conversion, with libpng; +* add progress feedback functionality, improved from libti*: the libti* + implementation is too complex. + Implementation notes: + * can information for current chunk of the transfer + information for the + transfer as a whole be passed through arguments of a single callback. +* add port number functionality, known from libticables, in the API. That + will enable passthrough mode for communication between real and/or + emulated calculators; +* fill in torture test; +* add two APIs for dynamically building NULL-terminated const char * arrays + from the two first columns of typesprime.c::PRIME_CONST. +* somehow gain better knowledge of the protocol, so as to be able to... +* ... add more operations and data structures. +* add dissectors and session logs, improved from libticables. + Design goals: + * make it possible to trace packet flow direction; + * make it possible to validate (sanity check) packets , to begin with... + Implementation notes: + * new APIs for registering and unregistering (multiple) callbacks; + * call the registered callbacks upon packet send and packet receive; + * leverage the packet parsing / validation code. +* add support for Prime in fake MSD mode, for firmware upgrades ? I've + already partially analyzed the protocol and published my notes at + http://tiplanet.org/hpwiki/index.php?title=HP_Prime/Linking_Protocol + but I'm not really confident about it ;) + Need to find a library for MSD. libusb does not recommend using it + in MSD mode. +* certainly more... at least, if users and programmers think the software + fits a purpose :-) + + +License +------- +Copyright (C) 2013-2015 Lionel Debroux + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + +Changelog +--------- +2013/11/03 - 2013/12/30: improve logging, create install_hplp.sh, improve +error passing, fix bugs reported by more beta-testers, add more operations. +2013/10/22 - 2013/11/03: Created autotools-based project and test client; +vastly expand the code, to the point it becomes releasable (if not really +usable). First public announcement (limited scope). +2013/10/21: Split into multiple files, but still piggybacking hidtest.cpp. +2013/10/20: First public PoC. +2013/10/19: Project started. + + +Credits (demoscene style) +------------------------- +* _many_ thanks to Xavier "critor" Andrani for testing many program versions + on his real Prime and providing many USB packet dumps (which I analyzed + with Wireshark) and terminal output sessions; +* many thanks to Marcus Von Cube for providing USB packet dumps for the 39gII, + which show that it's quite similar to the Prime; +* thanks to Jonathan Cameron for writing a more detailed build procedure for + Debian and derivatives; +* thanks to other beta-testers who reported bugs on TI-Planet and MoHPC: + * with the Prime: persalteas (Linux), wawachief (Linux & MacOS X), DJ + Omnimaga (Windows), Adriweb (MacOS X & Windows), Jonathan Cameron (Linux), + Cristian Arezzini (Linux), LarsF (MacOS X), Egan Ford (MacOS X); + * with the 39gII: Marcus Von Cube; +* greetings to the other TI-Planet ( http://tiplanet.org/ ) admins; +* greetings to the HP Prime developer team at HP: some of its members spend + time attending user message boards and interacting with users and programmers, + even in communities traditionally interested in TI calcs (Omnimaga, Cemetech, + TI-Planet), which is a win-win move for both HP and its users; +* fuck TI EdTech management imposing a closed-minded, lose-lose stance on the + Nspire platform, despite repeated community input since 2007. diff --git a/libhpcalcs/include/error.h b/libhpcalcs/include/error.h new file mode 100644 index 0000000..5d5ebd2 --- /dev/null +++ b/libhpcalcs/include/error.h @@ -0,0 +1,71 @@ +/* + * libhpfiles: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file error.h Files / cables / calcs: Error IDs. + */ + +#ifndef __HPLIBS_CALCS_ERROR_H__ +#define __HPLIBS_CALCS_ERROR_H__ + +typedef enum { + ERR_HPLIBS_GENERIC_FIRST = 0, + ERR_SUCCESS = 0, // Must be equal to ERR_HPLIBS_GENERIC_FIRST + ERR_MALLOC, + ERR_INVALID_HANDLE, + ERR_INVALID_PARAMETER, + ERR_INVALID_MODEL, + ERR_LIBRARY_INIT, + ERR_LIBRARY_EXIT, + ERR_LIBRARY_CONFIG_VERSION, + ERR_HPLIBS_GENERIC_LAST = 127, + + ERR_FILE_FIRST = 128, + ERR_FILE_FILENAME = 128, + ERR_FILE_LAST = 255, + + ERR_CABLE_FIRST = 256, + ERR_CABLE_NOT_OPEN = 256, + ERR_CABLE_OPEN, + ERR_CABLE_BUSY, + ERR_CABLE_WRITE_ERROR, + ERR_CABLE_READ_ERROR, + ERR_CABLE_INVALID_FNCTS, + ERR_CABLE_PROBE_FAILED, + ERR_CABLE_LAST = 383, + + ERR_CALC_FIRST = 384, + ERR_CALC_NO_CABLE = 384, + ERR_CALC_CABLE_NOT_OPEN, + ERR_CALC_BUSY, + ERR_CALC_INVALID_FNCTS, + ERR_CALC_PACKET_FORMAT, + ERR_CALC_SPLIT_TIMESTAMP, + ERR_CALC_PROBE_FAILED, + ERR_CALC_LAST = 511, + + ERR_OPER_FIRST = 512, + ERR_OPER_LAST = 639 +} hplibs_error; + +#endif diff --git a/libhpcalcs/include/export.h b/libhpcalcs/include/export.h new file mode 100644 index 0000000..2505dbb --- /dev/null +++ b/libhpcalcs/include/export.h @@ -0,0 +1,91 @@ +/* + * libhpfiles, libhpcables, libhpcalcs - hand-helds support library + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file export.h Stuff for function export and calling convention. + */ + +#ifndef __HPCALCS_EXPORT__ +#define __HPCALCS_EXPORT__ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// Calling convention: default +#define HPCALL + +// Symbols export +#if defined(HAVE_FVISIBILITY) // GCC 4.0 has introduced the -fvisibility flag (similar to declspec) +#define HPEXPORT __attribute__ ((visibility("default"))) + +#elif defined(__WIN32__) +# if defined(_MSC_VER) // MSVC 5.0 mini +# if defined(HPCALCS_EXPORTS) +# define HPEXPORT __declspec(dllexport) +# else +# define HPEXPORT __declspec(dllimport) +# endif + +# elif defined(__MINGW32__) // MinGW - GCC for Windows, (c) 2002 Kevin Kofler +# if defined(HPCALCS_EXPORTS) // defined by the configure script +# define HPEXPORT __declspec(dllexport) +# else +# define HPEXPORT __declspec(dllimport) +# endif +# endif + +#else +# define HPEXPORT // default +#endif + +#ifdef __cplusplus +} +#endif + +// Symbols deprecation +#ifndef HPLIBS_DEPRECATED +# ifdef __GNUC__ +# if (__GNUC__>3) || (__GNUC__==3 && __GNUC_MINOR__>=3) +# define HPLIBS_DEPRECATED __attribute__((deprecated)) +# else /* not GCC >= 3.3 */ +# define HPLIBS_DEPRECATED +# endif /* GCC >= 3.3 */ +# else /* not __GNUC__ */ +# ifdef _MSC_VER +# if _MSC_VER >= 1300 +# define HPLIBS_DEPRECATED __declspec(deprecated) +# else /* not _MSC_VER >= 1300 */ +# define HPLIBS_DEPRECATED +# endif /* _MSC_VER >= 1300 */ +# else /* not _MSC_VER */ +# define HPLIBS_DEPRECATED +# endif /* _MSC_VER */ +# endif /* __GNUC__ */ +#endif /* HPLIBS_DEPRECATED */ + +#endif diff --git a/libhpcalcs/include/filetypes.h b/libhpcalcs/include/filetypes.h new file mode 100644 index 0000000..ddfd14c --- /dev/null +++ b/libhpcalcs/include/filetypes.h @@ -0,0 +1,35 @@ +/* + * libhpfiles: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file filetypes.h Files: Umbrella header for file types. + */ + +#ifndef __HPLIBS_FILES_FILETYPES_H__ +#define __HPLIBS_FILES_FILETYPES_H__ + +#include "typesprime.h" + +#define HPLIBS_FILE_TYPE_UNKNOWN (0xFF) + +#endif diff --git a/libhpcalcs/include/gettext.h b/libhpcalcs/include/gettext.h new file mode 100644 index 0000000..1564ac7 --- /dev/null +++ b/libhpcalcs/include/gettext.h @@ -0,0 +1,60 @@ +/* + * libhpfiles: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file gettext.h Files / cables / calcs: internationalization (i18n) stuff. + */ + +#ifndef HPLIBS_GETTEXT_H +#define HPLIBS_GETTEXT_H + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +/* + * Standard gettext macros. + */ +#ifdef ENABLE_NLS +# include +# undef _ +# define _(String) dgettext (PACKAGE, String) +# ifdef gettext_noop +# define N_(String) gettext_noop (String) +# else +# define N_(String) (String) +# endif +#else +#warning NLS disabled +# define textdomain(String) (String) +# define gettext(String) (String) +# define dgettext(Domain,Message) (Message) +# define dcgettext(Domain,Message,Type) (Message) +# define bindtextdomain(Domain,Directory) (Domain) +# define _(String) (String) +# define N_(String) (String) +#endif + +#endif diff --git a/libhpcalcs/include/hpcables.h b/libhpcalcs/include/hpcables.h new file mode 100644 index 0000000..922477c --- /dev/null +++ b/libhpcalcs/include/hpcables.h @@ -0,0 +1,249 @@ +/* + * libhpcables: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file hpcables.h Cables: base part. + */ + +#ifndef __HPLIBS_CABLES_H__ +#define __HPLIBS_CABLES_H__ + +#include +#include + +#include "hplibs.h" + +//! Opaque type for internal _cable_fncts. +typedef struct _cable_fncts cable_fncts; +//! Opaque type for internal _cable_handle. +typedef struct _cable_handle cable_handle; + +//! Internal structure containing information about the cable, and function pointers. +struct _cable_fncts { + cable_model model; + const char * name; + const char * description; + int (*probe) (cable_handle * handle); + int (*open) (cable_handle * handle); + int (*close) (cable_handle * handle); + int (*set_read_timeout) (cable_handle * handle, int read_timeout); + int (*send) (cable_handle * handle, uint8_t * data, uint32_t len); + int (*recv) (cable_handle * handle, uint8_t ** data, uint32_t * len); +}; + +//! Internal structure containing state about the cable, returned and passed around by the user. +struct _cable_handle { + cable_model model; + void * handle; + const cable_fncts * fncts; + int read_timeout; + int open; // Should be made explicitly atomic with GCC >= 4.7 or Clang, but int is atomic on most ISAs anyway. + int busy; // Should be made explicitly atomic with GCC >= 4.7 or Clang, but int is atomic on most ISAs anyway. +}; + + +//! Structure passed to \a hpcables_init, contains e.g. callbacks for logging and memory allocation. +typedef struct { + unsigned int version; ///< Config version number. + void (*log_callback)(const char *format, va_list args); ///< Callback function for receiving logging output. + hplibs_malloc_funcs * alloc_funcs; ///< Function pointers used for dynamic memory allocation. If NULL, the library defaults to malloc(), calloc(), realloc(), free(). +} hpcables_config; + +//! Latest revision of the \a hpcables_config struct layout supported by this version of the library. +#define HPCABLES_CONFIG_VERSION (1) + + +typedef enum { + PACKET_DIRECTION_NONE = 0, + PACKET_DIRECTION_SEND, + PACKET_DIRECTION_RECV +} CablePacketDirection; + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Initializes library internals. Must be called before any other libhpcables function. + * \param config pointer to struct containing e.g. callbacks passed to the library. + * \return Whether the initialization succeeded. + * \note the contents of alloc_funcs are copied. + * \todo return instance count instead. + **/ +HPEXPORT int HPCALL hpcables_init(hpcables_config * config); +/** + * \brief Tears down library internals. No other libhpcables function can be called after this one. + * \return Whether the teardown succeeded. + * \todo return instance count instead. + **/ +HPEXPORT int HPCALL hpcables_exit(void); + +/** + * \brief Returns the library version string. + * \return The library version string, usually under the form "X.Y.Z". + **/ +HPEXPORT const char* HPCALL hpcables_version_get(void); + +/** + * \brief Returns the cables supported by the current build of the library. + * \return An integer containing a binary OR of (1 << CABLE_*) values, where CABLE_* values are defined in enum \a cable_model. + **/ +HPEXPORT uint32_t HPCALL hpcables_supported_cables(void); + +/** + * \brief Gets the error message if the error was produced by this library + * \param number the error number (from internal error.h) + * \param message out pointer for a newly allocated text error message, which must be freed by the caller + * \note the string is allocated with malloc(), therefore it must be freed with free(). + * \return 0 if the error was produced by this library, otherwise the error number (for propagation). + **/ +HPEXPORT int HPCALL hpcables_error_get(int number, char **message); + +/** + * \brief Sets the callback function used by the library for logging + * \param log_callback function pointer + */ +HPEXPORT void HPCALL hpcables_log_set_callback(void (*log_callback)(const char *format, va_list args)); +/** + * \brief Sets the log level of the library, for controlling how much logging output is produced. + * \param log_level log level (from hplibs.h) + * \return the previous log level + */ +HPEXPORT hplibs_logging_level HPCALL hpcables_log_set_level(hplibs_logging_level log_level); + +/** + * \brief Creates a new handle (opaque structure) for the given cable model. + * The handle must be freed with \a hpcables_handle_del when no longer needed. + * \param model the cable model. + * \return NULL if an error occurred, otherwise a valid handle. + **/ +HPEXPORT cable_handle * HPCALL hpcables_handle_new(cable_model model); +/** + * \brief Deletes a handle (opaque structure) created by \a hpcables_handle_new(). + * \param handle the handle to be deleted. + * \return 0 if the deletion succeeded, nonzero otherwise. + **/ +HPEXPORT int HPCALL hpcables_handle_del(cable_handle * handle); +/** + * \brief Shows basic information about a handle. + * \param handle the handle to be dumped. + * \return 0 if the handle was non-NULL, nonzero otherwise. + **/ +HPEXPORT int HPCALL hpcables_handle_display(cable_handle * handle); + +/** + * \brief Retrieves the cable model from the given cable handle. + * \param handle the cable handle + * \return the cable model corresponding to the given handle. + */ +HPEXPORT cable_model HPCALL hpcables_get_model(cable_handle * handle); + +/** + * \brief Gets the read timeout (in ms) for the given cable handle. + * \param handle the cable handle + * \return the current read timeout, 0 if error. + */ +HPEXPORT int HPCALL hpcables_options_get_read_timeout(cable_handle * handle); +/** + * \brief Sets the timeout (in ms) for the given cable handle. + * \param handle the cable handle + * \param timeout the new timeout. + * \return 0 if the operation succeeded, nonzero otherwise. + */ +HPEXPORT int HPCALL hpcables_options_set_read_timeout(cable_handle * handle, int timeout); + +/** + * \brief Probes the given cable. + * \param handle the handle to be probed. + * \return 0 if the operation succeeded, nonzero otherwise. + **/ +HPEXPORT int HPCALL hpcables_cable_probe(cable_handle * handle); +/** + * \brief Opens the given cable. + * \param handle the handle to be opened. + * \return 0 if the operation succeeded, nonzero otherwise. + **/ +HPEXPORT int HPCALL hpcables_cable_open(cable_handle * handle); +/** + * \brief Closes the given cable. + * \param handle the handle to be closed. + * \return 0 if the operation succeeded, nonzero otherwise. + **/ +HPEXPORT int HPCALL hpcables_cable_close(cable_handle * handle); +/** + * \brief Sends data through the given cable. + * \param handle the cable handle. + * \param data the data to be sent. + * \param len the size of the data to be sent. + * \return 0 if the operation succeeded, nonzero otherwise. + **/ +HPEXPORT int HPCALL hpcables_cable_send(cable_handle * handle, uint8_t * data, uint32_t len); +/** + * \brief Receives data through the given cable. + * \param handle the cable handle. + * \param data storage area for the data to be received. + * \param len storage area for the length of the received data. + * \return 0 if the operation succeeded, nonzero otherwise. + **/ +HPEXPORT int HPCALL hpcables_cable_recv(cable_handle * handle, uint8_t ** data, uint32_t * len); + +/** + * \brief Detects usable cables and builds an array of uint8_t booleans corresponding to the items of enum cable_model. + * \param result storage area for the cable models which were found. Use \a hpcables_probe_free to free the allocated memory. + * \return the number of usable cables. 0 means severe problem (e.g. memory allocation failure), as the null cable is always usable. + */ +HPEXPORT int HPCALL hpcables_probe_cables(uint8_t ** result); +/** + * \brief Frees the result of probing, created by \a hpcables_probe_cables. + * \param models the memory to be freed. + * \return 0 if the operation succeeded, nonzero otherwise. + */ +HPEXPORT int HPCALL hpcables_probe_free(uint8_t * models); +/** + * \brief Shows basic information about a probing result. + * \param models the probing information to be dumped. + * \return 0 if the operation succeeded, nonzero otherwise. + **/ +HPEXPORT int HPCALL hpcables_probe_display(uint8_t * models); + + + +/** + * \brief Converts a calculator model to a printable string. + * \param model the calculator model. + * \return the string corresponding to the calculator model. + **/ +HPEXPORT const char * HPCALL hpcables_model_to_string(cable_model model); +/** + * \brief Converts a string to a supported calculator model, if possible. + * \param str the string. + * \return the calculator model corresponding to the string, CALC_NONE if failed. + **/ +HPEXPORT cable_model HPCALL hpcables_string_to_model(const char *str); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libhpcalcs/include/hpcalcs.h b/libhpcalcs/include/hpcalcs.h new file mode 100644 index 0000000..90f4f22 --- /dev/null +++ b/libhpcalcs/include/hpcalcs.h @@ -0,0 +1,424 @@ +/* + * libhpcalcs: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file hpcalcs.h Calcs: base part. + */ + +#ifndef __HPLIBS_CALCS_H__ +#define __HPLIBS_CALCS_H__ + +#include +#include +#include + +#include "hplibs.h" +#include "hpfiles.h" +#include "hpcables.h" + +//! Opaque type for internal _calc_fncts. +typedef struct _calc_fncts calc_fncts; +//! Opaque type for internal _calc_handle. +typedef struct _calc_handle calc_handle; + +//! Indices of the function pointers in _calc_fncts. +typedef enum { + CALC_FNCT_CHECK_READY = 0, + CALC_FNCT_GET_INFOS = 1, + CALC_FNCT_SET_DATE_TIME = 2, + CALC_FNCT_RECV_SCREEN = 3, + CALC_FNCT_SEND_FILE = 4, + CALC_FNCT_RECV_FILE = 5, + CALC_FNCT_RECV_BACKUP = 6, + CALC_FNCT_SEND_KEY = 7, + CALC_FNCT_SEND_KEYS = 8, + CALC_FNCT_SEND_CHAT = 9, + CALC_FNCT_RECV_CHAT = 10, + CALC_FNCT_LAST ///< Keep this one last +} calc_fncts_idx; + +//! Used in the bit field of _calc_fncts, indicating whether a given calculator supports a given operation. +typedef enum { + CALC_OPS_NONE = 0, + CALC_OPS_CHECK_READY = (1 << CALC_FNCT_CHECK_READY), + CALC_OPS_GET_INFOS = (1 << CALC_FNCT_GET_INFOS), + CALC_OPS_SET_DATE_TIME = (1 << CALC_FNCT_SET_DATE_TIME), + CALC_OPS_RECV_SCREEN = (1 << CALC_FNCT_RECV_SCREEN), + CALC_OPS_SEND_FILE = (1 << CALC_FNCT_SEND_FILE), + CALC_OPS_RECV_FILE = (1 << CALC_FNCT_RECV_FILE), + CALC_OPS_RECV_BACKUP = (1 << CALC_FNCT_RECV_BACKUP), + CALC_OPS_SEND_KEY = (1 << CALC_FNCT_SEND_KEY), + CALC_OPS_SEND_KEYS = (1 << CALC_FNCT_SEND_KEYS), + CALC_OPS_SEND_CHAT = (1 << CALC_FNCT_SEND_CHAT), + CALC_OPS_RECV_CHAT = (1 << CALC_FNCT_RECV_CHAT) +} calc_features_operations; + +//! Screenshot formats supported by the calculators, list is known to be incomplete. +typedef enum { + // 5 is triggered periodically by the official connectivity kit. It returns something with a PNG header, but much smaller. + CALC_SCREENSHOT_FORMAT_FIRST = 8, + CALC_SCREENSHOT_FORMAT_PRIME_PNG_320x240x16 = 8, + CALC_SCREENSHOT_FORMAT_PRIME_PNG_320x240x4 = 9, + CALC_SCREENSHOT_FORMAT_PRIME_PNG_160x120x16 = 10, + CALC_SCREENSHOT_FORMAT_PRIME_PNG_160x120x4 = 11, + CALC_SCREENSHOT_FORMAT_LAST ///< Keep this one last +} calc_screenshot_format; + +//! Structure containing information returned by the calculator. This will change a lot when the returned data is better documented. +typedef struct { + uint32_t size; + uint8_t * data; +} calc_infos; + +//! Internal structure containing information about the calculator, and function pointers. +struct _calc_fncts { + calc_model model; + const char * name; + const char * description; + int features; + int (*check_ready) (calc_handle * handle, uint8_t ** out_data, uint32_t * out_size); + int (*get_infos) (calc_handle * handle, calc_infos * infos); + int (*set_date_time) (calc_handle * handle, time_t timestamp); + int (*recv_screen) (calc_handle * handle, calc_screenshot_format format, uint8_t ** out_data, uint32_t * out_size); + int (*send_file) (calc_handle * handle, files_var_entry * file); + int (*recv_file) (calc_handle * handle, files_var_entry * request, files_var_entry ** out_file); + int (*recv_backup) (calc_handle * handle, files_var_entry *** out_vars); + int (*send_key) (calc_handle * handle, uint32_t code); + int (*send_keys) (calc_handle * handle, const uint8_t * data, uint32_t size); + int (*send_chat) (calc_handle * handle, const uint16_t * data, uint32_t size); + int (*recv_chat) (calc_handle * handle, uint16_t ** out_data, uint32_t * out_size); +}; + +//! Internal structure containing state about the calculator, returned and passed around by the user. +struct _calc_handle { + calc_model model; + void * handle; + const calc_fncts * fncts; + cable_handle * cable; + int attached; // Should be made explicitly atomic with GCC >= 4.7 or Clang, but int is atomic on most ISAs anyway. + int open; // Should be made explicitly atomic with GCC >= 4.7 or Clang, but int is atomic on most ISAs anyway. + int busy; // Should be made explicitly atomic with GCC >= 4.7 or Clang, but int is atomic on most ISAs anyway. +}; + + +//! Structure passed to \a hpcalcs_init, contains e.g. callbacks for logging and memory allocation. +typedef struct { + unsigned int version; ///< Config version number. + void (*log_callback)(const char *format, va_list args); ///< Callback function for receiving logging output. + hplibs_malloc_funcs * alloc_funcs; ///< Function pointers used for dynamic memory allocation. If NULL, the library defaults to malloc(), calloc(), realloc(), free(). +} hpcalcs_config; + +//! Latest revision of the \a hpcalcs_config struct layout supported by this version of the library. +#define HPCALCS_CONFIG_VERSION (1) + + +//! Structure defining a raw packet for the Prime, used at the lowest layer of the protocol implementation. +typedef struct +{ + uint32_t size; + uint8_t data[PRIME_RAW_HID_DATA_SIZE + 1]; +} prime_raw_hid_pkt; + + +//! Structure defining a virtual packet for the Prime, used at the middle layer of the protocol implementation (fragmented to / reassembled from raw packets). +typedef struct +{ + uint32_t size; + uint8_t * data; + uint8_t cmd; +} prime_vtl_pkt; + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Initializes library internals. Must be called before any other libhpcalcs function. + * \param config pointer to struct containing e.g. callbacks passed to the library. + * \return Whether the initialization succeeded. + * \note the contents of alloc_funcs are copied. + * \todo return instance count instead. + **/ +HPEXPORT int HPCALL hpcalcs_init(hpcalcs_config * config); +/** + * \brief Tears down library internals. No other libhpcalcs function can be called after this one. + * \return Whether the teardown succeeded. + * \todo return instance count instead. + **/ +HPEXPORT int HPCALL hpcalcs_exit(void); + +/** + * \brief Returns the library version string. + * \return The library version string, usually under the form "X.Y.Z". + **/ +HPEXPORT const char* HPCALL hpcalcs_version_get(void); + +/** + * \brief Returns the calcs supported by the current build of the library. + * \return An integer containing a binary OR of (1 << CALC_*) values, where CALC_* values are defined in enum \a calc_model. + **/ +HPEXPORT uint32_t HPCALL hpcalcs_supported_calcs(void); + +/** + * \brief Gets the error message if the error was produced by this library + * \param number the error number (from internal error.h) + * \param message out pointer for a newly allocated text error message, which must be freed by the caller + * \note the string is allocated with malloc(), therefore it must be freed with free(). + * \return 0 if the error was produced by this library, otherwise the error number (for propagation). + **/ +HPEXPORT int HPCALL hpcalcs_error_get(int number, char **message); + +/** + * \brief Sets the callback function used by the library for logging + * \param log_callback function pointer + */ +HPEXPORT void HPCALL hpcalcs_log_set_callback(void (*log_callback)(const char *format, va_list args)); +/** + * \brief Sets the log level of the library, for controlling how much logging output is produced. + * \param log_level log level (from hplibs.h) + * \return the previous log level + */ +HPEXPORT hplibs_logging_level HPCALL hpcalcs_log_set_level(hplibs_logging_level log_level); + + +/** + * \brief Create a new handle (opaque structure) for the given calc model. + * The handle must be freed with \a hpcalcs_handle_del when no longer needed. + * \param model the calculator model. + * \return NULL if an error occurred, otherwise a valid handle. + **/ +HPEXPORT calc_handle * HPCALL hpcalcs_handle_new(calc_model model); +/** + * \brief Deletes a handle (opaque structure) created by \a hpcalcs_handle_new(). + * \param handle the handle to be deleted. + * \return 0 if the deletion succeeded, nonzero otherwise. + **/ +HPEXPORT int HPCALL hpcalcs_handle_del(calc_handle * handle); +/** + * \brief Shows basic information about a handle + * \param handle the handle to be dumped. + * \return 0 if the handle was non-NULL, nonzero otherwise. + **/ +HPEXPORT int HPCALL hpcalcs_handle_display(calc_handle * handle); + +/** + * \brief Retrieves the calc model from the given calc handle. + * \param handle the calc handle + * \return the calc model corresponding to the given handle. + */ +HPEXPORT calc_model HPCALL hpcalcs_get_model(calc_handle * handle); + +/** + * \brief Opens and attaches the given cable for use with the given calculator. + * \param handle the calculator handle. + * \param cable the cable handle. + * \return 0 upon success, nonzero otherwise. + **/ +HPEXPORT int HPCALL hpcalcs_cable_attach(calc_handle * handle, cable_handle * cable); +/** + * \brief Closes and detaches the cable attached to the given calculator handle. + * \param handle the calculator handle. + * \return 0 upon success, nonzero otherwise. + **/ +HPEXPORT int HPCALL hpcalcs_cable_detach(calc_handle * handle); +/** + * \brief Retrieves the cable handle attached to the given calculator handle, if any. + * \param handle the calculator handle. + * \return NULL if an error occurred, otherwise a cable handle. + **/ +HPEXPORT cable_handle * HPCALL hpcalcs_cable_get(calc_handle * handle); + +/** + * \brief Checks whether the calculator is ready + * \param handle the calculator handle. + * \param out_data storage area for information contained in the calculator's reply. + * \param out_size storage area for size of the information contained in the calculator's reply. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL hpcalcs_calc_check_ready(calc_handle * handle, uint8_t ** out_data, uint32_t * out_size); +/** + * \brief Retrieves some information, such as firmware version, from the calculator. + * \param handle the calculator handle. + * \param infos storage area for information contained in the reply. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL hpcalcs_calc_get_infos(calc_handle * handle, calc_infos * infos); +/** + * \brief Sets the calculator's date and time from a standard C89 / *nix timestamp. + * \param handle the calculator handle. + * \param timestamp the timestamp since the Epoch (1970). + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL hpcalcs_calc_set_date_time(calc_handle * handle, time_t timestamp); +/** + * \brief Retrieves a screenshot from the calculator + * \param handle the calculator handle. + * \param format the desired screenshot format. + * \param out_data storage area for screenshot contained in the calculator's reply. + * \param out_size storage area for size of the screenshot contained in the calculator's reply. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL hpcalcs_calc_recv_screen(calc_handle * handle, calc_screenshot_format format, uint8_t ** out_data, uint32_t * out_size); +/** + * \brief Sends a file to the calculator. + * \param handle the calculator handle. + * \param file information about the file to be sent. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL hpcalcs_calc_send_file(calc_handle * handle, files_var_entry * file); +/** + * \brief Receives a file from the calculator. + * \param handle the calculator handle. + * \param request information about the file to be received. + * \param out_file storage area for the file to be received. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL hpcalcs_calc_recv_file(calc_handle * handle, files_var_entry * request, files_var_entry ** out_file); +/** + * \brief Receives a backup (made of multiple files) from the calculator. + * \param handle the calculator handle. + * \param out_vars storage area for the files to be received. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL hpcalcs_calc_recv_backup(calc_handle * handle, files_var_entry *** out_vars); +/** + * \brief Sends a single keypress to the calculator. + * \param handle the calculator handle. + * \param code the key code. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL hpcalcs_calc_send_key(calc_handle * handle, uint32_t code); +/** + * \brief Sends potentially multiple keypresses to the calculator. + * \param handle the calculator handle. + * \param data the buffer containings key codes. + * \param size the size of the data in the buffer. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL hpcalcs_calc_send_keys(calc_handle * handle, const uint8_t * data, uint32_t size); +/** + * \brief Sends chat data to the calculator. + * \param handle the calculator handle. + * \param data the data to be sent (UTF-16LE string, with U+0000 terminator, without UTF-16 LE BOM). + * \param size the size of the data to be sent. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL hpcalcs_calc_send_chat(calc_handle * handle, const uint16_t * data, uint32_t size); +/** + * \brief Receives chat data from the calculator. + * \param handle the calculator handle. + * \param out_data storage area for the chat data contained in the calculator's reply. + * \param out_size storage area for size of the chat data contained in the calculator's reply. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL hpcalcs_calc_recv_chat(calc_handle * handle, uint16_t ** out_data, uint32_t * out_size); + + +/** + * \brief Sends the given raw packet to the Prime calculator using given calculator handle. + * \param handle the calculator handle. + * \param pkt the raw packet. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL prime_send(calc_handle * handle, prime_raw_hid_pkt * pkt); +/** + * \brief Receives a raw packet from the Prime calculator using given calculator handle, and store the result to given packet. + * \param handle the calculator handle. + * \param pkt the dest raw packet. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL prime_recv(calc_handle * handle, prime_raw_hid_pkt * pkt); + +/** + * \brief Probes the given cable model to find out what calculator is connected to it. + * \param cable the cable model to be probed. + * \param out_calc storage area for the calculator model attached to the cable (if any). + * \return 0 upon success, nonzero otherwise. + * \note For now, the calculator type is fully determined by the calculator type. This might change in the future. + */ +HPEXPORT int HPCALL hpcalcs_probe_calc(cable_model cable, calc_model * out_calc); + + +/** + * \brief Creates a virtual packet for the Prime calculator, preallocating the given size. + * \param size the size to be preallocated. + * \return NULL if an error occurred, a virtual packet otherwise. + */ +HPEXPORT prime_vtl_pkt * HPCALL prime_vtl_pkt_new(uint32_t size); +/** + * \brief Creates a virtual packet for the Prime calculator, filling it with the given size and data. + * \param size the size of the data. + * \param data the pre-allocated data (assumed to be allocated with the same memory allocator as the one given to libhpcalcs, if not using the default one). + * \return NULL if an error occurred, a virtual packet otherwise. + * \warning This function takes ownership of \a data. + */ +HPEXPORT prime_vtl_pkt * HPCALL prime_vtl_pkt_new_with_data_ptr(uint32_t size, uint8_t * data); +/** + * \brief Deletes a virtual packet for the Prime calculator. + * \param pkt the packet to be deleted. + */ +HPEXPORT void HPCALL prime_vtl_pkt_del(prime_vtl_pkt * pkt); + +/** + * \brief Sends the given virtual packet to the Prime calculator using given calculator handle. + * \param handle the calculator handle. + * \param pkt the virtual packet. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL prime_send_data(calc_handle * handle, prime_vtl_pkt * pkt); +/** + * \brief Receives a virtual packet from the Prime calculator using given calculator handle, and store the result to given packet. + * \param handle the calculator handle. + * \param pkt the dest virtual packet. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL prime_recv_data(calc_handle * handle, prime_vtl_pkt * pkt); +/** + * \brief Returns the packet size corresponding to command \a cmd, possibly corrected by the contents of \a data. + * \param cmd the command. + * \param data the data. + * \param size storage area for size of the data. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL prime_data_size(uint8_t cmd, uint8_t * data, uint32_t * out_size); + + +/** + * \brief Converts a calculator model to a printable string. + * \param model the calculator model. + * \return the string corresponding to the calculator model. + **/ +HPEXPORT const char * HPCALL hpcalcs_model_to_string(calc_model model); +/** + * \brief Converts a string to a supported calculator model, if possible. + * \param str the string. + * \return the calculator model corresponding to the string, CALC_NONE if failed. + **/ +HPEXPORT calc_model HPCALL hpcalcs_string_to_model(const char *str); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libhpcalcs/include/hpfiles.h b/libhpcalcs/include/hpfiles.h new file mode 100644 index 0000000..ad7aa50 --- /dev/null +++ b/libhpcalcs/include/hpfiles.h @@ -0,0 +1,283 @@ +/* + * libhpfiles: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file hpfiles.h Files: base part. + */ + +#ifndef __HPLIBS_FILES_H__ +#define __HPLIBS_FILES_H__ + +#include +#include +#include +#include + +// As of 2013/11, too many environments still don't support . +// Since the only thing from this header libhpfiles uses (for now) is char16_t: +// * on modern GCC and Clang (the main targets of this code base), use the builtin __CHAR16_TYPE__ preprocessor define; +// * otherwise, fall back to uint16_t. +#ifdef __CHAR16_TYPE__ +typedef __CHAR16_TYPE__ char16_t; +#else +typedef uint16_t char16_t; +#endif + +#include "hplibs.h" +#include "filetypes.h" + +//! Maximum length of a variable name for the calculators supported by libhpfiles. +// Not sure the target calculators even support such long filenames... +#define FILES_VARNAME_MAXLEN 128 + +//! Generic structure for storing the contents of a variable, or requesting that a variable be sent. +typedef struct +{ + char16_t name[FILES_VARNAME_MAXLEN+1]; + + uint8_t type; + uint8_t model; + uint8_t invalid; ///< Set to nonzero by e.g. hpcalcs_calc_recv_file() if a packet loss was detected. + uint32_t size; + uint8_t* data; +} files_var_entry; + + +//! Structure passed to \a hpfiles_init, contains e.g. callbacks for logging and memory allocation. +typedef struct { + unsigned int version; ///< Config version number. + void (*log_callback)(const char *format, va_list args); ///< Callback function for receiving logging output. + hplibs_malloc_funcs * alloc_funcs; ///< Function pointers used for dynamic memory allocation. If NULL, the library defaults to malloc(), calloc(), realloc(), free(). +} hpfiles_config; + +//! Latest revision of the \a hpfiles_config struct layout supported by this version of the library. +#define HPFILES_CONFIG_VERSION (1) + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Initializes library internals. Must be called before any other libhpfiles function. + * \param config pointer to struct containing e.g. callbacks passed to the library. + * \return Whether the initialization succeeded. + * \note the contents of alloc_funcs are copied. + * \todo return instance count instead. + **/ +HPEXPORT int HPCALL hpfiles_init(hpfiles_config * config); +/** + * \brief Tears down library internals. No other libhpfiles function can be called after this one. + * \return Whether the teardown succeeded. + * \todo return instance count instead. + **/ +HPEXPORT int HPCALL hpfiles_exit(void); + +/** + * \brief Returns the library version string. + * \return The library version string, usually under the form "X.Y.Z". + **/ +HPEXPORT const char* HPCALL hpfiles_version_get(void); + +/** + * \brief Gets the error message if the error was produced by this library + * \param number the error number (from internal error.h) + * \param message out pointer for a newly allocated text error message, which must be freed by the caller + * \note the string is allocated with malloc(), therefore it must be freed with free(). + * \return 0 if the error was produced by this library, otherwise the error number (for propagation). + **/ +HPEXPORT int HPCALL hpfiles_error_get(int number, char **message); + +/** + * \brief Sets the callback function used by the library for logging + * \param log_callback function pointer + */ +HPEXPORT void HPCALL hpfiles_log_set_callback(void (*log_callback)(const char *format, va_list args)); +/** + * \brief Sets the log level of the library, for controlling how much logging output is produced. + * \param log_level log level (from hplibs.h) + * \return the previous log level + */ +HPEXPORT hplibs_logging_level HPCALL hpfiles_log_set_level(hplibs_logging_level log_level); + + +/** + * \brief Creates an empty files_var_entry structure. + * \return Pointer to files_var_entry, NULL if failed. + */ +HPEXPORT files_var_entry * HPCALL hpfiles_ve_create(void); +/** + * \brief Creates and preallocates a files_var_entry structure. + * \param size the size to be allocated. + * \return Pointer to files_var_entry, NULL if failed. + */ +HPEXPORT files_var_entry * HPCALL hpfiles_ve_create_with_size(uint32_t size); +/** + * \brief Creates and fills a files_var_entry structure with the given data. + * \param data the data to be duplicated. + * \param size the size of the data. + * \return Pointer to files_var_entry, NULL if failed. + */ +HPEXPORT files_var_entry * HPCALL hpfiles_ve_create_with_data(uint8_t * data, uint32_t size); +/** + * \brief Creates and fills a files_var_entry structure with the given preallocated data. + * \param data the data to be attached to the files_var_entry (assumed to be allocated with the same memory allocator as the one given to libhpfiles, if not using the default one). + * \param size the size of the data. + * \return Pointer to files_var_entry, NULL if failed. + */ +HPEXPORT files_var_entry * HPCALL hpfiles_ve_create_with_data_ptr(uint8_t * data, uint32_t size); +/** + * \brief Creates and fills a files_var_entry structure with the given data. + * \param data the data to be copied + * \param size the size of the data. + * \param name the file name on the calculator side. + * \return Pointer to files_var_entry, NULL if failed. + */ +HPEXPORT files_var_entry * HPCALL hpfiles_ve_create_with_data_and_name(uint8_t * data, uint32_t size, const char16_t * name); +/** + * \brief Creates and fills a files_var_entry structure from the given file, if it exists. + * \param file the FILE pointer to be used for filling in a files_var_entry instance. + * \param filename the UTF-16LE name of the file on the calculator side, can be NULL if you want to set it later. + * \return Pointer to files_var_entry, NULL if failed. + */ +HPEXPORT files_var_entry * HPCALL hpfiles_ve_create_from_file(FILE * file, const char16_t * filename); +/** + * \brief Destroys the given files_var_entry instance (embedded data + the entry itself). + * \param entry the entry + */ +HPEXPORT void HPCALL hpfiles_ve_delete(files_var_entry * entry); + +/** + * \brief Allocates data for the \a data field of files_var_entry. + * \param size the size to be allocated + * \return Pointer to allocated data, NULL if failed. + */ +HPEXPORT void * HPCALL hpfiles_ve_alloc_data(uint32_t size); +/** + * \brief Copies files_var_entry and its content (if any) from pre-existing src to pre-existing dst. + * \param dst destination entry. + * \param src source entry. + * \return dst, NULL if failed. + **/ +HPEXPORT files_var_entry * HPCALL hpfiles_ve_copy(files_var_entry * dst, files_var_entry * src); +/** + * \brief Duplicates files_var_entry and its content (if any) by creating a new instance. + * \param src source entry + * \return The new entry, NULL if failed. + **/ +HPEXPORT files_var_entry * HPCALL hpfiles_ve_dup(files_var_entry * src); + +/** + * \brief Shows basic information about a variable entry + * \param entry the entry to be dumped. + * \return 0 if the handle was non-NULL, nonzero otherwise. + **/ +HPEXPORT int hpfiles_ve_display(files_var_entry * entry); + +/** + * \brief Creates a NULL-terminated array for storing the given number of potentially non-NULL files_var_entry pointers. + * \param element_count the number of pointers + * \return The new array containing element_count + 1 NULL pointers, NULL if failed. + **/ +HPEXPORT files_var_entry ** HPCALL hpfiles_ve_create_array(uint32_t element_count); +/** + * \brief Reallocates a NULL-terminated array for storing a new number of potentially non-NULL files_var_entry pointers. + * \param entries the array of entries. + * \param element_count the new number of pointers + * \return The new array containing element_count + 1 pointers, NULL if failed. + * \note If the array was enlarged, the new storage space isn't cleared. + **/ +HPEXPORT files_var_entry ** HPCALL hpfiles_ve_resize_array(files_var_entry ** entries, uint32_t element_count); +/** + * \brief Destroys the whole array of files_var_entry structs (including the structures themselves and their embedded data). + * \param entries the array to be destroyed. + **/ +HPEXPORT void HPCALL hpfiles_ve_delete_array(files_var_entry ** entries); + + +/** + * \brief Converts a calculator model to a printable string. + * \param model the calculator model. + * \return the string corresponding to the calculator model. + **/ +HPEXPORT const char * HPCALL hpfiles_model_to_string(calc_model model); +/** + * \brief Converts a string to a supported calculator model, if possible. + * \param str the string. + * \return the calculator model corresponding to the string, CALC_NONE if failed. + **/ +HPEXPORT calc_model HPCALL hpfiles_string_to_model(const char *str); + +/** + * \brief Converts a file type ID to a printable file type string, according to the given calculator model. + * \param model the calculator model + * \param type the file type ID + * \return the file type string corresponding to the given type ID, if any. + */ +HPEXPORT const char * HPCALL hpfiles_vartype2str(calc_model model, uint8_t type); +/** + * \brief Converts a file type (under string form) to a file type ID, according to the given calculator model. + * \param model the calculator model + * \param type the file type string + * \return the file type ID corresponding to the given type string, if any. + */ +HPEXPORT uint8_t HPCALL hpfiles_str2vartype(calc_model model, const char * type); +/** + * \brief Converts a file type to an extension, according to the given calculator model. + * \param model the calculator model + * \param type the file type ID + * \return the file extension corresponding to the given type, if any. + * \note may have to use char16_t instead of char... + */ +HPEXPORT const char * HPCALL hpfiles_vartype2fext(calc_model model, uint8_t type); +/** + * \brief Converts a file extension to a file type ID, according to the given calculator model. + * \param model the calculator model + * \param type the file type as string. + * \return the file type ID corresponding to the given extension, if any. + */ +HPEXPORT uint8_t HPCALL hpfiles_fext2vartype(calc_model model, const char * type); +/** + * \brief Converts a file path to a file type ID, according to the given calculator model. + * \param model the calculator model + * \param filepath the file path as string. + * \return the file type ID corresponding to the given file, if any. + */ +HPEXPORT uint8_t HPCALL hpfiles_filename2vartype(calc_model model, const char * filepath); +/** + * \brief Interprets a file path to a file type ID + calculator-side file name, according to the given calculator model. + * \param model the calculator model + * \param filepath the file path as string. + * \param out_type storage space for the file type ID corresponding to the given file, if any. + * \param out_calcfilename a dynamically allocated string containing the calculator-side filename (usually stripped from the computer-side file extension). + * \return 0 if parsing succeeded, nonzero otherwise. + * \note may have to use char16_t instead of char... + * \note the string is allocated with malloc(), therefore it must be freed with free(). + */ +HPEXPORT int HPCALL hpfiles_parsefilename(calc_model model, const char * filepath, uint8_t * out_type, char ** out_calcfilename); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libhpcalcs/include/hplibs.h b/libhpcalcs/include/hplibs.h new file mode 100644 index 0000000..254d66c --- /dev/null +++ b/libhpcalcs/include/hplibs.h @@ -0,0 +1,93 @@ +/* + * libhpfiles, libhpcables, libhpcalcs: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file hplibs.h Files, Cables, Calcs: definitions common to libhpfiles, libhpcables, libhpcalcs. + */ + +#ifndef __HPLIBS_H__ +#define __HPLIBS_H__ + +#include + +#include "export.h" + +//! Enumeration of cable types. +typedef enum { + CABLE_NUL = 0, + CABLE_PRIME_HID, + CABLE_MAX +} cable_model; + +//! Enumeration of calculator types. +typedef enum { + CALC_NONE = 0, + CALC_PRIME, + CALC_MAX +} calc_model; + +//! Enumeration of logging levels. +typedef enum { + LOG_LEVEL_ALL = 0, + LOG_LEVEL_DEBUG, + LOG_LEVEL_INFO, + LOG_LEVEL_WARN, + LOG_LEVEL_ERROR +} hplibs_logging_level; + +//! Struct containing function pointers used by the libraries for dynamic memory allocation. +typedef struct { + void * (*malloc)(size_t size); ///< A malloc()-compatible function + void * (*calloc) (size_t nmemb, size_t size); ///< A calloc()-compatible function + void * (*realloc)(void *ptr, size_t size); ///< A realloc()-compatible function + void (*free) (void *ptr); ///< A free()-compatible function +} hplibs_malloc_funcs; + +//! USB Vendor ID of Hewlett-Packard. +#define USB_VID_HP (0x03F0) +//! USB Product ID of the Prime calculator in firmware revisions < 8151. +#define USB_PID_PRIME1 (0x0441) +//! USB Product ID of the Prime calculator in firmware revisions >= 8151. +#define USB_PID_PRIME2 (0x1541) + +//! Size of a raw HID packet for the Prime. +#define PRIME_RAW_HID_DATA_SIZE (64) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Gets the error message if the error was produced by a libhp* library + * \param number the error number (from above) + * \param message out pointer for a newly allocated text error message, which must be freed by the caller + * + * \return the error number. + **/ +HPEXPORT int HPCALL hplibs_error_get(int number, char **message); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libhpcalcs/include/hpopers.h b/libhpcalcs/include/hpopers.h new file mode 100644 index 0000000..f82e717 --- /dev/null +++ b/libhpcalcs/include/hpopers.h @@ -0,0 +1,180 @@ +/* + * libhpopers: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file hpopers.h Higher-level operations: base part. + */ + +#ifndef __HPLIBS_OPERS_H__ +#define __HPLIBS_OPERS_H__ + +#include +#include +#include + +#include "hplibs.h" +#include "hpfiles.h" +#include "hpcables.h" +#include "hpcalcs.h" + + +//! Structure passed to \a hpopers_init, contains e.g. callbacks for logging and memory allocation. +typedef struct { + unsigned int version; ///< Config version number. + void (*log_callback)(const char *format, va_list args); ///< Callback function for receiving logging output. + hplibs_malloc_funcs * alloc_funcs; ///< Function pointers used for dynamic memory allocation. If NULL, the library defaults to malloc(), calloc(), realloc(), free(). +} hpopers_config; + +//! Latest revision of the \a hpopers_config struct layout supported by this version of the library. +#define HPOPERS_CONFIG_VERSION (1) + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Initializes library internals. Must be called before any other libhpopers function. + * \param config pointer to struct containing e.g. callbacks passed to the library. + * \return Whether the initialization succeeded. + * \note the contents of alloc_funcs are copied. + * \todo return instance count instead. + **/ +HPEXPORT int HPCALL hpopers_init(hpopers_config * config); +/** + * \brief Tears down library internals. No other libhpopers function can be called after this one. + * \return Whether the teardown succeeded. + * \todo return instance count instead. + **/ +HPEXPORT int HPCALL hpopers_exit(void); + +/** + * \brief Returns the library version string. + * \return The library version string, usually under the form "X.Y.Z". + **/ +HPEXPORT const char* HPCALL hpopers_version_get(void); + +/** + * \brief Gets the error message if the error was produced by this library + * \param number the error number (from internal error.h) + * \param message out pointer for a newly allocated text error message, which must be freed by the caller + * \note the string is allocated with malloc(), therefore it must be freed with free(). + * \return 0 if the error was produced by this library, otherwise the error number (for propagation). + **/ +HPEXPORT int HPCALL hpopers_error_get(int number, char **message); + +/** + * \brief Sets the callback function used by the library for logging + * \param log_callback function pointer + */ +HPEXPORT void HPCALL hpopers_log_set_callback(void (*log_callback)(const char *format, va_list args)); +/** + * \brief Sets the log level of the library, for controlling how much logging output is produced. + * \param log_level log level (from hplibs.h) + * \return the previous log level + */ +HPEXPORT hplibs_logging_level HPCALL hpopers_log_set_level(hplibs_logging_level log_level); + + +// Tentative APIs, may change in the near future. + +/** + * \brief Retrieves a screenshot from the calculator, without any form of conversion + * \param handle the calculator handle. + * \param format the desired screenshot format. + * \param out_file struct FILE pointer for storing the output. + * \return 0 upon success, nonzero otherwise. + * \note This shall be a wrapper over \a hpcalcs_calc_recv_screen . + */ +HPEXPORT int HPCALL hpopers_oper_recv_screen_verbatim_to_file(calc_handle * handle, calc_screenshot_format format, FILE * out_file); +/** + * \brief Retrieves a screenshot from the calculator, and convert it to R8G8B8 before recompressing it as PNG. + * \param handle the calculator handle. + * \param format the desired screenshot format. + * \param out_data storage area for converted R8G8B8 screenshot. + * \param out_size storage area for size of the R8G8B8 screenshot contained in the calculator's reply. + * \return 0 upon success, nonzero otherwise. + * \note This shall be a wrapper over \a hpcalcs_calc_recv_screen and \a hpopers_oper_convert_raw_screen_to_png_r8g8b8. + */ +HPEXPORT int HPCALL hpopers_oper_recv_screen_png_r8g8b8(calc_handle * handle, calc_screenshot_format format, uint8_t ** out_data, uint32_t * out_size); +/** + * \brief Retrieves a screenshot from the calculator, and convert it to R8G8B8 before recompressing it as PNG and writing it to a file. + * \param handle the calculator handle. + * \param format the desired screenshot format. + * \param out_file struct FILE pointer for storing the output. + * \return 0 upon success, nonzero otherwise. + * \note This shall be a wrapper over \a hpopers_oper_recv_screen_png_r8g8b8 . + */ +HPEXPORT int HPCALL hpopers_oper_recv_screen_png_r8g8b8_to_file(calc_handle * handle, calc_screenshot_format format, FILE * out_file); +/** + * \brief Converts a raw screenshot (output by e.g. \a hpcalcs_calc_recv_screen) to a more usual PNG R8G8B8 format. + * \param in_data storage area for raw screenshot. + * \param in_size storage area for size of the raw screenshot. + * \param format the desired screenshot format. + * \param out_data storage area for R8G8B8 screenshot. + * \param out_size storage area for size of the R8G8B8 screenshot. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL hpopers_oper_convert_raw_screen_to_png_r8g8b8(uint8_t * in_data, uint32_t in_size, calc_screenshot_format format, uint8_t ** out_data, uint32_t * out_size); +/** + * \brief Sends a file to the calculator. + * \param handle the calculator handle. + * \param file struct FILE pointer containing the contents to be sent. + * \param filename struct FILE pointer containing the contents to be sent. + * \return 0 upon success, nonzero otherwise. + * \note This shall be a wrapper over \a hpcalcs_calc_send_file . + */ +HPEXPORT int HPCALL hpopers_calc_send_file(calc_handle * handle, FILE * file, const char16_t * filename); +/** + * \brief Receives a file from the calculator. + * \param handle the calculator handle. + * \param calculator_filename name (on the calculator side) of the file to be received. + * \param file struct FILE pointer used for writing data. + * \return 0 upon success, nonzero otherwise. + * \note This shall be a wrapper over \a hpcalcs_calc_recv_file . + */ +HPEXPORT int HPCALL hpopers_calc_recv_file(calc_handle * handle, const char16_t * calculator_filename, FILE * file); +/** + * \brief Receives a backup (made of multiple files) from the calculator to a folder. + * \param handle the calculator handle. + * \param out_path name of the folder used as root for the files hierarchy. + * \return 0 upon success, nonzero otherwise. + * \note This shall be a wrapper over \a hpcalcs_calc_recv_backup . + */ +HPEXPORT int HPCALL hpopers_calc_recv_backup(calc_handle * handle, const char * out_path); +/** + * \brief Sends a backup (made of multiple files) from a folder to the calculator. + * \param handle the calculator handle. + * \param in_path name of the folder used as root for the files hierarchy. + * \return 0 upon success, nonzero otherwise. + * \note This shall be a wrapper over \a hpcalcs_calc_send_file (called in a loop) . + */ +HPEXPORT int HPCALL hpopers_calc_send_backup(calc_handle * handle, const char * in_path); + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/libhpcalcs/include/internal.h b/libhpcalcs/include/internal.h new file mode 100644 index 0000000..c64fa2f --- /dev/null +++ b/libhpcalcs/include/internal.h @@ -0,0 +1,32 @@ +/* + * libhpfiles, libhpcables, libhpcalcs, libhpopers: hand-helds support libraries. + * Copyright (C) 2015 Lionel Debroux + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file internal.h Files, Cables, Calcs, Opers: internal definitions for libhpfiles, libhpcables, libhpcalcs, libhpopers. + */ + +#ifndef __HPLIBS_INTERNAL_H__ +#define __HPLIBS_INTERNAL_H__ + +extern hplibs_malloc_funcs hpfiles_alloc_funcs; +extern hplibs_malloc_funcs hpcables_alloc_funcs; +extern hplibs_malloc_funcs hpcalcs_alloc_funcs; +extern hplibs_malloc_funcs hpopers_alloc_funcs; + +#endif diff --git a/libhpcalcs/include/logging.h b/libhpcalcs/include/logging.h new file mode 100644 index 0000000..f7ba2c5 --- /dev/null +++ b/libhpcalcs/include/logging.h @@ -0,0 +1,56 @@ +/* + * libhpfiles, libhpcables, libhpcalcs: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file logging.h Logging primitives. + */ + +#ifndef __HPLIBS_LOGGING_H__ +#define __HPLIBS_LOGGING_H__ + +#include + +void hpfiles_debug (const char *format, ...); +void hpfiles_info (const char *format, ...); +void hpfiles_warning (const char *format, ...); +void hpfiles_error (const char *format, ...); + + +void hpcables_debug (const char *format, ...); +void hpcables_info (const char *format, ...); +void hpcables_warning (const char *format, ...); +void hpcables_error (const char *format, ...); + + +void hpcalcs_debug (const char *format, ...); +void hpcalcs_info (const char *format, ...); +void hpcalcs_warning (const char *format, ...); +void hpcalcs_error (const char *format, ...); + + +void hpopers_debug (const char *format, ...); +void hpopers_info (const char *format, ...); +void hpopers_warning (const char *format, ...); +void hpopers_error (const char *format, ...); + +#endif diff --git a/libhpcalcs/include/prime_cmd.h b/libhpcalcs/include/prime_cmd.h new file mode 100644 index 0000000..b8a1994 --- /dev/null +++ b/libhpcalcs/include/prime_cmd.h @@ -0,0 +1,73 @@ +/* + * libhpcalcs: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file prime_cmd.h Calcs: Prime commands + */ + +#ifndef __HPLIBS_CALCS_PRIME_CMD_H__ +#define __HPLIBS_CALCS_PRIME_CMD_H__ + +#define CMD_PRIME_CHECK_READY (0xFF) +#define CMD_PRIME_GET_INFOS (0xFA) +#define CMD_PRIME_RECV_SCREEN (0xFC) +#define CMD_PRIME_RECV_BACKUP (0xF9) +#define CMD_PRIME_REQ_FILE (0xF8) +#define CMD_PRIME_RECV_FILE (0xF7) +#define CMD_PRIME_SEND_CHAT (0xF2) +#define CMD_PRIME_RECV_CHAT (0xF2) +#define CMD_PRIME_SEND_KEY (0xEC) +#define CMD_PRIME_SET_DATE_TIME (0xE7) + +HPEXPORT int HPCALL calc_prime_s_check_ready(calc_handle * handle); +HPEXPORT int HPCALL calc_prime_r_check_ready(calc_handle * handle, uint8_t ** out_data, uint32_t * out_size); + +HPEXPORT int HPCALL calc_prime_s_get_infos(calc_handle * handle); +HPEXPORT int HPCALL calc_prime_r_get_infos(calc_handle * handle, calc_infos * infos); + +HPEXPORT int HPCALL calc_prime_s_set_date_time(calc_handle * handle, time_t timestamp); +HPEXPORT int HPCALL calc_prime_r_set_date_time(calc_handle * handle); + +HPEXPORT int HPCALL calc_prime_s_recv_screen(calc_handle * handle, calc_screenshot_format format); +HPEXPORT int HPCALL calc_prime_r_recv_screen(calc_handle * handle, calc_screenshot_format format, uint8_t ** out_data, uint32_t * out_size); + +HPEXPORT int HPCALL calc_prime_s_send_file(calc_handle * handle, files_var_entry * file); +HPEXPORT int HPCALL calc_prime_r_send_file(calc_handle * handle); + +HPEXPORT int HPCALL calc_prime_s_recv_file(calc_handle * handle, files_var_entry * file); +HPEXPORT int HPCALL calc_prime_r_recv_file(calc_handle * handle, files_var_entry ** out_file); + +HPEXPORT int HPCALL calc_prime_s_recv_backup(calc_handle * handle); +HPEXPORT int HPCALL calc_prime_r_recv_backup(calc_handle * handle, files_var_entry *** out_vars); + +HPEXPORT int HPCALL calc_prime_s_send_key(calc_handle * handle, uint32_t code); +HPEXPORT int HPCALL calc_prime_r_send_key(calc_handle * handle); +HPEXPORT int HPCALL calc_prime_s_send_keys(calc_handle * handle, const uint8_t * data, uint32_t size); +HPEXPORT int HPCALL calc_prime_r_send_keys(calc_handle * handle); + +HPEXPORT int HPCALL calc_prime_s_send_chat(calc_handle * handle, const uint16_t * data, uint32_t size); +HPEXPORT int HPCALL calc_prime_r_send_chat(calc_handle * handle); + +HPEXPORT int HPCALL calc_prime_r_recv_chat(calc_handle * handle, uint16_t ** out_data, uint32_t * out_size); + +#endif diff --git a/libhpcalcs/include/typesprime.h b/libhpcalcs/include/typesprime.h new file mode 100644 index 0000000..5cf7d26 --- /dev/null +++ b/libhpcalcs/include/typesprime.h @@ -0,0 +1,69 @@ +/* + * libhpfiles: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file typesprime.h Files: Prime file types and utility functions. + */ + +#ifndef __HPLIBS_FILES_TYPESPRIME_H__ +#define __HPLIBS_FILES_TYPESPRIME_H__ + +#define PRIME_TYPE_SETTINGS (0x00) +// 0x01 ? +#define PRIME_TYPE_APP (0x02) +#define PRIME_TYPE_LIST (0x03) // variables L0-L9, for instance +#define PRIME_TYPE_MATRIX (0x04) // variables M0-M9, for instance +#define PRIME_TYPE_NOTE (0x05) +#define PRIME_TYPE_PRGM (0x06) // programs with identical data are sent twice during backup ?? +#define PRIME_TYPE_APPNOTE (0x07) // XXX Tentative +#define PRIME_TYPE_APPPRGM (0x08) // XXX Tentative +#define PRIME_TYPE_COMPLEX (0x09) // variables Z0-Z9, for instance +#define PRIME_TYPE_REAL (0x0A) // variables A-Z, 0x3B8 (theta), for instance +#define PRIME_TYPE_TESTMODECONFIG (0x0B) +#define PRIME_TYPE_UNKNOWN (0xFF) + +//! Return the string corresponding to the file type ID +const char * prime_vartype2str(uint8_t type); + +//! Return the type ID corresponding to the string +uint8_t prime_str2vartype(const char * type); + +//! Return the file extension corresponding to the value +const char * prime_byte2fext(uint8_t type); + +//! Return the type value corresponding to the file extension +uint8_t prime_fext2byte(const char * type); + +//! Return the description corresponding to the value +const char * prime_byte2desc(uint8_t type); + +//! Return the type value corresponding to the file path +uint8_t prime_filename2byte(const char * filepath); + +//! Parse the file name + extension and determines the type value and calculator-side filename +int prime_parsesplitfilename(char * file, char * extension, uint8_t * out_type, char ** out_calcfilename); + +//! Parse the file path and determines the type value and calculator-side filename +int prime_parsefilename(const char * filepath, uint8_t * out_type, char ** out_calcfilename); + +#endif diff --git a/libhpcalcs/include/utils.h b/libhpcalcs/include/utils.h new file mode 100644 index 0000000000000000000000000000000000000000..e8d17b19b35a1ca3369610077aae37635c2abc72 GIT binary patch literal 3014 zcmbW3>uwWC5QXb^o}v*F3K8H01Oj%o67dp(6d=Mz2x%qc%fuex#NM%;5Z-+k&R6Bm z*l{8V8rjp+U6)f;r@H(5{|(!+4NLk@HnP&XHnfQyX*9CJrgm>-zGodtT(>f^o-~r( z*Ka7v6YFa{l%0`AtW0e#>k}JWWwQ&*m1HYBzrfMd`b6KAPzYFC8qiZ3k;iF$V}Z29P2LO$=>%oHuPGT z@{nxLvjAe49lbzy3XhtuBaTylPZ#U-PjX9j9Q09*kbb8-bEP)jdd@i(>CNnqj-&?X zdXHT4Dz7)K{#CUkL(vI2H$z84ZkQnlx!z}z9Cf7Ix`9=_X}A-7n@8~npx;rWGw8C5B+OysB38kmE(aD%=ePp~Lw z_l>=jho-!Tmlw7xGzX3d3{-+jgYd0nTYC6@CoPb|Hc+-D(G>mz|6=xoW)CITlGX#s z!Jz1e+h zik#Is{vFIYUV4;0Y9On*nqm2>{fL^gQzU7ORX@7pOcBF^VBT0Wx%M~h)|!j2WxuC7 zVgIqtH))Zk)4L#8^(>Gz7zbT=;JPsob$Q9;Wbv+(HiBwsup=_*e%H)D);%6ZtA=bc!^gMz}o~b2T+&KPJ4v4H$ zMV|QDU-n#2Xb&Va^hmLawMVKX7U+;=J!)Ppv^FoTA-$zv$lTR#R!0zXR3f^qqxbS2 z29qxPnmM9X|Jldt?5g$aaB)8jUdJ7RuXUd}hDumr_IgH6bW2>0ZC@4mD#^ZN*_nek z>)eA$r;4*&>`Z879Wc{NtC#`{XBAypvv?WT(?y-OWQ!B3a}8?F=WnvItXo$Z9 +#endif + +#include + +static int calc_none_check_ready(calc_handle * handle, uint8_t ** out_data, uint32_t * out_size) { + return 0; +} + +static int calc_none_get_infos(calc_handle * handle, calc_infos * infos) { + return 0; +} + +static int calc_none_set_date_time(calc_handle * handle, time_t timestamp) { + return 0; +} + +static int calc_none_recv_screen(calc_handle * handle, calc_screenshot_format format, uint8_t ** out_data, uint32_t * out_size) { + return 0; +} + +static int calc_none_send_file(calc_handle * handle, files_var_entry * file) { + return 0; +} + +static int calc_none_recv_file(calc_handle * handle, files_var_entry * request, files_var_entry ** out_file) { + return 0; +} + +static int calc_none_recv_backup(calc_handle * handle, files_var_entry *** out_vars) { + return 0; +} + +static int calc_none_send_key(calc_handle * handle, uint32_t code) { + return 0; +} + +static int calc_none_send_keys(calc_handle * handle, const uint8_t * data, uint32_t size) { + return 0; +} + +static int calc_none_send_chat(calc_handle * handle, const uint16_t * data, uint32_t size) { + return 0; +} + +static int calc_none_recv_chat(calc_handle * handle, uint16_t ** out_data, uint32_t * out_size) { + return 0; +} + +const calc_fncts calc_none_fncts = +{ + CALC_NONE, + "Dummy calculator", + "Dummy calculator used when no calculator is set", + 0, + &calc_none_check_ready, + &calc_none_get_infos, + &calc_none_set_date_time, + &calc_none_recv_screen, + &calc_none_send_file, + &calc_none_recv_file, + &calc_none_recv_backup, + &calc_none_send_key, + &calc_none_send_keys, + &calc_none_send_chat, + &calc_none_recv_chat +}; diff --git a/libhpcalcs/src/calc_prime.c b/libhpcalcs/src/calc_prime.c new file mode 100644 index 0000000..4525b1f --- /dev/null +++ b/libhpcalcs/src/calc_prime.c @@ -0,0 +1,226 @@ +/* + * libhpcalcs: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file calc_prime.c Calcs: Prime top_level functions and structures. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include "logging.h" + +#include "prime_cmd.h" + +static int calc_prime_check_ready(calc_handle * handle, uint8_t ** out_data, uint32_t * out_size) { + int res; + + res = calc_prime_s_check_ready(handle); + if (res == 0) { + res = calc_prime_r_check_ready(handle, out_data, out_size); + if (res != 0) { + hpcalcs_error("%s: r_check_ready failed", __FUNCTION__); + } + } + else { + hpcalcs_error("%s: s_check_ready failed", __FUNCTION__); + } + return res; +} + +static int calc_prime_get_infos(calc_handle * handle, calc_infos * infos) { + int res; + + res = calc_prime_s_get_infos(handle); + if (res == 0) { + res = calc_prime_r_get_infos(handle, infos); + if (res != 0) { + hpcalcs_error("%s: r_get_infos failed", __FUNCTION__); + } + } + else { + hpcalcs_error("%s: s_get_infos failed", __FUNCTION__); + } + return res; +} + +static int calc_prime_set_date_time(calc_handle * handle, time_t timestamp) { + int res; + + res = calc_prime_s_set_date_time(handle, timestamp); + if (res == 0) { + res = calc_prime_r_set_date_time(handle); + if (res != 0) { + hpcalcs_error("%s: r_set_date_time failed", __FUNCTION__); + } + } + else { + hpcalcs_error("%s: s_set_date_time failed", __FUNCTION__); + } + return res; +} + +static int calc_prime_recv_screen(calc_handle * handle, calc_screenshot_format format, uint8_t ** out_data, uint32_t * out_size) { + int res; + + res = calc_prime_s_recv_screen(handle, format); + if (res == 0) { + res = calc_prime_r_recv_screen(handle, format, out_data, out_size); + if (res != 0) { + hpcalcs_error("%s: r_recv_screen failed", __FUNCTION__); + } + } + else { + hpcalcs_error("%s: s_recv_screen failed", __FUNCTION__); + } + return res; +} + +static int calc_prime_send_file(calc_handle * handle, files_var_entry * file) { + int res; + + res = calc_prime_s_send_file(handle, file); + if (res == 0) { + res = calc_prime_r_send_file(handle); + if (res != 0) { + hpcalcs_error("%s: r_send_file failed", __FUNCTION__); + } + } + else { + hpcalcs_error("%s: s_send_file failed", __FUNCTION__); + } + return res; +} + +static int calc_prime_recv_file(calc_handle * handle, files_var_entry * request, files_var_entry ** out_file) { + int res; + + res = calc_prime_s_recv_file(handle, request); + if (res == 0) { + res = calc_prime_r_recv_file(handle, out_file); + if (res != 0) { + hpcalcs_error("%s: r_recv_file failed", __FUNCTION__); + } + } + else { + hpcalcs_error("%s: s_recv_file failed", __FUNCTION__); + } + return res; +} + +static int calc_prime_recv_backup(calc_handle * handle, files_var_entry *** out_vars) { + int res; + + res = calc_prime_s_recv_backup(handle); + if (res == 0) { + res = calc_prime_r_recv_backup(handle, out_vars); + if (res != 0) { + hpcalcs_error("%s: r_recv_backup failed", __FUNCTION__); + } + } + else { + hpcalcs_error("%s: s_recv_backup failed", __FUNCTION__); + } + return res; +} + +static int calc_prime_send_key(calc_handle * handle, uint32_t code) { + int res; + + res = calc_prime_s_send_key(handle, code); + if (res == 0) { + res = calc_prime_r_send_key(handle); + if (res != 0) { + hpcalcs_error("%s: r_send_key failed", __FUNCTION__); + } + } + else { + hpcalcs_error("%s: s_send_key failed", __FUNCTION__); + } + return res; +} + +static int calc_prime_send_keys(calc_handle * handle, const uint8_t * data, uint32_t size) { + int res; + + res = calc_prime_s_send_keys(handle, data, size); + if (res == 0) { + res = calc_prime_r_send_keys(handle); + if (res != 0) { + hpcalcs_error("%s: r_send_keys failed", __FUNCTION__); + } + } + else { + hpcalcs_error("%s: s_send_keys failed", __FUNCTION__); + } + return res; +} + +static int calc_prime_send_chat(calc_handle * handle, const uint16_t * data, uint32_t size) { + int res; + + res = calc_prime_s_send_chat(handle, data, size); + if (res == 0) { + res = calc_prime_r_send_chat(handle); + if (res != 0) { + hpcalcs_error("%s: r_send_chat failed", __FUNCTION__); + } + } + else { + hpcalcs_error("%s: s_send_chat failed", __FUNCTION__); + } + return res; +} + +static int calc_prime_recv_chat(calc_handle * handle, uint16_t ** out_data, uint32_t * out_size) { + int res; + + res = calc_prime_r_recv_chat(handle, out_data, out_size); + if (res != 0) { + hpcalcs_error("%s: r_recv_chat failed", __FUNCTION__); + } + return res; +} + +const calc_fncts calc_prime_fncts = +{ + CALC_PRIME, + "HP Prime", + "HP Prime Graphing Calculator", + CALC_OPS_CHECK_READY | CALC_OPS_GET_INFOS | CALC_OPS_SET_DATE_TIME | CALC_OPS_RECV_SCREEN + | CALC_OPS_SEND_FILE | CALC_OPS_RECV_FILE | CALC_OPS_RECV_BACKUP | CALC_OPS_SEND_KEY + | CALC_OPS_SEND_KEYS | CALC_OPS_SEND_CHAT | CALC_OPS_RECV_CHAT, + &calc_prime_check_ready, + &calc_prime_get_infos, + &calc_prime_set_date_time, + &calc_prime_recv_screen, + &calc_prime_send_file, + &calc_prime_recv_file, + &calc_prime_recv_backup, + &calc_prime_send_key, + &calc_prime_send_keys, + &calc_prime_send_chat, + &calc_prime_recv_chat +}; diff --git a/libhpcalcs/src/error.c b/libhpcalcs/src/error.c new file mode 100644 index 0000000..f370187 --- /dev/null +++ b/libhpcalcs/src/error.c @@ -0,0 +1,256 @@ +/* libhpcalcs - hand-helds support library + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file error.c Files / cables / calcs: Error functions. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifndef _WIN32 +// For strdup +#define _BSD_SOURCE +#endif +#include + +#include +#include +#include +#include +#include "logging.h" +#include "error.h" +#include "gettext.h" + +HPEXPORT int HPCALL hpfiles_error_get(int number, char **message) { + int ret = number; + //hpfiles_debug("%s: entering", __FUNCTION__); + if (message != NULL) { + if (number >= ERR_FILE_FIRST && number <= ERR_FILE_LAST) { + switch (number) { + case ERR_FILE_FILENAME: + *message = strdup(_("Cannot understand filename")); + break; + default: + *message = strdup(_("")); + break; + } + ret = 0; + } + else { + *message = NULL; + } + } + else { + hpfiles_error("%s: message is NULL", __FUNCTION__); + } + //hpfiles_debug("%s: exiting %d", __FUNCTION__, ret); + return ret; +} + +HPEXPORT int HPCALL hpcables_error_get(int number, char **message) { + int ret = number; + //hpcables_debug("%s: entering", __FUNCTION__); + if (message != NULL) { + if (number >= ERR_CABLE_FIRST && number <= ERR_CABLE_LAST) { + switch (number) { + case ERR_CABLE_NOT_OPEN: + *message = strdup(_("Cable is not open")); + break; + case ERR_CABLE_OPEN: + *message = strdup(_("Cable is already open")); + break; + case ERR_CABLE_BUSY: + *message = strdup(_("Cable is busy")); + break; + case ERR_CABLE_WRITE_ERROR: + *message = strdup(_("Error writing to cable")); + break; + case ERR_CABLE_READ_ERROR: + *message = strdup(_("Error reading from cable")); + break; + case ERR_CABLE_INVALID_FNCTS: + *message = strdup(_("Invalid cable functions")); + break; + case ERR_CABLE_PROBE_FAILED: + *message = strdup(_("Cable probing failed")); + break; + default: + *message = strdup(_("")); + break; + } + ret = 0; + } + else { + *message = NULL; + } + } + else { + hpcables_error("%s: message is NULL", __FUNCTION__); + } + //hpcables_debug("%s: exiting %d", __FUNCTION__, ret); + return ret; +} + +HPEXPORT int HPCALL hpcalcs_error_get(int number, char **message) { + int ret = number; + //hpcalcs_debug("%s: entering", __FUNCTION__); + if (message != NULL) { + if (number >= ERR_CALC_FIRST && number <= ERR_CALC_LAST) { + switch (number) { + case ERR_CALC_NO_CABLE: + *message = strdup(_("No cable attached")); + break; + case ERR_CALC_CABLE_NOT_OPEN: + *message = strdup(_("Cable is not open")); + break; + case ERR_CALC_BUSY: + *message = strdup(_("Calc is busy")); + break; + case ERR_CALC_INVALID_FNCTS: + *message = strdup(_("Invalid cable functions")); + break; + case ERR_CALC_PACKET_FORMAT: + *message = strdup(_("Unhandled packet format")); + break; + case ERR_CALC_SPLIT_TIMESTAMP: + *message = strdup(_("Unable to get time constituents")); + break; + case ERR_CALC_PROBE_FAILED: + *message = strdup(_("Calc probing failed")); + break; + default: + *message = strdup(_("")); + break; + } + ret = 0; + } + else { + *message = NULL; + } + } + else { + hpcalcs_error("%s: message is NULL", __FUNCTION__); + } + //hpcalcs_debug("%s: exiting %d", __FUNCTION__, ret); + return ret; +} + +HPEXPORT int HPCALL hpopers_error_get(int number, char **message) { + int ret = number; + //hpopers_debug("%s: entering", __FUNCTION__); + if (message != NULL) { + if (number >= ERR_OPER_FIRST && number <= ERR_OPER_LAST) { + switch (number) { + default: + *message = strdup(_("")); + break; + } + ret = 0; + } + else { + *message = NULL; + } + } + else { + hpopers_error("%s: message is NULL", __FUNCTION__); + } + //hpopers_debug("%s: exiting %d", __FUNCTION__, ret); + return ret; +} + +HPEXPORT int HPCALL hplibs_error_get(int number, char **message) { + int err = number; + char *s = NULL; + + if (message != NULL) { + // Skip ERR_SUCCESS. + if (number > ERR_HPLIBS_GENERIC_FIRST && number <= ERR_HPLIBS_GENERIC_LAST) { + switch (number) { + case ERR_MALLOC: + *message = strdup(_("Failed to allocate memory")); + break; + case ERR_INVALID_HANDLE: + *message = strdup(_("Invalid handle pointer")); + break; + case ERR_INVALID_PARAMETER: + *message = strdup(_("Invalid function parameter")); + break; + case ERR_INVALID_MODEL: + *message = strdup(_("Invalid model")); + break; + case ERR_LIBRARY_INIT: + *message = strdup(_("Problem initializing the library")); + break; + case ERR_LIBRARY_EXIT: + *message = strdup(_("Problem deinitializing the library")); + break; + default: + *message = strdup(_("")); + break; + } + return 0; + } + else { + // Retrieve the error message. + err = hpfiles_error_get(err, &s); + if (err) { + free(s); + err = hpcables_error_get(err, &s); + if (err) { + free(s); + err = hpcalcs_error_get(err, &s); + if (err) { + free(s); + err = hpopers_error_get(err, &s); + if (err) { + // next level: not a libhp* error. + free(s); + s = NULL; + } + else { + hpopers_info("%s\n", s); + } + } + else { + hpcalcs_info("%s\n", s); + } + } + else { + hpcables_info("%s\n", s); + } + } + else { + hpfiles_info("%s\n", s); + } + + *message = s; + } + } + else { + hpcalcs_error("%s: message is NULL", __FUNCTION__); + return number; + } + + return number; +} diff --git a/libhpcalcs/src/error.h b/libhpcalcs/src/error.h new file mode 100644 index 0000000..5d5ebd2 --- /dev/null +++ b/libhpcalcs/src/error.h @@ -0,0 +1,71 @@ +/* + * libhpfiles: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file error.h Files / cables / calcs: Error IDs. + */ + +#ifndef __HPLIBS_CALCS_ERROR_H__ +#define __HPLIBS_CALCS_ERROR_H__ + +typedef enum { + ERR_HPLIBS_GENERIC_FIRST = 0, + ERR_SUCCESS = 0, // Must be equal to ERR_HPLIBS_GENERIC_FIRST + ERR_MALLOC, + ERR_INVALID_HANDLE, + ERR_INVALID_PARAMETER, + ERR_INVALID_MODEL, + ERR_LIBRARY_INIT, + ERR_LIBRARY_EXIT, + ERR_LIBRARY_CONFIG_VERSION, + ERR_HPLIBS_GENERIC_LAST = 127, + + ERR_FILE_FIRST = 128, + ERR_FILE_FILENAME = 128, + ERR_FILE_LAST = 255, + + ERR_CABLE_FIRST = 256, + ERR_CABLE_NOT_OPEN = 256, + ERR_CABLE_OPEN, + ERR_CABLE_BUSY, + ERR_CABLE_WRITE_ERROR, + ERR_CABLE_READ_ERROR, + ERR_CABLE_INVALID_FNCTS, + ERR_CABLE_PROBE_FAILED, + ERR_CABLE_LAST = 383, + + ERR_CALC_FIRST = 384, + ERR_CALC_NO_CABLE = 384, + ERR_CALC_CABLE_NOT_OPEN, + ERR_CALC_BUSY, + ERR_CALC_INVALID_FNCTS, + ERR_CALC_PACKET_FORMAT, + ERR_CALC_SPLIT_TIMESTAMP, + ERR_CALC_PROBE_FAILED, + ERR_CALC_LAST = 511, + + ERR_OPER_FIRST = 512, + ERR_OPER_LAST = 639 +} hplibs_error; + +#endif diff --git a/libhpcalcs/src/export.h b/libhpcalcs/src/export.h new file mode 100644 index 0000000..2505dbb --- /dev/null +++ b/libhpcalcs/src/export.h @@ -0,0 +1,91 @@ +/* + * libhpfiles, libhpcables, libhpcalcs - hand-helds support library + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file export.h Stuff for function export and calling convention. + */ + +#ifndef __HPCALCS_EXPORT__ +#define __HPCALCS_EXPORT__ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// Calling convention: default +#define HPCALL + +// Symbols export +#if defined(HAVE_FVISIBILITY) // GCC 4.0 has introduced the -fvisibility flag (similar to declspec) +#define HPEXPORT __attribute__ ((visibility("default"))) + +#elif defined(__WIN32__) +# if defined(_MSC_VER) // MSVC 5.0 mini +# if defined(HPCALCS_EXPORTS) +# define HPEXPORT __declspec(dllexport) +# else +# define HPEXPORT __declspec(dllimport) +# endif + +# elif defined(__MINGW32__) // MinGW - GCC for Windows, (c) 2002 Kevin Kofler +# if defined(HPCALCS_EXPORTS) // defined by the configure script +# define HPEXPORT __declspec(dllexport) +# else +# define HPEXPORT __declspec(dllimport) +# endif +# endif + +#else +# define HPEXPORT // default +#endif + +#ifdef __cplusplus +} +#endif + +// Symbols deprecation +#ifndef HPLIBS_DEPRECATED +# ifdef __GNUC__ +# if (__GNUC__>3) || (__GNUC__==3 && __GNUC_MINOR__>=3) +# define HPLIBS_DEPRECATED __attribute__((deprecated)) +# else /* not GCC >= 3.3 */ +# define HPLIBS_DEPRECATED +# endif /* GCC >= 3.3 */ +# else /* not __GNUC__ */ +# ifdef _MSC_VER +# if _MSC_VER >= 1300 +# define HPLIBS_DEPRECATED __declspec(deprecated) +# else /* not _MSC_VER >= 1300 */ +# define HPLIBS_DEPRECATED +# endif /* _MSC_VER >= 1300 */ +# else /* not _MSC_VER */ +# define HPLIBS_DEPRECATED +# endif /* _MSC_VER */ +# endif /* __GNUC__ */ +#endif /* HPLIBS_DEPRECATED */ + +#endif diff --git a/libhpcalcs/src/filetypes.c b/libhpcalcs/src/filetypes.c new file mode 100644 index 0000000..13039fc --- /dev/null +++ b/libhpcalcs/src/filetypes.c @@ -0,0 +1,112 @@ +/* + * libhpfiles, libhpcables, libhpcalcs: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file filetypes.c Files: File type utility functions. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include "logging.h" +#include "error.h" + +#include "filetypes.h" + +HPEXPORT const char * HPCALL hpfiles_vartype2str(calc_model model, uint8_t type) { + switch (model) { + case CALC_PRIME: + return prime_vartype2str(type); + default: + hpfiles_error("%s: invalid model argument", __FUNCTION__); + return ""; + } +} + +HPEXPORT uint8_t HPCALL hpfiles_str2vartype(calc_model model, const char * type) { + if (type == NULL) { + hpfiles_error("%s: invalid type argument", __FUNCTION__); + return HPLIBS_FILE_TYPE_UNKNOWN; + } + switch (model) { + case CALC_PRIME: + return prime_str2vartype(type); + default: + hpfiles_error("%s: invalid model argument", __FUNCTION__); + return HPLIBS_FILE_TYPE_UNKNOWN; + } +} + +HPEXPORT const char * HPCALL hpfiles_vartype2fext(calc_model model, uint8_t type) { + switch (model) { + case CALC_PRIME: + return prime_byte2fext(type); + default: + hpfiles_error("%s: invalid model argument", __FUNCTION__); + return ""; + } +} + +HPEXPORT uint8_t HPCALL hpfiles_fext2vartype(calc_model model, const char * type) { + if (type == NULL) { + hpfiles_error("%s: invalid type argument", __FUNCTION__); + return HPLIBS_FILE_TYPE_UNKNOWN; + } + switch (model) { + case CALC_PRIME: + return prime_fext2byte(type); + default: + hpfiles_error("%s: invalid model argument", __FUNCTION__); + return HPLIBS_FILE_TYPE_UNKNOWN; + } +} + +HPEXPORT uint8_t HPCALL hpfiles_filename2vartype(calc_model model, const char * filepath) { + if (filepath == NULL) { + hpfiles_error("%s: invalid filepath argument", __FUNCTION__); + return HPLIBS_FILE_TYPE_UNKNOWN; + } + switch (model) { + case CALC_PRIME: + return prime_filename2byte(filepath); + default: + hpfiles_error("%s: invalid model argument", __FUNCTION__); + return HPLIBS_FILE_TYPE_UNKNOWN; + } +} + +HPEXPORT int HPCALL hpfiles_parsefilename(calc_model model, const char * filepath, uint8_t * out_type, char ** out_calcfilename) { + if (filepath == NULL || out_type == NULL || out_calcfilename == NULL) { + hpfiles_error("%s: an argument is NULL", __FUNCTION__); + return ERR_INVALID_PARAMETER; + } + switch (model) { + case CALC_PRIME: + return prime_parsefilename(filepath, out_type, out_calcfilename); + default: + hpfiles_error("%s: invalid model argument", __FUNCTION__); + return HPLIBS_FILE_TYPE_UNKNOWN; + } +} diff --git a/libhpcalcs/src/filetypes.h b/libhpcalcs/src/filetypes.h new file mode 100644 index 0000000..ddfd14c --- /dev/null +++ b/libhpcalcs/src/filetypes.h @@ -0,0 +1,35 @@ +/* + * libhpfiles: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file filetypes.h Files: Umbrella header for file types. + */ + +#ifndef __HPLIBS_FILES_FILETYPES_H__ +#define __HPLIBS_FILES_FILETYPES_H__ + +#include "typesprime.h" + +#define HPLIBS_FILE_TYPE_UNKNOWN (0xFF) + +#endif diff --git a/libhpcalcs/src/gettext.h b/libhpcalcs/src/gettext.h new file mode 100644 index 0000000..1564ac7 --- /dev/null +++ b/libhpcalcs/src/gettext.h @@ -0,0 +1,60 @@ +/* + * libhpfiles: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file gettext.h Files / cables / calcs: internationalization (i18n) stuff. + */ + +#ifndef HPLIBS_GETTEXT_H +#define HPLIBS_GETTEXT_H + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +/* + * Standard gettext macros. + */ +#ifdef ENABLE_NLS +# include +# undef _ +# define _(String) dgettext (PACKAGE, String) +# ifdef gettext_noop +# define N_(String) gettext_noop (String) +# else +# define N_(String) (String) +# endif +#else +#warning NLS disabled +# define textdomain(String) (String) +# define gettext(String) (String) +# define dgettext(Domain,Message) (Message) +# define dcgettext(Domain,Message,Type) (Message) +# define bindtextdomain(Domain,Directory) (Domain) +# define _(String) (String) +# define N_(String) (String) +#endif + +#endif diff --git a/libhpcalcs/src/hpcables.c b/libhpcalcs/src/hpcables.c new file mode 100644 index 0000000..33a8df3 --- /dev/null +++ b/libhpcalcs/src/hpcables.c @@ -0,0 +1,537 @@ +/* + * libhpcables: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file hpcables.c Cables: base part. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include + +#include +#include "internal.h" +#include "logging.h" +#include "error.h" +#include "gettext.h" + + +extern const cable_fncts cable_nul_fncts; +extern const cable_fncts cable_prime_hid_fncts; + +const cable_fncts * hpcables_all_cables[CABLE_MAX] = { + &cable_nul_fncts, + &cable_prime_hid_fncts +}; + +static const uint32_t supported_cables = + (1U << CABLE_NUL) + | (1U << CABLE_PRIME_HID) +; + +hplibs_malloc_funcs hpcables_alloc_funcs = { + .malloc = malloc, + .calloc = calloc, + .realloc = realloc, + .free = free +}; + + +// not static, must be shared between instances +int hpcables_instance_count = 0; + +HPEXPORT int HPCALL hpcables_init(hpcables_config * config) { + int res = ERR_SUCCESS; + void (*log_callback)(const char *format, va_list args); + hplibs_malloc_funcs * alloc_funcs; + + if (config == NULL) { + log_callback = NULL; + alloc_funcs = NULL; + } + else { + if (config->version <= HPCABLES_CONFIG_VERSION) { + if (config->version == 1) { + log_callback = config->log_callback; + alloc_funcs = config->alloc_funcs; + } + else { + hpcables_error(_("%s: unsupported config version %u"), __FUNCTION__, config->version); + res = ERR_LIBRARY_CONFIG_VERSION; + } + } + else { + hpcables_error(_("%s: unsupported config version %u"), __FUNCTION__, config->version); + res = ERR_LIBRARY_CONFIG_VERSION; + } + } + + if (!res) { + // TODO: when (if) libhpcables is split from libhpcalcs, copy and adjust locale setting code from hpfiles.c. + + if (!hpcables_instance_count) { + hpcables_log_set_callback(log_callback); + if (alloc_funcs != NULL) { + hpcables_alloc_funcs = *alloc_funcs; + } + hpcables_info(_("hpcables library version %s"), hpcables_version_get()); + + res = hid_init(); + if (res == 0) { + res = ERR_SUCCESS; + hpcables_info(_("%s: init succeeded"), __FUNCTION__); + hpcables_instance_count++; + } + else { + res = ERR_LIBRARY_INIT; + hpcables_error(_("%s: init failed"), __FUNCTION__); + } + } + else { + res = ERR_SUCCESS; + hpcables_info(_("%s: re-init skipped"), __FUNCTION__); + hpcables_instance_count++; + } + } + + return res; +} + +HPEXPORT int HPCALL hpcables_exit(void) { + int res; + + if (hpcables_instance_count <= 0) { + hpcables_error(_("%s: more exits than inits"), __FUNCTION__); + res = ERR_LIBRARY_EXIT; + } + else { + if (hpcables_instance_count == 1) { + hid_exit(); + } + + hpcables_instance_count--; + + hpcables_info(_("%s: exit succeeded"), __FUNCTION__); + res = ERR_SUCCESS; + } + + return res; +} + + +HPEXPORT const char* HPCALL hpcables_version_get (void) { + return VERSION; +} + + +HPEXPORT uint32_t HPCALL hpcables_supported_cables (void) { + return supported_cables; +} + + +HPEXPORT cable_handle * HPCALL hpcables_handle_new(cable_model model) { + cable_handle * handle = NULL; + if (model < CABLE_MAX) { + handle = (cable_handle *)(hpcables_alloc_funcs.calloc)(1, sizeof(*handle)); + + if (handle != NULL) { + handle->model = model; + handle->handle = NULL; + handle->fncts = hpcables_all_cables[model]; + hpcables_info("%s: handle allocation for model %d succeeded", __FUNCTION__, model); + } + else { + hpcables_error("%s: handle allocation for model %d failed", __FUNCTION__, model ); + } + } + else { + hpcables_error("%s: invalid model %d", __FUNCTION__, model); + } + + return handle; +} + +HPEXPORT int HPCALL hpcables_handle_del(cable_handle * handle) { + int res; + if (handle != NULL) { + (hpcables_alloc_funcs.free)(handle->handle); + handle->handle = NULL; + + (hpcables_alloc_funcs.free)(handle); + res = ERR_SUCCESS; + hpcables_info("%s: handle deletion succeeded", __FUNCTION__); + } + else { + res = ERR_INVALID_HANDLE; + hpcables_error("%s: handle is NULL", __FUNCTION__); + } + + return res; +} + +HPEXPORT int HPCALL hpcables_handle_display(cable_handle * handle) { + int res; + if (handle != NULL) { + hpcables_info("Link cable handle details:"); + hpcables_info("\tmodel: %s", hpcables_model_to_string(handle->model)); + hpcables_info("\tread_timeout: %d", handle->read_timeout); + hpcables_info("\topen: %d", handle->open); + hpcables_info("\tbusy: %d", handle->busy); + res = ERR_SUCCESS; + } + else { + res = ERR_INVALID_HANDLE; + hpcables_error("%s: handle is NULL", __FUNCTION__); + } + + return res; +} + +HPEXPORT cable_model HPCALL hpcables_get_model(cable_handle * handle) { + cable_model model = CABLE_NUL; + if (handle != NULL) { + model = handle->model; + } + else { + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + + return model; +} + +#define DO_BASIC_HANDLE_CHECKS() \ + if (!handle->open) { \ + res = ERR_CABLE_NOT_OPEN; \ + hpcalcs_error("%s: cable not open", __FUNCTION__); \ + break; \ + } \ + DO_BASIC_HANDLE_CHECKS2() + +#define DO_BASIC_HANDLE_CHECKS2() \ + if (handle->busy) { \ + res = ERR_CABLE_BUSY; \ + hpcalcs_error("%s: cable busy", __FUNCTION__); \ + break; \ + } \ + if (handle->fncts == NULL) { \ + res = ERR_CABLE_INVALID_FNCTS; \ + hpcalcs_error("%s: fncts is NULL", __FUNCTION__); \ + break; \ + } + +HPEXPORT int HPCALL hpcables_options_get_read_timeout(cable_handle * handle) { + int timeout = 0; + if (handle != NULL) { + timeout = handle->read_timeout; + } + else { + hpcables_error("%s: handle is NULL", __FUNCTION__); + } + return timeout; +} + +HPEXPORT int HPCALL hpcables_options_set_read_timeout(cable_handle * handle, int read_timeout) { + int res; + if (handle != NULL) { + do { + int (*set_read_timeout) (cable_handle *, int); + + DO_BASIC_HANDLE_CHECKS2() + + set_read_timeout = handle->fncts->set_read_timeout; + if (set_read_timeout != NULL) { + handle->busy = 1; + res = (*set_read_timeout)(handle, read_timeout); + if (res == ERR_SUCCESS) { + hpcables_info("%s: set_read_timeout succeeded", __FUNCTION__); + } + else { + hpcables_error("%s: set_read_timeout failed", __FUNCTION__); + } + handle->busy = 0; + } + else { + res = ERR_CABLE_INVALID_FNCTS; + hpcables_error("%s: fncts->set_read_timeout is NULL", __FUNCTION__); + } + } while (0); + } + else { + res = ERR_INVALID_HANDLE; + hpcables_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL hpcables_cable_probe(cable_handle * handle) { + int res; + if (handle != NULL) { + do { + int (*probe) (cable_handle *); + + DO_BASIC_HANDLE_CHECKS2() + + probe = handle->fncts->probe; + if (probe != NULL) { + handle->busy = 1; + res = (*probe)(handle); + if (res == ERR_SUCCESS) { + handle->open = 0; + hpcables_info("%s: probe succeeded", __FUNCTION__); + } + else { + hpcables_error("%s: probe failed", __FUNCTION__); + } + handle->busy = 0; + } + else { + res = ERR_CABLE_INVALID_FNCTS; + hpcables_error("%s: fncts->probe is NULL", __FUNCTION__); + } + } while (0); + } + else { + res = ERR_INVALID_HANDLE; + hpcables_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL hpcables_cable_open(cable_handle * handle) { + int res; + if (handle != NULL) { + do { + int (*open) (cable_handle *); + + if (handle->open) { + res = ERR_CABLE_OPEN; + hpcables_error("%s: cable already open", __FUNCTION__); + break; + } + DO_BASIC_HANDLE_CHECKS2() + + open = handle->fncts->open; + if (open != NULL) { + handle->busy = 1; + res = (*open)(handle); + if (res == ERR_SUCCESS) { + handle->open = 1; + hpcables_info("%s: open succeeded", __FUNCTION__); + } + else { + hpcables_error("%s: open failed", __FUNCTION__); + } + handle->busy = 0; + } + else { + res = ERR_CABLE_INVALID_FNCTS; + hpcables_error("%s: fncts->open is NULL", __FUNCTION__); + } + } while (0); + } + else { + res = ERR_INVALID_HANDLE; + hpcables_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL hpcables_cable_close(cable_handle * handle) { + int res; + if (handle != NULL) { + do { + int (*close) (cable_handle *); + + DO_BASIC_HANDLE_CHECKS() + + close = handle->fncts->close; + if (close != NULL) { + handle->busy = 1; + res = (*close)(handle); + if (res == ERR_SUCCESS) { + handle->open = 0; + hpcables_info("%s: close succeeded", __FUNCTION__); + } + else { + hpcables_error("%s: close failed", __FUNCTION__); + } + handle->busy = 0; + } + else { + res = ERR_CABLE_INVALID_FNCTS; + hpcables_error("%s: fncts->close is NULL", __FUNCTION__); + } + } while (0); + } + else { + res = ERR_INVALID_HANDLE; + hpcables_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL hpcables_cable_send(cable_handle * handle, uint8_t * data, uint32_t len) { + int res; + if (handle != NULL) { + do { + int (*send) (cable_handle *, uint8_t *, uint32_t); + + DO_BASIC_HANDLE_CHECKS() + + send = handle->fncts->send; + if (send != NULL) { + handle->busy = 1; + res = (*send)(handle, data, len); + if (res == ERR_SUCCESS) { + //hpcables_info("%s: send succeeded", __FUNCTION__); + } + else { + hpcables_warning("%s: send failed", __FUNCTION__); + } + handle->busy = 0; + } + else { + res = ERR_CABLE_INVALID_FNCTS; + hpcables_error("%s: fncts->send is NULL", __FUNCTION__); + } + } while (0); + } + else { + res = ERR_INVALID_HANDLE; + hpcables_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL hpcables_cable_recv(cable_handle * handle, uint8_t ** data, uint32_t * len) { + int res; + if (handle != NULL) { + do { + int (*recv) (cable_handle *, uint8_t **, uint32_t *); + + DO_BASIC_HANDLE_CHECKS() + + recv = handle->fncts->recv; + if (recv != NULL) { + handle->busy = 1; + res = (*recv)(handle, data, len); + if (res == ERR_SUCCESS) { + //hpcables_info("%s: recv succeeded", __FUNCTION__); + } + else { + hpcables_warning("%s: recv failed", __FUNCTION__); + } + handle->busy = 0; + } + else { + res = ERR_CABLE_INVALID_FNCTS; + hpcables_error("%s: fncts->recv is NULL", __FUNCTION__); + } + } while (0); + } + else { + res = ERR_INVALID_HANDLE; + hpcables_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +#undef DO_BASIC_HANDLE_CHECKS2 +#undef DO_BASIC_HANDLE_CHECKS + +HPEXPORT int HPCALL hpcables_probe_cables(uint8_t ** result) { + int res = 0; + if (result != NULL) { + uint8_t * models = (uint8_t *)(hpcables_alloc_funcs.calloc)(CABLE_MAX, sizeof(uint8_t)); + uint8_t * ptr = models; + if (models != NULL) { + for (cable_model model = CABLE_NUL; model < CABLE_MAX; model++) { + cable_handle * handle = hpcables_handle_new(model); + if (handle != NULL) { + if (hpcables_cable_probe(handle) == ERR_SUCCESS) { + res++; + *ptr++ = 1; + hpcables_info("%s: probing cable %s succeeded", __FUNCTION__, hpcables_model_to_string(model)); + } + else { + ptr++; + hpcables_error("%s: probing cable %s failed", __FUNCTION__, hpcables_model_to_string(model)); + } + + if (hpcables_handle_del(handle) != ERR_SUCCESS) { + hpcables_error("%s: failed to destroy handle for model %s", __FUNCTION__, hpcables_model_to_string(model)); + } + else { + hpcables_info("%s: destroy handle for model %s success", __FUNCTION__, hpcables_model_to_string(model)); + } + } + else { + hpcables_error("%s: failed to create handle for model %s", __FUNCTION__, hpcables_model_to_string(model)); + } + } + } + *result = models; + } + else { + hpcables_error("%s: result is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL hpcables_probe_free(uint8_t * models) { + int res; + if (models != NULL) { + res = ERR_SUCCESS; + (hpcables_alloc_funcs.free)(models); + } + else { + res = ERR_INVALID_PARAMETER; + hpcables_error("%s: models is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL hpcables_probe_display(uint8_t * models) { + int res; + if (models != NULL) { + cable_model model; + res = 0; + hpcables_info("Probing data details:"); + for (model = CABLE_NUL; model < CABLE_MAX; model++) { + if (models[model] != 0) { + hpcables_info("\tfound cable #%d: %s", model, hpcables_model_to_string(model)); + } + } + res = ERR_SUCCESS; + hpcables_info("%s found %d cables", __FUNCTION__, res); + } + else { + res = ERR_INVALID_PARAMETER; + hpcables_error("%s: models is NULL", __FUNCTION__); + } + return res; +} diff --git a/libhpcalcs/src/hpcables.h b/libhpcalcs/src/hpcables.h new file mode 100644 index 0000000..922477c --- /dev/null +++ b/libhpcalcs/src/hpcables.h @@ -0,0 +1,249 @@ +/* + * libhpcables: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file hpcables.h Cables: base part. + */ + +#ifndef __HPLIBS_CABLES_H__ +#define __HPLIBS_CABLES_H__ + +#include +#include + +#include "hplibs.h" + +//! Opaque type for internal _cable_fncts. +typedef struct _cable_fncts cable_fncts; +//! Opaque type for internal _cable_handle. +typedef struct _cable_handle cable_handle; + +//! Internal structure containing information about the cable, and function pointers. +struct _cable_fncts { + cable_model model; + const char * name; + const char * description; + int (*probe) (cable_handle * handle); + int (*open) (cable_handle * handle); + int (*close) (cable_handle * handle); + int (*set_read_timeout) (cable_handle * handle, int read_timeout); + int (*send) (cable_handle * handle, uint8_t * data, uint32_t len); + int (*recv) (cable_handle * handle, uint8_t ** data, uint32_t * len); +}; + +//! Internal structure containing state about the cable, returned and passed around by the user. +struct _cable_handle { + cable_model model; + void * handle; + const cable_fncts * fncts; + int read_timeout; + int open; // Should be made explicitly atomic with GCC >= 4.7 or Clang, but int is atomic on most ISAs anyway. + int busy; // Should be made explicitly atomic with GCC >= 4.7 or Clang, but int is atomic on most ISAs anyway. +}; + + +//! Structure passed to \a hpcables_init, contains e.g. callbacks for logging and memory allocation. +typedef struct { + unsigned int version; ///< Config version number. + void (*log_callback)(const char *format, va_list args); ///< Callback function for receiving logging output. + hplibs_malloc_funcs * alloc_funcs; ///< Function pointers used for dynamic memory allocation. If NULL, the library defaults to malloc(), calloc(), realloc(), free(). +} hpcables_config; + +//! Latest revision of the \a hpcables_config struct layout supported by this version of the library. +#define HPCABLES_CONFIG_VERSION (1) + + +typedef enum { + PACKET_DIRECTION_NONE = 0, + PACKET_DIRECTION_SEND, + PACKET_DIRECTION_RECV +} CablePacketDirection; + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Initializes library internals. Must be called before any other libhpcables function. + * \param config pointer to struct containing e.g. callbacks passed to the library. + * \return Whether the initialization succeeded. + * \note the contents of alloc_funcs are copied. + * \todo return instance count instead. + **/ +HPEXPORT int HPCALL hpcables_init(hpcables_config * config); +/** + * \brief Tears down library internals. No other libhpcables function can be called after this one. + * \return Whether the teardown succeeded. + * \todo return instance count instead. + **/ +HPEXPORT int HPCALL hpcables_exit(void); + +/** + * \brief Returns the library version string. + * \return The library version string, usually under the form "X.Y.Z". + **/ +HPEXPORT const char* HPCALL hpcables_version_get(void); + +/** + * \brief Returns the cables supported by the current build of the library. + * \return An integer containing a binary OR of (1 << CABLE_*) values, where CABLE_* values are defined in enum \a cable_model. + **/ +HPEXPORT uint32_t HPCALL hpcables_supported_cables(void); + +/** + * \brief Gets the error message if the error was produced by this library + * \param number the error number (from internal error.h) + * \param message out pointer for a newly allocated text error message, which must be freed by the caller + * \note the string is allocated with malloc(), therefore it must be freed with free(). + * \return 0 if the error was produced by this library, otherwise the error number (for propagation). + **/ +HPEXPORT int HPCALL hpcables_error_get(int number, char **message); + +/** + * \brief Sets the callback function used by the library for logging + * \param log_callback function pointer + */ +HPEXPORT void HPCALL hpcables_log_set_callback(void (*log_callback)(const char *format, va_list args)); +/** + * \brief Sets the log level of the library, for controlling how much logging output is produced. + * \param log_level log level (from hplibs.h) + * \return the previous log level + */ +HPEXPORT hplibs_logging_level HPCALL hpcables_log_set_level(hplibs_logging_level log_level); + +/** + * \brief Creates a new handle (opaque structure) for the given cable model. + * The handle must be freed with \a hpcables_handle_del when no longer needed. + * \param model the cable model. + * \return NULL if an error occurred, otherwise a valid handle. + **/ +HPEXPORT cable_handle * HPCALL hpcables_handle_new(cable_model model); +/** + * \brief Deletes a handle (opaque structure) created by \a hpcables_handle_new(). + * \param handle the handle to be deleted. + * \return 0 if the deletion succeeded, nonzero otherwise. + **/ +HPEXPORT int HPCALL hpcables_handle_del(cable_handle * handle); +/** + * \brief Shows basic information about a handle. + * \param handle the handle to be dumped. + * \return 0 if the handle was non-NULL, nonzero otherwise. + **/ +HPEXPORT int HPCALL hpcables_handle_display(cable_handle * handle); + +/** + * \brief Retrieves the cable model from the given cable handle. + * \param handle the cable handle + * \return the cable model corresponding to the given handle. + */ +HPEXPORT cable_model HPCALL hpcables_get_model(cable_handle * handle); + +/** + * \brief Gets the read timeout (in ms) for the given cable handle. + * \param handle the cable handle + * \return the current read timeout, 0 if error. + */ +HPEXPORT int HPCALL hpcables_options_get_read_timeout(cable_handle * handle); +/** + * \brief Sets the timeout (in ms) for the given cable handle. + * \param handle the cable handle + * \param timeout the new timeout. + * \return 0 if the operation succeeded, nonzero otherwise. + */ +HPEXPORT int HPCALL hpcables_options_set_read_timeout(cable_handle * handle, int timeout); + +/** + * \brief Probes the given cable. + * \param handle the handle to be probed. + * \return 0 if the operation succeeded, nonzero otherwise. + **/ +HPEXPORT int HPCALL hpcables_cable_probe(cable_handle * handle); +/** + * \brief Opens the given cable. + * \param handle the handle to be opened. + * \return 0 if the operation succeeded, nonzero otherwise. + **/ +HPEXPORT int HPCALL hpcables_cable_open(cable_handle * handle); +/** + * \brief Closes the given cable. + * \param handle the handle to be closed. + * \return 0 if the operation succeeded, nonzero otherwise. + **/ +HPEXPORT int HPCALL hpcables_cable_close(cable_handle * handle); +/** + * \brief Sends data through the given cable. + * \param handle the cable handle. + * \param data the data to be sent. + * \param len the size of the data to be sent. + * \return 0 if the operation succeeded, nonzero otherwise. + **/ +HPEXPORT int HPCALL hpcables_cable_send(cable_handle * handle, uint8_t * data, uint32_t len); +/** + * \brief Receives data through the given cable. + * \param handle the cable handle. + * \param data storage area for the data to be received. + * \param len storage area for the length of the received data. + * \return 0 if the operation succeeded, nonzero otherwise. + **/ +HPEXPORT int HPCALL hpcables_cable_recv(cable_handle * handle, uint8_t ** data, uint32_t * len); + +/** + * \brief Detects usable cables and builds an array of uint8_t booleans corresponding to the items of enum cable_model. + * \param result storage area for the cable models which were found. Use \a hpcables_probe_free to free the allocated memory. + * \return the number of usable cables. 0 means severe problem (e.g. memory allocation failure), as the null cable is always usable. + */ +HPEXPORT int HPCALL hpcables_probe_cables(uint8_t ** result); +/** + * \brief Frees the result of probing, created by \a hpcables_probe_cables. + * \param models the memory to be freed. + * \return 0 if the operation succeeded, nonzero otherwise. + */ +HPEXPORT int HPCALL hpcables_probe_free(uint8_t * models); +/** + * \brief Shows basic information about a probing result. + * \param models the probing information to be dumped. + * \return 0 if the operation succeeded, nonzero otherwise. + **/ +HPEXPORT int HPCALL hpcables_probe_display(uint8_t * models); + + + +/** + * \brief Converts a calculator model to a printable string. + * \param model the calculator model. + * \return the string corresponding to the calculator model. + **/ +HPEXPORT const char * HPCALL hpcables_model_to_string(cable_model model); +/** + * \brief Converts a string to a supported calculator model, if possible. + * \param str the string. + * \return the calculator model corresponding to the string, CALC_NONE if failed. + **/ +HPEXPORT cable_model HPCALL hpcables_string_to_model(const char *str); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libhpcalcs/src/hpcalcs.c b/libhpcalcs/src/hpcalcs.c new file mode 100644 index 0000000..4c92870 --- /dev/null +++ b/libhpcalcs/src/hpcalcs.c @@ -0,0 +1,674 @@ +/* + * libhpcalcs: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file hpcalcs.c Calcs: base part. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +#include +#include +#include "internal.h" +#include "logging.h" +#include "error.h" +#include "gettext.h" + +extern const calc_fncts calc_none_fncts; +extern const calc_fncts calc_prime_fncts; + +const calc_fncts * hpcalcs_all_calcs[CALC_MAX] = { + &calc_none_fncts, + &calc_prime_fncts +}; + +static const uint32_t supported_calcs = + (1U << CALC_NONE) + | (1U << CALC_PRIME) +; + +hplibs_malloc_funcs hpcalcs_alloc_funcs = { + .malloc = malloc, + .calloc = calloc, + .realloc = realloc, + .free = free +}; + + +// not static, must be shared between instances +int hpcalcs_instance_count = 0; + +HPEXPORT int HPCALL hpcalcs_init(hpcalcs_config * config) { + int res = ERR_SUCCESS; + void (*log_callback)(const char *format, va_list args); + hplibs_malloc_funcs * alloc_funcs; + + if (config == NULL) { + log_callback = NULL; + alloc_funcs = NULL; + } + else { + if (config->version <= HPCALCS_CONFIG_VERSION) { + if (config->version == 1) { + log_callback = config->log_callback; + alloc_funcs = config->alloc_funcs; + } + else { + hpcables_error(_("%s: unsupported config version %u"), __FUNCTION__, config->version); + res = ERR_LIBRARY_CONFIG_VERSION; + } + } + else { + hpcables_error(_("%s: unsupported config version %u"), __FUNCTION__, config->version); + res = ERR_LIBRARY_CONFIG_VERSION; + } + } + + if (!res) { + // TODO: when (if) libhpfiles is split from libhpcalcs, copy and adjust locale setting code from hpfiles.c. + + if (!hpcalcs_instance_count) { + hpcalcs_log_set_callback(log_callback); + if (alloc_funcs != NULL) { + hpcalcs_alloc_funcs = *alloc_funcs; + } + hpcalcs_info(_("hpcalcs library version %s"), hpcalcs_version_get()); + + hpcalcs_info(_("%s: init succeeded"), __FUNCTION__); + hpcalcs_instance_count++; + } + else { + hpcalcs_info(_("%s: re-init skipped"), __FUNCTION__); + hpcalcs_instance_count++; + } + } + + return res; +} + +HPEXPORT int HPCALL hpcalcs_exit(void) { + int res; + if (hpcalcs_instance_count <= 0) { + hpcalcs_error(_("%s: more exits than inits"), __FUNCTION__); + res = ERR_LIBRARY_EXIT; + } + else { + hpcalcs_instance_count--; + + hpcalcs_info(_("%s: exit succeeded"), __FUNCTION__); + res = ERR_SUCCESS; + } + return res; +} + + +HPEXPORT const char* HPCALL hpcalcs_version_get (void) { + return VERSION; +} + + +HPEXPORT uint32_t HPCALL hpcalcs_supported_calcs (void) { + return supported_calcs; +} + + +HPEXPORT calc_handle * HPCALL hpcalcs_handle_new(calc_model model) { + calc_handle * handle = NULL; + if (model < CALC_MAX) { + handle = (calc_handle *)(hpcalcs_alloc_funcs.calloc)(1, sizeof(*handle)); + + if (handle != NULL) { + handle->model = model; + handle->fncts = hpcalcs_all_calcs[model]; + hpcalcs_info("%s: calc handle allocation succeeded", __FUNCTION__); + } + else { + hpcalcs_error("%s: calc handle allocation failed", __FUNCTION__); + } + } + else { + hpcalcs_error("%s: invalid model", __FUNCTION__); + } + return handle; +} + +HPEXPORT int HPCALL hpcalcs_handle_del(calc_handle * handle) { + int res; + if (handle != NULL) { + if (handle->attached) { + res = hpcalcs_cable_detach(handle); + } + else { + res = ERR_SUCCESS; + } + + (hpcalcs_alloc_funcs.free)(handle->handle); + handle->handle = NULL; + + (hpcalcs_alloc_funcs.free)(handle); + hpcalcs_info("%s: calc handle deletion succeeded", __FUNCTION__); + } + else { + res = ERR_INVALID_HANDLE; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL hpcalcs_cable_attach(calc_handle * handle, cable_handle * cable) { + int res; + if (handle != NULL && cable != NULL) { + res = hpcables_cable_open(cable); + if (res == ERR_SUCCESS) { + handle->cable = cable; + handle->attached = 1; + handle->open = 1; + hpcalcs_info("%s: cable open and attach succeeded", __FUNCTION__); + } + else { + hpcalcs_error("%s: cable open failed", __FUNCTION__); + } + } + else { + res = ERR_INVALID_PARAMETER; + hpcalcs_error("%s: an argument is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL hpcalcs_cable_detach(calc_handle * handle) { + int res; + if (handle != NULL) { + res = hpcables_cable_close(handle->cable); + if (res == ERR_SUCCESS) { + handle->open = 0; + handle->attached = 0; + handle->cable = NULL; + hpcalcs_info("%s: cable close and detach succeeded", __FUNCTION__); + } + else { + hpcalcs_error("%s: cable close and detach failed", __FUNCTION__); + } + } + else { + res = ERR_INVALID_HANDLE; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT cable_handle * HPCALL hpcalcs_cable_get(calc_handle * handle) { + cable_handle * res = NULL; + if (handle != NULL) { + res = handle->cable; + hpcalcs_info("%s: cable get succeeded", __FUNCTION__); + } + else { + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL hpcalcs_handle_display(calc_handle * handle) { + int res; + if (handle != NULL) { + hpcalcs_info("Link calc handle details:"); + hpcalcs_info("\tmodel: %s", hpcalcs_model_to_string(handle->model)); + hpcalcs_info("\tattached: %d", handle->attached); + hpcalcs_info("\topen: %d", handle->open); + hpcalcs_info("\tbusy: %d", handle->busy); + res = ERR_SUCCESS; + } + else { + res = ERR_INVALID_HANDLE; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + + return res; +} + +HPEXPORT calc_model HPCALL hpcalcs_get_model(calc_handle * handle) { + calc_model model = CALC_NONE; + if (handle != NULL) { + model = handle->model; + } + else { + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + + return model; +} + + +#define DO_BASIC_HANDLE_CHECKS() \ + if (!handle->attached) { \ + res = ERR_CALC_NO_CABLE; \ + hpcalcs_error("%s: no cable attached", __FUNCTION__); \ + break; \ + } \ + if (!handle->open) { \ + res = ERR_CALC_CABLE_NOT_OPEN; \ + hpcalcs_error("%s: cable not open", __FUNCTION__); \ + break; \ + } \ + if (handle->busy) { \ + res = ERR_CALC_BUSY; \ + hpcalcs_error("%s: cable busy", __FUNCTION__); \ + break; \ + } \ + if (handle->fncts == NULL) { \ + res = ERR_CALC_INVALID_FNCTS; \ + hpcalcs_error("%s: fncts is NULL", __FUNCTION__); \ + break; \ + } + +HPEXPORT int HPCALL hpcalcs_calc_check_ready(calc_handle * handle, uint8_t ** out_data, uint32_t * out_size) { + int res; + if (handle != NULL) { + do { + int (*check_ready) (calc_handle *, uint8_t **, uint32_t *); + + DO_BASIC_HANDLE_CHECKS() + + check_ready = handle->fncts->check_ready; + if (check_ready != NULL) { + handle->busy = 1; + res = (*check_ready)(handle, out_data, out_size); + if (res == ERR_SUCCESS) { + hpcalcs_info("%s: check_ready succeeded", __FUNCTION__); + } + else { + hpcalcs_error("%s: check_ready failed", __FUNCTION__); + } + handle->busy = 0; + } + else { + res = ERR_CALC_INVALID_FNCTS; + hpcalcs_error("%s: fncts->check_ready is NULL", __FUNCTION__); + } + } while (0); + } + else { + res = ERR_INVALID_HANDLE; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL hpcalcs_calc_get_infos(calc_handle * handle, calc_infos * infos) { + int res; + if (handle != NULL) { + do { + int (*get_infos) (calc_handle *, calc_infos *); + + DO_BASIC_HANDLE_CHECKS() + + get_infos = handle->fncts->get_infos; + if (get_infos != NULL) { + handle->busy = 1; + res = (*get_infos)(handle, infos); + if (res == 0) { + hpcalcs_info("%s: get_infos succeeded", __FUNCTION__); + } + else { + hpcalcs_error("%s: get_infos failed", __FUNCTION__); + } + handle->busy = 0; + } + else { + res = ERR_CALC_INVALID_FNCTS; + hpcalcs_error("%s: fncts->get_infos is NULL", __FUNCTION__); + } + } while (0); + } + else { + res = ERR_INVALID_HANDLE; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL hpcalcs_calc_set_date_time(calc_handle * handle, time_t timestamp) { + int res; + if (handle != NULL) { + do { + int (*set_date_time) (calc_handle *, time_t); + + DO_BASIC_HANDLE_CHECKS() + + set_date_time = handle->fncts->set_date_time; + if (set_date_time != NULL) { + handle->busy = 1; + res = (*set_date_time)(handle, timestamp); + if (res == 0) { + hpcalcs_info("%s: set_date_time succeeded", __FUNCTION__); + } + else { + hpcalcs_error("%s: set_date_time failed", __FUNCTION__); + } + handle->busy = 0; + } + else { + res = ERR_CALC_INVALID_FNCTS; + hpcalcs_error("%s: fncts->set_date_time is NULL", __FUNCTION__); + } + } while (0); + } + else { + res = ERR_INVALID_HANDLE; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL hpcalcs_calc_recv_screen(calc_handle * handle, calc_screenshot_format format, uint8_t ** out_data, uint32_t * out_size) { + int res; + // TODO: some checking on format, but for now, it would hamper documentation efforts. + if (handle != NULL) { + do { + int (*recv_screen) (calc_handle *, calc_screenshot_format, uint8_t **, uint32_t *); + + DO_BASIC_HANDLE_CHECKS() + + recv_screen = handle->fncts->recv_screen; + if (recv_screen != NULL) { + handle->busy = 1; + res = (*recv_screen)(handle, format, out_data, out_size); + if (res == 0) { + hpcalcs_info("%s: recv_screen succeeded", __FUNCTION__); + } + else { + hpcalcs_error("%s: recv_screen failed", __FUNCTION__); + } + handle->busy = 0; + } + else { + res = ERR_CALC_INVALID_FNCTS; + hpcalcs_error("%s: fncts->get_infos is NULL", __FUNCTION__); + } + } while (0); + } + else { + res = ERR_INVALID_HANDLE; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL hpcalcs_calc_send_file(calc_handle * handle, files_var_entry * file) { + int res; + if (handle != NULL) { + do { + int (*send_file) (calc_handle *, files_var_entry *); + + DO_BASIC_HANDLE_CHECKS() + + send_file = handle->fncts->send_file; + if (send_file != NULL) { + handle->busy = 1; + res = (*send_file)(handle, file); + if (res == 0) { + hpcalcs_info("%s: send_file succeeded", __FUNCTION__); + } + else { + hpcalcs_error("%s: send_file failed", __FUNCTION__); + } + handle->busy = 0; + } + else { + res = ERR_CALC_INVALID_FNCTS; + hpcalcs_error("%s: fncts->send_file is NULL", __FUNCTION__); + } + } while (0); + } + else { + res = ERR_INVALID_HANDLE; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL hpcalcs_calc_recv_file(calc_handle * handle, files_var_entry * name, files_var_entry ** out_file) { + int res; + if (handle != NULL) { + do { + int (*recv_file) (calc_handle *, files_var_entry *, files_var_entry **); + + DO_BASIC_HANDLE_CHECKS() + + recv_file = handle->fncts->recv_file; + if (recv_file != NULL) { + handle->busy = 1; + res = (*recv_file)(handle, name, out_file); + if (res == 0) { + hpcalcs_info("%s: recv_file succeeded", __FUNCTION__); + } + else { + hpcalcs_error("%s: recv_file failed", __FUNCTION__); + } + handle->busy = 0; + } + else { + res = ERR_CALC_INVALID_FNCTS; + hpcalcs_error("%s: fncts->recv_file is NULL", __FUNCTION__); + } + } while (0); + } + else { + res = ERR_INVALID_HANDLE; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL hpcalcs_calc_recv_backup(calc_handle * handle, files_var_entry *** out_vars) { + int res; + if (handle != NULL) { + do { + int (*recv_backup) (calc_handle *, files_var_entry ***); + + DO_BASIC_HANDLE_CHECKS() + + recv_backup = handle->fncts->recv_backup; + if (recv_backup != NULL) { + handle->busy = 1; + res = (*recv_backup)(handle, out_vars); + if (res == 0) { + hpcalcs_info("%s: recv_backup succeeded", __FUNCTION__); + } + else { + hpcalcs_error("%s: recv_backup failed", __FUNCTION__); + } + handle->busy = 0; + } + else { + res = ERR_CALC_INVALID_FNCTS; + hpcalcs_error("%s: fncts->recv_backup is NULL", __FUNCTION__); + } + } while (0); + } + else { + res = ERR_INVALID_HANDLE; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL hpcalcs_calc_send_key(calc_handle * handle, uint32_t code) { + int res; + if (handle != NULL) { + do { + int (*send_key) (calc_handle *, uint32_t); + + DO_BASIC_HANDLE_CHECKS() + + send_key = handle->fncts->send_key; + if (send_key != NULL) { + handle->busy = 1; + res = (*send_key)(handle, code); + if (res == 0) { + hpcalcs_info("%s: send_key succeeded", __FUNCTION__); + } + else { + hpcalcs_error("%s: send_key failed", __FUNCTION__); + } + handle->busy = 0; + } + else { + res = ERR_CALC_INVALID_FNCTS; + hpcalcs_error("%s: fncts->send_key is NULL", __FUNCTION__); + } + } while (0); + } + else { + res = ERR_INVALID_HANDLE; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL hpcalcs_calc_send_keys(calc_handle * handle, const uint8_t * data, uint32_t size) { + int res = -1; + if (handle != NULL) { + do { + int (*send_keys) (calc_handle *, const uint8_t *, uint32_t); + + DO_BASIC_HANDLE_CHECKS() + + send_keys = handle->fncts->send_keys; + if (send_keys != NULL) { + handle->busy = 1; + res = (*send_keys)(handle, data, size); + if (res == 0) { + hpcalcs_info("%s: send_keys succeeded", __FUNCTION__); + } + else { + hpcalcs_error("%s: send_keys failed", __FUNCTION__); + } + handle->busy = 0; + } + else { + res = ERR_CALC_INVALID_FNCTS; + hpcalcs_error("%s: fncts->send_keys is NULL", __FUNCTION__); + } + } while (0); + } + else { + res = ERR_INVALID_HANDLE; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL hpcalcs_calc_send_chat(calc_handle * handle, const uint16_t * data, uint32_t size) { + int res; + if (handle != NULL) { + do { + int (*send_chat) (calc_handle *, const uint16_t *, uint32_t); + + DO_BASIC_HANDLE_CHECKS() + + send_chat = handle->fncts->send_chat; + if (send_chat != NULL) { + handle->busy = 1; + res = (*send_chat)(handle, data, size); + if (res == 0) { + hpcalcs_info("%s: send_chat succeeded", __FUNCTION__); + } + else { + hpcalcs_error("%s: send_chat failed", __FUNCTION__); + } + handle->busy = 0; + } + else { + res = ERR_CALC_INVALID_FNCTS; + hpcalcs_error("%s: fncts->send_chat is NULL", __FUNCTION__); + } + } while (0); + } + else { + res = ERR_INVALID_HANDLE; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL hpcalcs_calc_recv_chat(calc_handle * handle, uint16_t ** data, uint32_t *size) { + int res; + if (handle != NULL) { + do { + int (*recv_chat) (calc_handle *, uint16_t **, uint32_t *); + + DO_BASIC_HANDLE_CHECKS() + + recv_chat = handle->fncts->recv_chat; + if (recv_chat != NULL) { + handle->busy = 1; + res = (*recv_chat)(handle, data, size); + if (res == 0) { + hpcalcs_info("%s: recv_chat succeeded", __FUNCTION__); + } + else { + hpcalcs_error("%s: recv_chat failed", __FUNCTION__); + } + handle->busy = 0; + } + else { + res = ERR_CALC_INVALID_FNCTS; + hpcalcs_error("%s: fncts->recv_chat is NULL", __FUNCTION__); + } + } while (0); + } + else { + res = ERR_INVALID_HANDLE; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +#undef DO_BASIC_HANDLE_CHECKS + +HPEXPORT int HPCALL hpcalcs_probe_calc(cable_model cable, calc_model * out_calc) { + int res; + if (out_calc != NULL) { + if (cable == CABLE_PRIME_HID) { + res = ERR_SUCCESS; + *out_calc = CALC_PRIME; + hpcalcs_info("%s: calc probe succeeded", __FUNCTION__); + } + else { + res = ERR_CALC_PROBE_FAILED; + hpcalcs_error("%s: calc probe failed", __FUNCTION__); + } + } + else { + res = ERR_INVALID_PARAMETER; + hpcalcs_error("%s: out_calc is NULL", __FUNCTION__); + } + return res; +} diff --git a/libhpcalcs/src/hpcalcs.h b/libhpcalcs/src/hpcalcs.h new file mode 100644 index 0000000..90f4f22 --- /dev/null +++ b/libhpcalcs/src/hpcalcs.h @@ -0,0 +1,424 @@ +/* + * libhpcalcs: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file hpcalcs.h Calcs: base part. + */ + +#ifndef __HPLIBS_CALCS_H__ +#define __HPLIBS_CALCS_H__ + +#include +#include +#include + +#include "hplibs.h" +#include "hpfiles.h" +#include "hpcables.h" + +//! Opaque type for internal _calc_fncts. +typedef struct _calc_fncts calc_fncts; +//! Opaque type for internal _calc_handle. +typedef struct _calc_handle calc_handle; + +//! Indices of the function pointers in _calc_fncts. +typedef enum { + CALC_FNCT_CHECK_READY = 0, + CALC_FNCT_GET_INFOS = 1, + CALC_FNCT_SET_DATE_TIME = 2, + CALC_FNCT_RECV_SCREEN = 3, + CALC_FNCT_SEND_FILE = 4, + CALC_FNCT_RECV_FILE = 5, + CALC_FNCT_RECV_BACKUP = 6, + CALC_FNCT_SEND_KEY = 7, + CALC_FNCT_SEND_KEYS = 8, + CALC_FNCT_SEND_CHAT = 9, + CALC_FNCT_RECV_CHAT = 10, + CALC_FNCT_LAST ///< Keep this one last +} calc_fncts_idx; + +//! Used in the bit field of _calc_fncts, indicating whether a given calculator supports a given operation. +typedef enum { + CALC_OPS_NONE = 0, + CALC_OPS_CHECK_READY = (1 << CALC_FNCT_CHECK_READY), + CALC_OPS_GET_INFOS = (1 << CALC_FNCT_GET_INFOS), + CALC_OPS_SET_DATE_TIME = (1 << CALC_FNCT_SET_DATE_TIME), + CALC_OPS_RECV_SCREEN = (1 << CALC_FNCT_RECV_SCREEN), + CALC_OPS_SEND_FILE = (1 << CALC_FNCT_SEND_FILE), + CALC_OPS_RECV_FILE = (1 << CALC_FNCT_RECV_FILE), + CALC_OPS_RECV_BACKUP = (1 << CALC_FNCT_RECV_BACKUP), + CALC_OPS_SEND_KEY = (1 << CALC_FNCT_SEND_KEY), + CALC_OPS_SEND_KEYS = (1 << CALC_FNCT_SEND_KEYS), + CALC_OPS_SEND_CHAT = (1 << CALC_FNCT_SEND_CHAT), + CALC_OPS_RECV_CHAT = (1 << CALC_FNCT_RECV_CHAT) +} calc_features_operations; + +//! Screenshot formats supported by the calculators, list is known to be incomplete. +typedef enum { + // 5 is triggered periodically by the official connectivity kit. It returns something with a PNG header, but much smaller. + CALC_SCREENSHOT_FORMAT_FIRST = 8, + CALC_SCREENSHOT_FORMAT_PRIME_PNG_320x240x16 = 8, + CALC_SCREENSHOT_FORMAT_PRIME_PNG_320x240x4 = 9, + CALC_SCREENSHOT_FORMAT_PRIME_PNG_160x120x16 = 10, + CALC_SCREENSHOT_FORMAT_PRIME_PNG_160x120x4 = 11, + CALC_SCREENSHOT_FORMAT_LAST ///< Keep this one last +} calc_screenshot_format; + +//! Structure containing information returned by the calculator. This will change a lot when the returned data is better documented. +typedef struct { + uint32_t size; + uint8_t * data; +} calc_infos; + +//! Internal structure containing information about the calculator, and function pointers. +struct _calc_fncts { + calc_model model; + const char * name; + const char * description; + int features; + int (*check_ready) (calc_handle * handle, uint8_t ** out_data, uint32_t * out_size); + int (*get_infos) (calc_handle * handle, calc_infos * infos); + int (*set_date_time) (calc_handle * handle, time_t timestamp); + int (*recv_screen) (calc_handle * handle, calc_screenshot_format format, uint8_t ** out_data, uint32_t * out_size); + int (*send_file) (calc_handle * handle, files_var_entry * file); + int (*recv_file) (calc_handle * handle, files_var_entry * request, files_var_entry ** out_file); + int (*recv_backup) (calc_handle * handle, files_var_entry *** out_vars); + int (*send_key) (calc_handle * handle, uint32_t code); + int (*send_keys) (calc_handle * handle, const uint8_t * data, uint32_t size); + int (*send_chat) (calc_handle * handle, const uint16_t * data, uint32_t size); + int (*recv_chat) (calc_handle * handle, uint16_t ** out_data, uint32_t * out_size); +}; + +//! Internal structure containing state about the calculator, returned and passed around by the user. +struct _calc_handle { + calc_model model; + void * handle; + const calc_fncts * fncts; + cable_handle * cable; + int attached; // Should be made explicitly atomic with GCC >= 4.7 or Clang, but int is atomic on most ISAs anyway. + int open; // Should be made explicitly atomic with GCC >= 4.7 or Clang, but int is atomic on most ISAs anyway. + int busy; // Should be made explicitly atomic with GCC >= 4.7 or Clang, but int is atomic on most ISAs anyway. +}; + + +//! Structure passed to \a hpcalcs_init, contains e.g. callbacks for logging and memory allocation. +typedef struct { + unsigned int version; ///< Config version number. + void (*log_callback)(const char *format, va_list args); ///< Callback function for receiving logging output. + hplibs_malloc_funcs * alloc_funcs; ///< Function pointers used for dynamic memory allocation. If NULL, the library defaults to malloc(), calloc(), realloc(), free(). +} hpcalcs_config; + +//! Latest revision of the \a hpcalcs_config struct layout supported by this version of the library. +#define HPCALCS_CONFIG_VERSION (1) + + +//! Structure defining a raw packet for the Prime, used at the lowest layer of the protocol implementation. +typedef struct +{ + uint32_t size; + uint8_t data[PRIME_RAW_HID_DATA_SIZE + 1]; +} prime_raw_hid_pkt; + + +//! Structure defining a virtual packet for the Prime, used at the middle layer of the protocol implementation (fragmented to / reassembled from raw packets). +typedef struct +{ + uint32_t size; + uint8_t * data; + uint8_t cmd; +} prime_vtl_pkt; + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Initializes library internals. Must be called before any other libhpcalcs function. + * \param config pointer to struct containing e.g. callbacks passed to the library. + * \return Whether the initialization succeeded. + * \note the contents of alloc_funcs are copied. + * \todo return instance count instead. + **/ +HPEXPORT int HPCALL hpcalcs_init(hpcalcs_config * config); +/** + * \brief Tears down library internals. No other libhpcalcs function can be called after this one. + * \return Whether the teardown succeeded. + * \todo return instance count instead. + **/ +HPEXPORT int HPCALL hpcalcs_exit(void); + +/** + * \brief Returns the library version string. + * \return The library version string, usually under the form "X.Y.Z". + **/ +HPEXPORT const char* HPCALL hpcalcs_version_get(void); + +/** + * \brief Returns the calcs supported by the current build of the library. + * \return An integer containing a binary OR of (1 << CALC_*) values, where CALC_* values are defined in enum \a calc_model. + **/ +HPEXPORT uint32_t HPCALL hpcalcs_supported_calcs(void); + +/** + * \brief Gets the error message if the error was produced by this library + * \param number the error number (from internal error.h) + * \param message out pointer for a newly allocated text error message, which must be freed by the caller + * \note the string is allocated with malloc(), therefore it must be freed with free(). + * \return 0 if the error was produced by this library, otherwise the error number (for propagation). + **/ +HPEXPORT int HPCALL hpcalcs_error_get(int number, char **message); + +/** + * \brief Sets the callback function used by the library for logging + * \param log_callback function pointer + */ +HPEXPORT void HPCALL hpcalcs_log_set_callback(void (*log_callback)(const char *format, va_list args)); +/** + * \brief Sets the log level of the library, for controlling how much logging output is produced. + * \param log_level log level (from hplibs.h) + * \return the previous log level + */ +HPEXPORT hplibs_logging_level HPCALL hpcalcs_log_set_level(hplibs_logging_level log_level); + + +/** + * \brief Create a new handle (opaque structure) for the given calc model. + * The handle must be freed with \a hpcalcs_handle_del when no longer needed. + * \param model the calculator model. + * \return NULL if an error occurred, otherwise a valid handle. + **/ +HPEXPORT calc_handle * HPCALL hpcalcs_handle_new(calc_model model); +/** + * \brief Deletes a handle (opaque structure) created by \a hpcalcs_handle_new(). + * \param handle the handle to be deleted. + * \return 0 if the deletion succeeded, nonzero otherwise. + **/ +HPEXPORT int HPCALL hpcalcs_handle_del(calc_handle * handle); +/** + * \brief Shows basic information about a handle + * \param handle the handle to be dumped. + * \return 0 if the handle was non-NULL, nonzero otherwise. + **/ +HPEXPORT int HPCALL hpcalcs_handle_display(calc_handle * handle); + +/** + * \brief Retrieves the calc model from the given calc handle. + * \param handle the calc handle + * \return the calc model corresponding to the given handle. + */ +HPEXPORT calc_model HPCALL hpcalcs_get_model(calc_handle * handle); + +/** + * \brief Opens and attaches the given cable for use with the given calculator. + * \param handle the calculator handle. + * \param cable the cable handle. + * \return 0 upon success, nonzero otherwise. + **/ +HPEXPORT int HPCALL hpcalcs_cable_attach(calc_handle * handle, cable_handle * cable); +/** + * \brief Closes and detaches the cable attached to the given calculator handle. + * \param handle the calculator handle. + * \return 0 upon success, nonzero otherwise. + **/ +HPEXPORT int HPCALL hpcalcs_cable_detach(calc_handle * handle); +/** + * \brief Retrieves the cable handle attached to the given calculator handle, if any. + * \param handle the calculator handle. + * \return NULL if an error occurred, otherwise a cable handle. + **/ +HPEXPORT cable_handle * HPCALL hpcalcs_cable_get(calc_handle * handle); + +/** + * \brief Checks whether the calculator is ready + * \param handle the calculator handle. + * \param out_data storage area for information contained in the calculator's reply. + * \param out_size storage area for size of the information contained in the calculator's reply. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL hpcalcs_calc_check_ready(calc_handle * handle, uint8_t ** out_data, uint32_t * out_size); +/** + * \brief Retrieves some information, such as firmware version, from the calculator. + * \param handle the calculator handle. + * \param infos storage area for information contained in the reply. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL hpcalcs_calc_get_infos(calc_handle * handle, calc_infos * infos); +/** + * \brief Sets the calculator's date and time from a standard C89 / *nix timestamp. + * \param handle the calculator handle. + * \param timestamp the timestamp since the Epoch (1970). + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL hpcalcs_calc_set_date_time(calc_handle * handle, time_t timestamp); +/** + * \brief Retrieves a screenshot from the calculator + * \param handle the calculator handle. + * \param format the desired screenshot format. + * \param out_data storage area for screenshot contained in the calculator's reply. + * \param out_size storage area for size of the screenshot contained in the calculator's reply. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL hpcalcs_calc_recv_screen(calc_handle * handle, calc_screenshot_format format, uint8_t ** out_data, uint32_t * out_size); +/** + * \brief Sends a file to the calculator. + * \param handle the calculator handle. + * \param file information about the file to be sent. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL hpcalcs_calc_send_file(calc_handle * handle, files_var_entry * file); +/** + * \brief Receives a file from the calculator. + * \param handle the calculator handle. + * \param request information about the file to be received. + * \param out_file storage area for the file to be received. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL hpcalcs_calc_recv_file(calc_handle * handle, files_var_entry * request, files_var_entry ** out_file); +/** + * \brief Receives a backup (made of multiple files) from the calculator. + * \param handle the calculator handle. + * \param out_vars storage area for the files to be received. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL hpcalcs_calc_recv_backup(calc_handle * handle, files_var_entry *** out_vars); +/** + * \brief Sends a single keypress to the calculator. + * \param handle the calculator handle. + * \param code the key code. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL hpcalcs_calc_send_key(calc_handle * handle, uint32_t code); +/** + * \brief Sends potentially multiple keypresses to the calculator. + * \param handle the calculator handle. + * \param data the buffer containings key codes. + * \param size the size of the data in the buffer. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL hpcalcs_calc_send_keys(calc_handle * handle, const uint8_t * data, uint32_t size); +/** + * \brief Sends chat data to the calculator. + * \param handle the calculator handle. + * \param data the data to be sent (UTF-16LE string, with U+0000 terminator, without UTF-16 LE BOM). + * \param size the size of the data to be sent. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL hpcalcs_calc_send_chat(calc_handle * handle, const uint16_t * data, uint32_t size); +/** + * \brief Receives chat data from the calculator. + * \param handle the calculator handle. + * \param out_data storage area for the chat data contained in the calculator's reply. + * \param out_size storage area for size of the chat data contained in the calculator's reply. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL hpcalcs_calc_recv_chat(calc_handle * handle, uint16_t ** out_data, uint32_t * out_size); + + +/** + * \brief Sends the given raw packet to the Prime calculator using given calculator handle. + * \param handle the calculator handle. + * \param pkt the raw packet. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL prime_send(calc_handle * handle, prime_raw_hid_pkt * pkt); +/** + * \brief Receives a raw packet from the Prime calculator using given calculator handle, and store the result to given packet. + * \param handle the calculator handle. + * \param pkt the dest raw packet. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL prime_recv(calc_handle * handle, prime_raw_hid_pkt * pkt); + +/** + * \brief Probes the given cable model to find out what calculator is connected to it. + * \param cable the cable model to be probed. + * \param out_calc storage area for the calculator model attached to the cable (if any). + * \return 0 upon success, nonzero otherwise. + * \note For now, the calculator type is fully determined by the calculator type. This might change in the future. + */ +HPEXPORT int HPCALL hpcalcs_probe_calc(cable_model cable, calc_model * out_calc); + + +/** + * \brief Creates a virtual packet for the Prime calculator, preallocating the given size. + * \param size the size to be preallocated. + * \return NULL if an error occurred, a virtual packet otherwise. + */ +HPEXPORT prime_vtl_pkt * HPCALL prime_vtl_pkt_new(uint32_t size); +/** + * \brief Creates a virtual packet for the Prime calculator, filling it with the given size and data. + * \param size the size of the data. + * \param data the pre-allocated data (assumed to be allocated with the same memory allocator as the one given to libhpcalcs, if not using the default one). + * \return NULL if an error occurred, a virtual packet otherwise. + * \warning This function takes ownership of \a data. + */ +HPEXPORT prime_vtl_pkt * HPCALL prime_vtl_pkt_new_with_data_ptr(uint32_t size, uint8_t * data); +/** + * \brief Deletes a virtual packet for the Prime calculator. + * \param pkt the packet to be deleted. + */ +HPEXPORT void HPCALL prime_vtl_pkt_del(prime_vtl_pkt * pkt); + +/** + * \brief Sends the given virtual packet to the Prime calculator using given calculator handle. + * \param handle the calculator handle. + * \param pkt the virtual packet. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL prime_send_data(calc_handle * handle, prime_vtl_pkt * pkt); +/** + * \brief Receives a virtual packet from the Prime calculator using given calculator handle, and store the result to given packet. + * \param handle the calculator handle. + * \param pkt the dest virtual packet. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL prime_recv_data(calc_handle * handle, prime_vtl_pkt * pkt); +/** + * \brief Returns the packet size corresponding to command \a cmd, possibly corrected by the contents of \a data. + * \param cmd the command. + * \param data the data. + * \param size storage area for size of the data. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL prime_data_size(uint8_t cmd, uint8_t * data, uint32_t * out_size); + + +/** + * \brief Converts a calculator model to a printable string. + * \param model the calculator model. + * \return the string corresponding to the calculator model. + **/ +HPEXPORT const char * HPCALL hpcalcs_model_to_string(calc_model model); +/** + * \brief Converts a string to a supported calculator model, if possible. + * \param str the string. + * \return the calculator model corresponding to the string, CALC_NONE if failed. + **/ +HPEXPORT calc_model HPCALL hpcalcs_string_to_model(const char *str); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libhpcalcs/src/hpfiles.c b/libhpcalcs/src/hpfiles.c new file mode 100644 index 0000000..7198a4e --- /dev/null +++ b/libhpcalcs/src/hpfiles.c @@ -0,0 +1,378 @@ +/* + * libhpfiles: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file hpfiles.c Files: base part. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#include +#include + +#include +#include "internal.h" +#include "logging.h" +#include "error.h" +#include "utils.h" +#include "gettext.h" + +hplibs_malloc_funcs hpfiles_alloc_funcs = { + .malloc = malloc, + .calloc = calloc, + .realloc = realloc, + .free = free +}; + + +// not static, must be shared between instances +int hpfiles_instance_count = 0; + +HPEXPORT int HPCALL hpfiles_init(hpfiles_config * config) { + int res = ERR_SUCCESS; + void (*log_callback)(const char *format, va_list args); + hplibs_malloc_funcs * alloc_funcs; + + if (config == NULL) { + log_callback = NULL; + alloc_funcs = NULL; + } + else { + if (config->version <= HPFILES_CONFIG_VERSION) { + if (config->version == 1) { + log_callback = config->log_callback; + alloc_funcs = config->alloc_funcs; + } + else { + hpcables_error(_("%s: unsupported config version %u"), __FUNCTION__, config->version); + res = ERR_LIBRARY_CONFIG_VERSION; + } + } + else { + hpcables_error(_("%s: unsupported config version %u"), __FUNCTION__, config->version); + res = ERR_LIBRARY_CONFIG_VERSION; + } + } + + if (!res) { + // Set up locale info, if NLS support is enabled. +#ifdef ENABLE_NLS + { + char locale_dir[65536]; + +#ifdef __WIN32__ + HANDLE hDll; + int i; + + hDll = GetModuleHandle("libhpcalcs-0.dll"); + GetModuleFileName(hDll, locale_dir, 65515); + + for (i = (int)strlen(locale_dir); i >= 0; i--) { + if (locale_dir[i] == '\\') { + break; + } + } + locale_dir[i] = '\0'; + + strcat(locale_dir, "\\locale"); +#else + strncpy(locale_dir, LOCALEDIR, sizeof(locale_dir) - 21); +#endif + + hpfiles_info("setlocale: %s", setlocale(LC_ALL, "")); + hpfiles_info("bindtextdomain: %s", bindtextdomain(PACKAGE, locale_dir)); + bind_textdomain_codeset(PACKAGE, "UTF-8"/*"ISO-8859-15"*/); + hpfiles_info("textdomain: %s", textdomain(NULL)); + } +#endif + + if (!hpfiles_instance_count) { + hpfiles_log_set_callback(log_callback); + if (alloc_funcs != NULL) { + hpfiles_alloc_funcs = *alloc_funcs; + } + hpfiles_info(_("hpfiles library version %s"), hpfiles_version_get()); + + hpfiles_info(_("%s: init succeeded"), __FUNCTION__); + hpfiles_instance_count++; + } + else { + hpfiles_info(_("%s: re-init skipped"), __FUNCTION__); + hpfiles_instance_count++; + } + } + + return res; +} + +HPEXPORT int HPCALL hpfiles_exit(void) { + int res; + if (hpfiles_instance_count <= 0) { + hpfiles_error(_("%s: more exits than inits"), __FUNCTION__); + res = ERR_LIBRARY_EXIT; + } + else { + hpfiles_instance_count--; + + hpfiles_info(_("%s: exit succeeded"), __FUNCTION__); + res = ERR_SUCCESS; + } + return res; +} + + +HPEXPORT const char* HPCALL hpfiles_version_get (void) { + return VERSION; +} + + +HPEXPORT files_var_entry * HPCALL hpfiles_ve_create(void) { + return (hpfiles_alloc_funcs.calloc)(1, sizeof(files_var_entry)); +} + +HPEXPORT files_var_entry * HPCALL hpfiles_ve_create_with_size(uint32_t size) { + files_var_entry * ve = hpfiles_ve_create(); + if (ve != NULL) { + ve->data = (uint8_t *)(hpfiles_alloc_funcs.calloc)(sizeof(uint8_t), size); + if (ve->data != NULL) { + ve->size = size; + } + else { + (hpfiles_alloc_funcs.free)(ve); + ve = NULL; + } + } + + if (ve == NULL) { + hpfiles_error("%s: failed to create ve", __FUNCTION__); + } + + return ve; +} + +HPEXPORT files_var_entry * HPCALL hpfiles_ve_create_with_data(uint8_t * data, uint32_t size) { + files_var_entry * ve = hpfiles_ve_create(); + if (ve != NULL) { + ve->data = (uint8_t *)(hpfiles_alloc_funcs.malloc)(size); + if (ve->data != NULL) { + if (data != NULL) { + memcpy(ve->data, data, size); + } + ve->size = size; + } + else { + (hpfiles_alloc_funcs.free)(ve); + ve = NULL; + } + } + + if (ve == NULL) { + hpfiles_error("%s: failed to create ve", __FUNCTION__); + } + + return ve; +} + +HPEXPORT files_var_entry * HPCALL hpfiles_ve_create_with_data_ptr(uint8_t * data, uint32_t size) { + files_var_entry * ve = hpfiles_ve_create(); + if (ve != NULL) { + ve->data = data; + ve->size = size; + } + else { + hpfiles_error("%s: failed to create ve", __FUNCTION__); + } + + return ve; +} + +HPEXPORT files_var_entry * HPCALL hpfiles_ve_create_with_data_and_name(uint8_t * data, uint32_t size, const char16_t * name) { + files_var_entry * ve = hpfiles_ve_create_with_data(data, size); + if (ve != NULL) { + char16_strncpy(ve->name, name, FILES_VARNAME_MAXLEN); + } + + if (ve == NULL) { + hpfiles_error("%s: failed to create ve", __FUNCTION__); + } + + return ve; +} + +HPEXPORT files_var_entry * HPCALL hpfiles_ve_create_from_file(FILE * file, const char16_t * filename) { + files_var_entry * ve = NULL; + if (file != NULL) { + if (!fseek(file, 0, SEEK_END)) { + long size = ftell(file); + if (size != -1) { + if (!fseek(file, 0, SEEK_END)) { + // No calculator has 4 GB memory, let alone handle 4 GB variables, so let's (potentially) truncate long to uint32_t. + ve = hpfiles_ve_create_with_size((uint32_t)size); + if (ve != NULL) { + if (fread(ve->data, 1, (uint32_t)size, file) == (uint32_t)size) { + char16_strncpy(ve->name, filename, FILES_VARNAME_MAXLEN); + } + else { + hpfiles_error("%s: couldn't read from file", __FUNCTION__); + } + } + // else fall through + } + else { + hpfiles_error("%s: couldn't seek to BOF", __FUNCTION__); + } + } + else { + hpfiles_error("%s: couldn't obtain file size", __FUNCTION__); + } + } + else { + hpfiles_error("%s: couldn't seek to EOF", __FUNCTION__); + } + } + else { + hpfiles_error("%s: file is NULL", __FUNCTION__); + } + + if (ve == NULL) { + hpfiles_error("%s: failed to create ve", __FUNCTION__); + } + + return ve; +} + +HPEXPORT void HPCALL hpfiles_ve_delete(files_var_entry * ve) { + if (ve != NULL) { + (hpfiles_alloc_funcs.free)(ve->data); + (hpfiles_alloc_funcs.free)(ve); + } + else { + hpfiles_error("%s: ve is NULL", __FUNCTION__); + } +} + + +HPEXPORT void *hpfiles_ve_alloc_data(uint32_t size) { + return (hpfiles_alloc_funcs.calloc)(sizeof(uint8_t), size + 1); +} + +HPEXPORT files_var_entry * HPCALL hpfiles_ve_copy(files_var_entry * dst, files_var_entry * src) { + if (src != NULL && dst != NULL) { + memcpy(dst, src, sizeof(files_var_entry)); + if (src->data != NULL) { + dst->data = (uint8_t *)(hpfiles_alloc_funcs.malloc)(src->size); + if (dst->data != NULL) { + memcpy(dst->data, src->data, src->size); + } + else { + dst = NULL; + } + } + } + else { + hpfiles_error("%s: an argument is NULL", __FUNCTION__); + dst = NULL; + } + + return dst; +} + +HPEXPORT files_var_entry * HPCALL hpfiles_ve_dup(files_var_entry * src) { + files_var_entry * dst = NULL; + + if (src != NULL) { + dst = (hpfiles_alloc_funcs.malloc)(sizeof(files_var_entry)); + if (dst != NULL) { + memcpy(dst, src, sizeof(files_var_entry)); + if (src->data != NULL) { + dst->data = (uint8_t *)(hpfiles_alloc_funcs.malloc)(src->size); + if (dst->data != NULL) { + memcpy(dst->data, src->data, src->size); + } + else { + (hpfiles_alloc_funcs.free)(dst); + dst = NULL; + } + } + } + } + else { + hpfiles_error("%s: src is NULL", __FUNCTION__); + } + + if (dst == NULL) { + hpfiles_error("%s: failed to create ve", __FUNCTION__); + } + + return dst; +} + + +HPEXPORT int hpfiles_ve_display(files_var_entry * ve) { + int res; + if (ve != NULL) { + hpfiles_info("Displaying var entry %p", ve); + hpfiles_info("Name: %ls", ve->name); + hpfiles_info("Model: %u (%02X)", ve->model, ve->model); + hpfiles_info("Type: %u (%02X)", ve->type, ve->type); + hpfiles_info("Invalid: %u", ve->invalid); + hpfiles_info("Size: %" PRIu32 " (%02" PRIX32 ")", ve->size, ve->size); + hpfiles_info("Data: %p", ve->data); + res = ERR_SUCCESS; + } + else { + res = ERR_INVALID_PARAMETER; + hpfiles_error("%s: ve is NULL", __FUNCTION__); + } + return res; +} + + +HPEXPORT files_var_entry ** HPCALL hpfiles_ve_create_array(uint32_t element_count) { + return (files_var_entry **)(hpfiles_alloc_funcs.calloc)(element_count + 1, sizeof(files_var_entry *)); +} + +HPEXPORT files_var_entry ** HPCALL hpfiles_ve_resize_array(files_var_entry ** array, uint32_t element_count) { + return (files_var_entry **)(hpfiles_alloc_funcs.realloc)(array, (element_count + 1) * sizeof(files_var_entry *)); +} + +HPEXPORT void HPCALL hpfiles_ve_delete_array(files_var_entry ** array) { + + if (array != NULL) { + files_var_entry ** ptr; + for (ptr = array; *ptr; ptr++) { + hpfiles_ve_delete(*ptr); + } + (hpfiles_alloc_funcs.free)(array); + } + else { + hpfiles_error("%s: array is NULL", __FUNCTION__); + } +} diff --git a/libhpcalcs/src/hpfiles.h b/libhpcalcs/src/hpfiles.h new file mode 100644 index 0000000..ad7aa50 --- /dev/null +++ b/libhpcalcs/src/hpfiles.h @@ -0,0 +1,283 @@ +/* + * libhpfiles: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file hpfiles.h Files: base part. + */ + +#ifndef __HPLIBS_FILES_H__ +#define __HPLIBS_FILES_H__ + +#include +#include +#include +#include + +// As of 2013/11, too many environments still don't support . +// Since the only thing from this header libhpfiles uses (for now) is char16_t: +// * on modern GCC and Clang (the main targets of this code base), use the builtin __CHAR16_TYPE__ preprocessor define; +// * otherwise, fall back to uint16_t. +#ifdef __CHAR16_TYPE__ +typedef __CHAR16_TYPE__ char16_t; +#else +typedef uint16_t char16_t; +#endif + +#include "hplibs.h" +#include "filetypes.h" + +//! Maximum length of a variable name for the calculators supported by libhpfiles. +// Not sure the target calculators even support such long filenames... +#define FILES_VARNAME_MAXLEN 128 + +//! Generic structure for storing the contents of a variable, or requesting that a variable be sent. +typedef struct +{ + char16_t name[FILES_VARNAME_MAXLEN+1]; + + uint8_t type; + uint8_t model; + uint8_t invalid; ///< Set to nonzero by e.g. hpcalcs_calc_recv_file() if a packet loss was detected. + uint32_t size; + uint8_t* data; +} files_var_entry; + + +//! Structure passed to \a hpfiles_init, contains e.g. callbacks for logging and memory allocation. +typedef struct { + unsigned int version; ///< Config version number. + void (*log_callback)(const char *format, va_list args); ///< Callback function for receiving logging output. + hplibs_malloc_funcs * alloc_funcs; ///< Function pointers used for dynamic memory allocation. If NULL, the library defaults to malloc(), calloc(), realloc(), free(). +} hpfiles_config; + +//! Latest revision of the \a hpfiles_config struct layout supported by this version of the library. +#define HPFILES_CONFIG_VERSION (1) + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Initializes library internals. Must be called before any other libhpfiles function. + * \param config pointer to struct containing e.g. callbacks passed to the library. + * \return Whether the initialization succeeded. + * \note the contents of alloc_funcs are copied. + * \todo return instance count instead. + **/ +HPEXPORT int HPCALL hpfiles_init(hpfiles_config * config); +/** + * \brief Tears down library internals. No other libhpfiles function can be called after this one. + * \return Whether the teardown succeeded. + * \todo return instance count instead. + **/ +HPEXPORT int HPCALL hpfiles_exit(void); + +/** + * \brief Returns the library version string. + * \return The library version string, usually under the form "X.Y.Z". + **/ +HPEXPORT const char* HPCALL hpfiles_version_get(void); + +/** + * \brief Gets the error message if the error was produced by this library + * \param number the error number (from internal error.h) + * \param message out pointer for a newly allocated text error message, which must be freed by the caller + * \note the string is allocated with malloc(), therefore it must be freed with free(). + * \return 0 if the error was produced by this library, otherwise the error number (for propagation). + **/ +HPEXPORT int HPCALL hpfiles_error_get(int number, char **message); + +/** + * \brief Sets the callback function used by the library for logging + * \param log_callback function pointer + */ +HPEXPORT void HPCALL hpfiles_log_set_callback(void (*log_callback)(const char *format, va_list args)); +/** + * \brief Sets the log level of the library, for controlling how much logging output is produced. + * \param log_level log level (from hplibs.h) + * \return the previous log level + */ +HPEXPORT hplibs_logging_level HPCALL hpfiles_log_set_level(hplibs_logging_level log_level); + + +/** + * \brief Creates an empty files_var_entry structure. + * \return Pointer to files_var_entry, NULL if failed. + */ +HPEXPORT files_var_entry * HPCALL hpfiles_ve_create(void); +/** + * \brief Creates and preallocates a files_var_entry structure. + * \param size the size to be allocated. + * \return Pointer to files_var_entry, NULL if failed. + */ +HPEXPORT files_var_entry * HPCALL hpfiles_ve_create_with_size(uint32_t size); +/** + * \brief Creates and fills a files_var_entry structure with the given data. + * \param data the data to be duplicated. + * \param size the size of the data. + * \return Pointer to files_var_entry, NULL if failed. + */ +HPEXPORT files_var_entry * HPCALL hpfiles_ve_create_with_data(uint8_t * data, uint32_t size); +/** + * \brief Creates and fills a files_var_entry structure with the given preallocated data. + * \param data the data to be attached to the files_var_entry (assumed to be allocated with the same memory allocator as the one given to libhpfiles, if not using the default one). + * \param size the size of the data. + * \return Pointer to files_var_entry, NULL if failed. + */ +HPEXPORT files_var_entry * HPCALL hpfiles_ve_create_with_data_ptr(uint8_t * data, uint32_t size); +/** + * \brief Creates and fills a files_var_entry structure with the given data. + * \param data the data to be copied + * \param size the size of the data. + * \param name the file name on the calculator side. + * \return Pointer to files_var_entry, NULL if failed. + */ +HPEXPORT files_var_entry * HPCALL hpfiles_ve_create_with_data_and_name(uint8_t * data, uint32_t size, const char16_t * name); +/** + * \brief Creates and fills a files_var_entry structure from the given file, if it exists. + * \param file the FILE pointer to be used for filling in a files_var_entry instance. + * \param filename the UTF-16LE name of the file on the calculator side, can be NULL if you want to set it later. + * \return Pointer to files_var_entry, NULL if failed. + */ +HPEXPORT files_var_entry * HPCALL hpfiles_ve_create_from_file(FILE * file, const char16_t * filename); +/** + * \brief Destroys the given files_var_entry instance (embedded data + the entry itself). + * \param entry the entry + */ +HPEXPORT void HPCALL hpfiles_ve_delete(files_var_entry * entry); + +/** + * \brief Allocates data for the \a data field of files_var_entry. + * \param size the size to be allocated + * \return Pointer to allocated data, NULL if failed. + */ +HPEXPORT void * HPCALL hpfiles_ve_alloc_data(uint32_t size); +/** + * \brief Copies files_var_entry and its content (if any) from pre-existing src to pre-existing dst. + * \param dst destination entry. + * \param src source entry. + * \return dst, NULL if failed. + **/ +HPEXPORT files_var_entry * HPCALL hpfiles_ve_copy(files_var_entry * dst, files_var_entry * src); +/** + * \brief Duplicates files_var_entry and its content (if any) by creating a new instance. + * \param src source entry + * \return The new entry, NULL if failed. + **/ +HPEXPORT files_var_entry * HPCALL hpfiles_ve_dup(files_var_entry * src); + +/** + * \brief Shows basic information about a variable entry + * \param entry the entry to be dumped. + * \return 0 if the handle was non-NULL, nonzero otherwise. + **/ +HPEXPORT int hpfiles_ve_display(files_var_entry * entry); + +/** + * \brief Creates a NULL-terminated array for storing the given number of potentially non-NULL files_var_entry pointers. + * \param element_count the number of pointers + * \return The new array containing element_count + 1 NULL pointers, NULL if failed. + **/ +HPEXPORT files_var_entry ** HPCALL hpfiles_ve_create_array(uint32_t element_count); +/** + * \brief Reallocates a NULL-terminated array for storing a new number of potentially non-NULL files_var_entry pointers. + * \param entries the array of entries. + * \param element_count the new number of pointers + * \return The new array containing element_count + 1 pointers, NULL if failed. + * \note If the array was enlarged, the new storage space isn't cleared. + **/ +HPEXPORT files_var_entry ** HPCALL hpfiles_ve_resize_array(files_var_entry ** entries, uint32_t element_count); +/** + * \brief Destroys the whole array of files_var_entry structs (including the structures themselves and their embedded data). + * \param entries the array to be destroyed. + **/ +HPEXPORT void HPCALL hpfiles_ve_delete_array(files_var_entry ** entries); + + +/** + * \brief Converts a calculator model to a printable string. + * \param model the calculator model. + * \return the string corresponding to the calculator model. + **/ +HPEXPORT const char * HPCALL hpfiles_model_to_string(calc_model model); +/** + * \brief Converts a string to a supported calculator model, if possible. + * \param str the string. + * \return the calculator model corresponding to the string, CALC_NONE if failed. + **/ +HPEXPORT calc_model HPCALL hpfiles_string_to_model(const char *str); + +/** + * \brief Converts a file type ID to a printable file type string, according to the given calculator model. + * \param model the calculator model + * \param type the file type ID + * \return the file type string corresponding to the given type ID, if any. + */ +HPEXPORT const char * HPCALL hpfiles_vartype2str(calc_model model, uint8_t type); +/** + * \brief Converts a file type (under string form) to a file type ID, according to the given calculator model. + * \param model the calculator model + * \param type the file type string + * \return the file type ID corresponding to the given type string, if any. + */ +HPEXPORT uint8_t HPCALL hpfiles_str2vartype(calc_model model, const char * type); +/** + * \brief Converts a file type to an extension, according to the given calculator model. + * \param model the calculator model + * \param type the file type ID + * \return the file extension corresponding to the given type, if any. + * \note may have to use char16_t instead of char... + */ +HPEXPORT const char * HPCALL hpfiles_vartype2fext(calc_model model, uint8_t type); +/** + * \brief Converts a file extension to a file type ID, according to the given calculator model. + * \param model the calculator model + * \param type the file type as string. + * \return the file type ID corresponding to the given extension, if any. + */ +HPEXPORT uint8_t HPCALL hpfiles_fext2vartype(calc_model model, const char * type); +/** + * \brief Converts a file path to a file type ID, according to the given calculator model. + * \param model the calculator model + * \param filepath the file path as string. + * \return the file type ID corresponding to the given file, if any. + */ +HPEXPORT uint8_t HPCALL hpfiles_filename2vartype(calc_model model, const char * filepath); +/** + * \brief Interprets a file path to a file type ID + calculator-side file name, according to the given calculator model. + * \param model the calculator model + * \param filepath the file path as string. + * \param out_type storage space for the file type ID corresponding to the given file, if any. + * \param out_calcfilename a dynamically allocated string containing the calculator-side filename (usually stripped from the computer-side file extension). + * \return 0 if parsing succeeded, nonzero otherwise. + * \note may have to use char16_t instead of char... + * \note the string is allocated with malloc(), therefore it must be freed with free(). + */ +HPEXPORT int HPCALL hpfiles_parsefilename(calc_model model, const char * filepath, uint8_t * out_type, char ** out_calcfilename); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libhpcalcs/src/hplibs.h b/libhpcalcs/src/hplibs.h new file mode 100644 index 0000000..254d66c --- /dev/null +++ b/libhpcalcs/src/hplibs.h @@ -0,0 +1,93 @@ +/* + * libhpfiles, libhpcables, libhpcalcs: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file hplibs.h Files, Cables, Calcs: definitions common to libhpfiles, libhpcables, libhpcalcs. + */ + +#ifndef __HPLIBS_H__ +#define __HPLIBS_H__ + +#include + +#include "export.h" + +//! Enumeration of cable types. +typedef enum { + CABLE_NUL = 0, + CABLE_PRIME_HID, + CABLE_MAX +} cable_model; + +//! Enumeration of calculator types. +typedef enum { + CALC_NONE = 0, + CALC_PRIME, + CALC_MAX +} calc_model; + +//! Enumeration of logging levels. +typedef enum { + LOG_LEVEL_ALL = 0, + LOG_LEVEL_DEBUG, + LOG_LEVEL_INFO, + LOG_LEVEL_WARN, + LOG_LEVEL_ERROR +} hplibs_logging_level; + +//! Struct containing function pointers used by the libraries for dynamic memory allocation. +typedef struct { + void * (*malloc)(size_t size); ///< A malloc()-compatible function + void * (*calloc) (size_t nmemb, size_t size); ///< A calloc()-compatible function + void * (*realloc)(void *ptr, size_t size); ///< A realloc()-compatible function + void (*free) (void *ptr); ///< A free()-compatible function +} hplibs_malloc_funcs; + +//! USB Vendor ID of Hewlett-Packard. +#define USB_VID_HP (0x03F0) +//! USB Product ID of the Prime calculator in firmware revisions < 8151. +#define USB_PID_PRIME1 (0x0441) +//! USB Product ID of the Prime calculator in firmware revisions >= 8151. +#define USB_PID_PRIME2 (0x1541) + +//! Size of a raw HID packet for the Prime. +#define PRIME_RAW_HID_DATA_SIZE (64) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Gets the error message if the error was produced by a libhp* library + * \param number the error number (from above) + * \param message out pointer for a newly allocated text error message, which must be freed by the caller + * + * \return the error number. + **/ +HPEXPORT int HPCALL hplibs_error_get(int number, char **message); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libhpcalcs/src/hpopers.c b/libhpcalcs/src/hpopers.c new file mode 100644 index 0000000..19d2fd2 --- /dev/null +++ b/libhpcalcs/src/hpopers.c @@ -0,0 +1,126 @@ +/* + * libhpopers: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file hpfiles.c Higher-level operations: base part. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#include +#include + +#include +#include "internal.h" +#include "logging.h" +#include "error.h" +#include "gettext.h" + +hplibs_malloc_funcs hpopers_alloc_funcs = { + .malloc = malloc, + .calloc = calloc, + .realloc = realloc, + .free = free +}; + + +// not static, must be shared between instances +int hpopers_instance_count = 0; + +HPEXPORT int HPCALL hpopers_init(hpopers_config * config) { + int res = ERR_SUCCESS; + void (*log_callback)(const char *format, va_list args); + hplibs_malloc_funcs * alloc_funcs; + + if (config == NULL) { + log_callback = NULL; + alloc_funcs = NULL; + } + else { + if (config->version <= HPOPERS_CONFIG_VERSION) { + if (config->version == 1) { + log_callback = config->log_callback; + alloc_funcs = config->alloc_funcs; + } + else { + hpcables_error(_("%s: unsupported config version %u"), __FUNCTION__, config->version); + res = ERR_LIBRARY_CONFIG_VERSION; + } + } + else { + hpcables_error(_("%s: unsupported config version %u"), __FUNCTION__, config->version); + res = ERR_LIBRARY_CONFIG_VERSION; + } + } + + if (!res) { + // TODO: when (if) libhpopers is split from libhpcalcs, copy and adjust locale setting code from hpfiles.c. + + if (!hpopers_instance_count) { + hpopers_log_set_callback(log_callback); + if (alloc_funcs != NULL) { + hpopers_alloc_funcs = *alloc_funcs; + } + hpopers_info(_("hpopers library version %s"), hpopers_version_get()); + + hpopers_info(_("%s: init succeeded"), __FUNCTION__); + hpopers_instance_count++; + } + else { + hpopers_info(_("%s: re-init skipped"), __FUNCTION__); + hpopers_instance_count++; + } + } + + return res; +} + +HPEXPORT int HPCALL hpopers_exit(void) { + int res; + + if (hpopers_instance_count <= 0) { + hpopers_error(_("%s: more exits than inits"), __FUNCTION__); + res = ERR_LIBRARY_EXIT; + } + else { + hpopers_instance_count--; + + hpopers_info(_("%s: exit succeeded"), __FUNCTION__); + res = ERR_SUCCESS; + } + + return res; +} + + +HPEXPORT const char* HPCALL hpopers_version_get (void) { + return VERSION; +} + diff --git a/libhpcalcs/src/hpopers.h b/libhpcalcs/src/hpopers.h new file mode 100644 index 0000000..f82e717 --- /dev/null +++ b/libhpcalcs/src/hpopers.h @@ -0,0 +1,180 @@ +/* + * libhpopers: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file hpopers.h Higher-level operations: base part. + */ + +#ifndef __HPLIBS_OPERS_H__ +#define __HPLIBS_OPERS_H__ + +#include +#include +#include + +#include "hplibs.h" +#include "hpfiles.h" +#include "hpcables.h" +#include "hpcalcs.h" + + +//! Structure passed to \a hpopers_init, contains e.g. callbacks for logging and memory allocation. +typedef struct { + unsigned int version; ///< Config version number. + void (*log_callback)(const char *format, va_list args); ///< Callback function for receiving logging output. + hplibs_malloc_funcs * alloc_funcs; ///< Function pointers used for dynamic memory allocation. If NULL, the library defaults to malloc(), calloc(), realloc(), free(). +} hpopers_config; + +//! Latest revision of the \a hpopers_config struct layout supported by this version of the library. +#define HPOPERS_CONFIG_VERSION (1) + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Initializes library internals. Must be called before any other libhpopers function. + * \param config pointer to struct containing e.g. callbacks passed to the library. + * \return Whether the initialization succeeded. + * \note the contents of alloc_funcs are copied. + * \todo return instance count instead. + **/ +HPEXPORT int HPCALL hpopers_init(hpopers_config * config); +/** + * \brief Tears down library internals. No other libhpopers function can be called after this one. + * \return Whether the teardown succeeded. + * \todo return instance count instead. + **/ +HPEXPORT int HPCALL hpopers_exit(void); + +/** + * \brief Returns the library version string. + * \return The library version string, usually under the form "X.Y.Z". + **/ +HPEXPORT const char* HPCALL hpopers_version_get(void); + +/** + * \brief Gets the error message if the error was produced by this library + * \param number the error number (from internal error.h) + * \param message out pointer for a newly allocated text error message, which must be freed by the caller + * \note the string is allocated with malloc(), therefore it must be freed with free(). + * \return 0 if the error was produced by this library, otherwise the error number (for propagation). + **/ +HPEXPORT int HPCALL hpopers_error_get(int number, char **message); + +/** + * \brief Sets the callback function used by the library for logging + * \param log_callback function pointer + */ +HPEXPORT void HPCALL hpopers_log_set_callback(void (*log_callback)(const char *format, va_list args)); +/** + * \brief Sets the log level of the library, for controlling how much logging output is produced. + * \param log_level log level (from hplibs.h) + * \return the previous log level + */ +HPEXPORT hplibs_logging_level HPCALL hpopers_log_set_level(hplibs_logging_level log_level); + + +// Tentative APIs, may change in the near future. + +/** + * \brief Retrieves a screenshot from the calculator, without any form of conversion + * \param handle the calculator handle. + * \param format the desired screenshot format. + * \param out_file struct FILE pointer for storing the output. + * \return 0 upon success, nonzero otherwise. + * \note This shall be a wrapper over \a hpcalcs_calc_recv_screen . + */ +HPEXPORT int HPCALL hpopers_oper_recv_screen_verbatim_to_file(calc_handle * handle, calc_screenshot_format format, FILE * out_file); +/** + * \brief Retrieves a screenshot from the calculator, and convert it to R8G8B8 before recompressing it as PNG. + * \param handle the calculator handle. + * \param format the desired screenshot format. + * \param out_data storage area for converted R8G8B8 screenshot. + * \param out_size storage area for size of the R8G8B8 screenshot contained in the calculator's reply. + * \return 0 upon success, nonzero otherwise. + * \note This shall be a wrapper over \a hpcalcs_calc_recv_screen and \a hpopers_oper_convert_raw_screen_to_png_r8g8b8. + */ +HPEXPORT int HPCALL hpopers_oper_recv_screen_png_r8g8b8(calc_handle * handle, calc_screenshot_format format, uint8_t ** out_data, uint32_t * out_size); +/** + * \brief Retrieves a screenshot from the calculator, and convert it to R8G8B8 before recompressing it as PNG and writing it to a file. + * \param handle the calculator handle. + * \param format the desired screenshot format. + * \param out_file struct FILE pointer for storing the output. + * \return 0 upon success, nonzero otherwise. + * \note This shall be a wrapper over \a hpopers_oper_recv_screen_png_r8g8b8 . + */ +HPEXPORT int HPCALL hpopers_oper_recv_screen_png_r8g8b8_to_file(calc_handle * handle, calc_screenshot_format format, FILE * out_file); +/** + * \brief Converts a raw screenshot (output by e.g. \a hpcalcs_calc_recv_screen) to a more usual PNG R8G8B8 format. + * \param in_data storage area for raw screenshot. + * \param in_size storage area for size of the raw screenshot. + * \param format the desired screenshot format. + * \param out_data storage area for R8G8B8 screenshot. + * \param out_size storage area for size of the R8G8B8 screenshot. + * \return 0 upon success, nonzero otherwise. + */ +HPEXPORT int HPCALL hpopers_oper_convert_raw_screen_to_png_r8g8b8(uint8_t * in_data, uint32_t in_size, calc_screenshot_format format, uint8_t ** out_data, uint32_t * out_size); +/** + * \brief Sends a file to the calculator. + * \param handle the calculator handle. + * \param file struct FILE pointer containing the contents to be sent. + * \param filename struct FILE pointer containing the contents to be sent. + * \return 0 upon success, nonzero otherwise. + * \note This shall be a wrapper over \a hpcalcs_calc_send_file . + */ +HPEXPORT int HPCALL hpopers_calc_send_file(calc_handle * handle, FILE * file, const char16_t * filename); +/** + * \brief Receives a file from the calculator. + * \param handle the calculator handle. + * \param calculator_filename name (on the calculator side) of the file to be received. + * \param file struct FILE pointer used for writing data. + * \return 0 upon success, nonzero otherwise. + * \note This shall be a wrapper over \a hpcalcs_calc_recv_file . + */ +HPEXPORT int HPCALL hpopers_calc_recv_file(calc_handle * handle, const char16_t * calculator_filename, FILE * file); +/** + * \brief Receives a backup (made of multiple files) from the calculator to a folder. + * \param handle the calculator handle. + * \param out_path name of the folder used as root for the files hierarchy. + * \return 0 upon success, nonzero otherwise. + * \note This shall be a wrapper over \a hpcalcs_calc_recv_backup . + */ +HPEXPORT int HPCALL hpopers_calc_recv_backup(calc_handle * handle, const char * out_path); +/** + * \brief Sends a backup (made of multiple files) from a folder to the calculator. + * \param handle the calculator handle. + * \param in_path name of the folder used as root for the files hierarchy. + * \return 0 upon success, nonzero otherwise. + * \note This shall be a wrapper over \a hpcalcs_calc_send_file (called in a loop) . + */ +HPEXPORT int HPCALL hpopers_calc_send_backup(calc_handle * handle, const char * in_path); + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/libhpcalcs/src/internal.h b/libhpcalcs/src/internal.h new file mode 100644 index 0000000..c64fa2f --- /dev/null +++ b/libhpcalcs/src/internal.h @@ -0,0 +1,32 @@ +/* + * libhpfiles, libhpcables, libhpcalcs, libhpopers: hand-helds support libraries. + * Copyright (C) 2015 Lionel Debroux + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file internal.h Files, Cables, Calcs, Opers: internal definitions for libhpfiles, libhpcables, libhpcalcs, libhpopers. + */ + +#ifndef __HPLIBS_INTERNAL_H__ +#define __HPLIBS_INTERNAL_H__ + +extern hplibs_malloc_funcs hpfiles_alloc_funcs; +extern hplibs_malloc_funcs hpcables_alloc_funcs; +extern hplibs_malloc_funcs hpcalcs_alloc_funcs; +extern hplibs_malloc_funcs hpopers_alloc_funcs; + +#endif diff --git a/libhpcalcs/src/link_nul.c b/libhpcalcs/src/link_nul.c new file mode 100644 index 0000000..7acf330 --- /dev/null +++ b/libhpcalcs/src/link_nul.c @@ -0,0 +1,78 @@ +/* + * libhpcables: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file link_nul.c Cables: Null cable. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +static int cable_nul_probe(cable_handle * handle) { + return 0; +} + +static int cable_nul_open(cable_handle * handle) { + handle->model = CABLE_NUL; + handle->handle = NULL; + handle->fncts = NULL; + handle->read_timeout = 0; + handle->open = 0; + handle->busy = 0; + return 0; +} + +static int cable_nul_close(cable_handle * handle) { + return 0; +} + + +static int cable_nul_set_read_timeout(cable_handle * handle, int read_timeout) { + return 0; +} + + +static int cable_nul_send(cable_handle * handle, uint8_t * data, uint32_t len) { + return 0; +} + +static int cable_nul_recv(cable_handle * handle, uint8_t ** data, uint32_t * len) { + return 0; +} + +const cable_fncts cable_nul_fncts = +{ + CABLE_NUL, + "Dummy cable", + "Dummy cable used when no cable is set", + &cable_nul_probe, + &cable_nul_open, + &cable_nul_close, + &cable_nul_set_read_timeout, + &cable_nul_send, + &cable_nul_recv +}; diff --git a/libhpcalcs/src/link_prime_hid.c b/libhpcalcs/src/link_prime_hid.c new file mode 100644 index 0000000..6815def --- /dev/null +++ b/libhpcalcs/src/link_prime_hid.c @@ -0,0 +1,240 @@ +/* + * libhpcables: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file link_prime_hid.c Cables: Prime HID cable. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include + +#include +#include +#include "logging.h" +#include "error.h" + +extern const cable_fncts cable_prime_hid_fncts; + +static int cable_prime_hid_probe(cable_handle * handle) { + int res; + // In fact, we're not using handle here, but let's nevertheless flag misuse of the API. + if (handle != NULL) { + // Enumerating the device seems to do the job. + struct hid_device_info * info = hid_enumerate(USB_VID_HP, USB_PID_PRIME1); + if (info != NULL) { + hid_free_enumeration(info); + res = ERR_SUCCESS; + hpcables_info("%s: cable probe succeeded, PID=%04X", __FUNCTION__, USB_PID_PRIME1); + } + else { + info = hid_enumerate(USB_VID_HP, USB_PID_PRIME2); + if (info != NULL) { + hid_free_enumeration(info); + res = ERR_SUCCESS; + hpcables_info("%s: cable probe succeeded, PID=%04X", __FUNCTION__, USB_PID_PRIME2); + } + else { + res = ERR_CABLE_PROBE_FAILED; + hpcables_error("%s: cable probe failed", __FUNCTION__); + } + } + } + else { + res = ERR_INVALID_HANDLE; + hpcables_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +static int cable_prime_hid_open(cable_handle * handle) { + int res; + if (handle != NULL) { + hid_device * device_handle = hid_open(USB_VID_HP, USB_PID_PRIME1, NULL); + unsigned int pid = USB_PID_PRIME1; + if (device_handle) { +device_handle_ok: + handle->model = CABLE_PRIME_HID; + handle->handle = (void *)device_handle; + handle->fncts = &cable_prime_hid_fncts; + // Especially screenshots can take a while before beginning to send data. + handle->read_timeout = 8000; + handle->open = 1; + handle->busy = 0; + res = ERR_SUCCESS; + hpcables_info("%s: cable open succeeded, PID=%04X", __FUNCTION__, pid); + } + else { + device_handle = hid_open(USB_VID_HP, USB_PID_PRIME2, NULL); + pid = USB_PID_PRIME2; + if (device_handle) { + goto device_handle_ok; + } + else { + res = ERR_CABLE_NOT_OPEN; + hpcables_error("%s: cable open failed", __FUNCTION__); + } + } + } + else { + res = ERR_INVALID_HANDLE; + hpcables_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +static int cable_prime_hid_close(cable_handle * handle) { + int res; + if (handle != NULL) { + hid_device * device_handle = (hid_device *)handle->handle; + if (device_handle != NULL) { + if (handle->open) { + hid_close(device_handle); + handle->model = CABLE_NUL; + handle->handle = NULL; + handle->fncts = NULL; + handle->open = 0; + res = ERR_SUCCESS; + hpcables_info("%s: cable close succeeded", __FUNCTION__); + } + else { + res = ERR_CABLE_NOT_OPEN; + hpcables_error("%s: cable was not open", __FUNCTION__); + } + } + else { + res = ERR_INVALID_HANDLE; + hpcables_error("%s: device_handle is NULL", __FUNCTION__); + } + } + else { + res = ERR_INVALID_HANDLE; + hpcables_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +static int cable_prime_hid_set_read_timeout(cable_handle * handle, int read_timeout) { + int res; + if (handle != NULL) { + res = ERR_SUCCESS; + handle->read_timeout = read_timeout; + } + else { + res = ERR_INVALID_HANDLE; + hpcables_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + + +static int cable_prime_hid_send(cable_handle * handle, uint8_t * data, uint32_t len) { + int res; + if (handle != NULL && data != NULL) { + hid_device * device_handle = (hid_device *)handle->handle; + if (device_handle != NULL) { + uint32_t bytes_written = 0; + while (bytes_written < len) { + if (handle->open) { + res = hid_write(device_handle, data + bytes_written, len - bytes_written); + if (res >= 0) { + bytes_written += res; + } + else { + res = ERR_CABLE_WRITE_ERROR; + hpcables_error("%s: write failed %ls", __FUNCTION__, hid_error(device_handle)); + return res; + } + } + else { + res = ERR_CABLE_NOT_OPEN; + hpcables_error("%s: cable was not open", __FUNCTION__); + return res; + } + } + res = ERR_SUCCESS; + hpcables_info("%s: wrote %d bytes", __FUNCTION__, bytes_written); + } + else { + res = ERR_INVALID_HANDLE; + hpcables_error("%s: device_handle is NULL", __FUNCTION__); + } + } + else { + res = ERR_INVALID_PARAMETER; + hpcables_error("%s: an argument is NULL", __FUNCTION__); + } + return res; +} + +static int cable_prime_hid_recv(cable_handle * handle, uint8_t ** data, uint32_t * len) { + int res; + // Use pre-allocated area pointed to by data. + if (handle != NULL && data != NULL && *data != NULL && len != NULL) { + hid_device * device_handle = (hid_device *)handle->handle; + if (device_handle != NULL) { + if (handle->open) { + res = hid_read_timeout(device_handle, *data, PRIME_RAW_HID_DATA_SIZE, handle->read_timeout); + if (res >= 0) { + *len = res; + res = ERR_SUCCESS; + hpcables_info("%s: read %" PRIu32 "bytes", __FUNCTION__, *len); + } + else { + res = ERR_CABLE_READ_ERROR; + hpcables_error("%s: read failed", __FUNCTION__); + } + } + else { + res = ERR_CABLE_NOT_OPEN; + hpcables_error("%s: cable was not open", __FUNCTION__); + } + } + else { + res = ERR_INVALID_HANDLE; + hpcables_error("%s: device_handle is NULL", __FUNCTION__); + } + } + else { + res = ERR_INVALID_PARAMETER; + hpcables_error("%s: an argument is NULL", __FUNCTION__); + } + return res; +} + +const cable_fncts cable_prime_hid_fncts = +{ + CABLE_PRIME_HID, + "Prime HID cable", + "Prime HID cable", + &cable_prime_hid_probe, + &cable_prime_hid_open, + &cable_prime_hid_close, + &cable_prime_hid_set_read_timeout, + &cable_prime_hid_send, + &cable_prime_hid_recv +}; diff --git a/libhpcalcs/src/logging.c b/libhpcalcs/src/logging.c new file mode 100644 index 0000000..5d49261 --- /dev/null +++ b/libhpcalcs/src/logging.c @@ -0,0 +1,179 @@ +/* + * libhpfiles, libhpcables, libhpcalcs: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file logging.c Logging primitives + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include "logging.h" + +#include +#include +#include + +#ifdef __GNUC__ +#ifndef alloca +#define alloca(size) __builtin_alloca(size) +#endif +#endif + +void (*hpfiles_log_callback)(const char *format, va_list args); +hplibs_logging_level hpfiles_log_level = LOG_LEVEL_ALL; + +void (*hpcables_log_callback)(const char *format, va_list args); +hplibs_logging_level hpcables_log_level = LOG_LEVEL_ALL; + +void (*hpcalcs_log_callback)(const char *format, va_list args); +hplibs_logging_level hpcalcs_log_level = LOG_LEVEL_ALL; + +void (*hpopers_log_callback)(const char *format, va_list args); +hplibs_logging_level hpopers_log_level = LOG_LEVEL_ALL; + + +#define DEBUG_FUNC_BODY(lib, level) \ + if (lib##_log_callback != NULL && lib##_log_level <= LOG_LEVEL_##level) { \ + va_list args; \ + char * format2; \ + va_start (args, format); \ + format2 = (char *)alloca(strlen(format) + sizeof(#lib " " #level ": ") + 10); \ + sprintf(format2, #lib " " #level ": %s\n", format); \ + (*lib##_log_callback)(format2, args); \ + } + +HPEXPORT void HPCALL hpfiles_log_set_callback(void (*log_callback)(const char *format, va_list args)) { + hpfiles_log_callback = log_callback; +} + +HPEXPORT hplibs_logging_level HPCALL hpfiles_log_set_level(hplibs_logging_level log_level) { + hplibs_logging_level ret = hpfiles_log_level; + hpfiles_log_level = log_level; + return ret; +} + +void hpfiles_debug (const char *format, ...) { + DEBUG_FUNC_BODY(hpfiles, DEBUG) +} + +void hpfiles_info (const char *format, ...) { + DEBUG_FUNC_BODY(hpfiles, INFO) +} + +void hpfiles_warning (const char *format, ...) { + DEBUG_FUNC_BODY(hpfiles, WARN) +} + +void hpfiles_error (const char *format, ...) { + DEBUG_FUNC_BODY(hpfiles, ERROR) +} + + + +HPEXPORT void HPCALL hpcables_log_set_callback(void (*log_callback)(const char *format, va_list args)) { + hpcables_log_callback = log_callback; +} + +HPEXPORT hplibs_logging_level HPCALL hpcables_log_set_level(hplibs_logging_level log_level) { + hplibs_logging_level ret = hpcables_log_level; + hpcables_log_level = log_level; + return ret; +} + +void hpcables_debug (const char *format, ...) { + DEBUG_FUNC_BODY(hpcables, DEBUG) +} + +void hpcables_info (const char *format, ...) { + DEBUG_FUNC_BODY(hpcables, INFO) +} + +void hpcables_warning (const char *format, ...) { + DEBUG_FUNC_BODY(hpcables, WARN) +} + +void hpcables_error (const char *format, ...) { + DEBUG_FUNC_BODY(hpcables, ERROR) +} + + + +HPEXPORT void HPCALL hpcalcs_log_set_callback(void (*log_callback)(const char *format, va_list args)) { + hpcalcs_log_callback = log_callback; +} + +HPEXPORT hplibs_logging_level HPCALL hpcalcs_log_set_level(hplibs_logging_level log_level) { + hplibs_logging_level ret = hpcalcs_log_level; + hpcalcs_log_level = log_level; + return ret; +} + +void hpcalcs_debug (const char *format, ...) { + DEBUG_FUNC_BODY(hpcalcs, DEBUG) +} + +void hpcalcs_info (const char *format, ...) { + DEBUG_FUNC_BODY(hpcalcs, INFO) +} + +void hpcalcs_warning (const char *format, ...) { + DEBUG_FUNC_BODY(hpcalcs, WARN) +} + +void hpcalcs_error (const char *format, ...) { + DEBUG_FUNC_BODY(hpcalcs, ERROR) +} + + + +HPEXPORT void HPCALL hpopers_log_set_callback(void (*log_callback)(const char *format, va_list args)) { + hpopers_log_callback = log_callback; +} + +HPEXPORT hplibs_logging_level HPCALL hpopers_log_set_level(hplibs_logging_level log_level) { + hplibs_logging_level ret = hpopers_log_level; + hpopers_log_level = log_level; + return ret; +} + +void hpopers_debug (const char *format, ...) { + DEBUG_FUNC_BODY(hpopers, DEBUG) +} + +void hpopers_info (const char *format, ...) { + DEBUG_FUNC_BODY(hpopers, INFO) +} + +void hpopers_warning (const char *format, ...) { + DEBUG_FUNC_BODY(hpopers, WARN) +} + +void hpopers_error (const char *format, ...) { + DEBUG_FUNC_BODY(hpopers, ERROR) +} diff --git a/libhpcalcs/src/logging.h b/libhpcalcs/src/logging.h new file mode 100644 index 0000000..f7ba2c5 --- /dev/null +++ b/libhpcalcs/src/logging.h @@ -0,0 +1,56 @@ +/* + * libhpfiles, libhpcables, libhpcalcs: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file logging.h Logging primitives. + */ + +#ifndef __HPLIBS_LOGGING_H__ +#define __HPLIBS_LOGGING_H__ + +#include + +void hpfiles_debug (const char *format, ...); +void hpfiles_info (const char *format, ...); +void hpfiles_warning (const char *format, ...); +void hpfiles_error (const char *format, ...); + + +void hpcables_debug (const char *format, ...); +void hpcables_info (const char *format, ...); +void hpcables_warning (const char *format, ...); +void hpcables_error (const char *format, ...); + + +void hpcalcs_debug (const char *format, ...); +void hpcalcs_info (const char *format, ...); +void hpcalcs_warning (const char *format, ...); +void hpcalcs_error (const char *format, ...); + + +void hpopers_debug (const char *format, ...); +void hpopers_info (const char *format, ...); +void hpopers_warning (const char *format, ...); +void hpopers_error (const char *format, ...); + +#endif diff --git a/libhpcalcs/src/prime_cmd.c b/libhpcalcs/src/prime_cmd.c new file mode 100644 index 0000000..2d61aa2 --- /dev/null +++ b/libhpcalcs/src/prime_cmd.c @@ -0,0 +1,787 @@ +/* + * libhpcalcs: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file prime_cmd.c Calcs: Prime commands + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include "prime_cmd.h" +#include "logging.h" +#include "error.h" +#include "utils.h" + +#include +#include +#include +#include + +static inline uint16_t crc16_block(const uint8_t * buffer, uint32_t len) { + static const uint16_t ccitt_crc16_table[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 + }; + uint16_t crc = 0; + + while (len--) { + crc = ccitt_crc16_table[(crc >> 8) ^ *buffer++] ^ (crc << 8); + } + return crc; +} + +static int read_vtl_pkt(calc_handle * handle, uint8_t cmd, prime_vtl_pkt ** pkt, int packet_contains_header) { + int res; + (void)packet_contains_header; + *pkt = prime_vtl_pkt_new(0); + if (*pkt != NULL) { + (*pkt)->cmd = cmd; + res = prime_recv_data(handle, *pkt); + if (res == ERR_SUCCESS) { + if ((*pkt)->size > 0) { + if ((*pkt)->data[0] == (*pkt)->cmd) { + hpcalcs_debug("%s: command matches returned data", __FUNCTION__); + } + else { + hpcalcs_debug("%s: command does not match returned data", __FUNCTION__); + // It's not necessarily an error. + } + } + else { + hpcalcs_info("%s: empty packet", __FUNCTION__); + } + } + else { + prime_vtl_pkt_del(*pkt); + *pkt = NULL; + } + } + else { + res = ERR_MALLOC; + hpcalcs_error("%s: couldn't create packet", __FUNCTION__); + } + return res; +} + +static int write_vtl_pkt(calc_handle * handle, prime_vtl_pkt * pkt) { + return prime_send_data(handle, pkt); +} + +HPEXPORT int HPCALL calc_prime_s_check_ready(calc_handle * handle) { + int res; + if (handle != NULL) { + prime_vtl_pkt * pkt = prime_vtl_pkt_new(1); + if (pkt != NULL) { + uint8_t * ptr; + + pkt->cmd = CMD_PRIME_CHECK_READY; + ptr = pkt->data; + *ptr++ = CMD_PRIME_CHECK_READY; + res = write_vtl_pkt(handle, pkt); + prime_vtl_pkt_del(pkt); + } + else { + res = ERR_MALLOC; + hpcalcs_error("%s: couldn't create packet", __FUNCTION__); + } + } + else { + res = ERR_INVALID_HANDLE; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL calc_prime_r_check_ready(calc_handle * handle, uint8_t ** out_data, uint32_t * out_size) { + int res; + if (handle != NULL) { + prime_vtl_pkt * pkt; + res = read_vtl_pkt(handle, CMD_PRIME_CHECK_READY, &pkt, 0); + if (res == ERR_SUCCESS && pkt != NULL) { + if (out_data != NULL && out_size != NULL) { + *out_size = pkt->size; + *out_data = pkt->data; // Transfer ownership of the memory block to the caller. + pkt->data = NULL; // Detach it from virtual packet. + } + // else do nothing. res is already ERR_SUCCESS. + prime_vtl_pkt_del(pkt); + } + else { + hpcalcs_error("%s: failed to read packet", __FUNCTION__); + } + } + else { + res = ERR_INVALID_HANDLE; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL calc_prime_s_get_infos (calc_handle * handle) { + int res; + if (handle != NULL) { + prime_vtl_pkt * pkt = prime_vtl_pkt_new(1); + if (pkt != NULL) { + uint8_t * ptr; + + pkt->cmd = CMD_PRIME_GET_INFOS; + ptr = pkt->data; + *ptr++ = CMD_PRIME_GET_INFOS; + res = write_vtl_pkt(handle, pkt); + prime_vtl_pkt_del(pkt); + } + else { + res = ERR_MALLOC; + hpcalcs_error("%s: couldn't create packet", __FUNCTION__); + } + } + else { + res = ERR_INVALID_HANDLE; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL calc_prime_r_get_infos (calc_handle * handle, calc_infos * infos) { + int res; + if (handle != NULL) { + prime_vtl_pkt * pkt; + res = read_vtl_pkt(handle, CMD_PRIME_GET_INFOS, &pkt, 1); + if (res == ERR_SUCCESS && pkt != NULL) { + if (infos != NULL) { + infos->size = pkt->size; + infos->data = pkt->data; // Transfer ownership of the memory block to the caller. + pkt->data = NULL; // Detach it from virtual packet. + } + // else do nothing. res is already ERR_SUCCESS. + prime_vtl_pkt_del(pkt); + } + else { + hpcalcs_error("%s: failed to read packet", __FUNCTION__); + } + } + else { + res = ERR_INVALID_HANDLE; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL calc_prime_s_set_date_time(calc_handle * handle, time_t timestamp) { + int res; + if (handle != NULL) { + struct tm * brokendowntime = localtime(×tamp); + if (brokendowntime != NULL) { + uint32_t size = 10; // Size of the data after the header. + prime_vtl_pkt * pkt = prime_vtl_pkt_new(size + 6); // Add size of the header. + if (pkt != NULL) { + uint8_t * ptr; + + pkt->cmd = CMD_PRIME_SET_DATE_TIME; + ptr = pkt->data; + *ptr++ = CMD_PRIME_SET_DATE_TIME; + *ptr++ = 0x01; + *ptr++ = (uint8_t)((size >> 24) & 0xFF); + *ptr++ = (uint8_t)((size >> 16) & 0xFF); + *ptr++ = (uint8_t)((size >> 8) & 0xFF); + *ptr++ = (uint8_t)((size ) & 0xFF); + *ptr++ = 0x00; // ? + *ptr++ = 0x00; // ? + *ptr++ = 0x54; // 84 decimal, ? + *ptr++ = 0x1E; // 30 decimal, ? + *ptr++ = brokendowntime->tm_year - (2000 - 1900); // tm_year: The number of years since 1900. + *ptr++ = brokendowntime->tm_mon + 1; // tm_mon: the number of months since January, in the range 0 to 11. + *ptr++ = brokendowntime->tm_mday; + *ptr++ = brokendowntime->tm_hour; + *ptr++ = brokendowntime->tm_min; + *ptr++ = brokendowntime->tm_sec; + res = write_vtl_pkt(handle, pkt); + prime_vtl_pkt_del(pkt); + } + else { + res = ERR_MALLOC; + hpcalcs_error("%s: couldn't create packet", __FUNCTION__); + } + } + else { + res = ERR_CALC_SPLIT_TIMESTAMP; + hpcalcs_error("%s: couldn't split timestamp", __FUNCTION__); + } + } + else { + res = ERR_INVALID_HANDLE; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL calc_prime_r_set_date_time(calc_handle * handle) { + int res = 0; + if (handle != NULL) { + // There doesn't seem to be anything to do. + //res = calc_prime_r_check_ready(handle, NULL, NULL); + } + else { + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL calc_prime_s_recv_screen(calc_handle * handle, calc_screenshot_format format) { + int res; + if (handle != NULL) { + prime_vtl_pkt * pkt = prime_vtl_pkt_new(2); + if (pkt != NULL) { + uint8_t * ptr; + + pkt->cmd = CMD_PRIME_RECV_SCREEN; + ptr = pkt->data; + *ptr++ = CMD_PRIME_RECV_SCREEN; + *ptr++ = (uint8_t)format; + res = write_vtl_pkt(handle, pkt); + prime_vtl_pkt_del(pkt); + } + else { + res = ERR_MALLOC; + hpcalcs_error("%s: couldn't create packet", __FUNCTION__); + } + } + else { + res = ERR_INVALID_HANDLE; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL calc_prime_r_recv_screen(calc_handle * handle, calc_screenshot_format format, uint8_t ** out_data, uint32_t * out_size) { + int res; + if (handle != NULL) { + prime_vtl_pkt * pkt; + res = read_vtl_pkt(handle, CMD_PRIME_RECV_SCREEN, &pkt, 1); + if (res == ERR_SUCCESS && pkt != NULL) { + if (pkt->size > 13) { + // Packet has CRC + uint16_t computed_crc; // 0x0000 ? + uint8_t * ptr = pkt->data; + // For whatever reason the CRC seems to be encoded the other way around compared to receiving files + uint16_t embedded_crc = (((uint16_t)(ptr[6])) << 8) | ((uint16_t)(ptr[7])); + // Reset CRC before computing + ptr[6] = 0x00; + ptr[7] = 0x00; + computed_crc = crc16_block(ptr + 6, pkt->size - 6); // The CRC for *screenshots* skips the header, and includes all data. + hpcalcs_info("%s: embedded=%" PRIX16 " computed=%" PRIX16, __FUNCTION__, embedded_crc, computed_crc); + if (computed_crc != embedded_crc) { + res = ERR_CALC_PACKET_FORMAT; + hpcalcs_error("%s: CRC mismatch", __FUNCTION__); + } + + // Skip marker. + if (pkt->data[8] == (uint8_t)format && pkt->data[9] == 0xFF && pkt->data[10] == 0xFF && pkt->data[11] == 0xFF && pkt->data[12] == 0xFF) { + if (out_data != NULL && out_size != NULL) { + *out_size = pkt->size - 13; + memmove(pkt->data, pkt->data + 13, pkt->size - 13); + *out_data = pkt->data; // Transfer ownership of the memory block to the caller. + pkt->data = NULL; // Detach it from virtual packet. + } + // else do nothing. res is already ERR_SUCCESS. + } + else { + res = ERR_CALC_PACKET_FORMAT; + hpcalcs_warning("%s: unknown marker at beginning of image", __FUNCTION__); + } + } + else { + res = ERR_CALC_PACKET_FORMAT; + hpcalcs_info("%s: packet is too short: %" PRIu32 "bytes", __FUNCTION__, pkt->size); + } + prime_vtl_pkt_del(pkt); + } + else { + hpcalcs_error("%s: failed to read packet", __FUNCTION__); + } + } + else { + res = ERR_INVALID_HANDLE; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +// Seems to be made of a series of CMD_PRIME_RECV_FILE. +HPEXPORT int HPCALL calc_prime_s_send_file(calc_handle * handle, files_var_entry * file) { + int res; + if (handle != NULL && file != NULL) { + uint8_t namelen = (uint8_t)char16_strlen(file->name) * 2; + uint32_t size = 10 - 6 + namelen + file->size; // Size of the data after the header. + prime_vtl_pkt * pkt = prime_vtl_pkt_new(size + 6); // Add size of the header. + hpcalcs_debug("Virtual packet has size %" PRIu32 " (%" PRIx32 ")\n", size, size); + if (pkt != NULL) { + uint8_t * ptr; + uint16_t crc16; + uint32_t offset = 0; + + // Some text editors add the UTF-16LE BOM at the beginning of the file, but the SDKV0.30 firmware version chokes on it. + // Therefore, skip the BOM. + if ( (file->type == PRIME_TYPE_PRGM || file->type == PRIME_TYPE_NOTE) + && (file->data[0] == 0xFF && file->data[1] == 0xFE) + ) { + offset = 2; + size -= 2; + } + + pkt->cmd = CMD_PRIME_RECV_FILE; + ptr = pkt->data; + *ptr++ = CMD_PRIME_RECV_FILE; + *ptr++ = 0x01; + *ptr++ = (uint8_t)((size >> 24) & 0xFF); + *ptr++ = (uint8_t)((size >> 16) & 0xFF); + *ptr++ = (uint8_t)((size >> 8) & 0xFF); + *ptr++ = (uint8_t)((size ) & 0xFF); + *ptr++ = file->type; + *ptr++ = namelen; + *ptr++ = 0x00; // CRC16, set it to 0 for now. + *ptr++ = 0x00; + memcpy(ptr, file->name, namelen); + ptr += namelen; + memcpy(ptr, file->data + offset, file->size - offset); + crc16 = crc16_block(pkt->data, size); // Yup, the last 6 bytes of the packet are excluded from the CRC. + pkt->data[8] = crc16 & 0xFF; + pkt->data[9] = (crc16 >> 8) & 0xFF; + res = write_vtl_pkt(handle, pkt); + + prime_vtl_pkt_del(pkt); + } + else { + res = ERR_MALLOC; + hpcalcs_error("%s: couldn't create packet", __FUNCTION__); + } + } + else { + res = ERR_INVALID_PARAMETER; + hpcalcs_error("%s: an argument is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL calc_prime_r_send_file(calc_handle * handle) { + int res; + if (handle != NULL) { + // There doesn't seem to be anything to do, beyond eliminating packets starting with 0xFF. + res = calc_prime_r_check_ready(handle, NULL, NULL); + } + else { + res = ERR_INVALID_HANDLE; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +// Receiving an individual file from the calculator seems to start by a CMD_PRIME_REQ_FILE. +HPEXPORT int HPCALL calc_prime_s_recv_file(calc_handle * handle, files_var_entry * file) { + int res; + if (handle != NULL && file != NULL) { + uint8_t namelen = (uint8_t)char16_strlen(file->name) * 2; + uint32_t size = 10 - 6 + namelen; // Size of the data after the header. + prime_vtl_pkt * pkt = prime_vtl_pkt_new(size + 6); // Add size of the header. + if (pkt != NULL) { + uint8_t * ptr; + uint16_t crc16; + + pkt->cmd = CMD_PRIME_RECV_FILE; + ptr = pkt->data; + *ptr++ = CMD_PRIME_REQ_FILE; + *ptr++ = 0x01; + *ptr++ = (uint8_t)((size >> 24) & 0xFF); + *ptr++ = (uint8_t)((size >> 16) & 0xFF); + *ptr++ = (uint8_t)((size >> 8) & 0xFF); + *ptr++ = (uint8_t)((size ) & 0xFF); + *ptr++ = file->type; + *ptr++ = namelen; + *ptr++ = 0x00; // CRC16, set it to 0 for now. + *ptr++ = 0x00; + memcpy(ptr, file->name, namelen); + crc16 = crc16_block(pkt->data, size); // Yup, the last 6 bytes are excluded from the CRC. + pkt->data[8] = crc16 & 0xFF; + pkt->data[9] = (crc16 >> 8) & 0xFF; + res = write_vtl_pkt(handle, pkt); + + prime_vtl_pkt_del(pkt); + } + else { + res = ERR_MALLOC; + hpcalcs_error("%s: couldn't create packet", __FUNCTION__); + } + } + else { + res = ERR_INVALID_PARAMETER; + hpcalcs_error("%s: an argument is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL calc_prime_r_recv_file(calc_handle * handle, files_var_entry ** out_file) { + int res; + prime_vtl_pkt * pkt; + // TODO: if no file was received, have *out_file = NULL, but res = 0. + if (handle != NULL) { + res = read_vtl_pkt(handle, CMD_PRIME_RECV_FILE, &pkt, 1); + if (res == ERR_SUCCESS && pkt != NULL) { + if (pkt->size >= 11) { + // Packet has CRC + uint16_t computed_crc; // 0x0000 ? + uint8_t * ptr = pkt->data; + uint16_t embedded_crc = (((uint16_t)(ptr[9])) << 8) | ((uint16_t)(ptr[8])); + // Reset CRC before computing + ptr[8] = 0x00; + ptr[9] = 0x00; + computed_crc = crc16_block(ptr, pkt->size - 6); // The CRC contains the initial 0x00, but not the final 6 bytes (...). + hpcalcs_info("%s: embedded=%" PRIX16 " computed=%" PRIX16, __FUNCTION__, embedded_crc, computed_crc); + if (computed_crc != embedded_crc) { + hpcalcs_error("%s: CRC mismatch", __FUNCTION__); + } + + if (out_file != NULL) { + uint8_t namelen; + uint32_t size; + uint8_t filetype; + + *out_file = NULL; + filetype = pkt->data[6]; + namelen = pkt->data[7]; + size = pkt->size - 10 - namelen; + + if (!(size & UINT32_C(0x80000000))) { + *out_file = hpfiles_ve_create_with_data(&pkt->data[10 + namelen], size); + if (*out_file != NULL) { + (*out_file)->type = filetype; + memcpy((*out_file)->name, &pkt->data[10], namelen); + (*out_file)->invalid = (computed_crc != embedded_crc); + hpcalcs_info("%s: created entry for %ls with size %" PRIu32 " and type %02X", __FUNCTION__, (*out_file)->name, (*out_file)->size, filetype); + } + else { + res = ERR_MALLOC; + hpcalcs_error("%s: couldn't create entry", __FUNCTION__); + } + } + else { + res = ERR_CALC_PACKET_FORMAT; + hpcalcs_error("%s: weird size (packet too short ?)", __FUNCTION__); + // TODO: change res. + } + } + } + else { + if (pkt->data[0] != 0xF9) { + res = ERR_CALC_PACKET_FORMAT; + hpcalcs_info("%s: packet is too short: %" PRIu32 "bytes", __FUNCTION__, pkt->size); + } + else { + hpcalcs_info("%s: skipping F9 packet", __FUNCTION__); + } + if (out_file != NULL) { + *out_file = NULL; + } + } + prime_vtl_pkt_del(pkt); + } + else { + hpcalcs_error("%s: failed to read packet", __FUNCTION__); + } + } + else { + res = ERR_INVALID_HANDLE; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL calc_prime_s_recv_backup(calc_handle * handle) { + int res; + if (handle != NULL) { + prime_vtl_pkt * pkt = prime_vtl_pkt_new(1); + if (pkt != NULL) { + uint8_t * ptr; + + pkt->cmd = CMD_PRIME_RECV_FILE; + ptr = pkt->data; + *ptr++ = CMD_PRIME_RECV_BACKUP; + res = write_vtl_pkt(handle, pkt); + prime_vtl_pkt_del(pkt); + } + else { + res = ERR_MALLOC; + hpcalcs_error("%s: couldn't create packet", __FUNCTION__); + } + } + else { + res = ERR_INVALID_HANDLE; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL calc_prime_r_recv_backup(calc_handle * handle, files_var_entry *** out_vars) { + int res; + if (handle != NULL) { + // TODO: in order to be more robust against packet losses, + // rewrite this code to read as much as possible, then attempt to split data according to file headers. + uint32_t count = 0; + files_var_entry ** entries = hpfiles_ve_create_array(count); + if (entries != NULL) { + for (;;) { + if (out_vars != NULL) { + *out_vars = entries; + } + res = calc_prime_r_recv_file(handle, &entries[count]); + if (res == ERR_SUCCESS) { + if (entries[count] != NULL) { + files_var_entry ** new_entries; + hpcalcs_info("%s: continuing due to non-empty entry", __FUNCTION__); + count++; + new_entries = hpfiles_ve_resize_array(entries, count); + if (new_entries != NULL) { + entries = new_entries; + entries[count] = NULL; + } + else { + res = ERR_MALLOC; + hpcalcs_error("%s: couldn't resize entries", __FUNCTION__); + break; + } + } + else { + hpcalcs_info("%s: breaking due to empty file", __FUNCTION__); + res = ERR_SUCCESS; + break; + } + } + else { + hpcalcs_error("%s: breaking due to reception failure", __FUNCTION__); + break; + } + } + } + else { + res = ERR_MALLOC; + hpcalcs_error("%s: couldn't create entries", __FUNCTION__); + } + } + else { + res = ERR_INVALID_PARAMETER; + hpcalcs_error("%s: an argument is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL calc_prime_s_send_key(calc_handle * handle, uint32_t code) { + int res; + if (handle != NULL) { + prime_vtl_pkt * pkt = prime_vtl_pkt_new(7); + if (pkt != NULL) { + uint8_t * ptr; + + pkt->cmd = CMD_PRIME_SEND_KEY; + ptr = pkt->data; + *ptr++ = CMD_PRIME_SEND_KEY; + *ptr++ = 0x01; + *ptr++ = 0x00; + *ptr++ = 0x00; + *ptr++ = 0x00; + *ptr++ = 0x01; + *ptr++ = (uint8_t)code; + res = write_vtl_pkt(handle, pkt); + prime_vtl_pkt_del(pkt); + } + else { + res = ERR_MALLOC; + hpcalcs_error("%s: couldn't create packet", __FUNCTION__); + } + } + else { + res = ERR_INVALID_PARAMETER; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL calc_prime_r_send_key(calc_handle * handle) { + int res = 0; + if (handle != NULL) { + // There doesn't seem to be anything to do. + //res = calc_prime_r_check_ready(handle, NULL, NULL); + } + else { + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL calc_prime_s_send_keys(calc_handle * handle, const uint8_t * data, uint32_t size) { + int res; + if (handle != NULL) { + prime_vtl_pkt * pkt = prime_vtl_pkt_new(6 + size); + if (pkt != NULL) { + uint8_t * ptr; + + pkt->cmd = CMD_PRIME_SEND_KEY; + ptr = pkt->data; + *ptr++ = CMD_PRIME_SEND_KEY; + *ptr++ = 0x01; + *ptr++ = (uint8_t)((size >> 24) & 0xFF); + *ptr++ = (uint8_t)((size >> 16) & 0xFF); + *ptr++ = (uint8_t)((size >> 8) & 0xFF); + *ptr++ = (uint8_t)((size ) & 0xFF); + memcpy(ptr, data, size); + res = write_vtl_pkt(handle, pkt); + prime_vtl_pkt_del(pkt); + } + else { + res = ERR_MALLOC; + hpcalcs_error("%s: couldn't create packet", __FUNCTION__); + } + } + else { + res = ERR_INVALID_PARAMETER; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL calc_prime_r_send_keys(calc_handle * handle) { + int res = 0; + if (handle != NULL) { + // There doesn't seem to be anything to do. + //res = calc_prime_r_check_ready(handle, NULL, NULL); + } + else { + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL calc_prime_s_send_chat(calc_handle * handle, const uint16_t * data, uint32_t size) { + int res; + if (handle != NULL) { + prime_vtl_pkt * pkt = prime_vtl_pkt_new(size + 6); + if (pkt != NULL) { + uint8_t * ptr; + + pkt->cmd = CMD_PRIME_SEND_CHAT; + ptr = pkt->data; + *ptr++ = CMD_PRIME_SEND_CHAT; + *ptr++ = 0x01; + *ptr++ = (uint8_t)((size >> 24) & 0xFF); + *ptr++ = (uint8_t)((size >> 16) & 0xFF); + *ptr++ = (uint8_t)((size >> 8) & 0xFF); + *ptr++ = (uint8_t)((size ) & 0xFF); + memcpy(ptr, data, size); + res = write_vtl_pkt(handle, pkt); + prime_vtl_pkt_del(pkt); + } + else { + res = ERR_MALLOC; + hpcalcs_error("%s: couldn't create packet", __FUNCTION__); + } + } + else { + res = ERR_INVALID_PARAMETER; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL calc_prime_r_send_chat(calc_handle * handle) { + int res = 0; + if (handle != NULL) { + // There doesn't seem to be anything to do. + //res = calc_prime_r_check_ready(handle, NULL, NULL); + } + else { + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL calc_prime_r_recv_chat(calc_handle * handle, uint16_t ** out_data, uint32_t * out_size) { + int res; + if (handle != NULL) { + prime_vtl_pkt * pkt; + res = read_vtl_pkt(handle, CMD_PRIME_RECV_CHAT, &pkt, 0); + if (res == ERR_SUCCESS && pkt != NULL) { + if (pkt->size >= 8) { + if (out_data != NULL && out_size != NULL) { + *out_size = pkt->size - 6; + memmove(pkt->data, pkt->data + 6, pkt->size - 6); + *out_data = (uint16_t *)pkt->data; // Transfer ownership of the memory block to the caller. + pkt->data = NULL; // Detach it from virtual packet. + } + } + else { + res = ERR_CALC_PACKET_FORMAT; + hpcalcs_info("%s: packet is too short: %" PRIu32 "bytes", __FUNCTION__, pkt->size); + } + prime_vtl_pkt_del(pkt); + } + else { + hpcalcs_error("%s: failed to read packet", __FUNCTION__); + } + } + else { + res = ERR_INVALID_HANDLE; + hpcalcs_error("%s: handle is NULL", __FUNCTION__); + } + return res; +} diff --git a/libhpcalcs/src/prime_cmd.h b/libhpcalcs/src/prime_cmd.h new file mode 100644 index 0000000..b8a1994 --- /dev/null +++ b/libhpcalcs/src/prime_cmd.h @@ -0,0 +1,73 @@ +/* + * libhpcalcs: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file prime_cmd.h Calcs: Prime commands + */ + +#ifndef __HPLIBS_CALCS_PRIME_CMD_H__ +#define __HPLIBS_CALCS_PRIME_CMD_H__ + +#define CMD_PRIME_CHECK_READY (0xFF) +#define CMD_PRIME_GET_INFOS (0xFA) +#define CMD_PRIME_RECV_SCREEN (0xFC) +#define CMD_PRIME_RECV_BACKUP (0xF9) +#define CMD_PRIME_REQ_FILE (0xF8) +#define CMD_PRIME_RECV_FILE (0xF7) +#define CMD_PRIME_SEND_CHAT (0xF2) +#define CMD_PRIME_RECV_CHAT (0xF2) +#define CMD_PRIME_SEND_KEY (0xEC) +#define CMD_PRIME_SET_DATE_TIME (0xE7) + +HPEXPORT int HPCALL calc_prime_s_check_ready(calc_handle * handle); +HPEXPORT int HPCALL calc_prime_r_check_ready(calc_handle * handle, uint8_t ** out_data, uint32_t * out_size); + +HPEXPORT int HPCALL calc_prime_s_get_infos(calc_handle * handle); +HPEXPORT int HPCALL calc_prime_r_get_infos(calc_handle * handle, calc_infos * infos); + +HPEXPORT int HPCALL calc_prime_s_set_date_time(calc_handle * handle, time_t timestamp); +HPEXPORT int HPCALL calc_prime_r_set_date_time(calc_handle * handle); + +HPEXPORT int HPCALL calc_prime_s_recv_screen(calc_handle * handle, calc_screenshot_format format); +HPEXPORT int HPCALL calc_prime_r_recv_screen(calc_handle * handle, calc_screenshot_format format, uint8_t ** out_data, uint32_t * out_size); + +HPEXPORT int HPCALL calc_prime_s_send_file(calc_handle * handle, files_var_entry * file); +HPEXPORT int HPCALL calc_prime_r_send_file(calc_handle * handle); + +HPEXPORT int HPCALL calc_prime_s_recv_file(calc_handle * handle, files_var_entry * file); +HPEXPORT int HPCALL calc_prime_r_recv_file(calc_handle * handle, files_var_entry ** out_file); + +HPEXPORT int HPCALL calc_prime_s_recv_backup(calc_handle * handle); +HPEXPORT int HPCALL calc_prime_r_recv_backup(calc_handle * handle, files_var_entry *** out_vars); + +HPEXPORT int HPCALL calc_prime_s_send_key(calc_handle * handle, uint32_t code); +HPEXPORT int HPCALL calc_prime_r_send_key(calc_handle * handle); +HPEXPORT int HPCALL calc_prime_s_send_keys(calc_handle * handle, const uint8_t * data, uint32_t size); +HPEXPORT int HPCALL calc_prime_r_send_keys(calc_handle * handle); + +HPEXPORT int HPCALL calc_prime_s_send_chat(calc_handle * handle, const uint16_t * data, uint32_t size); +HPEXPORT int HPCALL calc_prime_r_send_chat(calc_handle * handle); + +HPEXPORT int HPCALL calc_prime_r_recv_chat(calc_handle * handle, uint16_t ** out_data, uint32_t * out_size); + +#endif diff --git a/libhpcalcs/src/prime_rpkt.c b/libhpcalcs/src/prime_rpkt.c new file mode 100644 index 0000000..3f5eab9 --- /dev/null +++ b/libhpcalcs/src/prime_rpkt.c @@ -0,0 +1,103 @@ +/* + * libhpcalcs: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file prime_rpkt.c Calcs: Prime raw packets. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include "internal.h" +#include "logging.h" +#include "error.h" +#include "utils.h" + +#include +#include +#include +#include + +HPEXPORT int HPCALL prime_send(calc_handle * handle, prime_raw_hid_pkt * pkt) { + int res; + if (handle != NULL && pkt != NULL) { + cable_handle * cable = handle->cable; + if (cable != NULL) { + hexdump("OUT", pkt->data, pkt->size, 2); + res = hpcables_cable_send(cable, pkt->data, pkt->size); + if (res == ERR_SUCCESS) { + hpcalcs_info("%s: send succeeded", __FUNCTION__); + } + else { + hpcalcs_error("%s: send failed", __FUNCTION__); + } + } + else { + res = ERR_CALC_NO_CABLE; + hpcalcs_error("%s: cable is NULL", __FUNCTION__); + } + } + else { + res = ERR_INVALID_PARAMETER; + hpcalcs_error("%s: an argument is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL prime_recv(calc_handle * handle, prime_raw_hid_pkt * pkt) { + int res; + if (handle != NULL && pkt != NULL) { + cable_handle * cable = handle->cable; + if (cable != NULL) { + uint8_t * data; + data = (hpcalcs_alloc_funcs.malloc)(sizeof(pkt->data)); + if (data != NULL) { + res = hpcables_cable_recv(cable, &data, &pkt->size); + hexdump("IN", data, pkt->size, 2); + if (res == ERR_SUCCESS) { + //hpcalcs_info("%s: recv succeeded", __FUNCTION__); + memcpy(pkt->data, data, sizeof(pkt->data)); + } + else { + hpcalcs_warning("%s: recv failed", __FUNCTION__); + } + (hpcalcs_alloc_funcs.free)(data); + } + else { + res = ERR_MALLOC; + hpcalcs_error("%s: failed to allocate buffer", __FUNCTION__); + } + } + else { + res = ERR_CALC_NO_CABLE; + hpcalcs_error("%s: cable is NULL", __FUNCTION__); + } + } + else { + res = ERR_INVALID_PARAMETER; + hpcalcs_error("%s: an argument is NULL", __FUNCTION__); + } + return res; +} diff --git a/libhpcalcs/src/prime_vpkt.c b/libhpcalcs/src/prime_vpkt.c new file mode 100644 index 0000000..a574966 --- /dev/null +++ b/libhpcalcs/src/prime_vpkt.c @@ -0,0 +1,278 @@ +/* libhpcalcs - hand-helds support library + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file prime_vpkt.c Calcs: Prime virtual packets. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include "internal.h" +#include "logging.h" +#include "error.h" + +#include "prime_cmd.h" + +#include +#include +#include + +// ----------------------------------------------- +// Calcs - HP Prime virtual packets +// ----------------------------------------------- + + +HPEXPORT prime_vtl_pkt * HPCALL prime_vtl_pkt_new(uint32_t size) { + prime_vtl_pkt * pkt = (prime_vtl_pkt *)(hpcalcs_alloc_funcs.malloc)(sizeof(*pkt)); + + if (pkt != NULL) { + pkt->size = size; + if (size != 0) { + pkt->data = (uint8_t *)(hpcalcs_alloc_funcs.calloc)(size, sizeof(*pkt->data)); + + if (pkt->data == NULL) { + (hpcalcs_alloc_funcs.free)(pkt); + pkt = NULL; + } + } + } + + return pkt; +} + +HPEXPORT prime_vtl_pkt * HPCALL prime_vtl_pkt_new_with_data_ptr(uint32_t size, uint8_t * data) { + prime_vtl_pkt * pkt = (prime_vtl_pkt *)(hpcalcs_alloc_funcs.malloc)(sizeof(*pkt)); + + if (pkt != NULL) { + pkt->size = size; + pkt->data = data; + } + + return pkt; +} + +HPEXPORT void HPCALL prime_vtl_pkt_del(prime_vtl_pkt * pkt) { + if (pkt != NULL) { + (hpcalcs_alloc_funcs.free)(pkt->data); + (hpcalcs_alloc_funcs.free)(pkt); + } + else { + hpcalcs_error("%s: pkt is NULL", __FUNCTION__); + } +} + +HPEXPORT int HPCALL prime_send_data(calc_handle * handle, prime_vtl_pkt * pkt) { + int res; + if (handle != NULL && pkt != NULL) { + prime_raw_hid_pkt raw; + uint32_t i, q, r; + uint32_t offset = 0; + uint8_t pkt_id = 0; + + memset((void *)&raw, 0, sizeof(raw)); + q = (pkt->size) / (PRIME_RAW_HID_DATA_SIZE - 1); + r = (pkt->size) % (PRIME_RAW_HID_DATA_SIZE - 1); + + hpcalcs_info("%s: q:%" PRIu32 "\tr:%" PRIu32, __FUNCTION__, q, r); + + for (i = 1; i <= q; i++) { + raw.size = PRIME_RAW_HID_DATA_SIZE + 1; + raw.data[1] = pkt_id; + memcpy(raw.data + 2, pkt->data + offset, PRIME_RAW_HID_DATA_SIZE - 1); + offset += PRIME_RAW_HID_DATA_SIZE - 1; + + res = prime_send(handle, &raw); + if (res) { + hpcalcs_info("%s: send %" PRIu32 " failed", __FUNCTION__, i); + r = 0; + break; + } + else { + hpcalcs_info("%s: send %" PRIu32 " succeeded", __FUNCTION__, i); + } + + // Increment packet ID, which seems to be necessary for computer -> calc packets + pkt_id++; + if (pkt_id == 0xFF) { + pkt_id = 0; // Skip 0xFF, which is used for other purposes. + } + } + + if (r || !pkt->size) { + raw.size = r + 2; + raw.data[1] = pkt_id; + memcpy(raw.data + 2, pkt->data + offset, r); + + res = prime_send(handle, &raw); + if (res) { + hpcalcs_info("%s: send remaining failed", __FUNCTION__); + } + else { + hpcalcs_info("%s: send remaining succeeded", __FUNCTION__); + } + } + } + else { + res = ERR_INVALID_PARAMETER; + hpcalcs_error("%s: an argument is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL prime_recv_data(calc_handle * handle, prime_vtl_pkt * pkt) { + int res; + if (handle != NULL && pkt != NULL) { + prime_raw_hid_pkt raw; + uint32_t expected_size = 0; + uint32_t offset = 0; + uint32_t read_pkts_count = 0; + // WIP: reassembly. + + //size = pkt->size; + pkt->size = 0; + pkt->data = NULL; + + for(;;) { + memset(&raw, 0, sizeof(raw)); + res = prime_recv(handle, &raw); + if (res) { + hpcalcs_warning("%s: recv failed", __FUNCTION__); + break; + } + else { + //hpcalcs_info("%s: recv succeeded", __FUNCTION__); + } + //hpcalcs_info("%s: raw.size=%" PRIu32, __FUNCTION__, raw.size); + if (raw.size > 0) { + uint8_t * new_data; + + // Exclude those packets from reassembly (at least for screenshotting purposes, they seem to be spurious). + if (raw.data[0] == 0xFF) { + // TODO: investigate whether the second byte could indicate an error code ? + hpcalcs_error("%s: skipping packet starting with 0xFF", __FUNCTION__); + continue; + } + // Sanity check. The first byte is the sequence number. After reaching 0xFE. it wraps back to 0 (skipping 0xFF). + else if (raw.data[0] != ((read_pkts_count + (read_pkts_count / 0xFF)) & 0xFF)) { + res = ERR_CALC_PACKET_FORMAT; + hpcalcs_error("%s: packet out of sequence, got %d, expected %d", __FUNCTION__, (int)raw.data[0], read_pkts_count); + break; + } + + read_pkts_count++; + + // Over-read prevention (hopefully ^^) code: pre-set the expected size of the reply to the given command. + if (read_pkts_count == 1) { + res = prime_data_size(pkt->cmd, raw.data + 1, &expected_size); // +1: skip leading byte. + if (res != ERR_SUCCESS) { + break; + } + } + + pkt->size += raw.size - 1; + new_data = (hpcalcs_alloc_funcs.realloc)(pkt->data, pkt->size); + if (new_data != NULL) { + pkt->data = new_data; + // Skip first byte, which is usually 0x00. + memcpy(pkt->data + offset, &(raw.data[1]), raw.size - 1); + offset += raw.size - 1; + } + else { + res = ERR_MALLOC; + hpcalcs_error("%s: cannot reallocate memory", __FUNCTION__); + break; + } + } + + if (raw.size < PRIME_RAW_HID_DATA_SIZE) { + hpcalcs_info("%s: breaking due to short packet (1)", __FUNCTION__); + goto shorten_packet; + } + if (offset >= expected_size) { + hpcalcs_info("%s: breaking because the expected size was reached (2)", __FUNCTION__); +shorten_packet: + // Shorten packet. + if (expected_size <= pkt->size) { + hpcalcs_info("%s: shortening packet from %" PRIu32 " to %" PRIu32, __FUNCTION__, pkt->size, expected_size); + } + else { + hpcalcs_warning("%s: expected %" PRIu32 " bytes but only got %" PRIu32 " bytes, output corrupted", __FUNCTION__, expected_size, pkt->size); + } + pkt->data = (hpcalcs_alloc_funcs.realloc)(pkt->data, expected_size); + pkt->size = expected_size; + break; + } + } + } + else { + res = ERR_INVALID_PARAMETER; + hpcalcs_error("%s: an argument is NULL", __FUNCTION__); + } + return res; +} + +HPEXPORT int HPCALL prime_data_size(uint8_t cmd, uint8_t * data, uint32_t * out_size) { + int res = ERR_SUCCESS; + if (data != NULL && out_size != NULL) { + switch (cmd) { + case CMD_PRIME_CHECK_READY: + // Single-packet reply. + *out_size = 1; + break; + case CMD_PRIME_GET_INFOS: + case CMD_PRIME_RECV_SCREEN: + case CMD_PRIME_RECV_BACKUP: + // Not supposed to receive REQ_FILE + case CMD_PRIME_RECV_FILE: + case CMD_PRIME_RECV_CHAT: + // Not supposed to receive SEND_KEY + // Not supposed to receive SET_DATE_TIME + // Expected size is embedded in reply. + if (data[1] == 0x01) { + if (cmd != data[0]) { + hpcalcs_warning("%s: command in packet %02X does not match the expected command %02X", __FUNCTION__, data[0], cmd); + } + + *out_size = (((uint32_t)(data[2])) << 24) | (((uint32_t)(data[3])) << 16) | (((uint32_t)(data[4])) << 8) | ((uint32_t)(data[5])); + *out_size += 6; // cmd + 0x01 + size. + } + else { + res = ERR_CALC_PACKET_FORMAT; + hpcalcs_error("%s: expected 0x01 as second data byte", __FUNCTION__); + } + break; + default: + // Not implemented. + *out_size = 0; + hpcalcs_error("%s: received unknown command %u, size undetermined, please report", __FUNCTION__, cmd); + break; + } + } + else { + res = ERR_INVALID_PARAMETER; + hpcalcs_error("%s: an argument is NULL", __FUNCTION__); + } + return res; +} diff --git a/libhpcalcs/src/type2str.c b/libhpcalcs/src/type2str.c new file mode 100644 index 0000000..838df43 --- /dev/null +++ b/libhpcalcs/src/type2str.c @@ -0,0 +1,83 @@ +/* libhpcalcs - hand-helds support library + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file type2str.c Files / cables / calcs: Type (usually enum) <-> string conversion functions. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#include "logging.h" +#include "error.h" + +#include +#include + +HPEXPORT const char * HPCALL hpcalcs_model_to_string(calc_model model) { + return hpfiles_model_to_string(model); +} + +HPEXPORT calc_model HPCALL hpcalcs_string_to_model(const char *str) { + return hpfiles_string_to_model(str); +} + +HPEXPORT const char * HPCALL hpcables_model_to_string(cable_model model) { + switch (model) { + case CABLE_NUL: return ""; + case CABLE_PRIME_HID: return "Prime (HID)"; + default: return "unknown"; + } +} + +HPEXPORT cable_model HPCALL hpcables_string_to_model(const char *str) { + if (str != NULL) { + if (!strcasecmp("Prime HID", str) || !strcasecmp("Prime_HID", str) || !strcasecmp("HP Prime HID", str)) { + return CABLE_PRIME_HID; + } + // else fall through. + } + return CABLE_NUL; +} + +HPEXPORT const char * HPCALL hpfiles_model_to_string(calc_model model) { + switch (model) { + case CALC_NONE: return ""; + case CALC_PRIME: return "Prime"; + default: return "unknown"; + } +} + +HPEXPORT calc_model HPCALL hpfiles_string_to_model(const char *str) { + if (str != NULL) { + if (!strcasecmp("Prime", str) || !strcasecmp("HP Prime", str)) { + return CALC_PRIME; + } + // else fall through. + } + return CALC_NONE; +} diff --git a/libhpcalcs/src/typesprime.c b/libhpcalcs/src/typesprime.c new file mode 100644 index 0000000..91626b5 --- /dev/null +++ b/libhpcalcs/src/typesprime.c @@ -0,0 +1,192 @@ +/* + * libhpfiles: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file typesprime.c Files: Prime file types and utility functions. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include "typesprime.h" +#include "logging.h" +#include "error.h" + +#include +#include +// For path splitting. +#ifndef _WIN32 +#include +#include +#else +#include +#include +#endif + +const char * PRIME_CONST[][3] = { + { "SETTINGS", "", "Settings" }, // The extension is embedded in the file name. + { "", "unknown", "Unknown" }, + { "APP", "hpapp", "Application" }, + { "LIST", "hplist", "List" }, + { "MATRIX", "hpmatrix", "Matrix" }, + { "NOTE", "hpnote", "Note" }, + { "PRGM", "hpprgm", "Program" }, + { "APPNOTE", "hpappnote", "App Note" }, // XXX Tentative + { "APPPRGM", "hpappprgm", "App Program" }, // XXX Tentative + { "COMPLEX", "hpcomplex", "Complex" }, // Extension synthetized, not found with the connectivity kit or emulator ? + { "REAL", "hpreal", "Real" }, // Extension synthetized, not found with the connectivity kit or emulator ? + { "TESTMODECONFIG", "", "Real" }, // The extension is embedded in the file name. + { NULL, NULL, NULL } +}; + +const char * prime_vartype2str(uint8_t type) { + const char * res = "unknown"; + if (type < sizeof(PRIME_CONST)/sizeof(PRIME_CONST[0]) - 1) { + res = PRIME_CONST[type][0]; + } + hpfiles_debug("%s: returning %s", __FUNCTION__, res); + return res; +} + +uint8_t prime_str2vartype(const char * type) { + uint8_t res = PRIME_TYPE_UNKNOWN; + for (uint32_t i = 0; i < sizeof(PRIME_CONST)/sizeof(PRIME_CONST[0]) - 1; i++) { + if (!strcasecmp(PRIME_CONST[i][0], type)) { + res = i; + break; + } + } + hpfiles_debug("%s: returning %" PRIu8, __FUNCTION__, res); + return res; +} + +const char * prime_byte2fext(uint8_t type) { + const char * res = "unknown"; + if (type < sizeof(PRIME_CONST)/sizeof(PRIME_CONST[0]) - 1) { + res = PRIME_CONST[type][1]; + } + hpfiles_debug("%s: returning %s", __FUNCTION__, res); + return res; +} + +uint8_t prime_fext2byte(const char * type) { + uint8_t res = PRIME_TYPE_UNKNOWN; + for (uint32_t i = 0; i < sizeof(PRIME_CONST)/sizeof(PRIME_CONST[0]) - 1; i++) { + if (!strcasecmp(PRIME_CONST[i][1], type)) { + res = i; + break; + } + } + hpfiles_debug("%s: returning %" PRIu8, __FUNCTION__, res); + return res; +} + +const char * prime_byte2desc(uint8_t type) { + const char * res = "Unknown"; + if (type < sizeof(PRIME_CONST)/sizeof(PRIME_CONST[0]) - 1) { + res = PRIME_CONST[type][2]; + } + hpfiles_debug("%s: returning %s", __FUNCTION__, res); + return res; +} + +uint8_t prime_filename2byte(const char * filepath) { + uint8_t res = PRIME_TYPE_UNKNOWN; + prime_parsefilename(filepath, &res, NULL); + hpfiles_debug("%s: returning %" PRIu8, __FUNCTION__, res); + return res; +} + +int prime_parsesplitfilename(char * file, char * extension, uint8_t * out_type, char ** out_calcfilename) { + int res = ERR_FILE_FILENAME; + uint8_t type = PRIME_TYPE_UNKNOWN; + // The way to get the basename and file extension is platform-dependent, obviously... + hpfiles_info("%s: file:\"%s\" extension:\"%s\"", __FUNCTION__, file, extension); + if ( !strcmp(file, "calc.settings") + || !strcmp(file, "settings") + || !strcmp(file, "cas.settings") + ) { + type = PRIME_TYPE_SETTINGS; + } + else if (!strcmp(file, "testmodes.hptestmodes")) { + type = PRIME_TYPE_TESTMODECONFIG; + } + else { + if (extension != NULL && extension[0] == '.') { + type = prime_fext2byte(extension + 1); // Skip '.' + } + else { + hpfiles_error("%s: extension not found in filepath, unhandled type", __FUNCTION__); + } + } + if (type != PRIME_TYPE_UNKNOWN) { + res = ERR_SUCCESS; + *out_type = type; + if (out_calcfilename != NULL) { + char * calcfilename = strdup(file); +#ifndef _WIN32 + if (calcfilename != NULL) { + extension = strrchr(calcfilename, '.'); + if (extension != NULL) { + *extension = 0; // Strip extension. + } + } +#endif + *out_calcfilename = calcfilename; + } + } + return res; +} + +int prime_parsefilename(const char * filepath, uint8_t * out_type, char ** out_calcfilename) { + // The way to get the basename and file extension is platform-dependent, obviously... +#ifndef _WIN32 + int res = ERR_FILE_FILENAME; + char * duplicated = strdup(filepath); + char * file; + char * extension; + if (duplicated != NULL) { + file = basename(duplicated); + if (file != NULL) { + extension = strrchr(file, '.'); + res = prime_parsesplitfilename(file, extension, out_type, out_calcfilename); + } + free(duplicated); + } + else { + res = ERR_MALLOC; + hpfiles_debug("%s: failed to duplicate filepath", __FUNCTION__); + } +#else + int res; + char file[_MAX_FNAME + 1]; + char extension[_MAX_EXT + 1]; + _splitpath(filepath, NULL /* drive */, NULL /* dir */, file, extension); + res = prime_parsesplitfilename(file, extension, out_type, out_calcfilename); +#endif + + hpfiles_debug("%s: returning %d", __FUNCTION__, res); + return res; +} diff --git a/libhpcalcs/src/typesprime.h b/libhpcalcs/src/typesprime.h new file mode 100644 index 0000000..5cf7d26 --- /dev/null +++ b/libhpcalcs/src/typesprime.h @@ -0,0 +1,69 @@ +/* + * libhpfiles: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file typesprime.h Files: Prime file types and utility functions. + */ + +#ifndef __HPLIBS_FILES_TYPESPRIME_H__ +#define __HPLIBS_FILES_TYPESPRIME_H__ + +#define PRIME_TYPE_SETTINGS (0x00) +// 0x01 ? +#define PRIME_TYPE_APP (0x02) +#define PRIME_TYPE_LIST (0x03) // variables L0-L9, for instance +#define PRIME_TYPE_MATRIX (0x04) // variables M0-M9, for instance +#define PRIME_TYPE_NOTE (0x05) +#define PRIME_TYPE_PRGM (0x06) // programs with identical data are sent twice during backup ?? +#define PRIME_TYPE_APPNOTE (0x07) // XXX Tentative +#define PRIME_TYPE_APPPRGM (0x08) // XXX Tentative +#define PRIME_TYPE_COMPLEX (0x09) // variables Z0-Z9, for instance +#define PRIME_TYPE_REAL (0x0A) // variables A-Z, 0x3B8 (theta), for instance +#define PRIME_TYPE_TESTMODECONFIG (0x0B) +#define PRIME_TYPE_UNKNOWN (0xFF) + +//! Return the string corresponding to the file type ID +const char * prime_vartype2str(uint8_t type); + +//! Return the type ID corresponding to the string +uint8_t prime_str2vartype(const char * type); + +//! Return the file extension corresponding to the value +const char * prime_byte2fext(uint8_t type); + +//! Return the type value corresponding to the file extension +uint8_t prime_fext2byte(const char * type); + +//! Return the description corresponding to the value +const char * prime_byte2desc(uint8_t type); + +//! Return the type value corresponding to the file path +uint8_t prime_filename2byte(const char * filepath); + +//! Parse the file name + extension and determines the type value and calculator-side filename +int prime_parsesplitfilename(char * file, char * extension, uint8_t * out_type, char ** out_calcfilename); + +//! Parse the file path and determines the type value and calculator-side filename +int prime_parsefilename(const char * filepath, uint8_t * out_type, char ** out_calcfilename); + +#endif diff --git a/libhpcalcs/src/utils.c b/libhpcalcs/src/utils.c new file mode 100644 index 0000000..c7629b5 --- /dev/null +++ b/libhpcalcs/src/utils.c @@ -0,0 +1,114 @@ +/* + * libhpfiles, libhpcables, libhpcalcs - hand-helds support library + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file utils.c Files / Cables / Calcs: utility functions, e.g. UTF-16LE handling. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include "internal.h" +#include "utils.h" +#include "logging.h" + +#include +#include + +uint32_t char16_strlen(char16_t * str) { + uint32_t i = 0; + if (str != NULL) { + while (*str) { + i++; + str++; + } + } + return i; +} + +char16_t * char16_strncpy(char16_t * dst, const char16_t * src, uint32_t n) { + if (dst != NULL && src != NULL) { + uint32_t i = 0; + char16_t c = *src; + while (i < n && c != 0) { + *dst++ = c; + src++; + c = *src; + } + } + return dst; +} + +void hexdump(const char * direction, uint8_t *data, uint32_t size, uint32_t level) +{ + if (size > 0) { + if (level == 1) { + char str[64]; + + hpcalcs_debug("Dumping %s packet with size %" PRIu32, direction, size); + str[0] = 0; + if (size <= 12) + { + uint32_t i; + str[0] = ' '; str[1] = ' '; str[2] = ' '; str[3] = ' '; + + for (i = 0; i < size; i++) + { + sprintf(&str[3*i+4], "%02X ", data[i]); + } + } + else + { + sprintf(str, " %02X %02X %02X %02X %02X ..... %02X %02X %02X %02X %02X", + data[0], data[1], data[2], data[3], data[4], + data[size-5], data[size-4], data[size-3], data[size-2], data[size-1]); + } + hpcalcs_debug(str); + } + else if (level == 2) { + const int step = 16; + char str[4 + 3 * step + 1]; + uint32_t i, j; + + hpcalcs_debug("Dumping %s packet with size %" PRIu32, direction, size); + + str[0] = ' '; + str[1] = ' '; + str[2] = ' '; + str[3] = ' '; + str[sizeof(str) - 1] = 0; + + for (i = j = 0; i < size; i++, j++) { + if (i && !(i % step)) { + hpcalcs_debug(str); + j = 0; + } + + sprintf(&str[3*j+4], "%02X ", data[i]); + } + hpcalcs_debug(str); + } + } +} diff --git a/libhpcalcs/src/utils.h b/libhpcalcs/src/utils.h new file mode 100644 index 0000000..cdea8cf --- /dev/null +++ b/libhpcalcs/src/utils.h @@ -0,0 +1,38 @@ +/* + * libhpfiles, libhpcables, libhpcalcs - hand-helds support library + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file utils.h Files / Cables / Calcs: utility functions, e.g. UTF-16LE handling. + */ + +#ifndef __HPLIBS_UTILS_H__ +#define __HPLIBS_UTILS_H__ + +//! Plain C equivalent of char_traits::length. +uint32_t char16_strlen(char16_t * str); +//! strncpy applied to char16_t. +char16_t * char16_strncpy(char16_t * dst, const char16_t * src, uint32_t n); +//! Hex dumping function. +void hexdump(const char * direction, uint8_t *data, uint32_t size, uint32_t level); + +#endif diff --git a/libhpcalcs/tests/test_hpcalcs.c b/libhpcalcs/tests/test_hpcalcs.c new file mode 100644 index 0000000..7bdbcc2 --- /dev/null +++ b/libhpcalcs/tests/test_hpcalcs.c @@ -0,0 +1,816 @@ +/* + * libhpcalcs: hand-helds support libraries. + * Copyright (C) 2013 Lionel Debroux + * Code patterns and snippets borrowed from libticables & libticalcs: + * Copyright (C) 1999-2009 Romain Livin + * Copyright (C) 2009-2013 Lionel Debroux + * Copyright (C) 1999-2013 libti* contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * \file test_hpcalcs.c Calcs: Example / testing code, which can be considered authoritative. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../src/hpfiles.h" +#include "../src/hpcables.h" +#include "../src/hpcalcs.h" +#include "../src/hpopers.h" +#include "../src/prime_cmd.h" + +#undef VERSION +#define VERSION "Test program" + +#ifdef _WIN32 +static FILE * log_file = NULL; +#endif + +// Injecting memory allocation functions into the library is optional, the library will default to those if you don't. +static const hplibs_malloc_funcs alloc_funcs = { + .malloc = malloc, + .calloc = calloc, + .realloc = realloc, + .free = free +}; + +// Windows' terminal is extremely slow, cannot print the logging traces there. +#ifdef _WIN32 + +static void open_log_file(void) { + if (log_file == NULL) { + log_file = fopen("trace.txt", "w+b"); + if (log_file != NULL) { + time_t curtime = time(NULL); + fprintf(log_file, "Opening log file at %s", ctime(&curtime)); + } + } +} + +// Output logging traces to a file. +static void output_log_callback(const char * format, va_list args) { + open_log_file(); + if (log_file != NULL) { + vfprintf(log_file, format, args); + fflush(log_file); + } +} + +static void output_log(FILE *f, const char * format, ...) { + char str[256]; + va_list args; + va_start(args, format); + vsnprintf(str, 256, format, args); + str[255] = 0; + fprintf(f, "%s", str); + open_log_file(); + if (log_file != NULL) { + fprintf(log_file, "%s", str); + } +} + +#else + +// Using args twice, once for printing to stdout and once for printing to a file, triggers crashes. +// Therefore, let's have this function print only to stdout. +// Should there be a need to print to a file, on non-Windows, people can rely on "tee". +static void output_log_callback(const char * format, va_list args) { + vprintf(format, args); +} + +static void output_log(FILE *f, const char * format, ...) { + char str[256]; + va_list args; + va_start(args, format); + vsnprintf(str, 256, format, args); + str[255] = 0; + fprintf(f, "%s", str); +} + +#endif + +// Unconditionally output to stdout, on all platforms. +static void output_log_callback_stdout(const char * format, va_list args) { + vprintf(format, args); +} + + +// NOTE: this triplet of crude routines is just for demo and testing purposes !! +// In the general case, a proper i18n library (not offered by the C standard library) should be used ! +static void crude_convert_UTF16LE_to_8bit(const char16_t * input, char * output) { + const char * in_ptr = (const char *)input; + char16_t chr = 0; + do { + chr = ((char16_t)*in_ptr) | (((char16_t)*(in_ptr + 1)) << 8); + in_ptr += 2; + if (chr & 0xFF00) { + *output++ = '_'; // Mangle characters with high byte set... + } + else { + *output++ = (char)(chr & 0xFF); + } + } while (chr != 0); +} + +static void crude_convert_8bit_to_UTF16LE(const char * input, char16_t * output) { + char chr = 0; + do { + chr = *input++; + *((char *)output) = chr; + *(((char *)output) + 1) = 0x00; + output++; + } while (chr != 0); +} + +static void crude_convert_wchar_t_to_UTF16LE(const wchar_t * input, char16_t * output) { + wchar_t chr = 0; + do { + chr = *input++; + *output++ = (char16_t)chr; + } while (chr != 0); +} + +static void produce_output_file(calc_handle * handle, files_var_entry * entry) { + char filename[FILES_VARNAME_MAXLEN + 13]; + FILE * f; + const char * extension; + + output_log(stdout, "test_hpcalcs: Receive file success\n"); + hpfiles_ve_display(entry); + crude_convert_UTF16LE_to_8bit(entry->name, filename); + if (entry->invalid) { + output_log(stdout, "test_hpcalcs: NOTE: the data for file %s is corrupted (packet loss in transfer) !\n", filename); + } + extension = hpfiles_vartype2fext(hpcalcs_get_model(handle), entry->type); + if (extension[0] != 0) { + strcat(filename, "."); + strcat(filename, extension); + } + f = fopen(filename, "w+b"); + if (f != NULL) { + fwrite(entry->data, 1, entry->size, f); + fclose(f); + } + else { + output_log(stdout, "test_hpcalcs: Cannot open file for writing !\n"); + } +} + +static int is_ready(calc_handle * handle) { + int res; + uint8_t * data; + uint32_t size; + + res = hpcalcs_calc_check_ready(handle, &data, &size); + if (res == 0) { + output_log(stdout, "Check ready success\n"); + // TODO: do something with data & size. + } + else { + output_log(stdout, "hpcalcs_calc_check_ready failed\n"); + } + + return res; +} + +static int get_infos(calc_handle * handle) { + int res; + calc_infos infos; + + res = hpcalcs_calc_get_infos(handle, &infos); + if (res == 0) { + output_log(stdout, "Get infos success\n"); + // TODO: do something with infos. + } + else { + output_log(stdout, "hpcalcs_calc_get_infos failed\n"); + } + + return res; +} + +static int set_date_time(calc_handle * handle) { + int res; + + res = hpcalcs_calc_set_date_time(handle, time(NULL)); + if (res == 0) { + output_log(stdout, "Set date time success\n"); + } + else { + output_log(stdout, "hpcalcs_calc_set_date_time failed\n"); + } + + return res; +} + +static int recv_screen(calc_handle * handle) { + int res = 0; + uint8_t * data; + uint32_t size; + unsigned int format; + int err; + + output_log(stdout, "\nChoose a format (for Prime, usually 8 to 11): "); + + err = scanf("%u", &format); + if (err >= 1) { + char filename[1024]; + filename[0] = 0; + output_log(stdout, "\n%u\nEnter output filename: ", format); + err = scanf("%1023s", filename); + if (err >= 1) { + output_log(stdout, "\n%s\n", filename); + res = hpcalcs_calc_recv_screen(handle, format, &data, &size); + if (res == 0 && data != NULL) { + FILE * f; + output_log(stdout, "Receive screenshot success\n"); + f = fopen(filename, "w+b"); + if (f != NULL) { + fwrite(data, 1, size, f); + fclose(f); + } + else { + output_log(stdout, "Cannot open file for writing !\n"); + } + } + else { + output_log(stdout, "hpcalcs_calc_recv_screen failed\n"); + } + } + else { + fflush(stdin); + output_log(stdout, "Canceled\n"); + } + } + else { + fflush(stdin); + output_log(stdout, "Canceled\n"); + } + + return res; +} + +/*// On GCC 4.7+ and Clang, we can use the standard u"FOO" construct; otherwise, use L"x". +#if (defined(__GNUC__) && !defined(__clang__) && (__GNUC__ <= 4) && (__GNUC_MINOR__ < 7)) +#define UTF16(x) L##x +#else +#define UTF16(x) u##x +#endif*/ + +#define xstr(s) str(s) +#define str(s) #s + +static int send_file(calc_handle * handle) { + int res = 0; + int err; + files_var_entry * entry = NULL; + char filename[FILES_VARNAME_MAXLEN + 1]; + + output_log(stdout, "\nEnter input filename: "); + err = scanf("%" xstr(FILES_VARNAME_MAXLEN) "s", filename); + if (err >= 1) { + FILE * f = fopen(filename, "rb"); + if (f != NULL) { + uint32_t size; + uint8_t type; + fseek(f, 0, SEEK_END); + size = (uint32_t)ftell(f); + fseek(f, 0, SEEK_SET); + output_log(stdout, "Input file has size %" PRIu32 " (%" PRIx32 ")\n", size, size); + entry = hpfiles_ve_create_with_size(size); + if (entry != NULL) { + char * calcfilename = NULL; + if (!hpfiles_parsefilename(hpcalcs_get_model(handle), filename, &type, &calcfilename)) { + if (type != HPLIBS_FILE_TYPE_UNKNOWN && calcfilename != NULL) { + entry->type = type; + if (fread(entry->data, 1, size, f) == size) { + crude_convert_8bit_to_UTF16LE(calcfilename, entry->name); + // We can at last send the file ! + res = hpcalcs_calc_send_file(handle, entry); + if (res == 0 && entry != NULL) { + output_log(stdout, "hpcalcs_calc_send_file succeeded\n"); + } + else { + output_log(stdout, "hpcalcs_calc_send_file failed\n"); + } + } + else { + output_log(stdout, "Reading input file failed, aborted\n"); + } + free(calcfilename); + } + else { + output_log(stdout, "Unable to determine file type or calc filename, aborted (please report the bug !)\n"); + } + } + else { + output_log(stdout, "Unable to parse filename, aborted (please report the bug !)\n"); + } + hpfiles_ve_delete(entry); + } + else { + output_log(stdout, "Cannot create entry, aborted\n"); + } + } + else { + output_log(stdout, "Cannot open input file, aborted\n"); + } + } + else { + fflush(stdin); + output_log(stdout, "Canceled\n"); + } + + return res; +} + +static int recv_file(calc_handle * handle) { + int res = 0; + int err; + files_var_entry request; + files_var_entry * entry; + char typestr[11]; + wchar_t filename[FILES_VARNAME_MAXLEN + 1]; + + memset((void *)&request, 0, sizeof(request)); + output_log(stdout, "\nEnter input filename (without computer-side extension): "); + err = scanf("%" xstr(FILES_VARNAME_MAXLEN) "ls", filename); + if (err >= 1) { + output_log(stdout, "Enter file type: "); + + err = scanf("%10s", typestr); + if (err >= 1) { + uint8_t type = hpfiles_str2vartype(hpcalcs_get_model(handle), typestr); + if (type != HPLIBS_FILE_TYPE_UNKNOWN) { + /*const char * fext = hpfiles_vartype2fext(hpcalcs_get_model(handle), type); + if (fext != NULL && fext[0] != 0) { + wcscat(request.name, L"."); + crude_convert_8bit_to_UTF16LE(fext, request.name + wcslen(request.name) + 1); + }*/ + crude_convert_wchar_t_to_UTF16LE(filename, request.name); + request.type = type; + res = hpcalcs_calc_recv_file(handle, &request, &entry); + output_log(stdout, "hpcalcs_calc_recv_file finished\n"); + if (res == 0 && entry != NULL) { + produce_output_file(handle, entry); + hpfiles_ve_delete(entry); + } + else { + output_log(stdout, "hpcalcs_calc_recv_file failed\n"); + } + } + else { + output_log(stdout, "Unable to determine file type from stringe, aborted (please report the bug !)\n"); + } + } + else { + fflush(stdin); + output_log(stdout, "Canceled\n"); + } + } + else { + fflush(stdin); + output_log(stdout, "Canceled\n"); + } + + return res; +} + +static int recv_backup(calc_handle * handle) { + int res; + files_var_entry ** entries; + + res = hpcalcs_calc_recv_backup(handle, &entries); + if (res != 0) { + output_log(stdout, "hpcalcs_calc_recv_backup failed\nWill nevertheless attempt to salvage data, if possible\n"); + } + if (entries != NULL) { + files_var_entry ** ptr = entries; + while (*ptr != NULL) { + produce_output_file(handle, *ptr++); + } + hpfiles_ve_delete_array(entries); + } + else { + output_log(stdout, "hpcalcs_calc_recv_backup returned NULL entries, no data available\n"); + } + + return res; +} + +static int send_key(calc_handle * handle) { + int res = 0; + int err; + unsigned int type; + + output_log(stdout, "Enter key ID: "); + err = scanf("%u", &type); + if (err >= 1) { + res = hpcalcs_calc_send_key(handle, type); + if (res == 0) { + output_log(stdout, "hpcalcs_calc_send_key succeeded\n"); + } + else { + output_log(stdout, "hpcalcs_calc_send_key failed\n"); + } + } + else { + fflush(stdin); + output_log(stdout, "Canceled\n"); + } + + return res; +} + +#define STRBUF_SIZE 1024 +#define DATA_SIZE 512 +#define SEPARATORS ", \t\n" + +static int send_keys(calc_handle * handle) { + int res = 0; + int err; + char str[STRBUF_SIZE]; + uint8_t data[DATA_SIZE]; + uint32_t count = 0; + + fflush(stdin); + output_log(stdout, "Enter key IDs, separated by commas or spaces:"); + err = scanf("%" xstr(STRBUF_SIZE) "[0-9xa-fA-F, ]", str); + if (err >= 1) { + char * token = strtok(str, SEPARATORS); + if (token != NULL) { + int success = 1; + do { + unsigned long val; + char * endptr; + + errno = 0; + val = strtoul(token, &endptr, 0); + + if ((errno == ERANGE && val == ULONG_MAX) || (errno != 0 && val == 0)) { + output_log(stdout, "Item %" PRIu32 " is out of range, aborting\n", count); + success = 0; + break; + } + + if (endptr == str) { + output_log(stdout, "Item %" PRIu32 " is no number, aborting\n", count); + success = 0; + break; + } + + data[count++] = (uint8_t)val; + + token = strtok(NULL, SEPARATORS); + + } while (token != NULL); + + if (success) { + res = hpcalcs_calc_send_keys(handle, data, count); + if (res == 0) { + output_log(stdout, "hpcalcs_calc_send_keys succeeded\n"); + } + else { + output_log(stdout, "hpcalcs_calc_send_keys failed\n"); + } + } + } + else { + fflush(stdin); + output_log(stdout, "Failed to parse, canceled\n"); + } + } + else { + fflush(stdin); + output_log(stdout, "Canceled\n"); + } + + return res; +} + +#undef SEPARATORS +#undef DATA_SIZE +#undef STRBUF_SIZE + +static int send_chat(calc_handle * handle) { + int res = 0; + static const uint16_t chat_data[] = { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', 0 }; + + res = hpcalcs_calc_send_chat(handle, chat_data, sizeof(chat_data)); + if (res == 0) { + output_log(stdout, "hpcalcs_calc_send_chat succeeded\n"); + } + else { + output_log(stdout, "hpcalcs_calc_send_chat failed\n"); + } + + return res; +} + +static int recv_chat(calc_handle * handle) { + int res = 0; + uint32_t size; + uint16_t * data; + + res = hpcalcs_calc_recv_chat(handle, &data, &size); + if (res == 0) { + output_log(stdout, "hpcalcs_calc_recv_chat succeeded\n"); + // TODO: do something with chat data. + } + else { + output_log(stdout, "hpcalcs_calc_recv_chat failed\n"); + } + + return res; +} + +static int vpkt_send_experiments(calc_handle * handle) { + int res = 0; + int err; + unsigned int id; + + output_log(stdout, "Enter command ID: "); + err = scanf("%u", &id); + if (err >= 1) { + prime_vtl_pkt * pkt = prime_vtl_pkt_new(2); + if (pkt != NULL) { + uint8_t * ptr; + + pkt->cmd = (uint8_t)id; + ptr = pkt->data; + *ptr++ = (uint8_t)id; + res = prime_send_data(handle, pkt); + + if (res == 0) { + output_log(stdout, "prime_send_data succeeded\n"); + } + else { + output_log(stdout, "prime_send_data failed\n"); + } + + prime_vtl_pkt_del(pkt); + } + else { + output_log(stdout, "%s: couldn't create packet", __FUNCTION__); + } + } + else { + fflush(stdin); + output_log(stdout, "Canceled\n"); + } + + return res; +} + +#define NITEMS 13 + +static const char *str_menu[NITEMS] = { + "Exit", + "Check whether calc is ready", + "Get calculator information", + "Set date and time", + "Get screenshot", + "Send file", + "Receive file", + "Receive backup", + "Send key (single key)", + "Send keys (multiple keys)", + "Send chat", + "Receive chat", + "Virtual packet send experiments" +}; + +typedef int (*FNCT_MENU) (calc_handle*); + +static const FNCT_MENU fnct_menu[NITEMS] = { + NULL, + is_ready, + get_infos, + set_date_time, + recv_screen, + send_file, + recv_file, + recv_backup, + send_key, + send_keys, + send_chat, + recv_chat, + vpkt_send_experiments +}; + +static const hpfiles_config hpfiles_cfg = { + .version = HPFILES_CONFIG_VERSION, + .log_callback = output_log_callback, + .alloc_funcs = &alloc_funcs +}; +static const hpcables_config hpcables_cfg = { + .version = HPCABLES_CONFIG_VERSION, + .log_callback = output_log_callback, + .alloc_funcs = NULL +}; +static const hpcalcs_config hpcalcs_cfg = { + .version = HPCALCS_CONFIG_VERSION, + .log_callback = output_log_callback, + .alloc_funcs = &alloc_funcs +}; +static const hpopers_config hpopers_cfg = { + .version = HPOPERS_CONFIG_VERSION, + .log_callback = output_log_callback, + .alloc_funcs = NULL +}; + +int main(int argc, char **argv) { + cable_model model1 = CABLE_NUL; + calc_model model2 = CALC_NONE; + cable_handle * cable; + calc_handle * calc; + int res = 1; + uint8_t * probed_cables = NULL; + + // Set stdout and stderr to unbuffered mode. + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); + + output_log(stdout, "Entering program\n"); + + // Init libraries + if (hpfiles_init(&hpfiles_cfg)) { + goto final_teardown; + } + if (hpcables_init(&hpcables_cfg)) { + goto final_teardown; + } + if (hpcalcs_init(&hpcalcs_cfg)) { + goto final_teardown; + } + if (hpopers_init(&hpopers_cfg)) { + goto final_teardown; + } + + output_log(stdout, "Supported cables: 0x%" PRIX32 "\n", hpcables_supported_cables()); + output_log(stdout, "Supported calcs: 0x%" PRIX32 "\n", hpcalcs_supported_calcs()); + + if (model1 == CABLE_NUL) { + // Probe cables and calculators. + res = hpcables_probe_cables(&probed_cables); + if (res == 0) { + output_log(stdout, "Found no usable cables (?!): exiting\n"); + res = 1; + goto final_teardown; + } + else if (res == 1) { + // This means that the null cable is the only usable cable. Fall through. + model1 = CABLE_NUL; + hpcables_probe_free(probed_cables); + } + else { + int err; + + output_log(stdout, "hpcables_probe_cables found %d cables\n", res); + hpcables_log_set_callback(output_log_callback_stdout); + hpcables_probe_display(probed_cables); + hpcables_log_set_callback(output_log_callback); + hpcables_probe_free(probed_cables); + + output_log(stdout, "Enter the ID of the cable you want to use: "); + + err = scanf("%u", &model1); + if (err < 1) { + fflush(stdin); + output_log(stdout, "Canceled, exiting program\n"); + res = 1; + goto final_teardown; + } + } + } + + if (model1 == CABLE_NUL) { + output_log(stdout, "NOTE: null cable selected, the program won't do much in the way of useful things !\n"); + } + + output_log(stdout, "Initialized libraries\n"); + + cable = hpcables_handle_new(model1); + if (cable == NULL) { + output_log(stdout, "hpcables_handle_new failed\n"); + res = 1; + goto final_teardown; + } + + res = hpcalcs_probe_calc(model1, &model2); + if (res != 0) { + output_log(stdout, "hpcalcs_probe_calc failed\n"); + res = 1; + goto del_cable; + } + + calc = hpcalcs_handle_new(model2); + if (calc == NULL) { + output_log(stdout, "hpcalcs_handle_new failed\n"); + res = 1; + goto del_cable; + } + + // attach cable to calc (and open cable) + res = hpcalcs_cable_attach(calc, cable); + if (res) { + res = 1; + goto del_calc; + } + + if (model1 == CABLE_NUL) { + output_log(stdout, "NOTE: null cable selected, the program won't do much in the way of useful things !\n"); + } + + if (model2 == CALC_NONE) { + output_log(stdout, "NOTE: null calculator selected, the program won't do much in the way of useful things !\n"); + } + + do + { + int i; + int err; + unsigned int choice; + + // Display menu + output_log(stdout, "Choose an action:\n"); + for(i = 0; i < NITEMS; i++) { + output_log(stdout, "%2i. %s\n", i, str_menu[i]); + } + output_log(stdout, "Your choice: "); + + err = scanf("%u", &choice); + if (err < 1) { + fflush(stdin); + continue; + } + output_log(stdout, "\n"); + + if (choice == 0) { + break; + } + + // Process choice + if (choice < (int)(sizeof(fnct_menu)/sizeof(fnct_menu[0])) && fnct_menu[choice]) { + err = fnct_menu[choice](calc); + if (err) { + char * s; + err = hplibs_error_get(err, &s); + if (s != NULL) { + output_log(stdout, "%d %s", err, s); + free(s); + } + else { + output_log(stdout, "%d \n", err); + } + } + } + output_log(stdout, "\n"); + + } while(1); + + // detach cable + res = hpcalcs_cable_detach(calc); + + // remove calc & cable +del_calc: + hpcalcs_handle_del(calc); +del_cable: + hpcables_handle_del(cable); + +final_teardown: + output_log(stdout, "Exiting program\n"); + hpopers_exit(); + hpcalcs_exit(); + hpcables_exit(); + hpfiles_exit(); + + output_log(stdout, "Goodbye world!\n"); + return res; +} diff --git a/libhpcalcs/tests/torture_hpcalcs.c b/libhpcalcs/tests/torture_hpcalcs.c new file mode 100644 index 0000000..a2e806d --- /dev/null +++ b/libhpcalcs/tests/torture_hpcalcs.c @@ -0,0 +1,41 @@ +#include +#include +#include +#include +#include +#include +#include + +#define PRINTF(FUNCTION, TYPE, args...) \ +fprintf(stderr, "%d\t" TYPE "\n", i, FUNCTION(args)); i++ + +#define PRINTFVOID(FUNCTION, args...) \ +fprintf(stderr, "%d\n", i); FUNCTION(args); i++ + +#define INT "%d" +#define PTR "%p" +#define STR "\"%s\"" +#define VOID "" + +static void output_log_callback(const char *format, va_list args) { + vprintf(format, args); + fflush(stdout); +} + +int main(int argc, char **argv) { + int i = 1; + + hpfiles_init(NULL); + hpfiles_exit(); + + hpcables_init(NULL); + hpcables_exit(); + + hpcalcs_init(NULL); + hpcalcs_exit(); + + hpopers_init(NULL); + hpopers_exit(); + + return 0; +} diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..06f76d5 --- /dev/null +++ b/main.cpp @@ -0,0 +1,6 @@ +#include "main.h" + +main::main() +{ + +} diff --git a/main.h b/main.h new file mode 100644 index 0000000..23f7e35 --- /dev/null +++ b/main.h @@ -0,0 +1,11 @@ +#ifndef MAIN_H +#define MAIN_H + + +class main +{ +public: + main(); +}; + +#endif // MAIN_H diff --git a/mainwindow.cpp b/mainwindow.cpp new file mode 100644 index 0000000..49d64fc --- /dev/null +++ b/mainwindow.cpp @@ -0,0 +1,14 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow) +{ + ui->setupUi(this); +} + +MainWindow::~MainWindow() +{ + delete ui; +} diff --git a/mainwindow.h b/mainwindow.h new file mode 100644 index 0000000..a3948a9 --- /dev/null +++ b/mainwindow.h @@ -0,0 +1,22 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include + +namespace Ui { +class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + +private: + Ui::MainWindow *ui; +}; + +#endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui new file mode 100644 index 0000000..6050363 --- /dev/null +++ b/mainwindow.ui @@ -0,0 +1,24 @@ + + MainWindow + + + + 0 + 0 + 400 + 300 + + + + MainWindow + + + + + + + + + + + diff --git a/model.qmodel b/model.qmodel new file mode 100644 index 0000000..e070190 --- /dev/null +++ b/model.qmodel @@ -0,0 +1,55 @@ + + + + {fe28840b-a618-4a4d-ade6-3589798d4c2c} + + + + + + + + {c5070eb7-5f21-4a7f-a831-ee79fb344ca4} + + + model + + + + + + + {3fffd935-1b54-435e-ac46-2637a079e9a7} + + + + + + + + + + {3fffd935-1b54-435e-ac46-2637a079e9a7} + + + model + + + + + + + + + + + + + + + + + + + + diff --git a/qthpconnect.qrc b/qthpconnect.qrc new file mode 100644 index 0000000..90f4a83 --- /dev/null +++ b/qthpconnect.qrc @@ -0,0 +1,2 @@ + + diff --git a/texteditor.cpp b/texteditor.cpp new file mode 100644 index 0000000..5dbf702 --- /dev/null +++ b/texteditor.cpp @@ -0,0 +1,14 @@ +#include "texteditor.h" +#include "ui_texteditor.h" + +textEditor::textEditor(QWidget *parent) : + QDockWidget(parent), + ui(new Ui::textEditor) +{ + ui->setupUi(this); +} + +textEditor::~textEditor() +{ + delete ui; +} diff --git a/texteditor.h b/texteditor.h new file mode 100644 index 0000000..67bbcc2 --- /dev/null +++ b/texteditor.h @@ -0,0 +1,22 @@ +#ifndef TEXTEDITOR_H +#define TEXTEDITOR_H + +#include + +namespace Ui { +class textEditor; +} + +class textEditor : public QDockWidget +{ + Q_OBJECT + +public: + explicit textEditor(QWidget *parent = 0); + ~textEditor(); + +private: + Ui::textEditor *ui; +}; + +#endif // TEXTEDITOR_H diff --git a/treemodel.cpp b/treemodel.cpp new file mode 100644 index 0000000..e21b329 --- /dev/null +++ b/treemodel.cpp @@ -0,0 +1,6 @@ +#include "treemodel.h" + +treeModel::treeModel() +{ + +} diff --git a/treemodel.h b/treemodel.h new file mode 100644 index 0000000..6d79807 --- /dev/null +++ b/treemodel.h @@ -0,0 +1,12 @@ +#ifndef TREEMODEL_H +#define TREEMODEL_H + +#include + +class treeModel +{ +public: + treeModel(); +}; + +#endif // TREEMODEL_H \ No newline at end of file diff --git a/variableview.cpp b/variableview.cpp new file mode 100644 index 0000000..c2c8a38 --- /dev/null +++ b/variableview.cpp @@ -0,0 +1,14 @@ +#include "variableview.h" +#include "ui_variableview.h" + +variableView::variableView(QWidget *parent) : + QWidget(parent), + ui(new Ui::variableView) +{ + ui->setupUi(this); +} + +variableView::~variableView() +{ + delete ui; +} diff --git a/variableview.h b/variableview.h new file mode 100644 index 0000000..c89f59d --- /dev/null +++ b/variableview.h @@ -0,0 +1,22 @@ +#ifndef VARIABLEVIEW_H +#define VARIABLEVIEW_H + +#include + +namespace Ui { +class variableView; +} + +class variableView : public QWidget +{ + Q_OBJECT + +public: + explicit variableView(QWidget *parent = 0); + ~variableView(); + +private: + Ui::variableView *ui; +}; + +#endif // VARIABLEVIEW_H diff --git a/variableview.ui b/variableview.ui new file mode 100644 index 0000000..5ffce79 --- /dev/null +++ b/variableview.ui @@ -0,0 +1,22 @@ + + + + + + variableView + + + + 0 + 0 + 640 + 480 + + + + Form + + + + + diff --git a/vartablemodel.cpp b/vartablemodel.cpp new file mode 100644 index 0000000..4e7ee68 --- /dev/null +++ b/vartablemodel.cpp @@ -0,0 +1,6 @@ +#include "vartreemodel.h" + +varTreeModel::varTreeModel() +{ + +} diff --git a/vartablemodel.h b/vartablemodel.h new file mode 100644 index 0000000..ef61ea5 --- /dev/null +++ b/vartablemodel.h @@ -0,0 +1,11 @@ +#ifndef VARTREEMODEL_H +#define VARTREEMODEL_H + + +class varTreeModel +{ +public: + varTreeModel(); +}; + +#endif // VARTREEMODEL_H \ No newline at end of file