FileUtil.cpp 24 KB

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