file_util.cpp 27 KB

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