1 /* 2 * Copyright (c) 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 #ifndef SCENEPLUGIN_ASSETLOADER_H 17 #define SCENEPLUGIN_ASSETLOADER_H 18 19 #include <scene_plugin/interface/intf_asset_loader.h> 20 21 #include <3d/intf_graphics_context.h> 22 #include <base/containers/string_view.h> 23 #include <base/containers/unordered_map.h> 24 #include <base/math/mathf.h> 25 #include <render/intf_render_context.h> 26 27 SCENE_BEGIN_NAMESPACE() 28 29 class AssetManager; 30 31 IAssetLoader::Ptr CreateAssetLoader(AssetManager& assetManager, RENDER_NS::IRenderContext& renderContext, 32 CORE3D_NS::IGraphicsContext& graphicsContext, IEntityCollection& ec, BASE_NS::string_view src, 33 BASE_NS::string_view contextUri); 34 35 struct PathUtil { 36 public: NormalizePathPathUtil37 static BASE_NS::string NormalizePath(BASE_NS::string_view path) 38 { 39 BASE_NS::string res; 40 if (path[0] != '/') { 41 res.reserve(path.size() + 1); 42 res.push_back('/'); 43 } else { 44 res.reserve(path.size()); 45 } 46 while (!path.empty()) { 47 if (path[0] == '/') { 48 if (res.empty()) { 49 res.push_back('/'); 50 } 51 path = path.substr(1); 52 continue; 53 } 54 auto pos = path.find_first_of("/", 0); 55 BASE_NS::string_view sub = path.substr(0, pos); 56 if (sub == ".") { 57 path = path.substr(pos); 58 continue; 59 } else if (sub == "..") { 60 if ((!res.empty()) && (res.back() == '/')) { 61 res.resize(res.size() - 1); 62 } 63 if (auto p = res.find_last_of('/'); BASE_NS::string::npos != p) { 64 res.resize(p); 65 } else { 66 if (res.empty()) { 67 // trying to back out of root. (ie. invalid path) 68 return ""; 69 } 70 res.clear(); 71 } 72 if (pos == BASE_NS::string::npos) { 73 res.push_back('/'); 74 break; 75 } 76 } else { 77 res.append(sub); 78 } 79 if (pos == BASE_NS::string::npos) { 80 break; 81 } else { 82 res.push_back('/'); 83 } 84 path = path.substr(pos); 85 } 86 if (res[0] != '/') { 87 res.insert(0, "/"); 88 } 89 return res; 90 } 91 GetParentPathPathUtil92 static BASE_NS::string GetParentPath(BASE_NS::string_view path) 93 { 94 if (path.size() > 1 && path[path.size() - 1] == '/' && path[path.size() - 2] != '/') { // 2 array index 95 // Allow (ignore) trailing '/' for folders. 96 path = path.substr(0, path.size() - 1); 97 } 98 99 const size_t separatorPos = path.rfind('/'); 100 if (separatorPos == BASE_NS::string::npos) { 101 return ""; 102 } else { 103 return BASE_NS::string(path.substr(0, separatorPos + 1)); 104 } 105 } 106 ResolvePathPathUtil107 static BASE_NS::string ResolvePath(BASE_NS::string_view parent, BASE_NS::string_view uri, bool allowQueryString) 108 { 109 size_t queryPos = allowQueryString ? BASE_NS::string::npos : uri.find('?'); 110 BASE_NS::string_view path = (BASE_NS::string::npos != queryPos) ? uri.substr(0, queryPos) : uri; 111 112 if (parent.empty()) { 113 return BASE_NS::string(path); 114 } 115 116 if (path.empty()) { 117 return BASE_NS::string(parent); 118 } 119 120 if (path[0] == '/') { 121 path = path.substr(1); 122 } else if (path.find("://") != path.npos) { 123 return BASE_NS::string(path); 124 } 125 126 // NOTE: Resolve always assumes the parent path is a directory even if there is no '/' in the end 127 if (parent.back() == '/') { 128 return parent + path; 129 } else { 130 return parent + "/" + path; 131 } 132 } 133 GetRelativePathPathUtil134 static BASE_NS::string GetRelativePath(BASE_NS::string_view path, BASE_NS::string_view relativeTo) 135 { 136 // remove the common prefix 137 { 138 int lastSeparator = -1; 139 140 for (size_t i = 0, iMax = BASE_NS::Math::min(path.size(), relativeTo.size()); i < iMax; ++i) { 141 if (path[i] != relativeTo[i]) { 142 break; 143 } 144 if (path[i] == '/') { 145 lastSeparator = int(i); 146 } 147 } 148 149 path.remove_prefix(lastSeparator + 1); 150 relativeTo.remove_prefix(lastSeparator + 1); 151 } 152 153 if (path[1] == ':' && relativeTo[1] == ':' && path[0] != relativeTo[0]) { 154 // files are on different drives 155 return BASE_NS::string(path); 156 } 157 158 // count and remove the directories left in relative_to 159 auto directoriesCount = 0; 160 { 161 auto nextSeparator = relativeTo.find('/'); 162 while (nextSeparator != BASE_NS::string::npos) { 163 relativeTo.remove_prefix(nextSeparator + 1); 164 nextSeparator = relativeTo.find('/'); 165 ++directoriesCount; 166 } 167 } 168 169 BASE_NS::string relativePath = ""; 170 for (auto i = 0, iMax = directoriesCount; i < iMax; ++i) { 171 relativePath.append("../"); 172 } 173 174 relativePath.append(path); 175 176 return relativePath; 177 } 178 GetFilenamePathUtil179 static BASE_NS::string GetFilename(BASE_NS::string_view path) 180 { 181 if (!path.empty() && path[path.size() - 1] == '/') { 182 // Return a name also for folders. 183 path = path.substr(0, path.size() - 1); 184 } 185 186 size_t cutPos = path.find_last_of("\\/"); 187 if (BASE_NS::string::npos != cutPos) { 188 return BASE_NS::string(path.substr(cutPos + 1)); 189 } else { 190 return BASE_NS::string(path); 191 } 192 } 193 GetExtensionPathUtil194 static BASE_NS::string GetExtension(BASE_NS::string_view path) 195 { 196 size_t fileExtCut = path.rfind('.'); 197 if (fileExtCut != BASE_NS::string::npos) { 198 size_t queryCut = path.find('?', fileExtCut); 199 if (queryCut != BASE_NS::string::npos) { 200 return BASE_NS::string(path.substr(fileExtCut + 1, queryCut)); 201 } else { 202 return BASE_NS::string(path.substr(fileExtCut + 1, queryCut)); 203 } 204 } 205 return ""; 206 } 207 GetBaseNamePathUtil208 static BASE_NS::string GetBaseName(BASE_NS::string_view path) 209 { 210 auto filename = GetFilename(path); 211 size_t fileExtCut = filename.rfind("."); 212 if (BASE_NS::string::npos != fileExtCut) { 213 filename.erase(fileExtCut); 214 } 215 return filename; 216 } 217 GetUriParametersPathUtil218 static BASE_NS::unordered_map<BASE_NS::string, BASE_NS::string> GetUriParameters(BASE_NS::string_view uri) 219 { 220 const size_t queryPos = uri.find('?'); 221 if (queryPos != BASE_NS::string::npos) { 222 BASE_NS::unordered_map<BASE_NS::string, BASE_NS::string> params; 223 size_t paramStartPos = queryPos; 224 while (paramStartPos < uri.size()) { 225 size_t paramValuePos = uri.find('=', paramStartPos + 1); 226 size_t paramEndPos = uri.find('&', paramStartPos + 1); 227 if (paramEndPos == BASE_NS::string::npos) { 228 paramEndPos = uri.size(); 229 } 230 if (paramValuePos != BASE_NS::string::npos && paramValuePos < paramEndPos) { 231 auto key = uri.substr(paramStartPos + 1, paramValuePos - paramStartPos - 1); 232 auto value = uri.substr(paramValuePos + 1, paramEndPos - paramValuePos - 1); 233 params[key] = value; 234 } else { 235 auto key = uri.substr(paramStartPos + 1, paramEndPos - paramStartPos - 1); 236 params[key] = key; 237 } 238 paramStartPos = paramEndPos; 239 } 240 return params; 241 } 242 return {}; 243 } 244 245 static BASE_NS::string ResolveUri( 246 BASE_NS::string_view contextUri, BASE_NS::string_view uri, bool allowQueryString = true) 247 { 248 return ResolvePath(GetParentPath(contextUri), uri, allowQueryString); 249 } 250 }; 251 252 SCENE_END_NAMESPACE() 253 254 #endif // SCENEPLUGIN_ASSETLOADER_H 255