#include "gb_json.h" #include #include #include "../../sdk/include/huagao/brand.h" #if defined(WIN32) || defined(_WIN64) #define bzero(b, s) memset(b, 0, s) #endif namespace gb { static std::vector split_with(const char* str, const char* splitor = "/") { std::vector ret; std::string src(str); size_t pos = src.find(splitor); while(pos != std::string::npos) { if(pos++) ret.push_back(src.substr(0, pos - 1)); src.erase(0, pos + strlen(splitor)); pos = src.find(splitor); } if(src.length()) ret.push_back(src); return ret; } json::json(char* json_txt) : obj_(0), cur_child_(0), is_array_(false) { memset(&walk_head_, 0, sizeof(walk_head_)); attach_text(json_txt); } json::~json() { memset(&walk_head_, 0, sizeof(walk_head_)); clear(); } std::string json::to_string(cJSON* root, bool formatted) { char* txt = formatted ? cJSON_Print(root) : cJSON_PrintUnformatted(root); std::string ret(txt ? txt : ""); if (txt) free(txt); return ret; } std::string json::get_value_as_string(cJSON* root, bool integer) { std::string ret(""); switch (root->type) { case cJSON_False: ret = "false"; break; case cJSON_True: ret = "true"; break; case cJSON_NULL: ret = "null"; break; case cJSON_Number: { char buf[40]; if (integer) sprintf(buf, "%d", root->valueint); else sprintf(buf, "%f", root->valuedouble); ret = buf; } break; case cJSON_String: if (root->valuestring) ret = root->valuestring; break; default: ret = json::to_string(root, false); break; } return ret; } void json::free_node_data(cJSON* node) { if (node->type == cJSON_String && node->valuestring) free(node->valuestring); else if((node->type == cJSON_Object || node->type == cJSON_Array) && node->child) cJSON_Delete(node->child); node->type = cJSON_NULL; node->valuestring = NULL; node->child = NULL; } cJSON* json::create_element(bool is_array) { cJSON* obj = is_array ? cJSON_CreateArray() : cJSON_CreateObject(); // bzero(obj, sizeof(*obj)); // cleared in constructor already ! return obj; } cJSON* json::create_element_with_name(const char* name) { cJSON* obj = json::create_element(); if(name) { obj->string = (char*)malloc(strlen(name) + 4); bzero(obj->string, strlen(name) + 4); strcpy(obj->string, name); } return obj; } cJSON* json::find_sibling(cJSON* first, const char* name, cJSON*** prev) { cJSON* now = first, **prv = NULL; while(now) { if(now->string && strcmp(now->string, name) == 0) { break; } prv = &now->next; now = now->next; } if(prev) *prev = prv; return now; } cJSON* json::find_child(cJSON *parent, std::vector& path, bool create, cJSON*** addr) { if(!parent->child) { if(!create) return NULL; parent->child = json::create_element_with_name(path[0].c_str()); if(path.size() == 1) { if(addr) *addr = &parent->child; return parent->child; } } cJSON** prev = NULL, *now = find_sibling(parent->child, path[0].c_str(), &prev); if(!now) { if(!create) return now; now = json::create_element_with_name(path[0].c_str()); if (prev) { cJSON* pr = (cJSON*)((DWORD_PTR)prev - (DWORD_PTR)&((cJSON*)0)->next); pr->next = now; now->prev = pr; // *prev = now; } else { obj_->child = now; prev = &obj_->child; } } path.erase(path.begin()); if(path.empty()) { if(addr) *addr = prev ? prev : &parent->child; return now; } return find_child(now, path, create, addr); } cJSON* json::find(const char* path, bool create, cJSON*** addr) { std::vector tree(split_with(path)); if(tree.empty()) return NULL; if(!obj_) { if(!create) return NULL; obj_ = json::create_element(); obj_->child = json::create_element_with_name(tree[0].c_str()); } return find_child(obj_, tree, create, addr); } bool json::attach_text(char* json_txt) { clear(); obj_ = cJSON_Parse(json_txt); if(obj_) is_array_ = obj_->type == cJSON_Array; return obj_ != 0; } bool json::attach_cjson(cJSON* cjson) { clear(); if (cjson) { std::string txt(json::to_string(cjson, false)); if (txt.length()) obj_ = cJSON_Parse(txt.c_str()); } if(obj_) is_array_ = obj_->type == cJSON_Array; return obj_ != 0; } bool json::create_empty(bool array) { clear(); obj_ = json::create_element(array); is_array_ = array; return true; } void json::clear(void) { if (obj_) { cJSON_Delete(obj_); obj_ = 0; } } std::string json::to_string(bool formatted) { if (obj_) return json::to_string(obj_, formatted); else return ""; } bool json::get_value(const char* key, bool& val) { cJSON* obj = find(key); if (!obj) return false; if (obj->type == cJSON_True) val = true; else if (obj->type == cJSON_False) val = false; else return false; return true; } bool json::get_value(const char* key, int& val) { cJSON* obj = find(key); if (!obj) return false; if (obj->type != cJSON_Number) return false; val = obj->valueint; return true; } bool json::get_value(const char* key, double& val) { cJSON *obj = find(key); if (!obj) return false; if (obj->type != cJSON_Number) return false; val = obj->valuedouble; return true; } bool json::get_value(const char* key, std::string& val) { cJSON *obj = find(key); if (!obj) return false; if (obj->type != cJSON_String) return false; val = obj->valuestring ? obj->valuestring : ""; return true; } bool json::get_value(const char* key, json*& val) { cJSON *obj = find(key); if (!obj) return false; val = new json(); if (!val->attach_cjson(obj)) { delete val; return false; } return true; } bool json::get_value_as_string(const char* key, std::string& val, bool integer) { cJSON* obj = find(key); if (!obj) return false; val = json::get_value_as_string(obj, integer); return true; } bool json::get_as_array(const char* key, std::vector& val) { cJSON *obj = find(key); val.clear(); if (obj && obj->type == cJSON_Array) { cJSON *child = obj->child; while (child) { if (child->type == cJSON_Number) { char buf[40]; sprintf(buf, "%d", child->valueint); val.push_back(buf); } else if (child->type == cJSON_String) val.push_back(child->valuestring ? child->valuestring : ""); else { char *text = cJSON_Print(child); val.push_back(text); free(text); } child = child->next; } return true; } return false; } bool json::first_child(std::string& val, std::string* name) { cur_child_ = obj_->child; val = ""; if (cur_child_) { val = json::get_value_as_string(cur_child_); if (name && cur_child_->string) *name = cur_child_->string; return true; } else { return false; } } bool json::next_child(std::string& val, std::string* name) { if (cur_child_) cur_child_ = cur_child_->next; val = ""; if (cur_child_) { val = json::get_value_as_string(cur_child_); if (name && cur_child_->string) *name = cur_child_->string; return true; } else { return false; } } bool json::set_value(const char* key, bool val) { if(!key) { if(is_array_) { if(!obj_) obj_ = json::create_element(true); cJSON_AddItemToArray(obj_, val ? cJSON_CreateTrue() : cJSON_CreateFalse()); } return is_array_; } cJSON* ele = this->find(key, true); if (!ele) return false; json::free_node_data(ele); if (val) ele->type = cJSON_True; else ele->type = cJSON_False; return true; } bool json::set_value(const char* key, int val) { if(!key) { if(is_array_) { if(!obj_) obj_ = json::create_element(true); cJSON_AddItemToArray(obj_, cJSON_CreateNumber(val)); } return is_array_; } cJSON* ele = this->find(key, true); if (!ele) return false; json::free_node_data(ele); ele->type = cJSON_Number; ele->valuedouble = ele->valueint = val; return true; } bool json::set_value(const char* key, double val) { if(!key) { if(is_array_) { if(!obj_) obj_ = json::create_element(true); cJSON_AddItemToArray(obj_, cJSON_CreateNumber(val)); } return is_array_; } cJSON* ele = this->find(key, true); if (!ele) return false; json::free_node_data(ele); ele->type = cJSON_Number; ele->valuedouble = val; return true; } bool json::set_value(const char* key, std::string val) { if(!key) { if(is_array_) { if(!obj_) obj_ = json::create_element(true); cJSON_AddItemToArray(obj_, cJSON_CreateString(val.c_str())); } return is_array_; } cJSON* ele = this->find(key, true); if (!ele) return false; json::free_node_data(ele); ele->type = cJSON_String; ele->valuestring = (char*)malloc(val.length() + 4); bzero(ele->valuestring, val.length() + 4); strcpy(ele->valuestring, val.c_str()); return true; } bool json::set_value(const char* key, const char* val) { return set_value(key, std::string(val)); } bool json::set_value(const char* key, json* obj) { if(!key) { if(is_array_) { if(!obj_) obj_ = json::create_element(true); if(obj && obj->obj_) { cJSON_AddItemToArray(obj_, obj->obj_); obj->obj_ = NULL; } } return is_array_; } cJSON** addr = NULL; cJSON* ele = this->find(key, true, &addr); if (!ele) return false; // json::free_node_data(ele); cJSON_Delete(ele); *addr = obj->obj_; ele = obj->obj_; if(ele->string) free(ele->string); ele->string = (char*)malloc(strlen(key) + 4); bzero(ele->string, strlen(key) + 4); strcpy(ele->string, key); obj->obj_ = NULL; return true; } bool json::change_key(const char* old_key, const char* new_key) { if (!obj_ || !new_key || *new_key == 0 || !old_key || *old_key == 0) return false; cJSON** addr = NULL, *ele = find(old_key, false, &addr); if (!ele) return false; if (strlen(ele->string) < strlen(new_key)) { int l = strlen(new_key) + 4; free(ele->string); ele->string = (char*)malloc(l); memset(ele->string, 0, l); } strcpy(ele->string, new_key); return true; } bool json::remove(const char* key) { if(!obj_) return false; cJSON **addr = NULL, *ele = find(key, false, &addr); if(ele) { bool cur_child = cur_child_ == obj_->child; if(addr) *addr = ele->next; if (cur_child_ == ele) { if (cur_child) { walk_head_.next = obj_->child; cur_child_ = &walk_head_; } else cur_child_ = ele->prev; } if (ele->prev) ele->prev->next = ele->next; if (ele->next) ele->next->prev = ele->prev; ele->prev = NULL; ele->next = NULL; cJSON_Delete(ele); return true; } else return false; } } namespace gb { 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; } } namespace gb { sane_config::sane_config() : jsn_(NULL), bkp_(NULL), in_setting_(false) { def_val_ = new gb::json(); } sane_config::~sane_config() { clear(); delete def_val_; } bool sane_config::hex(unsigned char ch, unsigned char* val) { bool ret = true; if (ch >= '0' && ch <= '9') ch -= '0'; else if (ch >= 'a' && ch <= 'f') ch = ch - 'a' + 10; else if (ch >= 'A' && ch <= 'F') ch = ch - 'A' + 10; else ret = false; if (ret && val) { *val &= 0x0f0; *val |= ch; } return ret; } bool sane_config::hex_char(const char* data, unsigned char* val) { unsigned char v = 0; bool ret = false; if (sane_config::hex(*data++, &v)) { v <<= 4; if (sane_config::hex(*data++, &v)) { if (val) *val = v; ret = true; } } return ret; } void sane_config::clear() { if (jsn_) delete jsn_; jsn_ = NULL; if (bkp_) delete bkp_; bkp_ = NULL; file_ = L""; } std::string sane_config::to_hex_letter(const char* data, size_t bytes) { std::string hex(""); const unsigned char* ptr = (const unsigned char*)data; char buf[8] = { 0 }; for (size_t i = 0; i < bytes; ++i) { sprintf_s(buf, _countof(buf) - 1, "%02X", ptr[i]); hex += buf; } return hex; } std::string sane_config::from_hex_letter(const char* data, size_t bytes) { std::string stream(""); bytes /= 2; for (size_t i = 0; i < bytes; ++i) { unsigned char ch = 0; if (!sane_config::hex_char(data, &ch)) break; stream.append(1, ch); data += 2; } return stream; } std::string sane_config::default_value(const char* hex_title) { std::string val(""); def_val_->get_value(hex_title, val); return val; } bool sane_config::load_from_file(const wchar_t* file) { clear(); bool ret = false; FILE* src = _wfopen(file, L"rb"); if (src) { size_t size = 0; char* buf = NULL; gb::base64 b64; 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; return ret; } bool sane_config::load_from_mem(const char* mem) { gb::base64 b64; std::string stream(b64.decode(mem, lstrlenA(mem))); clear(); jsn_ = new gb::json(); if (!jsn_->attach_text(&stream[0])) { delete jsn_; jsn_ = NULL; return false; } return true; } bool sane_config::save_to(const wchar_t* file) { bool ret = false; std::string encode(to_text_stream()); if (!file || *file == 0) file = file_.c_str(); if (encode.length()) { FILE* dst = _wfopen(file, L"wb"); if (dst) { fwrite(encode.c_str(), 1, encode.length(), dst); fclose(dst); ret = true; } } return ret; } void sane_config::set_default_value(int sn, const char* name, const char* val, size_t bytes) { id_name_[sn] = name; def_val_->set_value(name, to_hex_letter(val, bytes).c_str()); } bool sane_config::first_config(std::string& name, std::string& val) { bool ret = false; std::string raw_v(""); if (jsn_ && jsn_->first_child(raw_v, &name)) { val = sane_config::from_hex_letter(raw_v.c_str(), raw_v.length()); ret = true; } return ret; } bool sane_config::next_config(std::string& name, std::string& val) { bool ret = false; std::string raw_v(""); if (jsn_ && jsn_->next_child(raw_v, &name)) { val = sane_config::from_hex_letter(raw_v.c_str(), raw_v.length()); ret = true; } return ret; } void sane_config::begin_setting(bool restore) { if (bkp_) delete bkp_; bkp_ = jsn_; in_setting_ = true; jsn_ = new gb::json(); if (!restore && bkp_) { std::string stream(bkp_->to_string(false)); if(stream.length()) jsn_->attach_text(&stream[0]); } } void sane_config::config_changed(const char* name, const char* val, size_t bytes) { 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()); } void sane_config::config_changed(int sn, const char* val, size_t bytes) { std::string name(""), hex_v(to_hex_letter(val, bytes)), def(""); if (id_name_.count(sn)) { name = id_name_[sn]; def = default_value(name.c_str()); if (hex_v == def) jsn_->remove(name.c_str()); else jsn_->set_value(name.c_str(), hex_v.c_str()); } } void sane_config::remove_config(const char* name) { if (jsn_) jsn_->remove(name); } void sane_config::end_setting(bool cancel) { if (in_setting_) { if (cancel) { delete jsn_; jsn_ = bkp_; bkp_ = NULL; } else if (bkp_) { delete bkp_; bkp_ = NULL; } } in_setting_ = false; } int sane_config::id_from_name(const char* name) { for (const auto& v : id_name_) { if (v.second == name) return v.first; } return -1; } std::string sane_config::to_text_stream(void) { if (jsn_) { { 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; encode = b64.encode(cont.c_str(), cont.length()); return encode; } else return ""; } std::string sane_config::get_version(void) { std::string ver(""); if (jsn_) jsn_->get_value("ver", ver); return ver; } void sane_config::update(bool(__stdcall* is_float)(int, void*), void* param, const char* (__stdcall* t2n)(const char*), std::string* discard) { if (!jsn_) return; std::string ver(get_version()), name(""), val(""); int mv = atoi(ver.c_str()), sv = ver.find("."); bool changed = false; char vs[40] = { 0 }; if (sv++ != -1) sv = atoi(ver.c_str() + sv); // change title to name ... if (mv <= 4 && sv < 30) { if (jsn_->first_child(val, &name)) { changed = true; do { jsn_->change_key(name.c_str(), t2n(sane_config::from_hex_letter(name.c_str(), name.length()).c_str())); } while (jsn_->next_child(val, &name)); } } // fix float does not convert to SANE_Fixed bug in eldest version .... (discard them) if (ver.empty()) { if (jsn_->first_child(val, &name)) { do { int id = id_from_name(name.c_str()); if (id == -1 || is_float(id, param)) { jsn_->remove(name.c_str()); if (discard) *discard += name + "\r\n"; changed = true; } } while (jsn_->next_child(val, &name)); } } sprintf_s(vs, _countof(vs) - 1, "%u.%u", VERSION_MAIN, VERSION_SUB); jsn_->set_value("ver", vs); if (changed) save_to(NULL); } }