feat(平台): 添加Switch平台支持并实现资源管理器
- 新增Switch平台初始化与清理功能 - 实现Asset资源管理器类,提供文件读写、路径处理等功能 - 完善Window类的销毁逻辑,释放SDL资源 - 更新Switch平台编译配置,移除冗余标志 - 在主程序中集成资源管理器功能
This commit is contained in:
parent
ce2cc4d210
commit
9f29192ae8
|
|
@ -79,7 +79,7 @@ struct WindowConfig {
|
||||||
class Window {
|
class Window {
|
||||||
public:
|
public:
|
||||||
Window() = default;
|
Window() = default;
|
||||||
virtual ~Window() = default;
|
~Window() = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 创建窗口
|
* @brief 创建窗口
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __SWITCH__
|
||||||
|
|
||||||
|
#include <switch.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
namespace frostbite2D {
|
||||||
|
/**
|
||||||
|
* @brief Switch平台相关函数
|
||||||
|
*/
|
||||||
|
|
||||||
|
void switchInit();
|
||||||
|
void switchShutdown();
|
||||||
|
|
||||||
|
} // namespace frostbite2D
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,397 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fostbite2D/types/type_alias.h>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace frostbite2D {
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 文件信息结构体
|
||||||
|
*/
|
||||||
|
struct FileInfo {
|
||||||
|
std::string name; ///< 文件名(包含扩展名)
|
||||||
|
std::string extension; ///< 文件扩展名(包含点,如 ".txt")
|
||||||
|
std::string fullPath; ///< 完整路径
|
||||||
|
uint64 size = 0; ///< 文件大小(字节)
|
||||||
|
bool isDirectory = false; ///< 是否为目录
|
||||||
|
bool isRegularFile = false; ///< 是否为普通文件
|
||||||
|
bool exists = false; ///< 是否存在
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 资源管理类(单例)
|
||||||
|
*
|
||||||
|
* 提供文件读写、目录操作、路径处理等功能。
|
||||||
|
* 支持设置工作目录,所有相对路径将基于工作目录解析。
|
||||||
|
* 支持 UTF-8 编码的中文路径。
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* auto& asset = Asset::get();
|
||||||
|
* asset.setWorkingDirectory("D:/游戏/资源");
|
||||||
|
* std::string content;
|
||||||
|
* asset.readTextFile("配置/设置.json", content);
|
||||||
|
*/
|
||||||
|
class Asset {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief 获取单例实例
|
||||||
|
* @return 资源管理器实例引用
|
||||||
|
*/
|
||||||
|
static Asset &get();
|
||||||
|
|
||||||
|
Asset(const Asset &) = delete;
|
||||||
|
Asset &operator=(const Asset &) = delete;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// 文件读写
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 读取文本文件
|
||||||
|
* @param path 文件路径(相对或绝对路径)
|
||||||
|
* @param outContent 输出文件内容
|
||||||
|
* @return 读取成功返回 true
|
||||||
|
*/
|
||||||
|
bool readTextFile(const std::string &path, std::string &outContent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 写入文本文件
|
||||||
|
* @param path 文件路径
|
||||||
|
* @param content 要写入的内容
|
||||||
|
* @param append 是否追加模式,默认覆盖
|
||||||
|
* @return 写入成功返回 true
|
||||||
|
*/
|
||||||
|
bool writeTextFile(const std::string &path, const std::string &content,
|
||||||
|
bool append = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 读取二进制文件
|
||||||
|
* @param path 文件路径
|
||||||
|
* @param outData 输出字节数组
|
||||||
|
* @return 读取成功返回 true
|
||||||
|
*/
|
||||||
|
bool readBinaryFile(const std::string &path, std::vector<uint8> &outData);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 写入二进制文件
|
||||||
|
* @param path 文件路径
|
||||||
|
* @param data 要写入的字节数组
|
||||||
|
* @param append 是否追加模式,默认覆盖
|
||||||
|
* @return 写入成功返回 true
|
||||||
|
*/
|
||||||
|
bool writeBinaryFile(const std::string &path, const std::vector<uint8> &data,
|
||||||
|
bool append = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 读取文本文件到字符串(便捷方法)
|
||||||
|
* @param path 文件路径
|
||||||
|
* @return 文件内容,失败返回 std::nullopt
|
||||||
|
*/
|
||||||
|
std::optional<std::string> readFileToString(const std::string &path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 读取二进制文件到字节数组(便捷方法)
|
||||||
|
* @param path 文件路径
|
||||||
|
* @return 字节数组,失败返回 std::nullopt
|
||||||
|
*/
|
||||||
|
std::optional<std::vector<uint8>> readFileToBytes(const std::string &path);
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// 文件/目录检查
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查路径是否存在
|
||||||
|
* @param path 路径
|
||||||
|
* @return 存在返回 true
|
||||||
|
*/
|
||||||
|
bool exists(const std::string &path) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查路径是否为目录
|
||||||
|
* @param path 路径
|
||||||
|
* @return 是目录返回 true
|
||||||
|
*/
|
||||||
|
bool isDirectory(const std::string &path) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查路径是否为普通文件
|
||||||
|
* @param path 路径
|
||||||
|
* @return 是普通文件返回 true
|
||||||
|
*/
|
||||||
|
bool isRegularFile(const std::string &path) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查文件或目录是否为空
|
||||||
|
* @param path 路径
|
||||||
|
* @return 为空返回 true
|
||||||
|
*/
|
||||||
|
bool isEmpty(const std::string &path) const;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// 目录操作
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建单层目录
|
||||||
|
* @param path 目录路径
|
||||||
|
* @return 创建成功返回 true
|
||||||
|
*/
|
||||||
|
bool createDirectory(const std::string &path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 递归创建多层目录
|
||||||
|
* @param path 目录路径
|
||||||
|
* @return 创建成功返回 true
|
||||||
|
*/
|
||||||
|
bool createDirectories(const std::string &path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 删除文件
|
||||||
|
* @param path 文件路径
|
||||||
|
* @return 删除成功返回 true
|
||||||
|
*/
|
||||||
|
bool removeFile(const std::string &path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 删除目录及其所有内容
|
||||||
|
* @param path 目录路径
|
||||||
|
* @return 删除成功返回 true
|
||||||
|
*/
|
||||||
|
bool removeDirectory(const std::string &path);
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// 文件操作
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 复制文件
|
||||||
|
* @param from 源文件路径
|
||||||
|
* @param to 目标文件路径
|
||||||
|
* @param overwrite 是否覆盖已存在的文件
|
||||||
|
* @return 复制成功返回 true
|
||||||
|
*/
|
||||||
|
bool copyFile(const std::string &from, const std::string &to,
|
||||||
|
bool overwrite = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 移动文件
|
||||||
|
* @param from 源文件路径
|
||||||
|
* @param to 目标文件路径
|
||||||
|
* @return 移动成功返回 true
|
||||||
|
*/
|
||||||
|
bool moveFile(const std::string &from, const std::string &to);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 重命名文件或目录
|
||||||
|
* @param oldPath 原路径
|
||||||
|
* @param newPath 新路径
|
||||||
|
* @return 重命名成功返回 true
|
||||||
|
*/
|
||||||
|
bool rename(const std::string &oldPath, const std::string &newPath);
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// 目录遍历
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 列出目录中的所有文件
|
||||||
|
* @param directoryPath 目录路径
|
||||||
|
* @param recursive 是否递归遍历子目录
|
||||||
|
* @return 文件路径列表
|
||||||
|
*/
|
||||||
|
std::vector<std::string> listFiles(const std::string &directoryPath,
|
||||||
|
bool recursive = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 列出目录中的所有子目录
|
||||||
|
* @param directoryPath 目录路径
|
||||||
|
* @param recursive 是否递归遍历子目录
|
||||||
|
* @return 子目录路径列表
|
||||||
|
*/
|
||||||
|
std::vector<std::string> listDirectories(const std::string &directoryPath,
|
||||||
|
bool recursive = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 列出目录中的所有文件和子目录
|
||||||
|
* @param directoryPath 目录路径
|
||||||
|
* @param recursive 是否递归遍历子目录
|
||||||
|
* @return 所有项目路径列表
|
||||||
|
*/
|
||||||
|
std::vector<std::string> listAll(const std::string &directoryPath,
|
||||||
|
bool recursive = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 按扩展名列出文件
|
||||||
|
* @param directoryPath 目录路径
|
||||||
|
* @param extension 文件扩展名(如 ".png")
|
||||||
|
* @param recursive 是否递归遍历子目录
|
||||||
|
* @return 匹配的文件路径列表
|
||||||
|
*/
|
||||||
|
std::vector<std::string>
|
||||||
|
listFilesWithExtension(const std::string &directoryPath,
|
||||||
|
const std::string &extension, bool recursive = false);
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// 文件信息
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取文件详细信息
|
||||||
|
* @param path 文件路径
|
||||||
|
* @return 文件信息结构体
|
||||||
|
*/
|
||||||
|
FileInfo getFileInfo(const std::string &path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取文件大小
|
||||||
|
* @param path 文件路径
|
||||||
|
* @return 文件大小(字节)
|
||||||
|
*/
|
||||||
|
uint64 getFileSize(const std::string &path) const;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// 路径处理
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取文件名(包含扩展名)
|
||||||
|
* @param path 文件路径
|
||||||
|
* @return 文件名
|
||||||
|
*/
|
||||||
|
std::string getFileName(const std::string &path) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取文件名(不含扩展名)
|
||||||
|
* @param path 文件路径
|
||||||
|
* @return 文件名
|
||||||
|
*/
|
||||||
|
std::string getFileNameWithoutExtension(const std::string &path) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取文件扩展名
|
||||||
|
* @param path 文件路径
|
||||||
|
* @return 扩展名(包含点,如 ".txt")
|
||||||
|
*/
|
||||||
|
std::string getExtension(const std::string &path) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取父目录路径
|
||||||
|
* @param path 文件路径
|
||||||
|
* @return 父目录路径
|
||||||
|
*/
|
||||||
|
std::string getParentPath(const std::string &path) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取绝对路径
|
||||||
|
* @param path 相对或绝对路径
|
||||||
|
* @return 绝对路径
|
||||||
|
*/
|
||||||
|
std::string getAbsolutePath(const std::string &path) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取规范路径(解析符号链接和相对路径)
|
||||||
|
* @param path 路径
|
||||||
|
* @return 规范化路径
|
||||||
|
*/
|
||||||
|
std::string getCanonicalPath(const std::string &path) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 合并两个路径
|
||||||
|
* @param left 左侧路径
|
||||||
|
* @param right 右侧路径
|
||||||
|
* @return 合并后的路径
|
||||||
|
*/
|
||||||
|
std::string combinePath(const std::string &left,
|
||||||
|
const std::string &right) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 规范化路径(转换为系统首选格式)
|
||||||
|
* @param path 路径
|
||||||
|
* @return 规范化路径
|
||||||
|
*/
|
||||||
|
std::string normalizePath(const std::string &path) const;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// 工作目录
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取当前工作目录
|
||||||
|
* @return 当前工作目录路径
|
||||||
|
*/
|
||||||
|
std::string getCurrentPath() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置当前工作目录
|
||||||
|
* @param path 目录路径
|
||||||
|
* @return 设置成功返回 true
|
||||||
|
*/
|
||||||
|
bool setCurrentPath(const std::string &path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置资源工作目录
|
||||||
|
*
|
||||||
|
* 设置后,所有相对路径操作都将基于此目录。
|
||||||
|
*
|
||||||
|
* @param path 工作目录路径
|
||||||
|
*/
|
||||||
|
void setWorkingDirectory(const std::string &path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取资源工作目录
|
||||||
|
* @return 工作目录路径
|
||||||
|
*/
|
||||||
|
const std::string &getWorkingDirectory() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 解析相对路径为完整路径
|
||||||
|
* @param relativePath 相对路径
|
||||||
|
* @return 完整路径
|
||||||
|
*/
|
||||||
|
std::string resolvePath(const std::string &relativePath) const;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// 资源根目录
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置资源根目录
|
||||||
|
* @param root 资源根目录路径
|
||||||
|
*/
|
||||||
|
void setAssetRoot(const std::string &root);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取资源根目录
|
||||||
|
* @return 资源根目录路径
|
||||||
|
*/
|
||||||
|
const std::string &getAssetRoot() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 解析资源相对路径
|
||||||
|
*
|
||||||
|
* 将相对路径解析为:工作目录/资源根目录/相对路径
|
||||||
|
*
|
||||||
|
* @param relativePath 相对路径
|
||||||
|
* @return 完整路径
|
||||||
|
*/
|
||||||
|
std::string resolveAssetPath(const std::string &relativePath) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Asset() = default;
|
||||||
|
~Asset() = default;
|
||||||
|
|
||||||
|
fs::path toPath(const std::string &path) const;
|
||||||
|
std::string fromPath(const fs::path &path) const;
|
||||||
|
std::string resolveFullPath(const std::string &path) const;
|
||||||
|
|
||||||
|
std::string workingDirectory_;
|
||||||
|
std::string assetRoot_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace frostbite2D
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include <fostbite2D/core/application.h>
|
#include <fostbite2D/core/application.h>
|
||||||
|
#include <fostbite2D/platform/switch.h>
|
||||||
namespace frostbite2D {
|
namespace frostbite2D {
|
||||||
|
|
||||||
Application &Application::get() {
|
Application &Application::get() {
|
||||||
|
|
@ -34,6 +35,11 @@ bool Application::init(const AppConfig &config) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 平台相关初始化
|
||||||
|
#ifdef __SWITCH__
|
||||||
|
switchInit();
|
||||||
|
#endif
|
||||||
|
|
||||||
this->window_ = new Window();
|
this->window_ = new Window();
|
||||||
|
|
||||||
if (!window_->create(config.windowConfig)) {
|
if (!window_->create(config.windowConfig)) {
|
||||||
|
|
@ -48,11 +54,15 @@ void Application::shutdown() {
|
||||||
if (!initialized_)
|
if (!initialized_)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// VRAMMgr::get().printStats();
|
if (window_) {
|
||||||
|
window_->destroy();
|
||||||
|
window_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// ServiceLocator::instance().clear();
|
// 平台相关清理
|
||||||
|
#ifdef __SWITCH__
|
||||||
// window_ = nullptr;
|
switchShutdown();
|
||||||
|
#endif
|
||||||
|
|
||||||
initialized_ = false;
|
initialized_ = false;
|
||||||
running_ = false;
|
running_ = false;
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,15 @@ bool Window::create(const WindowConfig &cfg) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::destroy() {}
|
void Window::destroy() {
|
||||||
|
if (sdlWindow_) {
|
||||||
|
SDL_GL_DeleteContext(glContext_);
|
||||||
|
glContext_ = nullptr;
|
||||||
|
SDL_DestroyWindow(sdlWindow_);
|
||||||
|
sdlWindow_ = nullptr;
|
||||||
|
}
|
||||||
|
SDL_Quit();
|
||||||
|
}
|
||||||
|
|
||||||
void Window::poll() {}
|
void Window::poll() {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
#include <fostbite2D/platform/switch.h>
|
||||||
|
|
||||||
|
#ifdef __SWITCH__
|
||||||
|
namespace frostbite2D {
|
||||||
|
void switchInit() {
|
||||||
|
// 初始化Switch平台相关的资源
|
||||||
|
socketInitializeDefault();
|
||||||
|
nxlinkStdio();
|
||||||
|
}
|
||||||
|
|
||||||
|
void switchShutdown() {
|
||||||
|
// 清理Switch平台相关的资源
|
||||||
|
socketExit();
|
||||||
|
}
|
||||||
|
} // namespace frostbite2D
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,435 @@
|
||||||
|
#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
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include <fostbite2D/core/application.h>
|
#include <fostbite2D/core/application.h>
|
||||||
#include <fostbite2D/core/window.h>
|
#include <fostbite2D/core/window.h>
|
||||||
|
#include <fostbite2D/utils/asset.h>
|
||||||
|
|
||||||
#include <glad/glad.h>
|
#include <glad/glad.h>
|
||||||
|
|
||||||
using namespace frostbite2D;
|
using namespace frostbite2D;
|
||||||
|
|
@ -22,6 +24,16 @@ int main(int argc, char **argv) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Asset &asset = Asset::get();
|
||||||
|
// asset.setWorkingDirectory("I:/DOF/骑士团");
|
||||||
|
asset.setWorkingDirectory("/switch/testgame");
|
||||||
|
std::string content;
|
||||||
|
if (asset.readTextFile("test.txt", content)) {
|
||||||
|
SDL_Log("test.txt content: %s", content.c_str());
|
||||||
|
} else {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to read test.txt!");
|
||||||
|
}
|
||||||
|
|
||||||
app.shutdown();
|
app.shutdown();
|
||||||
|
|
||||||
SDL_Log("程序正常退出");
|
SDL_Log("程序正常退出");
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ target("Frostbite2D")
|
||||||
set_toolset("strip", path.join(devkitA64, "bin/aarch64-none-elf-strip.exe"))
|
set_toolset("strip", path.join(devkitA64, "bin/aarch64-none-elf-strip.exe"))
|
||||||
|
|
||||||
-- 架构标志
|
-- 架构标志
|
||||||
local arch_flags = "-march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE"
|
local arch_flags = "-march=armv8-a+crc+crypto -mtune=cortex-a57 -fPIE"
|
||||||
add_cxflags(arch_flags)
|
add_cxflags(arch_flags)
|
||||||
-- 使用 devkitPro 提供的 switch.specs 文件
|
-- 使用 devkitPro 提供的 switch.specs 文件
|
||||||
add_ldflags("-specs=" .. path.join(devkitPro, "libnx/switch.specs"), "-g", arch_flags)
|
add_ldflags("-specs=" .. path.join(devkitPro, "libnx/switch.specs"), "-g", arch_flags)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue