From 6c350e3b0507dcda9668ff55bccb45fbe6e448c1 Mon Sep 17 00:00:00 2001 From: gb <741021719@qq.com> Date: Wed, 26 Oct 2022 14:41:18 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9JSON=E4=BB=A5=E5=85=BC?= =?UTF-8?q?=E5=AE=B9APP=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sane/gb_json.cpp | 562 +++++++++++++++++++++++++++++++++++++++++++---- sane/gb_json.h | 91 +++++++- sane/scanner.cpp | 4 +- 3 files changed, 609 insertions(+), 48 deletions(-) diff --git a/sane/gb_json.cpp b/sane/gb_json.cpp index cf1d576..428b8b0 100644 --- a/sane/gb_json.cpp +++ b/sane/gb_json.cpp @@ -30,6 +30,29 @@ namespace gb return ret; } + static int load_mini_file(const char* file, std::string& cont) + { + FILE* src = fopen(file, "rb"); + if (src) + { + size_t size = 0; + char *buf = NULL; + + fseek(src, 0, SEEK_END); + size = ftell(src); + fseek(src, 0, SEEK_SET); + buf = new char[size + 4]; + memset(buf, 0, size + 4); + fread(buf, 1, size, src); + fclose(src); + cont = std::string(buf, size); + delete[] buf; + + return 0; + } + else + return errno; + } refer::refer() : ref_(1) {} @@ -856,19 +879,209 @@ namespace gb } } +namespace updater +{ + static std::string hg_model_from_pid(const char* pid) + { + if (strcmp(pid, "7823") == 0) + return "G200"; + char m[] = { 'G', pid[0], '0', '0', 0}; + + return std::string(m) + " - " + pid; + } + static std::string hv_model_from_pid(const char* pid) + { + std::string m(""); + if (strcmp(pid, "1000") == 0) + m = "HW-1000NS"; + else if (strcmp(pid, "1002") == 0) + m = "HW-1000"; + else if (strcmp(pid, "7000") == 0) + m = "HW-7000NS"; + else if (strcmp(pid, "7002") == 0) + m = "HW-7000"; + else if (strcmp(pid, "7039") == 0) + m = "HW-7000NS"; + else + m = std::string("HW-") + pid; + + return m + " - " + pid; + } + static std::string lsc_model_from_pid(const char* pid) + { + if (strcmp(pid, "8200") == 0) + return "G42S - 8200"; + else + { + char m[] = {'G', pid[1], pid[2], 'S', 0}; + + return std::string(m) + " - " + pid; + } + } + static std::string scanner_chinese_name_2_model(const char* cn) + { + static const char* hg = "\345\215\216\351\253\230", + * hw = "\346\261\211\347\216\213", + * lsc = "\347\253\213\346\200\235\350\276\260", + * smy = "\346\211\253\346\217\217\344\273\252\342\200\224G", + * f = strstr(cn, hg); + std::string model(""); + std::string(* model_from_pid)(const char* pid) = nullptr; + + if (f == cn) + { + model = "HUAGOSCAN "; + model_from_pid = hg_model_from_pid;; + } + else if (strstr(cn, hw) == cn) + { + model = "Hanvon "; + model_from_pid = hv_model_from_pid;; + } + else if (strstr(cn, lsc) == cn) + { + model = "LANXUMSCAN "; + model_from_pid = lsc_model_from_pid;; + } + else + return ""; + + f = strstr(cn, smy); + if (!f) + return ""; + + f += strlen(smy); + model += model_from_pid(f); + + return model; + } + static int update_app_config(const char* scanner_name, const char* jsn_txt, const char* path, gb::scanner_cfg::LPUDF lpfunc) + { + std::string scanner(""), jsn_str(jsn_txt); + std::vector efiles; + + if ((unsigned char)scanner_name[0] > 0x7f) + scanner = scanner_chinese_name_2_model(scanner_name); + else + scanner = scanner_name; + + gb::json* jsn = new gb::json(); + int cur_sel = -1, ret = 0; + gb::scanner_cfg* cfg = nullptr; + if (!jsn->attach_text(&jsn_str[0])) + { + jsn->release(); + return EINVAL; + } + + if (jsn->first_child(jsn_str)) + { + gb::json* child = new gb::json(); + if (child->attach_text(&jsn_str[0])) + { + if (!child->get_value("cur_sel", cur_sel)) + ret = EINVAL; + } + if (ret == 0) + { + cfg = new gb::scanner_cfg(); + int ind = 0; + while (jsn->next_child(jsn_str)) + { + if (!child->attach_text(&jsn_str[0])) + { + ret = EINVAL; + break; + } + + std::string schm_name(""); + if (!child->get_value("scheme", schm_name)) + { + ret = EINVAL; + break; + } + + gb::json* items = nullptr; + if (!child->get_value("opts", items) || !items) + { + ret = EINVAL; + break; + } + + gb::sane_config_schm* schm = new gb::sane_config_schm(); + if (items->first_child(jsn_str)) + { + do + { + std::string name(""), val(""); + gb::json* item = new gb::json(); + if (item->attach_text(&jsn_str[0])) + { + if (item->get_value("name", name) && item->get_value("value", val)) + { + name = lpfunc->title2name(name.c_str(), lpfunc->func_param); + lpfunc->trans_number(name.c_str(), val, lpfunc->func_param); + schm->set_value(name.c_str(), val.c_str(), val.length()); + + val = ""; + item->get_value("extra", val); + if (val.length() && gb::load_mini_file(val.c_str(), val) == 0) + { + schm->set_value(name.c_str(), val.c_str(), val.length(), true); + item->get_value("extra", val); + efiles.push_back(val); + } + } + } + item->release(); + } while (items->next_child(jsn_str)); + } + items->release(); + + cfg->add_scheme(schm, schm_name.c_str()); + schm->release(); + if (ind++ == cur_sel) + cfg->select_scheme(schm_name.c_str()); + } + } + child->release(); + } + jsn->release(); + if (cfg) + { + if (ret == 0) + { + cfg->save((path + scanner + ".cfg").c_str()); + for (auto& v : efiles) + rename(v.c_str(), (v + "_bk").c_str()); + } + cfg->release(); + } + + return ret; + } +} namespace gb { std::string sane_config_schm::opt_data_appendix_("_data"); - sane_config_schm::sane_config_schm() : jsn_(NULL), bkp_(NULL), in_setting_(false), scheme_name_("") + sane_config_schm::sane_config_schm(scanner_cfg* scanner) : jsn_(NULL), bkp_(NULL), in_setting_(false), scheme_name_("") + , scanner_(scanner) { + char empty[8] = { "{}" }; + jsn_ = new gb::json(); + jsn_->attach_text(empty); def_val_ = new gb::json(); + if (scanner_) + scanner_->add_ref(); } sane_config_schm::~sane_config_schm() { clear(); def_val_->release(); + if (scanner_) + scanner_->release(); } bool sane_config_schm::hex(unsigned char ch, unsigned char* val) @@ -936,7 +1149,7 @@ namespace gb if (bkp_) bkp_->release(); bkp_ = NULL; - file_ = L""; + // file_ = ""; } std::string sane_config_schm::to_hex_letter(const char* data, size_t bytes) { @@ -977,36 +1190,22 @@ namespace gb return val; } - bool sane_config_schm::load_from_file(const wchar_t* file) + bool sane_config_schm::load_from_file(const char* file) { clear(); - bool ret = false; - FILE* src = _wfopen(file, L"rb"); - if (src) - { - size_t size = 0; - char* buf = NULL; - gb::base64 b64; + std::string cont(""); - fseek(src, 0, SEEK_END); - size = ftell(src); - fseek(src, 0, SEEK_SET); - buf = new char[size + 4]; - memset(buf, 0, size + 4); - fread(buf, 1, size, src); - fclose(src); - ret = load_from_mem(buf); - delete[] buf; - } file_ = file; + if (gb::load_mini_file(file, cont)) + return false; - return ret; + return load_from_mem(cont.c_str()); } - bool sane_config_schm::load_from_mem(const char* mem) + bool sane_config_schm::load_from_mem(const char* mem, bool in_b64) { gb::base64 b64; - std::string stream(b64.decode(mem, lstrlenA(mem))); + std::string stream(in_b64 ? b64.decode(mem, lstrlenA(mem)) : mem); clear(); jsn_ = new gb::json(); @@ -1020,7 +1219,7 @@ namespace gb return true; } - bool sane_config_schm::save_to(const wchar_t* file) + bool sane_config_schm::save_to(const char* file) { bool ret = false; std::string encode(to_text_stream()); @@ -1030,7 +1229,7 @@ namespace gb if (encode.length()) { - FILE* dst = _wfopen(file, L"wb"); + FILE* dst = fopen(file, "wb"); if (dst) { @@ -1091,13 +1290,7 @@ namespace gb } void sane_config_schm::config_changed(const char* name, const char* val, size_t bytes, bool extra) { - std::string hex_v(to_hex_letter(val, bytes)), - def(default_value(name)); - - if (hex_v == def) - jsn_->remove(name); - else - jsn_->set_value(name, hex_v.c_str()); + std::string hex_v(to_hex_letter(val, bytes)); if (extra) { @@ -1105,7 +1298,7 @@ namespace gb } else { - def = default_value(name); + std::string def = default_value(name); if (hex_v == def) { jsn_->remove(name); @@ -1130,6 +1323,15 @@ namespace gb if (jsn_) jsn_->remove(name); } + void sane_config_schm::set_value(const char* name, const char* val, size_t bytes, bool extra) + { + std::string hex_v(to_hex_letter(val, bytes)); + + if (extra) + jsn_->set_value((name + sane_config_schm::opt_data_appendix_).c_str(), hex_v.c_str()); + else + jsn_->set_value(name, hex_v.c_str()); + } void sane_config_schm::end_setting(bool cancel) { if (in_setting_) @@ -1158,22 +1360,26 @@ namespace gb return -1; } - std::string sane_config_schm::to_text_stream(void) + std::string sane_config_schm::to_text_stream(bool b64, bool with_ver) { if (jsn_) { + if(with_ver) { char ver[40] = { 0 }; sprintf_s(ver, "%u.%u", VERSION_MAIN, VERSION_SUB); jsn_->set_value("ver", ver); } - std::string cont(jsn_->to_string(false)), encode(""); - gb::base64 b64; + std::string cont(jsn_->to_string(false)); + if (b64) + { + gb::base64 b64; - encode = b64.encode(cont.c_str(), cont.length()); + cont = b64.encode(cont.c_str(), cont.length()); + } - return encode; + return cont; } else return ""; @@ -1254,4 +1460,284 @@ namespace gb /////////////////////////////////////////////////////////////////////////////////// // scanner_cfg + std::string scanner_cfg::global_name_ = "global"; + std::string scanner_cfg::cur_sel_ = "cur"; + std::string scanner_cfg::default_setting_name_ = "\351\273\230\350\256\244\350\256\276\347\275\256"; // utf-8: 默认设置 + + scanner_cfg::scanner_cfg() : path_(""), scanner_name_(""), global_(new json()) + { + init_version(); + init_select(); + } + scanner_cfg::~scanner_cfg() + { + clear(); + global_->release(); + } + + bool scanner_cfg::update(const char* file, LPUDF func) + { + std::string cont(""), name(""), path(file); + int ret = gb::load_mini_file(file, cont); + base64 b64; + json *jsn = nullptr; + bool ok = true; + + if (ret) + return false; + else if (cont.empty()) + return true; + + cont = b64.decode(cont.c_str(), cont.length()); + jsn = new json(); + if (!jsn->attach_text(&cont[0])) + { + jsn->release(); + return false; + } + + cont = ""; + ret = path.rfind(PATH_SYMBOL[0]); + if (ret++ != std::string::npos) + path.erase(ret); + if (jsn->first_child(cont, &name)) + { + do + { + ok &= updater::update_app_config(name.c_str(), cont.c_str(), path.c_str(), func) == 0; + } while (jsn->next_child(cont, &name)); + } + jsn->release(); + if (ok) + rename(file, (std::string(file) + "_bk").c_str()); + + return true; + } + + void scanner_cfg::clear(void) + { + global_->set_value("ver", ""); + global_->set_value(scanner_cfg::cur_sel_.c_str(), -1); + + for (size_t i = 0; i < schemes_.size(); ++i) + schemes_[i].schm->release(); + schemes_.clear(); + scanner_name_ = ""; + } + void scanner_cfg::init_version(void) + { + char vstr[40] = { 0 }; + + sprintf(vstr, "%u.%u", VERSION_MAIN, VERSION_SUB); + global_->set_value("ver", vstr); + } + void scanner_cfg::init_select(void) + { + global_->set_value(scanner_cfg::cur_sel_.c_str(), -1); + } + void scanner_cfg::walk_sibling_schemes(cJSON* first) + { + if (!first) + return; + + cJSON* next = first->next; + std::string name(first->string ? "" : first->string), + cont(""); + CFGSCHM sch; + + first->next = nullptr; + cont = json::to_string(first, false); + if (name == scanner_cfg::global_name_) + { + global_->attach_text(&cont[0]); + } + else + { + sch.schm = new sane_config_schm(); + if (sch.schm->load_from_mem(cont.c_str(), false)) + { + sch.name = sane_config_schm::from_hex_letter(name.c_str(), name.length()); + sch.schm->set_scheme_name(sch.name.c_str()); + schemes_.push_back(sch); + } + else + sch.schm->release(); + } + + first->next = next; + walk_sibling_schemes(next); + } + + int scanner_cfg::load_file(const char* file) + { + std::string cont(""); + int ret = gb::load_mini_file(file, cont); + + if (ret == 0) + ret = load_mem(cont.c_str()); + + if (ret == 0 && scanner_name_.empty()) + { + const char* name = strrchr(file, PATH_SYMBOL[0]); + if (name++ == nullptr) + name = file; + + scanner_name_ = name; + ret = scanner_name_.rfind('.'); + if (ret != std::string::npos) + scanner_name_.erase(ret); + ret = 0; + } + + return ret; + } + int scanner_cfg::load_mem(const char* mem) + { + cJSON* root = cJSON_Parse(mem); + + if (!root) + return EINVAL; + + clear(); + walk_sibling_schemes(root->child); + cJSON_Delete(root); + + return 0; + } + int scanner_cfg::save(const char* file) + { + if (!file && path_.empty() && scanner_name_.empty()) + return EINVAL; + + std::string cont("{\"" + scanner_cfg::global_name_ + "\":"), + f(file ? file : path_ + scanner_name_), + v(""); + int sel = -1; + + if (!global_->get_value("ver", v) || v.empty()) + init_version(); + if (!global_->get_value(scanner_cfg::cur_sel_.c_str(), sel) || sel >= schemes_.size()) + init_select(); + + cont += global_->to_string(false); + for (auto& v: schemes_) + { + cont += ",\"" + sane_config_schm::to_hex_letter(v.name.c_str(), v.name.length()) + "\":"; + cont += v.schm->to_text_stream(false, false); + } + cont += "}"; + + base64 b64; + FILE* dst = fopen(f.c_str(), "wb"); + + if (!dst) + return errno; + + f = b64.encode(cont.c_str(), cont.length()); + fwrite(f.c_str(), 1, f.length(), dst); + fclose(dst); + + return 0; + } + + void scanner_cfg::get_all_schemes(std::vector& schemes) + { + schemes.push_back(scanner_cfg::default_setting_name_); + for (auto& v : schemes_) + schemes.push_back(v.name); + } + sane_config_schm* scanner_cfg::get_scheme(const char* scheme_name) + { + sane_config_schm* found = nullptr; + + if (scheme_name) + { + std::vector::iterator it = std::find(schemes_.begin(), schemes_.end(), scheme_name); + if (it != schemes_.end()) + found = it->schm; + } + else + { + int ind = -1; + + global_->get_value(scanner_cfg::cur_sel_.c_str(), ind); + if (ind >= 0 && ind < schemes_.size()) + found = schemes_[ind].schm; + } + + if (found) + found->add_ref(); + + return found; + } + bool scanner_cfg::remove_scheme(const char* scheme_name) + { + std::vector::iterator it = std::find(schemes_.begin(), schemes_.end(), scheme_name); + + if (it != schemes_.end()) + { + int id = it - schemes_.begin(), + ind = -1; + + it->schm->release(); + schemes_.erase(it); + global_->get_value(scanner_cfg::cur_sel_.c_str(), ind); + if (ind == id) + global_->set_value(scanner_cfg::cur_sel_.c_str(), -1); + else if (ind > id) + global_->set_value(scanner_cfg::cur_sel_.c_str(), ind - 1); + + return true; + } + + return false; + } + bool scanner_cfg::select_scheme(const char* scheme_name) + { + std::vector::iterator it = std::find(schemes_.begin(), schemes_.end(), scheme_name); + + if (it == schemes_.end()) + global_->set_value(scanner_cfg::cur_sel_.c_str(), -1); + else + global_->set_value(scanner_cfg::cur_sel_.c_str(), it - schemes_.begin()); + + return true; + } + + sane_config_schm* scanner_cfg::copy_scheme(const char* cp_from_name) // for UI setting, call release() if not use anymore + { + if (!cp_from_name) + return nullptr; + else if (scanner_cfg::default_setting_name_ == cp_from_name) + return new sane_config_schm(); + else + { + std::vector::iterator it = std::find(schemes_.begin(), schemes_.end(), cp_from_name); + if (it == schemes_.end()) + return nullptr; + + std::string cont(it->schm->to_text_stream()); + sane_config_schm* schm = new sane_config_schm(); + schm->load_from_mem(cont.c_str()); + + return schm; + } + } + bool scanner_cfg::add_scheme(sane_config_schm* schm, const char* name) + { + if (name && std::find(schemes_.begin(), schemes_.end(), name) != schemes_.end()) + return false; + + CFGSCHM cs; + cs.name = name ? name : schm->get_scheme_name(); + cs.schm = schm; + if (cs.name == scanner_cfg::global_name_) + return false; + + schemes_.push_back(cs); + schm->set_scheme_name(cs.name.c_str()); + schm->add_ref(); + + return true; + } } diff --git a/sane/gb_json.h b/sane/gb_json.h index be55419..35ffec5 100644 --- a/sane/gb_json.h +++ b/sane/gb_json.h @@ -2,6 +2,9 @@ #if defined(WIN32) || defined(_WIN64) #include +#define PATH_SYMBOL "\\" +#else +#define PATH_SYMBOL "/" #endif // #include "cJSON.h" @@ -12,6 +15,7 @@ namespace gb { + class scanner_cfg; class refer { volatile long ref_; @@ -101,7 +105,8 @@ namespace gb class sane_config_schm : public refer { std::string scheme_name_; - std::wstring file_; + scanner_cfg *scanner_; + std::string file_; json* jsn_; json* bkp_; json* def_val_; @@ -109,25 +114,25 @@ namespace gb std::map id_name_; // (id, default-val) void clear(); - std::string to_hex_letter(const char* data, size_t bytes); - std::string from_hex_letter(const char* data, size_t bytes); std::string default_value(const char* name); protected: ~sane_config_schm(); public: - sane_config_schm(); + sane_config_schm(scanner_cfg* scanner = nullptr); static std::string opt_data_appendix_; static bool hex(unsigned char ch, unsigned char* val); static bool hex_char(const char* data, unsigned char* val); + static std::string to_hex_letter(const char* data, size_t bytes); + static std::string from_hex_letter(const char* data, size_t bytes); static bool is_option_data(std::string& name); // reset baase option name into 'name' if name was option data, and return true public: - bool load_from_file(const wchar_t* file); - bool load_from_mem(const char* mem); - bool save_to(const wchar_t* file); + bool load_from_file(const char* file); + bool load_from_mem(const char* mem, bool in_b64 = true); + bool save_to(const char* file); void set_default_value(int sn, const char* name, const char* val, size_t bytes); bool first_config(std::string& name, std::string& val); bool next_config(std::string& name, std::string& val); @@ -135,12 +140,82 @@ namespace gb void config_changed(const char* name, const char* val, size_t bytes, bool extra = false); void config_changed(int sn, const char* val, size_t bytes, bool extra = false); void remove_config(const char* name); + void set_value(const char* name, const char* val, size_t bytes, bool extra = false); void end_setting(bool cancel); int id_from_name(const char* name); - std::string to_text_stream(void); + std::string to_text_stream(bool b64 = true, bool with_ver = true); std::string get_version(void); std::string get_scheme_name(void); void set_scheme_name(const char* name); void update(bool(__stdcall* is_float)(int, void*), void* param, const char*(__stdcall* t2n)(const char*), std::string* discard = NULL); }; + + class scanner_cfg : public refer + { + // format: in base64 + // + // { + // "global": { + // "ver": "4.33", + // "cur": -1 + // }, + // "scheme_1": sane_config_schm*, + // "scheme_2": sane_config_schm*, + // "scheme_3": sane_config_schm*, + // ... + // } + // + std::string path_; + std::string scanner_name_; // scanner type: HUAGOSCAN G100 - 0100 + json *global_; // version, current scheme, ... + + typedef struct _cfg_schm + { + std::string name; + sane_config_schm* schm; + + bool operator==(const char* n) + { + return name == n; + } + }CFGSCHM; + std::vector schemes_; + + static std::string global_name_; + static std::string cur_sel_; + static std::string default_setting_name_; + + void clear(void); + void init_version(void); + void init_select(void); + void walk_sibling_schemes(cJSON* first); + + protected: + ~scanner_cfg(); + + public: + scanner_cfg(); + + typedef struct _update_func + { + void(__stdcall* trans_number)(const char* name, std::string& val, void* param); + const char* (__stdcall* title2name)(const char* title, void* param); + std::string discard_msg; // update failed items ... + void* func_param; + }UDF, *LPUDF; + static bool update(const char* file, LPUDF func); + + public: + int load_file(const char* file); + int load_mem(const char* mem); + int save(const char* file = nullptr); + + void get_all_schemes(std::vector& schemes); // return all schemes name queue, the first is always be 'Default settings' + sane_config_schm* get_scheme(const char* scheme_name = nullptr/*return current scheme if was null*/); // call sane_config_schm::release() if not use anymore + bool remove_scheme(const char* scheme_name); + bool select_scheme(const char* scheme_name); + + sane_config_schm* copy_scheme(const char* cp_from_name); // for UI setting, call release() if not use anymore + bool add_scheme(sane_config_schm* schm, const char* name = nullptr); + }; } diff --git a/sane/scanner.cpp b/sane/scanner.cpp index 0f6b2aa..447ea51 100644 --- a/sane/scanner.cpp +++ b/sane/scanner.cpp @@ -559,12 +559,12 @@ void scanner::update_config(void) } void scanner::load_config(const wchar_t* file) { - cfg_->load_from_file(file); + cfg_->load_from_file(local_trans::u2a(file).c_str()); update_config(); } void scanner::save_config(const wchar_t* file) { - cfg_->save_to(file); + cfg_->save_to(local_trans::u2a(file).c_str()); } void scanner::apply_config(void) {