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