18bf80f4bSopenharmony_ci/*
28bf80f4bSopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
38bf80f4bSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
48bf80f4bSopenharmony_ci * you may not use this file except in compliance with the License.
58bf80f4bSopenharmony_ci * You may obtain a copy of the License at
68bf80f4bSopenharmony_ci *
78bf80f4bSopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
88bf80f4bSopenharmony_ci *
98bf80f4bSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
108bf80f4bSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
118bf80f4bSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
128bf80f4bSopenharmony_ci * See the License for the specific language governing permissions and
138bf80f4bSopenharmony_ci * limitations under the License.
148bf80f4bSopenharmony_ci */
158bf80f4bSopenharmony_ci
168bf80f4bSopenharmony_ci#ifndef SCENEPLUGIN_ASSETLOADER_H
178bf80f4bSopenharmony_ci#define SCENEPLUGIN_ASSETLOADER_H
188bf80f4bSopenharmony_ci
198bf80f4bSopenharmony_ci#include <scene_plugin/interface/intf_asset_loader.h>
208bf80f4bSopenharmony_ci
218bf80f4bSopenharmony_ci#include <3d/intf_graphics_context.h>
228bf80f4bSopenharmony_ci#include <base/containers/string_view.h>
238bf80f4bSopenharmony_ci#include <base/containers/unordered_map.h>
248bf80f4bSopenharmony_ci#include <base/math/mathf.h>
258bf80f4bSopenharmony_ci#include <render/intf_render_context.h>
268bf80f4bSopenharmony_ci
278bf80f4bSopenharmony_ciSCENE_BEGIN_NAMESPACE()
288bf80f4bSopenharmony_ci
298bf80f4bSopenharmony_ciclass AssetManager;
308bf80f4bSopenharmony_ci
318bf80f4bSopenharmony_ciIAssetLoader::Ptr CreateAssetLoader(AssetManager& assetManager, RENDER_NS::IRenderContext& renderContext,
328bf80f4bSopenharmony_ci    CORE3D_NS::IGraphicsContext& graphicsContext, IEntityCollection& ec, BASE_NS::string_view src,
338bf80f4bSopenharmony_ci    BASE_NS::string_view contextUri);
348bf80f4bSopenharmony_ci
358bf80f4bSopenharmony_cistruct PathUtil {
368bf80f4bSopenharmony_cipublic:
378bf80f4bSopenharmony_ci    static BASE_NS::string NormalizePath(BASE_NS::string_view path)
388bf80f4bSopenharmony_ci    {
398bf80f4bSopenharmony_ci        BASE_NS::string res;
408bf80f4bSopenharmony_ci        if (path[0] != '/') {
418bf80f4bSopenharmony_ci            res.reserve(path.size() + 1);
428bf80f4bSopenharmony_ci            res.push_back('/');
438bf80f4bSopenharmony_ci        } else {
448bf80f4bSopenharmony_ci            res.reserve(path.size());
458bf80f4bSopenharmony_ci        }
468bf80f4bSopenharmony_ci        while (!path.empty()) {
478bf80f4bSopenharmony_ci            if (path[0] == '/') {
488bf80f4bSopenharmony_ci                if (res.empty()) {
498bf80f4bSopenharmony_ci                    res.push_back('/');
508bf80f4bSopenharmony_ci                }
518bf80f4bSopenharmony_ci                path = path.substr(1);
528bf80f4bSopenharmony_ci                continue;
538bf80f4bSopenharmony_ci            }
548bf80f4bSopenharmony_ci            auto pos = path.find_first_of("/", 0);
558bf80f4bSopenharmony_ci            BASE_NS::string_view sub = path.substr(0, pos);
568bf80f4bSopenharmony_ci            if (sub == ".") {
578bf80f4bSopenharmony_ci                path = path.substr(pos);
588bf80f4bSopenharmony_ci                continue;
598bf80f4bSopenharmony_ci            } else if (sub == "..") {
608bf80f4bSopenharmony_ci                if ((!res.empty()) && (res.back() == '/')) {
618bf80f4bSopenharmony_ci                    res.resize(res.size() - 1);
628bf80f4bSopenharmony_ci                }
638bf80f4bSopenharmony_ci                if (auto p = res.find_last_of('/'); BASE_NS::string::npos != p) {
648bf80f4bSopenharmony_ci                    res.resize(p);
658bf80f4bSopenharmony_ci                } else {
668bf80f4bSopenharmony_ci                    if (res.empty()) {
678bf80f4bSopenharmony_ci                        // trying to back out of root. (ie. invalid path)
688bf80f4bSopenharmony_ci                        return "";
698bf80f4bSopenharmony_ci                    }
708bf80f4bSopenharmony_ci                    res.clear();
718bf80f4bSopenharmony_ci                }
728bf80f4bSopenharmony_ci                if (pos == BASE_NS::string::npos) {
738bf80f4bSopenharmony_ci                    res.push_back('/');
748bf80f4bSopenharmony_ci                    break;
758bf80f4bSopenharmony_ci                }
768bf80f4bSopenharmony_ci            } else {
778bf80f4bSopenharmony_ci                res.append(sub);
788bf80f4bSopenharmony_ci            }
798bf80f4bSopenharmony_ci            if (pos == BASE_NS::string::npos) {
808bf80f4bSopenharmony_ci                break;
818bf80f4bSopenharmony_ci            } else {
828bf80f4bSopenharmony_ci                res.push_back('/');
838bf80f4bSopenharmony_ci            }
848bf80f4bSopenharmony_ci            path = path.substr(pos);
858bf80f4bSopenharmony_ci        }
868bf80f4bSopenharmony_ci        if (res[0] != '/') {
878bf80f4bSopenharmony_ci            res.insert(0, "/");
888bf80f4bSopenharmony_ci        }
898bf80f4bSopenharmony_ci        return res;
908bf80f4bSopenharmony_ci    }
918bf80f4bSopenharmony_ci
928bf80f4bSopenharmony_ci    static BASE_NS::string GetParentPath(BASE_NS::string_view path)
938bf80f4bSopenharmony_ci    {
948bf80f4bSopenharmony_ci        if (path.size() > 1 && path[path.size() - 1] == '/' && path[path.size() - 2] != '/') { // 2 array index
958bf80f4bSopenharmony_ci            // Allow (ignore) trailing '/' for folders.
968bf80f4bSopenharmony_ci            path = path.substr(0, path.size() - 1);
978bf80f4bSopenharmony_ci        }
988bf80f4bSopenharmony_ci
998bf80f4bSopenharmony_ci        const size_t separatorPos = path.rfind('/');
1008bf80f4bSopenharmony_ci        if (separatorPos == BASE_NS::string::npos) {
1018bf80f4bSopenharmony_ci            return "";
1028bf80f4bSopenharmony_ci        } else {
1038bf80f4bSopenharmony_ci            return BASE_NS::string(path.substr(0, separatorPos + 1));
1048bf80f4bSopenharmony_ci        }
1058bf80f4bSopenharmony_ci    }
1068bf80f4bSopenharmony_ci
1078bf80f4bSopenharmony_ci    static BASE_NS::string ResolvePath(BASE_NS::string_view parent, BASE_NS::string_view uri, bool allowQueryString)
1088bf80f4bSopenharmony_ci    {
1098bf80f4bSopenharmony_ci        size_t queryPos = allowQueryString ? BASE_NS::string::npos : uri.find('?');
1108bf80f4bSopenharmony_ci        BASE_NS::string_view path = (BASE_NS::string::npos != queryPos) ? uri.substr(0, queryPos) : uri;
1118bf80f4bSopenharmony_ci
1128bf80f4bSopenharmony_ci        if (parent.empty()) {
1138bf80f4bSopenharmony_ci            return BASE_NS::string(path);
1148bf80f4bSopenharmony_ci        }
1158bf80f4bSopenharmony_ci
1168bf80f4bSopenharmony_ci        if (path.empty()) {
1178bf80f4bSopenharmony_ci            return BASE_NS::string(parent);
1188bf80f4bSopenharmony_ci        }
1198bf80f4bSopenharmony_ci
1208bf80f4bSopenharmony_ci        if (path[0] == '/') {
1218bf80f4bSopenharmony_ci            path = path.substr(1);
1228bf80f4bSopenharmony_ci        } else if (path.find("://") != path.npos) {
1238bf80f4bSopenharmony_ci            return BASE_NS::string(path);
1248bf80f4bSopenharmony_ci        }
1258bf80f4bSopenharmony_ci
1268bf80f4bSopenharmony_ci        // NOTE: Resolve always assumes the parent path is a directory even if there is no '/' in the end
1278bf80f4bSopenharmony_ci        if (parent.back() == '/') {
1288bf80f4bSopenharmony_ci            return parent + path;
1298bf80f4bSopenharmony_ci        } else {
1308bf80f4bSopenharmony_ci            return parent + "/" + path;
1318bf80f4bSopenharmony_ci        }
1328bf80f4bSopenharmony_ci    }
1338bf80f4bSopenharmony_ci
1348bf80f4bSopenharmony_ci    static BASE_NS::string GetRelativePath(BASE_NS::string_view path, BASE_NS::string_view relativeTo)
1358bf80f4bSopenharmony_ci    {
1368bf80f4bSopenharmony_ci        // remove the common prefix
1378bf80f4bSopenharmony_ci        {
1388bf80f4bSopenharmony_ci            int lastSeparator = -1;
1398bf80f4bSopenharmony_ci
1408bf80f4bSopenharmony_ci            for (size_t i = 0, iMax = BASE_NS::Math::min(path.size(), relativeTo.size()); i < iMax; ++i) {
1418bf80f4bSopenharmony_ci                if (path[i] != relativeTo[i]) {
1428bf80f4bSopenharmony_ci                    break;
1438bf80f4bSopenharmony_ci                }
1448bf80f4bSopenharmony_ci                if (path[i] == '/') {
1458bf80f4bSopenharmony_ci                    lastSeparator = int(i);
1468bf80f4bSopenharmony_ci                }
1478bf80f4bSopenharmony_ci            }
1488bf80f4bSopenharmony_ci
1498bf80f4bSopenharmony_ci            path.remove_prefix(lastSeparator + 1);
1508bf80f4bSopenharmony_ci            relativeTo.remove_prefix(lastSeparator + 1);
1518bf80f4bSopenharmony_ci        }
1528bf80f4bSopenharmony_ci
1538bf80f4bSopenharmony_ci        if (path[1] == ':' && relativeTo[1] == ':' && path[0] != relativeTo[0]) {
1548bf80f4bSopenharmony_ci            // files are on different drives
1558bf80f4bSopenharmony_ci            return BASE_NS::string(path);
1568bf80f4bSopenharmony_ci        }
1578bf80f4bSopenharmony_ci
1588bf80f4bSopenharmony_ci        // count and remove the directories left in relative_to
1598bf80f4bSopenharmony_ci        auto directoriesCount = 0;
1608bf80f4bSopenharmony_ci        {
1618bf80f4bSopenharmony_ci            auto nextSeparator = relativeTo.find('/');
1628bf80f4bSopenharmony_ci            while (nextSeparator != BASE_NS::string::npos) {
1638bf80f4bSopenharmony_ci                relativeTo.remove_prefix(nextSeparator + 1);
1648bf80f4bSopenharmony_ci                nextSeparator = relativeTo.find('/');
1658bf80f4bSopenharmony_ci                ++directoriesCount;
1668bf80f4bSopenharmony_ci            }
1678bf80f4bSopenharmony_ci        }
1688bf80f4bSopenharmony_ci
1698bf80f4bSopenharmony_ci        BASE_NS::string relativePath = "";
1708bf80f4bSopenharmony_ci        for (auto i = 0, iMax = directoriesCount; i < iMax; ++i) {
1718bf80f4bSopenharmony_ci            relativePath.append("../");
1728bf80f4bSopenharmony_ci        }
1738bf80f4bSopenharmony_ci
1748bf80f4bSopenharmony_ci        relativePath.append(path);
1758bf80f4bSopenharmony_ci
1768bf80f4bSopenharmony_ci        return relativePath;
1778bf80f4bSopenharmony_ci    }
1788bf80f4bSopenharmony_ci
1798bf80f4bSopenharmony_ci    static BASE_NS::string GetFilename(BASE_NS::string_view path)
1808bf80f4bSopenharmony_ci    {
1818bf80f4bSopenharmony_ci        if (!path.empty() && path[path.size() - 1] == '/') {
1828bf80f4bSopenharmony_ci            // Return a name also for folders.
1838bf80f4bSopenharmony_ci            path = path.substr(0, path.size() - 1);
1848bf80f4bSopenharmony_ci        }
1858bf80f4bSopenharmony_ci
1868bf80f4bSopenharmony_ci        size_t cutPos = path.find_last_of("\\/");
1878bf80f4bSopenharmony_ci        if (BASE_NS::string::npos != cutPos) {
1888bf80f4bSopenharmony_ci            return BASE_NS::string(path.substr(cutPos + 1));
1898bf80f4bSopenharmony_ci        } else {
1908bf80f4bSopenharmony_ci            return BASE_NS::string(path);
1918bf80f4bSopenharmony_ci        }
1928bf80f4bSopenharmony_ci    }
1938bf80f4bSopenharmony_ci
1948bf80f4bSopenharmony_ci    static BASE_NS::string GetExtension(BASE_NS::string_view path)
1958bf80f4bSopenharmony_ci    {
1968bf80f4bSopenharmony_ci        size_t fileExtCut = path.rfind('.');
1978bf80f4bSopenharmony_ci        if (fileExtCut != BASE_NS::string::npos) {
1988bf80f4bSopenharmony_ci            size_t queryCut = path.find('?', fileExtCut);
1998bf80f4bSopenharmony_ci            if (queryCut != BASE_NS::string::npos) {
2008bf80f4bSopenharmony_ci                return BASE_NS::string(path.substr(fileExtCut + 1, queryCut));
2018bf80f4bSopenharmony_ci            } else {
2028bf80f4bSopenharmony_ci                return BASE_NS::string(path.substr(fileExtCut + 1, queryCut));
2038bf80f4bSopenharmony_ci            }
2048bf80f4bSopenharmony_ci        }
2058bf80f4bSopenharmony_ci        return "";
2068bf80f4bSopenharmony_ci    }
2078bf80f4bSopenharmony_ci
2088bf80f4bSopenharmony_ci    static BASE_NS::string GetBaseName(BASE_NS::string_view path)
2098bf80f4bSopenharmony_ci    {
2108bf80f4bSopenharmony_ci        auto filename = GetFilename(path);
2118bf80f4bSopenharmony_ci        size_t fileExtCut = filename.rfind(".");
2128bf80f4bSopenharmony_ci        if (BASE_NS::string::npos != fileExtCut) {
2138bf80f4bSopenharmony_ci            filename.erase(fileExtCut);
2148bf80f4bSopenharmony_ci        }
2158bf80f4bSopenharmony_ci        return filename;
2168bf80f4bSopenharmony_ci    }
2178bf80f4bSopenharmony_ci
2188bf80f4bSopenharmony_ci    static BASE_NS::unordered_map<BASE_NS::string, BASE_NS::string> GetUriParameters(BASE_NS::string_view uri)
2198bf80f4bSopenharmony_ci    {
2208bf80f4bSopenharmony_ci        const size_t queryPos = uri.find('?');
2218bf80f4bSopenharmony_ci        if (queryPos != BASE_NS::string::npos) {
2228bf80f4bSopenharmony_ci            BASE_NS::unordered_map<BASE_NS::string, BASE_NS::string> params;
2238bf80f4bSopenharmony_ci            size_t paramStartPos = queryPos;
2248bf80f4bSopenharmony_ci            while (paramStartPos < uri.size()) {
2258bf80f4bSopenharmony_ci                size_t paramValuePos = uri.find('=', paramStartPos + 1);
2268bf80f4bSopenharmony_ci                size_t paramEndPos = uri.find('&', paramStartPos + 1);
2278bf80f4bSopenharmony_ci                if (paramEndPos == BASE_NS::string::npos) {
2288bf80f4bSopenharmony_ci                    paramEndPos = uri.size();
2298bf80f4bSopenharmony_ci                }
2308bf80f4bSopenharmony_ci                if (paramValuePos != BASE_NS::string::npos && paramValuePos < paramEndPos) {
2318bf80f4bSopenharmony_ci                    auto key = uri.substr(paramStartPos + 1, paramValuePos - paramStartPos - 1);
2328bf80f4bSopenharmony_ci                    auto value = uri.substr(paramValuePos + 1, paramEndPos - paramValuePos - 1);
2338bf80f4bSopenharmony_ci                    params[key] = value;
2348bf80f4bSopenharmony_ci                } else {
2358bf80f4bSopenharmony_ci                    auto key = uri.substr(paramStartPos + 1, paramEndPos - paramStartPos - 1);
2368bf80f4bSopenharmony_ci                    params[key] = key;
2378bf80f4bSopenharmony_ci                }
2388bf80f4bSopenharmony_ci                paramStartPos = paramEndPos;
2398bf80f4bSopenharmony_ci            }
2408bf80f4bSopenharmony_ci            return params;
2418bf80f4bSopenharmony_ci        }
2428bf80f4bSopenharmony_ci        return {};
2438bf80f4bSopenharmony_ci    }
2448bf80f4bSopenharmony_ci
2458bf80f4bSopenharmony_ci    static BASE_NS::string ResolveUri(
2468bf80f4bSopenharmony_ci        BASE_NS::string_view contextUri, BASE_NS::string_view uri, bool allowQueryString = true)
2478bf80f4bSopenharmony_ci    {
2488bf80f4bSopenharmony_ci        return ResolvePath(GetParentPath(contextUri), uri, allowQueryString);
2498bf80f4bSopenharmony_ci    }
2508bf80f4bSopenharmony_ci};
2518bf80f4bSopenharmony_ci
2528bf80f4bSopenharmony_ciSCENE_END_NAMESPACE()
2538bf80f4bSopenharmony_ci
2548bf80f4bSopenharmony_ci#endif // SCENEPLUGIN_ASSETLOADER_H
255