- 新增Switch平台初始化与清理功能 - 实现Asset资源管理器类,提供文件读写、路径处理等功能 - 完善Window类的销毁逻辑,释放SDL资源 - 更新Switch平台编译配置,移除冗余标志 - 在主程序中集成资源管理器功能
436 lines
11 KiB
C++
436 lines
11 KiB
C++
#include <fostbite2D/utils/asset.h>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <system_error>
|
|
|
|
namespace frostbite2D {
|
|
|
|
Asset &Asset::get() {
|
|
static Asset instance;
|
|
return instance;
|
|
}
|
|
|
|
fs::path Asset::toPath(const std::string &path) const {
|
|
#ifdef _WIN32
|
|
return fs::u8path(path);
|
|
#else
|
|
return fs::path(path);
|
|
#endif
|
|
}
|
|
|
|
std::string Asset::fromPath(const fs::path &path) const {
|
|
#ifdef _WIN32
|
|
return path.u8string();
|
|
#else
|
|
return path.string();
|
|
#endif
|
|
}
|
|
|
|
std::string Asset::resolveFullPath(const std::string &path) const {
|
|
if (path.empty()) {
|
|
return path;
|
|
}
|
|
|
|
fs::path p = toPath(path);
|
|
if (p.is_absolute()) {
|
|
return path;
|
|
}
|
|
|
|
if (!workingDirectory_.empty()) {
|
|
return fromPath(toPath(workingDirectory_) / p);
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
bool Asset::readTextFile(const std::string &path, std::string &outContent) {
|
|
std::string fullPath = resolveFullPath(path);
|
|
|
|
if (!exists(fullPath)) {
|
|
return false;
|
|
}
|
|
|
|
std::ifstream file(toPath(fullPath), std::ios::in | std::ios::binary);
|
|
if (!file.is_open()) {
|
|
return false;
|
|
}
|
|
|
|
std::ostringstream ss;
|
|
ss << file.rdbuf();
|
|
outContent = ss.str();
|
|
file.close();
|
|
return true;
|
|
}
|
|
|
|
bool Asset::writeTextFile(const std::string &path, const std::string &content,
|
|
bool append) {
|
|
std::string fullPath = resolveFullPath(path);
|
|
|
|
auto mode = std::ios::out;
|
|
if (append) {
|
|
mode |= std::ios::app;
|
|
}
|
|
|
|
std::ofstream file(toPath(fullPath), mode);
|
|
if (!file.is_open()) {
|
|
return false;
|
|
}
|
|
|
|
file << content;
|
|
file.close();
|
|
return true;
|
|
}
|
|
|
|
bool Asset::readBinaryFile(const std::string &path,
|
|
std::vector<uint8> &outData) {
|
|
std::string fullPath = resolveFullPath(path);
|
|
|
|
if (!exists(fullPath)) {
|
|
return false;
|
|
}
|
|
|
|
std::ifstream file(toPath(fullPath), std::ios::in | std::ios::binary);
|
|
if (!file.is_open()) {
|
|
return false;
|
|
}
|
|
|
|
file.seekg(0, std::ios::end);
|
|
auto size = file.tellg();
|
|
file.seekg(0, std::ios::beg);
|
|
|
|
outData.resize(static_cast<size_t>(size));
|
|
file.read(reinterpret_cast<char *>(outData.data()), size);
|
|
file.close();
|
|
return true;
|
|
}
|
|
|
|
bool Asset::writeBinaryFile(const std::string &path,
|
|
const std::vector<uint8> &data, bool append) {
|
|
std::string fullPath = resolveFullPath(path);
|
|
|
|
auto mode = std::ios::out | std::ios::binary;
|
|
if (append) {
|
|
mode |= std::ios::app;
|
|
}
|
|
|
|
std::ofstream file(toPath(fullPath), mode);
|
|
if (!file.is_open()) {
|
|
return false;
|
|
}
|
|
|
|
file.write(reinterpret_cast<const char *>(data.data()), data.size());
|
|
file.close();
|
|
return true;
|
|
}
|
|
|
|
bool Asset::exists(const std::string &path) const {
|
|
std::error_code ec;
|
|
return fs::exists(toPath(resolveFullPath(path)), ec);
|
|
}
|
|
|
|
bool Asset::isDirectory(const std::string &path) const {
|
|
std::error_code ec;
|
|
return fs::is_directory(toPath(resolveFullPath(path)), ec);
|
|
}
|
|
|
|
bool Asset::isRegularFile(const std::string &path) const {
|
|
std::error_code ec;
|
|
return fs::is_regular_file(toPath(resolveFullPath(path)), ec);
|
|
}
|
|
|
|
bool Asset::createDirectory(const std::string &path) {
|
|
std::error_code ec;
|
|
return fs::create_directory(toPath(resolveFullPath(path)), ec);
|
|
}
|
|
|
|
bool Asset::createDirectories(const std::string &path) {
|
|
std::error_code ec;
|
|
return fs::create_directories(toPath(resolveFullPath(path)), ec);
|
|
}
|
|
|
|
bool Asset::removeFile(const std::string &path) {
|
|
std::error_code ec;
|
|
return fs::remove(toPath(resolveFullPath(path)), ec);
|
|
}
|
|
|
|
bool Asset::removeDirectory(const std::string &path) {
|
|
std::error_code ec;
|
|
return fs::remove_all(toPath(resolveFullPath(path)), ec) > 0;
|
|
}
|
|
|
|
bool Asset::copyFile(const std::string &from, const std::string &to,
|
|
bool overwrite) {
|
|
std::error_code ec;
|
|
auto options =
|
|
overwrite ? fs::copy_options::overwrite_existing : fs::copy_options::none;
|
|
return fs::copy_file(toPath(resolveFullPath(from)),
|
|
toPath(resolveFullPath(to)), options, ec);
|
|
}
|
|
|
|
bool Asset::moveFile(const std::string &from, const std::string &to) {
|
|
std::error_code ec;
|
|
fs::rename(toPath(resolveFullPath(from)), toPath(resolveFullPath(to)), ec);
|
|
return !ec;
|
|
}
|
|
|
|
bool Asset::rename(const std::string &oldPath, const std::string &newPath) {
|
|
std::error_code ec;
|
|
fs::rename(toPath(resolveFullPath(oldPath)), toPath(resolveFullPath(newPath)),
|
|
ec);
|
|
return !ec;
|
|
}
|
|
|
|
std::vector<std::string> Asset::listFiles(const std::string &directoryPath,
|
|
bool recursive) {
|
|
std::vector<std::string> files;
|
|
fs::path fullPath = toPath(resolveFullPath(directoryPath));
|
|
|
|
std::error_code ec;
|
|
if (!fs::exists(fullPath, ec) || !fs::is_directory(fullPath, ec)) {
|
|
return files;
|
|
}
|
|
|
|
try {
|
|
if (recursive) {
|
|
for (const auto &entry : fs::recursive_directory_iterator(fullPath, ec)) {
|
|
if (entry.is_regular_file()) {
|
|
files.push_back(fromPath(entry.path()));
|
|
}
|
|
}
|
|
} else {
|
|
for (const auto &entry : fs::directory_iterator(fullPath, ec)) {
|
|
if (entry.is_regular_file()) {
|
|
files.push_back(fromPath(entry.path()));
|
|
}
|
|
}
|
|
}
|
|
} catch (...) {
|
|
}
|
|
|
|
return files;
|
|
}
|
|
|
|
std::vector<std::string>
|
|
Asset::listDirectories(const std::string &directoryPath, bool recursive) {
|
|
std::vector<std::string> directories;
|
|
fs::path fullPath = toPath(resolveFullPath(directoryPath));
|
|
|
|
std::error_code ec;
|
|
if (!fs::exists(fullPath, ec) || !fs::is_directory(fullPath, ec)) {
|
|
return directories;
|
|
}
|
|
|
|
try {
|
|
if (recursive) {
|
|
for (const auto &entry : fs::recursive_directory_iterator(fullPath, ec)) {
|
|
if (entry.is_directory()) {
|
|
directories.push_back(fromPath(entry.path()));
|
|
}
|
|
}
|
|
} else {
|
|
for (const auto &entry : fs::directory_iterator(fullPath, ec)) {
|
|
if (entry.is_directory()) {
|
|
directories.push_back(fromPath(entry.path()));
|
|
}
|
|
}
|
|
}
|
|
} catch (...) {
|
|
}
|
|
|
|
return directories;
|
|
}
|
|
|
|
std::vector<std::string> Asset::listAll(const std::string &directoryPath,
|
|
bool recursive) {
|
|
std::vector<std::string> items;
|
|
fs::path fullPath = toPath(resolveFullPath(directoryPath));
|
|
|
|
std::error_code ec;
|
|
if (!fs::exists(fullPath, ec) || !fs::is_directory(fullPath, ec)) {
|
|
return items;
|
|
}
|
|
|
|
try {
|
|
if (recursive) {
|
|
for (const auto &entry : fs::recursive_directory_iterator(fullPath, ec)) {
|
|
items.push_back(fromPath(entry.path()));
|
|
}
|
|
} else {
|
|
for (const auto &entry : fs::directory_iterator(fullPath, ec)) {
|
|
items.push_back(fromPath(entry.path()));
|
|
}
|
|
}
|
|
} catch (...) {
|
|
}
|
|
|
|
return items;
|
|
}
|
|
|
|
std::vector<std::string>
|
|
Asset::listFilesWithExtension(const std::string &directoryPath,
|
|
const std::string &extension, bool recursive) {
|
|
std::vector<std::string> files;
|
|
fs::path fullPath = toPath(resolveFullPath(directoryPath));
|
|
fs::path ext = toPath(extension);
|
|
|
|
std::error_code ec;
|
|
if (!fs::exists(fullPath, ec) || !fs::is_directory(fullPath, ec)) {
|
|
return files;
|
|
}
|
|
|
|
try {
|
|
if (recursive) {
|
|
for (const auto &entry : fs::recursive_directory_iterator(fullPath, ec)) {
|
|
if (entry.is_regular_file() && entry.path().extension() == ext) {
|
|
files.push_back(fromPath(entry.path()));
|
|
}
|
|
}
|
|
} else {
|
|
for (const auto &entry : fs::directory_iterator(fullPath, ec)) {
|
|
if (entry.is_regular_file() && entry.path().extension() == ext) {
|
|
files.push_back(fromPath(entry.path()));
|
|
}
|
|
}
|
|
}
|
|
} catch (...) {
|
|
}
|
|
|
|
return files;
|
|
}
|
|
|
|
FileInfo Asset::getFileInfo(const std::string &path) {
|
|
FileInfo info;
|
|
std::string fullPath = resolveFullPath(path);
|
|
info.fullPath = fullPath;
|
|
|
|
if (!exists(fullPath)) {
|
|
info.exists = false;
|
|
return info;
|
|
}
|
|
|
|
info.exists = true;
|
|
info.isDirectory = isDirectory(fullPath);
|
|
info.isRegularFile = isRegularFile(fullPath);
|
|
|
|
fs::path p = toPath(fullPath);
|
|
info.name = fromPath(p.filename());
|
|
info.extension = fromPath(p.extension());
|
|
|
|
if (info.isRegularFile) {
|
|
std::error_code ec;
|
|
info.size = fs::file_size(p, ec);
|
|
if (ec) {
|
|
info.size = 0;
|
|
}
|
|
}
|
|
|
|
return info;
|
|
}
|
|
|
|
std::string Asset::getFileName(const std::string &path) const {
|
|
return fromPath(toPath(path).filename());
|
|
}
|
|
|
|
std::string Asset::getFileNameWithoutExtension(const std::string &path) const {
|
|
return fromPath(toPath(path).stem());
|
|
}
|
|
|
|
std::string Asset::getExtension(const std::string &path) const {
|
|
return fromPath(toPath(path).extension());
|
|
}
|
|
|
|
std::string Asset::getParentPath(const std::string &path) const {
|
|
return fromPath(toPath(path).parent_path());
|
|
}
|
|
|
|
std::string Asset::getAbsolutePath(const std::string &path) const {
|
|
std::error_code ec;
|
|
return fromPath(fs::absolute(toPath(resolveFullPath(path)), ec));
|
|
}
|
|
|
|
std::string Asset::getCanonicalPath(const std::string &path) const {
|
|
std::error_code ec;
|
|
return fromPath(fs::canonical(toPath(resolveFullPath(path)), ec));
|
|
}
|
|
|
|
std::string Asset::getCurrentPath() const {
|
|
std::error_code ec;
|
|
return fromPath(fs::current_path(ec));
|
|
}
|
|
|
|
bool Asset::setCurrentPath(const std::string &path) {
|
|
std::error_code ec;
|
|
fs::current_path(toPath(path), ec);
|
|
return !ec;
|
|
}
|
|
|
|
std::string Asset::combinePath(const std::string &left,
|
|
const std::string &right) const {
|
|
return fromPath(toPath(left) / toPath(right));
|
|
}
|
|
|
|
std::string Asset::normalizePath(const std::string &path) const {
|
|
std::error_code ec;
|
|
auto p = fs::absolute(toPath(resolveFullPath(path)), ec);
|
|
if (ec) {
|
|
return path;
|
|
}
|
|
return fromPath(p.make_preferred());
|
|
}
|
|
|
|
uint64 Asset::getFileSize(const std::string &path) const {
|
|
std::error_code ec;
|
|
auto size = fs::file_size(toPath(resolveFullPath(path)), ec);
|
|
if (ec) {
|
|
return 0;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
bool Asset::isEmpty(const std::string &path) const {
|
|
std::error_code ec;
|
|
return fs::is_empty(toPath(resolveFullPath(path)), ec);
|
|
}
|
|
|
|
std::optional<std::string> Asset::readFileToString(const std::string &path) {
|
|
std::string content;
|
|
if (readTextFile(path, content)) {
|
|
return content;
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<std::vector<uint8>>
|
|
Asset::readFileToBytes(const std::string &path) {
|
|
std::vector<uint8> data;
|
|
if (readBinaryFile(path, data)) {
|
|
return data;
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
void Asset::setWorkingDirectory(const std::string &path) {
|
|
workingDirectory_ = path;
|
|
}
|
|
|
|
const std::string &Asset::getWorkingDirectory() const {
|
|
return workingDirectory_;
|
|
}
|
|
|
|
std::string Asset::resolvePath(const std::string &relativePath) const {
|
|
return resolveFullPath(relativePath);
|
|
}
|
|
|
|
void Asset::setAssetRoot(const std::string &root) { assetRoot_ = root; }
|
|
|
|
const std::string &Asset::getAssetRoot() const { return assetRoot_; }
|
|
|
|
std::string Asset::resolveAssetPath(const std::string &relativePath) const {
|
|
if (assetRoot_.empty()) {
|
|
return resolveFullPath(relativePath);
|
|
}
|
|
return resolveFullPath(combinePath(assetRoot_, relativePath));
|
|
}
|
|
|
|
} // namespace frostbite2D
|