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