Add files via upload

This commit is contained in:
lateworker 2024-01-27 19:28:55 +08:00 committed by GitHub
parent 45c8689118
commit d2eed62f2d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 758 additions and 0 deletions

388
README.md Normal file
View File

@ -0,0 +1,388 @@
# lassxTookit
## Version 4.0.0.b Beta
### **注意RealESR_Tools 已正式更名为 lassxTookit早于 4.0.0 的版本暂时不受影响!可自行去 Release 中下载**
Version 4.0.0 Beta 为内测版本没有UI界面需要手动修改配置文件。有任何 ‘使用方面的问题 / BUG 应在 [B站](https://space.bilibili.com/454920362) / [Github](https://github.com/lateworker/lassxTookit) / QQ群935718273/ ~~洛谷~~ 提出。
## 使用方法
**从此版本起,使用时应将配置文件拖入主程序,而不是直接打开主程序!**
也就是说,程序不再局限于单个配置文件。
## 配置文件介绍
配置文件为严格的 .json 文件。任何格式错误都会导致 程序在控制台输出 "ERROR" 并跳过此配置。
接下来会介绍配置文件的内容、默认值,以及与之相关的必要的知识。
- 每个配置前的括号内标明了该配置的属性。
- **带 \*号的内容会在后文中详细解释。**
- 加粗的字符串为对应其功能的键名key name
如有不清晰,可参见文末的测试用配置文件。
### 以下是配置文件的内容:
- 必填String**input_path**:输入文件夹的路径
- 必填String**output_path**:输出文件夹的路径
- \*选填String**error_path**:错误文件夹的路径
- *选填String**selector**:选择器, 用来匹配文件
- 文件夹遍历时会用到选择器。
- *必填String**model**选用的AI模型
- 必填String**scale**:模型的放大倍数
- 选填String**denoise**:模型的降噪等级
- realesr 系列模型不支持降噪等级,这时此项无意义。
- 选填String**syncgap**:图片分块等级
- 我也不知道有什么效果(也许可以加速处理?),但有的模型需要这个参数。
- 只有 realcugan 模型支持图片分块等级,其它情况下此项无意义。
- *必填Boolean**tree_restore**:是否还原目录结构
- 必填Boolean**subdir_find**:是否递归处理子目录
- 选填Boolean**emptydir_find**:是否处理 不包含目标文件 的目录
- 如果将此项设为 true程序就会根据输入文件夹的结构生成完全相同的输出文件夹可能有些输出文件夹为空。某种意义上程序处理了空的文件夹相比于那些包含了待处理文件的因此将此项命名为 emptydir_find空目录寻找
- 可以帮你还原整个目录结构,即使不处理全部文件。
- *选填Boolean**file_error**:是否处理文件错误
- *选填Boolean**dir_error**:是否处理文件夹错误
- 一般来说dir_error 完全包含 file_error。
- *选填Array**match**:正则表达式形式的黑名单
- 可以筛选文件的名称("name")和目录("path")。
- 现有完全匹配("match"),部分匹配("search")两种模式。
- *选填Object**addons**:插件模块
- 自己写的插件要放到 项目根目录下 addons 文件夹里,填入文件名即可。
### 选填项默认值
```json
"error_path": "" // 空字符串
"selector": "\*" // 用于筛选所有文件
"denoise": "0"
"syncgap": "0"
"emptydir_find": false // 这一项一般用不到
"file_error": true
"dir_error": false
"match": null
"addons": null
```
### 模型介绍
以下是模型列表和一些注意事项(加粗为模型的默认值,仅为参考):
- DF2K / DF2K-JPEGrealesr一代模型
- 放大倍数2 / **4**
- realesrgan / realesrnetrealesr二代模型
- 放大倍数2 / **4**
- realesrgan-animerealesr三代模型使用内存
- 放大倍数2 / 3 / **4**
- realcugan
- 放大倍数1 / **2** / 3 / 4
- 降噪等级:**-1** / 0 / 1 / 2 / 3
- 分块等级0 / 1 / 2 / **3**
- waifu2x-anime / waifu2x-photowaifu2x模型
- 放大倍数1 / **2** / 4 / 8 / 16 / 32
- 降噪等级:-1 / **0** / 1 / 2 / 3
- realesr 系列realesrgan / realesrnet / realesrgan-anime / DF2K / DF2K-JPEG模型不支持 denoise降噪等级这时此项无意义。
- 只有 realcugan 模型支持 syncgap图片分块等级其它情况下此项无意义。
虽然模型有默认值,但配置文件内还是要手动设置。
**注意事项:**
- AI 模型使用显卡放大图片,处理速度取决于显卡算力和图片分辨率。
- 模型之间有一定的差异,具体可见 testimagine.7z 压缩包。
- realesrgan-anime 模型适合超分动漫图片。
- realesrnet & waifu2x-photo 模型适合超分真实图片。
- 若没有独显或独显很弱,建议使用 realesrgan-anime 模型。此模型处理速度最快。
- realesrgan-anime 模型为Ram版本内存版本没有独显也可以超分大图片。独显性能不受影响。
- 除 realesrgan-anime 以外所有模型都是非Ram版本显存和内存不足8G可能会导致崩溃。通常在处理 $30MB\ /\ 10^8$(一亿)像素‘ 以上的图片时会崩溃。
- 显卡测试1: RX588 ARCAEA-8K-HKT.png 16MB 7680*4320
- realesrgan模型-用时30min
- realesrgan-anime模型-用时14min.
此数据来自B站用户ZXOJ-LJX-安然x。~~其实就是同学帮忙测的~~
- 注意:关于显存占用问题,
- 如果使用独显, 在任务管理器中看不到显卡占用在NA软件中显示占用满。
- 集显可以直接在任务管理器中看到。
- **对真实图片进行超分不能使用anime模型** ~~会有意外惊喜~~
### 选择器selector介绍
选择器决定了程序在遍历文件夹、搜索输入文件时得到的结果。它是一个由Windows通配符和普通字符组成的字符串在使用上和Windows资源管理器explorer.exe的搜索框相同。
简单说,"?"(英文问号)可以代替文件路径中任意的**一个**字符,"\*"(乘号 / 星号)可以代替路径中任意的**一个或多个**字符。比如:
- "\*":所有文件 / 文件夹
- "\*.jpg":所有 .jpg 格式的文件
- "a*.jpg":所有以 'a' 开头, .jpg 格式的文件
更多细节可以参考微软官方文档:[通配符简介 - PowerShell | Microsoft Learn](https://learn.microsoft.com/zh-cn/powershell/module/microsoft.powershell.core/about/about_wildcards?view=powershell-7.4)
### tree_restore还原目录结构参数介绍
**tree_restore & subdir_find & emptydir_find** 三个参数功能相似,这里只对 tree_restore 参数展开描述。
tree_restore 参数功能很简单,当它为 true 时,程序会自动在输出文件夹内建立子文件夹,并将文件依次丢回原来的子文件夹里。举个例子:
- 假如输入文件夹内文件结构如下:
```
Input/
├── dir1/
│ ├── A.jpg
│ └── B.png
├── dir2/
│ ├── dir3/
│ │ └── C.jpeg
│ └── D.txt
└── E.webp
```
- 那么,如果 tree_restore = true 并且只处理图片的话,输出文件夹内是这样的:
```
Input/
├── dir1/
│ ├── A.jpg
│ └── B.png
├── dir2/
│ └── dir3/
│ └── C.jpeg
└── E.webp
```
- 反之,如果 tree_restore = false其他条件不变输出文件夹内是这样的
```
Input/
├── A.jpg
├── B.png
├── C.jpeg
└── E.webp
```
很直观,此参数会直接影响输出文件夹结构。另外两个参数就是字面意思,这里不做解释。
### 错误处理模块介绍(包括 error_pathfile_errordir_error
一般来说,程序的错误分为以下四种:
- 当 ‘输出文件夹和输入文件夹间 存在名称相同完全的文件’ 时,构成 ‘标准错误’ 。
- 当 ‘配置文件不存在 / json 格式错误 / 缺少必填配置’ 时,构成‘ 配置文件错误’ 。
- 当 ‘模型崩溃 / 程序卡死 / 文件、文件夹输出有误’ 时,构成 ‘程序运行错误’ 。
- 当 ’插件无法调用 / 插件失效 / 文件路径表示出错‘ 时,构成 ’其它功能错误‘ 。
程序内置了错误处理模块,以尽可能避免错误。由于第二至四项错误都 极少见 或由 ‘使用者操作不当 / 代码功能不完善’ 导致,无法预测,因此程序只提供了标准错误的解决方案和配置空间。
#### error_path
一个文件夹路径,该文件夹用于保存输出文件夹内的错误文件,相当于一个备份文件夹。
该文件夹一般位于输入或输出文件夹下,名称随意。比如:
```json
"input_path": "D:\input"
"output_path": "D:\output"
"error_path": "D:\output\ERROR" // 在输出文件夹下
```
当然,你也可以让 该错误文件夹 设为 输出文件夹。这样做,输出文件夹内的错误文件就会被直接覆盖。
**当 file_error 或 dir_error 其中一项为 true 时error_path 便不能为空!否则后果自负!**
~~没测过,不知道会发生什么。。可能是文件泄露?~~
#### file_error
是否备份错误的文件。推荐设为 true。
#### dir_error
是否备份错误的文件夹,默认为 false。
此选项在最终效果上完全覆盖 file_error 选项。也就是说,
```json
"file_error": false,
"dir_error": true
```
完全等同于
```json
"file_error": true,
"dir_error": true
```
### 正则表达式匹配介绍
lassxTookit 于 Version 4.0.0.b Beta 版本加入正则表达式功能。此功能在效果上与普通正则表达式无异,是一个**黑名单**,可以借此筛掉一些不需要处理的文件。
正则表达式的详细介绍可以参见菜鸟教程:[正则表达式教程 | 菜鸟教程](https://www.runoob.com/regexp)
如果想检查正则表达式的语法,可以去这个网站(英文):[正则表达式测试 | Regex101](https://regex101.com/)
接下来介绍 match 键配置内的格式要求:
```json
"match": [ // "match" 为正则表达式匹配模块的键名
{
"name": "RegexRule1", // "name"键用来筛选文件名
"path": "RegexRule2", // "path"键用来筛选路径(不包括文件名)
"name_mode": "MatchMode1",
"path_mode": "MatchMode2"
}, // RegexRule1 第一个正则表达式配置
{...}, // RegexRule2 注意Object外的逗号
.
.
.
]
```
如上文所示match 键内含若干个Object类型的子类子类中接收 "name" & "path" & "name_mode" & "path_mode" 四个键值对。
MatchMode匹配模式现有 "search"(搜索)、"match"(完全匹配)两种。
- "match" 模式要求,字符串(文件名 / 路径)必须与正则表达式完全匹配。
- "search" 模式要求,只要字符串中的某一子串与正则表达式匹配就视为匹配成功。
各个子类内部执行 and逻辑子类之间执行 or逻辑。
### 插件模块介绍
lassxTookit 于 Version 4.0.0.b Beta 版本同时引入了插件功能。
**所有自己写的插件都要放到项目根目录下 addons 文件夹里!**
可运行的程序都可以作为插件被主程序调用。
"addons"(插件)模块细分为三种键名,分别为:
- "first":这组插件在 ‘程序开始时 & 所有图片未做任何处理时’ 被调用。
- 插件需接收命令行传入的一个参数。
- 此参数为一个 Json 格式的字符串,内含配置文件中的所有内容。
- "second"**每处理**一个 ‘文件 / 文件夹’,这组插件就会被调用一次。
- 插件需接收命令行传入的**两个**参数。
- 第一个参数的形式和意义与 "first" 插件相同。
- 第二个参数为 **刚刚**处理完成的 ‘文件 / 文件夹’ 路径。
- "third":这组插件在所有图片处理完成后被调用。
- 插件需接收命令行传入的**两个**参数。
- 第一个参数的形式和意义与 "first" 插件相同。
- 第二个参数为一个 Json 格式的字符串,内含本次处理中所有 ‘文件 / 文件夹’ 路径。
以上三种键名都对应着一个 Array数组作为值。
插件模块格式如下:
```json
"addons": { // "addons" 为插件模块的键名
"first": [
"a.exe", "b.exe" ... // first组的插件列表填入文件名即可
],
"second": [ ... ], // second组的插件列表
"third": [ ... ] // third组的插件列表
}
```
软件自带 "print.exe" 插件,此插件可以输出命令行传入的字符串。可以借此自行测试并理解插件调用机制。
## 测试用配置文件
```json
{
"input_path": "D:\\input",
"output_path": "D:\\output",
"error_path": "D:\\error", // error 文件夹也可以放在其他地方
"selector": "*", // 处理所有文件
"model":"realesrgan-anime", // anime模型处理动漫图片
"scale":"4", // 放大倍数
"denoise":"0", // 降噪等级
"syncgap":"0", // 分块等级
"tree_restore": true, // 还原目录结构
"subdir_find": true, // 处理子文件夹
"emptydir_find": false, // 不处理空文件夹
"file_error": true, // 处理文件标准错误
"dir_error": false, // 不处理文件夹标准错误
"match": [
// 所有含有字母D的文件都不会被处理
{ "name": "D", "name_mode": "search" }
],
"addons": {
"first": [],
"second": [],
"third": [
"print.exe" // 调用 print.exe 作为第三组插件,查看参数传递情况
]
}
}
```

13
addons/change.cpp Normal file
View File

@ -0,0 +1,13 @@
#include <iostream>
using namespace std;
int main(int n, char **x) {
string f = x[2];
if(f.find('.')!=string::npos) {
string g = f.substr(0, f.size()-3);
g += "webp";
string command = "move " + f + " " + g;
// cout<<command<<"\n";
system(command.c_str());
}
return 0;
}

BIN
addons/change.exe Normal file

Binary file not shown.

9
addons/print.cpp Normal file
View File

@ -0,0 +1,9 @@
#include <iostream>
#include <fstream>
#include "../include/configor/json.hpp"
using namespace std;
using namespace configor;
int main(int n, char** _configPath) {
cout<<"\n\n"<<_configPath[1]<<"\n\n"<<_configPath[2];
return 0;
}

BIN
addons/print.exe Normal file

Binary file not shown.

23
config.json Normal file
View File

@ -0,0 +1,23 @@
{
"input_path": "\"D:\\Users\\lateworker\\Desktop\\input\"",
"output_path": "\"D:\\Users\\lateworker\\Desktop\\output\"",
"error_path": "error",
"selector": "*",
"model":"realesrgan-anime",
"scale":"4",
"denoise":"0",
"syncgap":"0",
"tree_restore": true,
"subdir_find": true,
"emptydir_find": true,
"file_error": true,
"dir_error": false,
"match": [],
"addons": {
"first": [],
"second": [],
"third": [
"print.exe"
]
}
}

325
lassxTookit_util.cpp Normal file
View File

@ -0,0 +1,325 @@
#include <iostream>
#include <regex>
#include <fstream>
#include <stdlib.h>
#include "include/configor/json.hpp"
#include "include/path.h"
using namespace std;
using namespace path;
using namespace configor;
string selfPath, selfName;
inline string strquote(string x) { return "\"" + x + "\""; }
inline void strdelete(string &data) {
while (data.front() == '\"') data.erase(data.begin());
while (data.back() == '\"') data.erase(data.end() - 1);
while (data.front() == '\\' || data.front() == '/') data.erase(data.begin());
while (data.back() == '\\' || data.back() == '/') data.erase(data.end() - 1);
for (size_t i = 1; i < data.size(); ++i)
if ((data[i] == '\\' && data[i - 1] == data[i]) || (data[i] == '/' && data[i - 1] == data[i]))
data.erase(data.begin() + i), --i;
}
inline void strsplit(string x, string &path, string &name) {
name = path = "";
size_t pos = x.find_last_of("\\");
if (pos == string::npos)
path = ".", name = x;
else {
name = x.substr(pos);
strdelete(name);
path = x.substr(0, pos);
strdelete(path);
}
}
inline bool loadConfig(string path, json::value &data) {
data.clear();
ifstream file(path.c_str());
if (!file.is_open()) return true;
try {
file >> json::wrap(data);
} catch (exception &ERROR) { return true; }
file.close();
return false;
}
struct RegexRule {
struct _rule {
string _name, _path, _name_mode, _path_mode;
_rule() { _name = _path = _name_mode = _path_mode = ""; }
CONFIGOR_BIND(
json::value, _rule,
OPTIONAL(_name, "name"),
OPTIONAL(_path, "path"),
OPTIONAL(_name_mode, "name_mode"),
OPTIONAL(_path_mode, "path_mode")
);
};
vector<_rule> rule;
inline void init(const json::value &data) {
for (json::value x : data) rule.push_back(x);
}
inline bool check(const string &name, const string &path) const {
for (RegexRule::_rule x : rule) {
bool res1, res2;
res1 = res2 = true;
if (x._name != "") {
regex match(x._name);
if (x._name_mode == "search") res1 = regex_search(name, match);
else res1 = regex_match(name, match);
}
if (x._path != "") {
regex match(x._path);
if (x._path_mode == "search") res2 = regex_search(name, match);
else res2 = regex_match(name, match);
}
if (res1 && res2) return false;
}
return true;
}
void operator= (const json::value &data) { init(data); }
RegexRule() { rule.clear(); }
};
#define Addon_config to_string(config.config_data)
#define Addon_first config.addons.first
#define Addon_second config.addons.second
#define Addon_third config.addons.third
struct Addon {
vector<string> first, second, third;
inline void init(const json::value &data) {
if (data.count("first") > 0) for (json::value x : data["first"]) first.push_back(x);
if (data.count("second") > 0) for (json::value x : data["second"]) second.push_back(x);
if (data.count("third") > 0) for (json::value x : data["third"]) third.push_back(x);
}
void operator= (const json::value &data) { init(data); }
Addon() { first.clear(), second.clear(), third.clear(); }
};
struct Config {
json::value config_data;
string config_path;
string input_path, output_path, error_path, selector;
string model, scale, denoise, syncgap;
size_t pos_slash, pos_dot, is_file;
bool tree_restore, subdir_find, emptydir_find;
bool file_error, dir_error;
RegexRule match;
Addon addons;
CONFIGOR_BIND(
json::value, Config,
REQUIRED(input_path, "input_path"),
REQUIRED(output_path, "output_path"),
OPTIONAL(error_path, "error_path"),
OPTIONAL(selector, "selector"),
REQUIRED(model, "model"),
REQUIRED(scale, "scale"),
OPTIONAL(denoise, "denoise"),
OPTIONAL(syncgap, "syncgap"),
REQUIRED(tree_restore, "tree_restore"),
REQUIRED(subdir_find, "subdir_find"),
OPTIONAL(emptydir_find, "emptydir_find"),
OPTIONAL(file_error, "file_error"),
OPTIONAL(dir_error, "dir_error")
);
inline void init() {
strdelete(input_path);
strdelete(output_path);
strdelete(selector);
pos_slash = input_path.find_last_of('\\');
pos_dot = input_path.find_last_of('.');
is_file = pos_dot != string::npos && (pos_slash == string::npos || pos_slash < pos_dot);
}
inline bool init(const string &configPath) {
json::value configData;
bool is_failed = loadConfig(configPath, configData);
if (is_failed) return true;
*this = configData;
config_data = configData;
config_path = configPath;
init();
if (configData.count("match") > 0) match.init(configData["match"]);
if (configData.count("addons") > 0) addons.init(configData["addons"]);
return false;
}
inline Config getSubConfig(const TraverseData *data) const {
Config _file = *this;
if (_file.is_file) {
_file.input_path = output_path + "\\" + input_path.substr(pos_slash);
} else {
if (tree_restore) _file.input_path = output_path + "\\" + data->fullpath().substr(input_path.length());
else _file.input_path = output_path + "\\" + data->name;
}
_file.output_path = _file.input_path;
_file.init();
return _file;
}
inline bool checkRegex(const string &name, const string &path) const { return match.check(name, path); }
Config() {
config_data = json::value();
config_path = "";
input_path = output_path = model = scale = "";
error_path = "";
selector = "*";
denoise = syncgap = "0";
pos_slash = pos_dot = is_file = 0;
tree_restore = subdir_find = emptydir_find = false;
file_error = true, dir_error = false;
match = RegexRule();
addons = Addon();
}
};
inline void makedir(string path) {
system(("md " + strquote(path) + " >nul 2>&1").c_str());
}
inline void removedir(string path) {
system(("rd " + strquote(path) + " >nul 2>&1").c_str());
}
inline void copyfile(string file_path, string direction_path) {
system(("xcopy /c/q/g/k/r/h/y/-i " + strquote(file_path) + " " + strquote(direction_path) + " >nul 2>&1").c_str());
}
inline void movefile(string file_path, string direction_path) {
system(("move /y " + strquote(file_path) + " " + strquote(direction_path) + " >nul 2>&1").c_str());
}
inline void executefile(string file) { system(file.c_str()); }
inline void deletefile(string file) {
system(("del /f/s/q " + strquote(file) + " >nul 2>&1").c_str());
}
inline bool existfile(string path) {
struct stat file_buffer;
return (stat(path.c_str(), &file_buffer) == 0);
}
#define Waifu2x_Anime "models-upconv_7_anime_style_art_rgb"
#define Waifu2x_Photo "models-upconv_7_photo"
inline int getgroup_model(const string &model) {
if (model == "realesrgan" || model == "realesrnet" || model == "realesrgan-anime" || model == "DF2K" || model == "DF2K-JPEG") return 1;
if (model == "waifu2x-anime" || model == "waifu2x-photo") return 2;
if (model == "realcugan") return 3;
return 0;
}
inline void core(const Config &file) {
string command;
if (getgroup_model(file.model) == 1) {
command = (string)"models\\realesrgan.exe"
+ " -i " + strquote(file.input_path)
+ " -o " + strquote(file.output_path)
+ " -n " + strquote(file.model + "-x" + file.scale)
+ " -s " + file.scale;
}
if (getgroup_model(file.model) == 2) {
command = (string)"models\\waifu2x.exe"
+ " -i " + strquote(file.input_path)
+ " -o " + strquote(file.output_path)
+ " -m " + strquote(file.model == "waifu2x-anime" ? Waifu2x_Anime : Waifu2x_Photo)
+ " -s " + file.scale
+ " -n " + file.denoise;
}
if (getgroup_model(file.model) == 3) {
command = (string)"models\\realcugan.exe"
+ " -i " + strquote(file.input_path)
+ " -o " + strquote(file.output_path)
+ " -s " + file.scale
+ " -n " + file.denoise
+ " -c " + file.syncgap;
}
chdir(selfPath.c_str()); // 不知道是哪里出问题了, 这里必须手动切换目录.
executefile(command);
}
string to_string(const json::value &x) {
string res = json::dump(x);
for (size_t i = 0; i < res.size(); ++i)
if (res[i] == '\"')
res.insert(i++, "\\");
strdelete(res);
return res;
}
string Addon_getRelativePath(const Config &config, string x) {
x = x.substr(config.output_path.size()); strdelete(x);
return x;
}
json::value to_json(const Config &config, const vector<string> &data) {
json::value jsondata;
for (string x : data)
jsondata.push_back(Addon_getRelativePath(config, x));
return jsondata;
}
inline void process_error(const Config &config, const Config &data) {
string errorPath = config.error_path;
errorPath = errorPath + "\\" + data.input_path.substr(0, data.pos_slash).substr(config.output_path.size());
strdelete(errorPath);
makedir(errorPath);
movefile(data.input_path, errorPath);
}
void executeAddon(const Config &config, const vector<string> &execute, const string &parameter1, const string &parameter2) {
for (string x : execute) {
copyfile("addons\\" + x, config.output_path + "\\" + x);
chdir(config.output_path.c_str());
executefile(x + " " + strquote(parameter1) + " " + strquote(parameter2));
deletefile(x);
chdir(selfPath.c_str());
}
}
void process(const Config &config) {
makedir(config.output_path);
executeAddon(config, Addon_first, Addon_config, "");
vector<string> data_addons;
if (config.is_file) {
Config _file = config.getSubConfig(nullptr);
if (config.file_error && existfile(_file.input_path)) {
process_error(config, _file);
}
copyfile(config.input_path, _file.input_path);
data_addons.push_back(_file.input_path);
core(_file);
executeAddon(config, Addon_second, Addon_config, Addon_getRelativePath(config, _file.input_path));
} else {
vector<TraverseData> data;
traverse(config.input_path, data, config.selector, {true, true, config.subdir_find, config.emptydir_find});
for (TraverseData x : data) {
if (!x.attrib.is_folder() && config.checkRegex(x.name, x.path)) {
Config _file = config.getSubConfig(&x);
if (config.file_error && existfile(_file.input_path)) {
process_error(config, _file);
}
data_addons.push_back(_file.input_path);
copyfile(x.fullpath(), _file.input_path);
core(_file);
executeAddon(config, Addon_second, Addon_config, Addon_getRelativePath(config, _file.input_path));
}
if (config.tree_restore && x.attrib.is_folder() && config.checkRegex(x.name, x.path)) {
Config _dir = config.getSubConfig(&x);
if (config.dir_error && existfile(_dir.input_path)) {
process_error(config, _dir);
}
data_addons.push_back(_dir.input_path);
makedir(_dir.input_path);
executeAddon(config, Addon_second, Addon_config, Addon_getRelativePath(config, _dir.input_path));
}
}
}
executeAddon(config, Addon_third, Addon_config, to_string(to_json(config, data_addons)));
}
int main(int n, char **configPath) {
strsplit(_pgmptr, selfPath, selfName);
strdelete(selfPath), strdelete(selfName);
for (int i = 1; i < n; ++i) {
Config config;
bool is_failed = config.init(configPath[i]);
if (is_failed) {
puts("Error");
// Create config file as default && reload
continue;
}
process(config);
}
return 0;
}