rres 是一种简单易用的资源打包文件格式
rres
旨在将游戏资产(图像、字体、文本、音频、模型...)打包成简单的独立综合格式,易于读写,旨在以快速高效的方式加载数据。
rres
灵感来自以下文件格式:XNB(由 XNA/MonoGame 使用)、RIFF、PNG 和 ZIP。
rresFileHeader
rresResourceChunk
rresResourceChunkInfo
rresResourceChunkData
rresResourceDataType
rresCentralDir
简单性: rres
结构简单,一个文件头,多个资源依次排列。尽管如此,每个资源都有一个小的(32 字节)资源信息标头,其中包含多个可用选项。
易于使用:用于读取.rres
文件的rres
库很小,只有从其 id 读取资源数据所需的最少功能。它仅使用标准 C 库中的一小部分函数。提供带有 GUI/CLI 的rrespacker
工具,可以轻松创建和查看.rres
文件。
灵活性: rres
格式支持任何类型的输入文件,如果数据不属于基本数据类型,则可以将其打包为原始文件数据。
可移植性: rres
文件格式不依赖于特定引擎,基础库仅读取打包的资源数据,并且该数据可以映射到任何引擎结构。辅助库rres-raylib.h
提供了一个使用示例,它将 rres 数据映射到 raylib 结构。
安全性:如果需要, rres
支持按资源压缩和加密。尽管如此,尽管文件格式提供了支持,但用户仍需要在rres
打包工具和引擎映射库中实现它。 rres
不强制设计任何特定的压缩/加密算法
可扩展性: rres
文件格式是可扩展的,如果需要,支持新数据类型、新功能和自定义数据。
免费开源: rres
是一个开放规范和免费开源库。它可以与提供的工具( rrespacker
)一起使用,或者可以为任何引擎实现自定义打包器/加载器。对于任何愿意创建自己的数据打包文件格式的人来说,格式描述也可以用作学习材料。
将游戏资产数据打包成像rres
这样的格式有一些重要的原因,这里有一些这样做的充分理由。
组织:避免在最终游戏构建中将数千个文件分布在数百个目录中。所有游戏文件数据都可以组织在一个或几个.rres
文件中。它使游戏目录保持干净且结构良好。
性能:将关卡/地图所需的所有文件保存到单个.rres
文件中,减少了访问多个文件时对文件句柄的需求;文件句柄是操作系统资源,打开文件的成本并不是微不足道的,它可能会导致游戏延迟。 .rres
文件内的数据访问时间也很重要,并且根据文件的组织方式,它还可以缩短加载时间。
安全性:避免将所有游戏资产直接暴露给用户以便于复制或修改。对于大多数用户来说,打包到.rres
文件中的数据将更难以提取和修改;保护您的游戏版权资产。 rres
还支持按资源进行数据压缩和加密,在需要时增加额外的安全级别。
下载时间:如果需要从服务器访问并下载数据,则与单独下载每个文件相比,将资源打包到单个或几个.rres
文件中可以缩短下载时间。
rres
自 2014 年以来一直在开发中。我开始这个项目的目的是为 raylib 创建一个类似于 XNB 的打包文件格式。在过去8 年中,该项目在文件格式学习过程中经历了多次重新设计和改进。在那段时间,我实现了20 多种不同文件格式的加载器/写入器,并为多个 raylibtech 自定义工具创建了12 种自定义文件格式。
rres
文件格式至少经历了4 次重大重新设计:
该格式的第一个设计仅限于打包一个又一个资源,每个资源都包含一个资源信息标头,后跟一组固定的四个可能参数和资源数据。沿着.rres
文件,生成了一个.h
头文件映射,定义了带有资源文件名的rresId
(通常是未处理数据的原始文件名)。该模型非常简单直观,但它有一些重要的缺点:它没有考虑可能需要多个块的复杂数据片段,并且无法提取/检索原始源文件(或类似的源文件)。
第二个设计要复杂得多,并试图解决第一个设计的缺点。在第二种设计中,每个资源都可以包含树结构中的多个数据块。实际上,该设计类似于 RIFF 文件格式:每个块都可以包含附加块,每个块都有其标头和数据。还添加了一些额外的资源选项,但格式变得非常难以理解和管理。最后,测试实现被放弃,并研究了更简单的替代方案。
第三种设计是对第一种设计的回归:简单但保留了个别资源的一些选项。使用资源信息标头中的一个简单偏移字段(在需要时指向下一个链接资源)解决了从单个输入文件生成多个资源的问题。资源作为资源块数组加载。添加了可选的中央目录资源块以保留输入文件的引用。格式很好,但仍然需要实现和进一步研究,它需要与引擎无关,并且仍然依赖于 raylib 结构和功能。
在实施过程中已经完成了第四次设计,为了一致性和简单性,几乎所有的结构和字段都经过了审查和重命名。已创建一个单独的库( rres-raylib.h
)来将加载的资源数据映射到自定义库/引擎数据类型,对 raylib 的任何依赖项已被删除,使其成为完全与引擎无关的文件格式。实现了几个raylib的使用示例来说明rres
的库引擎连接,并实现了多种类型的资源加载。 rrespacker
工具是从头开始创建的,用于生成rres
文件,它支持带有拖放支持的漂亮 GUI 界面,而且还支持用于批处理的强大命令行。压缩和加密实现已移至用户库实现,以与打包工具保持一致,并保持rres
文件格式更干净、更简单。
这是一个为期8 年的项目,断断续续地进行,进行了多次重新设计和修改,但我个人对最终结果非常满意。 rres
是一种资源打包文件格式,处于任何专业引擎包格式的级别,但免费且开源,可供任何想要使用它、实现它或创建自定义版本的游戏开发人员使用。
rres 文件格式由文件头 ( rresFileHeader
) 和后跟的多个资源块 ( rresResourceChunk
) 组成。每个资源块都有一个资源信息标头 ( rresResourceChunkInfo
),其中包括FOURCC
数据类型代码和资源数据信息。资源数据 ( rresResourceChunkData
) 包含一小组用于标识数据的属性,具体取决于类型,并且可能在末尾包含一些附加数据。
图 01.rres v1.0 文件结构。
注意:rresResourceChunk 是从输入文件生成的。需要注意的是,资源无法 1:1 映射到文件,一个输入文件可以生成多个资源块。例如,.ttf 输入文件可以生成图像资源块( RRES_DATA_IMAGE
类型)加上字体字形信息资源块( RRES_DATA_FONT_GLYPHS
类型)。
rresFileHeader ( 16 bytes )
Signature Id ( 4 bytes ) // File signature id: 'rres'
Version ( 2 bytes ) // Format version
Resource Count ( 2 bytes ) // Number of resource chunks contained
CD Offset ( 4 bytes ) // Central Directory offset (if available)
Reserved ( 4 bytes ) //
rresResourceChunk []
{
rresResourceChunkInfo ( 32 bytes )
Type ( 4 bytes ) // Resource type (FourCC)
Id ( 4 bytes ) // Resource identifier (CRC32 filename hash or custom)
Compressor ( 1 byte ) // Data compression algorithm
Cipher ( 1 byte ) // Data encryption algorithm
Flags ( 2 bytes ) // Data flags (if required)
Packed data Size ( 4 bytes ) // Packed data size (compressed/encrypted + custom data appended)
Base data Size ( 4 bytes ) // Base data size (uncompressed/unencrypted)
Next Offset ( 4 bytes ) // Next resource chunk offset (if required)
Reserved ( 4 bytes ) //
CRC32 ( 4 bytes ) // Resource Chunk Data CRC32
rresResourceChunkData ( n bytes ) // Packed data
Property Count ( 4 bytes ) // Number of properties contained
Properties [] ( 4 * i bytes ) // Resource data required properties, depend on Type
Data ( m bytes ) // Resource data
}
rresFileHeader
以下 C 结构体定义了rresFileHeader
:
// rres file header (16 bytes)
typedef struct rresFileHeader {
unsigned char id [ 4 ]; // File identifier: rres
unsigned short version ; // File version: 100 for version 1.0
unsigned short chunkCount ; // Number of resource chunks in the file (MAX: 65535)
unsigned int cdOffset ; // Central Directory offset in file (0 if not available)
unsigned int reserved ; //
} rresFileHeader ;
场地 | 描述 |
---|---|
id | 文件签名标识符,必须是四个字符: r , r , e , s 。 |
version | 定义格式的版本和颠覆。 |
chunkCount | 文件中存在的资源块的数量。请注意,它可能大于已处理的输入文件的数量。 |
cdOffset | Central Directory 在文件内的绝对偏移量,请注意, CDIR 只是另一种资源块类型, Central Directory 可以存在于文件中,也可以不存在。如果实现了自定义rres 打包程序,建议将其放置为文件中的最后一个块。检查rresCentralDir 部分以获取更多详细信息。 |
reserved | 如果需要,此字段保留供将来添加。 |
表 01. rresFileHeader
字段描述和详细信息
注意事项:
rres
文件在设计上被限制为最多 65535 个资源块,如果需要打包更多资源,建议创建多个rres
文件。rres
文件使用 32 位偏移量来寻址不同的资源块,因此,不能寻址超过 ~4GB 的数据,请保持rres
文件小于4GB 。如果打包资源需要更多空间,请创建多个rres
文件。 rresResourceChunk
rres
文件包含许多资源块。每个资源块都代表一个独立的数据包。资源块是由rres
packer 工具在创建rres
文件时根据输入文件生成的;根据文件扩展名, rres
打包工具从文件中提取所需的数据并生成一个或多个资源块。例如,对于图像文件,会生成RRES_DATA_IMAGE
type
的资源块,仅包含图像的像素数据以及从资源文件读回该数据所需的属性。
需要注意的是,创建rres
文件时,一个输入文件可能会生成多个资源块。例如, .ttf
输入文件可以生成RRES_DATA_IMAGE
资源块和RRES_DATA_FONT_GLYPHS
资源块;也可以将文件打包为普通RRES_DATA_RAW
资源块类型,在这种情况下,输入文件不会被处理,只是打包为原始数据。
在创建rres
时, rres
packer 可以创建一个RRES_DATA_DIRECTORY
类型的附加资源块,其中包含有关已处理输入文件的数据。它在某些情况下可能很有用,例如将输入文件名直接与生成的资源 ID 相关联,以及以与原始输入类似的文件结构提取数据。
每个资源块分为两部分: rresResourceChunkInfo
+ rresResourceData
。
rresResourceChunkInfo
以下 C 结构体定义了rresResourceChunkInfo
:
// rres resource chunk info header (32 bytes)
typedef struct rresResourceChunkInfo {
unsigned char type [ 4 ]; // Resource chunk type (FourCC)
unsigned int id ; // Resource chunk identifier (generated from filename CRC32 hash)
unsigned char compType ; // Data compression algorithm
unsigned char cipherType ; // Data encription algorithm
unsigned short flags ; // Data flags (if required)
unsigned int packedSize ; // Data chunk size (compressed/encrypted + custom data appended)
unsigned int baseSize ; // Data base size (uncompressed/unencrypted)
unsigned int nextOffset ; // Next resource chunk global offset (if resource has multiple chunks)
unsigned int reserved ; //
unsigned int crc32 ; // Data chunk CRC32 (propCount + props[] + data)
} rresResourceChunkInfo ;
场地 | 描述 |
---|---|
type | FourCC 代码并标识rresResourceChunkData 中包含的资源数据的类型。枚举rresResourceDataType 定义了多种数据类型,如果需要可以添加新的数据类型。 |
id | 全局资源标识符,它是使用 CRC32 哈希从输入文件名生成的,并且它不是唯一的。一个输入文件可以生成多个资源块,所有生成的资源块共享相同的标识符,并且在加载资源时将它们一起加载。例如,输入 .ttf 可以生成两个具有相同标识符的资源块 ( RRES_DATA_IMAGE + RRES_DATA_FONT_GLYPHS ),当请求其标识符时,这两个资源块将一起加载。由用户决定如何处理加载的数据。 |
compType | 定义用于资源块数据的压缩算法。压缩取决于 rres 和引擎之间的中间库, rres.h 只是定义了一些在实现压缩时使用的有用算法值。应始终在加密之前应用压缩,它会压缩完整的rresResourceData ( Property Count + Properties[] + Data )。如果未应用数据加密, packedSize 定义压缩数据的大小。 |
cipherType | 定义用于资源块数据的加密算法。与压缩一样,加密也依赖于 rres 和引擎之间的中间库, rres.h 只是定义了一些在实现加密时使用的有用算法值。压缩后应应用加密。根据加密算法和加密模式,可能需要将一些额外的数据附加到资源数据(即加密 MAC),这取决于实现,引擎的 rres 打包工具/rres 中间库负责管理该数据额外的数据。建议仅将其附加到资源数据中并在packedSize 上考虑。 |
flags | 保留附加标志,以防实现需要它们。 |
packedSize | 定义rresResourceChunkData 的打包大小(压缩/加密 + 附加用户数据)。打包数据可以在压缩/加密数据之后在末尾包含附加的用户数据,例如加密数据的随机数/MAC,但它与实现相关,由rres 打包工具和用户库管理以将块数据加载到其中目标发动机结构。 |
baseSize | 定义rresResourceChunkData 的基本大小(未压缩/未加密)。 |
nextOffset | 定义下一个相关资源块的全局文件位置地址,这对于生成多个资源(如字体或网格)的输入文件很有用。 |
reserved | 如果需要,此字段保留供将来添加。 |
crc32 | 在完整的rresResourceData 块 ( packedSize ) 上计算,旨在检测数据损坏错误。 |
表 02. rresResourceChunkInfo
字段描述和详细信息
rresResourceChunkData
rresResourceChunkData
包含以下数据:
Property Count
:包含的属性数量,取决于资源type
Properties[]
:资源数据所需属性,取决于资源type
Data
:资源原始数据,取决于资源type
注意: rresResourceChunkData 可能包含其他用户数据,在这种情况下,必须在packedSize
中考虑其他数据大小。
rresResourceDataType
rresResourceChunkInfo
中指定的资源type
定义了资源块中包含的数据类型和属性数量。
这里是当前定义的数据类型。请注意,某些输入文件可能会生成多种类型的多个资源块。如果需要,可以添加其他资源类型。
// rres resource chunk data type
// NOTE 1: Data type determines the properties and the data included in every chunk
// NOTE 2: This enum defines the basic resource data types, some input files could generate multiple resource chunks
typedef enum rresResourceDataType {
RRES_DATA_NULL = 0 , // FourCC: NULL - Reserved for empty chunks, no props/data
RRES_DATA_RAW = 1 , // FourCC: RAWD - Raw file data, input file is not processed, just packed as is
RRES_DATA_TEXT = 2 , // FourCC: TEXT - Text file data, byte data extracted from text file
RRES_DATA_IMAGE = 3 , // FourCC: IMGE - Image file data, pixel data extracted from image file
RRES_DATA_WAVE = 4 , // FourCC: WAVE - Audio file data, samples data extracted from audio file
RRES_DATA_VERTEX = 5 , // FourCC: VRTX - Vertex file data, extracted from a mesh file
RRES_DATA_FONT_GLYPHS = 6 , // FourCC: FNTG - Font glyphs info, generated from an input font file
RRES_DATA_LINK = 99 , // FourCC: LINK - External linked file, filepath as provided on file input
RRES_DATA_DIRECTORY = 100 , // FourCC: CDIR - Central directory for input files relation to resource chunks
// TODO: Add additional data types if required
} rresResourceDataType ;
当前定义的数据types
由以下属性和数据组成:
资源类型 | 四CC | 道具数量 | 道具 | 数据 |
---|---|---|---|---|
RRES_DATA_NULL | NULL | 0 | - | - |
RRES_DATA_RAW | RAWD | 4 | props[0] :大小props[1] : extension01 props[2] : extension02 props[3] :保留 | 原始文件字节 |
RRES_DATA_TEXT | TEXT | 4 | props[0] :大小props[1] : rresTextEncoding props[2] : rresCodeLang props[3] :文化代码 | 文本数据 |
RRES_DATA_IMAGE | IMGE | 4 | props[0] :宽度props[1] :高度props[2] : rresPixelFormat props[3] :mipmap | 像素数据 |
RRES_DATA_WAVE | WAVE | 4 | props[0] :帧数props[1] :采样率props[2] :样本大小props[3] :通道 | 音频样本数据 |
RRES_DATA_VERTEX | VRTX | 4 | props[0] :顶点计数props[1] : rresVertexAttribute props[2] :组件计数props[3] : rresVertexFormat | 顶点数据 |
RRES_DATA_FONT_GLYPHS | FNTG | 4 | props[0] :baseSizeprops[1] :字形计数props[2] :glyphPaddingprops[3] : rresFontStyle | rresFontGlyphInfo[0..glyphCount] |
RRES_DATA_LINK | LINK | 1 | props[0] :大小 | 文件路径数据 |
RRES_DATA_DIRECTORY | CDIR | 1 | props[0] :条目计数 | rresDirEntry[0..entryCount] |
表 03. rresResourceDataType
定义的值和详细信息
注意: RRES_DATA_RAW
包含打包文件扩展名作为其属性的一部分, char
扩展名转换为 4 字节unsigned int
大端值,以点开头。即".png"
=> 0x2e706e67
。如果扩展不相关,用户实现可以决定将这些属性设置为0
。
rres.h
定义了以下enums
以便于分配一些属性:
rresTextEncoding
:定义几种可能的文本编码,默认值为0(UTF-8)rresCodeLang
:定义多种编程语言,在嵌入代码文件或脚本时有用rresPixelFormat
:定义图像像素数据的多个像素格式值rresVertexAttribute
:为了方便定义了几种顶点属性类型rresVertexFormat
:定义顶点数据的几种数据格式rresFontStyle
:定义几种字体样式(常规、粗体、斜体...),默认值为 0 ( RRES_FONT_STYLE_DEFAULT
)rresCentralDir
Central Directory
资源块是一个特殊的块,可能存在于rres
文件中,也可能不存在,它存储有关处理以生成多个资源块的输入文件的信息,并且可用于:
按原始文件名引用资源。如果rres
打包是在现有项目上完成的,或者在项目开发结束时完成的,如果所有文件加载都是直接使用文件名完成的,这在实现方面非常有用,可以最大限度地减少所需的代码更改。
将一些资源提取到类似的输入文件中。仅当输入文件未被以破坏性方式处理时才有可能。例如,如果 .ttf 文件已被处理以生成字形图像图集 + 字形数据信息,则将无法检索原始 .ttf 文件。
rres
提供了一些有用的结构来处理Central Directory
资源块数据:
// rres central directory entry
typedef struct rresDirEntry {
unsigned int id ; // Resource id
unsigned int offset ; // Resource global offset in file
unsigned int reserved ; // reserved
unsigned int fileNameSize ; // Resource fileName size (NULL terminator and 4-byte alignment padding considered)
char fileName [ RRES_MAX_CDIR_FILENAME_LENGTH ]; // Resource original fileName (NULL terminated and padded to 4-byte alignment)
} rresDirEntry ;
// rres central directory
// NOTE: This data represents the rresResourceChunkData
typedef struct rresCentralDir {
unsigned int count ; // Central directory entries count
rresDirEntry * entries ; // Central directory entries
} rresCentralDir ;
注意:中央目录文件名条目与 4 字节填充对齐,以缩短文件访问时间。
rres.h
提供了一个从rres
文件加载Central Directory
如果可用)的函数: rresLoadCentralDirectory()
以及一个从原始文件名获取资源标识符的函数: rresGetIdFromFileName()
。
如果生成的rres
文件没有Central Directory
,则应提供辅助头文件 ( .h
),其中包含所有资源的 id 引用,以便在用户代码中使用。
rres
被设计为与引擎无关的文件格式,可以与任何游戏引擎一起使用。采用rres
的开发人员可以实现自定义库和自定义抽象,以将rres
通用数据映射到他们自己的引擎数据结构以及自定义rres
打包工具。
下图显示了raylib
库的rres
实现示例。
图 02.rres 示例实现:自定义引擎库和工具。
rres
实现由几个部分组成:
rres.h
rres-raylib.h
rrespacker
rres.h
Base rres
库负责将rres
文件资源块读取到通用资源结构中,返回给用户。用户公开的资源结构rresResourceChunk
遵循rres
规范结构 ( rresResourceChunkInfo
+ rresResourceChunkData
)。提供了以下结构:
rresResourceChunk
包含带有用户所需信息和数据的单个块: rresResourceChunkInfo
+ rresResourceChunkData
rresresourceChunkInfo
包含有关加载的资源块的信息rresResourceChunkData
包含资源的实际数据:所需的属性和原始数据。需要注意的是,在数据被压缩/加密的情况下,由用户库 ( rres-raylib.h
) 来处理该数据;在这些情况下chunk.data.raw
包含压缩/加密数据且chunk.data.propCount = 0
且chunk.data.props = NULL
;解压/解密后由用户库填充属性。rresResourceMulti
包含已处理输入文件的多个rresResourceChunks
// rres resource chunk
typedef struct rresResourceChunk {
rresResourceChunkInfo info ; // Resource chunk info
rresResourceChunkData data ; // Resource chunk packed data, contains propCount, props[] and raw data
} rresResourceChunk ;
// rres resource chunk info header (32 bytes)
typedef struct rresResourceChunkInfo {
unsigned char type [ 4 ]; // Resource chunk type (FourCC)
unsigned int id ; // Resource chunk identifier (generated from filename CRC32 hash)
unsigned char compType ; // Data compression algorithm
unsigned char cipherType ; // Data encription algorithm
unsigned short flags ; // Data flags (if required)
unsigned int packedSize ; // Data chunk size (compressed/encrypted + custom data appended)
unsigned int baseSize ; // Data base size (uncompressed/unencrypted)
unsigned int nextOffset ; // Next resource chunk global offset (if resource has multiple chunks)
unsigned int reserved ; //
unsigned int crc32 ; // Data chunk CRC32 (propCount + props[] + data)
} rresResourceChunkInfo ;
// rres resource chunk data
typedef struct rresResourceChunkData {
unsigned int propCount ; // Resource chunk properties count
unsigned int * props ; // Resource chunk properties
void * raw ; // Resource chunk raw data
} rresResourceChunkData ;
// rres resource multi
// NOTE: It supports multiple resource chunks
typedef struct rresResourceMulti {
unsigned int count ; // Resource chunks count
rresResourceChunk * chunks ; // Resource chunks
} rresResourceMulti ;
可以使用提供的函数rresLoadResourceChunk()
从.rres
文件加载单个rresResourceChunk
,并使用rresUnloadResourceChunk()
卸载。
可以使用提供的函数rresLoadResourceMulti()
从.rres
文件加载完整的rresResourceMulti
,并使用rresUnloadResourceMulti()
卸载。
rres-raylib.h
映射库包括rres.h
并提供将从rres
文件加载的资源块数据映射到raylib
结构的功能。提供的 API 简单直观,遵循raylib
约定:
RLAPI void * LoadDataFromResource ( rresResourceChunk chunk , int * size ); // Load raw data from rres resource chunk
RLAPI char * LoadTextFromResource ( rresResourceChunk chunk ); // Load text data from rres resource chunk
RLAPI Image LoadImageFromResource ( rresResourceChunk chunk ); // Load Image data from rres resource chunk
RLAPI Wave LoadWaveFromResource ( rresResourceChunk chunk ); // Load Wave data from rres resource chunk
RLAPI Font LoadFontFromResource ( rresResourceMulti multi ); // Load Font data from rres resource multiple chunks
RLAPI Mesh LoadMeshFromResource ( rresResourceMulti multi ); // Load Mesh data from rres resource multiple chunks
RLAPI int UnpackResourceChunk ( rresResourceChunk * chunk ); // Unpack resource chunk data (decompres/decrypt data)
RLAPI void SetBaseDirectory ( const char * baseDir ); // Set base directory for externally linked data
请注意,数据解压/解密是在此自定义库中实现的,为用户提供了UnpackResourceChunk()
。为了方便起见, rresResourceChunk
包含压缩器/密码标识符值。压缩器和密码支持取决于用户实现,并且必须与打包工具( rrespacker
)保持一致。
rres
文件格式与引擎无关,可以使用任何编程语言为任何引擎/框架创建库和工具。
rrespacker
rrespacker
是rres
打包工具,负责处理所有输入文件并按照规范创建rres
文件。如果支持某些压缩/加密算法,则必须由该工具实现,并且映射库应支持相同的算法,在我们的例子中为rres-raylib.h
。
图03. rrespacker工具,GUI界面,还支持CLI进行批处理。
rrespacker
可以从 itch.io 在线使用(或下载)。
rres
文件格式规范、 rres.h
库和rres-raylib.h
库均根据 MIT 许可证获得许可。检查许可证以了解更多详细信息。
版权所有 (c) 2014-2024 拉蒙·桑塔玛丽亚 (@raysan5)