diff --git a/src/modules/app/App.cpp b/src/modules/app/App.cpp index c2b6abedda..2d6d9c57b3 100644 --- a/src/modules/app/App.cpp +++ b/src/modules/app/App.cpp @@ -334,7 +334,7 @@ void App::onFrame() { if (!request.execute(stream, &statusCode)) { Log::error("Failed to upload crash log with status: %i", statusCode); } else { - _filesystem->sysRemoveFile(crashlogFilename); + _filesystem->sysRemoveFile(core::Path(crashlogFilename)); } } } @@ -447,9 +447,9 @@ AppState App::onConstruct() { } core_assert_init(_filesystem->homePath().c_str()); - for (const core::String &path : _filesystem->registeredPaths()) { + for (const core::Path &path : _filesystem->registeredPaths()) { _dictManager.addDirectory(path); - _dictManager.addDirectory(core::string::path(path, "po")); + _dictManager.addDirectory(path.append("po")); } FL_Locale *locale = nullptr; @@ -595,7 +595,7 @@ AppState App::onConstruct() { #endif } - const core::String &logfilePath = _filesystem->homeWritePath("log.txt"); + const core::Path &logfilePath = _filesystem->homeWritePath("log.txt"); Log::init(logfilePath.c_str()); return AppState::Init; @@ -947,7 +947,7 @@ void App::usage() const { Log::info("------------"); Log::info("Search paths:"); const io::Paths &paths = _filesystem->registeredPaths(); - for (const core::String &path : paths) { + for (const core::Path &path : paths) { Log::info(" * %s", path.c_str()); } Log::info("------------"); diff --git a/src/modules/app/i18n/DictionaryManager.cpp b/src/modules/app/i18n/DictionaryManager.cpp index 4d7e726e67..f0c7861065 100644 --- a/src/modules/app/i18n/DictionaryManager.cpp +++ b/src/modules/app/i18n/DictionaryManager.cpp @@ -105,7 +105,7 @@ Dictionary &DictionaryManager::getDictionary(const Language &language) { } if (!bestFilename.empty()) { - const core::String &pofile = core::string::path(*p, bestFilename); + const core::Path &pofile = p->append(bestFilename); const io::FilePtr &in = _filesystem->open(pofile); if (!in) { Log::error("failure opening: %s", pofile.c_str()); @@ -173,7 +173,7 @@ bool DictionaryManager::getUseFuzzy() const { return _useFuzzy; } -void DictionaryManager::addDirectory(const core::String &pathname, bool precedence /* = false */) { +void DictionaryManager::addDirectory(const core::Path &pathname, bool precedence /* = false */) { if (core::find(_searchPath.begin(), _searchPath.end(), pathname) == _searchPath.end()) { clearCache(); // adding directories invalidates cache if (precedence) { diff --git a/src/modules/app/i18n/DictionaryManager.h b/src/modules/app/i18n/DictionaryManager.h index a6a44363ce..127c106aa9 100644 --- a/src/modules/app/i18n/DictionaryManager.h +++ b/src/modules/app/i18n/DictionaryManager.h @@ -25,6 +25,7 @@ #include "Dictionary.h" #include "Language.h" +#include "core/Path.h" #include "io/Filesystem.h" namespace app { @@ -39,7 +40,7 @@ class DictionaryManager : public core::NonCopyable { typedef core::Map Dictionaries; Dictionaries _dictionaries; - typedef core::DynamicArray SearchPath; + typedef core::DynamicArray SearchPath; SearchPath _searchPath; core::String _charset; @@ -87,7 +88,7 @@ class DictionaryManager : public core::NonCopyable { * added directories have higher priority then later added ones. * Set @p precedence to true to invert this for a single addition. */ - void addDirectory(const core::String &pathname, bool precedence = false); + void addDirectory(const core::Path &pathname, bool precedence = false); /** Remove a directory from the search path */ void removeDirectory(const core::String &pathname); diff --git a/src/modules/app/i18n/POParser.cpp b/src/modules/app/i18n/POParser.cpp index 7f1ac698ca..dbe45fbc0a 100644 --- a/src/modules/app/i18n/POParser.cpp +++ b/src/modules/app/i18n/POParser.cpp @@ -33,12 +33,12 @@ namespace app { bool POParser::pedantic = true; -bool POParser::parse(const core::String &filename, io::SeekableReadStream &in, Dictionary &dict) { +bool POParser::parse(const core::Path &filename, io::SeekableReadStream &in, Dictionary &dict) { POParser parser(filename, in, dict); return parser.parse(); } -POParser::POParser(const core::String &filename, io::SeekableReadStream &in, Dictionary &dict, bool useFuzzy) +POParser::POParser(const core::Path &filename, io::SeekableReadStream &in, Dictionary &dict, bool useFuzzy) : _filename(filename), _in(in), _dict(dict), _useFuzzy(useFuzzy) { } diff --git a/src/modules/app/i18n/POParser.h b/src/modules/app/i18n/POParser.h index dd47dfe137..efdd4267d5 100644 --- a/src/modules/app/i18n/POParser.h +++ b/src/modules/app/i18n/POParser.h @@ -25,6 +25,7 @@ #include "Iconv.h" #include "core/NonCopyable.h" +#include "core/Path.h" #include "io/Stream.h" namespace app { @@ -33,7 +34,7 @@ class Dictionary; class POParser : public core::NonCopyable { private: - core::String _filename; + core::Path _filename; io::SeekableReadStream &_in; Dictionary &_dict; bool _useFuzzy; @@ -47,7 +48,7 @@ class POParser : public core::NonCopyable { IConv _conv; - POParser(const core::String &filename, io::SeekableReadStream &in, Dictionary &dict, bool useFuzzy = true); + POParser(const core::Path &filename, io::SeekableReadStream &in, Dictionary &dict, bool useFuzzy = true); ~POParser(); bool parseHeader(const core::String &header); @@ -66,7 +67,7 @@ class POParser : public core::NonCopyable { * @param in stream from which the PO file is read. * @param dict dictionary to which the strings are written */ - static bool parse(const core::String &filename, io::SeekableReadStream &in, Dictionary &dict); + static bool parse(const core::Path &filename, io::SeekableReadStream &in, Dictionary &dict); static bool pedantic; }; diff --git a/src/modules/app/tests/CommandCompleterTest.cpp b/src/modules/app/tests/CommandCompleterTest.cpp index c1911094f8..f108193830 100644 --- a/src/modules/app/tests/CommandCompleterTest.cpp +++ b/src/modules/app/tests/CommandCompleterTest.cpp @@ -12,7 +12,7 @@ class CommandCompleterTest: public app::AbstractTest { public: bool onInitApp() override{ const io::FilesystemPtr& fs = io::filesystem(); - fs->sysCreateDir("commandcompletertest/dir1"); + fs->sysCreateDir(core::Path("commandcompletertest/dir1")); fs->sysWrite("commandcompletertest/dir1/ignored", "ignore"); fs->sysWrite("commandcompletertest/dir1/ignoredtoo", "ignore"); fs->sysWrite("commandcompletertest/dir1/foo1.foo", "foo1"); diff --git a/src/modules/app/tests/POParserTest.cpp b/src/modules/app/tests/POParserTest.cpp index 9a3e024d1c..7302a2afb1 100644 --- a/src/modules/app/tests/POParserTest.cpp +++ b/src/modules/app/tests/POParserTest.cpp @@ -43,7 +43,7 @@ msgstr "Translation for Single line string" }; TEST_F(POParserTest, testParse) { - const core::String filename = "mem"; + const core::Path filename("mem"); io::MemoryReadStream stream(poString, strlen(poString)); Dictionary dict; ASSERT_TRUE(POParser::parse(filename, stream, dict)); diff --git a/src/modules/core/StringUtil.h b/src/modules/core/StringUtil.h index 727d9d38bf..211440110b 100644 --- a/src/modules/core/StringUtil.h +++ b/src/modules/core/StringUtil.h @@ -109,7 +109,6 @@ char *strncpyz(const char *input, size_t inputSize, char *target, size_t targetS /** * @brief Ensure that exactly one / is at the end of the given path - * @sa io::normalizePath() */ core::String sanitizeDirPath(core::String str); diff --git a/src/modules/io/Archive.cpp b/src/modules/io/Archive.cpp index 220cee661c..c5c026d0b8 100644 --- a/src/modules/io/Archive.cpp +++ b/src/modules/io/Archive.cpp @@ -25,7 +25,7 @@ bool Archive::exists(const core::String &file) const { void Archive::list(const core::String &basePath, ArchiveFiles &out, const core::String &filter) const { for (const auto &entry : _files) { - if (!basePath.empty() && !core::string::startsWith(entry.fullPath, basePath)) { + if (!basePath.empty() && !core::string::startsWith(entry.fullPath.str(), basePath)) { continue; } if (core::string::fileMatchesMultiple(entry.name.c_str(), filter.c_str())) { diff --git a/src/modules/io/File.cpp b/src/modules/io/File.cpp index 313666899f..2ce89ff92f 100644 --- a/src/modules/io/File.cpp +++ b/src/modules/io/File.cpp @@ -13,24 +13,14 @@ namespace io { -void normalizePath(core::String& str) { - core::string::replaceAllChars(str, '\\', '/'); -#ifndef __WINDOWS__ - if (str.size() >= 3 && str[0] != '\0' && core::string::isAlpha(str[0]) && str[1] == ':' && (str[2] == '\\' || str[2] == '/')) { - str.erase(0, 2); - } -#endif +File::File(const core::Path &rawPath, FileMode mode) : IOResource(), _rawPath(rawPath), _mode(mode) { + _file = createRWops(mode); } -File::File(const core::String& rawPath, FileMode mode) : - IOResource(), _rawPath(rawPath), _mode(mode) { - normalizePath(_rawPath); - _file = createRWops(mode); +File::File(const core::String &rawPath, FileMode mode) : File(core::String(rawPath), mode) { } -File::File(core::String&& rawPath, FileMode mode) : - IOResource(), _rawPath(core::move(rawPath)), _mode(mode) { - normalizePath(_rawPath); +File::File(core::String &&rawPath, FileMode mode) : IOResource(), _rawPath(core::move(rawPath)), _mode(mode) { _file = createRWops(mode); } @@ -69,6 +59,10 @@ bool File::exists() const { } const core::String& File::name() const { + return _rawPath.str(); +} + +const core::Path& File::path() const { return _rawPath; } @@ -173,21 +167,17 @@ long File::write(const unsigned char *buf, size_t len) const { return (long)len; } -core::String File::dir() const { - return core::string::extractDir(name()); +core::Path File::dir() const { + return _rawPath.dirname(); } core::String File::fileName() const { - return core::string::extractFilename(name()); + const core::Path &bn = _rawPath.basename(); + return core::string::stripExtension(bn.str()); } core::String File::extension() const { - const char *ext = SDL_strrchr(name().c_str(), '.'); - if (ext == nullptr) { - return ""; - } - ++ext; - return core::String(ext); + return _rawPath.extension(); } long File::length() const { diff --git a/src/modules/io/File.h b/src/modules/io/File.h index d0e1eb13da..b391a7f956 100644 --- a/src/modules/io/File.h +++ b/src/modules/io/File.h @@ -4,6 +4,7 @@ #pragma once +#include "core/Path.h" #include "core/String.h" #include "core/SharedPtr.h" #include "IOResource.h" @@ -25,11 +26,6 @@ enum class FileMode { ReadNoHome /**< reading from the virtual file system but skip user setting files in the home directories */ }; -/** - * @sa core::string::sanitizeDirPath() - */ -extern void normalizePath(core::String& str); - /** * @brief Wrapper for file based io. * @@ -41,13 +37,14 @@ class File : public IOResource { friend class core::SharedPtr; protected: SDL_RWops* _file; - core::String _rawPath; + core::Path _rawPath; FileMode _mode; mutable core::String _error; void error(CORE_FORMAT_STRING const char *msg, ...) const CORE_PRINTF_VARARG_FUNC(2); public: + File(const core::Path& rawPath, FileMode mode = FileMode::Read); File(const core::String& rawPath, FileMode mode = FileMode::Read); File(core::String&& rawPath, FileMode mode = FileMode::Read); virtual ~File(); @@ -87,7 +84,7 @@ class File : public IOResource { * @return The path of the file, without the name - or an * empty string if no path component was found */ - core::String dir() const; + core::Path dir() const; /** * @return Just the base file name component part - without * path and extension @@ -97,6 +94,7 @@ class File : public IOResource { * @return The full raw path of the file */ const core::String& name() const; + const core::Path& path() const; SDL_RWops* createRWops(FileMode mode) const; long write(io::ReadStream &stream) const; diff --git a/src/modules/io/Filesystem.cpp b/src/modules/io/Filesystem.cpp index 8ae80f21dc..df2879cf9b 100644 --- a/src/modules/io/Filesystem.cpp +++ b/src/modules/io/Filesystem.cpp @@ -33,56 +33,47 @@ bool Filesystem::init(const core::String &organisation, const core::String &appn _appname = appname; #ifdef __EMSCRIPTEN__ - EM_ASM( - FS.mkdir('/libsdl'); - FS.mount(IDBFS, {}, '/libsdl'); - FS.syncfs(true, function (err) { - assert(!err); - }); - ); + EM_ASM(FS.mkdir('/libsdl'); FS.mount(IDBFS, {}, '/libsdl'); FS.syncfs(true, function(err) { assert(!err); });); #endif char *path = SDL_GetBasePath(); - if (path == nullptr) { - _basePath = ""; - } else { - _basePath = path; - normalizePath(_basePath); + if (path != nullptr) { + _basePath = core::Path(path); SDL_free(path); } char *prefPath = SDL_GetPrefPath(_organisation.c_str(), _appname.c_str()); if (prefPath != nullptr) { - _homePath = prefPath; + _homePath = core::Path(prefPath); SDL_free(prefPath); } if (_homePath.empty()) { - _homePath = "./"; + _homePath = core::Path("./"); } const core::VarPtr &homePathVar = core::Var::get(cfg::AppHomePath, _homePath.c_str(), core::CV_READONLY | core::CV_NOPERSIST); - _homePath = homePathVar->strVal(); - normalizePath(_homePath); + _homePath = core::Path(homePathVar->strVal()); if (!sysCreateDir(_homePath, true)) { Log::error("Could not create home dir at: %s", _homePath.c_str()); return false; } - core_assert_always(registerPath(_homePath)); + core_assert_always(registerPath(core::Path(_homePath))); // this is a build system option that packagers could use to install // the application data into the proper system wide paths #ifdef PKGDATADIR - core_assert_always(registerPath(PKGDATADIR)); + core_assert_always(registerPath(core::Path(PKGDATADIR))); #endif // https://docs.appimage.org/packaging-guide/environment-variables.html const char *appImageDirectory = SDL_getenv("APPDIR"); if (appImageDirectory != nullptr) { const core::String appDir = _organisation + "-" + _appname; - const core::String appImagePath = core::string::sanitizeDirPath(core::string::path(appImageDirectory, "usr", "share", appDir)); + const core::String appImagePath = + core::string::sanitizeDirPath(core::string::path(appImageDirectory, "usr", "share", appDir)); if (exists(appImagePath)) { - core_assert_always(registerPath(appImagePath)); + core_assert_always(registerPath(core::Path(appImagePath))); } } @@ -92,14 +83,14 @@ bool Filesystem::init(const core::String &organisation, const core::String &appn core::Var::get(cfg::CorePath, "", 0, "Specifies an additional filesystem search path - must end on /"); if (!corePath->strVal().empty()) { if (exists(corePath->strVal())) { - core_assert_always(registerPath(corePath->strVal())); + core_assert_always(registerPath(core::Path(corePath->strVal()))); } else { Log::warn("%s '%s' does not exist", cfg::CorePath, corePath->strVal().c_str()); } } if (!_basePath.empty()) { - registerPath(_basePath); + registerPath(core::Path(_basePath)); } if (!initState(_state)) { @@ -108,20 +99,23 @@ bool Filesystem::init(const core::String &organisation, const core::String &appn return true; } -core::String Filesystem::sysFindBinary(const core::String &binaryName) const { - core::String binaryWithExtension = binaryName; +core::Path Filesystem::sysFindBinary(const core::String &binaryName) const { #ifdef _WIN32 + core::String binaryWithExtension = binaryName; binaryWithExtension += ".exe"; + core::Path binaryPath(binaryWithExtension); +#else + core::Path binaryPath(binaryName); #endif // Check current working directory - if (fs_exists(binaryWithExtension.c_str())) { - return sysAbsolutePath(binaryWithExtension); + if (fs_exists(binaryPath)) { + return sysAbsolutePath(binaryPath); } // Check the directory of the current binary - core::String binaryPath = core::string::path(_basePath, binaryWithExtension); - if (fs_exists(binaryPath.c_str())) { - return sysAbsolutePath(binaryPath); + core::Path fullBinaryPath = _basePath.append(binaryName); + if (fs_exists(fullBinaryPath)) { + return sysAbsolutePath(fullBinaryPath); } // Check PATH environment variable @@ -135,13 +129,14 @@ core::String Filesystem::sysFindBinary(const core::String &binaryName) const { core::DynamicArray paths; core::string::splitString(path, paths, pathSep); for (const auto &p : paths) { - const core::String binPath = core::string::path(p, binaryWithExtension); - if (fs_exists(binPath.c_str())) { + core::Path binPath(p); + binPath.append(binaryPath); + if (fs_exists(binPath)) { return binPath; } } } - return ""; + return core::Path(); } const core::DynamicArray Filesystem::sysOtherPaths() const { @@ -152,34 +147,34 @@ core::String Filesystem::sysSpecialDir(FilesystemDirectories dir) const { return _state._directories[dir]; } -bool Filesystem::sysRemoveFile(const core::String &file) const { +bool Filesystem::sysRemoveFile(const core::Path &file) const { if (file.empty()) { Log::error("Can't delete file: No path given"); return false; } - return fs_unlink(file.c_str()); + return fs_unlink(file); } -bool Filesystem::sysRemoveDir(const core::String &dir, bool recursive) const { +bool Filesystem::sysRemoveDir(const core::Path &dir, bool recursive) const { if (dir.empty()) { Log::error("Can't delete dir: No path given"); return false; } if (!recursive) { - return fs_rmdir(dir.c_str()); + return fs_rmdir(dir); } // TODO: implement me return false; } -bool Filesystem::sysCreateDir(const core::String &dir, bool recursive) const { +bool Filesystem::sysCreateDir(const core::Path &dir, bool recursive) const { if (dir.empty()) { return false; } if (!recursive) { - if (!fs_mkdir(dir.c_str())) { + if (!fs_mkdir(dir)) { Log::error("Failed to create dir '%s'", dir.c_str()); return false; } @@ -187,7 +182,7 @@ bool Filesystem::sysCreateDir(const core::String &dir, bool recursive) const { } // force trailing / so we can handle everything in loop - core::String s = core::string::sanitizeDirPath(dir); + core::String s = core::string::sanitizeDirPath(dir.str()); size_t pre = 0, pos; bool lastResult = false; @@ -197,9 +192,8 @@ bool Filesystem::sysCreateDir(const core::String &dir, bool recursive) const { if (dirpart.empty() || dirpart.last() == ':') { continue; // if leading / first time is 0 length } - const char *dirc = dirpart.c_str(); - if (!fs_mkdir(dirc)) { - Log::debug("Failed to create dir '%s'", dirc); + if (!fs_mkdir(core::Path(dirpart))) { + Log::debug("Failed to create dir '%s'", dirpart.c_str()); lastResult = false; continue; } @@ -208,16 +202,14 @@ bool Filesystem::sysCreateDir(const core::String &dir, bool recursive) const { return lastResult; } -bool Filesystem::_list(const core::String &directory, core::DynamicArray &entities, +bool Filesystem::_list(const core::Path &directory, core::DynamicArray &entities, const core::String &filter, int depth) { - const core::DynamicArray &entries = fs_scandir(directory.c_str()); + const core::DynamicArray &entries = fs_scandir(directory); Log::debug("Found %i entries in %s", (int)entries.size(), directory.c_str()); for (FilesystemEntry entry : entries) { - normalizePath(entry.name); - entry.fullPath = core::string::path(directory, entry.name); + entry.fullPath = directory.append(entry.name); if (entry.type == FilesystemEntry::Type::link) { - core::String symlink = fs_readlink(entry.fullPath.c_str()); - normalizePath(symlink); + core::Path symlink = fs_readlink(entry.fullPath); if (symlink.empty()) { Log::debug("Could not resolve symlink %s", entry.fullPath.c_str()); continue; @@ -229,7 +221,11 @@ bool Filesystem::_list(const core::String &directory, core::DynamicArray 0) { _list(entry.fullPath, entities, filter, depth - 1); } else { @@ -240,7 +236,7 @@ bool Filesystem::_list(const core::String &directory, core::DynamicArray &entities, const core::String &filter, int depth) const { - if (sysIsRelativePath(directory)) { - const core::String cwd = sysCurrentDir(); - for (const core::String &p : _paths) { - const core::String fullDir = core::string::path(p, directory); - if (core::string::isSamePath(fullDir, cwd)) { + core::Path path(directory); + if (path.isRelativePath()) { + const core::Path cwd = sysCurrentDir(); + for (const core::Path &p : _paths) { + const core::Path &fullDir = p.append(path); + if (fullDir == cwd) { continue; } _list(fullDir, entities, filter, depth); } - if (directory.empty()) { + if (path.empty()) { _list(cwd, entities, filter, depth); } } else { - _list(directory, entities, filter, depth); + _list(path, entities, filter, depth); } return true; } -bool Filesystem::sysChdir(const core::String &directory) { +bool Filesystem::sysChdir(const core::Path &directory) { Log::debug("Change current working dir to %s", directory.c_str()); - return fs_chdir(directory.c_str()); + return fs_chdir(directory); } void Filesystem::shutdown() { #ifdef __EMSCRIPTEN__ - EM_ASM( - FS.syncfs(true, function (err) { - }); - ); + EM_ASM(FS.syncfs(true, function(err){});); #endif } -core::String Filesystem::sysAbsolutePath(const core::String &path) const { - core::String abspath = fs_realpath(path.c_str()); +core::Path Filesystem::sysAbsolutePath(const core::Path& path) const { + core::Path abspath = fs_realpath(core::Path(path)); if (abspath.empty()) { - for (const core::String &p : registeredPaths()) { - const core::String &fullPath = core::string::path(p, path); - abspath = fs_realpath(fullPath.c_str()); + for (const core::Path &p : registeredPaths()) { + const core::Path &fullPath = p.append(path); + abspath = fs_realpath(fullPath); if (!abspath.empty()) { - normalizePath(abspath); return abspath; } } Log::error("Failed to get absolute path for '%s'", path.c_str()); - return ""; + return core::Path(); } - normalizePath(abspath); return abspath; } -bool Filesystem::sysIsHidden(const core::String &name) { - return fs_hidden(name.c_str()); +core::String Filesystem::sysAbsolutePath(const core::String &path) const { + core::Path p = sysAbsolutePath(core::Path(path)); + return p.str(); +} + +bool Filesystem::sysIsHidden(const core::Path &name) { + return fs_hidden(name); } bool Filesystem::sysIsReadableDir(const core::String &name) { - if (!fs_exists(name.c_str())) { + core::Path path(name); + if (!fs_exists(path)) { Log::trace("%s doesn't exist", name.c_str()); return false; } FilesystemEntry entry; - if (!fs_stat(name.c_str(), entry)) { + if (!fs_stat(path, entry)) { Log::trace("Could not stat '%s'", name.c_str()); return false; } @@ -323,39 +321,14 @@ bool Filesystem::sysIsReadableDir(const core::String &name) { return entry.type == FilesystemEntry::Type::dir; } -bool Filesystem::sysIsRelativePath(const core::String &name) { - const size_t size = name.size(); -#ifdef __WINDOWS__ - if (size < 2) { - return true; - } - if (name[0] == '/') { - return false; - } - // TODO: hm... not cool and most likely not enough - return name[1] != ':'; -#else - if (size == 0) { - return true; - } - return name[0] != '/'; -#endif -} - -bool Filesystem::registerPath(const core::String &path) { - if (!core::string::endsWith(path, "/")) { - Log::error("Failed to register data path: '%s' - it must end on /.", path.c_str()); - return false; - } +bool Filesystem::registerPath(const core::Path &path) { _paths.push_back(path); Log::debug("Registered data path: '%s'", path.c_str()); return true; } -core::String Filesystem::sysCurrentDir() const { - core::String cwd = fs_cwd(); - normalizePath(cwd); - return cwd; +core::Path Filesystem::sysCurrentDir() const { + return core::Path(fs_cwd()); } bool Filesystem::sysPopDir() { @@ -366,7 +339,7 @@ bool Filesystem::sysPopDir() { if (_dirStack.empty()) { return false; } - const core::String &directory = _dirStack.top(); + const core::Path &directory = _dirStack.top(); Log::trace("change current dir to %s", directory.c_str()); if (!sysChdir(directory)) { return false; @@ -374,9 +347,9 @@ bool Filesystem::sysPopDir() { return true; } -bool Filesystem::sysPushDir(const core::String &directory) { +bool Filesystem::sysPushDir(const core::Path &directory) { if (_dirStack.empty()) { - core::String cwd = sysCurrentDir(); + core::Path cwd = sysCurrentDir(); _dirStack.push(cwd); } if (!sysChdir(directory)) { @@ -387,63 +360,69 @@ bool Filesystem::sysPushDir(const core::String &directory) { return true; } -// TODO: case insensitive search should be possible - see searchPathFor() -io::FilePtr Filesystem::open(const core::String &filename, FileMode mode) const { +io::FilePtr Filesystem::open(const core::Path &filename, FileMode mode) const { core_assert_msg(!_homePath.empty(), "Filesystem is not yet initialized"); - if (sysIsReadableDir(filename)) { + if (sysIsReadableDir(filename.str())) { Log::debug("%s is a directory - skip this", filename.c_str()); return core::make_shared("", mode); } if (mode == FileMode::SysWrite) { Log::debug("Use absolute path to open file %s for writing", filename.c_str()); return core::make_shared(filename, mode); - } else if (mode == FileMode::SysRead && fs_exists(filename.c_str())) { + } else if (mode == FileMode::SysRead && fs_exists(filename)) { return core::make_shared(filename, mode); } else if (mode == FileMode::Write) { - if (!sysIsRelativePath(filename)) { + if (!filename.isRelativePath()) { Log::error("%s can't get opened in write mode", filename.c_str()); return core::make_shared("", mode); } - sysCreateDir(core::string::path(_homePath, core::string::extractDir(filename)), true); - return core::make_shared(core::string::path(_homePath, filename), mode); + const core::Path fullpath = _homePath.append(filename); + sysCreateDir(fullpath.dirname(), true); + return core::make_shared(_homePath.append(filename), mode); } FileMode openmode = mode; if (openmode == FileMode::ReadNoHome) { openmode = FileMode::Read; } - for (const core::String &p : _paths) { + for (const core::Path &p : _paths) { if (mode == FileMode::ReadNoHome && p == _homePath) { Log::debug("Skip reading home path"); continue; } - core::String fullpath = core::string::path(p, filename); - if (fs_exists(fullpath.c_str())) { + core::Path fullpath = p.append(filename); + if (fs_exists(fullpath)) { Log::debug("loading file %s from %s", filename.c_str(), p.c_str()); return core::make_shared(core::move(fullpath), openmode); } - if (sysIsRelativePath(p)) { - for (const core::String &s : _paths) { - if (core::string::isSamePath(s, p)) { + if (p.isRelativePath()) { + for (const core::Path &s : _paths) { + if (s.isRelativePath() || s == p) { continue; } - core::String fullrelpath = core::string::path(s, p, filename); - if (fs_exists(fullrelpath.c_str())) { + const core::Path fullrelpath = s + p + filename; + if (fs_exists(fullrelpath)) { Log::debug("loading file %s from %s%s", filename.c_str(), s.c_str(), p.c_str()); return core::make_shared(core::move(fullrelpath), openmode); } } } } - if (fs_exists(filename.c_str())) { + if (fs_exists(filename)) { Log::debug("loading file '%s'", filename.c_str()); return core::make_shared(filename, openmode); } - if (!sysIsRelativePath(filename)) { + if (!filename.isRelativePath()) { Log::debug("'%s' not found", filename.c_str()); return core::make_shared("", openmode); } Log::debug("Use %s from %s", filename.c_str(), _basePath.c_str()); - return core::make_shared(core::string::path(_basePath, filename), openmode); + return core::make_shared(_basePath.append(filename), openmode); +} + +// TODO: case insensitive search should be possible - see searchPathFor() +io::FilePtr Filesystem::open(const core::String &filename, FileMode mode) const { + const core::Path path(filename); + return open(path, mode); } core::String Filesystem::load(const char *filename, ...) { @@ -459,18 +438,23 @@ core::String Filesystem::load(const char *filename, ...) { return load(core::String(text)); } +core::String Filesystem::load(const core::Path& filename) const { + const io::FilePtr &f = open(filename); + return f->load(); +} + core::String Filesystem::load(const core::String &filename) const { const io::FilePtr &f = open(filename); return f->load(); } -core::String Filesystem::homeWritePath(const core::String &name) const { - return core::string::path(_homePath, name); +core::Path Filesystem::homeWritePath(const core::String &name) const { + return _homePath.append(name); } long Filesystem::homeWrite(const core::String &filename, io::ReadStream &stream) { - const core::String &fullPath = core::string::path(_homePath, filename); - const core::String path(core::string::extractDir(fullPath.c_str())); + const core::Path fullPath = _homePath.append(filename); + const core::Path path = fullPath.dirname(); sysCreateDir(path, true); io::File f(fullPath, FileMode::Write); long written = f.write(stream); @@ -479,8 +463,8 @@ long Filesystem::homeWrite(const core::String &filename, io::ReadStream &stream) } bool Filesystem::homeWrite(const core::String &filename, const uint8_t *content, size_t length) { - const core::String &fullPath = core::string::path(_homePath, filename); - const core::String path(core::string::extractDir(fullPath.c_str())); + const core::Path fullPath = _homePath.append(filename); + const core::Path path = fullPath.dirname(); sysCreateDir(path, true); io::File f(fullPath, FileMode::Write); return f.write(content, length) == static_cast(length); @@ -542,9 +526,7 @@ core::String searchPathFor(const FilesystemPtr &filesystem, const core::String & const core::String abspath = filesystem->sysAbsolutePath(relativePath); filesystem->list(abspath, entities); Log::trace("Found %i entries in %s", (int)entities.size(), abspath.c_str()); - auto predicate = [&] (const io::FilesystemEntry &e) { - return core::string::iequals(e.name, filename); - }; + auto predicate = [&](const io::FilesystemEntry &e) { return core::string::iequals(e.name, filename); }; auto iter = core::find_if(entities.begin(), entities.end(), predicate); if (iter == entities.end()) { Log::debug("Could not find %s in '%s'", filename.c_str(), abspath.c_str()); diff --git a/src/modules/io/Filesystem.h b/src/modules/io/Filesystem.h index bba63ac7a0..c9ddebd64e 100644 --- a/src/modules/io/Filesystem.h +++ b/src/modules/io/Filesystem.h @@ -6,6 +6,7 @@ #include "File.h" #include "FilesystemEntry.h" +#include "core/Path.h" #include "core/SharedPtr.h" #include "core/String.h" #include "core/collection/DynamicArray.h" @@ -15,7 +16,7 @@ namespace io { -using Paths = core::DynamicArray; +using Paths = core::DynamicArray; enum FilesystemDirectories { FS_Dir_Download, @@ -59,14 +60,14 @@ class Filesystem { * the installation directory or the current working directory. In case the * binary is a symlink, it it resolved. */ - core::String _basePath; - core::String _homePath; + core::Path _basePath; + core::Path _homePath; FilesystemState _state; Paths _paths; - core::Stack _dirStack; + core::Stack _dirStack; - static bool _list(const core::String& directory, core::DynamicArray& entities, const core::String& filter = "", int depth = 0); + static bool _list(const core::Path& directory, core::DynamicArray& entities, const core::String& filter = "", int depth = 0); public: ~Filesystem(); @@ -79,7 +80,7 @@ class Filesystem { * @param[in] path If this is a relative path the filesystem will append this relative path to all * known search paths when trying to find a file. */ - bool registerPath(const core::String& path); + bool registerPath(const core::Path& path); /** * @brief Get the path where the application resides. @@ -89,12 +90,12 @@ class Filesystem { * be the process's current working directory. * @note Ends with path separator */ - const core::String& basePath() const; + const core::Path& basePath() const; /** * @brief The path where the application can store data * @note Ends with path separator */ - const core::String& homePath() const; + const core::Path& homePath() const; bool exists(const core::String& filename) const; @@ -107,17 +108,19 @@ class Filesystem { */ bool list(const core::String& directory, core::DynamicArray& entities, const core::String& filter = "", int depth = 0) const; + io::FilePtr open(const core::Path &filename, FileMode mode = FileMode::Read) const; io::FilePtr open(const core::String& filename, FileMode mode = FileMode::Read) const; core::String load(CORE_FORMAT_STRING const char *filename, ...) CORE_PRINTF_VARARG_FUNC(2); core::String load(const core::String& filename) const; + core::String load(const core::Path& filename) const; // HOME PATH HANDLING /** * @brief Returns a path where the given file can be saved. */ - core::String homeWritePath(const core::String &name) const; + core::Path homeWritePath(const core::String &name) const; bool homeWrite(const core::String& filename, const uint8_t* content, size_t length); long homeWrite(const core::String& filename, io::ReadStream &stream); @@ -132,29 +135,29 @@ class Filesystem { /** * @brief Push a working dir change onto the stack for later returning without knowing the origin */ - bool sysPushDir(const core::String& directory); + bool sysPushDir(const core::Path& directory); core::String sysSpecialDir(FilesystemDirectories dir) const; const core::DynamicArray sysOtherPaths() const; - core::String sysFindBinary(const core::String &binaryName) const; + core::Path sysFindBinary(const core::String &binaryName) const; /** * @brief The current working directory without a tailing / */ - core::String sysCurrentDir() const; + core::Path sysCurrentDir() const; static bool sysIsReadableDir(const core::String& name); - static bool sysIsHidden(const core::String &name); - static bool sysIsRelativePath(const core::String &name); + static bool sysIsHidden(const core::Path &name); core::String sysAbsolutePath(const core::String& path) const; + core::Path sysAbsolutePath(const core::Path& path) const; /** * @brief Changes the current working directory * @see popDir() * @see pushDir() */ - static bool sysChdir(const core::String& directory); + static bool sysChdir(const core::Path& directory); /** * @note The difference to the usual write() methods is that the given path is not put into the @@ -173,18 +176,18 @@ class Filesystem { * @brief This will create the directory without taking the write path into account. BEWARE! * @param dir The full path to the directory or relative to the current working dir of your app. */ - bool sysCreateDir(const core::String& dir, bool recursive = true) const; + bool sysCreateDir(const core::Path& dir, bool recursive = true) const; /** * @brief This will remove the directory without taking the write path into account. BEWARE! * @param dir The full path to the directory or relative to the current working dir of your app. */ - bool sysRemoveDir(const core::String& dir, bool recursive = false) const; + bool sysRemoveDir(const core::Path& dir, bool recursive = false) const; /** * @brief This will remove the file without taking the write path into account. BEWARE! * @param file The full path to the file or relative to the current working dir of your app. */ - bool sysRemoveFile(const core::String& file) const; + bool sysRemoveFile(const core::Path& file) const; }; inline const Paths& Filesystem::registeredPaths() const { @@ -198,11 +201,11 @@ inline bool Filesystem::exists(const core::String& filename) const { return open(filename)->exists(); } -inline const core::String& Filesystem::basePath() const { +inline const core::Path& Filesystem::basePath() const { return _basePath; } -inline const core::String& Filesystem::homePath() const { +inline const core::Path& Filesystem::homePath() const { return _homePath; } diff --git a/src/modules/io/FilesystemEntry.cpp b/src/modules/io/FilesystemEntry.cpp index 63dccd4530..fe0ef09e8d 100644 --- a/src/modules/io/FilesystemEntry.cpp +++ b/src/modules/io/FilesystemEntry.cpp @@ -4,17 +4,18 @@ #include "FilesystemEntry.h" #include "core/Log.h" +#include "core/Path.h" #include "core/StringUtil.h" #include "system/System.h" namespace io { -FilesystemEntry createFilesystemEntry(const core::String &filename) { +FilesystemEntry createFilesystemEntry(const core::Path &filename) { FilesystemEntry entry; - entry.name = core::string::extractFilenameWithExtension(filename); + entry.name = core::string::extractFilenameWithExtension(filename.str()); entry.fullPath = filename; - if (!fs_stat(filename.c_str(), entry)) { - Log::trace("Could not stat '%s'", filename.c_str()); + if (!fs_stat(entry.fullPath, entry)) { + Log::trace("Could not stat '%s'", entry.fullPath.c_str()); } return entry; } diff --git a/src/modules/io/FilesystemEntry.h b/src/modules/io/FilesystemEntry.h index 3c9a1342bc..2c159c2b06 100644 --- a/src/modules/io/FilesystemEntry.h +++ b/src/modules/io/FilesystemEntry.h @@ -4,6 +4,7 @@ #pragma once +#include "core/Path.h" #include "core/String.h" #include @@ -11,7 +12,7 @@ namespace io { struct FilesystemEntry { core::String name; - core::String fullPath; + core::Path fullPath; enum class Type : uint8_t { file, dir, @@ -35,6 +36,6 @@ struct FilesystemEntry { } }; -FilesystemEntry createFilesystemEntry(const core::String &filename); +FilesystemEntry createFilesystemEntry(const core::Path &filename); } diff --git a/src/modules/io/ZipArchive.cpp b/src/modules/io/ZipArchive.cpp index ab1b3c8daf..fc7782e0fb 100644 --- a/src/modules/io/ZipArchive.cpp +++ b/src/modules/io/ZipArchive.cpp @@ -140,8 +140,8 @@ bool ZipArchive::init(const core::String &path, io::SeekableReadStream *stream) continue; } FilesystemEntry entry; - entry.fullPath = zipStat.m_filename; - entry.name = core::string::extractFilenameWithExtension(entry.fullPath); + entry.fullPath = core::Path(zipStat.m_filename); + entry.name = core::string::extractFilenameWithExtension(entry.fullPath.str()); entry.type = FilesystemEntry::Type::file; entry.size = zipStat.m_uncomp_size; entry.mtime = zipStat.m_time; diff --git a/src/modules/io/system/Null.cpp b/src/modules/io/system/Null.cpp index 68cdac042c..614179fe50 100644 --- a/src/modules/io/system/Null.cpp +++ b/src/modules/io/system/Null.cpp @@ -13,53 +13,53 @@ bool initState(io::FilesystemState &state) { return false; } -bool fs_mkdir(const char *path) { +bool fs_mkdir(const core::Path &path) { return false; } -bool fs_rmdir(const char *path) { +bool fs_rmdir(const core::Path &path) { return false; } -bool fs_hidden(const char *path) { +bool fs_hidden(const core::Path &path) { return false; } -bool fs_unlink(const char *path) { +bool fs_unlink(const core::Path &path) { return false; } -bool fs_exists(const char *path) { +bool fs_exists(const core::Path &path) { return false; } -bool fs_writeable(const char *path) { +bool fs_writeable(const core::Path &path) { return false; } -bool fs_chdir(const char *path) { +bool fs_chdir(const core::Path &path) { return false; } -core::String fs_realpath(const char *path) { +core::Path fs_realpath(const core::Path &path) { return path; } -bool fs_stat(const char *path, FilesystemEntry &entry) { +bool fs_stat(const core::Path &path, FilesystemEntry &entry) { return false; } -core::DynamicArray fs_scandir(const char *path) { +core::DynamicArray fs_scandir(const core::Path &path) { core::DynamicArray foo; return foo; } -core::String fs_readlink(const char *path) { +core::Path fs_readlink(const core::Path &path) { return path; } -core::String fs_cwd() { - return "/"; +core::Path fs_cwd() { + return core::Path("/"); } } // namespace io diff --git a/src/modules/io/system/System.h b/src/modules/io/system/System.h index 1c1b570497..4315236e9d 100644 --- a/src/modules/io/system/System.h +++ b/src/modules/io/system/System.h @@ -4,22 +4,23 @@ #pragma once +#include "core/Path.h" #include "core/collection/DynamicArray.h" #include "io/FilesystemEntry.h" namespace io { -bool fs_mkdir(const char *path); -bool fs_rmdir(const char *path); -bool fs_unlink(const char *path); -bool fs_exists(const char *path); -bool fs_writeable(const char *path); -bool fs_hidden(const char *path); -bool fs_chdir(const char *path); -core::String fs_realpath(const char *path); -bool fs_stat(const char *path, FilesystemEntry &entry); -core::DynamicArray fs_scandir(const char *path); -core::String fs_readlink(const char *path); -core::String fs_cwd(); +bool fs_mkdir(const core::Path &path); +bool fs_rmdir(const core::Path &path); +bool fs_unlink(const core::Path &path); +bool fs_exists(const core::Path &path); +bool fs_writeable(const core::Path &path); +bool fs_hidden(const core::Path &path); +bool fs_chdir(const core::Path &path); +core::Path fs_realpath(const core::Path &path); +bool fs_stat(const core::Path &path, FilesystemEntry &entry); +core::DynamicArray fs_scandir(const core::Path &path); +core::Path fs_readlink(const core::Path &path); +core::Path fs_cwd(); } // namespace io diff --git a/src/modules/io/system/Unix.cpp b/src/modules/io/system/Unix.cpp index 3b615977d0..7e974f8b9b 100644 --- a/src/modules/io/system/Unix.cpp +++ b/src/modules/io/system/Unix.cpp @@ -187,77 +187,78 @@ bool initState(io::FilesystemState &state) { return true; } -bool fs_mkdir(const char *path) { - const int ret = mkdir(path, 0740); +bool fs_mkdir(const core::Path &path) { + const int ret = mkdir(path.c_str(), 0740); if (ret == 0) { return true; } if (errno == EEXIST) { return true; } - Log::error("Failed to mkdir %s: %s", path, strerror(errno)); + Log::error("Failed to mkdir %s: %s", path.c_str(), strerror(errno)); return false; } -bool fs_rmdir(const char *path) { - const int ret = rmdir(path); +bool fs_rmdir(const core::Path& path) { + const int ret = rmdir(path.c_str()); if (ret != 0) { - Log::error("Failed to rmdir %s: %s", path, strerror(errno)); + Log::error("Failed to rmdir %s: %s", path.c_str(), strerror(errno)); } return ret == 0; } -bool fs_unlink(const char *path) { - const int ret = unlink(path); +bool fs_unlink(const core::Path& path) { + const int ret = unlink(path.c_str()); if (ret != 0) { - Log::error("Failed to unlink %s: %s", path, strerror(errno)); + Log::error("Failed to unlink %s: %s", path.c_str(), strerror(errno)); } return ret == 0; } -bool fs_exists(const char *path) { - const int ret = access(path, F_OK); +bool fs_exists(const core::Path& path) { + const int ret = access(path.c_str(), F_OK); if (ret != 0) { - Log::trace("Failed to access %s: %s", path, strerror(errno)); + Log::trace("Failed to access %s: %s", path.c_str(), strerror(errno)); } return ret == 0; } -bool fs_chdir(const char *path) { - const int ret = chdir(path); +bool fs_chdir(const core::Path& path) { + const int ret = chdir(path.c_str()); if (ret != 0) { - Log::error("Failed to chdir to %s: %s", path, strerror(errno)); + Log::error("Failed to chdir to %s: %s", path.c_str(), strerror(errno)); } return ret == 0; } -core::String fs_cwd() { +core::Path fs_cwd() { char buf[4096]; const char *p = getcwd(buf, lengthof(buf)); if (p == nullptr) { Log::error("Failed to get current working dir: %s", strerror(errno)); + return core::Path(); } - return p; + return core::Path(p); } -core::String fs_realpath(const char *path) { - if (path[0] == '\0') { +core::Path fs_realpath(const core::Path& path) { + if (path.empty()) { // unified with _fullpath on windows return fs_cwd(); } char buf[PATH_MAX]; - const char *rp = realpath(path, buf); + const char *rp = realpath(path.c_str(), buf); if (rp == nullptr) { - return ""; + return core::Path(""); } - return rp; + return core::Path(rp); } -bool fs_stat(const char *path, FilesystemEntry &entry) { +bool fs_stat(const core::Path& path, FilesystemEntry &entry) { struct stat s; - const int ret = stat(path, &s); + const int ret = stat(path.c_str(), &s); if (ret != 0) { - Log::debug("Failed to stat %s: %s", path, strerror(errno)); + Log::debug("Failed to stat %s: %s", path.c_str(), strerror(errno)); return false; } @@ -270,9 +271,9 @@ bool fs_stat(const char *path, FilesystemEntry &entry) { return true; } -core::String fs_readlink(const char *path) { +core::String fs_readlink(const core::Path& path) { char buf[4096]; - ssize_t len = readlink(path, buf, lengthof(buf)); + ssize_t len = readlink(path.c_str(), buf, lengthof(buf)); if (len == -1) { return ""; } @@ -281,8 +282,8 @@ core::String fs_readlink(const char *path) { return buf; } -bool fs_writeable(const char *path) { - return access(path, W_OK) == 0; +bool fs_writeable(const core::Path& path) { + return access(path.c_str(), W_OK) == 0; } static int fs_scandir_filter(const struct dirent *dent) { @@ -293,9 +294,9 @@ static int fs_scandir_sort(const struct dirent **a, const struct dirent **b) { return strcoll((*a)->d_name, (*b)->d_name); } -core::DynamicArray fs_scandir(const char *path) { +core::DynamicArray fs_scandir(const core::Path& path) { struct dirent **files = nullptr; - const int n = scandir(path, &files, fs_scandir_filter, fs_scandir_sort); + const int n = scandir(path.c_str(), &files, fs_scandir_filter, fs_scandir_sort); core::DynamicArray entries; entries.reserve(n); for (int i = 0; i < n; ++i) { @@ -325,11 +326,11 @@ core::DynamicArray fs_scandir(const char *path) { return entries; } -bool fs_hidden(const char *path) { +bool fs_hidden(const core::Path& path) { if (path == nullptr) { return false; } - return path[0] == '.'; + return path.str().first() == '.'; } } // namespace io diff --git a/src/modules/io/system/Windows.cpp b/src/modules/io/system/Windows.cpp index 4243a5be90..320726f779 100644 --- a/src/modules/io/system/Windows.cpp +++ b/src/modules/io/system/Windows.cpp @@ -80,8 +80,8 @@ bool initState(io::FilesystemState &state) { return true; } -bool fs_mkdir(const char *path) { - WCHAR *wpath = io_UTF8ToStringW(path); +bool fs_mkdir(const core::Path& path) { + WCHAR *wpath = io_UTF8ToStringW(path.c_str()); priv::denormalizePath(wpath); const int ret = _wmkdir(wpath); SDL_free(wpath); @@ -91,108 +91,108 @@ bool fs_mkdir(const char *path) { if (errno == EEXIST) { return true; } - Log::error("Failed to mkdir %s: %s", path, strerror(errno)); + Log::error("Failed to mkdir %s: %s", path.c_str(), strerror(errno)); return false; } -bool fs_unlink(const char *path) { - WCHAR *wpath = io_UTF8ToStringW(path); +bool fs_unlink(const core::Path& path) { + WCHAR *wpath = io_UTF8ToStringW(path.c_str()); priv::denormalizePath(wpath); const int ret = _wunlink(wpath); SDL_free(wpath); if (ret != 0) { - Log::error("Failed to unlink %s: %s", path, strerror(errno)); + Log::error("Failed to unlink %s: %s", path.c_str(), strerror(errno)); } return ret == 0; } -bool fs_rmdir(const char *path) { - WCHAR *wpath = io_UTF8ToStringW(path); +bool fs_rmdir(const core::Path& path) { + WCHAR *wpath = io_UTF8ToStringW(path.c_str()); priv::denormalizePath(wpath); const int ret = _wrmdir(wpath); SDL_free(wpath); if (ret != 0) { - Log::error("Failed to rmdir %s: %s", path, strerror(errno)); + Log::error("Failed to rmdir %s: %s", path.c_str(), strerror(errno)); } return ret == 0; } -bool fs_hidden(const char *path) { - WCHAR *wpath = io_UTF8ToStringW(path); +bool fs_hidden(const core::Path& path) { + WCHAR *wpath = io_UTF8ToStringW(path.c_str()); priv::denormalizePath(wpath); DWORD attributes = GetFileAttributesW(wpath); SDL_free(wpath); if (attributes == INVALID_FILE_ATTRIBUTES) { - Log::debug("Failed to get file attributes for %s: %s", path, strerror(errno)); + Log::debug("Failed to get file attributes for %s: %s", path.c_str(), strerror(errno)); return false; } return (attributes & FILE_ATTRIBUTE_HIDDEN) != 0; } -bool fs_exists(const char *path) { - WCHAR *wpath = io_UTF8ToStringW(path); +bool fs_exists(const core::Path& path) { + WCHAR *wpath = io_UTF8ToStringW(path.c_str()); priv::denormalizePath(wpath); const int ret = _waccess(wpath, 0); SDL_free(wpath); if (ret != 0) { - Log::trace("Failed to access %s: %s", path, strerror(errno)); + Log::trace("Failed to access %s: %s", path.c_str(), strerror(errno)); } return ret == 0; } -bool fs_writeable(const char *path) { - WCHAR *wpath = io_UTF8ToStringW(path); +bool fs_writeable(const core::Path& path) { + WCHAR *wpath = io_UTF8ToStringW(path.c_str()); priv::denormalizePath(wpath); const int ret = _waccess(wpath, 2); SDL_free(wpath); return ret == 0; } -bool fs_chdir(const char *path) { - WCHAR *wpath = io_UTF8ToStringW(path); +bool fs_chdir(const core::Path& path) { + WCHAR *wpath = io_UTF8ToStringW(path.c_str()); priv::denormalizePath(wpath); const bool ret = SetCurrentDirectoryW(wpath); SDL_free(wpath); if (!ret) { - Log::error("Failed to chdir to %s: %s", path, strerror(errno)); + Log::error("Failed to chdir to %s: %s", path.c_str(), strerror(errno)); } return ret; } -core::String fs_cwd() { +core::Path fs_cwd() { WCHAR buf[4096]; const WCHAR *p = _wgetcwd(buf, lengthof(buf)); if (p == nullptr) { Log::error("Failed to get current working dir: %s", strerror(errno)); - return ""; + return core::Path(); } char *utf8 = io_StringToUTF8W(p); const core::String str(utf8); SDL_free(utf8); - return str; + return core::Path(str); } -core::String fs_realpath(const char *path) { - WCHAR *wpath = io_UTF8ToStringW(path); +core::Path fs_realpath(const core::Path &path) { + WCHAR *wpath = io_UTF8ToStringW(path.c_str()); WCHAR wfull[_MAX_PATH]; if (_wfullpath(wfull, wpath, lengthof(wfull)) == nullptr) { SDL_free(wpath); - return ""; + return core::Path(); } SDL_free(wpath); priv::denormalizePath(wfull); char *full = io_StringToUTF8W(wfull); const core::String str(full); SDL_free(full); - return str; + return core::Path(str); } -bool fs_stat(const char *path, FilesystemEntry &entry) { +bool fs_stat(const core::Path &path, FilesystemEntry &entry) { struct _stat s; - WCHAR *wpath = io_UTF8ToStringW(path); + WCHAR *wpath = io_UTF8ToStringW(path.c_str()); priv::denormalizePath(wpath); const int ret = _wstat(wpath, &s); SDL_free(wpath); @@ -204,21 +204,21 @@ bool fs_stat(const char *path, FilesystemEntry &entry) { entry.size = s.st_size; return true; } - Log::debug("Failed to stat %s: %s", path, strerror(errno)); + Log::debug("Failed to stat %s: %s", path.c_str(), strerror(errno)); return false; } -core::String fs_readlink(const char *path) { - return ""; +core::Path fs_readlink(const core::Path &path) { + return path; } static int fs_scandir_filter(const struct dirent *dent) { return strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0; } -core::DynamicArray fs_scandir(const char *path) { +core::DynamicArray fs_scandir(const core::Path &path) { struct dirent **files = nullptr; - const int n = scandir(path, &files, fs_scandir_filter, alphasort); + const int n = scandir(path.c_str(), &files, fs_scandir_filter, alphasort); core::DynamicArray entries; entries.reserve(n); for (int i = 0; i < n; ++i) { diff --git a/src/modules/io/tests/FileTest.cpp b/src/modules/io/tests/FileTest.cpp index c3e5bd770c..2eaeb1b39b 100644 --- a/src/modules/io/tests/FileTest.cpp +++ b/src/modules/io/tests/FileTest.cpp @@ -21,7 +21,7 @@ TEST_F(FileTest, testGetPath) { io::Filesystem fs; fs.init("test", "test"); const io::FilePtr &file = fs.open("foobar/1.txt", io::FileMode::Read); - EXPECT_TRUE(core::string::endsWith(file->dir(), "foobar/")); + EXPECT_TRUE(core::string::endsWith(file->dir().str(), "foobar")); EXPECT_EQ("txt", file->extension()); EXPECT_EQ("1", file->fileName()); EXPECT_TRUE(core::string::endsWith(file->name(), "foobar/1.txt")); diff --git a/src/modules/io/tests/FilesystemArchiveTest.cpp b/src/modules/io/tests/FilesystemArchiveTest.cpp index 8ca441a01b..83649e829e 100644 --- a/src/modules/io/tests/FilesystemArchiveTest.cpp +++ b/src/modules/io/tests/FilesystemArchiveTest.cpp @@ -6,7 +6,6 @@ #include "app/tests/AbstractTest.h" #include "core/ScopedPtr.h" #include "core/String.h" -#include "core/tests/TestHelper.h" #include "io/Archive.h" #include "io/Filesystem.h" #include "io/Stream.h" @@ -41,7 +40,7 @@ TEST_F(FilesystemArchiveTest, DISABLED_testFilesytemArchiveCurrentDir) { fsa.init("."); ASSERT_FALSE(fsa.files().empty()); FilesystemEntry entry = fsa.files().front(); - core::ScopedPtr rs(fsa.readStream(entry.fullPath)); + core::ScopedPtr rs(fsa.readStream(entry.fullPath.str())); ASSERT_TRUE(rs) << "Should be able to read a file with a full path"; core::ScopedPtr rs2(fsa.readStream(entry.name)); ASSERT_TRUE(rs2) diff --git a/src/modules/io/tests/FilesystemTest.cpp b/src/modules/io/tests/FilesystemTest.cpp index e3f6f76ccc..97d67bb79a 100644 --- a/src/modules/io/tests/FilesystemTest.cpp +++ b/src/modules/io/tests/FilesystemTest.cpp @@ -25,7 +25,7 @@ ::std::ostream &operator<<(::std::ostream &ostream, const core::DynamicArrayname(); - filepath = file->dir(); + filepath = file->dir().str(); content = file->load(); file->close(); } EXPECT_EQ("123", content) << "Written content doesn't match expected"; - EXPECT_TRUE(fs.sysRemoveFile(filename)) << "Failed to delete " << filename.c_str(); - EXPECT_TRUE(fs.sysRemoveDir(filepath)) << "Failed to delete " << filepath.c_str(); + EXPECT_TRUE(fs.sysRemoveFile(core::Path(filename))) << "Failed to delete " << filename.c_str(); + EXPECT_TRUE(fs.sysRemoveDir(core::Path(filepath))) << "Failed to delete " << filepath.c_str(); fs.shutdown(); } TEST_F(FilesystemTest, testCreateDirRecursive) { io::Filesystem fs; EXPECT_TRUE(fs.init("test", "test")) << "Failed to initialize the filesystem"; - EXPECT_TRUE(fs.sysCreateDir("dir1/dir2/dir3/dir4", true)); - EXPECT_TRUE(fs.sysRemoveDir("dir1/dir2/dir3/dir4")); - EXPECT_TRUE(fs.sysRemoveDir("dir1/dir2/dir3")); - EXPECT_TRUE(fs.sysRemoveDir("dir1/dir2")); - EXPECT_TRUE(fs.sysRemoveDir("dir1")); + EXPECT_TRUE(fs.sysCreateDir(core::Path("dir1/dir2/dir3/dir4"), true)); + EXPECT_TRUE(fs.sysRemoveDir(core::Path("dir1/dir2/dir3/dir4"))); + EXPECT_TRUE(fs.sysRemoveDir(core::Path("dir1/dir2/dir3"))); + EXPECT_TRUE(fs.sysRemoveDir(core::Path("dir1/dir2"))); + EXPECT_TRUE(fs.sysRemoveDir(core::Path("dir1"))); fs.shutdown(); } TEST_F(FilesystemTest, testCreateDirNonRecursiveFail) { io::Filesystem fs; EXPECT_TRUE(fs.init("test", "test")) << "Failed to initialize the filesystem"; - EXPECT_FALSE(fs.sysCreateDir("does/not/exist", false)); + EXPECT_FALSE(fs.sysCreateDir(core::Path("does/not/exist"), false)); fs.shutdown(); } TEST_F(FilesystemTest, testSearchPathFor) { io::FilesystemPtr fs = core::make_shared(); EXPECT_TRUE(fs->init("test", "test")) << "Failed to initialize the filesystem"; - EXPECT_EQ(core::string::path(fs->sysCurrentDir(), "iotest.txt"), searchPathFor(fs, "foobar/does/not/exist", "iotest.txt")); + EXPECT_EQ(core::string::path(fs->sysCurrentDir().str(), "iotest.txt"), searchPathFor(fs, "foobar/does/not/exist", "iotest.txt")); ASSERT_TRUE(fs->sysWrite("dir123/testfile", "123")) << "Failed to write content to testfile in dir123"; - EXPECT_EQ(core::string::path(fs->sysCurrentDir(), "dir123/testfile"), searchPathFor(fs, "/foobar/does/not/dir123", "TestFile")); + EXPECT_EQ(core::string::path(fs->sysCurrentDir().str(), "dir123/testfile"), searchPathFor(fs, "/foobar/does/not/dir123", "TestFile")); fs->shutdown(); } diff --git a/src/modules/io/tests/ZipArchiveTest.cpp b/src/modules/io/tests/ZipArchiveTest.cpp index f41b2b8397..0922720fe5 100644 --- a/src/modules/io/tests/ZipArchiveTest.cpp +++ b/src/modules/io/tests/ZipArchiveTest.cpp @@ -34,7 +34,7 @@ TEST_F(ZipArchiveTest, testZipArchive) { EXPECT_STREQ("yet another file in root\n", buf); EXPECT_EQ("file.txt", files[1].name); EXPECT_EQ("file.txt", files[2].name); - EXPECT_EQ("dir/file.txt", files[2].fullPath); + EXPECT_EQ("dir/file.txt", files[2].fullPath.str()); } } // namespace io diff --git a/src/modules/ui/FileDialog.cpp b/src/modules/ui/FileDialog.cpp index da236047b2..55fe8930e2 100644 --- a/src/modules/ui/FileDialog.cpp +++ b/src/modules/ui/FileDialog.cpp @@ -116,7 +116,7 @@ static const struct FileDialogSorter { static core::String assemblePath(const core::String &dir, const io::FilesystemEntry &ent) { if (ent.isDirectory() && ent.name == "..") { - return ent.fullPath; + return ent.fullPath.str(); } return core::string::path(dir, ent.name); } @@ -128,7 +128,7 @@ void FileDialog::applyFilter(video::OpenFileMode type) { if (!isRootPath) { _parentDir.name = ".."; _parentDir.type = io::FilesystemEntry::Type::dir; - _parentDir.fullPath = _app->filesystem()->sysAbsolutePath(core::string::path(_currentPath, "..")); + _parentDir.fullPath = core::Path(_app->filesystem()->sysAbsolutePath(core::string::path(_currentPath, ".."))); _filteredEntities.push_back(&_parentDir); } for (size_t i = 0; i < _entities.size(); ++i) { @@ -229,22 +229,23 @@ bool FileDialog::openDir(video::OpenFileMode type, const io::FormatDescription* selectFilter(type, lastFilter); } - const core::String &filePath = core::string::extractDir(filename); - if (filePath.empty() || !_app->filesystem()->exists(filePath)) { + const core::Path path(filename); + const core::Path pathDir = path.dirname(); + if (pathDir.empty() || !_app->filesystem()->exists(pathDir.str())) { const core::String &lastDir = _lastDirVar->strVal(); if (_app->filesystem()->exists(lastDir)) { _currentPath = lastDir; } else { - _currentPath = _app->filesystem()->homePath(); + _currentPath = _app->filesystem()->homePath().str(); } } else { - _currentPath = filePath; + _currentPath = pathDir.str(); } - _selectedEntry = io::FilesystemEntry{core::string::extractFilenameWithExtension(filename), filename, io::FilesystemEntry::Type::file, 0, 0}; + _selectedEntry = io::FilesystemEntry{core::string::extractFilenameWithExtension(filename), path, io::FilesystemEntry::Type::file, 0, 0}; _entryIndex = -1; if (!_app->filesystem()->exists(_currentPath)) { - _currentPath = _app->filesystem()->homePath(); + _currentPath = _app->filesystem()->homePath().str(); _lastDirVar->setVal(_currentPath); } @@ -345,8 +346,8 @@ void FileDialog::quickAccessPanel(video::OpenFileMode type, const core::String & quickAccessEntry(index++, type, dir, contentRegionWidth, folderNames[n], folderIcons[n]); } const io::Paths& paths = _app->filesystem()->registeredPaths(); - for (const core::String& path : paths) { - const core::String& absPath = _app->filesystem()->sysAbsolutePath(path); + for (const core::Path& path : paths) { + const core::String& absPath = _app->filesystem()->sysAbsolutePath(path.str()); if (absPath.empty()) { continue; } @@ -407,7 +408,7 @@ bool FileDialog::hide(const core::String &file) const { if (_showHidden->boolVal()) { return false; } - return io::Filesystem::sysIsHidden(file); + return io::Filesystem::sysIsHidden(core::Path(file)); } static const char *iconForType(io::FilesystemEntry::Type type) { @@ -614,7 +615,7 @@ void FileDialog::popupNewFolder() { _newFolderError = TimedError(_("Folder name can't be empty"), timeProvider->tickNow(), 1500UL); } else { const core::String &newFilePath = assemblePath(_currentPath, _newFolderName); - _app->filesystem()->sysCreateDir(newFilePath); + _app->filesystem()->sysCreateDir(core::Path(newFilePath)); ImGui::CloseCurrentPopup(); } } diff --git a/src/modules/ui/IMGUIApp.cpp b/src/modules/ui/IMGUIApp.cpp index 05deadeb99..2902704089 100644 --- a/src/modules/ui/IMGUIApp.cpp +++ b/src/modules/ui/IMGUIApp.cpp @@ -335,13 +335,15 @@ app::AppState IMGUIApp::onInit() { if (_persistUISettings) { const core::String iniFile = core::string::format("%s-%i-imgui.ini", _appname.c_str(), _iniVersion); - _writePathIni = _filesystem->homeWritePath(iniFile); + const core::Path &iniPath = _filesystem->homeWritePath(iniFile); + _writePathIni = iniPath.str(); io.IniFilename = _writePathIni.c_str(); } else { io.IniFilename = nullptr; } const core::String logFile = _appname + "-imgui.log"; - _writePathLog = _filesystem->homeWritePath(logFile); + const core::Path &logPath = _filesystem->homeWritePath(logFile); + _writePathLog = logPath.str(); io.LogFilename = _writePathLog.c_str(); io.DisplaySize = _windowDimension; diff --git a/src/modules/ui/PopupAbout.cpp b/src/modules/ui/PopupAbout.cpp index f50509de2c..9ef72ec3f2 100644 --- a/src/modules/ui/PopupAbout.cpp +++ b/src/modules/ui/PopupAbout.cpp @@ -87,8 +87,8 @@ void popupAbout(const std::function &customTabs, bool isNewVersionAvaila } if (ImGui::BeginTabItem(_("Paths"))) { - for (const core::String &path : io::filesystem()->registeredPaths()) { - const core::String &abspath = io::filesystem()->sysAbsolutePath(path); + for (const core::Path &path : io::filesystem()->registeredPaths()) { + const core::String &abspath = io::filesystem()->sysAbsolutePath(path.str()); if (abspath.empty()) { continue; } diff --git a/src/modules/util/KeybindingHandler.cpp b/src/modules/util/KeybindingHandler.cpp index ec259c4ed7..c80ea8eede 100644 --- a/src/modules/util/KeybindingHandler.cpp +++ b/src/modules/util/KeybindingHandler.cpp @@ -246,7 +246,7 @@ void KeyBindingHandler::shutdown(int version) { void KeyBindingHandler::removeApplicationKeyBindings(int version) { const core::String &f = filename(version); - const core::String &path = io::filesystem()->homeWritePath(f); + const core::Path &path = io::filesystem()->homeWritePath(f); io::filesystem()->sysRemoveFile(path); } diff --git a/src/modules/voxelcollection/CollectionManager.cpp b/src/modules/voxelcollection/CollectionManager.cpp index 74295b3395..2a53b6bee7 100644 --- a/src/modules/voxelcollection/CollectionManager.cpp +++ b/src/modules/voxelcollection/CollectionManager.cpp @@ -39,7 +39,7 @@ bool CollectionManager::init() { documents = _filesystem->sysSpecialDir(io::FilesystemDirectories::FS_Dir_Download); } if (documents.empty()) { - documents = _filesystem->homePath(); + documents = _filesystem->homePath().str(); } core_assert(!documents.empty()); const core::VarPtr &var = core::Var::get(cfg::AssetPanelLocalDirectory, documents); @@ -50,10 +50,10 @@ bool CollectionManager::init() { return true; } -core::String CollectionManager::absolutePath(const VoxelFile &voxelFile) const { +core::Path CollectionManager::absolutePath(const VoxelFile &voxelFile) const { // this has to match with the http cache stream if (voxelFile.isLocal()) { - return voxelFile.targetFile(); + return core::Path(voxelFile.targetFile()); } return _filesystem->homeWritePath(voxelFile.targetFile()); } @@ -134,9 +134,9 @@ bool CollectionManager::local() { continue; } VoxelFile voxelFile; - voxelFile.name = entry.fullPath.substr(localDir.size()); - voxelFile.fullPath = entry.fullPath; - voxelFile.url = "file://" + entry.fullPath; + voxelFile.name = entry.fullPath.str().substr(localDir.size()); + voxelFile.fullPath = entry.fullPath.str(); + voxelFile.url = "file://" + entry.fullPath.str(); voxelFile.source = LOCAL_SOURCE; voxelFile.license = "unknown"; // voxelFile.licenseUrl = ""; @@ -225,8 +225,8 @@ void CollectionManager::loadThumbnail(const VoxelFile &voxelFile) { bool CollectionManager::createThumbnail(const VoxelFile &voxelFile) { scenegraph::SceneGraph sceneGraph; io::FileDescription fileDesc; - const core::String &fileName = absolutePath(voxelFile); - fileDesc.set(fileName); + const core::Path &fileName = absolutePath(voxelFile); + fileDesc.set(fileName.str()); voxelformat::LoadContext loadctx; if (!voxelformat::loadFormat(fileDesc, _archive, sceneGraph, loadctx)) { Log::error("Failed to load given input file: %s", fileName.c_str()); diff --git a/src/modules/voxelcollection/CollectionManager.h b/src/modules/voxelcollection/CollectionManager.h index d6985d896f..86b63dd2fe 100644 --- a/src/modules/voxelcollection/CollectionManager.h +++ b/src/modules/voxelcollection/CollectionManager.h @@ -88,7 +88,7 @@ class CollectionManager : public core::IComponent { int downloadProgress() const; int allEntries() const; - core::String absolutePath(const VoxelFile &voxelFile) const; + core::Path absolutePath(const VoxelFile &voxelFile) const; }; inline const core::String &CollectionManager::localDir() const { diff --git a/src/modules/voxelcollection/Downloader.cpp b/src/modules/voxelcollection/Downloader.cpp index a7f85962ef..9337c21b08 100644 --- a/src/modules/voxelcollection/Downloader.cpp +++ b/src/modules/voxelcollection/Downloader.cpp @@ -146,7 +146,7 @@ bool Downloader::handleArchive(const io::ArchivePtr &archive, const VoxelFile &a subFile.license = archiveFile.license; subFile.licenseUrl = archiveFile.licenseUrl; subFile.thumbnailUrl = archiveFile.thumbnailUrl; - subFile.fullPath = core::string::path(core::string::extractDir(archiveFile.fullPath), f.fullPath); + subFile.fullPath = core::string::path(core::string::extractDir(archiveFile.fullPath), f.fullPath.str()); subFile.downloaded = true; if (supportedFileExtension(subFile.name)) { @@ -155,7 +155,7 @@ bool Downloader::handleArchive(const io::ArchivePtr &archive, const VoxelFile &a files.push_back(subFile); continue; } - core::ScopedPtr rs(zipArchive->readStream(f.fullPath)); + core::ScopedPtr rs(zipArchive->readStream(f.fullPath.str())); if (!rs) { Log::error("Failed to read file %s from archive %s", f.fullPath.c_str(), archiveFile.fullPath.c_str()); @@ -169,7 +169,7 @@ bool Downloader::handleArchive(const io::ArchivePtr &archive, const VoxelFile &a } } else if (io::isZipArchive(subFile.name)) { // save file and call handleArchive again - core::ScopedPtr rs(zipArchive->readStream(f.fullPath)); + core::ScopedPtr rs(zipArchive->readStream(f.fullPath.str())); if (!rs) { Log::error("Failed to read file %s from archive %s", f.fullPath.c_str(), archiveFile.fullPath.c_str()); diff --git a/src/modules/voxelcollection/tests/CollectionManagerTest.cpp b/src/modules/voxelcollection/tests/CollectionManagerTest.cpp index 76f412ab0b..c83ddb27d5 100644 --- a/src/modules/voxelcollection/tests/CollectionManagerTest.cpp +++ b/src/modules/voxelcollection/tests/CollectionManagerTest.cpp @@ -24,7 +24,7 @@ class CollectionManagerTest : public app::AbstractTest { // _texturePool->init(); _mgr = core::make_shared(_testApp->filesystem(), _texturePool); ASSERT_TRUE(_mgr->init()); - ASSERT_TRUE(_mgr->setLocalDir(_testApp->filesystem()->homePath())); + ASSERT_TRUE(_mgr->setLocalDir(_testApp->filesystem()->homePath().str())); } virtual void TearDown() override { diff --git a/src/modules/voxelformat/private/image/PNGFormat.cpp b/src/modules/voxelformat/private/image/PNGFormat.cpp index 5d24725472..2de84ae0fa 100644 --- a/src/modules/voxelformat/private/image/PNGFormat.cpp +++ b/src/modules/voxelformat/private/image/PNGFormat.cpp @@ -38,9 +38,9 @@ static int extractLayerFromFilename(const core::String &filename) { bool PNGFormat::importSlices(scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const io::ArchiveFiles &entities) const { - const core::String filename = entities.front().fullPath; + const core::Path filename = entities.front().fullPath; Log::debug("Use %s as reference image", filename.c_str()); - image::ImagePtr referenceImage = image::loadImage(filename); + image::ImagePtr referenceImage = image::loadImage(filename.str()); if (!referenceImage || !referenceImage->isLoaded()) { Log::error("Failed to load first image as reference %s", filename.c_str()); return false; @@ -52,8 +52,8 @@ bool PNGFormat::importSlices(scenegraph::SceneGraph &sceneGraph, const palette:: int maxsZ = -1000000; for (const auto &entity : entities) { - const core::String &layerFilename = entity.fullPath; - const int layer = extractLayerFromFilename(layerFilename); + const core::Path &layerFilename = entity.fullPath; + const int layer = extractLayerFromFilename(layerFilename.str()); minsZ = glm::min(minsZ, layer); maxsZ = glm::max(maxsZ, layer); } @@ -62,11 +62,11 @@ bool PNGFormat::importSlices(scenegraph::SceneGraph &sceneGraph, const palette:: voxel::RawVolume *volume = new voxel::RawVolume(region); scenegraph::SceneGraphNode node(scenegraph::SceneGraphNodeType::Model); node.setVolume(volume, true); - node.setName(core::string::extractFilename(filename)); + node.setName(core::string::extractFilename(filename.str())); for (const auto &entity : entities) { - const core::String &layetFilename = entity.fullPath; - const image::ImagePtr &image = image::loadImage(layetFilename); + const core::Path &layetFilename = entity.fullPath; + const image::ImagePtr &image = image::loadImage(layetFilename.str()); if (!image || !image->isLoaded()) { Log::error("Failed to load image %s", layetFilename.c_str()); return false; @@ -76,7 +76,7 @@ bool PNGFormat::importSlices(scenegraph::SceneGraph &sceneGraph, const palette:: image->width(), image->height(), imageWidth, imageHeight); return false; } - const int layer = extractLayerFromFilename(layetFilename); + const int layer = extractLayerFromFilename(layetFilename.str()); Log::debug("Import layer %i of image %s", layer, layetFilename.c_str()); for (int y = 0; y < imageHeight; ++y) { for (int x = 0; x < imageWidth; ++x) { @@ -193,7 +193,7 @@ bool PNGFormat::loadGroupsRGBA(const core::String &filename, const io::ArchivePt io::ArchiveFiles entities; archive->list(directory, entities, core::string::format("%s-*.png", basename.c_str())); if (entities.empty()) { - io::FilesystemEntry val = io::createFilesystemEntry(filename); + io::FilesystemEntry val = io::createFilesystemEntry(core::Path(filename)); entities.push_back(val); } Log::debug("Found %i images for import", (int)entities.size()); diff --git a/src/modules/voxelformat/private/mesh/MeshFormat.cpp b/src/modules/voxelformat/private/mesh/MeshFormat.cpp index 04d9f1c58e..656f161ae0 100644 --- a/src/modules/voxelformat/private/mesh/MeshFormat.cpp +++ b/src/modules/voxelformat/private/mesh/MeshFormat.cpp @@ -523,66 +523,62 @@ bool MeshFormat::voxelizeGroups(const core::String &filename, const io::ArchiveP // TODO: use io::Archive here, too core::String MeshFormat::lookupTexture(const core::String &meshFilename, const core::String &in) { - const core::String &meshPath = core::string::extractDir(meshFilename); - core::String name = in; - io::normalizePath(name); - if (!core::string::isAbsolutePath(name)) { - name = core::string::path(meshPath, name); + const core::Path meshPath(core::string::extractDir(meshFilename)); + core::Path name(in); + if (!name.isAbsolutePath()) { + name = meshPath.append(name); } - if (io::filesystem()->exists(name)) { + if (io::filesystem()->exists(name.str())) { Log::debug("Found image %s in path %s", in.c_str(), name.c_str()); - return name; + return name.str(); } if (!meshPath.empty()) { io::filesystem()->sysPushDir(meshPath); } - core::String filename = core::string::extractFilenameWithExtension(name); - const core::String &path = core::string::extractDir(name); - core::String fullpath = io::searchPathFor(io::filesystem(), path, filename); + core::Path basename = name.basename(); + const core::Path path = name.dirname(); + core::String fullpath = io::searchPathFor(io::filesystem(), path.str(), basename.str()); if (fullpath.empty() && path != meshPath) { - fullpath = io::searchPathFor(io::filesystem(), meshPath, filename); + fullpath = io::searchPathFor(io::filesystem(), meshPath.str(), basename.str()); } if (fullpath.empty()) { - fullpath = io::searchPathFor(io::filesystem(), "texture", filename); + fullpath = io::searchPathFor(io::filesystem(), "texture", basename.str()); } if (fullpath.empty()) { - fullpath = io::searchPathFor(io::filesystem(), "textures", filename); + fullpath = io::searchPathFor(io::filesystem(), "textures", basename.str()); } // if not found, loop over all supported image formats and repeat the search - if (fullpath.empty()) { - const core::String &baseFilename = core::string::extractFilename(name); - if (!baseFilename.empty()) { - for (const io::FormatDescription *desc = io::format::images(); desc->valid(); ++desc) { - for (const core::String &ext : desc->exts) { - const core::String &f = core::string::format("%s.%s", baseFilename.c_str(), ext.c_str()); - if (f == filename) { - continue; - } - fullpath = io::searchPathFor(io::filesystem(), path, f); - if (fullpath.empty() && path != meshPath) { - fullpath = io::searchPathFor(io::filesystem(), meshPath, f); - } - if (fullpath.empty()) { - fullpath = io::searchPathFor(io::filesystem(), "texture", f); - } - if (fullpath.empty()) { - fullpath = io::searchPathFor(io::filesystem(), "textures", f); - } - if (!fullpath.empty()) { - if (!meshPath.empty()) { - io::filesystem()->sysPopDir(); - } - return fullpath; + if (fullpath.empty() && !basename.empty()) { + for (const io::FormatDescription *desc = io::format::images(); desc->valid(); ++desc) { + for (const core::String &ext : desc->exts) { + const core::String &f = core::string::format("%s.%s", basename.c_str(), ext.c_str()); + if (basename == f) { + continue; + } + fullpath = io::searchPathFor(io::filesystem(), path.str(), f); + if (fullpath.empty() && path != meshPath) { + fullpath = io::searchPathFor(io::filesystem(), meshPath.str(), f); + } + if (fullpath.empty()) { + fullpath = io::searchPathFor(io::filesystem(), "texture", f); + } + if (fullpath.empty()) { + fullpath = io::searchPathFor(io::filesystem(), "textures", f); + } + if (!fullpath.empty()) { + if (!meshPath.empty()) { + io::filesystem()->sysPopDir(); } + return fullpath; } } } } if (fullpath.empty()) { - Log::error("Failed to perform texture lookup for '%s' (filename: '%s')", name.c_str(), filename.c_str()); + Log::error("Failed to perform texture lookup for '%s' (filename: '%s')", name.c_str(), basename.c_str()); } if (!meshPath.empty()) { io::filesystem()->sysPopDir(); diff --git a/src/modules/voxelformat/private/rooms/ThingFormat.cpp b/src/modules/voxelformat/private/rooms/ThingFormat.cpp index 5f154fe008..ac741eb68d 100644 --- a/src/modules/voxelformat/private/rooms/ThingFormat.cpp +++ b/src/modules/voxelformat/private/rooms/ThingFormat.cpp @@ -151,7 +151,7 @@ bool ThingFormat::loadGroups(const core::String &filename, const io::ArchivePtr io::ArchiveFiles files; zipArchive->list("*.node", files); for (const io::FilesystemEntry &file : files) { - core::ScopedPtr nodeSpecStream(zipArchive->readStream(file.fullPath)); + core::ScopedPtr nodeSpecStream(zipArchive->readStream(file.fullPath.str())); if (nodeSpecStream) { NodeSpec nodeSpec; if (!loadNodeSpec(*nodeSpecStream, nodeSpec)) { diff --git a/src/modules/voxelformat/private/sandbox/VXCArchive.h b/src/modules/voxelformat/private/sandbox/VXCArchive.h index 3199912261..d569b46fca 100644 --- a/src/modules/voxelformat/private/sandbox/VXCArchive.h +++ b/src/modules/voxelformat/private/sandbox/VXCArchive.h @@ -27,7 +27,7 @@ class VXCArchive : public io::Archive { io::FilesystemEntry entry; entry.size = fileSize; entry.name = path; - entry.fullPath = entry.name; + entry.fullPath = core::Path(entry.name); entry.type = io::FilesystemEntry::Type::file; _files.push_back(entry); } diff --git a/src/modules/voxelformat/private/sandbox/VXCFormat.cpp b/src/modules/voxelformat/private/sandbox/VXCFormat.cpp index 61771080b0..3c466b99e8 100644 --- a/src/modules/voxelformat/private/sandbox/VXCFormat.cpp +++ b/src/modules/voxelformat/private/sandbox/VXCFormat.cpp @@ -115,7 +115,7 @@ image::ImagePtr VXCFormat::loadScreenshot(const core::String &filename, const io Log::debug("Skip image %s", entry.name.c_str()); continue; } - core::ScopedPtr thumbnailStream(vxcArchive->readStream(entry.fullPath)); + core::ScopedPtr thumbnailStream(vxcArchive->readStream(entry.fullPath.str())); if (!thumbnailStream) { Log::error("Could not load file %s", entry.fullPath.c_str()); return image::ImagePtr(); diff --git a/src/modules/voxelformat/private/starmade/SMFormat.cpp b/src/modules/voxelformat/private/starmade/SMFormat.cpp index e822488696..77841a292a 100644 --- a/src/modules/voxelformat/private/starmade/SMFormat.cpp +++ b/src/modules/voxelformat/private/starmade/SMFormat.cpp @@ -111,7 +111,7 @@ bool SMFormat::loadGroupsRGBA(const core::String &filename, const io::ArchivePtr position[i] = core::string::toInt(e.name.substr(dot + 1)) * priv::segments; } } - core::ScopedPtr modelStream(zipArchive->readStream(e.fullPath)); + core::ScopedPtr modelStream(zipArchive->readStream(e.fullPath.str())); if (!modelStream) { Log::warn("Failed to load zip archive entry %s", e.fullPath.c_str()); continue; diff --git a/src/modules/voxelgenerator/LUAApi.cpp b/src/modules/voxelgenerator/LUAApi.cpp index 791878e388..a4bf0b0220 100644 --- a/src/modules/voxelgenerator/LUAApi.cpp +++ b/src/modules/voxelgenerator/LUAApi.cpp @@ -1979,13 +1979,12 @@ static bool luaVoxel_pushargs(lua_State* s, const core::DynamicArrayexists(filename)) { - if (core::string::extractExtension(filename) != "lua") { - filename.append(".lua"); + core::Path filename(scriptName); + if (!_filesystem->exists(filename.str())) { + if (filename.extension() != "lua") { + filename = filename.append(".lua"); } - filename = core::string::path("scripts", filename); + filename = core::Path(core::string::path("scripts", filename.str())); } #if LUA_VERSION_NUM < 504 core::String luaStr = _filesystem->load(filename); diff --git a/src/modules/voxelgenerator/tests/LUAApiTest.cpp b/src/modules/voxelgenerator/tests/LUAApiTest.cpp index 8a0bd0a685..9cfc2d9d51 100644 --- a/src/modules/voxelgenerator/tests/LUAApiTest.cpp +++ b/src/modules/voxelgenerator/tests/LUAApiTest.cpp @@ -29,7 +29,7 @@ class LUAApiTest : public app::AbstractTest { virtual bool onInitApp() { app::AbstractTest::onInitApp(); - return _testApp->filesystem()->registerPath("scripts/"); + return _testApp->filesystem()->registerPath(core::Path("scripts/")); } void runFile(scenegraph::SceneGraph &sceneGraph, const core::String &filename, diff --git a/src/tools/shadertool/ShaderTool.cpp b/src/tools/shadertool/ShaderTool.cpp index f7df75c851..15101bd41a 100644 --- a/src/tools/shadertool/ShaderTool.cpp +++ b/src/tools/shadertool/ShaderTool.cpp @@ -54,9 +54,9 @@ void ShaderTool::validate(const core::String &name) { if (_glslangValidatorBin.empty()) { return; } - const core::String &writePath = filesystem()->homePath(); + const core::Path &writePath = filesystem()->homePath(); core::DynamicArray args; - args.push_back(writePath + name); + args.push_back(writePath.str() + name); Log::debug("Execute glslang validator with the following commandline: %s %s", _glslangValidatorBin.c_str(), args[0].c_str()); io::BufferedReadWriteStream stream(4096); @@ -131,7 +131,7 @@ app::AppState ShaderTool::onRunning() { _constantsTemplateFile = getArgVal("--constantstemplate"); _shaderDirectory = getArgVal("--shaderdir"); _sourceDirectory = getArgVal("--sourcedir", - core::string::path(_filesystem->basePath(), "src", "modules", _namespaceSrc)); + core::string::path(_filesystem->basePath().str(), "src", "modules", _namespaceSrc)); _postfix = getArgVal("--postfix", ""); // handle include dirs @@ -161,11 +161,11 @@ app::AppState ShaderTool::onRunning() { Log::debug("Preparing shader file %s", _shaderfile.c_str()); const io::FilesystemPtr& fs = filesystem(); _shaderpath = core::string::extractDir(shaderfile.c_str()); - const bool changedDir = fs->sysPushDir(_shaderpath); + const bool changedDir = fs->sysPushDir(core::Path(_shaderpath)); video::Shader shader; - const core::String& writePath = fs->homePath(); + const core::Path& writePath = fs->homePath(); Log::debug("Writing shader file %s to %s", _shaderfile.c_str(), writePath.c_str()); const core::String& templateShaderHeader = fs->load(_headerTemplateFile); diff --git a/src/tools/voxconvert/VoxConvert.cpp b/src/tools/voxconvert/VoxConvert.cpp index a17e9aee1a..90c1abd321 100644 --- a/src/tools/voxconvert/VoxConvert.cpp +++ b/src/tools/voxconvert/VoxConvert.cpp @@ -105,7 +105,7 @@ app::AppState VoxConvert::onConstruct() { _withColor = core::Var::getSafe(cfg::VoxformatWithColor); _withTexCoords = core::Var::getSafe(cfg::VoxformatWithtexcoords); - if (!filesystem()->registerPath("scripts/")) { + if (!filesystem()->registerPath(core::Path("scripts/"))) { Log::warn("Failed to register lua generator script path"); } @@ -258,7 +258,7 @@ app::AppState VoxConvert::onInit() { const bool hasScript = hasArg("--script"); core::String infilesstr; - core::DynamicArray infiles; + core::DynamicArray infiles; bool inputIsMesh = false; if (hasArg("--input")) { int argn = 0; @@ -267,8 +267,7 @@ app::AppState VoxConvert::onInit() { if (val.empty()) { break; } - io::normalizePath(val); - infiles.push_back(val); + infiles.emplace_back(val); if (voxelformat::isMeshFormat(val, false)) { inputIsMesh = true; } @@ -283,7 +282,7 @@ app::AppState VoxConvert::onInit() { } core::String outfilesstr; - core::DynamicArray outfiles; + core::DynamicArray outfiles; bool outputIsMesh = false; if (hasArg("--output")) { int argn = 0; @@ -292,8 +291,7 @@ app::AppState VoxConvert::onInit() { if (val.empty()) { break; } - io::normalizePath(val); - outfiles.push_back(val); + outfiles.emplace_back(val); if (voxelformat::isMeshFormat(val, false)) { outputIsMesh = true; } @@ -367,7 +365,7 @@ app::AppState VoxConvert::onInit() { if (!outfiles.empty()) { if (!hasArg("--force")) { - for (const core::String &outfile : outfiles) { + for (const core::Path &outfile : outfiles) { const bool outfileExists = filesystem()->open(outfile)->exists(); if (outfileExists) { Log::error("Given output file '%s' already exists", outfile.c_str()); @@ -382,13 +380,13 @@ app::AppState VoxConvert::onInit() { const io::ArchivePtr &fsArchive = io::openFilesystemArchive(filesystem()); scenegraph::SceneGraph sceneGraph; - for (const core::String &infile : infiles) { + for (const core::Path &infile : infiles) { if (shouldQuit()) { break; } - if (filesystem()->sysIsReadableDir(infile)) { + if (filesystem()->sysIsReadableDir(infile.str())) { core::DynamicArray entities; - filesystem()->list(infile, entities, getArgVal("--wildcard", "")); + filesystem()->list(infile.str(), entities, getArgVal("--wildcard", "")); Log::info("Found %i entries in dir %s", (int)entities.size(), infile.c_str()); int success = 0; for (const io::FilesystemEntry &entry : entities) { @@ -398,7 +396,7 @@ app::AppState VoxConvert::onInit() { if (entry.type != io::FilesystemEntry::Type::file) { continue; } - const core::String fullpath = core::string::path(infile, entry.name); + const core::Path &fullpath = infile.append(entry.name); if (handleInputFile(fullpath, fsArchive, sceneGraph, entities.size() > 1)) { ++success; } @@ -407,7 +405,7 @@ app::AppState VoxConvert::onInit() { Log::error("Could not find a valid input file in directory %s", infile.c_str()); return app::AppState::InitFailure; } - } else if (io::isZipArchive(infile)) { + } else if (io::isZipArchive(infile.str())) { io::FileStream archiveStream(filesystem()->open(infile, io::FileMode::SysRead)); io::ArchivePtr archive = io::openZipArchive(&archiveStream); if (!archive) { @@ -432,7 +430,7 @@ app::AppState VoxConvert::onInit() { continue; } } - const core::String &fullPath = filesystem()->homeWritePath(entry.fullPath); + const core::Path &fullPath = filesystem()->homeWritePath(entry.fullPath.str()); if (!handleInputFile(fullPath, archive, sceneGraph, archive->files().size() > 1)) { Log::error("Failed to handle input file %s", fullPath.c_str()); } @@ -489,13 +487,13 @@ app::AppState VoxConvert::onInit() { if (infiles.size() > 1) { Log::warn("The format and path of the first input file is used for exporting all models"); } - for (const core::String &outfile : outfiles) { + for (const core::Path &outfile : outfiles) { io::FilePtr outputFile = filesystem()->open(outfile, io::FileMode::SysWrite); if (!outputFile->validHandle()) { Log::error("Could not open target file: %s", outfile.c_str()); return app::AppState::InitFailure; } - exportModelsIntoSingleObjects(sceneGraph, infiles[0], outputFile ? outputFile->extension() : ""); + exportModelsIntoSingleObjects(sceneGraph, infiles[0].str(), outputFile ? outputFile->extension() : ""); } return state; } @@ -552,8 +550,8 @@ app::AppState VoxConvert::onInit() { split(getArgIvec3("--split"), sceneGraph); } - for (const core::String &outfile : outfiles) { - if (_exportPalette || (!io::isA(outfile, voxelformat::voxelSave()) && io::isA(outfile, io::format::palettes()))) { + for (const core::Path &outfile : outfiles) { + if (_exportPalette || (!io::isA(outfile.str(), voxelformat::voxelSave()) && io::isA(outfile.str(), io::format::palettes()))) { // if the given format is a palette only format (some voxel formats might have the same // extension - so we check that here) const palette::Palette &palette = sceneGraph.mergePalettes(false); @@ -566,7 +564,7 @@ app::AppState VoxConvert::onInit() { Log::debug("Save %i models", (int)sceneGraph.size()); voxelformat::SaveContext saveCtx; const io::ArchivePtr &archive = io::openFilesystemArchive(filesystem()); - if (!voxelformat::saveFormat(sceneGraph, outfile, nullptr, archive, saveCtx)) { + if (!voxelformat::saveFormat(sceneGraph, outfile.str(), nullptr, archive, saveCtx)) { Log::error("Failed to write to output file '%s'", outfile.c_str()); return app::AppState::InitFailure; } @@ -594,10 +592,10 @@ static void printProgress(const char *name, int cur, int max) { // Log::info("%s: %i/%i", name, cur, max); } -bool VoxConvert::handleInputFile(const core::String &infile, const io::ArchivePtr &archive, +bool VoxConvert::handleInputFile(const core::Path &infile, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, bool multipleInputs) { Log::info("-- current input file: %s", infile.c_str()); - core::ScopedPtr stream(archive->readStream(infile)); + core::ScopedPtr stream(archive->readStream(infile.str())); if (!stream) { Log::error("Given input file '%s' does not exist", infile.c_str()); _exitCode = 127; @@ -607,7 +605,7 @@ bool VoxConvert::handleInputFile(const core::String &infile, const io::ArchivePt voxelformat::LoadContext loadCtx; loadCtx.monitor = printProgress; io::FileDescription fileDesc; - fileDesc.set(infile); + fileDesc.set(infile.str()); if (!voxelformat::loadFormat(fileDesc, archive, newSceneGraph, loadCtx)) { return false; } @@ -615,7 +613,8 @@ bool VoxConvert::handleInputFile(const core::String &infile, const io::ArchivePt int parent = sceneGraph.root().id(); if (multipleInputs) { scenegraph::SceneGraphNode groupNode(scenegraph::SceneGraphNodeType::Group); - groupNode.setName(core::string::extractFilename(infile)); + const core::Path &bn = infile.basename(); + groupNode.setName(bn.str()); parent = sceneGraph.emplace(core::move(groupNode), parent); } scenegraph::addSceneGraphNodes(sceneGraph, newSceneGraph, parent); diff --git a/src/tools/voxconvert/VoxConvert.h b/src/tools/voxconvert/VoxConvert.h index 87b5b5fe75..d68717ad04 100644 --- a/src/tools/voxconvert/VoxConvert.h +++ b/src/tools/voxconvert/VoxConvert.h @@ -64,7 +64,7 @@ class VoxConvert: public app::CommandlineApp { glm::ivec3 getArgIvec3(const core::String &name); core::String getFilenameForModelName(const core::String &inputfile, const core::String &modelName, const core::String &outExt, int id, bool uniqueNames); - bool handleInputFile(const core::String &infile, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, + bool handleInputFile(const core::Path &infile, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, bool multipleInputs); void usage() const override; diff --git a/src/tools/voxconvert/VoxConvertUI.cpp b/src/tools/voxconvert/VoxConvertUI.cpp index 93e81bf7cb..b9b955e2a2 100644 --- a/src/tools/voxconvert/VoxConvertUI.cpp +++ b/src/tools/voxconvert/VoxConvertUI.cpp @@ -360,7 +360,7 @@ void VoxConvertUI::onRenderUI() { if (_overwriteTargetFile) { arguments.push_back("-f"); } - const int exitCode = core::Process::exec(_voxconvertBinary, arguments, nullptr, &stream); + const int exitCode = core::Process::exec(_voxconvertBinary.str(), arguments, nullptr, &stream); stream.seek(0); stream.readString(stream.size(), _output); _output = core::string::removeAnsiColors(_output.c_str()); diff --git a/src/tools/voxconvert/VoxConvertUI.h b/src/tools/voxconvert/VoxConvertUI.h index 6c39a20b01..2c6dd94f6e 100644 --- a/src/tools/voxconvert/VoxConvertUI.h +++ b/src/tools/voxconvert/VoxConvertUI.h @@ -16,7 +16,7 @@ class VoxConvertUI : public ui::IMGUIApp { using Super = ui::IMGUIApp; core::String _output; core::String _source; - core::String _voxconvertBinary = "vengi-voxconvert"; + core::Path _voxconvertBinary {"vengi-voxconvert"}; core::String _targetFile; bool _targetFileExists = false; bool _overwriteTargetFile = false; diff --git a/src/tools/voxedit/VoxEdit.cpp b/src/tools/voxedit/VoxEdit.cpp index db5488fa60..7db42c222f 100644 --- a/src/tools/voxedit/VoxEdit.cpp +++ b/src/tools/voxedit/VoxEdit.cpp @@ -444,7 +444,7 @@ app::AppState VoxEdit::onInit() { } // needed for handling the module includes - if (!filesystem()->registerPath("scripts/")) { + if (!filesystem()->registerPath(core::Path("scripts/"))) { Log::error("Failed to register lua generator script path"); return app::AppState::InitFailure; } diff --git a/src/tools/voxedit/modules/voxedit-ui/CollectionPanel.cpp b/src/tools/voxedit/modules/voxedit-ui/CollectionPanel.cpp index e239c6d9b0..86c132c860 100644 --- a/src/tools/voxedit/modules/voxedit-ui/CollectionPanel.cpp +++ b/src/tools/voxedit/modules/voxedit-ui/CollectionPanel.cpp @@ -205,12 +205,13 @@ void CollectionPanel::contextMenu(voxelcollection::VoxelFile *voxelFile) { if (!io::isA(voxelFile->name, voxelformat::voxelLoad())) { if (ImGui::MenuItem(_("Open target file"))) { - core::String absPath = _collectionMgr->absolutePath(*voxelFile); - command::executeCommands("url \"file://" + absPath + "\""); + const core::Path &absPath = _collectionMgr->absolutePath(*voxelFile); + command::executeCommands("url \"file://" + absPath.str() + "\""); } if (ImGui::MenuItem(_("Open target dir"))) { - core::String absPath = _collectionMgr->absolutePath(*voxelFile); - command::executeCommands("url \"file://" + core::string::extractDir(absPath) + "\""); + const core::Path &absPath = _collectionMgr->absolutePath(*voxelFile); + const core::Path &dir = absPath.dirname(); + command::executeCommands("url \"file://" + dir.str() + "\""); } } else if (!thumbnailLookup(*voxelFile)) { if (ImGui::MenuItem(_("Create thumbnail"))) { diff --git a/src/tools/voxedit/modules/voxedit-util/SceneManager.cpp b/src/tools/voxedit/modules/voxedit-util/SceneManager.cpp index f64676bea4..e5376ef149 100644 --- a/src/tools/voxedit/modules/voxedit-util/SceneManager.cpp +++ b/src/tools/voxedit/modules/voxedit-util/SceneManager.cpp @@ -164,7 +164,8 @@ void SceneManager::autosave() { // autosaves go into the write path directory (which is usually the home directory of the user) io::FileDescription autoSaveFilename; if (_lastFilename.empty()) { - autoSaveFilename.set(_filesystem->homeWritePath("autosave-noname." + voxelformat::vengi().mainExtension())); + const core::Path path(_filesystem->homeWritePath("autosave-noname." + voxelformat::vengi().mainExtension())); + autoSaveFilename.set(path.str()); } else { const io::FilePtr &file = _filesystem->open(_lastFilename.name); const core::String &filename = file->fileName(); @@ -172,7 +173,8 @@ void SceneManager::autosave() { const core::String &ext = file->extension(); const core::String &autosaveFilename = core::string::format("%s%s.%s", prefix.c_str(), filename.c_str(), ext.c_str()); - autoSaveFilename.set(_filesystem->homeWritePath(autosaveFilename), &_lastFilename.desc); + const core::Path path(_filesystem->homeWritePath(autosaveFilename)); + autoSaveFilename.set(path.str(), &_lastFilename.desc); } if (save(autoSaveFilename, true)) { Log::info("Autosave file %s", autoSaveFilename.c_str());