feat(资源加载): 替换stb_image为SDL_image并添加脚本解析功能

- 将stb_image替换为SDL_image以解决Switch平台兼容性问题
- 添加PVF资源包解析器和脚本解析器功能
- 修改各平台配置文件添加SDL_image依赖
- 更新纹理加载逻辑使用SDL_image API
- 新增脚本解析相关类用于处理游戏脚本数据
This commit is contained in:
2026-03-18 04:18:57 +08:00
parent a4883b433e
commit cb9f497fbb
17 changed files with 1647 additions and 32 deletions

View File

@@ -0,0 +1,46 @@
# 清理 Xmake 缓存并重新构建计划
## 问题描述
- 已在 `sprite.cpp` 中添加 `printf("LoadA")` 调试代码
- 但运行时该调试语句未执行
- 说明 xmake 仍在使用旧的编译缓存
## 解决方案
### 步骤 1: 清理 Xmake 缓存
运行以下命令清理所有编译缓存:
```bash
xmake clean -a
```
或者更彻底的清理(删除整个 build 目录):
```bash
xmake clean
rmdir /s /q build # Windows
# 或者
rm -rf build # Linux/Mac
```
### 步骤 2: 重新配置项目(可选)
为了确保配置也是最新的:
```bash
xmake f -c
```
### 步骤 3: 重新构建 Switch 版本
```bash
xmake build -p switch
```
## 验证步骤
1. 清理完成后,查看 build 目录是否被删除
2. 重新构建时,应该能看到所有源文件被重新编译
3. 运行新构建的 NRO 文件,验证 `printf("LoadA")` 是否输出
## 预期结果
- 所有源文件被重新编译
- 新的修改生效
- `printf("LoadA")` 能够正常输出
- 可以进一步定位真正的崩溃位置

View File

@@ -0,0 +1,66 @@
# 修复 SDL2_image 链接错误计划
## 问题描述
链接时出现大量 undefined reference 错误,主要是关于 libjpeg 的函数:
- `jpeg_calc_output_dimensions`
- `jpeg_CreateDecompress`
- `jpeg_destroy_decompress`
- `jpeg_finish_decompress`
等等
## 问题原因
SDL2_image 依赖多个第三方库来支持不同的图片格式:
- libjpeg - JPEG 格式支持
- libpng - PNG 格式支持
- libtiff - TIFF 格式支持
- libwebp - WebP 格式支持
等等
需要在 switch.lua 中添加这些依赖库的链接。
## 修复方案
### 修改 platform/switch.lua
`add_syslinks` 中添加 SDL2_image 所需的依赖库。
根据 devkitPro/portlibs 的通常配置,需要添加:
- libpng
- libjpeg
- zlib (libpng 依赖)
- 可能还需要其他库
## 具体修复代码
修改 `platform/switch.lua` 中的 `add_syslinks` 部分:
```lua
-- 修改前
add_syslinks("SDL2_mixer", "SDL2_image", "SDL2", "opusfile", "opus", "vorbisidec", "ogg",
"modplug", "mpg123", "FLAC", "GLESv2", "EGL", "glapi", "drm_nouveau",
{public = true})
-- 修改后
add_syslinks("SDL2_mixer", "SDL2_image", "SDL2",
"png", "jpeg", "z", -- SDL2_image 依赖
"opusfile", "opus", "vorbisidec", "ogg",
"modplug", "mpg123", "FLAC", "GLESv2", "EGL", "glapi", "drm_nouveau",
{public = true})
```
## 库的顺序说明
链接顺序很重要,被依赖的库要放在后面:
- SDL2_image 依赖 png、jpeg、z
- png 依赖 z
- 所以顺序是: SDL2_image → png → jpeg → z
## 备选方案
如果上面的库还不够,可以尝试添加更多库:
- `tiff` - TIFF 支持
- `webp` - WebP 支持
- `lzma` - 某些图片格式可能需要
## 测试步骤
1. 应用修复
2. 清理缓存
3. 重新编译
4. 验证链接成功

View File

