From 4d0f93932dfc13eb014ad0280ad4c333bcc9019e Mon Sep 17 00:00:00 2001 From: gb <741021719@qq.com> Date: Fri, 1 Dec 2023 17:17:09 +0800 Subject: [PATCH] New arhitecture --- scanner/async_scanner.cpp | 375 +++++ scanner/async_scanner.h | 51 + scanner/main.cpp | 45 + scanner/xmake.lua | 12 + sdk/base/data.cpp | 473 +++++++ sdk/base/data.h | 260 ++++ sdk/base/encrypt.cpp | 133 ++ sdk/base/encrypt.h | 45 + sdk/base/ini_file.cpp | 243 ++++ sdk/base/ini_file.h | 68 + sdk/base/packet.h | 379 +++++ sdk/base/plat_types.h | 125 ++ sdk/base/utils.cpp | 1942 ++++++++++++++++++++++++++ sdk/base/utils.h | 368 +++++ sdk/huagao/hgscanner_error.h | 380 ++++++ sdk/json/cJSON.c | 794 +++++++++++ sdk/json/cJSON.h | 157 +++ sdk/json/gb_json.cpp | 962 +++++++++++++ sdk/json/gb_json.h | 122 ++ sdk/sane/sane.h | 252 ++++ sdk/sane/sane_ex.h | 897 ++++++++++++ sdk/sane/sane_option_definitions.h | 508 +++++++ sdk/sane/sanei.h | 160 +++ sdk/sane/sanei_backend.h | 192 +++ sdk/sane/sanei_debug.h | 153 +++ sdk/sane_opt_json/base_opt.cpp | 97 ++ sdk/sane_opt_json/base_opt.h | 44 + sdk/sane_opt_json/device_opt.cpp | 2052 ++++++++++++++++++++++++++++ sdk/sane_opt_json/device_opt.h | 412 ++++++ sdk/sane_opt_json/simple_logic.cpp | 594 ++++++++ sdk/sane_opt_json/simple_logic.h | 79 ++ usb/buildconf.h | 12 + usb/camtp.h | 82 ++ usb/default_cfg.h | 64 + usb/usb_dev.cpp | 716 ++++++++++ usb/usb_dev.h | 56 + usb/usb_gadget.h | 176 +++ usb/usb_io.cpp | 636 +++++++++ usb/usb_io.h | 98 ++ usb/usbstring.h | 42 + usb/xmake.lua | 10 + xmake.lua | 73 + 42 files changed, 14339 insertions(+) create mode 100644 scanner/async_scanner.cpp create mode 100644 scanner/async_scanner.h create mode 100644 scanner/main.cpp create mode 100644 scanner/xmake.lua create mode 100644 sdk/base/data.cpp create mode 100644 sdk/base/data.h create mode 100644 sdk/base/encrypt.cpp create mode 100644 sdk/base/encrypt.h create mode 100644 sdk/base/ini_file.cpp create mode 100644 sdk/base/ini_file.h create mode 100644 sdk/base/packet.h create mode 100644 sdk/base/plat_types.h create mode 100644 sdk/base/utils.cpp create mode 100644 sdk/base/utils.h create mode 100644 sdk/huagao/hgscanner_error.h create mode 100644 sdk/json/cJSON.c create mode 100644 sdk/json/cJSON.h create mode 100644 sdk/json/gb_json.cpp create mode 100644 sdk/json/gb_json.h create mode 100644 sdk/sane/sane.h create mode 100644 sdk/sane/sane_ex.h create mode 100644 sdk/sane/sane_option_definitions.h create mode 100644 sdk/sane/sanei.h create mode 100644 sdk/sane/sanei_backend.h create mode 100644 sdk/sane/sanei_debug.h create mode 100644 sdk/sane_opt_json/base_opt.cpp create mode 100644 sdk/sane_opt_json/base_opt.h create mode 100644 sdk/sane_opt_json/device_opt.cpp create mode 100644 sdk/sane_opt_json/device_opt.h create mode 100644 sdk/sane_opt_json/simple_logic.cpp create mode 100644 sdk/sane_opt_json/simple_logic.h create mode 100644 usb/buildconf.h create mode 100644 usb/camtp.h create mode 100644 usb/default_cfg.h create mode 100644 usb/usb_dev.cpp create mode 100644 usb/usb_dev.h create mode 100644 usb/usb_gadget.h create mode 100644 usb/usb_io.cpp create mode 100644 usb/usb_io.h create mode 100644 usb/usbstring.h create mode 100644 usb/xmake.lua create mode 100644 xmake.lua diff --git a/scanner/async_scanner.cpp b/scanner/async_scanner.cpp new file mode 100644 index 0000000..5aefba6 --- /dev/null +++ b/scanner/async_scanner.cpp @@ -0,0 +1,375 @@ +#include "async_scanner.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + + + + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +async_scanner::async_scanner() : usb_(nullptr), cfg_mgr_(nullptr), scan_id_(0) +{ + utils::init_log(LOG_TYPE_FILE); + init(); + + auto bulk_handle = [&](dyn_mem_ptr data, uint32_t* used, packet_data_base_ptr* required) -> dyn_mem_ptr + { + LPPACK_BASE pack = (LPPACK_BASE)data->ptr(); + + if(used) + *used = data->get_rest(); + + return handle_bulk_cmd(pack, used, required); + }; + auto on_connect = [&](bool connected) -> void + { + utils::to_log(LOG_LEVEL_ALL, "USB %s\n", connected ? "connected" : "dis-connected"); + // if(!connected) + // stop_scan(); + }; + + auto user = [&](int priv) -> bool + { + return true; + }; + auto on_log = [&](const char* msg) -> void + { + utils::log_info(msg, LOG_LEVEL_DEBUG); + }; + cfg_mgr_ = new device_option(user, on_log); + init(); + usb_ = new async_usb_gadget(bulk_handle, on_connect); +} + +async_scanner::~async_scanner() +{ + if(usb_) + { + usb_->stop(); + usb_->release(); + } + if(cfg_mgr_) + { + cfg_mgr_->clear(); + delete cfg_mgr_; + } +} + +dyn_mem_ptr async_scanner::handle_bulk_cmd(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required) +{ + dyn_mem_ptr reply = nullptr; + LPPACK_BASE pk = nullptr; + size_t base_head_size = sizeof(PACK_BASE); + + if(pack->size != base_head_size) + { + if(used) + { + utils::log_mem_info("Invalid packet", (uint8_t*)pack, *used); + } + reply = dyn_mem::memory(base_head_size); + LPPACK_BASE p = (LPPACK_BASE)reply->ptr(); + BASE_PACKET_REPLY(*p, PACK_CMD_INVALID, pack->pack_id, pack->cmd); + reply->set_len(base_head_size); + + return reply; + } + + switch(pack->cmd) + { + case PACK_CMD_SYNC: + reply = handle_simple_roger(pack, used, required); + break; + case PACK_CMD_SETTING_GET_CUR: + reply = handle_get_opt_value(pack, used, required); + break; + case PACK_CMD_SETTING_GET: + reply = handle_get_opt_all(pack, used, required); + break; + case PACK_CMD_SETTING_SET: + reply = handle_set_opt(pack, used, required); + break; + case PACK_CMD_FILE_WRITE_REQ: + reply = handle_file_receive(pack, used, required); + break; + case PACK_CMD_FILE_READ_REQ: + reply = handle_file_send(pack, used, required); + break; + case PACK_CMD_SCAN_START: + reply = handle_scan_start(pack, used, required); + break; + case PACK_CMD_SCAN_STOP: + reply = handle_scan_stop(pack, used, required); + break; + default: + if(used) + { + utils::log_mem_info("Unsupported command packet", (uint8_t*)pack, *used); + } + reply = dyn_mem::memory(base_head_size); + LPPACK_BASE p = (LPPACK_BASE)reply->ptr(); + BASE_PACKET_REPLY(*p, pack->cmd + 1, pack->pack_id, EINVAL); + reply->set_len(base_head_size); + break; + } + + return reply; +} +void async_scanner::init(void) +{ +} + +dyn_mem_ptr async_scanner::handle_simple_roger(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required) +{ + uint32_t base_head_size = sizeof(PACK_BASE); + dyn_mem_ptr reply = dyn_mem::memory(base_head_size); + LPPACK_BASE pk = (LPPACK_BASE)reply->ptr(); + + *used = base_head_size; + BASE_PACKET_REPLY(*pk, pack->cmd + 1, pack->pack_id, 0); + reply->set_len(base_head_size); + + return reply; +} +dyn_mem_ptr async_scanner::handle_get_opt_value(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required) +{ + uint32_t base_head_size = sizeof(PACK_BASE); + dyn_mem_ptr reply = nullptr; + LPPACK_BASE pk = nullptr; + + if(*used < base_head_size + pack->payload_len) + { + *used = 0; + } + else + { + std::string val(cfg_mgr_->get_option_value(pack->payload, SANE_ACTION_GET_VALUE)); + uint32_t err = val.empty() ? SCANNER_ERR_NO_DATA : SCANNER_ERR_OK; + LPCFGVAL cfg = nullptr; + + reply = dyn_mem::memory(base_head_size + sizeof(CFGVAL) + strlen(pack->payload) + 1 + val.length() + 1); + pk = (LPPACK_BASE)reply->ptr(); + BASE_PACKET_REPLY(*pk, pack->cmd + 1, pack->pack_id, err); + cfg = (LPCFGVAL)pk->payload; + cfg->val_size = val.length(); + cfg->val_off = 0; + cfg->name_off = val.length() + 1; + pk->payload_len = sizeof(CFGVAL) + strlen(pack->payload) + 1 + val.length() + 1; + memcpy(cfg->data, val.c_str(), val.length()); + strcpy(cfg->data + cfg->name_off, pack->payload); + reply->set_len(base_head_size + pk->payload_len); + *used = base_head_size + pack->payload_len; + } + + return reply; +} +dyn_mem_ptr async_scanner::handle_get_opt_all(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required) +{ + uint32_t base_head_size = sizeof(PACK_BASE); + dyn_mem_ptr reply = nullptr; + LPPACK_BASE pk = nullptr; + + *used = base_head_size; + { + std::string val(cfg_mgr_->get_option_value(nullptr, SANE_ACTION_GET_ENTIRE_JSON)); + uint32_t err = 0; + + reply = dyn_mem::memory(base_head_size + val.length() + 1); + pk = (LPPACK_BASE)reply->ptr(); + BASE_PACKET_REPLY(*pk, pack->cmd + 1, pack->pack_id, err); + strcpy(pk->payload, val.c_str()); + pk->payload_len = val.length() + 1; + reply->set_len(base_head_size + val.length() + 1); + } + + return reply; +} +dyn_mem_ptr async_scanner::handle_set_opt(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required) +{ uint32_t base_head_size = sizeof(PACK_BASE); + dyn_mem_ptr reply = nullptr; + LPPACK_BASE pk = nullptr; + + if (*used < base_head_size + pack->payload_len) + *used = 0; + else + { + LPCFGVAL cfg = (LPCFGVAL)pack->payload, + cfg_ret = nullptr; + std::string name(cfg->data + cfg->name_off); + size_t l = base_head_size + sizeof(CFGVAL) + name.length() + 1 + cfg->max_size + 1, val_size = cfg->val_size; + int32_t err = 0; + uint32_t after = 0; + + reply = dyn_mem::memory(l); + pk = (LPPACK_BASE)reply->ptr(); + cfg_ret = (LPCFGVAL)pk->payload; + cfg_ret->name_off = 0; + strcpy(cfg_ret->data + cfg_ret->name_off, name.c_str()); + cfg_ret->val_off = name.length() + 1; + memcpy(cfg_ret->data + cfg_ret->val_off, cfg->data + cfg->val_off, cfg->val_size); + cfg_ret->val_size = cfg->val_size; + cfg_ret->max_size = cfg->max_size; + cfg_ret->type = cfg->type; + err = cfg_mgr_->update_data(cfg->data + cfg->name_off, cfg_ret->data + cfg_ret->val_off); + cfg_ret->after_do = after; + cfg_ret->val_size = val_size; + BASE_PACKET_REPLY(*pk, pack->cmd + 1, pack->pack_id, err); + pk->payload_len = sizeof(CFGVAL) + name.length() + 1 + cfg->max_size + 1; + reply->set_len(l); + } + + return reply; +} +dyn_mem_ptr async_scanner::handle_file_receive(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required) +{ + uint32_t base_head_size = sizeof(PACK_BASE); + dyn_mem_ptr reply = nullptr; + LPPACK_BASE pk = nullptr; + + if(*used < pack->payload_len + pack->size) + { + *used = 0; + } + else + { + LPTXFILE pfi = (LPTXFILE)pack->payload; + std::string path(pfi->path); + int err = 0; + file_saver *saver = new file_saver(); + + err = saver->open(path.c_str(), pfi->size); + reply = dyn_mem::memory(base_head_size); + reply->set_len(base_head_size); + BASE_PACKET_REPLY(*((LPPACK_BASE)reply->ptr()), pack->cmd + 1, pack->pack_id, err); + *used = base_head_size + pack->payload_len; + if(err) + { + saver->release(); + saver = nullptr; + } + else + { + saver->set_packet_param(pack->cmd, pack->pack_id); + } + utils::to_log(LOG_LEVEL_DEBUG, "receive file (%u bytes): %s = %d\n", pfi->size, path.c_str(), err); + *required = dynamic_cast(saver); + } + + return reply; +} +dyn_mem_ptr async_scanner::handle_file_send(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required) +{ + uint32_t base_head_size = sizeof(PACK_BASE); + dyn_mem_ptr reply = dyn_mem::memory(base_head_size); + LPPACK_BASE pk = (LPPACK_BASE)reply->ptr(); + + if(*used < pack->payload_len + pack->size) + { + *used = 0; + } + else + { + LPTXFILE pfi = (LPTXFILE)pack->payload; + std::string path(pfi->path); + int err = 0; + file_reader *reader = new file_reader(); + + err = reader->open(path.c_str()); + reply = dyn_mem::memory(base_head_size + sizeof(TXFILE)); + reply->set_len(base_head_size + sizeof(TXFILE)); + BASE_PACKET_REPLY(*((LPPACK_BASE)reply->ptr()), pack->cmd + 1, pack->pack_id, err); + *used = base_head_size + pack->payload_len; + utils::to_log(LOG_LEVEL_DEBUG, "To send file '%s' with %u bytes = %d\n", path.c_str(), reader->get_rest(), err); + if(err) + { + reader->release(); + reader = nullptr; + } + else + { + ((LPPACK_BASE)reply->ptr())->payload_len = sizeof(TXFILE); + ((LPTXFILE)((LPPACK_BASE)reply->ptr())->payload)->size = reader->get_rest(); + reader->set_packet_param(pack->cmd, pack->pack_id); + } + *required = dynamic_cast(reader); + } + + return reply; +} +dyn_mem_ptr async_scanner::handle_scan_start(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required) +{ + if(!used) + { + reply_start_ = true; + return nullptr; + } + + uint32_t base_head_size = sizeof(PACK_BASE); + dyn_mem_ptr reply = dyn_mem::memory(base_head_size); + + img_cnt_ = 0; + scan_id_ = pack->pack_id; + scan_err_ = 0; + reply_start_ = false; + + *used = base_head_size; + reply->set_len(base_head_size); + // scan_err_ = capture_->start(); + BASE_PACKET_REPLY(*((LPPACK_BASE)reply->ptr()), pack->cmd + 1, pack->pack_id, scan_err_); + *used |= INT32_MAX + 1; + + return reply; +} +dyn_mem_ptr async_scanner::handle_scan_stop(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required) +{ + uint32_t base_head_size = sizeof(PACK_BASE); + dyn_mem_ptr reply = dyn_mem::memory(base_head_size); + int err = 0; + + utils::to_log(LOG_LEVEL_DEBUG, "Received command Stop-Scan.\n"); + // err = capture_->stop(); + BASE_PACKET_REPLY(*((LPPACK_BASE)reply->ptr()), pack->cmd + 1, pack->pack_id, err); + reply->set_len(base_head_size); + *used = base_head_size; + + return reply; +} +dyn_mem_ptr async_scanner::handle_process_cmd(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required) +{ + uint32_t base_head_size = sizeof(PACK_BASE); + dyn_mem_ptr reply = dyn_mem::memory(base_head_size); + LPPACK_BASE pk = (LPPACK_BASE)reply->ptr(); + + *used = base_head_size + pack->payload_len; + BASE_PACKET_REPLY(*pk, pack->cmd + 1, pack->pack_id, EINVAL); + reply->set_len(base_head_size); + + return reply; +} + + +uint32_t async_scanner::stop(void) +{ + if(usb_) + { + usb_->stop(); + } +} + diff --git a/scanner/async_scanner.h b/scanner/async_scanner.h new file mode 100644 index 0000000..6982122 --- /dev/null +++ b/scanner/async_scanner.h @@ -0,0 +1,51 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + + + +class async_usb_gadget; +class device_option; +class image_capture; +class img_processor; +class gb_json; + +class async_scanner : public refer +{ + async_usb_gadget *usb_; + device_option *cfg_mgr_; + + MUTEX locker_; + uint32_t img_cnt_; + uint32_t scan_id_; + uint32_t scan_err_; + volatile bool reply_start_; + + dyn_mem_ptr handle_bulk_cmd(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required); + void init(void); + + dyn_mem_ptr handle_simple_roger(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required); + dyn_mem_ptr handle_get_opt_value(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required); + dyn_mem_ptr handle_get_opt_all(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required); + dyn_mem_ptr handle_set_opt(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required); + dyn_mem_ptr handle_file_receive(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required); + dyn_mem_ptr handle_file_send(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required); + dyn_mem_ptr handle_scan_start(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required); + dyn_mem_ptr handle_scan_stop(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required); + dyn_mem_ptr handle_process_cmd(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required); + +public: + async_scanner(); + +protected: + ~async_scanner(); + +public: + uint32_t stop(void); +}; diff --git a/scanner/main.cpp b/scanner/main.cpp new file mode 100644 index 0000000..735e14d --- /dev/null +++ b/scanner/main.cpp @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "async_scanner.h" + +#define BUF_LEN_FOR_PID 64 + + +static void sigHandler(int sig) +{ + // if (sig == SIGINT || sig == SIGTERM) + // remove(MY_PID_FILE); + printf("exit now for signal: %d\n", sig); + _exit(0); +} + + +int main() +{ + /* Ctrl + C */ + if (signal(SIGINT, sigHandler) == SIG_ERR) + { + exit(-1); + } + + /* kill pid / killall name */ + if (signal(SIGTERM, sigHandler) == SIG_ERR) + { + exit(-1); + } + async_scanner *scanner = new async_scanner(); + + while(1) {std::this_thread::sleep_for(std::chrono::milliseconds(2));} + return 0; +} \ No newline at end of file diff --git a/scanner/xmake.lua b/scanner/xmake.lua new file mode 100644 index 0000000..6c42f90 --- /dev/null +++ b/scanner/xmake.lua @@ -0,0 +1,12 @@ +add_rules("mode.debug", "mode.release") + +target("hgscanner") + set_kind("binary") + add_syslinks("pthread", "dl") + add_includedirs("../sdk", "../usb") + add_files("*.cpp", "../sdk/base/*.c*", "../sdk/imgprc/*.c*", "../sdk/json/*.c*", "../sdk/sane_opt_json/*.c*") + --add_deps("gusb", "applog") + --add_rules("utils.bin2c",{linewidth = 32,extension = {".bin"}}) + --add_files("table.bin") + add_packages("sdk") + add_deps("usb") \ No newline at end of file diff --git a/sdk/base/data.cpp b/sdk/base/data.cpp new file mode 100644 index 0000000..f8fbac4 --- /dev/null +++ b/sdk/base/data.cpp @@ -0,0 +1,473 @@ +#include "data.h" + + +#include + +#if defined(WIN32) || defined(_WIN64) +#else +#include +#include +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +packet_data_base::packet_data_base() : pack_cmd_(0), pack_id_(0) + , progress_notify_(PROGRESS_NOTIFYER()), user_data_(nullptr) +{} +packet_data_base::~packet_data_base() +{} + +int packet_data_base::notify_progress(uint64_t total, uint64_t cur_size, uint32_t err) +{ + if (progress_notify_) + progress_notify_(total, cur_size, err, user_data_); + else + return ENOENT; +} + +void packet_data_base::set_packet_param(uint32_t cmd, uint32_t id) +{ + pack_cmd_ = cmd; + pack_id_ = id; +} +int packet_data_base::get_packet_command(void) +{ + return pack_cmd_; +} +int packet_data_base::get_packet_id(void) +{ + return pack_id_; +} + +void packet_data_base::set_progress_notify(PROGRESS_NOTIFYER notify, void* param) +{ + progress_notify_ = notify; + user_data_ = param; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +data_holder::data_holder() +{} +data_holder::~data_holder() +{} + + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +mem_holder::mem_holder(size_t size) : space_(size) +{ + buf_ = (uint8_t*)malloc(size); + if (buf_) + memset(buf_, 0, size); +} +mem_holder::~mem_holder() +{ + if (buf_) + free(buf_); + buf_ = nullptr; +} + +int mem_holder::put_data(const void* data, uint32_t* size) +{ + if (*size > space_ - wpos_) + *size = space_ - wpos_; + + memcpy(buf_ + wpos_, data, *size); + wpos_ += *size; + + return 0; +} +bool mem_holder::is_complete(void) +{ + return wpos_ >= space_; +} +uint32_t mem_holder::get_required(void) +{ + if (wpos_ >= space_) + return 0; + else + return space_ - wpos_; +} + +size_t mem_holder::data_length(void) +{ + return wpos_; +} +uint8_t* mem_holder::data(void) +{ + return buf_; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +empty_holer::empty_holer(uint64_t size) : size_(size), put_(0) +{} +empty_holer::~empty_holer() +{} + +int empty_holer::put_data(const void* data, uint32_t* size) +{ + if (*size >= size_ - put_) + { + *size -= size_ - put_; + put_ = size_; + } + else + { + put_ += *size; + } + notify_progress(size_, put_, 0); + + return 0; +} +bool empty_holer::is_complete(void) +{ + return size_ == put_; +} +uint32_t empty_holer::get_required(void) +{ + return size_ - put_; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +file_saver::file_saver(void) : size_(0), wrote_(0), path_(""), check_(""), dst_(nullptr), pack_cmd_(0), pack_id_(0) +{} +file_saver::~file_saver() +{ + close(); +} + +void file_saver::close(void) +{ + if(dst_) + fclose(dst_); + dst_ = nullptr; + + size_ = wrote_ = pack_cmd_ = pack_id_ = 0; + path_ = check_ = ""; +} + +int file_saver::open(const char* path, uint64_t size, const char* check) +{ + int err = 0; + + close(); + dst_ = fopen(path, "wb"); + if(dst_) + { + unsigned long long space = 0; + + err = utils::get_disk_space(path, nullptr, &space, nullptr); + if (err || space < size * 1.5) + { + fclose(dst_); + dst_ = nullptr; + remove(path); + if (err == 0) + err = ENOSPC; + } + else + { + path_ = path; + size_ = size; + check_ = check ? check : ""; + } + } + else + { + err = errno; + } + + return err; +} + +int file_saver::put_data(const void* data, uint32_t* size/*[in] - total bytes of data; [out] - used bytes*/) +{ + if(!dst_) + return ENOENT; + + int w = *size > size_ - wrote_ ? size_ - wrote_ : *size, + real_w = fwrite(data, 1, w, dst_), // should handle error here ! + err = 0; + + *size = real_w; + wrote_ += real_w; + if(wrote_ >= size_) + { + fclose(dst_); + dst_ = nullptr; + } + else if (real_w < w) // what happens ? + { + err = ferror(dst_); + } + notify_progress(size_, wrote_, err); + + return 0; +} +bool file_saver::is_complete(void) +{ + return wrote_ >= size_; +} +uint32_t file_saver::get_required(void) +{ + return size_ - wrote_; +} + + + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +data_source::data_source() +{} +data_source::~data_source() +{} + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// dyn_mem +uint64_t dyn_mem::mem_used_bytes_ = 0; +MUTEX dyn_mem::mem_lock_; + +dyn_mem::dyn_mem(size_t size) : buf_(nullptr), len_(0), space_(ALIGN_TO(size, 16)) +{ + buf_ = (uint8_t*)malloc(space_); + if (buf_) + { + SIMPLE_LOCK(dyn_mem::mem_lock_); + dyn_mem::mem_used_bytes_ += space_; + memset(buf_, 0, space_); + } +} +dyn_mem::dyn_mem(void* buf, size_t size) : buf_((uint8_t*)buf), space_(size), len_(size) +{ + +} + +dyn_mem::~dyn_mem() +{ + if (buf_) + { + free(buf_); + { + SIMPLE_LOCK(dyn_mem::mem_lock_); + dyn_mem::mem_used_bytes_ -= space_; + } + } +} + +uint64_t dyn_mem::mem_used(void) +{ + return dyn_mem::mem_used_bytes_; +} +dyn_mem_ptr dyn_mem::memory(size_t size) +{ + return new dyn_mem(size); +} + +uint32_t dyn_mem::space(void) +{ + return space_; +} +bool dyn_mem::set_len(size_t len) +{ + if (len > space_) + return false; + + len_ = len; + + return true; +} +int dyn_mem::put(const void* data, int len) +{ + if (len + len_ > space_) + len = space_ - len; + + if (len > 0) + { + memcpy(buf_ + len_, data, len); + len_ += len; + } + else + len = 0; + + return len; +} +void* dyn_mem::detach(size_t* size) +{ + void* buf = buf_; + + if (size) + *size = space_; + space_ = len_ = 0; + buf_ = nullptr; + + return buf; +} + +size_t dyn_mem::used(size_t len) +{ + if (len >= len_) + { + len_ = 0; + } + else if (len) + { + memcpy(buf_, buf_ + len, len_ - len); + len_ -= len; + } + + return len_; +} +dyn_mem& dyn_mem::operator+=(dyn_mem& r) +{ + if (len_ + r.get_rest() > space_) + { + size_t size = ALIGN_TO(len_ + r.get_rest(), 16); + uint8_t* buf = (uint8_t*)malloc(size); + memcpy(buf, buf_, len_); + free(buf_); + buf_ = buf; + { + SIMPLE_LOCK(dyn_mem::mem_lock_); + dyn_mem::mem_used_bytes_ += size - space_; + } + space_ = size; + } + memcpy(buf_ + len_, r.buf_, r.get_rest()); + len_ += r.get_rest(); + + return *this; +} + +bool dyn_mem::is_memory_block(void) +{ + return true; +} +uint32_t dyn_mem::get_rest(void) +{ + return len_; +} + +// following API valid when is_memory_block() return true +uint8_t* dyn_mem::ptr(void) +{ + return buf_; +} + +// following API valid when is_memory_block() return false +int dyn_mem::fetch_data(void* buf, uint32_t* size) +{ + if (*size >= len_) + { + memcpy(buf, buf_, len_); + *size = len_; + len_ = 0; + } + else + { + memcpy(buf, buf_, *size); + used(*size); + } + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +file_reader::file_reader() : len_(0), src_(nullptr), path_(""), consume_(0) +{} +file_reader::~file_reader() +{ + if(src_) + fclose(src_); +} + +int file_reader::open(const char* file) +{ + if(src_) + fclose(src_); + + src_ = fopen(file, "rb"); + if(!src_) + return errno; + + FSEEK(src_, 0, SEEK_END); + len_ = FTELL(src_); + FSEEK(src_, 0, SEEK_SET); + path_ = file; + consume_ = 0; + + return 0; +} +int file_reader::attach(FILE* f) +{ + if (src_) + { + fclose(src_); + src_ = nullptr; + } + + uint64_t cur = FTELL(f); + + FSEEK(f, 0, SEEK_END); + len_ = FTELL(f); + FSEEK(f, cur, SEEK_SET); + if (len_ <= cur) + return EINVAL; + + src_ = f; + len_ -= cur; + consume_ = 0; + + return 0; +} +FILE* file_reader::detach(void) +{ + FILE* ret = src_; + + src_ = nullptr; + len_ = 0; + path_ = ""; + + return ret; +} + +bool file_reader::is_memory_block(void) +{ + return false; +} +uint32_t file_reader::get_rest(void) +{ + return len_ - consume_; +} + +// following API valid when is_memory_block() return true +uint8_t* file_reader::ptr(void) +{ + return nullptr; +} + +// following API valid when is_memory_block() return false +int file_reader::fetch_data(void* buf, uint32_t* size) +{ + if (!src_) + return ENODATA; + + size_t r = fread(buf, 1, *size, src_); // fix me if ERROR occurs !!! + + consume_ += r; + *size = r; + if (consume_ >= len_) + { + fclose(src_); + src_ = nullptr; + } + notify_progress(len_, consume_, 0); + + return 0; +} diff --git a/sdk/base/data.h b/sdk/base/data.h new file mode 100644 index 0000000..e3e6a50 --- /dev/null +++ b/sdk/base/data.h @@ -0,0 +1,260 @@ +#pragma once + +// Objects IO +// +// created on 2023-03-10 + +#include "utils.h" +#include "packet.h" + +#include +#include + +#define CLS_PTR(cls) typedef cls* cls##_ptr; + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +/* packet parameter keeper, parameter of corresponding packet +*/ +#define PROGRESS_NOTIFYER std::function + +class packet_data_base : public refer +{ + PROGRESS_NOTIFYER progress_notify_; + void* user_data_; + +protected: + uint32_t pack_cmd_; + uint32_t pack_id_; + +public: + packet_data_base(); + +protected: + virtual ~packet_data_base(); + int notify_progress(uint64_t total, uint64_t cur_size, uint32_t err); + +public: + void set_packet_param(uint32_t cmd, uint32_t id); + int get_packet_command(void); + int get_packet_id(void); + + void set_progress_notify(PROGRESS_NOTIFYER notify = PROGRESS_NOTIFYER(), void* param = nullptr); +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +/* data_holder, used when data is also required for a certain packet +*/ +class data_holder : public packet_data_base +{ +public: + data_holder(); +protected: + virtual ~data_holder(); + +public: + virtual int put_data(const void* data, uint32_t* size/*[in] - total bytes of data; [out] - used bytes*/) = 0; // return error code + virtual bool is_complete(void) = 0; + virtual uint32_t get_required(void) = 0; +}; + +class mem_holder : public data_holder +{ + uint8_t* buf_ = nullptr; + size_t space_ = 0; + size_t wpos_ = 0; + +public: + mem_holder(size_t size); + +protected: + ~mem_holder(); + + // data_holder +public: + virtual int put_data(const void* data, uint32_t* size/*[in] - total bytes of data; [out] - used bytes*/) override; // return error code + virtual bool is_complete(void) override; + virtual uint32_t get_required(void) override; + +public: + size_t data_length(void); + uint8_t* data(void); +}; + +class empty_holer : public data_holder +{ + uint64_t size_; + uint64_t put_; + +public: + empty_holer(uint64_t size); +protected: + ~empty_holer(); + + +public: + virtual int put_data(const void* data, uint32_t* size/*[in] - total bytes of data; [out] - used bytes*/) override; // return error code + virtual bool is_complete(void) override; + virtual uint32_t get_required(void) override; +}; + +class file_saver : public data_holder +{ + uint64_t size_; + uint64_t wrote_; + std::string path_; + std::string check_; + FILE *dst_; + uint32_t pack_cmd_; + uint32_t pack_id_; + + void close(void); + +public: + file_saver(void); +protected: + ~file_saver(); + +public: + int open(const char* path, uint64_t size, const char* check = nullptr); + +public: + virtual int put_data(const void* data, uint32_t* size/*[in] - total bytes of data; [out] - used bytes*/) override; + virtual bool is_complete(void) override; + virtual uint32_t get_required(void) override; +}; + + + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +/* data_source, can be a memory block or STREAM object +*/ +class data_source : public packet_data_base +{ + uint32_t pack_cmd_; + uint32_t pack_id_; + +public: + data_source(); + +protected: + virtual ~data_source(); + +public: + virtual bool is_memory_block(void) = 0; + virtual uint32_t get_rest(void) = 0; + + // following API valid when is_memory_block() return true + virtual uint8_t* ptr(void) = 0; + + // following API valid when is_memory_block() return false. return error code + virtual int fetch_data(void* buf, uint32_t* size) = 0; +}; + + +class dyn_mem : public data_source +{ + uint8_t* buf_; // data buf + size_t space_; // occupy space in bytes + size_t len_; // data length in bytes + + static MUTEX mem_lock_; + static uint64_t mem_used_bytes_; + +public: + dyn_mem(size_t size); + dyn_mem(void* buf, size_t size); + + static uint64_t mem_used(void); + static dyn_mem* memory(size_t size); + +protected: + ~dyn_mem(); + +public: + uint32_t space(void); + bool set_len(size_t len); + int put(const void* data, int len); + void* detach(size_t* size); // for constructed from dyn_mem(void* buf, size_t size) + + size_t used(size_t len); // used len bytes content, move following data to head and set data length, return rest data length + dyn_mem& operator+=(dyn_mem& r); + + // data_source +public: + virtual bool is_memory_block(void) override; + virtual uint32_t get_rest(void) override; + + // following API valid when is_memory_block() return true + virtual uint8_t* ptr(void) override; + + // following API valid when is_memory_block() return false + virtual int fetch_data(void* buf, uint32_t* size) override; +}; + +class file_reader : public data_source +{ + size_t len_; + size_t consume_; + FILE *src_; + std::string path_; + +public: + file_reader(); + +protected: + ~file_reader(); + +public: + int open(const char* file); + int attach(FILE* f); + FILE* detach(void); + +public: + virtual bool is_memory_block(void) override; + virtual uint32_t get_rest(void) override; + + // following API valid when is_memory_block() return true + virtual uint8_t* ptr(void) override; + + // following API valid when is_memory_block() return false + virtual int fetch_data(void* buf, uint32_t* size) override; +}; + +CLS_PTR(packet_data_base); +CLS_PTR(data_holder); +CLS_PTR(mem_holder); +CLS_PTR(data_source); +CLS_PTR(dyn_mem); +CLS_PTR(file_reader); + + +// callback proto +// +// parameters: usb_functionfs_event* - the function event ptr +// +// dyn_mem_ptr - the packet buffer, read-only +// +// uint32_t* - to return how many data in bytes the handler consumed, the most high bit is to indicate whether should notify the returned packet has sent +// +// normally, the value should be sizeof(PACK_BASE) + PACK_BASE::payload_len, i.e. the handler consume all data of an entire packet +// +// when invalid packet, suggest use the entire data +// +// packet_data_base_ptr* - return data_holder or data_source or nullptr £¨The number of bytes required for this packet, 0 is over for this packet£© +// +// data_holder: the packet/command need more data than dyn_mem_ptr provides to complete the business. such as 'write a large file' +// +// data_source: the reply content may be a large data (a large file content) +// +// return value of all routines is the reply packet, nullptr if the packet need not reply +// +// NOTE: when parameter uint32_t* and packet_data_base_ptr* both are nullptr, it is notifying the command reply packet has sent, callback should return nullptr only +// +#define FUNCTION_PROTO_PARAMETERS dyn_mem_ptr, uint32_t*, packet_data_base_ptr* +#define FUNCTION_PROTO_COMMAND_HANDLE dyn_mem_ptr(FUNCTION_PROTO_PARAMETERS) + diff --git a/sdk/base/encrypt.cpp b/sdk/base/encrypt.cpp new file mode 100644 index 0000000..2bcb000 --- /dev/null +++ b/sdk/base/encrypt.cpp @@ -0,0 +1,133 @@ +#include "encrypt.h" + + + + + + + + + + + + + + + + + + + + + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// exporting api: +dyn_mem_ptr packet_encrypt(dyn_mem_ptr packet, uint32_t cmd_type, uint32_t type, uint8_t enc_data) +{ + dyn_mem_ptr ret = packet; + LPPACK_BASE pack = (LPPACK_BASE)packet->ptr(); + + ret->add_ref(); + if (cmd_type == ENCRYPT_CMD_XOR_PID) + pack->cmd ^= pack->pack_id; + else if (cmd_type == ENCRYPT_CMD_ADD_PID) + pack->cmd += pack->pack_id; + else if (cmd_type == ENCRYPT_CMD_SUB_PID) + pack->cmd -= pack->pack_id; + + pack->enc_cmd = cmd_type; + if (type != ENCRYPT_NONE) + { + std::string cont(""); + if (type == ENCRYPT_BASE64) + { + + } + else if (type == ENCRYPT_AES) + { + + } + else if (type == ENCRYPT_ZIP) + { + + } + + if (cont.length()) + { + // space is enough ? + if (cont.length() + sizeof(PACK_BASE) > packet->space()) + { + ret->release(); + ret = dyn_mem::memory(sizeof(PACK_BASE) + cont.length()); + memcpy(ret->ptr(), packet->ptr(), sizeof(PACK_BASE)); + pack = (LPPACK_BASE)ret->ptr(); + } + + // copy cipher text and set encrypt type ... + memcpy(pack->payload, cont.c_str(), cont.length()); + ret->set_len(sizeof(PACK_BASE) + cont.length()); + pack->payload_len = cont.length(); + pack->encrypt = type; + pack->enc_data = enc_data; + } + } + + return ret; +} +dyn_mem_ptr packet_decrypt(dyn_mem_ptr packet) +{ + dyn_mem_ptr ret = packet; + LPPACK_BASE pack = (LPPACK_BASE)packet->ptr(); + + ret->add_ref(); + if (pack->enc_cmd == ENCRYPT_CMD_XOR_PID) + pack->cmd ^= pack->pack_id; + else if (pack->enc_cmd == ENCRYPT_CMD_ADD_PID) + pack->cmd -= pack->pack_id; + else if (pack->enc_cmd == ENCRYPT_CMD_SUB_PID) + pack->cmd += pack->pack_id; + + if (pack->encrypt != ENCRYPT_NONE && pack->payload_len && packet->get_rest() >= sizeof(PACK_BASE) + pack->payload_len) + { + std::string cont(""); + if (pack->encrypt == ENCRYPT_BASE64) + { + + } + else if (pack->encrypt == ENCRYPT_AES) + { + + } + else if (pack->encrypt == ENCRYPT_ZIP) + { + + } + + if (cont.length()) + { + // have out-packet data ? + if (packet->get_rest() > sizeof(PACK_BASE) + pack->payload_len) + cont += std::string(pack->payload + pack->payload_len, packet->get_rest() - (sizeof(PACK_BASE) + pack->payload_len)); + + // space is enough ? + if (cont.length() + sizeof(PACK_BASE) > packet->space()) + { + ret->release(); + ret = dyn_mem::memory(sizeof(PACK_BASE) + cont.length()); + memcpy(ret->ptr(), packet->ptr(), sizeof(PACK_BASE)); + pack = (LPPACK_BASE)ret->ptr(); + } + + // copy plain text and set encrypt type to none ... + memcpy(pack->payload, cont.c_str(), cont.length()); + ret->set_len(sizeof(PACK_BASE) + cont.length()); + pack->encrypt = ENCRYPT_NONE; + pack->payload_len = cont.length(); + } + } + + return ret; +} + diff --git a/sdk/base/encrypt.h b/sdk/base/encrypt.h new file mode 100644 index 0000000..ffe1bb9 --- /dev/null +++ b/sdk/base/encrypt.h @@ -0,0 +1,45 @@ +#pragma once + +// Objects for encrypting/decrypting +// +// created on 2023-04-04 + + +#include "data.h" + + +enum encryptor +{ + ENCRYPT_NONE = 0, + ENCRYPT_BASE64, + ENCRYPT_AES, + ENCRYPT_ZIP, +}; +enum encrypt_cmd +{ + ENCRYPT_CMD_NONE = 0, + ENCRYPT_CMD_XOR_PID, // cmd ^= pack_id + ENCRYPT_CMD_ADD_PID, // cmd += pack_id + ENCRYPT_CMD_SUB_PID, // cmd -= pack_id +}; + +// Function: encrypting & decrypting packet +// +// Parameters: packet - pointer to the base packet +// +// data - cipher/plain data +// +// size - [in]: bytes of source 'data'; +// +// cmd_type - member 'cmd' encrypting method +// +// type - payload encrypting method +// +// enc_data - encrypting data for payload encrypting method +// +// Return: 'cmd' returned on origin packet, and returning value is for payload only. nullptr on failure +// +// NOTE: nullptr also returned if data was nullptr or size was ZERO +// +dyn_mem_ptr packet_encrypt(dyn_mem_ptr packet, uint32_t cmd_type = ENCRYPT_CMD_NONE, uint32_t type = ENCRYPT_NONE, uint8_t enc_data = 0); +dyn_mem_ptr packet_decrypt(dyn_mem_ptr packet); diff --git a/sdk/base/ini_file.cpp b/sdk/base/ini_file.cpp new file mode 100644 index 0000000..e9c3a4c --- /dev/null +++ b/sdk/base/ini_file.cpp @@ -0,0 +1,243 @@ + +#include "ini_file.h" + +#include +#include + + + +#if defined(WIN32) || defined(_WIN64) +#define bzero(b, s) memset(b, 0, s) +#endif + + +simple_ini::simple_ini() +{} +simple_ini::~simple_ini() +{} + + +std::string simple_ini::temporary_path(void) +{ +#if defined(WIN32) || defined(_WIN64) + char path[MAX_PATH] = { 0 }; + + if (GetTempPathA(_countof(path) - 1, path)) + { + if (path[lstrlenA(path) - 1] == '\\') + path[lstrlenA(path) - 1] = 0; + } + return path; +#else + return "/tmp"; +#endif +} +bool simple_ini::skip_empty(char*& ptr) +{ + char* p = ptr; + while(*ptr == ' ' || *ptr == '\t' || *ptr == '\r' || *ptr == '\n') + ptr++; + + return ptr > p; +} +void simple_ini::trime(char*& ptr) +{ + skip_empty(ptr); + + char* tail = ptr + strlen(ptr) - 1; + while (tail >= ptr) + { + if (*tail != ' ' && *tail != '\t' && *tail != '\r' && *tail != '\n') + break; + tail--; + } + tail[1] = 0; +} + +int simple_ini::load(const char* local_file) +{ + int ret = 0; +#if defined(WIN32) || defined(_WIN64) + file_ = local_file; +#else + values_.clear(); + + FILE* src = fopen(local_file, "rb"); + if (src) + { + char line[256]; + char* oper = NULL; + SECKEY sec; + + bzero(line, sizeof(line)); + while (fgets(line, sizeof(line) - 1, src)) + { + oper = line; + simple_ini::skip_empty(oper); + if (strstr(oper, "//") == oper || + strstr(oper, "#") == oper || + *oper == 0) + { + bzero(line, sizeof(line)); + continue; + } + + if (*oper == '[') + { + oper++; + if (strstr(oper, (char*)"]")) + strstr(oper, (char*)"]")[0] = 0; + if (sec.sec != oper) + { + if (sec.vals.size()) + values_.push_back(sec); + sec.vals.clear(); + + sec.sec = oper; + std::vector::iterator it = std::find(values_.begin(), values_.end(), oper); + if (it != values_.end()) + { + sec = *it; + values_.erase(it); + } + } + } + else + { + char* v = strstr(oper, "="); + if (v) + { + *v++ = 0; + trime(oper); + if (*oper) + { + trime(v); + + KEYVAL kv; + kv.key = oper; + kv.val = v; + sec.vals.push_back(kv); + std::sort(sec.vals.begin(), sec.vals.end()); + } + } + } + bzero(line, sizeof(line)); + } + fclose(src); + if (sec.vals.size()) + values_.push_back(sec); + std::sort(values_.begin(), values_.end()); + } + else + ret = errno; +#endif + + return ret; +} +#if !defined(WIN32) && !defined(_WIN64) +int simple_ini::save(const char* local_file) +{ + FILE* dst = fopen(local_file, "wb"); + if (dst) + { + for (size_t i = 0; i < values_.size(); ++i) + { + std::string val(values_[i].sec); + + val.insert(0, "["); + val += "]\r\n"; + fwrite(val.c_str(), 1, val.length(), dst); + for (size_t j = 0; j < values_[i].vals.size(); ++j) + { + val = values_[i].vals[j].key + "=" + values_[i].vals[j].val + "\r\n"; + fwrite(val.c_str(), 1, val.length(), dst); + } + val = "\r\n"; + fwrite(val.c_str(), 1, val.length(), dst); + } + fclose(dst); + } + else + return errno; +} +#endif + +std::string simple_ini::get(const char* sec, const char* key, const char* default_val) +{ +#if defined(WIN32) || defined(_WIN64) + char str[MAX_PATH] = { 0 }; + GetPrivateProfileStringA(sec, key, default_val, str, _countof(str), file_.c_str()); + + return str; +#else + std::vector::iterator it = std::find(values_.begin(), values_.end(), sec); + if (it == values_.end()) + return default_val; + + std::vector::iterator it1 = std::find(it->vals.begin(), it->vals.end(), key); + if (it1 == it->vals.end()) + return default_val; + else + return it1->val; +#endif +} +void simple_ini::set(const char* sec, const char* key, const char* val) +{ +#if defined(WIN32) || defined(_WIN64) + WritePrivateProfileStringA(sec, key, val, file_.c_str()); +#else + std::vector::iterator it = std::find(values_.begin(), values_.end(), sec); + if (it == values_.end()) + { + KEYVAL kv; + SECKEY sk; + + kv.key = key; + kv.val = val; + sk.sec = sec; + sk.vals.push_back(kv); + values_.push_back(sk); + std::sort(values_.begin(), values_.end()); + return; + } + + std::vector::iterator it1 = std::find(it->vals.begin(), it->vals.end(), key); + if (it1 == it->vals.end()) + { + KEYVAL kv; + + kv.key = key; + kv.val = val; + it->vals.push_back(kv); + std::sort(it->vals.begin(), it->vals.end()); + } + else + it1->val = val; +#endif +} +void simple_ini::remove(const char* sec, const char* key) +{ +#if defined(WIN32) || defined(_WIN64) +#else + std::vector::iterator it = std::find(values_.begin(), values_.end(), sec); + if (it == values_.end()) + return; + + std::vector::iterator it1 = std::find(it->vals.begin(), it->vals.end(), key); + if (it1 != it->vals.end()) + it->vals.erase(it1); +#endif +} +void simple_ini::remove(const char* sec) +{ +#if defined(WIN32) || defined(_WIN64) +#else + std::vector::iterator it = std::find(values_.begin(), values_.end(), sec); + if (it != values_.end()) + values_.erase(it); +#endif +} +void simple_ini::clear(void) +{ + values_.clear(); +} diff --git a/sdk/base/ini_file.h b/sdk/base/ini_file.h new file mode 100644 index 0000000..02aa8cf --- /dev/null +++ b/sdk/base/ini_file.h @@ -0,0 +1,68 @@ +#pragma once + +#if defined(WIN32) || defined(_WIN64) +#include +#else +#include +#define stricmp strcasecmp +#endif + +#include +#include +#include + +class simple_ini +{ + typedef struct _key_val + { + std::string key; + std::string val; + + bool operator==(const char* k) + { + return key == k; + } + bool operator<(const struct _key_val& r) + { + return key.compare(r.key) < 0; + } + }KEYVAL; + typedef struct _sec_key + { + std::string sec; + std::vector vals; + bool operator==(const char* s) + { + return sec == s; + } + bool operator<(const struct _sec_key& r) + { + return sec.compare(r.sec) < 0; + } + }SECKEY; + std::vector values_; + +#if defined(WIN32) || defined(_WIN64) + std::string file_; +#endif + +public: + simple_ini(); + ~simple_ini(); + + static std::string temporary_path(void); + static bool skip_empty(char*& ptr); + static void trime(char*& ptr); + +public: + int load(const char* local_file); +#if !defined(WIN32) && !defined(_WIN64) + int save(const char* local_file); +#endif + + std::string get(const char* sec, const char* key, const char* default_val = ""); + void set(const char* sec, const char* key, const char* val); + void remove(const char* sec, const char* key); + void remove(const char* sec); + void clear(void); +}; diff --git a/sdk/base/packet.h b/sdk/base/packet.h new file mode 100644 index 0000000..e40ae4c --- /dev/null +++ b/sdk/base/packet.h @@ -0,0 +1,379 @@ +#pragma once + +// packet structures and command +// +// created on 2022-12-06 +// +#if !defined(WIN32) +#include +#endif +#include + +#define TEMPORARY_API + +/////////////////////////////////////////////////////////////////////////////// +// definitions ... +#define CONFIG_NAME_MAX_LEN 32 // max bytes of configuration name +#define FLOAT_PRECISION .000001f +#define IS_FLOAT_EQUAL(x, y) (-FLOAT_PRECISION <= (x) - (y) && (x) - (y) <= FLOAT_PRECISION) +#define MAKE_WORD(b0, b1) (((b0) & 0xff) | (((b1) << 8) & 0x0ff00)) +#define MAKE_STR(str) #str +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +#define ROGER(cmd) cmd##_ROGER +#define PAIR_COMMAND(cmd) \ + cmd, \ + cmd##_ROGER + +#define RETURN_STR_ENUM(v, e) \ + if(v == e) \ + return #e; + +#define STRUCT_CONSTRUCTOR(st_name) \ + st_name() \ + { \ + memset(this, 0, sizeof(st_name));\ + } + +// protocol version, The first thing to do after connecting is to check whether the field is compatible !!! +#define PROTOCOL_VER MAKE_WORD(0, 1) + +// NOTE: All text transmitted by pack cmd is in UTF-8 format !!! + + +enum ep0_req +{ + USB_REQ_EP0_GET_PROTO_VER = 100, // get protocol version (PROTOCOL_VER), req = me, ind = 0, val = 0, len = 2 + USB_REQ_EP0_GET_STATUS, // 获å–å„工作线程状æ€, return EP0REPLYSTATUS. req = me, ind = 0, val = 0, len = sizeof(EP0REPLYSTATUS) + USB_REQ_EP0_RESET_BULK, // 关闭并é‡æ–°æ‰“å¼€BULK端点, return error number (uint32_t). req = me, ind = 0, val = 0, len = sizeof(uint32_t) + USB_REQ_EP0_CANCEL_CMD, // å–消当å‰æŒ‡ä»¤çš„继续执行(一般用于中止大数æ®çš„传输). req = me, ind = 0, val = 0, len = sizeof(uint32_t) * 2 [(uint32_t)cmd + (uint32_t)pack-id] + USB_REQ_EP0_SET_ENCRYPT, // 设置加密方å¼ï¼Œ req = me, ind = 0, val = 0, len = sizeof(PACK_BASE) + USB_REQ_EP0_SET_BULK_BUFFER, // 设置bulk缓冲区大å°ç³»æ•°ï¼Œ req = me, ind = coef, val = 0, len = 0 +}; +enum bulk_status +{ + BULK_STATUS_NOT_START = 0, // has not initialized + BULK_STATUS_IDLE, // wait IO + BULK_STATUS_IO, // in reading or writing + BULK_STATUS_ERROR, // error occurs + BULK_STATUS_RESET, // in reset(close and reopen) process +}; + +enum packet_cmd +{ + PACK_CMD_NULL, + + PAIR_COMMAND(PACK_CMD_HEART_BEAT), // notify peers you are still alive, receiver should reply the same pack + PAIR_COMMAND(PACK_CMD_INVALID), // reply when received an invalid packet + PAIR_COMMAND(PACK_CMD_SYNC), + + // attributes get/set, all content in PACK_BASE::payload should be in JSON style - all readonly attributes move to readonly SANE-options on 2023-03-20 + //PACK_CMD_ATTR_SYS_VER_GET = 10, // get system version on device, [in]: PACK_BASE, [out] PACK_BASE::payload - {"os":"linux", "ver":"4.4.194", ...} + //PACK_CMD_ATTR_FIRMWARE_VER_GET, // get firmware version, [in]: PACK_BASE, [out] PACK_BASE::payload - {"firmware":"G2393A1234", "CIS":"CIS-123", ...} + //PACK_CMD_ATTR_SERIAL_NUM_GET, // get device serial num, [in]: PACK_BASE, [out] PACK_BASE::payload - {"serial":"20221206001"} + //PACK_CMD_ATTR_SERIAL_NUM_SET, // set device serial num, [in]: PACK_BASE::payload - {"serial":"20221206001"}, [out] PACK_BASE + //PACK_CMD_ATTR_MAC_GET, // get mac address, [in]: PACK_BASE, [out] PACK_BASE::payload - {"mac":"12:34:56:78:9a:bc"} + //PACK_CMD_ATTR_IP_GET, // get ip address, [in]: PACK_BASE, [out] PACK_BASE::payload - {"ipv4":"192.168.1.123", "ipv6":"::1"} + //PACK_CMD_ATTR_HARDWARE_INFO_GET, // get hardwares information on device, [in]: PACK_BASE, [out] PACK_BASE::payload - {"CPU":"ARM x86", "mem":"16GB", ...} + //PACK_CMD_ATTR_HISTORY_COUNT_GET, // get history count, [in]: PACK_BASE, [out] PACK_BASE::payload - {"history-count":12345, ...} + //PACK_CMD_ATTR_ROLLER_COUNT_GET, // get roller count, [in]: PACK_BASE, [out] PACK_BASE::payload - {"roller-count":2345} + //PACK_CMD_ATTR_ROLLER_COUNT_SET, // set roller count, [in]: PACK_BASE::payload - {"roller-count":2345}, [out] PACK_BASE + + // configuration get/set + PACK_CMD_STATUS_ROGER = 100, // device -> host. PACK_BASE::result -> status + PAIR_COMMAND(PACK_CMD_SETTING_GET), // get all settings supported by the device, [in]: PACK_BASE, [out]: PACK_BASE::payload - configuration JSON, see SANE-configuration format + PAIR_COMMAND(PACK_CMD_SETTING_GET_CUR), // get current value of given setting, [in]: PACK_BASE::payload - (char*)name, [out]: PACK_BASE::payload - LPCFGVAL + PAIR_COMMAND(PACK_CMD_SETTING_SET), // set value of given setting, [in]: PACK_BASE::payload - LPCFGVAL, [out]: PACK_BASE::payload - LPCFGVAL + PAIR_COMMAND(PACK_CMD_SETTING_RESTORE), // restore given settings, [in]: PACK_BASE::payload - LPCFGVAL, [out]: PACK_BASE::payload - LPCFGVAL + + // scan command + PACK_CMD_SCAN_BASE = 200, + PAIR_COMMAND(PACK_CMD_SCAN_START), // start scanning, [in]: PACK_BASE, [out]: PACK_BASE + PAIR_COMMAND(PACK_CMD_SCAN_IMG), // device -> host, PACK_BASE::payload - LPPACKIMAGE + PAIR_COMMAND(PACK_CMD_SCAN_PAPER), // device -> host, ONE paper has passed through the CIS. PACK_BASE::data - index of this paper + PACK_CMD_SCAN_FINISHED_ROGER, // device -> host, PACK_BASE::data is scanner_status + PAIR_COMMAND(PACK_CMD_SCAN_STOP), // stop scanning, [in]: PACK_BASE, [out]: PACK_BASE + //PAIR_COMMAND(PACK_CMD_SCAN_IMAGE_REQ), // get image request, [in]: PACK_BASE, [out] PACK_BASE on error, or PACK_BASE::payload - LPPACKIMAGE + //PAIR_COMMAND(PACK_CMD_SCAN_STATUS), // get scanner status, [in]: PACK_BASE, [out] PACK_BASE::result is status code + + // file operation + PACK_CMD_FILE_BASE = 300, + //PAIR_COMMAND(PACK_CMD_FILE_QUERY), // query file information, [in]: PACK_BASE::payload - (char*)file-path, [out] PACK_BASE::payload - LPFILEINFO + PAIR_COMMAND(PACK_CMD_FILE_READ_REQ), // read file content, [in]: PACK_BASE::payload - LPTXFILE, [out] PACK_BASE::payload - LPTXFILE + PAIR_COMMAND(PACK_CMD_FILE_WRITE_REQ), // write a file, [in]: PACK_BASE::payload - LPTXFILE, [out] PACK_BASE + PAIR_COMMAND(PACK_CMD_FILE_MOVE), // move/rename a file, [in]: PACK_BASE::payload - src\0dst\0\0, [out] PACK_BASE + PAIR_COMMAND(PACK_CMD_FILE_REMOVE), // delete a file, [in]: PACK_BASE::payload - (char*)file-path, [out] PACK_BASE + + // process operation + PACK_CMD_PROCESS_BASE = 400, + PAIR_COMMAND(PACK_CMD_PROCESS_START), // start a program [in]: PACK_BASE::payload - (char*)pe\0param\0\0, [out]: PACK_BASE::payload - (uint64_t)process-id on success or PACK_BASE on failure + PAIR_COMMAND(PACK_CMD_PROCESS_STOP), // kill a process [in]: PACK_BASE::payload - (char*)process-id, [out]: PACK_BASE + PAIR_COMMAND(PACK_CMD_PROCESS_REBOOT), // reboot system, [in]: PACK_BASE, [out]: PACK_BASE + //PAIR_COMMAND(PACK_CMD_PROCESS_EXEC_RESULT), // get result of a command, [in]: PACK_BASE::payload - (char*)command string, [out]: PACK_BASE::payload - (char*)execute result. popen(), fgets ... + //PAIR_COMMAND(PACK_CMD_PROCESS_QUERY), // query process information [in]: PACK_BASE::payload - (char*)process-id(-1 for all), [out]: LPPROCINFO + + PACK_CMD_TOKEN_GET = 900, // Obtain the token of the required command, [in] PACK_BASE, [out] - PACK_BASE::payload - LPOPERTOKEN +}; + +enum img_cb_type +{ + IMG_CB_IMAGE = 0, + IMG_CB_STATUS, + IMG_CB_STOPPED, +}; + +enum scanner_status +{ + SCANNER_STATUS_READY = 0x10000, // status beginning, avoiding conficts with standards/system error code + SCANNER_STATUS_NOT_OPEN, + SCANNER_STATUS_LOST_CONNECT, + SCANNER_STATUS_RESET_BULK, + SCANNER_STATUS_START_SCANNING, // start ok, but scanning-thread not working + SCANNER_STATUS_SCANNING, // start ok, and scanning-thread is working + SCANNER_STATUS_SCAN_FINISHED, // not a persistance status + SCANNER_STATUS_BUSY, // doing task exclude scanning + SCANNER_STATUS_COVER_OPENNED, + SCANNER_STATUS_COVER_CLOSED, + SCANNER_STATUS_SLEEPING, + SCANNER_STATUS_WAKED_UP, + SCANNER_STATUS_COUNT_MODE, + SCANNER_STATUS_DOUBLE_FEEDED, + SCANNER_STATUS_PAPER_JAMMED, + SCANNER_STATUS_PAPER_ASKEW, + SCANNER_STATUS_FEED_FAILED, + SCANNER_STATUS_NO_PAPER, + SCANNER_STATUS_PAPER_ON, + SCANNER_STATUS_STAPLE_ON, + SCANNER_STATUS_SIZE_ERR, + SCANNER_STATUS_DOGEAR, + SCANNER_STATUS_CFG_CHANGED, // PACK_BASE::payload - LPCFGVAL +}; + +// option affection if value changed, see SANE_INFO_xxx +enum opt_affect +{ + OPT_AFFECT_NONE = 0, + OPT_AFFECT_INEXACT = 1, + OPT_AFFECT_OTHERS = 2, + OPT_AFFECT_IMG_PARAM = 4, +}; + +enum img_format +{ + IMG_FMT_UNKNOWN = 0, // unknown format + IMG_FMT_TIFF, + IMG_FMT_BMP, + IMG_FMT_JPEG, + IMG_FMT_PNG, + IMG_FMT_SVG, + IMG_FMT_WEBP, + IMG_FMT_GIF, +}; +enum img_compression +{ + IMG_COMPRESSION_NONE = 0, + IMG_COMPRESSION_GROUP4, + IMG_COMPRESSION_RLE4, + IMG_COMPRESSION_RLE8, + IMG_COMPRESSION_LZW, + IMG_COMPRESSION_ZIP, +}; +enum img_status +{ + IMG_STATUS_OK = 0, // normal + IMG_STATUS_DOUBLE = 1 << 0, // double-feeded paper + IMG_STATUS_JAM = 1 << 1, // jammed paper + IMG_STATUS_STAPLE = 1 << 2, // staples on the paper + IMG_STATUS_SIZE_ERR = 1 << 3, // size check failed + IMG_STATUS_DOGEAR = 1 << 4, // paper has dogear - common + IMG_STATUS_DOGEAR_PARTIAL = 1 << 5, // dogear - scanned partial + IMG_STATUS_BLANK = 1 << 6, // blank image +}; +enum data_type +{ + DATA_TYPE_BOOL = 0, // (bool*) + DATA_TYPE_INT4, // (uint32_t*) + DATA_TYPE_FLOAT, // (double*) + DATA_TYPE_STRING, // (char*) with max_len space. befor and include me, keep same with SANE_TYPE_BOOL, SANE_TYPE_xxx ... + + DATA_TYPE_INT1, // (uint8_t*) + DATA_TYPE_INT2, // (uint16_t*) + DATA_TYPE_INT8, // (uint64_t*) + DATA_TYPE_CUSTOM, +}; + +enum paper_side +{ + PAPER_SIDE_FRONT = 0, // single side, this is front + PAPER_SIDE_BACK, // single side, this is back + PAPER_SIDE_TOP, // VERT-compound sides, and front side is at top + PAPER_SIDE_BOTTOM, // VERT-compound sides, and front side is at bottom + PAPER_SIDE_LEFT, // HORZ-compound sides, and front side is at left + PAPER_SIDE_RIGHT, // HORZ-compound sides, and front side is at right + + PAPER_SIDE_DSP, // a special type +}; +enum rot_angle +{ + ROT_ANGLE_0 = 0, + ROT_ANGLE_90, + ROT_ANGLE_180, + ROT_ANGLE_270, +}; +enum clr_channel +{ + COLOR_CHANNEL_RGB = 0, + COLOR_CHANNEL_RGBA, + COLOR_CHANNEL_GRAY, + COLOR_CHANNEL_RED, + COLOR_CHANNEL_GREEN, + COLOR_CHANNEL_BLUE, + COLOR_CHANNEL_ALPHA, +}; +enum color_mode +{ + COLOR_MODE_BW = 0, + COLOR_MODE_GRAY, + COLOR_MODE_RGB, +}; + +#pragma pack(push) +#pragma pack(1) +typedef struct _ep0_reply +{ + uint8_t in_status; // BULK-IN status, enum bulk_statu + uint8_t out_status; // BULK-OUT status, enum bulk_statu + uint16_t in_err; // valid if in_statu == BULK_STATU_ERROR + uint16_t out_err; // valid if out_statu == BULK_STATU_ERROR + uint16_t task_cnt; // tasks in command queue + uint32_t task_cmd; // the cmd of the task thread is doing + uint32_t task_pack_id; // packet id of the cmd + uint32_t task_required_bytes; // required byte of this packet + uint32_t packets_to_sent; // how many packets in sent queue + uint32_t bytes_to_sent; // how many bytes data is waiting for be sent in one replying packet +}EP0REPLYSTATUS, *LPEP0REPLYSTATUS; + +typedef struct _pack_base // A piece of data has only one header +{ + uint32_t enc_cmd : 2; // encrypting type, for 'cmd' + uint32_t encrypt : 3; // encrypting type, for payload content. the payload must cotains self-check if was encrypted packet + uint32_t enc_data : 5; // data for encrypt + uint32_t size : 6; // bytes of this structure + uint32_t cmd : 16; // packet command + uint32_t data; // simple data in command packet depends 'cmd', or error code in reply packet + uint32_t pack_id; // maintain by the initiator, the reply packet use the same id + uint32_t payload_len; // total bytes of payload of this command packet (the data in the range will sent in ONE 'write'), + // big data can be described in payload and independent communication, and this field should not include them. + // if encrypted packet, this field is the length after encrypting + char payload[0]; // payloads, according to 'cmd' + + STRUCT_CONSTRUCTOR(_pack_base) +}PACK_BASE, * LPPACK_BASE; +#define BASE_PACKET_REPLY(reply, command, id, err) \ + (reply).encrypt = 0; \ + (reply).enc_data = 0; \ + (reply).size = sizeof(reply); \ + (reply).data = err; \ + (reply).cmd = command; \ + (reply).pack_id = id; \ + (reply).payload_len = 0; + + +typedef struct _config_val +{ + uint8_t type; // same as SANE_Value_Type + uint8_t name_off; // name offset of the option in data, end with '\0' + uint8_t val_off; // option value offset in data + uint8_t after_do; // see SANE_INFO_xxx in sane.h + uint16_t val_size; // real size of value + uint16_t max_size; // max size of this option, this value has given in gb_json::size + char data[0]; // contains value and name. fetch them according name_off and val_off members. +}CFGVAL, *LPCFGVAL; + +typedef struct _img_pos +{ + uint64_t paper_ind : 32; // paper index in this turn/start, based ZERO. (image-collector set) + uint64_t new_img : 1; // 0 - partial data; 1 - new image data. (image-collector set) + uint64_t img_over : 1; // 0 - has data yet; 1 - END for the image. (image-collector set) + uint64_t paper_side : 3; // enum paper_side. front of paper(When scanning multiple sheets, the paper feeding side is the front side). (image-collector set) + uint64_t back_rot : 2; // back rotation angle, enum rot_angle. (image-collector set) + uint64_t channel_ind : 4; // index of color channel, enum clr_channel. (image-collector set) + uint64_t status : 7; // img_status. (image-collector set) + uint64_t split_ind : 7; // splitting order, from left to right and then top to bottom, based ZERO + uint64_t multiout_ind : 4; // index of multi-out + uint64_t reserved : 3; // reserved + + STRUCT_CONSTRUCTOR(_img_pos) +}IMGPOS, * LPIMGPOS; +typedef struct _pack_img +{ + IMGPOS pos; // image pos info ... + uint32_t width; // image width in pixel. (image-collector set) + uint32_t height; // image height in pixel. (image-collector set) + uint32_t resolution_x; // image horizontal reolution. (image-collector set) + uint32_t resolution_y; // image vertical reolution. (image-collector set) + uint32_t channels : 6; // image channels per pixel. (image-collector set) + uint32_t format : 6; // image format, see 'img_format'. (image-collector set) + uint32_t bpp : 6; // bits per pixel. (image-collector set) + uint32_t bppc : 6; // bits per pixel in this channel, equal to 'bpp' if pos.channel_ind == 0x0f. (image-collector set) + uint32_t compression : 6; // image data compression, see 'img_compression'. (image-collector set) + uint32_t reserve : 2; // unused now + uint32_t info_size; // image information size in bytes, information part is used for quality of JPEG, pallete of BMP .... (image-collector set) + uint64_t data_size; // image data size in 'data' with bytes. (image-collector set) +// char data[0]; // two parts: image info (info_size) + image data (data_size) + + STRUCT_CONSTRUCTOR(_pack_img) +}PACKIMAGE, * LPPACKIMAGE; + +typedef struct _oper_token +{ + uint32_t type; // token type + char data[128]; // token data +}OPERTOKEN, * LPOPERTOKEN; + +typedef struct _tx_file +{ + uint64_t size; // total size + uint64_t offset; // offset in the file + char path[2]; // file full path-name +}TXFILE, *LPTXFILE; +typedef struct _file_info +{ + OPERTOKEN token; // operation token, returned by command PACK_CMD_TOKEN_GET + uint64_t size; // file size + uint16_t name_len; // bytes of file name string + uint16_t create_time_len; // bytes of create time string: '2022-12-07 12:34:56.789', or target file path in command PACK_CMD_FILE_MOVE + uint16_t modify_time_len; + uint16_t version_len; // bytes of version string + char data[0]; // 4 parts: path-file(name_len) + create-time(create_time_len) + modify-time(modify_time_len) + version(version_len) + // or 5 parts in command PACK_CMD_FILE_WRITE, add content at the last part of bytes 'size' + + STRUCT_CONSTRUCTOR(_file_info) +}FILEINFO, * LPFILEINFO; + +typedef struct _proc_info +{ + OPERTOKEN token; // operation token, returned by command PACK_CMD_TOKEN_GET + uint32_t count; // number of elements in array proc + struct _info + { + uint16_t len; // bytes of this element, include this head + uint64_t pid; // process id + uint64_t ppid; // parent process id + uint64_t start; // started time in ns from 1970-01-01 00:00:00 + uint64_t mem; // memory usage, in bytes + uint64_t cpu_clk; // cpu clock + char path_name[4]; + }proc[1]; + + STRUCT_CONSTRUCTOR(_proc_info) +}PROCINFO, * LPPROCINFO; +#pragma pack(pop) + +//////////////////////////////////////////////////////////////////////////////////////////////// +// configurations ... +// +// 1 - App has whole set, group definitions +// +// 2 - device provides sub-set, or a customizing item +// diff --git a/sdk/base/plat_types.h b/sdk/base/plat_types.h new file mode 100644 index 0000000..bc54e17 --- /dev/null +++ b/sdk/base/plat_types.h @@ -0,0 +1,125 @@ +#pragma once + +#if defined(WIN32) || defined(_WIN64) +#define OS_WIN 1 +#else +#define OS_WIN 0 +#endif + +#define SIZE_KB(n) ((n) * 1024) +#define SIZE_MB(n) SIZE_KB(n * 1024) +#define SIZE_GB(n) SIZE_MB(n * 1024) + +#define SEC_2_MS(s) ((s) * 1000) +#define MSEC_2_US(ms) ((ms) * 1000) +#define SEC_2_US(s) MSEC_2_US(SEC_2_MS(s)) + +#define ALIGN_TO(v, align) (((v) + (align) - 1) / (align) * (align)) +#define ALIGN_INT(v) ALIGN_TO(v, sizeof(int)) + +#define RETURN_ENUM_STR(v, e) \ + if(v == e) \ + return #e; + + +#if !OS_WIN // migrate codes from windows to linux ... +#include +#include +#include +#include + +#pragma pack(push) +#pragma pack(1) +typedef struct BITMAPFILEHEADER +{ + u_int16_t bfType; + u_int32_t bfSize; + u_int16_t bfReserved1; + u_int16_t bfReserved2; + u_int32_t bfOffBits; +}BITMAPFILEHEADER; + +typedef struct BITMAPINFOHEADER +{ + u_int32_t biSize; + u_int32_t biWidth; + u_int32_t biHeight; + u_int16_t biPlanes; + u_int16_t biBitCount; + u_int32_t biCompression; + u_int32_t biSizeImage; + u_int32_t biXPelsPerMeter; + u_int32_t biYPelsPerMeter; + u_int32_t biClrUsed; + u_int32_t biClrImportant; +}BITMAPINFODEADER; +#pragma pack(pop) +#define BI_RGB 0 +#define MAKEWORD(a, b) (((a) & 0x0ff) | (((b) & 0x0ff) << 8)) +#define MAKELONG(a, b) (((a) & 0x0ffff) | (((b) & 0x0ffff) << 16)) +#define _countof(a) (sizeof(a) / sizeof((a)[0])) + +typedef long LONG; +typedef void* HANDLE; +typedef void* HWND; +typedef void* HMODULE; +typedef void* LPVOID; +typedef void* FARPROC; +typedef unsigned int DWORD; +typedef unsigned short WORD; +typedef unsigned short UINT16; +typedef unsigned char BYTE; +typedef int BOOL; + +#define TRUE 1 +#define FALSE 0 +#define MAX_PATH 256 +#define huge +#define FAR +#define NEAR +#define LOWORD(v) ((v) & 0x0ffff) +#define HIWORD(v) (((v) >> 16) & 0x0ffff) +#define PASCAL __attribute__((stdcall)) +#define _countof(a) (sizeof(a) / sizeof(a[0])) +#define LOAD_WITH_ALTERED_SEARCH_PATH RTLD_NOW +#define FreeLibrary dlclose +#define GetProcAddress dlsym +#define GetPrivateProfileIntW GetPrivateProfileIntA +#define lstrlenA strlen +#define lstrlenW strlen + +#define DLL_EXTESION "so" +#define PATH_SEPARATOR "/" +#define STRICMP strcasecmp +#define MKDIR(a, b) mkdir(a, b) +#define STDCALL +#define ERROR_CANCELLED 1223 +#define USB_TIMEOUT_INFINITE 0 +#define FSEEK fseek +#define FTELL ftell + + +extern DWORD GetLastError(void); +extern DWORD GetPrivateProfileIntA(const char* app, const char* key, DWORD def, const char* file); +extern DWORD GetPrivateProfileStringA(const char* app, const char* key, const char* init, char* buf, size_t len, const char* file); +extern void Sleep(DWORD milliseconds); +extern int GetModuleFileNameA(HMODULE module, char* buf, size_t len); // NOTE: parameter 'module' is consinder as a part of the module file name +extern int GetCurrentProcessId(void); +extern int GetCurrentThreadId(void); + +#else +#include + +#define bzero(a, l) memset(a, 0, l) +#define DLL_EXTESION "dll" +#define PATH_SEPARATOR "\\" +#define STRICMP stricmp +#define MKDIR(a, b) mkdir(a) +#define STDCALL __stdcall +#define sem_t HANDLE +#define USB_TIMEOUT_INFINITE -1 +#define FSEEK _fseeki64 +#define FTELL _ftelli64 +#define pid_t int + +#endif diff --git a/sdk/base/utils.cpp b/sdk/base/utils.cpp new file mode 100644 index 0000000..6650b56 --- /dev/null +++ b/sdk/base/utils.cpp @@ -0,0 +1,1942 @@ +#include "utils.h" + +#include "ini_file.h" + + +#include +#include + +#if OS_WIN +#include +#include +#include +#include +#include +#pragma comment(lib, "Psapi.lib") + +#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 // microseconds from '1601-01-01 00:00:00' to '1970-01-01 00:00:00' + +int gettimeofday(TIMEV* tv, struct timezone* tz) +{ + FILETIME ft = { 0 }; + uint64_t ui64 = 0; + static bool set_tz = true; + + GetSystemTimeAsFileTime(&ft); // 100 ns - from 1601-01-01 00:00:00 + ui64 = ft.dwHighDateTime; + ui64 <<= 32; + ui64 |= ft.dwLowDateTime; + + // convert to microseconds ... + ui64 += 5; + ui64 /= 10; + + // move to 1970-01-01 00:00:00 + ui64 -= DELTA_EPOCH_IN_MICROSECS; + if (tv) + { + tv->tv_sec = ui64 / 1000000; + tv->tv_usec = ui64 % 1000000; + } + + if (tz) + { + if (set_tz) + { + set_tz = false; + _tzset(); + } + tz->tz_minuteswest = _timezone / 60; + tz->tz_dsttime = _daylight; + } + + return 0; +} + + +#else + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PRODUCT_VENDOR "HuaGo" + +static std::mutex ini_lock_; +static std::map ini_files_; +static std::string debug_cfg_file_ = ""; + +static void init_default_config_file_path(void) +{ + if(debug_cfg_file_.empty()) + { + debug_cfg_file_ = utils::get_local_data_path() + PATH_SEPARATOR + "config" + PATH_SEPARATOR + "debug.cfg"; + } +} +static simple_ini* get_ini_object(const char* file) +{ + if(!file || file[0] == 0) + { + init_default_config_file_path(); + file = debug_cfg_file_.c_str(); + } + + { + std::lock_guard lock(ini_lock_); + + if (ini_files_.count(file) == 0) + { + simple_ini* ini = new simple_ini(); + ini->load(file); + + ini_files_[file] = ini; + } + } + + return ini_files_[file]; +} + +DWORD GetPrivateProfileStringA(const char* lpAppName, const char* lpKeyName, const char* lpDefault, char* lpReturnedString, DWORD nSize, const char* lpFileName) +{ + simple_ini* ini = get_ini_object(lpFileName); + std::string str(ini->get(lpAppName, lpKeyName, lpDefault)); + + if (nSize) + { + if (nSize < str.length()) + { + memcpy(lpReturnedString, str.c_str(), nSize); + } + else + { + strcpy(lpReturnedString, str.c_str()); + nSize = str.length(); + } + } + + return nSize; +} + +BOOL WritePrivateProfileStringA(const char* lpAppName, const char* lpKeyName, const char* lpString, const char* lpFileName) +{ + simple_ini* ini = get_ini_object(lpFileName); + + ini->set(lpAppName, lpKeyName, lpString); + ini->save(lpFileName); + + return TRUE; +} + +DWORD GetLastError(void) +{ + return errno; +} +DWORD GetPrivateProfileIntA(const char* app, const char* key, DWORD def, const char* file) +{ + std::string val(get_ini_object(file)->get(app, key)); + + return val.empty() ? def : atoi(val.c_str()); +} +DWORD GetPrivateProfileStringA(const char* app, const char* key, const char* init, char* buf, size_t len, const char* file) +{ + std::string val(get_ini_object(file)->get(app, key)); + + if(val.empty()) + { + if(init) + { + strcpy(buf, init); + len = strlen(init); + } + else + { + len = 0; + } + } + else + { + if(len < val.length()) + memcpy(buf, val.c_str(), len); + else + { + strcpy(buf, val.c_str()); + len = val.length(); + } + } + + return len; +} +void Sleep(DWORD milliseconds) +{ + std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds)); +} +int GetModuleFileNameA(HMODULE module, char* buf, size_t len) +{ + std::string name(""), + val(utils::get_module_full_path((char*)module)); + + val += PATH_SEPARATOR + name; + if(len < val.length()) + memcpy(buf, val.c_str(), len); + else + { + strcpy(buf, val.c_str()); + len = val.length(); + } + + return len; +} +int GetCurrentThreadId(void) +{ + return pthread_self(); +} +int GetCurrentProcessId(void) +{ + return getpid(); +} + +#endif + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// log class +#define MAX_LOG_FILE_SIZE SIZE_MB(10) + +class log_cls +{ + void(*log_)(const char*, void*, void*); + std::string path_file_; + FILE* file_; + int level_; + int type_; + std::mutex lock_; + + static log_cls* inst_; + + static FILE* create_log_file(const char* path_file, bool truncate) + { + FILE* file_ = fopen(path_file, "a+b"); + + if (file_) + { + fseek(file_, 0, SEEK_END); + if (ftell(file_) == 0) + { + unsigned char bom[] = { 0x0ef, 0x0bb, 0x0bf }; + fwrite(bom, sizeof(bom), 1, file_); + } + else + { + std::string sep("\n\n===================================================================================================================\n"); + fwrite(sep.c_str(), sizeof(sep[0]), sep.length(), file_); + } + + std::string now("[" + utils::format_current_time() + "]: ====================================="); + now += truncate ? "--truncated--=====================================\n" : "--started--" + std::to_string(GetCurrentProcessId()) + "=====================================\n"; + fwrite(now.c_str(), sizeof(now[0]), now.length(), file_); + } + + return file_; + } + static void log_none(const char* info, void* param, void* param2) + {} + static void log_consonle(const char* info, void* param, void* param2) + { + printf(info); + } + static void log_file(const char* info, void* param, void* param2) + { + FILE** file = (FILE**)param; + + if (*file == nullptr) + *file = create_log_file(((std::string*)param2)->c_str(), false); + + if (*file) + { + fwrite(info, 1, strlen(info), *file); + fflush(*file); + if (ftell(*file) >= MAX_LOG_FILE_SIZE) + { + fclose(*file); + remove(((std::string*)param2)->c_str()); + *file = create_log_file(((std::string*)param2)->c_str(), true); + } + } + } + + +protected: + log_cls() : path_file_(""), file_(0), log_(&log_cls::log_consonle), level_(LOG_LEVEL_ALL) + {} + ~log_cls() + { + if (file_) + { + fclose(file_); + file_ = 0; + } + } + +public: + static log_cls* instance(void) + { + if (!log_cls::inst_) + log_cls::inst_ = new log_cls(); + + return log_cls::inst_; + } + static void clear(void) + { + if(log_cls::inst_) + { + delete log_cls::inst_; + log_cls::inst_ = nullptr; + } + } + + int set_log_type(int type, void* param) + { + int ret = 0; + + if (file_) + { + fclose(file_); + file_ = 0; + } + + type_ = type; + if (type == LOG_TYPE_NONE) + log_ = &log_cls::log_none; + else if (type == LOG_TYPE_CONSOLE) + log_ = &log_cls::log_consonle; + else if (type == LOG_TYPE_FILE) + { + log_ = &log_cls::log_file; + + ret = -1; + if (param) + { + path_file_ = (char*)param; + file_ = create_log_file(path_file_.c_str(), false); + if (file_) + ret = 0; + } + } + + if (ret != 0) + { + log_ = &log_cls::log_none; + type_ = LOG_TYPE_NONE; + } + + return ret; + } + void set_log_level(int level) + { + level_ = level; + } + int level(void) + { + return level_; + } + int type(void) + { + return type_; + } + + void log(const char* info) + { + std::lock_guard lock(lock_); + + log_(info, &file_, &path_file_); + } + std::string get_log_file_path(const char* dst = nullptr) + { + std::string file(""); + + if (log_ == &log_cls::log_file && file_) + { + file = path_file_; + + if (dst) + { + file = dst; + + FILE* dst = fopen(file.c_str(), "wb"); + + if (!dst) + file = ""; + else + { + std::lock_guard lock(lock_); + char buf[1024] = { 0 }; + size_t l = 0; + + fseek(file_, 0, SEEK_SET); + while ((l = fread(buf, 1, sizeof(buf), file_))) + fwrite(buf, 1, l, dst); + fclose(dst); + } + } + } + + return file; + } + void clear_log(void) + { + if (log_ == &log_cls::log_file && file_) + { + std::lock_guard lock(lock_); + + fclose(file_); + remove(path_file_.c_str()); + file_ = create_log_file(path_file_.c_str(), true); + } + } +}; +log_cls* log_cls::inst_ = NULL; + + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// util + +namespace utils +{ + typedef struct _match_part + { + std::string pattern; + std::string found; + bool(*match)(const char*, const char*); + }MATCHPART; + + static bool find_sub_str(const char* text, const char* pattern) + { + return strstr(text, pattern) != nullptr; + } + static bool STDCALL match_part_filename(const char* path_name, bool dir, void* param) + { + MATCHPART* partn = (MATCHPART*)param; + + if (!dir) + { + if (partn->pattern.empty()) + { + partn->found = path_name; + } + else + { + const char* name = strrchr(path_name, PATH_SEPARATOR[0]); + if (name++ == nullptr) + name = path_name; + + std::string n(name); + to_lower(n); + // if (strstr(n.c_str(), partn[0].c_str())) + if(partn->match(n.c_str(), partn->pattern.c_str())) + partn->found = path_name; + else + dir = true; + } + } + + return dir; + } + +#if OS_WIN + static std::string u2m(const wchar_t* u, int page) + { + char* ansi = NULL; + int len = 0; + std::string mb(""); + + len = WideCharToMultiByte(page, 0, u, lstrlenW(u), NULL, 0, NULL, NULL); + ansi = new char[len + 2]; + len = WideCharToMultiByte(page, 0, u, lstrlenW(u), ansi, len, NULL, NULL); + ansi[len--] = 0; + mb = ansi; + delete[] ansi; + + return mb; + } + static std::wstring m2u(const char* m, int page) + { + wchar_t* unic = NULL; + int len = 0; + std::wstring u(L""); + + len = MultiByteToWideChar(page, 0, m, lstrlenA(m), NULL, 0); + unic = new wchar_t[len + 2]; + len = MultiByteToWideChar(page, 0, m, lstrlenA(m), unic, len); + unic[len--] = 0; + u = unic; + delete[] unic; + + return u; + } + + std::string utf82ansi(const char* utf8) + { + return u2m(m2u(utf8, CP_UTF8).c_str(), CP_ACP); + } + std::string ansi2utf8(const char* ansi) + { + return u2m(m2u(ansi, CP_ACP).c_str(), CP_UTF8); + } +#else + // This function will return 'in' string if failed ! + static std::string transform_between_gbk_and_utf8(const char* in, bool to_utf8, int *err, const char* ansi = "GBK") + { + size_t len = strlen(in) + 8, ol = len * 2; + char *buf = (char*)malloc(len), *oper = buf, *out = nullptr, *oper1 = nullptr; + iconv_t conv; + + memset(buf, 0, len); + strcpy(buf, in); + if(to_utf8) + conv = iconv_open("UTF-8", ansi); + else + conv = iconv_open(ansi, "UTF-8"); + if(conv == (iconv_t)-1) + { + if(err) + *err = errno; + + free(buf); + + return in; + } + + oper1 = out = (char*)malloc(ol); + memset(out, 0, ol); + len -= 8; + if(iconv(conv, &oper, &len, &oper1, &ol)) + { + if(err) + *err = errno; + } + else if(err) + *err = 0; + + std::string ret(out); + + free(buf); + free(out); + iconv_close(conv); + + return ret.empty() ? in : std::move(ret); + } + + std::string utf82ansi(const char* utf8) + { + // fix me ... + return transform_between_gbk_and_utf8(utf8, false, nullptr); + } + std::string ansi2utf8(const char* ansi) + { + // fix me ... + return transform_between_gbk_and_utf8(ansi, true, nullptr); + } +#endif + + std::string get_command_result(const char* cmd, int len, int *err) + { + std::string result(""); + +#if OS_WIN +#else + FILE* src = popen(cmd, "r"); + + if(err) + *err = 0; + if (src) + { + char buf[128] = { 0 }; + int rv = fread(buf, 1, sizeof(buf) - 1, src); + + while (rv > 0) + { + buf[rv] = 0; + result += buf; + if (len != -1 && result.length() >= len) + { + result.erase(len); + break; + } + rv = fread(buf, 1, sizeof(buf) - 1, src); + } + pclose(src); + } + else if(err) + *err = errno; +#endif + + return std::move(result); + } + std::string get_local_data_path(void) + { + static std::string ldp(""); + + if (ldp.empty()) + { +#if OS_WIN + const char* path(getenv("LOCALAPPDATA")); + + if (path) + { + ldp = path; + ldp += PATH_SEPARATOR; + } +#else + const char* path(getenv("HOME")); + + if (path) + { + if (strstr(path, "/root")) + { + std::string usr(get_command_result("logname")); + ldp = std::string("/home/") + trim(usr); + if (!opendir(ldp.c_str())) + { + printf("opendir(%s) failed: %s\n", ldp.c_str(), strerror(errno)); + ldp = path; + } + } + else + { + ldp = path; + } + ldp += std::string(PATH_SEPARATOR) + "."; + } +#endif + + ldp += PRODUCT_VENDOR; + ldp += "Scan"; + create_folder(ldp.c_str()); + + std::string first("[" + format_current_time() + "]: Process "), + ff(temporary_path() + PATH_SEPARATOR + PRODUCT_VENDOR + "scanner-first.log"); + first += std::to_string(GetCurrentProcessId()) + " root of local data path is " + ldp + "\n"; + save_2_file(&first[0], first.length(), ff.c_str(), true, SIZE_KB(1)); + } + + return ldp; + } + std::string temporary_path(void) + { + return std::move(simple_ini::temporary_path()); + } + std::string format_current_time(void) + { + return std::move(chronograph::now()); + } + std::string get_module_full_path(const char* part_name/*nullptr to get main-pe/first module's full path*/) + { + MATCHPART file = {part_name ? part_name : "", "", find_sub_str}; + + to_lower(file.pattern); + if(file.pattern.find("*") != std::string::npos) + file.match = is_match_pattern; + +#if OS_WIN + if (part_name && *part_name) + { + HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId()); + MODULEENTRY32W pei = { 0 }; + + if (h != INVALID_HANDLE_VALUE) + { + pei.dwSize = sizeof(pei); + if (Module32FirstW(h, &pei)) + { + do + { + char path[256] = { 0 }; + + GetModuleFileNameA(pei.hModule, path, _countof(path) - 1); + if (!match_part_filename(path, false, (void*)&file)) + break; + pei.dwSize = sizeof(pei); + } while (Module32NextW(h, &pei)); + } + CloseHandle(h); + } + } + else + { + char path[256] = { 0 }; + + GetModuleFileNameA(NULL, path, _countof(path) - 1); + file.found = path; + } +#else + char path[128] = { 0 }; + sprintf(path, "/proc/%u/map_files/", getpid()); + enum_file(path, false, match_part_filename, (void*)&file); +#endif + + return std::move(file.found); + } + std::string find_file(const char* root_dir, const char* part_name, bool recursive) + { + MATCHPART file = {part_name ? part_name : "", "", find_sub_str}; + + to_lower(file.pattern); + if(file.pattern.find("*") != std::string::npos) + file.match = is_match_pattern; + enum_file(root_dir, recursive, match_part_filename, (void*)&file); + + return std::move(file.found); + } + std::string target_file_from_link(const char* lnk_file) + { +#if OS_WIN + std::string ret(""); + + return ret; +#else + char path[256] = { 0 }; + int len = readlink(lnk_file, path, sizeof(path) - 1); + + return len > 0 ? path : lnk_file; +#endif + } + std::string get_ini_value(const char* seg, const char* key, const char* cfg_file) + { + char buf[512] = { 0 }; + + GetPrivateProfileStringA(seg, key, "", buf, sizeof(buf) - 1, cfg_file); + + return buf; + } + std::string load_mini_file(const char* file, int* err) + { + std::string cont(""); + FILE* src = fopen(file, "rb"); + int en = 0; + + if (src) + { + long len = 0; + + fseek(src, 0, SEEK_END); + len = ftell(src); + fseek(src, 0, SEEK_SET); + if (len > SIZE_MB(1)) + { + en = E2BIG; + } + else + { + char* buf = new char[len]; + if (buf) + { + len = fread(buf, 1, len, src); + cont = std::string(buf, len); + en = 0; + delete[] buf; + } + else + en = ENOMEM; + } + fclose(src); + } + else + en = errno; + + if(err) + *err = en; + + return std::move(cont); + } + int save_2_file(void* data, size_t len, const char* file, bool append, size_t max_size) + { + FILE* dst = fopen(file, append ? "a+b" : "wb"); + int err = 0; + + if (!dst) + return errno; + + if(append && max_size != -1 && ftell(dst) >= max_size) + fseek(dst, 0, SEEK_SET); + + if (fwrite(data, 1, len, dst) < len) + err = ENOSPC; + fclose(dst); + + return err; + } + + bool is_match_pattern(const char* text, const char* pattern) + { + int str_ind = 0, pattern_ind = 0, star = -1, m = 0, + str_len = strlen(text), + patt_len = strlen(pattern); + bool ok = true; + + while (str_ind < str_len) + { + if (pattern_ind < patt_len && (text[str_ind] == pattern[pattern_ind] || pattern[pattern_ind] == '?')) + { + str_ind++; + pattern_ind++; + } + else if (pattern_ind < patt_len && pattern[pattern_ind] == '*') + { + star = pattern_ind++; + m = str_ind; + } + else if (star != -1) + { + pattern_ind = star + 1; + str_ind = ++m; + } + else + { + ok = false; + break; + } + } + if(ok) + { + while (pattern_ind < patt_len && pattern[pattern_ind] == '*') + pattern_ind++; + ok = pattern_ind == patt_len; + } + + return ok; + } + const char* to_lower(std::string& str) + { + std::transform(str.begin(), str.end(), str.begin(), tolower); + + return str.c_str(); + } + const char* trim(std::string& str, const char* sp) + { + int pos = 0; + char ch[2] = { 0 }; + + for (; pos < str.length(); ++pos) + { + ch[0] = str[pos]; + if (!strstr(sp, ch)) + break; + } + if (pos) + str.erase(0, pos); + + pos = str.length() - 1; + for (; pos >= 0; --pos) + { + ch[0] = str[pos]; + if (!strstr(sp, ch)) + break; + } + if (++pos < str.length()) + str.erase(pos); + + return str.c_str(); + } + + HMODULE load_dll(const char* path_file, int flag) + { +#if OS_WIN + HMODULE h = LoadLibraryA(path_file); + int ret = GetLastError(); + + utils::to_log(1, "[TWAIN]Load: LoadLibraryA(%s) = %d\r\n", path_file, ret); + if (!h && (ret == ERROR_MOD_NOT_FOUND || ret == ERROR_BAD_EXE_FORMAT)) + { + std::string dir(path_file); + size_t pos = dir.rfind('\\'); + char path[MAX_PATH] = { 0 }; + + GetDllDirectoryA(_countof(path) - 1, path); + if (pos != std::wstring::npos) + dir.erase(pos); + utils::to_log(LOG_LEVEL_FATAL, "[TWAIN]Load: change directory to '%s' and retry LoadLibraryA(%s) ...\r\n", dir.c_str(), path_file); + SetDllDirectoryA(dir.c_str()); + h = LoadLibraryA(path_file); + // h = LoadLibraryExW(path_dll, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + ret = GetLastError(); + utils::to_log(1, "[TWAIN]Load: trying LoadLibraryA(%s) = %d, restore directory to '%s'\r\n", path_file, ret, path); + SetDllDirectoryA(path); + } + + errno = GetLastError(); + + return h; +#else + return dlopen(path_file, flag); +#endif + } + bool create_folder(const char* folder) + { + int ret = MKDIR(folder, S_IREAD | S_IWRITE | S_IEXEC); + + return ret == 0 || errno == EEXIST; + } + void set_ini_value(const char* seg, const char* key, const char* val, const char* cfg_file) + { + WritePrivateProfileStringA(seg, key, val, cfg_file); + } + int enum_file(const char* folder, bool recursive, bool(STDCALL* found)(const char* path_name, bool dir, void* param), void* param) + { + int ret = EACCES; + +#if OS_WIN + WIN32_FIND_DATAA fd = { 0 }; + std::string root(folder); + HANDLE hf = FindFirstFileA((root + "\\*").c_str(), &fd); + + root += "\\"; + if (hf == INVALID_HANDLE_VALUE) + ret = GetLastError(); + else + { + do + { + bool is_dir = (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY; + if (!found((root + fd.cFileName).c_str(), is_dir, param)) + { + ret = ERROR_CANCELLED; + break; + } + if (is_dir && recursive) + { + char* name = strrchr(fd.cFileName, PATH_SEPARATOR[0]); + if (name++ == NULL) + name = fd.cFileName; + if (strcmp(name, ".") && strcmp(name, "..")) + { + if (enum_file(fd.cFileName, recursive, found, param) == ERROR_CANCELLED) + { + ret = ERROR_CANCELLED; + break; + } + } + } + } while (FindNextFileA(hf, &fd)); + FindClose(hf); + } +#else + DIR* pdir = nullptr; + struct dirent* ent = nullptr; + + pdir = opendir(folder); + if (!pdir) + ret = errno; + else + { + while ((ent = readdir(pdir))) + { + std::string file(folder); + + file += PATH_SEPARATOR; + file += ent->d_name; + if (!found(target_file_from_link(file.c_str()).c_str(), ent->d_type & DT_DIR, param)) + { + ret = ERROR_CANCELLED; + break; + } + + if (ent->d_type & DT_DIR) + { + if (recursive) + { + if (strcmp(ent->d_name, ".") && strcmp(ent->d_name, "..")) + { + std::string sub(folder); + sub += PATH_SEPARATOR; + sub += ent->d_name; + if (enum_file(sub.c_str(), recursive, found, param) == ERROR_CANCELLED) + { + ret = ERROR_CANCELLED; + break; + } + } + } + } + } + } +#endif + + return ret; + } + int move_file(const char* from, const char* to) + { + return rename(from, to); + } + + int get_disk_space(const char* path, unsigned long long* total, unsigned long long* avail, unsigned long long* block) + { + int ret = 0; + +#if OS_WIN + ULARGE_INTEGER av = { 0 }, + all = { 0 }; + if (GetDiskFreeSpaceExA(path, &av, &all, NULL)) + { + if (total) + *total = all.QuadPart; + if (avail) + *avail = av.QuadPart; + if (block) + { + DWORD sec = 0, + clu = 0; + std::string root(path); + size_t pos = root.find(":\\"); + if (pos != std::string::npos) + root.erase(pos + 2); + if (GetDiskFreeSpaceA(root.c_str(), &clu, &sec, NULL, NULL)) + { + *block = clu * sec; + } + } + } + else + ret = GetLastError(); +#else + struct statfs fs = { 0 }; + + ret = statfs(path, &fs); + if (ret == 0) + { + utils::to_log(LOG_LEVEL_DEBUG, " Total: %lld, Free: %lld, Avail: %lld, block size: %lld\n", + fs.f_blocks, fs.f_bfree, fs.f_bavail, fs.f_bsize); + if (total) + *total = fs.f_blocks * fs.f_bsize; + if (avail) + *avail = fs.f_bavail * fs.f_bsize; + if (block) + *block = fs.f_bsize; + } +#endif + return ret; + } + unsigned int get_page_size(unsigned int* map_unit) + { + unsigned int ps = 1024; +#if OS_WIN + SYSTEM_INFO si = { 0 }; + + GetSystemInfo(&si); + ps = si.dwPageSize; + if (map_unit) + *map_unit = si.dwAllocationGranularity; +#else + ps = sysconf(_SC_PAGESIZE); + if(ps < 1024 || (ps & 0x0fe0000ff)) // nKB && < 16MB + ps = getpagesize(); + if (map_unit) + *map_unit = ps; +#endif + if (ps < 1024 || (ps & 0x0fe0000ff)) // nKB && < 16MB + ps = 1024; + + return ps; + } + + void init_log(log_type type, log_level level, const char* fn_appendix) + { + std::string file(""); + + if (type == LOG_TYPE_FILE) + { + file = get_local_data_path() + PATH_SEPARATOR + "Log"; + create_folder(file.c_str()); + + std::string pe(get_module_full_path()); + size_t pos = pe.rfind(PATH_SEPARATOR[0]); + + if (pos++ == std::string::npos) + pos = 0; + file += PATH_SEPARATOR + pe.substr(pos); + if (fn_appendix) + file += fn_appendix; + file += ".log"; + } + + log_cls::instance()->set_log_type(type, &file[0]); + log_cls::instance()->set_log_level(level); + } + void uninit(void) + { + log_info(("=====================================--Exited--" + std::to_string(GetCurrentProcessId()) + "=====================================\n\n\n\n").c_str(), LOG_LEVEL_FATAL); + + log_cls::clear(); + } + void log_info(const char* info, int level) + { + if (get_log_type() != LOG_TYPE_NONE && get_log_level() <= level) + { + log_cls::instance()->log(("[" + format_current_time() + "]: " + info).c_str()); + } + } + void log_mem_info(const char* desc, const void* data, size_t bytes, int level) + { + if (get_log_type() == LOG_TYPE_NONE || get_log_level() > level) + return; + + std::string line(desc); + char buf[40] = {0}; + + utils::log_info((line + "\n").c_str(), level); + line = ""; + for(size_t i = 0; i < bytes; ++i) + { + if((i % 16) == 0) + { + if(line.length()) + utils::log_info((line + "\n").c_str(), level); + sprintf(buf, "%p ", (const char*)data + i); + line = buf; + } + else if((i % 8) == 0) + line += " "; + sprintf(buf, "%02x ", ((const unsigned char*)data)[i]); + line += buf; + } + if(line.length()) + utils::log_info((line + "\n").c_str(), level); + } + int get_log_type(void) + { + return log_cls::instance()->type(); + } + int get_log_level(void) + { + return log_cls::instance()->level(); + } + + int copy_log_file_to(const char* dst) + { + log_cls::instance()->get_log_file_path(dst); + + return 0; + } + int clear_log_file(void) + { + log_cls::instance()->clear_log(); + + return 0; + } + +#if OS_WIN + bool run_get_message(HWND hwnd, UINT filter_min, UINT filter_max, std::function msghandler) + { + MSG msg = { 0 }; + BOOL ret = GetMessageW(&msg, hwnd, filter_min, filter_max); + bool handled = false; + + if ((DWORD)ret == -1 || !ret) + return false; + + if (msghandler) + ret = msghandler(&msg, &handled) == true; + if (!handled) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + + return ret == TRUE; + } +#endif + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // base64 util .. + static char base64_default_table[] = { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" }; + + base64::base64() : padding_char_('=') + { + base64_ind_[0] = base64_char_[0] = 0; + + initialize_base64_table(base64_default_table); + } + base64::~base64() + {} + + bool base64::is_valid_base64_table(const char* table) + { + bool valid = false; + + if (table && strlen(table) >= 64) + { + char repeat[4] = { 0 }; + + valid = true; + for (int i = 0; i < 63; ++i) + { + repeat[0] = table[i]; + if (strstr(table + i + 1, repeat)) + { + valid = false; + break; + } + } + } + + return valid; + } + bool base64::initialize_base64_table(const char* table) + { + if (!table || strlen(table) < 64) + { + if (memcmp(base64_default_table, base64_char_, 64) == 0) + { + return !table; + } + + memcpy(base64_char_, base64_default_table, 64); + } + else + { + if (memcmp(base64_char_, table, 64) == 0) + { + return true; + } + else if (!is_valid_base64_table(table)) + return false; + + memcpy(base64_char_, table, 64); + } + base64_char_[64] = base64_char_[65] = 0; + + // initialize base64_index + memset(base64_ind_, 0, sizeof(base64_ind_)); + for (int i = 0; i < 64; ++i) + { + base64_ind_[base64_char_[i]] = i; + } + + // padding char + padding_char_ = '='; + if (base64_ind_[padding_char_]) + { + for (padding_char_ = 0x21; padding_char_ < 0x7e && base64_ind_[padding_char_] && padding_char_ != base64_char_[0]; ++padding_char_); + } + + return padding_char_ < 0x7e; + } + + bool base64::set_base64_table(const char* table) + { + return initialize_base64_table(table ? table : base64_default_table); + } + std::string base64::encode(const char* data, size_t bytes, unsigned int line_bytes, bool need_padding) + { + char* str = (char*)malloc(bytes * 2 + 3); + unsigned char c1 = 0, c2 = 0, c3 = 0; + unsigned long line_len = 0; + unsigned long words = bytes / 3; + int rest = bytes % 3, + pos = 0; + std::string ret(""); + + for (unsigned long i = 0; i < words; ++i) + { + // fetch 3 letters + c1 = *data++; + c2 = *data++; + c3 = *data++; + + // encoding into 4-bytes + str[pos++] = base64_char_[c1 >> 2]; + str[pos++] = base64_char_[((c1 << 4) | (c2 >> 4)) & 0x3f]; + str[pos++] = base64_char_[((c2 << 2) | (c3 >> 6)) & 0x3f]; + str[pos++] = base64_char_[c3 & 0x3f]; + line_len += 4; + + // new line ... + if ((unsigned int)line_len > line_bytes - 4) + { + str[pos++] = '\r'; + str[pos++] = '\n'; + line_len = 0; + } + } + + // rest ... + if (rest == 1) + { + c1 = *data++; + str[pos++] = base64_char_[(c1 & 0xfc) >> 2]; + str[pos++] = base64_char_[((c1 & 0x03) << 4)]; + if (need_padding) + { + str[pos++] = padding_char_; + str[pos++] = padding_char_; + } + } + else if (rest == 2) + { + c1 = *data++; + c2 = *data++; + str[pos++] = base64_char_[(c1 & 0xfc) >> 2]; + str[pos++] = base64_char_[((c1 & 0x03) << 4) | ((c2 & 0xf0) >> 4)]; + str[pos++] = base64_char_[((c2 & 0x0f) << 2)]; + if (need_padding) + { + str[pos++] = padding_char_; + } + } + if (pos > 0) + { + str[pos++] = 0; + ret = std::string(str, pos - 1); + } + free(str); + + return ret; + } + std::string base64::decode(const char* data, size_t bytes) + { + char* str = (char*)malloc(bytes + 1); + int pos = 0, + shifts = 18, + value = 0; + std::string ret(""); + + for (int i = 0; i < (int)bytes; ++i) + { + if (*data != '\r' && *data != '\n') + { + if (*data == padding_char_) + { + break; + } + + value += base64_ind_[*data] << shifts; + if (shifts == 0) + { + shifts = 18; + str[pos++] = (value >> 16) & 0x0ff; + str[pos++] = (value >> 8) & 0x0ff; + str[pos++] = (value >> 0) & 0x0ff; + value = 0; + } + else + { + shifts -= 6; + } + } + data++; + } + + if (shifts == 12 || shifts == 6) + { + str[pos++] = (value >> 16) & 0x0ff; + } + else if (shifts == 0) + { + str[pos++] = (value >> 16) & 0x0ff; + str[pos++] = (value >> 8) & 0x0ff; + } + + if (pos > 0) + { + str[pos++] = 0; + ret = std::string(str, pos - 1); + } + free(str); + + return ret; + } + +}; + + + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// refer +refer::refer() : ref_(1) +{ + on_born(); +} +refer::~refer() +{ + on_dead(); +} + +void refer::on_born(void) +{} +void refer::on_dead(void) +{} + +int32_t refer::add_ref(void) +{ + SIMPLE_LOCK(mutex_); + + return ++ref_; +} +int32_t refer::release(void) +{ + int32_t ref = 0; + + { + SIMPLE_LOCK(mutex_); + ref = --ref_; + } + + if (ref == 0) + delete this; + + return ref; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// util +chronograph::chronograph() +{ + reset(); +} +chronograph::~chronograph() +{} + +bool chronograph::now(TIMEV* tv) +{ + struct timezone tz = { 0 }; + + return gettimeofday(tv, &tz) == 0; +} +bool chronograph::now(uint64_t* seconds, uint64_t* u_seconds) +{ + TIMEV tv = { 0 }; + struct timezone tz = { 0 }; + + if (gettimeofday(&tv, &tz) == 0) + { + if (seconds) + *seconds = tv.tv_sec; + if (u_seconds) + *u_seconds = tv.tv_usec; + + return true; + } + else + { + return false; + } +} +std::string chronograph::now(bool with_ms/*whether with milliseconds*/) // return '2022-11-30 10:38:42.123', no '.123' if with_ms was false +{ + TIMEV tv = { 0 }; + + if (!chronograph::now(&tv)) + return ""; + + char buf[40] = { 0 }; + time_t t = tv.tv_sec; + struct tm* l = localtime(&t); + + if (with_ms) + sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d.%06d", l->tm_year + 1900, l->tm_mon + 1, l->tm_mday + , l->tm_hour, l->tm_min, l->tm_sec, tv.tv_usec); + else + sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d", l->tm_year + 1900, l->tm_mon + 1, l->tm_mday + , l->tm_hour, l->tm_min, l->tm_sec); + + return buf; +} + +uint64_t chronograph::elapse_s(void) +{ + TIMEV tv = { 0 }; + + chronograph::now(&tv); + + return tv.tv_sec - bgn_.tv_sec; +} +uint64_t chronograph::elapse_ms(void) +{ + TIMEV tv = { 0 }; + uint64_t dif = 0; + + chronograph::now(&tv); + dif = SEC_2_MS(tv.tv_sec - bgn_.tv_sec); + dif += tv.tv_usec / MSEC_2_US(1); + dif -= bgn_.tv_usec / MSEC_2_US(1); + + return dif; +} +uint64_t chronograph::elapse_us(void) +{ + TIMEV tv = { 0 }; + uint64_t dif = 0; + + chronograph::now(&tv); + dif = SEC_2_US(tv.tv_sec - bgn_.tv_sec); + dif += tv.tv_usec; + dif -= bgn_.tv_usec; + + return dif; +} +void chronograph::reset() +{ + chronograph::now(&bgn_); +} + + + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// event + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// windows event ... +#if OS_WIN +int __stdcall sem_init(sem_t* handle, int, int) +{ + if (!handle) + { + errno = EINVAL; + return -1; + } + + *handle = CreateEvent(NULL, TRUE, FALSE, NULL); + if (*handle) + return 0; + else + { + errno = GetLastError(); + + return -1; + } +} +void __stdcall sem_destroy(sem_t* handle) +{ + if (*handle) + { + CloseHandle(*handle); + *handle = NULL; + } +} +int __stdcall sem_trywait(sem_t* handle) +{ + return WaitForSingleObject(*handle, 1) == WAIT_TIMEOUT ? -1 : 0; +} +void __stdcall sem_wait(sem_t* handle) +{ + if (WaitForSingleObject(*handle, INFINITE) == WAIT_OBJECT_0) + ResetEvent(*handle); +} +int __stdcall sem_timedwait(sem_t* handle, struct timespec* to) +{ + DWORD elapse = to->tv_sec * 1000; + elapse += to->tv_nsec / (1000 * 1000); + + int ret = WaitForSingleObject(*handle, elapse) == WAIT_TIMEOUT ? -1 : 0; + + if (ret == 0) + ResetEvent(*handle); + + return ret; +} +void __stdcall sem_post(sem_t* handle) +{ + SetEvent(*handle); +} + +#endif + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// platform_event (base on semaphore) +platform_event::platform_event(const char* info) : waiting_(false), dbg_info_(info ? info : "") +{ + int err = sem_init(&sem_, 0, 0); + + if (err == -1) + { + err = errno; + utils::to_log(LOG_LEVEL_FATAL, "(%p)sem_init failed: %d\n", this, err); + } +} +platform_event::~platform_event() +{ + sem_destroy(&sem_); +} + +bool platform_event::try_wait(void) +{ + return sem_trywait(&sem_) == 0; +} +bool platform_event::wait(unsigned timeout) +{ + bool waited = true; + + if(log_) + utils::to_log(LOG_LEVEL_DEBUG, "platform_event(%p - %s) --> waiting...\n", this, dbg_info_.c_str()); + waiting_ = true; + if (timeout == USB_TIMEOUT_INFINITE) + sem_wait(&sem_); + else + { + struct timespec to; + to.tv_sec = timeout / 1000; + to.tv_nsec = (long)((timeout % 1000) * 1000 * 1000); + waited = sem_timedwait(&sem_, &to) == 0; + } + if (log_) + utils::to_log(LOG_LEVEL_DEBUG, "platform_event(%p - %s) --> %s.\n", this, dbg_info_.c_str(), waited ? "waited" : "wait timeout"); + waiting_ = false; + + return waited; +} +void platform_event::trigger(void) +{ + sem_post(&sem_); +} +bool platform_event::is_waiting(void) +{ + return waiting_; +} +void platform_event::set_debug_info(const char* info) +{ + dbg_info_ = info ? info : ""; +} +void platform_event::enable_log(bool enable) +{ + log_ = enable; +} + + + + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// shared_memory +shared_memory::shared_memory(unsigned long long key, size_t size) : key_(key), obj_((void*)-1), first_(true), bytes_(size), len_(0) +{ + unsigned int* ptr = (unsigned int*)&key_; + utils::to_log(LOG_LEVEL_DEBUG, "shared memory key = 0x%x%08x\n", ptr[1], ptr[0]); + + init(); +} +shared_memory::~shared_memory() +{ + clear(); +} + +void shared_memory::init(void) +{ +#if OS_WIN + char name[40] = { 0 }; + DWORD* key = (DWORD*)&key_; + HANDLE h = NULL; + + sprintf(name, "scanner_0x%08x-%08x", key[1], key[0]); + h = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, bytes_, name); + if (h == NULL) + return; + + first_ = !(GetLastError() == ERROR_ALREADY_EXISTS); + obj_ = (void*)h; +#else + int obj = shmget(key_, bytes_, IPC_EXCL | IPC_CREAT | 0666); + if (obj < 0) + { + unsigned int* v = (unsigned int*)&key_; + if (errno == EEXIST) + { + first_ = false; + obj = shmget(key_, bytes_, 0600); + if (obj == -1) + obj = shmget(key_, bytes_, 0); + utils::to_log(LOG_LEVEL_DEBUG, "open existing: shmget(0x%x%08x) = %d\n", v[1], v[0], obj); + obj_ = (void*)obj; + + std::string prev(read()), proc(""); + utils::to_log(LOG_LEVEL_DEBUG, "shared memory content: %s\n", prev.c_str()); + if (prev.length()) + { + proc = prev; + size_t pos = proc.find("pid: "); + if (pos != std::string::npos) + proc.erase(0, pos + 5); + pos = proc.find(")"); + if (pos != std::string::npos) + proc.erase(pos); + proc = shared_memory::get_proc_name_by_pid(atoi(proc.c_str())); + if (proc.length()) + { + pos = prev.find("("); + if (pos == std::string::npos) + pos = prev.length(); + if (strcasecmp(proc.c_str(), prev.substr(0, pos).c_str())) + proc = ""; + } + } + if (proc.empty()) + { + first_ = true; + clear(); + obj = shmget(key_, bytes_, IPC_EXCL | IPC_CREAT | 0600); + utils::to_log(LOG_LEVEL_DEBUG, "%s is not existing and reopen it\n", prev.c_str()); + } + } + else + { + utils::to_log(LOG_LEVEL_DEBUG, "shmget(0x%x%08x) = %d\n", v[1], v[0], errno); + return; + } + } + obj_ = (void*)obj; + utils::to_log(LOG_LEVEL_DEBUG, "shared memory id = %d[%s], \n", obj, first_ ? "created" : "opened"); +#endif + + if (first_) + { + pid_t pid = getpid(); + std::string me(""); + char buf[40] = { 0 }; + unsigned int* pn = (unsigned int*)&pid; + + if (sizeof(pid) > 4 && pn[1]) + sprintf(buf, "(pid: 0x%x%08x)", pn[1], pn[0]); + else + sprintf(buf, "(pid: %u)", pn[0]); + + me = utils::get_module_full_path(); + pid = me.rfind(PATH_SEPARATOR[0]); + if ((size_t)pid != std::string::npos) + me.erase(0, pid + 1); + + me += buf; + write(me.c_str(), me.length()); + } +} +void shared_memory::clear(void) +{ + if (obj_ != (void*)-1) +#if OS_WIN + CloseHandle((HANDLE)obj_); +#else + { + if (first_) + { + struct shmid_ds ds = { 0 }; + int* h = (int*)&obj_; + shmctl(*h, IPC_RMID, &ds); + } + } +#endif + + obj_ = (void*)-1; +} +char* shared_memory::get_buf(void) +{ +#if OS_WIN + char* buf = (char*)MapViewOfFile((HANDLE)obj_, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); + if (!buf) + buf = (char*)-1; +#else + int* h = (int*)&obj_; + char* buf = (char*)shmat(*h, 0, 0); + utils::to_log(LOG_LEVEL_DEBUG, "shared memory %d buffer = %p, error = %d\n", *h, buf, errno); +#endif + + return buf; +} +void shared_memory::release_buf(void* buf) +{ +#if OS_WIN + UnmapViewOfFile(buf); +#else + shmdt(buf); +#endif +} + +#if !OS_WIN +std::string shared_memory::get_proc_name_by_pid(pid_t pid) +{ + char path[512] = { 0 }; + unsigned int* v = (unsigned int*)&pid; + std::string ret(""); + + if (sizeof(pid) > 4 && v[1]) + sprintf(path, "/proc/%lld/status", pid); + else + sprintf(path, "/proc/%u/status", pid); + + FILE* src = fopen(path, "rb"); + if (src) + { + char val[512] = { 0 }; + + memset(path, 0, sizeof(path)); + fgets(path, sizeof(path) - 1, src); + fclose(src); + sscanf(path, "%*s %s", val); + ret = val; + } + if (sizeof(pid) > 4 && v[1]) + { + utils::to_log(LOG_LEVEL_DEBUG, "PID(%lld) name is: %s\n", pid, ret.c_str()); + } + else + { + utils::to_log(LOG_LEVEL_DEBUG, "PID(%u) name is: %s\n", pid, ret.c_str()); + } + + return ret; +} +#endif + +bool shared_memory::is_ok(void) +{ + return obj_ != (void*)-1; +} +bool shared_memory::is_first(void) +{ + return is_ok() && first_; +} +std::string shared_memory::read(void) +{ + if (obj_ == (void*)-1) + return ""; + + char* buf = get_buf(); + if (buf == (char*)-1) + return ""; + + std::string ret(""); + size_t len = 0; + int off = 4; // sizeof(size_t); + + memcpy(&len, buf, off); + ret = std::string(buf + off, len); + release_buf(buf); + + return ret; +} +int shared_memory::write(const char* data, size_t len) +{ + if (len > bytes_) + return E2BIG; + + char* buf = get_buf(); + int off = 4; // sizeof(len); + if (buf == (char*)-1) + return errno; + + memcpy(buf, &len, off); + memcpy(buf + off, data, len); + len_ = len; + release_buf(buf); + + return 0; +} + + + + + + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// safe_thread +safe_thread::safe_thread() : excep_que_("thread-exception") +{ + notify_thread_.reset(new std::thread(&safe_thread::thread_notify_exception, this)); +} +safe_thread::~safe_thread() +{ + run_ = false; + excep_que_.trigger(); + + if(notify_thread_->joinable()) + notify_thread_->join(); + + for(auto& v: threads_) + { + if(v.thread.get() && v.thread->joinable()) + v.thread->join(); + } + threads_.clear(); +} + +void safe_thread::thread_worker(std::function func, std::string name) +{ + try + { + utils::to_log(LOG_LEVEL_DEBUG, "safe_thread of '%s' is running ...\n", name.c_str()); + func(); + utils::to_log(LOG_LEVEL_DEBUG, "safe_thread of '%s' exited.\n", name.c_str()); + return; + } + catch (std::exception e) + { + utils::to_log(LOG_LEVEL_FATAL, "Exception in thread '%s': %s\n", name.c_str(), e.what()); + } + catch (...) + { + utils::to_log(LOG_LEVEL_FATAL, "Unknown exception in thread '%s'!\n", name.c_str()); + } + excep_que_.save(name, true); +} +void safe_thread::thread_notify_exception(void) +{ + while(run_) + { + std::string name(""); + if(excep_que_.take(name, true)) + { + if(excep_handler_) + excep_handler_(name.c_str()); + } + } +} + +void safe_thread::set_exception_handler(std::function on_exception) +{ + excep_handler_ = on_exception; +} +int safe_thread::start(std::function f, const char* thread_name) +{ + SAFETHRD st; + + st.name = thread_name ? thread_name : ""; + st.thread.reset(new std::thread(&safe_thread::thread_worker, this, f, thread_name)); + + { + SIMPLE_LOCK(lock_); + threads_.push_back(st); + } + + return 0; +} +int safe_thread::stop(const char* thread_name) +{ + int ret = ENOENT; + + SIMPLE_LOCK(lock_); + for(int i = 0; i < threads_.size(); ++i) + { + if(threads_[i].name == thread_name) + { + if(threads_[i].thread.get() && threads_[i].thread->joinable()) + threads_[i].thread->join(); + threads_.erase(threads_.begin() + i); + ret = 0; + break; + } + } + + return ret; +} \ No newline at end of file diff --git a/sdk/base/utils.h b/sdk/base/utils.h new file mode 100644 index 0000000..1805f35 --- /dev/null +++ b/sdk/base/utils.h @@ -0,0 +1,368 @@ +// utilities for platform ... +// +// Date: 2023-06-30 +// + +#pragma once + +#include "plat_types.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#define USE_SAFE_THREAD + + +enum log_type +{ + LOG_TYPE_NONE = 0, // no logging + LOG_TYPE_CONSOLE, // print to console + LOG_TYPE_FILE, // write log into file +}; +enum log_level +{ + LOG_LEVEL_ALL = 0, + LOG_LEVEL_DEBUG, + LOG_LEVEL_WARNING, + LOG_LEVEL_FATAL, +}; + +namespace utils +{ + std::string utf82ansi(const char* utf8); + std::string ansi2utf8(const char* ansi); + + std::string get_command_result(const char* cmd, int len = -1, int *err = nullptr); + std::string get_local_data_path(void); + std::string temporary_path(void); + std::string format_current_time(void); + std::string get_module_full_path(const char* part_name = nullptr/*nullptr to get main-pe/first module's full path*/); + std::string find_file(const char* root_dir, const char* part_name, bool recursive = false); + std::string target_file_from_link(const char* lnk_file); + std::string get_ini_value(const char* seg, const char* key, const char* cfg_file); // return "" if not found + std::string load_mini_file(const char* file, int* err); // <= 1MB + int save_2_file(void* data, size_t len, const char* file, bool append = false/*append or new*/, size_t max_size = -1/*in append mode, truncate file if size is exceeded this value if was not -1*/); + + bool is_match_pattern(const char* text, const char* pattern); + const char* to_lower(std::string& str); // return str.c_str() + const char* trim(std::string& str, const char* sp = "\r\n\t "); // return str.c_str() + + HMODULE load_dll(const char* path_file, int flag); + bool create_folder(const char* folder); + void set_ini_value(const char* seg, const char* key, const char* val, const char* cfg_file); + int enum_file(const char* folder, bool recursive, bool/*return false to stop enumeration*/(STDCALL* found)(const char* path_name, bool dir, void* param), void* param); + int move_file(const char* from, const char* to); + + int get_disk_space(const char* path, unsigned long long* total, unsigned long long* avail, unsigned long long* block); + unsigned int get_page_size(unsigned int* map_unit = nullptr); + + void init_log(log_type type, log_level level = LOG_LEVEL_ALL, const char* fn_appendix = nullptr/*appendix to default log-file-name*/); + void uninit(void); + void log_info(const char* info, int level = LOG_LEVEL_ALL); + void log_mem_info(const char* desc, const void* data, size_t bytes, int level = LOG_LEVEL_ALL); // log as 0x12345678 00 01 02 ... + int get_log_type(void); + int get_log_level(void); + int copy_log_file_to(const char* dst); + int clear_log_file(void); + +#if OS_WIN + // Function: pump message recycle (GetMessageW) + // + // Parameters: hwnd - target window + // + // filter_min - the minimal message + // + // filter_max - the maximal message + // + // msghandler - custom message processor + // MSG*, received message by GetMessage + // bool*, return whether user handled the message, no TranslateMessage and DispatchMessage if this was true + // + // Return: whether quit the message recycle + bool run_get_message(HWND hwnd, UINT filter_min = 0, UINT filter_max = 0, std::function msghandler = std::function()); +#endif + + template + void to_log(int level, const char* fmt, Args ... args) + { + if (get_log_type() != LOG_TYPE_NONE && get_log_level() <= level) + { + size_t size = snprintf(nullptr, 0, fmt, args ...) + 2; + std::unique_ptr buf(new char[size]); + + snprintf(buf.get(), size, fmt, args ...); + log_info(buf.get(), (log_level)level); + } + } + + template + void to_log_with_api(bool(*is_enable)(int), void(*log_api)(const char*, int), int level, const char* fmt, Args ... args) + { + if (is_enable(level)) + { + size_t size = snprintf(nullptr, 0, fmt, args ...) + 2; + std::unique_ptr buf(new char[size]); + + snprintf(buf.get(), size, fmt, args ...); + log_api(buf.get(), (log_level)level); + } + } + + template + T swap_half(T v) + { + T mask = (1 << (sizeof(T) * 4)) - 1, + h = v & mask; + + v >>= sizeof(T) * 4; + v &= mask; + h <<= sizeof(T) * 4; + v |= h; + + return v; + } + + + class base64 + { + char base64_ind_[128]; + char base64_char_[80]; + char padding_char_; + + bool is_valid_base64_table(const char* table); + bool initialize_base64_table(const char* table); + + public: + base64(); + ~base64(); + + public: + bool set_base64_table(const char* table = nullptr); + std::string encode(const char* data, size_t bytes, unsigned int line_bytes = -1, bool need_padding = true); + std::string decode(const char* data, size_t bytes); + }; +}; + +#if OS_WIN +struct _time_val +{ + time_t tv_sec; /* Seconds. */ + time_t tv_usec; /* Microseconds. */ +}; +typedef struct _time_val TIMEV; + +struct timezone +{ + int tz_minuteswest; /* Minutes west of GMT. */ + int tz_dsttime; /* Nonzero if DST is ever in effect. */ +}; +int gettimeofday(TIMEV* tv, struct timezone* tz); + +#else +#include + +typedef struct timeval TIMEV; +#endif + +// object life referer +// +// derived from 'refer' if your class used in multi-threads +// +#define MUTEX std::mutex +#define LOCK_WITH_NAME(n, v) std::lock_guard n(v) +#define SIMPLE_LOCK(v) LOCK_WITH_NAME(locker, v) + +class refer +{ + volatile int32_t ref_; + MUTEX mutex_; + +protected: + refer(); + virtual ~refer(); + + virtual void on_born(void); + virtual void on_dead(void); + +public: + virtual int32_t add_ref(void); + virtual int32_t release(void); +}; + +// time utility +class chronograph +{ + TIMEV bgn_; + +public: + chronograph(); + ~chronograph(); + + static bool now(TIMEV* tv); + static bool now(uint64_t* seconds, uint64_t* u_seconds); + static std::string now(bool with_ms = true/*whether with milliseconds*/); // return '2022-11-30 10:38:42.123', no '.123' if with_ms was false + +public: + uint64_t elapse_s(void); + uint64_t elapse_ms(void); + uint64_t elapse_us(void); + void reset(void); +}; + +// event +class platform_event : public refer +{ + sem_t sem_; + volatile bool waiting_; + std::string dbg_info_; + bool log_ = true; + +public: + platform_event(const char* info = ""); + ~platform_event(); + +public: + bool try_wait(void); + bool wait(unsigned timeout = USB_TIMEOUT_INFINITE/*ms*/); // USB_TIMEOUT_INFINITE is waiting unfinite, true when watied and false for wait timeout + void trigger(void); + bool is_waiting(void); + void enable_log(bool enable); + + void set_debug_info(const char* info); +}; + +// share memory +class shared_memory : public refer +{ + unsigned long long key_; + void* obj_; + bool first_; + size_t bytes_; + size_t len_; + + void init(void); + void clear(void); + char* get_buf(void); + void release_buf(void* buf); + +#if !defined(WIN32) && !defined(_WIN64) + static std::string get_proc_name_by_pid(pid_t pid); +#endif + +public: + shared_memory(unsigned long long key, size_t size = 1024); + +protected: + ~shared_memory(); + +public: + bool is_ok(void); + bool is_first(void); + std::string read(void); + int write(const char* data, size_t len); +}; + + +template +class safe_fifo +{ + MUTEX lock_; + std::deque que_; + platform_event* wait_; + +public: + safe_fifo(const char* who) : wait_(new platform_event(who && *who ? who : "fifo")) + {} + ~safe_fifo() + { + wait_->release(); + } + +public: + size_t save(const T& t, bool notify = false) + { + SIMPLE_LOCK(lock_); + size_t cnt = que_.size(); + + que_.push_back(std::move(t)); + if (notify) + wait_->trigger(); + + return cnt + 1; + } + bool take(T& t, bool wait = false) + { + if (wait && size() == 0) + { + wait_->wait(); + } + + { + SIMPLE_LOCK(lock_); + + if (que_.size()) + { + t = std::move(que_.front()); + que_.pop_front(); + + return true; + } + else + { + return false; + } + } + } + size_t size(void) + { + SIMPLE_LOCK(lock_); + + return que_.size(); + } + void clear(void) + { + SIMPLE_LOCK(lock_); + + que_.clear(); + } + void trigger(void) + { + wait_->trigger(); + } + void enable_wait_log(bool enable) + { + wait_->enable_log(enable); + } +}; + +class safe_thread +{ + typedef struct _safe_thrd + { + std::string name; + std::shared_ptr thread; + }SAFETHRD; + volatile bool run_ = true; + MUTEX lock_; + std::unique_ptr notify_thread_; + std::vector threads_; + safe_fifo excep_que_; + std::function excep_handler_ = std::function(); + + void thread_worker(std::function func, std::string name); + void thread_notify_exception(void); + +public: + safe_thread(void); + ~safe_thread(); + +public: + void set_exception_handler(std::function on_exception = std::function()); + int start(std::function f, const char* thread_name); + int stop(const char* thread_name); +}; diff --git a/sdk/huagao/hgscanner_error.h b/sdk/huagao/hgscanner_error.h new file mode 100644 index 0000000..906a810 --- /dev/null +++ b/sdk/huagao/hgscanner_error.h @@ -0,0 +1,380 @@ +// this file is include huagao scanner error definitions +// +// created: 2022-02-07 +// + +#pragma once + +#define RETURN_IF(var, enum_val) \ + if(var == enum_val) \ + return #enum_val; +#define RETURN_DESC_IF(var, hgerr) \ + if(var == hgerr) \ + return from_default_language(STATU_DESC_##hgerr); + +enum scanner_err +{ + SCANNER_ERR_OK = 0, // æˆåŠŸï¼Œæ­£å¸¸çŠ¶æ€ + + // 1:软件逻辑错误 + SCANNER_ERR_INVALID_PARAMETER = 0x100, // éžæ³•çš„å‚数调用 + SCANNER_ERR_USER_CANCELED, // 用户å–消了æ“作 + SCANNER_ERR_INSUFFICIENT_MEMORY, // 分é…的内存ä¸è¶³ + SCANNER_ERR_ACCESS_DENIED, // è®¿é—®è¢«æ‹’ç» + SCANNER_ERR_IO_PENDING, // 异步访问,数æ®ç¨åŽè¿”回 + SCANNER_ERR_NOT_EXACT, // æ•°æ®ä¸ç²¾ç¡®ï¼Œç²¾ç¡®çš„æ•°æ®å·²ç»åœ¨åŒä¸€ç¼“存中返回 + SCANNER_ERR_CONFIGURATION_CHANGED, // 设备的é…置项å‘生改å˜ï¼Œéœ€è¦é‡æ–°åŠ è½½æ˜¾ç¤º + SCANNER_ERR_NOT_OPEN, // 设备未打开 + SCANNER_ERR_NOT_START, // 设备没有å¯åŠ¨ + SCANNER_ERR_NOT_ANY_MORE, // 用于回调返回,在本次扫æ中,对相åŒæ“作ä¸å†å›žè°ƒ + SCANNER_ERR_NO_DATA, // æ²¡æœ‰æ•°æ® + SCANNER_ERR_HAS_DATA_YET, // 有数æ®æœªè¢«è¯»å–(异步æ“作中) + SCANNER_ERR_OUT_OF_RANGE, // 相关æ“作超出范围 + SCANNER_ERR_IO, // IO错误 + SCANNER_ERR_TIMEOUT, // 超时错误 + SCANNER_ERR_OPEN_FILE_FAILED, // 打开本地文件失败 + SCANNER_ERR_CREATE_FILE_FAILED, // 创建本地文件失败 + SCANNER_ERR_WRITE_FILE_FAILED, // 写本地文件失败 + SCANNER_ERR_DATA_DAMAGED, // æ•°æ®æŸå(内置资æºæ•°æ®æŸå) + SCANNER_ERR_OPENED_BY_OTHER_PROCESS, // 设备已ç»è¢«å…¶å®ƒè¿›ç¨‹æ‰“å¼€å ç”¨ + SCANNER_ERR_LANG_PAK_LOST, // 语言包丢失 + SCANNER_ERR_RELOAD_IMAGE_PARAM, // é…ç½®æˆåŠŸï¼Œä¼šå½±å“图åƒå‚数,应用需è¦é‡æ–°åŠ è½½å›¾åƒå‚æ•° - added on 2023-02-18 for XSANE修改影å“图åƒå‚数的属性åŽæ‰«æ崩溃的问题 + SCANNER_ERR_RELOAD_OPT_PARAM, // SCANNER_ERR_CONFIGURATION_CHANGED + SCANNER_ERR_RELOAD_IMAGE_PARAM - added on 2023-02-18 for XSANE修改影å“图åƒå‚数的属性åŽæ‰«æ崩溃的问题 + SCANNER_ERR_THROW_EXCEPTION, // catched exception + + // 2:USB错误 + SCANNER_ERR_USB_INIT_FAILED = 0x5b00, // libusb_init 失败 + SCANNER_ERR_USB_REGISTER_PNP_FAILED, // 注册USB监å¬äº‹ä»¶å¤±è´¥ + SCANNER_ERR_USB_CLAIM_INTERFACE_FAILED, // failed in calling libusb_claim_interface + + // 3:硬件错误 + SCANNER_ERR_DEVICE_NOT_FOUND = 0x0de00, // 设备未找到 + SCANNER_ERR_DEVICE_NOT_SUPPORT, // 设备ä¸æ”¯æŒè¯¥æ“作 + SCANNER_ERR_DEVICE_BUSY, // 设备正忙,ä¸èƒ½å“应该æ“作 + SCANNER_ERR_DEVICE_SLEEPING, // 设备处于ç¡çœ çŠ¶æ€ + SCANNER_ERR_DEVICE_COUNT_MODE, // 设备处于计数扫æ状æ€ï¼Ÿ + SCANNER_ERR_DEVICE_STOPPED, // 扫æåœæ­¢ + SCANNER_ERR_DEVICE_COVER_OPENNED, // 扫æ仪盖æ¿å‘ˆæ‰“å¼€çŠ¶æ€ + SCANNER_ERR_DEVICE_NO_PAPER, // 没有纸张输入 + SCANNER_ERR_DEVICE_FEEDING_PAPER, // æ“纸失败 + SCANNER_ERR_DEVICE_DOUBLE_FEEDING, // åŒå¼ æ£€æµ‹ + SCANNER_ERR_DEVICE_PAPER_JAMMED, // å¡çº¸ + SCANNER_ERR_DEVICE_STAPLE_ON, // 有钉书钉 + SCANNER_ERR_DEVICE_PAPER_SKEW, // 纸张倾斜 + SCANNER_ERR_DEVICE_SIZE_CHECK, // 尺寸检测错误 + SCANNER_ERR_DEVICE_DOGEAR, // 纸张有折角 + SCANNER_ERR_DEVICE_NO_IMAGE, // 设备没å–到图 + SCANNER_ERR_DEVICE_SCANN_ERROR, // 设备扫图失败 + SCANNER_ERR_DEVICE_PC_BUSY, // PCç¹å¿™æˆ–出错 + SCANNER_ERR_DEVICE_ISLOCK, // 设备被é”定 + SCANNER_ERR_DEVICE_UPGRADE_SUCCESSFUL, // 固件å‡çº§æˆåŠŸ + SCANNER_ERR_DEVICE_UPGRADE_FAIL, // 固件å‡çº§å¤±è´¥+ + SCANNER_ERR_DEVICE_AUTO_FAIL_OVER, // è®¾å¤‡å¹³åœºè‡ªåŠ¨æ ¡æ­£ç»“æŸ + SCANNER_ERR_DEVICE_AUTO_FAIL_INFO, // 设备平场自动校正信æ¯ä¼ è¾“ + SCANNER_ERR_DEVICE_DISTORTION, // 畸å˜ä¿®æ­£å¤±è´¥ + SCANNER_ERR_DEVICE_MAYBE_IS_HOLE, // 纸张有孔洞 + SCANNER_ERR_DEVICE_DEVS_BOOTING, // 请ç¨ç­‰ - 设备正在准备 + SCANNER_ERR_DEVICE_UNKNOWN_STATUS, // è®¾å¤‡å¤„äºŽæœªçŸ¥çŠ¶æ€ +}; + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// 状æ€ä¿¡æ¯æ述符 +// +// #define STATU_DESC_PREPARE_START "准备å¯åŠ¨â€¦â€¦" +#define STATU_DESC_PREPARE_START "\345\207\206\345\244\207\345\220\257\345\212\250\342\200\246\342\200\246" +#define ID_STATU_DESC_PREPARE_START 62135 + +// #define STATU_DESC_REWRITE_CONFIGURATION "写入é…置……" +#define STATU_DESC_REWRITE_CONFIGURATION "\345\206\231\345\205\245\351\205\215\347\275\256\342\200\246\342\200\246" +#define ID_STATU_DESC_REWRITE_CONFIGURATION 16489 + +// #define STATU_DESC_CLEAR_CACHE "清ç†ç¼“存……" +#define STATU_DESC_CLEAR_CACHE "\346\270\205\347\220\206\347\274\223\345\255\230\342\200\246\342\200\246" +#define ID_STATU_DESC_CLEAR_CACHE 6523 + +// #define STATU_DESC_START_SUCCESS "å¯åŠ¨æˆåŠŸ" +#define STATU_DESC_START_SUCCESS "\345\220\257\345\212\250\346\210\220\345\212\237" +#define ID_STATU_DESC_START_SUCCESS 33731 + +// #define STATU_DESC_START_FAIL "å¯åŠ¨å¤±è´¥" +#define STATU_DESC_START_FAIL "\345\220\257\345\212\250\345\244\261\350\264\245" +#define ID_STATU_DESC_START_FAIL 51358 + +// #define STATU_DESC_SCAN_WORKING "正在扫æ……" +#define STATU_DESC_SCAN_WORKING "\346\255\243\345\234\250\346\211\253\346\217\217\342\200\246\342\200\246" +#define ID_STATU_DESC_SCAN_WORKING 21315 + +// #define STATU_DESC_SCAN_STOPPED "扫æ完æˆ" +#define STATU_DESC_SCAN_STOPPED "\346\211\253\346\217\217\345\256\214\346\210\220" +#define ID_STATU_DESC_SCAN_STOPPED 17731 + +// #define STATU_DESC_SCAN_CANCELED "扫æå·²å–消" +#define STATU_DESC_SCAN_CANCELED "\346\211\253\346\217\217\345\267\262\345\217\226\346\266\210" +#define ID_STATU_DESC_SCAN_CANCELED 63314 + +// #define STATU_DESC_WAIT_FOR_MEM "内存ä¸è¶³ï¼Œç­‰å¾…内存释放……" +#define STATU_DESC_WAIT_FOR_MEM "\345\206\205\345\255\230\344\270\215\350\266\263\357\274\214\347\255\211\345\276\205\345\206\205\345\255\230\351\207\212\346\224\276\342\200\246\342\200\246" +#define ID_STATU_DESC_WAIT_FOR_MEM 7697 + +// #define STATU_DESC_DEVICE_RESET "正在é‡ç½®ï¼Œå¦‚果设备迟迟没有动作,请é‡æ–°æ‰«æ……" +#define STATU_DESC_DEVICE_RESET "\346\255\243\345\234\250\351\207\215\347\275\256\357\274\214\345\246\202\346\236\234\350\256\276\345\244\207\350\277\237\350\277\237\346\262\241\346\234\211\345\212\250\344\275\234\357\274\214\350\257\267\351\207\215\346\226\260\346\211\253\346\217\217\342\200\246\342\200\246" +#define ID_STATU_DESC_DEVICE_RESET 47482 + +// #define STATU_DESC_SCANNER_ERR_OK "æ“作æˆåŠŸ" +#define STATU_DESC_SCANNER_ERR_OK "\346\223\215\344\275\234\346\210\220\345\212\237" +//#define ID_STATU_DESC_SCANNER_ERR_OK 3249 + +// #define STATU_DESC_SCANNER_ERR_USER_CANCELED "æ“作被å–消" +#define STATU_DESC_SCANNER_ERR_USER_CANCELED "\346\223\215\344\275\234\350\242\253\345\217\226\346\266\210" +#define ID_STATU_DESC_SCANNER_ERR_USER_CANCELED 48546 + +// #define STATU_DESC_SCANNER_ERR_DATA_DAMAGED "æ•°æ®æŸå" +#define STATU_DESC_SCANNER_ERR_DATA_DAMAGED "\346\225\260\346\215\256\346\215\237\345\235\217" +#define ID_STATU_DESC_SCANNER_ERR_DATA_DAMAGED 45852 + +// #define STATU_DESC_SCANNER_ERR_INVALID_PARAMETER "éžæ³•çš„å‚数调用" +#define STATU_DESC_SCANNER_ERR_INVALID_PARAMETER "\351\235\236\346\263\225\347\232\204\345\217\202\346\225\260\350\260\203\347\224\250" +#define ID_STATU_DESC_SCANNER_ERR_INVALID_PARAMETER 6119 + +// #define STATU_DESC_SCANNER_ERR_INSUFFICIENT_MEMORY "内存ä¸è¶³æˆ–内存分é…失败" +#define STATU_DESC_SCANNER_ERR_INSUFFICIENT_MEMORY "\345\206\205\345\255\230\344\270\215\350\266\263\346\210\226\345\206\205\345\255\230\345\210\206\351\205\215\345\244\261\350\264\245" +#define ID_STATU_DESC_SCANNER_ERR_INSUFFICIENT_MEMORY 56958 + +// #define STATU_DESC_SCANNER_ERR_ACCESS_DENIED "访问被拒ç»" +#define STATU_DESC_SCANNER_ERR_ACCESS_DENIED "\350\256\277\351\227\256\350\242\253\346\213\222\347\273\235" +#define ID_STATU_DESC_SCANNER_ERR_ACCESS_DENIED 56275 + +// #define STATU_DESC_SCANNER_ERR_IO_PENDING "异步访问,数æ®ç¨åŽè¿”回" +#define STATU_DESC_SCANNER_ERR_IO_PENDING "\345\274\202\346\255\245\350\256\277\351\227\256\357\274\214\346\225\260\346\215\256\347\250\215\345\220\216\350\277\224\345\233\236" +#define ID_STATU_DESC_SCANNER_ERR_IO_PENDING 25758 + +// #define STATU_DESC_SCANNER_ERR_NOT_EXACT "æ•°æ®ä¸ç²¾ç¡®ï¼Œç²¾ç¡®çš„æ•°æ®å·²ç»åœ¨åŒä¸€ç¼“存中返回" +#define STATU_DESC_SCANNER_ERR_NOT_EXACT "\346\225\260\346\215\256\344\270\215\347\262\276\347\241\256\357\274\214\347\262\276\347\241\256\347\232\204\346\225\260\346\215\256\345\267\262\347\273\217\345\234\250\345\220\214\344\270\200\347\274\223\345\255\230\344\270\255\350\277\224\345\233\236" +#define ID_STATU_DESC_SCANNER_ERR_NOT_EXACT 64193 + +// #define STATU_DESC_SCANNER_ERR_CONFIGURATION_CHANGED "设备的é…置项å‘生改å˜ï¼Œéœ€è¦é‡æ–°åŠ è½½æ˜¾ç¤º" +#define STATU_DESC_SCANNER_ERR_CONFIGURATION_CHANGED "\350\256\276\345\244\207\347\232\204\351\205\215\347\275\256\351\241\271\345\217\221\347\224\237\346\224\271\345\217\230\357\274\214\351\234\200\350\246\201\351\207\215\346\226\260\345\212\240\350\275\275\346\230\276\347\244\272" +#define ID_STATU_DESC_SCANNER_ERR_CONFIGURATION_CHANGED 26730 + +// #define STATU_DESC_SCANNER_ERR_RELOAD_IMAGE_PARAM "图åƒå‚æ•°å·²ç»æ”¹å˜ï¼Œè¯·é‡æ–°åŠ è½½" +#define STATU_DESC_SCANNER_ERR_RELOAD_IMAGE_PARAM "\345\233\276\345\203\217\345\217\202\346\225\260\345\267\262\347\273\217\346\224\271\345\217\230\357\274\214\350\257\267\351\207\215\346\226\260\345\212\240\350\275\275" +#define ID_STATU_DESC_SCANNER_ERR_RELOAD_IMAGE_PARAM 54637 + +// #define STATU_DESC_SCANNER_ERR_RELOAD_OPT_PARAM "å…³è”属性状æ€åŠå›¾åƒå‚æ•°å·²ç»æ”¹å˜ï¼Œè¯·é‡æ–°åŠ è½½" +#define STATU_DESC_SCANNER_ERR_RELOAD_OPT_PARAM "\345\205\263\350\201\224\345\261\236\346\200\247\347\212\266\346\200\201\345\217\212\345\233\276\345\203\217\345\217\202\346\225\260\345\267\262\347\273\217\346\224\271\345\217\230\357\274\214\350\257\267\351\207\215\346\226\260\345\212\240\350\275\275" +#define ID_STATU_DESC_SCANNER_ERR_RELOAD_OPT_PARAM 4762 + +// #define STATU_DESC_SCANNER_ERR_NOT_OPEN "设备未打开" +#define STATU_DESC_SCANNER_ERR_NOT_OPEN "\350\256\276\345\244\207\346\234\252\346\211\223\345\274\200" +#define ID_STATU_DESC_SCANNER_ERR_NOT_OPEN 38521 + +// #define STATU_DESC_SCANNER_ERR_NOT_START "设备没有å¯åŠ¨" +#define STATU_DESC_SCANNER_ERR_NOT_START "\350\256\276\345\244\207\346\262\241\346\234\211\345\220\257\345\212\250" +#define ID_STATU_DESC_SCANNER_ERR_NOT_START 5681 + +// #define STATU_DESC_SCANNER_ERR_NOT_ANY_MORE "在本次扫æ中,对相åŒæ“作ä¸å†è¯¢é—®" +#define STATU_DESC_SCANNER_ERR_NOT_ANY_MORE "\345\234\250\346\234\254\346\254\241\346\211\253\346\217\217\344\270\255\357\274\214\345\257\271\347\233\270\345\220\214\346\223\215\344\275\234\344\270\215\345\206\215\350\257\242\351\227\256" +#define ID_STATU_DESC_SCANNER_ERR_NOT_ANY_MORE 16267 + +// #define STATU_DESC_SCANNER_ERR_NO_DATA "没有数æ®" +#define STATU_DESC_SCANNER_ERR_NO_DATA "\346\262\241\346\234\211\346\225\260\346\215\256" +#define ID_STATU_DESC_SCANNER_ERR_NO_DATA 15331 + +// #define STATU_DESC_SCANNER_ERR_HAS_DATA_YET "有数æ®æœªè¢«è¯»å–" +#define STATU_DESC_SCANNER_ERR_HAS_DATA_YET "\346\234\211\346\225\260\346\215\256\346\234\252\350\242\253\350\257\273\345\217\226" +#define ID_STATU_DESC_SCANNER_ERR_HAS_DATA_YET 31030 + +// #define STATU_DESC_SCANNER_ERR_OUT_OF_RANGE "æ“作超出范围" +#define STATU_DESC_SCANNER_ERR_OUT_OF_RANGE "\346\223\215\344\275\234\350\266\205\345\207\272\350\214\203\345\233\264" +#define ID_STATU_DESC_SCANNER_ERR_OUT_OF_RANGE 22268 + +// #define STATU_DESC_SCANNER_ERR_IO "IO错误,请é‡å¯è®¾å¤‡æˆ–æ‹”æ’USB" +#define STATU_DESC_SCANNER_ERR_IO "IO\351\224\231\350\257\257\357\274\214\350\257\267\351\207\215\345\220\257\350\256\276\345\244\207\346\210\226\346\213\224\346\217\222USB" +#define ID_STATU_DESC_SCANNER_ERR_IO 27027 + +// #define STATU_DESC_SCANNER_ERR_TIMEOUT "æ“作超时" +#define STATU_DESC_SCANNER_ERR_TIMEOUT "\346\223\215\344\275\234\350\266\205\346\227\266" +#define ID_STATU_DESC_SCANNER_ERR_TIMEOUT 65371 + +// #define STATU_DESC_SCANNER_ERR_OPEN_FILE_FAILED "打开本地文件失败" +#define STATU_DESC_SCANNER_ERR_OPEN_FILE_FAILED "\346\211\223\345\274\200\346\234\254\345\234\260\346\226\207\344\273\266\345\244\261\350\264\245" +#define ID_STATU_DESC_SCANNER_ERR_OPEN_FILE_FAILED 52380 + +// #define STATU_DESC_SCANNER_ERR_CREATE_FILE_FAILED "创建本地文件失败" +#define STATU_DESC_SCANNER_ERR_CREATE_FILE_FAILED "\345\210\233\345\273\272\346\234\254\345\234\260\346\226\207\344\273\266\345\244\261\350\264\245" +#define ID_STATU_DESC_SCANNER_ERR_CREATE_FILE_FAILED 23361 + +// #define STATU_DESC_SCANNER_ERR_WRITE_FILE_FAILED "写本地文件失败" +#define STATU_DESC_SCANNER_ERR_WRITE_FILE_FAILED "\345\206\231\346\234\254\345\234\260\346\226\207\344\273\266\345\244\261\350\264\245" +#define ID_STATU_DESC_SCANNER_ERR_WRITE_FILE_FAILED 22164 + +// #define STATU_DESC_SCANNER_ERR_OPENED_BY_OTHER_PROCESS "设备已ç»è¢«å…¶å®ƒè¿›ç¨‹å ç”¨" +#define STATU_DESC_SCANNER_ERR_OPENED_BY_OTHER_PROCESS "\350\256\276\345\244\207\345\267\262\347\273\217\350\242\253\345\205\266\345\256\203\350\277\233\347\250\213\345\215\240\347\224\250" +#define ID_STATU_DESC_SCANNER_ERR_OPENED_BY_OTHER_PROCESS 62384 + +// #define STATU_DESC_SCANNER_ERR_USB_INIT_FAILED "USB通信åˆå§‹åŒ–失败" +#define STATU_DESC_SCANNER_ERR_USB_INIT_FAILED "USB\351\200\232\344\277\241\345\210\235\345\247\213\345\214\226\345\244\261\350\264\245" +#define ID_STATU_DESC_SCANNER_ERR_USB_INIT_FAILED 49159 + +// #define STATU_DESC_SCANNER_ERR_USB_REGISTER_PNP_FAILED "注册USB监å¬äº‹ä»¶å¤±è´¥" +#define STATU_DESC_SCANNER_ERR_USB_REGISTER_PNP_FAILED "\346\263\250\345\206\214USB\347\233\221\345\220\254\344\272\213\344\273\266\345\244\261\350\264\245" +#define ID_STATU_DESC_SCANNER_ERR_USB_REGISTER_PNP_FAILED 5685 + +// #define STATU_DESC_SCANNER_ERR_USB_CLAIM_INTERFACE_FAILED "声明USB接å£å¤±è´¥" +#define STATU_DESC_SCANNER_ERR_USB_CLAIM_INTERFACE_FAILED "\345\243\260\346\230\216USB\346\216\245\345\217\243\345\244\261\350\264\245" +#define ID_STATU_DESC_SCANNER_ERR_USB_CLAIM_INTERFACE_FAILED 21918 + +// #define STATU_DESC_SCANNER_ERR_DEVICE_NOT_FOUND "设备未找到" +#define STATU_DESC_SCANNER_ERR_DEVICE_NOT_FOUND "\350\256\276\345\244\207\346\234\252\346\211\276\345\210\260" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_NOT_FOUND 43988 + +// #define STATU_DESC_SCANNER_ERR_DEVICE_NOT_SUPPORT "设备ä¸æ”¯æŒè¯¥æ“作" +#define STATU_DESC_SCANNER_ERR_DEVICE_NOT_SUPPORT "\350\256\276\345\244\207\344\270\215\346\224\257\346\214\201\350\257\245\346\223\215\344\275\234" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_NOT_SUPPORT 15726 + +// #define STATU_DESC_SCANNER_ERR_DEVICE_BUSY "设备正忙,ä¸èƒ½å“应该æ“作" +#define STATU_DESC_SCANNER_ERR_DEVICE_BUSY "\350\256\276\345\244\207\346\255\243\345\277\231\357\274\214\344\270\215\350\203\275\345\223\215\345\272\224\350\257\245\346\223\215\344\275\234" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_BUSY 29315 + +// #define STATU_DESC_SCANNER_ERR_DEVICE_SLEEPING "设备处于ç¡çœ çŠ¶æ€" +#define STATU_DESC_SCANNER_ERR_DEVICE_SLEEPING "\350\256\276\345\244\207\345\244\204\344\272\216\347\235\241\347\234\240\347\212\266\346\200\201" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_SLEEPING 26372 + +//#define STATU_DESC_SCANNER_ERR_DEVICE_NOTIFY_SLEEP 正在唤醒...请等待å秒å†æ¬¡æ‰«æ +#define STATU_DESC_SCANNER_ERR_DEVICE_NOTIFY_SLEEP "\346\255\243\345\234\250\345\224\244\351\206\222...\350\257\267\347\255\211\345\276\205\345\215\201\347\247\222\345\206\215\346\254\241\346\211\253\346\217\217" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_NOTIFY_SLEEP 64756 + +// #define STATU_DESC_SCANNER_ERR_DEVICE_COUNT_MODE "设备处于计数模å¼æ‰«æ状æ€" +#define STATU_DESC_SCANNER_ERR_DEVICE_COUNT_MODE "\350\256\276\345\244\207\345\244\204\344\272\216\350\256\241\346\225\260\346\250\241\345\274\217\346\211\253\346\217\217\347\212\266\346\200\201" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_COUNT_MODE 602 + +// #define STATU_DESC_SCANNER_ERR_DEVICE_STOPPED "扫æåœæ­¢" +#define STATU_DESC_SCANNER_ERR_DEVICE_STOPPED "\346\211\253\346\217\217\345\201\234\346\255\242" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_STOPPED 45291 + +// #define STATU_DESC_SCANNER_ERR_DEVICE_COVER_OPENNED "请关闭扫æ仪盖æ¿" +#define STATU_DESC_SCANNER_ERR_DEVICE_COVER_OPENNED "\350\257\267\345\205\263\351\227\255\346\211\253\346\217\217\344\273\252\347\233\226\346\235\277" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_COVER_OPENNED 29725 + +// #define STATU_DESC_SCANNER_ERR_DEVICE_NO_PAPER "无纸" +#define STATU_DESC_SCANNER_ERR_DEVICE_NO_PAPER "\346\227\240\347\272\270" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_NO_PAPER 61284 + +// #define STATU_DESC_SCANNER_ERR_DEVICE_FEEDING_PAPER "æ“纸失败" +#define STATU_DESC_SCANNER_ERR_DEVICE_FEEDING_PAPER "\346\220\223\347\272\270\345\244\261\350\264\245" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_FEEDING_PAPER 60256 + +// #define STATU_DESC_SCANNER_ERR_DEVICE_DOUBLE_FEEDING "有多张纸被åŒæ—¶æ“进扫æ仪" +#define STATU_DESC_SCANNER_ERR_DEVICE_DOUBLE_FEEDING "\346\234\211\345\244\232\345\274\240\347\272\270\350\242\253\345\220\214\346\227\266\346\220\223\350\277\233\346\211\253\346\217\217\344\273\252" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_DOUBLE_FEEDING 58398 + +// #define STATU_DESC_SCANNER_ERR_DEVICE_PAPER_JAMMED "扫æ仪å¡çº¸" +#define STATU_DESC_SCANNER_ERR_DEVICE_PAPER_JAMMED "\346\211\253\346\217\217\344\273\252\345\215\241\347\272\270" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_PAPER_JAMMED 39928 + +// #define STATU_DESC_SCANNER_ERR_DEVICE_STAPLE_ON "纸张上检测到有钉书钉" +#define STATU_DESC_SCANNER_ERR_DEVICE_STAPLE_ON "\347\272\270\345\274\240\344\270\212\346\243\200\346\265\213\345\210\260\346\234\211\351\222\211\344\271\246\351\222\211" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_STAPLE_ON 3126 + +// #define STATU_DESC_SCANNER_ERR_DEVICE_PAPER_SKEW "纸张倾斜" +#define STATU_DESC_SCANNER_ERR_DEVICE_PAPER_SKEW "\347\272\270\345\274\240\345\200\276\346\226\234" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_PAPER_SKEW 5570 + +// #define STATU_DESC_SCANNER_ERR_DEVICE_SIZE_CHECK "纸张尺寸检测错误" +#define STATU_DESC_SCANNER_ERR_DEVICE_SIZE_CHECK "\347\272\270\345\274\240\345\260\272\345\257\270\346\243\200\346\265\213\351\224\231\350\257\257" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_SIZE_CHECK 32107 + +// #define STATU_DESC_SCANNER_ERR_DEVICE_DOGEAR "纸张有折角" +#define STATU_DESC_SCANNER_ERR_DEVICE_DOGEAR "\347\272\270\345\274\240\346\234\211\346\212\230\350\247\222" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_DOGEAR 61565 + +// #define STATU_DESC_SCANNER_ERR_DEVICE_NO_IMAGE "设备没å–到图" +#define STATU_DESC_SCANNER_ERR_DEVICE_NO_IMAGE "\350\256\276\345\244\207\346\262\241\345\217\226\345\210\260\345\233\276" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_NO_IMAGE 41789 + +// #define STATU_DESC_SCANNER_ERR_DEVICE_SCANN_ERROR "扫æ失败" +#define STATU_DESC_SCANNER_ERR_DEVICE_SCANN_ERROR "\346\211\253\346\217\217\345\244\261\350\264\245" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_SCANN_ERROR 14901 + +// #define STATU_DESC_SCANNER_ERR_DEVICE_PC_BUSY "PCç¹å¿™æˆ–出错" +#define STATU_DESC_SCANNER_ERR_DEVICE_PC_BUSY "PC\347\271\201\345\277\231\346\210\226\345\207\272\351\224\231" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_PC_BUSY 61142 + +// #define STATU_DESC_SCANNER_ERR_DEVICE_ISLOCK "设备被é”定" +#define STATU_DESC_SCANNER_ERR_DEVICE_ISLOCK "\350\256\276\345\244\207\350\242\253\351\224\201\345\256\232" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_ISLOCK 1535 + +// #define STATU_DESC_SCANNER_ERR_DEVICE_ISLOCK "此固件ä¸æ”¯æŒå¾…纸扫æ" +#define STATU_DESC_SCANNER_ERR_DEVICE_NOT_SUPPORTED "\346\255\244\345\233\272\344\273\266\344\270\215\346\224\257\346\214\201\345\276\205\347\272\270\346\211\253\346\217\217" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_NOT_SUPPORTED 9610 + +//#define STATU_DESC_SCANNER_ERR_DEVICE_AUTO_FAIL_OVER "自动平场校正结æŸ" +#define STATU_DESC_SCANNER_ERR_DEVICE_AUTO_FAIL_OVER "\350\207\252\345\212\250\345\271\263\345\234\272\346\240\241\346\255\243\347\273\223\346\235\237" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_AUTO_FAIL_OVER 38824 + +//#define STATU_DESC_SCANNER_ERR_DEVICE_DISTORTION "疑是éžä¸“用畸å˜ä¿®æ­£çº¸" +#define STATU_DESC_SCANNER_ERR_DEVICE_DISTORTION "\347\226\221\346\230\257\351\235\236\344\270\223\347\224\250\347\225\270\345\217\230\344\277\256\346\255\243\347\272\270" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_DISTORTION 32402 + +//#define STATU_DESC_SCANNER_ERR_DEVICE_GET_IMAGE_OUTTIME "å–图通信超时" +#define STATU_DESC_SCANNER_ERR_DEVICE_GET_IMAGE_OUTTIME "\345\217\226\345\233\276\351\200\232\344\277\241\350\266\205\346\227\266" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_GET_IMAGE_OUTTIME 10438 + +//#define STATU_DESC_SCANNER_ERR_DEVICE_GET_USER_CANCEL_SCAN "用户å–消扫æ" +#define STATU_DESC_SCANNER_ERR_DEVICE_GET_USER_CANCEL_SCAN "\347\224\250\346\210\267\345\217\226\346\266\210\346\211\253\346\217\217" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_GET_USER_CANCEL_SCAN 39020 + +//#define STATU_DESC_SCANNER_ERR_DEVICE_FAIL_OUTTIME "设备校正超时" +#define STATU_DESC_SCANNER_ERR_DEVICE_FAIL_OUTTIME "\350\256\276\345\244\207\346\240\241\346\255\243\350\266\205\346\227\266" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_FAIL_OUTTIME 21710 + +//#define STATU_DESC_SCANNER_ERR_DEVICE_ROUND_SACN_OVER "此轮扫æ完æˆ" +#define STATU_DESC_SCANNER_ERR_DEVICE_ROUND_SACN_OVER "\346\255\244\350\275\256\346\211\253\346\217\217\345\256\214\346\210\220" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_ROUND_SACN_OVER 57619 + +//#define STATU_DESC_SCANNER_ERR_DEVICE_DEVS "设备" +#define STATU_DESC_SCANNER_ERR_DEVICE_DEVS "\350\256\276\345\244\207\342\200\234" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_DEVS 603 + +//#define STATU_DESC_SCANNER_ERR_DEVICE_IS_CLOSE "å·²ç»å…³é—­" +#define STATU_DESC_SCANNER_ERR_DEVICE_IS_CLOSE "\342\200\235\345\267\262\347\273\217\345\205\263\351\227\255\343\200\202" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_IS_CLOSE 35078 + +//#define STATU_DESC_SCANNER_ERR_DEVICE_UNKNOWN_ERROR "未知错误,请é‡å¯è®¾å¤‡ï¼Œç­‰å¾…设备å¤ä½æˆåŠŸåœ¨å¯åŠ¨è½¯ä»¶ã€‚" +#define STATU_DESC_SCANNER_ERR_DEVICE_UNKNOWN_ERROR "\346\234\252\347\237\245\351\224\231\350\257\257\357\274\214\350\257\267\351\207\215\345\220\257\350\256\276\345\244\207\357\274\214\347\255\211\345\276\205\350\256\276\345\244\207\345\244\215\344\275\215\346\210\220\345\212\237\345\234\250\345\220\257\345\212\250\350\275\257\344\273\266" +#define ID_STATU_DESC_SCANNER_ERR_DEVICE_UNKNOWN_ERROR 22744 + +//#define STATU_DESC_SCANNER_ERR_DEVICE_MAYBE_IS_HOLE "疑是纸张有较大孔洞" +#define STATU_DESC_SCANNER_ERR_DEVICE_MAYBE_IS_HOLE "\347\226\221\346\230\257\347\272\270\345\274\240\346\234\211\350\276\203\345\244\247\345\255\224\346\264\236" +//#define STATU_DESC_SCANNER_ERR_DEVICE_EXIT_WAIT_SCAN "退出待纸扫æ" +#define STATU_DESC_SCANNER_ERR_DEVICE_EXIT_WAIT_SCAN "\351\200\200\345\207\272\345\276\205\347\272\270\346\211\253\346\217\217" + +//#define STATU_DESC_SCANNER_ERR_DEVICE_GET_IMAGE_ERR "获å–图åƒå¼‚常" +#define STATU_DESC_SCANNER_ERR_DEVICE_GET_IMAGE_ERR "\350\216\267\345\217\226\345\233\276\345\203\217\345\274\202\345\270\270" + +//#define STATU_DESC_SCANNER_ERR_DEVICE_DEVS_BOOTING "请ç¨ç­‰-设备正在准备中" +#define STATU_DESC_SCANNER_ERR_DEVICE_DEVS_BOOTING "\350\257\267\347\250\215\347\255\211-\350\256\276\345\244\207\346\255\243\345\234\250\345\207\206\345\244\207\344\270\255" + +//#define STATU_DESC_SCANNER_ERR_DEVICE_DEVS_BOOTING "设备å¯åŠ¨æˆåŠŸ-å¯ä»¥å¼€å§‹æ‰«æ" +#define STATU_DESC_SCANNER_ERR_DEVICE_DEVS_START_SUCCES "\350\256\276\345\244\207\345\220\257\345\212\250\346\210\220\345\212\237-\345\217\257\344\273\245\345\274\200\345\247\213\346\211\253\346\217\217" + +//#define STATU_DESC_SCANNER_ERR_LANG_PAK_LOST 语言包缺失è¯æ¡ +#define STATU_DESC_SCANNER_ERR_LANG_PAK_LOST "\350\257\255\350\250\200\345\214\205\347\274\272\345\244\261\350\257\215\346\235\241" + +//#define STATU_DESC_SCANNER_ERR_DEVICE_UPGRADE_SUCCESSFUL 设备å‡çº§æˆåŠŸ +#define STATU_DESC_SCANNER_ERR_DEVICE_UPGRADE_SUCCESSFUL "\350\256\276\345\244\207\345\215\207\347\272\247\346\210\220\345\212\237" + +//#define STATU_DESC_SCANNER_ERR_DEVICE_UPGRADE_FAIL 设备å‡çº§å¤±è´¥ +#define STATU_DESC_SCANNER_ERR_DEVICE_UPGRADE_FAIL "\350\256\276\345\244\207\345\215\207\347\272\247\345\244\261\350\264\245" + +//#define STATU_DESC_SCANNER_ERR_DEVICE_AUTO_FAIL_INFO è‡ªåŠ¨å¹³åœºæ ¡æ­£ä¿¡æ¯ +#define STATU_DESC_SCANNER_ERR_DEVICE_AUTO_FAIL_INFO "\350\207\252\345\212\250\345\271\263\345\234\272\346\240\241\346\255\243\344\277\241\346\201\257" + +//#define STATU_DESC_SCANNER_ERR_DEVICE_UNKNOWN_STATUS è®¾å¤‡å¤„äºŽæœªçŸ¥çŠ¶æ€ +#define STATU_DESC_SCANNER_ERR_DEVICE_UNKNOWN_STATUS "\350\256\276\345\244\207\345\244\204\344\272\216\346\234\252\347\237\245\347\212\266\346\200\201" + +// 状æ€ä¿¡æ¯æ述符 - OVER + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + diff --git a/sdk/json/cJSON.c b/sdk/json/cJSON.c new file mode 100644 index 0000000..594376b --- /dev/null +++ b/sdk/json/cJSON.c @@ -0,0 +1,794 @@ +/* + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +#include +#include +#include +#include +#include +#include +#include +#include "cJSON.h" + +static const char *ep; + +const char *cJSON_GetErrorPtr(void) {return ep;} + +static int cJSON_strcasecmp(const char *s1,const char *s2) +{ + if (!s1) return (s1==s2)?0:1;if (!s2) return 1; + for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; + return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); +} + +static void* ask_memory(size_t bytes) +{ +// printf("allocate %u bytes memory in cJSON\n", bytes); + + return malloc(bytes); +} +static void *(*cJSON_malloc)(size_t sz) = ask_memory; +static void (*cJSON_free)(void *ptr) = free; + +static char* cJSON_strdup(const char* str) +{ + size_t len; + char* copy; + + len = strlen(str) + 1; + if (!(copy = (char*)cJSON_malloc(len))) return 0; + memcpy(copy,str,len); + return copy; +} + +void cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (!hooks) { /* Reset hooks */ + cJSON_malloc = ask_memory; + cJSON_free = free; + return; + } + + cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:ask_memory; + cJSON_free = (hooks->free_fn)?hooks->free_fn:free; +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(void) +{ + cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); + if (node) memset(node,0,sizeof(cJSON)); + return node; +} + +/* Delete a cJSON structure. */ +void cJSON_Delete(cJSON *c) +{ + cJSON *next; + while (c) + { + next=c->next; + if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child); + if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); + if (!(c->type&cJSON_StringIsConst) && c->string) cJSON_free(c->string); + cJSON_free(c); + c=next; + } +} + +/* Parse the input text to generate a number, and populate the result into item. */ +static const char *parse_number(cJSON *item,const char *num) +{ + double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; + + if (*num=='-') sign=-1,num++; /* Has sign? */ + if (*num=='0') num++; /* is zero */ + if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */ + if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */ + if (*num=='e' || *num=='E') /* Exponent? */ + { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */ + while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */ + } + + n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ + + item->valuedouble=n; + item->valueint=(int)n; + item->type=cJSON_Number; + return num; +} + +static int pow2gt (int x) { --x; x|=x>>1; x|=x>>2; x|=x>>4; x|=x>>8; x|=x>>16; return x+1; } + +typedef struct {char *buffer; int length; int offset; } printbuffer; + +static char* ensure(printbuffer *p,int needed) +{ + char *newbuffer;int newsize; + if (!p || !p->buffer) return 0; + needed+=p->offset; + if (needed<=p->length) return p->buffer+p->offset; + + newsize=pow2gt(needed); + newbuffer=(char*)cJSON_malloc(newsize); + if (!newbuffer) {cJSON_free(p->buffer);p->length=0,p->buffer=0;return 0;} + if (newbuffer) memcpy(newbuffer,p->buffer,p->length); + cJSON_free(p->buffer); + p->length=newsize; + p->buffer=newbuffer; + return newbuffer+p->offset; +} + +static int update(printbuffer *p) +{ + char *str; + if (!p || !p->buffer) return 0; + str=p->buffer+p->offset; + return p->offset+strlen(str); +} + +/* Render the number nicely from the given item into a string. */ +static char *print_number(cJSON *item,printbuffer *p) +{ + char *str=0; + double d=item->valuedouble; + if (d==0) + { + if (p) str=ensure(p,2); + else str=(char*)cJSON_malloc(2); /* special case for 0. */ + if (str) strcpy(str,"0"); + } + else if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) + { + if (p) str=ensure(p,21); + else str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */ + if (str) sprintf(str,"%d",item->valueint); + } + else + { + if (p) str=ensure(p,64); + else str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */ + if (str) + { + if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d); + else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d); + else sprintf(str,"%f",d); + } + } + return str; +} + +static unsigned parse_hex4(const char *str) +{ + unsigned h=0; + if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; + h=h<<4;str++; + if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; + h=h<<4;str++; + if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; + h=h<<4;str++; + if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; + return h; +} + +/* Parse the input text into an unescaped cstring, and populate item. */ +static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; +static const char *parse_string(cJSON *item,const char *str) +{ + const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2; + if (*str!='\"') {ep=str;return 0;} /* not a string! */ + + while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */ + + out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */ + if (!out) return 0; + + ptr=str+1;ptr2=out; + while (*ptr!='\"' && *ptr) + { + if (*ptr!='\\') *ptr2++=*ptr++; + else + { + ptr++; + switch (*ptr) + { + case 'b': *ptr2++='\b'; break; + case 'f': *ptr2++='\f'; break; + case 'n': *ptr2++='\n'; break; + case 'r': *ptr2++='\r'; break; + case 't': *ptr2++='\t'; break; + case 'u': /* transcode utf16 to utf8. */ + uc=parse_hex4(ptr+1);ptr+=4; /* get the unicode char. */ + + if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */ + + if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */ + { + if (ptr[1]!='\\' || ptr[2]!='u') break; /* missing second-half of surrogate. */ + uc2=parse_hex4(ptr+3);ptr+=6; + if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */ + uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF)); + } + + len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len; + + switch (len) { + case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 1: *--ptr2 =(uc | firstByteMark[len]); + } + ptr2+=len; + break; + default: *ptr2++=*ptr; break; + } + ptr++; + } + } + *ptr2=0; + if (*ptr=='\"') ptr++; + item->valuestring=out; + item->type=cJSON_String; + return ptr; +} +char* cJSON_utf8_2_unic(const char* utf8) +{ + char* unic = (char*)malloc(strlen(utf8) * 3 + 8); + unsigned char * cur = unic; + + while (*cur = *utf8++) + { + if ((*cur & 0x0f0) == 0x0e0) + { + if (((unsigned char)utf8[0] & 0x0c0) == 0x80 && + ((unsigned char)utf8[1] & 0x0c0) == 0x80) + { + char* hex = "0123456789ABCDEF"; + unsigned short us = *cur & 0x0f; + us <<= 6; + us += utf8[0] & 0x3f; + us <<= 6; + us += utf8[1] & 0x3f; + + *cur++ = '\\'; + *cur++ = 'u'; + cur[3] = hex[us & 0x0f]; + us >>= 4; + cur[2] = hex[us & 0x0f]; + us >>= 4; + cur[1] = hex[us & 0x0f]; + us >>= 4; + cur[0] = hex[us & 0x0f]; + cur += 3; + utf8 += 2; + } + } + cur++; + } + *cur++ = 0; + + return unic; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static char *print_string_ptr(const char *str,printbuffer *p) +{ + const char *ptr;char *ptr2,*out;int len=0,flag=0;unsigned char token; + + for (ptr=str;*ptr;ptr++) flag|=((*ptr>0 && *ptr<32)||(*ptr=='\"')||(*ptr=='\\'))?1:0; + if (!flag) + { + len=ptr-str; + if (p) out=ensure(p,len+3); + else out=(char*)cJSON_malloc(len+3); + if (!out) return 0; + ptr2=out;*ptr2++='\"'; + strcpy(ptr2,str); + ptr2[len]='\"'; + ptr2[len+1]=0; + return out; + } + + if (!str) + { + if (p) out=ensure(p,3); + else out=(char*)cJSON_malloc(3); + if (!out) return 0; + strcpy(out,"\"\""); + return out; + } + ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;} + + if (p) out=ensure(p,len+3); + else out=(char*)cJSON_malloc(len+3); + if (!out) return 0; + + ptr2=out;ptr=str; + *ptr2++='\"'; + while (*ptr) + { + if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; + else + { + *ptr2++='\\'; + switch (token=*ptr++) + { + case '\\': *ptr2++='\\'; break; + case '\"': *ptr2++='\"'; break; + case '\b': *ptr2++='b'; break; + case '\f': *ptr2++='f'; break; + case '\n': *ptr2++='n'; break; + case '\r': *ptr2++='r'; break; + case '\t': *ptr2++='t'; break; + default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */ + } + } + } + *ptr2++='\"';*ptr2++=0; + return out; +} +/* Invote print_string_ptr (which is useful) on an item. */ +static char *print_string(cJSON *item,printbuffer *p) {return print_string_ptr(item->valuestring,p);} + +/* Predeclare these prototypes. */ +static const char *parse_value(cJSON *item,const char *value); +static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p); +static const char *parse_array(cJSON *item,const char *value); +static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p); +static const char *parse_object(cJSON *item,const char *value); +static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p); + +/* Utility to jump whitespace and cr/lf */ +static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;} + +/* Parse an object - create a new root, and populate. */ +cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated) +{ + const char *end=0; + cJSON *c=cJSON_New_Item(); + ep=0; + if (!c) return 0; /* memory fail */ + + end=parse_value(c,skip(value)); + if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */ + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}} + if (return_parse_end) *return_parse_end=end; + return c; +} +/* Default options for cJSON_Parse */ +cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);} + +/* Render a cJSON item/entity/structure to text. */ +char *cJSON_Print(cJSON *item) {return print_value(item,0,1,0);} +char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0,0);} + +char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt) +{ + printbuffer p; + p.buffer=(char*)cJSON_malloc(prebuffer); + p.length=prebuffer; + p.offset=0; + return print_value(item,0,fmt,&p); + return p.buffer; +} + + +/* Parser core - when encountering text, process appropriately. */ +static const char *parse_value(cJSON *item,const char *value) +{ + if (!value) return 0; /* Fail on null. */ + if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } + if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } + if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } + if (*value=='\"') { return parse_string(item,value); } + if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } + if (*value=='[') { return parse_array(item,value); } + if (*value=='{') { return parse_object(item,value); } + + ep=value;return 0; /* failure. */ +} + +/* Render a value to text. */ +static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p) +{ + char *out=0; + if (!item) return 0; + if (p) + { + switch ((item->type)&255) + { + case cJSON_NULL: {out=ensure(p,5); if (out) strcpy(out,"null"); break;} + case cJSON_False: {out=ensure(p,6); if (out) strcpy(out,"false"); break;} + case cJSON_True: {out=ensure(p,5); if (out) strcpy(out,"true"); break;} + case cJSON_Number: out=print_number(item,p);break; + case cJSON_String: out=print_string(item,p);break; + case cJSON_Array: out=print_array(item,depth,fmt,p);break; + case cJSON_Object: out=print_object(item,depth,fmt,p);break; + } + } + else + { + switch ((item->type)&255) + { + case cJSON_NULL: out=cJSON_strdup("null"); break; + case cJSON_False: out=cJSON_strdup("false");break; + case cJSON_True: out=cJSON_strdup("true"); break; + case cJSON_Number: out=print_number(item,0);break; + case cJSON_String: out=print_string(item,0);break; + case cJSON_Array: out=print_array(item,depth,fmt,0);break; + case cJSON_Object: out=print_object(item,depth,fmt,0);break; + } + } + return out; +} + +/* Build an array from input text. */ +static const char *parse_array(cJSON *item,const char *value) +{ + cJSON *child; + if (*value!='[') {ep=value;return 0;} /* not an array! */ + + item->type=cJSON_Array; + value=skip(value+1); + if (*value==']') return value+1; /* empty array. */ + + item->child=child=cJSON_New_Item(); + if (!item->child) return 0; /* memory fail */ + value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */ + if (!value) return 0; + + while (*value==',') + { + cJSON *new_item; + if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ + child->next=new_item;new_item->prev=child;child=new_item; + value=skip(parse_value(child,skip(value+1))); + if (!value) return 0; /* memory fail */ + } + + if (*value==']') return value+1; /* end of array */ + ep=value;return 0; /* malformed. */ +} + +/* Render an array to text */ +static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p) +{ + char **entries; + char *out=0,*ptr,*ret;int len=5; + cJSON *child=item->child; + int numentries=0,i=0,fail=0; + size_t tmplen=0; + + /* How many entries in the array? */ + while (child) numentries++,child=child->next; + /* Explicitly handle numentries==0 */ + if (!numentries) + { + if (p) out=ensure(p,3); + else out=(char*)cJSON_malloc(3); + if (out) strcpy(out,"[]"); + return out; + } + + if (p) + { + /* Compose the output array. */ + i=p->offset; + ptr=ensure(p,1);if (!ptr) return 0; *ptr='['; p->offset++; + child=item->child; + while (child && !fail) + { + print_value(child,depth+1,fmt,p); + p->offset=update(p); + if (child->next) {len=fmt?2:1;ptr=ensure(p,len+1);if (!ptr) return 0;*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;p->offset+=len;} + child=child->next; + } + ptr=ensure(p,2);if (!ptr) return 0; *ptr++=']';*ptr=0; + out=(p->buffer)+i; + } + else + { + /* Allocate an array to hold the values for each */ + entries=(char**)cJSON_malloc(numentries*sizeof(char*)); + if (!entries) return 0; + memset(entries,0,numentries*sizeof(char*)); + /* Retrieve all the results: */ + child=item->child; + while (child && !fail) + { + ret=print_value(child,depth+1,fmt,0); + entries[i++]=ret; + if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; + child=child->next; + } + + /* If we didn't fail, try to ask_memory the output string */ + if (!fail) out=(char*)cJSON_malloc(len); + /* If that fails, we fail. */ + if (!out) fail=1; + + /* Handle failure. */ + if (fail) + { + for (i=0;itype=cJSON_Object; + value=skip(value+1); + if (*value=='}') return value+1; /* empty array. */ + + item->child=child=cJSON_New_Item(); + if (!item->child) return 0; + value=skip(parse_string(child,skip(value))); + if (!value) return 0; + child->string=child->valuestring;child->valuestring=0; + if (*value!=':') {ep=value;return 0;} /* fail! */ + value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ + if (!value) return 0; + + while (*value==',') + { + cJSON *new_item; + if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ + child->next=new_item;new_item->prev=child;child=new_item; + value=skip(parse_string(child,skip(value+1))); + if (!value) return 0; + child->string=child->valuestring;child->valuestring=0; + if (*value!=':') {ep=value;return 0;} /* fail! */ + value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ + if (!value) return 0; + } + + if (*value=='}') return value+1; /* end of array */ + ep=value;return 0; /* malformed. */ +} + +/* Render an object to text. */ +static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p) +{ + char **entries=0,**names=0; + char *out=0,*ptr,*ret,*str;int len=7,i=0,j; + cJSON *child=item->child; + int numentries=0,fail=0; + size_t tmplen=0; + /* Count the number of entries. */ + while (child) numentries++,child=child->next; + /* Explicitly handle empty object case */ + if (!numentries) + { + if (p) out=ensure(p,fmt?depth+4:3); + else out=(char*)cJSON_malloc(fmt?depth+4:3); + if (!out) return 0; + ptr=out;*ptr++='{'; + if (fmt) {*ptr++='\n';for (i=0;ioffset; + len=fmt?2:1; ptr=ensure(p,len+1); if (!ptr) return 0; + *ptr++='{'; if (fmt) *ptr++='\n'; *ptr=0; p->offset+=len; + child=item->child;depth++; + while (child) + { + if (fmt) + { + ptr=ensure(p,depth); if (!ptr) return 0; + for (j=0;joffset+=depth; + } + print_string_ptr(child->string,p); + p->offset=update(p); + + len=fmt?2:1; + ptr=ensure(p,len); if (!ptr) return 0; + *ptr++=':';if (fmt) *ptr++='\t'; + p->offset+=len; + + print_value(child,depth,fmt,p); + p->offset=update(p); + + len=(fmt?1:0)+(child->next?1:0); + ptr=ensure(p,len+1); if (!ptr) return 0; + if (child->next) *ptr++=','; + if (fmt) *ptr++='\n';*ptr=0; + p->offset+=len; + child=child->next; + } + ptr=ensure(p,fmt?(depth+1):2); if (!ptr) return 0; + if (fmt) for (i=0;ibuffer)+i; + } + else + { + /* Allocate space for the names and the objects */ + entries=(char**)cJSON_malloc(numentries*sizeof(char*)); + if (!entries) return 0; + names=(char**)cJSON_malloc(numentries*sizeof(char*)); + if (!names) {cJSON_free(entries);return 0;} + memset(entries,0,sizeof(char*)*numentries); + memset(names,0,sizeof(char*)*numentries); + + /* Collect all the results into our arrays: */ + child=item->child;depth++;if (fmt) len+=depth; + while (child) + { + names[i]=str=print_string_ptr(child->string,0); + entries[i++]=ret=print_value(child,depth,fmt,0); + if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; + child=child->next; + } + + /* Try to allocate the output string */ + if (!fail) out=(char*)cJSON_malloc(len); + if (!out) fail=1; + + /* Handle failure */ + if (fail) + { + for (i=0;ichild;int i=0;while(c)i++,c=c->next;return i;} +cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;} +cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} +/* Utility for handling references. */ +static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} + +/* Add item to array/object. */ +void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} +void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} +void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (!(item->type&cJSON_StringIsConst) && item->string) cJSON_free(item->string);item->string=(char*)string;item->type|=cJSON_StringIsConst;cJSON_AddItemToArray(object,item);} +void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} +void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} + +cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0; + if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;} +void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} +cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} +void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} + +/* Replace array/object items with new ones. */ +void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) {cJSON_AddItemToArray(array,newitem);return;} + newitem->next=c;newitem->prev=c->prev;c->prev=newitem;if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;} +void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return; + newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem; + if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);} +void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}} + +/* Create basic types: */ +cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;} +cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;} +cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} +cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} +cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} +cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} +cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} +cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} + +/* Create Arrays: */ +cJSON *cJSON_CreateIntArray(const int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateFloatArray(const float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateDoubleArray(const double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} + +/* Duplication */ +cJSON *cJSON_Duplicate(cJSON *item,int recurse) +{ + cJSON *newitem,*cptr,*nptr=0,*newchild; + /* Bail on bad ptr */ + if (!item) return 0; + /* Create new item */ + newitem=cJSON_New_Item(); + if (!newitem) return 0; + /* Copy over all vars */ + newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble; + if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}} + if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}} + /* If non-recursive, then we're done! */ + if (!recurse) return newitem; + /* Walk the ->next chain for the child. */ + cptr=item->child; + while (cptr) + { + newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) {cJSON_Delete(newitem);return 0;} + if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */ + cptr=cptr->next; + } + return newitem; +} + +void cJSON_Minify(char *gb_json) +{ + char *into=gb_json; + while (*gb_json) + { + if (*gb_json==' ') gb_json++; + else if (*gb_json=='\t') gb_json++; /* Whitespace characters. */ + else if (*gb_json=='\r') gb_json++; + else if (*gb_json=='\n') gb_json++; + else if (*gb_json=='/' && gb_json[1]=='/') while (*gb_json && *gb_json!='\n') gb_json++; /* double-slash comments, to end of line. */ + else if (*gb_json=='/' && gb_json[1]=='*') {while (*gb_json && !(*gb_json=='*' && gb_json[1]=='/')) gb_json++;gb_json+=2;} /* multiline comments. */ + else if (*gb_json=='\"'){*into++=*gb_json++;while (*gb_json && *gb_json!='\"'){if (*gb_json=='\\') *into++=*gb_json++;*into++=*gb_json++;}*into++=*gb_json++;} /* string literals, which are \" sensitive. */ + else *into++=*gb_json++; /* All other characters. */ + } + *into=0; /* and null-terminate. */ +} diff --git a/sdk/json/cJSON.h b/sdk/json/cJSON.h new file mode 100644 index 0000000..9fdbf9b --- /dev/null +++ b/sdk/json/cJSON.h @@ -0,0 +1,157 @@ +/* + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* cJSON Types: */ +#define cJSON_False 0 +#define cJSON_True 1 +#define cJSON_NULL 2 +#define cJSON_Number 3 +#define cJSON_String 4 +#define cJSON_Array 5 +#define cJSON_Object 6 + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + + +typedef void *(*malloc_fnxx)(size_t sz); +typedef void (*free_fnxx)(void *ptr); + +/* The cJSON structure: */ +typedef struct cJSON { + struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + + int type; /* The type of the item, as above. */ + + char *valuestring; /* The item's string, if type==cJSON_String */ + int valueint; /* The item's number, if type==cJSON_Number */ + double valuedouble; /* The item's number, if type==cJSON_Number */ + + char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ +} cJSON; + +typedef struct cJSON_Hooks { + malloc_fnxx malloc_fn; + free_fnxx free_fn; +} cJSON_Hooks; + +/* Supply malloc, realloc and free functions to cJSON */ +extern void cJSON_InitHooks(cJSON_Hooks* hooks); + + +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ +extern cJSON *cJSON_Parse(const char *value); +/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ +extern char *cJSON_Print(cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */ +extern char *cJSON_PrintUnformatted(cJSON *item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +extern char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt); +/* Delete a cJSON entity and all subentities. */ +extern void cJSON_Delete(cJSON *c); + +/* Returns the number of items in an array (or object). */ +extern int cJSON_GetArraySize(cJSON *array); +/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ +extern cJSON *cJSON_GetArrayItem(cJSON *array,int item); +/* Get item "string" from object. Case insensitive. */ +extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); + +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +extern const char *cJSON_GetErrorPtr(void); + +/* These calls create a cJSON item of the appropriate type. */ +extern cJSON *cJSON_CreateNull(void); +extern cJSON *cJSON_CreateTrue(void); +extern cJSON *cJSON_CreateFalse(void); +extern cJSON *cJSON_CreateBool(int b); +extern cJSON *cJSON_CreateNumber(double num); +extern cJSON *cJSON_CreateString(const char *string); +extern cJSON *cJSON_CreateArray(void); +extern cJSON *cJSON_CreateObject(void); + +/* These utilities create an Array of count items. */ +extern cJSON *cJSON_CreateIntArray(const int *numbers,int count); +extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count); +extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count); +extern cJSON *cJSON_CreateStringArray(const char **strings,int count); + +/* Append item to the specified array/object. */ +extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); +extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); +extern void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item); /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object */ +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); + +/* Remove/Detatch items from Arrays/Objects. */ +extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which); +extern void cJSON_DeleteItemFromArray(cJSON *array,int which); +extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); +extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string); + +/* Update array items. */ +extern void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem); /* Shifts pre-existing items to the right. */ +extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); +extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +extern cJSON *cJSON_Duplicate(cJSON *item,int recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will +need to be released. With recurse!=0, it will duplicate any children connected to the item. +The item->next and ->prev pointers are always zero on return from Duplicate. */ + +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated); + +extern void cJSON_Minify(char *gb_json); + +// convert e681a2 to \u6062, call 'free' to free the returned value +extern char* cJSON_utf8_2_unic(const char* utf8); + +/* Macros for creating things quickly. */ +#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) +#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) +#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) +#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b)) +#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) +#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) +#define cJSON_SetNumberValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/sdk/json/gb_json.cpp b/sdk/json/gb_json.cpp new file mode 100644 index 0000000..24a73f0 --- /dev/null +++ b/sdk/json/gb_json.cpp @@ -0,0 +1,962 @@ +#include "gb_json.h" + +#include "cJSON.h" +#include +#include +#include + +namespace special_char_trans +{ + struct + { + const char* writedown_text; + char readable_char; + }transferred_chars[] = { { "\\\"", '\"' } + , { "\\'", '\'' } + , { "\\a", '\a' } + , { "\\b", '\b' } + , { "\\f", '\f' } + , { "\\n", '\n' } + , { "\\r", '\r' } + , { "\\t", '\t' } + , { "\\v", '\v' } + // , { "\\?", '\?' } + , { "\\\\", '\\' } + , { "\\/", '/' } + // , { "\\0", '\0' } + }; + + void to_writedown(std::string& str) + { + std::string trans(str); + const char* ptr = trans.c_str(); + + str.clear(); + while (*ptr) + { + bool rep = false; + if (*ptr == '\\') + { + if (ptr[1] == '\\') + { + str += "\\\\"; + ptr++; + rep = true; + } + else if( ptr[1] == '/' || + ptr[1] == 'a' || + ptr[1] == 'b' || + ptr[1] == 'f' || + ptr[1] == 'n' || + ptr[1] == 'r' || + ptr[1] == 't' || + ptr[1] == 'u' || + ptr[1] == 'v') + { + str += "\\"; + ptr++; + } + else + { + str += "\\\\"; + rep = true; + } + } + else + { + for (size_t i = 0; i < sizeof(transferred_chars) / sizeof(transferred_chars[0]); ++i) + { + if (*ptr == transferred_chars[i].readable_char) + { + str += transferred_chars[i].writedown_text; + rep = true; + break; + } + } + } + if (!rep) + str.append(1, *ptr); + ptr++; + } + } +} + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// life callback ... +#ifdef DUMP_JSON_OBJECT_LIFE +static void record_life_empty(gb_json*, bool, void*) +{} +static void(*g_life)(gb_json*, bool, void*) = &record_life_empty; +static void* g_life_param = nullptr; + +void set_gbjson_life_callback(void(*life)(gb_json*, bool, void*), void* param) +{ + g_life_param = param; + g_life = life ? life : &record_life_empty; +} +#endif + + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// gb_json ... +gb_json::gb_json(char* json_txt) : ref_(1), type_(VAL_TYPE_OBJECT), key_(""), strval_(""), cur_child_(-1) +{ +#ifdef DUMP_JSON_OBJECT_LIFE + g_life(this, true, g_life_param); +#endif + + simple_val_.dval = .0f; + if(json_txt) + attach_text(json_txt); +} +gb_json::gb_json(const char* key, bool val) : ref_(1), type_(VAL_TYPE_BOOL), key_(key ? key : ""), strval_(""), cur_child_(-1) +{ +#ifdef DUMP_JSON_OBJECT_LIFE + g_life(this, true, g_life_param); +#endif + + simple_val_.bval = val; +} +gb_json::gb_json(const char* key, int val) : ref_(1), type_(VAL_TYPE_INT), key_(key ? key : ""), strval_(""), cur_child_(-1) +{ +#ifdef DUMP_JSON_OBJECT_LIFE + g_life(this, true, g_life_param); +#endif + + simple_val_.nval = val; +} +gb_json::gb_json(const char* key, double val) : ref_(1), type_(VAL_TYPE_FLOAT), key_(key ? key : ""), strval_(""), cur_child_(-1) +{ +#ifdef DUMP_JSON_OBJECT_LIFE + g_life(this, true, g_life_param); +#endif + + simple_val_.dval = val; +} +gb_json::gb_json(const char* key, const char* val) : ref_(1), type_(VAL_TYPE_STRING), key_(key ? key : ""), strval_(val ? val : ""), cur_child_(-1) +{ +#ifdef DUMP_JSON_OBJECT_LIFE + g_life(this, true, g_life_param); +#endif +} +gb_json::gb_json(const char* key, const std::string& val) : ref_(1), type_(VAL_TYPE_STRING), key_(key ? key : ""), strval_(val), cur_child_(-1) +{ +#ifdef DUMP_JSON_OBJECT_LIFE + g_life(this, true, g_life_param); +#endif +} +gb_json::~gb_json() +{ + clear(); +#ifdef DUMP_JSON_OBJECT_LIFE + g_life(this, false, g_life_param); +#endif +} + +std::string gb_json::object_key(gb_json* jsn) +{ + return "\"" + jsn->key() + "\":"; +} +std::string gb_json::array_key(gb_json* jsn) +{ + return ""; +} + +void gb_json::from_cjson(cJSON* cj) +{ + key_ = cj && cj->string ? cj->string : ""; + while (cj) + { + gb_json* child = nullptr; + if (cj->type == cJSON_True) + { + child = new gb_json(cj->string, true); + } + else if(cj->type == cJSON_False) + { + child = new gb_json(cj->string, false); + } + else if (cj->type == cJSON_Number) + { + if (cj->valuedouble - (int)cj->valuedouble < .00001) + { + child = new gb_json(cj->string, cj->valueint); + } + else + { + child = new gb_json(cj->string, cj->valuedouble); + } + } + else if (cj->type == cJSON_String) + { + child = new gb_json(cj->string, cj->valuestring); + } + else if (cj->type == cJSON_Object || cj->type == cJSON_Array) + { + child = new gb_json(); + child->from_cjson(cj->child); + child->key_ = cj->string ? cj->string : ""; + if (child->key_.empty()) + child->type_ = VAL_TYPE_OBJECT; + } + arr_val_.push_back(child); + cj = cj->next; + } + + //if (arr_val_.size() == 1 && arr_val_[0]->arr_val_.size() == 0) + //{ + // gb_json* child = arr_val_[0]; + // + // if (!child->key_.empty()) // array + // { + // arr_val_.clear(); + // type_ = child->type_; + // key_ = child->key_; + // simple_val_.dval = child->simple_val_.dval; + // strval_ = child->strval_; + // for (auto& v : child->arr_val_) + // arr_val_.push_back(v); + // child->arr_val_.clear(); + // child->release(); + // } + //} + + if (arr_val_.size()) + { + type_ = arr_val_[0]->key().empty() ? VAL_TYPE_ARRAY : VAL_TYPE_OBJECT; + } +} +gb_json* gb_json::find_child(const char* key, bool remove) +{ + gb_json* ret = nullptr; + + if (type_ == VAL_TYPE_OBJECT) + { + for (size_t i = 0; i < arr_val_.size(); ++i) + { + if (arr_val_[i]->key() == key) + { + ret = arr_val_[i]; + if (remove) + arr_val_.erase(arr_val_.begin() + i); + else + ret->add_ref(); + + break; + } + } + } + + return ret; +} + +int32_t gb_json::add_ref() +{ + std::lock_guard lock(ref_mutex_); + int32_t ref = ++ref_; + + return ref; +} +int32_t gb_json::release() +{ + int32_t ref = 0; + + { + std::lock_guard lock(ref_mutex_); + ref = --ref_; + } + + if (ref == 0) + delete this; + + return ref; +} + +static void check_cJSON(cJSON* root, std::vector* existing) +{ + if (!root) + return; + + for (auto& v : *existing) + { + if (v == root) + { + printf("cJSON* 0x%08x is repeat!\n", v); + break; + } + } + + existing->push_back(root); + check_cJSON(root->child, existing); + + cJSON* next = root->next; + while (next) + { + check_cJSON(next, existing); + next = next->next; + } +} +bool gb_json::attach_text(char* json_txt) +{ + clear(); + + cJSON* jsn = cJSON_Parse(json_txt); + if (jsn) + { + char *text = cJSON_Print(jsn); + if (text) + { + if (0) + { + FILE* dst = fopen("e:\\test-json.txt", "wb"); + fwrite(text, 1, strlen(text), dst); + fclose(dst); + } + free(text); + } + std::vector repeat; + + if(0) + check_cJSON(jsn, &repeat); + from_cjson(jsn->child); + cJSON_Delete(jsn); + + return true; + } + + return false; +} +void gb_json::clear(bool as_array) +{ + if (type_ == VAL_TYPE_ARRAY || type_ == VAL_TYPE_OBJECT) + { + for (auto& v : arr_val_) + v->release(); + } + type_ = as_array ? VAL_TYPE_ARRAY : VAL_TYPE_OBJECT; + simple_val_.dval = .0f; + key_ = ""; + strval_ = ""; + arr_val_.clear(); + cur_child_ = -1; +} +std::string gb_json::to_string(void) +{ + if (type_ == VAL_TYPE_NULL) + return ""; + if (type_ == VAL_TYPE_BOOL) + return (simple_val_.bval ? "true" : "false"); + if (type_ == VAL_TYPE_INT) + return std::to_string(simple_val_.nval); + if (type_ == VAL_TYPE_FLOAT) + return std::to_string(simple_val_.dval); + if (type_ == VAL_TYPE_STRING) + { + char* u = cJSON_utf8_2_unic(strval_.c_str()); + std::string r(u); + + free(u); + special_char_trans::to_writedown(r); + + return "\"" + r + "\""; + } + + std::string(*k)(gb_json*) = type_ == VAL_TYPE_OBJECT ? gb_json::object_key : gb_json::array_key; + std::string str(type_ == VAL_TYPE_OBJECT ? "{" : "["); + + if (arr_val_.size()) + { + str += k(arr_val_[0]) + arr_val_[0]->to_string(); + for(size_t i = 1; i < arr_val_.size(); ++i) + str += "," + k(arr_val_[i]) + arr_val_[i]->to_string(); + } + str += type_ == VAL_TYPE_OBJECT ? "}" : "]"; + + return str; +} + +std::string& gb_json::key(void) +{ + return key_; +} +bool gb_json::is_array(void) +{ + return type_ == VAL_TYPE_ARRAY; +} +bool gb_json::is_leaf_node(void) +{ + return type_ == VAL_TYPE_BOOL || + type_ == VAL_TYPE_INT || + type_ == VAL_TYPE_FLOAT || + type_ == VAL_TYPE_STRING; +} + +bool gb_json::get_value(const char* key, bool& val) +{ + bool ret = false; + gb_json* child = find_child(key); + + if (child) + { + if (child->type_ == VAL_TYPE_BOOL) + { + val = child->simple_val_.bval; + ret = true; + } + child->release(); + } + else if (type_ == VAL_TYPE_BOOL && key_ == key) + { + val = simple_val_.bval; + ret = true; + } + + return ret; +} +bool gb_json::get_value(const char* key, int& val) +{ + bool ret = false; + gb_json* child = find_child(key); + + if (child) + { + if (child->type_ == VAL_TYPE_INT) + { + val = child->simple_val_.nval; + ret = true; + } + child->release(); + } + else if (type_ == VAL_TYPE_INT && key_ == key) + { + val = simple_val_.nval; + ret = true; + } + + return ret; +} +bool gb_json::get_value(const char* key, double& val) +{ + bool ret = false; + gb_json* child = find_child(key); + + if (child) + { + if (child->type_ == VAL_TYPE_FLOAT) + { + val = child->simple_val_.dval; + ret = true; + } + child->release(); + } + else if (type_ == VAL_TYPE_FLOAT && key_ == key) + { + val = simple_val_.dval; + ret = true; + } + + // added on 2023-04-27: for cJSON consider both int and float as CJSON_Number, we consider int if the value is just an integer + if(!ret) + { + int v = 0; + ret = get_value(key, v); + if(ret) + val = v; + } + + return ret; +} +bool gb_json::get_value(const char* key, std::string& val) +{ + bool ret = false; + gb_json* child = find_child(key); + + if (child) + { + if (child->type_ == VAL_TYPE_STRING) + { + val = child->strval_; + ret = true; + } + child->release(); + } + else if (type_ == VAL_TYPE_STRING && key_ == key) + { + val = strval_; + ret = true; + } + + return ret; +} +bool gb_json::get_value(const char* key, gb_json*& val) +{ + bool ret = false; + gb_json *child = find_child(key); + + if (child) + { + if (child->type_ == VAL_TYPE_OBJECT || child->type_ == VAL_TYPE_ARRAY) + { + val = child; + ret = true; + } + else + { + child->release(); + } + } + + return ret; +} + +size_t gb_json::children(void) +{ + if (type_ == VAL_TYPE_ARRAY || type_ == VAL_TYPE_OBJECT) + return arr_val_.size(); + else + return -1; +} +gb_json* gb_json::child(size_t ind) +{ + if (type_ == VAL_TYPE_ARRAY || type_ == VAL_TYPE_OBJECT) + { + if (ind >= 0 && ind < arr_val_.size()) + { + arr_val_[ind]->add_ref(); + + return arr_val_[ind]; + } + } + + return nullptr; +} +gb_json* gb_json::first_child(void) +{ + if (type_ == VAL_TYPE_OBJECT || type_ == VAL_TYPE_ARRAY) + { + cur_child_ = 0; + if (arr_val_.size()) + { + arr_val_[0]->add_ref(); + + return arr_val_[0]; + } + } + + return nullptr; +} +gb_json* gb_json::next_child(void) +{ + if (type_ == VAL_TYPE_OBJECT || type_ == VAL_TYPE_ARRAY) + { + if (++cur_child_ < arr_val_.size()) + { + arr_val_[cur_child_]->add_ref(); + + return arr_val_[cur_child_]; + } + } + + return nullptr; +} + +bool gb_json::set_value(const char* key, bool val) +{ + if (type_ != VAL_TYPE_OBJECT) + return false; + + gb_json* child = find_child(key); + + if (child) + { + child->clear(); + child->type_ = VAL_TYPE_BOOL; + child->key() = key ? key : ""; + child->simple_val_.bval = val; + child->release(); + } + else + { + child = new gb_json(key, val); + arr_val_.push_back(child); + } + + return true; +} +bool gb_json::set_value(const char* key, int val) +{ + if (type_ != VAL_TYPE_OBJECT) + return false; + + gb_json* child = find_child(key); + + if (child) + { + child->clear(); + child->type_ = VAL_TYPE_INT; + child->key() = key ? key : ""; + child->simple_val_.nval = val; + child->release(); + } + else + { + child = new gb_json(key, val); + arr_val_.push_back(child); + } + + return true; +} +bool gb_json::set_value(const char* key, double val) +{ + if (type_ != VAL_TYPE_OBJECT) + return false; + + gb_json* child = find_child(key); + + if (child) + { + child->clear(); + child->type_ = VAL_TYPE_FLOAT; + child->key() = key ? key : ""; + child->simple_val_.dval = val; + child->release(); + } + else + { + child = new gb_json(key, val); + arr_val_.push_back(child); + } + + return true; +} +bool gb_json::set_value(const char* key, const char* val) +{ + if (type_ != VAL_TYPE_OBJECT) + return false; + + gb_json* child = find_child(key); + + if (child) + { + child->clear(); + child->type_ = VAL_TYPE_STRING; + child->key() = key ? key : ""; + child->strval_ = val ? val : ""; + child->release(); + } + else + { + child = new gb_json(key, val); + arr_val_.push_back(child); + } + + return true; +} +bool gb_json::set_value(const char* key, gb_json* val) +{ + if (type_ != VAL_TYPE_OBJECT) + return false; + + for (size_t i = 0; i < arr_val_.size(); ++i) + { + if (arr_val_[i]->key() == key) + { + arr_val_[i]->release(); + arr_val_[i] = val; + val->add_ref(); + return true; + } + } + + arr_val_.push_back(val); + val->key() = key; + val->add_ref(); + + return true; +} + +gb_json& gb_json::operator+=(bool val) +{ + if (type_ == VAL_TYPE_ARRAY) + { + gb_json* child = new gb_json(nullptr, val); + arr_val_.push_back(child); + } + + return *this; +} +gb_json& gb_json::operator+=(int val) +{ + if (type_ == VAL_TYPE_ARRAY) + { + gb_json* child = new gb_json(nullptr, val); + arr_val_.push_back(child); + } + + return *this; +} +gb_json& gb_json::operator+=(double val) +{ + if (type_ == VAL_TYPE_ARRAY) + { + gb_json* child = new gb_json(nullptr, val); + arr_val_.push_back(child); + } + + return *this; +} +gb_json& gb_json::operator+=(const char* val) +{ + if (type_ == VAL_TYPE_ARRAY) + { + gb_json* child = new gb_json(nullptr, val); + arr_val_.push_back(child); + } + + return *this; +} +gb_json& gb_json::operator+=(gb_json* val) +{ + if (type_ == VAL_TYPE_ARRAY) + { + val->add_ref(); + arr_val_.push_back(val); + } + + return *this; +} + +gb_json& gb_json::operator-=(int ind) +{ + remove(ind); + + return *this; +} +bool gb_json::remove(const char* key) +{ + gb_json* child = find_child(key, true); + + if (child) + { + child->release(); + return true; + } + else + { + return false; + } +} +bool gb_json::remove(gb_json* child) +{ + if (type_ == VAL_TYPE_ARRAY || type_ == VAL_TYPE_OBJECT) + { + for (size_t i = 0; i < arr_val_.size(); ++i) + { + if (arr_val_[i] == child) + { + arr_val_[i]->release(); + arr_val_.erase(arr_val_.begin() + i); + + return true; + } + } + } + + return false; +} +bool gb_json::remove(int ind) +{ + bool ret = false; + + if (type_ == VAL_TYPE_ARRAY || type_ == VAL_TYPE_OBJECT) + { + if (ind >= 0 && ind < arr_val_.size()) + { + arr_val_[ind]->release(); + arr_val_.erase(arr_val_.begin() + ind); + ret = true; + } + } + + return ret; +} + +int gb_json::index(gb_json* child) +{ + if (type_ == VAL_TYPE_ARRAY || type_ == VAL_TYPE_OBJECT) + { + for (int i = 0; i < arr_val_.size(); ++i) + { + if (arr_val_[i] == child) + return i; + } + } + + return -1; +} +int gb_json::index_move_to(gb_json* child, int ind) +{ + int i = index(child); + + if (i == -1) + return -1; + + arr_val_.erase(arr_val_.begin() + i); + if (ind < 0) + ind = 0; + if (ind > arr_val_.size()) + ind = arr_val_.size(); + arr_val_.insert(arr_val_.begin() + ind, child); + + return ind; +} +int gb_json::insert(int ind, const char* key, gb_json* child) +{ + int i = index(child); + + if (i == -1) + { + if (ind < 0) + ind = 0; + else if (ind > arr_val_.size()) + ind = arr_val_.size(); + + child->key() = key ? key : ""; + arr_val_.insert(arr_val_.begin() + ind, child); + child->add_ref(); + } + else if(i != ind) + { + arr_val_.erase(arr_val_.begin() + i); + if (ind < 0) + ind = 0; + if (ind > arr_val_.size()) + ind = arr_val_.size(); + child->key() = key ? key : ""; + arr_val_.insert(arr_val_.begin() + ind, child); + } + + return ind; +} + +bool gb_json::value(bool& val) +{ + bool ret = false; + + if (is_leaf_node() && type_ == VAL_TYPE_BOOL) + { + val = simple_val_.bval; + ret = true; + } + + return ret; +} +bool gb_json::value(int& val) +{ + bool ret = false; + + if (is_leaf_node() && type_ == VAL_TYPE_INT) + { + val = simple_val_.nval; + ret = true; + } + + return ret; +} +bool gb_json::value(double& val) +{ + bool ret = false; + + if (is_leaf_node() && type_ == VAL_TYPE_FLOAT) + { + val = simple_val_.dval; + ret = true; + } + + return ret; +} +bool gb_json::value(std::string& val) +{ + bool ret = false; + + if (is_leaf_node() && type_ == VAL_TYPE_STRING) + { + val = strval_; + ret = true; + } + + return ret; +} +gb_json& gb_json::operator=(bool val) +{ + if (is_leaf_node()) + { + simple_val_.bval = val; + type_ = VAL_TYPE_BOOL; + } + + return *this; +} +gb_json& gb_json::operator=(int val) +{ + if (is_leaf_node()) + { + simple_val_.nval = val; + type_ = VAL_TYPE_INT; + } + + return *this; +} +gb_json& gb_json::operator=(double val) +{ + if (is_leaf_node()) + { + simple_val_.dval = val; + type_ = VAL_TYPE_FLOAT; + } + + return *this; +} +gb_json& gb_json::operator=(const char* val) +{ + if (is_leaf_node()) + { + strval_ = val ? val : ""; + type_ = VAL_TYPE_STRING; + } + + return *this; +} + +bool gb_json::operator==(const gb_json& r) +{ + if (type_ != r.type_) + return false; + + if (type_ == VAL_TYPE_BOOL) + return simple_val_.bval == r.simple_val_.bval; + if (type_ == VAL_TYPE_INT) + return simple_val_.nval == r.simple_val_.nval; + if (type_ == VAL_TYPE_FLOAT) + return fabs(simple_val_.dval - r.simple_val_.dval) < .00001; + if (type_ == VAL_TYPE_STRING) + return strval_ == r.strval_; + if (arr_val_.size() != r.arr_val_.size()) + return false; + + for (int i = 0; i < arr_val_.size(); ++i) + { + if (!(*arr_val_[i] == *r.arr_val_[i])) + return false; + } + + return true; +} +bool gb_json::operator!=(const gb_json& r) +{ + return !(*this == r); +} diff --git a/sdk/json/gb_json.h b/sdk/json/gb_json.h new file mode 100644 index 0000000..628c8ce --- /dev/null +++ b/sdk/json/gb_json.h @@ -0,0 +1,122 @@ +#pragma once + +#include +#include +#include + +struct cJSON; + +class gb_json +{ + volatile int32_t ref_; + std::mutex ref_mutex_; + + enum val_type + { + VAL_TYPE_NULL = 0, + VAL_TYPE_BOOL, + VAL_TYPE_INT, + VAL_TYPE_FLOAT, + VAL_TYPE_STRING, + VAL_TYPE_OBJECT, + VAL_TYPE_ARRAY, + }; + val_type type_; + std::string key_; + union + { + bool bval; + int nval; + double dval; + }simple_val_; + std::string strval_; + std::vector arr_val_; + size_t cur_child_; + + static std::string object_key(gb_json* jsn); + static std::string array_key(gb_json* jsn); + + void from_cjson(cJSON* cj); + gb_json* find_child(const char* key, bool remove = false); + +public: + gb_json(char* json_txt = 0); + gb_json(const char* key, bool val); + gb_json(const char* key, int val); + gb_json(const char* key, double val); + gb_json(const char* key, const char* val); + gb_json(const char* key, const std::string& val); + +protected: + ~gb_json(); + +public: + int32_t add_ref(); + int32_t release(); + +public: + // parse/un-parse ... + bool attach_text(char* json_txt); + void clear(bool as_array = false); + std::string to_string(void); + + // attributes ... + std::string& key(void); + bool is_array(void); + bool is_leaf_node(void); // whether this object is a leaf node contains final value + + // value access ... + bool get_value(const char* key, bool& val); + bool get_value(const char* key, int& val); + bool get_value(const char* key, double& val); + bool get_value(const char* key, std::string& val); + bool get_value(const char* key, gb_json*& val); + + // enumeration ... + size_t children(void); // return children count if was object or array, or else -1 returned + gb_json* child(size_t ind); + gb_json* first_child(void); + gb_json* next_child(void); + + // change the item matching 'key', otherwise add a new item + bool set_value(const char* key, bool val); + bool set_value(const char* key, int val); + bool set_value(const char* key, double val); + bool set_value(const char* key, const char* val); + bool set_value(const char* key, gb_json* val); + + // operator+= only for array + gb_json& operator+=(bool val); + gb_json& operator+=(int val); + gb_json& operator+=(double val); + gb_json& operator+=(const char* val); + gb_json& operator+=(gb_json* val); + + // remove item + gb_json& operator-=(int ind); + bool remove(const char* key); + bool remove(gb_json* child); + bool remove(int ind); + + // position management, return index + int index(gb_json* child); + int index_move_to(gb_json* child, int ind); + int insert(int ind, const char* key, gb_json* child); + + // leaf node value ... + bool value(bool& val); + bool value(int& val); + bool value(double& val); + bool value(std::string& val); + gb_json& operator=(bool val); + gb_json& operator=(int val); + gb_json& operator=(double val); + gb_json& operator=(const char* val); + + bool operator==(const gb_json& r); + bool operator!=(const gb_json& r); +}; + +#ifdef DUMP_JSON_OBJECT_LIFE +void set_gbjson_life_callback(void(*life)(gb_json*, bool, void*), void* param); +#endif \ No newline at end of file diff --git a/sdk/sane/sane.h b/sdk/sane/sane.h new file mode 100644 index 0000000..0114649 --- /dev/null +++ b/sdk/sane/sane.h @@ -0,0 +1,252 @@ +/* sane - Scanner Access Now Easy. + Copyright (C) 1997-1999 David Mosberger-Tang and Andreas Beck + This file is part of the SANE package. + + This file is in the public domain. You may use and modify it as + you see fit, as long as this copyright message is included and + that there is an indication as to what modifications have been + made (if any). + + SANE 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. + + This file declares SANE application interface. See the SANE + standard for a detailed explanation of the interface. */ +#ifndef sane_h +#define sane_h + +#ifndef BACKEND_NAME +#define BACKEND_NAME hgsane +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * SANE types and defines + */ + +#define SANE_CURRENT_MAJOR 1 +#define SANE_CURRENT_MINOR 0 + +#define SANE_VERSION_CODE(major, minor, build) \ + ( (((SANE_Word) (major) & 0xff) << 24) \ + | (((SANE_Word) (minor) & 0xff) << 16) \ + | (((SANE_Word) (build) & 0xffff) << 0)) + +#define SANE_VERSION_MAJOR(code) ((((SANE_Word)(code)) >> 24) & 0xff) +#define SANE_VERSION_MINOR(code) ((((SANE_Word)(code)) >> 16) & 0xff) +#define SANE_VERSION_BUILD(code) ((((SANE_Word)(code)) >> 0) & 0xffff) + +#define SANE_FALSE 0 +#define SANE_TRUE 1 + +typedef unsigned char SANE_Byte; +typedef int SANE_Word; +typedef SANE_Word SANE_Bool; +typedef SANE_Word SANE_Int; +typedef char SANE_Char; +typedef SANE_Char *SANE_String; +typedef const SANE_Char *SANE_String_Const; +typedef void *SANE_Handle; +typedef SANE_Word SANE_Fixed; + +#define SANE_FIXED_SCALE_SHIFT 16 +#define SANE_FIX(v) ((SANE_Word) ((v) * (1 << SANE_FIXED_SCALE_SHIFT))) +#define SANE_UNFIX(v) ((double)(v) / (1 << SANE_FIXED_SCALE_SHIFT)) + +typedef enum + { + SANE_STATUS_GOOD = 0, /* everything A-OK */ + SANE_STATUS_UNSUPPORTED, /* operation is not supported */ + SANE_STATUS_CANCELLED, /* operation was cancelled */ + SANE_STATUS_DEVICE_BUSY, /* device is busy; try again later */ + SANE_STATUS_INVAL, /* data is invalid (includes no dev at open) */ + SANE_STATUS_EOF, /* no more data available (end-of-file) */ + SANE_STATUS_JAMMED, /* document feeder jammed */ + SANE_STATUS_NO_DOCS, /* document feeder out of documents */ + SANE_STATUS_COVER_OPEN, /* scanner cover is open */ + SANE_STATUS_IO_ERROR, /* error during device I/O */ + SANE_STATUS_NO_MEM, /* out of memory */ + SANE_STATUS_ACCESS_DENIED /* access to resource has been denied */ + } +SANE_Status; + +/* following are for later sane version, older frontends wont support */ +#if 0 +#define SANE_STATUS_WARMING_UP 12 /* lamp not ready, please retry */ +#define SANE_STATUS_HW_LOCKED 13 /* scanner mechanism locked for transport */ +#endif + +typedef enum + { + SANE_TYPE_BOOL = 0, + SANE_TYPE_INT, + SANE_TYPE_FIXED, + SANE_TYPE_STRING, + SANE_TYPE_BUTTON, + SANE_TYPE_GROUP + } +SANE_Value_Type; + +typedef enum + { + SANE_UNIT_NONE = 0, /* the value is unit-less (e.g., # of scans) */ + SANE_UNIT_PIXEL, /* value is number of pixels */ + SANE_UNIT_BIT, /* value is number of bits */ + SANE_UNIT_MM, /* value is millimeters */ + SANE_UNIT_DPI, /* value is resolution in dots/inch */ + SANE_UNIT_PERCENT, /* value is a percentage */ + SANE_UNIT_MICROSECOND /* value is micro seconds */ + } +SANE_Unit; + +typedef struct + { + SANE_String_Const name; /* unique device name */ + SANE_String_Const vendor; /* device vendor string */ + SANE_String_Const model; /* device model name */ + SANE_String_Const type; /* device type (e.g., "flatbed scanner") */ + } +SANE_Device; + +#define SANE_CAP_SOFT_SELECT (1 << 0) +#define SANE_CAP_HARD_SELECT (1 << 1) +#define SANE_CAP_SOFT_DETECT (1 << 2) +#define SANE_CAP_EMULATED (1 << 3) +#define SANE_CAP_AUTOMATIC (1 << 4) +#define SANE_CAP_INACTIVE (1 << 5) +#define SANE_CAP_ADVANCED (1 << 6) + +#define SANE_OPTION_IS_ACTIVE(cap) (((cap) & SANE_CAP_INACTIVE) == 0) +#define SANE_OPTION_IS_SETTABLE(cap) (((cap) & SANE_CAP_SOFT_SELECT) != 0) + +#define SANE_INFO_INEXACT (1 << 0) +#define SANE_INFO_RELOAD_OPTIONS (1 << 1) +#define SANE_INFO_RELOAD_PARAMS (1 << 2) + +typedef enum + { + SANE_CONSTRAINT_NONE = 0, + SANE_CONSTRAINT_RANGE, + SANE_CONSTRAINT_WORD_LIST, + SANE_CONSTRAINT_STRING_LIST + } +SANE_Constraint_Type; + +typedef struct + { + SANE_Word min; /* minimum (element) value */ + SANE_Word max; /* maximum (element) value */ + SANE_Word quant; /* quantization value (0 if none) */ + } +SANE_Range; + +typedef struct + { + SANE_String_Const name; /* name of this option (command-line name) */ + SANE_String_Const title; /* title of this option (single-line) */ + SANE_String_Const desc; /* description of this option (multi-line) */ + SANE_Value_Type type; /* how are values interpreted? */ + SANE_Unit unit; /* what is the (physical) unit? */ + SANE_Int size; + SANE_Int cap; /* capabilities */ + + SANE_Constraint_Type constraint_type; + union + { + const SANE_String_Const *string_list; /* NULL-terminated list */ + const SANE_Word *word_list; /* first element is list-length */ + const SANE_Range *range; + } + constraint; + } +SANE_Option_Descriptor; + +typedef enum + { + SANE_ACTION_GET_VALUE = 0, + SANE_ACTION_SET_VALUE, + SANE_ACTION_SET_AUTO + } +SANE_Action; + +typedef enum + { + SANE_FRAME_GRAY, /* band covering human visual range */ + SANE_FRAME_RGB, /* pixel-interleaved red/green/blue bands */ + SANE_FRAME_RED, /* red band only */ + SANE_FRAME_GREEN, /* green band only */ + SANE_FRAME_BLUE /* blue band only */ + } +SANE_Frame; + +/* push remaining types down to match existing backends */ +/* these are to be exposed in a later version of SANE */ +/* most front-ends will require updates to understand them */ +#if 0 +#define SANE_FRAME_TEXT 0x0A /* backend specific textual data */ +#define SANE_FRAME_JPEG 0x0B /* complete baseline JPEG file */ +#define SANE_FRAME_G31D 0x0C /* CCITT Group 3 1-D Compressed (MH) */ +#define SANE_FRAME_G32D 0x0D /* CCITT Group 3 2-D Compressed (MR) */ +#define SANE_FRAME_G42D 0x0E /* CCITT Group 4 2-D Compressed (MMR) */ + +#define SANE_FRAME_IR 0x0F /* bare infrared channel */ +#define SANE_FRAME_RGBI 0x10 /* red+green+blue+infrared */ +#define SANE_FRAME_GRAYI 0x11 /* gray+infrared */ +#define SANE_FRAME_XML 0x12 /* undefined schema */ +#endif + +typedef struct + { + SANE_Frame format; + SANE_Bool last_frame; + SANE_Int bytes_per_line; + SANE_Int pixels_per_line; + SANE_Int lines; + SANE_Int depth; + } +SANE_Parameters; + +struct SANE_Auth_Data; + +#define SANE_MAX_USERNAME_LEN 128 +#define SANE_MAX_PASSWORD_LEN 128 + +typedef void (*SANE_Auth_Callback) (SANE_String_Const resource, + SANE_Char *username, + SANE_Char *password); + +extern SANE_Status sane_init (SANE_Int * version_code, + SANE_Auth_Callback authorize); +extern void sane_exit (void); +extern SANE_Status sane_get_devices (const SANE_Device *** device_list, + SANE_Bool local_only); +extern SANE_Status sane_open (SANE_String_Const devicename, + SANE_Handle * handle); +extern void sane_close (SANE_Handle handle); +extern const SANE_Option_Descriptor * + sane_get_option_descriptor (SANE_Handle handle, SANE_Int option); +extern SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, + SANE_Action action, void *value, + SANE_Int * info); +extern SANE_Status sane_get_parameters (SANE_Handle handle, + SANE_Parameters * params); +extern SANE_Status sane_start (SANE_Handle handle); +extern SANE_Status sane_read (SANE_Handle handle, SANE_Byte * data, + SANE_Int max_length, SANE_Int * length); +extern void sane_cancel (SANE_Handle handle); +extern SANE_Status sane_set_io_mode (SANE_Handle handle, + SANE_Bool non_blocking); +extern SANE_Status sane_get_select_fd (SANE_Handle handle, + SANE_Int * fd); +extern SANE_String_Const sane_strstatus (SANE_Status status); + +#ifdef __cplusplus +} +#endif + + +#endif /* sane_h */ diff --git a/sdk/sane/sane_ex.h b/sdk/sane/sane_ex.h new file mode 100644 index 0000000..2617738 --- /dev/null +++ b/sdk/sane/sane_ex.h @@ -0,0 +1,897 @@ +/* sane - Scanner Access Now Easy. + 对sane标准头文件的扩展 + + Author: Gongbing + + Date: 2022-01-14 + + Path: SANE_CONFIG_DIR + This environment variable specifies the list of directories that may contain theconfiguration file. Under UNIX, + the directories are separated by a colon (:'),under os/2,they are separated by a semi-colon (;'). If this variable + is not set,the configuration file is searched in two default directories: first, the currentworking directory(".") + and then in letc/sane.d. If the value of the environmentvariable ends with the directory separator character,then + the default directoriesare searched after the explicitly specified directories. For example,setting SANE_CONFIG_DIR + to" /tmp/config :" would result in directories tmp/config, ., andletclsane.d being searched (in this order). +*/ + +#ifndef sane_ex_h +#define sane_ex_h + + +#define SIZE_KB(n) ((n) * 1024) +#define SIZE_MB(n) SIZE_KB(n * 1024) +#define SIZE_GB(n) SIZE_MB(n * 1024) + +#define SEC_2_MS(s) ((s) * 1000) +#define MSEC_2_US(ms) ((ms) * 1000) +#define SEC_2_US(s) MSEC_2_US(SEC_2_MS(s)) + +#define ALIGN_TO(v, align) (((v) + (align) - 1) / (align) * (align)) +#define ALIGN_INT(v) ALIGN_TO(v, sizeof(int)) + +#include + +#ifndef EXPORT_SANE_API +#ifdef OEM_HANWANG + #define ENTIRE_API(pre, tail) pre##_hwsane_##tail +#elif defined(OEM_LISICHENG) + #define ENTIRE_API(pre, tail) pre##_lscsane_##tail +#elif defined(OEM_CANGTIAN) + #define ENTIRE_API(pre, tail) pre##_ctssane_##tail +#elif defined(OEM_ZHONGJING) +#define ENTIRE_API(pre, tail) pre##_zjsane_##tail +#elif defined(OEM_ZIGUANG) +#define ENTIRE_API(pre, tail) pre##_zgsane_##tail +#elif defined(OEM_DELI) + #define ENTIRE_API(pre, tail) pre##_dlsane_##tail +#elif defined(OEM_NEUTRAL) + #define ENTIRE_API(pre, tail) pre##_neusane_##tail +#else + #define ENTIRE_API(pre, tail) pre##_hgsane_##tail +#endif + +#define sane_init ENTIRE_API(sane, init) +#define sane_init_ex ENTIRE_API(sane, init_ex) +#define sane_exit ENTIRE_API(sane, exit) +#define sane_get_devices ENTIRE_API(sane, get_devices) +#define sane_open ENTIRE_API(sane, open) +#define sane_close ENTIRE_API(sane, close) +#define sane_get_option_descriptor ENTIRE_API(sane, get_option_descriptor) +#define sane_control_option ENTIRE_API(sane, control_option) +#define sane_get_parameters ENTIRE_API(sane, get_parameters) +#define sane_start ENTIRE_API(sane, start) +#define sane_read ENTIRE_API(sane, read) +#define sane_cancel ENTIRE_API(sane, cancel) +#define sane_set_io_mode ENTIRE_API(sane, set_io_mode) +#define sane_get_select_fd ENTIRE_API(sane, get_select_fd) +#define sane_strstatus ENTIRE_API(sane, strstatus) +#define sane_io_control ENTIRE_API(sane, io_control) +#define sane_err_desc ENTIRE_API(sane, err_desc) +#define sane_get_option_descriptor_ex ENTIRE_API(get_option_descriptor_ex) +#define sane_control_option_ex ENTIRE_API(control_option_ex) +#define sane_read_ext_info ENTIRE_API(sane, read_ext_info) +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#define SANE_OPT_NAME(name) SANE_STD_OPT_NAME_##name +#define SANE_OPT_FIXED_ID(id) SANE_OPT_ID_##id +#define IS_SANE_OPT(v, sd) strcmp(v, SANE_OPT_NAME(sd)) == 0 +#define MAX_OPT_NAME_LEN 64 +#define JSON_SANE_TYPE_BOOL "bool" +#define JSON_SANE_TYPE_INT "int" +#define JSON_SANE_TYPE_FIXED "float" +#define JSON_SANE_TYPE_STRING "string" +#define JSON_SANE_TYPE_GROUP "group" +#define JSON_SANE_TYPE_BUTTON "button" + +// 用户æƒé™ +enum +{ + USER_PRIVILEGE_COMMON = 0, // 普通用户æƒé™ + USER_PRIVILEGE_LOCAL_MGR = 10, // 本地管ç†å‘˜æƒé™ + USER_PRIVILEGE_TECH_SUPPORTING = 20, // 技术支æŒæƒé™ + USER_PRIVILEGE_DEVLOPER = 100, // å¼€å‘者æƒé™ï¼ˆæŸ¥çœ‹æ‰€æœ‰å±žæ€§ï¼‰ +}; + +// 属性å¯è§ +enum opt_visible_level // "visible" field +{ + OPT_VISIBLE_ALL = 0, // visible on ui and accessible + OPT_VISIBLE_ACCESS, // accessible only + OPT_VISIBLE_HIDE, // invisible and inaccessible unless user has DEVELOPE privilege + OPT_VISIBLE_NOT_SUPPORT, // device does not support this option +}; + +// sane-standard-option-name defined by third-app +// +// PART I: å‚数类型与åŽé«˜ä¸€è‡´ï¼Œå¯ç›´æŽ¥é€šè¿‡æ”¹name字段为标准值实现 +#define SANE_STD_OPT_NAME_RESTORE "restore" // OPTION_TITLE_HFMRSZ +#define SANE_STD_OPT_NAME_HELP "help" // OPTION_TITLE_BZ +#define SANE_STD_OPT_NAME_IS_MULTI_OUT "is-multiout" // OPTION_TITLE_DLSC +#define SANE_STD_OPT_NAME_MULTI_OUT_TYPE "multiout-type" // OPTION_TITLE_DLSCLX +#define SANE_STD_OPT_NAME_COLOR_MODE "mode" // OPTION_TITLE_YSMS +#define SANE_STD_OPT_NAME_BINARY_THRESHOLD "binary-threshold" // OPTION_TITLE_HBTXYZ +#define SANE_STD_OPT_NAME_REVERSE_01 "reverse-bw" // OPTION_TITLE_HBTXFSSC +#define SANE_STD_OPT_NAME_FILTER "filter" // OPTION_TITLE_HDHHBTX_CSYZQ +#define SANE_STD_OPT_NAME_RID_MULTIOUT_RED "is-rid-multiout-red" // OPTION_TITLE_24WCSTX_DLSCCH +#define SANE_STD_OPT_NAME_RID_ANSWER_SHEET_RED "is-rid-answer-sheet-red" // OPTION_TITLE_24WCSTX_DTKCH +#define SANE_STD_OPT_NAME_ERASE_BACKGROUND "is-erase-bkg" // OPTION_TITLE_BJYC +#define SANE_STD_OPT_NAME_BKG_COLOR_RANGE "bkg-color-range" // OPTION_TITLE_BJSCFDFW +#define SANE_STD_OPT_NAME_SHARPEN "sharpen" // OPTION_TITLE_RHYMH +#define SANE_STD_OPT_NAME_RID_MORR "is-rid-morr" // OPTION_TITLE_QCMW +#define SANE_STD_OPT_NAME_RID_GRID "is-rid-grid" // OPTION_TITLE_CWW +#define SANE_STD_OPT_NAME_ERROR_EXTENSION "is-err-extension" // OPTION_TITLE_CWKS +#define SANE_STD_OPT_NAME_NOISE_OPTIMIZE "is-noise-optimize" // OPTION_TITLE_HBTXZDYH +#define SANE_STD_OPT_NAME_NOISE_SIZE "noise-size" // OPTION_TITLE_ZDYHCC +#define SANE_STD_OPT_NAME_PAPER "paper" // OPTION_TITLE_ZZCC +#define SANE_STD_OPT_NAME_PAPER_W "paper-w" // 纸张宽度 +#define SANE_STD_OPT_NAME_PAPER_H "paper-h" // 纸张高度 +#define SANE_STD_OPT_NAME_LATERAL "lateral" // 纸张横å‘标志 +#define SANE_STD_OPT_NAME_CUSTOM_AREA "is-custom-area" // OPTION_TITLE_ZDYSMQY +#define SANE_STD_OPT_NAME_CUSTOM_AREA_LEFT "tl-x" // OPTION_TITLE_SMQYZCmm +#define SANE_STD_OPT_NAME_CUSTOM_AREA_RIGHT "br-x" // OPTION_TITLE_SMQYYCmm +#define SANE_STD_OPT_NAME_CUSTOM_AREA_TOP "tl-y" // OPTION_TITLE_SMQYSCmm +#define SANE_STD_OPT_NAME_CUSTOM_AREA_BOTTOM "br-y" // OPTION_TITLE_SMQYXCmm +#define SANE_STD_OPT_NAME_SIZE_CHECK "is-size-check" // OPTION_TITLE_CCJC +#define SANE_STD_OPT_NAME_PAGE "page" // OPTION_TITLE_SMYM +#define SANE_STD_OPT_NAME_DISCARD_BLANK_SENS "blank-sensitivity" // OPTION_TITLE_TGKBYLMD +#define SANE_STD_OPT_NAME_RESOLUTION "resolution" // OPTION_TITLE_FBL +#define SANE_STD_OPT_NAME_TIME_TO_SLEEP "time-to-sleep" // OPTION_TITLE_XMSJ +#define SANE_STD_OPT_NAME_IMAGE_QUALITY "image-quality" // OPTION_TITLE_HZ +#define SANE_STD_OPT_NAME_EXCHANGE "is-exchange" // OPTION_TITLE_JHZFM +#define SANE_STD_OPT_NAME_SPLIT "is-split" // OPTION_TITLE_TXCF +#define SANE_STD_OPT_NAME_ANTI_SKEW "is-anti-skew" // OPTION_TITLE_ZDJP +#define SANE_STD_OPT_NAME_IS_CUSTOM_GAMMA "is-custom-gamma" // OPTION_TITLE_QYSDQX +#define SANE_STD_OPT_NAME_GAMMA "gamma" // OPTION_TITLE_JMZ +#define SANE_STD_OPT_NAME_CUSTOM_GAMMA "custom-gamma" // OPTION_TITLE_CUSTOM_JMZ; data - SANE_Gamma* +#define SANE_STD_OPT_NAME_GRAY_GAMMA "gray-gamma-table" +#define SANE_STD_OPT_NAME_COLOR_GAMMA "color-gamma-table" +#define SANE_STD_OPT_NAME_RED_GAMMA "red-gamma-table" +#define SANE_STD_OPT_NAME_GREEN_GAMMA "green-gamma-table" +#define SANE_STD_OPT_NAME_BLUE_GAMMA "blue-gamma-table" +#define SANE_STD_OPT_NAME_BRIGHTNESS "brightness" // OPTION_TITLE_LDZ +#define SANE_STD_OPT_NAME_CONTRAST "contrast" // OPTION_TITLE_DBD +#define SANE_STD_OPT_NAME_IS_PHOTO_MODE "is-photo-mode" // OPTION_TITLE_ZPMS +#define SANE_STD_OPT_NAME_ERASE_BLACK_FRAME "is-erase-black-frame" // OPTION_TITLE_XCHK +#define SANE_STD_OPT_NAME_DARK_SAMPLE "is-dark-sample" // OPTION_TITLE_SSYZ +#define SANE_STD_OPT_NAME_THRESHOLD "threshold" // OPTION_TITLE_YZ +#define SANE_STD_OPT_NAME_ANTI_NOISE_LEVEL "anti-noise-level" // OPTION_TITLE_BJKZDJ +#define SANE_STD_OPT_NAME_MARGIN "margin" // OPTION_TITLE_BYSJ +#define SANE_STD_OPT_NAME_FILL_BKG_MODE "bkg-fill-mode" // OPTION_TITLE_BJTCFS +#define SANE_STD_OPT_NAME_IS_ANTI_PERMEATE "is-anti-permeate" // OPTION_TITLE_FZST +#define SANE_STD_OPT_NAME_ANTI_PERMEATE_LEVEL "permeate-level" // OPTION_TITLE_FZSTDJ +#define SANE_STD_OPT_NAME_RID_HOLE "is-rid-hole" // OPTION_TITLE_CKYC +#define SANE_STD_OPT_NAME_HOLE_THRESHOLD "hole-threshold" // +#define SANE_STD_OPT_NAME_SEARCH_HOLE_RANGE "search-hole-range" // OPTION_TITLE_CKSSFWZFMBL +#define SANE_STD_OPT_NAME_RID_HOLE_L "is-rid-hole-l" // OPTION_TITLE_CKYCZC +#define SANE_STD_OPT_NAME_SEARCH_HOLE_RANGE_L "search-hole-range-l" // OPTION_TITLE_ZCCKSSFWZFMBL +#define SANE_STD_OPT_NAME_RID_HOLE_R "is-rid-hole-r" // OPTION_TITLE_CKYCYC +#define SANE_STD_OPT_NAME_SEARCH_HOLE_RANGE_R "search-hole-range-r" // OPTION_TITLE_YCCKSSFWZFMBL +#define SANE_STD_OPT_NAME_RID_HOLE_T "is-rid-hole-t" // OPTION_TITLE_CKYCSC +#define SANE_STD_OPT_NAME_SEARCH_HOLE_RANGE_T "search-hole-range-t" // OPTION_TITLE_SCCKSSFWZFMBL +#define SANE_STD_OPT_NAME_RID_HOLE_B "is-rid-hole-b" // OPTION_TITLE_CKYCXC +#define SANE_STD_OPT_NAME_SEARCH_HOLE_RANGE_B "search-hole-range-b" // OPTION_TITLE_XCCKSSFWZFMBL +#define SANE_STD_OPT_NAME_IS_FILL_COLOR "is-fill-color" // OPTION_TITLE_SCTC +#define SANE_STD_OPT_NAME_IS_ULTROSONIC_CHECK "is-ultrosonic" // OPTION_TITLE_CSBJC +#define SANE_STD_OPT_NAME_DOUBLE_FEED_HANDLE "double-feed" // OPTION_TITLE_SZTPCL +#define SANE_STD_OPT_NAME_IS_CHECK_STAPLE "is-staple" // OPTION_TITLE_ZDJC +#define SANE_STD_OPT_NAME_SCAN_MODE "scan-mode" // OPTION_TITLE_SMZS +#define SANE_STD_OPT_NAME_SCAN_COUNT "scan-count" // OPTION_TITLE_SMSL +#define SANE_STD_OPT_NAME_TEXT_DIRECTION "direction" // OPTION_TITLE_WGFX +#define SANE_STD_OPT_NAME_IS_ROTATE_BKG_180 "is-rotate-bkg-180" // OPTION_TITLE_BMXZ180 +#define SANE_STD_OPT_NAME_IS_CHECK_DOG_EAR "is-check-dog-ear" // OPTION_TITLE_ZJJC +#define SANE_STD_OPT_NAME_DOG_EAR_SIZE "dog-ear-size" // OPTION_TITLE_ZJDX +#define SANE_STD_OPT_NAME_IS_CHECK_ASKEW "is-check-askew" // OPTION_TITLE_WXJC +#define SANE_STD_OPT_NAME_ASKEW_RANGE "askew-range" // OPTION_TITLE_WXRRD +#define SANE_STD_OPT_NAME_FEED_STRENGTH "feed-strength" // OPTION_TITLE_FZQD +#define SANE_STD_OPT_NAME_IS_AUTO_FEED_STRENGTH "is-auto-strength" // OPTION_TITLE_ZDFZQD +#define SANE_STD_OPT_NAME_FEED_STRENGTH_VALUE "feed-strength-value" // OPTION_TITLE_JZSBL +#define SANE_STD_OPT_NAME_WAIT_TO_SCAN "is-wait-scan" // OPTION_TITLE_DZSM +#define SANE_STD_OPT_NAME_FOLD_TYPE "fold-type" // OPTION_TITLE_DZMS +#define SANE_STD_OPT_NAME_COLOR_CORRECTION "color-correction" // OPTION_TITLE_SPJZ +#define SANE_STD_OPT_NAME_WAIT_SCAN_EXIT "wait-scan-exit" // OPTION_TITLE_DZSMTCSJ +#define SANE_STD_OPT_NAME_DISCARDBLANK "discardblank" // OPTION_TITLE_TGKBY + +#define SANE_STD_OPT_NAME_DEVICE_NAME "dev-name" // 设备å称 +#define SANE_STD_OPT_NAME_DEVICE_VID "dev-vid" // 设备VID +#define SANE_STD_OPT_NAME_DEVICE_PID "dev-pid" // 设备PID +#define SANE_STD_OPT_NAME_DEVICE_MODEL "dev-model" // 设备系列 +#define SANE_STD_OPT_NAME_DEV_NAME SANE_STD_OPT_NAME_DEVICE_NAME +#define SANE_STD_OPT_NAME_VID SANE_STD_OPT_NAME_DEVICE_VID +#define SANE_STD_OPT_NAME_PID SANE_STD_OPT_NAME_DEVICE_PID +#define SANE_STD_OPT_NAME_DEV_FAMILY SANE_STD_OPT_NAME_DEVICE_MODEL +#define SANE_STD_OPT_NAME_DEVICE_TYPE "dev-type" // è®¾å¤‡åž‹å· +#define SANE_STD_OPT_NAME_DEVICE_SERIAL_NO "dev-sn" // 设备åºåˆ—å· +#define SANE_STD_OPT_NAME_FIRMWARE_VERSION "fmw-ver" // 固件版本 +#define SANE_STD_OPT_NAME_DEVICE_IP_ADDR "ip-addr" // 设备IPåœ°å€ +#define SANE_STD_OPT_NAME_DEVICE_MAC_ADDR "mac-addr" // 设备MACåœ°å€ +#define SANE_STD_OPT_NAME_ROLLER_COUNT "roll-cnt" // 滚轴张数 +#define SANE_STD_OPT_NAME_TOTAL_COUNT "total-cnt" // 历å²æ€»å¼ æ•° +#define SANE_STD_OPT_NAME_HISTORY_COUNT SANE_STD_OPT_NAME_TOTAL_COUNT +#define SANE_STD_OPT_NAME_GET_DEVS_L0G "devs-log" // 获å–设备日志 +#define SANE_STD_OPT_NAME_DRIVER_VERSION "drv-ver" // 设备VID +#define SANE_STD_OPT_NAME_MANUFACTURER "company" // å…¬å¸å称 +#define SANE_STD_OPT_NAME_COPYRIGHT "copyright" // ç‰ˆæƒ +#define SANE_STD_OPT_NAME_CO_URL "co-url" // å…¬å¸ç½‘å€ +#define SANE_STD_OPT_NAME_CO_TEL "co-tel" // å…¬å¸ç”µè¯ +#define SANE_STD_OPT_NAME_CO_ADDR "co-addr" // å…¬å¸åœ°å€ +#define SANE_STD_OPT_NAME_CO_GPS "co-gps" // å…¬å¸åœ°å›¾å®šä½ +#define SANE_STD_OPT_NAME_LOGIN "login" // 登录 +#define SANE_STD_OPT_NAME_LOGOUT "logout" // 登出 +#define SANE_STD_OPT_NAME_DRIVER_LOG "drv-log" // 驱动日志 +#define SANE_STD_OPT_NAME_DEVICE_LOG SANE_STD_OPT_NAME_GET_DEVS_L0G +#define SANE_STD_OPT_NAME_ROLLER_LIFE "roller-life" // 滚轴最大寿命(过纸张数) +#define SANE_STD_OPT_NAME_LANGUAGE "language" // 语言 +#define SANE_STD_OPT_NAME_MOTOR_VER "motor-ver" // 电机固件版本, data = char* +#define SANE_STD_OPT_NAME_TRANSFORM_IMAGE_FORMAT "trans-img-fmt" // 图åƒæ ¼å¼è½¬æ¢, data - SANE_ImageFormatConvert*, dst.data 调用SANE_STD_OPT_NAME_FREE_BUFFER释放 +#define SANE_STD_OPT_NAME_FREE_BUFFER "free-buf" // 释放由驱动返回的内存, data - (void**)&buf +#define SANE_STD_OPT_NAME_PAPER_ON "paper-on" // check whether paper is on +#define SANE_STD_OPT_NAME_INITIAL_BOOT_TIME "initial-boot-time" // 设备的åˆå§‹å¼€æœºæ—¶é—´ +#define SANE_STD_OPT_NAME_DUMP_IMG "dumpimg" // 是å¦è¾“出算法å„é˜¶æ®µä¸­é—´å›¾åƒ +#define SANE_STD_OPT_NAME_DUMP_IMG_PATH "dump-path" // 中间图åƒè¾“出路径 + +// PART II: å‚数类型与åŽé«˜ä¸ä¸€è‡´ï¼Œéœ€è¦é€šè¿‡â€œhgsaneâ€ç»„ä»¶åœ¨ä¸­é—´è½¬æ¢ +#define SANE_STD_OPT_NAME_PAGE_W "page-width" // OPTION_TITLE_ZZCC +#define SANE_STD_OPT_NAME_PAGE_H "page-height" // OPTION_TITLE_ZZCC +#define SANE_STD_OPT_NAME_DUPLEX "duplex" // OPTION_TITLE_SMYM +// END for sane-standard-option-name defined by third-app +// +// +// +// 借鉴TWAINå议固定IDçš„åšæ³•ï¼Œå®šä¹‰SANE属性的固定ID - 这里与TWAIN中的IDä¿æŒä¸€è‡´ï¼Œä»¥æ–¹ä¾¿TWAIN的调用 +enum sane_option_id +{ + SANE_OPT_ID_BASE = 0x8800, + SANE_OPT_ID_IS_MULTI_OUT = 0x8801, + SANE_OPT_ID_MULTI_OUT_TYPE = 0x8802, + SANE_OPT_ID_COLOR_MODE = 0x8803, + SANE_OPT_ID_FILTER = 0x8804, + SANE_OPT_ID_RID_MULTIOUT_RED = 0x8805, + SANE_OPT_ID_RID_ANSWER_SHEET_RED = 0x8806, + SANE_OPT_ID_ERASE_BACKGROUND = 0x8807, + SANE_OPT_ID_BKG_COLOR_RANGE = 0x8808, + SANE_OPT_ID_SHARPEN = 0x8809, + SANE_OPT_ID_RID_MORR = 0x880A, + SANE_OPT_ID_RID_GRID = 0x880B, + SANE_OPT_ID_ERROR_EXTENSION = 0x880C, + SANE_OPT_ID_NOISE_OPTIMIZE = 0x880D, + SANE_OPT_ID_NOISE_SIZE = 0x880E, + SANE_OPT_ID_PAPER = 0x880F, + SANE_OPT_ID_CUSTOM_AREA = 0x8810, + SANE_OPT_ID_CUSTOM_AREA_LEFT = 0x8811, + SANE_OPT_ID_CUSTOM_AREA_RIGHT = 0x8812, + SANE_OPT_ID_CUSTOM_AREA_TOP = 0x8813, + SANE_OPT_ID_CUSTOM_AREA_BOTTOM = 0x8814, + SANE_OPT_ID_SIZE_CHECK = 0x8815, + SANE_OPT_ID_PAGE = 0x8816, + SANE_OPT_ID_DISCARD_BLANK_SENS = 0x8817, + SANE_OPT_ID_RESOLUTION = 0x8818, + SANE_OPT_ID_IMAGE_QUALITY = 0x8819, + SANE_OPT_ID_EXCHANGE = 0x881A, + SANE_OPT_ID_SPLIT = 0x881B, + SANE_OPT_ID_ANTI_SKEW = 0x881C, + SANE_OPT_ID_IS_CUSTOM_GAMMA = 0x881D, + SANE_OPT_ID_BRIGHTNESS = 0x881E, + SANE_OPT_ID_CONTRAST = 0x881F, + SANE_OPT_ID_GAMMA = 0x8820, + SANE_OPT_ID_ERASE_BLACK_FRAME = 0x8821, + SANE_OPT_ID_DARK_SAMPLE = 0x8822, + SANE_OPT_ID_THRESHOLD = 0x8823, + SANE_OPT_ID_ANTI_NOISE_LEVEL = 0x8824, + SANE_OPT_ID_MARGIN = 0x8825, + SANE_OPT_ID_FILL_BKG_MODE = 0x8826, + SANE_OPT_ID_IS_ANTI_PERMEATE = 0x8827, + SANE_OPT_ID_ANTI_PERMEATE_LEVEL = 0x8828, + SANE_OPT_ID_RID_HOLE = 0x8829, + SANE_OPT_ID_SEARCH_HOLE_RANGE = 0x882A, + SANE_OPT_ID_IS_FILL_COLOR = 0x882B, + SANE_OPT_ID_IS_ULTROSONIC_CHECK = 0x882C, + SANE_OPT_ID_IS_CHECK_STAPLE = 0x882D, + SANE_OPT_ID_SCAN_MODE = 0x882E, + SANE_OPT_ID_SCAN_COUNT = 0x882F, + SANE_OPT_ID_TEXT_DIRECTION = 0x8830, + SANE_OPT_ID_IS_ROTATE_BKG_180 = 0x8831, + SANE_OPT_ID_IS_CHECK_DOG_EAR = 0x8832, + SANE_OPT_ID_DOG_EAR_SIZE = 0x8833, + SANE_OPT_ID_IS_CHECK_ASKEW = 0x8834, + SANE_OPT_ID_ASKEW_RANGE = 0x8835, + SANE_OPT_ID_BINARY_THRESHOLD = 0x8836, + SANE_OPT_ID_IS_PHOTO_MODE = 0x8837, + SANE_OPT_ID_DOUBLE_FEED_HANDLE = 0x8838, + SANE_OPT_ID_WAIT_TO_SCAN = 0x8839, + SANE_OPT_ID_FEED_STRENGTH = 0x883A, + SANE_OPT_ID_TIME_TO_SLEEP = 0x883B, + SANE_OPT_ID_IS_AUTO_FEED_STRENGTH = 0x883C, + SANE_OPT_ID_FEED_STRENGTH_VALUE = 0x883D, + SANE_OPT_ID_REVERSE_01 = 0x883E, + SANE_OPT_ID_RID_HOLE_L = 0x883F, + SANE_OPT_ID_SEARCH_HOLE_RANGE_L = 0x8840, + SANE_OPT_ID_RID_HOLE_R = 0x8841, + SANE_OPT_ID_SEARCH_HOLE_RANGE_R = 0x8842, + SANE_OPT_ID_RID_HOLE_T = 0x8843, + SANE_OPT_ID_SEARCH_HOLE_RANGE_T = 0x8844, + SANE_OPT_ID_RID_HOLE_B = 0x8845, + SANE_OPT_ID_SEARCH_HOLE_RANGE_B = 0x8846, + SANE_OPT_ID_FOLD_TYPE = 0x8847, + SANE_OPT_ID_COLOR_CORRECTION = 0x8848, + SANE_OPT_ID_HISTORY_COUNT = 0x8849, + SANE_OPT_ID_DRIVER_VERSION = 0x884A, + SANE_OPT_ID_MANUFACTURER = 0x884B, + SANE_OPT_ID_COPYRIGHT = 0x884C, + SANE_OPT_ID_CO_URL = 0x884D, + SANE_OPT_ID_CO_TEL = 0x884E, + SANE_OPT_ID_CO_ADDR = 0x884F, + SANE_OPT_ID_CO_GPS = 0x8850, + SANE_OPT_ID_HELP = 0x8851, + SANE_OPT_ID_VID = 0x8852, + SANE_OPT_ID_PID = 0x8853, + SANE_OPT_ID_DEV_NAME = 0x8854, + SANE_OPT_ID_DEV_FAMILY = 0x8855, + SANE_OPT_ID_DEVICE_SERIAL_NO = 0x8856, + SANE_OPT_ID_FIRMWARE_VERSION = 0x8857, + SANE_OPT_ID_DEVICE_IP_ADDR = 0x8858, + SANE_OPT_ID_DEVICE_MAC_ADDR = 0x8859, + SANE_OPT_ID_CUSTOM_GAMMA = 0x885A, // data: SANE_Gamma* + SANE_OPT_ID_ROLLER_LIFE = 0x885B, // data: uint32_t* + SANE_OPT_ID_LANGUAGE = 0x885C, // data: char* + SANE_OPT_ID_MOTOR_VER = 0x885D, // 电机固件版本, data - char* + SANE_OPT_ID_TRANSFORM_IMAGE_FORMAT = 0x885E,// 图åƒæ ¼å¼è½¬æ¢, data - SANE_ImageFormatConvert*, dst.data 调用SANE_STD_OPT_NAME_FREE_BUFFER释放 + SANE_OPT_ID_FREE_BUFFER = 0x885F, // 释放由驱动返回的内存, data - (void**)&buf + SANE_OPT_ID_PAPER_ON = 0x8860, // 是å¦æœ‰çº¸å¼ , data - SANE_Bool* + + SANE_OPT_ID_GRAY_GAMMA = 0x8861, // gray-gamma-table + SANE_OPT_ID_COLOR_GAMMA = 0x8862, // color-gamma-table + SANE_OPT_ID_RED_GAMMA = 0x8863, // red-gamma-table + SANE_OPT_ID_GREEN_GAMMA = 0x8864, // green-gamma-table + SANE_OPT_ID_BLUE_GAMMA = 0x8865, // blue-gamma-table + SANE_OPT_ID_INITIAL_BOOT_TIME = 0x8866, + SANE_OPT_ID_DISCARDBLANK = 0x8867, + SANE_OPT_ID_WAIT_SCAN_EXIT = 0x8868, + SANE_OPT_ID_RESTORE = 0x8869, + SANE_OPT_ID_PAPER_W = 0x886A, // 纸张宽度,å•ä½ä¸ºmm + SANE_OPT_ID_PAPER_H = 0x886B, // 纸张高度,å•ä½ä¸ºmm + SANE_OPT_ID_LATERAL = 0x886C, // 纸张横å‘标志,bool + + SANE_OPT_ID_LOGIN = 0x9900, + SANE_OPT_ID_LOGOUT = 0x9901, + SANE_OPT_ID_ROLLER_COUNT = 0x9902, + SANE_OPT_ID_DRIVER_LOG = 0x9903, + SANE_OPT_ID_DEVICE_LOG = 0x9904, +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "sane.h" +#include + +#define MAX_STRING_LEN 256 +#define IS_DOUBLE_EQUAL(a, b) fabs((a) - (b)) < .000001 +#define IS_PTR_NUMBER(ptr) (((unsigned long long)(ptr)) < 0x10000) + +#define CAPABILITY_ORDINARY (SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT) +#define CAPABILITY_READONLY SANE_CAP_SOFT_DETECT + +// è®¾ç½®æ´»åŠ¨çŠ¶æ€ +#define SET_CAP_ACTIVE(cap, act) \ + { \ + if(act) \ + cap &= ~SANE_CAP_INACTIVE; \ + else \ + cap |= SANE_CAP_INACTIVE; \ + } + +// 设置为åªè¯»å±žæ€§ +#define SET_CAP_READONLY(cap) \ + { \ + cap &= ~(CAPABILITY_ORDINARY | SANE_CAP_HARD_SELECT); \ + cap |= CAPABILITY_READONLY; \ + } +#define IS_CAP_READONLY(cap) (((cap) & (CAPABILITY_ORDINARY | SANE_CAP_HARD_SELECT)) == CAPABILITY_READONLY) + +// 设置为软件å¯è®¾ç½®/è¯»å– +#define SET_CAP_SOFT_SETTABLE(cap) \ + { \ + cap &= ~(CAPABILITY_ORDINARY | SANE_CAP_HARD_SELECT); \ + cap |= SANE_CAP_HARD_SELECT; \ + } + +// 设置为由设备上的按键æ“作属性 +#define SET_CAP_DEVICE_SETTABLE(cap, can_read) \ + { \ + cap &= ~(CAPABILITY_ORDINARY | SANE_CAP_HARD_SELECT); \ + cap |= can_read ? SANE_CAP_SOFT_DETECT | CAPABILITY_ORDINARY : CAPABILITY_ORDINARY;\ + } + +#ifdef __cplusplus +extern "C" { +#endif + +#pragma pack(push) +#pragma pack(1) +#define ZERO_STRUCT(struct_ptr) memset(struct_ptr, 0, sizeof(*(struct_ptr))) + + //////////////////////////////////////////////////////////////////////////////// + // extension for standard SANE ... + enum SANE_Action_Ex // extension for SANE_Action + { + SANE_ACTION_GET_DEFAULT_VALUE = 100, // 获å–设置项默认值,å‚æ•°åŒSANE_ACTION_GET_VALUE + SANE_ACTION_GET_FIX_ID, // 获å–属性的固定ID,void* = SANE_Int* + SANE_ACTION_GET_ENTIRE_JSON, // 获å–该属性JSON文本 + + // Function: 枚举对SANEåè®®ä¸å¯è§çš„固定ID属性(以使TWAINå¯è®¿é—®ï¼‰ + // + // Parameter: void* = FIXDCB* + SANE_ACTION_ENUM_INVISIBLE_FIX_ID = 0x0FEC7, + }; + + enum SANE_Frame_Ex // after SANE_Frame + { + SANE_FRAME_RAW = 5, + SANE_FRAME_MIME, // MIME descriptor + }; + + // SANE_ACTION_ENUM_INVISIBLE_FIX_ID + struct _fix_id_cb + { + SANE_Bool (*cb)(int, void*); + void* param; + }; + + typedef struct _sane_stream + { + SANE_Int bytes; + SANE_Byte *data; + }SANE_Stream; + + typedef struct _sane_dev_ex + { + SANE_String_Const name; /* unique device name */ + SANE_String_Const vendor; /* device vendor string */ + SANE_String_Const model; /* device model name */ + SANE_String_Const family; /* device type (e.g., "flatbed scanner") */ + SANE_Bool openned; // whether openned + }SANE_Device_Ex; + typedef struct _sane_auth // for SANE_EVENT_NEED_AUTH + { + SANE_String_Const resource; + SANE_Char name[MAX_STRING_LEN]; + SANE_Char pwd[MAX_STRING_LEN]; + SANE_Char method[MAX_STRING_LEN]; + }SANEAUTH; + + typedef enum + { + SANE_TYPE_EX_BOOL = 0, + SANE_TYPE_EX_INT, + SANE_TYPE_EX_FIXED, // same as SANE_TYPE_FIXED + SANE_TYPE_EX_STRING, // sane as SANE_TYPE_STRING + SANE_TYPE_EX_POS, // position in origin image, SANE_Rect* + SANE_TYPE_EX_IMAGE, // SANE_Image*, src_id is meanless + }SANE_Value_Ext_Type; + typedef struct + { + SANE_Int left; + SANE_Int top; + SANE_Int right; + SANE_Int bottom; + }SANE_Rect; + typedef struct + { + SANE_Int src_id; // unique(in this running) image ID from SANE + SANE_Int count; // length of array 'info' + struct + { + SANE_Int ext_inf_id; // Extended information ID + SANE_Value_Ext_Type type; // value type + SANE_Int inf_bytes; // length of information + union + { + SANE_Bool bval; // bool + SANE_Int ival; // integer + SANE_Fixed fval; // float value + SANE_Char *pval; // string, byte-stream or other structure + }; + }info[1]; + }SANE_Img_Ext_Info; // added on 2023-01-13 + + typedef struct + { + SANE_Int src_id; // unique(in this running) image ID from SANE + SANE_Parameters header; + struct + { + unsigned int statu : 4; // SANE_Image_Statu + unsigned int dpi : 12; // resolution, dots per inch + unsigned int reserve : 16; + }flag; + unsigned long bytes; + unsigned char *data; + }SANE_Image; + typedef struct _about_info + { + SANE_String_Const title; // APPå称 + SANE_String_Const version; // ç‰ˆæœ¬å· + SANE_String_Const copyright; // 版æƒä¿¡æ¯ + unsigned int logo_bytes; // LOGO æ•°æ®é•¿åº¦ + void* logo; // LOGO æ•°æ® + struct + { + SANE_String_Const key; // 附加信æ¯å称,该数æ®ä¸ºNULL时,appendixæ•°ç»„ç»“æŸ + SANE_String_Const content;// 附加信æ¯å†…容 + SANE_String_Const url; // 附加信æ¯é“¾æŽ¥ï¼Œ NULL则忽略 + }appendix[1]; + }SANE_About; + + typedef enum + { + SANE_COMPRESSION_FIRST = 0, + SANE_COMPRESSION_NONE = 0, // default value + //SANE_COMPRESSION_PACBITS, + //SANE_COMPRESSION_GROUP31D, + //SANE_COMPRESSION_GROUP31DEOL, + //SANE_COMPRESSION_GROUP32D, + SANE_COMPRESSION_GROUP4 = 5, // support now ! detail is threshold converting color to BlackWhite, e.g. (void*)128 + //SANE_COMPRESSION_JPEG, + //SANE_COMPRESSION_LZW, + //SANE_COMPRESSION_JBIG, + //SANE_COMPRESSION_PNG, + //SANE_COMPRESSION_RLE4, + //SANE_COMPRESSION_RLE8, + //SANE_COMPRESSION_BITFIELDS, + //SANE_COMPRESSION_ZIZ, + //SANE_COMPRESSION_JPEG2000, + + SANE_COMPRESSION_LAST, + SANE_COMPRESSION_DONTCARE = 0xFFFF, + }SANE_CompressionType; + typedef struct _img_compression + { + SANE_CompressionType compression; + void* detail; // see SANE_CompressionType (该å‚数在当å‰ç‰ˆæœ¬ä¸è€ƒè™‘,暂使用压缩类型的默认值) + }SANE_Compression; + typedef enum // 与Twpp::ImageFileFormat ä¿æŒä¸€è‡´ + { + SANE_IMAGE_TYPE_FIRST = 0, + SANE_IMAGE_TYPE_TIFF = 0, + SANE_IMAGE_TYPE_BMP = 2, // (BITMAPINFOHEADER*)detail + SANE_IMAGE_TYPE_PNG = 7, + SANE_IMAGE_TYPE_JPG = 13, + SANE_IMAGE_TYPE_JFIF = 4, + SANE_IMAGE_TYPE_PDF = 10, + + // 以下为Twpp::ImageFileFormatä¸å­˜åœ¨çš„值 + SANE_IMAGE_TYPE_GIF = 100, + SANE_IMAGE_TYPE_WEBP, + SANE_IMAGE_TYPE_SVG, + SANE_IMAGE_TYPE_LAST, + }SANE_ImageType; + typedef struct _img_final_fmt + { + SANE_ImageType img_format; + void* detail; // see SANE_ImageType (该å‚数在当å‰ç‰ˆæœ¬ä¸è€ƒè™‘,暂使用该图åƒç±»åž‹çš„默认值) + SANE_Compression compress; + }SANE_FinalImgFormat; + typedef struct _img_format_convert + { + struct + { + SANE_FinalImgFormat fmt; // format of the data + SANE_Bool is_file; // data is a 'path-file' if it was true, or else is raw file data + SANE_String_Const data; // represents a local file or a memory data. call 'IO_CTRL_CODE_FREE_MEMORY' to free the dst.data memory if 'is_file' was true !!! + unsigned int data_len; // bytes of 'data' + }src, dst; + }SANE_ImageFormatConvert; + + typedef struct _sane_point + { + SANE_Byte x; + SANE_Byte y; + }SANE_Gamma_Point; + enum gamma_index + { + GAMMA_INDEX_GRAY = 0, + GAMMA_INDEX_COLOR, + GAMMA_INDEX_RED, + GAMMA_INDEX_GREEN, + GAMMA_INDEX_BLUE, + }; + typedef struct _user_gamma // NOTE: exceeds 4KB !!! + { + SANE_Int apply_to_back : 8; // SANE_TRUE: 应用到背é¢; SANE_FALSE: åº”ç”¨åˆ°æ­£é¢ + SANE_Int app_data : 24; // APP è‡ªå®šä¹‰æ•°æ® + SANE_Byte count[8]; // 控制点个数,顺åºä¸º(gamma_index): Gray + Color + Red + Green + Blue + SANE_Byte pt_gray[4]; // ç°åº¦æŽ§åˆ¶ç‚¹ - åªè®°å½•æ¨ªå标,纵å标请在table表中查得 + SANE_Byte pt_color[4]; // 彩色控制点 - åªè®°å½•æ¨ªå标,纵å标请在table表中查得 + SANE_Byte pt_red[4]; // 红色控制点 - åªè®°å½•æ¨ªå标,纵å标请在table表中查得 + SANE_Byte pt_green[4]; // 绿色控制点 - åªè®°å½•æ¨ªå标,纵å标请在table表中查得 + SANE_Byte pt_blue[4]; // è“色控制点 - åªè®°å½•æ¨ªå标,纵å标请在table表中查得 + SANE_Byte table[5 * 256]; // gammaå˜æ¢é˜µåˆ—,顺åºä¸º(gamma_index):Gray[256] + Color[256] + R[256] + G[256] + B[256] + }SANE_Gamma; + typedef struct _user_gamma_ex // from draft-2.0 + { + SANE_Int size : 16; // size of this structure + SANE_Int ver : 8; // version + SANE_Int count : 8; // 控制点(point数组中有效元素)个数 + SANE_Int side : 4; // 0 - 应用到正åé¢ï¼›1 - 应用到正é¢ï¼›2 - 应用到åé¢ + SANE_Int channel : 4; // 0 - ç°åº¦æ›²çº¿ï¼›1 - 彩色曲线;2 - Red通é“曲线;3 - Green通é“曲线;4 - Blue通é“曲线 + SANE_Int app_data : 24; // APP è‡ªå®šä¹‰æ•°æ® + SANE_Byte point[4]; // 控制点 - åªè®°å½•æ¨ªå标,纵å标请在table表中查得 + SANE_Byte table[256]; // å˜æ¢è¡¨ + }SANE_Gamma_Ex; + typedef struct CISTestResult + { + double w; //圆横轴 + double h; //圆纵轴 + double scaleXY; //圆3横纵比 + + SANE_Byte colorBlock1[3]; //彩色色å—1RGB //余下ä¿ç•™ + SANE_Byte colorBlock2[3]; //彩色色å—2RGB + SANE_Byte colorBlock3[3]; //彩色色å—3RGB + SANE_Byte colorBlock4[3]; //彩色色å—4RGB + SANE_Byte colorBlock5[3]; //彩色色å—5RGB + SANE_Byte colorBlock6[3]; //彩色色å—6RGB + SANE_Byte colorBlock7[3]; //彩色色å—7RGB + SANE_Byte colorBlock8[3]; //彩色色å—8RGB + SANE_Byte colorBlock9[3]; //彩色色å—9RGB + + SANE_Byte grayBlock1; //ç°åº¦è‰²å—1ç°åº¦å€¼ + SANE_Byte grayBlock2; //ç°åº¦è‰²å—2ç°åº¦å€¼ + SANE_Byte grayBlock3; //ç°åº¦è‰²å—3ç°åº¦å€¼ + SANE_Byte grayBlock4; //ç°åº¦è‰²å—4ç°åº¦å€¼ + SANE_Byte grayBlock5; //ç°åº¦è‰²å—5ç°åº¦å€¼ + SANE_Byte grayBlock6; //ç°åº¦è‰²å—6ç°åº¦å€¼ + SANE_Byte grayBlock7; //ç°åº¦è‰²å—7ç°åº¦å€¼ + SANE_Byte grayBlock8; //ç°åº¦è‰²å—8ç°åº¦å€¼ + SANE_Byte grayBlock9; //ç°åº¦è‰²å—9ç°åº¦å€¼ + }SANE_DISTORTION_VAL;//获å–畸å˜ä¿®æ­£å€¼ + typedef enum + { + SANE_FUNCTION_PARAMETER_TYPE_STRING = 0, // SANE_String_List + SANE_FUNCTION_PARAMETER_TYPE_STREAM, // SANE_Stream + SANE_FUNCTION_PARAMETER_TYPE_USER_INPUT, // SANE_String + }SANE_Function_Parameter_Type; + typedef struct _sane_function_parameter + { + SANE_Function_Parameter_Type type; + }SANE_Function_Parameter; + typedef struct _sane_function + { + SANE_Int func_id; + SANE_Int parameter_count; + SANE_Function_Parameter parameter[1]; + }SANE_Function; + typedef struct _sane_img_kits + { + SANE_Int kit_id; + SANE_String name; + SANE_String title; + SANE_String desc; + SANE_Stream icon; + SANE_Int function_count; + SANE_Function function[1]; + }SANE_Image_Kits; + + typedef enum + { + AUTO_COLOR_BLACKWHITE = 0, + AUTO_COLOR_GRAY, + }SANE_AutoColorType; + typedef enum + { + LOG_FILE_DEVICE = 0, // 设备端日志文件 + LOG_FILE_DRIVER, // 驱动层日志文件 + }SANE_LogFileType; + + typedef enum // IO_CTRL_CODE_SET_POWER_LEVEL + { + SANE_POWER_FIRST = 0, + SANE_POWER_NONE = SANE_POWER_FIRST, + SANE_POWER_MINUTES_5, + SANE_POWER_MINUTES_10, + SANE_POWER_MINUTES_20, + SANE_POWER_MINUTES_30, + SANE_POWER_MINUTES_60, + SANE_POWER_MINUTES_120, + SANE_POWER_MINUTES_240, + SANE_POWER_LAST, + SANE_POWER_SHUTDOWN, // 关闭设备 239 设备é‡å¯å’Œå…³é—­è®¾å¤‡ä¸ä¼šè§¦å‘热拔æ’,所以使用此命令时需è¦æ‰‹åŠ¨å…³é—­è®¾å¤‡å’Œæ‰“开设备 1:rebootloader ,0:reboot + SANE_POWER_RESTART, // é‡å¯è®¾å¤‡ + }SANE_Power; + + typedef enum + { + SANE_EVENT_NONE = 0, // æ— æ„义的回调, both data & len are NULL + SANE_EVENT_SUPPORT_ASYNC_IO, // 是å¦æ”¯æŒå¼‚æ­¥IO,返回“0â€æ”¯æŒï¼Œå…¶å®ƒä¸æ”¯æŒã€‚data and len unused + SANE_EVENT_IS_MEMORY_ENOUGH, // 当å‰å†…存是å¦è¶³å¤Ÿè¯»å–图片, data - unusedï¼›*len - needed size. return SCANNER_ERR_OK or SCANNER_ERR_INSUFFICIENT_MEM + SANE_EVENT_NEED_AUTH, // data - (SANEAUTH*); len - unused; return none-zero to giveup + SANE_EVENT_DEVICE_ARRIVED, // data - SANE_Device_Ex*; *len - sizeof(SANE_Device_Ex) + SANE_EVENT_DEVICE_LEFT, // data - SANE_Device*; *len - sizeof(SANE_Device) + SANE_EVENT_STATUS, // normal status description. data - (utf8*), len - unused, be NULL + SANE_EVENT_ERROR, // error happens, should stop operations. data - (utf8*)description, *len - error code + SANE_EVENT_WORKING, // 扫æ仪正在扫æ, data - (char*)description, len - unused + SANE_EVENT_USB_DATA_RECEIVED, // 从USB读å–到一个图片数æ®åŒ…,当å‰åªç”¨ä½œçº¸å¼ ç»Ÿè®¡ã€‚data - NULL, *len - bytes + SANE_EVENT_IMAGE_OK, // new image data is ready for UI. data - (SANE_Image*), *len - index of the image (ZERO base) + SANE_EVENT_IMAGE_EXT_INFO_OK, // new image data is ready for UI. data - (SANE_Img_Ext_Info*), *len - index of the image (ZERO base) + SANE_EVENT_SCAN_FINISHED, // 扫æ仪完æˆæ‰«æ, data - (char*)description, *len - error code + SANE_EVENT_ABOUT_INFORMATION, // APP关于信æ¯, data - (SANE_About*), len - unused, be NULL + SANE_EVENT_SCANNER_CLOSED, // 扫æ仪已ç»å…³é—­ï¼Œdata & len åŒ SANE_EVENT_STATUS + SANE_EVENT_IMG_UPLOADED, // APPå–走一张图片, data - unused, len - count + // SANE_EVENT_WIN_DEBUG_INFO, // writedown debug info on windows platform ... data - (utf8*), *len - HG_LOG_LEVEL, param - NULL !!! + + // ui event ... + SANE_EVENT_UI_CLOSE_CANCEL = 0x1000, + SANE_EVENT_UI_CLOSE_NORMAL, + SANE_EVENT_UI_SCAN_COMMAND, // setting ui notify button 'scan' clicked + SANE_EVENT_UI_CLOSE_SETTING, + SANE_EVENT_TWAIN_XFER_READY, + }SANE_Event; + +#pragma pack(pop) + + // 'start' and 'stop' will be blocked until image arrived or worker-threads stopped - 2023-11-01 + // callback may return before these functions !!! + // the calling program should pay more attention to this situation !!! + typedef int(*sane_callback)( // 注册回调的对象,需è¦ä¿è¯è¯¥å›žè°ƒæ˜¯å¤šçº¿ç¨‹å®‰å…¨çš„ + SANE_Handle hdev // 产生事件的设备å¥æŸ„ + , int code // å›žè°ƒäº‹ä»¶ä»£ç  + , void* data // 回调事件数æ®ï¼Œæ ¹æ®äº‹ä»¶ä»£ç æœ‰æ‰€ä¸åŒï¼Œå‚照具体事件定义 + , unsigned int* len // æ•°æ®é•¿åº¦ï¼ˆå­—节),或者event_dataçš„ç¼“å†²åŒºé•¿åº¦ï¼Œè¯¦ç»†è¯·çœ‹ç›¸åº”çš„äº‹ä»¶ä»£ç  + , void* param // 用户自定义数æ®ï¼Œä¸Žè°ƒç”¨sane_init_ex传入时的ä¿æŒä¸€è‡´ + ); // 返回值ä¾ä¸åŒçš„事件代ç è€Œå®šï¼Œé€šå¸¸ä¸ºâ€œ0†+ extern SANE_Status sane_init_ex(SANE_Int* version_code, sane_callback cb, void* param); + + enum io_code + { + IO_CTRL_CODE_BASE = 0x00C0DE111, + IO_CTRL_CODE_TEST_SINGLE = IO_CTRL_CODE_BASE,// å•å¼ æµ‹è¯•æ‰«æ data - NULL, len - NULL + IO_CTRL_CODE_ABOUT_INFO, // 获å–软件关于信æ¯ã€‚data - (SANE_About*), 如果data为空或者由len指示的内存大å°ä¸è¶³ï¼Œ + // 则会在len中返回所需è¦çš„最å°å†…存长度,并返回 SANE_STATUS_NO_MEM 错误 + IO_CTRL_CODE_RESTORE_SETTINGS, // æ¢å¤é»˜è®¤è®¾ç½® data - NULL, len - NULL + IO_CTRL_CODE_GET_DEFAULT_VALUE, // 获å–设置项默认值 data - åŒsane_control_option - SANE_ACTION_GET_VALUE时的定义, *len - [in] 设置项åºå·ï¼ŒåŒsane_control_option中option的值 + IO_CTRL_CODE_GET_FINAL_IMAGE_FORMAT, // 获å–图åƒå¤„ç†æœ€ç»ˆè¾“出(final())的图åƒæ•°æ®æ ¼å¼ data - (SANE_FinalImgFormat*), len - bytes of data + IO_CTRL_CODE_SET_FINAL_IMAGE_FORMAT, // 设置图åƒå¤„ç†æœ€ç»ˆè¾“出(final())的图åƒæ•°æ®æ ¼å¼ data - (SANE_FinalImgFormat*), len - bytes of data + IO_CTRL_CODE_GET_FINAL_COMPRESSION, // 获å–支æŒçš„压缩格å¼ï¼Œdata - (SANE_Int*), to receive supported SANE_CompressionType, data[0]: supported counts, data[1]: default value, data[2]: current value; data[3...]: all supported values + // *len - array length of data, or need length if it was too small + IO_CTRL_CODE_SET_FINAL_COMPRESSION, // 设置图åƒæ•°æ®æœ€ç»ˆè¾“出的压缩格å¼ï¼Œdata - (SANE_Compression*), len - bytes of data + + IO_CTRL_CODE_SET_AUTO_COLOR_TYPE, // 设置自动匹é…颜色模å¼, data - (SANE_AutoColorType*), *len - sizeof(data) + IO_CTRL_CODE_SET_CLEAR_ROLLER_COUNT, // 清除滚轴计数 data - NULL, len - to receive current roller count, can be NULL + IO_CTRL_CODE_SET_CLEAR_HISTORY_COUNT, // 清除历å²è®¡æ•° data - NULL, len - to receive current history count, can be NULL + + IO_CTRL_CODE_GET_DEVICE_CODE, // 获å–设备编ç ï¼Œ data - (char*), *len - bytes of data + + + + IO_CTRL_CODE_GET_PAPER_ON, // 获å–进纸盘上是å¦æœ‰çº¸ï¼Œ data - (SANE_Bool*), *len - sizeof(SANE_Bool) data:false 无纸 true有纸 + IO_CTRL_CODE_SET_POWER_LEVEL, // 设置功耗模å¼ï¼ˆä¼‘眠), data - (SANE_Power*), *len - sizeof(SANE_Power) (包括é‡å¯è®¾å¤‡ 关闭断电) + IO_CTRL_CODE_GET_POWER_LEVEL, // 获å–功耗模å¼ï¼ˆä¼‘眠), data - (SANE_Power*), *len - sizeof(SANE_Power) + + + IO_CTRL_CODE_GET_CUSTOM_GAMMA, // 用于获å–用户自定义gamma数值,data - SANE_Gamma*, *len - to receive current color mode + IO_CTRL_CODE_SET_CUSTOM_GAMMA, // 用于设置用户自定义gamma数值,data - SANE_Gamma*, len - unused + IO_CTRL_CODE_DISPLAY_APP_HELP, // 显示APP帮助文档 data - NULL, len - NULL + IO_CTRL_CODE_GET_PAPER_SIZE, // 获å–纸张尺寸(mm)。 data - (utf8*)paper name, *len - MAKELPARAM(w, h) + IO_CTRL_CODE_GET_IMAGE_QUEUE_COUNT, // 获å–图åƒé˜Ÿåˆ—的长度/æ•°é‡ï¼Œ data - NULL, len - to receive the count + IO_CTRL_CODE_CONVERT_IMAGE_FORMAT, // 图åƒæ ¼å¼è½¬æ¢ï¼ˆæ–‡ä»¶æ–¹å¼ï¼‰, data - SANE_ImageFormatConvert*, len - unused, be NULL + IO_CTRL_CODE_FREE_MEMORY, // 释放由该模å—分é…的内存, data - 内存å—,*len - 内存å—长度 + + IO_CTRL_CODE_GET_LOG_FILE, // 获å–日志文件,data - char[260],用于接收日志文件路径,返回空则获å–失败;*len - 日志文件类型 SANE_LogFileType. + // 返回的日志文件,如果ä¸å†ä½¿ç”¨ï¼Œè°ƒç”¨è€…负责删除。 + IO_CTRL_CODE_GET_SCAN_ISLOCK, // 获å–设备是å¦æ”¯æŒé”定设备功能,data - (SANE_Bool*), *len - sizeof(SANE_Bool) + IO_CTRL_CODE_SET_SCAN_LOCK, // 设置设备é”定, data - (SANE_Bool), len - unused + IO_CTRL_CODE_SET_SCAN_LOCK_CHECK_VAL, // è®¾ç½®æ ¡éªŒç  ï¼Œ data - (char*), *len - bytes of data + IO_CTRL_CODE_SET_FIRMWARE_UPGRADE, // 设置固件å‡çº§ï¼Œ data - (char*), *len - bytes of data + IO_CTRL_CODE_GET_HISTORY_SCAN_NUM, // 获å–历å²å¼ æ•° data - (SANE_Int*), *len - sizeof(SANE_Int) + IO_CTRL_CODE_GET_CLEAN_PAPER_ROAD, // æ¸…é™¤çº¸é“ data - (SANE_Bool*), *len - sizeof(SANE_Bool) + IO_CTRL_CODE_GET_ROLLER_NUM, // 获å–滚轴张数 data - (SANE_Int*), *len - sizeof(SANE_Int) + IO_CTRL_CODE_SET_FEEDMODE, // 设置分纸强度 data - (SANE_Int*) ,*len - sizeof(SANE_Int) data: 0 ,1 ,2) + IO_CTRL_CODE_SET_SKEW_CHECK, // 设置歪斜检测 data - (SANE_Bool*), *len - sizeof(SANE_Bool) + IO_CTRL_CODE_SET_SKEW_CHECK_VAL, // 设置歪斜检测值 data - (SANE_Int*), *len - sizeof(SANE_Int) + IO_CTRL_CODE_GET_IS_MODE, // 获å–是å¦è®¡æ•°æ¨¡å¼ data - (SANE_Bool*), *len - sizeof(SANE_Bool) + IO_CTRL_CODE_SET_ULTRASONIC_MODULE, // 设置超声波检测 data - (SANE_Bool*), *len - sizeof(SANE_Bool) + IO_CTRL_CODE_SET_SPEED_MODE, // è®¾ç½®é€Ÿåº¦æ¨¡å¼ data - (SANE_Int*) ,*len - sizeof(SANE_Int) data = G100-G200:100,110,120 G300:40,50,60,70 G400:40,50,60,70,80 + IO_CTRL_CODE_GET_SPEED_MODE, // 获å–é€Ÿåº¦æ¨¡å¼ data - (SANE_Int*) ,*len - sizeof(SANE_Int) + IO_CTRL_CODE_SET_CIS_IMAGE, // 获å–CIS原图 data - (SANE_Bool*), *len - sizeof(SANE_Bool) + IO_CTRL_CODE_GET_IMAGE, // 批é‡æ‰«æ data - NULL,*len-NULL + + IO_CTRL_CODE_SET_DISTORTION_IMAGE, // 设置畸å˜æ ¡æ­£å›¾ data - NULL,*len-NULL + IO_CTRL_CODE_GET_PC_DISTORTION_CHECK_VAL, // 获å–PC计算畸å˜æ ¡æ­£å€¼ data - (SANE_DISTORTION_VAL*) ,*len - sizeof(SANE_DISTORTION_VAL) //通过PC算法计算获å–的畸å˜å€¼ + IO_CTRL_CODE_SET_DISTORTION_DEVS_CHECK_VAL, // 设置设备畸å˜å€¼ data - (float*) ,*len - sizeof(float) + IO_CTRL_CODE_GET_DISTORTION_DEVS_CHECK_VAL, // 获å–设备畸å˜å€¼ data - (float*) ,*len - sizeof(float) + + IO_CTRL_CODE_SET_DEVS_REBOOT, // 设置设备é‡å¯ data - (SANE_Int*) ,*len - sizeof(SANE_Int) data 1:loader,data 0:普通é‡å¯ + IO_CTRL_CODE_SET_AUTO_FALT, // 设置自动平场校正 data - (int *),*len - sizeof(SANE_Int) + // data:0(ALL) 1(200dpiã€gray) 2(200dpiã€color) 3(300dpiã€gray) 4(300dpiã€color) 5(600dpiã€gray) 6(600dpiã€color) + + IO_CTRL_CODE_SET_COLOR, // 设置颜色 data - (char*), *len - bytes of data + IO_CTRL_CODE_SET_DPI, // 设置DPI data - (int *) *len - sizeof(int) + + + ///////////////////////固定的硬件信æ¯////////////// + IO_CTRL_CODE_SET_VIDPID, // 设置VID PID, data - (int *) *len - sizeof(int) + IO_CTRL_CODE_GET_VIDPID, // 获å–VID PID, data - (int *) *len - sizeof(int) + IO_CTRL_CODE_SET_SERIAL, // 设置设备åºåˆ—å· data - (char*), *len - bytes of data + IO_CTRL_CODE_GET_SERIAL, // 获å–设备åºåˆ—å·, data - (char*), *len - bytes of data + IO_CTRL_CODE_GET_HARDWARE_VERSION, // 获å–硬件版本å·ï¼Œ data - (char*), *len - bytes of data + IO_CTRL_CODE_GET_IP, // 获å–设备IP, data - (char*), *len - bytes of data + IO_CTRL_CODE_SET_DEVS_MODEL, // 获å–设备型å·ï¼Œ data - (char*), *len - bytes of data + IO_CTRL_CODE_GET_DEVS_MODEL, // 设置设备型å·ï¼Œ data - (char*), *len - bytes of data + + ///////////////////////未使用//////////////æš‚æ—¶ä¿ç•™ + IO_CTRL_CODE_GET_SCAN_WHEN_PAPER_ON, //获å–是å¦å¸¦çº¸æ‰«æ + IO_CTRL_CODE_SET_SCAN_WHEN_PAPER_ON, //设置带纸扫æ + IO_CTRL_CODE_GET_SCAN_WITH_HOLE, //获å–是å¦ç©¿å­”移除 + IO_CTRL_CODE_SET_SCAN_WITH_HOLE, //设置穿孔移除 + // all control code must be less than the following value + IO_CTRL_CODE_LAST = 0x10000000, + }; + + // Function: 直接访问控制 + // + // Parameter: h - hg_open_scanner打开的设备å¥æŸ„ + // + // code - æŽ§åˆ¶ç  + // + // data - 用户分é…的内存,对应于控制ç çš„åŽŸå§‹æ•°æ® + // + // len - data中数æ®é•¿åº¦ã€‚如果是获å–æ“作时,长度å°äºŽæ‰€éœ€è¦çš„长度,则返回需è¦çš„长度且返回 E_INSUFFICIENTMEM 的错误 + // + // Return: é”™è¯¯ç  + extern SANE_Status sane_io_control(SANE_Handle h, unsigned long code, void* data, unsigned* len); + + // Function: 获å–错误æè¿° + // + // Parameters: err - 其它SANEå‡½æ•°è¿”å›žçš„é”™è¯¯ä»£ç  + // + // Return: 错误æè¿°ä¿¡æ¯(UTF-8),调用者应立å³ä½¿ç”¨ï¼Œæ— é¡»é‡Šæ”¾ + extern const char* sane_err_desc(SANE_Status err); + + // Function: 读å–刚刚(调用sane_get_parametersåŽ)获å–çš„å›¾ç‰‡çš„æ‰©å±•ä¿¡æ¯ + // + // Parameter: ext_info - 用户分é…的用户获å–扩展信æ¯çš„å†…å­˜åœ°å€ + // + // len - [in]: ext_info 空间大å°ï¼› [out] - ext_info中实际存储的字节数,或者空间ä¸è¶³æ—¶éœ€è¦çš„最å°å­—节数 + // + // Return: 错误ç ï¼šSANE_STATUS_GOOD - æˆåŠŸèŽ·å–到图åƒæ‰©å±•ä¿¡æ¯ + // SANE_STATUS_NO_DOCS - è¯¥å›¾ç‰‡æ²¡æœ‰æ‰©å±•ä¿¡æ¯ + // SANE_STATUS_NO_MEM - 空间ä¸è¶³ï¼Œæœ€å°ç©ºé—´å·²ç»å­˜å‚¨åœ¨å‚æ•°len中 + // SANE_STATUS_UNSUPPORTED - 当å‰ç‰ˆæœ¬ä¸æ”¯æŒè¯¥é¡¹æ“作 + extern SANE_Status sane_read_ext_info(SANE_Img_Ext_Info* ext_info, SANE_Int* len); + + // for ui interface + typedef struct _sane_api + { + SANE_Status (*sane_get_devices_api)(const SANE_Device*** device_list, SANE_Bool local_only); + SANE_Status (*sane_open_api)(SANE_String_Const devicename, SANE_Handle* handle); + void (* sane_close_api)(SANE_Handle handle); + const SANE_Option_Descriptor* (*sane_get_option_descriptor_api)(SANE_Handle handle, SANE_Int option); + SANE_Status (*sane_control_option_api)(SANE_Handle handle, SANE_Int option, SANE_Action action, void* value, SANE_Int* info); + SANE_Status (*sane_get_parameters_api)(SANE_Handle handle, SANE_Parameters* params); + SANE_Status (*sane_start_api)(SANE_Handle handle); + SANE_Status (*sane_read_api)(SANE_Handle handle, SANE_Byte* data, SANE_Int max_length, SANE_Int* length); + void (*sane_cancel_api)(SANE_Handle handle); + SANE_Status (*sane_set_io_mode_api)(SANE_Handle handle, SANE_Bool non_blocking); + SANE_Status (*sane_get_select_fd_api)(SANE_Handle handle, SANE_Int* fd); + SANE_String_Const (*sane_strstatus_api)(SANE_Status status); + SANE_Status (*sane_io_control_api)(SANE_Handle h, unsigned long code, void* data, unsigned* len); + SANE_Status (*sane_init_api)(SANE_Int* version_code, SANE_Auth_Callback authorize); + void (*sane_exit_api)(void); + }SANEAPI, *LPSANEAPI; + +#ifdef __cplusplus +} +#endif + +//#include +// +//template +//std::string format_string(int level, const char* fmt, Args ... args) +//{ +// size_t size = snprintf(nullptr, 0, fmt, args ...) + 2; +// std::string str(size); +// +// snprintf(&str[0], size, fmt, args ...); +// +// return std::move(str); +//} + +#endif /* sane_ex_h */ diff --git a/sdk/sane/sane_option_definitions.h b/sdk/sane/sane_option_definitions.h new file mode 100644 index 0000000..7b512c7 --- /dev/null +++ b/sdk/sane/sane_option_definitions.h @@ -0,0 +1,508 @@ +// +// definitions for option titles and values +// +// all multi-bytes letter are in UTF-8 format +// +// Date: 2022-09-30 14:23:02 +// + +#pragma once + + +// #define OPTION_TITLE_HFMRSZ "æ¢å¤é»˜è®¤è®¾ç½®" +#define OPTION_TITLE_HFMRSZ "\346\201\242\345\244\215\351\273\230\350\256\244\350\256\276\347\275\256" + +// #define OPTION_TITLE_BZ "帮助" +#define OPTION_TITLE_BZ "\345\270\256\345\212\251" + +// #define OPTION_TITLE_DLSC "多æµè¾“出" +#define OPTION_TITLE_DLSC "\345\244\232\346\265\201\350\276\223\345\207\272" + +// #define OPTION_TITLE_DLSCLX "多æµè¾“出类型" +#define OPTION_TITLE_DLSCLX "\345\244\232\346\265\201\350\276\223\345\207\272\347\261\273\345\236\213" + +// #define OPTION_VALUE_DLSCLX_W "æ— " +#define OPTION_VALUE_DLSCLX_W "\346\227\240" + +// #define OPTION_VALUE_DLSCLX_CS_HD_HB "彩色+ç°åº¦+黑白" +#define OPTION_VALUE_DLSCLX_CS_HD_HB "\345\275\251\350\211\262+\347\201\260\345\272\246+\351\273\221\347\231\275" + +// #define OPTION_VALUE_DLSCLX_CS_HD "彩色+ç°åº¦" +#define OPTION_VALUE_DLSCLX_CS_HD "\345\275\251\350\211\262+\347\201\260\345\272\246" + +// #define OPTION_VALUE_DLSCLX_CS_HB "彩色+黑白" +#define OPTION_VALUE_DLSCLX_CS_HB "\345\275\251\350\211\262+\351\273\221\347\231\275" + +// #define OPTION_VALUE_DLSCLX_HD_HB "ç°åº¦+黑白" +#define OPTION_VALUE_DLSCLX_HD_HB "\347\201\260\345\272\246+\351\273\221\347\231\275" + +// #define OPTION_TITLE_YSMS "颜色模å¼" +#define OPTION_TITLE_YSMS "\351\242\234\350\211\262\346\250\241\345\274\217" + +// #define OPTION_VALUE_YSMS_24WCS "24ä½å½©è‰²" +#define OPTION_VALUE_YSMS_24WCS "24\344\275\215\345\275\251\350\211\262" + +// #define OPTION_VALUE_YSMS_256JHD "256级ç°åº¦" +#define OPTION_VALUE_YSMS_256JHD "256\347\272\247\347\201\260\345\272\246" + +// #define OPTION_VALUE_YSMS_HB "黑白" +#define OPTION_VALUE_YSMS_HB "\351\273\221\347\231\275" + +// #define OPTION_VALUE_YSMS_YSZDSB "颜色自动识别" +#define OPTION_VALUE_YSMS_YSZDSB "\351\242\234\350\211\262\350\207\252\345\212\250\350\257\206\345\210\253" + +// #define OPTION_TITLE_HBTXYZ "黑白图åƒé˜ˆå€¼" +#define OPTION_TITLE_HBTXYZ "\351\273\221\347\231\275\345\233\276\345\203\217\351\230\210\345\200\274" + +// #define OPTION_TITLE_HBTXFSSC "黑白图åƒå色输出" +#define OPTION_TITLE_HBTXFSSC "\351\273\221\347\231\275\345\233\276\345\203\217\345\217\215\350\211\262\350\276\223\345\207\272" + +// #define OPTION_TITLE_HDHHBTX_CSYZQ "ç°åº¦æˆ–é»‘ç™½å›¾åƒ - 除色与增强" +#define OPTION_TITLE_HDHHBTX_CSYZQ "\347\201\260\345\272\246\346\210\226\351\273\221\347\231\275\345\233\276\345\203\217 - \351\231\244\350\211\262\344\270\216\345\242\236\345\274\272" + +// #define OPTION_VALUE_HDHHBTX_CSYZQ_BCS "ä¸é™¤è‰²" +#define OPTION_VALUE_HDHHBTX_CSYZQ_BCS "\344\270\215\351\231\244\350\211\262" + +// #define OPTION_VALUE_HDHHBTX_CSYZQ_CHS "除红色" +#define OPTION_VALUE_HDHHBTX_CSYZQ_CHS "\351\231\244\347\272\242\350\211\262" + +// #define OPTION_VALUE_HDHHBTX_CSYZQ_CLS "除绿色" +#define OPTION_VALUE_HDHHBTX_CSYZQ_CLS "\351\231\244\347\273\277\350\211\262" + +// #define OPTION_VALUE_HDHHBTX_CSYZQ_CHULANSE "除è“色" +#define OPTION_VALUE_HDHHBTX_CSYZQ_CHULANSE "\351\231\244\350\223\235\350\211\262" + +// #define OPTION_VALUE_HDHHBTX_CSYZQ_HSZQ "红色增强" +#define OPTION_VALUE_HDHHBTX_CSYZQ_HSZQ "\347\272\242\350\211\262\345\242\236\345\274\272" + +// #define OPTION_VALUE_HDHHBTX_CSYZQ_LSZQ "绿色增强" +#define OPTION_VALUE_HDHHBTX_CSYZQ_LSZQ "\347\273\277\350\211\262\345\242\236\345\274\272" + +// #define OPTION_VALUE_HDHHBTX_CSYZQ_LANSEZENGQIANG "è“色增强" +#define OPTION_VALUE_HDHHBTX_CSYZQ_LANSEZENGQIANG "\350\223\235\350\211\262\345\242\236\345\274\272" + +// #define OPTION_TITLE_24WCSTX_DLSCCH "24ä½å½©è‰²å›¾åƒ - 多æµè¾“出除红" +#define OPTION_TITLE_24WCSTX_DLSCCH "24\344\275\215\345\275\251\350\211\262\345\233\276\345\203\217 - \345\244\232\346\265\201\350\276\223\345\207\272\351\231\244\347\272\242" + +// #define OPTION_TITLE_24WCSTX_DTKCH "24ä½å½©è‰²å›¾åƒ - 答题å¡é™¤çº¢" +#define OPTION_TITLE_24WCSTX_DTKCH "24\344\275\215\345\275\251\350\211\262\345\233\276\345\203\217 - \347\255\224\351\242\230\345\215\241\351\231\244\347\272\242" + +// #define OPTION_TITLE_BJYC "背景移除" +#define OPTION_TITLE_BJYC "\350\203\214\346\231\257\347\247\273\351\231\244" + +// #define OPTION_TITLE_BJSCFDFW "背景色彩浮动范围" +#define OPTION_TITLE_BJSCFDFW "\350\203\214\346\231\257\350\211\262\345\275\251\346\265\256\345\212\250\350\214\203\345\233\264" + +// #define OPTION_TITLE_RHYMH "é”化与模糊" +#define OPTION_TITLE_RHYMH "\351\224\220\345\214\226\344\270\216\346\250\241\347\263\212" + +// #define OPTION_VALUE_RHYMH_W "æ— " +#define OPTION_VALUE_RHYMH_W "\346\227\240" + +// #define OPTION_VALUE_RHYMH_RH "é”化" +#define OPTION_VALUE_RHYMH_RH "\351\224\220\345\214\226" + +// #define OPTION_VALUE_RHYMH_JYBRH "进一步é”化" +#define OPTION_VALUE_RHYMH_JYBRH "\350\277\233\344\270\200\346\255\245\351\224\220\345\214\226" + +// #define OPTION_VALUE_RHYMH_MH "模糊" +#define OPTION_VALUE_RHYMH_MH "\346\250\241\347\263\212" + +// #define OPTION_VALUE_RHYMH_JYBMH "进一步模糊" +#define OPTION_VALUE_RHYMH_JYBMH "\350\277\233\344\270\200\346\255\245\346\250\241\347\263\212" + +// #define OPTION_TITLE_QCMW "去除摩尔纹" +#define OPTION_TITLE_QCMW "\345\216\273\351\231\244\346\221\251\345\260\224\347\272\271" + +// #define OPTION_TITLE_CWW "除网纹" +#define OPTION_TITLE_CWW "\351\231\244\347\275\221\347\272\271" + +// #define OPTION_TITLE_CWKS "错误扩散" +#define OPTION_TITLE_CWKS "\351\224\231\350\257\257\346\211\251\346\225\243" + +// #define OPTION_TITLE_HBTXZDYH "黑白图åƒå™ªç‚¹ä¼˜åŒ–" +#define OPTION_TITLE_HBTXZDYH "\351\273\221\347\231\275\345\233\276\345\203\217\345\231\252\347\202\271\344\274\230\345\214\226" + +// #define OPTION_TITLE_ZDYHCC "噪点优化尺寸" +#define OPTION_TITLE_ZDYHCC "\345\231\252\347\202\271\344\274\230\345\214\226\345\260\272\345\257\270" + +// #define OPTION_TITLE_ZZCC "纸张尺寸" +#define OPTION_TITLE_ZZCC "\347\272\270\345\274\240\345\260\272\345\257\270" + +// #define OPTION_VALUE_ZZCC_A3 "A3" +#define OPTION_VALUE_ZZCC_A3 "A3" + +// #define OPTION_VALUE_ZZCC_8K "8å¼€" +#define OPTION_VALUE_ZZCC_8K "8\345\274\200" + +// #define OPTION_VALUE_ZZCC_A4 "A4" +#define OPTION_VALUE_ZZCC_A4 "A4" + +// #define OPTION_VALUE_ZZCC_A4HX "A4横å‘" +#define OPTION_VALUE_ZZCC_A4HX "A4\346\250\252\345\220\221" + +// #define OPTION_VALUE_ZZCC_16K "16å¼€" +#define OPTION_VALUE_ZZCC_16K "16\345\274\200" + +// #define OPTION_VALUE_ZZCC_16KHX "16开横å‘" +#define OPTION_VALUE_ZZCC_16KHX "16\345\274\200\346\250\252\345\220\221" + +// #define OPTION_VALUE_ZZCC_A5 "A5" +#define OPTION_VALUE_ZZCC_A5 "A5" + +// #define OPTION_VALUE_ZZCC_A5HX "A5横å‘" +#define OPTION_VALUE_ZZCC_A5HX "A5\346\250\252\345\220\221" + +// #define OPTION_VALUE_ZZCC_A6 "A6" +#define OPTION_VALUE_ZZCC_A6 "A6" + +// #define OPTION_VALUE_ZZCC_A6HX "A6横å‘" +#define OPTION_VALUE_ZZCC_A6HX "A6\346\250\252\345\220\221" + +// #define OPTION_VALUE_ZZCC_B4 "B4" +#define OPTION_VALUE_ZZCC_B4 "B4" + +// #define OPTION_VALUE_ZZCC_B5 "B5" +#define OPTION_VALUE_ZZCC_B5 "B5" + +// #define OPTION_VALUE_ZZCC_B5HX "B5横å‘" +#define OPTION_VALUE_ZZCC_B5HX "B5\346\250\252\345\220\221" + +// #define OPTION_VALUE_ZZCC_B6 "B6" +#define OPTION_VALUE_ZZCC_B6 "B6" + +// #define OPTION_VALUE_ZZCC_B6HX "B6横å‘" +#define OPTION_VALUE_ZZCC_B6HX "B6\346\250\252\345\220\221" + +// #define OPTION_VALUE_ZZCC_Letter "Letter" +#define OPTION_VALUE_ZZCC_Letter "Letter" + +// #define OPTION_VALUE_ZZCC_LetterHX "Letter横å‘" +#define OPTION_VALUE_ZZCC_LetterHX "Letter\346\250\252\345\220\221" + +// #define OPTION_VALUE_ZZCC_DoubleLetter "Double Letter" +#define OPTION_VALUE_ZZCC_DoubleLetter "Double Letter" + +// #define OPTION_VALUE_ZZCC_LEGAL "LEGAL" +#define OPTION_VALUE_ZZCC_LEGAL "LEGAL" + +// #define OPTION_VALUE_ZZCC_PPYSCC "匹é…原始尺寸" +#define OPTION_VALUE_ZZCC_PPYSCC "\345\214\271\351\205\215\345\216\237\345\247\213\345\260\272\345\257\270" + +// #define OPTION_VALUE_ZZCC_ZDSMCCZDCQ "最大扫æ尺寸自动è£åˆ‡" +#define OPTION_VALUE_ZZCC_ZDSMCCZDCQ "\346\234\200\345\244\247\346\211\253\346\217\217\345\260\272\345\257\270\350\207\252\345\212\250\350\243\201\345\210\207" + +// #define OPTION_VALUE_ZZCC_ZDSMCC "最大扫æ尺寸" +#define OPTION_VALUE_ZZCC_ZDSMCC "\346\234\200\345\244\247\346\211\253\346\217\217\345\260\272\345\257\270" + +// #define OPTION_VALUE_ZZCC_SLSJ "三è”试å·" +#define OPTION_VALUE_ZZCC_SLSJ "\344\270\211\350\201\224\350\257\225\345\215\267" + +// #define OPTION_TITLE_ZDYSMQY "自定义扫æ区域" +#define OPTION_TITLE_ZDYSMQY "\350\207\252\345\256\232\344\271\211\346\211\253\346\217\217\345\214\272\345\237\237" + +// #define OPTION_TITLE_SMQYZCmm "扫æ区域左侧(mm)" +#define OPTION_TITLE_SMQYZCmm "\346\211\253\346\217\217\345\214\272\345\237\237\345\267\246\344\276\247\357\274\210mm\357\274\211" + +// #define OPTION_TITLE_SMQYYCmm "扫æ区域å³ä¾§ï¼ˆmm)" +#define OPTION_TITLE_SMQYYCmm "\346\211\253\346\217\217\345\214\272\345\237\237\345\217\263\344\276\247\357\274\210mm\357\274\211" + +// #define OPTION_TITLE_SMQYSCmm "扫æ区域上侧(mm)" +#define OPTION_TITLE_SMQYSCmm "\346\211\253\346\217\217\345\214\272\345\237\237\344\270\212\344\276\247\357\274\210mm\357\274\211" + +// #define OPTION_TITLE_SMQYXCmm "扫æ区域下侧(mm)" +#define OPTION_TITLE_SMQYXCmm "\346\211\253\346\217\217\345\214\272\345\237\237\344\270\213\344\276\247\357\274\210mm\357\274\211" + +// #define OPTION_TITLE_CCJC "尺寸检测" +#define OPTION_TITLE_CCJC "\345\260\272\345\257\270\346\243\200\346\265\213" + +// #define OPTION_TITLE_SMYM "扫æ页é¢" +#define OPTION_TITLE_SMYM "\346\211\253\346\217\217\351\241\265\351\235\242" + +// #define OPTION_VALUE_SMYM_DM "å•é¢" +#define OPTION_VALUE_SMYM_DM "\345\215\225\351\235\242" + +// #define OPTION_VALUE_SMYM_SM "åŒé¢" +#define OPTION_VALUE_SMYM_SM "\345\217\214\351\235\242" + +// #define OPTION_VALUE_SMYM_TGKBYTY "跳过空白页(通用)" +#define OPTION_VALUE_SMYM_TGKBYTY "\350\267\263\350\277\207\347\251\272\347\231\275\351\241\265\357\274\210\351\200\232\347\224\250\357\274\211" + +// #define OPTION_VALUE_SMYM_TGKBYFPZ "跳过空白页(å‘票纸)" +#define OPTION_VALUE_SMYM_TGKBYFPZ "\350\267\263\350\277\207\347\251\272\347\231\275\351\241\265\357\274\210\345\217\221\347\245\250\347\272\270\357\274\211" + +// #define OPTION_VALUE_SMYM_DZ "对折" +#define OPTION_VALUE_SMYM_DZ "\345\257\271\346\212\230" + +// #define OPTION_TITLE_TGKBYLMD "跳过空白页çµæ•åº¦" +#define OPTION_TITLE_TGKBYLMD "\350\267\263\350\277\207\347\251\272\347\231\275\351\241\265\347\201\265\346\225\217\345\272\246" + +// #define OPTION_TITLE_FBL "分辨率" +#define OPTION_TITLE_FBL "\345\210\206\350\276\250\347\216\207" + +// #define OPTION_TITLE_HZ "画质" +#define OPTION_TITLE_HZ "\347\224\273\350\264\250" + +// #define OPTION_VALUE_HZ_W "æ— " +#define OPTION_VALUE_HZ_W "\346\227\240" + +// #define OPTION_VALUE_HZ_SDYX "速度优先" +#define OPTION_VALUE_HZ_SDYX "\351\200\237\345\272\246\344\274\230\345\205\210" + +// #define OPTION_VALUE_HZ_HZYX "画质优先" +#define OPTION_VALUE_HZ_HZYX "\347\224\273\350\264\250\344\274\230\345\205\210" + +// #define OPTION_TITLE_JHZFM "交æ¢æ­£åé¢" +#define OPTION_TITLE_JHZFM "\344\272\244\346\215\242\346\255\243\345\217\215\351\235\242" + +// #define OPTION_TITLE_QYSDQX "å¯ç”¨è‰²è°ƒæ›²çº¿" +#define OPTION_TITLE_QYSDQX "\345\220\257\347\224\250\350\211\262\350\260\203\346\233\262\347\272\277" + +// #define OPTION_TITLE_LDZ "亮度值" +#define OPTION_TITLE_LDZ "\344\272\256\345\272\246\345\200\274" + +// #define OPTION_TITLE_DBD "对比度" +#define OPTION_TITLE_DBD "\345\257\271\346\257\224\345\272\246" + +// #define OPTION_TITLE_JMZ "伽马值" +#define OPTION_TITLE_JMZ "\344\274\275\351\251\254\345\200\274" + +// #define OPTION_TITLE_ZDJP "自动纠å" +#define OPTION_TITLE_ZDJP "\350\207\252\345\212\250\347\272\240\345\201\217" + +// #define OPTION_TITLE_TXCF "图åƒæ‹†åˆ†" +#define OPTION_TITLE_TXCF "\345\233\276\345\203\217\346\213\206\345\210\206" + +// #define OPTION_TITLE_ZPMS "照片模å¼" +#define OPTION_TITLE_ZPMS "\347\205\247\347\211\207\346\250\241\345\274\217" + +//#define OPTION_TITLE_XCHK "消除黑框" +#define OPTION_TITLE_XCHK "\346\266\210\351\231\244\351\273\221\346\241\206" + +// #define OPTION_TITLE_BJTCFS "背景填充方å¼" +#define OPTION_TITLE_BJTCFS "\350\203\214\346\231\257\345\241\253\345\205\205\346\226\271\345\274\217" + +// #define OPTION_VALUE_BJTCFS_TDBX "凸多边形" +#define OPTION_VALUE_BJTCFS_TDBX "\345\207\270\345\244\232\350\276\271\345\275\242" + +// #define OPTION_VALUE_BJTCFS_ADBX "凹多边形" +#define OPTION_VALUE_BJTCFS_ADBX "\345\207\271\345\244\232\350\276\271\345\275\242" + +// #define OPTION_TITLE_SCTC "色彩填充" +#define OPTION_TITLE_SCTC "\350\211\262\345\275\251\345\241\253\345\205\205" + +// #define OPTION_TITLE_YZ "阈值" +#define OPTION_TITLE_YZ "\351\230\210\345\200\274" + +// #define OPTION_TITLE_BJKZDJ "背景抗噪等级" +#define OPTION_TITLE_BJKZDJ "\350\203\214\346\231\257\346\212\227\345\231\252\347\255\211\347\272\247" + +// #define OPTION_TITLE_BYSJ "边缘缩进" +#define OPTION_TITLE_BYSJ "\350\276\271\347\274\230\347\274\251\350\277\233" + +// #define OPTION_TITLE_SSYZ "深色样张" +#define OPTION_TITLE_SSYZ "\346\267\261\350\211\262\346\240\267\345\274\240" + +// #define OPTION_TITLE_FZST "防止渗é€" +#define OPTION_TITLE_FZST "\351\230\262\346\255\242\346\270\227\351\200\217" + +// #define OPTION_TITLE_FZSTDJ "防止渗é€ç­‰çº§" +#define OPTION_TITLE_FZSTDJ "\351\230\262\346\255\242\346\270\227\351\200\217\347\255\211\347\272\247" + +// #define OPTION_VALUE_FZSTDJ_R "å¼±" +#define OPTION_VALUE_FZSTDJ_R "\345\274\261" + +// #define OPTION_VALUE_FZSTDJ_JR "较弱" +#define OPTION_VALUE_FZSTDJ_JR "\350\276\203\345\274\261" + +// #define OPTION_VALUE_FZSTDJ_YB "一般" +#define OPTION_VALUE_FZSTDJ_YB "\344\270\200\350\210\254" + +// #define OPTION_VALUE_FZSTDJ_JQ "较强" +#define OPTION_VALUE_FZSTDJ_JQ "\350\276\203\345\274\272" + +// #define OPTION_VALUE_FZSTDJ_Q "强" +#define OPTION_VALUE_FZSTDJ_Q "\345\274\272" + +// #define OPTION_TITLE_CKYC "穿孔移除" +#define OPTION_TITLE_CKYC "\347\251\277\345\255\224\347\247\273\351\231\244" + +// #define OPTION_TITLE_CKSSFWZFMBL "ç©¿å­”æœç´¢èŒƒå›´å å¹…é¢æ¯”例" +#define OPTION_TITLE_CKSSFWZFMBL "\347\251\277\345\255\224\346\220\234\347\264\242\350\214\203\345\233\264\345\215\240\345\271\205\351\235\242\346\257\224\344\276\213" + +// #define OPTION_TITLE_CKYCZC "穿孔移除—左侧" +#define OPTION_TITLE_CKYCZC "\347\251\277\345\255\224\347\247\273\351\231\244\342\200\224\345\267\246\344\276\247" + +// #define OPTION_TITLE_ZCCKSSFWZFMBL "左侧穿孔æœç´¢èŒƒå›´å å¹…é¢æ¯”例" +#define OPTION_TITLE_ZCCKSSFWZFMBL "\345\267\246\344\276\247\347\251\277\345\255\224\346\220\234\347\264\242\350\214\203\345\233\264\345\215\240\345\271\205\351\235\242\346\257\224\344\276\213" + +// #define OPTION_TITLE_CKYCYC "穿孔移除—å³ä¾§" +#define OPTION_TITLE_CKYCYC "\347\251\277\345\255\224\347\247\273\351\231\244\342\200\224\345\217\263\344\276\247" + +// #define OPTION_TITLE_YCCKSSFWZFMBL "å³ä¾§ç©¿å­”æœç´¢èŒƒå›´å å¹…é¢æ¯”例" +#define OPTION_TITLE_YCCKSSFWZFMBL "\345\217\263\344\276\247\347\251\277\345\255\224\346\220\234\347\264\242\350\214\203\345\233\264\345\215\240\345\271\205\351\235\242\346\257\224\344\276\213" + +// #define OPTION_TITLE_CKYCSC "穿孔移除—上侧" +#define OPTION_TITLE_CKYCSC "\347\251\277\345\255\224\347\247\273\351\231\244\342\200\224\344\270\212\344\276\247" + +// #define OPTION_TITLE_SCCKSSFWZFMBL "上侧穿孔æœç´¢èŒƒå›´å å¹…é¢æ¯”例" +#define OPTION_TITLE_SCCKSSFWZFMBL "\344\270\212\344\276\247\347\251\277\345\255\224\346\220\234\347\264\242\350\214\203\345\233\264\345\215\240\345\271\205\351\235\242\346\257\224\344\276\213" + +// #define OPTION_TITLE_CKYCXC "穿孔移除—下侧" +#define OPTION_TITLE_CKYCXC "\347\251\277\345\255\224\347\247\273\351\231\244\342\200\224\344\270\213\344\276\247" + +// #define OPTION_TITLE_XCCKSSFWZFMBL "下侧穿孔æœç´¢èŒƒå›´å å¹…é¢æ¯”例" +#define OPTION_TITLE_XCCKSSFWZFMBL "\344\270\213\344\276\247\347\251\277\345\255\224\346\220\234\347\264\242\350\214\203\345\233\264\345\215\240\345\271\205\351\235\242\346\257\224\344\276\213" + +// #define OPTION_TITLE_DZSM "待纸扫æ" +#define OPTION_TITLE_DZSM "\345\276\205\347\272\270\346\211\253\346\217\217" + +// #define OPTION_TITLE_SMZS "扫æå¼ æ•°" +#define OPTION_TITLE_SMZS "\346\211\253\346\217\217\345\274\240\346\225\260" + +// #define OPTION_VALUE_SMZS_LXSM "连续扫æ" +#define OPTION_VALUE_SMZS_LXSM "\350\277\236\347\273\255\346\211\253\346\217\217" + +// #define OPTION_VALUE_SMZS_SMZDZS "扫æ指定张数" +#define OPTION_VALUE_SMZS_SMZDZS "\346\211\253\346\217\217\346\214\207\345\256\232\345\274\240\346\225\260" + +// #define OPTION_TITLE_SMSL "扫ææ•°é‡" +#define OPTION_TITLE_SMSL "\346\211\253\346\217\217\346\225\260\351\207\217" + +// #define OPTION_TITLE_WGFX "文稿方å‘" +#define OPTION_TITLE_WGFX "\346\226\207\347\250\277\346\226\271\345\220\221" + +// #define OPTION_VALUE_WGFX_0 "0°" +#define OPTION_VALUE_WGFX_0 "0\302\260" + +// #define OPTION_VALUE_WGFX_90 "90°" +#define OPTION_VALUE_WGFX_90 "90\302\260" + +// #define OPTION_VALUE_WGFX_180 "180°" +#define OPTION_VALUE_WGFX_180 "180\302\260" + +// #define OPTION_VALUE_WGFX__90 "-90°" +#define OPTION_VALUE_WGFX__90 "-90\302\260" + +// #define OPTION_VALUE_WGFX_ZDWBFXSB "自动文本方å‘识别°" +#define OPTION_VALUE_WGFX_ZDWBFXSB "\350\207\252\345\212\250\346\226\207\346\234\254\346\226\271\345\220\221\350\257\206\345\210\253\302\260" + +// #define OPTION_TITLE_BMXZ180 "背é¢æ—‹è½¬180°" +#define OPTION_TITLE_BMXZ180 "\350\203\214\351\235\242\346\227\213\350\275\254180\302\260" + +// #define OPTION_TITLE_CSBJC "超声波检测" +#define OPTION_TITLE_CSBJC "\350\266\205\345\243\260\346\263\242\346\243\200\346\265\213" + +// #define OPTION_TITLE_SZTPCL "åŒå¼ å›¾ç‰‡å¤„ç†" +#define OPTION_TITLE_SZTPCL "\345\217\214\345\274\240\345\233\276\347\211\207\345\244\204\347\220\206" + +// #define OPTION_VALUE_SZTPCL_DQTXBTZSM "丢弃图åƒå¹¶åœæ­¢æ‰«æ" +#define OPTION_VALUE_SZTPCL_DQTXBTZSM "\344\270\242\345\274\203\345\233\276\345\203\217\345\271\266\345\201\234\346\255\242\346\211\253\346\217\217" + +// #define OPTION_VALUE_SZTPCL_DQTXBJXSM "丢弃图åƒå¹¶ç»§ç»­æ‰«æ" +#define OPTION_VALUE_SZTPCL_DQTXBJXSM "\344\270\242\345\274\203\345\233\276\345\203\217\345\271\266\347\273\247\347\273\255\346\211\253\346\217\217" + +// #define OPTION_VALUE_SZTPCL_SCTXBTZSM "上传图åƒå¹¶åœæ­¢æ‰«æ" +#define OPTION_VALUE_SZTPCL_SCTXBTZSM "\344\270\212\344\274\240\345\233\276\345\203\217\345\271\266\345\201\234\346\255\242\346\211\253\346\217\217" + +// #define OPTION_VALUE_SZTPCL_SCTXBJXSM "上传图åƒå¹¶ç»§ç»­æ‰«æ" +#define OPTION_VALUE_SZTPCL_SCTXBJXSM "\344\270\212\344\274\240\345\233\276\345\203\217\345\271\266\347\273\247\347\273\255\346\211\253\346\217\217" + +// #define OPTION_TITLE_ZDJC "装订检测" +#define OPTION_TITLE_ZDJC "\350\243\205\350\256\242\346\243\200\346\265\213" + +// #define OPTION_TITLE_WXJC "歪斜检测" +#define OPTION_TITLE_WXJC "\346\255\252\346\226\234\346\243\200\346\265\213" + +// #define OPTION_TITLE_WXRRD "歪斜容å¿åº¦" +#define OPTION_TITLE_WXRRD "\346\255\252\346\226\234\345\256\271\345\277\215\345\272\246" + +// #define OPTION_TITLE_ZJJC "折角检测" +#define OPTION_TITLE_ZJJC "\346\212\230\350\247\222\346\243\200\346\265\213" + +// #define OPTION_TITLE_ZJDX "折角大å°" +#define OPTION_TITLE_ZJDX "\346\212\230\350\247\222\345\244\247\345\260\217" + +// #define OPTION_TITLE_FZQD "分纸强度" +#define OPTION_TITLE_FZQD "\345\210\206\347\272\270\345\274\272\345\272\246" + +// #define OPTION_VALUE_FZQD_R "å¼±" +#define OPTION_VALUE_FZQD_R "\345\274\261" + +// #define OPTION_VALUE_FZQD_YB "一般" +#define OPTION_VALUE_FZQD_YB "\344\270\200\350\210\254" + +// #define OPTION_VALUE_FZQD_Q "强" +#define OPTION_VALUE_FZQD_Q "\345\274\272" + +// #define OPTION_TITLE_XMSJ "休眠时间" +#define OPTION_TITLE_XMSJ "\344\274\221\347\234\240\346\227\266\351\227\264" + +// #define OPTION_VALUE_XMSJ_BXM "ä¸ä¼‘眠" +#define OPTION_VALUE_XMSJ_BXM "\344\270\215\344\274\221\347\234\240" + +// #define OPTION_VALUE_XMSJ_WFZ "五分钟" +#define OPTION_VALUE_XMSJ_WFZ "\344\272\224\345\210\206\351\222\237" + +// #define OPTION_VALUE_XMSJ_SFZ "å分钟" +#define OPTION_VALUE_XMSJ_SFZ "\345\215\201\345\210\206\351\222\237" + +// #define OPTION_VALUE_XMSJ_BXS "åŠå°æ—¶" +#define OPTION_VALUE_XMSJ_BXS "\345\215\212\345\260\217\346\227\266" + +// #define OPTION_VALUE_XMSJ_YXS "一å°æ—¶" +#define OPTION_VALUE_XMSJ_YXS "\344\270\200\345\260\217\346\227\266" + +// #define OPTION_VALUE_XMSJ_LXS "两å°æ—¶" +#define OPTION_VALUE_XMSJ_LXS "\344\270\244\345\260\217\346\227\266" + +// #define OPTION_VALUE_XMSJ_SXS "å››å°æ—¶" +#define OPTION_VALUE_XMSJ_SXS "\345\233\233\345\260\217\346\227\266" + +// #define OPTION_TITLE_ZDFZQD "自动分纸强度" +#define OPTION_TITLE_ZDFZQD "\350\207\252\345\212\250\345\210\206\347\272\270\345\274\272\345\272\246" + +// #define OPTION_TITLE_JZSBL "进纸失败率" +#define OPTION_TITLE_JZSBL "\350\277\233\347\272\270\345\244\261\350\264\245\347\216\207" + +//#define OPTION_TITLE_DZMS "对折模å¼" +#define OPTION_TITLE_DZMS "\345\257\271\346\212\230\346\250\241\345\274\217" + +//#define OPTION_VALUE_ZYDZ "å·¦å³å¯¹æŠ˜" +#define OPTION_VALUE_ZYDZ "\345\267\246\345\217\263\345\257\271\346\212\230" + +//#define OPTION_VALUE_SXDZ "上下对折" +#define OPTION_VALUE_SXDZ "\344\270\212\344\270\213\345\257\271\346\212\230" + +//#define OPTION_VALUE_SXDZ "自动对折" +#define OPTION_VALUE_ZDDZ "\350\207\252\345\212\250\345\257\271\346\212\230" + +//#define OPTION_TITLE_SPJZ "色åæ ¡æ­£" +#define OPTION_TITLE_SPJZ "\350\211\262\345\201\217\346\240\241\346\255\243" + +//#define OPTION_TITLE_DZSMTCSJ "待纸扫æ退出时间" +#define OPTION_TITLE_DZSMTCSJ "\345\276\205\347\272\270\346\211\253\346\217\217\351\200\200\345\207\272\346\227\266\351\227\264" + +//#define OPTION_TITLE_TGKBY "跳过空白页" +#define OPTION_TITLE_TGKBY "\350\267\263\350\277\207\347\251\272\347\231\275\351\241\265" + + +//////////////////////////////////////////////////////////////// +// reserved ... +//////////////////////////////////////////////////////////////// +#define OPTION_TITLE_GMZ OPTION_TITLE_JMZ + + +//////////////////////////////////////////////////////////////// +// string compare ... +//////////////////////////////////////////////////////////////// +#include +#define IMPLEMENT_OPTION_STRING_COMPARE(func_name) \ + bool func_name(const char* opt_define, const char* value) \ + { \ + while(*value++ == L' '); \ + value--; \ + return strcmp(opt_define, value) == 0; \ + } + + diff --git a/sdk/sane/sanei.h b/sdk/sane/sanei.h new file mode 100644 index 0000000..ece1beb --- /dev/null +++ b/sdk/sane/sanei.h @@ -0,0 +1,160 @@ +/* sane - Scanner Access Now Easy. + Copyright (C) 1996 David Mosberger-Tang and Andreas Beck + Copyright (C) 2002, 2003 Henning Meier-Geinitz + + This file is part of the SANE package. + + SANE 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. + + SANE 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 sane; see the file COPYING. If not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + As a special exception, the authors of SANE give permission for + additional uses of the libraries contained in this release of SANE. + + The exception is that, if you link a SANE library with other files + to produce an executable, this does not by itself cause the + resulting executable to be covered by the GNU General Public + License. Your use of that executable is in no way restricted on + account of linking the SANE library code into it. + + This exception does not, however, invalidate any other reasons why + the executable file might be covered by the GNU General Public + License. + + If you submit changes to SANE to the maintainers to be included in + a subsequent release, you agree by submitting the changes that + those changes may be distributed with this exception intact. + + If you write modifications of your own for SANE, it is your choice + whether to permit this exception to apply to your modifications. + If you do not wish that, delete this exception notice. +*/ + +/** @file sanei.h + * Convenience macros and function declarations for backends + * @sa sanei_backend.h sanei_thread.h + */ + +/* Doxygen documentation */ + +/** @mainpage SANEI (SANE internal routines) documentation + * + * @image html sane-logo2.jpg + * @section intro Introduction + * + * The header files in the include/sane/ directory named sanei_*.h provide + * function declarations and macros that can be used by every SANE backend. + * Their implementations can be found in the sanei/ directory. The code aims + * to be platform-independent to avoid lots of \#ifdef code in the backends. + * Please use the SANEI functions wherever possible. + * + * This documentation was created by the use of doxygen, the + * doc/doxygen-sanei.conf configuration file and documentation in the sanei_*.h + * files. + * + * This documentation is far from complete. Any help is appreciated. + * + * @section additional Additional documentation + * - The SANE standard can be found at the SANE webserver, + * though the PostScript version produced from the source may be more recent. + * - Information on how to write a backend: backend-writing.txt. + * - General SANE documentation is on the SANE documentation + * page. + * + * @section contact Contact + * + * The common way to contact the developers of SANE is the sane-devel + * mailing list. See the mailing list webpage + * for details. That's the place to ask questions, report bugs, or announce + * a new backend. + * + */ + +#ifndef sanei_h +#define sanei_h + +#include + +/** @name Public macros and functions + * @{ + */ +/** @def STRINGIFY(x) + * Turn parameter into string. + */ +/** @def PASTE(x,y) + * Concatenate parameters. + * + */ +/** @def NELEMS(a) + * Return number of elements of an array. + * + */ + +/** @fn extern SANE_Status sanei_check_value (const SANE_Option_Descriptor * opt, void * value); + * Check the constraints of a SANE option. + * + * @param opt option to check + * @param value value of the option + * + * @return + * - SANE_STATUS_GOOD - on success + * - SANE_STATUS_INVAL - if the value doesn't fit inside the constraint + * or any other error occured + * @sa sanei_constrain_value() + */ + +/** @fn extern SANE_Status sanei_constrain_value (const SANE_Option_Descriptor * opt, void * value, SANE_Word * info); + * Check the constraints of a SANE option and adjust its value if necessary. + * + * Depending on the type of the option and constraint, value is modified + * to fit inside constraint. + * + * @param opt option to check + * @param value value of the option + * @param info info is set to SANE_INFO_INEXACT if value was changed + * + * @return + * - SANE_STATUS_GOOD - on success + * - SANE_STATUS_INVAL - if the function wasn't able to fit value into the + * constraint or any other error occured + * @sa sanei_check_value() + */ + +/* @} */ + +/* A few convenience macros: */ +/** @hideinitializer */ +#define NELEMS(a) ((int)(sizeof (a) / sizeof (a[0]))) + +/** @hideinitializer */ +#define STRINGIFY1(x) #x +/** @hideinitializer */ +#define STRINGIFY(x) STRINGIFY1(x) + +/** @hideinitializer */ +#define PASTE1(x,y) x##y +/** @hideinitializer */ +#define PASTE(x,y) PASTE1(x,y) + +extern SANE_Status sanei_check_value (const SANE_Option_Descriptor * opt, + void * value); + +extern SANE_Status sanei_constrain_value (const SANE_Option_Descriptor * opt, + void * value, SANE_Word * info); + + +#endif /* sanei_h */ diff --git a/sdk/sane/sanei_backend.h b/sdk/sane/sanei_backend.h new file mode 100644 index 0000000..7b9797c --- /dev/null +++ b/sdk/sane/sanei_backend.h @@ -0,0 +1,192 @@ +/** @file sanei_backend.h + * Compatibility header file for backends + * + * This file provides some defines for macros missing on some platforms. + * It also has the SANE API entry points. sanei_backend.h must be included + * by every backend. + * + * @sa sanei.h sanei_thread.h + */ + + +/* + * Compiler related options + */ + +/** Mark unused variables/parameters + * + * Tells the compiler a variable is unused, so the compiler doesn't spit a warning. + */ +#ifdef __GNUC__ +#define __sane_unused__ __attribute__((unused)) +#else +#define __sane_unused__ +#endif + +/** @name Compatibility macros + * @{ + */ +#include "sanei_debug.h" + +#ifdef HAVE_SYS_HW_H + /* OS/2 i/o-port access compatibility macros: */ +# define inb(p) _inp8 (p) +# define outb(v,p) _outp8 ((p),(v)) +# define ioperm(b,l,o) _portaccess ((b),(b)+(l)-1) +# define HAVE_IOPERM 1 +#endif + +#ifndef HAVE_OS2_H +#include +#ifndef O_NONBLOCK +# ifdef O_NDELAY +# define O_NONBLOCK O_NDELAY +# else +# ifdef FNDELAY +# define O_NONBLOCK FNDELAY /* last resort */ +# endif +# endif +#endif +#endif /* HAVE_OS2_H */ + +#include +#ifndef PATH_MAX +# define PATH_MAX 1024 +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#ifndef MM_PER_INCH +#define MM_PER_INCH 25.4 +#endif + +#ifdef HAVE_SIGPROCMASK +# define SIGACTION sigaction +#else + +/* Just enough backwards compatibility that we get by in the backends + without making handstands. */ +# ifdef sigset_t +# undef sigset_t +# define sigset_t int +# endif +# ifdef sigemptyset +# undef sigemptyset +# endif +# ifdef sigfillset +# undef sigfillset +# endif +# ifdef sigaddset +# undef sigaddset +# endif +# ifdef sigdelset +# undef sigdelset +# endif +# ifdef sigprocmask +# undef sigprocmask +# endif +# ifdef SIG_BLOCK +# undef SIG_BLOCK +# endif +# ifdef SIG_UNBLOCK +# undef SIG_UNBLOCK +# endif +# ifdef SIG_SETMASK +# undef SIG_SETMASK +# endif + +# define sigemptyset(set) do { *(set) = 0; } while (0) +# define sigfillset(set) do { *(set) = ~0; } while (0) +# define sigaddset(set,signal) do { *(set) |= sigmask (signal); } while (0) +# define sigdelset(set,signal) do { *(set) &= ~sigmask (signal); } while (0) +# define sigaction(sig,new,old) sigvec (sig,new,old) + + /* Note: it's not safe to just declare our own "struct sigaction" since + some systems (e.g., some versions of OpenStep) declare that structure, + but do not implement sigprocmask(). Hard to believe, aint it? */ +# define SIGACTION sigvec +# define SIG_BLOCK 1 +# define SIG_UNBLOCK 2 +# define SIG_SETMASK 3 +#endif /* !HAVE_SIGPROCMASK */ +/* @} */ + + +/** @name Declaration of entry points: + * @{ + */ +extern SANE_Status ENTRY(init) (SANE_Int *, SANE_Auth_Callback); +extern SANE_Status ENTRY(get_devices) (const SANE_Device ***, SANE_Bool); +extern SANE_Status ENTRY(open) (SANE_String_Const, SANE_Handle *); +extern const SANE_Option_Descriptor * + ENTRY(get_option_descriptor) (SANE_Handle, SANE_Int); +extern SANE_Status ENTRY(control_option) (SANE_Handle, SANE_Int, SANE_Action, + void *, SANE_Word *); +extern SANE_Status ENTRY(get_parameters) (SANE_Handle, SANE_Parameters *); +extern SANE_Status ENTRY(start) (SANE_Handle); +extern SANE_Status ENTRY(read) (SANE_Handle, SANE_Byte *, SANE_Int, + SANE_Int *); +extern SANE_Status ENTRY(set_io_mode) (SANE_Handle, SANE_Bool); +extern SANE_Status ENTRY(get_select_fd) (SANE_Handle, SANE_Int *); +extern void ENTRY(cancel) (SANE_Handle); +extern void ENTRY(close) (SANE_Handle); +extern void ENTRY(exit) (void); +extern void sanei_debug_msg(int level, int max_level, const char* be, const char* fmt, va_list ap); + +#ifndef STUBS +/* Now redirect sane_* calls to backend's functions: */ + +#define sane_init(a,b) ENTRY(init) (a,b) +#define sane_get_devices(a,b) ENTRY(get_devices) (a,b) +#define sane_open(a,b) ENTRY(open) (a,b) +#define sane_get_option_descriptor(a,b) ENTRY(get_option_descriptor) (a,b) +#define sane_control_option(a,b,c,d,e) ENTRY(control_option) (a,b,c,d,e) +#define sane_get_parameters(a,b) ENTRY(get_parameters) (a,b) +#define sane_start(a) ENTRY(start) (a) +#define sane_read(a,b,c,d) ENTRY(read) (a,b,c,d) +#define sane_set_io_mode(a,b) ENTRY(set_io_mode) (a,b) +#define sane_get_select_fd(a,b) ENTRY(get_select_fd) (a,b) +#define sane_cancel(a) ENTRY(cancel) (a) +#define sane_close(a) ENTRY(close) (a) +#define sane_exit(a) ENTRY(exit) (a) +#define sane_strstatus(a) ENTRY(strstatus)(a) + +//////////////////////////////////////////////////////////////////////////////// +// extension for standard SANE ... +#define sane_init_ex(a, b, c) ENTRY(init_ex) (a,b,c) +#define sane_io_control(a,b,c,d) ENTRY(io_control)(a,b,c,d) +#define sane_err_desc(a) ENTRY(err_desc)(a) + +#define sane_get_option_descriptor_ex(a,b) ENTRY(get_option_descriptor_ex) (a,b) // added on 2023-01-06, option can be 'key' +#define sane_control_option_ex(a,b,c,d,e) ENTRY(control_option_ex) (a,b,c,d,e) // added on 2023-01-06, option can be 'key' +#define sane_read_ext_info(a, b) ENTRY(read_ext_info)(a, b) // added on 2023-01-13 + +#endif /* STUBS */ +/* @} */ + +/** Internationalization for SANE backends + * + * Add SANE_I18N() to all texts that can be translated. + * E.g. out_txt = SANE_I18N("Hello"); + */ +#ifndef SANE_I18N +#define SANE_I18N(text) text +#endif + +/** Option_Value union + * + * Convenience union to access option values given to the backend + */ +#ifndef SANE_OPTION +typedef union +{ + SANE_Bool b; /**< bool */ + SANE_Word w; /**< word */ + SANE_Word *wa; /**< word array */ + SANE_String s; /**< string */ +} +Option_Value; +#define SANE_OPTION 1 +#endif diff --git a/sdk/sane/sanei_debug.h b/sdk/sane/sanei_debug.h new file mode 100644 index 0000000..557f8a7 --- /dev/null +++ b/sdk/sane/sanei_debug.h @@ -0,0 +1,153 @@ +/** @file sanei_debug.h + * Support for printing debug messages. + * + * Use the functions of this header file to print debug or warning messages. + */ + +#ifndef _SANEI_DEBUG_H +#define _SANEI_DEBUG_H + +#include + +/** @name Public macros + * These macros can be used in backends and other SANE-related + * code. + * + * Before including sanei_debug.h, the following macros must be set: + * + * - BACKEND_NAME - The name of your backend without double-quotes (must be set in any case) + * - STUBS - If this is defined, no macros will be included. Used in + * backends consisting of more than one .c file. + * - DEBUG_DECLARE_ONLY - Generates prototypes instead of functions. Used in + * backends consisting of more than one .c file. + * - DEBUG_NOT_STATIC - Doesn't generate static functions. Used in header files if + * they are include in more than one .c file. + * + * @{ + */ + +/** @def DBG_INIT() + * Initialize sanei_debug. + * + * Call this function before you use any DBG function. + */ + +/** @def DBG(level, fmt, ...) + * Print a message at debug level `level' or higher using a printf-like + * function. Example: DBG(1, "sane_open: opening fd \%d\\n", fd). + * + * @param level debug level + * @param fmt format (see man 3 printf for details) + * @param ... additional arguments + */ + +/** @def IF_DBG(x) + * Compile code only if debugging is enabled. + * + * Expands to x if debug support is enabled at compile-time. If NDEBUG is + * defined at compile-time this macro expands to nothing. + * + * @param x code to expand when debugging is enabled + */ + +/** + * @def DBG_LEVEL + * Current debug level. + * + * You can only read this "variable". + */ + +/** @def ENTRY(name) + * Expands to sane_BACKEND_NAME_name. + * + * Example: ENTRY(init) in mustek.c will expand to sane_mustek_init. + */ + +/* @} */ + + + /** @hideinitializer*/ +#define ENTRY(name) PASTE(PASTE(PASTE(sane_,BACKEND_NAME),_),name) + +#ifdef NDEBUG + +extern void sanei_debug_ndebug (int level, const char *msg, ...); + +# define DBG_LEVEL (0) +# define DBG_INIT() +# define DBG sanei_debug_ndebug +# define IF_DBG(x) + +#else /* !NDEBUG */ + + /** @hideinitializer*/ +# define DBG_LEVEL PASTE(sanei_debug_,BACKEND_NAME) + +# if defined(BACKEND_NAME) && !defined(STUBS) +# ifdef DEBUG_DECLARE_ONLY +extern int DBG_LEVEL; +# else /* !DEBUG_DECLARE_ONLY */ +int DBG_LEVEL = 0; +# endif /* DEBUG_DECLARE_ONLY */ +# endif /* BACKEND_NAME && !STUBS */ + + /** @hideinitializer*/ +# define DBG_INIT() \ + sanei_init_debug (STRINGIFY(BACKEND_NAME), &DBG_LEVEL) + + /** @hideinitializer*/ +# define DBG_LOCAL PASTE(DBG_LEVEL,_call) + + +# ifndef STUBS + +# ifdef DEBUG_DECLARE_ONLY + +extern void DBG_LOCAL (int level, const char *msg, ...) +#ifdef __GNUC__ +__attribute__ ((format (printf, 2, 3))) +#endif +; + +# else /* !DEBUG_DECLARE_ONLY */ + +# include + +extern void sanei_debug_msg + (int level, int max_level, const char *be, const char *fmt, va_list ap); + +#ifdef __GNUC__ +# ifndef DEBUG_NOT_STATIC +static +# endif /* !DEBUG_NOT_STATIC */ +void DBG_LOCAL (int level, const char *msg, ...) __attribute__ ((format (printf, 2, 3))); +#endif /* __GNUC__ */ + +# ifndef DEBUG_NOT_STATIC +static +# endif /* !DEBUG_NOT_STATIC */ +void +DBG_LOCAL (int level, const char *msg, ...) +{ + va_list ap; + + va_start (ap, msg); + sanei_debug_msg (level, DBG_LEVEL, STRINGIFY(BACKEND_NAME), msg, ap); + va_end (ap); +} + +# endif /* DEBUG_DECLARE_ONLY */ + +# endif /* !STUBS */ + + /** @hideinitializer*/ +# define DBG DBG_LOCAL + +extern void sanei_init_debug (const char * backend, int * debug_level_var); + + /** @hideinitializer*/ +# define IF_DBG(x) x + +#endif /* NDEBUG */ + +#endif /* _SANEI_DEBUG_H */ diff --git a/sdk/sane_opt_json/base_opt.cpp b/sdk/sane_opt_json/base_opt.cpp new file mode 100644 index 0000000..70ae256 --- /dev/null +++ b/sdk/sane_opt_json/base_opt.cpp @@ -0,0 +1,97 @@ +#include "base_opt.h" + +#include +#include + + +sane_opt_provider::sane_opt_provider() +{ + set_where(nullptr); +} + +sane_opt_provider::~sane_opt_provider() +{ + for (auto& v : following_) + v.second->release(); + following_.clear(); +} + +bool sane_opt_provider::set_opt_json_text(char* txt) +{ + gb_json* jsn = new gb_json(); + bool ret = jsn->attach_text(txt); + + jsn->release(); + if (ret) + opt_jsn_txt_ = txt; + else + opt_jsn_txt_ = ""; + + return ret; +} +void sane_opt_provider::set_where(const char* where) +{ + if (where && *where) + { + where_ = where; + } + else + { + char buf[20] = { 0 }; + + sprintf(buf, "%p", this); + where_ = buf; + } +} + +const char* sane_opt_provider::get_opt_json(void) +{ + return opt_jsn_txt_.c_str(); +} +const char* sane_opt_provider::from(void) +{ + return where_.c_str(); +} +void sane_opt_provider::set_following_provider(const char* name, sane_opt_provider* following) +{ + if (following_.count(name)) + { + following_[name]->release(); + following_.erase(name); + } + + if (following) + { + following_[name] = following; + following->add_ref(); + } +} +sane_opt_provider* sane_opt_provider::get_following(const char* name) +{ + sane_opt_provider* prvd = nullptr; + + if (following_.count(name)) + { + prvd = following_[name]; + if (prvd) + prvd->add_ref(); + } + + return prvd; +} +char* sane_opt_provider::get_value(const char* name, void* value, size_t* size, int* err) +{ + if (err) + *err = SCANNER_ERR_DEVICE_NOT_SUPPORT; + + if (size) + *size = 0; + + return nullptr; +} +int sane_opt_provider::set_value(const char* name, void* val) +{ + return SCANNER_ERR_DEVICE_NOT_SUPPORT; +} +void sane_opt_provider::enable(const char* name, bool able) +{} diff --git a/sdk/sane_opt_json/base_opt.h b/sdk/sane_opt_json/base_opt.h new file mode 100644 index 0000000..6f163cd --- /dev/null +++ b/sdk/sane_opt_json/base_opt.h @@ -0,0 +1,44 @@ +#pragma once + +// SANE-Option +// +// created on 2022-10-24 +// + +#include +#include +#include // for refer + +class sane_opt_provider : public refer +{ + bool is_in_another_module_; + + std::string opt_jsn_txt_; + std::string where_; + +protected: + std::map following_; + +public: + sane_opt_provider(); + +protected: + virtual ~sane_opt_provider(); + + bool set_opt_json_text(char* txt); + void set_where(const char* where); + +public: + const char* get_opt_json(void); // if no content, return "" plz. + const char* from(void); // if no content, return "" plz. + bool is_in_another_module(void) { return is_in_another_module_; } + void set_following_provider(const char* name, sane_opt_provider* following); // when option has provided by more than one + sane_opt_provider* get_following(const char* name); // caller should ->release returned value + +public: + // return malloc(), real data size stored in parameter 'size'. invoker should free() the returned value + virtual char* get_value(const char* name, void* value, size_t* size, int* err = nullptr); + virtual int set_value(const char* name, void* val); + virtual void enable(const char* name, bool able); +}; + diff --git a/sdk/sane_opt_json/device_opt.cpp b/sdk/sane_opt_json/device_opt.cpp new file mode 100644 index 0000000..fd82cab --- /dev/null +++ b/sdk/sane_opt_json/device_opt.cpp @@ -0,0 +1,2052 @@ +#include "device_opt.h" + +#include +#include "base_opt.h" +#include +#include + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +static const char* empty_from_default_language(const char* deflang) +{ + return deflang; +} +static const char*(*from_def_lang)(const char*) = empty_from_default_language; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// image-processing jsons ... + +static bool refine_string_data(gb_json* jsn, void* value) +{ + bool refined = false; + gb_json* range = nullptr; + std::string v((char*)value); + + jsn->get_value("range", range); + if (range) + { + std::string vl(""), vu(""), s(""); + if (range->get_value("min", vl)) + { + if (v < vl) + { + strcpy((char*)value, vl.c_str()); + refined = true; + } + else if (range->get_value("max", vu)) + { + if (v > vu) + { + strcpy((char*)value, vu.c_str()); + refined = true; + } + //else if (range->get_value("step", s)) + //{ + // T cur(*(TC*)value); + // + // vl = cur - vl; + // vl /= s; + // if (!IS_DOUBLE_EQUAL(vl, (int)vl)) + // { + // vl += .5f; + // vl = (int)vl * s; + // if (vl > vu) + // vl = vu; + // cp((TC*)value, vl); + // refined = true; + // } + //} + } + } + else + { + gb_json* val = range->first_child(); + bool found = false; + + while (val) + { + if (val->value(vl)) + { + if (v == vl) + { + found = true; + val->release(); + break; + } + } + val->release(); + val = range->next_child(); + } + if (!found) + { + if (jsn->get_value("default", vl)) + { + refined = true; + strcpy((char*)value, vl.c_str()); + } + } + } + range->release(); + } + + return refined; +} + +template +static std::string get_real_value(gb_json* jsn, bool def_val) +{ + T v; + + jsn->get_value(def_val ? "default" : "cur", v); + + return std::move(std::string((char*)&v, sizeof(v))); +} +template +static std::string get_real_string(gb_json* jsn, bool def_val) +{ + T v; + + jsn->get_value(def_val ? "default" : "cur", v); + + return std::move(v); +} + +static std::string get_real_value(gb_json* jsn, const char* type) +{ + if (strcmp(type, JSON_SANE_TYPE_BOOL) == 0) + { + bool v = false; + jsn->value(v); + + return std::string((char*)&v, sizeof(v)); + } + else if (strcmp(type, JSON_SANE_TYPE_INT) == 0) + { + int v = 0; + jsn->value(v); + + return std::string((char*)&v, sizeof(v)); + } + else if (strcmp(type, JSON_SANE_TYPE_FIXED) == 0) + { + double v = .0f; + jsn->value(v); + + return std::string((char*)&v, sizeof(v)); + } + else if (strcmp(type, JSON_SANE_TYPE_STRING) == 0) + { + std::string v(""); + jsn->value(v); + + return std::move(v); + } + + return ""; +} + +static struct +{ + std::string name; + std::string title; // in UTF8 +}g_known_group_with_sn[] = { {"base", "\xE5\x9F\xBA\xE6\x9C\xAC\xE8\xAE\xBE\xE7\xBD\xAE"} + , {"bright", "\xE4\xBA\xAE\xE5\xBA\xA6"} + , {"imgp", "\xE5\x9B\xBE\xE5\x83\x8F\xE5\xA4\x84\xE7\x90\x86"} + , {"feeder", "\xE9\x80\x81\xE7\xBA\xB8\xE6\x96\xB9\xE5\xBC\x8F\xE8\xAE\xBE\xE7\xBD\xAE"} // ���ã�\xE8\xAE\xBE\xE7\xBD\xAE + }; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// class device_opt ... +bool device_option::condition_value::set_value(gb_json* jsn, const char* type, device_option* parent) // jsn contains only ONE value or its object +{ + bool ret = false; + + clear(); + if (!jsn) + { + // consistant value, and type is name ... + CONDVAL cv; + + cv.logic = nullptr; + cv.value = type; + vals_.push_back(cv); + parent_ = parent; + + return true; + } + + if (jsn->is_leaf_node()) + { + CONDVAL cv; + + cv.logic = nullptr; + cv.value = get_real_value(jsn, type); + vals_.push_back(cv); + ret = true; + } + else + { + gb_json* item = jsn->first_child(); + CONDVAL val0; // make the default value at last + + ret = true; + val0.logic = nullptr; + val0.value = ""; + + while (item) + { + // "condition": value + std::string cond(item->key()), val(""); + CONDVAL cv; + + if (cond == "default") + { + val0.logic = nullptr; + val0.value = get_real_value(item, type); + } + else + { + int end = 0; + + cv.logic = new simple_logic(); + if (cv.logic->parse(cond.c_str(), &end, &device_option::init_condition, parent)) + { + cv.value = get_real_value(item, type); + vals_.push_back(cv); + } + else + { + delete cv.logic; + item->release(); + ret = false; + break; + } + } + item->release(); + item = jsn->next_child(); + } + + if (!val0.value.empty()) + vals_.push_back(val0); + } + + return ret; +} +std::string device_option::condition_value::value(bool(*compare)(const char*, void*), void* param) +{ + if (parent_) + return parent_->get_option_value(vals_[0].value.c_str(), SANE_ACTION_GET_VALUE); + + for (auto& v : vals_) + { + if (!v.logic) + return v.value; + else if (v.logic->value(compare, param)) + return v.value; + } + + return ""; +} +bool device_option::range_value::set_value(gb_json* jsn, const char* type, device_option* parent) // jsn contains all range object +{ + bool ret = true; + + parent->depend_opts_[jsn->key()]; + clear(); + if (jsn->is_array()) + { + gb_json* item = jsn->first_child(); + while (item) + { + device_option::condition_value* v = new device_option::condition_value(); + if (v->set_value(item, type, parent)) + { + vals_.push_back(v); + } + else + { + delete v; + item->release(); + ret = false; + break; + } + item->release(); + item = jsn->next_child(); + } + } + else + { + condition_value* cv = nullptr; + if (strcmp(type, JSON_SANE_TYPE_BOOL) == 0) + { + cv = device_option::to_condition_value(jsn, "min", type, parent); + ret = false; + if (cv) + { + vals_.push_back(cv); + cv = device_option::to_condition_value(jsn, "max", type, parent); + if (cv) + { + vals_.push_back(cv); + cv = device_option::to_condition_value(jsn, "step", type, parent); + if (cv) + { + vals_.push_back(cv); + ret = true; + } + } + } + } + else if (strcmp(type, JSON_SANE_TYPE_INT) == 0) + { + cv = device_option::to_condition_value(jsn, "min", type, parent); + ret = false; + if (cv) + { + vals_.push_back(cv); + cv = device_option::to_condition_value(jsn, "max", type, parent); + if (cv) + { + vals_.push_back(cv); + cv = device_option::to_condition_value(jsn, "step", type, parent); + if (cv) + { + vals_.push_back(cv); + ret = true; + } + } + } + } + else if (strcmp(type, JSON_SANE_TYPE_FIXED) == 0) + { + cv = device_option::to_condition_value(jsn, "min", type, parent); + ret = false; + if (cv) + { + vals_.push_back(cv); + cv = device_option::to_condition_value(jsn, "max", type, parent); + if (cv) + { + vals_.push_back(cv); + cv = device_option::to_condition_value(jsn, "step", type, parent); + if (cv) + { + vals_.push_back(cv); + ret = true; + } + } + } + } + else if (strcmp(type, JSON_SANE_TYPE_STRING) == 0) + { + cv = device_option::to_condition_value(jsn, "min", type, parent); + ret = false; + if (cv) + { + vals_.push_back(cv); + cv = device_option::to_condition_value(jsn, "max", type, parent); + if (cv) + { + vals_.push_back(cv); + cv = device_option::to_condition_value(jsn, "step", type, parent); + if (cv) + { + vals_.push_back(cv); + ret = true; + } + } + } + } + } + + return ret; +} + + +device_option::device_option(std::function user_priv, std::function log) : origin_(nullptr), now_(nullptr) + , user_(user_priv), log_(log) +{} +device_option::~device_option() +{ + clear(); +} + +bool device_option::is_equal_b(gb_json* opt, void* val, void* v1, void* v2) +{ + return *(bool*)val == *(bool*)v1; +} +bool device_option::is_equal_i(gb_json* opt, void* val, void* v1, void* v2) +{ + return *(int*)val == *(int*)v1; +} +bool device_option::is_equal_f(gb_json* opt, void* val, void* v1, void* v2) +{ + return IS_DOUBLE_EQUAL(*(double*)val, *(double*)v1); +} +bool device_option::is_equal_s(gb_json* opt, void* val, void* v1, void* v2) +{ + return strcmp((char*)val, from_def_lang((char*)v1)) == 0; +} + +bool device_option::is_less_b(gb_json* opt, void* val, void* v1, void* v2) +{ + return *(bool*)val < *(bool*)v1; +} +bool device_option::is_less_i(gb_json* opt, void* val, void* v1, void* v2) +{ + return *(int*)val < *(int*)v1; +} +bool device_option::is_less_f(gb_json* opt, void* val, void* v1, void* v2) +{ + return *(double*)val < *(double*)v1; +} +bool device_option::is_less_s(gb_json* opt, void* val, void* v1, void* v2) +{ + return strcmp((char*)val, from_def_lang((char*)v1)) < 0; +} + +bool device_option::is_great_b(gb_json* opt, void* val, void* v1, void* v2) +{ + return *(bool*)val > *(bool*)v1; +} +bool device_option::is_great_i(gb_json* opt, void* val, void* v1, void* v2) +{ + return *(int*)val > *(int*)v1; +} +bool device_option::is_great_f(gb_json* opt, void* val, void* v1, void* v2) +{ + return *(double*)val > *(double*)v1; +} +bool device_option::is_great_s(gb_json* opt, void* val, void* v1, void* v2) +{ + return strcmp((char*)val, from_def_lang((char*)v1)) > 0; +} + +bool device_option::is_between_b(gb_json* opt, void* val, void* v1, void* v2) +{ + return *(bool*)val == *(bool*)v1 || *(bool*)val <= *(bool*)v2; +} +bool device_option::is_between_i(gb_json* opt, void* val, void* v1, void* v2) +{ + return *(int*)val >= *(int*)v1 && *(int*)val <= *(int*)v2; +} +bool device_option::is_between_f(gb_json* opt, void* val, void* v1, void* v2) +{ + return *(double*)val >= *(double*)v1 && *(double*)val <= *(double*)v2; +} +bool device_option::is_between_s(gb_json* opt, void* val, void* v1, void* v2) +{ + return strcmp((char*)val, from_def_lang((char*)v1)) >= 0 && + strcmp((char*)val, from_def_lang((char*)v2)) <= 0; +} + +bool device_option::is_opt_enabled(gb_json* opt, void* val, void* v1, void* v2) +{ + bool en = true; + + if (!opt->get_value("enabled", en)) + en = true; + + return en; +} + +bool device_option::get_equal(const char* type, bool(**f)(gb_json*, void*, void*, void*)) +{ + bool ret = true; + if (strcmp(type, JSON_SANE_TYPE_BOOL) == 0) + { + *f = &device_option::is_equal_b; + } + else if (strcmp(type, JSON_SANE_TYPE_INT) == 0) + { + *f = &device_option::is_equal_i; + } + else if (strcmp(type, JSON_SANE_TYPE_FIXED) == 0) + { + *f = &device_option::is_equal_f; + } + else if (strcmp(type, JSON_SANE_TYPE_STRING) == 0) + { + *f = &device_option::is_equal_s; + } + else + { + ret = false; + } + + return ret; +} +bool device_option::get_less(const char* type, bool(**f)(gb_json*, void*, void*, void*)) +{ + bool ret = true; + if (strcmp(type, JSON_SANE_TYPE_BOOL) == 0) + { + *f = &device_option::is_less_b; + } + else if (strcmp(type, JSON_SANE_TYPE_INT) == 0) + { + *f = &device_option::is_less_i; + } + else if (strcmp(type, JSON_SANE_TYPE_FIXED) == 0) + { + *f = &device_option::is_less_f; + } + else if (strcmp(type, JSON_SANE_TYPE_STRING) == 0) + { + *f = &device_option::is_less_s; + } + else + { + ret = false; + } + + return ret; +} +bool device_option::get_great(const char* type, bool(**f)(gb_json*, void*, void*, void*)) +{ + bool ret = true; + if (strcmp(type, JSON_SANE_TYPE_BOOL) == 0) + { + *f = &device_option::is_great_b; + } + else if (strcmp(type, JSON_SANE_TYPE_INT) == 0) + { + *f = &device_option::is_great_i; + } + else if (strcmp(type, JSON_SANE_TYPE_FIXED) == 0) + { + *f = &device_option::is_great_f; + } + else if (strcmp(type, JSON_SANE_TYPE_STRING) == 0) + { + *f = &device_option::is_great_s; + } + else + { + ret = false; + } + + return ret; +} +bool device_option::get_between(const char* type, bool(**f)(gb_json*, void*, void*, void*)) +{ + bool ret = true; + if (strcmp(type, JSON_SANE_TYPE_BOOL) == 0) + { + *f = &device_option::is_between_b; + } + else if (strcmp(type, JSON_SANE_TYPE_INT) == 0) + { + *f = &device_option::is_between_i; + } + else if (strcmp(type, JSON_SANE_TYPE_FIXED) == 0) + { + *f = &device_option::is_between_f; + } + else if (strcmp(type, JSON_SANE_TYPE_STRING) == 0) + { + *f = &device_option::is_between_s; + } + else + { + ret = false; + } + + return ret; +} + +bool device_option::is_string_function(const char* expr, std::string& name, int& start, int& cnt) +{ + bool ret = false; + std::string exp(expr); + size_t pos = exp.find("."); + + cnt = 0; + if (pos != std::string::npos) + { + name = exp.substr(0, pos++); + exp.erase(0, pos); + if (exp.find("left(") == 0) + { + exp.erase(0, 5); + start = 0; + cnt = atoi(exp.c_str()); + pos = 0; + } + else if (exp.find("right(") == 0) + { + exp.erase(0, 6); + start = -1 * atoi(exp.c_str()); + cnt = -1; + pos = 0; + } + else if (exp.find("mid(") == 0) + { + exp.erase(0, 4); + start = atoi(exp.c_str()); + pos = exp.find(","); + if (pos != std::string::npos) + { + exp.erase(0, pos + 1); + cnt = atoi(exp.c_str()); + pos = 0; + } + } + if (pos == 0) + { + pos = exp.find(")"); + ret = pos != std::string::npos && (cnt > 0 || cnt == -1); + } + } + + return ret; +} +std::string device_option::from_text_value(const char* type, const char* text_val) +{ + std::string real_v(""); + + if (strcmp(type, JSON_SANE_TYPE_BOOL) == 0) + { + bool v = STRICMP(text_val, "true") == 0; + real_v = std::string((char*)&v, sizeof(v)); + } + else if (strcmp(type, JSON_SANE_TYPE_INT) == 0) + { + int v = atoi(text_val); + real_v = std::string((char*)&v, sizeof(v)); + } + else if (strcmp(type, JSON_SANE_TYPE_FIXED) == 0) + { + double v = atof(text_val); + real_v = std::string((char*)&v, sizeof(v)); + } + else if (strcmp(type, JSON_SANE_TYPE_STRING) == 0) + { + real_v = text_val; + } + + return std::move(real_v); +} +bool device_option::parse_simple_logic_expression(gb_json* root, const char* expr, std::string* name, EXPRCALC& calc) +{ + const char* opers[] = { ">", "<", "!", "=", "." }, * tag = nullptr, * dot = strstr(expr, "."); + + for (auto& v : opers) + { + tag = strstr(expr, v); + if (tag) + break; + } + + if (!tag) + return false; + + if (dot && dot < tag) // is mode.!enable ? + { + tag = dot; + } + + std::string n(expr, tag - expr); + bool ret = true; + gb_json *child = nullptr; + + if (name) + *name = n; + + calc.name = n; + root->get_value(n.c_str(), child); + if (!child) + return false; + if (!child->get_value("type", n)) + { + child->release(); + return false; + } + child->release(); + + if (*tag == '>') + { + if (tag[1] == '=') + { + if (!device_option::get_less(n.c_str(), &calc.compare)) + return false; + calc.not_op = true; + tag++; + } + else + { + if (!device_option::get_great(n.c_str(), &calc.compare)) + return false; + calc.not_op = false; + } + tag++; + } + else if (*tag == '<') + { + if (tag[1] == '=') + { + if (!device_option::get_great(n.c_str(), &calc.compare)) + return false; + calc.not_op = true; + tag++; + } + else + { + if (!device_option::get_less(n.c_str(), &calc.compare)) + return false; + calc.not_op = false; + } + tag++; + } + else if (*tag == '!') + { + if (tag[1] == '=') + { + if (!device_option::get_equal(n.c_str(), &calc.compare)) + return false; + calc.not_op = true; + tag++; + } + else + { + return false; + } + tag++; + } + else if (*tag == '=') + { + if (tag[1] == '=') + { + if (!device_option::get_equal(n.c_str(), &calc.compare)) + return false; + calc.not_op = false; + tag++; + } + else + { + return false; + } + tag++; + } + else if (*tag++ == '.') + { + calc.compare = &device_option::is_opt_enabled; + if (strcmp(tag, "enabled") == 0) + { + calc.not_op = false; + } + else if (strcmp(tag, "!enabled") == 0) + { + calc.not_op = true; + } + else + { + // substr function ... + if (strstr(tag, "left(") || strstr(tag, "right(") || strstr(tag, "mid(")) + { + const char* end = nullptr, *start = strstr(tag, "("); + int l = string_util::find_end_of_pair(start + 1, '(', ')'); + bool ret = start[++l] == ')'; + + if (ret) + { + std::string exp(calc.name + (start + l + 1)), + name1(tag, start + l - tag + 1); + name1.insert(0, calc.name + "."); + ret = device_option::parse_simple_logic_expression(root, exp.c_str(), name, calc); + if (ret) + calc.name = std::move(name1); + } + + return ret; + } + + return false; + } + tag = ""; + } + else + { + return false; + } + + // value ... + if (*tag) + { + if (*tag == '[') + { + const char* next = strstr(tag++, ","); + if (next++ == nullptr) + { + ret = false; + } + else + { + std::string v(tag, next - tag - 1); + calc.val1 = device_option::from_text_value(n.c_str(), v.c_str()); + tag = strstr(next, "]"); + if (!tag) + ret = false; + else + { + v = std::string(next, tag - next); + calc.val2 = device_option::from_text_value(n.c_str(), v.c_str()); + } + } + } + else + { + calc.val1 = device_option::from_text_value(n.c_str(), tag); + } + } + + return ret; +} +void device_option::init_condition(const char* expr, void* param) +{ + if (((device_option*)param)->compare_.count(expr)) + return; + + std::string name(""); + EXPRCALC calc; + + if (device_option::parse_simple_logic_expression(((device_option*)param)->origin_, expr, &name, calc)) + { + ((device_option*)param)->compare_[expr] = calc; + + if (std::find(((device_option*)param)->master_opts_.begin(), ((device_option*)param)->master_opts_.end(), name) + == ((device_option*)param)->master_opts_.end()) + ((device_option*)param)->master_opts_.push_back(name); + } +} +bool device_option::calc_simple_logic_expression(const char* expr, void* param) +{ + device_option* obj = (device_option*)param; + bool ret = true; + + if (obj->compare_.count(expr)) + { + gb_json* child = nullptr; + bool substr = false; + int start = 0, cnt = 0; + + if(obj->now_) + obj->now_->get_value(obj->compare_[expr].name.c_str(), child); + else + obj->origin_->get_value(obj->compare_[expr].name.c_str(), child); + if (!child) + { + // check string function ... + std::string name(""); + substr = device_option::is_string_function(expr, name, start, cnt); + if (substr) + { + if (obj->now_) + obj->now_->get_value(name.c_str(), child); + else + obj->origin_->get_value(name.c_str(), child); + } + } + if (child) + { + bool bv = false; + int nv = 0; + double dv = .0f; + std::string sv(""); + void* val = nullptr; + + child->get_value("type", sv); + if (strcmp(sv.c_str(), JSON_SANE_TYPE_BOOL) == 0) + { + sv = get_real_value(child, false); + } + else if (strcmp(sv.c_str(), JSON_SANE_TYPE_INT) == 0) + { + sv = get_real_value(child, false); + } + else if (strcmp(sv.c_str(), JSON_SANE_TYPE_FIXED) == 0) + { + sv = get_real_value(child, false); + } + else if (strcmp(sv.c_str(), JSON_SANE_TYPE_STRING) == 0) + { + child->get_value("cur", sv); + if (substr) + { + if (start < 0) + start = sv.length() + start; + if (start > 0 && start < sv.length()) + sv.erase(0, start); + if (cnt > 0 && cnt < sv.length()) + sv.erase(cnt); + } + } + val = &sv[0]; + ret = obj->compare_[expr].compare(child, val, &obj->compare_[expr].val1[0], &obj->compare_[expr].val2[0]) ^ obj->compare_[expr].not_op; + + child->release(); + } + } + + return ret; +} + +void device_option::clear_for_reconstruct(void) +{ + if (now_) + now_->release(); + now_ = nullptr; + + master_opts_.clear(); + compare_.clear(); + + for (auto& v : slaver_) + delete v.second; + slaver_.clear(); + + for (auto& v : range_value_) + delete v.second; + range_value_.clear(); + + for (auto& v : init_value_) + delete v.second; + init_value_.clear(); + + for (auto& v : support_value_) + delete v.second; + support_value_.clear(); + + depend_opts_.clear(); +} +gb_json* device_option::group_opt(const char* title) +{ + gb_json* jsn = new gb_json(); + + jsn->set_value("title", title); + jsn->set_value("type", "group"); + + return jsn; +} +int device_option::next_group(int start) +{ + for (; start < origin_->children(); ++start) + { + gb_json* child = origin_->child(start); + std::string str(""); + + child->get_value("type", str); + child->release(); + + if (str == JSON_SANE_TYPE_GROUP) + break; + } + + return start; +} +int device_option::insert_group(const char* name, const char* title) +{ + gb_json* group = nullptr; + int ind = origin_->children(); + + origin_->get_value(name, group); + if (group) + { + ind = origin_->index(group); + group->release(); + } + else + { + for (auto& v : g_known_group_with_sn) + { + if (v.name == name) + { + title = v.title.c_str(); + break; + } + + gb_json* child = nullptr; + origin_->get_value(v.name.c_str(), child); + if (child) + { + if (group) + group->release(); + group = child; + } + } + + if (group) + { + ind = origin_->index(group) + 1; + group->release(); + } + else + ind = 0; + ind = next_group(ind); + group = group_opt(title); + origin_->insert(ind, name, group); + group->release(); + } + + return ind; +} +void device_option::insert_option(gb_json* opt, sane_opt_provider* from, const char* group) +{ + // first compare version, reserve higher version + // second compare position, replace with last insertion + // last, sort by position + gb_json* existing = nullptr; + bool added = true; + + origin_->get_value(opt->key().c_str(), existing); + if (existing) + { + int vo = 0, vn = 0; + opt->get_value("ver", vn); + existing->get_value("ver", vo); + if (vn > vo) + { + // replace and discard all following options ... + write_log("SANE-OPT: use %s::%s(ver: %d) replaced with %s::%s(ver: %d)\n", src_[existing->key()]->from(), existing->key().c_str(), vo + , from->from(), opt->key().c_str(), vn); + + vo = origin_->index(existing); + origin_->remove(vo); + if (src_.count(existing->key())) + { + // disable discarded options ... + sane_opt_provider* next = src_[existing->key()]->get_following(existing->key().c_str()); + src_[existing->key()]->enable(existing->key().c_str(), false); + while (next) + { + next->enable(existing->key().c_str(), false); + + sane_opt_provider* n = next->get_following(existing->key().c_str()); + next->release(); + next = n; + } + + src_[existing->key()]->release(); + src_.erase(existing->key()); + } + + origin_->insert(vo, opt->key().c_str(), opt); + opt->add_ref(); + + src_[opt->key()] = from; + from->add_ref(); + } + else if (vn == vo) + { + // insert into following queue ... + write_log("SANE-OPT: inserting %s::%s to provider queue ...\n", from->from(), opt->key().c_str()); + opt->get_value("pos", vn); + existing->get_value("pos", vo); + if (vo < vn) + { + sane_opt_provider* prev = src_[existing->key()], + *que = src_[existing->key()]->get_following(existing->key().c_str()); + + prev->add_ref(); + while (que) + { + std::string text(que->get_opt_json()); + gb_json* jsn = new gb_json(), *child = nullptr; + + jsn->attach_text(&text[0]); + jsn->get_value(existing->key().c_str(), child); + if (child) + { + child->get_value("ver", vo); + child->release(); + } + jsn->release(); + if (vo >= vn) + break; + prev->release(); + prev = que; + + que = que->get_following(existing->key().c_str()); + } + prev->set_following_provider(opt->key().c_str(), from); + if (vo == vn) + { + // replace ... + if(que) + { + sane_opt_provider* next = que->get_following(opt->key().c_str()); + que->set_following_provider(opt->key().c_str(), nullptr); + if (next) + { + from->set_following_provider(opt->key().c_str(), next); + next->release(); + } + que->release(); // release get_following reference + } + } + else + { + // insert ... + from->set_following_provider(opt->key().c_str(), que); + if (que) + que->release(); + } + prev->release(); + } + else if (vo > vn) + { + from->set_following_provider(opt->key().c_str(), src_[existing->key()]); + src_[existing->key()]->release(); + src_[opt->key()] = from; + from->add_ref(); + } + else + { + // replace ... + vo = origin_->index(existing); + origin_->remove(vo); + if (src_.count(existing->key())) + { + sane_opt_provider* next = src_[existing->key()]->get_following(existing->key().c_str()); + src_[existing->key()]->set_following_provider(existing->key().c_str(), nullptr); + src_[existing->key()]->release(); + src_.erase(existing->key()); + while (next) + { + sane_opt_provider* next1 = next->get_following(existing->key().c_str()); + next->set_following_provider(existing->key().c_str(), nullptr); + next->release(); + next = next1; + } + } + + origin_->insert(vo, opt->key().c_str(), opt); + opt->add_ref(); + + src_[opt->key()] = from; + from->add_ref(); + } + + { + std::string logi("SANE-OPT: option '"); + logi += opt->key() + "' queue: " + src_[opt->key().c_str()]->from(); + + sane_opt_provider* next = src_[opt->key().c_str()]->get_following(opt->key().c_str()); + while (next) + { + logi += std::string(" -> ") + next->from(); + + sane_opt_provider* next1 = next; + next = next->get_following(opt->key().c_str()); + next1->release(); + } + write_log("%s\n", logi.c_str()); + } + } + else + { + // discard new option ... + write_log("SANE-OPT: discard %s::%s(ver: %d) for %s::%s(ver: %d) existed!\n", from->from(), opt->key().c_str(), vn + , src_[existing->key()]->from(), existing->key().c_str(), vo); + + // disable discarded option + from->enable(existing->key().c_str(), false); + added = false; + } + + existing->release(); + } + else + { + int index = -1; + + if (group) + index = insert_group(group, group); + index = next_group(index + 1); + + origin_->insert(index, opt->key().c_str(), opt); + src_[opt->key()] = from; + from->add_ref(); + } + + if (added) + { + // restore to default value ... + int size = 0; + bool can_auto = false; + std::string val(get_option_value(opt->key().c_str(), SANE_ACTION_GET_DEFAULT_VALUE, &size)); + + if (!opt->get_value("auto", can_auto) || can_auto) + { + val.resize(size); + from->set_value(opt->key().c_str(), &val[0]); + } + } +} +bool device_option::arrange_raw_json(sane_opt_provider* sop) +{ + std::vector ungroup; + std::map> ingroup; + gb_json* jsn = new gb_json(), *child = nullptr; + std::string text(sop->get_opt_json()), str(""); + bool ret = jsn->attach_text(&text[0]); + + if (ret) + { + if (!origin_) + origin_ = new gb_json(); + + text.clear(); + child = jsn->first_child(); + while (child) + { + child->get_value("type", str); + if (str != JSON_SANE_TYPE_GROUP) // omit group + { + child->get_value("group", str); + insert_option(child, sop, str.empty() ? nullptr : str.c_str()); + } + child->release(); + child = jsn->next_child(); + } + } + jsn->release(); + + return ret; +} +void device_option::init_depends(gb_json* opt) +{ + gb_json* range = nullptr; + std::string dpnd(""); + + if (opt->get_value("depend", dpnd) && !dpnd.empty()) + { + simple_logic* logic = new simple_logic(); + int pos = 0; + + if (logic->parse(dpnd.c_str(), &pos, &device_option::init_condition, this)) + slaver_[opt->key().c_str()] = logic; + else + delete logic; + } + + // values ... + opt->get_value("range", range); + if (range) + { + range_value* val = new range_value(); + + opt->get_value("type", dpnd); + if (val->set_value(range, dpnd.c_str(), this)) + range_value_[opt->key()] = val; + else + delete val; + + range->release(); + } + + // default: + range = nullptr; + opt->get_value("default", range); + opt->get_value("type", dpnd); + if (range) + { + condition_value* rv = new condition_value(); + if (rv->set_value(range, dpnd.c_str(), this)) + init_value_[opt->key()] = rv; + else + delete rv; + + range->release(); + } + else if (opt->get_value("default", dpnd)) + { + if (dpnd.find("=") == 0) + { + // consitant with another option ... + dpnd.erase(0, 1); + condition_value* rv = new condition_value(); + rv->set_value(nullptr, dpnd.c_str(), this); + init_value_[opt->key()] = rv; + if (std::find(master_opts_.begin(), master_opts_.end(), dpnd) + == master_opts_.end()) + master_opts_.push_back(dpnd); + } + } + + // visible + range = nullptr; + opt->get_value("visible", range); + if(range) + { + condition_value* v = new condition_value(); + if (v->set_value(range, JSON_SANE_TYPE_INT, this)) + support_value_[opt->key()] = v; + else + delete v; + } +} +gb_json* device_option::copy_opt(gb_json* from) +{ + std::string text(from->to_string()); + gb_json* to = new gb_json(); + + if (!to->attach_text(&text[0])) + { + to->release(); + to = nullptr; + } + else + { + std::string val(""); + + to->key() = from->key(); + + // 1: language changed ... (title, description, string-list) + if (to->get_value("title", val)) + to->set_value("title", from_def_lang(val.c_str())); + if (to->get_value("desc", val)) + to->set_value("desc", from_def_lang(val.c_str())); + + // 2: enabled ... + if (slaver_.count(to->key())) + to->set_value("enabled", slaver_[to->key()]->value(&device_option::calc_simple_logic_expression, this)); + + // 3: default value ... + if (init_value_.count(to->key())) + { + std::string val(init_value_[to->key()]->value(&device_option::calc_simple_logic_expression, this)); + std::string type(""); + to->get_value("type", type); + + if (type == JSON_SANE_TYPE_BOOL) + to->set_value("default", *(bool*)val.c_str()); + else if(type == JSON_SANE_TYPE_INT) + to->set_value("default", *(int*)val.c_str()); + else if(type == JSON_SANE_TYPE_FIXED) + to->set_value("default", *(double*)val.c_str()); + else if(type == JSON_SANE_TYPE_STRING) + to->set_value("default", (const wchar_t*)val.c_str()); + } + + // 4: range value ... + if (range_value_.count(to->key())) + { + gb_json* src = nullptr, * dst = nullptr; + from->get_value("range", src); + to->get_value("range", dst); + + if (src && dst) + { + dst->clear(src->is_array()); + dst->key() = src->key(); + to->get_value("type", val); + if (val == JSON_SANE_TYPE_BOOL) + { + if (dst->is_array()) + { + val = range_value_[to->key()]->first_value(&device_option::calc_simple_logic_expression, this); + for (int i = 0; i < range_value_[to->key()]->count(); ++i) + { + if (!val.empty()) + *dst += *(bool*)&val[0]; + val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this); + } + } + else + { + val = range_value_[to->key()]->first_value(&device_option::calc_simple_logic_expression, this); + dst->set_value("min", *(bool*)&val[0]); + val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this); + dst->set_value("max", *(bool*)&val[0]); + val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this); + dst->set_value("step", *(bool*)&val[0]); + } + } + else if (val == JSON_SANE_TYPE_INT) + { + if (dst->is_array()) + { + val = range_value_[to->key()]->first_value(&device_option::calc_simple_logic_expression, this); + for (int i = 0; i < range_value_[to->key()]->count(); ++i) + { + if (!val.empty()) + *dst += *(int*)&val[0]; + val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this); + } + } + else + { + val = range_value_[to->key()]->first_value(&device_option::calc_simple_logic_expression, this); + dst->set_value("min", *(int*)&val[0]); + val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this); + dst->set_value("max", *(int*)&val[0]); + val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this); + dst->set_value("step", *(int*)&val[0]); + } + } + else if (val == JSON_SANE_TYPE_FIXED) + { + if (dst->is_array()) + { + val = range_value_[to->key()]->first_value(&device_option::calc_simple_logic_expression, this); + for (int i = 0; i < range_value_[to->key()]->count(); ++i) + { + if (!val.empty()) + *dst += *(double*)&val[0]; + val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this); + } + } + else + { + val = range_value_[to->key()]->first_value(&device_option::calc_simple_logic_expression, this); + dst->set_value("min", *(double*)&val[0]); + val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this); + dst->set_value("max", *(double*)&val[0]); + val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this); + dst->set_value("step", *(double*)&val[0]); + } + } + else if (val == JSON_SANE_TYPE_STRING) + { + if (dst->is_array()) + { + val = range_value_[to->key()]->first_value(&device_option::calc_simple_logic_expression, this); + for(int i = 0; i < range_value_[to->key()]->count(); ++i) + { + if(!val.empty()) + *dst += from_def_lang(val.c_str()); + val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this); + } + } + else + { + val = range_value_[to->key()]->first_value(&device_option::calc_simple_logic_expression, this); + dst->set_value("min", from_def_lang(val.c_str())); + val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this); + dst->set_value("max", from_def_lang(val.c_str())); + val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this); + dst->set_value("step", from_def_lang(val.c_str())); + } + } + } + + if (src) + src->release(); + if (dst) + dst->release(); + } + } + + return to; +} +int device_option::visibility(gb_json* jsn) +{ + int visb = OPT_VISIBLE_ALL; + + if (!jsn->get_value("visible", visb)) + { + if (support_value_.count(jsn->key())) + { + std::string val(support_value_[jsn->key()]->value(&device_option::calc_simple_logic_expression, this)); + visb = *(int*)&val[0]; + } + } + + return visb; +} +bool device_option::to_now(bool init, bool* changed) +{ + if (!origin_) + return false; + + gb_json* from = nullptr, * to = nullptr, * tmp = new gb_json(); + + from = origin_->first_child(); + while (from) + { + std::string name(from->key()); + + if (init) + init_depends(from); + + int pub = visibility(from); + if (pub == OPT_VISIBLE_HIDE || pub == OPT_VISIBLE_NOT_SUPPORT) + { + int auth = 0; + from->get_value("auth", auth); + if (pub == OPT_VISIBLE_NOT_SUPPORT || !user_ || !user_(auth)) + { + from->release(); + from = origin_->next_child(); + continue; + } + } + + to = copy_opt(from); + from->release(); + if (to) + { + // copy cur value ... + if (now_) + { + gb_json* now = nullptr; + now_->get_value(to->key().c_str(), now); + if (now) + { + std::string type(""); + now->get_value("type", type); + if (type == JSON_SANE_TYPE_BOOL) + { + bool v = false; + now->get_value("cur", v); + refine_data_to_range(to, &v); + to->set_value("cur", v); + } + else if (type == JSON_SANE_TYPE_INT) + { + int v = 0; + now->get_value("cur", v); + refine_data_to_range(to, &v); + to->set_value("cur", v); + } + else if (type == JSON_SANE_TYPE_FIXED) + { + double v = .0f; + now->get_value("cur", v); + refine_data_to_range(to, &v); + to->set_value("cur", v); + } + else if (type == JSON_SANE_TYPE_STRING) + { + std::string v(""); + int size = 0, s1 = 0; + char *vv = nullptr; + + to->get_value("size", size); + now->get_value("size", s1); + if (size < s1) + size = s1; + vv = new char[size + 4]; + memset(vv, 0, size + 4); + now->get_value("cur", v); + strcpy(vv, v.c_str()); + refine_string_data(to, vv); + to->set_value("cur", vv); + delete[] vv; + } + now->release(); + } + } + + tmp->set_value(name.c_str(), to); + to->release(); + + from = origin_->next_child(); + } + else + { + tmp->release(); + tmp = nullptr; + break; + } + } + + if (changed) + { + if (!now_ || !tmp) + *changed = true; + else + *changed = *now_ != *tmp; + } + + if (now_) + now_->release(); + now_ = tmp; + + return now_ != nullptr; +} + +std::string device_option::option_value(gb_json* jsn, bool def_val) +{ + std::string type(""), key(def_val ? "default" : "cur"); + + jsn->get_value("type", type); + if (type == "bool") + { + bool v = false; + jsn->get_value(key.c_str(), v); + type = std::string((char*)&v, sizeof(v)); + } + else if (type == "int") + { + int v = 0; + jsn->get_value(key.c_str(), v); + type = std::string((char*)&v, sizeof(v)); + } + else if (type == "float") + { + double v = .0f; + jsn->get_value(key.c_str(), v); + type = std::string((char*)&v, sizeof(v)); + } + else if (type == "string") + { + if (!jsn->get_value(key.c_str(), type)) + type = ""; + } + + return std::move(type); +} + +std::string device_option::trans_group(const char* utf8, bool to_title) +{ + if (to_title) + { + for (auto& v : g_known_group_with_sn) + { + if (v.name == utf8) + return v.title; + } + } + else + { + for (auto& v : g_known_group_with_sn) + { + if (v.title == utf8) + return v.name; + } + } + + return utf8; +} +std::string device_option::get_group(int ind, bool title) +{ + if (ind >= 0 && ind < _countof(g_known_group_with_sn)) + return title ? g_known_group_with_sn[ind].title : g_known_group_with_sn[ind].name; + else + return ""; +} +void device_option::set_from_default_language_api(const char*(*fdl)(const char*)) +{ + from_def_lang = fdl ? fdl : empty_from_default_language; +} + +void device_option::clear(void) +{ + clear_for_reconstruct(); + + if (origin_) + origin_->release(); + origin_ = nullptr; + + for (auto& v : src_) + v.second->release(); + src_.clear(); +} +bool device_option::add(sane_opt_provider* sop) +{ + bool ret = false; + + clear_for_reconstruct(); + if (arrange_raw_json(sop)) + { + ret = to_now(true, nullptr); + if (!ret) + clear(); + } + else + { + clear(); + } + + return ret; +} +bool device_option::refine_data(const char* name, void* value) +{ + bool refined = false; + gb_json* child = nullptr; + + now_->get_value(name, child); + if (child) + { + std::string type(""); + child->get_value("type", type); + if (type == JSON_SANE_TYPE_BOOL) + { + refined = refine_data_to_range(child, value); + } + else if (type == JSON_SANE_TYPE_INT) + { + refined = refine_data_to_range(child, value); + } + else if (type == JSON_SANE_TYPE_FIXED) + { + refined = refine_data_to_range(child, value); + } + else if (type == JSON_SANE_TYPE_STRING) + { + refined = refine_string_data(child, value); + } + + child->release(); + } + + return refined; +} +int device_option::update_data(const char* name, void* value, bool reorder_if_need) +{ + int err = SCANNER_ERR_NO_DATA; + + if (!name) + { + to_now(false, nullptr); + err = SCANNER_ERR_RELOAD_OPT_PARAM; + } + else if(now_) + { + std::string type(""); + gb_json* child = nullptr; + + err = SCANNER_ERR_DEVICE_NOT_SUPPORT; + now_->get_value(name, child); + if (child) + { + bool ro = false; + if ((child->get_value("readonly", ro) && ro) + || (child->get_value("auth", err) && user_ && !user_(err))) + { + err = SCANNER_ERR_ACCESS_DENIED; + } + else + { + std::string pre(device_option::option_value(child, false)); + bool changed = false; + + // pass to sane_opt_provider ... + err = SCANNER_ERR_OK; + if (src_.count(name)) + { + sane_opt_provider* opt = src_[name]; + opt->add_ref(); + + while (opt) + { + int r = opt->set_value(name, value); + if (r) + err = r; + + sane_opt_provider* next = opt->get_following(name); + opt->release(); + opt = next; + } + } + + if (err == SCANNER_ERR_OK || err == SCANNER_ERR_NOT_EXACT + || err == SCANNER_ERR_RELOAD_IMAGE_PARAM || err == SCANNER_ERR_RELOAD_OPT_PARAM + || err == SCANNER_ERR_CONFIGURATION_CHANGED) + { + child->get_value("type", type); + if (type == JSON_SANE_TYPE_BOOL) + { + child->set_value("cur", *(bool*)value); + changed = *(bool*)value != *(bool*)pre.c_str(); + } + else if (type == JSON_SANE_TYPE_INT) + { + child->set_value("cur", *(int*)value); + changed = *(int*)value != *(int*)pre.c_str(); + } + else if (type == JSON_SANE_TYPE_FIXED) + { + child->set_value("cur", *(double*)value); + changed = !IS_DOUBLE_EQUAL(*(double*)value, *(double*)pre.c_str()); + } + else if (type == JSON_SANE_TYPE_STRING) + { + child->set_value("cur", (char*)value); + changed = pre != (char*)value; + } + + // set paper-w and paper-h + if (strcmp(name, SANE_STD_OPT_NAME_PAPER) == 0 || + strcmp(name, SANE_STD_OPT_NAME_LATERAL) == 0) + { + if (src_.count(SANE_STD_OPT_NAME_PAPER_W)) + { + size_t wl = 0, hl = 0; + char *ws = src_[SANE_STD_OPT_NAME_PAPER_W]->get_value(SANE_STD_OPT_NAME_PAPER_W, nullptr, &wl), + *hs = src_[SANE_STD_OPT_NAME_PAPER_H]->get_value(SANE_STD_OPT_NAME_PAPER_H, nullptr, &hl); + std::string w(ws ? std::string(ws, wl) : ""), + h(hs ? std::string(hs, hl) : ""), + lateral(get_option_value(SANE_STD_OPT_NAME_LATERAL, SANE_ACTION_GET_VALUE)); + gb_json *jsnl = nullptr; + + if (ws) + free(ws); + if (hs) + free(hs); + + now_->get_value(SANE_STD_OPT_NAME_LATERAL, jsnl); + if (jsnl) + { + if (device_option::is_opt_enabled(jsnl, nullptr, nullptr, nullptr)) + { + refine_data(SANE_STD_OPT_NAME_LATERAL, &lateral[0]); + if (*(bool*)&lateral[0]) + w.swap(h); + } + jsnl->release(); + } + + gb_json* pw = nullptr, + * ph = nullptr; + now_->get_value(SANE_STD_OPT_NAME_PAPER_W, pw); + now_->get_value(SANE_STD_OPT_NAME_PAPER_H, ph); + if (pw) + { + pw->set_value("cur", *(double*)&w[0]); + pw->release(); + } + if (ph) + { + ph->set_value("cur", *(double*)&h[0]); + ph->release(); + } + name = SANE_STD_OPT_NAME_PAPER; // ensure invoked refresh ... + } + } + + if (reorder_if_need && changed && // value has changed + std::find(master_opts_.begin(), master_opts_.end(), name) != master_opts_.end()) // can affect others + { + changed = false; + + do + { + changed = false; + to_now(false, &changed); + } while (changed); + + err = SCANNER_ERR_RELOAD_OPT_PARAM; + } + } // provider has processed right + } // not read-only option + + child->release(); + + } // has option named 'name' + } // has initialized + + return err; +} +int device_option::restore(sane_opt_provider* holder) +{ + gb_json* child = nullptr, * cur = now_; + + if (cur) + { + cur->add_ref(); + child = cur->first_child(); + while (child) + { + if (src_.count(child->key()) && src_[child->key()] == holder + && is_auto_restore_default(child->key().c_str())) + { + std::string val(device_option::option_value(child, true)); + update_data(child->key().c_str(), &val[0], false); + } + child->release(); + child = cur->next_child(); + } + cur->release(); + + bool changed = false; + do + { + changed = false; + to_now(false, &changed); + } while (changed); + } + + return SCANNER_ERR_RELOAD_OPT_PARAM; +} + +int device_option::count(void) +{ + gb_json* jsn = now_ ? now_ : origin_; + + if (jsn) + return jsn->children(); + else + return 0; +} +bool device_option::is_auto_restore_default(const char* name) +{ + gb_json* jsn = now_ ? now_ : origin_, + * child = nullptr; + bool support = true; + + jsn->get_value(name, child); + if (child) + { + if (!child->get_value("auto", support)) + support = true; + + child->release(); + } + + return support; +} +std::string device_option::get_name_by_sane_id(int sane_ind) +{ + std::string value(""); + gb_json* jsn = now_ ? now_ : origin_; + + if (sane_ind > 0 && sane_ind - 1 < jsn->children()) + { + gb_json* child = now_->child(sane_ind - 1); + //child->get_value("name", value); + value = child->key(); + child->release(); + } + + return std::move(value); +} +std::string device_option::get_option_value_type(const char* name, size_t* size) +{ + std::string value(""); + gb_json* jsn = now_ ? now_ : origin_; + + if (jsn) + { + gb_json* child = nullptr; + + jsn->get_value(name, child); + if (child) + { + child->get_value("type", value); + if (size) + { + int v = 0; + child->get_value("size", v); + *size = v; + } + child->release(); + } + } + + return std::move(value); +} +std::string device_option::get_option_value(const char* name, int type, int* size, void* in_data) +{ + std::string value(""); + gb_json* jsn = now_ ? now_ : origin_; + + if (jsn) + { + if (!name) + { + value = jsn->to_string(); + } + else + { + gb_json* child = nullptr; + bool own_read = false; + + jsn->get_value(name, child); + if (child) + { + if (type == SANE_ACTION_GET_ENTIRE_JSON) + value = child->to_string(); + else if (child->get_value("ownread", own_read) && own_read) + { + if (src_.count(name)) + { + size_t len = 0; + char* v = src_[name]->get_value(name, in_data, &len); + if (v) + { + value = std::string(v, len); + free(v); + } + } + } + else + value = device_option::option_value(child, type == SANE_ACTION_GET_DEFAULT_VALUE); + + if (size) + { + int n = 0; + child->get_value("size", n); + *size = n; + } + + child->release(); + } + } + } + + return std::move(value); +} +std::string device_option::get_option_field_string(const char* name, const char* key) +{ + std::string value(""); + gb_json* jsn = now_ ? now_ : origin_; + + if (jsn) + { + gb_json* child = nullptr; + + jsn->get_value(name, child); + if (child) + { + child->get_value(key, value); + child->release(); + } + } + + return std::move(value); +} +std::string device_option::get_option_value_type(int sane_ind, size_t* size) +{ + std::string value(""); + gb_json* jsn = now_ ? now_ : origin_; + + if (sane_ind > 0 && sane_ind - 1 < jsn->children()) + { + gb_json* child = now_->child(sane_ind - 1); + child->get_value("type", value); + if (size) + { + int v = 0; + child->get_value("size", v); + *size = v; + } + child->release(); + } + + return std::move(value); +} +std::string device_option::get_option_value(int sane_ind, int type, int* size, void* in_data) +{ + std::string value(""); + + if (now_) + { + if (sane_ind <= 0) + { + value = std::move(now_->to_string()); + } + else if(sane_ind - 1 < now_->children()) + { + gb_json* child = now_->child(sane_ind - 1); + std::string name(child->key()); + + child->release(); + value = std::move(get_option_value(name.c_str(), type, size, in_data)); + } + } + + return std::move(value); +} diff --git a/sdk/sane_opt_json/device_opt.h b/sdk/sane_opt_json/device_opt.h new file mode 100644 index 0000000..3cd0255 --- /dev/null +++ b/sdk/sane_opt_json/device_opt.h @@ -0,0 +1,412 @@ +// +// device_opt: option manager of device +// +// Created: 2023-09-07 +// + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "simple_logic.h" +#include +#include +#include + +class sane_opt_provider; + +class device_option : public refer +{ + gb_json* origin_; + gb_json* now_; + std::map src_; + std::vector master_opts_; // options that value changed will affect others + std::map slaver_; + std::function user_; + std::function log_; + + typedef struct _expr_calc + { + std::string name; + std::string val1; + std::string val2; + bool not_op; + bool(*compare)(gb_json*, void* val, void* v1, void* v2); + }EXPRCALC; + std::map compare_; // simple condition compare + + class condition_value + { + typedef struct _cond_val + { + simple_logic *logic; + std::string value; + }CONDVAL; + std::vector vals_; + device_option* parent_; // if this value was valid, the condition value is a consistant value with vals_[0].value + + void clear(void) + { + for (auto& v : vals_) + { + if (v.logic) + delete v.logic; + } + vals_.clear(); + parent_ = nullptr; + } + + public: + condition_value() : parent_(nullptr) + {} + ~condition_value() + { + clear(); + } + + public: + bool set_value(gb_json* jsn, const char* type, device_option* parent); // jsn contains only ONE value or its object, or nullptr for a consistant value + std::string value(bool(*compare)(const char*, void*), void* param); + }; + class range_value + { + bool is_range_; // true - range; false - list + int val_ind_; + std::vector vals_; + + void clear(void) + { + for (auto& v : vals_) + delete v; + vals_.clear(); + } + + public: + range_value() : is_range_(false), val_ind_(0) + {} + ~range_value() + { + clear(); + } + + public: + bool set_value(gb_json* jsn, const char* type, device_option *parent); // jsn contains all range object + int count(void) + { + return vals_.size(); + } + bool is_range(void) + { + return is_range_; + } + + // return first element in list-value or min-value of range + std::string first_value(bool(*compare)(const char*, void*), void* param) + { + val_ind_ = 0; + if (val_ind_ < count()) + return vals_[val_ind_]->value(compare, param); + else + return ""; + } + + // return next element in list-value or max-value of range + std::string next_value(bool(*compare)(const char*, void*), void* param) + { + if (++val_ind_ < count()) + return vals_[val_ind_]->value(compare, param); + else + return ""; + } + }; + std::map range_value_; + std::map init_value_; + std::map support_value_; + std::map > depend_opts_; // values that depend on other option's current value + + static bool is_equal_b(gb_json* opt, void* val, void* v1, void* v2); + static bool is_equal_i(gb_json* opt, void* val, void* v1, void* v2); + static bool is_equal_f(gb_json* opt, void* val, void* v1, void* v2); + static bool is_equal_s(gb_json* opt, void* val, void* v1, void* v2); + + static bool is_less_b(gb_json* opt, void* val, void* v1, void* v2); + static bool is_less_i(gb_json* opt, void* val, void* v1, void* v2); + static bool is_less_f(gb_json* opt, void* val, void* v1, void* v2); + static bool is_less_s(gb_json* opt, void* val, void* v1, void* v2); + + static bool is_great_b(gb_json* opt, void* val, void* v1, void* v2); + static bool is_great_i(gb_json* opt, void* val, void* v1, void* v2); + static bool is_great_f(gb_json* opt, void* val, void* v1, void* v2); + static bool is_great_s(gb_json* opt, void* val, void* v1, void* v2); + + static bool is_between_b(gb_json* opt, void* val, void* v1, void* v2); + static bool is_between_i(gb_json* opt, void* val, void* v1, void* v2); + static bool is_between_f(gb_json* opt, void* val, void* v1, void* v2); + static bool is_between_s(gb_json* opt, void* val, void* v1, void* v2); + + static bool is_opt_enabled(gb_json* opt, void* val, void* v1, void* v2); + + static bool get_equal(const char* type, bool(**f)(gb_json*, void*, void*, void*)); + static bool get_less(const char* type, bool(**f)(gb_json*, void*, void*, void*)); + static bool get_great(const char* type, bool(**f)(gb_json*, void*, void*, void*)); + static bool get_between(const char* type, bool(**f)(gb_json*, void*, void*, void*)); + + // Function: parse string function - .left(cnt), .right(cnt), .mid(start, cnt) + // + // Parameter: expr - expression of string function, e.g. mode.left(2) + // + // name - to receive the final option name, e.g. mode + // + // start - to receive the starting position of the sub-string, negative is for right() + // + // cnt - to receive the length of the sub string, -1 is to end + // + // Return: true if was string function + static bool is_string_function(const char* expr, std::string& name, int& start, int& cnt); + static std::string from_text_value(const char* type, const char* text_val); + static bool parse_simple_logic_expression(gb_json* root, const char* expr, std::string* name, EXPRCALC& calc); + static void init_condition(const char* expr, void* param); + static bool calc_simple_logic_expression(const char* expr, void* param); + + void clear_for_reconstruct(void); + gb_json* group_opt(const char* title); + int next_group(int start); // return index of the next group + int insert_group(const char* name, const char* title); // return index of the group + void insert_option(gb_json* opt, sane_opt_provider* from, const char* group = nullptr); + bool arrange_raw_json(sane_opt_provider* sop); // create origin_ and re-arrange groups + void init_depends(gb_json* opt); + gb_json* copy_opt(gb_json* from); + int visibility(gb_json* jsn); + bool to_now(bool init, bool* changed); + +protected: + static std::string option_value(gb_json* jsn, bool def_val); + + template + static condition_value* to_condition_value(gb_json* jsn, const char* key, const char* type, device_option* parent) + { + condition_value* ret = nullptr; + gb_json* child = nullptr; + + if (!jsn->get_value(key, child)) + { + T v; + if(jsn->get_value(key, v)) + child = new gb_json("", v); + else + { + std::string sv(""); + if (jsn->get_value(key, sv)) + { + // consistant with another option ... + ret = new condition_value(); + ret->set_value(nullptr, sv.c_str(), parent); + if (std::find(parent->master_opts_.begin(), parent->master_opts_.end(), sv) + == parent->master_opts_.end()) + parent->master_opts_.push_back(sv); + } + } + } + + if (child) + { + ret = new condition_value(); + if (!ret->set_value(child, type, parent)) + { + delete ret; + ret = nullptr; + } + child->release(); + } + + return ret; + } + + template + bool get_range(gb_json* jsn, const char* key, T& val) + { + if (jsn->get_value(key, val)) + return true; + + std::string optn(""); + + if (!jsn->get_value(key, optn)) + return false; + + gb_json* opt = nullptr; + if (now_) + now_->get_value(optn.c_str(), opt); + if (!opt && origin_) + origin_->get_value(optn.c_str(), opt); + if (!opt) + return false; + + bool ret = opt->get_value("cur", val); + + opt->release(); + + return ret; + } + + template + bool refine_data_to_range(gb_json* jsn, void* value) + { + bool refined = false; + gb_json* range = nullptr; + + jsn->get_value("range", range); + if (range) + { + T vl, vu, s; + if (get_range(range, "min", vl)) + { + if (*(T*)value < vl) + { + *(T*)value = vl; + refined = true; + } + else if (get_range(range, "max", vu)) + { + if (*(T*)value > vu) + { + *(T*)value = vu; + refined = true; + } + else if (get_range(range, "step", s)) + { + // step check, FIXED me ... + T cur(*(T*)value); + + cur -= vl; + cur /= s; + if (!IS_DOUBLE_EQUAL(cur, (int)cur)) + { + cur *= s; + cur += vl; + if (cur > vu) + cur = vu; + refined = !IS_DOUBLE_EQUAL(cur, *(T*)value); + *(T*)value = cur; + } + } + } + } + else + { + gb_json* val = range->first_child(); + bool found = false; + + while (val) + { + if (val->value(vl)) + { + if (*(T*)value == vl) + { + found = true; + val->release(); + break; + } + } + val->release(); + val = range->next_child(); + } + if (!found) + { + if (jsn->get_value("default", vl)) + { + refined = true; + *(T*)value = vl; + } + } + } + range->release(); + } + + return refined; + } + + template + void write_log(const char* fmt, Args ... args) + { + if (log_) + { + size_t size = snprintf(nullptr, 0, fmt, args ...) + 2; + std::unique_ptr buf(new char[size]); + + snprintf(buf.get(), size, fmt, args ...); + log_(buf.get()); + } + } + + +public: + device_option(std::function user_priv = std::function() + , std::function log = std::function()); + ~device_option(); + + static std::string trans_group(const char* utf8, bool to_title); + static std::string get_group(int ind, bool title); + static void set_from_default_language_api(const char*(*fdl)(const char*)); + +public: + void clear(void); + bool add(sane_opt_provider* sop); + bool refine_data(const char* name, void* value); // return true if the 'value' is out of range and refined it in the range + int update_data(const char* name, void* value, bool reorder_if_need = true); // return scanner_err. name and value would be null if invoked for language changed + int restore(sane_opt_provider* holder); // + + int count(void); // return option count + bool is_auto_restore_default(const char* name); + std::string get_name_by_sane_id(int sane_ind); + std::string get_option_value_type(const char* name, size_t* size = nullptr); + std::string get_option_value_type(int sane_ind, size_t* size = nullptr); + std::string get_option_field_string(const char* name, const char* key); + std::string get_option_value(const char* name, int type/*OPT_VAL_xxx*/, int* size = nullptr, void* in_data = nullptr); // return whole json-text if name was null + std::string get_option_value(int sane_ind, int type/*OPT_VAL_xxx*/, int* size = nullptr, void* in_data = nullptr); // return whole json-text if name was null +}; + +//{ +// "resolution": { +// "cat": "base", +// "group" : "base", +// "title" : "�ֱ���", +// "desc" : "����ɨ��ͼ��ķֱ���", +// "type" : "int", +// "fix-id" : 34840, +// "size" : 4, +// "cur" : 200, +// "default" : 200, +// "range" : { +// "min": 100, +// "max" : { +// "default": 600, +// "paper==���ɨ��ߴ��Զ����� || paper==���ɨ��ߴ� || paper==�����Ծ�" : 500 +// }, +// "step" : 1 +// } +// }, +// +// "paper": { +// "cat": "base", +// "group" : "base", +// "title" : "ֽ�ųߴ�", +// "desc" : "���ó�ͼ��С", +// "type" : "string", +// "fix-id" : 34831, +// "size" : 44, +// "cur" : "ƥ��ԭʼ�ߴ�", +// "default" : "ƥ��ԭʼ�ߴ�", +// "range" : ["A3", "8��", "A4", "16��", "A5", "A6", "B4", "B5", "B6", "Letter", "Double Letter", "LEGAL", "ƥ��ԭʼ�ߴ�", { +// "resolution<500": "���ɨ��ߴ��Զ�����" +// }, { +// "resolution<500": "���ɨ��ߴ�" +// }, { +// "resolution<500": "�����Ծ�" +// }] +// } +//} diff --git a/sdk/sane_opt_json/simple_logic.cpp b/sdk/sane_opt_json/simple_logic.cpp new file mode 100644 index 0000000..f6a9f0c --- /dev/null +++ b/sdk/sane_opt_json/simple_logic.cpp @@ -0,0 +1,594 @@ + +#include "simple_logic.h" + + +#include + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// logic_expression +namespace string_util +{ + // Function: find the ending position in str + // + // Parameter: str - the string beginning after the first letter 'head' + // + // head - the leading letter, can be emblaced + // + // tail - the ending letter + // + // Return: position at the ending letter, or '\0' in the string + int find_end_of_pair(const char* str, char head, char tail) + { + int end = 0, banlance = 1; + + while (str[end]) + { + if (str[end] == '\\') + { + end++; + if (!str[end]) + break; + + // skip this translating-letter + end++; + continue; + } + + if (str[end] == head) + { + banlance++; + } + else if (str[end] == tail) + { + if (--banlance == 0) + break; + } + + end++; + } + + return end; + } + + void skip_space(const char*& ptr, const char* space) + { + char mark[2] = { 0 }; + + while (*ptr) + { + mark[0] = *ptr; + if (!strstr(space, mark)) + break; + ptr++; + } + } + + void trim(std::string& str, int type) + { + int pos = 0; + + if (type & TRIM_LEFT) + { + for (; pos < str.length(); ++pos) + { + if (str[pos] != ' ') + break; + } + + if (pos) + str.erase(0, pos); + } + if (type & TRIM_RIGHT) + { + pos = str.length() - 1; + for (; pos >= 0; --pos) + { + if (str[pos] != ' ') + break; + } + str.erase(pos + 1); + } + } +}; + + + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// simple_logic +simple_logic::simple_logic() : l_(nullptr), r_(nullptr), oper_(LOGIC_OPER_NONE), expr_(""), not_(false) +{} +simple_logic::simple_logic(const simple_logic& r) : l_(nullptr), r_(nullptr) +{ + copy(r); +} +simple_logic::~simple_logic() +{ + clear(); +} + +void simple_logic::clear(void) +{ + if (l_) + delete l_; + if (r_) + delete r_; + l_ = r_ = nullptr; + + oper_ = LOGIC_OPER_NONE; + expr_ = ""; + not_ = false; +} +void simple_logic::copy(const simple_logic& r) +{ + clear(); + + oper_ = r.oper_; + not_ = r.not_; + expr_ = r.expr_; + if (r.l_) + { + l_ = new simple_logic(); + l_->copy(*r.l_); + } + if (r.r_) + { + r_ = new simple_logic(); + r_->copy(*r.r_); + } +} +void simple_logic::set_not(bool notv) +{ + not_ = notv; +} +bool simple_logic::parse_internal(const char* expr, int* end, void(*leaf)(const char*, void*), void* leaf_param) +{ + const char* ptr = expr, * first = nullptr; + std::vector ele; + std::vector oper; + bool need_oper = false, good = true; + + string_util::skip_space(ptr); + if (*ptr == 0) + return false; + + while (*ptr) + { + if (*ptr == '(') + { + if (need_oper) + { + good = false; + *end = ptr - expr; + break; + } + + int len = string_util::find_end_of_pair(ptr + 1, '(', ')'); + if (ptr[++len] != ')') + { + *end = ptr - expr + len; + good = false; + break; + } + + std::string sub(ptr + 1, len - 1); + if (sub.find("||") == std::string::npos && + sub.find("&&") == std::string::npos && + sub.find("^") == std::string::npos) + { + // count as function ... + ptr += len; + } + else + { + simple_logic* e = new simple_logic(); + int over = 0; + bool not_v = false; + + if (first) + { + while (first < ptr) + { + if (*first == '!') + not_v ^= true; + else + break; + first++; + } + if (first < ptr) + { + *end = first - expr; + good = false; + delete e; + break; + } + first = nullptr; + } + + if (e->parse(sub.c_str(), &over, leaf, leaf_param)) + { + e->set_not(not_v); + ele.push_back(e); + ptr += len; + need_oper = true; + } + else + { + *end = ptr - expr + 1 + over; + good = false; + delete e; + break; + } + } + } + else if (*ptr == '|') + { + if (*(ptr + 1) == '|') + { + if (need_oper || first) + { + if (first) + { + simple_logic* e = new simple_logic(); + e->expr_ = std::string(first, ptr - first); + string_util::trim(e->expr_); + e->oper_ = LOGIC_OPER_LEAF; + ele.push_back(e); + first = nullptr; + if (leaf) + leaf(e->expr_.c_str(), leaf_param); + } + + ptr++; + oper.push_back(LOGIC_OPER_OR); + need_oper = false; + } + else + { + good = false; + *end = ptr - expr; + break; + } + } + else + { + good = false; + *end = ptr - expr + 1; + break; + } + } + else if (*ptr == '&') + { + if (*(ptr + 1) == '&') + { + if (need_oper || first) + { + if (first) + { + simple_logic* e = new simple_logic(); + e->expr_ = std::string(first, ptr - first); + string_util::trim(e->expr_); + e->oper_ = LOGIC_OPER_LEAF; + ele.push_back(e); + first = nullptr; + if (leaf) + leaf(e->expr_.c_str(), leaf_param); + } + + ptr++; + oper.push_back(LOGIC_OPER_AND); + need_oper = false; + } + else + { + good = false; + *end = ptr - expr; + break; + } + } + else + { + good = false; + *end = ptr - expr + 1; + break; + } + } + else if (*ptr == '^') + { + if (need_oper || first) + { + if (first) + { + simple_logic* e = new simple_logic(); + e->expr_ = std::string(first, ptr - first); + string_util::trim(e->expr_); + e->oper_ = LOGIC_OPER_LEAF; + ele.push_back(e); + first = nullptr; + if (leaf) + leaf(e->expr_.c_str(), leaf_param); + } + + oper.push_back(LOGIC_OPER_XOR); + need_oper = false; + } + else + { + good = false; + *end = ptr - expr; + break; + } + } + else + { + // expression ... + if (need_oper) + { + good = false; + *end = ptr - expr; + break; + } + + if (!first) + first = ptr; + } + + ptr++; + string_util::skip_space(ptr); + } + + if (good && first) + { + if (need_oper) + { + *end = first - expr; + good = false; + } + else + { + simple_logic* e = new simple_logic(); + int over = 0; + if (e->parse(first, &over)) + { + ele.push_back(e); + if (e->oper_ == LOGIC_OPER_LEAF && leaf) + leaf(e->expr_.c_str(), leaf_param); + } + else + { + good = false; + *end = first - expr + over; + delete e; + } + } + } + + if (good && oper.size() == ele.size() - 1) + { + simple_logic* root = make_binary(ele, oper); + l_ = root->l_; + r_ = root->r_; + oper_ = root->oper_; + expr_ = root->expr_; + root->l_ = root->r_ = nullptr; + delete root; + *end = ptr - expr; + } + else + { + for (auto& v : ele) + delete v; + } + + return good; +} +simple_logic* simple_logic::make_binary(const std::vector& eles, const std::vector& opers) +{ + if (eles.size() == 0 && opers.size() == 0) + return nullptr; + else if (eles.size() == 1) + return eles[0]; + + int or_pos = -1; + + for (int i = 0; i < opers.size(); ++i) + { + if (opers[i] == LOGIC_OPER_OR) + { + or_pos = i; + break; + } + } + + simple_logic* node = new simple_logic(); + + if (or_pos == -1) + { + //node->l_ = eles[0]; + //node->oper_ = opers[0]; + // + //std::vector re; + //std::vector ro; + //for (int i = 1; i < eles.size(); ++i) + // re.push_back(eles[i]); + //for (int i = 1; i < opers.size(); ++i) + // ro.push_back(opers[i]); + //node->r_ = make_binary(re, ro); + node->r_ = eles[eles.size() - 1]; + node->l_ = eles[eles.size() - 2]; + node->oper_ = opers[opers.size() - 1]; + + simple_logic** left = &node->l_; + int cnt = 2; + + while (++cnt <= eles.size()) + { + simple_logic* n = new simple_logic(); + n->oper_ = opers[opers.size() - cnt + 1]; + n->l_ = eles[eles.size() - cnt]; + n->r_ = *left; + *left = n; + left = &n->l_; + } + } + else + { + std::vector re; + std::vector ro; + + node->oper_ = LOGIC_OPER_OR; + for (int i = 0; i < or_pos + 1; ++i) + re.push_back(eles[i]); + for (int i = 0; i < or_pos; ++i) + ro.push_back(opers[i]); + node->l_ = make_binary(re, ro); + + re.clear(); + ro.clear(); + for (int i = or_pos + 1; i < eles.size(); ++i) + re.push_back(eles[i]); + for (int i = or_pos + 1; i < opers.size(); ++i) + ro.push_back(opers[i]); + node->r_ = make_binary(re, ro); + } + + return node; +} +std::string simple_logic::to_string_internal(bool& single) +{ + single = true; + if (oper_ == LOGIC_OPER_LEAF) + return expr_; + else if (oper_ == LOGIC_OPER_AND || oper_ == LOGIC_OPER_OR || oper_ == LOGIC_OPER_XOR) + { + std::string exp(""); + + if (l_) + { + exp = l_->to_string_internal(single); + if (!single) + { + exp += ")"; + exp.insert(0, "("); + } + } + + if (oper_ == LOGIC_OPER_AND) + exp += " && "; + else if (oper_ == LOGIC_OPER_OR) + exp += " || "; + else + exp += " ^ "; + + if (r_) + { + bool s = false; + std::string r(r_->to_string_internal(s)); + + if (!s) + { + r.insert(0, "("); + r += ")"; + } + exp += r; + } + + if (not_) + { + exp.insert(0, "!("); + exp += ")"; + single = true; + } + else + single = false; + + return std::move(exp); + } + else + return ""; +} + +simple_logic& simple_logic::operator=(const simple_logic& r) +{ + copy(r); + + return *this; +} + +bool simple_logic::parse(const char* expr, int* end_pos, void(*leaf)(const char*, void*), void* leaf_param) +{ + bool ret = false; + + clear(); + if (strstr(expr, "||") == nullptr && + strstr(expr, "&&") == nullptr && + strstr(expr, "^") == nullptr) + { + oper_ = LOGIC_OPER_LEAF; + expr_ = expr; + ret = true; + if (leaf) + leaf(expr_.c_str(), leaf_param); + } + else + { + int end = 0; + + ret = parse_internal(expr, &end, leaf, leaf_param); + if (end_pos) + *end_pos = end; + } + + return ret; +} +bool simple_logic::value(bool(*simple_expr_value)(const char*, void*), void* param) +{ + if (oper_ == LOGIC_OPER_LEAF) + return simple_expr_value(expr_.c_str(), param); + else if (oper_ == LOGIC_OPER_AND) + { + bool ret = true; + + if (l_) + ret = l_->value(simple_expr_value, param); + if (ret && r_) + ret &= r_->value(simple_expr_value, param); + + return ret ^ not_; + } + else if (oper_ == LOGIC_OPER_OR) + { + bool ret = false; + + if (l_) + ret = l_->value(simple_expr_value, param); + if (!ret && r_) + ret = r_->value(simple_expr_value, param); + + return ret ^ not_; + } + else if (oper_ == LOGIC_OPER_XOR) + { + bool ret = false; + + if (l_) + ret = l_->value(simple_expr_value, param); + if (r_) + ret ^= r_->value(simple_expr_value, param); + + return ret ^ not_; + } + else + { + return false; + } +} +std::string simple_logic::to_expression(void) +{ + bool single = false; + + return std::move(to_string_internal(single)); +} diff --git a/sdk/sane_opt_json/simple_logic.h b/sdk/sane_opt_json/simple_logic.h new file mode 100644 index 0000000..9868a7c --- /dev/null +++ b/sdk/sane_opt_json/simple_logic.h @@ -0,0 +1,79 @@ + +// simple_logic.h : include file for simple logical operation +// +// Purpose: to parse and execute a logical expression like 'b1 && !(b2 || b3) ^ b4 && b5' +// +// Date: 2023-09-03 +// +// Author: gb +// +// Supporting opers: &&, ||, ^ +// +// Priority: (&&, ^, ), (||) + +#pragma once + +#include +#include + +namespace string_util +{ + // Function: find the ending position in str + // + // Parameter: str - the string beginning after the first letter 'head' + // + // head - the leading letter, can be emblaced + // + // tail - the ending letter + // + // Return: position at the ending letter, or '\0' in the string + int find_end_of_pair(const char* str, char head, char tail); + void skip_space(const char*& ptr, const char* space = " \t"); + + enum + { + TRIM_LEFT = 1, + TRIM_RIGHT, + TRIM_BOTH, + }; + void trim(std::string& str, int type = TRIM_BOTH); +}; + +class simple_logic +{ + int oper_; + bool not_; + std::string expr_; + simple_logic* l_; + simple_logic* r_; + + void clear(void); + void copy(const simple_logic& r); + void set_not(bool notv); + bool parse_internal(const char* expr, int* end, void(*leaf)(const char*, void*), void* leaf_param); + simple_logic* make_binary(const std::vector& eles, const std::vector& opers); + std::string to_string_internal(bool& single); + +public: + simple_logic(); + simple_logic(const simple_logic& r); + ~simple_logic(); + + // && == ^ > || + enum + { + LOGIC_OPER_NONE = 0, + LOGIC_OPER_LEAF, + LOGIC_OPER_OR, + LOGIC_OPER_AND, + LOGIC_OPER_XOR, + }; + + simple_logic& operator=(const simple_logic& r); + +public: + bool parse(const char* expr, int *end_pos = nullptr, void(*leaf)(const char*, void*) = nullptr, void* leaf_param = nullptr); // leaf: callback for leaf notifying + bool value(bool(*simple_expr_value)(const char*, void*), void* param); + std::string to_expression(void); +}; + diff --git a/usb/buildconf.h b/usb/buildconf.h new file mode 100644 index 0000000..88ff97f --- /dev/null +++ b/usb/buildconf.h @@ -0,0 +1,12 @@ +#define _LARGEFILE64_SOURCE +#define _FILE_OFFSET_BITS 64 + +//#define CONFIG_USB_NON_BLOCKING_WRITE 1 + +#define CONFIG_USB_FS_SUPPORT 1 // USB 1.1 Full speed +#define CONFIG_USB_HS_SUPPORT 1 // USB 2.0 High speed +#define CONFIG_USB_SS_SUPPORT 1 // USB 3.0 SuperSpeed + +#define CONFIG_READ_FILE_BUFFER_SIZE (1024*1024) // Must be a 2^x value. +#define CONFIG_MAX_TX_USB_BUFFER_SIZE (16*512) // Must be a multiple of 512 and be less than CONFIG_READ_FILE_BUFFER_SIZE +#define CONFIG_MAX_RX_USB_BUFFER_SIZE (16*512) // Must be a multiple of 512 \ No newline at end of file diff --git a/usb/camtp.h b/usb/camtp.h new file mode 100644 index 0000000..ed9edcb --- /dev/null +++ b/usb/camtp.h @@ -0,0 +1,82 @@ +/* + * CAMTP Responder + * Copyright (c) 2020 Holdtecs Technologies + * + * CAMTP Responder 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 3.0 of the License, or (at your option) any later version. + * + * CAMTP Responder 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 version 3 for more details. + * + * You should have received a copy of the GNU General Public License + * along with CAMTP Responder; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file camtp.h + * @brief Main CAMMTP protocol functions. + * @author Barry Ruan + */ + +#ifndef _INC_CAMTP_H_ +#define _INC_CAMTP_H_ + +#define MAX_STORAGE_NB 16 +#define MAX_CFG_STRING_SIZE 512 + +#include + +#pragma pack() +typedef struct mtp_usb_cfg_ +{ + uint16_t usb_vendor_id; + uint16_t usb_product_id; + uint8_t usb_class; + uint8_t usb_subclass; + uint8_t usb_protocol; + uint16_t usb_dev_version; + uint16_t usb_max_packet_size; + uint8_t usb_functionfs_mode; + + char usb_device_path[MAX_CFG_STRING_SIZE + 1]; + char usb_endpoint_in[MAX_CFG_STRING_SIZE + 1]; + char usb_endpoint_out[MAX_CFG_STRING_SIZE + 1]; + char usb_endpoint_intin[MAX_CFG_STRING_SIZE + 1]; + + char usb_string_manufacturer[MAX_CFG_STRING_SIZE + 1]; + char usb_string_product[MAX_CFG_STRING_SIZE + 1]; + char usb_string_serial[MAX_CFG_STRING_SIZE + 1]; + char usb_string_version[MAX_CFG_STRING_SIZE + 1]; + + char usb_string_interface[MAX_CFG_STRING_SIZE + 1]; + + int wait_connection; + int loop_on_disconnect; + + int show_hidden_files; + + int val_umask; + +}camtp_usb_cfg; + + +typedef struct camtp_ctx_ +{ + uint32_t session_id; + + camtp_usb_cfg usb_cfg; + + void * usb_ctx; + + volatile int cancel_req; + +}camtp_ctx; + +int camtp_load_config_file(camtp_ctx * context, const char * conffile); + +#endif \ No newline at end of file diff --git a/usb/default_cfg.h b/usb/default_cfg.h new file mode 100644 index 0000000..1039003 --- /dev/null +++ b/usb/default_cfg.h @@ -0,0 +1,64 @@ +/* + * CAMTP Responder + * Copyright (c) 2020 Holdtecs Technologies + * + * CAMTP Responder 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 3.0 of the License, or (at your option) any later version. + * + * CAMTP Responder 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 version 3 for more details. + * + * You should have received a copy of the GNU General Public License + * along with CAMTP Responder; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file default.h + * @brief Main CAMMTP protocol functions. + * @author Barry Ruan + */ + +#ifndef _INC_DEFAULT_CFG_H_ +#define _INC_DEFAULT_CFG_H_ + +#ifndef CAMTPR_CONF_FILE +#define CAMTPR_CONF_FILE "/etc/camtprd/camtprd.conf" +#endif + +#define MAX_PACKET_SIZE 1024 + +#define USB_DEV_VENDOR_ID 0x04b4 +//0x04B4 // Linux Foundation +#define USB_DEV_PRODUCT_ID 0x8613 // PTP Gadget + +#define USB_DEV_CLASS USB_CLASS_STILL_IMAGE // Still Imaging device +#define USB_DEV_SUBCLASS 0x0 // +#define USB_DEV_PROTOCOL 0x0 // + +#define USB_DEV_VERSION 0x3008 + +#define USB_FFS_MODE 1 + +#define USB_DEV "/dev/ffs-camtp/ep0" + +#define USB_EP_BULK_IN "/dev/ffs-camtp/ep1" +#define USB_EP_BULK_OUT "/dev/ffs-camtp/ep2" +#define USB_EP_INT_IN "/dev/ffs-camtp/ep3" +enum +{ + EP_IND_BULK_IN = 0, + EP_IND_BULK_OUT, + EP_IND_INT_IN, +}; + +#define MANUFACTURER "HUAGAO Technologies" +#define PRODUCT "HUAGAO" +#define SERIALNUMBER "01234567ABCDEFG" +#define USB_INTERFACE "camtp" + +#endif diff --git a/usb/usb_dev.cpp b/usb/usb_dev.cpp new file mode 100644 index 0000000..116ec8c --- /dev/null +++ b/usb/usb_dev.cpp @@ -0,0 +1,716 @@ +#include "usb_dev.h" + +#include +#include +#include +#include +#include "usbstring.h" +#include "default_cfg.h" +#include +#include +#include + + +#define CONFIG_VALUE 1 +#define USB_MAX_PACKAGE_LENGTH_FILE "/opt/usbpkgconfig" + +static struct usb_gadget_strings strings = { + .language = 0x0409, /* en-us */ + .strings = 0, +}; + +typedef struct camtp_device_status_ { + uint16_t wLength; + uint16_t wCode; +}camtp_device_status; + +static const int cacheSize = SIZE_KB(64); + +usb_device::usb_device(const char* dwc3, const char* udc, const char* pwd) + : dwc3_(dwc3), udc_(udc), pwd_(pwd) +{ + memset(&gadget_, 0, sizeof(gadget_)); + gadget_.usb_device = -1; + for(int i = 0; i < _countof(gadget_.ep_handles); ++i) + gadget_.ep_handles[i] = -1; + + memset( &ffs_strs_, 0, sizeof(ffs_strings)); + ffs_strs_.header.magic = htole32(FUNCTIONFS_STRINGS_MAGIC); + ffs_strs_.header.length = htole32(sizeof(struct usb_functionfs_strings_head) + sizeof(uint16_t) + strlen(USB_INTERFACE) + 1); + ffs_strs_.header.str_count = htole32(1); + ffs_strs_.header.lang_count = htole32(1); + ffs_strs_.code = htole16(0x0409); // en-us + strcpy(ffs_strs_.string_data, USB_INTERFACE); +} + +usb_device::~usb_device() +{ + close_device(); + deinit_usb_camtp_gadget(); +} + +std::string usb_device::endpoint_index_str(int epind) +{ + RETURN_ENUM_STR(epind, EP_IND_BULK_IN); + RETURN_ENUM_STR(epind, EP_IND_BULK_OUT); + RETURN_ENUM_STR(epind, EP_IND_INT_IN); + + return std::to_string(epind); +} + +void usb_device::fill_if_descriptor(bool ffs_mode, usb_gadget * usbctx, struct usb_interface_descriptor * desc) +{ + memset(desc,0,sizeof(struct usb_interface_descriptor)); + + desc->bLength = sizeof(struct usb_interface_descriptor); + desc->bDescriptorType = USB_DT_INTERFACE; //!< nick + desc->bInterfaceNumber = 0; + desc->iInterface = 1; + desc->bAlternateSetting = 0; + desc->bNumEndpoints = _countof(usbctx->ep_path); + + desc->bInterfaceClass = USB_DEV_CLASS; + desc->bInterfaceSubClass = USB_DEV_SUBCLASS; + desc->bInterfaceProtocol = USB_DEV_PROTOCOL; + if( ffs_mode ) + { + desc->iInterface = STRINGID_MANUFACTURER; + } + else + { + desc->iInterface = STRINGID_INTERFACE; + } +} +void usb_device::fill_ep_descriptor(unsigned short max_packet, usb_gadget * usbctx,struct usb_endpoint_descriptor_no_audio * desc,int index,unsigned int flags) +{ + memset(desc,0,sizeof(struct usb_endpoint_descriptor_no_audio)); + + desc->bLength = USB_DT_ENDPOINT_SIZE; + desc->bDescriptorType = USB_DT_ENDPOINT; + + if(flags & EP_OUT_DIR) + desc->bEndpointAddress = USB_DIR_OUT | (index); + else + desc->bEndpointAddress = USB_DIR_IN | (index); + + if(flags & EP_BULK_MODE) + { + desc->bmAttributes = USB_ENDPOINT_XFER_BULK; + desc->wMaxPacketSize = max_packet; + //printf("desc->wMaxPacketSize = %d \n",desc->wMaxPacketSize); + } + else + { + desc->bmAttributes = USB_ENDPOINT_XFER_INT; + desc->wMaxPacketSize = 64; // HS size 64 + desc->bInterval = 6; + } + +#if defined(CONFIG_USB_SS_SUPPORT) + if(flags & EP_SS_MODE) + { + ep_cfg_descriptor * ss_descriptor; + + ss_descriptor = (ep_cfg_descriptor *)desc; + + ss_descriptor->ep_desc_comp.bLength = sizeof(struct usb_ss_ep_comp_descriptor); + ss_descriptor->ep_desc_comp.bDescriptorType = USB_DT_SS_ENDPOINT_COMP; + if(flags & EP_BULK_MODE){ + ss_descriptor->ep_desc_comp.bMaxBurst = 15; + ss_descriptor->ep_desc_comp.wBytesPerInterval = 0x00; + }else{ + ss_descriptor->ep_desc_comp.bMaxBurst = 0; + ss_descriptor->ep_desc_comp.wBytesPerInterval = 64;//0x1c + } + } +#endif +} +int usb_device::add_usb_string(usb_gadget * usbctx, int id, char * string) +{ + int i; + + i = 0; + + while( i < MAX_USB_STRING ) + { + if( !usbctx->stringtab[i].id ) + { + usbctx->stringtab[i].id = id; + if(string) + { + usbctx->stringtab[i].str = (char*)malloc(strlen(string) + 1); + if(usbctx->stringtab[i].str) + { + memset(usbctx->stringtab[i].str,0,strlen(string) + 1); + strcpy(usbctx->stringtab[i].str,string); + return i; + } + else + { + usbctx->stringtab[i].id = 0; + return -2; + } + } + else + { + return i; + } + } + i++; + } + + return -1; +} +void usb_device::fill_config_descriptor(usb_gadget * usbctx,struct usb_config_descriptor * desc,int total_size, int hs) +{ + memset(desc,0,sizeof(struct usb_config_descriptor)); + + desc->bLength = sizeof(struct usb_config_descriptor); + desc->bDescriptorType = USB_DT_CONFIG; + desc->wTotalLength = desc->bLength + total_size; + desc->bNumInterfaces = 1; + desc->bConfigurationValue = CONFIG_VALUE; + if(hs) + desc->iConfiguration = STRINGID_CONFIG_HS; + else + desc->iConfiguration = STRINGID_CONFIG_LS; + desc->bmAttributes = USB_CONFIG_ATT_ONE; + desc->bMaxPower = 1; +} +void usb_device::fill_dev_descriptor(usb_gadget * usbctx,struct usb_device_descriptor * desc) +{ + memset(desc,0,sizeof(struct usb_device_descriptor)); + + desc->bLength = USB_DT_DEVICE_SIZE; + desc->bDescriptorType = USB_DT_DEVICE; + desc->bDeviceClass = USB_DEV_CLASS; + desc->bDeviceSubClass = USB_DEV_SUBCLASS; + desc->bDeviceProtocol = USB_DEV_PROTOCOL; + desc->idVendor = USB_DEV_VENDOR_ID; + desc->idProduct = USB_DEV_PRODUCT_ID; + desc->bcdDevice = USB_DEV_VERSION; // Version + // Strings + desc->iManufacturer = STRINGID_MANUFACTURER; + desc->iProduct = STRINGID_PRODUCT; + desc->iSerialNumber = STRINGID_SERIAL; + desc->bNumConfigurations= 1; // Only one configuration + + return; +} +void usb_device::init_usb_camtp_gadget(bool ffs_mode) +{ + usb_gadget *usbctx = &gadget_; //(usb_gadget *)malloc(sizeof(usb_gadget)); + int cfg_size; + + if(usbctx) + { + std::ifstream fs(USB_MAX_PACKAGE_LENGTH_FILE); + int maxpackagesize = 0; + fs >> maxpackagesize; + if(maxpackagesize <= 0) + maxpackagesize = 512; + + max_packet_ = maxpackagesize; + memset(usbctx, 0, sizeof(usb_gadget)); + usbctx->usb_device = -1; + for(int i = 0; i < _countof(usbctx->ep_handles); ++i) + usbctx->ep_handles[i] = -1; + + add_usb_string(usbctx, STRINGID_MANUFACTURER, MANUFACTURER); + add_usb_string(usbctx, STRINGID_PRODUCT, PRODUCT); + add_usb_string(usbctx, STRINGID_SERIAL, SERIALNUMBER); + add_usb_string(usbctx, STRINGID_CONFIG_HS, (char*)"High speed configuration"); + add_usb_string(usbctx, STRINGID_CONFIG_LS, (char*)"Low speed configuration"); + add_usb_string(usbctx, STRINGID_INTERFACE, USB_INTERFACE); + add_usb_string(usbctx, STRINGID_MAX, NULL); + + strings.strings = usbctx->stringtab; + + for(int i = 0; i < _countof(usbctx->ep_config); ++i) + { + usbctx->ep_config[i] = (ep_cfg*)malloc(sizeof(ep_cfg)); + if(!usbctx->ep_config[i]) + goto init_error; + + memset(usbctx->ep_config[i], 0, sizeof(ep_cfg)); + } + + usbctx->ep_path[EP_IND_BULK_IN] = &ep_path_[EP_IND_BULK_IN][0]; + usbctx->ep_path[EP_IND_BULK_OUT] = &ep_path_[EP_IND_BULK_OUT][0]; + usbctx->ep_path[EP_IND_INT_IN] = &ep_path_[EP_IND_INT_IN][0]; + + cfg_size = sizeof(struct usb_interface_descriptor) + (sizeof(struct usb_endpoint_descriptor_no_audio) * _countof(usbctx->ep_config)); + + if( ffs_mode ) + { + // FunctionFS mode + usbctx->usb_ffs_config = (usb_ffs_cfg *)malloc(sizeof(usb_ffs_cfg)); + if(!usbctx->usb_ffs_config) + goto init_error; + + memset(usbctx->usb_ffs_config, 0, sizeof(usb_ffs_cfg)); + +#ifdef OLD_FUNCTIONFS_DESCRIPTORS // Kernel < v3.15 + usbctx->usb_ffs_config->magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC); +#else + usbctx->usb_ffs_config->magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2); + usbctx->usb_ffs_config->flags = htole32(0); + usbctx->usb_ffs_config->flags |= htole32(FUNCTIONFS_ALL_CTRL_RECIP); + +#ifdef CONFIG_USB_FS_SUPPORT + usbctx->usb_ffs_config->flags |= htole32(FUNCTIONFS_HAS_FS_DESC); +#endif + +#ifdef CONFIG_USB_HS_SUPPORT + usbctx->usb_ffs_config->flags |= htole32(FUNCTIONFS_HAS_HS_DESC); +#endif + +#ifdef CONFIG_USB_SS_SUPPORT + usbctx->usb_ffs_config->flags |= htole32(FUNCTIONFS_HAS_SS_DESC); +#endif + +#endif + usbctx->usb_ffs_config->length = htole32(sizeof(usb_ffs_cfg)); + +#ifdef CONFIG_USB_FS_SUPPORT + usbctx->usb_ffs_config->fs_count = htole32(1 + _countof(usbctx->ep_config)); + + fill_if_descriptor(ffs_mode, usbctx, &usbctx->usb_ffs_config->ep_desc_fs.if_desc); + fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_ffs_config->ep_desc_fs.ep_desc_in, EP_IND_BULK_IN + 1, EP_BULK_MODE | EP_IN_DIR); + fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_ffs_config->ep_desc_fs.ep_desc_out, EP_IND_BULK_OUT + 1, EP_BULK_MODE | EP_OUT_DIR); + fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_ffs_config->ep_desc_fs.ep_desc_int_in, EP_IND_INT_IN + 1, EP_INT_MODE | EP_IN_DIR); +#endif + +#ifdef CONFIG_USB_HS_SUPPORT + usbctx->usb_ffs_config->hs_count = htole32(1 + _countof(usbctx->ep_config)); + fill_if_descriptor(ffs_mode, usbctx, &usbctx->usb_ffs_config->ep_desc_hs.if_desc); + fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_ffs_config->ep_desc_hs.ep_desc_in, EP_IND_BULK_IN + 1, EP_BULK_MODE | EP_IN_DIR | EP_HS_MODE); + fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_ffs_config->ep_desc_hs.ep_desc_out, EP_IND_BULK_OUT + 1, EP_BULK_MODE | EP_OUT_DIR | EP_HS_MODE); + fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_ffs_config->ep_desc_hs.ep_desc_int_in, EP_IND_INT_IN + 1, EP_INT_MODE | EP_IN_DIR | EP_HS_MODE); +#endif + +#ifdef CONFIG_USB_SS_SUPPORT + usbctx->usb_ffs_config->ss_count = htole32(1 + (_countof(usbctx->ep_config) * 2)); + fill_if_descriptor(ffs_mode, usbctx, &usbctx->usb_ffs_config->ep_desc_ss.if_desc); + fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_ffs_config->ep_desc_ss.ep_desc_in, EP_IND_BULK_IN + 1, EP_BULK_MODE | EP_IN_DIR | EP_SS_MODE); + fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_ffs_config->ep_desc_ss.ep_desc_out, EP_IND_BULK_OUT + 1, EP_BULK_MODE | EP_OUT_DIR | EP_SS_MODE); + fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_ffs_config->ep_desc_ss.ep_desc_int_in, EP_IND_INT_IN + 1, EP_INT_MODE | EP_IN_DIR | EP_SS_MODE); +#endif + } + else + { + usbctx->usb_config = (usb_cfg *)malloc(sizeof(usb_cfg)); + if(!usbctx->usb_config) + goto init_error; + + memset(usbctx->usb_config,0,sizeof(usb_cfg)); + + usbctx->usb_config->head = 0x00000000; + +#ifdef CONFIG_USB_FS_SUPPORT + fill_config_descriptor(usbctx, &usbctx->usb_config->cfg_fs, cfg_size, 0); + fill_if_descriptor(ffs_mode, usbctx, &usbctx->usb_config->ep_desc_fs.if_desc); + fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_config->ep_desc_fs.ep_desc_in, EP_IND_BULK_IN + 1, EP_BULK_MODE | EP_IN_DIR); + fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_config->ep_desc_fs.ep_desc_out, EP_IND_BULK_OUT + 1, EP_BULK_MODE | EP_OUT_DIR); + fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_config->ep_desc_fs.ep_desc_int_in, EP_IND_INT_IN + 1, EP_INT_MODE | EP_IN_DIR); +#endif + +#ifdef CONFIG_USB_HS_SUPPORT + fill_config_descriptor(usbctx, &usbctx->usb_config->cfg_hs, cfg_size, 1); + fill_if_descriptor(ffs_mode, usbctx, &usbctx->usb_config->ep_desc_hs.if_desc); + fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_config->ep_desc_hs.ep_desc_in, EP_IND_BULK_IN + 1, EP_BULK_MODE | EP_IN_DIR | EP_HS_MODE); + fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_config->ep_desc_hs.ep_desc_out, EP_IND_BULK_OUT + 1, EP_BULK_MODE | EP_OUT_DIR | EP_HS_MODE); + fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_config->ep_desc_hs.ep_desc_int_in, EP_IND_INT_IN + 1, EP_INT_MODE | EP_IN_DIR | EP_HS_MODE); +#endif + +#ifdef CONFIG_USB_SS_SUPPORT + fill_config_descriptor(usbctx, &usbctx->usb_config->cfg_ss, cfg_size, 1); + fill_if_descriptor(ffs_mode, usbctx, &usbctx->usb_config->ep_desc_ss.if_desc); + fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_config->ep_desc_ss.ep_desc_in, EP_IND_BULK_IN + 1, EP_BULK_MODE | EP_IN_DIR | EP_SS_MODE); + fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_config->ep_desc_ss.ep_desc_out, EP_IND_BULK_OUT + 1, EP_BULK_MODE | EP_OUT_DIR | EP_SS_MODE); + fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_config->ep_desc_ss.ep_desc_int_in, EP_IND_INT_IN + 1, EP_INT_MODE | EP_IN_DIR | EP_SS_MODE); +#endif + + fill_dev_descriptor(usbctx, &usbctx->usb_config->dev_desc); + } + + return; + } + +init_error: + deinit_usb_camtp_gadget(); +} +void usb_device::deinit_usb_camtp_gadget() +{ + usb_gadget * usbctx = &gadget_; + + if( usbctx ) + { + if(usbctx->usb_config) + { + free(usbctx->usb_config); + usbctx->usb_config = 0; + } + + if(usbctx->usb_ffs_config) + { + free(usbctx->usb_ffs_config); + usbctx->usb_ffs_config = 0; + } + + for(int i = 0; i < _countof(usbctx->ep_config); ++i) + { + if( usbctx->ep_config[i] ) + free( usbctx->ep_config[i] ); + usbctx->ep_config[i] = nullptr; + } + + for(int i = 0; i < _countof(usbctx->stringtab); ++i) + { + if( usbctx->stringtab[i].str ) + free ( usbctx->stringtab[i].str ); + usbctx->stringtab[i].str = nullptr; + } + } +} + +int usb_device::config_device(bool ffs_mode) +{ + void *data = ffs_mode ? (void*)gadget_.usb_ffs_config : (void*)gadget_.usb_config; + int size = ffs_mode ? sizeof(*gadget_.usb_ffs_config) : sizeof(*gadget_.usb_config), + ret = write(gadget_.usb_device, data, size); + + if(ret == size) + { + if(ffs_mode) + { + ret = write(gadget_.usb_device, &ffs_strs_, sizeof(ffs_strs_)); + if(ret) + { + ret = errno; + utils::to_log(LOG_LEVEL_FATAL, "config device with ffs_strings failed: %d - %s\n", errno, strerror(errno)); + } + } + } + else + { + ret = errno; + utils::to_log(LOG_LEVEL_FATAL, "config device with '%s' failed: %d - %s\n", ffs_mode ? "ffs_config" : "usb_config", errno, strerror(errno)); + } + + return ret; +} +int usb_device::config_endpoint(int ep_ind) +{ + int ret = 0; + + void * descriptor_ptr; + int descriptor_size; + int index = ep_ind; + usb_gadget* ctx = &gadget_; + + ctx->ep_config[index]->head = 1; + descriptor_size = 0; + + if( ctx->usb_ffs_config ) + { +#if defined(CONFIG_USB_SS_SUPPORT) + descriptor_ptr = (void *)&ctx->usb_ffs_config->ep_desc_ss; + descriptor_size = sizeof(ep_cfg_descriptor); +#elif defined(CONFIG_USB_HS_SUPPORT) + descriptor_ptr = (void *)&ctx->usb_ffs_config->ep_desc_hs; + descriptor_size = sizeof(struct usb_endpoint_descriptor_no_audio); +#elif defined(CONFIG_USB_FS_SUPPORT) + descriptor_ptr = (void *)&ctx->usb_ffs_config->ep_desc_fs; + descriptor_size = sizeof(struct usb_endpoint_descriptor_no_audio); +#else + +#error Configuration Error ! At least one USB mode support must be enabled ! (CONFIG_USB_FS_SUPPORT/CONFIG_USB_HS_SUPPORT/CONFIG_USB_SS_SUPPORT) + +#endif + } + else + { +#if defined(CONFIG_USB_SS_SUPPORT) + descriptor_ptr = (void *)&ctx->usb_config->ep_desc_ss; + descriptor_size = sizeof(ep_cfg_descriptor); +#elif defined(CONFIG_USB_HS_SUPPORT) + descriptor_ptr = (void *)&ctx->usb_config->ep_desc_hs; + descriptor_size = sizeof(struct usb_endpoint_descriptor_no_audio); +#elif defined(CONFIG_USB_FS_SUPPORT) + descriptor_ptr = (void *)&ctx->usb_config->ep_desc_fs; + descriptor_size = sizeof(struct usb_endpoint_descriptor_no_audio); +#else + +#error Configuration Error ! At least one USB mode support must be enabled ! (CONFIG_USB_FS_SUPPORT/CONFIG_USB_HS_SUPPORT/CONFIG_USB_SS_SUPPORT) + +#endif + } + +#if defined(CONFIG_USB_SS_SUPPORT) + switch(index) + { + case EP_DESCRIPTOR_IN: + memcpy(&ctx->ep_config[index]->ep_desc[0], &((SSEndPointsDesc*)descriptor_ptr)->ep_desc_in,descriptor_size); + memcpy(&ctx->ep_config[index]->ep_desc[1], &((SSEndPointsDesc*)descriptor_ptr)->ep_desc_in,descriptor_size); + break; + case EP_DESCRIPTOR_OUT: + memcpy(&ctx->ep_config[index]->ep_desc[0], &((SSEndPointsDesc*)descriptor_ptr)->ep_desc_out,descriptor_size); + memcpy(&ctx->ep_config[index]->ep_desc[1], &((SSEndPointsDesc*)descriptor_ptr)->ep_desc_out,descriptor_size); + break; + case EP_DESCRIPTOR_INT_IN: + memcpy(&ctx->ep_config[index]->ep_desc[0], &((SSEndPointsDesc*)descriptor_ptr)->ep_desc_int_in,descriptor_size); + memcpy(&ctx->ep_config[index]->ep_desc[1], &((SSEndPointsDesc*)descriptor_ptr)->ep_desc_int_in,descriptor_size); + break; + } +#else + switch(index) + { + case EP_DESCRIPTOR_IN: + memcpy(&ctx->ep_config[index]->ep_desc[0], &((EndPointsDesc*)descriptor_ptr)->ep_desc_in,descriptor_size); + memcpy(&ctx->ep_config[index]->ep_desc[1], &((EndPointsDesc*)descriptor_ptr)->ep_desc_in,descriptor_size); + break; + case EP_DESCRIPTOR_OUT: + memcpy(&ctx->ep_config[index]->ep_desc[0], &((EndPointsDesc*)descriptor_ptr)->ep_desc_out,descriptor_size); + memcpy(&ctx->ep_config[index]->ep_desc[1], &((EndPointsDesc*)descriptor_ptr)->ep_desc_out,descriptor_size); + break; + case EP_DESCRIPTOR_INT_IN: + memcpy(&ctx->ep_config[index]->ep_desc[0], &((EndPointsDesc*)descriptor_ptr)->ep_desc_int_in,descriptor_size); + memcpy(&ctx->ep_config[index]->ep_desc[1], &((EndPointsDesc*)descriptor_ptr)->ep_desc_int_in,descriptor_size); + break; + } +#endif + + if(ffs_mode_) + { + + } + else + { + ret = write(ctx->ep_handles[index], ctx->ep_config[index], sizeof(ep_cfg)); + + if (ret != sizeof(ep_cfg)) + { + ret = errno; + utils::to_log(LOG_LEVEL_FATAL, "config_endpoint '%s' failed: %d - %s", usb_device::endpoint_index_str(index).c_str(), errno, strerror(errno)); + } + } + + return ret; +} + +int usb_device::add_endpoint(const char* path, bool bulk/*true - bulk, false - int*/, bool in) +{ + int ret = 0, ind = -1; + if(bulk) + { + if(in) + ind = EP_IND_BULK_IN; + else + ind = EP_IND_BULK_OUT; + } + else + { + if(in) + ind = EP_IND_INT_IN; + } + + if(ind == -1) + ret = EINVAL; + else + ep_path_[ind] = path ? path : ""; + + return ret; +} +int usb_device::open_device(const char* dev, bool ffs_mode, int* fd) +{ + int ret = close_device(); + + if(fd) + *fd = -1; + + if(ret == 0) + { + init_usb_camtp_gadget(ffs_mode); + gadget_.usb_device = open(dev, O_RDWR | O_SYNC); + if(gadget_.usb_device == -1) + { + ret = errno; + utils::to_log(LOG_LEVEL_FATAL, "Open usb device(%s) failed: %d - %s\n", dev, ret, strerror(ret)); + } + else + { + ret = config_device(ffs_mode); + if(ret) + { + close_device(); + } + else + { + utils::to_log(LOG_LEVEL_DEBUG, "Open usb device(%s) success.\n", dev); + ffs_mode_ = ffs_mode; + + // get_system_output(R"(echo linaro | sudo -S sh -c "chmod 777 /dev/ffs-camtp -R")"); + std::string cmd(dev); + size_t pos = cmd.rfind('/'); + + if(pos != std::string::npos) + cmd.erase(pos); + cmd += " -R\""; + cmd.insert(0, "echo " + pwd_ + " | sudo -S sh -c \"chmod 777 "); + utils::get_command_result(cmd.c_str()); + } + if(fd) + *fd = gadget_.usb_device; + } + } + + return ret; +} +int usb_device::open_endpoint(int ep_ind, int* fd) +{ + int ret = ENOTCONN; + + if(fd) + *fd = -1; + + if(gadget_.usb_device >= 0) + { + if(ep_ind >= 0 && ep_ind < _countof(gadget_.ep_handles)) + { + if(gadget_.ep_handles[ep_ind] >= 0) + { + ret = EALREADY; + } + else + { + gadget_.ep_handles[ep_ind] = open(gadget_.ep_path[ep_ind], O_RDWR); + if(gadget_.ep_handles[ep_ind] == -1) + { + utils::to_log(LOG_LEVEL_FATAL, "open endpoint(%s - %s) failed: %d - %s\n", usb_device::endpoint_index_str(ep_ind).c_str(), gadget_.ep_path[ep_ind], errno, strerror(errno)); + ret = errno; + } + else + { + ret = config_endpoint(ep_ind); + if(ret) + { + close(gadget_.ep_handles[ep_ind]); + gadget_.ep_handles[ep_ind] = -1; + } + else + { + utils::to_log(LOG_LEVEL_DEBUG, "open endpoint(%s - %s) ok.\n", usb_device::endpoint_index_str(ep_ind).c_str(), gadget_.ep_path[ep_ind]); + } + } + } + if(fd) + *fd = gadget_.ep_handles[ep_ind]; + } + else + { + ret = EINVAL; + } + } + + return ret; +} +int usb_device::close_endpoint(int ep_ind) +{ + int ret = 0; + + if(ep_ind == -1) + { + for(int i = 0; i < _countof(gadget_.ep_handles); ++i) + { + if(gadget_.ep_handles[i] != -1) + { + ret = close(gadget_.ep_handles[i]); + if(ret) + { + utils::to_log(LOG_LEVEL_FATAL, "close endpoint(%s) failed: %d(%s)\n", usb_device::endpoint_index_str(i).c_str(), errno, strerror(errno)); + break; + } + gadget_.ep_handles[i] = -1; + } + } + utils::to_log(LOG_LEVEL_DEBUG, "close all endpoints = %d\n", ret); + } + else if(ep_ind >= 0 && ep_ind < _countof(gadget_.ep_handles)) + { + if(gadget_.ep_handles[ep_ind] != -1) + { + ret = close(gadget_.ep_handles[ep_ind]); + if(ret) + utils::to_log(LOG_LEVEL_FATAL, "close endpoint(%s) failed: %d(%s)\n", usb_device::endpoint_index_str(ep_ind).c_str(), errno, strerror(errno)); + else + { + gadget_.ep_handles[ep_ind] = -1; + utils::to_log(LOG_LEVEL_DEBUG, "close endpoint(%s) ok.\n", usb_device::endpoint_index_str(ep_ind).c_str()); + } + } + } + else + { + ret = EINVAL; + } + + return ret; +} +int usb_device::close_device(void) +{ + int ret = pull_down(); + + if(ret == 0) + { + ret = close_endpoint(-1); + if(ret == 0) + { + deinit_usb_camtp_gadget(); + if(gadget_.usb_device >= 0) + { + ret = close(gadget_.usb_device); + if(ret == 0) + gadget_.usb_device = -1; + } + } + } + + return ret; +} +int usb_device::pull_up(std::string* msg) +{ + std::string cmd("echo " + pwd_ + " | sudo -S sh -c \"echo " + dwc3_ + " > " + udc_ + "\""); + int err = 0; + std::string info(utils::get_command_result(cmd.c_str(), -1, &err)); + + if(msg) + *msg = std::move(info); + + return err; +} +int usb_device::pull_down(void) +{ + std::string cmd("echo " + pwd_ + " | sudo -S sh -c \"echo " + "''" + " > " + udc_ + "\""); + int err = 0; + + utils::get_command_result(cmd.c_str(), -1, &err); + + return err; +} + +int usb_device::get_device_fd(void) +{ + return gadget_.usb_device; +} +int usb_device::get_endpoint_fd(int ep_ind) +{ + if(ep_ind >= 0 && ep_ind < _countof(gadget_.ep_handles)) + return gadget_.ep_handles[ep_ind]; + else + return -1; +} +size_t usb_device::get_max_packet(void) +{ + return max_packet_; +} diff --git a/usb/usb_dev.h b/usb/usb_dev.h new file mode 100644 index 0000000..4a96559 --- /dev/null +++ b/usb/usb_dev.h @@ -0,0 +1,56 @@ +// USB device configuration and open +// +// Date: 2023-11-28 +// +#pragma once + + + +#include "buildconf.h" +#include "camtp.h" +#include "usb_gadget.h" +#include "../sdk/base/utils.h" + +class usb_device : public refer +{ + usb_gadget gadget_; + std::string dwc3_; // fe900000.dwc3 + std::string udc_; // /opt/cfg/usb_gadget/g1/UDC + std::string pwd_; // password of login user + std::string ep_path_[EP_NB_OF_DESCRIPTORS]; + ffs_strings ffs_strs_; + bool ffs_mode_ = true; + size_t max_packet_ = 512; + + void fill_if_descriptor(bool ffs_mode, usb_gadget * usbctx, struct usb_interface_descriptor * desc); + void fill_ep_descriptor(unsigned short max_packet, usb_gadget * usbctx, struct usb_endpoint_descriptor_no_audio * desc, int index, unsigned int flags); + int add_usb_string(usb_gadget * usbctx, int id, char * string); + void fill_config_descriptor(usb_gadget * usbctx,struct usb_config_descriptor * desc, int total_size, int hs); + void fill_dev_descriptor(usb_gadget * usbctx, struct usb_device_descriptor * desc); + void init_usb_camtp_gadget(bool ffs_mode); + void deinit_usb_camtp_gadget(void); + + int config_device(bool ffs_mode); + int config_endpoint(int ep_ind); + +public: + usb_device(const char* dwc3, const char* udc, const char* pwd); + + static std::string endpoint_index_str(int epind); + +protected: + virtual ~usb_device(); + +public: + int add_endpoint(const char* path, bool bulk/*true - bulk, false - int*/, bool in); + int open_device(const char* dev, bool ffs_mode, int* fd = nullptr); + int open_endpoint(int ep_ind, int* fd = nullptr); + int close_endpoint(int ep_ind); + int close_device(void); + int pull_up(std::string* msg = nullptr); // turn USB-pin on, so that the host can detect me + int pull_down(void); + + int get_device_fd(void); + int get_endpoint_fd(int ep_ind); + size_t get_max_packet(void); +}; diff --git a/usb/usb_gadget.h b/usb/usb_gadget.h new file mode 100644 index 0000000..f951402 --- /dev/null +++ b/usb/usb_gadget.h @@ -0,0 +1,176 @@ +/* + * CAMTP Responder + * Copyright (c) 2020 Holdtecs Technologies + * + * CAMTP Responder 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 3.0 of the License, or (at your option) any later version. + * + * CAMTP Responder 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 version 3 for more details. + * + * You should have received a copy of the GNU General Public License + * along with CAMTP Responder; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file usb_gadget.c + * @brief USB gadget layer + * @author *** + */ + +#ifndef _INC_USB_GADGET_H_ +#define _INC_USB_GADGET_H_ + +#include +#include +#include + +#include "usbstring.h" + +enum +{ + EP_DESCRIPTOR_IN = 0, + EP_DESCRIPTOR_OUT, + EP_DESCRIPTOR_INT_IN, + + EP_NB_OF_DESCRIPTORS +}; + +#define EP_INT_MODE 0x00000000 +#define EP_BULK_MODE 0x00000001 +#define EP_IN_DIR 0x00000000 +#define EP_OUT_DIR 0x00000002 +#define EP_HS_MODE 0x00000004 +#define EP_SS_MODE 0x00000008 + +typedef struct _EndPointsDesc { + struct usb_interface_descriptor if_desc; + struct usb_endpoint_descriptor_no_audio ep_desc_in; + struct usb_endpoint_descriptor_no_audio ep_desc_out; + struct usb_endpoint_descriptor_no_audio ep_desc_int_in; +} __attribute__((packed)) EndPointsDesc; + +typedef struct _SSEndPointsDesc { + struct usb_interface_descriptor if_desc; + struct usb_endpoint_descriptor_no_audio ep_desc_in; + struct usb_ss_ep_comp_descriptor ep_desc_in_comp; + struct usb_endpoint_descriptor_no_audio ep_desc_out; + struct usb_ss_ep_comp_descriptor ep_desc_out_comp; + struct usb_endpoint_descriptor_no_audio ep_desc_int_in; + struct usb_ss_ep_comp_descriptor ep_desc_int_in_comp; + +} __attribute__((packed)) SSEndPointsDesc; + +typedef struct ep_cfg_descriptor { + struct usb_endpoint_descriptor_no_audio ep_desc; +#ifdef CONFIG_USB_SS_SUPPORT + struct usb_ss_ep_comp_descriptor ep_desc_comp; +#endif +} __attribute__((packed)) ep_cfg_descriptor; + +// Direct GadgetFS mode +typedef struct _usb_cfg +{ + uint32_t head; + +#if CONFIG_USB_FS_SUPPORT + struct usb_config_descriptor cfg_fs; + EndPointsDesc ep_desc_fs; +#endif + +#if CONFIG_USB_HS_SUPPORT + struct usb_config_descriptor cfg_hs; + EndPointsDesc ep_desc_hs; +#endif + +#if CONFIG_USB_SS_SUPPORT + struct usb_config_descriptor cfg_ss; + SSEndPointsDesc ep_desc_ss; +#endif + + struct usb_device_descriptor dev_desc; + +} __attribute__ ((packed)) usb_cfg; + +// FunctionFS mode +typedef struct _usb_ffs_cfg +{ + uint32_t magic; + uint32_t length; +#ifndef OLD_FUNCTIONFS_DESCRIPTORS // Kernel > v3.14 + uint32_t flags; +#endif + +#ifdef CONFIG_USB_FS_SUPPORT + uint32_t fs_count; +#endif + +#ifdef CONFIG_USB_HS_SUPPORT + uint32_t hs_count; +#endif + +#if defined(CONFIG_USB_SS_SUPPORT) && !defined(OLD_FUNCTIONFS_DESCRIPTORS) + uint32_t ss_count; +#endif + +#ifdef CONFIG_USB_FS_SUPPORT + EndPointsDesc ep_desc_fs; +#endif + +#ifdef CONFIG_USB_HS_SUPPORT + EndPointsDesc ep_desc_hs; +#endif + +#if defined(CONFIG_USB_SS_SUPPORT) && !defined(OLD_FUNCTIONFS_DESCRIPTORS) + SSEndPointsDesc ep_desc_ss; +#endif + +} __attribute__ ((packed)) usb_ffs_cfg; + +typedef struct _ffs_strings +{ + struct usb_functionfs_strings_head header; + uint16_t code; + char string_data[128]; // string data. +} __attribute__((packed)) ffs_strings; + +typedef struct _ep_cfg +{ + uint32_t head; + + ep_cfg_descriptor ep_desc[2]; + +} __attribute__ ((packed)) ep_cfg; + +enum { + STRINGID_MANUFACTURER = 1, + STRINGID_PRODUCT, + STRINGID_SERIAL, + STRINGID_CONFIG_HS, + STRINGID_CONFIG_LS, + STRINGID_INTERFACE, + STRINGID_MAX +}; + +#define MAX_USB_STRING 16 + +typedef struct _usb_gadget +{ + int usb_device; + int ep_handles[EP_NB_OF_DESCRIPTORS]; + + usb_cfg *usb_config; + usb_ffs_cfg *usb_ffs_config; + + ep_cfg *ep_config[EP_NB_OF_DESCRIPTORS]; + char *ep_path[EP_NB_OF_DESCRIPTORS]; + + struct usb_string stringtab[MAX_USB_STRING]; +}usb_gadget; + +#endif diff --git a/usb/usb_io.cpp b/usb/usb_io.cpp new file mode 100644 index 0000000..41cb8b1 --- /dev/null +++ b/usb/usb_io.cpp @@ -0,0 +1,636 @@ +#include "usb_io.h" + +#include +#include +#include +#include +#include + +#include "usb_dev.h" +#include "default_cfg.h" + +// #define TEST + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// async_usb_gadget +async_usb_gadget::async_usb_gadget(std::function cmd_handler + , std::function dev_conn) + : handle_cmd_(cmd_handler), dev_connect_(dev_conn) + , enc_head_(ENCRYPT_CMD_NONE), enc_payload_(ENCRYPT_NONE), enc_data_(0) + , cmd_que_("in-queue"), sent_que_("out-queue") +{ + memset((void*)&status_, 0, sizeof(status_)); + + dev_ = new usb_device("fe900000.dwc3", "/opt/cfg/usb_gadget/g1/UDC", "linaro"); + dev_->add_endpoint(USB_EP_BULK_IN, true, true); + dev_->add_endpoint(USB_EP_BULK_OUT, true, false); + if(dev_->open_device(USB_DEV, true)) + { + dev_->release(); + dev_ = nullptr; + run_ = false; + return; + } + + auto ep0 = [this](void) -> void + { + thread_read_ep0(); + }; + auto task = [this](void) -> void + { + thread_pump_task(); + }; + auto bulkw = [this](void) -> void + { + int fd = -1, + err = dev_->open_endpoint(EP_IND_BULK_IN, &fd); + + if(err == 0) + { + thread_read_bulk(fd); + dev_->close_endpoint(EP_IND_BULK_IN); + } + }; + auto bulkr = [this](void) -> void + { + int fd = -1, + err = dev_->open_endpoint(EP_IND_BULK_OUT, &fd); + + if(err == 0) + { + thread_write_bulk(fd); + dev_->close_endpoint(EP_IND_BULK_OUT); + } + }; + + auto excep = [&](const char* thread_name) -> void + { + threads_.stop(thread_name); + // threads_.start(task, "thread_pump_task"); + }; + + threads_.set_exception_handler(excep); + unit_in_ = unit_out_ = dev_->get_max_packet(); + threads_.start(task, "thread_pump_task"); + threads_.start(bulkw, "thread_read_bulk"); + threads_.start(bulkr, "thread_write_bulk"); + threads_.start(ep0, "thread_read_ep0"); +} + +async_usb_gadget::~async_usb_gadget() +{ + stop(); +} + +const char* async_usb_gadget::ep0_status_desc(int ep0_status, char* unk_buf/*>= 20 bytes*/) +{ + RETURN_ENUM_STR(ep0_status, EP0_STATUS_IDLE); + RETURN_ENUM_STR(ep0_status, EP0_STATUS_READ_DATA); + RETURN_ENUM_STR(ep0_status, EP0_STATUS_READ_INVAL_DATA); + RETURN_ENUM_STR(ep0_status, EP0_STATUS_HANDLE_CMD); + + sprintf(unk_buf, "Unknown status (%d)", ep0_status); + + return unk_buf; +} + +int async_usb_gadget::wait_fd_event(int fd, int to_ms) +{ + struct timeval timeout, *pto = NULL; + fd_set read_set; + int ret = 0; + + FD_ZERO(&read_set); + FD_SET(fd, &read_set); + if (to_ms != -1) + { + timeout.tv_sec = to_ms / 1000; + timeout.tv_usec = (to_ms % 1000) * 1000; + pto = &timeout; + } + ret = select(fd + 1, &read_set, NULL, NULL, pto); + + return ret; +} +uint16_t async_usb_gadget::get_buf_coefficient(void) +{ + SIMPLE_LOCK(buf_coef_lock_); + + return buf_coef_; +} +void async_usb_gadget::set_buf_coefficient(int coef) +{ + SIMPLE_LOCK(buf_coef_lock_); + + buf_coef_ = (coef <= 0 || coef > 10000) ? 1 : coef; +} + +dyn_mem_ptr async_usb_gadget::handle_ctrl_message(dyn_mem_ptr data) +{ + struct usb_functionfs_event* pev = (struct usb_functionfs_event*)data->ptr(); + dyn_mem_ptr reply = nullptr; + + switch (pev->type) + { + case FUNCTIONFS_ENABLE: + utils::to_log(LOG_LEVEL_ALL, "EP0 FFS ENABLE\n"); + if(dev_connect_) + dev_connect_(true); + break; + case FUNCTIONFS_DISABLE: + utils::to_log(LOG_LEVEL_ALL, "EP0 FFS DISABLE\n"); + if(dev_connect_) + dev_connect_(false); + break; + case FUNCTIONFS_SETUP: + reply = handle_ctrl_setup(data); + break; + case FUNCTIONFS_BIND: + utils::to_log(LOG_LEVEL_ALL, "EP0 FFS BIND\n"); + break; + case FUNCTIONFS_UNBIND: + utils::to_log(LOG_LEVEL_ALL, "EP0 FFS UNBIND\n"); + break; + case FUNCTIONFS_SUSPEND: + utils::to_log(LOG_LEVEL_ALL, "EP0 FFS SUSPEND\n"); + break; + case FUNCTIONFS_RESUME: + utils::to_log(LOG_LEVEL_ALL, "EP0 FFS RESUME\n"); + break; + } + + if (pev->u.setup.bRequestType & USB_DIR_IN) + { + // if host need data back, and reply is null, we send nonsense data back ... + if (!reply) + { + reply = dyn_mem::memory(pev->u.setup.wLength + sizeof(uint16_t)); + reply->set_len(pev->u.setup.wLength); + for (int i = 0; i <= pev->u.setup.wLength / sizeof(uint16_t); ++i) + ((uint16_t*)reply->ptr())[i] = 0x0baad; + utils::to_log(LOG_LEVEL_DEBUG, "fake control message reply(%u bytes) while request with USB_DIR_IN flag but no reply returned.\n", reply->get_rest()); + } + } + else if(reply) + { + utils::to_log(LOG_LEVEL_DEBUG, "discard control message reply(%u bytes) while request without USB_DIR_IN flag.\n", reply->get_rest()); + reply->release(); + reply = nullptr; + } + + return reply; +} +dyn_mem_ptr async_usb_gadget::handle_ctrl_setup(dyn_mem_ptr data) +{ + struct usb_functionfs_event* pev = (struct usb_functionfs_event*)data->ptr(); + bool handled = (pev->u.setup.bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR; + dyn_mem_ptr reply = nullptr; + uint32_t err = 0; + + if(handled) + { + switch (pev->u.setup.bRequest) + { + case USB_REQ_EP0_GET_PROTO_VER: + reply = dyn_mem::memory(sizeof(short)); + *(short*)reply->ptr() = PROTOCOL_VER; + reply->set_len(sizeof(short)); + break; + case USB_REQ_EP0_GET_STATUS: + reply = dyn_mem::memory(sizeof(struct _ep0_reply)); + { + reply->put((void*)&status_, sizeof(status_)); + } + break; + case USB_REQ_EP0_RESET_BULK: + reply = dyn_mem::memory(sizeof(uint32_t)); + reply->put(&err, sizeof(err)); + break; + case USB_REQ_EP0_SET_ENCRYPT: + if (pev->u.setup.wLength == sizeof(PACK_BASE)) + { + LPPACK_BASE pack = (LPPACK_BASE)&pev[1]; + enc_head_ = pack->enc_cmd; + enc_payload_ = pack->encrypt; + enc_data_ = pack->enc_data; + utils::to_log(LOG_LEVEL_DEBUG, "Set encrypting method: command - %d; payload - %d\n", enc_head_, enc_payload_); + } + break; + case USB_REQ_EP0_SET_BULK_BUFFER: + // if(pev->u.setup.wLength == sizeof(short)) + { + uint16_t pre = buf_coef_; + + set_buf_coefficient(pev->u.setup.wIndex); + utils::to_log(LOG_LEVEL_DEBUG, "Set bulk buffer size coefficient from %d to %d\n", pre, buf_coef_); + } + break; + default: + handled = false; + } + } + if(!handled) + { + utils::to_log(LOG_LEVEL_ALL, "EP0 event(0x%x, 0x%x, 0x%x, 0x%x, 0x%x) has not handled by user business.\n" + , pev->u.setup.bRequestType, pev->u.setup.bRequest, pev->u.setup.wValue, pev->u.setup.wIndex, pev->u.setup.wLength); + } + + return reply; +} +int async_usb_gadget::inner_write_bulk(int fd, data_source_ptr data, int* err) +{ + unsigned char* ptr = data->ptr(); + size_t bulk_size = unit_in_ * get_buf_coefficient(), + total = data->get_rest(), off = 0, size = total > bulk_size ? bulk_size : total; + int w = 0; + + if(data->is_memory_block()) + { + while((w = write(fd, ptr + off, size)) > 0) + { + off += w; + status_.bytes_to_sent -= w; + if(off >= total) + break; + if(off + bulk_size < total) + size = bulk_size; + else + size = total - off; + } + if(w <= 0 && err) + *err = errno; + } + else + { + dyn_mem_ptr buf = dyn_mem::memory(bulk_size); + uint32_t len = bulk_size; + + if(err) + *err = 0; + total = 0; + while((w = data->fetch_data(buf->ptr(), &len)) == 0) + { + ptr = buf->ptr(); + off = 0; + w = write(fd, ptr + off, len); + while(w > 0 && w + off < len) + { + status_.bytes_to_sent -= w; + total += w; + off += w; + w = write(fd, ptr + off, len - off); + } + + if(w <= 0) + { + if(err) + *err = errno; + utils::to_log(LOG_LEVEL_ALL, "inner_write_bulk error(%d/%d): %d (%s)\n", off, len, errno, strerror(errno)); + break; + } + else + { + status_.bytes_to_sent -= w; + total += w; + } + + if(data->get_rest() == 0) + break; + len = bulk_size; + } + buf->release(); + off = total; + } + + return off; +} +dyn_mem_ptr async_usb_gadget::handle_bulk_command(dyn_mem_ptr data, uint32_t* used, packet_data_base_ptr* pkd) +{ + dyn_mem_ptr decrypt = packet_decrypt(data); + + if (decrypt) + { + data->release(); + data = decrypt; + + return handle_cmd_(data, used, pkd); + } + else + { + *used = data->get_rest(); + *pkd = nullptr; + + return nullptr; + } +} + +void async_usb_gadget::thread_read_ep0(void) +{ + struct timeval timeout; + int headl = sizeof(struct usb_functionfs_event), + datal = 128, // gadget_->usb_config->dev_desc.bMaxPacketSize0, + ret = 0, + fd = dev_->get_device_fd(); + dyn_mem_ptr mem = dyn_mem::memory(headl + datal); + uint8_t* ptr = mem->ptr(); + uint32_t recycles = 0; + std::string msg(""); + struct usb_functionfs_event* pe = (struct usb_functionfs_event*)ptr; + + utils::to_log(LOG_LEVEL_ALL, "EP0 monitoring thread(%p of id %lx) started ...\n", &async_usb_gadget::thread_read_ep0, getpid()); + ret = dev_->pull_up(&msg); + while(run_) + { + ret = wait_fd_event(fd); + if( ret <= 0 ) + { + utils::to_log(LOG_LEVEL_ALL, "select EP0(%d) failed: %d(%s)\n", fd, errno, strerror(errno)); + break; + } + + // timeout.tv_sec = 0; + ret = read(fd, ptr, headl); + if (ret < 0) + { + utils::to_log(LOG_LEVEL_ALL, "read EP0 failed: %d(%s)\n", errno, strerror(errno)); + break; + } + + bool good = true; + if(pe->u.setup.bRequestType & USB_DIR_IN) + mem->set_len(headl); + else + { + if (pe->u.setup.wLength > datal) + { + good = false; + utils::to_log(LOG_LEVEL_ALL, "EP0 data too long(%d > %d), we will discard this packet(0x%x, 0x%x, 0x%x, 0x%x)!\n", pe->u.setup.wLength, datal, + pe->u.setup.bRequestType, pe->u.setup.bRequest, pe->u.setup.wValue, pe->u.setup.wIndex); + + // we read-out whole data here ... + int rest = pe->u.setup.wLength; + while (rest > 0) + { + read(fd, ptr + headl, rest > datal ? datal : rest); + rest -= datal; + } + } + else + { + read(fd, ptr + headl, pe->u.setup.wLength); + mem->set_len(headl + pe->u.setup.wLength); + } + } + if (good) + { + dyn_mem_ptr reply = handle_ctrl_message(mem); + if(reply) + { + write(fd, reply->ptr(), reply->get_rest()); + reply->release(); + } + } + } + mem->release(); +} +void async_usb_gadget::thread_read_bulk(int fd) +{ + size_t bulk_size = unit_out_ * get_buf_coefficient(); + uint32_t cnt_0 = 0; + + while(run_) + { + dyn_mem_ptr buf(dyn_mem::memory(bulk_size)); + int l = 0; + + status_.out_status = BULK_STATUS_IO; + l = read(fd, buf->ptr(), bulk_size); + if(l <= 0) + { + buf->release(); + if(errno) + { + utils::to_log(LOG_LEVEL_ALL, "read bulk failed: %d(%s)\n", errno, strerror(errno)); + status_.out_status = BULK_STATUS_ERROR; + status_.out_err = errno; + break; + } + else + { + cnt_0++; + } + } + else if (!run_) + { + utils::to_log(LOG_LEVEL_ALL, "thread_read_bulk do reset-bulk ...\n"); + buf->release(); + if(!run_) + break; + } + else + { + status_.out_status = BULK_STATUS_IDLE; + buf->set_len(l); + cmd_que_.save(buf, true); + } + } + if(status_.out_status == BULK_STATUS_IDLE) + status_.out_status = BULK_STATUS_NOT_START; +} +void async_usb_gadget::thread_write_bulk(int fd) +{ + status_.in_status = BULK_STATUS_IDLE; + while(run_) + { + data_source_ptr data; + int err = 0; + + if(sent_que_.take(data, true)) + { + status_.packets_to_sent = sent_que_.size(); + status_.bytes_to_sent = data->get_rest(); + status_.in_status = BULK_STATUS_IO; + inner_write_bulk(fd, data, &err); + status_.in_status = BULK_STATUS_IDLE; + status_.bytes_to_sent = 0; + data->release(); + if(err) + { + utils::to_log(LOG_LEVEL_ALL, "write bulk failed: %d(%s)\n", errno, strerror(errno)); + status_.in_status = BULK_STATUS_ERROR; + status_.in_err = err; + break; + } + } + } + if(status_.in_status == BULK_STATUS_IDLE) + status_.in_status = BULK_STATUS_NOT_START; +} +void async_usb_gadget::thread_pump_task(void) +{ + dyn_mem_ptr prev = nullptr, data = nullptr, reply = nullptr; + bool in_err_statu = false; + uint32_t required = 0, used = 0, restore_err_cnt = 0, max_que = 0; + data_holder *dh = nullptr; + LPPACK_BASE pack = nullptr; + uint64_t total_size = 0; + + while(run_) + { + data = nullptr; + if(cmd_que_.take(data, true) && data) + { + status_.task_cnt = cmd_que_.size(); + if(max_que < status_.task_cnt) + max_que = status_.task_cnt; + + if (in_err_statu) + { + + } + if(prev) + { + utils::to_log(LOG_LEVEL_ALL, "Combine partial packet with length %u and %u ...\n", prev->get_rest(), data->get_rest()); + *prev += *data; + data->release(); + data = prev; + prev = nullptr; + } + + do + { + packet_data_base_ptr pack_data = nullptr; + data_source_ptr ds = nullptr; + bool notify_reply_ok = false; + + if(dh == nullptr) + { + if (data->get_rest() < sizeof(PACK_BASE)) + break; + else + { + pack = (LPPACK_BASE)data->ptr(); + status_.task_cmd = pack->cmd; + status_.task_pack_id = pack->pack_id; + used = data->get_rest(); + reply = handle_bulk_command(data, &used, &pack_data); + notify_reply_ok = used & (INT32_MAX + 1); + used &= INT32_MAX; + if(pack_data) + { + dh = dynamic_cast(pack_data); + if(!dh) + { + ds = dynamic_cast(pack_data); + if(!ds) + pack_data->release(); + } + else + total_size = 0; + } + } + } + else + { + uint32_t len = data->get_rest(); + int err = dh->put_data(data->ptr(), &len); + + if(len < data->get_rest()) + { + utils::to_log(LOG_LEVEL_WARNING, "Put partial data %u/%u! at +%08X with error %d\n", len, data->get_rest(), total_size, err); + } + total_size += len; + if(dh->is_complete() || err) + { + reply = dyn_mem::memory(sizeof(PACK_BASE)); + pack = (LPPACK_BASE)reply->ptr(); + BASE_PACKET_REPLY(*pack, dh->get_packet_command() + 1, dh->get_packet_id(), err); + reply->set_len(sizeof(PACK_BASE)); + + utils::to_log(LOG_LEVEL_ALL, "Finished receiving file with error(Max queue size is %u): %d, total size = 0x%x\n", max_que, err, total_size); + + dh->release(); + dh = nullptr; + } + used = len; + + if (err) + { + in_err_statu = true; + restore_err_cnt = 0; + } + } + + // first reply the packet ... + if(reply) + { + // encrypt ... + dyn_mem_ptr enc = packet_encrypt(reply, enc_head_, enc_payload_, enc_data_); + if (enc) + { + reply->release(); + reply = enc; + } + + write_bulk(dynamic_cast(reply)); + if(notify_reply_ok) + handle_bulk_command(data, nullptr, nullptr); + reply->release(); + reply = nullptr; + } + + // second send appendix data ... + if (ds) + { + write_bulk(ds); + ds->release(); + ds = nullptr; + } + + status_.task_required_bytes = dh ? dh->get_required() : 0; + if(status_.task_required_bytes == 0) + status_.task_cmd = status_.task_pack_id = 0; + + data->used(used); + }while(used && data->get_rest()); + + if(data->get_rest()) + prev = data; + else + data->release(); + } + } + if(prev) + prev->release(); + if (data) + data->release(); + if (reply) + reply->release(); + if (dh) + dh->release(); +} + +int async_usb_gadget::stop(void) +{ + run_ = false; + + if(dev_) + { + dev_->pull_down(); + dev_->close_device(); + dev_->release(); + dev_ = nullptr; + } + + return 0; +} +int async_usb_gadget::write_bulk(data_source_ptr data) +{ + int quel = 0; + + data->add_ref(); + quel = sent_que_.save(data, true); + + return quel; +} diff --git a/usb/usb_io.h b/usb/usb_io.h new file mode 100644 index 0000000..a6bc63b --- /dev/null +++ b/usb/usb_io.h @@ -0,0 +1,98 @@ +#pragma once + +// Objects IO +// +// created on 2023-03-10 + +#include +#include +#include + +#include +#include +#include +#include + + +// callback proto +// +// parameters: usb_functionfs_event* - the function event ptr +// +// dyn_mem_ptr - the packet buffer, read-only +// +// uint32_t* - to return how many data in bytes the handler consumed +// +// normally, the value should be sizeof(PACK_BASE) + PACK_BASE::payload_len, i.e. the handler consume all data of an entire packet +// +// when invalid packet, suggest use the entire data +// +// packet_data_base_ptr* - return data_holder or data_source or nullptr +// +// data_holder: the packet/command need more data than dyn_mem_ptr provides to complete the business. such as 'write a large file' +// +// data_source: the reply content may be a large data (a large file content) +// +// return value of all routines is the reply packet, nullptr if the packet need not reply +// +#define FUNCTION_PROTO_UNHANDLED_EP0 dyn_mem_ptr(struct usb_functionfs_event*) + + +class usb_device; + +class async_usb_gadget : public refer +{ + volatile bool run_ = true; + usb_device *dev_ = nullptr; + safe_thread threads_; + size_t unit_in_ = 0x200; + size_t unit_out_ = 0x200; + uint16_t buf_coef_ = 1; + MUTEX buf_coef_lock_; + + uint32_t enc_head_; + uint32_t enc_payload_; + uint8_t enc_data_; + + volatile EP0REPLYSTATUS status_; + safe_fifo cmd_que_; + safe_fifo sent_que_; + + enum + { + EP0_STATUS_IDLE = 0, + EP0_STATUS_READ_DATA, // ep0_statu::data is rest data len + EP0_STATUS_READ_INVAL_DATA, // ep0_statu::data is rest data len + EP0_STATUS_HANDLE_CMD, + }; + + std::function handle_cmd_; + std::function dev_connect_; + + const char* ep0_status_desc(int ep0_status, char* unk_buf/*>= 40 bytes*/); + + int wait_fd_event(int fd, int to_ms = -1); + uint16_t get_buf_coefficient(void); + void set_buf_coefficient(int coef); + + dyn_mem_ptr handle_ctrl_message(dyn_mem_ptr data); + dyn_mem_ptr handle_ctrl_setup(dyn_mem_ptr data); // user command ... + int inner_write_bulk(int fd, data_source_ptr data, int* err = nullptr); + dyn_mem_ptr handle_bulk_command(dyn_mem_ptr data, uint32_t* used, packet_data_base_ptr* pkd); + + void thread_read_ep0(void); + void thread_read_bulk(int fd); + void thread_write_bulk(int fd); + void thread_pump_task(void); + +public: + async_usb_gadget(std::function cmd_handler = std::function() + , std::function dev_conn = std::function()); + +protected: + ~async_usb_gadget(); + +public: + int stop(void); + int write_bulk(data_source_ptr data); // return sent-que length +}; + diff --git a/usb/usbstring.h b/usb/usbstring.h new file mode 100644 index 0000000..da2dff5 --- /dev/null +++ b/usb/usbstring.h @@ -0,0 +1,42 @@ +/* + * CAMTP Responder + * Copyright (c) 2020 Holdtecs Technologies + * + * CAMTP Responder 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 3.0 of the License, or (at your option) any later version. + * + * CAMTP Responder 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 version 3 for more details. + * + * You should have received a copy of the GNU General Public License + * along with CAMTP Responder; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + /** + * @file usbstring.h + * @brief USB strings - Function & structures declarations. + * @author *** + */ + +#ifndef _INC_USBSTRING_H_ +#define _INC_USBSTRING_H_ + +struct usb_string { + uint8_t id; + char *str; +}; + +struct usb_gadget_strings { + uint16_t language; /* 0x0409 for en-us */ + struct usb_string *strings; +}; + +int usb_gadget_get_string (struct usb_gadget_strings *table, int id, uint8_t *buf); +int unicode2charstring(char * str, uint16_t * unicodestr, int maxstrsize); +int char2unicodestring(char * unicodestr, int index, int maxsize, char * str, int unicodestrsize); +#endif diff --git a/usb/xmake.lua b/usb/xmake.lua new file mode 100644 index 0000000..d28e5e2 --- /dev/null +++ b/usb/xmake.lua @@ -0,0 +1,10 @@ +add_rules("mode.debug", "mode.release") + +target("usb") + set_kind("static") + add_syslinks("pthread", "dl") + add_files("*.cpp", "../sdk/base/*.c*", "../sdk/imgprc/*.c*", "../sdk/json/*.c*", "../sdk/sane_opt_json/*.c*") + add_includedirs("../sdk") + add_includedirs(".", { public = true}) + add_packages("sdk") + -- add_deps("regs", "gimgproc", "applog", "capimage") \ No newline at end of file diff --git a/xmake.lua b/xmake.lua new file mode 100644 index 0000000..97150e2 --- /dev/null +++ b/xmake.lua @@ -0,0 +1,73 @@ +set_project("GXX") + +-- option("device") +-- set_showmenu(true) +-- set_default("g100") +-- set_values("g100", "g200", "g300", "g400") +-- set_category("device") +-- option_end() + +-- option("chip") +-- set_showmenu(true) +-- set_default("rk3399") +-- set_values("rk3288", "rk3399") +-- set_category("chip") +-- option_end() + +-- option("isp1") +-- set_showmenu(true) +-- set_default(true) +-- set_category("isp") +-- option_end() + +-- option("testdemo") +-- set_showmenu(true) +-- set_default(true) +-- set_category("testdemo") +-- option_end() + +-- option("testfpga") +-- set_showmenu(true) +-- set_default(true) +-- set_category("regs test") +-- set_configvar("TEST_FPGA", "ON") +-- option_end() + +-- option("loopdebug") +-- set_showmenu(true) +-- set_default(false) +-- set_category("regs test") +-- set_configvar("LOOP_DEBUG", "ON") +-- option_end() + +-- option("unfixed") +-- set_showmenu(true) +-- set_default(false ) +-- set_configvar("CAP_UNFIXED", "ON") +-- option_end() + +add_packagedirs("sdk") +-- -- if is_mode("debug") then +-- -- add_defines("DEBUG") +-- -- end + +-- set_configvar("MOTOR_UART", get_config("chip") == "rk3288" and "/dev/ttyS3" or "/dev/ttyS4") +-- set_configvar("FPGA_UART", "/dev/ttyUSB0") +-- -- set_configvar("FPGA_UART", get_config("chip") == "rk3288" and "/dev/ttyS3" or "/dev/ttyS4") +-- -- set_configvar("MOTOR_UART", "/dev/ttyUSB0") +-- set_configvar("VIDEO_CLASS", has_config("isp1") and "GVideoISP1" or "gVideo") + +target("conf") + set_kind("phony") + add_options("usb_isp1", "chip_type", "unfixed", "testfpga", "loopdebug") + set_configdir("$(buildir)/config") + add_configfiles("config.h.in", {prefixdir = "header"}) + add_includedirs("$(buildir)/config/header", { public = true }) + +includes("usb", "scanner") + +-- includes("regs", "deviceio", "motorboard", "capimage", "usb", "service", "scanner", "imgproc", "applog","scanservice","fpgaupdate","motorcontroller","display","testlcd","keymonitor","testkeymonitor","testdisplay","testwakeup") + +-- if has_config("testdemo") then +-- includes("testmotorboard", "testcapimage", "testusb", "testscanner", "testimgproc", "testregs","testwakeup") +-- end