1/*
2 * Copyright (c) 2021 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 "util.h"
17
18#include <event_handler.h>
19#include <fstream>
20#include <securec.h>
21#include <sstream>
22
23namespace OHOS {
24constexpr const float HALF = 2.0;
25constexpr const float RADIO = 360.0;
26
27void PostTask(std::function<void()> func, uint32_t delayTime)
28{
29    auto handler = AppExecFwk::EventHandler::Current();
30    if (handler) {
31        handler->PostTask(func, delayTime);
32    }
33}
34
35bool IsFileExisted(const std::string& filePath)
36{
37    if (filePath.empty()) {
38        LOGE("check filepath is empty");
39        return false;
40    }
41    char newpath[PATH_MAX + 1] = { 0x00 };
42    if (strlen(filePath.c_str()) > PATH_MAX || realpath(filePath.c_str(), newpath) == nullptr) {
43        LOGE("check filepath fail! %{public}s %{public}d %{public}s", filePath.c_str(), errno, ::strerror(errno));
44        return false;
45    }
46    struct stat info = {0};
47    if (stat(newpath, &info) != 0) {
48        LOGE("stat filepath fail! %{public}s %{public}d %{public}s", filePath.c_str(), errno, ::strerror(errno));
49        return false;
50    }
51    return true;
52}
53
54bool ParseBootConfig(const std::string& path, int32_t& duration, bool& isCompatible, bool& isMultiDisplay,
55    std::vector<BootAnimationConfig>& configs)
56{
57    char newpath[PATH_MAX + 1] = { 0x00 };
58    if (strlen(path.c_str()) > PATH_MAX || realpath(path.c_str(), newpath) == nullptr) {
59        LOGE("check config path fail! %{public}s %{public}d %{public}s", path.c_str(), errno, ::strerror(errno));
60        return false;
61    }
62
63    std::ifstream configFile;
64    configFile.open(newpath);
65    std::stringstream JFilterParamsStream;
66    JFilterParamsStream << configFile.rdbuf();
67    configFile.close();
68    std::string JParamsString = JFilterParamsStream.str();
69
70    cJSON* overallData = cJSON_Parse(JParamsString.c_str());
71    if (overallData == nullptr) {
72        LOGE("can not parse config to json");
73        return false;
74    }
75    cJSON_bool isNewConfig = cJSON_HasObjectItem(overallData, "screen_config");
76    if (!isNewConfig) {
77        isCompatible = true;
78        ParseOldConfigFile(overallData, configs);
79    } else {
80        ParseNewConfigFile(overallData, isMultiDisplay, configs);
81    }
82    ParseBootDuration(overallData, duration);
83    cJSON_Delete(overallData);
84    return true;
85}
86
87void ParseOldConfigFile(cJSON* data, std::vector<BootAnimationConfig>& configs)
88{
89    LOGD("ParseOldConfigFile");
90    BootAnimationConfig config;
91    cJSON* custPicPath = cJSON_GetObjectItem(data, "cust.bootanimation.pics");
92    if (custPicPath != nullptr && cJSON_IsString(custPicPath)) {
93        config.picZipPath = custPicPath->valuestring;
94        LOGI("cust piczip path: %{public}s", config.picZipPath.c_str());
95    }
96    cJSON* custSoundPath = cJSON_GetObjectItem(data, "cust.bootanimation.sounds");
97    if (custSoundPath != nullptr && cJSON_IsString(custSoundPath)) {
98        config.soundPath = custSoundPath->valuestring;
99        LOGI("cust sound path: %{public}s", config.soundPath.c_str());
100    }
101    cJSON* custVideoDefaultPath = cJSON_GetObjectItem(data, "cust.bootanimation.video");
102    if (custVideoDefaultPath != nullptr && cJSON_IsString(custVideoDefaultPath)) {
103        config.videoDefaultPath = custVideoDefaultPath->valuestring;
104        LOGI("cust video path: %{public}s", config.videoDefaultPath.c_str());
105    }
106    cJSON* custVideoExtraPath = cJSON_GetObjectItem(data, "cust.bootanimation.video.extra");
107    if (custVideoExtraPath != nullptr && cJSON_IsString(custVideoExtraPath)) {
108        config.videoExtraPath = custVideoExtraPath->valuestring;
109        LOGI("cust extra video path: %{public}s", config.videoExtraPath.c_str());
110    }
111    cJSON* rotateScreenJson = cJSON_GetObjectItem(data, "cust.bootanimation.rotate.screenid");
112    if (rotateScreenJson != nullptr && cJSON_IsString(rotateScreenJson)) {
113        config.rotateScreenId = std::atoi(rotateScreenJson->valuestring);
114        LOGI("cust rotateScreenId: %{public}d", config.rotateScreenId);
115    }
116    cJSON* rotateDegreeJson = cJSON_GetObjectItem(data, "cust.bootanimation.rotate.degree");
117    if (rotateDegreeJson != nullptr && cJSON_IsString(rotateDegreeJson)) {
118        config.rotateDegree = std::atoi(rotateDegreeJson->valuestring);
119        LOGI("cust rotateDegree: %{public}d", config.rotateDegree);
120    }
121    configs.emplace_back(config);
122}
123
124void ParseNewConfigFile(cJSON* data, bool& isMultiDisplay, std::vector<BootAnimationConfig>& configs)
125{
126    LOGD("ParseNewConfigFile");
127    cJSON* isSupport = cJSON_GetObjectItem(data, "cust.bootanimation.multi_display");
128    if (isSupport != nullptr && cJSON_IsBool(isSupport)) {
129        if (cJSON_IsTrue(isSupport)) {
130            isMultiDisplay = true;
131        }
132    }
133    LOGI("isMultiDisplay: %{public}d", isMultiDisplay);
134
135    cJSON* screens = cJSON_GetObjectItem(data, "screen_config");
136    if (screens != nullptr) {
137        BootAnimationConfig config;
138        cJSON* item = screens->child;
139        while (item != nullptr) {
140            cJSON* screenIdJson = cJSON_GetObjectItem(item, "cust.bootanimation.screen_id");
141            if (screenIdJson != nullptr && cJSON_IsString(screenIdJson)) {
142                config.screenId = std::strtoul(screenIdJson->valuestring, nullptr, 0);
143                LOGI("screenId: " BPUBU64 "", config.screenId);
144            }
145            cJSON* custPicPath = cJSON_GetObjectItem(item, "cust.bootanimation.pics");
146            if (custPicPath != nullptr && cJSON_IsString(custPicPath)) {
147                config.picZipPath = custPicPath->valuestring;
148                LOGI("cust piczip path: %{public}s", config.picZipPath.c_str());
149            }
150            cJSON* custSoundPath = cJSON_GetObjectItem(item, "cust.bootanimation.sounds");
151            if (custSoundPath != nullptr && cJSON_IsString(custSoundPath)) {
152                config.soundPath = custSoundPath->valuestring;
153                LOGI("cust sound path: %{public}s", config.soundPath.c_str());
154            }
155            cJSON* custVideoDefaultPath = cJSON_GetObjectItem(item, "cust.bootanimation.video_default");
156            if (custVideoDefaultPath != nullptr && cJSON_IsString(custVideoDefaultPath)) {
157                config.videoDefaultPath = custVideoDefaultPath->valuestring;
158                LOGI("cust default video path: %{public}s", config.videoDefaultPath.c_str());
159            }
160            cJSON* rotateDegreeJson = cJSON_GetObjectItem(item, "cust.bootanimation.rotate_degree");
161            if (rotateDegreeJson != nullptr && cJSON_IsString(rotateDegreeJson)) {
162                config.rotateDegree = std::atoi(rotateDegreeJson->valuestring);
163                LOGI("cust rotateDegree: %{public}d", config.rotateDegree);
164            }
165            cJSON* extraVideoPath = cJSON_GetObjectItem(item, "cust.bootanimation.video_extensions");
166            if (extraVideoPath != nullptr && cJSON_IsArray(extraVideoPath)) {
167                ParseVideoExtraPath(extraVideoPath, config);
168            }
169            configs.emplace_back(config);
170            item = item->next;
171        }
172    }
173}
174
175void ParseVideoExtraPath(cJSON* data, BootAnimationConfig& config)
176{
177    int size = cJSON_GetArraySize(data);
178    for (int index = 0; index < size; index++) {
179        cJSON* extraPath = cJSON_GetArrayItem(data, index);
180        if (extraPath != nullptr && cJSON_IsString(extraPath)) {
181            config.videoExtPath.emplace_back(extraPath->valuestring);
182        }
183    }
184}
185
186void ParseBootDuration(cJSON* data, int32_t& duration)
187{
188    cJSON* durationJson = cJSON_GetObjectItem(data, "cust.bootanimation.duration");
189    if (durationJson != nullptr && cJSON_IsString(durationJson)) {
190        duration = std::atoi(durationJson->valuestring);
191        LOGI("cust duration: %{public}d", duration);
192    }
193}
194
195bool ReadZipFile(const std::string& srcFilePath, ImageStructVec& imgVec, FrameRateConfig& frameConfig)
196{
197    unzFile zipFile = unzOpen2(srcFilePath.c_str(), nullptr);
198    if (zipFile == nullptr) {
199        LOGE("Open zipFile fail: %{public}s", srcFilePath.c_str());
200        return false;
201    }
202
203    unz_global_info globalInfo;
204    if (unzGetGlobalInfo(zipFile, &globalInfo) != UNZ_OK) {
205        LOGE("Get ZipGlobalInfo fail");
206        return CloseZipFile(zipFile, false);
207    }
208
209    LOGD("read zip file num: %{public}ld", globalInfo.number_entry);
210    for (unsigned long i = 0; i < globalInfo.number_entry; ++i) {
211        unz_file_info fileInfo;
212        char filename[MAX_FILE_NAME] = {0};
213        if (unzGetCurrentFileInfo(zipFile, &fileInfo, filename, MAX_FILE_NAME, nullptr, 0, nullptr, 0) != UNZ_OK) {
214            return CloseZipFile(zipFile, false);
215        }
216        size_t length = strlen(filename);
217        if (length > MAX_FILE_NAME || length == 0) {
218            return CloseZipFile(zipFile, false);
219        }
220        if (filename[length - 1] != '/') {
221            if (unzOpenCurrentFile(zipFile) != UNZ_OK) {
222                return CloseZipFile(zipFile, false);
223            }
224            std::string name = std::string(filename);
225            size_t npos = name.find_last_of("//");
226            if (npos != std::string::npos) {
227                name = name.substr(npos + 1, name.length());
228            }
229            if (!ReadImageFile(zipFile, name, imgVec, frameConfig, fileInfo.uncompressed_size)) {
230                LOGE("read zip deal single file failed");
231                unzCloseCurrentFile(zipFile);
232                return CloseZipFile(zipFile, false);
233            }
234            unzCloseCurrentFile(zipFile);
235        }
236        if (i < (globalInfo.number_entry - 1)) {
237            if (unzGoToNextFile(zipFile) != UNZ_OK) {
238                return CloseZipFile(zipFile, false);
239            }
240        }
241    }
242    return CloseZipFile(zipFile, true);
243}
244
245bool CloseZipFile(const unzFile zipFile, bool ret)
246{
247    unzClose(zipFile);
248    return ret;
249}
250
251void SortZipFile(ImageStructVec& imgVec)
252{
253    if (imgVec.size() == 0) {
254        return;
255    }
256
257    sort(imgVec.begin(), imgVec.end(), [](std::shared_ptr<ImageStruct> image1,
258        std::shared_ptr<ImageStruct> image2)
259        -> bool {return image1->fileName < image2->fileName;});
260}
261
262bool ReadImageFile(const unzFile zipFile, const std::string& fileName, ImageStructVec& imgVec,
263    FrameRateConfig& frameConfig, unsigned long fileSize)
264{
265    if (zipFile == nullptr) {
266        LOGE("ReadImageFile failed, zip is null");
267        return false;
268    }
269    int readLen = UNZ_OK;
270    int totalLen = 0;
271    int size = static_cast<int>(fileSize);
272    char readBuffer[READ_SIZE] = {0};
273    std::shared_ptr<ImageStruct> imageStruct = std::make_shared<ImageStruct>();
274    imageStruct->memPtr.SetBufferSize(fileSize);
275    do {
276        readLen = unzReadCurrentFile(zipFile, readBuffer, READ_SIZE);
277        if (readLen < 0) {
278            LOGE("unzReadCurrentFile length error");
279            return false;
280        }
281        if (imageStruct->memPtr.memBuffer == nullptr) {
282            LOGE("ReadImageFile memPtr is null");
283            return false;
284        }
285        if (memcpy_s(imageStruct->memPtr.memBuffer + totalLen, size - totalLen, \
286            readBuffer, readLen) == EOK) {
287            totalLen += readLen;
288        }
289    } while (readLen > 0);
290
291    if (totalLen > 0) {
292        LOGD("fileName: %{public}s, fileSize: %{public}d, totalLen: %{public}d", fileName.c_str(), size, totalLen);
293        if (strstr(fileName.c_str(), BOOT_PIC_CONFIG_FILE.c_str()) != nullptr) {
294            ParseImageConfig(imageStruct->memPtr.memBuffer, totalLen, frameConfig);
295        } else {
296            CheckImageData(fileName, imageStruct, totalLen, imgVec);
297        }
298    }
299    return true;
300}
301
302bool ParseImageConfig(const char* fileBuffer, int totalsize, FrameRateConfig& frameConfig)
303{
304    std::string JParamsString;
305    JParamsString.assign(fileBuffer, totalsize);
306    cJSON* overallData = cJSON_Parse(JParamsString.c_str());
307    if (overallData == nullptr) {
308        LOGE("parse image config failed");
309        return false;
310    }
311    cJSON* frameRate = cJSON_GetObjectItem(overallData, "FrameRate");
312    if (frameRate != nullptr && cJSON_IsNumber(frameRate)) {
313        frameConfig.frameRate = frameRate->valueint;
314        LOGI("freq: %{public}d", frameConfig.frameRate);
315    }
316    cJSON_Delete(overallData);
317    return true;
318}
319
320bool CheckImageData(const std::string& fileName, std::shared_ptr<ImageStruct> imageStruct,
321    int32_t bufferLen, ImageStructVec& imgVec)
322{
323    if (imageStruct->memPtr.memBuffer == nullptr) {
324        LOGE("json file buffer is null");
325        return false;
326    }
327    auto data = std::make_shared<Rosen::Drawing::Data>();
328    data->BuildFromMalloc(imageStruct->memPtr.memBuffer, bufferLen);
329    if (data->GetData() == nullptr) {
330        LOGE("data memory data is null. update data failed");
331        return false;
332    }
333    imageStruct->memPtr.setOwnerShip(data);
334    imageStruct->fileName = fileName;
335    imageStruct->imageData = std::make_shared<Rosen::Drawing::Image>();
336    imageStruct->imageData->MakeFromEncoded(data);
337    imgVec.push_back(imageStruct);
338    return true;
339}
340
341/**
342 * Transate vp to pixel.
343 *
344 * @param sideLen The short side length of screen.
345 * @param vp vp value.
346 * @return Returns the font size.
347 */
348int32_t TransalteVp2Pixel(const int32_t sideLen, const int32_t vp)
349{
350    return static_cast<int32_t>(std::ceil(sideLen * HALF / RADIO) / HALF * vp);
351}
352} // namespace OHOS
353