载入中...
搜索中...
未找到
Filesystem.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <iostream>
5#include <memory>
6#include <mutex>
7#include <sstream>
8#include <string>
9
11#include "common/b64.h"
12#include "common/utf8.h"
13#include "common/Exception.h"
14#include "cmdline/cmdline.h"
15
16// PhysFS
17#include "physfs/physfs.h"
18
19#ifdef EVENGINE_WINDOWS
20#include <direct.h>
21#include <windows.h>
22#else
23#include <sys/param.h>
24#include <unistd.h>
25#endif
26
27#ifdef EVENGINE_IOS
28#include "ios/ios.h"
29#endif
30
31#ifdef EVENGINE_WEBGPU
32#include "webgpu/webplatform.h"
33#endif
34
35#include <string>
36
37#ifdef EVENGINE_ANDROID
38#include <SDL2/SDL.h>
39
40#include "android/android.h"
41#endif
42
43using namespace std;
44
45namespace {
46size_t getDriveDelim(const std::string &input) {
47 for (size_t i = 0; i < input.size(); ++i)
48 if (input[i] == '/' || input[i] == '\\') return i;
49 // Something's horribly wrong
50 return 0;
51}
52
53std::string getDriveRoot(const std::string &input) { return input.substr(0, getDriveDelim(input) + 1); }
54
55std::string skipDriveRoot(const std::string &input) { return input.substr(getDriveDelim(input) + 1); }
56
57std::string normalize(const std::string &input) {
58 std::stringstream out;
59
60 bool seenSep = false, isSep = false;
61 for (size_t i = 0; i < input.size(); ++i) {
62 isSep = (input[i] == EVENGINE_PATH_SEPARATOR[0]);
63 if (!isSep || !seenSep) out << input[i];
64 seenSep = isSep;
65 }
66
67 return out.str();
68}
69
70} // namespace
71
72namespace eve {
73namespace filesystem {
74namespace physfs {
75
76Filesystem::Filesystem() : fused(false), fusedSet(false) {
77 requirePath = {"?.lua", "?/init.lua"};
78 cRequirePath = {"??"};
79 if (auto* c = getModInst(eve::cmd,Cmdline)) {
80 // unit_test / embedders never call Cmdline::runArgs: argv stays empty.
81 const std::string a0 = c->getArgv(0);
82 if (a0.empty()) init(NULL);
83 else init(a0.c_str());
84 } else {
85 init(NULL);
86 }
87}
88
90 unwatchAll();
91
92 if (memoryArchive_) {
93 free(memoryArchive_);
94 memoryArchive_ = nullptr;
95 }
96
97#ifdef EVENGINE_ANDROID
98 android::deinitializeVirtualArchive();
99#endif
100
101 if (PHYSFS_isInit()) PHYSFS_deinit();
102}
103
104void Filesystem::init(const char* arg0) {
105 if (!PHYSFS_init(arg0))
106 throw Exception("Failed to initialize filesystem: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
107
108 // Enable symlinks by default.
109 setSymlinksEnabled(true);
110}
111
112void Filesystem::setFused(bool fused) {
113 if (fusedSet) return;
114 this->fused = fused;
115 fusedSet = true;
116}
117
119 if (!fusedSet) return false;
120 return fused;
121}
122
123bool Filesystem::setIdentity(std::string ident, bool appendToPath) {
124 if (!PHYSFS_isInit()) return false;
125
126 std::string old_save_path = save_path_full;
127
128 // Store the save directory.
129 save_identity = std::string(ident);
130
131 // Generate the relative path to the game save folder.
132 save_path_relative = std::string(EVENGINE_APPDATA_PREFIX EVENGINE_APPDATA_FOLDER EVENGINE_PATH_SEPARATOR) + save_identity;
133
134 // Generate the full path to the game save folder.
135 save_path_full = std::string(getAppdataDirectory()) + std::string(EVENGINE_PATH_SEPARATOR);
136 if (fused)
137 save_path_full += std::string(EVENGINE_APPDATA_PREFIX) + save_identity;
138 else
139 save_path_full += save_path_relative;
140
141 save_path_full = normalize(save_path_full);
142
143#ifdef EVENGINE_ANDROID
144 if (save_identity == "") save_identity = "unnamed";
145
146 std::string storage_path;
148 storage_path = SDL_AndroidGetExternalStoragePath();
149 else
150 storage_path = SDL_AndroidGetInternalStoragePath();
151
152 std::string save_directory = storage_path + "/save";
153
154 save_path_full = storage_path + std::string("/save/") + save_identity;
155
156 if (!android::directoryExists(save_path_full.c_str()) && !android::mkdir(save_path_full.c_str()))
157 SDL_Log("Error: Could not create save directory %s!", save_path_full.c_str());
158#endif
159
160 // We now have something like:
161 // save_identity: game
162 // save_path_relative: ./LOVE/game
163 // save_path_full: C:\Documents and Settings\user\Application Data/LOVE/game
164
165 // We don't want old read-only save paths to accumulate when we set a new
166 // identity.
167 if (!old_save_path.empty()) PHYSFS_unmount(old_save_path.c_str());
168
169 // Try to add the save directory to the search path.
170 // (No error on fail, it means that the path doesn't exist).
171 PHYSFS_mount(save_path_full.c_str(), nullptr, appendToPath);
172
173 // HACK: This forces setupWriteDirectory to be called the next time a file
174 // is opened for writing - otherwise it won't be called at all if it was
175 // already called at least once before.
176 PHYSFS_setWriteDir(nullptr);
177
178 return true;
179}
180
181std::string Filesystem::getIdentity() const { return save_identity.c_str(); }
182
183bool Filesystem::setSource(std::string source) {
184 if (!PHYSFS_isInit()) return false;
185
186 // Check whether directory is already set.
187 if (!game_source.empty()) return false;
188
189 std::string new_search_path = source;
190
191#ifdef EVENGINE_ANDROID
192 if (!android::createStorageDirectories()) SDL_Log("Error creating storage directories!");
193
194 new_search_path = "";
195
196 PHYSFS_Io *gameLoveIO;
197 bool hasFusedGame = android::checkFusedGame((void **)&gameLoveIO);
198
199 if (hasFusedGame) {
200 if (gameLoveIO) {
201 // Actually we should just be able to mount gameLoveIO, but that's experimental.
202 gameLoveIO->destroy(gameLoveIO);
203 goto oldschool;
204 } else {
205 if (!android::initializeVirtualArchive()) {
206 SDL_Log("Unable to mount AAsset: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
207 return false;
208 }
209 }
210 } else {
211 oldschool:
212 new_search_path = android::getSelectedGameFile();
213
214 // try mounting first, if that fails, load to memory and mount
215 if (!PHYSFS_mount(new_search_path.c_str(), nullptr, 1)) {
216 // PHYSFS cannot yet mount a zip file inside an .apk
217 SDL_Log("Mounting %s did not work. Loading to memory.", new_search_path.c_str());
218 char * game_archive_ptr = NULL;
219 size_t game_archive_size = 0;
220 if (!android::loadGameArchiveToMemory(new_search_path.c_str(), &game_archive_ptr,
221 &game_archive_size)) {
222 SDL_Log("Failure memory loading archive %s", new_search_path.c_str());
223 return false;
224 }
225 if (!PHYSFS_mountMemory(game_archive_ptr, game_archive_size, android::freeGameArchiveMemory,
226 "archive.zip", "/", 0)) {
227 SDL_Log("Failure mounting in-memory archive.");
228 android::freeGameArchiveMemory(game_archive_ptr);
229 return false;
230 }
231 }
232 }
233#else
234 // Add the directory.
235 if (!PHYSFS_mount(new_search_path.c_str(), nullptr, 1)) return false;
236#endif
237
238 // Save the game source.
239 game_source = new_search_path;
240
241 return true;
242}
243
244std::string Filesystem::getSource() const { return game_source.c_str(); }
245
246bool Filesystem::setSourceFromMemory(const void* data, size_t size) {
247 if (!PHYSFS_isInit() || !data || size == 0) return false;
248 // Only one source per process.
249 if (!game_source.empty()) return false;
250
251 // PhysFS keeps a pointer to the buffer for the lifetime of the mount, so we
252 // must own a copy that outlives it. Freed in the destructor.
253 void* owned = malloc(size);
254 if (!owned) return false;
255 memcpy(owned, data, size);
256
257 // Mount at "/" so dofile("config.nut") / "main.nut" resolve via PhysFS.
258 if (!PHYSFS_mountMemory(owned, size, nullptr, "game.eve", "/", 1)) {
259 const auto err = PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode());
260 std::cerr << "PhysFS mountMemory failed: " << (err ? err : "unknown error") << std::endl;
261 free(owned);
262 return false;
263 }
264
265 memoryArchive_ = owned;
266 game_source = "game.eve";
267 return true;
268}
269
271 if (!PHYSFS_isInit()) return false;
272
273 // These must have one be set.
274 if (save_identity.empty() || save_path_full.empty() || save_path_relative.empty()) return false;
275
276 // We need to make sure the write directory is created. To do that, we also
277 // need to make sure all its parent directories are also created.
278 std::string temp_writedir = getDriveRoot(save_path_full);
279 std::string temp_createdir = skipDriveRoot(save_path_full);
280
281 // On some sandboxed platforms, physfs will break when its write directory
282 // is the root of the drive and it tries to create a folder (even if the
283 // folder's path is in a writable location.) If the user's home folder is
284 // in the save path, we'll try starting from there instead.
285 if (save_path_full.find(getUserDirectory()) == 0) {
286 temp_writedir = getUserDirectory();
287 temp_createdir = save_path_full.substr(getUserDirectory().length());
288
289 // Strip leading '/' characters from the path we want to create.
290 size_t startpos = temp_createdir.find_first_not_of('/');
291 if (startpos != std::string::npos) temp_createdir = temp_createdir.substr(startpos);
292 }
293
294 // Set either '/' or the user's home as a writable directory.
295 // (We must create the save folder before mounting it).
296 if (!PHYSFS_setWriteDir(temp_writedir.c_str())) return false;
297
298 // Create the save folder. (We're now "at" either '/' or the user's home).
299 if (!createDirectory(temp_createdir.c_str())) {
300 // Clear the write directory in case of error.
301 PHYSFS_setWriteDir(nullptr);
302 return false;
303 }
304
305 // Set the final write directory.
306 if (!PHYSFS_setWriteDir(save_path_full.c_str())) return false;
307
308 // Add the directory. (Will not be readded if already present).
309 if (!PHYSFS_mount(save_path_full.c_str(), nullptr, 0)) {
310 PHYSFS_setWriteDir(nullptr); // Clear the write directory in case of error.
311 return false;
312 }
313
314 return true;
315}
316
317bool Filesystem::mount(std::string archive, std::string mountpoint, bool appendToPath) {
318 if (!PHYSFS_isInit() || archive.empty()) return false;
319
320 std::string realPath;
321 std::string sourceBase = getSourceBaseDirectory();
322
323 // Check whether the given archive path is in the list of allowed full paths.
324 auto it = std::find(allowedMountPaths.begin(), allowedMountPaths.end(), archive);
325
326 if (it != allowedMountPaths.end())
327 realPath = *it;
328 else if (isFused() && sourceBase.compare(archive) == 0) {
329 // Special case: if the game is fused and the archive is the source's
330 // base directory, mount it even though it's outside of the save dir.
331 realPath = sourceBase;
332 } else {
333 // Not allowed for safety reasons.
334 if (archive.size() == 0 || archive.find("..") != string::npos || archive == "/") return false;
335
336 const char* realDir = PHYSFS_getRealDir(archive.c_str());
337 if (!realDir) return false;
338
339 realPath = realDir;
340
341 // Always disallow mounting of files inside the game source, since it
342 // won't work anyway if the game source is a zipped .love file.
343 if (realPath.find(game_source) == 0) return false;
344
345 realPath += EVENGINE_PATH_SEPARATOR;
346 realPath += archive;
347 }
348
349 if (realPath.length() == 0) return false;
350
351 return PHYSFS_mount(realPath.c_str(), mountpoint.c_str(), appendToPath) != 0;
352}
353
354bool Filesystem::mount(Data *data, std::string archivename, std::string mountpoint, bool appendToPath) {
355 if (!PHYSFS_isInit()) return false;
356
357 if (PHYSFS_mountMemory(data->getData(), data->getSize(), nullptr, archivename.c_str(), mountpoint.c_str(), appendToPath) != 0) {
358 mountedData[archivename] = data;
359 return true;
360 }
361
362 return false;
363}
364
365bool Filesystem::mountRealDirectory(std::string realDir, std::string mountpoint, bool appendToPath) {
366 if (!PHYSFS_isInit() || realDir.empty()) return false;
367 if (!isRealDirectory(realDir)) return false;
368 if (mountpoint.empty()) mountpoint = "/";
369
370 // Track the dir so unmountRealDirectory() / the destructor can clean it up,
371 // even though PhysFS itself needs the OS path string to unmount.
372 std::lock_guard<std::mutex> lock(mountMu_);
373 if (std::find(mountedRealDirs_.begin(), mountedRealDirs_.end(), realDir) == mountedRealDirs_.end()) {
374 if (!PHYSFS_mount(realDir.c_str(), mountpoint.c_str(), appendToPath ? 1 : 0)) return false;
375 mountedRealDirs_.push_back(realDir);
376 }
377 return true;
378}
379
380bool Filesystem::unmountRealDirectory(std::string realDir) {
381 std::lock_guard<std::mutex> lock(mountMu_);
382 auto it = std::find(mountedRealDirs_.begin(), mountedRealDirs_.end(), realDir);
383 if (it == mountedRealDirs_.end()) return false;
384 const bool ok = PHYSFS_unmount(realDir.c_str()) != 0;
385 mountedRealDirs_.erase(it);
386 return ok;
387}
388
389bool Filesystem::unmount(std::string archive) {
390 if (!PHYSFS_isInit() || archive.empty()) return false;
391
392 auto datait = mountedData.find(archive);
393
394 if (datait != mountedData.end() && PHYSFS_unmount(archive.c_str()) != 0) {
395 mountedData.erase(datait);
396 return true;
397 }
398
399 std::string realPath;
400 std::string sourceBase = getSourceBaseDirectory();
401
402 // Check whether the given archive path is in the list of allowed full paths.
403 auto it = std::find(allowedMountPaths.begin(), allowedMountPaths.end(), archive);
404
405 if (it != allowedMountPaths.end())
406 realPath = *it;
407 else if (isFused() && sourceBase.compare(archive) == 0) {
408 // Special case: if the game is fused and the archive is the source's
409 // base directory, unmount it even though it's outside of the save dir.
410 realPath = sourceBase;
411 } else {
412 // Not allowed for safety reasons.
413 if (archive.size() == 0 || archive.find("..") != string::npos || archive == "/") return false;
414
415 const char* realDir = PHYSFS_getRealDir(archive.c_str());
416 if (!realDir) return false;
417
418 realPath = realDir;
419 realPath += EVENGINE_PATH_SEPARATOR;
420 realPath += archive;
421 }
422
423 const char* mountPoint = PHYSFS_getMountPoint(realPath.c_str());
424 if (!mountPoint) return false;
425
426 return PHYSFS_unmount(realPath.c_str()) != 0;
427}
428
430 for (const auto &datapair : mountedData) {
431 if (datapair.second == data) {
432 std::string archive = datapair.first;
433 return unmount(archive.c_str());
434 }
435 }
436
437 return false;
438}
439
440filesystem::File *Filesystem::newFile(std::string filename) const { return new File(filename); }
441
443 if (cwd.empty()) {
444#ifdef EVENGINE_WINDOWS
445
446 WCHAR w_cwd[EVENGINE_MAX_PATH];
447 _wgetcwd(w_cwd, EVENGINE_MAX_PATH);
448 cwd = to_utf8(w_cwd);
449 replace_char(cwd, '\\', '/');
450#else
451 char *cwd_char = new char[EVENGINE_MAX_PATH];
452
453 if (getcwd(cwd_char, EVENGINE_MAX_PATH))
454 cwd = cwd_char; // if getcwd fails, cwd_char (and thus cwd) will still be empty
455
456 delete[] cwd_char;
457#endif
458 }
459
460 return cwd.c_str();
461}
462
464#ifdef EVENGINE_IOS
465 // PHYSFS_getUserDir doesn't give exactly the path we want on iOS.
466 static std::string userDir = normalize(ios::getHomeDirectory());
467#elif defined(EVENGINE_WEBGPU)
468 static std::string userDir = normalize(eve::webgpu_platform::getHomeDirectory());
469#else
470 const char* dir = PHYSFS_getUserDir();
471 static std::string userDir;
472 if (dir) userDir = normalize(dir);
473#endif
474 return userDir;
475}
476
478 if (appdata.empty()) {
479#ifdef EVENGINE_WINDOWS_UWP
480 appdata = getUserDirectory();
481#elif defined(EVENGINE_WEBGPU)
482 appdata = normalize(eve::webgpu_platform::getAppdataDirectory());
483#elif defined(EVENGINE_WINDOWS)
484 wchar_t *w_appdata = _wgetenv(L"APPDATA");
485 appdata = to_utf8(w_appdata);
486 replace_char(appdata, '\\', '/');
487#elif defined(EVENGINE_MACOSX)
488 std::string udir = getUserDirectory();
489 udir.append("/Library/Application Support");
490 appdata = normalize(udir);
491#elif defined(EVENGINE_IOS)
492 appdata = normalize(ios::getAppdataDirectory());
493#elif defined(EVENGINE_LINUX)
494 char *xdgdatahome = getenv("XDG_DATA_HOME");
495 if (!xdgdatahome)
496 appdata = normalize(std::string(getUserDirectory()) + "/.local/share/");
497 else
498 appdata = xdgdatahome;
499#else
500 appdata = getUserDirectory();
501#endif
502 }
503 return appdata;
504}
505
506std::string Filesystem::getSaveDirectory() { return save_path_full.c_str(); }
507
509 size_t source_len = game_source.length();
510
511 if (source_len == 0) return "";
512
513 // FIXME: This doesn't take into account parent and current directory
514 // symbols (i.e. '..' and '.')
515#ifdef EVENGINE_WINDOWS
516 // In windows, delimiters can be either '/' or '\'.
517 size_t base_end_pos = game_source.find_last_of("/\\", source_len - 2);
518#else
519 size_t base_end_pos = game_source.find_last_of('/', source_len - 2);
520#endif
521
522 if (base_end_pos == std::string::npos) return "";
523
524 // If the source is in the unix root (aka '/'), we want to keep the '/'.
525 if (base_end_pos == 0) base_end_pos = 1;
526
527 return game_source.substr(0, base_end_pos);
528}
529
530std::string Filesystem::getRealDirectory(std::string filename) const {
531 if (!PHYSFS_isInit()) throw Exception("PhysFS is not initialized.");
532
533 const char* dir = PHYSFS_getRealDir(filename.c_str());
534
535 if (dir == nullptr) throw Exception("File does not exist on disk.");
536
537 return std::string(dir);
538}
539
540bool Filesystem::getInfo(std::string filepath, Info &info) const {
541 if (!PHYSFS_isInit()) return false;
542
543 PHYSFS_Stat stat = {};
544 if (!PHYSFS_stat(filepath.c_str(), &stat)) return false;
545
546 info.size = (int64_t)stat.filesize;
547 info.modtime = (int64_t)stat.modtime;
548
549 if (stat.filetype == PHYSFS_FILETYPE_REGULAR)
550 info.type = "file";
551 else if (stat.filetype == PHYSFS_FILETYPE_DIRECTORY)
552 info.type = "directory";
553 else if (stat.filetype == PHYSFS_FILETYPE_SYMLINK)
554 info.type = "symlink";
555 else
556 info.type = "other";
557
558 return true;
559}
560
562 if (!PHYSFS_isInit()) return false;
563
564 if (PHYSFS_getWriteDir() == 0 && !setupWriteDirectory()) return false;
565
566 if (!PHYSFS_mkdir(dir.c_str())) return false;
567
568 return true;
569}
570
571bool Filesystem::remove(std::string file) {
572 if (!PHYSFS_isInit()) return false;
573
574 if (PHYSFS_getWriteDir() == 0 && !setupWriteDirectory()) return false;
575
576 if (!PHYSFS_delete(file.c_str())) return false;
577
578 return true;
579}
580
581FileData *Filesystem::read(std::string filename, int64_t size) const {
582 physfs::File file(filename);
583
584 file.open("rb");
585
586 // close() is called in the File destructor.
587 return file.read(size);
588}
589
590void Filesystem::write(std::string filename, const void *data, int64_t size) const {
591 physfs::File file(filename);
592
593 file.open("wb");
594
595 // close() is called in the File destructor.
596 if (!file.write(data, size)) throw eve::Exception("Data could not be written.");
597}
598
599void Filesystem::append(std::string filename, const void *data, int64_t size) const {
600 physfs::File file(filename);
601
602 file.open("ab");
603
604 // close() is called in the File destructor.
605 if (!file.write(data, size)) throw eve::Exception("Data could not be written.");
606}
607
608std::vector<std::string> Filesystem::getDirectoryItems(std::string dir) {
609 std::vector<std::string> items;
610 if (!PHYSFS_isInit()) return items;
611
612 char **rc = PHYSFS_enumerateFiles(dir.c_str());
613
614 if (rc == nullptr) return items;
615
616 for (char **i = rc; *i != 0; i++) items.push_back(*i);
617
618 PHYSFS_freeList(rc);
619 return items;
620}
621
623 if (!PHYSFS_isInit()) return;
624
625 PHYSFS_permitSymbolicLinks(enable ? 1 : 0);
626}
627
629 if (!PHYSFS_isInit()) return false;
630
631 return PHYSFS_symbolicLinksPermitted() != 0;
632}
633
634std::vector<std::string> &Filesystem::getRequirePath() { return requirePath; }
635
636std::vector<std::string> &Filesystem::getCRequirePath() { return cRequirePath; }
637
638void Filesystem::allowMountingForPath(const std::string &path) {
639 if (std::find(allowedMountPaths.begin(), allowedMountPaths.end(), path) == allowedMountPaths.end())
640 allowedMountPaths.push_back(path);
641}
642
643namespace {
644
645bool looksAbsolute(const std::string &path) {
646 if (path.empty()) return false;
647 if (path[0] == '/' || path[0] == '\\') return true;
648#ifdef EVENGINE_WINDOWS
649 if (path.size() >= 2 && path[1] == ':') return true;
650#endif
651 return false;
652}
653
654std::string joinPath(const std::string &a, const std::string &b) {
655 if (a.empty()) return b;
656 if (b.empty()) return a;
657 char last = a.back();
658 if (last == '/' || last == '\\') return a + b;
659 return a + "/" + b;
660}
661
662std::string parentDir(const std::string &path) {
663 auto pos = path.find_last_of("/\\");
664 if (pos == std::string::npos) return ".";
665 if (pos == 0) return "/";
666 return path.substr(0, pos);
667}
668
669std::string baseName(const std::string &path) {
670 auto pos = path.find_last_of("/\\");
671 if (pos == std::string::npos) return path;
672 return path.substr(pos + 1);
673}
674
675} // namespace
676
677FileWatch &Filesystem::watchers() {
678 if (!fileWatch_) fileWatch_ = std::make_unique<FileWatch>();
679 return *fileWatch_;
680}
681
682bool Filesystem::resolveWatchTarget(const std::string &path, std::string &realDir,
683 std::string &filterName, std::string &reportPath) {
684 reportPath = path;
685 filterName.clear();
686 realDir.clear();
687 if (path.empty()) return false;
688
689 // "." / "" are not reliable PhysFS paths — watch the real working directory.
690 if (path == "." || path == "./") {
691 reportPath = ".";
692 realDir = getWorkingDirectory();
693 filterName.clear();
694 return isRealDirectory(realDir);
695 }
696
697 if (looksAbsolute(path)) {
698 if (isRealDirectory(path)) {
699 realDir = path;
700 return true;
701 }
702 realDir = parentDir(path);
703 filterName = baseName(path);
704 return isRealDirectory(realDir);
705 }
706
707 Info info{};
708 if (getInfo(path, info)) {
709 std::string root;
710 try {
711 root = getRealDirectory(path);
712 } catch (...) {
713 return false;
714 }
715 std::string full = joinPath(root, path);
716 if (info.type == "directory") {
717 realDir = full;
718 filterName.clear();
719 return isRealDirectory(realDir);
720 }
721 realDir = parentDir(full);
722 filterName = baseName(full);
723 return isRealDirectory(realDir);
724 }
725
726 std::string parent = parentDir(path);
727 if (parent == ".") parent.clear();
728 if (!parent.empty()) {
729 Info pinfo{};
730 if (getInfo(parent, pinfo) && pinfo.type == "directory") {
731 std::string root;
732 try {
733 root = getRealDirectory(parent);
734 } catch (...) {
735 return false;
736 }
737 realDir = joinPath(root, parent);
738 filterName = baseName(path);
739 return isRealDirectory(realDir);
740 }
741 }
742
743 // Virtual path not in VFS yet (e.g. new file): watch under cwd, not save dir.
744 {
745 std::string cwd = getWorkingDirectory();
746 if (!cwd.empty() && isRealDirectory(cwd)) {
747 if (parent.empty()) {
748 realDir = cwd;
749 filterName = baseName(path);
750 return true;
751 }
752 }
753 }
754
755 std::string save = getSaveDirectory();
756 if (!save.empty() && isRealDirectory(save)) {
757 realDir = save;
758 filterName = baseName(path);
759 return true;
760 }
761 return false;
762}
763
764bool Filesystem::watch(std::string path) {
765 std::string realDir, filter, report;
766 if (!resolveWatchTarget(path, realDir, filter, report)) return false;
767 return watchers().add(realDir, filter, report, 1);
768}
769
770bool Filesystem::unwatch(std::string path) { return watchers().remove(path); }
771
773 if (fileWatch_) fileWatch_->clear();
774}
775
777 if (!fileWatch_) return 0;
778 return fileWatch_->count();
779}
780
782 if (!fileWatch_) return "";
784 if (!fileWatch_->poll(ev)) {
785 lastWatchPath_.clear();
786 lastWatchRealPath_.clear();
787 return "";
788 }
789 lastWatchPath_ = ev.path;
790 lastWatchRealPath_ = ev.realPath;
791 return ev.kind;
792}
793
794std::string Filesystem::getLastWatchPath() const { return lastWatchPath_; }
795std::string Filesystem::getLastWatchRealPath() const { return lastWatchRealPath_; }
796
797} // namespace physfs
798} // namespace filesystem
799} // namespace eve
filesystem::File * file
#define EVENGINE_MAX_PATH
Definition Filesystem.h:26
#define EVENGINE_PATH_SEPARATOR
Definition Filesystem.h:25
#define EVENGINE_APPDATA_PREFIX
Definition Filesystem.h:11
#define EVENGINE_APPDATA_FOLDER
Definition Filesystem.h:23
uint32_t a
uint32_t b
uint32_t c
#define getModInst(N, T)
Definition Module.h:36
Light2D::Data * data
std::string filter
int parent
Definition TreeMesh.cpp:175
V3 dir
Definition TreeMesh.cpp:121
Data buffer paired with a filename (used for type identification).
Definition FileData.h:12
bool add(const std::string &realDir, const std::string &filterName, const std::string &reportPath, int scanInterval=1)
Definition FileWatch.cpp:64
bool remove(const std::string &reportPath)
Remove by reportPath previously passed to add().
A File interface, providing generic means of reading from and writing to files.
Definition File.h:14
virtual bool isAndroidSaveExternal() const
Gets whether the Android save is external. Returns a bool.
Definition Filesystem.h:69
virtual bool isRealDirectory(const std::string &path) const
Gets whether the given full (OS-dependent) path is a directory.
std::string getSaveDirectory() override
Gets the full path of the save folder.
void append(std::string filename, const void *data, int64_t size) const override
Append data to a file, creating it if it doesn't exist.
bool setSource(std::string source) override
Sets the path to the game source. This can only be set once.
bool setIdentity(std::string ident, bool appendToPath=false) override
Sets the name of the save folder.
std::string getRealDirectory(std::string filename) const override
Gets the real directory path containing the file.
bool setSourceFromMemory(const void *data, size_t size) override
Loads the game source from a packaged archive (.eve / zip) held entirely in memory and mounts it at "...
void setFused(bool fused) override
bool watch(std::string path) override
Watch a virtual or absolute OS path (file or directory). File watches monitor the parent directory an...
std::string getSource() const override
Gets the path to the game source. Returns a 0-length string if the source has not been set.
filesystem::File * newFile(std::string filename) const override
Creates a new file.
std::string getUserDirectory() override
Gets the user home directory.
bool createDirectory(std::string dir) override
Creates a directory. Write dir must be set.
bool remove(std::string file) override
Removes a file (or directory).
std::vector< std::string > getDirectoryItems(std::string dir) override
This "native" method returns a table of all files in a given directory.
bool unmount(std::string archive) override
std::string pollWatch() override
Pop next watch event kind: "added"|"removed"|"modified"|"movedFrom"|"movedTo". Empty string if queue ...
std::string getLastWatchRealPath() const override
bool mountRealDirectory(std::string realDir, std::string mountpoint, bool appendToPath=false) override
bool unwatch(std::string path) override
Stop watching a path previously passed to watch().
bool mount(std::string archive, std::string mountpoint, bool appendToPath=false) override
std::string getIdentity() const override
void init(const char *arg0) override
bool setupWriteDirectory() override
This sets up the save directory. If the it is already set up, nothing happens.
std::vector< std::string > & getCRequirePath() override
std::string getWorkingDirectory() override
Gets the current working directory.
FileData * read(std::string filename, int64_t size=File::ALL) const override
Reads data from a file.
bool unmountRealDirectory(std::string realDir) override
void allowMountingForPath(const std::string &path) override
Allows a full (OS-dependent) path to be used with Filesystem::mount.
void write(std::string filename, const void *data, int64_t size) const override
Write data to a file.
void setSymlinksEnabled(bool enable) override
Enable or disable symbolic link support in love.filesystem.
bool getInfo(std::string filepath, Info &info) const override
Gets information about the item at the specified filepath. Returns false if nothing exists at the pat...
bool areSymlinksEnabled() const override
Gets whether symbolic link support is enabled.
std::vector< std::string > & getRequirePath() override
std::string getSourceBaseDirectory() const override
Gets the full path to the directory containing the game source. For example if the game source is C:\...
std::string getAppdataDirectory() override
Gets the APPDATA directory. On Windows, this is the folder in the APPDATA% enviroment variable....
std::string getLastWatchPath() const override
Definition Build.cpp:11