|
|
|
/*==LICENSE==*
|
|
|
|
|
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools
|
|
|
|
Copyright (C) 2011 Cyan Worlds, Inc.
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
Additional permissions under GNU GPL version 3 section 7
|
|
|
|
|
|
|
|
If you modify this Program, or any covered work, by linking or
|
|
|
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
|
|
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
|
|
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
|
|
|
(or a modified version of those libraries),
|
|
|
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
|
|
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
|
|
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
|
|
|
licensors of this Program grant you additional
|
|
|
|
permission to convey the resulting work. Corresponding Source for a
|
|
|
|
non-source form of such a combination shall include the source code for
|
|
|
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
|
|
|
work.
|
|
|
|
|
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
|
|
|
or by snail mail at:
|
|
|
|
Cyan Worlds, Inc.
|
|
|
|
14617 N Newport Hwy
|
|
|
|
Mead, WA 99021
|
|
|
|
|
|
|
|
*==LICENSE==*/
|
|
|
|
|
|
|
|
#include "HeadSpin.h"
|
|
|
|
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
# include "hsWindows.h"
|
|
|
|
# include <shlobj.h>
|
|
|
|
#else
|
|
|
|
# include <limits.h>
|
|
|
|
# include <unistd.h>
|
|
|
|
# include <sys/types.h>
|
|
|
|
# include <dirent.h>
|
|
|
|
# include <fnmatch.h>
|
|
|
|
# include <cstdlib>
|
|
|
|
# include <functional>
|
|
|
|
# include <memory>
|
|
|
|
#endif
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#pragma hdrstop
|
|
|
|
|
|
|
|
#include "plFileSystem.h"
|
|
|
|
#include "plProduct.h"
|
|
|
|
|
|
|
|
/* NOTE For this file: Windows uses UTF-16 filenames, and does not support
|
|
|
|
* the use of UTF-8 in their ANSI API. In order to ensure proper unicode
|
|
|
|
* support, we convert the UTF-8 format stored in plString to UTF-16 before
|
|
|
|
* passing them along to Windows.
|
|
|
|
*/
|
|
|
|
|
|
|
|
plString plFileName::GetFileName() const
|
|
|
|
{
|
|
|
|
int end = fName.FindLast('/');
|
|
|
|
if (end < 0)
|
|
|
|
end = fName.FindLast('\\');
|
|
|
|
if (end < 0)
|
|
|
|
return fName;
|
|
|
|
|
|
|
|
return fName.Substr(end + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
plString plFileName::GetFileExt() const
|
|
|
|
{
|
|
|
|
int dot = fName.FindLast('.');
|
|
|
|
|
|
|
|
// Be sure not to get a dot in the directory!
|
|
|
|
int end = fName.FindLast('/');
|
|
|
|
if (end < 0)
|
|
|
|
end = fName.FindLast('\\');
|
|
|
|
|
|
|
|
if (dot > end)
|
|
|
|
return fName.Substr(dot + 1);
|
|
|
|
|
|
|
|
return plString::Null;
|
|
|
|
}
|
|
|
|
|
|
|
|
plString plFileName::GetFileNameNoExt() const
|
|
|
|
{
|
|
|
|
int dot = fName.FindLast('.');
|
|
|
|
|
|
|
|
int end = fName.FindLast('/');
|
|
|
|
if (end < 0)
|
|
|
|
end = fName.FindLast('\\');
|
|
|
|
|
|
|
|
// Be sure not to get a dot in the directory!
|
|
|
|
if (dot > end)
|
|
|
|
return fName.Substr(end + 1, dot - end - 1);
|
|
|
|
return fName.Substr(end + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
plFileName plFileName::StripFileName() const
|
|
|
|
{
|
|
|
|
int end = fName.FindLast('/');
|
|
|
|
if (end < 0)
|
|
|
|
end = fName.FindLast('\\');
|
|
|
|
if (end < 0)
|
|
|
|
return "";
|
|
|
|
|
|
|
|
return fName.Left(end);
|
|
|
|
}
|
|
|
|
|
|
|
|
plFileName plFileName::StripFileExt() const
|
|
|
|
{
|
|
|
|
int dot = fName.FindLast('.');
|
|
|
|
|
|
|
|
// Be sure not to get a dot in the directory!
|
|
|
|
int end = fName.FindLast('/');
|
|
|
|
if (end < 0)
|
|
|
|
end = fName.FindLast('\\');
|
|
|
|
|
|
|
|
if (dot > end)
|
|
|
|
return fName.Left(dot);
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
plFileName plFileName::Normalize(char slash) const
|
|
|
|
{
|
|
|
|
plStringBuffer<char> norm;
|
|
|
|
char *norm_p = norm.CreateWritableBuffer(fName.GetSize());
|
|
|
|
for (const char *p = fName.c_str(); *p; ++p) {
|
|
|
|
if (*p == '/' || *p == '\\')
|
|
|
|
*norm_p++ = slash;
|
|
|
|
else
|
|
|
|
*norm_p++ = *p;
|
|
|
|
}
|
|
|
|
*norm_p = 0;
|
|
|
|
return plString(norm);
|
|
|
|
}
|
|
|
|
|
|
|
|
plFileName plFileName::AbsolutePath() const
|
|
|
|
{
|
|
|
|
if (!IsValid())
|
|
|
|
return *this;
|
|
|
|
|
|
|
|
plFileName path = Normalize();
|
|
|
|
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
plStringBuffer<wchar_t> wideName = path.fName.ToWchar();
|
|
|
|
wchar_t path_sm[MAX_PATH];
|
|
|
|
uint32_t path_length = GetFullPathNameW(wideName, MAX_PATH, path_sm, nullptr);
|
|
|
|
if (path_length >= MAX_PATH) {
|
|
|
|
// Buffer not big enough
|
|
|
|
wchar_t *path_lg = new wchar_t[path_length];
|
|
|
|
GetFullPathNameW(wideName, path_length, path_lg, nullptr);
|
|
|
|
path = plString::FromWchar(path_lg);
|
|
|
|
delete [] path_lg;
|
|
|
|
} else {
|
|
|
|
path = plString::FromWchar(path_sm);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
char *path_a = realpath(path.fName.c_str(), nullptr);
|
|
|
|
hsAssert(path_a, "Failure to get absolute path (unsupported libc?)");
|
|
|
|
path = path_a;
|
|
|
|
free(path_a);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
|
|
|
plFileName plFileName::Join(const plFileName &base, const plFileName &path)
|
|
|
|
{
|
|
|
|
if (!base.IsValid())
|
|
|
|
return path;
|
|
|
|
if (!path.IsValid())
|
|
|
|
return base;
|
|
|
|
|
|
|
|
char last = base.fName.CharAt(base.GetSize() - 1);
|
|
|
|
char first = path.fName.CharAt(0);
|
|
|
|
if (last != '/' && last != '\\') {
|
|
|
|
if (first != '/' && first != '\\')
|
|
|
|
return plFormat("{}" PATH_SEPARATOR_STR "{}", base, path);
|
|
|
|
return base.fName + path.fName;
|
|
|
|
} else if (first != '/' && first != '\\') {
|
|
|
|
return base.fName + path.fName;
|
|
|
|
}
|
|
|
|
// Both have a slash, but we only need one
|
|
|
|
return base.fName + path.fName.Substr(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
PL_FORMAT_IMPL(const plFileName &)
|
|
|
|
{
|
|
|
|
return PL_FORMAT_FORWARD(format, value.AsString());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* plFileInfo */
|
|
|
|
plFileInfo::plFileInfo(const plFileName &filename)
|
|
|
|
: fFileSize(-1), fCreateTime(), fModifyTime(), fFlags()
|
|
|
|
{
|
|
|
|
if (!filename.IsValid())
|
|
|
|
return;
|
|
|
|
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
struct __stat64 info;
|
|
|
|
if (_wstat64(filename.AsString().ToWchar(), &info) != 0)
|
|
|
|
return;
|
|
|
|
#else
|
|
|
|
struct stat info;
|
|
|
|
if (stat(filename.AsString().c_str(), &info) != 0)
|
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
fFlags |= kEntryExists;
|
|
|
|
fFileSize = info.st_size;
|
|
|
|
fCreateTime = info.st_ctime;
|
|
|
|
fModifyTime = info.st_mtime;
|
|
|
|
if (info.st_mode & S_IFDIR)
|
|
|
|
fFlags |= kIsDirectory;
|
|
|
|
if (info.st_mode & S_IFREG)
|
|
|
|
fFlags |= kIsNormalFile;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* plFileSystem */
|
|
|
|
plFileName plFileSystem::GetCWD()
|
|
|
|
{
|
|
|
|
plFileName cwd;
|
|
|
|
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
wchar_t cwd_sm[MAX_PATH];
|
|
|
|
uint32_t cwd_length = GetCurrentDirectoryW(MAX_PATH, cwd_sm);
|
|
|
|
if (cwd_length >= MAX_PATH) {
|
|
|
|
// Buffer not big enough
|
|
|
|
wchar_t *cwd_lg = new wchar_t[cwd_length];
|
|
|
|
GetCurrentDirectoryW(cwd_length, cwd_lg);
|
|
|
|
cwd = plString::FromWchar(cwd_lg);
|
|
|
|
delete [] cwd_lg;
|
|
|
|
} else {
|
|
|
|
cwd = plString::FromWchar(cwd_sm);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
char *cwd_a = getcwd(nullptr, 0);
|
|
|
|
hsAssert(cwd_a, "Failure to get working directory (unsupported libc?)");
|
|
|
|
cwd = cwd_a;
|
|
|
|
free(cwd_a);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return cwd;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plFileSystem::SetCWD(const plFileName &cwd)
|
|
|
|
{
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
return SetCurrentDirectoryW(cwd.AsString().ToWchar());
|
|
|
|
#else
|
|
|
|
return (chdir(cwd.AsString().c_str()) == 0);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
FILE *plFileSystem::Open(const plFileName &filename, const char *mode)
|
|
|
|
{
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
wchar_t wmode[8];
|
|
|
|
size_t mlen = strlen(mode);
|
|
|
|
hsAssert(mlen < arrsize(wmode), "Mode string too long");
|
|
|
|
|
|
|
|
// Quick and dirty, because mode should only ever be ANSI chars
|
|
|
|
for (size_t i = 0; i < mlen; ++i) {
|
|
|
|
hsAssert(!(mode[i] & 0x80), "I SAID mode should ONLY ever be ANSI chars!");
|
|
|
|
wmode[i] = static_cast<wchar_t>(mode[i]);
|
|
|
|
}
|
|
|
|
wmode[mlen] = 0;
|
|
|
|
|
|
|
|
return _wfopen(filename.AsString().ToWchar(), wmode);
|
|
|
|
#else
|
|
|
|
return fopen(filename.AsString().c_str(), mode);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plFileSystem::Unlink(const plFileName &filename)
|
|
|
|
{
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
plStringBuffer<wchar_t> wfilename = filename.AsString().ToWchar();
|
|
|
|
_wchmod(wfilename, S_IWRITE);
|
|
|
|
return _wunlink(wfilename) == 0;
|
|
|
|
#else
|
|
|
|
chmod(filename.AsString().c_str(), S_IWRITE);
|
|
|
|
return unlink(filename.AsString().c_str()) == 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plFileSystem::Move(const plFileName &from, const plFileName &to)
|
|
|
|
{
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
return MoveFileExW(from.AsString().ToWchar(), to.AsString().ToWchar(),
|
|
|
|
MOVEFILE_REPLACE_EXISTING);
|
|
|
|
#else
|
|
|
|
if (!Copy(from, to))
|
|
|
|
return false;
|
|
|
|
return Unlink(from);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plFileSystem::Copy(const plFileName &from, const plFileName &to)
|
|
|
|
{
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
return CopyFileW(from.AsString().ToWchar(), to.AsString().ToWchar(), FALSE);
|
|
|
|
#else
|
|
|
|
typedef std::unique_ptr<FILE, std::function<int (FILE *)>> _FileRef;
|
|
|
|
|
|
|
|
_FileRef ffrom(Open(from, "rb"), fclose);
|
|
|
|
_FileRef fto(Open(to, "wb"), fclose);
|
|
|
|
if (!ffrom.get() || !fto.get())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
size_t count;
|
|
|
|
uint8_t buffer[4096];
|
|
|
|
while (!feof(ffrom.get())) {
|
|
|
|
count = fread(buffer, sizeof(uint8_t), arrsize(buffer), ffrom.get());
|
|
|
|
if (ferror(ffrom.get()))
|
|
|
|
return false;
|
|
|
|
fwrite(buffer, sizeof(uint8_t), count, fto.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plFileSystem::CreateDir(const plFileName &dir, bool checkParents)
|
|
|
|
{
|
|
|
|
plFileName fdir = dir;
|
|
|
|
if (fdir.GetFileName().IsEmpty()) {
|
|
|
|
hsDebugMessage("WARNING: CreateDir called with useless trailing slash", 0);
|
|
|
|
fdir = fdir.StripFileName();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (checkParents) {
|
|
|
|
plFileName parent = fdir.StripFileName();
|
|
|
|
if (parent.IsValid() && !plFileInfo(parent).Exists() && !CreateDir(parent, true))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (plFileInfo(fdir).Exists())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
return CreateDirectoryW(fdir.AsString().ToWchar(), nullptr);
|
|
|
|
#else
|
|
|
|
return (mkdir(fdir.AsString().c_str(), 0755) == 0);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<plFileName> plFileSystem::ListDir(const plFileName &path, const char *pattern)
|
|
|
|
{
|
|
|
|
std::vector<plFileName> contents;
|
|
|
|
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
if (!pattern || !pattern[0])
|
|
|
|
pattern = "*";
|
|
|
|
plFileName searchPattern = plFileName::Join(path, pattern);
|
|
|
|
|
|
|
|
WIN32_FIND_DATAW findData;
|
|
|
|
HANDLE hFind = FindFirstFileW(searchPattern.AsString().ToWchar(), &findData);
|
|
|
|
if (hFind == INVALID_HANDLE_VALUE)
|
|
|
|
return contents;
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
|
|
// Should also handle . and ..
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
contents.push_back(plFileName::Join(path, plString::FromWchar(findData.cFileName)));
|
|
|
|
} while (FindNextFileW(hFind, &findData));
|
|
|
|
|
|
|
|
FindClose(hFind);
|
|
|
|
#else
|
|
|
|
DIR *dir = opendir(path.AsString().c_str());
|
|
|
|
if (!dir)
|
|
|
|
return contents;
|
|
|
|
|
|
|
|
struct dirent *de;
|
|
|
|
while (de = readdir(dir)) {
|
|
|
|
plFileName dir_name = plFileName::Join(path, de->d_name);
|
|
|
|
if (plFileInfo(dir_name).IsDirectory()) {
|
|
|
|
// Should also handle . and ..
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pattern && pattern[0] && fnmatch(pattern, de->d_name, 0) == 0)
|
|
|
|
contents.push_back(dir_name);
|
|
|
|
else if (!pattern || !pattern[0])
|
|
|
|
contents.push_back(dir_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
closedir(dir);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return contents;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<plFileName> plFileSystem::ListSubdirs(const plFileName &path)
|
|
|
|
{
|
|
|
|
std::vector<plFileName> contents;
|
|
|
|
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
plFileName searchPattern = plFileName::Join(path, "*");
|
|
|
|
|
|
|
|
WIN32_FIND_DATAW findData;
|
|
|
|
HANDLE hFind = FindFirstFileW(searchPattern.AsString().ToWchar(), &findData);
|
|
|
|
if (hFind == INVALID_HANDLE_VALUE)
|
|
|
|
return contents;
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
|
|
plFileName name = plString::FromWchar(findData.cFileName);
|
|
|
|
if (name != "." && name != "..")
|
|
|
|
contents.push_back(plFileName::Join(path, name));
|
|
|
|
}
|
|
|
|
} while (FindNextFileW(hFind, &findData));
|
|
|
|
|
|
|
|
FindClose(hFind);
|
|
|
|
#else
|
|
|
|
DIR *dir = opendir(path.AsString().c_str());
|
|
|
|
if (!dir)
|
|
|
|
return contents;
|
|
|
|
|
|
|
|
struct dirent *de;
|
|
|
|
while (de = readdir(dir)) {
|
|
|
|
if (plFileInfo(de->d_name).IsDirectory()) {
|
|
|
|
plFileName name = de->d_name;
|
|
|
|
if (name != "." && name != "..")
|
|
|
|
contents.push_back(plFileName::Join(path, name));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
closedir(dir);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return contents;
|
|
|
|
}
|
|
|
|
|
|
|
|
plFileName plFileSystem::GetUserDataPath()
|
|
|
|
{
|
|
|
|
static plFileName _userData;
|
|
|
|
|
|
|
|
if (!_userData.IsValid()) {
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
wchar_t path[MAX_PATH];
|
|
|
|
if (!SHGetSpecialFolderPathW(NULL, path, CSIDL_LOCAL_APPDATA, TRUE))
|
|
|
|
return "";
|
|
|
|
|
|
|
|
_userData = plFileName::Join(plString::FromWchar(path), plProduct::LongName());
|
|
|
|
#else
|
|
|
|
_userData = plFileName::Join(getenv("HOME"), "." + plProduct::LongName());
|
|
|
|
#endif
|
|
|
|
plFileSystem::CreateDir(_userData);
|
|
|
|
}
|
|
|
|
|
|
|
|
return _userData;
|
|
|
|
}
|
|
|
|
|
|
|
|
plFileName plFileSystem::GetInitPath()
|
|
|
|
{
|
|
|
|
static plFileName _initPath = plFileName::Join(GetUserDataPath(), "Init");
|
|
|
|
plFileSystem::CreateDir(_initPath);
|
|
|
|
return _initPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
plFileName plFileSystem::GetLogPath()
|
|
|
|
{
|
|
|
|
static plFileName _logPath = plFileName::Join(GetUserDataPath(), "Log");
|
|
|
|
plFileSystem::CreateDir(_logPath);
|
|
|
|
return _logPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if !HS_BUILD_FOR_WIN32
|
|
|
|
static plFileName _CheckReadlink(const char *link_path)
|
|
|
|
{
|
|
|
|
plFileInfo info(link_path);
|
|
|
|
if (info.Exists()) {
|
|
|
|
char *path = new char[info.FileSize()];
|
|
|
|
readlink(link_path, path, info.FileSize());
|
|
|
|
plFileName appPath = plString::FromUtf8(path, info.FileSize());
|
|
|
|
delete [] path;
|
|
|
|
return appPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
plFileName plFileSystem::GetCurrentAppPath()
|
|
|
|
{
|
|
|
|
plFileName appPath;
|
|
|
|
|
|
|
|
// Neither OS makes this one simple...
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
wchar_t path[MAX_PATH];
|
|
|
|
size_t size = GetModuleFileNameW(nullptr, path, MAX_PATH);
|
|
|
|
if (size >= MAX_PATH) {
|
|
|
|
// Buffer not big enough
|
|
|
|
size_t bigger = MAX_PATH;
|
|
|
|
do {
|
|
|
|
bigger *= 2;
|
|
|
|
wchar_t *path_lg = new wchar_t[bigger];
|
|
|
|
size = GetModuleFileNameW(nullptr, path_lg, bigger);
|
|
|
|
if (size < bigger)
|
|
|
|
appPath = plString::FromWchar(path_lg);
|
|
|
|
delete [] path_lg;
|
|
|
|
} while (!appPath.IsValid());
|
|
|
|
} else {
|
|
|
|
appPath = plString::FromWchar(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
return appPath;
|
|
|
|
#else
|
|
|
|
// Look for /proc/self/exe (Linux), /proc/curproc/file (FreeBSD / Mac),
|
|
|
|
// then /proc/self/path/a.out (Solaris). If none were found, you're SOL
|
|
|
|
appPath = _CheckReadlink("/proc/self/exe");
|
|
|
|
if (appPath.IsValid())
|
|
|
|
return appPath;
|
|
|
|
|
|
|
|
appPath = _CheckReadlink("/proc/curproc/file");
|
|
|
|
if (appPath.IsValid())
|
|
|
|
return appPath;
|
|
|
|
|
|
|
|
appPath = _CheckReadlink("/proc/self/path/a.out");
|
|
|
|
if (appPath.IsValid())
|
|
|
|
return appPath;
|
|
|
|
|
|
|
|
hsAssert(0, "Your OS doesn't make life easy, does it?");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
plString plFileSystem::ConvertFileSize(uint64_t size)
|
|
|
|
{
|
|
|
|
const char* labels[] = { "KiB", "MiB", "GiB", "TiB", "PiB", "EiB" };
|
|
|
|
if (size < 1024)
|
|
|
|
return plFormat("{} B", size);
|
|
|
|
|
|
|
|
uint64_t last_div = size;
|
|
|
|
for (size_t i = 0; i < arrsize(labels); ++i) {
|
|
|
|
uint64_t my_div = last_div / 1024;
|
|
|
|
if (my_div < 1024) {
|
|
|
|
float decimal = static_cast<float>(last_div) / 1024.f;
|
|
|
|
// Kilobytes are so small that we only care about whole numbers
|
|
|
|
if (i < 1)
|
|
|
|
return plFormat("{.0f} {}", decimal, labels[i]);
|
|
|
|
else
|
|
|
|
return plFormat("{.2f} {}", decimal, labels[i]);
|
|
|
|
}
|
|
|
|
last_div = my_div;
|
|
|
|
}
|
|
|
|
|
|
|
|
// this should never happen
|
|
|
|
return plFormat("{} {}", last_div, labels[arrsize(labels) - 1]);
|
|
|
|
}
|