@@ -0,0 +1,169 @@
# 将 stb_image 替换为 SDL_image 完整计划
## 概述
将项目中的 stb_image 完全替换为 SDL_image以解决 Switch 平台兼容性问题。
## 修改文件清单
### 1. 平台配置文件3个文件
- `platform/mingw.lua` - Windows/Mingw 配置
- `platform/linux.lua` - Linux 配置
- `platform/switch.lua` - Switch 配置
### 2. 核心代码文件1个文件
- `Frostbite2D/src/frostbite2D/graphics/texture.cpp` - 纹理加载实现
## 详细修改方案
### 修改 1: platform/mingw.lua
**变更**: 添加 SDL_image 依赖
```lua
-- 修改前
add_requires("libsdl2", {configs = {shared = true}})
add_requires("glm")
-- 修改后
add_requires("libsdl2", {configs = {shared = true}})
add_requires("libsdl2_image", {configs = {shared = true}})
add_requires("glm")
-- 在 add_packages 部分添加
add_packages("libsdl2")
add_packages("libsdl2_image")
add_packages("glm")
```
### 修改 2: platform/linux.lua
**变更**: 添加 SDL_image 依赖
```lua
-- 修改前
add_requires("libsdl2", {configs = {shared = true,wayland = true}})
add_requires("glm")
-- 修改后
add_requires("libsdl2", {configs = {shared = true,wayland = true}})
add_requires("libsdl2_image")
add_requires("glm")
-- 在 add_packages 部分添加
add_packages("libsdl2")
add_packages("libsdl2_image")
add_packages("glm")
```
### 修改 3: platform/switch.lua
**变更**: 添加 SDL2_image 库链接
```lua
-- 修改前
add_syslinks("SDL2_mixer", "SDL2", "opusfile", "opus", "vorbisidec", "ogg",
"modplug", "mpg123", "FLAC", "GLESv2", "EGL", "glapi", "drm_nouveau",
{public = true})
-- 修改后
add_syslinks("SDL2_mixer", "SDL2_image", "SDL2", "opusfile", "opus", "vorbisidec", "ogg",
"modplug", "mpg123", "FLAC", "GLESv2", "EGL", "glapi", "drm_nouveau",
{public = true})
```
### 修改 4: texture.cpp
**变更**: 完全重写 `loadFromFile` 函数,使用 SDL_image
```cpp
#include <cstdio>
#include <cstdlib>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
// 移除所有 stb_image 相关的定义和 include
// #define STB_IMAGE_IMPLEMENTATION
// #include <stb/stb_image.h>
namespace frostbite2D {
Ptr<Texture> Texture::loadFromFile(const std::string& path) {
Asset& asset = Asset::get();
std::string resolvedPath = asset.resolveAssetPath(path);
std::vector<uint8> fileData;
SDL_Log("LoadQ");
if (!asset.readBinaryFile(path, fileData)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to read texture file: %s", resolvedPath.c_str());
return nullptr;
}
SDL_Log("LoadQ1: file size = %zu bytes", fileData.size());
if (fileData.empty()) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Texture file is empty: %s", resolvedPath.c_str());
return nullptr;
}
// 使用 SDL_image 从内存加载
SDL_RWops* rw = SDL_RWFromConstMem(fileData.data(), static_cast<int>(fileData.size()));
if (!rw) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create RWops: %s", SDL_GetError());
return nullptr;
}
SDL_Surface* surface = IMG_Load_RW(rw, 1);
if (!surface) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load image: %s", IMG_GetError());
SDL_FreeRW(rw);
return nullptr;
}
SDL_Log("LoadQ2: %dx%d, format: %d", surface->w, surface->h, surface->format->format);
// 确定格式
GLenum format;
int channels;
if (surface->format->BytesPerPixel == 4) {
format = GL_RGBA;
channels = 4;
} else {
format = GL_RGB;
channels = 3;
}
int width = surface->w;
int height = surface->h;
uint32_t textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, surface->pixels);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
SDL_FreeSurface(surface);
auto texture = Ptr<Texture>(new Texture(width, height, textureID));
texture->path_ = resolvedPath;
texture->channels_ = channels;
SDL_Log("Loaded texture: %s (%dx%d, %d channels)", resolvedPath.c_str(), width, height, channels);
return texture;
}
// ... 其余函数保持不变
}
```
## 实施步骤
1. 修改 3 个平台配置文件,添加 SDL_image 依赖
2. 修改 texture.cpp替换 stb_image 为 SDL_image
3. 清理 xmake 缓存
4. 重新编译项目
5. 测试所有平台
## 注意事项
- SDL_image 支持的图片格式包括: PNG, JPEG, BMP, GIF, TIFF, WebP 等
- 确保所有平台都能正确链接 SDL_image 库
- Switch 平台的 devkitPro/portlibs 应该已经包含 SDL2_image

