file_util.cpp 27 KB

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