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_util.h"
17 #include <algorithm>
18 #include <cstdlib>
19 #include <fstream>
20 #include <mutex>
21 #include <iostream>
22 #include <iomanip>
23 #include <regex>
24 #include <sstream>
25 #include "file_entry.h"
26
27 namespace OHOS {
28 namespace Global {
29 namespace Restool {
30 using namespace std;
31 const map<string, ResourceUtil::IgnoreType> ResourceUtil::IGNORE_FILE_REGEX = {
32 { "\\.git", IgnoreType::IGNORE_ALL },
33 { "\\.svn", IgnoreType::IGNORE_ALL },
34 { ".+\\.scc", IgnoreType::IGNORE_ALL },
35 { "\\.ds_store", IgnoreType::IGNORE_ALL },
36 { "desktop\\.ini", IgnoreType::IGNORE_ALL },
37 { "picasa\\.ini", IgnoreType::IGNORE_ALL },
38 { "\\..+", IgnoreType::IGNORE_ALL },
39 { "cvs", IgnoreType::IGNORE_ALL },
40 { "thumbs\\.db", IgnoreType::IGNORE_ALL },
41 { ".+~", IgnoreType::IGNORE_ALL }
42 };
43
44 static std::mutex fileMutex_;
45
Split(const string &str, vector<string> &out, const string &splitter)46 void ResourceUtil::Split(const string &str, vector<string> &out, const string &splitter)
47 {
48 string::size_type len = str.size();
49 string::size_type begin = 0;
50 string::size_type end = str.find(splitter, begin);
51 while (end != string::npos) {
52 string sub = str.substr(begin, end - begin);
53 out.push_back(sub);
54 begin = end + splitter.size();
55 if (begin >= len) {
56 break;
57 }
58 end = str.find(splitter, begin);
59 }
60
61 if (begin < len) {
62 out.push_back(str.substr(begin));
63 }
64 }
65
FileExist(const string &path)66 bool ResourceUtil::FileExist(const string &path)
67 {
68 return FileEntry::Exist(path);
69 }
70
RmoveAllDir(const string &path)71 bool ResourceUtil::RmoveAllDir(const string &path)
72 {
73 return FileEntry::RemoveAllDir(path);
74 }
75
RmoveFile(const string &path)76 bool ResourceUtil::RmoveFile(const string &path)
77 {
78 return FileEntry::RemoveFile(path);
79 }
80
OpenJsonFile(const string &path, cJSON **root)81 bool ResourceUtil::OpenJsonFile(const string &path, cJSON **root)
82 {
83 ifstream ifs(FileEntry::AdaptLongPath(path), ios::binary);
84 if (!ifs.is_open()) {
85 cerr << "Error: open json failed '" << path << "', reason: " << strerror(errno) << endl;
86 return false;
87 }
88
89 string jsonString((istreambuf_iterator<char>(ifs)), istreambuf_iterator<char>());
90 *root = cJSON_Parse(jsonString.c_str());
91 if (!*root) {
92 cerr << "Error: cJSON_Parse failed, please check the JSON file." << NEW_LINE_PATH << path << endl;
93 cerr << SOLUTIONS << endl;
94 cerr << SOLUTIONS_ARROW << "Check the JSON file and delete unnecessary commas (,)." << endl;
95 cerr << SOLUTIONS_ARROW << "Check the JSON file to make sure the root bracket is {}" << endl;
96 ifs.close();
97 return false;
98 }
99 ifs.close();
100 return true;
101 }
102
SaveToJsonFile(const string &path, const cJSON *root)103 bool ResourceUtil::SaveToJsonFile(const string &path, const cJSON *root)
104 {
105 ofstream out(FileEntry::AdaptLongPath(path), ofstream::out | ofstream::binary);
106 if (!out.is_open()) {
107 cerr << "Error: SaveToJsonFile open failed '" << path <<"', reason: " << strerror(errno) << endl;
108 return false;
109 }
110 char *jsonString = cJSON_Print(root);
111 out << jsonString;
112 free(jsonString);
113
114 out.close();
115 return true;
116 }
117
GetResTypeByDir(const string &name)118 ResType ResourceUtil::GetResTypeByDir(const string &name)
119 {
120 auto ret = g_fileClusterMap.find(name);
121 if (ret == g_fileClusterMap.end()) {
122 return ResType::INVALID_RES_TYPE;
123 }
124 return ret->second;
125 }
126
ResTypeToString(ResType type)127 string ResourceUtil::ResTypeToString(ResType type)
128 {
129 auto ret = find_if(g_fileClusterMap.begin(), g_fileClusterMap.end(), [type](auto iter) {
130 return iter.second == type;
131 });
132 if (ret != g_fileClusterMap.end()) {
133 return ret->first;
134 }
135
136 ret = find_if(g_contentClusterMap.begin(), g_contentClusterMap.end(), [type](auto iter) {
137 return iter.second == type;
138 });
139 if (ret != g_contentClusterMap.end()) {
140 return ret->first;
141 }
142 return "";
143 }
144
GetIdName(const string &name, ResType type)145 string ResourceUtil::GetIdName(const string &name, ResType type)
146 {
147 if (type != ResType::MEDIA && type != ResType::PROF) {
148 return name;
149 }
150
151 string::size_type pos = name.find_last_of(".");
152 if (pos != string::npos) {
153 return name.substr(0, pos);
154 }
155 return name;
156 }
157
ComposeStrings(const vector<string> &contents, bool addNull)158 string ResourceUtil::ComposeStrings(const vector<string> &contents, bool addNull)
159 {
160 string result;
161 for (const auto &iter : contents) {
162 if (iter.length() > UINT16_MAX) {
163 return "";
164 }
165
166 uint16_t size = iter.length();
167 if (addNull) {
168 size += sizeof(char);
169 }
170 result.append(sizeof(char), (size & 0xff));
171 result.append(sizeof(char), (size >> 8)); // Move 8 bits to the right
172 result.append(iter);
173 result.append(sizeof(char), '\0');
174 if (result.length() > UINT16_MAX) {
175 return "";
176 }
177 }
178 return result;
179 }
180
DecomposeStrings(const string &content)181 vector<string> ResourceUtil::DecomposeStrings(const string &content)
182 {
183 vector<string> result;
184 size_t length = content.length();
185 size_t pos = 0;
186 const size_t HEAD_LENGTH = 2;
187 while (pos < length) {
188 if (pos + HEAD_LENGTH >= length) {
189 result.clear();
190 return result;
191 }
192 uint16_t size = (content[pos] & 0xff) | ((content[pos + 1] & 0xff) << 8); // Move 8 bits to the left
193 pos += HEAD_LENGTH;
194
195 if (pos + size >= length) {
196 result.clear();
197 return result;
198 }
199 string buffer = content.substr(pos, size);
200 result.push_back(buffer);
201 pos += size + sizeof(char);
202 }
203 return result;
204 }
205
GetResTypeFromString(const string &type)206 ResType ResourceUtil::GetResTypeFromString(const string &type)
207 {
208 ResType resType = GetResTypeByDir(type);
209 if (resType != ResType::INVALID_RES_TYPE) {
210 return resType;
211 }
212
213 auto ret = g_contentClusterMap.find(type);
214 if (ret != g_contentClusterMap.end()) {
215 return ret->second;
216 }
217 return ResType::INVALID_RES_TYPE;
218 }
219
CopyFileInner(const string &src, const string &dst)220 bool ResourceUtil::CopyFileInner(const string &src, const string &dst)
221 {
222 return FileEntry::CopyFileInner(src, dst);
223 }
224
CreateDirs(const string &filePath)225 bool ResourceUtil::CreateDirs(const string &filePath)
226 {
227 std::lock_guard<std::mutex> lock(fileMutex_);
228 if (FileExist(filePath)) {
229 return true;
230 }
231
232 if (!FileEntry::CreateDirs(filePath)) {
233 cerr << "Error: create dir '" << filePath << "' failed, reason:" << strerror(errno) << endl;
234 return false;
235 }
236 return true;
237 }
238
IsIgnoreFile(const string &filename, bool isFile)239 bool ResourceUtil::IsIgnoreFile(const string &filename, bool isFile)
240 {
241 string key = filename;
242 transform(key.begin(), key.end(), key.begin(), ::tolower);
243 for (const auto &iter : IGNORE_FILE_REGEX) {
244 if ((iter.second == IgnoreType::IGNORE_FILE && !isFile) ||
245 (iter.second == IgnoreType::IGNORE_DIR && isFile)) {
246 continue;
247 }
248 if (regex_match(key, regex(iter.first))) {
249 return true;
250 }
251 }
252 return false;
253 }
254
GenerateHash(const string &key)255 string ResourceUtil::GenerateHash(const string &key)
256 {
257 hash<string> hash_function;
258 return to_string(hash_function(key));
259 }
260
RealPath(const string &path)261 string ResourceUtil::RealPath(const string &path)
262 {
263 return FileEntry::RealPath(path);
264 }
265
IslegalPath(const string &path)266 bool ResourceUtil::IslegalPath(const string &path)
267 {
268 return path == "element" || path == "media" || path == "profile";
269 }
270
StringReplace(string &sourceStr, const string &oldStr, const string &newStr)271 void ResourceUtil::StringReplace(string &sourceStr, const string &oldStr, const string &newStr)
272 {
273 string::size_type pos = 0;
274 string::size_type oldSize = oldStr.size();
275 string::size_type newSize = newStr.size();
276 while ((pos = sourceStr.find(oldStr, pos)) != string::npos) {
277 sourceStr.replace(pos, oldSize, newStr.c_str());
278 pos += newSize;
279 }
280 }
281
GetLocaleLimitkey(const KeyParam &KeyParam)282 string ResourceUtil::GetLocaleLimitkey(const KeyParam &KeyParam)
283 {
284 string str(reinterpret_cast<const char *>(&KeyParam.value));
285 reverse(str.begin(), str.end());
286 return str;
287 }
288
GetDeviceTypeLimitkey(const KeyParam &KeyParam)289 string ResourceUtil::GetDeviceTypeLimitkey(const KeyParam &KeyParam)
290 {
291 auto ret = find_if(g_deviceMap.begin(), g_deviceMap.end(), [KeyParam](const auto &iter) {
292 return KeyParam.value == static_cast<const uint32_t>(iter.second);
293 });
294 if (ret == g_deviceMap.end()) {
295 return string();
296 }
297 return ret->first;
298 }
299
GetResolutionLimitkey(const KeyParam &KeyParam)300 string ResourceUtil::GetResolutionLimitkey(const KeyParam &KeyParam)
301 {
302 auto ret = find_if(g_resolutionMap.begin(), g_resolutionMap.end(), [KeyParam](const auto &iter) {
303 return KeyParam.value == static_cast<const uint32_t>(iter.second);
304 });
305 if (ret == g_resolutionMap.end()) {
306 return string();
307 }
308 return ret->first;
309 }
310
GetKeyParamValue(const KeyParam &KeyParam)311 string ResourceUtil::GetKeyParamValue(const KeyParam &KeyParam)
312 {
313 string val;
314 switch (KeyParam.keyType) {
315 case KeyType::ORIENTATION:
316 val = KeyParam.value == static_cast<const uint32_t>(OrientationType::VERTICAL) ? "vertical" : "horizontal";
317 break;
318 case KeyType::NIGHTMODE:
319 val = KeyParam.value == static_cast<const uint32_t>(NightMode::DARK) ? "dark" : "light";
320 break;
321 case KeyType::DEVICETYPE:
322 val = GetDeviceTypeLimitkey(KeyParam);
323 break;
324 case KeyType::RESOLUTION:
325 val = GetResolutionLimitkey(KeyParam);
326 break;
327 case KeyType::LANGUAGE:
328 case KeyType::REGION:
329 val = GetLocaleLimitkey(KeyParam);
330 break;
331 default:
332 val = to_string(KeyParam.value);
333 break;
334 }
335 return val;
336 }
337
PaserKeyParam(const vector<KeyParam> &keyParams)338 string ResourceUtil::PaserKeyParam(const vector<KeyParam> &keyParams)
339 {
340 if (keyParams.size() == 0) {
341 return "base";
342 }
343 string result;
344 for (const auto &keyparam : keyParams) {
345 string limitKey = GetKeyParamValue(keyparam);
346 if (limitKey.empty()) {
347 continue;
348 }
349 if (keyparam.keyType == KeyType::MCC) {
350 limitKey = "mcc" + limitKey;
351 }
352 if (keyparam.keyType == KeyType::MNC) {
353 limitKey = "mnc" + limitKey;
354 }
355 if (keyparam.keyType == KeyType::REGION || keyparam.keyType == KeyType::MNC) {
356 result = result + "_" + limitKey;
357 } else {
358 result = result + "-" + limitKey;
359 }
360 }
361 if (!result.empty()) {
362 result = result.substr(1);
363 }
364 return result;
365 }
366
DecToHexStr(const uint32_t i)367 string ResourceUtil::DecToHexStr(const uint32_t i)
368 {
369 stringstream ot;
370 string result;
371 ot << setiosflags(ios::uppercase) << "0x" << hex << setw(8) << setfill('0') << i; // 0x expadding 8 bit
372 ot >> result;
373 return result;
374 }
375
CheckHexStr(const string &hex)376 bool ResourceUtil::CheckHexStr(const string &hex)
377 {
378 if (regex_match(hex, regex("^0[xX][0-9a-fA-F]{8}"))) {
379 return true;
380 }
381 return false;
382 }
383
GetAllRestypeString()384 string ResourceUtil::GetAllRestypeString()
385 {
386 string result;
387 for (auto iter = g_contentClusterMap.begin(); iter != g_contentClusterMap.end(); ++iter) {
388 result = result + "," + iter->first;
389 }
390 return result;
391 }
392
GetBaseElementPath(const string input)393 FileEntry::FilePath ResourceUtil::GetBaseElementPath(const string input)
394 {
395 return FileEntry::FilePath(input).Append("base").Append("element");
396 }
397
GetMainPath(const string input)398 FileEntry::FilePath ResourceUtil::GetMainPath(const string input)
399 {
400 return FileEntry::FilePath(input).GetParent();
401 }
402
GetNormalSize(const vector<KeyParam> &keyParams, uint32_t index)403 uint32_t ResourceUtil::GetNormalSize(const vector<KeyParam> &keyParams, uint32_t index)
404 {
405 string device;
406 string dpi;
407 if (keyParams.size() == 0) {
408 device = "phone";
409 dpi = "sdpi";
410 }
411 for (const auto &keyparam : keyParams) {
412 string limitKey = GetKeyParamValue(keyparam);
413 if (limitKey.empty()) {
414 continue;
415 }
416 if (keyparam.keyType == KeyType::DEVICETYPE) {
417 device = limitKey;
418 } else if (keyparam.keyType == KeyType::RESOLUTION) {
419 dpi = limitKey;
420 }
421 }
422 if (device.empty()) {
423 device = "phone";
424 }
425 if (dpi.empty()) {
426 dpi = "sdpi";
427 }
428 if (device != "phone" && device != "tablet") {
429 return 0;
430 }
431 return g_normalIconMap.find(dpi + "-" + device)->second[index];
432 }
433
isUnicodeInPlane15or16(int unicode)434 bool ResourceUtil::isUnicodeInPlane15or16(int unicode)
435 {
436 return (unicode >= 0xF0000 && unicode <= 0xFFFFF) || (unicode >= 0x100000 && unicode <= 0x10FFFF);
437 }
438
RemoveSpaces(string &str)439 void ResourceUtil::RemoveSpaces(string &str)
440 {
441 str.erase(0, str.find_first_not_of(" "));
442 str.erase(str.find_last_not_of(" ") + 1); // move back one place
443 }
444
IsIntValue(const cJSON *node)445 bool ResourceUtil::IsIntValue(const cJSON *node)
446 {
447 if (node && cJSON_IsNumber(node)) {
448 double num = node->valuedouble;
449 if (num == static_cast<int>(num)) {
450 return true;
451 } else {
452 return false;
453 }
454 }
455 return false;
456 }
457
IsValidName(const string &name)458 bool ResourceUtil::IsValidName(const string &name)
459 {
460 if (!regex_match(name, regex("[a-zA-Z0-9_]+"))) {
461 cerr << "Error: the name '" << name << "' can only contain [a-zA-Z0-9_]." << endl;
462 return false;
463 }
464 return true;
465 }
466
PrintWarningMsg(vector<pair<ResType, string>> &noBaseResource)467 void ResourceUtil::PrintWarningMsg(vector<pair<ResType, string>> &noBaseResource)
468 {
469 for (const auto &item : noBaseResource) {
470 cerr << "Warning: the " << ResourceUtil::ResTypeToString(item.first);
471 cerr << " of '" << item.second << "' does not have a base resource." << endl;
472 }
473 }
474 }
475 }
476 }
477