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 28namespace OHOS { 29namespace Global { 30namespace Restool { 31using namespace std; 32ResourcePack::ResourcePack(const PackageParser &packageParser):packageParser_(packageParser) 33{ 34} 35 36uint32_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 48uint32_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 61uint32_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 78uint32_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 119void 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 127uint32_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 146uint32_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 165uint32_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 189uint32_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 209uint32_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 230uint32_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 255uint32_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 265void ResourcePack::CheckConfigJson() 266{ 267 ResourceCheck resourceCheck(configJson_.GetCheckNode()); 268 resourceCheck.CheckConfigJson(); 269} 270 271uint32_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 281uint32_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 306uint32_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 344uint32_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 396uint32_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 423uint32_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 452bool 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 475uint32_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 514void 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 524uint32_t ResourcePack::PackAppend() 525{ 526 ResourceAppend resourceAppend(packageParser_); 527 if (!packageParser_.GetAppend().empty()) { 528 return resourceAppend.Append(); 529 } 530 return RESTOOL_SUCCESS; 531} 532 533uint32_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 558void ResourcePack::CheckConfigJsonForCombine(ResourceAppend &resourceAppend) 559{ 560 ResourceCheck resourceCheck(configJson_.GetCheckNode(), make_shared<ResourceAppend>(resourceAppend)); 561 resourceCheck.CheckConfigJsonForCombine(); 562} 563 564} 565} 566} 567