2044 lines
53 KiB
C++
2044 lines
53 KiB
C++
|
|
#include "gb_json.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "../../sdk/include/huagao/brand.h"
|
|
#include "../../sdk/include/sane/sane_ex.h"
|
|
|
|
#if defined(WIN32) || defined(_WIN64)
|
|
#define bzero(b, s) memset(b, 0, s)
|
|
#endif
|
|
|
|
|
|
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++;
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace gb
|
|
{
|
|
static std::vector<std::string> split_with(const char* str, const char* splitor = "/")
|
|
{
|
|
std::vector<std::string> 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;
|
|
}
|
|
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)
|
|
{}
|
|
refer::~refer()
|
|
{}
|
|
|
|
long refer::add_ref(void)
|
|
{
|
|
#if defined(WIN32) || defined(_WIN64)
|
|
return InterlockedIncrement(&ref_);
|
|
#else
|
|
return ++ref_;
|
|
#endif
|
|
}
|
|
long refer::release(void)
|
|
{
|
|
#if defined(WIN32) || defined(_WIN64)
|
|
long ref = InterlockedDecrement(&ref_);
|
|
#else
|
|
long ref = --ref_;
|
|
#endif
|
|
|
|
if (ref == 0)
|
|
delete this;
|
|
|
|
return ref;
|
|
}
|
|
|
|
|
|
json::json(char* json_txt) : type_(VAL_TYPE_OBJECT), key_(""), strval_(""), cur_child_(-1)
|
|
{
|
|
simple_val_.dval = .0f;
|
|
if (json_txt)
|
|
attach_text(json_txt);
|
|
}
|
|
json::json(const char* key, bool val) : type_(VAL_TYPE_BOOL), key_(key ? key : ""), strval_(""), cur_child_(-1)
|
|
{
|
|
simple_val_.bval = val;
|
|
}
|
|
json::json(const char* key, int val) : type_(VAL_TYPE_INT), key_(key ? key : ""), strval_(""), cur_child_(-1)
|
|
{
|
|
simple_val_.nval = val;
|
|
}
|
|
json::json(const char* key, double val) : type_(VAL_TYPE_FLOAT), key_(key ? key : ""), strval_(""), cur_child_(-1)
|
|
{
|
|
simple_val_.dval = val;
|
|
}
|
|
json::json(const char* key, const char* val) : type_(VAL_TYPE_STRING), key_(key ? key : ""), strval_(val ? val : ""), cur_child_(-1)
|
|
{
|
|
}
|
|
json::json(json& r)
|
|
{
|
|
copy_from(r);
|
|
}
|
|
json::~json()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
std::string json::object_key(json* jsn)
|
|
{
|
|
return "\"" + jsn->key() + "\":";
|
|
}
|
|
std::string json::array_key(json* jsn)
|
|
{
|
|
return "";
|
|
}
|
|
|
|
void json::from_cjson(cJSON* cj)
|
|
{
|
|
key_ = cj && cj->string ? cj->string : "";
|
|
while (cj)
|
|
{
|
|
json* child = nullptr;
|
|
if (cj->type == cJSON_True)
|
|
{
|
|
child = new json(cj->string, true);
|
|
}
|
|
else if (cj->type == cJSON_False)
|
|
{
|
|
child = new json(cj->string, false);
|
|
}
|
|
else if (cj->type == cJSON_Number)
|
|
{
|
|
if (cj->valuedouble - (int)cj->valuedouble < .00001)
|
|
{
|
|
child = new json(cj->string, cj->valueint);
|
|
}
|
|
else
|
|
{
|
|
child = new json(cj->string, cj->valuedouble);
|
|
}
|
|
}
|
|
else if (cj->type == cJSON_String)
|
|
{
|
|
child = new json(cj->string, cj->valuestring);
|
|
}
|
|
else if (cj->type == cJSON_Object || cj->type == cJSON_Array)
|
|
{
|
|
child = new json();
|
|
child->from_cjson(cj->child);
|
|
child->key_ = cj->string ? cj->string : "";
|
|
}
|
|
arr_val_.push_back(child);
|
|
cj = cj->next;
|
|
}
|
|
|
|
//if (arr_val_.size() == 1 && arr_val_[0]->arr_val_.size() == 0)
|
|
//{
|
|
// 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;
|
|
}
|
|
}
|
|
json* json::find_child(const char* key, bool remove)
|
|
{
|
|
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;
|
|
}
|
|
|
|
void json::copy_from(json& r)
|
|
{
|
|
clear();
|
|
|
|
type_ = r.type_;
|
|
key_ = r.key_;
|
|
simple_val_.dval = r.simple_val_.dval;
|
|
strval_ = r.strval_;
|
|
for (auto& v : r.arr_val_)
|
|
arr_val_.push_back(new json(*v));
|
|
}
|
|
bool json::attach_text(char* json_txt)
|
|
{
|
|
clear();
|
|
|
|
cJSON* jsn = cJSON_Parse(json_txt);
|
|
if (jsn)
|
|
{
|
|
char* text = cJSON_Print(jsn);
|
|
if (text)
|
|
free(text);
|
|
|
|
from_cjson(jsn->child);
|
|
cJSON_Delete(jsn);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
void 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 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)(json*) = type_ == VAL_TYPE_OBJECT ? json::object_key : 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& json::key(void)
|
|
{
|
|
return key_;
|
|
}
|
|
bool json::is_array(void)
|
|
{
|
|
return type_ == VAL_TYPE_ARRAY;
|
|
}
|
|
bool json::is_leaf_node(void)
|
|
{
|
|
return type_ == VAL_TYPE_BOOL ||
|
|
type_ == VAL_TYPE_INT ||
|
|
type_ == VAL_TYPE_FLOAT ||
|
|
type_ == VAL_TYPE_STRING;
|
|
}
|
|
|
|
bool json::get_value(const char* key, bool& val)
|
|
{
|
|
bool ret = false;
|
|
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 json::get_value(const char* key, int& val)
|
|
{
|
|
bool ret = false;
|
|
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 json::get_value(const char* key, double& val)
|
|
{
|
|
bool ret = false;
|
|
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;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
bool json::get_value(const char* key, std::string& val)
|
|
{
|
|
bool ret = false;
|
|
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 json::get_value(const char* key, json*& val)
|
|
{
|
|
bool ret = false;
|
|
json* child = find_child(key);
|
|
|
|
if (child)
|
|
{
|
|
if (child->type_ == VAL_TYPE_OBJECT)
|
|
{
|
|
val = child;
|
|
ret = true;
|
|
}
|
|
else
|
|
{
|
|
child->release();
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
size_t json::children(void)
|
|
{
|
|
if (type_ == VAL_TYPE_ARRAY || type_ == VAL_TYPE_OBJECT)
|
|
return arr_val_.size();
|
|
else
|
|
return -1;
|
|
}
|
|
json* 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;
|
|
}
|
|
json* json::first_child(void)
|
|
{
|
|
cur_child_ = 0;
|
|
if (type_ == VAL_TYPE_OBJECT || type_ == VAL_TYPE_ARRAY)
|
|
{
|
|
if (arr_val_.size())
|
|
{
|
|
arr_val_[0]->add_ref();
|
|
|
|
return arr_val_[0];
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
// leaf node, return self
|
|
//add_ref();
|
|
|
|
//return this;
|
|
}
|
|
json* 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 json::set_value(const char* key, bool val)
|
|
{
|
|
if (type_ != VAL_TYPE_OBJECT)
|
|
return false;
|
|
|
|
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 json(key, val);
|
|
arr_val_.push_back(child);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
bool json::set_value(const char* key, int val)
|
|
{
|
|
if (type_ != VAL_TYPE_OBJECT)
|
|
return false;
|
|
|
|
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 json(key, val);
|
|
arr_val_.push_back(child);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
bool json::set_value(const char* key, double val)
|
|
{
|
|
if (type_ != VAL_TYPE_OBJECT)
|
|
return false;
|
|
|
|
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 json(key, val);
|
|
arr_val_.push_back(child);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
bool json::set_value(const char* key, const char* val)
|
|
{
|
|
if (type_ != VAL_TYPE_OBJECT)
|
|
return false;
|
|
|
|
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 json(key, val);
|
|
arr_val_.push_back(child);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
bool json::set_value(const char* key, 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;
|
|
}
|
|
|
|
json& json::operator+=(bool val)
|
|
{
|
|
if (type_ == VAL_TYPE_ARRAY)
|
|
{
|
|
json* child = new json(nullptr, val);
|
|
arr_val_.push_back(child);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
json& json::operator+=(int val)
|
|
{
|
|
if (type_ == VAL_TYPE_ARRAY)
|
|
{
|
|
json* child = new json(nullptr, val);
|
|
arr_val_.push_back(child);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
json& json::operator+=(double val)
|
|
{
|
|
if (type_ == VAL_TYPE_ARRAY)
|
|
{
|
|
json* child = new json(nullptr, val);
|
|
arr_val_.push_back(child);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
json& json::operator+=(const char* val)
|
|
{
|
|
if (type_ == VAL_TYPE_ARRAY)
|
|
{
|
|
json* child = new json(nullptr, val);
|
|
arr_val_.push_back(child);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
json& json::operator+=(json* val)
|
|
{
|
|
if (type_ == VAL_TYPE_ARRAY)
|
|
{
|
|
val->add_ref();
|
|
arr_val_.push_back(val);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
json& json::operator-=(int ind)
|
|
{
|
|
remove(ind);
|
|
|
|
return *this;
|
|
}
|
|
bool json::remove(const char* key)
|
|
{
|
|
json* child = find_child(key, true);
|
|
|
|
if (child)
|
|
{
|
|
child->release();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
bool json::remove(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 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 json::index(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 json::index_move_to(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;
|
|
}
|
|
|
|
bool json::value(bool& val)
|
|
{
|
|
bool ret = false;
|
|
|
|
if (is_leaf_node() && type_ == VAL_TYPE_BOOL)
|
|
{
|
|
val = simple_val_.bval;
|
|
ret = true;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
bool json::value(int& val)
|
|
{
|
|
bool ret = false;
|
|
|
|
if (is_leaf_node() && type_ == VAL_TYPE_INT)
|
|
{
|
|
val = simple_val_.nval;
|
|
ret = true;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
bool json::value(double& val)
|
|
{
|
|
bool ret = false;
|
|
|
|
if (is_leaf_node() && type_ == VAL_TYPE_FLOAT)
|
|
{
|
|
val = simple_val_.dval;
|
|
ret = true;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
bool json::value(std::string& val)
|
|
{
|
|
bool ret = false;
|
|
|
|
if (is_leaf_node() && type_ == VAL_TYPE_STRING)
|
|
{
|
|
val = strval_;
|
|
ret = true;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
json& json::operator=(bool val)
|
|
{
|
|
if (is_leaf_node())
|
|
{
|
|
simple_val_.bval = val;
|
|
type_ = VAL_TYPE_BOOL;
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
json& json::operator=(int val)
|
|
{
|
|
if (is_leaf_node())
|
|
{
|
|
simple_val_.nval = val;
|
|
type_ = VAL_TYPE_INT;
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
json& json::operator=(double val)
|
|
{
|
|
if (is_leaf_node())
|
|
{
|
|
simple_val_.dval = val;
|
|
type_ = VAL_TYPE_FLOAT;
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
json& json::operator=(const char* val)
|
|
{
|
|
if (is_leaf_node())
|
|
{
|
|
strval_ = val ? val : "";
|
|
type_ = VAL_TYPE_STRING;
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
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 std::move(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 std::move(ret);
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
namespace gb
|
|
{
|
|
std::string sane_config_schm::opt_data_appendix_("_data");
|
|
std::string sane_config_schm::opt_def_id_appendix_("_id");
|
|
std::string sane_config_schm::opt_def_val_appendix_("_val");
|
|
std::string sane_config_schm::opt_def_type_appendix_("_type");
|
|
std::string sane_config_schm::opt_def_title_appendix_("_title");
|
|
|
|
sane_config_schm::sane_config_schm(scanner_cfg* scanner) : jsn_(nullptr), bkp_(nullptr), in_setting_(false), scheme_name_("")
|
|
, scanner_(scanner)
|
|
{
|
|
char empty[8] = { "{}" };
|
|
jsn_ = new gb::json();
|
|
jsn_->attach_text(empty);
|
|
if (scanner_)
|
|
{
|
|
scanner_->add_ref();
|
|
def_val_ = scanner_->get_option_default_value();
|
|
}
|
|
else
|
|
def_val_ = new gb::json();
|
|
}
|
|
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)
|
|
{
|
|
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_schm::hex_char(const char* data, unsigned char* val)
|
|
{
|
|
unsigned char v = 0;
|
|
bool ret = false;
|
|
|
|
if (sane_config_schm::hex(*data++, &v))
|
|
{
|
|
v <<= 4;
|
|
if (sane_config_schm::hex(*data++, &v))
|
|
{
|
|
if (val)
|
|
*val = v;
|
|
|
|
ret = true;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
std::string sane_config_schm::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(buf, "%02X", ptr[i]);
|
|
hex += buf;
|
|
}
|
|
|
|
return hex;
|
|
}
|
|
std::string sane_config_schm::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_schm::hex_char(data, &ch))
|
|
break;
|
|
stream.append(1, ch);
|
|
data += 2;
|
|
}
|
|
|
|
return stream;
|
|
}
|
|
bool sane_config_schm::is_option_data(std::string& name)
|
|
{
|
|
size_t pos = name.find(sane_config_schm::opt_data_appendix_);
|
|
|
|
if (pos != std::string::npos)
|
|
{
|
|
if (pos + sane_config_schm::opt_data_appendix_.length() == name.length())
|
|
{
|
|
name.erase(pos);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
void sane_config_schm::set_default_value(json* jsn, int id, const char* name, const char* title, void* data, size_t bytes, int type)
|
|
{
|
|
std::string key(name);
|
|
|
|
jsn->set_value(std::to_string(id).c_str(), name);
|
|
jsn->set_value((name + sane_config_schm::opt_def_id_appendix_).c_str(), id);
|
|
jsn->set_value((name + sane_config_schm::opt_def_val_appendix_).c_str(), sane_config_schm::to_hex_letter((const char*)data, bytes).c_str());
|
|
jsn->set_value((name + sane_config_schm::opt_def_type_appendix_).c_str(), type);
|
|
jsn->set_value((name + sane_config_schm::opt_def_title_appendix_).c_str(), title);
|
|
}
|
|
int sane_config_schm::option_name_2_id(json* jsn, const char* name)
|
|
{
|
|
int id = -1;
|
|
|
|
return jsn->get_value((std::string(name) + sane_config_schm::opt_def_id_appendix_).c_str(), id) ? id : -1;
|
|
}
|
|
int sane_config_schm::option_type(json* jsn, const char* name)
|
|
{
|
|
int id = -1;
|
|
|
|
return jsn->get_value((std::string(name) + sane_config_schm::opt_def_type_appendix_).c_str(), id) ? id : -1;
|
|
}
|
|
std::string sane_config_schm::option_title(json* jsn, const char* name)
|
|
{
|
|
std::string title("");
|
|
|
|
jsn->get_value((std::string(name) + sane_config_schm::opt_def_title_appendix_).c_str(), title);
|
|
|
|
return std::move(title);
|
|
}
|
|
std::string sane_config_schm::option_default_value(json* jsn, const char* name)
|
|
{
|
|
std::string val("");
|
|
|
|
return jsn->get_value((std::string(name) + sane_config_schm::opt_def_val_appendix_).c_str(), val) ? std::move(val) : "";
|
|
}
|
|
std::string sane_config_schm::sane_option_value_2_string(void* val, size_t bytes, int type)
|
|
{
|
|
char buf[80] = { 0 };
|
|
|
|
switch (type)
|
|
{
|
|
case SANE_TYPE_BOOL:
|
|
return *((SANE_Bool*)val) == SANE_TRUE ? "true" : "false";
|
|
break;
|
|
case SANE_TYPE_INT:
|
|
return std::to_string(*(SANE_Int*)val);
|
|
break;
|
|
case SANE_TYPE_FIXED:
|
|
sprintf_s(buf, _countof(buf) - 1, "%f", SANE_UNFIX(*(SANE_Fixed*)val));
|
|
break;
|
|
case SANE_TYPE_STRING:
|
|
return std::string((char*)val, bytes);
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
void sane_config_schm::clear()
|
|
{
|
|
if (jsn_)
|
|
jsn_->release();
|
|
jsn_ = nullptr;
|
|
if (bkp_)
|
|
bkp_->release();
|
|
bkp_ = nullptr;
|
|
}
|
|
std::string sane_config_schm::default_value(const char* hex_title)
|
|
{
|
|
return sane_config_schm::option_default_value(def_val_, hex_title);
|
|
}
|
|
|
|
sane_config_schm* sane_config_schm::copy(void)
|
|
{
|
|
sane_config_schm *cp = new sane_config_schm(scanner_);
|
|
|
|
cp->scheme_name_ = scheme_name_;
|
|
cp->file_ = file_;
|
|
if(jsn_)
|
|
cp->jsn_->copy_from(*jsn_);
|
|
if (bkp_)
|
|
{
|
|
cp->bkp_ = new gb::json();
|
|
cp->bkp_->copy_from(*bkp_);
|
|
}
|
|
cp->def_val_->release();
|
|
cp->def_val_ = def_val_;
|
|
def_val_->add_ref();
|
|
|
|
return cp;
|
|
}
|
|
bool sane_config_schm::load_from_file(const char* file)
|
|
{
|
|
clear();
|
|
|
|
std::string cont("");
|
|
|
|
file_ = file;
|
|
if (gb::load_mini_file(file, cont))
|
|
return false;
|
|
|
|
return load_from_mem(cont.c_str());
|
|
}
|
|
bool sane_config_schm::load_from_mem(const char* mem, bool in_b64)
|
|
{
|
|
gb::base64 b64;
|
|
std::string stream(in_b64 ? b64.decode(mem, strlen(mem)) : mem);
|
|
|
|
clear();
|
|
jsn_ = new gb::json();
|
|
if (!jsn_->attach_text(&stream[0]))
|
|
{
|
|
jsn_->release();
|
|
jsn_ = NULL;
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
bool sane_config_schm::save_to(const char* file)
|
|
{
|
|
bool ret = false;
|
|
std::string encode(to_text_stream());
|
|
|
|
if (!file || *file == 0)
|
|
file = file_.c_str();
|
|
|
|
if (encode.length())
|
|
{
|
|
FILE* dst = fopen(file, "wb");
|
|
|
|
if (dst)
|
|
{
|
|
fwrite(encode.c_str(), 1, encode.length(), dst);
|
|
fclose(dst);
|
|
ret = true;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
void sane_config_schm::set_default_value(int sn, const char* name, const char* title, const char* val, size_t bytes, int type)
|
|
{
|
|
sane_config_schm::set_default_value(def_val_, sn, name, title, (void*)val, bytes, type);
|
|
}
|
|
bool sane_config_schm::first_config(std::string& name, std::string& val)
|
|
{
|
|
bool ret = false;
|
|
json* child = nullptr;
|
|
std::string raw_v("");
|
|
|
|
if (jsn_ && (child = jsn_->first_child()))
|
|
{
|
|
name = child->key();
|
|
child->value(raw_v);
|
|
val = sane_config_schm::from_hex_letter(raw_v.c_str(), raw_v.length());
|
|
child->release();
|
|
|
|
ret = true;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
bool sane_config_schm::next_config(std::string& name, std::string& val)
|
|
{
|
|
bool ret = false;
|
|
json* child = nullptr;
|
|
std::string raw_v("");
|
|
|
|
if (jsn_ && (child = jsn_->next_child()))
|
|
{
|
|
name = child->key();
|
|
child->value(raw_v);
|
|
val = sane_config_schm::from_hex_letter(raw_v.c_str(), raw_v.length());
|
|
child->release();
|
|
|
|
ret = true;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
bool sane_config_schm::get_config(const char* name, std::string& val)
|
|
{
|
|
bool ret = jsn_ ? jsn_->get_value(name, val) : false;
|
|
|
|
if(!ret && def_val_)
|
|
ret = def_val_->get_value(name, val);
|
|
|
|
if(ret)
|
|
val = sane_config_schm::from_hex_letter(val.c_str(), val.length());
|
|
|
|
return ret;
|
|
}
|
|
void sane_config_schm::begin_setting(bool restore)
|
|
{
|
|
if (bkp_)
|
|
bkp_->release();
|
|
bkp_ = jsn_;
|
|
in_setting_ = true;
|
|
jsn_ = new gb::json();
|
|
if (!restore && bkp_)
|
|
{
|
|
jsn_->copy_from(*bkp_);
|
|
}
|
|
}
|
|
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));
|
|
|
|
if (extra)
|
|
{
|
|
jsn_->set_value((name + sane_config_schm::opt_data_appendix_).c_str(), hex_v.c_str());
|
|
}
|
|
else
|
|
{
|
|
std::string def = default_value(name);
|
|
if (hex_v == def)
|
|
{
|
|
jsn_->remove(name);
|
|
jsn_->remove((name + sane_config_schm::opt_data_appendix_).c_str());
|
|
}
|
|
else
|
|
jsn_->set_value(name, hex_v.c_str());
|
|
}
|
|
}
|
|
void sane_config_schm::config_changed(int sn, const char* val, size_t bytes, bool extra)
|
|
{
|
|
std::string name("");
|
|
|
|
def_val_->get_value(std::to_string(sn).c_str(), name);
|
|
if (!name.empty())
|
|
{
|
|
config_changed(name.c_str(), val, bytes, extra);
|
|
}
|
|
}
|
|
void sane_config_schm::remove_config(const char* name)
|
|
{
|
|
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());
|
|
}
|
|
bool sane_config_schm::has_changed(int* items)
|
|
{
|
|
if(items)
|
|
*items = jsn_ ? jsn_->children() : 0;
|
|
|
|
if(!bkp_)
|
|
return false;
|
|
|
|
std::map<std::string, std::string> old;
|
|
std::string n(""), v("");
|
|
json* child = nullptr;
|
|
if((child = bkp_->first_child()))
|
|
{
|
|
do
|
|
{
|
|
child->value(v);
|
|
old[child->key()] = std::move(v);
|
|
child->release();
|
|
}while((child = bkp_->next_child()));
|
|
}
|
|
|
|
if((child = jsn_->first_child()))
|
|
{
|
|
do
|
|
{
|
|
n = child->key();
|
|
if(old.count(n) == 0)
|
|
{
|
|
child->release();
|
|
return true;
|
|
}
|
|
child->value(v);
|
|
if(old[n]!=v)
|
|
{
|
|
child->release();
|
|
return true;
|
|
}
|
|
|
|
child->release();
|
|
old.erase(n);
|
|
} while ((child = jsn_->next_child()));
|
|
}
|
|
|
|
return old.size() > 0;
|
|
}
|
|
void sane_config_schm::end_setting(bool cancel)
|
|
{
|
|
if (in_setting_)
|
|
{
|
|
if (cancel)
|
|
{
|
|
jsn_->release();
|
|
jsn_ = bkp_;
|
|
bkp_ = nullptr;
|
|
}
|
|
else if (bkp_)
|
|
{
|
|
bkp_->release();
|
|
bkp_ = nullptr;
|
|
}
|
|
}
|
|
in_setting_ = false;
|
|
}
|
|
int sane_config_schm::id_from_name(const char* name)
|
|
{
|
|
return sane_config_schm::option_name_2_id(def_val_, name);
|
|
}
|
|
std::string sane_config_schm::to_text_stream(bool b64, bool with_ver)
|
|
{
|
|
if (jsn_)
|
|
{
|
|
if(with_ver)
|
|
{
|
|
char ver[40] = { 0 };
|
|
sprintf(ver, "%u.%u", VERSION_MAIN, VERSION_SUB);
|
|
jsn_->set_value("ver", ver);
|
|
}
|
|
|
|
std::string cont("");
|
|
//if (jsn_->is_leaf_node())
|
|
//{
|
|
// jsn_->value(cont);
|
|
// cont.insert(0, "{\"" + jsn_->key() + "\":\"");
|
|
// cont += "\"}";
|
|
//}
|
|
//else
|
|
cont = jsn_->to_string();
|
|
if (b64)
|
|
{
|
|
gb::base64 b64;
|
|
|
|
cont = b64.encode(cont.c_str(), cont.length());
|
|
}
|
|
|
|
return cont;
|
|
}
|
|
else
|
|
return "";
|
|
}
|
|
std::string sane_config_schm::get_version(void)
|
|
{
|
|
std::string ver("");
|
|
|
|
if (jsn_)
|
|
jsn_->get_value("ver", ver);
|
|
|
|
return ver;
|
|
}
|
|
std::string sane_config_schm::get_scheme_name(void)
|
|
{
|
|
return scheme_name_;
|
|
}
|
|
void sane_config_schm::set_scheme_name(const char* name)
|
|
{
|
|
scheme_name_ = name ? name : "";
|
|
}
|
|
|
|
std::string sane_config_schm::auto_gen_scheme_name(const char* (__stdcall* lang_trans)(const char*, bool/*true - default language to cur language*/, void*), void* param, bool replace_name)
|
|
{
|
|
std::string name(""), key(""), val(""), add("");
|
|
int cnt = 0;
|
|
|
|
if (first_config(key, val))
|
|
{
|
|
do
|
|
{
|
|
if (cnt++ > 3)
|
|
break;
|
|
|
|
SANE_Value_Type type = (SANE_Value_Type)sane_config_schm::option_type(def_val_, key.c_str());
|
|
if (type == SANE_TYPE_STRING)
|
|
name += add + lang_trans(val.c_str(), true, param);
|
|
else
|
|
name += add + sane_config_schm::option_title(def_val_, key.c_str()) + "(" + sane_config_schm::sane_option_value_2_string(&val[0], val.length(), type) + ")";
|
|
add = "+";
|
|
} while (next_config(key, val));
|
|
}
|
|
|
|
if (name.empty())
|
|
name = lang_trans(scanner_cfg::default_setting_name_.c_str(), true, param);
|
|
|
|
cnt = 0;
|
|
if (scanner_)
|
|
{
|
|
sane_config_schm* cfg = scanner_->get_scheme(name.c_str());
|
|
while (cfg)
|
|
{
|
|
cfg->release();
|
|
cfg = scanner_->get_scheme((name + "(" + std::to_string(++cnt) + ")").c_str());
|
|
}
|
|
if (cnt)
|
|
name += "(" + std::to_string(++cnt) + ")";
|
|
}
|
|
if(replace_name)
|
|
scheme_name_ = name;
|
|
|
|
return std::move(name);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
// scanner_cfg
|
|
std::string scanner_cfg::global_name_ = "global";
|
|
std::string scanner_cfg::cur_sel_ = "cur";
|
|
std::string scanner_cfg::default_setting_name_ = "\xE9\xBB\x98\xE8\xAE\xA4\xE8\xAE\xBE\xE7\xBD\xAE"; // utf-8: 默认设置
|
|
|
|
scanner_cfg::scanner_cfg() : path_(""), scanner_name_(""), global_(new json()), lang_trans_(scanner_cfg::language_trans), lang_param_(nullptr)
|
|
{
|
|
opt_default_value_ = new json();
|
|
|
|
init_version();
|
|
init_select();
|
|
}
|
|
scanner_cfg::~scanner_cfg()
|
|
{
|
|
clear();
|
|
global_->release();
|
|
opt_default_value_->release();
|
|
}
|
|
|
|
bool scanner_cfg::update(const char* file, LPUDF func)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const char* __stdcall scanner_cfg::language_trans(const char* in, bool from_def, void* param)
|
|
{
|
|
return in;
|
|
}
|
|
|
|
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);
|
|
}
|
|
const char* scanner_cfg::trans_language(const char* in, bool from_default)
|
|
{
|
|
return lang_trans_(in, from_default, lang_param_);
|
|
}
|
|
|
|
int scanner_cfg::load_file(const char* file)
|
|
{
|
|
std::string cont("");
|
|
int ret = gb::load_mini_file(file, cont);
|
|
|
|
if (ret == 0)
|
|
{
|
|
const char* name = strrchr(file, PATH_SYMBOL[0]);
|
|
if (name++ == nullptr)
|
|
name = file;
|
|
else
|
|
path_ = std::string(file, name - file);
|
|
|
|
scanner_name_ = name;
|
|
ret = scanner_name_.rfind('.');
|
|
if (ret != std::string::npos)
|
|
scanner_name_.erase(ret);
|
|
ret = load_mem(cont.c_str());;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
int scanner_cfg::load_mem(const char* mem)
|
|
{
|
|
json* jsn = new json(*(char**)&mem), * glb = nullptr;
|
|
|
|
if (jsn->get_value("global", glb))
|
|
{
|
|
int sel = -1;
|
|
glb->get_value(scanner_cfg::cur_sel_.c_str(), sel);
|
|
glb->release();
|
|
glb = jsn->first_child();
|
|
glb->release();
|
|
while ((glb = jsn->next_child()))
|
|
{
|
|
std::string val("");
|
|
sane_config_schm * cfg = new sane_config_schm(this);
|
|
|
|
glb->value(val);
|
|
if (cfg->load_from_mem(val.c_str()))
|
|
{
|
|
val = glb->key();
|
|
val = sane_config_schm::from_hex_letter(val.c_str(), val.length());
|
|
add_scheme(cfg, val.c_str());
|
|
cfg->set_scheme_name(val.c_str());
|
|
}
|
|
cfg->release();
|
|
glb->release();
|
|
}
|
|
if (sel < 0 || sel >= schemes_.size())
|
|
sel = -1;
|
|
global_->set_value(scanner_cfg::cur_sel_.c_str(), sel);
|
|
}
|
|
else
|
|
{
|
|
sane_config_schm* cfg = new sane_config_schm(this);
|
|
if (cfg->load_from_mem(mem))
|
|
{
|
|
cfg->remove_config("ver");
|
|
|
|
std::string name(cfg->auto_gen_scheme_name(lang_trans_, lang_param_));
|
|
add_scheme(cfg);
|
|
select_scheme(name.c_str());
|
|
save();
|
|
}
|
|
cfg->release();
|
|
}
|
|
jsn->release();
|
|
|
|
return 0;
|
|
}
|
|
int scanner_cfg::save(const char* file)
|
|
{
|
|
if (!file && path_.empty() && scanner_name_.empty())
|
|
return EINVAL;
|
|
|
|
std::string cont(to_text_stream()),
|
|
f(file ? file : path_ + scanner_name_ + ".cfg");
|
|
FILE* dst = fopen(f.c_str(), "wb");
|
|
|
|
if (!dst)
|
|
return errno;
|
|
|
|
fwrite(cont.c_str(), 1, cont.length(), dst);
|
|
fclose(dst);
|
|
|
|
return 0;
|
|
}
|
|
void scanner_cfg::set_language_transform(const char* (__stdcall* lang_trans)(const char*, bool/*true - default language to cur language*/, void*), void* param)
|
|
{
|
|
lang_trans_ = lang_trans ? lang_trans : &scanner_cfg::language_trans;
|
|
lang_param_ = param;
|
|
}
|
|
|
|
json* scanner_cfg::get_option_default_value(void)
|
|
{
|
|
opt_default_value_->add_ref();
|
|
|
|
return opt_default_value_;
|
|
}
|
|
void scanner_cfg::get_all_schemes(std::vector<std::string>& schemes)
|
|
{
|
|
schemes.push_back(trans_language(scanner_cfg::default_setting_name_.c_str(), true));
|
|
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 && *scheme_name)
|
|
{
|
|
std::vector<CFGSCHM>::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;
|
|
}
|
|
sane_config_schm* scanner_cfg::create_empty_scheme(bool selected)
|
|
{
|
|
sane_config_schm* schm = new sane_config_schm(this);
|
|
int ind = 1;
|
|
std::string prev(trans_language(scanner_cfg::default_setting_name_.c_str(), true));
|
|
|
|
while (std::find(schemes_.begin(), schemes_.end(), (prev + "-" + std::to_string(ind)).c_str()) != schemes_.end())
|
|
ind++;
|
|
|
|
prev += "-" + std::to_string(ind);
|
|
add_scheme(schm, prev.c_str(), true);
|
|
if (selected)
|
|
select_scheme(prev.c_str());
|
|
|
|
return schm;
|
|
}
|
|
std::string scanner_cfg::get_current_scheme_name(void)
|
|
{
|
|
int ind = -1;
|
|
|
|
global_->get_value(scanner_cfg::cur_sel_.c_str(), ind);
|
|
if (ind >= 0 && ind < schemes_.size())
|
|
return schemes_[ind].name;
|
|
else
|
|
return trans_language(scanner_cfg::default_setting_name_.c_str(), true);
|
|
}
|
|
std::string scanner_cfg::to_text_stream(void)
|
|
{
|
|
std::string text(""), val("");
|
|
int sel = -1;
|
|
|
|
if (!global_->get_value("ver", val) || val.empty())
|
|
init_version();
|
|
if (!global_->get_value(scanner_cfg::cur_sel_.c_str(), sel) || sel >= schemes_.size())
|
|
init_select();
|
|
text = "{\"" + scanner_cfg::global_name_ + "\":" + global_->to_string();
|
|
|
|
for (auto& v : schemes_)
|
|
{
|
|
if (v.should_rename)
|
|
{
|
|
v.schm->auto_gen_scheme_name(lang_trans_, lang_param_);
|
|
v.name = v.schm->get_scheme_name();
|
|
v.should_rename = false;
|
|
}
|
|
text += ",\"" + sane_config_schm::to_hex_letter(v.name.c_str(), v.name.length()) + "\":\"" + v.schm->to_text_stream(true, false) + "\"";
|
|
}
|
|
text += "}";
|
|
|
|
return std::move(text);
|
|
}
|
|
void scanner_cfg::set_default_value(int id, const char* name, const char* title, void* data, size_t bytes, int type)
|
|
{
|
|
sane_config_schm::set_default_value(opt_default_value_, id, name, title, data, bytes, type);
|
|
}
|
|
int scanner_cfg::option_value_type(const char* name)
|
|
{
|
|
return sane_config_schm::option_type(opt_default_value_, name);
|
|
}
|
|
std::string scanner_cfg::option_title(const char* name)
|
|
{
|
|
return sane_config_schm::option_title(opt_default_value_, name);
|
|
}
|
|
bool scanner_cfg::remove_scheme(const char* scheme_name)
|
|
{
|
|
std::vector<CFGSCHM>::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;
|
|
}
|
|
void scanner_cfg::remove_all_schemes(void)
|
|
{
|
|
for(auto& v: schemes_)
|
|
v.schm->release();
|
|
|
|
schemes_.clear();
|
|
}
|
|
bool scanner_cfg::select_scheme(const char* scheme_name)
|
|
{
|
|
std::vector<CFGSCHM>::iterator it = scheme_name ? std::find(schemes_.begin(), schemes_.end(), scheme_name) : schemes_.end();
|
|
|
|
if (it == schemes_.end())
|
|
global_->set_value(scanner_cfg::cur_sel_.c_str(), -1);
|
|
else
|
|
global_->set_value(scanner_cfg::cur_sel_.c_str(), (int)(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 || *cp_from_name == 0 ||
|
|
std::string(trans_language(scanner_cfg::default_setting_name_.c_str(), true)) == cp_from_name)
|
|
{
|
|
sane_config_schm* schm = new sane_config_schm(this);
|
|
|
|
schm->set_scheme_name(trans_language(scanner_cfg::default_setting_name_.c_str(), true));
|
|
|
|
return schm;
|
|
}
|
|
else
|
|
{
|
|
std::vector<CFGSCHM>::iterator it = std::find(schemes_.begin(), schemes_.end(), cp_from_name);
|
|
if (it == schemes_.end())
|
|
return nullptr;
|
|
|
|
return it->schm->copy();
|
|
}
|
|
}
|
|
bool scanner_cfg::add_scheme(sane_config_schm* schm, const char* name, bool should_rename)
|
|
{
|
|
CFGSCHM cs;
|
|
|
|
cs.name = name ? name : schm->get_scheme_name();
|
|
if(cs.name.empty() || cs.name == scanner_cfg::global_name_)
|
|
return false;
|
|
|
|
if (std::find(schemes_.begin(), schemes_.end(), cs.name.c_str()) != schemes_.end())
|
|
return false;
|
|
|
|
cs.schm = schm;
|
|
cs.should_rename = should_rename;
|
|
|
|
schemes_.push_back(cs);
|
|
schm->set_scheme_name(cs.name.c_str());
|
|
schm->add_ref();
|
|
|
|
return true;
|
|
}
|
|
bool scanner_cfg::rename_scheme(const char* from, const char* to)
|
|
{
|
|
if (to && (strcmp(to, trans_language(scanner_cfg::default_setting_name_.c_str(), true)) == 0 || std::find(schemes_.begin(), schemes_.end(), to) != schemes_.end()))
|
|
return false;
|
|
|
|
for(auto& v: schemes_)
|
|
{
|
|
if(v.name == from)
|
|
{
|
|
v.name = to;
|
|
v.should_rename = false;
|
|
v.schm->set_scheme_name(to);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|