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