diff --git a/BUILD.gn b/BUILD.gn index 10aa5b1..1aece17 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -19,6 +19,7 @@ ohos_executable("restool") { sources = [ "src/append_compiler.cpp", "src/cmd_parser.cpp", + "src/compression_parser.cpp", "src/config_parser.cpp", "src/factory_resource_compiler.cpp", "src/file_entry.cpp", diff --git a/include/cmd_parser.h b/include/cmd_parser.h index 0a12bb1..7f3772c 100644 --- a/include/cmd_parser.h +++ b/include/cmd_parser.h @@ -56,6 +56,7 @@ public: const TargetConfig &GetTargetConfigValues() const; bool IsTargetConfig() const; const std::vector &GetSysIdDefinedPaths() const; + const std::string &GetCompressionPath() const; private: void InitCommand(); @@ -85,6 +86,7 @@ private: bool IsLongOpt(int argc, char *argv[]) const; uint32_t IconCheck(); uint32_t ParseTargetConfig(const std::string& argValue); + uint32_t AddCompressionPath(const std::string& argValue); static const struct option CMD_OPTS[]; static const std::string CMD_PARAMS; @@ -109,6 +111,7 @@ private: TargetConfig targetConfig_; bool isTtargetConfig_; std::vector sysIdDefinedPaths_; + std::string compressionPath_; }; template @@ -150,6 +153,7 @@ void CmdParser::ShowUseage() std::cout << " module resources are independently built in the FA model.\n"; std::cout << " --icon-check Enable the PNG image verification function for icons and startwindows.\n"; std::cout << " --target-config When used with '-i', selective compilation is supported.\n"; + std::cout << " --compressed-config opt-compression.json path.\n"; } template diff --git a/include/compression_parser.h b/include/compression_parser.h new file mode 100644 index 0000000..bcc69ee --- /dev/null +++ b/include/compression_parser.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OHOS_RESTOOL_COMPRESSION_PARSER_H +#define OHOS_RESTOOL_COMPRESSION_PARSER_H + +#include +#include "resource_util.h" + +namespace OHOS { +namespace Global { +namespace Restool { +class CompressionParser { +public: + static std::shared_ptr GetCompressionParser(const std::string &filePath); + static std::shared_ptr GetCompressionParser(); + CompressionParser(); + explicit CompressionParser(const std::string &filePath); + virtual ~CompressionParser(); + uint32_t Init(); +private: + bool ParseContext(const cJSON *contextNode); + bool ParseCompression(const cJSON *compressionNode); + bool ParseFilter(const cJSON *filterNode); + std::string filePath_; + std::string extensionPath_; + bool mediaSwitch_; + cJSON *root_; +}; +} +} +} +#endif \ No newline at end of file diff --git a/include/resource_data.h b/include/resource_data.h index b52b9bd..8b410b7 100644 --- a/include/resource_data.h +++ b/include/resource_data.h @@ -41,7 +41,7 @@ const static std::string SOLUTIONS_ARROW = "> "; const static std::string LONG_PATH_HEAD = "\\\\?\\"; const static int32_t VERSION_MAX_LEN = 128; const static int32_t INT_TO_BYTES = sizeof(uint32_t); -static const int8_t RESTOOL_VERSION[VERSION_MAX_LEN] = { "Restool 5.008" }; +static const int8_t RESTOOL_VERSION[VERSION_MAX_LEN] = { "Restool 5.009" }; const static int32_t TAG_LEN = 4; enum class KeyType { @@ -129,6 +129,7 @@ enum Option { ICON_CHECK = 4, TARGET_CONFIG = 5, DEFINED_SYSIDS = 6, + COMPRESSED_CONFIG = 7, STARTID = 'e', FORCEWRITE = 'f', HELP = 'h', diff --git a/src/cmd_parser.cpp b/src/cmd_parser.cpp index 82d75cf..8c09d3a 100644 --- a/src/cmd_parser.cpp +++ b/src/cmd_parser.cpp @@ -43,6 +43,7 @@ const struct option PackageParser::CMD_OPTS[] = { { "icon-check", no_argument, nullptr, Option::ICON_CHECK}, { "target-config", required_argument, nullptr, Option::TARGET_CONFIG}, { "defined-sysids", required_argument, nullptr, Option::DEFINED_SYSIDS}, + { "compressed-config", required_argument, nullptr, Option::COMPRESSED_CONFIG}, { 0, 0, 0, 0}, }; @@ -427,6 +428,21 @@ bool PackageParser::IsAscii(const string& argValue) const return true; } +uint32_t PackageParser::AddCompressionPath(const std::string& argValue) +{ + if (!compressionPath_.empty()) { + cerr << "Error: double opt-compression.json " << compressionPath_ << " vs " << argValue << endl; + return RESTOOL_ERROR; + } + compressionPath_ = argValue; + return RESTOOL_SUCCESS; +} + +const std::string &PackageParser::GetCompressionPath() const +{ + return compressionPath_; +} + void PackageParser::InitCommand() { using namespace placeholders; @@ -448,6 +464,7 @@ void PackageParser::InitCommand() handles_.emplace(Option::ICON_CHECK, [this](const string &) -> uint32_t { return IconCheck(); }); handles_.emplace(Option::TARGET_CONFIG, bind(&PackageParser::ParseTargetConfig, this, _1)); handles_.emplace(Option::DEFINED_SYSIDS, bind(&PackageParser::AddSysIdDefined, this, _1)); + handles_.emplace(Option::COMPRESSED_CONFIG, bind(&PackageParser::AddCompressionPath, this, _1)); } uint32_t PackageParser::HandleProcess(int c, const string& argValue) diff --git a/src/compression_parser.cpp b/src/compression_parser.cpp new file mode 100644 index 0000000..d89602b --- /dev/null +++ b/src/compression_parser.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "compression_parser.h" +#include +#include +#include "restool_errors.h" + +namespace OHOS { +namespace Global { +namespace Restool { +using namespace std; +static shared_ptr compressionParseMgr = nullptr; +static once_flag compressionParserMgrFlag; + +CompressionParser::CompressionParser() + : filePath_(""), extensionPath_(""), mediaSwitch_(false), root_(nullptr) +{ +} + +CompressionParser::CompressionParser(const string &filePath) + : filePath_(filePath), extensionPath_(""), mediaSwitch_(false), root_(nullptr) +{ +} + +CompressionParser::~CompressionParser() +{ + if (root_) { + cJSON_Delete(root_); + } +} + +shared_ptr CompressionParser::GetCompressionParser(const string &filePath) +{ + call_once(compressionParserMgrFlag, [&] { + compressionParseMgr = make_shared(filePath); + }); + return compressionParseMgr; +} + +shared_ptr CompressionParser::GetCompressionParser() +{ + if (!compressionParseMgr) { + compressionParseMgr = make_shared(); + } + return compressionParseMgr; +} + +uint32_t CompressionParser::Init() +{ + if (!ResourceUtil::OpenJsonFile(filePath_, &root_)) { + return RESTOOL_ERROR; + } + if (!root_ || !cJSON_IsObject(root_)) { + cerr << "Error: JSON file parsing failed, please check the JSON file." << NEW_LINE_PATH << filePath_ << endl; + return RESTOOL_ERROR; + } + cJSON *compressionNode = cJSON_GetObjectItem(root_, "compression"); + if (!ParseCompression(compressionNode)) { + return RESTOOL_ERROR; + } + if (!mediaSwitch_) { + return RESTOOL_SUCCESS; + } + cJSON *contextNode = cJSON_GetObjectItem(root_, "context"); + cJSON *filterNode = cJSON_GetObjectItem(compressionNode, "filter"); + if (!ParseContext(contextNode) || !ParseFilter(filterNode)) { + cerr << NEW_LINE_PATH << filePath_ << endl; + return RESTOOL_ERROR; + } + return RESTOOL_SUCCESS; +} + +bool CompressionParser::ParseCompression(const cJSON *compressionNode) +{ + if (!compressionNode) { + cerr << "Warning: get 'compression' node is empty, the compiled images are not transcoded."; + cerr << NEW_LINE_PATH << filePath_ << endl; + return true; + } + if (!cJSON_IsObject(compressionNode)) { + cerr << "Error: 'compression' must be object." << NEW_LINE_PATH << filePath_ << endl; + return false; + } + cJSON *mediaNode = cJSON_GetObjectItem(compressionNode, "media"); + if (!mediaNode) { + cerr << "Warning: get 'media' node is empty, the compiled images are not transcoded."; + cerr << NEW_LINE_PATH << filePath_ << endl; + return true; + } + if (!cJSON_IsObject(mediaNode)) { + cerr << "Error: 'media' must be object." << NEW_LINE_PATH << filePath_ << endl; + return false; + } + cJSON *enableNode = cJSON_GetObjectItem(mediaNode, "enable"); + if (!enableNode) { + cerr << "Warning: get 'enable' node is empty, the compiled images are not transcoded."; + cerr << NEW_LINE_PATH << filePath_ << endl; + return true; + } + if (!cJSON_IsBool(enableNode)) { + cerr << "Error: 'enable' must be bool." << NEW_LINE_PATH << filePath_ << endl; + return false; + } + mediaSwitch_ = cJSON_IsTrue(enableNode); + return true; +} + +bool CompressionParser::ParseContext(const cJSON *contextNode) +{ + if (!contextNode) { + cerr << "Error: if image transcoding is supported, the 'context' node cannot be empty."; + return false; + } + if (!cJSON_IsObject(contextNode)) { + cerr << "Error: 'context' must be object."; + return false; + } + cJSON *extensionPathNode = cJSON_GetObjectItem(contextNode, "extensionPath"); + if (!extensionPathNode) { + cerr << "Error: if image transcoding is supported, the 'extensionPath' node cannot be empty."; + return false; + } + if (!cJSON_IsString(extensionPathNode)) { + cerr << "Error: 'extensionPath' must be string."; + return false; + } + extensionPath_ = extensionPathNode->valuestring; + if (extensionPath_.empty()) { + cerr << "Error: 'extensionPath' value cannot be empty."; + return false; + } + return true; +} + +bool CompressionParser::ParseFilter(const cJSON *filterNode) +{ + if (!filterNode) { + cerr << "Error: if image transcoding is supported, the 'filter' node cannot be empty."; + return false; + } + if (!cJSON_IsArray(filterNode)) { + cerr << "Error: 'filter' must be array."; + return false; + } + if (cJSON_GetArraySize(filterNode) == 0) { + cerr << "Error: 'filter' value cannot be empty."; + return false; + } + for (cJSON *item = filterNode->child; item; item = item->next) { + if (!cJSON_IsObject(item)) { + cerr << "Error: 'filter' value type must be object."; + return false; + } + cJSON *methodNode = cJSON_GetObjectItem(item, "method"); + if (!methodNode) { + cerr << "Error: if image transcoding is supported, the 'method' node cannot be empty."; + return false; + } + if (!cJSON_IsObject(methodNode)) { + cerr << "Error: 'method' must be object."; + return false; + } + } + return true; +} +} +} +} diff --git a/src/resconfig_parser.cpp b/src/resconfig_parser.cpp index 815b7ec..0efafcd 100644 --- a/src/resconfig_parser.cpp +++ b/src/resconfig_parser.cpp @@ -90,6 +90,8 @@ void ResConfigParser::InitFileListCommand(HandleBack callback) Option::ICON_CHECK, callback)); fileListHandles_.emplace("definedSysIds", bind(&ResConfigParser::GetString, this, _1, Option::DEFINED_SYSIDS, callback)); + fileListHandles_.emplace("compression", bind(&ResConfigParser::GetString, this, _1, + Option::COMPRESSED_CONFIG, callback)); } uint32_t ResConfigParser::GetString(const cJSON *node, int c, HandleBack callback) diff --git a/src/resource_pack.cpp b/src/resource_pack.cpp index c3d0cb0..0dab305 100644 --- a/src/resource_pack.cpp +++ b/src/resource_pack.cpp @@ -22,6 +22,7 @@ #include "resource_check.h" #include "resource_merge.h" #include "resource_table.h" +#include "compression_parser.h" namespace OHOS { namespace Global { @@ -36,7 +37,12 @@ uint32_t ResourcePack::Package() if (!packageParser_.GetAppend().empty()) { return PackAppend(); } - + if (!packageParser_.GetCompressionPath().empty()) { + auto compressionMgr = CompressionParser::GetCompressionParser(packageParser_.GetCompressionPath()); + if (compressionMgr->Init() != RESTOOL_SUCCESS) { + return RESTOOL_ERROR; + } + } if (packageParser_.GetCombine()) { return PackCombine(); }