View File

@@ -0,0 +1,74 @@
# stbi_load_from_memory 失败调试和修复计划
## 问题描述
`stbi_load_from_memory()` 执行失败,但不知道具体失败原因。
## 修复方案
### 1. 添加详细的调试信息
修改 `texture.cpp`,添加以下调试信息:
- 输出读取到的文件大小
- 输出 stbi 的失败原因(使用 `stbi_failure_reason()`
### 2. 修改代码
**文件**: `Frostbite2D/src/frostbite2D/graphics/texture.cpp`
**变更**:
```cpp
Ptr<Texture> Texture::loadFromFile(const std::string& path) {
Asset& asset = Asset::get();
std::string resolvedPath = asset.resolveAssetPath(path);
std::vector<uint8> fileData;
SDL_Log("LoadQ");
if (!asset.readBinaryFile(path, fileData)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to read texture file: %s", resolvedPath.c_str());
return nullptr;
}
SDL_Log("LoadQ1: file size = %zu bytes", fileData.size());
// 检查文件是否为空
if (fileData.empty()) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Texture file is empty: %s", resolvedPath.c_str());
return nullptr;
}
int width, height, channels;
uint8* data = stbi_load_from_memory(fileData.data(), static_cast<int>(fileData.size()), &width, &height, &channels, 0);
SDL_Log("LoadQ2");
if (!data) {
const char* failureReason = stbi_failure_reason();
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load texture: %s, reason: %s", resolvedPath.c_str(), failureReason ? failureReason : "unknown");
return nullptr;
}
SDL_Log("LoadQ3: %dx%d, %d channels", width, height, channels);
// ... 其余代码保持不变
}
```
## 调试步骤
1. 应用上述修改
2. 重新编译并运行
3. 查看日志输出,特别注意:
- `file size` 的值是多少
- `stbi_failure_reason()` 输出的失败原因是什么
## 根据失败原因的可能解决方案
### 如果失败原因是 "png" 相关
- 检查 PNG 文件是否损坏
- 尝试用其他图片格式(如 BMP测试
### 如果失败原因是 "out of memory"
- 检查图片是否太大
- 考虑调整 stbi 的内存分配设置
### 如果文件大小为 0
- 检查文件路径是否正确
- 检查 Asset 类的读取是否有问题
- 检查文件是否存在于 Switch 上
### 如果是其他原因
- 根据具体的失败原因进一步分析

View File

@@ -0,0 +1,68 @@
# Switch 平台 stbi_load_from_memory 崩溃修复计划
## 问题描述
- 文件读取成功129244 字节
- 但调用 `stbi_load_from_memory()` 时直接崩溃
- 说明 stbi_image 在 Switch 平台上有兼容性问题
## 可能的原因
1. **stbi_image 的内存分配问题** - stbi 默认使用 malloc/free在 Switch 上可能有问题
2. **缺少 stbi_image 的平台特定配置**
3. **SIMD 优化问题** - stbi_image 的 ARM NEON 优化在 Switch 上可能有问题
4. **栈溢出** - stbi_image 可能在栈上分配了太大的结构
## 修复方案
### 方案 1: 配置 stbi_image 使用自定义内存分配(推荐)
修改 texture.cpp在 include stb_image.h 之前定义自定义的内存分配函数。
### 方案 2: 禁用 stbi_image 的 SIMD 优化
定义 `STBI_NO_SIMD` 来禁用 NEON 优化。
### 方案 3: 两者结合(最稳妥)
同时使用自定义内存分配和禁用 SIMD 优化。
## 具体修复代码
修改 `Frostbite2D/src/frostbite2D/graphics/texture.cpp` 的开头部分:
```cpp
#include <cstdio>
#include <cstdlib>
// 自定义 stbi_image 的内存分配函数
#define STBI_MALLOC(sz) malloc(sz)
#define STBI_REALLOC(p,sz) realloc(p,sz)
#define STBI_FREE(p) free(p)
// 禁用 SIMD 优化,避免 Switch 平台兼容性问题
#define STBI_NO_SIMD
#define STB_IMAGE_IMPLEMENTATION
#include "SDL_log.h"
#include <SDL2/SDL.h>
#include <frostbite2D/graphics/texture.h>
#include <frostbite2D/utils/asset.h>
#include <stb/stb_image.h>
#include <glad/glad.h>
```
## 额外调试建议
如果上述修复后仍然崩溃,可以添加更多调试来定位具体位置:
1. 在 stbi_load_from_memory 调用前后添加 printf
2. 尝试用最简单的图片(小尺寸 BMP测试
3. 检查 stbi_image 的版本
## 测试步骤
1. 应用修复
2. 清理缓存并重新编译
3. 运行测试
4. 查看是否仍然崩溃
## 预期结果
- stbi_load_from_memory 不再崩溃
- 图片能够正常加载
- LoadQ2 和 LoadQ3 日志能够正常输出

View File

@@ -0,0 +1,74 @@
# Switch 平台 Asset 类崩溃修复计划
## 问题描述
调用 `Sprite::createFromFile("assets/player.png")` 时崩溃,甚至连 printf 都没执行。崩溃发生在 `Asset::readBinaryFile()` 内部的 `fs::exists()` 调用上。
## 问题根因分析
Switch 平台上的 `std::filesystem` 实现可能不稳定或不完整,导致 `fs::exists()` 调用直接崩溃。
## 修复方案
修改 `Asset::readBinaryFile()` 函数,**移除文件存在性检查**,直接尝试打开文件。这样可以避免调用有问题的 `fs::exists()`
### 修复: 修改 asset.cpp
**文件**: `Frostbite2D/src/frostbite2D/utils/asset.cpp`
**变更**:
- 移除 `readBinaryFile()` 中的 `exists()` 检查
- 直接尝试打开文件,通过 `file.is_open()` 来判断是否成功
**代码修改**:
```cpp
bool Asset::readBinaryFile(const std::string &path,
std::vector<uint8> &outData) {
std::string fullPath = resolveFullPath(path);
// 移除 fs::exists() 检查,直接尝试打开文件
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;
}
```
### 可选: 同时修复 readTextFile
为了保持一致性,也可以对 `readTextFile()` 做同样的修改:
```cpp
bool Asset::readTextFile(const std::string &path, std::string &outContent) {
std::string fullPath = resolveFullPath(path);
// 移除 fs::exists() 检查,直接尝试打开文件
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;
}
```
## 测试步骤
1. 应用修复
2. 重新构建 Switch 版本: `xmake build -p switch`
3. 在 Switch 上运行生成的 NRO 文件
4. 验证图片能正常加载
## 预期结果
- 不再崩溃
- 文件能正常读取
- 图片能正常加载和显示

View File

@@ -0,0 +1,51 @@
# Switch 平台 stbi_load 崩溃修复计划
## 问题描述
在 Switch 平台运行时,调用 `Sprite::createFromFile("assets/player.png")` 会在 `stbi_load` 这一行崩溃。
## 修复方案(简化版)
只修改一个文件:使用 Asset 类读取文件到内存,再用 `stbi_load_from_memory` 加载。
### 修复: 修改 texture.cpp
**文件**: `Frostbite2D/src/frostbite2D/graphics/texture.cpp`
**变更**:
- 使用 `Asset::readBinaryFile()` 读取文件到内存
- 使用 `stbi_load_from_memory()` 替代 `stbi_load()`
**代码修改**:
```cpp
Ptr<Texture> Texture::loadFromFile(const std::string& path) {
Asset& asset = Asset::get();
std::vector<uint8> fileData;
if (!asset.readBinaryFile(path, fileData)) {
std::string resolvedPath = asset.resolveAssetPath(path);
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to read texture file: %s", resolvedPath.c_str());
return nullptr;
}
int width, height, channels;
uint8* data = stbi_load_from_memory(fileData.data(), static_cast<int>(fileData.size()), &width, &height, &channels, 0);
if (!data) {
std::string resolvedPath = asset.resolveAssetPath(path);
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load texture: %s", resolvedPath.c_str());
return nullptr;
}
// ... 其余代码保持不变
}
```
## 测试步骤
1. 应用修复
2. 重新构建 Switch 版本: `xmake build -p switch`
3. 在 Switch 上运行生成的 NRO 文件
4. 验证图片能正常加载
## 预期结果
- stbi_load 不再崩溃
- 图片能正常加载和显示
- 程序能正常运行