Filesystem.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. /**
  2. * Copyright (c) 2006-2019 LOVE Development Team
  3. *
  4. * This software is provided 'as-is', without any express or implied
  5. * warranty. In no event will the authors be held liable for any damages
  6. * arising from the use of this software.
  7. *
  8. * Permission is granted to anyone to use this software for any purpose,
  9. * including commercial applications, and to alter it and redistribute it
  10. * freely, subject to the following restrictions:
  11. *
  12. * 1. The origin of this software must not be misrepresented; you must not
  13. * claim that you wrote the original software. If you use this software
  14. * in a product, an acknowledgment in the product documentation would be
  15. * appreciated but is not required.
  16. * 2. Altered source versions must be plainly marked as such, and must not be
  17. * misrepresented as being the original software.
  18. * 3. This notice may not be removed or altered from any source distribution.
  19. **/
  20. #include <iostream>
  21. #include <sstream>
  22. #include <algorithm>
  23. #include "common/utf8.h"
  24. #include "common/b64.h"
  25. #include "Filesystem.h"
  26. #include "File.h"
  27. // PhysFS
  28. #include "libraries/physfs/physfs.h"
  29. // For great CWD. (Current Working Directory)
  30. // Using this instead of boost::filesystem which totally
  31. // cramped our style.
  32. #ifdef LOVE_WINDOWS
  33. # include <windows.h>
  34. # include <direct.h>
  35. # include <initguid.h>
  36. # include <Shlobj.h>
  37. # include <Knownfolders.h>
  38. #else
  39. # include <sys/param.h>
  40. # include <unistd.h>
  41. #endif
  42. #ifdef LOVE_IOS
  43. # include "common/ios.h"
  44. #endif
  45. #ifdef LOVE_MACOS
  46. # include "common/macos.h"
  47. #endif
  48. #include <string>
  49. #ifdef LOVE_ANDROID
  50. #include <SDL.h>
  51. #include "common/android.h"
  52. #endif
  53. namespace
  54. {
  55. size_t getDriveDelim(const std::string &input)
  56. {
  57. for (size_t i = 0; i < input.size(); ++i)
  58. if (input[i] == '/' || input[i] == '\\')
  59. return i;
  60. // Something's horribly wrong
  61. return 0;
  62. }
  63. std::string getDriveRoot(const std::string &input)
  64. {
  65. return input.substr(0, getDriveDelim(input)+1);
  66. }
  67. std::string skipDriveRoot(const std::string &input)
  68. {
  69. return input.substr(getDriveDelim(input)+1);
  70. }
  71. std::string normalize(const std::string &input)
  72. {
  73. std::stringstream out;
  74. bool seenSep = false, isSep = false;
  75. for (size_t i = 0; i < input.size(); ++i)
  76. {
  77. isSep = (input[i] == LOVE_PATH_SEPARATOR[0]);
  78. if (!isSep || !seenSep)
  79. out << input[i];
  80. seenSep = isSep;
  81. }
  82. return out.str();
  83. }
  84. }
  85. namespace love
  86. {
  87. namespace filesystem
  88. {
  89. namespace physfs
  90. {
  91. Filesystem::Filesystem()
  92. : fused(false)
  93. , fusedSet(false)
  94. {
  95. requirePath = {"?.lua", "?/init.lua"};
  96. cRequirePath = {"??"};
  97. }
  98. Filesystem::~Filesystem()
  99. {
  100. if (PHYSFS_isInit())
  101. PHYSFS_deinit();
  102. }
  103. const char *Filesystem::getName() const
  104. {
  105. return "love.filesystem.physfs";
  106. }
  107. void Filesystem::init(const char *arg0)
  108. {
  109. if (!PHYSFS_init(arg0))
  110. throw love::Exception("Failed to initialize filesystem: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
  111. // Enable symlinks by default.
  112. setSymlinksEnabled(true);
  113. }
  114. void Filesystem::setFused(bool fused)
  115. {
  116. if (fusedSet)
  117. return;
  118. this->fused = fused;
  119. fusedSet = true;
  120. }
  121. bool Filesystem::isFused() const
  122. {
  123. if (!fusedSet)
  124. return false;
  125. return fused;
  126. }
  127. bool Filesystem::setIdentity(const char *ident, bool appendToPath)
  128. {
  129. if (!PHYSFS_isInit())
  130. return false;
  131. std::string old_save_path = save_path_full;
  132. // Store the save directory.
  133. save_identity = std::string(ident);
  134. // Generate the relative path to the game save folder.
  135. save_path_relative = std::string(LOVE_APPDATA_PREFIX LOVE_APPDATA_FOLDER LOVE_PATH_SEPARATOR) + save_identity;
  136. // Generate the full path to the game save folder.
  137. save_path_full = std::string(getAppdataDirectory()) + std::string(LOVE_PATH_SEPARATOR);
  138. if (fused)
  139. save_path_full += std::string(LOVE_APPDATA_PREFIX) + save_identity;
  140. else
  141. save_path_full += save_path_relative;
  142. save_path_full = normalize(save_path_full);
  143. #ifdef LOVE_ANDROID
  144. if (save_identity == "")
  145. save_identity = "unnamed";
  146. std::string storage_path;
  147. if (isAndroidSaveExternal())
  148. storage_path = SDL_AndroidGetExternalStoragePath();
  149. else
  150. storage_path = SDL_AndroidGetInternalStoragePath();
  151. std::string save_directory = storage_path + "/save";
  152. save_path_full = storage_path + std::string("/save/") + save_identity;
  153. if (!love::android::directoryExists(save_path_full.c_str()) &&
  154. !love::android::mkdir(save_path_full.c_str()))
  155. SDL_Log("Error: Could not create save directory %s!", save_path_full.c_str());
  156. #endif
  157. // We now have something like:
  158. // save_identity: game
  159. // save_path_relative: ./LOVE/game
  160. // save_path_full: C:\Documents and Settings\user\Application Data/LOVE/game
  161. // We don't want old read-only save paths to accumulate when we set a new
  162. // identity.
  163. if (!old_save_path.empty())
  164. PHYSFS_unmount(old_save_path.c_str());
  165. // Try to add the save directory to the search path.
  166. // (No error on fail, it means that the path doesn't exist).
  167. PHYSFS_mount(save_path_full.c_str(), nullptr, appendToPath);
  168. // HACK: This forces setupWriteDirectory to be called the next time a file
  169. // is opened for writing - otherwise it won't be called at all if it was
  170. // already called at least once before.
  171. PHYSFS_setWriteDir(nullptr);
  172. return true;
  173. }
  174. const char *Filesystem::getIdentity() const
  175. {
  176. return save_identity.c_str();
  177. }
  178. bool Filesystem::setSource(const char *source)
  179. {
  180. if (!PHYSFS_isInit())
  181. return false;
  182. // Check whether directory is already set.
  183. if (!game_source.empty())
  184. return false;
  185. std::string new_search_path = source;
  186. #ifdef LOVE_ANDROID
  187. if (!love::android::createStorageDirectories())
  188. SDL_Log("Error creating storage directories!");
  189. new_search_path = love::android::getSelectedGameFile();
  190. // try mounting first, if that fails, load to memory and mount
  191. if (!PHYSFS_mount(new_search_path.c_str(), nullptr, 1))
  192. {
  193. // PHYSFS cannot yet mount a zip file inside an .apk
  194. SDL_Log("Mounting %s did not work. Loading to memory.",
  195. new_search_path.c_str());
  196. char* game_archive_ptr = NULL;
  197. size_t game_archive_size = 0;
  198. if (!love::android::loadGameArchiveToMemory(
  199. new_search_path.c_str(), &game_archive_ptr,
  200. &game_archive_size))
  201. {
  202. SDL_Log("Failure memory loading archive %s", new_search_path.c_str());
  203. return false;
  204. }
  205. if (!PHYSFS_mountMemory(
  206. game_archive_ptr, game_archive_size,
  207. love::android::freeGameArchiveMemory, "archive.zip", "/", 0))
  208. {
  209. SDL_Log("Failure mounting in-memory archive.");
  210. love::android::freeGameArchiveMemory(game_archive_ptr);
  211. return false;
  212. }
  213. }
  214. #else
  215. // Add the directory.
  216. if (!PHYSFS_mount(new_search_path.c_str(), nullptr, 1))
  217. return false;
  218. #endif
  219. // Save the game source.
  220. game_source = new_search_path;
  221. return true;
  222. }
  223. const char *Filesystem::getSource() const
  224. {
  225. return game_source.c_str();
  226. }
  227. bool Filesystem::setupWriteDirectory()
  228. {
  229. if (!PHYSFS_isInit())
  230. return false;
  231. // These must all be set.
  232. if (save_identity.empty() || save_path_full.empty() || save_path_relative.empty())
  233. return false;
  234. // We need to make sure the write directory is created. To do that, we also
  235. // need to make sure all its parent directories are also created.
  236. std::string temp_writedir = getDriveRoot(save_path_full);
  237. std::string temp_createdir = skipDriveRoot(save_path_full);
  238. // On some sandboxed platforms, physfs will break when its write directory
  239. // is the root of the drive and it tries to create a folder (even if the
  240. // folder's path is in a writable location.) If the user's home folder is
  241. // in the save path, we'll try starting from there instead.
  242. if (save_path_full.find(getUserDirectory()) == 0)
  243. {
  244. temp_writedir = getUserDirectory();
  245. temp_createdir = save_path_full.substr(getUserDirectory().length());
  246. // Strip leading '/' characters from the path we want to create.
  247. size_t startpos = temp_createdir.find_first_not_of('/');
  248. if (startpos != std::string::npos)
  249. temp_createdir = temp_createdir.substr(startpos);
  250. }
  251. // Set either '/' or the user's home as a writable directory.
  252. // (We must create the save folder before mounting it).
  253. if (!PHYSFS_setWriteDir(temp_writedir.c_str()))
  254. return false;
  255. // Create the save folder. (We're now "at" either '/' or the user's home).
  256. if (!createDirectory(temp_createdir.c_str()))
  257. {
  258. // Clear the write directory in case of error.
  259. PHYSFS_setWriteDir(nullptr);
  260. return false;
  261. }
  262. // Set the final write directory.
  263. if (!PHYSFS_setWriteDir(save_path_full.c_str()))
  264. return false;
  265. // Add the directory. (Will not be readded if already present).
  266. if (!PHYSFS_mount(save_path_full.c_str(), nullptr, 0))
  267. {
  268. PHYSFS_setWriteDir(nullptr); // Clear the write directory in case of error.
  269. return false;
  270. }
  271. return true;
  272. }
  273. bool Filesystem::mount(const char *archive, const char *mountpoint, bool appendToPath)
  274. {
  275. if (!PHYSFS_isInit() || !archive)
  276. return false;
  277. std::string realPath;
  278. std::string sourceBase = getSourceBaseDirectory();
  279. // Check whether the given archive path is in the list of allowed full paths.
  280. auto it = std::find(allowedMountPaths.begin(), allowedMountPaths.end(), archive);
  281. if (it != allowedMountPaths.end())
  282. realPath = *it;
  283. else if (isFused() && sourceBase.compare(archive) == 0)
  284. {
  285. // Special case: if the game is fused and the archive is the source's
  286. // base directory, mount it even though it's outside of the save dir.
  287. realPath = sourceBase;
  288. }
  289. else
  290. {
  291. // Not allowed for safety reasons.
  292. if (strlen(archive) == 0 || strstr(archive, "..") || strcmp(archive, "/") == 0)
  293. return false;
  294. const char *realDir = PHYSFS_getRealDir(archive);
  295. if (!realDir)
  296. return false;
  297. realPath = realDir;
  298. // Always disallow mounting of files inside the game source, since it
  299. // won't work anyway if the game source is a zipped .love file.
  300. if (realPath.find(game_source) == 0)
  301. return false;
  302. realPath += LOVE_PATH_SEPARATOR;
  303. realPath += archive;
  304. }
  305. if (realPath.length() == 0)
  306. return false;
  307. return PHYSFS_mount(realPath.c_str(), mountpoint, appendToPath) != 0;
  308. }
  309. bool Filesystem::mount(Data *data, const char *archivename, const char *mountpoint, bool appendToPath)
  310. {
  311. if (!PHYSFS_isInit())
  312. return false;
  313. if (PHYSFS_mountMemory(data->getData(), data->getSize(), nullptr, archivename, mountpoint, appendToPath) != 0)
  314. {
  315. mountedData[archivename] = data;
  316. return true;
  317. }
  318. return false;
  319. }
  320. bool Filesystem::unmount(const char *archive)
  321. {
  322. if (!PHYSFS_isInit() || !archive)
  323. return false;
  324. auto datait = mountedData.find(archive);
  325. if (datait != mountedData.end() && PHYSFS_unmount(archive) != 0)
  326. {
  327. mountedData.erase(datait);
  328. return true;
  329. }
  330. std::string realPath;
  331. std::string sourceBase = getSourceBaseDirectory();
  332. // Check whether the given archive path is in the list of allowed full paths.
  333. auto it = std::find(allowedMountPaths.begin(), allowedMountPaths.end(), archive);
  334. if (it != allowedMountPaths.end())
  335. realPath = *it;
  336. else if (isFused() && sourceBase.compare(archive) == 0)
  337. {
  338. // Special case: if the game is fused and the archive is the source's
  339. // base directory, unmount it even though it's outside of the save dir.
  340. realPath = sourceBase;
  341. }
  342. else
  343. {
  344. // Not allowed for safety reasons.
  345. if (strlen(archive) == 0 || strstr(archive, "..") || strcmp(archive, "/") == 0)
  346. return false;
  347. const char *realDir = PHYSFS_getRealDir(archive);
  348. if (!realDir)
  349. return false;
  350. realPath = realDir;
  351. realPath += LOVE_PATH_SEPARATOR;
  352. realPath += archive;
  353. }
  354. const char *mountPoint = PHYSFS_getMountPoint(realPath.c_str());
  355. if (!mountPoint)
  356. return false;
  357. return PHYSFS_unmount(realPath.c_str()) != 0;
  358. }
  359. bool Filesystem::unmount(Data *data)
  360. {
  361. for (const auto &datapair : mountedData)
  362. {
  363. if (datapair.second.get() == data)
  364. {
  365. std::string archive = datapair.first;
  366. return unmount(archive.c_str());
  367. }
  368. }
  369. return false;
  370. }
  371. love::filesystem::File *Filesystem::newFile(const char *filename) const
  372. {
  373. return new File(filename);
  374. }
  375. const char *Filesystem::getWorkingDirectory()
  376. {
  377. if (cwd.empty())
  378. {
  379. #ifdef LOVE_WINDOWS
  380. WCHAR w_cwd[LOVE_MAX_PATH];
  381. _wgetcwd(w_cwd, LOVE_MAX_PATH);
  382. cwd = to_utf8(w_cwd);
  383. replace_char(cwd, '\\', '/');
  384. #else
  385. char *cwd_char = new char[LOVE_MAX_PATH];
  386. if (getcwd(cwd_char, LOVE_MAX_PATH))
  387. cwd = cwd_char; // if getcwd fails, cwd_char (and thus cwd) will still be empty
  388. delete [] cwd_char;
  389. #endif
  390. }
  391. return cwd.c_str();
  392. }
  393. std::string Filesystem::getUserDirectory()
  394. {
  395. #ifdef LOVE_IOS
  396. // PHYSFS_getUserDir doesn't give exactly the path we want on iOS.
  397. static std::string userDir = normalize(love::ios::getHomeDirectory());
  398. #else
  399. static std::string userDir = normalize(PHYSFS_getUserDir());
  400. #endif
  401. return userDir;
  402. }
  403. std::string Filesystem::getAppdataDirectory()
  404. {
  405. if (appdata.empty())
  406. {
  407. #ifdef LOVE_WINDOWS_UWP
  408. appdata = getUserDirectory();
  409. #elif defined(LOVE_WINDOWS)
  410. PWSTR path = nullptr;
  411. if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, nullptr, &path)))
  412. {
  413. appdata = to_utf8(path);
  414. CoTaskMemFree(path);
  415. }
  416. else
  417. {
  418. wchar_t *w_appdata = _wgetenv(L"APPDATA");
  419. appdata = to_utf8(w_appdata);
  420. }
  421. replace_char(appdata, '\\', '/');
  422. #elif defined(LOVE_MACOS)
  423. appdata = normalize(love::macos::getAppdataDirectory());
  424. #elif defined(LOVE_IOS)
  425. appdata = normalize(love::ios::getAppdataDirectory());
  426. #elif defined(LOVE_LINUX)
  427. char *xdgdatahome = getenv("XDG_DATA_HOME");
  428. if (!xdgdatahome)
  429. appdata = normalize(std::string(getUserDirectory()) + "/.local/share/");
  430. else
  431. appdata = xdgdatahome;
  432. #else
  433. appdata = getUserDirectory();
  434. #endif
  435. }
  436. return appdata;
  437. }
  438. const char *Filesystem::getSaveDirectory()
  439. {
  440. return save_path_full.c_str();
  441. }
  442. std::string Filesystem::getSourceBaseDirectory() const
  443. {
  444. size_t source_len = game_source.length();
  445. if (source_len == 0)
  446. return "";
  447. // FIXME: This doesn't take into account parent and current directory
  448. // symbols (i.e. '..' and '.')
  449. #ifdef LOVE_WINDOWS
  450. // In windows, delimiters can be either '/' or '\'.
  451. size_t base_end_pos = game_source.find_last_of("/\\", source_len - 2);
  452. #else
  453. size_t base_end_pos = game_source.find_last_of('/', source_len - 2);
  454. #endif
  455. if (base_end_pos == std::string::npos)
  456. return "";
  457. // If the source is in the unix root (aka '/'), we want to keep the '/'.
  458. if (base_end_pos == 0)
  459. base_end_pos = 1;
  460. return game_source.substr(0, base_end_pos);
  461. }
  462. std::string Filesystem::getRealDirectory(const char *filename) const
  463. {
  464. if (!PHYSFS_isInit())
  465. throw love::Exception("PhysFS is not initialized.");
  466. const char *dir = PHYSFS_getRealDir(filename);
  467. if (dir == nullptr)
  468. throw love::Exception("File does not exist on disk.");
  469. return std::string(dir);
  470. }
  471. bool Filesystem::getInfo(const char *filepath, Info &info) const
  472. {
  473. if (!PHYSFS_isInit())
  474. return false;
  475. PHYSFS_Stat stat = {};
  476. if (!PHYSFS_stat(filepath, &stat))
  477. return false;
  478. info.size = (int64) stat.filesize;
  479. info.modtime = (int64) stat.modtime;
  480. if (stat.filetype == PHYSFS_FILETYPE_REGULAR)
  481. info.type = FILETYPE_FILE;
  482. else if (stat.filetype == PHYSFS_FILETYPE_DIRECTORY)
  483. info.type = FILETYPE_DIRECTORY;
  484. else if (stat.filetype == PHYSFS_FILETYPE_SYMLINK)
  485. info.type = FILETYPE_SYMLINK;
  486. else
  487. info.type = FILETYPE_OTHER;
  488. return true;
  489. }
  490. bool Filesystem::createDirectory(const char *dir)
  491. {
  492. if (!PHYSFS_isInit())
  493. return false;
  494. if (PHYSFS_getWriteDir() == 0 && !setupWriteDirectory())
  495. return false;
  496. if (!PHYSFS_mkdir(dir))
  497. return false;
  498. return true;
  499. }
  500. bool Filesystem::remove(const char *file)
  501. {
  502. if (!PHYSFS_isInit())
  503. return false;
  504. if (PHYSFS_getWriteDir() == 0 && !setupWriteDirectory())
  505. return false;
  506. if (!PHYSFS_delete(file))
  507. return false;
  508. return true;
  509. }
  510. FileData *Filesystem::read(const char *filename, int64 size) const
  511. {
  512. File file(filename);
  513. file.open(File::MODE_READ);
  514. // close() is called in the File destructor.
  515. return file.read(size);
  516. }
  517. void Filesystem::write(const char *filename, const void *data, int64 size) const
  518. {
  519. File file(filename);
  520. file.open(File::MODE_WRITE);
  521. // close() is called in the File destructor.
  522. if (!file.write(data, size))
  523. throw love::Exception("Data could not be written.");
  524. }
  525. void Filesystem::append(const char *filename, const void *data, int64 size) const
  526. {
  527. File file(filename);
  528. file.open(File::MODE_APPEND);
  529. // close() is called in the File destructor.
  530. if (!file.write(data, size))
  531. throw love::Exception("Data could not be written.");
  532. }
  533. bool Filesystem::getDirectoryItems(const char *dir, std::vector<std::string> &items)
  534. {
  535. if (!PHYSFS_isInit())
  536. return false;
  537. char **rc = PHYSFS_enumerateFiles(dir);
  538. if (rc == nullptr)
  539. return false;
  540. for (char **i = rc; *i != 0; i++)
  541. items.push_back(*i);
  542. PHYSFS_freeList(rc);
  543. return true;
  544. }
  545. void Filesystem::setSymlinksEnabled(bool enable)
  546. {
  547. if (!PHYSFS_isInit())
  548. return;
  549. PHYSFS_permitSymbolicLinks(enable ? 1 : 0);
  550. }
  551. bool Filesystem::areSymlinksEnabled() const
  552. {
  553. if (!PHYSFS_isInit())
  554. return false;
  555. return PHYSFS_symbolicLinksPermitted() != 0;
  556. }
  557. std::vector<std::string> &Filesystem::getRequirePath()
  558. {
  559. return requirePath;
  560. }
  561. std::vector<std::string> &Filesystem::getCRequirePath()
  562. {
  563. return cRequirePath;
  564. }
  565. void Filesystem::allowMountingForPath(const std::string &path)
  566. {
  567. if (std::find(allowedMountPaths.begin(), allowedMountPaths.end(), path) == allowedMountPaths.end())
  568. allowedMountPaths.push_back(path);
  569. }
  570. } // physfs
  571. } // filesystem
  572. } // love