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