├── .gitignore ├── CheckDependency.h ├── Cifa.cpp ├── Cifa.h ├── ConsoleControl.h ├── DrawStringFT.h ├── DynamicLibrary.h ├── FakeJson.h ├── FunctionTrait.h ├── INIReader.h ├── INIReaderBin.h ├── LICENSE ├── PotConv.cpp ├── PotConv.h ├── Random.h ├── SimpleBuffer.h ├── Timer.h ├── cifa └── readme.md ├── cmdline.h ├── filefunc.cpp ├── filefunc.h ├── readme.md ├── runtime_format.h ├── strcvt.cpp ├── strcvt.h ├── strfunc.cpp ├── strfunc.h ├── targetlnk.h ├── versioninfo.h ├── vramusage.cpp └── vramusage.h /.gitignore: -------------------------------------------------------------------------------- 1 | test/ 2 | *.bat 3 | -------------------------------------------------------------------------------- /CheckDependency.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef _CHECKDEPENDENCY_H_ 4 | #define _CHECKDEPENDENCY_H_ 5 | #endif 6 | 7 | #include "windows.h" 8 | 9 | #include "ImageHlp.h" 10 | #include 11 | #include 12 | #include 13 | 14 | #pragma comment(lib, "imagehlp.lib") 15 | 16 | // auto dlls = CheckDependency(pe_name).DllsNotGood(); 17 | // Return value is a map. If all are OK, the map is empty. 18 | // Else, the keys are the names of dlls and the values are vectors of struct NotGoodInfo. 19 | // Empty full path means the file is not found. 20 | // If the path and machine of a file are both OK, but some functions are lost, usually that means the version of this file is not right. 21 | 22 | class CheckDependency 23 | { 24 | private: 25 | PLOADED_IMAGE image = nullptr; 26 | struct ImportInfo 27 | { 28 | std::string full_path; 29 | std::string machine; 30 | std::vector used_functions; // functions used by other modules 31 | }; 32 | struct NotGoodInfo 33 | { 34 | std::string full_path; 35 | std::string machine; 36 | std::vector lost_functions; // functions which should exist 37 | }; 38 | std::map import_table_; 39 | std::map dlls_not_good_; 40 | std::map> export_table_; 41 | 42 | PIMAGE_SECTION_HEADER GetEnclosingSectionHeader(DWORD rva) 43 | { 44 | auto section = IMAGE_FIRST_SECTION(image->FileHeader); 45 | unsigned i; 46 | 47 | for (i = 0; i < image->FileHeader->FileHeader.NumberOfSections; i++, section++) 48 | { 49 | // This 3 line idiocy is because Watcom's linker actually sets the 50 | // Misc.VirtualSize field to 0. (!!! - Retards....!!!) 51 | DWORD size = section->Misc.VirtualSize; 52 | if (0 == size) 53 | { 54 | size = section->SizeOfRawData; 55 | } 56 | 57 | // Is the RVA within this section? 58 | if ((rva >= section->VirtualAddress) && (rva < (section->VirtualAddress + size))) 59 | { 60 | return section; 61 | } 62 | } 63 | 64 | return 0; 65 | } 66 | 67 | LPVOID GetPtrFromRVA(DWORD rva) 68 | { 69 | INT delta; 70 | auto pSectionHdr = GetEnclosingSectionHeader(rva); 71 | if (!pSectionHdr) 72 | { 73 | return 0; 74 | } 75 | delta = (INT)(pSectionHdr->VirtualAddress - pSectionHdr->PointerToRawData); 76 | return (PVOID)(image->MappedAddress + rva - delta); 77 | } 78 | 79 | void DumpTable(const std::string& path, std::map& check_map) 80 | { 81 | check_map[path]++; 82 | image = ImageLoad(path.c_str(), 0); 83 | char full_path[MAX_PATH] = {0}; 84 | SearchPathA(NULL, path.c_str(), NULL, MAX_PATH, full_path, NULL); 85 | import_table_[path].full_path = full_path; 86 | if (!image) 87 | { 88 | return; 89 | } 90 | if (image->FileHeader->FileHeader.Machine == 0x014c) 91 | { 92 | // if the machine is not x64, the export/import table maybe not right 93 | import_table_[path].machine = "x86"; 94 | } 95 | else if (image->FileHeader->FileHeader.Machine == 0x8664) 96 | { 97 | import_table_[path].machine = "x64"; 98 | } 99 | else 100 | { 101 | import_table_[path].machine = "unknown"; 102 | } 103 | if (image->FileHeader->OptionalHeader.NumberOfRvaAndSizes >= 1) 104 | { 105 | auto export_dir = (PIMAGE_EXPORT_DIRECTORY)GetPtrFromRVA(image->FileHeader->OptionalHeader.DataDirectory[0].VirtualAddress); 106 | if (!export_dir) 107 | { 108 | return; 109 | } 110 | auto name = (PDWORD)GetPtrFromRVA(export_dir->AddressOfNames); 111 | for (size_t i = 0; i < export_dir->NumberOfNames; i++) 112 | { 113 | //printf("export %s::%s\n", path.c_str(), (const char*)GetPtrFromRVA(*name)); 114 | if (!name) 115 | { 116 | break; 117 | } 118 | export_table_[path][(const char*)GetPtrFromRVA(*name)]++; 119 | name++; 120 | } 121 | } 122 | 123 | if (image->FileHeader->OptionalHeader.NumberOfRvaAndSizes >= 2) 124 | { 125 | auto import_desc = (PIMAGE_IMPORT_DESCRIPTOR)GetPtrFromRVA(image->FileHeader->OptionalHeader.DataDirectory[1].VirtualAddress); 126 | 127 | if (!import_desc) 128 | { 129 | return; 130 | } 131 | while (1) 132 | { 133 | // See if we've reached an empty IMAGE_IMPORT_DESCRIPTOR 134 | if ((import_desc->TimeDateStamp == 0) && (import_desc->Name == 0)) 135 | { 136 | break; 137 | } 138 | auto dll_name = (const char*)GetPtrFromRVA(import_desc->Name); 139 | auto thunk = (PIMAGE_THUNK_DATA64)GetPtrFromRVA(import_desc->OriginalFirstThunk); 140 | while (1) 141 | { 142 | if (thunk->u1.AddressOfData == 0) 143 | { 144 | break; 145 | } 146 | auto name = (PIMAGE_IMPORT_BY_NAME)GetPtrFromRVA(thunk->u1.AddressOfData); 147 | if (name && name->Name) 148 | { 149 | //printf("import %s::%s\n", dll_name, name->Name); 150 | import_table_[dll_name].used_functions.push_back(name->Name); 151 | } 152 | thunk++; 153 | } 154 | if (check_map.count(dll_name) == 0) 155 | { 156 | check_map[dll_name] = 0; 157 | } 158 | import_desc++; 159 | } 160 | } 161 | ImageUnload(image); 162 | } 163 | 164 | public: 165 | CheckDependency() 166 | { 167 | } 168 | CheckDependency(const std::string& file) 169 | { 170 | dlls_not_good_ = Check(file); 171 | } 172 | std::map DllsNotGood() 173 | { 174 | return dlls_not_good_; 175 | } 176 | // used functions in all dlls 177 | std::map ImportTable() 178 | { 179 | return import_table_; 180 | } 181 | // all functions in all dlls 182 | std::map> ExportTable() 183 | { 184 | return export_table_; 185 | } 186 | std::map Check(const std::string& file) 187 | { 188 | std::map check_map; 189 | DumpTable(file.c_str(), check_map); 190 | while (1) 191 | { 192 | bool recheck = false; 193 | for (auto& pair : check_map) 194 | { 195 | if (pair.second == 0) 196 | { 197 | DumpTable(pair.first.c_str(), check_map); 198 | recheck = true; 199 | } 200 | } 201 | if (!recheck) 202 | { 203 | break; 204 | } 205 | } 206 | std::map problem_dlls; 207 | for (auto& import_pair : import_table_) 208 | { 209 | auto& dll_name = import_pair.first; 210 | if (dll_name.find("api-ms-win") == 0 || dll_name.find("KERNEL32") == 0) 211 | { 212 | continue; 213 | } 214 | if (import_pair.second.full_path.empty()) 215 | { 216 | problem_dlls[dll_name] = NotGoodInfo(); 217 | } 218 | for (auto& function_name : import_pair.second.used_functions) 219 | { 220 | if (export_table_[dll_name].count(function_name) == 0) 221 | { 222 | problem_dlls[dll_name].lost_functions.push_back(function_name); 223 | } 224 | } 225 | } 226 | for (auto& dll : problem_dlls) 227 | { 228 | dll.second.full_path = import_table_[dll.first].full_path; 229 | dll.second.machine = import_table_[dll.first].machine; 230 | } 231 | return problem_dlls; 232 | } 233 | }; -------------------------------------------------------------------------------- /Cifa.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace cifa 13 | { 14 | struct CalUnit; 15 | class Cifa; 16 | 17 | struct Object 18 | { 19 | friend CalUnit; 20 | friend Cifa; 21 | 22 | Object() {} 23 | 24 | Object(double v) 25 | { 26 | value = v; 27 | } 28 | 29 | Object(double v, const std::string& t) 30 | { 31 | value = v; 32 | type1 = t; 33 | } 34 | 35 | Object(const std::string& str) 36 | { 37 | value = str; 38 | } 39 | 40 | Object(const std::string& str, const std::string& t) 41 | { 42 | value = str; 43 | type1 = t; 44 | } 45 | 46 | template , Object>, int>::type = 0> 47 | Object(const T& v) 48 | { 49 | value = v; 50 | } 51 | 52 | Object(int v) 53 | { 54 | value = double(v); 55 | } 56 | 57 | Object(bool v) 58 | { 59 | value = double(v); 60 | } 61 | 62 | operator bool() const { return toDouble() != 0; } 63 | 64 | operator int() const { return int(toDouble()); } 65 | 66 | operator double() const { return toDouble(); } 67 | 68 | operator std::string() const { return toString(); } 69 | 70 | bool toBool() const { return toDouble() != 0; } 71 | 72 | int toInt() const { return int(toDouble()); } 73 | 74 | double toDouble() const 75 | { 76 | if (value.type() == typeid(double)) 77 | { 78 | return std::any_cast(value); 79 | } 80 | fprintf(stderr, "Error: Object %s to double failed.\n", value.type().name()); 81 | return NAN; 82 | } 83 | 84 | std::string toString() const 85 | { 86 | if (value.type() == typeid(std::string)) 87 | { 88 | return std::any_cast(value); 89 | } 90 | fprintf(stderr, "Error: Object %s to string failed.\n", value.type().name()); 91 | return ""; 92 | } 93 | 94 | //复制,不会改变原来的值 95 | template 96 | T to() const 97 | { 98 | if (value.type() == typeid(T)) 99 | { 100 | return std::any_cast(value); 101 | } 102 | fprintf(stderr, "Error: Object %s to %s failed.\n", value.type().name(), typeid(T).name()); 103 | return T(); 104 | } 105 | 106 | //const与非const版本,按需使用 107 | //如果转换失败,后续使用时也不会正常,因此应谨慎使用,或者在isType()判断后使用 108 | template 109 | const T& ref() const 110 | { 111 | if (value.type() == typeid(T)) 112 | { 113 | return std::any_cast(value); 114 | } 115 | fprintf(stderr, "Error: Object %s to %s failed.\n", value.type().name(), typeid(T).name()); 116 | } 117 | 118 | template 119 | T& ref() 120 | { 121 | if (value.type() == typeid(T)) 122 | { 123 | return std::any_cast(value); 124 | } 125 | fprintf(stderr, "Error: Object %s to %s failed.\n", value.type().name(), typeid(T).name()); 126 | } 127 | 128 | template 129 | bool isType() const { return value.type() == typeid(T); } 130 | 131 | bool isNumber() const { return value.type() == typeid(double); } 132 | 133 | bool isEffectNumber() const { return isNumber() && !std::isnan(toDouble()) && !std::isinf(toDouble()); } 134 | 135 | bool hasValue() const { return value.has_value(); } 136 | 137 | const std::vector& subV() const { return v; } 138 | 139 | const std::string& getSpecialType() const { return type1; } 140 | 141 | std::type_info const& getType() const { return value.type(); } 142 | 143 | private: 144 | std::any value; 145 | std::string type1; //特别的类型,用于Error、break、continue 146 | std::vector v; //仅用于处理逗号表达式 147 | }; 148 | 149 | using ObjectVector = std::vector; 150 | 151 | enum class CalUnitType 152 | { 153 | None = 0, 154 | Constant, 155 | String, 156 | Operator, 157 | Split, 158 | Parameter, 159 | Function, 160 | Key, 161 | Type, 162 | Union, 163 | //UnionRound, //()合并模式,仅for语句使用 164 | }; 165 | 166 | struct CalUnit 167 | { 168 | CalUnitType type = CalUnitType::None; 169 | std::vector v; //语法树的节点,v.size():[0,3] 170 | std::string str; 171 | size_t line = 0, col = 0; 172 | bool suffix = false; //有后缀,可视为一个语句 173 | bool with_type = false; //有前置的类型 174 | bool un_combine = false; //是否合并到语法树,目前仅case和default后面的冒号使用 175 | 176 | CalUnit(CalUnitType s, std::string s1) 177 | { 178 | type = s; 179 | str = s1; 180 | } 181 | 182 | CalUnit() {} 183 | 184 | bool can_cal() 185 | { 186 | return type == CalUnitType::Constant || type == CalUnitType::String || type == CalUnitType::Parameter || type == CalUnitType::Function || type == CalUnitType::Operator && v.size() > 0; 187 | } 188 | 189 | bool is_statement() 190 | { 191 | return suffix || !can_cal(); 192 | } 193 | }; 194 | 195 | struct Function2 196 | { 197 | std::vector arguments; 198 | CalUnit body; 199 | }; 200 | 201 | template 202 | bool vector_have(const std::vector& ops, const T& op) 203 | { 204 | for (auto& o : ops) 205 | { 206 | if (op == o) 207 | { 208 | return true; 209 | } 210 | } 211 | return false; 212 | } 213 | 214 | template 215 | bool vector_have(const std::vector>& ops, const T& op) 216 | { 217 | for (auto& ops1 : ops) 218 | { 219 | for (auto& o : ops1) 220 | { 221 | if (op == o) 222 | { 223 | return true; 224 | } 225 | } 226 | } 227 | return false; 228 | } 229 | 230 | class Cifa 231 | { 232 | private: 233 | //运算符,此处的顺序即优先级,单目和右结合由下面的列表判断 234 | std::vector> ops = { { "::", ".", "++", "--" }, { "!" }, { "*", "/", "%" }, { "+", "-" }, { ">", "<", ">=", "<=" }, { "==", "!=" }, { "&" }, { "|" }, { "&&" }, { ":", "?" }, { "||" }, { "=", "*=", "/=", "+=", "-=" }, { "," } }; 235 | std::vector ops_single = { "++", "--", "!", "()++", "()--" }; //单目全部是右结合 236 | std::vector ops_right = { "=", "*=", "/=", "+=", "-=" }; //右结合 237 | //关键字,在表中的位置为其所需参数个数 238 | std::vector> keys = { { "true", "false" }, { "break", "continue", "else", "return", "default" }, { "if", "for", "while", "do", "switch", "case" } }; 239 | std::vector types = { "auto", "int", "float", "double" }; 240 | std::map op_representations = { { "and", "&&" }, { "and_eq", "&=" }, { "bitand", "&" }, { "bitor", "|" }, { "compl", "~" }, { "not", "!" }, { "not_eq", "!=" }, { "or", "||" }, { "or_eq", "|=" }, { "xor", "^" }, { "xor_eq", "^=" }, { "<%", "{" }, { "%>", "}" }, { "<:", "[" }, { ":>", "]" }, { "%:", "#" }, { "%:%:", "##" } }; 241 | //两个函数表都是全局的 242 | using func_type = std::function; 243 | std::unordered_map functions; //在宿主程序中注册的函数 244 | std::unordered_map functions2; //在cifa程序中定义的函数 245 | 246 | std::unordered_map user_data; 247 | std::unordered_map parameters; //变量表,注意每次定义的函数调用都是独立的 248 | 249 | struct ErrorMessage 250 | { 251 | size_t line = 0, col = 0; 252 | std::string message; 253 | }; 254 | 255 | struct ErrorMessageComp 256 | { 257 | bool operator()(const ErrorMessage& l, const ErrorMessage& r) const 258 | { 259 | if (l.line == r.line) 260 | { 261 | return l.col < r.col; 262 | } 263 | return l.line < r.line; 264 | } 265 | }; 266 | 267 | std::set errors; 268 | 269 | bool output_error = true; 270 | 271 | public: 272 | Cifa(); 273 | Object eval(CalUnit& c, std::unordered_map& p); 274 | void expand_comma(CalUnit& c1, std::vector& v); 275 | CalUnit& find_right_side(CalUnit& c1); 276 | //bool need_suffix(CalUnit& c) { return c.can_cal() || vector_have(keys[0], c.str); } 277 | 278 | CalUnitType guess_char(char c); 279 | std::list split(std::string& str); 280 | 281 | CalUnit combine_all_cal(std::list& ppp, bool curly = true, bool square = true, bool round = true); 282 | std::list::iterator inside_bracket(std::list& ppp, std::list& ppp2, const std::string& bl, const std::string& br); 283 | void combine_curly_bracket(std::list& ppp); 284 | void combine_square_bracket(std::list& ppp); 285 | void combine_round_bracket(std::list& ppp); 286 | void combine_ops(std::list& ppp); 287 | void combine_semi(std::list& ppp); 288 | void deal_special_keys(std::list& ppp); 289 | void combine_keys(std::list& ppp); 290 | void combine_types(std::list& ppp); 291 | void combine_functions2(std::list& ppp); 292 | 293 | void register_function(const std::string& name, func_type func); 294 | void register_user_data(const std::string& name, void* p); 295 | void register_parameter(const std::string& name, Object o); 296 | 297 | template 298 | void register_parameter(const std::string& name, std::map m) 299 | { 300 | //两重的暂时如此处理 301 | parameters[name] = ""; 302 | for (auto& o : m) 303 | { 304 | parameters[name + "::" + o.first] = Object(o.second); 305 | } 306 | } 307 | 308 | template 309 | void register_vector(const std::string& name, const std::vector& v) 310 | { 311 | int i = 0; 312 | for (auto& o : v) 313 | { 314 | parameters[name + "[" + std::to_string(i++) + "]"] = Object(o); 315 | } 316 | } 317 | 318 | void* get_user_data(const std::string& name); 319 | Object run_function(const std::string& name, std::vector& vc, std::unordered_map& p); 320 | Object& get_parameter(CalUnit& c, std::unordered_map& p); 321 | std::string convert_parameter_name(CalUnit& c, std::unordered_map& p); 322 | bool check_parameter(CalUnit& c, std::unordered_map& p); 323 | Object& get_parameter(const std::string& name, std::unordered_map& p); 324 | bool check_parameter(const std::string& name, std::unordered_map& p); 325 | 326 | void check_cal_unit(CalUnit& c, CalUnit* father, std::unordered_map& p); 327 | 328 | Object run_script(std::string str); //运行脚本,注意实际上使用独立的变量表 329 | 330 | bool has_error() const { return !errors.empty(); } 331 | 332 | std::vector get_errors() const; 333 | 334 | template 335 | void add_error(CalUnit& c, Args... args) 336 | { 337 | ErrorMessage e; 338 | e.line = c.line; 339 | e.col = c.col; 340 | char buffer[1024] = { '\0' }; 341 | if (sizeof...(args) == 1) 342 | { 343 | snprintf(buffer, 1024, "%s", args...); 344 | } 345 | else if (sizeof...(args) >= 2) 346 | { 347 | snprintf(buffer, 1024, args...); 348 | } 349 | e.message = buffer; 350 | errors.emplace(std::move(e)); 351 | } 352 | 353 | void set_output_error(bool oe) { output_error = oe; } 354 | 355 | //四则运算准许用户增加自定义功能 356 | 357 | #define OPERATOR(o1, o2, op, userop_v, trans_type) \ 358 | if (o1.isNumber() && o2.isNumber()) \ 359 | { \ 360 | return double(trans_type(o1) op trans_type(o2)); \ 361 | } \ 362 | for (auto& f : userop_v) \ 363 | { \ 364 | auto o = f(o1, o2); \ 365 | if (!o.isNumber()) \ 366 | { \ 367 | return o; \ 368 | } \ 369 | } \ 370 | return Object(); 371 | 372 | #define OPERATOR_DEF(opname, op, trans_type) \ 373 | Object opname(const Object& o1, const Object& o2) { OPERATOR(o1, o2, op, user_##opname, trans_type); } 374 | 375 | #define OPERATOR_DEF_CONTENT(opname, op, trans_type) \ 376 | Object opname(const Object& o1, const Object& o2) \ 377 | { \ 378 | if (o1.isType() && o2.isType()) \ 379 | { \ 380 | return Object(std::any_cast(o1.value) + std::any_cast(o2.value)); \ 381 | } \ 382 | OPERATOR(o1, o2, op, user_##opname, trans_type); \ 383 | } 384 | 385 | std::vector> user_add, user_sub, user_mul, user_div, 386 | user_less, user_more, user_less_equal, user_more_equal, 387 | user_equal, user_not_equal, user_bit_and, user_bit_or; 388 | 389 | OPERATOR_DEF_CONTENT(add, +, double) 390 | OPERATOR_DEF(sub, -, double) 391 | OPERATOR_DEF(mul, *, double) 392 | OPERATOR_DEF(div, /, double) 393 | OPERATOR_DEF(less, <, double) 394 | OPERATOR_DEF(more, >, double) 395 | OPERATOR_DEF_CONTENT(less_equal, <=, double) 396 | OPERATOR_DEF_CONTENT(more_equal, >=, double) 397 | OPERATOR_DEF_CONTENT(equal, ==, double) 398 | OPERATOR_DEF_CONTENT(not_equal, !=, double) 399 | OPERATOR_DEF(bit_and, &, int) 400 | OPERATOR_DEF(bit_or, |, int) 401 | }; 402 | 403 | //#define OPERATOR_DEF_DOUBLE(op) \ 404 | // Object op(const Object& o1, const Object& o2) { return Object(double(o1.value) op double(o2.value)); } 405 | 406 | } // namespace cifa -------------------------------------------------------------------------------- /ConsoleControl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #ifdef _WIN32 7 | #define NOMINMAX 8 | #include 9 | #undef NOMINMAX 10 | #else 11 | #endif 12 | 13 | enum ConsoleColor 14 | { 15 | CONSOLE_COLOR_NONE = -1, 16 | CONSOLE_COLOR_RED = 4, 17 | CONSOLE_COLOR_LIGHT_RED = 12, 18 | CONSOLE_COLOR_GREEN = 2, 19 | CONSOLE_COLOR_LIGHT_GREEN = 10, 20 | CONSOLE_COLOR_BLUE = 1, 21 | CONSOLE_COLOR_LIGHT_BLUE = 9, 22 | CONSOLE_COLOR_WHITE = 7, 23 | CONSOLE_COLOR_BLACK = 0, 24 | }; 25 | 26 | class ConsoleControl 27 | { 28 | private: 29 | ConsoleControl() 30 | { 31 | if (color_map_.empty()) 32 | { 33 | color_map_ = { 34 | { CONSOLE_COLOR_NONE, "\e[0m" }, 35 | { CONSOLE_COLOR_RED, "\e[0;31m" }, 36 | { CONSOLE_COLOR_LIGHT_RED, "\e[1;31m" }, 37 | { CONSOLE_COLOR_GREEN, "\e[0;32m" }, 38 | { CONSOLE_COLOR_LIGHT_GREEN, "\e[1;32m" }, 39 | { CONSOLE_COLOR_BLUE, "\e[0;34m" }, 40 | { CONSOLE_COLOR_LIGHT_BLUE, "\e[1;34m" }, 41 | { CONSOLE_COLOR_WHITE, "\e[1;37m" }, 42 | { CONSOLE_COLOR_BLACK, "\e[0;30m" }, 43 | }; 44 | #ifdef _WIN32 45 | CONSOLE_SCREEN_BUFFER_INFO csbiInfo; 46 | GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbiInfo); 47 | old_color_ = csbiInfo.wAttributes; 48 | #endif 49 | } 50 | } 51 | 52 | unsigned short old_color_; 53 | std::map color_map_; 54 | 55 | static ConsoleControl* getInstance() 56 | { 57 | static ConsoleControl console_control; 58 | return &console_control; 59 | } 60 | 61 | private: 62 | ConsoleControl(ConsoleControl&) = delete; 63 | ConsoleControl& operator=(ConsoleControl&) = delete; 64 | 65 | public: 66 | static void setColor(ConsoleColor c) 67 | { 68 | setColor(static_cast(c)); 69 | } 70 | static void setColor(int c) 71 | { 72 | auto cc = getInstance(); 73 | #ifdef _MSC_VER 74 | if (c != CONSOLE_COLOR_NONE) 75 | { 76 | SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); 77 | } 78 | else 79 | { 80 | SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), cc->old_color_); 81 | } 82 | #else 83 | fprintf(stderr, "%s", cc->color_map_[c].c_str()); 84 | #endif 85 | } 86 | static void resetColor() 87 | { 88 | setColor(CONSOLE_COLOR_NONE); 89 | } 90 | static void moveUp(int l = 1) 91 | { 92 | #ifdef _MSC_VER 93 | CONSOLE_SCREEN_BUFFER_INFO info; 94 | GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info); 95 | info.dwCursorPosition.Y -= l; 96 | SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), info.dwCursorPosition); 97 | #else 98 | if (l > 0) 99 | { 100 | fprintf(stderr, "\e[%dA", l); 101 | } 102 | else if (l < 0) 103 | { 104 | for (int i = 0; i < -l; i++) { fprintf(stderr, "\n"); } 105 | } 106 | #endif 107 | } 108 | static void moveDown(int l = 1) 109 | { 110 | moveUp(-l); 111 | } 112 | }; 113 | -------------------------------------------------------------------------------- /DrawStringFT.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "PotConv.h" 3 | #include "ft2build.h" 4 | 5 | #include "freetype/freetype.h" 6 | #include "opencv2/opencv.hpp" 7 | #include 8 | 9 | class DrawStringFT 10 | { 11 | private: 12 | FT_Library library{ nullptr }; 13 | FT_Face face{ nullptr }; 14 | FT_GlyphSlot slot{ nullptr }; 15 | //FT_Matrix matrix{nullptr}; 16 | FT_Vector pen{ 0, 0 }; 17 | int fontsize_ = 0; 18 | 19 | public: 20 | DrawStringFT() 21 | { 22 | FT_Init_FreeType(&library); 23 | } 24 | 25 | ~DrawStringFT() 26 | { 27 | FT_Done_Face(face); 28 | FT_Done_FreeType(library); 29 | } 30 | 31 | void openFont(const std::string& fontname, int fontsize) 32 | { 33 | if (FT_New_Face(library, fontname.c_str(), 0, &face)) 34 | { 35 | fprintf(stderr, "Cannot open font file: %s \n", fontname.c_str()); 36 | } 37 | FT_Select_Charmap(face, FT_ENCODING_UNICODE); 38 | FT_Set_Char_Size(face, 64 * fontsize, 0, 100, 0); 39 | slot = face->glyph; 40 | fontsize_ = fontsize; 41 | } 42 | 43 | int drawString(std::string text, cv::Mat& mat, int x, int y, cv::Vec3b color, int fusion = 0, const std::string& code = "utf-8") 44 | { 45 | pen.x = 0; 46 | pen.y = 0; 47 | int width = 0; 48 | auto text1 = PotConv::conv(text, code, "utf-16le"); 49 | for (int n = 0; n < text1.size(); n += 2) 50 | { 51 | FT_Set_Transform(face, nullptr, &pen); 52 | wchar_t v = *(wchar_t*)&text1[n]; 53 | FT_Load_Char(face, v, FT_LOAD_RENDER); 54 | draw_bitmap(&slot->bitmap, slot->bitmap_left + x, fontsize_ * 1 - slot->bitmap_top + y, mat, color, fusion); 55 | pen.x += slot->advance.x; 56 | pen.y += slot->advance.y; 57 | width = slot->bitmap.width + slot->bitmap_left; 58 | } 59 | return width; 60 | } 61 | 62 | private: 63 | void draw_bitmap(FT_Bitmap* bitmap, FT_Int x, FT_Int y, cv::Mat& image, cv::Vec3b color, int fusion) 64 | { 65 | FT_Int x_max = x + bitmap->width; 66 | FT_Int y_max = y + bitmap->rows; 67 | for (int i = x, p = 0; i < x_max; i++, p++) 68 | { 69 | for (int j = y, q = 0; j < y_max; j++, q++) 70 | { 71 | if (i < 0 || j < 0 || i >= image.cols || j >= image.rows) 72 | { 73 | continue; 74 | } 75 | auto& v0 = bitmap->buffer[q * bitmap->width + p]; 76 | if (v0 == 0) 77 | { 78 | continue; 79 | } 80 | if (image.channels() == 1) 81 | { 82 | auto& a = image.data[j * image.step + i]; 83 | auto v = v0; 84 | if (fusion == 0) 85 | { 86 | a = v; 87 | } 88 | else if (fusion == -1) 89 | { 90 | a = std::min(v, a); 91 | } 92 | else if (fusion == 1) 93 | { 94 | a = std::max(v, a); 95 | } 96 | } 97 | else if (image.channels() == 3) 98 | { 99 | for (int c = 0; c < image.channels(); c++) 100 | { 101 | auto& a = image.data[j * image.step + i * 3 + c]; 102 | auto v = uchar(v0 * color[c] / 255); 103 | if (fusion == 0) 104 | { 105 | a = v; 106 | } 107 | else if (fusion == -1) 108 | { 109 | a = std::min(v, a); 110 | } 111 | else if (fusion == 1) 112 | { 113 | a = std::max(v, a); 114 | } 115 | } 116 | } 117 | } 118 | } 119 | } 120 | }; 121 | -------------------------------------------------------------------------------- /DynamicLibrary.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #ifdef _WIN32 7 | #define NOMINMAX 8 | #include 9 | #undef NOMINMAX 10 | #else 11 | #include 12 | #endif 13 | 14 | class DynamicLibrary 15 | { 16 | private: 17 | static DynamicLibrary* getInstance() 18 | { 19 | static DynamicLibrary dl; 20 | return &dl; 21 | } 22 | 23 | DynamicLibrary(DynamicLibrary&) = delete; 24 | DynamicLibrary& operator=(DynamicLibrary&) = delete; 25 | 26 | private: 27 | DynamicLibrary() 28 | { 29 | } 30 | 31 | //free loaded dynamic libraries automatically when destruction 32 | ~DynamicLibrary() 33 | { 34 | for (auto dl : dynamic_libraries_) 35 | { 36 | if (dl.second) 37 | { 38 | #ifdef _WIN32 39 | FreeLibrary(dl.second); 40 | #else 41 | dlclose(dl.second); 42 | #endif 43 | } 44 | } 45 | } 46 | 47 | private: 48 | #ifdef _WIN32 49 | std::map dynamic_libraries_; 50 | #else 51 | std::map dynamic_libraries_; 52 | #endif 53 | 54 | public: 55 | static void* loadDynamicLibrary(std::string library_name) 56 | { 57 | if (library_name.find(".") == std::string::npos) 58 | { 59 | #ifdef _WIN32 60 | library_name = library_name + ".dll"; 61 | #else 62 | library_name = "lib" + library_name + ".so"; 63 | #endif 64 | } 65 | auto dl = getInstance(); 66 | if (dl->dynamic_libraries_.count(library_name) == 0) 67 | { 68 | if (library_name.find_first_of("/\\") != std::string::npos) 69 | { 70 | library_name = std::filesystem::absolute(library_name).string(); 71 | } 72 | #ifdef _WIN32 73 | auto hlib = LoadLibraryA(library_name.c_str()); 74 | #else 75 | auto hlib = dlopen(library_name.c_str(), RTLD_LAZY); 76 | #endif 77 | dl->dynamic_libraries_[library_name] = hlib; 78 | //if (hlib) 79 | //{ 80 | // fprintf(stdout, "Loaded dynamic library %s\n", library_name.c_str()); 81 | //} 82 | //else 83 | //{ 84 | // fprintf(stdout, "Failed to load dynamic library %s\n", library_name.c_str()); 85 | //} 86 | } 87 | return dl->dynamic_libraries_[library_name]; 88 | } 89 | 90 | static void* getFunction(const std::string& library_name, const std::string& function_name) 91 | { 92 | auto dl = getInstance()->loadDynamicLibrary(library_name); 93 | void* func = nullptr; 94 | if (dl) 95 | { 96 | #ifdef _WIN32 97 | func = GetProcAddress((HINSTANCE)dl, function_name.c_str()); 98 | #else 99 | func = dlsym(dl, function_name.c_str()); 100 | #endif 101 | //fprintf(stdout, "Loaded function %s\n", function_name.c_str()); 102 | } 103 | else 104 | { 105 | //fprintf(stdout, "Failed to load function %s\n", function_name.c_str()); 106 | } 107 | return func; 108 | } 109 | 110 | static void freeDynamicLibrary(const std::string& library_name) 111 | { 112 | auto dl = getInstance(); 113 | if (dl->dynamic_libraries_.count(library_name) > 0) 114 | { 115 | auto hlib = dl->dynamic_libraries_[library_name]; 116 | #ifdef _WIN32 117 | FreeLibrary(hlib); 118 | #else 119 | dlclose(hlib); 120 | #endif 121 | dl->dynamic_libraries_.erase(library_name); 122 | } 123 | } 124 | }; 125 | -------------------------------------------------------------------------------- /FakeJson.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class FakeJson 9 | { 10 | private: 11 | std::variant, std::vector> value; 12 | std::map& value_map() 13 | { 14 | if (!isMap()) 15 | { 16 | value = std::map(); 17 | } 18 | return std::get>(value); 19 | } 20 | std::vector& value_vector() 21 | { 22 | if (!isVector()) 23 | { 24 | value = std::vector(); 25 | } 26 | return std::get>(value); 27 | } 28 | enum 29 | { 30 | type_null, 31 | type_int, 32 | type_double, 33 | type_bool, 34 | type_string, 35 | type_map, 36 | type_vector 37 | }; 38 | 39 | public: 40 | FakeJson() = default; 41 | 42 | FakeJson(int v) { value = v; } 43 | 44 | FakeJson(double v) { value = v; } 45 | 46 | FakeJson(const std::string& v) { value = v; } 47 | 48 | FakeJson(const char* v) { value = std::string(v); } 49 | 50 | FakeJson(bool v) { value = v; } 51 | 52 | template 53 | FakeJson(const std::vector& v) 54 | { 55 | for (auto& v1 : v) 56 | { 57 | pushBack(v1); 58 | } 59 | } 60 | 61 | template 62 | FakeJson(const std::map& m) 63 | { 64 | for (auto& p : m) 65 | { 66 | (*this)[p.first] = p.second; 67 | } 68 | } 69 | 70 | template 71 | FakeJson(const std::pair& p) { (*this)[p.first] = p.second; } 72 | 73 | template 74 | FakeJson(const std::pair& p) { (*this)[p.first] = p.second; } 75 | 76 | //std::type_info const& type() const { return value.type(); } 77 | 78 | template 79 | bool isType() const { return std::holds_alternative(value); } 80 | 81 | bool isInt() const { return value.index() == type_int; } 82 | 83 | bool isDouble() const { return value.index() == type_double; } 84 | 85 | bool isString() const { return value.index() == type_string; } 86 | 87 | bool isBool() const { return value.index() == type_bool; } 88 | 89 | bool isNull() const { return value.index() == type_null; } 90 | 91 | bool isValue() const { return value.index() != type_null && value.index() != type_map && value.index() != type_vector; } 92 | 93 | bool isMap() const { return value.index() == type_map; } 94 | 95 | bool isVector() const { return value.index() == type_vector; } 96 | 97 | int toInt() { return std::get(value); } 98 | 99 | double toDouble() { return std::get(value); } 100 | 101 | std::string toString() { return std::get(value); } 102 | 103 | bool toBool() { return std::get(value); } 104 | 105 | FakeJson& operator[](int v) { return value_vector()[v]; } 106 | 107 | FakeJson& operator[](const std::string& v) { return value_map()[v]; } 108 | 109 | FakeJson& operator[](const char* v) { return value_map()[std::string(v)]; } 110 | 111 | void pushBack(const FakeJson& v) { value_vector().push_back(v); } 112 | 113 | void erase(const std::string& v) { value_map().erase(v); } //需由上一级删除,而不能自己删除,下同 114 | 115 | void erase(int v) 116 | { 117 | if (v >= 0 && v < value_vector().size()) { value_vector().erase(value_vector().begin() + v); } 118 | } 119 | 120 | void clear() 121 | { 122 | value = nullptr; 123 | } 124 | 125 | bool exist(const std::string& v) const { return std::get>(value).count(v); } 126 | 127 | bool exist(int v) const { return v >= 0 && v < std::get>(value).size(); } 128 | 129 | //template 130 | //std::vector toVector() const 131 | //{ 132 | // std::vector v; 133 | // for (auto& i : value_vector) 134 | // { 135 | // v.emplace_back(i.to()); 136 | // } 137 | // return v; 138 | //} 139 | 140 | //template 141 | //std::map toMap() const 142 | //{ 143 | // std::map v; 144 | // for (auto& i : value_map) 145 | // { 146 | // v[i.first] = i.second.to(); 147 | // } 148 | // return v; 149 | //} 150 | 151 | bool isPrintable() const 152 | { 153 | return isInt() || isDouble() || isString() || isBool() || isNull(); 154 | } 155 | 156 | bool isNum() const { return isInt() || isDouble(); } 157 | 158 | std::string to_string(bool narrow = true, int space = 0) const 159 | { 160 | if (isInt()) 161 | { 162 | return std::to_string(std::get(value)); 163 | } 164 | if (isDouble()) 165 | { 166 | return std::to_string(std::get(value)); 167 | } 168 | if (isString()) 169 | { 170 | return "\"" + std::get(value) + "\""; 171 | } 172 | if (isBool()) 173 | { 174 | return std::get(value) ? "true" : "false"; 175 | } 176 | if (isNull()) 177 | { 178 | return "null"; 179 | } 180 | if (isMap()) 181 | { 182 | std::string res; 183 | for (auto& [k, v] : std::get>(value)) 184 | { 185 | if (!narrow) 186 | { 187 | res += std::string(space+4, ' '); 188 | } 189 | res += "\"" + k + "\": " + v.to_string(narrow, space+4) + ", "; 190 | if (!narrow) 191 | { 192 | res += "\n"; 193 | } 194 | } 195 | if (!res.empty()) 196 | { 197 | res.pop_back(); 198 | res.pop_back(); 199 | if (!narrow) 200 | { 201 | res.pop_back(); 202 | } 203 | } 204 | if (narrow) 205 | { 206 | res = "{" + res + "}"; 207 | } 208 | else 209 | { 210 | res = "\n"+ std::string(space, ' ') + "{\n" + res + "\n" + std::string(space , ' ') + "}"; 211 | } 212 | return res; 213 | } 214 | if (isVector()) 215 | { 216 | std::string res; 217 | for (auto& i : std::get>(value)) 218 | { 219 | if (!narrow) 220 | { 221 | res += std::string(space + 4, ' '); 222 | } 223 | res += i.to_string(narrow, space+4) + ", "; 224 | if (!narrow) 225 | { 226 | res += "\n"; 227 | } 228 | } 229 | if (!res.empty()) 230 | { 231 | res.pop_back(); 232 | res.pop_back(); 233 | if (!narrow) 234 | { 235 | res.pop_back(); 236 | } 237 | } 238 | if (narrow) 239 | { 240 | res = "[" + res + "]"; 241 | } 242 | else 243 | { 244 | res = "\n" + std::string(space, ' ') + "[\n" + res + "\n" + std::string(space, ' ') + "]"; 245 | } 246 | return res; 247 | } 248 | 249 | return ""; 250 | } 251 | 252 | void parse(const std::string& str) 253 | { 254 | FakeJson& o = *this; 255 | std::vector ptr{ &o }; 256 | int ignore_space = 1; 257 | char quote = '\0'; 258 | bool found_backslash = false; 259 | std::string cur; 260 | 261 | auto try_to_variant = [](std::string&& str1) -> decltype(value) 262 | { 263 | auto str = std::move(str1); 264 | str1.clear(); 265 | if (str.empty()) 266 | { 267 | return nullptr; 268 | } 269 | if (str[0] == '\"' && str.back() == '\"') 270 | { 271 | return str.substr(1, str.size() - 2); 272 | } 273 | if (str[0] == '\'' && str.back() == '\'') 274 | { 275 | return str.substr(1, str.size() - 2); 276 | } 277 | if (str == "true") 278 | { 279 | return true; 280 | } 281 | if (str == "false") 282 | { 283 | return false; 284 | } 285 | if (str == "null") 286 | { 287 | return nullptr; 288 | } 289 | char* end = nullptr; 290 | auto i = strtoll(str.c_str(), &end, 10); 291 | if (end == str.c_str() + str.size()) 292 | { 293 | return int(i); 294 | } 295 | char* end2 = nullptr; 296 | auto d = strtod(str.c_str(), &end2); 297 | if (end2 == str.c_str() + str.size()) 298 | { 299 | return d; 300 | } 301 | return std::move(str); 302 | }; 303 | 304 | auto dequote = [](std::string& str) -> std::string 305 | { 306 | if (str.empty()) 307 | { 308 | return str; 309 | } 310 | if (str[0] == '\"' && str.back() == '\"') 311 | { 312 | return str.substr(1, str.size() - 2); 313 | } 314 | if (str[0] == '\'' && str.back() == '\'') 315 | { 316 | return str.substr(1, str.size() - 2); 317 | } 318 | return str; 319 | }; 320 | 321 | for (auto& c : str) 322 | { 323 | if (c == '\"' || c == '\'') 324 | { 325 | if (!found_backslash) 326 | { 327 | if (quote == '\0') 328 | { 329 | quote = c; 330 | //continue; 331 | } 332 | else if (quote == c) 333 | { 334 | quote = '\0'; 335 | //continue; 336 | } 337 | } 338 | } 339 | if (quote == '\0') 340 | { 341 | if (c == '[') 342 | { 343 | ptr.push_back(&ptr.back()->value_vector().emplace_back()); 344 | ignore_space = 1; 345 | } 346 | else if (c == ']') 347 | { 348 | if (ptr.back()->isNull()) 349 | { 350 | ptr.back()->value = try_to_variant(std::move(cur)); 351 | } 352 | if (ptr.size() >= 2) 353 | { 354 | ptr.pop_back(); 355 | } 356 | } 357 | else if (c == '{') 358 | { 359 | ptr.push_back(&ptr.back()->value_map()[""]); 360 | ignore_space = 1; 361 | } 362 | else if (c == '}') 363 | { 364 | if (ptr.back()->isNull()) 365 | { 366 | ptr.back()->value = try_to_variant(std::move(cur)); 367 | } 368 | ptr[ptr.size() - 2]->value_map().erase(""); 369 | if (ptr.size() >= 2) 370 | { 371 | ptr.pop_back(); 372 | } 373 | } 374 | else if (c == ':') 375 | { 376 | ptr.back() = &ptr[ptr.size() - 2]->value_map()[dequote(cur)]; 377 | cur.clear(); 378 | ignore_space = 1; 379 | } 380 | else if (c == ',') 381 | { 382 | if (ptr.back()->isValue() || ptr.back()->isNull()) 383 | { 384 | ptr.back()->value = try_to_variant(std::move(cur)); 385 | } 386 | if (ptr.size() >= 2) 387 | { 388 | if (ptr[ptr.size() - 2]->isVector()) 389 | { 390 | ptr.back() = &ptr[ptr.size() - 2]->value_vector().emplace_back(); 391 | } 392 | if (ptr[ptr.size() - 2]->isMap()) 393 | { 394 | ptr.back() = &ptr[ptr.size() - 2]->value_map()[""]; 395 | } 396 | } 397 | ignore_space = 1; 398 | } 399 | else if ((c == ' ' || c == '\n' || c == '\r') && ignore_space == 1) 400 | { 401 | } 402 | else 403 | { 404 | cur += c; 405 | ignore_space = 1; 406 | //如果是非紧凑格式,需要忽略这里的空格 407 | //因此转为非json字串时,string必须用引号包围 408 | } 409 | } 410 | else 411 | { 412 | if (c == '\\' && !found_backslash) 413 | { 414 | found_backslash = true; 415 | continue; 416 | } 417 | else if (found_backslash) 418 | { 419 | found_backslash = false; 420 | } 421 | cur += c; 422 | ignore_space = 0; 423 | } 424 | } 425 | //return o; 426 | } 427 | 428 | std::string allToString(bool narrow = true) const 429 | { 430 | return to_string(narrow, 0); 431 | } 432 | }; 433 | -------------------------------------------------------------------------------- /FunctionTrait.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | template 6 | struct arg_counter 7 | { 8 | }; 9 | 10 | template 11 | struct arg_counter 12 | { 13 | static constexpr std::size_t value = sizeof...(Args); 14 | }; 15 | 16 | template 17 | struct check_return_type 18 | { 19 | }; 20 | 21 | template 22 | struct check_return_type 23 | { 24 | static constexpr bool value = std::is_same::value; 25 | }; 26 | -------------------------------------------------------------------------------- /INIReader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace INIReader 11 | { 12 | 13 | struct KeyType 14 | { 15 | std::string key, value; 16 | }; 17 | 18 | template 19 | class INIReader_t 20 | { 21 | public: 22 | template 23 | struct Record 24 | { 25 | std::string key; 26 | T value; 27 | }; 28 | 29 | template 30 | struct ListWithIndex : std::list> 31 | { 32 | std::unordered_map index; 33 | 34 | T& operator[](const std::string& key) 35 | { 36 | auto key1 = COM_METHOD()(key); 37 | auto it = index.find(key1); 38 | if (it != index.end()) 39 | { 40 | return *it->second; 41 | } 42 | else 43 | { 44 | std::list>::push_back({ key, T() }); 45 | T* p = &std::list>::back().value; 46 | index.insert({ key1, p }); 47 | return *p; 48 | } 49 | } 50 | 51 | const T& at(const std::string& key) const 52 | { 53 | return *index.at(COM_METHOD()(key)); 54 | } 55 | 56 | int count(const std::string& key) const 57 | { 58 | return index.count(COM_METHOD()(key)); 59 | } 60 | 61 | void erase(const std::string& key) 62 | { 63 | auto& it = (*this)[key]; 64 | std::list>::remove_if([&](Record& sec) 65 | { 66 | return &sec.value == ⁢ 67 | }); 68 | index.erase(COM_METHOD()(key)); 69 | } 70 | }; 71 | 72 | public: 73 | struct KeyType1 74 | { 75 | public: 76 | std::string value; 77 | std::string other; 78 | ListWithIndex group; 79 | 80 | public: 81 | KeyType1() {} 82 | 83 | KeyType1(const std::string& value, const std::string& other = "") : 84 | value(value), other(other) {} 85 | 86 | KeyType1(const char* value) : 87 | value(value) {} 88 | 89 | template 90 | KeyType1(const T& value) : 91 | value(std::to_string(value)) {} 92 | 93 | int toInt(int default_value = 0) const 94 | { 95 | if (value.empty()) 96 | { 97 | return default_value; 98 | } 99 | return atoi(value.c_str()); 100 | } 101 | 102 | double toDouble(double default_value = 0) const 103 | { 104 | if (value.empty()) 105 | { 106 | return default_value; 107 | } 108 | return atof(value.c_str()); 109 | } 110 | 111 | const std::string& toString(const std::string& default_value = "") const 112 | { 113 | if (value.empty()) 114 | { 115 | return default_value; 116 | } 117 | return value; 118 | } 119 | 120 | KeyType1& operator[](const std::string& key) 121 | { 122 | return group[key]; 123 | } 124 | 125 | const KeyType1& operator[](const std::string& key) const 126 | { 127 | return group.at(key); 128 | } 129 | 130 | int count(const std::string& key) const 131 | { 132 | return group.count(key); 133 | } 134 | 135 | void erase(const std::string& key) 136 | { 137 | group.erase(key); 138 | } 139 | 140 | bool isKey() const 141 | { 142 | return group.size() == 0; 143 | } 144 | 145 | std::vector getAllSections() const 146 | { 147 | std::vector ret; 148 | for (auto& value : group) 149 | { 150 | if (!value.value.isKey()) 151 | { 152 | ret.push_back(value.key); 153 | } 154 | } 155 | return ret; 156 | } 157 | 158 | std::vector getAllKeys() const 159 | { 160 | std::vector ret; 161 | for (auto& value : group) 162 | { 163 | if (value.value.isKey()) 164 | { 165 | ret.push_back(value.key); 166 | } 167 | } 168 | return ret; 169 | } 170 | 171 | void addWithoutIndex(const KeyType1& value) 172 | { 173 | group.push_back({ "", value }); 174 | } 175 | 176 | std::string allToString(int layer = 1, bool show_other = true, const std::string& line_break = "\n") const //ignore the value of first layer 177 | { 178 | std::string str; 179 | for (auto& sec : group) 180 | { 181 | if (sec.value.group.size() == 0) 182 | { 183 | if (sec.key.empty()) 184 | { 185 | if (show_other) 186 | { 187 | str += sec.value.other; 188 | } 189 | } 190 | else 191 | { 192 | str += sec.key; 193 | str += " = "; 194 | str += dealValue(sec.value.value); 195 | if (show_other) 196 | { 197 | str += sec.value.other; 198 | } 199 | } 200 | str += line_break; 201 | } 202 | } 203 | for (auto& sec : group) 204 | { 205 | if (sec.value.group.size() > 0) 206 | { 207 | if (!(layer == 1 && sec.key.empty() && str.empty())) 208 | { 209 | str += std::string(layer, '['); 210 | str += sec.key; 211 | str += std::string(layer, ']'); 212 | str += line_break; 213 | } 214 | str += sec.value.allToString(layer + 1, show_other, line_break); 215 | } 216 | } 217 | return str; 218 | } 219 | }; 220 | 221 | private: 222 | KeyType1 root; 223 | 224 | #ifdef _WIN32 225 | std::string line_break_ = "\r\n"; 226 | #else 227 | std::string line_break_ = "\n"; 228 | #endif 229 | int error_ = 0; 230 | bool bom_ = false; 231 | 232 | //return value: the key has existed, 0 means it is a new key 233 | int valueHandler(KeyType1& keytype, const std::string& section, const std::string& key, const std::string& value, const std::string& other = "") 234 | { 235 | if (key.empty()) 236 | { 237 | keytype[section].addWithoutIndex(KeyType1(value, other)); //comment or others 238 | } 239 | else 240 | { 241 | keytype[section][key] = KeyType1(value, other); 242 | } 243 | return 0; 244 | } 245 | 246 | private: 247 | static std::string dealValue(const std::string& str) 248 | { 249 | if (str.find_first_of(";#\n\r") != std::string::npos) 250 | { 251 | return "\"" + str + "\""; 252 | } 253 | return str; 254 | } 255 | 256 | public: 257 | INIReader_t() 258 | { 259 | } 260 | 261 | KeyType1& operator[](const std::string& key) 262 | { 263 | return root[key]; 264 | } 265 | 266 | // parse a given filename 267 | int loadFile(const std::string& filename) 268 | { 269 | FILE* fp = fopen(filename.c_str(), "rb"); 270 | if (!fp) 271 | { 272 | //fprintf(stderr, "Cannot open file %s\n", filename.c_str()); 273 | return 1; 274 | } 275 | fseek(fp, 0, SEEK_END); 276 | int length = ftell(fp); 277 | fseek(fp, 0, 0); 278 | std::string str; 279 | str.resize(length, '\0'); 280 | if (fread((void*)str.c_str(), 1, length, fp) < length) 281 | { 282 | //fprintf(stderr, "Read file %s unfinished\n", filename.c_str()); 283 | return 1; 284 | } 285 | fclose(fp); 286 | loadString(str); 287 | return 0; 288 | } 289 | 290 | // parse an ini string 291 | void loadString(const std::string& content) 292 | { 293 | line_break_ = "\n"; 294 | int pos = content.find(line_break_, 1); 295 | if (pos != std::string::npos && pos < content.size()) 296 | { 297 | if (content[pos - 1] == '\r') 298 | { 299 | line_break_ = "\r\n"; 300 | } 301 | else if (content[pos + 1] == '\r') 302 | { 303 | line_break_ = "\n\r"; 304 | } 305 | } 306 | error_ = ini_parse_content(content); 307 | } 308 | 309 | // Return the result of ini_parse(), i.e., 0 on success, line number of 310 | // first error on parse error, or -1 on file open error. 311 | int parseError() const 312 | { 313 | return error_; 314 | } 315 | 316 | // Get a string value from INI file, returning default_value if not found. 317 | std::string getString(const std::string& section, const std::string& key, const std::string& default_value = "") const 318 | { 319 | auto section1 = (section); 320 | if (root.count(section1) == 0) 321 | { 322 | return default_value; 323 | } 324 | auto key1 = (key); 325 | if (root[section1].count(key1) == 0) 326 | { 327 | return default_value; 328 | } 329 | return root[section1][key1].value; 330 | } 331 | 332 | // Get an integer (long) value from INI file, returning default_value if 333 | // not found or not a valid integer (decimal "1234", "-1234", or hex "0x4d2"). 334 | int getInt(const std::string& section, const std::string& key, int default_value = 0) const 335 | { 336 | auto valstr = getString(section, key, ""); 337 | const char* value = valstr.c_str(); 338 | char* end; 339 | // This parses "1234" (decimal) and also "0x4D2" (hex) 340 | int n = strtol(value, &end, 0); 341 | return end > value ? n : default_value; 342 | } 343 | 344 | // Get a real (floating point double) value from INI file, returning 345 | // default_value if not found or not a valid floating point value 346 | // according to strtod(). 347 | double getReal(const std::string& section, const std::string& key, double default_value = 0.0) const 348 | { 349 | auto valstr = getString(section, key, ""); 350 | const char* value = valstr.c_str(); 351 | char* end; 352 | double n = strtod(value, &end); 353 | return end > value ? n : default_value; 354 | } 355 | 356 | // Get a boolean value from INI file, returning default_value if not found or if 357 | // not a valid true/false value. Valid true values are "true", "yes", "on", "1", 358 | // and valid false values are "false", "no", "off", "0" (not case sensitive). 359 | bool getBoolean(const std::string& section, const std::string& key, bool default_value = false) const 360 | { 361 | auto valstr = getString(section, key, ""); 362 | // Convert to lower case to make string comparisons case-insensitive 363 | std::transform(valstr.begin(), valstr.end(), valstr.begin(), ::tolower); 364 | if (valstr == "true" || valstr == "yes" || valstr == "on" || valstr == "1") 365 | { 366 | return true; 367 | } 368 | else if (valstr == "false" || valstr == "no" || valstr == "off" || valstr == "0") 369 | { 370 | return false; 371 | } 372 | else 373 | { 374 | return getReal(section, key, default_value); 375 | } 376 | } 377 | 378 | template 379 | static T stringTo(const std::string& s) 380 | { 381 | double v = atof(s.c_str()); 382 | return T(v); 383 | } 384 | 385 | //only support int, float, double 386 | template 387 | T get(const std::string& section, const std::string& key, T default_value = T(0)) const 388 | { 389 | auto v = stringTo(getString(section, key, std::to_string(default_value))); 390 | return v; 391 | } 392 | 393 | //note if the result vector is shorter than default_v, the rest will be filled with default_v rest 394 | std::vector getStringVector(const std::string& section, const std::string& key, const std::string& split_chars = ",", const std::vector& default_v = {}) const 395 | { 396 | auto v = splitString(getString(section, key), split_chars, true); 397 | for (auto i = v.size(); i < default_v.size(); i++) { v.push_back(default_v[i]); } 398 | return v; 399 | } 400 | 401 | //only support int, float, double 402 | //note if the result vector is shorter than default_v, the rest will be filled with default_v rest 403 | template 404 | std::vector getVector(const std::string& section, const std::string& key, const std::string& split_chars = ",", const std::vector& default_v = {}) const 405 | { 406 | auto v_str = getStringVector(section, key, split_chars); 407 | std::vector v; 408 | for (auto& s : v_str) { v.push_back(stringTo(s)); } 409 | for (auto i = v.size(); i < default_v.size(); i++) { v.push_back(default_v[i]); } 410 | return v; 411 | } 412 | 413 | //check one section exist or not 414 | int hasSection(const std::string& section) const 415 | { 416 | return root.count(section); 417 | } 418 | 419 | //check one section and one key exist or not 420 | int hasKey(const std::string& section, const std::string& key) const 421 | { 422 | if (root.count(section) == 0) 423 | { 424 | return 0; 425 | } 426 | if (root[section].count(key) == 0) 427 | { 428 | return 0; 429 | } 430 | return 1; 431 | } 432 | 433 | std::vector getAllSections() const 434 | { 435 | std::vector ret; 436 | for (auto& value : root.group) 437 | { 438 | ret.push_back(value.key); 439 | } 440 | return ret; 441 | } 442 | 443 | std::vector getAllKeys(const std::string& section) const 444 | { 445 | std::vector ret; 446 | if (root.count(section) == 0) 447 | { 448 | return ret; 449 | } 450 | auto& sec = root[section].group; 451 | for (auto& kv : sec) 452 | { 453 | if (!kv.key.empty()) 454 | { 455 | ret.push_back(kv.key); 456 | } 457 | } 458 | return ret; 459 | } 460 | 461 | std::vector getAllKeyValues(const std::string& section) const 462 | { 463 | std::vector ret; 464 | if (root.count(section) == 0) 465 | { 466 | return ret; 467 | } 468 | auto& sec = root[section].group; 469 | for (auto& kv : sec) 470 | { 471 | if (!kv.key.empty()) 472 | { 473 | ret.push_back({ kv.key, kv.value.value }); 474 | } 475 | } 476 | return ret; 477 | } 478 | 479 | void setKey(const std::string& section, const std::string& key, const std::string& value) 480 | { 481 | valueHandler(root, section, key, value); 482 | } 483 | 484 | void eraseKey(const std::string& section, const std::string& key) 485 | { 486 | root[section].erase(key); 487 | } 488 | 489 | void eraseSection(const std::string& section) 490 | { 491 | root.erase(section); 492 | } 493 | 494 | void clear() 495 | { 496 | root.group.clear(); 497 | root.group.index.clear(); 498 | } 499 | 500 | private: 501 | int ini_parse_content(const std::string& content) 502 | { 503 | /* Return pointer to first non-whitespace char in given string */ 504 | auto lskip = [](std::string& s) -> void 505 | { 506 | auto pre = s.find_first_not_of(" \t"); 507 | if (pre != std::string::npos) 508 | { 509 | s = s.substr(pre); 510 | } 511 | }; 512 | 513 | /* Strip whitespace chars off end of given string */ 514 | auto rstrip = [](std::string& s, size_t& suf) -> void 515 | { 516 | auto pos = s.find_last_not_of(" \t"); 517 | if (pos != std::string::npos) 518 | { 519 | suf = s.size() - pos - 1; 520 | s.resize(pos + 1); 521 | } 522 | else 523 | { 524 | suf = 0; 525 | } 526 | }; 527 | 528 | /* Uses a fair bit of stack (use heap instead if you need to) */ 529 | std::string section = ""; 530 | int error = 0; 531 | /* Scan all lines */ 532 | size_t i = 0; 533 | if (content.size() >= 3 && (unsigned char)content[0] == 0xEF && (unsigned char)content[1] == 0xBB && (unsigned char)content[2] == 0xBF) 534 | { 535 | i = 3; 536 | bom_ = true; 537 | } 538 | bool new_line = true; 539 | 540 | int status = 0; //0: new line, 1: comment, 2: section, 3: key, 4: value 541 | std::string str; 542 | std::vector stack = { &root }; 543 | while (i < content.size()) 544 | { 545 | auto& c = content[i]; 546 | 547 | if (c == '\r') { i++; } 548 | else if (!new_line && c == '\n') 549 | { 550 | i++; 551 | new_line = true; 552 | } 553 | else if (new_line && c == '\n') 554 | { 555 | new_line = true; 556 | valueHandler(*stack.back(), section, "", "", ""); 557 | i++; 558 | } 559 | else if (new_line && c == '[') 560 | { 561 | auto square_count = content.find_first_not_of("[", i + 1); 562 | if (square_count != std::string::npos && square_count >= i) 563 | { 564 | square_count -= i; 565 | auto end = content.find(std::string(square_count, ']'), i + square_count); 566 | if (square_count > stack.size()) 567 | { 568 | stack.push_back(&stack.back()->group[section]); 569 | } 570 | else if (square_count < stack.size()) 571 | { 572 | if (square_count < 1) { square_count = 1; } 573 | stack.resize(square_count); 574 | } 575 | if (end != std::string::npos) 576 | { 577 | section = content.substr(i + square_count, end - i - square_count); //found a new section 578 | } 579 | i = end; 580 | } 581 | new_line = false; 582 | } 583 | else if (new_line && c != ' ') 584 | { 585 | new_line = false; 586 | auto end = content.find_first_of("=", i + 1); 587 | if (end != std::string::npos) 588 | { 589 | auto end2 = content.find_first_of(";#\n\r\'\"", i); 590 | if (end2 < end) 591 | { 592 | auto endline = content.find_first_of("\n\r", i); 593 | if (endline != std::string::npos) 594 | { 595 | std::string o = content.substr(i, endline - i); 596 | valueHandler(*stack.back(), section, "", "", o); 597 | i = endline; 598 | } 599 | else 600 | { 601 | i++; 602 | } 603 | } 604 | else 605 | { 606 | auto key = content.substr(i, end - i); //found a new key 607 | size_t suf; 608 | rstrip(key, suf); 609 | //if find a key, search the value from the next char of '=', considering quote and new line 610 | size_t i1 = end + 1; 611 | int quote = 0; //0: no quote, 1: ', 2: " 612 | std::string v; 613 | size_t o_begin = std::string::npos; 614 | bool begin = true; 615 | bool end = false; 616 | while (i1 < content.size()) 617 | { 618 | auto& c1 = content[i1]; 619 | if (begin && c1 == ' ') 620 | { 621 | i1++; 622 | continue; 623 | } 624 | if (begin && c1 != ' ') 625 | { 626 | begin = false; 627 | } 628 | if (!begin) 629 | { 630 | if (!end) 631 | { 632 | if (quote == 0 && c1 == '\'') { quote = 1; } 633 | else if (quote == 0 && c1 == '\"') { quote = 2; } 634 | else if (quote == 1 && c1 == '\'') { quote = 0; } 635 | else if (quote == 2 && c1 == '\"') { quote = 0; } 636 | } 637 | if (quote == 0) 638 | { 639 | if (c1 == '\r' || c1 == '\n') 640 | { 641 | i = i1; 642 | break; 643 | } 644 | if (c1 == '#' || c1 == ';') 645 | { 646 | o_begin = i1; 647 | end = true; 648 | } 649 | if (!end) 650 | { 651 | v += c1; 652 | } 653 | } 654 | else 655 | { 656 | v += c1; 657 | } 658 | } 659 | i1++; 660 | } 661 | if (o_begin == std::string::npos) 662 | { 663 | o_begin = i1; 664 | } 665 | //remove the last space of v 666 | rstrip(v, suf); 667 | if (suf) 668 | { 669 | o_begin -= suf; 670 | } 671 | if (v.size() >= 2 672 | && (v.front() == '\'' && v.back() == '\'' || v.front() == '\"' && v.back() == '\"')) 673 | { 674 | v = v.substr(1, v.size() - 2); 675 | } 676 | valueHandler(*stack.back(), section, key, v, content.substr(o_begin, i1 - o_begin)); 677 | i = i1; 678 | } 679 | } 680 | else 681 | { 682 | //no "=" 683 | auto endline = content.find_first_of("\n\r", i); 684 | if (endline != std::string::npos) 685 | { 686 | std::string o = content.substr(i, endline - i); 687 | valueHandler(*stack.back(), section, "", "", o); 688 | i = endline; 689 | } 690 | else 691 | { 692 | std::string o = content.substr(i); 693 | valueHandler(*stack.back(), section, "", "", o); 694 | i = content.size(); 695 | } 696 | } 697 | } 698 | else 699 | { 700 | i++; 701 | } 702 | } 703 | return 0; 704 | } 705 | 706 | static std::vector splitString(std::string str, std::string pattern, bool ignore_psspace) 707 | { 708 | std::string::size_type pos; 709 | std::vector result; 710 | if (str.empty()) 711 | { 712 | return result; 713 | } 714 | if (pattern.empty()) 715 | { 716 | pattern = ",;| "; 717 | } 718 | str += pattern[0]; //expand string to find the last one 719 | bool have_space = pattern.find(" ") != std::string::npos; 720 | int size = str.size(); 721 | for (int i = 0; i < size; i++) 722 | { 723 | if (have_space) 724 | { 725 | //treat continuous space as one, when space is in pattern 726 | while (str[i] == ' ') 727 | { 728 | i++; 729 | } 730 | } 731 | pos = str.find_first_of(pattern, i); 732 | if (pos < size) 733 | { 734 | std::string s = str.substr(i, pos - i); 735 | if (ignore_psspace) 736 | { 737 | auto pre = s.find_first_not_of(" "); 738 | auto suf = s.find_last_not_of(" "); 739 | if (pre != std::string::npos && suf != std::string::npos) 740 | { 741 | s = s.substr(pre, suf - pre + 1); 742 | } 743 | } 744 | result.push_back(s); 745 | i = pos; 746 | } 747 | } 748 | return result; 749 | } 750 | 751 | public: 752 | //write modified file 753 | int saveFile(const std::string& filename) 754 | { 755 | FILE* fp = fopen(filename.c_str(), "wb"); 756 | if (fp) 757 | { 758 | auto content = toString(); 759 | int length = content.length(); 760 | fwrite(content.c_str(), length, 1, fp); 761 | fclose(fp); 762 | return 0; 763 | } 764 | return 1; 765 | } 766 | 767 | //make a string with trying to keep the original style 768 | std::string toString(bool comment = true) const 769 | { 770 | std::string content; 771 | if (bom_) 772 | { 773 | content += "\xEF\xBB\xBF"; 774 | } 775 | bool first = true; 776 | content += root.allToString(1, comment, line_break_); 777 | if (content.size() >= line_break_.size()) 778 | { 779 | content.resize(content.size() - line_break_.size()); 780 | } 781 | return content; 782 | } 783 | 784 | //a pure string without comments or blank lines 785 | std::string toPureString() const 786 | { 787 | return toString(false); 788 | } 789 | }; 790 | 791 | struct CaseSensitivity 792 | { 793 | std::string operator()(const std::string& l) const 794 | { 795 | return l; 796 | } 797 | }; 798 | 799 | struct CaseInsensitivity 800 | { 801 | std::string operator()(const std::string& l) const 802 | { 803 | auto l1 = l; 804 | std::transform(l1.begin(), l1.end(), l1.begin(), ::tolower); 805 | return l1; 806 | } 807 | }; 808 | 809 | struct NoUnderline 810 | { 811 | std::string operator()(const std::string& l) const 812 | { 813 | auto l1 = l; 814 | auto replaceAllString = [](std::string& s, const std::string& oldstring, const std::string& newstring) 815 | { 816 | int pos = s.find(oldstring); 817 | while (pos >= 0) 818 | { 819 | s.erase(pos, oldstring.length()); 820 | s.insert(pos, newstring); 821 | pos = s.find(oldstring, pos + newstring.length()); 822 | } 823 | }; 824 | replaceAllString(l1, "_", ""); 825 | std::transform(l1.begin(), l1.end(), l1.begin(), ::tolower); 826 | return l1; 827 | } 828 | }; 829 | }; //namespace INIReader 830 | 831 | using INIReaderNormal = INIReader::INIReader_t; 832 | using INIReaderNoUnderline = INIReader::INIReader_t; 833 | -------------------------------------------------------------------------------- /INIReaderBin.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "filefunc.h" 4 | #include "INIReader.h" 5 | 6 | struct INIReaderBin 7 | { 8 | private: 9 | INIReaderNormal ini_; 10 | const char* head_ = "CFG_BIN INI"; 11 | const int head_size_ = 32; 12 | // The head is "CFG_BIN INI", 32 bytes is kept 13 | // The next 8 bytes is the length of the txt information 14 | // Next is the txt information. One value has two integers which are the differ from the beginning of content and the length 15 | // Next is the binary content 16 | // The beginning of the binary is 40+length of txt 17 | public: 18 | int parse(const std::string& str) 19 | { 20 | if (str.size() > head_size_ + sizeof(uint64_t) && str.substr(0, 11) == head_) 21 | { 22 | uint64_t size_ini = 0; 23 | if (str.size() >= sizeof(uint64_t)) 24 | { 25 | size_ini = *(uint64_t*)(str.data() + head_size_); 26 | } 27 | uint64_t begin = head_size_ + sizeof(uint64_t) + size_ini; 28 | INIReaderNormal assist; 29 | if (str.size() < begin) 30 | { 31 | return -1; 32 | } 33 | assist.loadString(str.substr(head_size_ + sizeof(uint64_t), size_ini)); 34 | for (auto& section : assist.getAllSections()) 35 | { 36 | for (auto& key : assist.getAllKeys(section)) 37 | { 38 | auto vec = assist.getVector(section, key); 39 | if (vec.size() == 2) 40 | { 41 | if (begin + vec[0] + vec[1] > str.size()) 42 | { 43 | return -1; 44 | } 45 | ini_.setKey(section, key, str.substr(begin + vec[0], vec[1])); 46 | } 47 | } 48 | } 49 | return 0; 50 | } 51 | return -1; 52 | } 53 | 54 | // The result may be not a text string. 55 | std::string to_string() 56 | { 57 | std::string str_content; 58 | INIReaderNormal assist; 59 | for (auto& section : ini_.getAllSections()) 60 | { 61 | for (auto& key : ini_.getAllKeys(section)) 62 | { 63 | std::string str1 = ini_.getString(section, key); 64 | assist.setKey(section, key, std::to_string(str_content.size()) + "," + std::to_string(str1.size())); 65 | str_content += str1; 66 | } 67 | } 68 | auto str_ini = assist.toString(); 69 | uint64_t l = str_ini.size(); 70 | std::string result = head_; 71 | result.resize(head_size_ + sizeof(uint64_t)); 72 | memcpy(&result[head_size_], &l, sizeof(uint64_t)); 73 | result = result + str_ini + str_content; 74 | return result; 75 | } 76 | 77 | int save(const std::string& filename) 78 | { 79 | return filefunc::writeStringToFile(to_string(), filename); 80 | } 81 | 82 | int load(const std::string& filename) 83 | { 84 | return parse(filefunc::readFileToString(filename)); 85 | } 86 | 87 | std::string get_value(const std::string& key) 88 | { 89 | return ini_.getString("", key); 90 | } 91 | 92 | void set_value(const std::string& key, const std::string& value) 93 | { 94 | ini_.setKey("", key, value); 95 | } 96 | }; 97 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2024, SunTY 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /PotConv.cpp: -------------------------------------------------------------------------------- 1 | #include "PotConv.h" 2 | 3 | PotConv::PotConv() 4 | { 5 | } 6 | 7 | PotConv::~PotConv() 8 | { 9 | for (auto& cd : cds_) 10 | { 11 | iconv_close(cd.second); 12 | } 13 | } 14 | 15 | std::string PotConv::conv(const std::string& src, const char* from, const char* to) 16 | { 17 | //const char *from_charset, const char *to_charset, const char *inbuf, size_t inlen, char *outbuf; 18 | iconv_t cd = createcd(from, to); 19 | if (cd == nullptr) 20 | { 21 | return ""; 22 | } 23 | size_t inlen = src.length(); 24 | size_t outlen = src.length() * 2; 25 | auto in = new char[inlen + 1]; 26 | auto out = new char[outlen + 1]; 27 | memset(in, 0, inlen + 1); 28 | memcpy(in, src.c_str(), inlen); 29 | memset(out, 0, outlen + 1); 30 | char *pin = in, *pout = out; 31 | if (iconv(cd, &pin, &inlen, &pout, &outlen) == -1) 32 | { 33 | out[0] = '\0'; 34 | } 35 | std::string result(out, src.length() * 2 - outlen); 36 | delete[] in; 37 | delete[] out; 38 | return result; 39 | } 40 | 41 | std::string PotConv::conv(const std::string& src, const std::string& from, const std::string& to) 42 | { 43 | return conv(src, from.c_str(), to.c_str()); 44 | } 45 | 46 | std::string PotConv::to_read(const std::string& src) 47 | { 48 | #ifdef _WIN32 49 | return conv(src, "utf-8", "cp936"); 50 | #else 51 | return src; 52 | #endif 53 | } 54 | 55 | PotConv PotConv::potconv_; 56 | 57 | iconv_t PotConv::createcd(const char* from, const char* to) 58 | { 59 | std::string cds = std::string(from) + std::string(to); 60 | if (potconv_.cds_.count(cds) == 0) 61 | { 62 | iconv_t cd; 63 | cd = iconv_open(to, from); 64 | potconv_.cds_[cds] = cd; 65 | return cd; 66 | } 67 | else 68 | { 69 | return potconv_.cds_[cds]; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /PotConv.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "iconv.h" 4 | #include 5 | #include 6 | #include 7 | 8 | class PotConv 9 | { 10 | public: 11 | PotConv(); 12 | virtual ~PotConv(); 13 | 14 | static std::string conv(const std::string& src, const char* from, const char* to); 15 | static std::string conv(const std::string& src, const std::string& from, const std::string& to); 16 | static std::string cp936toutf8(const std::string& src) { return conv(src, "cp936", "utf-8"); } 17 | static std::string cp950toutf8(const std::string& src) { return conv(src, "cp950", "utf-8"); } 18 | static std::string cp950tocp936(const std::string& src) { return conv(src, "cp950", "cp936"); } 19 | static std::string utf8tocp936(const std::string& src) { return conv(src, "utf-8", "cp936"); } 20 | static void fromCP950ToCP936(const char* s0, char* s1) 21 | { 22 | auto str = PotConv::cp950tocp936(s0); 23 | memcpy(s1, str.data(), str.length()); 24 | } 25 | static void fromCP950ToUTF8(const char* s0, char* s1) 26 | { 27 | auto str = PotConv::cp950toutf8(s0); 28 | memcpy(s1, str.data(), str.length()); 29 | } 30 | static void fromCP936ToUTF8(const char* s0, char* s1) 31 | { 32 | auto str = PotConv::cp936toutf8(s0); 33 | memcpy(s1, str.data(), str.length()); 34 | } 35 | 36 | static std::wstring toWide(const std::string& src, const char* from) 37 | { 38 | auto str = PotConv::conv(src, from, "utf-16le"); 39 | std::wstring ws; 40 | ws.resize(str.size() / 2); 41 | memcpy(&ws[0], str.data(),str.size()/2*2); 42 | return ws; 43 | } 44 | static std::string to_read(const std::string& src); 45 | 46 | private: 47 | std::map cds_; 48 | static PotConv potconv_; 49 | static iconv_t createcd(const char* from, const char* to); 50 | }; 51 | -------------------------------------------------------------------------------- /Random.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | enum RandomType 5 | { 6 | RANDOM_UNIFORM = 0, 7 | RANDOM_NORMAL = 1, 8 | }; 9 | 10 | template 11 | class Random 12 | { 13 | protected: 14 | RandomType type_ = RANDOM_UNIFORM; 15 | std::random_device device_; 16 | std::mt19937 generator_; 17 | std::uniform_real_distribution uniform_dist_{ 0, 1 }; 18 | std::normal_distribution normal_dist_{ 0, 1 }; 19 | std::minstd_rand0 generator_fast_; 20 | 21 | public: 22 | Random() { set_seed(); } 23 | 24 | void set_random_type(RandomType t) { type_ = t; } 25 | 26 | void set_parameter(T a, T b) 27 | { 28 | uniform_dist_.param(decltype(uniform_dist_.param())(a, b)); 29 | normal_dist_.param(decltype(normal_dist_.param())(a, b)); 30 | } 31 | 32 | T rand() 33 | { 34 | if (type_ == RANDOM_UNIFORM) 35 | { 36 | return uniform_dist_(generator_); 37 | } 38 | else if (type_ == RANDOM_NORMAL) 39 | { 40 | return normal_dist_(generator_); 41 | } 42 | return 0; 43 | } 44 | 45 | void set_seed() 46 | { 47 | generator_ = std::mt19937(device_()); 48 | } 49 | 50 | void set_seed(unsigned int seed) 51 | { 52 | generator_ = std::mt19937(seed); 53 | } 54 | 55 | int rand_int(int n) 56 | { 57 | return int(rand() * n); 58 | } 59 | 60 | int rand_int(int n1, int n2) 61 | { 62 | return n1 + int(rand() * (n2 - n1)); 63 | } 64 | 65 | std::mt19937& get_generator() 66 | { 67 | return generator_; 68 | } 69 | 70 | void rand_data(T* data, size_t size) 71 | { 72 | for (int i = 0; i < size; i++) 73 | { 74 | data[i] = rand(); 75 | } 76 | } 77 | }; 78 | 79 | using RandomDouble = Random; //use this in usual 80 | using RandomFloat = Random; //use this in usual 81 | -------------------------------------------------------------------------------- /SimpleBuffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | //replace vector when you only need a buffer 6 | template 7 | struct SimpleBuffer 8 | { 9 | SimpleBuffer() {} 10 | SimpleBuffer(size_t size) { resize(size); } 11 | SimpleBuffer(void* p) 12 | { 13 | shared = 1; 14 | data_ = p; 15 | } 16 | ~SimpleBuffer() { clear(); } 17 | void resize(size_t n) 18 | { 19 | if (shared) 20 | { 21 | size_ = n; 22 | return; 23 | } 24 | if (n > capacity_) 25 | { 26 | clear(); 27 | data_ = new T[n]; 28 | size_ = n; 29 | capacity_ = n; 30 | } 31 | else 32 | { 33 | size_ = n; 34 | } 35 | } 36 | void clear() 37 | { 38 | if (shared) 39 | { 40 | data_ = nullptr; 41 | return; 42 | } 43 | delete[] data_; 44 | data_ = nullptr; 45 | size_ = 0; 46 | capacity_ = 0; 47 | } 48 | T* data() { return data_; } 49 | size_t size() { return size_; } 50 | T& operator[](const size_t i) { return data_[i]; } 51 | void set_pointer(void* p) 52 | { 53 | clear(); 54 | shared = 1; 55 | data_ = p; 56 | } 57 | 58 | private: 59 | T* data_{}; 60 | size_t size_{}; 61 | size_t capacity_{}; 62 | int shared{}; 63 | }; 64 | -------------------------------------------------------------------------------- /Timer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #ifdef _WIN32 7 | #define localtime _localtime64 8 | #endif 9 | 10 | class Timer 11 | { 12 | private: 13 | std::chrono::time_point t0_, t1_, t_last_; 14 | bool running_ = false; 15 | 16 | public: 17 | Timer() { start(); } 18 | 19 | // Returns current time as a string 20 | static std::string getNowAsString(const std::string& format = "%F %a %H:%M:%S") 21 | { 22 | //#ifdef __cpp_lib_format 23 | // auto t = std::chrono::time_point_cast(std::chrono::system_clock::now()); 24 | // auto t1 = std::chrono::current_zone()->to_local(t); 25 | // auto fmt = "{:" + format + "}"; 26 | // return std::vformat(fmt, std::make_format_args(t1)); 27 | //#else 28 | auto t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); 29 | auto tm = *localtime(&t); 30 | char buffer[64] = {}; 31 | strftime(buffer, sizeof(buffer), format.c_str(), &tm); 32 | return std::string(buffer); 33 | //#endif 34 | } 35 | 36 | void start() 37 | { 38 | running_ = true; 39 | t0_ = std::chrono::system_clock::now(); 40 | t_last_ = t0_; 41 | } 42 | 43 | void stop() 44 | { 45 | running_ = false; 46 | t1_ = std::chrono::system_clock::now(); 47 | } 48 | 49 | double getElapsedTime(bool restart = false) 50 | { 51 | if (running_) 52 | { 53 | t1_ = std::chrono::system_clock::now(); 54 | } 55 | auto s = std::chrono::duration_cast>(t1_ - t0_); 56 | if (restart) 57 | { 58 | t0_ = std::chrono::system_clock::now(); 59 | } 60 | return s.count(); 61 | } 62 | 63 | double getLastPeriod() 64 | { 65 | return getElapsedTime(true); 66 | } 67 | 68 | static std::string autoFormatTime(double s) 69 | { 70 | const int size = 80; 71 | char buffer[size]; 72 | int h = s / 3600; 73 | int m = (s - h * 3600) / 60; 74 | s = s - h * 3600 - m * 60; 75 | if (h > 0) 76 | { 77 | snprintf(buffer, size, "%d:%02d:%05.2f", h, m, s); 78 | } 79 | else if (m > 0) 80 | { 81 | snprintf(buffer, size, "%d:%05.2f", m, s); 82 | } 83 | else 84 | { 85 | snprintf(buffer, size, "%.2f s", s); 86 | } 87 | return std::string(buffer); 88 | } 89 | 90 | static std::string formatTime(double s, const std::string& format_str = "%d:%02d:%05.2f") 91 | { 92 | const int size = 80; 93 | char buffer[size]; 94 | int h = s / 3600; 95 | int m = (s - h * 3600) / 60; 96 | s = s - h * 3600 - m * 60; 97 | snprintf(buffer, size, format_str.c_str(), h, m, s); 98 | return std::string(buffer); 99 | } 100 | 101 | static int64_t getNanoTime() 102 | { 103 | return std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); 104 | } 105 | 106 | // return the time within two callings of getLastPeriod2, no restart 107 | double getLastPeriod2() 108 | { 109 | if (running_) 110 | { 111 | t1_ = std::chrono::system_clock::now(); 112 | } 113 | auto s = std::chrono::duration_cast>(t1_ - t_last_); 114 | t_last_ = t1_; 115 | return s.count(); 116 | } 117 | }; -------------------------------------------------------------------------------- /cifa/readme.md: -------------------------------------------------------------------------------- 1 | # Cifa 2 | 3 | ## 简介 4 | 5 | 一个简易类C语法脚本。语法是C的子集,有少量C++风格的扩展。 6 | 7 | 用于嵌入一些只需要简单计算,且不想引入较复杂的外部库的C++程序。 8 | 9 | 例如某些情况下,需要在外部配置文件中执行一个简单的C++过程,且希望程序内部的代码可以不修改直接放到配置文件。 10 | 11 | 项目名就是“词法”。实际上本垃圾一开始理解错了,以为词法分析要生成语法树,就这样吧。 12 | 13 | 执行模式为直接对语法树求值,并处理分支和循环。 14 | 15 | 千万不要用它完成过于复杂的任务,正如描述所说,这只是一个非常简单的语法解析器和执行器。如果你想做更复杂的事,请选用Python或者Lua。如果你喜欢C风格的语法,请选用AngelScript或者ChaiScript,但是这两个库都不小,而且在很多Linux发行版上没有预编译包可用。 16 | 17 | ## 使用方法 18 | 19 | ### 基本用法 20 | 21 | 在自己的工程中加入Cifa.h和Cifa.cpp,例程如下: 22 | 23 | ```c++ 24 | #include "Cifa.h" 25 | #include 26 | #include 27 | 28 | using namespace cifa; 29 | 30 | int main() 31 | { 32 | Cifa c1; 33 | std::ifstream ifs; 34 | ifs.open("1.c"); 35 | std::string str; 36 | getline(ifs, str, '\0'); 37 | auto o = c1.run_script(str); 38 | if(o.hasValue() && o.isNumber() && o.isType()) 39 | { 40 | std::cout << "Cifa value is: " << o.ref() << "\n"; 41 | } 42 | } 43 | ``` 44 | 45 | 其中1.c文件即为脚本内容,一个例子为: 46 | 47 | ```c++ 48 | int sum = 1; 49 | for (int i = 1; i <= 10; i++) 50 | { 51 | for (int j = 1; j <= 10; j++) 52 | { 53 | int x = 0; 54 | if ((i + j) % 2 == 0) 55 | { 56 | x = -1; 57 | } 58 | else 59 | x = 1; 60 | sum += (i * j) * x; 61 | } 62 | } 63 | return sum; 64 | ``` 65 | 66 | 计算的结果为-24。 67 | 68 | 若脚本只有一个表达式,则结果就是表达式的求值。若脚本包含多行,则需用return来指定返回值,否则返回值是nan。 69 | 70 | ### 宿主程序添加自定义函数 71 | 72 | 自定义函数必须可以转化为`std::function`,其中`cifa::ObjectVector`即`std::vector`。 73 | 74 | #### 方式一 75 | 以下自定义3个数学函数(省略了检测越界): 76 | ```c++ 77 | using namespace cifa; 78 | 79 | Object sin1(ObjectVector& d) { return sin(d[0]); } 80 | Object cos1(ObjectVector& d) { return cos(d[0]); } 81 | Object pow1(ObjectVector& d) { return pow(d[0].value, d[1].value); } 82 | 83 | int main() 84 | { 85 | Cifa c1; 86 | c1.register_function("sin", sin1); 87 | c1.register_function("cos", cos1); 88 | c1.register_function("pow", pow1); 89 | //.... 90 | } 91 | ``` 92 | 这里函数原型写成了xxx1的形式,只是为了避免与cmath中的数学函数同名,造成一些麻烦。 93 | 94 | #### 方式二 95 | 其实更推荐lambda表达式的形式,例如将上面正弦函数的注册修改为: 96 | 97 | ```c++ 98 | c1.register_function("sin", [](ObjectVector& d) { return sin(d[0]); }); 99 | ``` 100 | 这样也不必再定义sin1这个函数。 101 | 102 | 此时再运行如下脚本: 103 | ```c++ 104 | auto pi = 3.1415927; 105 | print(sin(pi / 6)); 106 | print(cos(pi / 6)); 107 | print(pow(2, 10)); 108 | ``` 109 | 输出应是: 110 | ``` 111 | 0.5 112 | 0.866025 113 | 1024 114 | ``` 115 | 需注意语言已经内置了3个函数:print,to_number和to_string。 116 | 117 | ### 脚本中的自定义函数 118 | 119 | 例如脚本为: 120 | 121 | ```c++ 122 | myfun(i) 123 | { 124 | return i*i*i+i*i+i+1; 125 | } 126 | 127 | print(myfun(3)); 128 | ``` 129 | 可以得到输出为40。 130 | 131 | 注意这种函数不能做类型检查。 132 | 133 | ### 预定义变量 134 | 135 | 通过预定义变量可以模拟一些外置函数的效果。下面这个例子中,将pi预先定义好,并将degree视作一个C++送到Cifa的参数: 136 | ```c++ 137 | c1.register_parameter("degree", 30); 138 | c1.register_parameter("pi", 3.14159265358979323846); 139 | ``` 140 | 脚本为: 141 | ```c++ 142 | print(sin(degree*pi/180)); 143 | ``` 144 | 输出应为0.5。 145 | 146 | ### 用户的数据类型 147 | 148 | 一般来说,Cifa可以容纳任何类型。目前数值相关的类型实际都是double,另内置了std::string的支持。 149 | 150 | 如果用户希望使用自己的类型,一般来说需要增加一些功能函数,和对应的运算符重载。 151 | 152 | 例如,增加以下几个函数支持OpenCV中cv::Mat相关的一些功能: 153 | 154 | ```c++ 155 | c.register_function("imread", [](cifa::ObjectVector& v) -> cifa::Object 156 | { 157 | int flag = -1; 158 | if (v.size() >= 2) 159 | { 160 | flag = int(v[1]); 161 | } 162 | return cv::Mat(cv::imread(v[0].toString(), flag)); 163 | }); 164 | c.register_function("imshow", [](cifa::ObjectVector& v) -> cifa::Object 165 | { 166 | cv::imshow(v[0].toString(), v[1].to()); 167 | return cifa::Object(); 168 | }); 169 | c.register_function("imwrite", [](cifa::ObjectVector& v) -> cifa::Object 170 | { 171 | cv::imwrite(v[0].toString(), v[1].to()); 172 | return cifa::Object(); 173 | }); 174 | ``` 175 | 除此之外,也可以支持用户自定义某些运算符的重载,但是需注意应进行类型检查。下面以增加加号和减号的重载为例: 176 | ```c++ 177 | c.user_add.push_back([](const cifa::Object& l, const cifa::Object& r) -> cifa::Object 178 | { 179 | if (l.isType() && r.isType()) 180 | { 181 | return cv::Mat(l.to() + r.to()); 182 | } 183 | return cifa::Object(); 184 | }); 185 | c.user_sub.push_back([](const cifa::Object& l, const cifa::Object& r) -> cifa::Object 186 | { 187 | if (l.isType() && r.isType()) 188 | { 189 | return cv::Mat(l.to() - r.to()); 190 | } 191 | return cifa::Object(); 192 | }); 193 | ``` 194 | 195 | 196 | ### 语法上的注意事项 197 | 198 | - auto、int、float、double等类型名会被忽略,若想无脑复制可以在C++部分使用auto。 199 | - 未经初始化即出现在赋值号右侧的变量值为nan,相当于强制要求显式初始化。 200 | - 函数调用时,a.func(c)等价于func(a, c)。 201 | - 自加算符不支持++++或----这种写法,请不要瞎折腾。 202 | - 没有goto。 203 | 204 | ### 一个完整的用例 205 | 206 | ```c++ 207 | cifa::Cifa cifa; 208 | std::string str1 = "a1 = 2;\na3=a1;\nreturn 5+4*9*(a1+3)/23;"; 209 | auto c = cifa.run_script(str1); 210 | if (cifa.has_error()) //检查语法错误 211 | { 212 | //可以选择输出语法错误 213 | //一般错误也会在stderr输出 214 | auto errors = cifa.get_errors(); 215 | for (auto e : errors) 216 | { 217 | std::print("error({}, {}): {}\n", e.line, e.col, e.message); 218 | } 219 | } 220 | //无语法错误,判断结果是否是一个数值 221 | if (c.isNumber()) 222 | { 223 | std::print("{}\n", c.toDouble()); 224 | } 225 | //若需正常继续计算,需要排除nan和inf 226 | if (c.isEffectNumber()) 227 | { 228 | //do something 229 | } 230 | ``` 231 | 232 | 233 | ## 其他 234 | 235 | ### 已知问题 236 | 237 | - 生成语法树时的检查不太严格,例如if和while后面的条件其实可以不写括号,但是最好要写全(此处若严格处理需要将括号多归约一层,略微影响效率)。 238 | - 报错位置有时不太准确。例如括号被归约后,在语法树上就不再存在,所以会相差一个或多个括号。要解决需要在归约时记录最终位置,比较复杂且意义不是很大,不再处理。 239 | 240 | ### 有可能会加的 241 | 242 | - 变量的括号初始化。 243 | - 遍历最终语法树可以生成执行码,不再处理。 244 | -------------------------------------------------------------------------------- /cmdline.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2009, Hideyuki Tanaka 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of the nor the 13 | names of its contributors may be used to endorse or promote products 14 | derived from this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY ''AS IS'' AND ANY 17 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #pragma once 29 | 30 | //#define USE_DEMANGLING 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #ifdef USE_DEMANGLING 42 | #include 43 | #endif 44 | #include 45 | 46 | namespace cmdline{ 47 | 48 | namespace detail{ 49 | 50 | template 51 | class lexical_cast_t{ 52 | public: 53 | static Target cast(const Source &arg){ 54 | Target ret; 55 | std::stringstream ss; 56 | if (!(ss<>ret && ss.eof())) 57 | throw std::bad_cast(); 58 | 59 | return ret; 60 | } 61 | }; 62 | 63 | template 64 | class lexical_cast_t{ 65 | public: 66 | static Target cast(const Source &arg){ 67 | return arg; 68 | } 69 | }; 70 | 71 | template 72 | class lexical_cast_t{ 73 | public: 74 | static std::string cast(const Source &arg){ 75 | std::ostringstream ss; 76 | ss< 82 | class lexical_cast_t{ 83 | public: 84 | static Target cast(const std::string &arg){ 85 | Target ret; 86 | std::istringstream ss(arg); 87 | if (!(ss>>ret && ss.eof())) 88 | throw std::bad_cast(); 89 | return ret; 90 | } 91 | }; 92 | 93 | template 94 | struct is_same { 95 | static const bool value = false; 96 | }; 97 | 98 | template 99 | struct is_same{ 100 | static const bool value = true; 101 | }; 102 | 103 | template 104 | Target lexical_cast(const Source &arg) 105 | { 106 | return lexical_cast_t::value>::cast(arg); 107 | } 108 | 109 | static inline std::string demangle(const std::string &name) 110 | { 111 | #ifdef USE_DEMANGLING 112 | int status=0; 113 | const char *p=::__cxa_demangle(name.c_str(), 0, 0, &status); 114 | std::string ret(p); 115 | free(p); 116 | return ret; 117 | #else 118 | return name; 119 | #endif 120 | } 121 | 122 | template 123 | std::string readable_typename() 124 | { 125 | return demangle(typeid(T).name()); 126 | } 127 | 128 | template 129 | std::string default_value(T def) 130 | { 131 | return detail::lexical_cast(def); 132 | } 133 | 134 | template <> 135 | inline std::string readable_typename() 136 | { 137 | return "string"; 138 | } 139 | 140 | } // detail 141 | 142 | //----- 143 | 144 | class cmdline_error : public std::exception { 145 | public: 146 | cmdline_error(const std::string &msg): msg(msg){} 147 | ~cmdline_error() throw() {} 148 | const char *what() const throw() { return msg.c_str(); } 149 | private: 150 | std::string msg; 151 | }; 152 | 153 | template 154 | struct default_reader{ 155 | T operator()(const std::string &str){ 156 | return detail::lexical_cast(str); 157 | } 158 | }; 159 | 160 | template 161 | struct range_reader{ 162 | range_reader(const T &low, const T &high): low(low), high(high) {} 163 | T operator()(const std::string &s) const { 164 | T ret=default_reader()(s); 165 | if (!(ret>=low && ret<=high)) throw cmdline::cmdline_error("range_error"); 166 | return ret; 167 | } 168 | private: 169 | T low, high; 170 | }; 171 | 172 | template 173 | range_reader range(const T &low, const T &high) 174 | { 175 | return range_reader(low, high); 176 | } 177 | 178 | template 179 | struct oneof_reader{ 180 | T operator()(const std::string &s){ 181 | T ret=default_reader()(s); 182 | if (std::find(alt.begin(), alt.end(), ret)==alt.end()) 183 | throw cmdline_error(""); 184 | return ret; 185 | } 186 | void add(const T &v){ alt.push_back(v); } 187 | private: 188 | std::vector alt; 189 | }; 190 | 191 | template 192 | oneof_reader oneof(T a1) 193 | { 194 | oneof_reader ret; 195 | ret.add(a1); 196 | return ret; 197 | } 198 | 199 | template 200 | oneof_reader oneof(T a1, T a2) 201 | { 202 | oneof_reader ret; 203 | ret.add(a1); 204 | ret.add(a2); 205 | return ret; 206 | } 207 | 208 | template 209 | oneof_reader oneof(T a1, T a2, T a3) 210 | { 211 | oneof_reader ret; 212 | ret.add(a1); 213 | ret.add(a2); 214 | ret.add(a3); 215 | return ret; 216 | } 217 | 218 | template 219 | oneof_reader oneof(T a1, T a2, T a3, T a4) 220 | { 221 | oneof_reader ret; 222 | ret.add(a1); 223 | ret.add(a2); 224 | ret.add(a3); 225 | ret.add(a4); 226 | return ret; 227 | } 228 | 229 | template 230 | oneof_reader oneof(T a1, T a2, T a3, T a4, T a5) 231 | { 232 | oneof_reader ret; 233 | ret.add(a1); 234 | ret.add(a2); 235 | ret.add(a3); 236 | ret.add(a4); 237 | ret.add(a5); 238 | return ret; 239 | } 240 | 241 | template 242 | oneof_reader oneof(T a1, T a2, T a3, T a4, T a5, T a6) 243 | { 244 | oneof_reader ret; 245 | ret.add(a1); 246 | ret.add(a2); 247 | ret.add(a3); 248 | ret.add(a4); 249 | ret.add(a5); 250 | ret.add(a6); 251 | return ret; 252 | } 253 | 254 | template 255 | oneof_reader oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7) 256 | { 257 | oneof_reader ret; 258 | ret.add(a1); 259 | ret.add(a2); 260 | ret.add(a3); 261 | ret.add(a4); 262 | ret.add(a5); 263 | ret.add(a6); 264 | ret.add(a7); 265 | return ret; 266 | } 267 | 268 | template 269 | oneof_reader oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8) 270 | { 271 | oneof_reader ret; 272 | ret.add(a1); 273 | ret.add(a2); 274 | ret.add(a3); 275 | ret.add(a4); 276 | ret.add(a5); 277 | ret.add(a6); 278 | ret.add(a7); 279 | ret.add(a8); 280 | return ret; 281 | } 282 | 283 | template 284 | oneof_reader oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9) 285 | { 286 | oneof_reader ret; 287 | ret.add(a1); 288 | ret.add(a2); 289 | ret.add(a3); 290 | ret.add(a4); 291 | ret.add(a5); 292 | ret.add(a6); 293 | ret.add(a7); 294 | ret.add(a8); 295 | ret.add(a9); 296 | return ret; 297 | } 298 | 299 | template 300 | oneof_reader oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9, T a10) 301 | { 302 | oneof_reader ret; 303 | ret.add(a1); 304 | ret.add(a2); 305 | ret.add(a3); 306 | ret.add(a4); 307 | ret.add(a5); 308 | ret.add(a6); 309 | ret.add(a7); 310 | ret.add(a8); 311 | ret.add(a9); 312 | ret.add(a10); 313 | return ret; 314 | } 315 | 316 | //----- 317 | 318 | class parser{ 319 | public: 320 | parser(){ 321 | } 322 | ~parser(){ 323 | for (std::map::iterator p=options.begin(); 324 | p!=options.end(); p++) 325 | delete p->second; 326 | } 327 | 328 | void add(const std::string &name, 329 | char short_name=0, 330 | const std::string &desc=""){ 331 | if (options.count(name)) throw cmdline_error("multiple definition: "+name); 332 | options[name]=new option_without_value(name, short_name, desc); 333 | ordered.push_back(options[name]); 334 | } 335 | 336 | template 337 | void add(const std::string &name, 338 | char short_name=0, 339 | const std::string &desc="", 340 | bool need=true, 341 | const T def=T()){ 342 | add(name, short_name, desc, need, def, default_reader()); 343 | } 344 | 345 | template 346 | void add(const std::string &name, 347 | char short_name=0, 348 | const std::string &desc="", 349 | bool need=true, 350 | const T def=T(), 351 | F reader=F()){ 352 | if (options.count(name)) throw cmdline_error("multiple definition: "+name); 353 | options[name]=new option_with_value_with_reader(name, short_name, need, def, desc, reader); 354 | ordered.push_back(options[name]); 355 | } 356 | 357 | void footer(const std::string &f){ 358 | ftr=f; 359 | } 360 | 361 | void set_program_name(const std::string &name){ 362 | prog_name=name; 363 | } 364 | 365 | bool exist(const std::string &name) const { 366 | if (options.count(name)==0) throw cmdline_error("there is no flag: --"+name); 367 | return options.find(name)->second->has_set(); 368 | } 369 | 370 | template 371 | const T &get(const std::string &name) const { 372 | if (options.count(name)==0) throw cmdline_error("there is no flag: --"+name); 373 | const option_with_value *p=dynamic_cast*>(options.find(name)->second); 374 | if (p==NULL) throw cmdline_error("type mismatch flag '"+name+"'"); 375 | return p->get(); 376 | } 377 | 378 | const std::vector &rest() const { 379 | return others; 380 | } 381 | 382 | bool parse(const std::string &arg){ 383 | std::vector args; 384 | 385 | std::string buf; 386 | bool in_quote=false; 387 | for (std::string::size_type i=0; i=arg.length()){ 402 | errors.push_back("unexpected occurrence of '\\' at end of string"); 403 | return false; 404 | } 405 | } 406 | 407 | buf+=arg[i]; 408 | } 409 | 410 | if (in_quote){ 411 | errors.push_back("quote is not closed"); 412 | return false; 413 | } 414 | 415 | if (buf.length()>0) 416 | args.push_back(buf); 417 | 418 | //for (size_t i=0; i &args){ 425 | int argc=static_cast(args.size()); 426 | std::vector argv(argc); 427 | 428 | for (int i=0; i lookup; 446 | for (std::map::iterator p=options.begin(); 447 | p!=options.end(); p++){ 448 | if (p->first.length()==0) continue; 449 | char initial=p->second->short_name(); 450 | if (initial){ 451 | if (lookup.count(initial)>0){ 452 | lookup[initial]=""; 453 | errors.push_back(std::string("short option '")+initial+"' is ambiguous"); 454 | return false; 455 | } 456 | else lookup[initial]=p->first; 457 | } 458 | } 459 | 460 | for (int i=1; i &args){ 542 | if (!options.count("help")) 543 | add("help", '?', "print this message"); 544 | check(args.size(), parse(args)); 545 | } 546 | 547 | void parse_check(int argc, char *argv[]){ 548 | if (!options.count("help")) 549 | add("help", '?', "print this message"); 550 | check(argc, parse(argc, argv)); 551 | } 552 | 553 | std::string error() const{ 554 | return errors.size()>0?errors[0]:""; 555 | } 556 | 557 | std::string error_full() const{ 558 | std::ostringstream oss; 559 | for (size_t i=0; imust()) 569 | oss<short_description()<<" "; 570 | } 571 | 572 | oss<<"[options] ... "<name().length()); 578 | } 579 | for (size_t i=0; ishort_name()){ 581 | oss<<" -"<short_name()<<", "; 582 | } 583 | else{ 584 | oss<<" "; 585 | } 586 | 587 | oss<<"--"<name(); 588 | for (size_t j=ordered[i]->name().length(); jdescription()<set()){ 615 | errors.push_back("option needs value: --"+name); 616 | return; 617 | } 618 | } 619 | 620 | void set_option(const std::string &name, const std::string &value){ 621 | if (options.count(name)==0){ 622 | errors.push_back("undefined option: --"+name); 623 | return; 624 | } 625 | if (!options[name]->set(value)){ 626 | errors.push_back("option value is invalid: --"+name+"="+value); 627 | return; 628 | } 629 | } 630 | 631 | class option_base{ 632 | public: 633 | virtual ~option_base(){} 634 | 635 | virtual bool has_value() const=0; 636 | virtual bool set()=0; 637 | virtual bool set(const std::string &value)=0; 638 | virtual bool has_set() const=0; 639 | virtual bool valid() const=0; 640 | virtual bool must() const=0; 641 | 642 | virtual const std::string &name() const=0; 643 | virtual char short_name() const=0; 644 | virtual const std::string &description() const=0; 645 | virtual std::string short_description() const=0; 646 | }; 647 | 648 | class option_without_value : public option_base { 649 | public: 650 | option_without_value(const std::string &name, 651 | char short_name, 652 | const std::string &desc) 653 | :nam(name), snam(short_name), desc(desc), has(false){ 654 | } 655 | ~option_without_value(){} 656 | 657 | bool has_value() const { return false; } 658 | 659 | bool set(){ 660 | has=true; 661 | return true; 662 | } 663 | 664 | bool set(const std::string &){ 665 | return false; 666 | } 667 | 668 | bool has_set() const { 669 | return has; 670 | } 671 | 672 | bool valid() const{ 673 | return true; 674 | } 675 | 676 | bool must() const{ 677 | return false; 678 | } 679 | 680 | const std::string &name() const{ 681 | return nam; 682 | } 683 | 684 | char short_name() const{ 685 | return snam; 686 | } 687 | 688 | const std::string &description() const { 689 | return desc; 690 | } 691 | 692 | std::string short_description() const{ 693 | return "--"+nam; 694 | } 695 | 696 | private: 697 | std::string nam; 698 | char snam; 699 | std::string desc; 700 | bool has; 701 | }; 702 | 703 | template 704 | class option_with_value : public option_base { 705 | public: 706 | option_with_value(const std::string &name, 707 | char short_name, 708 | bool need, 709 | const T &def, 710 | const std::string &desc) 711 | : nam(name), snam(short_name), need(need), has(false) 712 | , def(def), actual(def) { 713 | this->desc=full_description(desc); 714 | } 715 | ~option_with_value(){} 716 | 717 | const T &get() const { 718 | return actual; 719 | } 720 | 721 | bool has_value() const { return true; } 722 | 723 | bool set(){ 724 | return false; 725 | } 726 | 727 | bool set(const std::string &value){ 728 | try{ 729 | actual=read(value); 730 | has=true; 731 | } 732 | catch(const std::exception &e){ 733 | return false; 734 | } 735 | return true; 736 | } 737 | 738 | bool has_set() const{ 739 | return has; 740 | } 741 | 742 | bool valid() const{ 743 | if (need && !has) return false; 744 | return true; 745 | } 746 | 747 | bool must() const{ 748 | return need; 749 | } 750 | 751 | const std::string &name() const{ 752 | return nam; 753 | } 754 | 755 | char short_name() const{ 756 | return snam; 757 | } 758 | 759 | const std::string &description() const { 760 | return desc; 761 | } 762 | 763 | std::string short_description() const{ 764 | return "--"+nam+"="+detail::readable_typename(); 765 | } 766 | 767 | protected: 768 | std::string full_description(const std::string &desc){ 769 | return 770 | desc+" ("+detail::readable_typename()+ 771 | (need?"":" [="+detail::default_value(def)+"]") 772 | +")"; 773 | } 774 | 775 | virtual T read(const std::string &s)=0; 776 | 777 | std::string nam; 778 | char snam; 779 | bool need; 780 | std::string desc; 781 | 782 | bool has; 783 | T def; 784 | T actual; 785 | }; 786 | 787 | template 788 | class option_with_value_with_reader : public option_with_value { 789 | public: 790 | option_with_value_with_reader(const std::string &name, 791 | char short_name, 792 | bool need, 793 | const T def, 794 | const std::string &desc, 795 | F reader) 796 | : option_with_value(name, short_name, need, def, desc), reader(reader){ 797 | } 798 | 799 | private: 800 | T read(const std::string &s){ 801 | return reader(s); 802 | } 803 | 804 | F reader; 805 | }; 806 | 807 | std::map options; 808 | std::vector ordered; 809 | std::string ftr; 810 | 811 | std::string prog_name; 812 | std::vector others; 813 | 814 | std::vector errors; 815 | }; 816 | 817 | } // cmdline 818 | -------------------------------------------------------------------------------- /filefunc.cpp: -------------------------------------------------------------------------------- 1 | #include "filefunc.h" 2 | #include 3 | #include 4 | #include 5 | //#include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef _WIN32 11 | #include 12 | // include windows.h before stringapiset.h 13 | #include //for MultiByteToWideChar() and WideCharToMultiByte() 14 | #else 15 | #include "dirent.h" 16 | #ifndef __APPLE__ 17 | //#include 18 | #endif 19 | #include 20 | #include 21 | #define _mkdir(p) mkdir(p, S_IRWXU) 22 | #endif 23 | 24 | #ifdef __GNUC__ 25 | #include 26 | #include 27 | #endif 28 | 29 | namespace 30 | { 31 | #ifdef _MSC_VER 32 | std::wstring str2wstr(const std::string& str) 33 | { 34 | int wlen = MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), nullptr, 0); 35 | std::wstring wstr; 36 | wstr.resize(wlen); 37 | MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), wstr.data(), wlen); 38 | return wstr; 39 | } 40 | #endif 41 | } 42 | 43 | bool filefunc::fileExist(const std::string& name) 44 | { 45 | #ifdef _MSC_VER 46 | return std::filesystem::is_regular_file(str2wstr(name)); 47 | #endif 48 | return std::filesystem::is_regular_file(name); 49 | } 50 | 51 | bool filefunc::pathExist(const std::string& name) 52 | { 53 | #ifdef _MSC_VER 54 | return std::filesystem::is_directory(str2wstr(name)); 55 | #endif 56 | return std::filesystem::is_directory(name); 57 | } 58 | 59 | std::vector filefunc::readFile(const std::string& filename, int length) 60 | { 61 | std::ifstream ifs(filename, std::fstream::binary); 62 | if (!ifs) { return {}; } 63 | if (length <= 0) 64 | { 65 | ifs.seekg(0, std::ios::end); 66 | length = ifs.tellg(); 67 | ifs.seekg(0, std::ios::beg); 68 | } 69 | std::vector buffer(length); 70 | buffer.assign((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); 71 | return buffer; 72 | } 73 | 74 | int filefunc::writeFile(const char* data, int length, const std::string& filename) 75 | { 76 | if (filename.empty()) { return 0; } 77 | std::ofstream ofs(filename, std::fstream::binary); 78 | ofs.write(data, length); 79 | return length; 80 | } 81 | 82 | int filefunc::writeFile(const std::vector& data, const std::string& filename) 83 | { 84 | return writeFile(data.data(), data.size(), filename); 85 | } 86 | 87 | std::string filefunc::readFileToString(const std::string& filename) 88 | { 89 | std::ifstream ifs(filename, std::fstream::binary); 90 | if (!ifs) { return {}; } 91 | std::string str; 92 | str.assign((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); 93 | return str; 94 | } 95 | 96 | int filefunc::writeStringToFile(const std::string& str, const std::string& filename) 97 | { 98 | if (filename.empty()) { return 0; } 99 | std::ofstream ofs(filename, std::fstream::binary); 100 | ofs << str; 101 | return str.size(); 102 | } 103 | 104 | bool filefunc::is_path_char(char c) 105 | { 106 | #ifdef _WIN32 107 | return c == '\\' || c == '/'; 108 | #else 109 | return c == '/'; 110 | #endif 111 | } 112 | 113 | char filefunc::get_path_char() 114 | { 115 | #ifdef _WIN32 116 | return '\\'; 117 | #else 118 | return '/'; 119 | #endif 120 | } 121 | 122 | size_t filefunc::getLastPathCharPos(const std::string& filename, int utf8) 123 | { 124 | #ifndef _WIN32 125 | utf8 = 1; 126 | #endif 127 | size_t pos = std::string::npos; 128 | if (utf8 == 0) 129 | { 130 | //ansi 131 | for (int i = 0; i < filename.size(); i++) 132 | { 133 | if (utf8 == 0 && uint8_t(filename[i]) >= 128) 134 | { 135 | i++; 136 | } 137 | else if (is_path_char(filename[i])) 138 | { 139 | pos = i; 140 | } 141 | } 142 | } 143 | else 144 | { 145 | for (auto i = filename.size(); i--;) 146 | { 147 | if (is_path_char(filename[i])) 148 | { 149 | pos = i; 150 | break; 151 | } 152 | } 153 | } 154 | return pos; 155 | } 156 | 157 | size_t filefunc::getLastEftPathCharPos(const std::string& filename, int utf8) 158 | { 159 | #ifndef _WIN32 160 | utf8 = 1; 161 | #endif 162 | size_t pos = std::string::npos; 163 | //avoid mistakes for continued path char 164 | if (utf8 == 0) 165 | { 166 | //ansi 167 | bool found = false; 168 | size_t pos1 = std::string::npos; 169 | for (int i = 0; i < filename.size(); i++) 170 | { 171 | if (is_path_char(filename[i])) 172 | { 173 | if (!found) 174 | { 175 | pos1 = i; 176 | found = true; 177 | } 178 | } 179 | else 180 | { 181 | pos = pos1; 182 | found = false; 183 | if (uint8_t(filename[i]) >= 128) 184 | { 185 | i++; 186 | } 187 | } 188 | } 189 | } 190 | else 191 | { 192 | bool found_not_path = false; 193 | int found_not_path_count = 0; 194 | for (auto i = filename.size(); i--;) 195 | { 196 | if (is_path_char(filename[i])) 197 | { 198 | found_not_path = false; 199 | } 200 | else 201 | { 202 | if (!found_not_path) 203 | { 204 | found_not_path_count++; 205 | if (found_not_path_count == 2) 206 | { 207 | return i + 1; 208 | } 209 | } 210 | found_not_path = true; 211 | } 212 | } 213 | } 214 | return pos; 215 | } 216 | 217 | std::vector filefunc::getFilesInPath(const std::string& pathname, int recursive /*= 0*/, int include_path /*= 0*/, int absolute_path /*= 0*/) 218 | { 219 | if (!pathname.empty() && !std::filesystem::is_directory(pathname.c_str())) { return {}; } 220 | std::vector ret; 221 | std::filesystem::directory_entry path0; 222 | if (pathname.empty()) 223 | { 224 | path0.assign("."); 225 | } 226 | else 227 | { 228 | path0.assign(pathname); 229 | } 230 | std::function getFiles = [&](std::filesystem::path path) 231 | { 232 | for (auto const& dir_entry : std::filesystem::directory_iterator{ path }) 233 | { 234 | if (std::filesystem::is_directory(dir_entry.path())) 235 | { 236 | if (recursive == 1) 237 | { 238 | getFiles(dir_entry.path()); 239 | } 240 | if (include_path == 1) 241 | { 242 | if (absolute_path) 243 | { 244 | ret.push_back(std::filesystem::absolute(dir_entry.path()).string()); 245 | } 246 | else 247 | { 248 | ret.push_back(std::filesystem::relative(dir_entry.path(), path0).string()); 249 | } 250 | } 251 | } 252 | else 253 | { 254 | if (absolute_path) 255 | { 256 | ret.push_back(std::filesystem::absolute(dir_entry.path()).string()); 257 | } 258 | else 259 | { 260 | ret.push_back(std::filesystem::relative(dir_entry.path(), path0).string()); 261 | } 262 | } 263 | } 264 | }; 265 | getFiles(path0); 266 | return ret; 267 | } 268 | 269 | std::string filefunc::getFileTime(const std::string& filename, const std::string& format) 270 | { 271 | if (!fileExist(filename)) { return ""; } 272 | auto t = std::filesystem::last_write_time(filename); 273 | auto t1 = std::chrono::time_point_cast(t - std::filesystem::file_time_type::clock::now() + std::chrono::system_clock::now()); 274 | auto t2 = std::chrono::system_clock::to_time_t(t1); 275 | char buf[64] = {}; 276 | strftime(buf, sizeof(buf), format.c_str(), localtime(&t2)); 277 | return buf; 278 | } 279 | 280 | void filefunc::changePath(const std::string& path) 281 | { 282 | if (!path.empty()) 283 | { 284 | std::filesystem::current_path(path); 285 | } 286 | } 287 | 288 | std::string filefunc::getCurrentPath() 289 | { 290 | return std::filesystem::current_path().string(); 291 | } 292 | 293 | void filefunc::makePath(const std::string& path) 294 | { 295 | if (!path.empty()) 296 | { 297 | std::filesystem::create_directories(path); 298 | } 299 | } 300 | 301 | void filefunc::copyFile(const std::string& src, const std::string& dst) 302 | { 303 | std::filesystem::copy_file(src, dst, std::filesystem::copy_options::overwrite_existing); 304 | } 305 | 306 | void filefunc::moveFile(const std::string& src, const std::string& dst) 307 | { 308 | std::filesystem::rename(src, dst); 309 | } 310 | 311 | void filefunc::removeFile(const std::string& filename) 312 | { 313 | std::filesystem::remove_all(filename); 314 | } 315 | 316 | void filefunc::removePath(const std::string& path) 317 | { 318 | std::filesystem::remove_all(path); 319 | } 320 | 321 | std::string filefunc::getRelativePath(const std::string& filename, const std::string& basepath) 322 | { 323 | return std::filesystem::relative(filename, basepath).string(); 324 | } 325 | 326 | std::string filefunc::getFileExt(const std::string& filename) 327 | { 328 | auto ext = std::filesystem::path(filename).extension().string(); 329 | if (!ext.empty() && ext[0] == '.') 330 | { 331 | ext = ext.substr(1); 332 | } 333 | return ext; 334 | } 335 | 336 | //find the last point as default, and find the first when mode is 1 337 | std::string filefunc::getFileMainName(const std::string& filename) 338 | { 339 | auto name = std::filesystem::path(filename).parent_path().string(); 340 | if (!name.empty() && !is_path_char(name.back())) 341 | { 342 | name += get_path_char(); 343 | } 344 | return name + std::filesystem::path(filename).stem().string(); 345 | } 346 | 347 | std::string filefunc::getFilenameWithoutPath(const std::string& filename) 348 | { 349 | return std::filesystem::path(filename).filename().string(); 350 | } 351 | 352 | std::string filefunc::getFileMainNameWithoutPath(const std::string& filename) 353 | { 354 | return std::filesystem::path(filename).stem().string(); 355 | } 356 | 357 | std::string filefunc::changeFileExt(const std::string& filename, const std::string& ext) 358 | { 359 | auto e = ext; 360 | if (!e.empty() && e[0] != '.') 361 | { 362 | e = "." + e; 363 | } 364 | return getFileMainName(filename) + e; 365 | } 366 | 367 | std::string filefunc::getParentPath(const std::string& filename, int utf8) 368 | { 369 | if (filename.size() > 0 && is_path_char(filename.back())) 370 | { 371 | return std::filesystem::path(filename).parent_path().parent_path().string(); 372 | } 373 | else 374 | { 375 | return std::filesystem::path(filename).parent_path().string(); 376 | } 377 | } 378 | 379 | std::string filefunc::getFilePath(const std::string& filename, int utf8) 380 | { 381 | return getParentPath(filename, utf8); 382 | } 383 | 384 | std::string filefunc::toLegalFilename(const std::string& filename, int allow_path) 385 | { 386 | std::string f = filename, chars = " *<>?|:"; 387 | if (!allow_path) 388 | { 389 | chars += "/\\"; 390 | } 391 | for (char i = 31; i >= 0; i--) 392 | { 393 | chars += i; 394 | } 395 | size_t pos = 0; 396 | #ifdef _WIN32 397 | if (allow_path && f.size() >= 2 && f[1] == ':') 398 | { 399 | pos = 2; 400 | } 401 | #endif 402 | while ((pos = f.find_first_of(chars, pos)) != std::string::npos) 403 | { 404 | f[pos] = '_'; 405 | } 406 | return f; 407 | } 408 | 409 | std::string filefunc::getAbsolutePath(const std::string& filename) 410 | { 411 | return std::filesystem::absolute(filename).string(); 412 | } 413 | 414 | bool filefunc::compareNature(const std::string& a, const std::string& b) 415 | { 416 | if (a.empty() && b.empty()) { return false; } 417 | if (a.empty()) { return true; } 418 | if (b.empty()) { return false; } 419 | 420 | auto is_digit = [](char c) -> bool 421 | { 422 | return c >= '0' && c <= '9'; 423 | }; 424 | 425 | if (is_digit(a[0]) && !is_digit(b[0])) { return true; } 426 | if (!is_digit(a[0]) && is_digit(b[0])) { return false; } 427 | if (!is_digit(a[0]) && !is_digit(b[0])) 428 | { 429 | if (std::toupper(a[0]) == std::toupper(b[0])) { return compareNature(a.substr(1), b.substr(1)); } 430 | { 431 | return (std::toupper(a[0]) < std::toupper(b[0])); 432 | } 433 | } 434 | 435 | // Both strings begin with digit --> parse both numbers 436 | std::istringstream issa(a); 437 | std::istringstream issb(b); 438 | int ia, ib; 439 | issa >> ia; 440 | issb >> ib; 441 | if (ia != ib) { return ia < ib; } 442 | 443 | // Numbers are the same --> remove numbers and recurse 444 | std::string anew, bnew; 445 | std::getline(issa, anew); 446 | std::getline(issb, bnew); 447 | return (compareNature(anew, bnew)); 448 | } 449 | -------------------------------------------------------------------------------- /filefunc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | namespace filefunc 7 | { 8 | 9 | //read and write file 10 | bool fileExist(const std::string& name); 11 | bool pathExist(const std::string& name); 12 | 13 | std::vector readFile(const std::string& filename, int length = -1); 14 | int writeFile(const char* data, int length, const std::string& filename); 15 | int writeFile(const std::vector& data, const std::string& filename); 16 | 17 | std::string readFileToString(const std::string& filename); 18 | int writeStringToFile(const std::string& str, const std::string& filename); 19 | 20 | template 21 | void readDataToVector(const char* data, int length, std::vector& v, int length_one) 22 | { 23 | int count = length / length_one; 24 | v.resize(count); 25 | for (int i = 0; i < count; i++) 26 | { 27 | memcpy(&v[i], data + length_one * i, length_one); 28 | } 29 | } 30 | 31 | template 32 | void readDataToVector(const char* data, int length, std::vector& v) 33 | { 34 | readDataToVector(data, length, v, sizeof(T)); 35 | } 36 | 37 | template 38 | void readFileToVector(const std::string& filename, std::vector& v) 39 | { 40 | auto buffer = readFile(filename); 41 | readDataToVector(buffer.data(), buffer.size(), v); 42 | } 43 | 44 | template 45 | void writeVectorToData(char* data, int length, std::vector& v, int length_one) 46 | { 47 | int count = length / length_one; 48 | v.resize(count); 49 | for (int i = 0; i < count; i++) 50 | { 51 | memcpy(data + length_one * i, &v[i], length_one); 52 | } 53 | } 54 | 55 | template 56 | int writeVectorToFile(const std::vector& v, const std::string& filename) 57 | { 58 | return writeFile((const char*)v.data(), v.size() * sizeof(T), filename); 59 | } 60 | 61 | //other file operations 62 | 63 | bool is_path_char(char c); 64 | char get_path_char(); 65 | size_t getLastPathCharPos(const std::string& filename, int utf8 = 0); 66 | size_t getLastEftPathCharPos(const std::string& filename, int utf8 = 0); 67 | 68 | std::vector getFilesInPath(const std::string& pathname, int recursive = 0, int include_path = 0, int absolute_path = 0); 69 | std::string getFileTime(const std::string& filename, const std::string& format = "%Y-%m-%d %H:%M:%S"); 70 | void changePath(const std::string& path); 71 | std::string getCurrentPath(); 72 | void makePath(const std::string& path); 73 | void copyFile(const std::string& src, const std::string& dst); 74 | void moveFile(const std::string& src, const std::string& dst); 75 | void removeFile(const std::string& filename); 76 | void removePath(const std::string& path); 77 | std::string getRelativePath(const std::string& filename, const std::string& basepath); 78 | 79 | //functions about file name 80 | std::string getFileExt(const std::string& filename); 81 | std::string getFileMainName(const std::string& filename); 82 | std::string getFilenameWithoutPath(const std::string& filename); 83 | std::string getFileMainNameWithoutPath(const std::string& filename); 84 | std::string changeFileExt(const std::string& filename, const std::string& ext); 85 | std::string getParentPath(const std::string& filename, int utf8 = 0); //utf8 has no effect on non-win32 86 | std::string getFilePath(const std::string& filename, int utf8 = 0); 87 | std::string toLegalFilename(const std::string& filename, int allow_path = 1); 88 | std::string getAbsolutePath(const std::string& filename); 89 | bool compareNature(const std::string& a, const std::string& b); 90 | 91 | } //namespace filefunc 92 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # MLCC Library 2 | 3 | MLCC is a set of C++ libraries. 4 | 5 | MLCC means Multi-layer Ceramic Capacitors. 6 | 7 | All libraries are of standard C++ 11 and of cross platforms. 8 | 9 | ## Cifa 10 | 11 | A simple c-style script, please see [cifa](cifa) for more details. The old develop branch is , now it has been moved here. 12 | 13 | ## INIReader 14 | 15 | INIReader.h 16 | 17 | Read and write ini file. Modified from . The writting of it is very quick. 18 | 19 | ### Read an ini file 20 | 21 | example.ini: 22 | 23 | ```ini 24 | ; last modified 1 April 2001 by John Doe 25 | [owner] 26 | name=John Doe 27 | organization=Acme Widgets Inc. 28 | 29 | [database] 30 | ; use IP address in case network name resolution is not working 31 | server=192.0.2.62 32 | port=143 33 | file="payroll.dat" 34 | ``` 35 | C++ code: 36 | 37 | ```c++ 38 | int main() 39 | { 40 | INIReaderNoUnderline ini; 41 | ini.loadFile("example.ini"); 42 | int port = ini.getInt("database", "port", 0); //port = 143 43 | int port_ = ini.getInt("database", "port_", 0); //port_ = 143 44 | std::string name = ini.getString("owner", "name", ""); //name = Joha Doe 45 | auto file = ini["database"]["file"].toString(); //file = payroll.dat (the quotation mark will be removed) 46 | return 0; 47 | } 48 | ``` 49 | 50 | Please note that here we use a subclass to ignore the underline in the keys. 51 | 52 | ### Modify an ini file 53 | 54 | Use setKey and eraseKey after loading the file, then use saveFile to write results to a file. 55 | 56 | An example: 57 | 58 | ```c++ 59 | int main() 60 | { 61 | INIReaderNormal ini; 62 | ini.loadFile("example.ini"); 63 | ini.setKey("", "head", "nothing"); 64 | ini.setKey("owner", "age", "30"); 65 | ini.eraseKey("database", "port"); 66 | ini["owner"].erase("name"); 67 | ini["account"]["password"] = "***"; 68 | ini.saveFile("example1.ini"); 69 | return 0; 70 | } 71 | ``` 72 | The content of example1.ini after running is: 73 | 74 | ```ini 75 | [] 76 | head = nothing 77 | ; last modified 1 April 2001 by John Doe 78 | [owner] 79 | organization = Acme Widgets Inc. 80 | age = 30 81 | 82 | [database] 83 | ; use IP address in case network name resolution is not working 84 | server = 192.0.2.62 85 | file = payroll.dat 86 | 87 | [account] 88 | password = *** 89 | 90 | ``` 91 | A space will be added at both sides of "=", and the comments will be maintained. 92 | 93 | If the string includes some special charactors, such as ";" and "#" (which are the comment prefix), or line break, please use quote to surround it. Examples: 94 | 95 | ```ini 96 | [sec] 97 | key1 = "cc;dd#l" 98 | key2 = "line1 99 | line2" 100 | ``` 101 | 102 | This library does not support the escape characters or operating the comments. 103 | 104 | If a key has been multi-defined, the last value is reserved. **Please note all the multi-defined keys EXCLUDE the first one with the last value will be ERASED when saving!** But the last comment to it will be kept. 105 | 106 | You can define how to compare the section and key. Such as: 107 | 108 | ```c++ 109 | struct CaseInsensitivity 110 | { 111 | std::string operator()(const std::string& l) const 112 | { 113 | auto l1 = l; 114 | std::transform(l1.begin(), l1.end(), l1.begin(), ::tolower); 115 | return l1; 116 | } 117 | }; 118 | 119 | using INIReaderNormal = INIReader; 120 | 121 | ``` 122 | 123 | Multi level hierarchy is supported, the sub section should be put inisde two or more square brackets to. Example: 124 | 125 | ```c++ 126 | INIReaderNormal ini; 127 | ini["sec0"]["a"] = 1; 128 | ini["sec0"]["sec0_1"]["a"] = 1; 129 | ini["sec1"]["a"] = 1; 130 | ``` 131 | Note that if you use toString(), toInt() or toDouble() of this type, a new key with empty value will be created if it does not exist. 132 | 133 | These C++ code will give an ini file like this: 134 | 135 | ```ini 136 | [sec0] 137 | a=1 138 | [[sec0_1]] 139 | a=1 140 | [sec1] 141 | a=1 142 | ``` 143 | 144 | ### About comment 145 | 146 | If you load one file for many times, the comments will be repeated at the end of their section. So please do not do this. 147 | 148 | ## strfunc 149 | 150 | strfunc.h, strfunc.cpp 151 | 152 | example.txt: 153 | 154 | ``` 155 | abc_def_;dddd. 156 | ``` 157 | 158 | Some examples: 159 | 160 | ```c++ 161 | std::string str = readStringFromFile("example.txt"); //str = "abc_def_;dddd." 162 | replaceAllSubStringRef(str, "_", "."); //str = "abc.def.;dddd." 163 | 164 | str = "123,467,222;44"; 165 | std::vector numbers = findNumbers(str); //numbers = {123, 467, 222, 44}, scientific notation is supported 166 | std::vector strs = splitString(str, ",;"); //strs = {"123", "467", "222", "44"} 167 | ``` 168 | 169 | "splitString" also supports treating continuous spaces as one pattern. 170 | 171 | vectorToString can convert a std::vector to a std::string, it is too bother to deal with the last splitting char manually. 172 | 173 | It also supplies some encoding converters. 174 | 175 | ## Timer 176 | 177 | Timer.h 178 | 179 | A timer. 180 | 181 | ```c++ 182 | Timer t; 183 | // do something... 184 | double elapsed_second = t.getElapsedTime(); //you can check how long the program spent 185 | ``` 186 | 187 | ## Random 188 | 189 | Random.h 190 | 191 | An Mersenne twister random number generator. 192 | 193 | ```c++ 194 | Random rand; 195 | rand.set_type(RANDOM_NORMAL); 196 | double d = rand.rand(); //Gaussian(0, 1) 197 | rand.set_type(RANDOM_UNIFORM); 198 | int i = rand.rand_int(100); //[0, 100) 199 | ``` 200 | 201 | ## DynamicLibrary 202 | 203 | DynamicLibrary.h 204 | 205 | This library is a static class and is of single instance. 206 | 207 | Get the C-style function pointer like this: 208 | 209 | ```c++ 210 | void* func = DynamicLibrary::getFunction("xxx.dll", "xxx"); 211 | ``` 212 | 213 | You have to give the full name of the library include the path. 214 | 215 | The loaded libraries will be unload automatically when the program exits. 216 | 217 | ## ConsoleControl 218 | 219 | ConsoleControl.h 220 | 221 | This library can change the color of the output characters on the console, or change the position of the cursor to format the output. 222 | 223 | ```c++ 224 | ConsoleControl::setColor(CONSOLE_COLOR_LIGHT_RED); //change the color of printf, fprintf... 225 | ConsoleControl::moveUp(2); //move up the cursor for 2 lines 226 | ``` 227 | ## filefunc 228 | 229 | filefunc.h, filefunc.cpp 230 | 231 | This class can read and write file as a vector of any class. 232 | 233 | Some functions are very similar to those of "strfunc". 234 | 235 | This class can also extract path, main name or extension from a filename string, examples: 236 | 237 | ```c++ 238 | std::string filename = R"(C:\Windows\system32\xxx.exe.1)"; 239 | std::string result; 240 | result = filefunc::getParentPath(filename); // C:\Windows\system32 241 | result = filefunc::getFileMainname(filename); // C:\Windows\system32\xxx.exe 242 | result = filefunc::getFilenameWithoutPath(filename); // xxx.exe.1 243 | result = filefunc::changeFileExt(filename, "dll"); // C:\Windows\system32\xxx.exe.dll 244 | ``` 245 | 246 | On Windows, "\\" and "/" are both supported. A mixed style string (such as "C:\Windows\system32/xxx.exe.1") can also be treated correctly. It can treat ANSI string correctly, but for UTF8 string the result may be not right. In fact Windows cannot open a file with a UTF8 string file name directly, so this problem is not serious. 247 | 248 | On Linux and other Unix-like systems, "\\" is not a path pattern, only "/" is effective and it is a single byte character in UTF8 coding, so the result should always be correct. 249 | 250 | # cmdline 251 | 252 | cmdline.h. 253 | 254 | Modified from . Please read the instruction on the original project. 255 | 256 | A bug when parsing a full command line string has been corrected. 257 | 258 | You'd better to use it like this: 259 | 260 | ```c++ 261 | 262 | #ifdef _WIN32 // or _MSC_VER, as you wish 263 | #include 264 | #endif 265 | 266 | int main(int argc, char* argv[]) 267 | { 268 | ... 269 | #ifdef _WIN32 270 | cmd.parse_check(GetCommandLineA()); 271 | #else 272 | cmd.parse_check(argc, argv); 273 | #endif 274 | ... 275 | } 276 | ``` 277 | or a command line mixing backslash and quote cannot be correctly parsed on Windows. For an example: 278 | ```shell 279 | something.exe --path "C:\Windows\system32\" --other-option values 280 | ``` 281 | In this case, "argc" and "argv" in the program are NOT right with CMD, but are right with Power Shell, is it a bug of Windows? 282 | 283 | # fmt1 284 | 285 | A simple substitute of std::format. 286 | 287 | If you cannot stand the neglect of Clang and GCC, maybe you can try it. 288 | 289 | It also provide the formatter of vector and map to use is C++20. 290 | 291 | It has been removed due to the supporting of the mainstream compilers. 292 | 293 | # runtime_format 294 | 295 | A simple runtime format implement, temporary use before C++26 is released. 296 | 297 | # PotConv 298 | 299 | A C++ warp for iconv. 300 | 301 | # StrCvt 302 | 303 | Some practical functions involving string coding, wide characters, and multi-byte characters. 304 | Requires >= C++11 and < C++26. 305 | 306 | ```c++ 307 | std::string strfunc::CvtStringToUTF8(const std::string& localstr); //windows only 308 | std::string strfunc::CvtUTF8ToLocal(const std::string& utf8str); //windows only 309 | 310 | std::string strfunc::CvtStringToUTF8(const char16_t& src); 311 | std::string strfunc::CvtStringToUTF8(const std::u16string& src); 312 | std::string strfunc::CvtStringToUTF8(const wchar_t* start, std::uint64_t len); 313 | std::string strfunc::CvtStringToUTF8(const std::wstring& str); 314 | std::u16string strfunc::CvtStringToUTF16(const std::string& src); 315 | std::u16string strfunc::CvtStringToUTF16(const char* start, int len); 316 | std::wstring strfunc::CvtStringToWString(const std::string& src); 317 | std::wstring strfunc::CvtStringToWString(const char* start, uint64_t len); 318 | 319 | ``` 320 | 321 | # DrawStringFT 322 | 323 | A C++ warp for freetype and opencv Mat. It can write charactors (such as Chinese) on an image. 324 | 325 | # CheckDependency 326 | 327 | Only for Windows. 328 | 329 | To check the dependencies of a exe or dll file. 330 | 331 | The CheckDependency is similar to the data viewed using the command `dumpbin /DEPENDENTS`. 332 | It can help you get the platform (x64 or x86) of each dll that this exe or dll depends on, as well as the functions exported by each dll, the functions of each dll used by this file, and each problem Dlls (missing functions). 333 | 334 | An example: 335 | 336 | ```c++ 337 | CheckDependency dep; 338 | auto problem_dlls = dep.Check("myself.dll"); 339 | std::cout << "Problem Dlls : " << std::endl; 340 | for(auto& item : problem_dlls) 341 | { 342 | std::cout << "(" << item.second.machine.c_str() << ")" 343 | << item.first << ", lost functions: " << item.second.lost_functions.size() 344 | << std::endl; 345 | } 346 | std::cout << "Import Table: " << std::endl; 347 | for(auto& item : dep.ImportTable()) 348 | { 349 | std::cout << "(" << item.second.machine.c_str() << ")" 350 | << item.first << ", used functions: " << item.second.used_functions.size() 351 | << std::endl; 352 | } 353 | std::cout << "Export Table: " << std::endl; 354 | for(auto& item : dep.ExportTable()) 355 | { 356 | std::cout << item.first << ", export functions: " << item.second.size() << std::endl; 357 | } 358 | ``` 359 | 360 | # FunctionTrait 361 | 362 | Check the number of patameters anf the return type of a class member function. 363 | 364 | # FakeJson 365 | 366 | A simllified JSON library. It does not support escape characters. 367 | 368 | # vramusage 369 | 370 | Only for Windows. 371 | 372 | CUDA and HIP supply the apis to get the usage of video memory, but on Windows the result is not right. 373 | 374 | This can help you to get that correctly. 375 | 376 | First, get the LUID or PCI bus with cudaGetDeviceProperties / hipGetDeviceProperties, and get the memory usage of it. 377 | 378 | ## 备注 379 | 380 | 此功能用到了未公开用法的Windows API`D3DKMTQueryStatistics`和结构体`D3DKMT_QUERYSTATISTICS`。 381 | 382 | `D3DKMT_QUERYSTATISTICS`是一个以Union为主的结构,首先需要赋值查询内容和LUID,查询成功之后,Union的其余部分是没有用的。 383 | 384 | 例如以下查询: 385 | 386 | ```c++ 387 | D3DKMT_QUERYSTATISTICS queryStatistics{}; 388 | queryStatistics.Type = D3DKMT_QUERYSTATISTICS_ADAPTER; 389 | queryStatistics.AdapterLuid = luid; 390 | if (D3DKMTQueryStatistics(&queryStatistics)) 391 | { 392 | //printf("D3DKMTQueryStatistics failed with %d\n", ret); 393 | return 1; 394 | } 395 | ``` 396 | 397 | 查询之后, 只有`queryStatistics.QueryResult.AdapterInformation`是有用的。 398 | 399 | 在显存查询的部分,需要把每段的占用加起来得到总的占用。 400 | 401 | # targetlnk 402 | 403 | Get the target of a .lnk file. 404 | 405 | -------------------------------------------------------------------------------- /runtime_format.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace std 7 | { 8 | 9 | struct runtime_format 10 | { 11 | string_view fmt; 12 | }; 13 | 14 | template 15 | concept is_printable = requires { std::formatter>(); }; 16 | 17 | template 18 | std::string format(const runtime_format& rfmt, Args&&... args) 19 | { 20 | return std::vformat(rfmt.fmt, std::make_format_args(args...)); 21 | } 22 | 23 | template 24 | void print(FILE* fout, const runtime_format& rfmt, Args&&... args) 25 | { 26 | std::vprint_nonunicode(fout, rfmt.fmt, std::make_format_args(args...)); 27 | } 28 | 29 | template 30 | void print(const runtime_format& rfmt, Args&&... args) 31 | { 32 | print(stdout, rfmt, std::forward(args)...); 33 | } 34 | } //namespace std 35 | -------------------------------------------------------------------------------- /strcvt.cpp: -------------------------------------------------------------------------------- 1 | #include "strcvt.h" 2 | #include 3 | #include 4 | #include 5 | #ifdef _MSC_VER 6 | #define vsprintf vsprintf_s 7 | //#define fopen fopen_s 8 | #include 9 | // include windows.h before stringapiset.h 10 | #include //for MultiByteToWideChar() and WideCharToMultiByte() 11 | 12 | #endif 13 | 14 | #ifdef _MSC_VER 15 | std::string strcvt::CvtStringToUTF8(const std::string& localstr) 16 | { 17 | int wlen = MultiByteToWideChar(CP_ACP, 0, localstr.c_str(), -1, nullptr, 0); 18 | std::vector wstr(wlen); 19 | MultiByteToWideChar(CP_ACP, 0, localstr.c_str(), -1, wstr.data(), wlen); 20 | int utf8len = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), -1, nullptr, 0, nullptr, nullptr); 21 | std::vector utf8str(utf8len); 22 | WideCharToMultiByte(CP_UTF8, 0, wstr.data(), -1, utf8str.data(), utf8len, nullptr, nullptr); 23 | std::string result(utf8str.data()); 24 | return result; 25 | } 26 | 27 | std::string strcvt::CvtUTF8ToLocal(const std::string& utf8str) 28 | { 29 | int wlen = MultiByteToWideChar(CP_UTF8, 0, utf8str.c_str(), -1, nullptr, 0); 30 | std::vector wstr(wlen); 31 | MultiByteToWideChar(CP_UTF8, 0, utf8str.c_str(), -1, wstr.data(), wlen); 32 | int locallen = WideCharToMultiByte(CP_ACP, 0, wstr.data(), -1, nullptr, 0, nullptr, nullptr); 33 | std::vector localstr(locallen); 34 | WideCharToMultiByte(CP_ACP, 0, wstr.data(), -1, localstr.data(), locallen, nullptr, nullptr); 35 | std::string result(localstr.data()); 36 | return result; 37 | } 38 | 39 | std::wstring strcvt::CvtUTF8ToWChar(const std::string& utf8str, int utf8strlen) 40 | { 41 | int wlen = MultiByteToWideChar(CP_UTF8, 0, utf8str.c_str(), utf8strlen, nullptr, 0); 42 | std::vector wstr(wlen); 43 | MultiByteToWideChar(CP_UTF8, 0, utf8str.c_str(), utf8strlen, wstr.data(), wlen); 44 | std::wstring ret(wstr.data()); 45 | return ret; 46 | } 47 | 48 | std::wstring strcvt::CvtLocalStringToWString(const std::string& string) 49 | { 50 | int wlen = MultiByteToWideChar(CP_ACP, 0, string.c_str(), -1, nullptr, 0); 51 | std::vector wstr(wlen); 52 | MultiByteToWideChar(CP_ACP, 0, string.c_str(), -1, wstr.data(), wlen); 53 | std::wstring ret(wstr.data()); 54 | return ret; 55 | } 56 | 57 | std::string strcvt::CvtWStringToLocalString(const std::wstring& wstring) 58 | { 59 | int locallen = WideCharToMultiByte(CP_ACP, 0, wstring.c_str(), -1, nullptr, 0, nullptr, nullptr); 60 | std::vector localstr(locallen); 61 | WideCharToMultiByte(CP_ACP, 0, wstring.c_str(), -1, localstr.data(), locallen, nullptr, nullptr); 62 | std::string ret(localstr.data()); 63 | return ret; 64 | } 65 | 66 | #endif // _WIN32 67 | 68 | // codecvt在C++26中被弃用,且暂时没有替代方案 69 | // 建议使用PotConv 70 | std::string strcvt::CvtStringToUTF8(const char16_t& src) 71 | { 72 | std::wstring_convert, std::int16_t> convert; 73 | auto p = reinterpret_cast(&src); 74 | return convert.to_bytes(p, p + 1); 75 | } 76 | 77 | std::string strcvt::CvtStringToUTF8(const std::u16string& src) 78 | { 79 | std::wstring_convert, std::int16_t> convert; 80 | auto p = reinterpret_cast(src.data()); 81 | return convert.to_bytes(p, p + src.size()); 82 | } 83 | 84 | std::string strcvt::CvtStringToUTF8(const wchar_t* start, std::uint64_t len) 85 | { 86 | std::wstring_convert, std::int16_t> convert; 87 | auto p = reinterpret_cast(start); 88 | return convert.to_bytes(p, p + len); 89 | } 90 | 91 | std::string strcvt::CvtStringToUTF8(const std::wstring& str) 92 | { 93 | std::wstring_convert> convert; 94 | return convert.to_bytes(str); 95 | } 96 | 97 | std::u16string strcvt::CvtStringToUTF16(const std::string& src) 98 | { 99 | std::wstring_convert, std::int16_t> convert; 100 | auto p = reinterpret_cast(src.data()); 101 | auto str = convert.from_bytes(p, p + src.size()); 102 | return std::u16string(str.begin(), str.end()); 103 | } 104 | 105 | std::u16string strcvt::CvtStringToUTF16(const char* start, int len) 106 | { 107 | std::wstring_convert, std::int16_t> convert; 108 | auto p = reinterpret_cast(start); 109 | auto str = convert.from_bytes(p, p + len); 110 | return std::u16string(str.begin(), str.end()); 111 | } 112 | 113 | std::wstring strcvt::CvtStringToWString(const std::string& src) 114 | { 115 | #ifdef _MSC_VER 116 | return CvtUTF8ToWChar(src, -1); 117 | #else 118 | std::wstring_convert> convert; 119 | auto p = reinterpret_cast(src.data()); 120 | auto str = convert.from_bytes(p, p + src.size()); 121 | return std::wstring(str.begin(), str.end()); 122 | #endif 123 | } 124 | 125 | std::wstring strcvt::CvtStringToWString(const char* start, uint64_t len) 126 | { 127 | #ifdef _MSC_VER 128 | return CvtUTF8ToWChar(start, len); 129 | #else 130 | std::wstring_convert> convert; 131 | auto p = reinterpret_cast(start); 132 | auto str = convert.from_bytes(p, p + len); 133 | return std::wstring(str.begin(), str.end()); 134 | #endif 135 | } 136 | -------------------------------------------------------------------------------- /strcvt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace strcvt 10 | { 11 | // string coding/type convertor functions 12 | #ifdef _MSC_VER 13 | std::string CvtStringToUTF8(const std::string& localstr); 14 | std::string CvtUTF8ToLocal(const std::string& utf8str); 15 | std::wstring CvtUTF8ToWChar(const std::string& utf8str, int utf8strlen = -1); 16 | std::wstring CvtLocalStringToWString(const std::string& string); 17 | std::string CvtWStringToLocalString(const std::wstring& wstring); 18 | #endif 19 | std::string CvtStringToUTF8(const char16_t& src); 20 | std::string CvtStringToUTF8(const std::u16string& src); 21 | std::string CvtStringToUTF8(const wchar_t* start, std::uint64_t len); 22 | std::string CvtStringToUTF8(const std::wstring& str); 23 | std::u16string CvtStringToUTF16(const std::string& src); 24 | std::u16string CvtStringToUTF16(const char* start, int len); 25 | std::wstring CvtStringToWString(const std::string& src); 26 | std::wstring CvtStringToWString(const char* start, std::uint64_t len); 27 | 28 | } //namespace strfunc 29 | -------------------------------------------------------------------------------- /strfunc.cpp: -------------------------------------------------------------------------------- 1 | #include "strfunc.h" 2 | #include 3 | #include 4 | 5 | #ifdef _WIN32 6 | #define popen _popen 7 | #define pclose _pclose 8 | #endif 9 | 10 | void strfunc::replaceOneSubStringRef(std::string& s, const std::string& oldstring, const std::string& newstring, int pos0 /*=0*/) 11 | { 12 | if (oldstring.empty() || oldstring == newstring) 13 | { 14 | return; 15 | } 16 | auto pos = s.find(oldstring, pos0); 17 | if (pos != std::string::npos) 18 | { 19 | s.replace(pos, oldstring.length(), newstring); 20 | } 21 | } 22 | 23 | void strfunc::replaceAllSubStringRef(std::string& s, const std::string& oldstring, const std::string& newstring) 24 | { 25 | if (oldstring.empty() || oldstring == newstring) 26 | { 27 | return; 28 | } 29 | auto pos = s.find(oldstring); 30 | while (pos != std::string::npos) 31 | { 32 | s.replace(pos, oldstring.length(), newstring); 33 | pos = s.find(oldstring, pos + newstring.length()); 34 | } 35 | } 36 | 37 | std::string strfunc::replaceOneSubString(const std::string& s, const std::string& oldstring, const std::string& newstring, int pos0 /*= 0*/) 38 | { 39 | std::string s1 = s; 40 | replaceOneSubStringRef(s1, oldstring, newstring, pos0); 41 | return s1; 42 | } 43 | 44 | std::string strfunc::replaceAllSubString(const std::string& s, const std::string& oldstring, const std::string& newstring) 45 | { 46 | std::string s1 = s; 47 | replaceAllSubStringRef(s1, oldstring, newstring); 48 | return s1; 49 | } 50 | 51 | std::string strfunc::findANumber(const std::string& s) 52 | { 53 | bool findPoint = false; 54 | bool findNumber = false; 55 | bool findE = false; 56 | std::string n; 57 | for (int i = 0; i < s.length(); i++) 58 | { 59 | char c = s[i]; 60 | if ((c >= '0' && c <= '9') || c == '-' || c == '.' || c == 'e' || c == 'E') 61 | { 62 | if ((c >= '0' && c <= '9') || c == '-') 63 | { 64 | findNumber = true; 65 | n += c; 66 | } 67 | if (c == '.') 68 | { 69 | if (!findPoint) 70 | { 71 | n += c; 72 | } 73 | findPoint = true; 74 | } 75 | if (c == 'e' || c == 'E') 76 | { 77 | if (findNumber && !(findE)) 78 | { 79 | n += c; 80 | findE = true; 81 | } 82 | } 83 | } 84 | else 85 | { 86 | if (findNumber) 87 | { 88 | break; 89 | } 90 | } 91 | } 92 | return n; 93 | } 94 | 95 | unsigned strfunc::findTheLast(const std::string& s, const std::string& content) 96 | { 97 | size_t pos = 0, prepos = 0; 98 | while (pos != std::string::npos) 99 | { 100 | prepos = pos; 101 | pos = s.find(content, prepos + 1); 102 | //printf("%d\n",pos); 103 | } 104 | return prepos; 105 | } 106 | 107 | std::vector strfunc::splitString(std::string str, std::string pattern, bool trim_space, bool quote) 108 | { 109 | std::vector result; 110 | if (str.empty()) 111 | { 112 | return result; 113 | } 114 | if (pattern.empty()) 115 | { 116 | pattern = ",;| "; 117 | } 118 | 119 | auto trim = [trim_space](const std::string& s) -> std::string 120 | { 121 | if (trim_space) 122 | { 123 | std::string s1 = s; 124 | s1.erase(0, s1.find_first_not_of(" ")); 125 | s1.erase(s1.find_last_not_of(" ") + 1); 126 | return s1; 127 | } 128 | return s; 129 | }; 130 | 131 | str += pattern[0]; 132 | bool have_space = pattern.find(" ") != std::string::npos; 133 | auto size = str.size(); 134 | 135 | if (quote) 136 | { 137 | bool inquote = false; 138 | char quotechar = '\"'; 139 | std::string::size_type pos = 0; 140 | if (have_space) 141 | { 142 | pos = str.find_first_not_of(" "); 143 | } 144 | for (int i = pos; i < size; i++) 145 | { 146 | if (have_space && !inquote) 147 | { 148 | //当空格作为分隔符时,连续空格视为一个 149 | while (str[i + 1] == ' ') 150 | { 151 | i++; 152 | } 153 | } 154 | if (!inquote && (str[i] == '\"' || str[i] == '\'')) 155 | { 156 | inquote = true; 157 | quotechar = str[i]; 158 | } 159 | else if (inquote && str[i] == quotechar) 160 | { 161 | inquote = false; 162 | } 163 | if (!inquote) 164 | { 165 | if (pattern.find_first_of(str[i]) != std::string::npos) 166 | { 167 | result.push_back(trim(str.substr(pos, i - pos))); 168 | pos = i + 1; 169 | } 170 | } 171 | } 172 | } 173 | else 174 | { 175 | std::string::size_type pos = 0; 176 | for (int i = 0; i < size; i++) 177 | { 178 | if (have_space) 179 | { 180 | //当空格作为分隔符时,连续空格视为一个 181 | while (str[i] == ' ') 182 | { 183 | i++; 184 | } 185 | } 186 | pos = str.find_first_of(pattern, i); 187 | if (pos < size) 188 | { 189 | result.push_back(trim(str.substr(i, pos - i))); 190 | i = pos; 191 | } 192 | } 193 | } 194 | 195 | return result; 196 | } 197 | 198 | bool strfunc::isProChar(char c) 199 | { 200 | return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'z') || (c >= '(' && c <= ')'); 201 | } 202 | 203 | std::string strfunc::toLowerCase(const std::string& s) 204 | { 205 | std::string s1 = s; 206 | std::transform(s1.begin(), s1.end(), s1.begin(), ::tolower); 207 | return s1; 208 | } 209 | 210 | std::string strfunc::toUpperCase(const std::string& s) 211 | { 212 | std::string s1 = s; 213 | std::transform(s1.begin(), s1.end(), s1.begin(), ::toupper); 214 | return s1; 215 | } 216 | 217 | std::string strfunc::ltrim(const std::string& s) 218 | { 219 | size_t start = s.find_first_not_of(" \n\r\t\f\v"); 220 | return (start == std::string::npos) ? "" : s.substr(start); 221 | } 222 | 223 | std::string strfunc::rtrim(const std::string& s) 224 | { 225 | size_t end = s.find_last_not_of(" \n\r\t\f\v"); 226 | return (end == std::string::npos) ? "" : s.substr(0, end + 1); 227 | } 228 | 229 | std::string strfunc::trim(const std::string& s) 230 | { 231 | return rtrim(ltrim(s)); 232 | } 233 | 234 | bool strfunc::meet_utf8(const std::string& str) 235 | { 236 | unsigned int n = 0; 237 | bool all_ascii = true; 238 | for (const unsigned char c : str) 239 | { 240 | if (all_ascii && c < 0x20 || c >= 0x80) 241 | { 242 | all_ascii = false; 243 | } 244 | if (n == 0) 245 | { 246 | //the first of multi byte 247 | if (c >= 0x80) 248 | { 249 | if (c >= 0xFC && c <= 0xFD) 250 | { 251 | n = 6; 252 | } 253 | else if (c >= 0xF8) 254 | { 255 | n = 5; 256 | } 257 | else if (c >= 0xF0) 258 | { 259 | n = 4; 260 | } 261 | else if (c >= 0xE0) 262 | { 263 | n = 3; 264 | } 265 | else if (c >= 0xC0) 266 | { 267 | n = 2; 268 | } 269 | else 270 | { 271 | return false; 272 | } 273 | n--; 274 | } 275 | } 276 | else 277 | { 278 | //it should be 10xxxxxx 279 | if ((c & 0xC0) != 0x80) 280 | { 281 | return false; 282 | } 283 | n--; 284 | } 285 | } 286 | if (n != 0) 287 | { 288 | return false; 289 | } 290 | if (all_ascii) 291 | { 292 | return true; 293 | } 294 | return true; 295 | } 296 | 297 | bool strfunc::meet_gbk(const std::string& str) 298 | { 299 | unsigned int n = 0; 300 | bool all_ascii = true; 301 | for (const unsigned char c : str) 302 | { 303 | if (all_ascii && c < 0x20 || c >= 0x80) 304 | { 305 | all_ascii = false; 306 | } 307 | if (n == 0) 308 | { 309 | if (c >= 0x80) 310 | { 311 | if (c >= 0x81 && c <= 0xFE) 312 | { 313 | n = +2; 314 | } 315 | else 316 | { 317 | return false; 318 | } 319 | n--; 320 | } 321 | } 322 | else 323 | { 324 | if (c < 0x40 || c > 0xFE) 325 | { 326 | return false; 327 | } 328 | n--; 329 | } 330 | } 331 | if (n != 0) 332 | { 333 | return false; 334 | } 335 | if (all_ascii) 336 | { 337 | return true; 338 | } 339 | return true; 340 | } 341 | 342 | std::string strfunc::get_cmd_output(const std::string& cmdstring) 343 | { 344 | std::string str; 345 | FILE* p_file = NULL; 346 | const int BUF_SIZE = 1024; 347 | char buf[BUF_SIZE]; 348 | p_file = popen(cmdstring.c_str(), "r"); 349 | if (!p_file) 350 | { 351 | return ""; 352 | } 353 | while (fgets(buf, BUF_SIZE, p_file) != NULL) 354 | { 355 | str += buf; 356 | } 357 | pclose(p_file); 358 | return str; 359 | } 360 | -------------------------------------------------------------------------------- /strfunc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace strfunc 6 | { 7 | void replaceOneSubStringRef(std::string& s, const std::string& oldstring, const std::string& newstring, int pos0 = 0); 8 | void replaceAllSubStringRef(std::string& s, const std::string& oldstring, const std::string& newstring); 9 | std::string replaceOneSubString(const std::string& s, const std::string& oldstring, const std::string& newstring, int pos0 = 0); 10 | std::string replaceAllSubString(const std::string& s, const std::string& oldstring, const std::string& newstring); 11 | 12 | std::string findANumber(const std::string& s); 13 | unsigned findTheLast(const std::string& s, const std::string& content); 14 | std::vector splitString(std::string str, std::string pattern = "", bool trim_space = true, bool quote = false); 15 | bool isProChar(char c); 16 | 17 | std::string toLowerCase(const std::string& s); 18 | std::string toUpperCase(const std::string& s); 19 | 20 | std::string ltrim(const std::string& s); 21 | std::string rtrim(const std::string& s); 22 | std::string trim(const std::string& s); 23 | 24 | bool meet_utf8(const std::string& str); 25 | bool meet_gbk(const std::string& str); 26 | 27 | std::string get_cmd_output(const std::string& cmdstring); 28 | 29 | template 30 | int findNumbers(const std::string& s, std::vector* data) 31 | { 32 | int n = 0; 33 | std::string str = ""; 34 | bool haveNum = false; 35 | for (int i = 0; i < s.length(); i++) 36 | { 37 | char c = s[i]; 38 | bool findNumChar = (c >= '0' && c <= '9') || c == '.' || c == '-' || c == '+' || c == 'E' || c == 'e'; 39 | if (findNumChar) 40 | { 41 | str += c; 42 | if (c >= '0' && c <= '9') 43 | { 44 | haveNum = true; 45 | } 46 | } 47 | if (!findNumChar || i == s.length() - 1) 48 | { 49 | if (str != "" && haveNum) 50 | { 51 | auto f = T(atof(str.c_str())); 52 | data->push_back(f); 53 | n++; 54 | } 55 | str = ""; 56 | haveNum = false; 57 | } 58 | } 59 | return n; 60 | } 61 | 62 | template 63 | int findNumbers(const std::string& s, std::vector& data) 64 | { 65 | return findNumbers(s, &data); 66 | } 67 | 68 | template 69 | std::vector findNumbers(const std::string& s) 70 | { 71 | std::vector data; 72 | findNumbers(s, &data); 73 | return data; 74 | } 75 | 76 | } //namespace strfunc 77 | -------------------------------------------------------------------------------- /targetlnk.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #ifdef _WIN32 4 | #include "shlobj_core.h" 5 | #include "strcvt.h" 6 | #include "windows.h" 7 | #include "wrl.h" 8 | 9 | class CoInitializeHelper 10 | { 11 | public: 12 | CoInitializeHelper() 13 | { 14 | CoInitializeEx(nullptr, COINIT_MULTITHREADED); 15 | } 16 | 17 | ~CoInitializeHelper() 18 | { 19 | CoUninitialize(); 20 | } 21 | }; 22 | 23 | inline HRESULT path_from_shortcut(const std::wstring &shortcut_filename, std::wstring &path) 24 | { 25 | Microsoft::WRL::ComPtr shell_link; 26 | CoInitializeHelper co_helper; 27 | HRESULT hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shell_link)); 28 | 29 | if (FAILED(hr)) 30 | { 31 | return hr; 32 | } 33 | 34 | Microsoft::WRL::ComPtr persistFile; // = (IPersistFile *)shell_link; 35 | hr = shell_link.As(&persistFile); 36 | 37 | if (FAILED(hr)) 38 | { 39 | return hr; 40 | } 41 | 42 | hr = persistFile->Load(shortcut_filename.c_str(), STGM_READ); 43 | 44 | if (FAILED(hr)) 45 | { 46 | return hr; 47 | } 48 | 49 | LPITEMIDLIST itemIdList{ NULL }; 50 | hr = shell_link->GetIDList(&itemIdList); 51 | 52 | if (FAILED(hr)) 53 | { 54 | return hr; 55 | } 56 | 57 | wchar_t target_path[MAX_PATH]; 58 | 59 | hr = E_FAIL; 60 | 61 | if (SHGetPathFromIDList(itemIdList, target_path)) 62 | { 63 | path = std::wstring(target_path); 64 | hr = S_OK; 65 | } 66 | 67 | if (itemIdList != NULL) 68 | { 69 | CoTaskMemFree(itemIdList); 70 | } 71 | return hr; 72 | } 73 | 74 | inline std::string target_of_lnk(const std::string &lnk) 75 | { 76 | std::wstring path; 77 | HRESULT hr = path_from_shortcut(strcvt::CvtLocalStringToWString(lnk), path); 78 | if (SUCCEEDED(hr)) 79 | { 80 | return strcvt::CvtWStringToLocalString(path); 81 | } 82 | return ""; 83 | } 84 | #else 85 | inline std::string target_of_lnk(const std::string &lnk) 86 | { 87 | return ""; 88 | } 89 | #endif 90 | -------------------------------------------------------------------------------- /versioninfo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #pragma comment(lib, "version.lib") 6 | 7 | enum InfoType 8 | { 9 | InfoType_Version, 10 | InfoType_FileDescription, 11 | }; 12 | 13 | inline std::string get_version(const std::string& filename) 14 | { 15 | DWORD verHandle = 0; 16 | UINT size = 0; 17 | LPBYTE lpBuffer = NULL; 18 | DWORD verSize = GetFileVersionInfoSizeA(filename.c_str(), &verHandle); 19 | 20 | if (verSize != NULL) 21 | { 22 | std::string verData; 23 | verData.resize(verSize); 24 | if (GetFileVersionInfoA(filename.c_str(), verHandle, verSize, (LPVOID)verData.data())) 25 | { 26 | if (VerQueryValueA(verData.data(), "\\", (VOID FAR * FAR*)&lpBuffer, &size)) 27 | { 28 | if (size) 29 | { 30 | VS_FIXEDFILEINFO* verInfo = (VS_FIXEDFILEINFO*)lpBuffer; 31 | if (verInfo->dwSignature == 0xfeef04bd) 32 | { 33 | // Doesn't matter if you are on 32 bit or 64 bit, 34 | // DWORD is always 32 bits, so first two revision numbers 35 | // come from dwFileVersionMS, last two come from dwFileVersionLS 36 | 37 | return std::to_string((verInfo->dwFileVersionMS >> 16) & 0xffff) 38 | + "." + std::to_string((verInfo->dwFileVersionMS >> 0) & 0xffff) 39 | + "." + std::to_string((verInfo->dwFileVersionLS >> 16) & 0xffff) 40 | + "." + std::to_string((verInfo->dwFileVersionLS >> 0) & 0xffff); 41 | } 42 | } 43 | } 44 | } 45 | } 46 | return ""; 47 | } 48 | 49 | inline std::string get_string_info(const std::string& filename, const std::string& info) 50 | { 51 | DWORD verHandle = 0; 52 | UINT size = 0; 53 | LPBYTE lpBuffer = NULL; 54 | DWORD verSize = GetFileVersionInfoSizeA(filename.c_str(), &verHandle); 55 | 56 | if (verSize != NULL) 57 | { 58 | std::string verData; 59 | verData.resize(verSize); 60 | if (GetFileVersionInfoA(filename.c_str(), verHandle, verSize, (LPVOID)verData.data())) 61 | { 62 | if (VerQueryValue(verData.data(), TEXT("\\VarFileInfo\\Translation"), (VOID FAR * FAR*)&lpBuffer, &size)) 63 | { 64 | if (size) 65 | { 66 | // Read the list of languages and code pages. 67 | struct LANGANDCODEPAGE 68 | { 69 | WORD wLanguage; 70 | WORD wCodePage; 71 | }* lpTranslate; 72 | 73 | lpTranslate = (struct LANGANDCODEPAGE*)lpBuffer; 74 | // Read the file description for each language and code page. 75 | for (UINT i = 0; i < (size / sizeof(struct LANGANDCODEPAGE)); i++) 76 | { 77 | char SubBlock[50]; 78 | snprintf(SubBlock, 50, ("\\StringFileInfo\\%04x%04x\\" + info).c_str(), lpTranslate[i].wLanguage, lpTranslate[i].wCodePage); 79 | if (VerQueryValueA(verData.data(), SubBlock, (VOID FAR * FAR*)&lpBuffer, &size)) 80 | { 81 | return (const char*)lpBuffer; 82 | } 83 | } 84 | } 85 | } 86 | } 87 | } 88 | } 89 | 90 | inline std::string get_version(const std::string& filename, InfoType it) 91 | { 92 | if (it == InfoType_Version) 93 | { 94 | return get_version(filename); 95 | } 96 | else if (it == InfoType_FileDescription) 97 | { 98 | return get_string_info(filename, "FileDescription"); 99 | } 100 | return ""; 101 | } 102 | -------------------------------------------------------------------------------- /vramusage.cpp: -------------------------------------------------------------------------------- 1 | #include "vramusage.h" 2 | 3 | // the turn of the next 4 lines cannot be changed 4 | #define INITGUID 5 | #include 6 | 7 | #include 8 | 9 | #include //after ntddvdeo.h 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | #pragma comment(lib, "cfgmgr32.lib") 20 | #pragma comment(lib, "Gdi32.lib") 21 | #pragma comment(lib, "kernel32.lib") //for HOST Memory(RAM) 22 | 23 | std::vector get_gpu_devices_luid() 24 | { 25 | std::vector devices; 26 | LUID luid{ 0, 0 }; 27 | 28 | PWCHAR deviceInterfaceList; 29 | ULONG deviceInterfaceListLength = 0; 30 | PWCHAR deviceInterface; 31 | if (CM_Get_Device_Interface_List_Size(&deviceInterfaceListLength, (GUID*)&GUID_DISPLAY_DEVICE_ARRIVAL, NULL, CM_GET_DEVICE_INTERFACE_LIST_PRESENT)) { return devices; } 32 | std::vector deviceInterfaceListBuffer(deviceInterfaceListLength); 33 | deviceInterfaceList = deviceInterfaceListBuffer.data(); 34 | 35 | if (CM_Get_Device_Interface_List((GUID*)&GUID_DISPLAY_DEVICE_ARRIVAL, NULL, deviceInterfaceList, deviceInterfaceListLength, CM_GET_DEVICE_INTERFACE_LIST_PRESENT)) { return devices; } 36 | 37 | auto p = deviceInterfaceList; 38 | while (*p != L'\0') 39 | { 40 | deviceInterface = p; 41 | p += wcslen(p) + 1; 42 | #ifdef _DEBUG 43 | //wprintf(L"%s\n", deviceInterface); 44 | #endif 45 | 46 | DEVPROPTYPE devicePropertyType; 47 | DEVINST deviceInstanceHandle; 48 | ULONG deviceInstanceIdLength = MAX_DEVICE_ID_LEN; 49 | WCHAR deviceInstanceId[MAX_DEVICE_ID_LEN]; 50 | 51 | if (CM_Get_Device_Interface_Property(deviceInterface, &DEVPKEY_Device_InstanceId, &devicePropertyType, (PBYTE)deviceInstanceId, &deviceInstanceIdLength, 0)) { continue; } 52 | if (CM_Locate_DevNode(&deviceInstanceHandle, deviceInstanceId, CM_LOCATE_DEVNODE_NORMAL)) { continue; } 53 | 54 | ULONG buffer; 55 | ULONG bufferSize = sizeof(ULONG); 56 | DEVPROPTYPE propertyType = DEVPROP_TYPE_EMPTY; 57 | 58 | if (CM_Get_DevNode_Property(deviceInstanceHandle, &DEVPKEY_Device_BusNumber, &propertyType, (PBYTE)&buffer, &bufferSize, 0)) { continue; } 59 | 60 | D3DKMT_OPENADAPTERFROMDEVICENAME openAdapterFromDeviceName{}; 61 | openAdapterFromDeviceName.pDeviceName = deviceInterface; 62 | D3DKMTOpenAdapterFromDeviceName(&openAdapterFromDeviceName); 63 | D3DKMT_CLOSEADAPTER closeAdapter; 64 | closeAdapter.hAdapter = openAdapterFromDeviceName.hAdapter; 65 | D3DKMTCloseAdapter(&closeAdapter); 66 | luid = openAdapterFromDeviceName.AdapterLuid; 67 | GPUDeviceLUID device; 68 | 69 | memcpy(device.luid, &luid, sizeof(LUID)); 70 | device.pcibus = buffer; 71 | devices.push_back(device); 72 | } 73 | return devices; 74 | } 75 | 76 | int get_luid_from_pcibus(int pcibus, void* luid) 77 | { 78 | auto devices = get_gpu_devices_luid(); 79 | for (auto& device : devices) 80 | { 81 | if (device.pcibus == pcibus) 82 | { 83 | memcpy(luid, device.luid, sizeof(LUID)); 84 | return 0; 85 | } 86 | } 87 | return -1; 88 | } 89 | 90 | int get_free_mem_by_luid(LUID_PTR luid_ptr, size_t* physical, size_t* shared) 91 | { 92 | auto luid = *(LUID*)luid_ptr; 93 | D3DKMT_QUERYSTATISTICS queryStatistics{}; 94 | queryStatistics.Type = D3DKMT_QUERYSTATISTICS_ADAPTER; 95 | queryStatistics.AdapterLuid = luid; 96 | if (D3DKMTQueryStatistics(&queryStatistics)) 97 | { 98 | //printf("D3DKMTQueryStatistics failed with %d\n", ret); 99 | return 1; 100 | } 101 | 102 | ULONG64 total = 0, shared_usage = 0, physical_usage = 0; 103 | for (int i = 0; i < queryStatistics.QueryResult.AdapterInformation.NbSegments; i++) 104 | { 105 | D3DKMT_QUERYSTATISTICS queryStatistics2{}; 106 | queryStatistics2.Type = D3DKMT_QUERYSTATISTICS_SEGMENT; 107 | queryStatistics2.AdapterLuid = luid; 108 | queryStatistics2.QuerySegment.SegmentId = i; 109 | if (D3DKMTQueryStatistics(&queryStatistics2) == 0) 110 | { 111 | auto commit_limit = queryStatistics2.QueryResult.SegmentInformation.BytesResident; 112 | auto aperture = queryStatistics2.QueryResult.SegmentInformation.Aperture; 113 | if (aperture) 114 | { 115 | shared_usage += commit_limit; 116 | } 117 | else 118 | { 119 | physical_usage += commit_limit; 120 | } 121 | total += commit_limit; 122 | } 123 | } 124 | //printf("%g %g\n", sharedUsage / pow(2, 20), dedicatedUsage / pow(2, 20)); 125 | if (physical) { *physical = physical_usage; } 126 | if (shared) { *shared = shared_usage; } 127 | return 0; 128 | } 129 | 130 | float get_temperature_by_luid(LUID_PTR luid_ptr) 131 | { 132 | D3DKMT_QUERYSTATISTICS queryStatistics{}; 133 | queryStatistics.Type = D3DKMT_QUERYSTATISTICS_PHYSICAL_ADAPTER; 134 | queryStatistics.AdapterLuid = *(LUID*)luid_ptr; 135 | if (D3DKMTQueryStatistics(&queryStatistics)) 136 | { 137 | //printf("D3DKMTQueryStatistics failed with %d\n", ret); 138 | return 1; 139 | } 140 | return queryStatistics.QueryResult.PhysAdapterInformation.AdapterPerfData.Temperature / 10.0; 141 | } 142 | 143 | int get_free_mem_by_pcibus(int pcibus, size_t* resident, size_t* shared) 144 | { 145 | LUID luid; 146 | get_luid_from_pcibus(pcibus, &luid); 147 | return get_free_mem_by_luid(&luid, resident, shared); 148 | } 149 | 150 | int get_free_host_mem(size_t* totalMemory, size_t* freeMemory, size_t* totalVirtualMemory, size_t* freeVirtualMemory) 151 | { 152 | MEMORYSTATUSEX memoryInfo; 153 | memoryInfo.dwLength = sizeof(memoryInfo); 154 | if (!GlobalMemoryStatusEx(&memoryInfo)) 155 | { 156 | return -1; 157 | } 158 | 159 | if (totalMemory) { *totalMemory = memoryInfo.ullTotalPhys; } 160 | if (freeMemory) { *freeMemory = memoryInfo.ullAvailPhys; } 161 | if (totalVirtualMemory) { *totalVirtualMemory = memoryInfo.ullTotalVirtual; } 162 | if (freeVirtualMemory) { *freeVirtualMemory = memoryInfo.ullAvailVirtual; } 163 | 164 | return 0; 165 | } 166 | -------------------------------------------------------------------------------- /vramusage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | using LUID_PTR = const void*; 6 | 7 | struct GPUDeviceLUID 8 | { 9 | char luid[8]; 10 | int pcibus; 11 | }; 12 | 13 | // Get GPU Devices and their LUID 14 | // PCIBus is the bus number of the GPU device, if it is 0, it may be a virtual device 15 | std::vector get_gpu_devices_luid(); 16 | 17 | // Get LUID from PCIBus, only for GPU devices 18 | // the LUID is a 64-bit value (8 bytes) that uniquely identifies the adapter 19 | // return 0 if success, otherwise return -1 20 | int get_luid_from_pcibus(int pcibus, void* luid); 21 | 22 | // GPU Device Memory(VRAM) 23 | // physical means the memory that is dedicated to the GPU, shared means the memory that is shared with the system 24 | // physical为硬件上的显存占用,shared为系统共享显存的占用 25 | int get_free_mem_by_luid(LUID_PTR luid_ptr, size_t* physical, size_t* shared); 26 | 27 | // GPU Temperature 28 | float get_temperature_by_luid(LUID_PTR luid_ptr); 29 | 30 | // GPU Device Memory Usage by PCIBus 31 | int get_free_mem_by_pcibus(int pcibus, size_t* resident, size_t* shared); 32 | 33 | // CPU Host Memory(RAM) 34 | int get_free_host_mem(size_t* totalMemory, size_t* freeMemory, size_t* totalVirtualMemory, size_t* freeVirtualMemory); 35 | --------------------------------------------------------------------------------