CryPath.h 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. // Description : Defines namespace PathUtil for operations on files paths.
  9. #ifndef CRYINCLUDE_CRYCOMMON_CRYPATH_H
  10. #define CRYINCLUDE_CRYCOMMON_CRYPATH_H
  11. #pragma once
  12. #include <ISystem.h>
  13. #include <AzFramework/Archive/IArchive.h>
  14. #include <IConsole.h>
  15. #include <AzCore/std/string/string.h>
  16. #include <AzCore/std/string/conversions.h>
  17. #include <AzCore/StringFunc/StringFunc.h>
  18. #include "platform.h"
  19. #define UNIX_PATH_SEP_STR "/"
  20. #define UNIX_PATH_SEP_CHR '/'
  21. #define DOS_PATH_SEP_STR "\\"
  22. #define DOS_PATH_SEP_CHR '\\'
  23. #if AZ_LEGACY_CRYCOMMON_TRAIT_USE_UNIX_PATHS
  24. #define CRY_NATIVE_PATH_SEPSTR UNIX_PATH_SEP_STR
  25. #else
  26. #define CRY_NATIVE_PATH_SEPSTR DOS_PATH_SEP_STR
  27. #endif
  28. typedef AZStd::fixed_string<512> stack_string;
  29. namespace PathUtil
  30. {
  31. const static int maxAliasLength = 32;
  32. inline AZStd::string GetLocalizationFolder()
  33. {
  34. return gEnv->pCryPak->GetLocalizationFolder();
  35. }
  36. inline AZStd::string GetLocalizationRoot()
  37. {
  38. return gEnv->pCryPak->GetLocalizationRoot();
  39. }
  40. //! Convert a path to the uniform form.
  41. inline AZStd::string ToUnixPath(const AZStd::string& strPath)
  42. {
  43. if (strPath.find(DOS_PATH_SEP_CHR) != AZStd::string::npos)
  44. {
  45. AZStd::string path = strPath;
  46. AZ::StringFunc::Replace(path, DOS_PATH_SEP_CHR, UNIX_PATH_SEP_CHR);
  47. return path;
  48. }
  49. return strPath;
  50. }
  51. //! Convert a path to the uniform form in place on stack
  52. inline void ToUnixPath(stack_string& rConv)
  53. {
  54. const char* const cpEnd = &(rConv.c_str()[rConv.size()]);
  55. char* __restrict pC = rConv.begin();
  56. while (pC != cpEnd)
  57. {
  58. char c = *pC;
  59. if (c == DOS_PATH_SEP_CHR)
  60. {
  61. c = UNIX_PATH_SEP_CHR;
  62. }
  63. *pC++ = c;
  64. }
  65. }
  66. //! Convert a path to the DOS form.
  67. inline AZStd::string ToDosPath(const AZStd::string& strPath)
  68. {
  69. if (strPath.find(UNIX_PATH_SEP_CHR) != AZStd::string::npos)
  70. {
  71. AZStd::string path = strPath;
  72. AZ::StringFunc::Replace(path, UNIX_PATH_SEP_CHR, DOS_PATH_SEP_CHR);
  73. return path;
  74. }
  75. return strPath;
  76. }
  77. //! Convert a path to the Native form.
  78. inline AZStd::string ToNativePath(const AZStd::string& strPath)
  79. {
  80. #if AZ_LEGACY_CRYCOMMON_TRAIT_USE_UNIX_PATHS
  81. return ToUnixPath(strPath);
  82. #else
  83. return ToDosPath(strPath);
  84. #endif
  85. }
  86. //! Convert a path to lowercase form
  87. inline AZStd::string ToLower(const AZStd::string& strPath)
  88. {
  89. AZStd::string path = strPath;
  90. AZStd::to_lower(path.begin(), path.end());
  91. return path;
  92. }
  93. //! Split full file name to path and filename
  94. //! @param filepath [IN] Full file name including path.
  95. //! @param path [OUT] Extracted file path.
  96. //! @param filename [OUT] Extracted file (without extension).
  97. //! @param ext [OUT] Extracted files extension.
  98. inline void Split(const AZStd::string& filepath, AZStd::string& path, AZStd::string& filename, AZStd::string& fext)
  99. {
  100. path = filename = fext = AZStd::string();
  101. if (filepath.empty())
  102. {
  103. return;
  104. }
  105. const char* str = filepath.c_str();
  106. const char* pext = str + filepath.length() - 1;
  107. const char* p;
  108. for (p = str + filepath.length() - 1; p >= str; --p)
  109. {
  110. switch (*p)
  111. {
  112. case ':':
  113. case '/':
  114. case '\\':
  115. path = filepath.substr(0, p - str + 1);
  116. filename = filepath.substr(p - str + 1, pext - p);
  117. return;
  118. case '.':
  119. // there's an extension in this file name
  120. fext = filepath.substr(p - str + 1);
  121. pext = p;
  122. break;
  123. }
  124. }
  125. filename = filepath.substr(p - str + 1, pext - p);
  126. }
  127. //! Split full file name to path and filename
  128. //! @param filepath [IN] Full file name inclusing path.
  129. //! @param path [OUT] Extracted file path.
  130. //! @param file [OUT] Extracted file (with extension).
  131. inline void Split(const AZStd::string& filepath, AZStd::string& path, AZStd::string& file)
  132. {
  133. AZStd::string fext;
  134. Split(filepath, path, file, fext);
  135. file += fext;
  136. }
  137. // Extract extension from full specified file path
  138. // Returns
  139. // pointer to the extension (without .) or pointer to an empty 0-terminated AZStd::string
  140. inline const char* GetExt(const char* filepath)
  141. {
  142. const char* str = filepath;
  143. size_t len = strlen(filepath);
  144. for (const char* p = str + len - 1; p >= str; --p)
  145. {
  146. switch (*p)
  147. {
  148. case ':':
  149. case '/':
  150. case '\\':
  151. // we've reached a path separator - it means there's no extension in this name
  152. return "";
  153. case '.':
  154. // there's an extension in this file name
  155. return p + 1;
  156. }
  157. }
  158. return "";
  159. }
  160. //! Extract path from full specified file path.
  161. inline AZStd::string GetPath(const AZStd::string& filepath)
  162. {
  163. const char* str = filepath.c_str();
  164. for (const char* p = str + filepath.length() - 1; p >= str; --p)
  165. {
  166. switch (*p)
  167. {
  168. case ':':
  169. case '/':
  170. case '\\':
  171. return filepath.substr(0, p - str + 1);
  172. }
  173. }
  174. return "";
  175. }
  176. //! Extract path from full specified file path.
  177. inline AZStd::string GetPath(const char* filepath)
  178. {
  179. return GetPath(AZStd::string(filepath));
  180. }
  181. //! Extract path from full specified file path.
  182. inline stack_string GetPath(const stack_string& filepath)
  183. {
  184. const char* str = filepath.c_str();
  185. for (const char* p = str + filepath.length() - 1; p >= str; --p)
  186. {
  187. switch (*p)
  188. {
  189. case ':':
  190. case '/':
  191. case '\\':
  192. return filepath.substr(0, p - str + 1);
  193. }
  194. }
  195. return "";
  196. }
  197. //! Extract file name with extension from full specified file path.
  198. inline AZStd::string GetFile(const AZStd::string& filepath)
  199. {
  200. const char* str = filepath.c_str();
  201. for (const char* p = str + filepath.length() - 1; p >= str; --p)
  202. {
  203. switch (*p)
  204. {
  205. case ':':
  206. case '/':
  207. case '\\':
  208. return filepath.substr(p - str + 1);
  209. }
  210. }
  211. return filepath;
  212. }
  213. inline const char* GetFile(const char* filepath)
  214. {
  215. const size_t len = strlen(filepath);
  216. for (const char* p = filepath + len - 1; p >= filepath; --p)
  217. {
  218. switch (*p)
  219. {
  220. case ':':
  221. case '/':
  222. case '\\':
  223. return p + 1;
  224. }
  225. }
  226. return filepath;
  227. }
  228. //! Replace extension for given file.
  229. inline void RemoveExtension(AZStd::string& filepath)
  230. {
  231. const char* str = filepath.c_str();
  232. for (const char* p = str + filepath.length() - 1; p >= str; --p)
  233. {
  234. switch (*p)
  235. {
  236. case ':':
  237. case '/':
  238. case '\\':
  239. // we've reached a path separator - it means there's no extension in this name
  240. return;
  241. case '.':
  242. // there's an extension in this file name
  243. filepath = filepath.substr(0, p - str);
  244. return;
  245. }
  246. }
  247. // it seems the file name is a pure name, without path or extension
  248. }
  249. //! Replace extension for given file.
  250. inline void RemoveExtension(stack_string& filepath)
  251. {
  252. const char* str = filepath.c_str();
  253. for (const char* p = str + filepath.length() - 1; p >= str; --p)
  254. {
  255. switch (*p)
  256. {
  257. case ':':
  258. case '/':
  259. case '\\':
  260. // we've reached a path separator - it means there's no extension in this name
  261. return;
  262. case '.':
  263. // there's an extension in this file name
  264. filepath = filepath.substr(0, p - str);
  265. return;
  266. }
  267. }
  268. // it seems the file name is a pure name, without path or extension
  269. }
  270. //! Extract file name without extension from full specified file path.
  271. inline AZStd::string GetFileName(const AZStd::string& filepath)
  272. {
  273. AZStd::string file = filepath;
  274. RemoveExtension(file);
  275. return GetFile(file);
  276. }
  277. //! Removes the trailing slash or backslash from a given path.
  278. inline AZStd::string RemoveSlash(const AZStd::string& path)
  279. {
  280. if (path.empty() || (path[path.length() - 1] != '/' && path[path.length() - 1] != '\\'))
  281. {
  282. return path;
  283. }
  284. return path.substr(0, path.length() - 1);
  285. }
  286. //! get slash
  287. inline AZStd::string GetSlash()
  288. {
  289. return CRY_NATIVE_PATH_SEPSTR;
  290. }
  291. //! add a backslash if needed
  292. inline AZStd::string AddSlash(const AZStd::string& path)
  293. {
  294. if (path.empty() || path[path.length() - 1] == '/')
  295. {
  296. return path;
  297. }
  298. if (path[path.length() - 1] == '\\')
  299. {
  300. return path.substr(0, path.length() - 1) + "/";
  301. }
  302. return path + "/";
  303. }
  304. //! add a backslash if needed
  305. inline stack_string AddSlash(const stack_string& path)
  306. {
  307. if (path.empty() || path[path.length() - 1] == '/')
  308. {
  309. return path;
  310. }
  311. if (path[path.length() - 1] == '\\')
  312. {
  313. return path.substr(0, path.length() - 1) + "/";
  314. }
  315. return path + "/";
  316. }
  317. //! add a backslash if needed
  318. inline AZStd::string AddSlash(const char* path)
  319. {
  320. return AddSlash(AZStd::string(path));
  321. }
  322. inline stack_string ReplaceExtension(const stack_string& filepath, const char* ext)
  323. {
  324. stack_string str = filepath;
  325. if (ext != 0)
  326. {
  327. RemoveExtension(str);
  328. if (ext[0] != 0 && ext[0] != '.')
  329. {
  330. str += ".";
  331. }
  332. str += ext;
  333. }
  334. return str;
  335. }
  336. //! Replace extension for given file.
  337. inline AZStd::string ReplaceExtension(const AZStd::string& filepath, const char* ext)
  338. {
  339. AZStd::string str = filepath;
  340. if (ext != 0)
  341. {
  342. RemoveExtension(str);
  343. if (ext[0] != 0 && ext[0] != '.')
  344. {
  345. str += ".";
  346. }
  347. str += ext;
  348. }
  349. return str;
  350. }
  351. //! Replace extension for given file.
  352. inline AZStd::string ReplaceExtension(const char* filepath, const char* ext)
  353. {
  354. return ReplaceExtension(AZStd::string(filepath), ext);
  355. }
  356. //! Makes a fully specified file path from path and file name.
  357. inline AZStd::string Make(const AZStd::string& path, const AZStd::string& file)
  358. {
  359. return AddSlash(path) + file;
  360. }
  361. //! Makes a fully specified file path from path and file name.
  362. inline AZStd::string Make(const AZStd::string& dir, const AZStd::string& filename, const AZStd::string& ext)
  363. {
  364. AZStd::string path = filename;
  365. AZ::StringFunc::Path::ReplaceExtension(path, ext.c_str());
  366. path = AddSlash(dir) + path;
  367. return path;
  368. }
  369. //! Makes a fully specified file path from path and file name.
  370. inline AZStd::string Make(const AZStd::string& dir, const AZStd::string& filename, const char* ext)
  371. {
  372. return Make(dir, filename, AZStd::string(ext));
  373. }
  374. //! Makes a fully specified file path from path and file name.
  375. inline stack_string Make(const stack_string& path, const stack_string& file)
  376. {
  377. return AddSlash(path) + file;
  378. }
  379. //! Makes a fully specified file path from path and file name.
  380. inline stack_string Make(const stack_string& dir, const stack_string& filename, const stack_string& ext)
  381. {
  382. AZStd::string path = filename.c_str();
  383. AZ::StringFunc::Path::ReplaceExtension(path, ext.c_str());
  384. path = AddSlash(dir.c_str()) + path;
  385. return stack_string(path.c_str());
  386. }
  387. //! Makes a fully specified file path from path and file name.
  388. inline AZStd::string Make(const char* path, const AZStd::string& file)
  389. {
  390. return Make(AZStd::string(path), file);
  391. }
  392. //! Makes a fully specified file path from path and file name.
  393. inline AZStd::string Make(const AZStd::string& path, const char* file)
  394. {
  395. return Make(path, AZStd::string(file));
  396. }
  397. //! Makes a fully specified file path from path and file name.
  398. inline AZStd::string Make(const char path[], const char file[])
  399. {
  400. return Make(AZStd::string(path), AZStd::string(file));
  401. }
  402. //! Makes a fully specified file path from path and file name.
  403. inline AZStd::string Make(const char* path, const char* file, const char* ext)
  404. {
  405. return Make(AZStd::string(path), AZStd::string(file), AZStd::string(ext));
  406. }
  407. //! Makes a fully specified file path from path and file name.
  408. inline AZStd::string MakeFullPath(const AZStd::string& relativePath)
  409. {
  410. return relativePath;
  411. }
  412. inline AZStd::string GetParentDirectory (const AZStd::string& strFilePath, int nGeneration = 1)
  413. {
  414. for (const char* p = strFilePath.c_str() + strFilePath.length() - 2; // -2 is for the possible trailing slash: there always must be some trailing symbol which is the file/directory name for which we should get the parent
  415. p >= strFilePath.c_str();
  416. --p)
  417. {
  418. switch (*p)
  419. {
  420. case ':':
  421. return AZStd::string(strFilePath.c_str(), p);
  422. case '/':
  423. case '\\':
  424. // we've reached a path separator - return everything before it.
  425. if (!--nGeneration)
  426. {
  427. return AZStd::string(strFilePath.c_str(), p);
  428. }
  429. break;
  430. }
  431. }
  432. // it seems the file name is a pure name, without path or extension
  433. return AZStd::string();
  434. }
  435. template<typename T, size_t SIZE>
  436. inline AZStd::basic_fixed_string<T, SIZE> GetParentDirectoryStackString(const AZStd::basic_fixed_string<T, SIZE>& strFilePath, int nGeneration = 1)
  437. {
  438. for (const char* p = strFilePath.c_str() + strFilePath.length() - 2; // -2 is for the possible trailing slash: there always must be some trailing symbol which is the file/directory name for which we should get the parent
  439. p >= strFilePath.c_str();
  440. --p)
  441. {
  442. switch (*p)
  443. {
  444. case ':':
  445. return AZStd::basic_fixed_string<T, SIZE> (strFilePath.c_str(), p);
  446. case '/':
  447. case '\\':
  448. // we've reached a path separator - return everything before it.
  449. if (!--nGeneration)
  450. {
  451. return AZStd::basic_fixed_string<T, SIZE>(strFilePath.c_str(), p);
  452. }
  453. break;
  454. }
  455. }
  456. // it seems the file name is a pure name, without path or extension
  457. return AZStd::basic_fixed_string<T, SIZE>();
  458. }
  459. //////////////////////////////////////////////////////////////////////////
  460. // Description:
  461. // Make a game correct path out of any input path.
  462. inline stack_string MakeGamePath(const stack_string& path)
  463. {
  464. stack_string relativePath = path;
  465. ToUnixPath(relativePath);
  466. if ((!gEnv) || (!gEnv->pFileIO))
  467. {
  468. return relativePath;
  469. }
  470. unsigned int index = 0;
  471. if (relativePath.length() && relativePath[index] == '@') // already aliased
  472. {
  473. if (AZ::StringFunc::Equal(relativePath.c_str(), "@products@/", false, 9))
  474. {
  475. return relativePath.substr(9); // assets is assumed.
  476. }
  477. return relativePath;
  478. }
  479. const char* rootValue = gEnv->pFileIO->GetAlias("@products@");
  480. if (rootValue)
  481. {
  482. stack_string rootPath(ToUnixPath(rootValue));
  483. if (
  484. (rootPath.size() > 0) &&
  485. (rootPath.size() < relativePath.size()) &&
  486. (AZ::StringFunc::Equal(relativePath.c_str(), rootPath.c_str(), false, rootPath.size()))
  487. )
  488. {
  489. stack_string chopped_string = relativePath.substr(rootPath.size());
  490. stack_string rooted = stack_string("@products@") + chopped_string;
  491. return rooted;
  492. }
  493. }
  494. return relativePath;
  495. }
  496. //////////////////////////////////////////////////////////////////////////
  497. // Description:
  498. // Make a game correct path out of any input path.
  499. inline AZStd::string MakeGamePath(const AZStd::string& path)
  500. {
  501. stack_string stackPath(path.c_str());
  502. return MakeGamePath(stackPath).c_str();
  503. }
  504. // returns true if the AZStd::string matches the wildcard
  505. inline bool MatchWildcard (const char* szString, const char* szWildcard)
  506. {
  507. const char* pString = szString, * pWildcard = szWildcard;
  508. // skip the obviously the same starting substring
  509. while (*pWildcard && *pWildcard != '*' && *pWildcard != '?')
  510. {
  511. if (*pString != *pWildcard)
  512. {
  513. return false; // must be exact match unless there's a wildcard character in the wildcard string
  514. }
  515. else
  516. {
  517. ++pString, ++pWildcard;
  518. }
  519. }
  520. if (!*pString)
  521. {
  522. // this will only match if there are no non-wild characters in the wildcard
  523. for (; *pWildcard; ++pWildcard)
  524. {
  525. if (*pWildcard != '*' && *pWildcard != '?')
  526. {
  527. return false;
  528. }
  529. }
  530. return true;
  531. }
  532. switch (*pWildcard)
  533. {
  534. case '\0':
  535. return false; // the only way to match them after the leading non-wildcard characters is !*pString, which was already checked
  536. // we have a wildcard with wild character at the start.
  537. case '*':
  538. {
  539. // merge consecutive ? and *, since they are equivalent to a single *
  540. while (*pWildcard == '*' || *pWildcard == '?')
  541. {
  542. ++pWildcard;
  543. }
  544. if (!*pWildcard)
  545. {
  546. return true; // the rest of the AZStd::string doesn't matter: the wildcard ends with *
  547. }
  548. for (; *pString; ++pString)
  549. {
  550. if (MatchWildcard(pString, pWildcard))
  551. {
  552. return true;
  553. }
  554. }
  555. return false;
  556. }
  557. case '?':
  558. return MatchWildcard(pString + 1, pWildcard + 1) || MatchWildcard(pString, pWildcard + 1);
  559. default:
  560. assert (0);
  561. return false;
  562. }
  563. }
  564. };
  565. #endif // CRYINCLUDE_CRYCOMMON_CRYPATH_H