最轻量方式是将png/jpg按序拼接为二进制块,每张图前加含name_len、data_len、format的header,用分块read()校验gcount(),vector存数据,异或混淆防提取。

怎么把 PNG/JPG 文件塞进二进制文件里(不依赖 ZIP)
直接把多个图片按顺序拼成一个二进制块,是最轻量、启动最快的方式。不需要第三方库,C++ 标准库 std::ifstream 和 std::ofstream 就够用。关键不是“怎么读写”,而是“怎么让程序知道每张图从哪开始、到哪结束”。
常见错误是只存原始像素数据,没留元信息——结果运行时根本分不清哪段是 icon.png,哪段是 bg.jpg,更别说识别格式或尺寸了。
- 必须在资源包开头或每张图前加一段描述头(header),至少含:
name_len、data_len、format(如 "png" 或 "jpg") - 文件名建议用 null-terminated C string,避免长度硬编码;实际数据紧接 header 后面,不补零、不对齐
- 别用
std::ios::binary | std::ios::ate一次性读整个文件到内存再写——大图容易崩,改用循环read()分块处理更稳
怎么在 C++ 程序里安全读出某张图的原始字节
加载时不能假设资源包永远合法。用户可能替换包、传输损坏、甚至故意拖个空文件进来——所以每个读取步骤都得校验,而不是靠 eof() 或 fail() 最后才报错。
典型崩溃点:header 里声明 data_len = 2048,但实际只剩 100 字节可读,read() 不满不报错,后续解码就崩。
- 每次
read()后立刻检查gcount(),确保读够指定字节数 - 用
std::vector<uint8_t></uint8_t>接收数据,别用裸new uint8_t[]——析构自动清理,避免资源包反复加载泄漏 - 查图名时用
std::string_view做常量比较,别转std::string再==,少一次分配
为什么不用 std::filesystem::path 而用 char* 处理文件路径
std::filesystem 在 Windows 上默认用宽字符(wchar_t),而资源打包工具通常跑在命令行环境,输入路径来自 argv,就是 char*。混用会触发隐式转换,Windows 下容易乱码或跳过文件。
尤其当路径含中文或特殊符号(比如 C:\assets\图标\),std::filesystem::path(argv[1]) 可能解析失败,但你看到的错误只是 “No such file”,根本不知道是编码问题。
- 打包阶段统一用
const char*接收路径,用fopen(filename, "rb")打开,绕过std::filesystem的编码层 - 资源包内只存文件名(不含盘符/路径),例如
"ui/button.png",避免绝对路径绑定 - 如果真要支持 Unicode 路径,宁可用 WinAPI 的
CreateFileA+CP_UTF8,也别赌std::filesystem的跨平台一致性
资源包格式不加加密,但怎么防被轻易拖出来
纯二进制打包本身不防提取——只要有人 hex 编辑器扫一遍,找到 PNG 的 \x89PNG 或 JPG 的 \xff\xd8\xff 签名,就能手动切出原图。所谓“防提取”其实是提高门槛,不是杜绝。
最容易被忽略的是:加个简单异或(XOR)比不加强得多,而且不影响运行时性能。别碰 AES、base64 这类——前者要密钥管理,后者膨胀体积,对资源加载纯属添堵。
- 打包时对每个文件的 data 区域做
byte ^= 0x5a(单字节 key 足够混淆) - 加载时同样异或一遍,CPU 几个周期的事,比 memcmp 还快
- key 别硬写在代码里,至少藏在几个分散的 const 变量相加结果中,防字符串扫描
- 别试图压缩——LZ4 要额外链接,zlib 有 license 风险,且游戏贴图本就是有损压缩过的,再压收益极小
真正麻烦的从来不是打包动作本身,而是资源更新时新旧包结构不一致、header 版本没升级、或者某张图悄悄改了格式但没改 header 里的 format 字段。这些地方没日志、没断言、没校验,出问题只能靠截图反推。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











