file_util.cpp 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009
  1. // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include "common/assert.h"
  5. #include "common/common_funcs.h"
  6. #include "common/common_paths.h"
  7. #include "common/file_util.h"
  8. #include "common/logging/log.h"
  9. #ifdef _WIN32
  10. #include <windows.h>
  11. #include <shlobj.h> // for SHGetFolderPath
  12. #include <shellapi.h>
  13. #include <commdlg.h> // for GetSaveFileName
  14. #include <io.h>
  15. #include <direct.h> // getcwd
  16. #include <tchar.h>
  17. // 64 bit offsets for windows
  18. #define fseeko _fseeki64
  19. #define ftello _ftelli64
  20. #define atoll _atoi64
  21. #define stat64 _stat64
  22. #define fstat64 _fstat64
  23. #define fileno _fileno
  24. #else
  25. #include <sys/param.h>
  26. #include <sys/types.h>
  27. #include <dirent.h>
  28. #include <pwd.h>
  29. #include <unistd.h>
  30. #endif
  31. #if defined(__APPLE__)
  32. #include <CoreFoundation/CFString.h>
  33. #include <CoreFoundation/CFURL.h>
  34. #include <CoreFoundation/CFBundle.h>
  35. #endif
  36. #include <algorithm>
  37. #include <sys/stat.h>
  38. #ifndef S_ISDIR
  39. #define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR)
  40. #endif
  41. #ifdef BSD4_4
  42. #define stat64 stat
  43. #define fstat64 fstat
  44. #endif
  45. // This namespace has various generic functions related to files and paths.
  46. // The code still needs a ton of cleanup.
  47. // REMEMBER: strdup considered harmful!
  48. namespace FileUtil
  49. {
  50. // Remove any ending forward slashes from directory paths
  51. // Modifies argument.
  52. static void StripTailDirSlashes(std::string &fname)
  53. {
  54. if (fname.length() > 1)
  55. {
  56. size_t i = fname.length() - 1;
  57. while (fname[i] == DIR_SEP_CHR)
  58. fname[i--] = '\0';
  59. }
  60. return;
  61. }
  62. // Returns true if file filename exists
  63. bool Exists(const std::string &filename)
  64. {
  65. struct stat64 file_info;
  66. std::string copy(filename);
  67. StripTailDirSlashes(copy);
  68. #ifdef _WIN32
  69. int result = _tstat64(Common::UTF8ToTStr(copy).c_str(), &file_info);
  70. #else
  71. int result = stat64(copy.c_str(), &file_info);
  72. #endif
  73. return (result == 0);
  74. }
  75. // Returns true if filename is a directory
  76. bool IsDirectory(const std::string &filename)
  77. {
  78. struct stat64 file_info;
  79. std::string copy(filename);
  80. StripTailDirSlashes(copy);
  81. #ifdef _WIN32
  82. int result = _tstat64(Common::UTF8ToTStr(copy).c_str(), &file_info);
  83. #else
  84. int result = stat64(copy.c_str(), &file_info);
  85. #endif
  86. if (result < 0) {
  87. LOG_WARNING(Common_Filesystem, "stat failed on %s: %s",
  88. filename.c_str(), GetLastErrorMsg());
  89. return false;
  90. }
  91. return S_ISDIR(file_info.st_mode);
  92. }
  93. // Deletes a given filename, return true on success
  94. // Doesn't supports deleting a directory
  95. bool Delete(const std::string &filename)
  96. {
  97. LOG_INFO(Common_Filesystem, "file %s", filename.c_str());
  98. // Return true because we care about the file no
  99. // being there, not the actual delete.
  100. if (!Exists(filename))
  101. {
  102. LOG_WARNING(Common_Filesystem, "%s does not exist", filename.c_str());
  103. return true;
  104. }
  105. // We can't delete a directory
  106. if (IsDirectory(filename))
  107. {
  108. LOG_ERROR(Common_Filesystem, "Failed: %s is a directory", filename.c_str());
  109. return false;
  110. }
  111. #ifdef _WIN32
  112. if (!DeleteFile(Common::UTF8ToTStr(filename).c_str()))
  113. {
  114. LOG_ERROR(Common_Filesystem, "DeleteFile failed on %s: %s",
  115. filename.c_str(), GetLastErrorMsg());
  116. return false;
  117. }
  118. #else
  119. if (unlink(filename.c_str()) == -1) {
  120. LOG_ERROR(Common_Filesystem, "unlink failed on %s: %s",
  121. filename.c_str(), GetLastErrorMsg());
  122. return false;
  123. }
  124. #endif
  125. return true;
  126. }
  127. // Returns true if successful, or path already exists.
  128. bool CreateDir(const std::string &path)
  129. {
  130. LOG_TRACE(Common_Filesystem, "directory %s", path.c_str());
  131. #ifdef _WIN32
  132. if (::CreateDirectory(Common::UTF8ToTStr(path).c_str(), nullptr))
  133. return true;
  134. DWORD error = GetLastError();
  135. if (error == ERROR_ALREADY_EXISTS)
  136. {
  137. LOG_WARNING(Common_Filesystem, "CreateDirectory failed on %s: already exists", path.c_str());
  138. return true;
  139. }
  140. LOG_ERROR(Common_Filesystem, "CreateDirectory failed on %s: %i", path.c_str(), error);
  141. return false;
  142. #else
  143. if (mkdir(path.c_str(), 0755) == 0)
  144. return true;
  145. int err = errno;
  146. if (err == EEXIST)
  147. {
  148. LOG_WARNING(Common_Filesystem, "mkdir failed on %s: already exists", path.c_str());
  149. return true;
  150. }
  151. LOG_ERROR(Common_Filesystem, "mkdir failed on %s: %s", path.c_str(), strerror(err));
  152. return false;
  153. #endif
  154. }
  155. // Creates the full path of fullPath returns true on success
  156. bool CreateFullPath(const std::string &fullPath)
  157. {
  158. int panicCounter = 100;
  159. LOG_TRACE(Common_Filesystem, "path %s", fullPath.c_str());
  160. if (FileUtil::Exists(fullPath))
  161. {
  162. LOG_WARNING(Common_Filesystem, "path exists %s", fullPath.c_str());
  163. return true;
  164. }
  165. size_t position = 0;
  166. while (true)
  167. {
  168. // Find next sub path
  169. position = fullPath.find(DIR_SEP_CHR, position);
  170. // we're done, yay!
  171. if (position == fullPath.npos)
  172. return true;
  173. // Include the '/' so the first call is CreateDir("/") rather than CreateDir("")
  174. std::string const subPath(fullPath.substr(0, position + 1));
  175. if (!FileUtil::IsDirectory(subPath) && !FileUtil::CreateDir(subPath)) {
  176. LOG_ERROR(Common, "CreateFullPath: directory creation failed");
  177. return false;
  178. }
  179. // A safety check
  180. panicCounter--;
  181. if (panicCounter <= 0)
  182. {
  183. LOG_ERROR(Common, "CreateFullPath: directory structure is too deep");
  184. return false;
  185. }
  186. position++;
  187. }
  188. }
  189. // Deletes a directory filename, returns true on success
  190. bool DeleteDir(const std::string &filename)
  191. {
  192. LOG_INFO(Common_Filesystem, "directory %s", filename.c_str());
  193. // check if a directory
  194. if (!FileUtil::IsDirectory(filename))
  195. {
  196. LOG_ERROR(Common_Filesystem, "Not a directory %s", filename.c_str());
  197. return false;
  198. }
  199. #ifdef _WIN32
  200. if (::RemoveDirectory(Common::UTF8ToTStr(filename).c_str()))
  201. return true;
  202. #else
  203. if (rmdir(filename.c_str()) == 0)
  204. return true;
  205. #endif
  206. LOG_ERROR(Common_Filesystem, "failed %s: %s", filename.c_str(), GetLastErrorMsg());
  207. return false;
  208. }
  209. // renames file srcFilename to destFilename, returns true on success
  210. bool Rename(const std::string &srcFilename, const std::string &destFilename)
  211. {
  212. LOG_TRACE(Common_Filesystem, "%s --> %s",
  213. srcFilename.c_str(), destFilename.c_str());
  214. if (rename(srcFilename.c_str(), destFilename.c_str()) == 0)
  215. return true;
  216. LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s",
  217. srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
  218. return false;
  219. }
  220. // copies file srcFilename to destFilename, returns true on success
  221. bool Copy(const std::string &srcFilename, const std::string &destFilename)
  222. {
  223. LOG_TRACE(Common_Filesystem, "%s --> %s",
  224. srcFilename.c_str(), destFilename.c_str());
  225. #ifdef _WIN32
  226. if (CopyFile(Common::UTF8ToTStr(srcFilename).c_str(), Common::UTF8ToTStr(destFilename).c_str(), FALSE))
  227. return true;
  228. LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s",
  229. srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
  230. return false;
  231. #else
  232. // buffer size
  233. #define BSIZE 1024
  234. char buffer[BSIZE];
  235. // Open input file
  236. FILE *input = fopen(srcFilename.c_str(), "rb");
  237. if (!input)
  238. {
  239. LOG_ERROR(Common_Filesystem, "opening input failed %s --> %s: %s",
  240. srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
  241. return false;
  242. }
  243. // open output file
  244. FILE *output = fopen(destFilename.c_str(), "wb");
  245. if (!output)
  246. {
  247. fclose(input);
  248. LOG_ERROR(Common_Filesystem, "opening output failed %s --> %s: %s",
  249. srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
  250. return false;
  251. }
  252. // copy loop
  253. while (!feof(input))
  254. {
  255. // read input
  256. int rnum = fread(buffer, sizeof(char), BSIZE, input);
  257. if (rnum != BSIZE)
  258. {
  259. if (ferror(input) != 0)
  260. {
  261. LOG_ERROR(Common_Filesystem,
  262. "failed reading from source, %s --> %s: %s",
  263. srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
  264. goto bail;
  265. }
  266. }
  267. // write output
  268. int wnum = fwrite(buffer, sizeof(char), rnum, output);
  269. if (wnum != rnum)
  270. {
  271. LOG_ERROR(Common_Filesystem,
  272. "failed writing to output, %s --> %s: %s",
  273. srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
  274. goto bail;
  275. }
  276. }
  277. // close files
  278. fclose(input);
  279. fclose(output);
  280. return true;
  281. bail:
  282. if (input)
  283. fclose(input);
  284. if (output)
  285. fclose(output);
  286. return false;
  287. #endif
  288. }
  289. // Returns the size of filename (64bit)
  290. u64 GetSize(const std::string &filename)
  291. {
  292. if (!Exists(filename))
  293. {
  294. LOG_ERROR(Common_Filesystem, "failed %s: No such file", filename.c_str());
  295. return 0;
  296. }
  297. if (IsDirectory(filename))
  298. {
  299. LOG_ERROR(Common_Filesystem, "failed %s: is a directory", filename.c_str());
  300. return 0;
  301. }
  302. struct stat64 buf;
  303. #ifdef _WIN32
  304. if (_tstat64(Common::UTF8ToTStr(filename).c_str(), &buf) == 0)
  305. #else
  306. if (stat64(filename.c_str(), &buf) == 0)
  307. #endif
  308. {
  309. LOG_TRACE(Common_Filesystem, "%s: %lld",
  310. filename.c_str(), (long long)buf.st_size);
  311. return buf.st_size;
  312. }
  313. LOG_ERROR(Common_Filesystem, "Stat failed %s: %s",
  314. filename.c_str(), GetLastErrorMsg());
  315. return 0;
  316. }
  317. // Overloaded GetSize, accepts file descriptor
  318. u64 GetSize(const int fd)
  319. {
  320. struct stat64 buf;
  321. if (fstat64(fd, &buf) != 0) {
  322. LOG_ERROR(Common_Filesystem, "GetSize: stat failed %i: %s",
  323. fd, GetLastErrorMsg());
  324. return 0;
  325. }
  326. return buf.st_size;
  327. }
  328. // Overloaded GetSize, accepts FILE*
  329. u64 GetSize(FILE *f)
  330. {
  331. // can't use off_t here because it can be 32-bit
  332. u64 pos = ftello(f);
  333. if (fseeko(f, 0, SEEK_END) != 0) {
  334. LOG_ERROR(Common_Filesystem, "GetSize: seek failed %p: %s",
  335. f, GetLastErrorMsg());
  336. return 0;
  337. }
  338. u64 size = ftello(f);
  339. if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) {
  340. LOG_ERROR(Common_Filesystem, "GetSize: seek failed %p: %s",
  341. f, GetLastErrorMsg());
  342. return 0;
  343. }
  344. return size;
  345. }
  346. // creates an empty file filename, returns true on success
  347. bool CreateEmptyFile(const std::string &filename)
  348. {
  349. LOG_TRACE(Common_Filesystem, "%s", filename.c_str());
  350. if (!FileUtil::IOFile(filename, "wb"))
  351. {
  352. LOG_ERROR(Common_Filesystem, "failed %s: %s",
  353. filename.c_str(), GetLastErrorMsg());
  354. return false;
  355. }
  356. return true;
  357. }
  358. // Scans the directory tree gets, starting from _Directory and adds the
  359. // results into parentEntry. Returns the number of files+directories found
  360. u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry)
  361. {
  362. LOG_TRACE(Common_Filesystem, "directory %s", directory.c_str());
  363. // How many files + directories we found
  364. u32 foundEntries = 0;
  365. #ifdef _WIN32
  366. // Find the first file in the directory.
  367. WIN32_FIND_DATA ffd;
  368. HANDLE hFind = FindFirstFile(Common::UTF8ToTStr(directory + "\\*").c_str(), &ffd);
  369. if (hFind == INVALID_HANDLE_VALUE)
  370. {
  371. FindClose(hFind);
  372. return foundEntries;
  373. }
  374. // windows loop
  375. do
  376. {
  377. FSTEntry entry;
  378. const std::string virtualName(Common::TStrToUTF8(ffd.cFileName));
  379. #else
  380. struct dirent dirent, *result = nullptr;
  381. DIR *dirp = opendir(directory.c_str());
  382. if (!dirp)
  383. return 0;
  384. // non windows loop
  385. while (!readdir_r(dirp, &dirent, &result) && result)
  386. {
  387. FSTEntry entry;
  388. const std::string virtualName(result->d_name);
  389. #endif
  390. // check for "." and ".."
  391. if (((virtualName[0] == '.') && (virtualName[1] == '\0')) ||
  392. ((virtualName[0] == '.') && (virtualName[1] == '.') &&
  393. (virtualName[2] == '\0')))
  394. continue;
  395. entry.virtualName = virtualName;
  396. entry.physicalName = directory;
  397. entry.physicalName += DIR_SEP + entry.virtualName;
  398. if (IsDirectory(entry.physicalName.c_str()))
  399. {
  400. entry.isDirectory = true;
  401. // is a directory, lets go inside
  402. entry.size = ScanDirectoryTree(entry.physicalName, entry);
  403. foundEntries += (u32)entry.size;
  404. }
  405. else
  406. { // is a file
  407. entry.isDirectory = false;
  408. entry.size = GetSize(entry.physicalName.c_str());
  409. }
  410. ++foundEntries;
  411. // Push into the tree
  412. parentEntry.children.push_back(entry);
  413. #ifdef _WIN32
  414. } while (FindNextFile(hFind, &ffd) != 0);
  415. FindClose(hFind);
  416. #else
  417. }
  418. closedir(dirp);
  419. #endif
  420. // Return number of entries found.
  421. return foundEntries;
  422. }
  423. // Deletes the given directory and anything under it. Returns true on success.
  424. bool DeleteDirRecursively(const std::string &directory)
  425. {
  426. LOG_TRACE(Common_Filesystem, "%s", directory.c_str());
  427. #ifdef _WIN32
  428. // Find the first file in the directory.
  429. WIN32_FIND_DATA ffd;
  430. HANDLE hFind = FindFirstFile(Common::UTF8ToTStr(directory + "\\*").c_str(), &ffd);
  431. if (hFind == INVALID_HANDLE_VALUE)
  432. {
  433. FindClose(hFind);
  434. return false;
  435. }
  436. // windows loop
  437. do
  438. {
  439. const std::string virtualName(Common::TStrToUTF8(ffd.cFileName));
  440. #else
  441. struct dirent dirent, *result = nullptr;
  442. DIR *dirp = opendir(directory.c_str());
  443. if (!dirp)
  444. return false;
  445. // non windows loop
  446. while (!readdir_r(dirp, &dirent, &result) && result)
  447. {
  448. const std::string virtualName = result->d_name;
  449. #endif
  450. // check for "." and ".."
  451. if (((virtualName[0] == '.') && (virtualName[1] == '\0')) ||
  452. ((virtualName[0] == '.') && (virtualName[1] == '.') &&
  453. (virtualName[2] == '\0')))
  454. continue;
  455. std::string newPath = directory + DIR_SEP_CHR + virtualName;
  456. if (IsDirectory(newPath))
  457. {
  458. if (!DeleteDirRecursively(newPath))
  459. {
  460. #ifndef _WIN32
  461. closedir(dirp);
  462. #endif
  463. return false;
  464. }
  465. }
  466. else
  467. {
  468. if (!FileUtil::Delete(newPath))
  469. {
  470. #ifndef _WIN32
  471. closedir(dirp);
  472. #endif
  473. return false;
  474. }
  475. }
  476. #ifdef _WIN32
  477. } while (FindNextFile(hFind, &ffd) != 0);
  478. FindClose(hFind);
  479. #else
  480. }
  481. closedir(dirp);
  482. #endif
  483. FileUtil::DeleteDir(directory);
  484. return true;
  485. }
  486. // Create directory and copy contents (does not overwrite existing files)
  487. void CopyDir(const std::string &source_path, const std::string &dest_path)
  488. {
  489. #ifndef _WIN32
  490. if (source_path == dest_path) return;
  491. if (!FileUtil::Exists(source_path)) return;
  492. if (!FileUtil::Exists(dest_path)) FileUtil::CreateFullPath(dest_path);
  493. struct dirent dirent, *result = nullptr;
  494. DIR *dirp = opendir(source_path.c_str());
  495. if (!dirp) return;
  496. while (!readdir_r(dirp, &dirent, &result) && result)
  497. {
  498. const std::string virtualName(result->d_name);
  499. // check for "." and ".."
  500. if (((virtualName[0] == '.') && (virtualName[1] == '\0')) ||
  501. ((virtualName[0] == '.') && (virtualName[1] == '.') &&
  502. (virtualName[2] == '\0')))
  503. continue;
  504. std::string source, dest;
  505. source = source_path + virtualName;
  506. dest = dest_path + virtualName;
  507. if (IsDirectory(source))
  508. {
  509. source += '/';
  510. dest += '/';
  511. if (!FileUtil::Exists(dest)) FileUtil::CreateFullPath(dest);
  512. CopyDir(source, dest);
  513. }
  514. else if (!FileUtil::Exists(dest)) FileUtil::Copy(source, dest);
  515. }
  516. closedir(dirp);
  517. #endif
  518. }
  519. // Returns the current directory
  520. std::string GetCurrentDir()
  521. {
  522. char *dir;
  523. // Get the current working directory (getcwd uses malloc)
  524. if (!(dir = getcwd(nullptr, 0))) {
  525. LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: %s",
  526. GetLastErrorMsg());
  527. return nullptr;
  528. }
  529. std::string strDir = dir;
  530. free(dir);
  531. return strDir;
  532. }
  533. // Sets the current directory to the given directory
  534. bool SetCurrentDir(const std::string &directory)
  535. {
  536. return chdir(directory.c_str()) == 0;
  537. }
  538. #if defined(__APPLE__)
  539. std::string GetBundleDirectory()
  540. {
  541. CFURLRef BundleRef;
  542. char AppBundlePath[MAXPATHLEN];
  543. // Get the main bundle for the app
  544. BundleRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
  545. CFStringRef BundlePath = CFURLCopyFileSystemPath(BundleRef, kCFURLPOSIXPathStyle);
  546. CFStringGetFileSystemRepresentation(BundlePath, AppBundlePath, sizeof(AppBundlePath));
  547. CFRelease(BundleRef);
  548. CFRelease(BundlePath);
  549. return AppBundlePath;
  550. }
  551. #endif
  552. #ifdef _WIN32
  553. std::string& GetExeDirectory()
  554. {
  555. static std::string exe_path;
  556. if (exe_path.empty())
  557. {
  558. TCHAR tchar_exe_path[2048];
  559. GetModuleFileName(nullptr, tchar_exe_path, 2048);
  560. exe_path = Common::TStrToUTF8(tchar_exe_path);
  561. exe_path = exe_path.substr(0, exe_path.find_last_of('\\'));
  562. }
  563. return exe_path;
  564. }
  565. #else
  566. /**
  567. * @return The user’s home directory on POSIX systems
  568. */
  569. static const std::string& GetHomeDirectory() {
  570. static std::string home_path;
  571. if (home_path.empty()) {
  572. const char* envvar = getenv("HOME");
  573. if (envvar) {
  574. home_path = envvar;
  575. } else {
  576. auto pw = getpwuid(getuid());
  577. ASSERT_MSG(pw, "$HOME isn’t defined, and the current user can’t be found in /etc/passwd.");
  578. home_path = pw->pw_dir;
  579. }
  580. }
  581. return home_path;
  582. }
  583. /**
  584. * Follows the XDG Base Directory Specification to get a directory path
  585. * @param envvar The XDG environment variable to get the value from
  586. * @return The directory path
  587. * @sa http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
  588. */
  589. static const std::string GetUserDirectory(const std::string& envvar) {
  590. const char* directory = getenv(envvar.c_str());
  591. std::string user_dir;
  592. if (directory) {
  593. user_dir = directory;
  594. } else {
  595. std::string subdirectory;
  596. if (envvar == "XDG_DATA_HOME")
  597. subdirectory = DIR_SEP ".local" DIR_SEP "share";
  598. else if (envvar == "XDG_CONFIG_HOME")
  599. subdirectory = DIR_SEP ".config";
  600. else if (envvar == "XDG_CACHE_HOME")
  601. subdirectory = DIR_SEP ".cache";
  602. else
  603. ASSERT_MSG(false, "Unknown XDG variable %s.", envvar.c_str());
  604. user_dir = GetHomeDirectory() + subdirectory;
  605. }
  606. ASSERT_MSG(!user_dir.empty(), "User directory %s musn’t be empty.", envvar.c_str());
  607. ASSERT_MSG(user_dir[0] == '/', "User directory %s must be absolute.", envvar.c_str());
  608. return user_dir;
  609. }
  610. #endif
  611. std::string GetSysDirectory()
  612. {
  613. std::string sysDir;
  614. #if defined (__APPLE__)
  615. sysDir = GetBundleDirectory();
  616. sysDir += DIR_SEP;
  617. sysDir += SYSDATA_DIR;
  618. #else
  619. sysDir = SYSDATA_DIR;
  620. #endif
  621. sysDir += DIR_SEP;
  622. LOG_DEBUG(Common_Filesystem, "Setting to %s:", sysDir.c_str());
  623. return sysDir;
  624. }
  625. // Returns a string with a Citra data dir or file in the user's home
  626. // directory. To be used in "multi-user" mode (that is, installed).
  627. const std::string& GetUserPath(const unsigned int DirIDX, const std::string &newPath)
  628. {
  629. static std::string paths[NUM_PATH_INDICES];
  630. // Set up all paths and files on the first run
  631. if (paths[D_USER_IDX].empty())
  632. {
  633. #ifdef _WIN32
  634. paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
  635. paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
  636. paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
  637. #else
  638. if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) {
  639. paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
  640. paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
  641. paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
  642. } else {
  643. std::string data_dir = GetUserDirectory("XDG_DATA_HOME");
  644. std::string config_dir = GetUserDirectory("XDG_CONFIG_HOME");
  645. std::string cache_dir = GetUserDirectory("XDG_CACHE_HOME");
  646. paths[D_USER_IDX] = data_dir + DIR_SEP EMU_DATA_DIR DIR_SEP;
  647. paths[D_CONFIG_IDX] = config_dir + DIR_SEP EMU_DATA_DIR DIR_SEP;
  648. paths[D_CACHE_IDX] = cache_dir + DIR_SEP EMU_DATA_DIR DIR_SEP;
  649. }
  650. #endif
  651. paths[D_GAMECONFIG_IDX] = paths[D_USER_IDX] + GAMECONFIG_DIR DIR_SEP;
  652. paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
  653. paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
  654. paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP;
  655. paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP;
  656. paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
  657. paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
  658. paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP;
  659. paths[D_SCREENSHOTS_IDX] = paths[D_USER_IDX] + SCREENSHOTS_DIR DIR_SEP;
  660. paths[D_DUMP_IDX] = paths[D_USER_IDX] + DUMP_DIR DIR_SEP;
  661. paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP;
  662. paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP;
  663. paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP;
  664. paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOGS_DIR DIR_SEP;
  665. paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG;
  666. paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG;
  667. paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG;
  668. }
  669. if (!newPath.empty())
  670. {
  671. if (!FileUtil::IsDirectory(newPath))
  672. {
  673. LOG_ERROR(Common_Filesystem, "Invalid path specified %s", newPath.c_str());
  674. return paths[DirIDX];
  675. }
  676. else
  677. {
  678. paths[DirIDX] = newPath;
  679. }
  680. switch (DirIDX)
  681. {
  682. case D_ROOT_IDX:
  683. paths[D_USER_IDX] = paths[D_ROOT_IDX] + DIR_SEP;
  684. paths[D_SYSCONF_IDX] = paths[D_USER_IDX] + SYSCONF_DIR + DIR_SEP;
  685. paths[F_SYSCONF_IDX] = paths[D_SYSCONF_IDX] + SYSCONF;
  686. break;
  687. case D_USER_IDX:
  688. paths[D_USER_IDX] = paths[D_ROOT_IDX] + DIR_SEP;
  689. paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
  690. paths[D_GAMECONFIG_IDX] = paths[D_USER_IDX] + GAMECONFIG_DIR DIR_SEP;
  691. paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
  692. paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
  693. paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
  694. paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP;
  695. paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
  696. paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
  697. paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP;
  698. paths[D_SCREENSHOTS_IDX] = paths[D_USER_IDX] + SCREENSHOTS_DIR DIR_SEP;
  699. paths[D_DUMP_IDX] = paths[D_USER_IDX] + DUMP_DIR DIR_SEP;
  700. paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP;
  701. paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP;
  702. paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP;
  703. paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOGS_DIR DIR_SEP;
  704. paths[D_SYSCONF_IDX] = paths[D_USER_IDX] + SYSCONF_DIR DIR_SEP;
  705. paths[F_EMUCONFIG_IDX] = paths[D_CONFIG_IDX] + EMU_CONFIG;
  706. paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG;
  707. paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG;
  708. paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG;
  709. break;
  710. case D_CONFIG_IDX:
  711. paths[F_EMUCONFIG_IDX] = paths[D_CONFIG_IDX] + EMU_CONFIG;
  712. paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG;
  713. paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG;
  714. break;
  715. case D_DUMP_IDX:
  716. paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP;
  717. paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP;
  718. paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP;
  719. break;
  720. case D_LOGS_IDX:
  721. paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG;
  722. }
  723. }
  724. return paths[DirIDX];
  725. }
  726. size_t WriteStringToFile(bool text_file, const std::string &str, const char *filename)
  727. {
  728. return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size());
  729. }
  730. size_t ReadFileToString(bool text_file, const char *filename, std::string &str)
  731. {
  732. FileUtil::IOFile file(filename, text_file ? "r" : "rb");
  733. auto const f = file.GetHandle();
  734. if (!f)
  735. return false;
  736. str.resize(static_cast<u32>(GetSize(f)));
  737. return file.ReadArray(&str[0], str.size());
  738. }
  739. /**
  740. * Splits the filename into 8.3 format
  741. * Loosely implemented following https://en.wikipedia.org/wiki/8.3_filename
  742. * @param filename The normal filename to use
  743. * @param short_name A 9-char array in which the short name will be written
  744. * @param extension A 4-char array in which the extension will be written
  745. */
  746. void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name,
  747. std::array<char, 4>& extension) {
  748. const std::string forbidden_characters = ".\"/\\[]:;=, ";
  749. // On a FAT32 partition, 8.3 names are stored as a 11 bytes array, filled with spaces.
  750. short_name = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '\0'};
  751. extension = {' ', ' ', ' ', '\0'};
  752. std::string::size_type point = filename.rfind('.');
  753. if (point == filename.size() - 1)
  754. point = filename.rfind('.', point);
  755. // Get short name.
  756. int j = 0;
  757. for (char letter : filename.substr(0, point)) {
  758. if (forbidden_characters.find(letter, 0) != std::string::npos)
  759. continue;
  760. if (j == 8) {
  761. // TODO(Link Mauve): also do that for filenames containing a space.
  762. // TODO(Link Mauve): handle multiple files having the same short name.
  763. short_name[6] = '~';
  764. short_name[7] = '1';
  765. break;
  766. }
  767. short_name[j++] = toupper(letter);
  768. }
  769. // Get extension.
  770. if (point != std::string::npos) {
  771. j = 0;
  772. for (char letter : filename.substr(point + 1, 3))
  773. extension[j++] = toupper(letter);
  774. }
  775. }
  776. IOFile::IOFile()
  777. : m_file(nullptr), m_good(true)
  778. {}
  779. IOFile::IOFile(std::FILE* file)
  780. : m_file(file), m_good(true)
  781. {}
  782. IOFile::IOFile(const std::string& filename, const char openmode[])
  783. : m_file(nullptr), m_good(true)
  784. {
  785. Open(filename, openmode);
  786. }
  787. IOFile::~IOFile()
  788. {
  789. Close();
  790. }
  791. IOFile::IOFile(IOFile&& other)
  792. : m_file(nullptr), m_good(true)
  793. {
  794. Swap(other);
  795. }
  796. IOFile& IOFile::operator=(IOFile&& other)
  797. {
  798. Swap(other);
  799. return *this;
  800. }
  801. void IOFile::Swap(IOFile& other)
  802. {
  803. std::swap(m_file, other.m_file);
  804. std::swap(m_good, other.m_good);
  805. }
  806. bool IOFile::Open(const std::string& filename, const char openmode[])
  807. {
  808. Close();
  809. #ifdef _WIN32
  810. _tfopen_s(&m_file, Common::UTF8ToTStr(filename).c_str(), Common::UTF8ToTStr(openmode).c_str());
  811. #else
  812. m_file = fopen(filename.c_str(), openmode);
  813. #endif
  814. m_good = IsOpen();
  815. return m_good;
  816. }
  817. bool IOFile::Close()
  818. {
  819. if (!IsOpen() || 0 != std::fclose(m_file))
  820. m_good = false;
  821. m_file = nullptr;
  822. return m_good;
  823. }
  824. std::FILE* IOFile::ReleaseHandle()
  825. {
  826. std::FILE* const ret = m_file;
  827. m_file = nullptr;
  828. return ret;
  829. }
  830. void IOFile::SetHandle(std::FILE* file)
  831. {
  832. Close();
  833. Clear();
  834. m_file = file;
  835. }
  836. u64 IOFile::GetSize()
  837. {
  838. if (IsOpen())
  839. return FileUtil::GetSize(m_file);
  840. else
  841. return 0;
  842. }
  843. bool IOFile::Seek(s64 off, int origin)
  844. {
  845. if (!IsOpen() || 0 != fseeko(m_file, off, origin))
  846. m_good = false;
  847. return m_good;
  848. }
  849. u64 IOFile::Tell()
  850. {
  851. if (IsOpen())
  852. return ftello(m_file);
  853. else
  854. return -1;
  855. }
  856. bool IOFile::Flush()
  857. {
  858. if (!IsOpen() || 0 != std::fflush(m_file))
  859. m_good = false;
  860. return m_good;
  861. }
  862. bool IOFile::Resize(u64 size)
  863. {
  864. if (!IsOpen() || 0 !=
  865. #ifdef _WIN32
  866. // ector: _chsize sucks, not 64-bit safe
  867. // F|RES: changed to _chsize_s. i think it is 64-bit safe
  868. _chsize_s(_fileno(m_file), size)
  869. #else
  870. // TODO: handle 64bit and growing
  871. ftruncate(fileno(m_file), size)
  872. #endif
  873. )
  874. m_good = false;
  875. return m_good;
  876. }
  877. } // namespace