1205 lines
25 KiB
C++
1205 lines
25 KiB
C++
#include "io/dl_file.h"
|
||
|
||
#include <filesystem>
|
||
#include <regex>
|
||
#include <sstream>
|
||
|
||
#if DL_PLATFORM == DL_PLATFORM_WIN32
|
||
#include <Shlobj_core.h>
|
||
#endif
|
||
|
||
#include "io/dl_log.h"
|
||
#include "math/dl_algorithm.h"
|
||
#include "memory/dl_code_cvt.h"
|
||
#include "memory/dl_stream_input.h"
|
||
#include "memory/dl_stream_output.h"
|
||
#include "base/dl_string.h"
|
||
#include "system/dl_android.h"
|
||
#include "memory/dl_crypto.h"
|
||
#include "system/dl_engine.h"
|
||
#include "io/dl_json.h"
|
||
|
||
namespace dl::File
|
||
{
|
||
#if (DL_PLATFORM == DL_PLATFORM_ANDROID)
|
||
std::string_view CastU8StringView(std::string_view str)
|
||
{
|
||
return { str.data(), str.size() };
|
||
}
|
||
std::string ToString(std::u8string_view str)
|
||
{
|
||
return { (char*)str.data(), str.size() };
|
||
}
|
||
#else
|
||
std::u8string_view CastU8StringView(std::string_view str)
|
||
{
|
||
return { (char8_t*)str.data(), str.size() };
|
||
}
|
||
std::string ToString(std::u8string_view str)
|
||
{
|
||
return { (char*)str.data(), str.size() };
|
||
}
|
||
#endif
|
||
|
||
std::string_view CastStringView(std::u8string_view str)
|
||
{
|
||
return { (char*)str.data(), str.size() };
|
||
}
|
||
|
||
|
||
Buffer GetBuffer(std::ifstream& ifs)
|
||
{
|
||
Buffer ret;
|
||
|
||
ifs.seekg(0, std::ios::end);
|
||
ret.resize((size_t)ifs.tellg());
|
||
ifs.seekg(0, std::ios::beg);
|
||
|
||
|
||
ifs.read((char*)ret.data(), ret.size());
|
||
ifs.seekg(0, std::ios::beg);
|
||
return ret;
|
||
}
|
||
|
||
|
||
Buffer GetBuffer(std::string_view path_name)
|
||
{
|
||
std::ifstream ifs = Get(path_name, true);
|
||
if (!ifs.is_open())
|
||
{
|
||
log_err("文件打开失败: {}", path_name );
|
||
return {};
|
||
}
|
||
return GetBuffer(ifs);
|
||
}
|
||
//
|
||
//
|
||
//bool File::HashBlake2b(string_view path_name, Crypto::Blake2b& blake2b)
|
||
//{
|
||
// Buffer buf = GetBuffer(path_name);
|
||
// if (buf.Empty())
|
||
// return false;
|
||
//
|
||
// blake2b = Crypto::HashBlake2b(buf);
|
||
// buf.Delete();
|
||
//
|
||
// return true;
|
||
//}
|
||
//
|
||
//
|
||
//bool File::HashBlake2bCheck(string_view path_name, const Crypto::Blake2b& digest)
|
||
//{
|
||
// Buffer buf = GetBuffer(path_name);
|
||
// if (buf.Empty())
|
||
// return false;
|
||
//
|
||
// bool ret = Crypto::HashBlake2bCheck(buf, digest);
|
||
// buf.Delete();
|
||
//
|
||
// return ret;
|
||
//}
|
||
//
|
||
//
|
||
//uint32_t File::GetCrc32(string_view path_name)
|
||
//{
|
||
// Buffer buf = GetBuffer(path_name);
|
||
// if (buf.Empty())
|
||
// return 0;
|
||
//
|
||
// uint32_t ret = Crypto::HashCrc32(buf);
|
||
// buf.Delete();
|
||
// return ret;
|
||
//}
|
||
|
||
|
||
// 目录迭代器
|
||
inline std::filesystem::directory_iterator MakeDirectoryIterator(std::string_view path)
|
||
{
|
||
#if DL_PLATFORM == DL_PLATFORM_ANDROID
|
||
std::string str{path};
|
||
if(str.empty() || str[0] != '/')
|
||
{
|
||
str = PathCurrent() + str;
|
||
}
|
||
return std::filesystem::directory_iterator{ str };
|
||
#else
|
||
return std::filesystem::directory_iterator{ CastU8StringView(path) };
|
||
#endif
|
||
}
|
||
|
||
|
||
// 生成路径
|
||
inline std::filesystem::path MakePath(std::string_view path)
|
||
{
|
||
#if DL_PLATFORM == DL_PLATFORM_ANDROID
|
||
std::string str{path};
|
||
if(str.empty() || str[0] != '/')
|
||
{
|
||
str = PathCurrent() + str;
|
||
}
|
||
return std::filesystem::path{ str };
|
||
#else
|
||
return std::filesystem::path{ CastU8StringView(path) };
|
||
#endif
|
||
}
|
||
// 还原至安卓的相对路径
|
||
inline void RevertPath(std::string& path)
|
||
{
|
||
#if DL_PLATFORM == DL_PLATFORM_ANDROID
|
||
if(!Erase::HeadSub(path, PathCurrent()))
|
||
log_err("RevertPath 失败!");
|
||
#endif
|
||
}
|
||
|
||
|
||
void GetPathFileAll(std::string_view path, std::vector<std::string>& ret,
|
||
std::string_view regex, bool recursion)
|
||
{
|
||
using directory_iterator = std::filesystem::directory_iterator;
|
||
std::list<directory_iterator> vec_path;
|
||
|
||
try
|
||
{
|
||
directory_iterator path_iter = MakeDirectoryIterator(path);
|
||
vec_path.push_back(path_iter);
|
||
}
|
||
catch (std::filesystem::filesystem_error& e)
|
||
{
|
||
log_err0(e);
|
||
return;
|
||
}
|
||
|
||
bool no_reg = true;
|
||
std::regex reg;
|
||
if (!regex.empty())
|
||
{
|
||
try
|
||
{
|
||
reg = std::regex{ std::string{ regex }, std::regex::icase };
|
||
}
|
||
catch (std::regex_error& e)
|
||
{
|
||
log_err0(e);
|
||
return;
|
||
}
|
||
no_reg = false;
|
||
}
|
||
|
||
operate_front(vec_path, [&](const directory_iterator& iter_path)
|
||
{
|
||
for (auto& fe : iter_path)
|
||
{
|
||
if (fe.status().type() == std::filesystem::file_type::directory)
|
||
{
|
||
if(recursion)
|
||
vec_path.push_back(directory_iterator{ fe });
|
||
}
|
||
else
|
||
{
|
||
std::string path_name = fe.path().string();
|
||
String::CvtPathSeparator(path_name);
|
||
#if DL_PLATFORM == DL_PLATFORM_WIN32
|
||
path_name = dl::CodeCvt::MultiByteToUtf8(path_name.c_str());
|
||
#endif
|
||
|
||
if (no_reg || std::regex_search(path_name, reg))
|
||
{
|
||
RevertPath(path_name);
|
||
ret.push_back(path_name);
|
||
}
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
void GetPathFileAll(std::string_view path, std::vector<std::string>& ret, const SearchRule& rule, bool recursion)
|
||
{
|
||
using directory_iterator = std::filesystem::directory_iterator;
|
||
std::list<directory_iterator> vec_path;
|
||
|
||
try
|
||
{
|
||
directory_iterator path_iter = MakeDirectoryIterator(path);
|
||
vec_path.push_back(path_iter);
|
||
}
|
||
catch (std::filesystem::filesystem_error& e)
|
||
{
|
||
log_err0(e);
|
||
return;
|
||
}
|
||
|
||
operate_front(vec_path, [&](const directory_iterator& iter_path)
|
||
{
|
||
for (auto& fe : iter_path)
|
||
{
|
||
if (fe.status().type() == std::filesystem::file_type::directory)
|
||
{
|
||
if (recursion)
|
||
vec_path.push_back(directory_iterator{ fe });
|
||
}
|
||
else
|
||
{
|
||
std::string path_name = fe.path().string();
|
||
String::CvtPathSeparator(path_name);
|
||
#if DL_PLATFORM == DL_PLATFORM_WIN32
|
||
path_name = dl::CodeCvt::MultiByteToUtf8(path_name.c_str());
|
||
#endif
|
||
bool match = false;
|
||
for (auto& iter : rule._vecFmt)
|
||
{
|
||
if (Sub::IsTail(path_name, iter))
|
||
{
|
||
match = true;
|
||
break;
|
||
}
|
||
}
|
||
if (match)
|
||
{
|
||
for (auto& iter : rule._vecFilter)
|
||
{
|
||
if (Sub::IsTail(path_name, iter))
|
||
{
|
||
match = false;
|
||
break;
|
||
}
|
||
}
|
||
if (match)
|
||
{
|
||
RevertPath(path_name);
|
||
ret.push_back(path_name);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
void GetPathContent(std::string_view path, std::vector<std::string>& ret,
|
||
std::string_view regex, bool folder, bool file)
|
||
{
|
||
using directory_iterator = std::filesystem::directory_iterator;
|
||
directory_iterator path_iter;
|
||
|
||
try
|
||
{
|
||
path_iter = MakeDirectoryIterator(path);
|
||
}
|
||
catch (std::filesystem::filesystem_error& e)
|
||
{
|
||
log_err0(e);
|
||
return;
|
||
}
|
||
|
||
bool no_reg = true;
|
||
std::regex reg;
|
||
if (!regex.empty())
|
||
{
|
||
reg = std::string{ regex };
|
||
no_reg = false;
|
||
}
|
||
|
||
for (auto& fe : path_iter)
|
||
{
|
||
if (fe.status().type() == std::filesystem::file_type::directory)
|
||
{
|
||
if (folder)
|
||
{
|
||
std::string path = fe.path().string();
|
||
String::CvtPathSeparator(path);
|
||
RevertPath(path);
|
||
#if DL_PLATFORM == DL_PLATFORM_WIN32
|
||
path = dl::CodeCvt::MultiByteToUtf8(path.c_str());
|
||
#endif
|
||
ret.push_back(path + "/");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (file)
|
||
{
|
||
std::string path_name = fe.path().string();
|
||
String::CvtPathSeparator(path_name);
|
||
RevertPath(path_name);
|
||
#if DL_PLATFORM == DL_PLATFORM_WIN32
|
||
path_name = dl::CodeCvt::MultiByteToUtf8(path_name.c_str());
|
||
#endif
|
||
if (no_reg || std::regex_search(path_name, reg))
|
||
ret.push_back(path_name);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
FILE* GetFile(std::string_view path_name, bool is_binary /*= true*/)
|
||
{
|
||
#if DL_PLATFORM == DL_PLATFORM_ANDROID
|
||
std::string str{ path_name };
|
||
if (str.empty() || str[0] != '/')
|
||
{
|
||
str = PathCurrent() + str;
|
||
}
|
||
#else
|
||
std::string str{ path_name };
|
||
#if DL_PLATFORM == DL_PLATFORM_WIN32
|
||
str = CodeCvt::Utf8ToMultiByte(str);
|
||
#endif
|
||
#endif
|
||
FILE* ret = fopen(str.c_str(), is_binary ? "rb" : "r");
|
||
return ret;
|
||
|
||
}
|
||
|
||
|
||
std::ifstream Get(std::string_view path_name, bool is_binary /*= true*/)
|
||
{
|
||
if(path_name.empty())
|
||
return {};
|
||
|
||
try
|
||
{
|
||
std::ifstream ret;
|
||
std::filesystem::path p = MakePath(path_name);
|
||
ret.open(p, is_binary ? (std::ifstream::binary | std::ifstream::in) : std::ifstream::in);
|
||
return ret;
|
||
}
|
||
catch (std::filesystem::filesystem_error& e)
|
||
{
|
||
log_err("{}", e.what());
|
||
}
|
||
catch (...)
|
||
{
|
||
log_err("未知异常!");
|
||
}
|
||
return {};
|
||
}
|
||
|
||
|
||
std::ofstream Set(std::string_view path_name,
|
||
bool is_binary, bool is_app)
|
||
{
|
||
try
|
||
{
|
||
std::ofstream ret;
|
||
std::filesystem::path p = MakePath(path_name);
|
||
std::ofstream::openmode mode = std::ofstream::out;
|
||
if (is_binary)
|
||
mode |= std::ofstream::binary;
|
||
if (is_app)
|
||
mode |= std::ofstream::app;
|
||
else
|
||
mode |= std::ofstream::trunc;
|
||
|
||
ret.open(p, mode);
|
||
return ret;
|
||
}
|
||
catch (std::filesystem::filesystem_error& e)
|
||
{
|
||
log_err("{}", e.what());
|
||
}
|
||
catch (...)
|
||
{
|
||
log_err("未知异常!");
|
||
}
|
||
return {};
|
||
}
|
||
|
||
bool ConvertMb2U8(std::string_view path_name, std::string_view out)
|
||
{
|
||
std::ifstream ifs = Get(path_name, false);
|
||
if (!ifs.is_open())
|
||
{
|
||
log_err("转换文件编码,文件加载失败:{}", path_name);
|
||
return false;
|
||
}
|
||
std::string str = GetString(path_name);
|
||
if (str.empty())
|
||
{
|
||
log_err("文件不存在,或为空:{}", path_name);
|
||
return false;
|
||
}
|
||
|
||
std::string str_u8 = CodeCvt::MultiByteToUtf8(str);
|
||
|
||
if (!SaveString(str_u8, out))
|
||
{
|
||
log_err("文件保存失败:{}", out );
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
time_t GetLastWriteTime(std::string_view path_name)
|
||
{
|
||
try
|
||
{
|
||
std::filesystem::path p = MakePath(path_name);
|
||
std::filesystem::file_time_type t = last_write_time(p);
|
||
std::chrono::file_clock::time_point tp = std::filesystem::file_time_type::clock::time_point(t);
|
||
std::chrono::nanoseconds dt = tp.time_since_epoch();
|
||
return (time_t)duration_cast<std::chrono::seconds>(dt).count();
|
||
}
|
||
catch (std::filesystem::filesystem_error& e)
|
||
{
|
||
log_err("{}", e.what());
|
||
}
|
||
catch (...)
|
||
{
|
||
log_err("未知异常!");
|
||
}
|
||
return 0;
|
||
|
||
// struct _stat64 fileInfo;
|
||
// std::string p = CodeCvt::Utf8ToMultiByte(path_name);
|
||
// if (_stat64(p.c_str(), &fileInfo) != 0)
|
||
// {
|
||
// log_err("获取文件信息失败: {}", path_name);
|
||
// }
|
||
// return fileInfo.st_mtime;
|
||
// return std::asctime(std::localtime(&cftime));
|
||
}
|
||
struct FileInfoCrc
|
||
{
|
||
std::string _pathName;
|
||
time_t _t;
|
||
uint32_t _crc32;
|
||
};
|
||
void to_json(json& j, const FileInfoCrc& info)
|
||
{
|
||
j = json{
|
||
{"path", info._pathName},
|
||
{"t", info._t},
|
||
{"crc", info._crc32}
|
||
};
|
||
}
|
||
|
||
void from_json(const json& j, FileInfoCrc& info)
|
||
{
|
||
j.at("path").get_to(info._pathName);
|
||
j.at("t").get_to(info._t);
|
||
j.at("crc").get_to(info._crc32);
|
||
}
|
||
|
||
bool CheckPathFileModified(std::string_view path_name, const SearchRule& rule, std::string_view cache, bool update)
|
||
{
|
||
log_info("尝试对文件夹进行改动检查:{},缓存文件:{}", path_name, cache);
|
||
|
||
|
||
constexpr uint32_t VERSION = 3;
|
||
|
||
|
||
if (!IsExist(cache))
|
||
{// cache文件不存在
|
||
|
||
// 按遍历加入即可
|
||
std::vector<FileInfoCrc> vec_file;
|
||
|
||
std::vector<std::string> mul_file;
|
||
GetPathFileAll(path_name, mul_file, rule);
|
||
|
||
// 遍历文件夹所有文件,记录{path_name, write_time}
|
||
for (auto& iter : mul_file)
|
||
{
|
||
uint32_t crc32 = GetCrc32(iter);
|
||
FileInfoCrc info;
|
||
info._crc32 = crc32;
|
||
info._pathName = iter;
|
||
info._t = GetLastWriteTime(iter);
|
||
|
||
vec_file.push_back(info);
|
||
}
|
||
|
||
//写入cache文件
|
||
json j;
|
||
j["version"] = VERSION;
|
||
j["files"] = vec_file;
|
||
|
||
File::SaveString(j.dump(4), cache);
|
||
}
|
||
else
|
||
{//cache文件存在
|
||
|
||
bool not_match = false;
|
||
do
|
||
{
|
||
json j = Json::ParseFile(cache);
|
||
if (j.is_null())
|
||
{
|
||
log_err("cache加载失败:{}", cache);
|
||
not_match = true;
|
||
break;
|
||
}
|
||
|
||
//
|
||
uint32_t version;
|
||
Json::Get(j, "version", version);
|
||
if (version != VERSION)
|
||
{
|
||
log_msg("VERSION值不匹配!");
|
||
not_match = true;
|
||
break;
|
||
}
|
||
|
||
std::vector<FileInfoCrc> vec_file;
|
||
Json::Get(j, "files", vec_file);
|
||
|
||
// 读取到cache文件数据后,再与现有文件比对
|
||
|
||
std::vector<std::string> mul_file;
|
||
GetPathFileAll(path_name, mul_file, rule);
|
||
|
||
// 数量不一致
|
||
if (vec_file.size() != mul_file.size())
|
||
{
|
||
log_msg("增加了文件:{} -> {}",
|
||
vec_file.size(), mul_file.size());
|
||
not_match = true;
|
||
}
|
||
else
|
||
{
|
||
auto iter = vec_file.begin();
|
||
//遍历文件夹所有文件
|
||
for (auto& iter_file : mul_file)
|
||
{
|
||
// 比较操作
|
||
time_t t = GetLastWriteTime(iter_file);
|
||
if (t != iter->_t)
|
||
{
|
||
log_msg("此文件时间戳变化:{},t:{} -> {}",
|
||
iter_file, iter->_t, t);
|
||
|
||
uint32_t crc32 = GetCrc32(iter_file);
|
||
|
||
if (crc32 != iter->_crc32
|
||
|| iter_file != iter->_pathName)
|
||
{
|
||
log_msg("此文件内容被修改:{},crc32:{} -> {}",
|
||
iter_file, iter->_crc32, crc32);
|
||
not_match = true;
|
||
break;
|
||
}
|
||
}
|
||
++iter;
|
||
}
|
||
}
|
||
} while (false);
|
||
|
||
if (not_match && update)
|
||
{
|
||
//删除
|
||
Delete(cache);
|
||
|
||
//再重建
|
||
if (CheckPathFileModified(path_name, rule, cache, false) == false)
|
||
log_err0("此处应该返回真。");
|
||
}
|
||
return not_match;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
void DeleteContent(std::string_view path_name)
|
||
{
|
||
try
|
||
{
|
||
std::filesystem::path p = MakePath(path_name);
|
||
if (!exists(p))
|
||
return;
|
||
for (auto& de : std::filesystem::directory_iterator(p))
|
||
{
|
||
std::filesystem::remove_all(de.path());
|
||
}
|
||
}
|
||
catch (std::filesystem::filesystem_error& e)
|
||
{
|
||
log_err("{}", e.what());
|
||
}
|
||
catch (...)
|
||
{
|
||
log_err("未知异常!");
|
||
}
|
||
}
|
||
|
||
std::string Search(std::string_view path, std::string_view file_name)
|
||
{
|
||
if (!File::IsExist(path))
|
||
return {};
|
||
using dir_iter = std::filesystem::directory_iterator;
|
||
std::list<dir_iter> vec_path;
|
||
try
|
||
{
|
||
dir_iter iter{ CastU8StringView(path) };
|
||
vec_path.push_back(iter);
|
||
}
|
||
catch (std::filesystem::filesystem_error& e)
|
||
{
|
||
log_err0(e);
|
||
return {};
|
||
}
|
||
|
||
std::string ret;
|
||
operate_front(vec_path, [&](const dir_iter& iter_path)
|
||
{
|
||
for (auto& fe : iter_path)
|
||
{
|
||
if (fe.status().type() == std::filesystem::file_type::directory)
|
||
{
|
||
vec_path.push_back(dir_iter{ fe });
|
||
}
|
||
else
|
||
{
|
||
std::string name = fe.path().filename().string();
|
||
if (name != file_name)
|
||
continue;
|
||
|
||
ret = fe.path().string();
|
||
String::CvtPathSeparator(ret);
|
||
#if DL_PLATFORM == DL_PLATFORM_WIN32
|
||
ret = dl::CodeCvt::MultiByteToUtf8(ret);
|
||
#endif
|
||
vec_path.clear();
|
||
}
|
||
}
|
||
});
|
||
return ret;
|
||
}
|
||
|
||
bool Copy(std::string_view source, std::string_view target,
|
||
CopyMode mode, bool recursion)
|
||
{
|
||
try
|
||
{
|
||
std::filesystem::path p0 = MakePath(source);
|
||
std::filesystem::path p1 = MakePath(target);
|
||
std::filesystem::copy_options option;
|
||
switch (mode)
|
||
{
|
||
case CopyMode::NONE:
|
||
option = std::filesystem::copy_options::none;
|
||
break;
|
||
case CopyMode::OVERRIDE:
|
||
option = std::filesystem::copy_options::overwrite_existing;
|
||
break;
|
||
case CopyMode::SKIP:
|
||
option = std::filesystem::copy_options::skip_existing;
|
||
break;
|
||
}
|
||
if (recursion)
|
||
option |= std::filesystem::copy_options::recursive;
|
||
|
||
std::error_code e;
|
||
copy(p0, p1, option, e);
|
||
if (e)
|
||
{
|
||
log_err0(to_string(e));
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
catch (std::filesystem::filesystem_error& e)
|
||
{
|
||
log_err("{}", e.what());
|
||
}
|
||
catch (...)
|
||
{
|
||
log_err("未知异常!");
|
||
}
|
||
return false;
|
||
}
|
||
|
||
std::string GetExtension(std::string_view path_name, bool has_dot /*= false*/)
|
||
{
|
||
try
|
||
{
|
||
std::filesystem::path p = MakePath(path_name);
|
||
std::string ret = ToString(p.extension().u8string());
|
||
if (!has_dot && !ret.empty())
|
||
return std::string{ ret.begin() + 1, ret.end() };
|
||
return ret;
|
||
}
|
||
catch (std::filesystem::filesystem_error& e)
|
||
{
|
||
log_err("{}", e.what());
|
||
}
|
||
catch (...)
|
||
{
|
||
log_err("未知异常!");
|
||
}
|
||
return {};
|
||
}
|
||
|
||
std::string GetFileName(std::string_view path_name)
|
||
{
|
||
try
|
||
{
|
||
std::filesystem::path p = MakePath(path_name);
|
||
return ToString(p.filename().u8string());
|
||
}
|
||
catch (std::filesystem::filesystem_error& e)
|
||
{
|
||
log_err("{}", e.what());
|
||
}
|
||
catch (...)
|
||
{
|
||
log_err("未知异常!");
|
||
}
|
||
return {};
|
||
}
|
||
|
||
std::string GetStem(std::string_view path_name)
|
||
{
|
||
try
|
||
{
|
||
std::filesystem::path p = MakePath(path_name);
|
||
return ToString(p.stem().u8string());
|
||
}
|
||
catch (std::filesystem::filesystem_error& e)
|
||
{
|
||
log_err("{}", e.what());
|
||
}
|
||
catch (...)
|
||
{
|
||
log_err("未知异常!");
|
||
}
|
||
return {};
|
||
}
|
||
|
||
bool CreateFolder(std::string_view path)
|
||
{
|
||
try
|
||
{
|
||
if (path.empty())
|
||
return false;
|
||
if (File::IsExist(path))
|
||
return true;
|
||
#if DL_PLATFORM == DL_PLATFORM_ANDROID
|
||
if (path[0] != '/')
|
||
{// 非绝对路径,则创建到Storage目录
|
||
std::filesystem::path p{ PathStorage() + path };
|
||
return create_directories(p);
|
||
}
|
||
#endif
|
||
if (path.back() == '/')
|
||
{// 末尾斜杠移除
|
||
std::string_view s{ path.begin(), path.end() - 1 };
|
||
std::filesystem::path p{ CastU8StringView(s) };
|
||
return create_directories(p);
|
||
}
|
||
std::filesystem::path p{ CastU8StringView(path) };
|
||
return create_directories(p);
|
||
}
|
||
catch (std::filesystem::filesystem_error& e)
|
||
{
|
||
log_err("{}", e.what());
|
||
}
|
||
catch (...)
|
||
{
|
||
log_err("未知异常!");
|
||
}
|
||
return false;
|
||
}
|
||
|
||
#if DL_PLATFORM == DL_PLATFORM_WIN32
|
||
struct FileImp
|
||
{
|
||
std::string _pathAppDataLocal;
|
||
std::string _pathAppDataLocalLow;
|
||
std::string _pathAppDataRoaming;
|
||
std::string _pathProgramData;
|
||
|
||
void Build()
|
||
{
|
||
{
|
||
wchar_t str[MAX_PATH] = {};
|
||
SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, str);
|
||
|
||
_pathAppDataLocal = CodeCvt::WideCharToUtf8(str);
|
||
String::CvtPathSeparator(_pathAppDataLocal);
|
||
_pathAppDataLocalLow = _pathAppDataLocal + "Low/";
|
||
_pathAppDataLocal += "/";
|
||
}
|
||
{
|
||
wchar_t str[MAX_PATH] = {};
|
||
SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, str);
|
||
|
||
_pathAppDataRoaming = CodeCvt::WideCharToUtf8(str);
|
||
String::CvtPathSeparator(_pathAppDataRoaming);
|
||
_pathAppDataRoaming += "/";
|
||
}
|
||
{
|
||
wchar_t str[MAX_PATH] = {};
|
||
SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, 0, str);
|
||
|
||
_pathProgramData = CodeCvt::WideCharToUtf8(str);
|
||
String::CvtPathSeparator(_pathProgramData);
|
||
_pathProgramData += "/";
|
||
}
|
||
}
|
||
};
|
||
#elif DL_PLATFORM == DL_PLATFORM_ANDROID
|
||
struct FileImp
|
||
{
|
||
std::string _pathFilesDir;
|
||
std::string _pathExternalFilesDir;
|
||
std::string _pathExternalStorageDirectory;
|
||
void Build()
|
||
{
|
||
}
|
||
};
|
||
#elif DL_PLATFORM == DL_PLATFORM_LINUX
|
||
struct FileImp
|
||
{
|
||
std::string _pathFilesDir;
|
||
std::string _pathExternalFilesDir;
|
||
std::string _pathExternalStorageDirectory;
|
||
void Build()
|
||
{
|
||
_pathFilesDir = "/home/lveyou/dltest/";
|
||
_pathExternalFilesDir = _pathFilesDir;
|
||
_pathExternalStorageDirectory = _pathFilesDir;
|
||
}
|
||
};
|
||
#endif
|
||
|
||
#if DL_PLATFORM == DL_PLATFORM_APPLE
|
||
struct FileImp
|
||
{
|
||
std::string _pathFilesDir;
|
||
std::string _pathExternalFilesDir;
|
||
std::string _pathExternalStorageDirectory;
|
||
void Build()
|
||
{
|
||
_pathFilesDir = "/Users/zego/code/dl/out/";
|
||
_pathExternalFilesDir = _pathFilesDir;
|
||
_pathExternalStorageDirectory = _pathFilesDir;
|
||
}
|
||
};
|
||
#endif
|
||
FileImp g_fileImp;
|
||
|
||
void InitFileSystem()
|
||
{
|
||
g_fileImp.Build();
|
||
|
||
File::CreateFolder(PathStorage());
|
||
File::CreateFolder(PathPrivate());
|
||
File::CreateFolder(PathPublic());
|
||
}
|
||
|
||
std::string PathCurrent()
|
||
{
|
||
#if DL_PLATFORM != DL_PLATFORM_ANDROID
|
||
// current_path 在安卓返回的是右斜杠
|
||
std::filesystem::path p = std::filesystem::current_path();
|
||
std::string ret = ToString(p.u8string());
|
||
ret += '/';
|
||
String::CvtPathSeparator(ret);
|
||
return ret;
|
||
#else
|
||
return PathStorage() + "assets/";
|
||
#endif
|
||
}
|
||
|
||
std::string PathTemp()
|
||
{
|
||
std::filesystem::path p = std::filesystem::temp_directory_path();
|
||
std::string ret = ToString(p.u8string());
|
||
String::CvtPathSeparator(ret);
|
||
return ret;
|
||
}
|
||
|
||
std::string PathStorage()
|
||
{
|
||
#if DL_PLATFORM == DL_PLATFORM_WIN32
|
||
return fmt::format("{}dl/{}/", g_fileImp._pathAppDataLocal, Engine::GetParamRef()._projectName);
|
||
#elif DL_PLATFORM == DL_PLATFORM_ANDROID
|
||
return g_fileImp._pathExternalFilesDir;
|
||
#endif
|
||
}
|
||
|
||
std::string PathStorageRoot()
|
||
{
|
||
#if DL_PLATFORM == DL_PLATFORM_WIN32
|
||
return g_fileImp._pathAppDataLocal;
|
||
#elif DL_PLATFORM == DL_PLATFORM_ANDROID
|
||
return PathPublic();
|
||
#endif
|
||
}
|
||
|
||
std::string PathPrivate()
|
||
{
|
||
#if DL_PLATFORM == DL_PLATFORM_WIN32
|
||
return fmt::format("{}dl/{}/", g_fileImp._pathAppDataLocal, Engine::GetParamRef()._projectName);
|
||
#elif DL_PLATFORM == DL_PLATFORM_ANDROID
|
||
return g_fileImp._pathFilesDir;
|
||
#endif
|
||
}
|
||
|
||
std::string PathPublic()
|
||
{
|
||
#if DL_PLATFORM == DL_PLATFORM_WIN32
|
||
return fmt::format("{}dl/{}/", g_fileImp._pathProgramData, Engine::GetParamRef()._projectName);
|
||
#elif DL_PLATFORM == DL_PLATFORM_ANDROID
|
||
return g_fileImp._pathExternalStorageDirectory;
|
||
#endif
|
||
}
|
||
|
||
|
||
bool IsExist(std::string_view path_name)
|
||
{
|
||
if (path_name.empty())
|
||
{
|
||
return false;
|
||
}
|
||
try
|
||
{
|
||
std::filesystem::path p = MakePath(path_name);
|
||
return exists(p);
|
||
}
|
||
catch (std::filesystem::filesystem_error&)
|
||
{
|
||
}
|
||
catch (...)
|
||
{
|
||
}
|
||
return false;
|
||
}
|
||
|
||
bool Delete(std::string_view path_name, bool all /*= false*/)
|
||
{
|
||
try
|
||
{
|
||
std::filesystem::path p = MakePath(path_name);
|
||
return all ? remove_all(p) : remove(p);
|
||
}
|
||
catch (std::filesystem::filesystem_error& e)
|
||
{
|
||
log_err("{}", e.what());
|
||
}
|
||
catch (...)
|
||
{
|
||
log_err("未知异常!");
|
||
}
|
||
return false;
|
||
}
|
||
|
||
bool SaveBuffer(BufferView buf, std::string_view path_name)
|
||
{
|
||
std::ofstream ofs = Set(path_name, true);
|
||
if (!ofs.is_open())
|
||
return false;
|
||
ofs.write((char*)buf.data(), buf.size());
|
||
return true;
|
||
}
|
||
|
||
bool SaveString(BufferView buf, std::string_view path_name)
|
||
{
|
||
std::ofstream ofs = Set(path_name, false);
|
||
if (!ofs.is_open())
|
||
return false;
|
||
ofs.write((char*)buf.data(), buf.size());
|
||
return true;
|
||
}
|
||
|
||
bool SaveString(std::string_view str, std::string_view path_name)
|
||
{
|
||
return SaveString(CreateBufferView(str), path_name);
|
||
}
|
||
|
||
uint32_t GetCrc32(std::string_view path_name)
|
||
{
|
||
Buffer buf = GetBuffer(path_name);
|
||
if (buf.empty())
|
||
return 0;
|
||
return Crypto::HashCrc32(buf);
|
||
}
|
||
|
||
std::string GetString(std::string_view path_name)
|
||
{
|
||
std::ifstream ifs = Get(path_name, false);
|
||
if (!ifs.is_open())
|
||
{
|
||
return {};
|
||
}
|
||
std::stringstream buffer;
|
||
buffer << ifs.rdbuf();
|
||
return buffer.str();
|
||
}
|
||
|
||
std::string GetFolderName(std::string_view path_name)
|
||
{
|
||
std::string ret{ path_name };
|
||
if (Erase::TailFromLast(ret, '/'))
|
||
{
|
||
ret.pop_back();
|
||
return GetStem(ret);
|
||
}
|
||
return {};
|
||
}
|
||
|
||
std::string GetFilePath(std::string_view path_name)
|
||
{
|
||
if(path_name.empty())
|
||
return {};
|
||
std::string ret{ path_name };
|
||
if (ret.back() == '/')
|
||
return ret;
|
||
if (Erase::TailFromLast(ret, '/'))
|
||
{
|
||
return ret;
|
||
}
|
||
return {};
|
||
}
|
||
|
||
std::string GetAbsolute(std::string_view path_name)
|
||
{
|
||
try
|
||
{
|
||
std::filesystem::path p = MakePath(path_name);
|
||
std::string ret = std::filesystem::absolute(p).string();
|
||
#if DL_PLATFORM == DL_PLATFORM_WIN32
|
||
ret = dl::CodeCvt::MultiByteToUtf8(ret);
|
||
String::CvtPathSeparator(ret);
|
||
#endif
|
||
return ret;
|
||
}
|
||
catch (std::filesystem::filesystem_error& e)
|
||
{
|
||
log_err("{}", e.what());
|
||
}
|
||
catch (...)
|
||
{
|
||
log_err("未知异常!");
|
||
}
|
||
return {};
|
||
}
|
||
|
||
std::string GetRelative(std::string_view path_name, std::string_view path_base)
|
||
{
|
||
try
|
||
{
|
||
std::filesystem::path p = MakePath(path_name);
|
||
std::filesystem::path p_base = MakePath(path_base);
|
||
std::string ret = std::filesystem::relative(p, p_base).string();
|
||
#if DL_PLATFORM == DL_PLATFORM_WIN32
|
||
ret = dl::CodeCvt::MultiByteToUtf8(ret);
|
||
String::CvtPathSeparator(ret);
|
||
#endif
|
||
return ret;
|
||
}
|
||
catch (std::filesystem::filesystem_error& e)
|
||
{
|
||
log_err("{}", e.what());
|
||
}
|
||
catch (...)
|
||
{
|
||
log_err("未知异常!");
|
||
}
|
||
return {};
|
||
}
|
||
|
||
bool Rename(std::string_view src, std::string_view tar)
|
||
{
|
||
try
|
||
{
|
||
std::filesystem::path p_src = MakePath(src);
|
||
std::filesystem::path p_tar = MakePath(tar);
|
||
|
||
std::filesystem::rename(p_src, p_tar);
|
||
return true;
|
||
}
|
||
catch (std::filesystem::filesystem_error& e)
|
||
{
|
||
log_err("{}", e.what());
|
||
}
|
||
catch (...)
|
||
{
|
||
log_err("未知异常!");
|
||
}
|
||
return false;
|
||
}
|
||
|
||
std::string GetParentPath(std::string_view path_name)
|
||
{
|
||
try
|
||
{
|
||
std::string str_path{ path_name };
|
||
if (!str_path.empty() && str_path.back() == '/')
|
||
str_path.pop_back();
|
||
|
||
std::filesystem::path p = MakePath(path_name);
|
||
str_path = ToString(p.parent_path().u8string());
|
||
if (!str_path.empty() && str_path.back() != '/')
|
||
str_path += '/';
|
||
return str_path;
|
||
}
|
||
catch (std::filesystem::filesystem_error& e)
|
||
{
|
||
log_err("{}", e.what());
|
||
}
|
||
catch (...)
|
||
{
|
||
log_err("未知异常!");
|
||
}
|
||
return std::string{ path_name };
|
||
}
|
||
|
||
std::vector<std::string> PathRegular()
|
||
{
|
||
std::vector<std::string> ret;
|
||
#if DL_PLATFORM == DL_PLATFORM_WIN32
|
||
// 1. 获取盘符位掩码
|
||
DWORD drive_mask = GetLogicalDrives();
|
||
if (drive_mask == 0)
|
||
{ // 获取失败
|
||
DWORD error_code = GetLastError();
|
||
log_warn("获取盘符失败!错误码:{}", error_code);
|
||
}
|
||
else
|
||
{
|
||
// 2. 遍历 A-Z 盘符(共26个)
|
||
for (char drive = 'A'; drive <= 'Z'; ++drive)
|
||
{
|
||
// 计算当前盘符对应的位掩码
|
||
DWORD bit = 1 << (drive - 'A');
|
||
|
||
// 判断该位是否为1(盘符存在)
|
||
if (drive_mask & bit)
|
||
{
|
||
std::string drive_path = std::string(1, drive) + ":/";
|
||
ret.push_back(drive_path);
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
return ret;
|
||
}
|
||
|
||
}
|
||
|
||
#if DL_PLATFORM == DL_PLATFORM_ANDROID
|
||
|
||
std::string Android_JstringToString(JNIEnv *env, jstring j_str)
|
||
{
|
||
const char *cstr = env->GetStringUTFChars(j_str, NULL);
|
||
std::string ret = std::string(cstr);
|
||
env->ReleaseStringUTFChars(j_str, cstr);
|
||
return ret;
|
||
}
|
||
|
||
extern "C"
|
||
JNIEXPORT void JNICALL
|
||
Java_com_dl_project_MainActivity_SetStoragePath(JNIEnv *env, jobject thiz,
|
||
jstring str_private,
|
||
jstring str_storage,
|
||
jstring str_public)
|
||
{
|
||
dl::File::g_fileImp._pathFilesDir = Android_JstringToString(env, str_private);
|
||
dl::File::g_fileImp._pathExternalFilesDir = Android_JstringToString(env, str_storage);
|
||
dl::File::g_fileImp._pathExternalStorageDirectory = Android_JstringToString(env, str_public);
|
||
dl::File::g_fileImp._pathFilesDir += "/";
|
||
dl::File::g_fileImp._pathExternalFilesDir += "/";
|
||
dl::File::g_fileImp._pathExternalStorageDirectory += "/";
|
||
}
|
||
|
||
#endif |