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