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