• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "resource_pack.h"
17 #include <algorithm>
18 #include <iomanip>
19 #include "file_entry.h"
20 #include "file_manager.h"
21 #include "header.h"
22 #include "resource_merge.h"
23 #include "resource_table.h"
24 #include "resource_check.h"
25 
26 namespace OHOS {
27 namespace Global {
28 namespace Restool {
29 using namespace std;
ResourcePack(const PackageParser & packageParser)30 ResourcePack::ResourcePack(const PackageParser &packageParser):packageParser_(packageParser)
31 {
32 }
33 
Package()34 uint32_t ResourcePack::Package()
35 {
36     if (!packageParser_.GetAppend().empty()) {
37         return PackAppend();
38     }
39 
40     if (packageParser_.GetCombine()) {
41         return PackCombine();
42     }
43     return PackNormal();
44 }
45 
46 // below private founction
Init()47 uint32_t ResourcePack::Init()
48 {
49     InitHeaderCreater();
50     if (InitOutput() != RESTOOL_SUCCESS) {
51         return RESTOOL_ERROR;
52     }
53 
54     if (InitConfigJson() != RESTOOL_SUCCESS) {
55         return RESTOOL_ERROR;
56     }
57 
58     if (InitModule() != RESTOOL_SUCCESS) {
59         return RESTOOL_ERROR;
60     }
61     return RESTOOL_SUCCESS;
62 }
63 
InitModule()64 uint32_t ResourcePack::InitModule()
65 {
66     IdWorker::ResourceIdCluster hapType = IdWorker::ResourceIdCluster::RES_ID_APP;
67     string packageName = packageParser_.GetPackageName();
68     if (packageName == "ohos.global.systemres") {
69         hapType = IdWorker::ResourceIdCluster::RES_ID_SYS;
70     }
71 
72     moduleName_ = configJson_.GetModuleName();
73     vector<string> moduleNames = packageParser_.GetModuleNames();
74     IdWorker &idWorker = IdWorker::GetInstance();
75     int32_t startId = packageParser_.GetStartId();
76     if (startId > 0) {
77         return idWorker.Init(hapType, startId);
78     }
79 
80     if (moduleNames.empty()) {
81         return idWorker.Init(hapType);
82     } else {
83         sort(moduleNames.begin(), moduleNames.end());
84         auto it = find_if(moduleNames.begin(), moduleNames.end(), [this](auto iter) {
85                 return moduleName_ == iter;
86             });
87         if (it == moduleNames.end()) {
88             string buffer(" ");
89             for_each(moduleNames.begin(), moduleNames.end(), [&buffer](const auto &iter) {
90                     buffer.append(" " + iter + " ");
91                 });
92             cerr << "Error: module name '" << moduleName_ << "' not in [" << buffer << "]" << endl;
93             return RESTOOL_ERROR;
94         }
95 
96         startId = ((it - moduleNames.begin()) + 1) * 0x01000000;
97         if (startId >= 0x07000000) {
98             startId = startId + 0x01000000;
99         }
100         return idWorker.Init(hapType, startId);
101     }
102     return RESTOOL_SUCCESS;
103 }
104 
InitHeaderCreater()105 void ResourcePack::InitHeaderCreater()
106 {
107     using namespace placeholders;
108     headerCreaters_.emplace(".txt", bind(&ResourcePack::GenerateTextHeader, this, _1));
109     headerCreaters_.emplace(".js", bind(&ResourcePack::GenerateJsHeader, this, _1));
110     headerCreaters_.emplace(".h", bind(&ResourcePack::GenerateCplusHeader, this, _1));
111 }
112 
InitOutput() const113 uint32_t ResourcePack::InitOutput() const
114 {
115     bool forceWrite = packageParser_.GetForceWrite();
116     bool combine = packageParser_.GetCombine();
117     string output = packageParser_.GetOutput();
118     string resourcesPath = FileEntry::FilePath(output).Append(RESOURCES_DIR).GetPath();
119     if (ResourceUtil::FileExist(resourcesPath)) {
120         if (!forceWrite) {
121             cerr << "Error: output path exists." << NEW_LINE_PATH << resourcesPath << endl;
122             return RESTOOL_ERROR;
123         }
124 
125         if (!ResourceUtil::RmoveAllDir(resourcesPath)) {
126             return combine ? RESTOOL_SUCCESS : RESTOOL_ERROR;
127         }
128     }
129     return RESTOOL_SUCCESS;
130 }
131 
GenerateHeader() const132 uint32_t ResourcePack::GenerateHeader() const
133 {
134     auto headerPaths = packageParser_.GetResourceHeaders();
135     string textPath = FileEntry::FilePath(packageParser_.GetOutput()).Append("ResourceTable.txt").GetPath();
136     headerPaths.push_back(textPath);
137     for (const auto &headerPath : headerPaths) {
138         string extension = FileEntry::FilePath(headerPath).GetExtension();
139         auto it = headerCreaters_.find(extension);
140         if (it == headerCreaters_.end()) {
141             cout << "Warning: don't support header file format '" << headerPath << "'" << endl;
142             continue;
143         }
144         if (it->second(headerPath) != RESTOOL_SUCCESS) {
145             return RESTOOL_ERROR;
146         }
147     }
148     return RESTOOL_SUCCESS;
149 }
150 
InitConfigJson()151 uint32_t ResourcePack::InitConfigJson()
152 {
153     string config = packageParser_.GetConfig();
154     if (config.empty()) {
155         if (packageParser_.GetInputs().size() > 1) {
156             cerr << "Error: more input path, -j config.json empty" << endl;
157             return RESTOOL_ERROR;
158         }
159         config = ResourceUtil::GetMainPath(packageParser_.GetInputs()[0]).Append(CONFIG_JSON).GetPath();
160         if (!ResourceUtil::FileExist(config)) {
161             config = ResourceUtil::GetMainPath(packageParser_.GetInputs()[0]).Append(MODULE_JSON).GetPath();
162         }
163     }
164 
165     if (FileEntry::FilePath(config).GetFilename() == MODULE_JSON) {
166         ConfigParser::SetUseModule();
167     }
168     configJson_ = ConfigParser(config);
169     if (configJson_.Init() != RESTOOL_SUCCESS) {
170         return RESTOOL_ERROR;
171     }
172     return RESTOOL_SUCCESS;
173 }
174 
GenerateTextHeader(const string & headerPath) const175 uint32_t ResourcePack::GenerateTextHeader(const string &headerPath) const
176 {
177     Header textHeader(headerPath);
178     bool first = true;
179     uint32_t result = textHeader.Create([](stringstream &buffer) {},
180         [&first](stringstream &buffer, const IdWorker::ResourceId& resourceId) {
181             if (first) {
182                 first = false;
183             } else {
184                 buffer << "\n";
185             }
186             buffer << resourceId.type << " " << resourceId.name;
187             buffer << " 0x" << hex << setw(8)  << setfill('0') << resourceId.id;
188         }, [](stringstream &buffer) {});
189     if (result != RESTOOL_SUCCESS) {
190         return RESTOOL_ERROR;
191     }
192     return RESTOOL_SUCCESS;
193 }
194 
GenerateCplusHeader(const string & headerPath) const195 uint32_t ResourcePack::GenerateCplusHeader(const string &headerPath) const
196 {
197     Header cplusHeader(headerPath);
198     uint32_t result = cplusHeader.Create([](stringstream &buffer) {
199         buffer << Header::LICENSE_HEADER << "\n";
200         buffer << "#ifndef RESOURCE_TABLE_H\n";
201         buffer << "#define RESOURCE_TABLE_H\n\n";
202         buffer << "#include<stdint.h>\n\n";
203         buffer << "namespace OHOS {\n";
204     }, [](stringstream &buffer, const IdWorker::ResourceId& resourceId) {
205         string name = resourceId.type + "_" + resourceId.name;
206         transform(name.begin(), name.end(), name.begin(), ::toupper);
207         buffer << "const int32_t " << name << " = ";
208         buffer << "0x" << hex << setw(8)  << setfill('0') << resourceId.id << ";\n";
209     }, [](stringstream &buffer) {
210         buffer << "}\n";
211         buffer << "#endif";
212     });
213     return result;
214 }
215 
GenerateJsHeader(const std::string & headerPath) const216 uint32_t ResourcePack::GenerateJsHeader(const std::string &headerPath) const
217 {
218     Header JsHeader(headerPath);
219     string itemType;
220     uint32_t result = JsHeader.Create([](stringstream &buffer) {
221         buffer << Header::LICENSE_HEADER << "\n";
222         buffer << "export default {\n";
223     }, [&itemType](stringstream &buffer, const IdWorker::ResourceId& resourceId) {
224         if (itemType != resourceId.type) {
225             if (!itemType.empty()) {
226                 buffer << "\n" << "    " << "},\n";
227             }
228             buffer << "    " << resourceId.type << " : {\n";
229             itemType = resourceId.type;
230         } else {
231             buffer << ",\n";
232         }
233         buffer << "        " << resourceId.name << " : " << resourceId.id;
234     }, [](stringstream &buffer) {
235         buffer << "\n" << "    " << "}\n";
236         buffer << "}\n";
237     });
238     return result;
239 }
240 
CopyRawFile(const vector<string> & inputs) const241 uint32_t ResourcePack::CopyRawFile(const vector<string> &inputs) const
242 {
243     for (const auto &input : inputs) {
244         string rawfilePath = FileEntry::FilePath(input).Append(RAW_FILE_DIR).GetPath();
245         if (!ResourceUtil::FileExist(rawfilePath)) {
246             continue;
247         }
248 
249         if (!FileEntry::IsDirectory(rawfilePath)) {
250             cerr << "Error: '" << rawfilePath << "' not directory." << endl;
251             return RESTOOL_ERROR;
252         }
253 
254         string dst = FileEntry::FilePath(packageParser_.GetOutput())
255             .Append(RESOURCES_DIR).Append(RAW_FILE_DIR).GetPath();
256         if (CopyRawFileImpl(rawfilePath, dst) != RESTOOL_SUCCESS) {
257             return RESTOOL_ERROR;
258         }
259     }
260     return RESTOOL_SUCCESS;
261 }
262 
CopyRawFileImpl(const string & src,const string & dst) const263 uint32_t ResourcePack::CopyRawFileImpl(const string &src, const string &dst) const
264 {
265     if (!ResourceUtil::CreateDirs(dst)) {
266         return RESTOOL_ERROR;
267     }
268 
269     FileEntry f(src);
270     if (!f.Init()) {
271         return RESTOOL_ERROR;
272     }
273     for (const auto &entry : f.GetChilds()) {
274         string filename = entry->GetFilePath().GetFilename();
275         if (ResourceUtil::IsIgnoreFile(filename, entry->IsFile())) {
276             continue;
277         }
278 
279         string subPath = FileEntry::FilePath(dst).Append(filename).GetPath();
280         if (!entry->IsFile()) {
281             if (CopyRawFileImpl(entry->GetFilePath().GetPath(), subPath) != RESTOOL_SUCCESS) {
282                 return RESTOOL_ERROR;
283             }
284         } else {
285             if (ResourceUtil::FileExist(subPath)) {
286                 continue;
287             }
288             if (!ResourceUtil::CopyFleInner(entry->GetFilePath().GetPath(), subPath)) {
289                 return RESTOOL_ERROR;
290             }
291         }
292     }
293     return RESTOOL_SUCCESS;
294 }
295 
GenerateConfigJson()296 uint32_t ResourcePack::GenerateConfigJson()
297 {
298     if (configJson_.ParseRefence() != RESTOOL_SUCCESS) {
299         return RESTOOL_ERROR;
300     }
301     string outputPath = FileEntry::FilePath(packageParser_.GetOutput())
302         .Append(ConfigParser::GetConfigName()).GetPath();
303     return configJson_.Save(outputPath);
304 }
305 
CheckConfigJson()306 void ResourcePack::CheckConfigJson()
307 {
308     ResourceCheck resourceCheck(configJson_);
309     resourceCheck.CheckConfigJson();
310 }
311 
ScanResources(const vector<string> & inputs,const string & output)312 uint32_t ResourcePack::ScanResources(const vector<string> &inputs, const string &output)
313 {
314     auto &fileManager = FileManager::GetInstance();
315     fileManager.SetModuleName(moduleName_);
316     if (fileManager.ScanModules(inputs, output) != RESTOOL_SUCCESS) {
317         return RESTOOL_ERROR;
318     }
319     return RESTOOL_SUCCESS;
320 }
321 
PackNormal()322 uint32_t ResourcePack::PackNormal()
323 {
324     if (Init() != RESTOOL_SUCCESS) {
325         return RESTOOL_ERROR;
326     }
327 
328     ResourceMerge resourceMerge;
329     if (resourceMerge.Init() != RESTOOL_SUCCESS) {
330         return RESTOOL_ERROR;
331     }
332 
333     if (ScanResources(resourceMerge.GetInputs(), packageParser_.GetOutput()) != RESTOOL_SUCCESS) {
334         return RESTOOL_ERROR;
335     }
336 
337     if (GenerateHeader() != RESTOOL_SUCCESS) {
338         return RESTOOL_ERROR;
339     }
340 
341     if (CopyRawFile(resourceMerge.GetInputs()) != RESTOOL_SUCCESS) {
342         return RESTOOL_ERROR;
343     }
344 
345     if (GenerateConfigJson() != RESTOOL_SUCCESS) {
346         return RESTOOL_ERROR;
347     }
348 
349     if (packageParser_.GetIconCheck()) {
350         CheckConfigJson();
351     }
352 
353     ResourceTable resourceTable;
354     if (!packageParser_.GetDependEntry().empty()) {
355         if (HandleFeature() != RESTOOL_SUCCESS) {
356             return RESTOOL_ERROR;
357         }
358         if (GenerateHeader() != RESTOOL_SUCCESS) {
359             return RESTOOL_ERROR;
360         }
361     }
362 
363     if (resourceTable.CreateResourceTable() != RESTOOL_SUCCESS) {
364         return RESTOOL_ERROR;
365     }
366     return RESTOOL_SUCCESS;
367 }
368 
HandleFeature()369 uint32_t ResourcePack::HandleFeature()
370 {
371     string output = packageParser_.GetOutput();
372     string featureDependEntry = packageParser_.GetDependEntry();
373     if (featureDependEntry.empty()) {
374         return RESTOOL_SUCCESS;
375     }
376     string jsonFile = FileEntry::FilePath(featureDependEntry).Append(CONFIG_JSON).GetPath();
377     ConfigParser entryJson(jsonFile);
378     entryJson.SetDependEntry(true);
379     if (entryJson.Init() != RESTOOL_SUCCESS) {
380         cerr << "Error: config json invalid." << NEW_LINE_PATH << jsonFile << endl;
381         return RESTOOL_ERROR;
382     }
383 
384     int32_t labelId = entryJson.GetAbilityLabelId();
385     int32_t iconId = entryJson.GetAbilityIconId();
386     if (labelId <= 0 || iconId <= 0) {
387         cerr << "Error: Entry MainAbility must have 'icon' and 'label'." << endl;
388         return RESTOOL_ERROR;
389     }
390     string path = FileEntry::FilePath(featureDependEntry).Append(RESOURCE_INDEX_FILE).GetPath();
391     map<int32_t, vector<ResourceItem>> resInfoLocal;
392     ResourceTable resourceTable;
393     if (resourceTable.LoadResTable(path, resInfoLocal) != RESTOOL_SUCCESS) {
394         cerr << "Error: LoadResTable fail." << endl;
395         return RESTOOL_ERROR;
396     }
397     jsonFile = FileEntry::FilePath(output).Append(CONFIG_JSON).GetPath();
398     ConfigParser config(jsonFile);
399     if (config.Init() != RESTOOL_SUCCESS) {
400         cerr << "Error: config json invalid." << NEW_LINE_PATH << jsonFile << endl;
401         return RESTOOL_ERROR;
402     }
403     vector<ResourceItem> items;
404     if (FindResourceItems(resInfoLocal, items, labelId) != RESTOOL_SUCCESS ||
405         HandleLabel(items, config) != RESTOOL_SUCCESS) {
406         return RESTOOL_ERROR;
407     }
408     items.clear();
409     if (FindResourceItems(resInfoLocal, items, iconId) != RESTOOL_SUCCESS ||
410         HandleIcon(items, config) != RESTOOL_SUCCESS) {
411         return RESTOOL_ERROR;
412     }
413     string outputPath = FileEntry::FilePath(output).Append(ConfigParser::GetConfigName()).GetPath();
414     if (config.Save(outputPath) != RESTOOL_SUCCESS) {
415         return RESTOOL_ERROR;
416     }
417     entryJson.SetDependEntry(false);
418     return RESTOOL_SUCCESS;
419 }
420 
FindResourceItems(const map<int32_t,vector<ResourceItem>> & resInfoLocal,vector<ResourceItem> & items,int32_t id) const421 uint32_t ResourcePack::FindResourceItems(const map<int32_t, vector<ResourceItem>> &resInfoLocal,
422                                          vector<ResourceItem> &items, int32_t id) const
423 {
424     auto ret = resInfoLocal.find(id);
425     if (ret == resInfoLocal.end()) {
426         cerr << "Error: FindResourceItems don't found '" << id << "'." << endl;
427         return RESTOOL_ERROR;
428     }
429     ResType type = ResType::INVALID_RES_TYPE;
430     items = ret->second;
431     if (items.empty()) {
432         cerr << "Error: FindResourceItems resource item empty '" << id << "'." << endl;
433         return RESTOOL_ERROR;
434     }
435     for (auto &it : items) {
436         if (type == ResType::INVALID_RES_TYPE) {
437             type = it.GetResType();
438         }
439         if (type != it.GetResType()) {
440             cerr << "Error: FindResourceItems invalid restype '" << ResourceUtil::ResTypeToString(type);
441             cerr << "' vs '"  << ResourceUtil::ResTypeToString(it.GetResType()) << "'." << endl;
442             return RESTOOL_ERROR;
443         }
444     }
445     return RESTOOL_SUCCESS;
446 }
447 
HandleLabel(vector<ResourceItem> & items,ConfigParser & config) const448 uint32_t ResourcePack::HandleLabel(vector<ResourceItem> &items, ConfigParser &config) const
449 {
450     int32_t nextId = 0;
451     string idName;
452     for (auto it : items) {
453         if (it.GetResType() != ResType::STRING) {
454             cerr << "Error: HandleLabel invalid restype '";
455             cerr << ResourceUtil::ResTypeToString(it.GetResType()) << "'." << endl;
456             return RESTOOL_ERROR;
457         }
458         idName = it.GetName() + "_entry";
459         it.SetName(idName);
460         string data(reinterpret_cast<const char *>(it.GetData()));
461         if (!it.SetData(reinterpret_cast<const int8_t *>(data.c_str()), it.GetDataLength()-1)) {
462             return RESTOOL_ERROR;
463         }
464         if (nextId <= 0) {
465             nextId = IdWorker::GetInstance().GenerateId(ResType::STRING, idName);
466         }
467         SaveResourceItem(it, nextId);
468     }
469     string label = "$string:" +idName;
470     config.SetAppLabel(label, nextId);
471     return RESTOOL_SUCCESS;
472 }
473 
CopyIcon(string & dataPath,const string & idName,string & fileName) const474 bool ResourcePack::CopyIcon(string &dataPath, const string &idName, string &fileName) const
475 {
476     string featureDependEntry = packageParser_.GetDependEntry();
477     string source = FileEntry::FilePath(featureDependEntry).Append(dataPath).GetPath();
478     string suffix = FileEntry::FilePath(source).GetExtension();
479     fileName = idName + suffix;
480     string output = packageParser_.GetOutput();
481 #ifdef _WIN32
482     ResourceUtil::StringReplace(dataPath, SEPARATOR, WIN_SEPARATOR);
483 #endif
484     string dstDir = FileEntry::FilePath(output).Append(dataPath).GetParent().GetPath();
485     string dst = FileEntry::FilePath(dstDir).Append(fileName).GetPath();
486     if (!ResourceUtil::CreateDirs(dstDir)) {
487         cerr << "Error: Create Dirs fail '" << dstDir << "'."<< endl;
488         return false;
489     }
490     if (!ResourceUtil::CopyFleInner(source, dst)) {
491         cerr << "Error: copy file fail from '" << source << "' to '" << dst << "'." << endl;
492         return false;
493     }
494     return true;
495 }
496 
HandleIcon(vector<ResourceItem> & items,ConfigParser & config) const497 uint32_t ResourcePack::HandleIcon(vector<ResourceItem> &items, ConfigParser &config) const
498 {
499     int32_t nextId = 0;
500     string idName;
501     for (auto it : items) {
502         if (it.GetResType() != ResType::MEDIA) {
503             cerr << "Error: HandleLabel invalid restype '";
504             cerr << ResourceUtil::ResTypeToString(it.GetResType()) << "'." << endl;
505             return RESTOOL_ERROR;
506         }
507         string dataPath(reinterpret_cast<const char *>(it.GetData()));
508         string::size_type pos = dataPath.find_first_of(SEPARATOR);
509         if (pos == string::npos) {
510             cerr << "Error: HandleIcon invalid '" << dataPath << "'."<< endl;
511             return RESTOOL_ERROR;
512         }
513         dataPath = dataPath.substr(pos + 1);
514         idName = it.GetName() + "_entry";
515         string fileName;
516         if (!CopyIcon(dataPath, idName, fileName)) {
517             return RESTOOL_ERROR;
518         }
519         string data = FileEntry::FilePath(moduleName_).Append(dataPath).GetParent().Append(fileName).GetPath();
520         ResourceUtil::StringReplace(data, WIN_SEPARATOR, SEPARATOR);
521         ResourceItem resourceItem(fileName, it.GetKeyParam(), ResType::MEDIA);
522         resourceItem.SetLimitKey(it.GetLimitKey());
523         if (!resourceItem.SetData(reinterpret_cast<const int8_t *>(data.c_str()), data.length())) {
524             return RESTOOL_ERROR;
525         }
526         if (nextId <= 0) {
527             nextId = IdWorker::GetInstance().GenerateId(ResType::MEDIA, idName);
528         }
529         SaveResourceItem(resourceItem, nextId);
530     }
531     string icon = "$media:" + idName;
532     config.SetAppIcon(icon, nextId);
533     return RESTOOL_SUCCESS;
534 }
535 
SaveResourceItem(const ResourceItem & resourceItem,int32_t nextId) const536 void ResourcePack::SaveResourceItem(const ResourceItem &resourceItem, int32_t nextId) const
537 {
538     map<int32_t, vector<ResourceItem>> resInfo;
539     vector<ResourceItem> vet;
540     vet.push_back(resourceItem);
541     resInfo.insert(make_pair(nextId, vet));
542     FileManager &fileManager = FileManager::GetInstance();
543     fileManager.MergeResourceItem(resInfo);
544 }
545 
PackAppend()546 uint32_t ResourcePack::PackAppend()
547 {
548     ResourceAppend resourceAppend(packageParser_);
549     if (!packageParser_.GetAppend().empty()) {
550         return resourceAppend.Append();
551     }
552     return RESTOOL_SUCCESS;
553 }
554 
PackCombine()555 uint32_t ResourcePack::PackCombine()
556 {
557     if (Init() != RESTOOL_SUCCESS) {
558         return RESTOOL_ERROR;
559     }
560 
561     ResourceAppend resourceAppend(packageParser_);
562     if (resourceAppend.Combine() != RESTOOL_SUCCESS) {
563         return RESTOOL_ERROR;
564     }
565 
566     if (GenerateConfigJson() != RESTOOL_SUCCESS) {
567         return RESTOOL_ERROR;
568     }
569 
570     if (packageParser_.GetIconCheck()) {
571         CheckConfigJsonForCombine(resourceAppend);
572     }
573 
574     if (GenerateHeader() != RESTOOL_SUCCESS) {
575         return RESTOOL_ERROR;
576     }
577     return RESTOOL_SUCCESS;
578 }
579 
CheckConfigJsonForCombine(ResourceAppend & resourceAppend)580 void ResourcePack::CheckConfigJsonForCombine(ResourceAppend &resourceAppend)
581 {
582     ResourceCheck resourceCheck(configJson_, make_shared<ResourceAppend>(resourceAppend));
583     resourceCheck.CheckConfigJsonForCombine();
584 }
585 
586 }
587 }
588 }
589