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