diff --git a/.gitignore b/.gitignore index 46ea449..f44438a 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ build/ *.json .vscode/compile_commands.json +参考代码/ +*.pvf diff --git a/Frostbite2D/include/frostbite2D/utils/binary_reader.h b/Frostbite2D/include/frostbite2D/utils/binary_reader.h new file mode 100644 index 0000000..91d8b24 --- /dev/null +++ b/Frostbite2D/include/frostbite2D/utils/binary_reader.h @@ -0,0 +1,269 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace frostbite2D { + +/** + * @brief 二进制文件读取器 + * + * 将整个二进制文件加载到内存中,提供便捷的读取接口。 + * 支持各种基本类型的读取、定位操作和 CRC 解码。 + * + * @example + * BinaryReader reader("data.bin"); + * if (reader.isOpen()) { + * int32 value = reader.read(); + * std::string str = reader.readString(10); + * } + */ +class BinaryReader { +public: + /** + * @brief 默认构造函数 + */ + BinaryReader() = default; + + /** + * @brief 从文件构造 + * @param filePath 文件路径 + */ + explicit BinaryReader(const std::string& filePath); + + /** + * @brief 从内存数据构造 + * @param data 数据指针 + * @param size 数据大小 + */ + BinaryReader(const char* data, size_t size); + + /** + * @brief 析构函数 + */ + ~BinaryReader() = default; + + // --------------------------------------------------------------------------- + // 文件操作 + // --------------------------------------------------------------------------- + + /** + * @brief 打开文件 + * @param filePath 文件路径 + * @return 打开成功返回 true + */ + bool open(const std::string& filePath); + + /** + * @brief 从内存加载数据 + * @param data 数据指针 + * @param size 数据大小 + */ + void loadFromMemory(const char* data, size_t size); + + /** + * @brief 关闭并清空数据 + */ + void close(); + + /** + * @brief 检查文件是否成功打开 + * @return 已打开返回 true + */ + bool isOpen() const; + + // --------------------------------------------------------------------------- + // 位置操作 + // --------------------------------------------------------------------------- + + /** + * @brief 获取当前读取位置 + * @return 当前位置(字节偏移) + */ + size_t tell() const; + + /** + * @brief 设置读取位置 + * @param pos 目标位置(字节偏移) + */ + void seek(size_t pos); + + /** + * @brief 跳过指定字节数 + * @param count 要跳过的字节数 + */ + void skip(size_t count); + + /** + * @brief 检查是否已到达文件末尾 + * @return 到达末尾返回 true + */ + bool eof() const; + + // --------------------------------------------------------------------------- + // 信息获取 + // --------------------------------------------------------------------------- + + /** + * @brief 获取数据总大小 + * @return 数据大小(字节) + */ + size_t size() const; + + /** + * @brief 获取剩余可读取的字节数 + * @return 剩余字节数 + */ + size_t remaining() const; + + /** + * @brief 获取上一次读取的字节数 + * @return 上次读取的字节数 + */ + size_t lastReadCount() const; + + // --------------------------------------------------------------------------- + // 原始数据读取 + // --------------------------------------------------------------------------- + + /** + * @brief 读取原始字节数据 + * @param buffer 输出缓冲区 + * @param size 要读取的字节数 + * @return 实际读取的字节数 + */ + size_t read(char* buffer, size_t size); + + /** + * @brief 读取原始字节数据到 vector + * @param size 要读取的字节数 + * @return 读取的数据 + */ + std::vector readBytes(size_t size); + + /** + * @brief 获取数据指针(不移动读取位置) + * @return 当前位置的数据指针 + */ + const char* data() const; + + /** + * @brief 获取当前位置的数据指针(不移动读取位置) + * @return 当前位置的数据指针 + */ + const char* currentData() const; + + // --------------------------------------------------------------------------- + // 类型化读取(模板方法) + // --------------------------------------------------------------------------- + + /** + * @brief 读取指定类型的值 + * @tparam T 要读取的类型 + * @return 读取的值 + */ + template + T read() { + T value; + read(reinterpret_cast(&value), sizeof(T)); + return value; + } + + /** + * @brief 读取 int8 + * @return 读取的值 + */ + int8 readInt8(); + + /** + * @brief 读取 int16 + * @return 读取的值 + */ + int16 readInt16(); + + /** + * @brief 读取 int32 + * @return 读取的值 + */ + int32 readInt32(); + + /** + * @brief 读取 int64 + * @return 读取的值 + */ + int64 readInt64(); + + /** + * @brief 读取 uint8 + * @return 读取的值 + */ + uint8 readUInt8(); + + /** + * @brief 读取 uint16 + * @return 读取的值 + */ + uint16 readUInt16(); + + /** + * @brief 读取 uint32 + * @return 读取的值 + */ + uint32 readUInt32(); + + /** + * @brief 读取 uint64 + * @return 读取的值 + */ + uint64 readUInt64(); + + /** + * @brief 读取 float + * @return 读取的值 + */ + float readFloat(); + + /** + * @brief 读取 double + * @return 读取的值 + */ + double readDouble(); + + // --------------------------------------------------------------------------- + // 字符串读取 + // --------------------------------------------------------------------------- + + /** + * @brief 读取指定长度的字符串 + * @param length 字符串长度(字节) + * @return 读取的字符串 + */ + std::string readString(size_t length); + + /** + * @brief 读取以 null 结尾的字符串 + * @return 读取的字符串 + */ + std::string readNullTerminatedString(); + + // --------------------------------------------------------------------------- + // CRC 解码 + // --------------------------------------------------------------------------- + + /** + * @brief CRC 解码 + * @param length 要解码的数据长度(字节) + * @param crc32 CRC32 值 + */ + void crcDecode(size_t length, uint32 crc32); + +private: + std::vector data_; ///< 存储的数据 + size_t position_ = 0; ///< 当前读取位置 + size_t lastReadCount_ = 0; ///< 上一次读取的字节数 +}; + +} // namespace frostbite2D diff --git a/Frostbite2D/src/frostbite2D/graphics/renderer.cpp b/Frostbite2D/src/frostbite2D/graphics/renderer.cpp index 853c6f1..b369768 100644 --- a/Frostbite2D/src/frostbite2D/graphics/renderer.cpp +++ b/Frostbite2D/src/frostbite2D/graphics/renderer.cpp @@ -28,7 +28,7 @@ bool Renderer::init() { } //初始化着色器管理器 - if (!shaderManager_.init("shaders")) { + if (!shaderManager_.init("assets/shaders")) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize shader manager"); return false; } diff --git a/Frostbite2D/src/frostbite2D/utils/binary_reader.cpp b/Frostbite2D/src/frostbite2D/utils/binary_reader.cpp new file mode 100644 index 0000000..55b3971 --- /dev/null +++ b/Frostbite2D/src/frostbite2D/utils/binary_reader.cpp @@ -0,0 +1,235 @@ +#include +#include +#include +#include + +namespace frostbite2D { + +BinaryReader::BinaryReader(const std::string& filePath) { + open(filePath); +} + +BinaryReader::BinaryReader(const char* data, size_t size) { + loadFromMemory(data, size); +} + +bool BinaryReader::open(const std::string& filePath) { + close(); + + Asset& asset = Asset::get(); + auto dataOpt = asset.readFileToBytes(filePath); + + if (!dataOpt.has_value()) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "BinaryReader: 无法打开文件: %s", filePath.c_str()); + return false; + } + + const std::vector& bytes = dataOpt.value(); + data_.resize(bytes.size()); + std::memcpy(data_.data(), bytes.data(), bytes.size()); + + position_ = 0; + lastReadCount_ = 0; + return true; +} + +void BinaryReader::loadFromMemory(const char* data, size_t size) { + close(); + + if (data && size > 0) { + data_.resize(size); + std::memcpy(data_.data(), data, size); + } + + position_ = 0; + lastReadCount_ = 0; +} + +void BinaryReader::close() { + data_.clear(); + position_ = 0; + lastReadCount_ = 0; +} + +bool BinaryReader::isOpen() const { + return !data_.empty(); +} + +size_t BinaryReader::tell() const { + return position_; +} + +void BinaryReader::seek(size_t pos) { + position_ = std::clamp(pos, static_cast(0), data_.size()); +} + +void BinaryReader::skip(size_t count) { + seek(position_ + count); +} + +bool BinaryReader::eof() const { + return position_ >= data_.size(); +} + +size_t BinaryReader::size() const { + return data_.size(); +} + +size_t BinaryReader::remaining() const { + if (position_ >= data_.size()) { + return 0; + } + return data_.size() - position_; +} + +size_t BinaryReader::lastReadCount() const { + return lastReadCount_; +} + +size_t BinaryReader::read(char* buffer, size_t size) { + if (!buffer || size == 0 || eof()) { + lastReadCount_ = 0; + return 0; + } + + size_t bytesToRead = std::min(size, remaining()); + std::memcpy(buffer, data_.data() + position_, bytesToRead); + position_ += bytesToRead; + lastReadCount_ = bytesToRead; + + if (bytesToRead != size) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "BinaryReader: 读取数据不完整,期望 %zu 字节,实际读取 %zu 字节", size, bytesToRead); + } + + return bytesToRead; +} + +std::vector BinaryReader::readBytes(size_t size) { + std::vector result; + size_t bytesToRead = std::min(size, remaining()); + + if (bytesToRead > 0) { + result.resize(bytesToRead); + read(reinterpret_cast(result.data()), bytesToRead); + } + + return result; +} + +const char* BinaryReader::data() const { + return data_.data(); +} + +const char* BinaryReader::currentData() const { + if (eof()) { + return nullptr; + } + return data_.data() + position_; +} + +int8 BinaryReader::readInt8() { + return read(); +} + +int16 BinaryReader::readInt16() { + return read(); +} + +int32 BinaryReader::readInt32() { + return read(); +} + +int64 BinaryReader::readInt64() { + return read(); +} + +uint8 BinaryReader::readUInt8() { + return read(); +} + +uint16 BinaryReader::readUInt16() { + return read(); +} + +uint32 BinaryReader::readUInt32() { + return read(); +} + +uint64 BinaryReader::readUInt64() { + return read(); +} + +float BinaryReader::readFloat() { + return read(); +} + +double BinaryReader::readDouble() { + return read(); +} + +std::string BinaryReader::readString(size_t length) { + if (length == 0 || eof()) { + return ""; + } + + size_t bytesToRead = std::min(length, remaining()); + std::string result(data_.data() + position_, bytesToRead); + position_ += bytesToRead; + lastReadCount_ = bytesToRead; + + if (bytesToRead != length) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "BinaryReader: 读取字符串不完整,期望 %zu 字节,实际读取 %zu 字节", length, bytesToRead); + } + + return result; +} + +std::string BinaryReader::readNullTerminatedString() { + if (eof()) { + return ""; + } + + size_t startPos = position_; + while (position_ < data_.size() && data_[position_] != '\0') { + ++position_; + } + + size_t length = position_ - startPos; + std::string result(data_.data() + startPos, length); + + if (position_ < data_.size()) { + ++position_; + } + + lastReadCount_ = length + 1; + return result; +} + +void BinaryReader::crcDecode(size_t length, uint32 crc32) { + if (length == 0 || eof()) { + return; + } + + const uint32 key = 0x81A79011; + size_t originalPos = position_; + size_t bytesToProcess = std::min(length, remaining()); + + for (size_t i = 0; i < bytesToProcess; i += 4) { + size_t pos = position_; + uint32 value = read(); + + uint32 decoded = (value ^ key ^ crc32); + decoded = (decoded >> 6) | ((decoded << (32 - 6)) & 0xFFFFFFFF); + + if (pos + 3 < data_.size()) { + data_[pos] = static_cast((decoded >> 0) & 0xFF); + data_[pos + 1] = static_cast((decoded >> 8) & 0xFF); + data_[pos + 2] = static_cast((decoded >> 16) & 0xFF); + data_[pos + 3] = static_cast((decoded >> 24) & 0xFF); + } + } + + seek(originalPos + bytesToProcess); +} + +} // namespace frostbite2D diff --git a/Game/shaders/colored_quad.frag b/Game/assets/shaders/colored_quad.frag similarity index 100% rename from Game/shaders/colored_quad.frag rename to Game/assets/shaders/colored_quad.frag diff --git a/Game/shaders/colored_quad.vert b/Game/assets/shaders/colored_quad.vert similarity index 100% rename from Game/shaders/colored_quad.vert rename to Game/assets/shaders/colored_quad.vert diff --git a/Game/shaders/sprite.frag b/Game/assets/shaders/sprite.frag similarity index 100% rename from Game/shaders/sprite.frag rename to Game/assets/shaders/sprite.frag diff --git a/Game/shaders/sprite.vert b/Game/assets/shaders/sprite.vert similarity index 100% rename from Game/shaders/sprite.vert rename to Game/assets/shaders/sprite.vert diff --git a/Game/src/main.cpp b/Game/src/main.cpp index 7846487..48f3ace 100644 --- a/Game/src/main.cpp +++ b/Game/src/main.cpp @@ -6,9 +6,11 @@ #include #include +#include #include #include -#include +#include + using namespace frostbite2D; @@ -36,7 +38,7 @@ int main(int argc, char **argv) { SDL_Log("Testing colored quad..."); // 尝试加载精灵 - auto sprite = Sprite::createFromFile("assets\\player.png"); + auto sprite = Sprite::createFromFile("assets/player.png"); if (sprite) { sprite->SetPosition(100, 100); menuScene->AddActor(sprite); @@ -45,6 +47,7 @@ int main(int argc, char **argv) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create sprite from file!"); } + app.run(); app.shutdown(); diff --git a/platform/linux.lua b/platform/linux.lua index 6616fab..76f1b4f 100644 --- a/platform/linux.lua +++ b/platform/linux.lua @@ -16,39 +16,15 @@ target("Frostbite2D") -- 复制着色器文件到输出目录 after_build(function (target) - -- 复制 shaders 目录 - local shaders_dir = path.join(os.projectdir(), "Game/shaders") + -- 复制 assets 目录 + local assets_dir = path.join(os.projectdir(), "Game/assets") local output_dir = target:targetdir() - local target_shaders_dir = path.join(output_dir, "shaders") + local target_assets_dir = path.join(output_dir, "assets") - if os.isdir(shaders_dir) then - -- 确保目标目录存在 - if not os.isdir(target_shaders_dir) then - os.mkdir(target_shaders_dir) - end - - -- 复制所有着色器文件 - for _, file in ipairs(os.files(path.join(shaders_dir, "*.*"))) do - local filename = path.filename(file) - local target_file = path.join(target_shaders_dir, filename) - os.cp(file, target_file) - end - end - - -- 复制图标文件到输出目录 - local icons_dir = path.join(os.projectdir(), "assets/icons") - local target_icons_dir = path.join(output_dir, "assets/icons") - - if os.isdir(icons_dir) then - if not os.isdir(target_icons_dir) then - os.mkdir(target_icons_dir) - end - for _, file in ipairs(os.files(path.join(icons_dir, "*.*"))) do - local filename = path.filename(file) - local target_file = path.join(target_icons_dir, filename) - os.cp(file, target_file) - print("Copy icon: " .. filename) - end + if os.isdir(assets_dir) then + os.rm(target_assets_dir) + os.cp(assets_dir, output_dir) + print("Copy assets directory: " .. assets_dir .. " -> " .. target_assets_dir) end end) target_end() diff --git a/platform/mingw.lua b/platform/mingw.lua index ec2151b..3ab8f48 100644 --- a/platform/mingw.lua +++ b/platform/mingw.lua @@ -16,26 +16,17 @@ target("Frostbite2D") add_packages("libsdl2") add_packages("glm") - -- 复制着色器文件到输出目录 + -- 复制 assets 目录到输出目录 after_build(function (target) - -- 复制 shaders 目录 - local shaders_dir = path.join(os.projectdir(), "Game/shaders") + -- 复制 assets 目录 + local assets_dir = path.join(os.projectdir(), "Game/assets") local output_dir = target:targetdir() - local target_shaders_dir = path.join(output_dir, "shaders") + local target_assets_dir = path.join(output_dir, "assets") - if os.isdir(shaders_dir) then - -- 确保目标目录存在 - if not os.isdir(target_shaders_dir) then - os.mkdir(target_shaders_dir) - end - - -- 复制所有着色器文件 - for _, file in ipairs(os.files(path.join(shaders_dir, "*.*"))) do - local filename = path.filename(file) - local target_file = path.join(target_shaders_dir, filename) - os.cp(file, target_file) - print("Copy shader: " .. filename) - end + if os.isdir(assets_dir) then + os.rm(target_assets_dir) + os.cp(assets_dir, output_dir) + print("Copy assets directory: " .. assets_dir .. " -> " .. target_assets_dir) end -- 复制 SDL2 DLL (Windows 平台) diff --git a/platform/switch.lua b/platform/switch.lua index 7c32ed5..4744e25 100644 --- a/platform/switch.lua +++ b/platform/switch.lua @@ -69,23 +69,15 @@ target("Frostbite2D") print("Generated NRO: " .. nro_file) end - -- 复制 shaders 目录 - local shaders_dir = path.join(os.projectdir(), "Game/shaders") - local target_shaders_dir = path.join(output_dir, "shaders") + -- 复制 assets 目录 + local assets_dir = path.join(os.projectdir(), "Game/assets") + local output_dir = target:targetdir() + local target_assets_dir = path.join(output_dir, "assets") - if os.isdir(shaders_dir) then - -- 确保目标目录存在 - if not os.isdir(target_shaders_dir) then - os.mkdir(target_shaders_dir) - end - - -- 复制所有着色器文件 - for _, file in ipairs(os.files(path.join(shaders_dir, "*.*"))) do - local filename = path.filename(file) - local target_file = path.join(target_shaders_dir, filename) - os.cp(file, target_file) - print("Copy shader: " .. filename) - end + if os.isdir(assets_dir) then + os.rm(target_assets_dir) + os.cp(assets_dir, output_dir) + print("Copy assets directory: " .. assets_dir .. " -> " .. target_assets_dir) end end) target_end()