497 lines
18 KiB
Go
497 lines
18 KiB
Go
package rres
|
|
|
|
/*
|
|
#define RRES_IMPLEMENTATION
|
|
#include <rres.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
rresResourceChunkInfo GetResourceChunkInfoFromArray(rresResourceChunkInfo *infos, int index)
|
|
{
|
|
return infos[index];
|
|
}
|
|
*/
|
|
import "C"
|
|
import (
|
|
"hash/crc32"
|
|
"unsafe"
|
|
)
|
|
|
|
const MaxFilenameSize = 1024
|
|
|
|
// FileHeader - (16 bytes)
|
|
type FileHeader struct {
|
|
Id [4]byte // File identifier: rres
|
|
Version uint16 // File version: 100 for version 1.0
|
|
ChunkCount uint16 // Number of resource chunks in the file (MAX: 65535)
|
|
CdOffset uint32 // Central Directory offset in file (0 if not available)
|
|
Reserved uint32 // <reserved>
|
|
}
|
|
|
|
// ResourceChunkInfo - header (32 bytes)
|
|
type ResourceChunkInfo struct {
|
|
Type [4]byte // Resource chunk type (FourCC)
|
|
Id uint32 // Resource chunk identifier (generated from filename CRC32 hash)
|
|
CompType byte // Data compression algorithm
|
|
CipherType byte // Data encryption algorithm
|
|
Flags uint16 // Data flags (if required)
|
|
PackedSize uint32 // Data chunk size (compressed/encrypted + custom data appended)
|
|
BaseSize uint32 // Data base size (uncompressed/unencrypted)
|
|
NextOffset uint32 // Next resource chunk global offset (if resource has multiple chunks)
|
|
Reserved uint32 // <reserved>
|
|
Crc32 uint32 // Data chunk CRC32 (propCount + props[] + data)
|
|
}
|
|
|
|
// ResourceChunkData
|
|
type ResourceChunkData struct {
|
|
PropCount uint32 // Resource chunk properties count
|
|
Props *uint32 // Resource chunk properties
|
|
Raw unsafe.Pointer // Resource chunk raw data
|
|
}
|
|
|
|
// ResourceChunk
|
|
type ResourceChunk struct {
|
|
Info ResourceChunkInfo // Resource chunk info
|
|
Data ResourceChunkData // Resource chunk packed data, contains propCount, props[] and raw data
|
|
}
|
|
|
|
// ResourceMulti
|
|
//
|
|
// NOTE: It supports multiple resource chunks
|
|
type ResourceMulti struct {
|
|
Count uint32 // Resource chunks count
|
|
chunks *ResourceChunk // Resource chunks
|
|
}
|
|
|
|
// Chunks - Resource chunks
|
|
func (r *ResourceMulti) Chunks() []ResourceChunk {
|
|
return unsafe.Slice(r.chunks, r.Count)
|
|
}
|
|
|
|
// DirEntry - CDIR: rres central directory entry
|
|
type DirEntry struct {
|
|
Id uint32 // Resource id
|
|
Offset uint32 // Resource global offset in file
|
|
Reserved uint32 // reserved
|
|
FileNameSize uint32 // Resource fileName size (NULL terminator and 4-byte alignment padding considered)
|
|
fileName [MaxFilenameSize]int8 // Resource original fileName (NULL terminated and padded to 4-byte alignment)
|
|
}
|
|
|
|
// FileName - Resource original fileName
|
|
func (d *DirEntry) FileName() string {
|
|
cpointer := (*C.char)(unsafe.Pointer(&d.fileName[0]))
|
|
clength := C.int(d.FileNameSize)
|
|
fileName := C.GoStringN(cpointer, clength)
|
|
return fileName
|
|
}
|
|
|
|
// CentralDir - CDIR: rres central directory
|
|
//
|
|
// NOTE: This data conforms the ResourceChunkData
|
|
type CentralDir struct {
|
|
Count uint32 // Central directory entries count
|
|
entries *DirEntry // Central directory entries
|
|
}
|
|
|
|
// Entries - Central directory entries
|
|
func (c *CentralDir) Entries() []DirEntry {
|
|
return unsafe.Slice(c.entries, c.Count)
|
|
}
|
|
|
|
// FontGlyphInfo - FNTG: rres font glyphs info (32 bytes)
|
|
//
|
|
// NOTE: Array of this type conforms the ResourceChunkData
|
|
type FontGlyphInfo struct {
|
|
X, Y, Width, Height int32 // Glyph rectangle in the atlas image
|
|
Value int32 // Glyph codepoint value
|
|
OffsetX, OffsetY int32 // Glyph drawing offset (from base line)
|
|
AdvanceX int32 // Glyph advance X for next character
|
|
}
|
|
|
|
// ResourceDataType
|
|
//
|
|
// 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:
|
|
//
|
|
// Fonts processed could generate (2) resource chunks:
|
|
// - [FNTG] rres[0]: RRES_DATA_FONT_GLYPHS
|
|
// - [IMGE] rres[1]: RRES_DATA_IMAGE
|
|
//
|
|
// Mesh processed could generate (n) resource chunks:
|
|
// - [VRTX] rres[0]: RRES_DATA_VERTEX
|
|
// ...
|
|
// - [VRTX] rres[n]: RRES_DATA_VERTEX
|
|
type ResourceDataType int32
|
|
|
|
const (
|
|
// FourCC: NULL - Reserved for empty chunks, no props/data
|
|
DataNull ResourceDataType = iota
|
|
// FourCC: RAWD - Raw file data, 4 properties
|
|
// props[0]:size (bytes)
|
|
// props[1]:extension01 (big-endian: ".png" = 0x2e706e67)
|
|
// props[2]:extension02 (additional part, extensions with +3 letters)
|
|
// props[3]:reserved
|
|
// data: raw bytes
|
|
DataRaw
|
|
// FourCC: TEXT - Text file data, 4 properties
|
|
// props[0]:size (bytes)
|
|
// props[1]:rresTextEncoding
|
|
// props[2]:rresCodeLang
|
|
// props[3]:cultureCode
|
|
// data: text
|
|
DataText
|
|
// FourCC: IMGE - Image file data, 4 properties
|
|
// props[0]:width
|
|
// props[1]:height
|
|
// props[2]:rresPixelFormat
|
|
// props[3]:mipmaps
|
|
// data: pixels
|
|
DataImage
|
|
// FourCC: WAVE - Audio file data, 4 properties
|
|
// props[0]:frameCount
|
|
// props[1]:sampleRate
|
|
// props[2]:sampleSize
|
|
// props[3]:channels
|
|
// data: samples
|
|
DataWave
|
|
// FourCC: VRTX - Vertex file data, 4 properties
|
|
// props[0]:vertexCount
|
|
// props[1]:rresVertexAttribute
|
|
// props[2]:componentCount
|
|
// props[3]:rresVertexFormat
|
|
// data: vertex
|
|
DataVertex
|
|
// FourCC: FNTG - Font glyphs info data, 4 properties
|
|
// props[0]:baseSize
|
|
// props[1]:glyphCount
|
|
// props[2]:glyphPadding
|
|
// props[3]:rresFontStyle
|
|
// data: rresFontGlyphInfo[0..glyphCount]
|
|
DataFontGlyphs
|
|
// FourCC: LINK - External linked file, 1 property
|
|
// props[0]:size (bytes)
|
|
// data: filepath (as provided on input)
|
|
DataLink ResourceDataType = 99
|
|
// FourCC: CDIR - Central directory for input files
|
|
// props[0]:entryCount, 1 property
|
|
// data: rresDirEntry[0..entryCount]
|
|
DataDirectory ResourceDataType = 100
|
|
)
|
|
|
|
// CompressionType - Compression algorithms
|
|
//
|
|
// value required by ResourceChunkInfo.CompType
|
|
//
|
|
// NOTE 1: This enum just lists some common data compression algorithms for convenience,
|
|
// The rres packer tool and the engine-specific library are responsible to implement the desired ones,
|
|
//
|
|
// NOTE 2: ResourceChunkInfo.CompType is a byte-size value, limited to [0..255]
|
|
type CompressionType int32
|
|
|
|
const (
|
|
CompNone CompressionType = 0 // No data compression
|
|
CompRle CompressionType = 1 // RLE compression
|
|
CompDeflate CompressionType = 10 // DEFLATE compression
|
|
CompLz4 CompressionType = 20 // LZ4 compression
|
|
CompLzma2 CompressionType = 30 // LZMA2 compression
|
|
CompQoi CompressionType = 40 // QOI compression, useful for RGB(A) image data
|
|
)
|
|
|
|
// EncryptionType - Encryption algorithms
|
|
//
|
|
// value required by ResourceChunkInfo.CipherType
|
|
//
|
|
// NOTE 1: This enum just lists some common data encryption algorithms for convenience,
|
|
// The rres packer tool and the engine-specific library are responsible to implement the desired ones,
|
|
//
|
|
// NOTE 2: Some encryption algorithms could require/generate additional data (seed, salt, nonce, MAC...)
|
|
// in those cases, that extra data must be appended to the original encrypted message and added to the resource data chunk
|
|
//
|
|
// NOTE 3: ResourceChunkInfo.CipherType is a byte-size value, limited to [0..255]
|
|
type EncryptionType int32
|
|
|
|
const (
|
|
CipherNone EncryptionType = 0 // No data encryption
|
|
CipherXor EncryptionType = 1 // XOR encryption, generic using 128bit key in blocks
|
|
CipherDes EncryptionType = 10 // DES encryption
|
|
CipherTdes EncryptionType = 11 // Triple DES encryption
|
|
CipherIdea EncryptionType = 20 // IDEA encryption
|
|
CipherAes EncryptionType = 30 // AES (128bit or 256bit) encryption
|
|
CipherAesGCM EncryptionType = 31 // AES Galois/Counter Mode (Galois Message Authentication Code - GMAC)
|
|
CipherXtea EncryptionType = 40 // XTEA encryption
|
|
CipherBlowfish EncryptionType = 50 // BLOWFISH encryption
|
|
CipherRsa EncryptionType = 60 // RSA asymmetric encryption
|
|
CipherSalsa20 EncryptionType = 70 // SALSA20 encryption
|
|
CipherChacha20 EncryptionType = 71 // CHACHA20 encryption
|
|
CipherXchacha20 EncryptionType = 72 // XCHACHA20 encryption
|
|
CipherXchacha20Poly1305 EncryptionType = 73 // XCHACHA20 with POLY1305 for message authentication (MAC)
|
|
)
|
|
|
|
// ErrorType - error codes
|
|
//
|
|
// NOTE: Error codes when processing rres files
|
|
type ErrorType int32
|
|
|
|
const (
|
|
Success ErrorType = iota // rres file loaded/saved successfully
|
|
ErrorFileNotFound // rres file can not be opened (spelling issues, file actually does not exist...)
|
|
ErrorFileFormat // rres file format not a supported (wrong header, wrong identifier)
|
|
ErrorMemoryAlloc // Memory could not be allocated for operation.
|
|
)
|
|
|
|
// TextEncoding - TEXT: Text encoding property values
|
|
type TextEncoding int32
|
|
|
|
const (
|
|
TextEncodingUndefined TextEncoding = 0 // Not defined, usually UTF-8
|
|
TextEncodingUtf8 TextEncoding = 1 // UTF-8 text encoding
|
|
TextEncodingUtf8Bom TextEncoding = 2 // UTF-8 text encoding with Byte-Order-Mark
|
|
TextEncodingUtf16Le TextEncoding = 10 // UTF-16 Little Endian text encoding
|
|
TextEncodingUtf16Be TextEncoding = 11 // UTF-16 Big Endian text encoding
|
|
)
|
|
|
|
// CodeLang - TEXT: Text code language
|
|
//
|
|
// NOTE: It could be useful for code script resources
|
|
type CodeLang int32
|
|
|
|
const (
|
|
CodeLangUndefined CodeLang = iota // Undefined code language, text is plain text
|
|
CodeLangC // Text contains C code
|
|
CodeLangCpp // Text contains C++ code
|
|
CodeLangCs // Text contains C# code
|
|
CodeLangLua // Text contains Lua code
|
|
CodeLangJs // Text contains JavaScript code
|
|
CodeLangPython // Text contains Python code
|
|
CodeLangRust // Text contains Rust code
|
|
CodeLangZig // Text contains Zig code
|
|
CodeLangOdin // Text contains Odin code
|
|
CodeLangJai // Text contains Jai code
|
|
CodeLangGdscript // Text contains GDScript (Godot) code
|
|
CodeLangGlsl // Text contains GLSL shader code
|
|
)
|
|
|
|
// PixelFormat - IMGE: Image/Texture pixel formats
|
|
type PixelFormat int32
|
|
|
|
const (
|
|
PixelFormatUndefined PixelFormat = iota // Undefined pixel format
|
|
PixelFormatUncompGrayscale // 8 bit per pixel (no alpha)
|
|
PixelFormatUncompGrayAlpha // 16 bpp (2 channels)
|
|
PixelFormatUncompR5g6b5 // 16 bpp
|
|
PixelFormatUncompR8g8b8 // 24 bpp
|
|
PixelFormatUncompR5g5b5a1 // 16 bpp (1 bit alpha)
|
|
PixelFormatUncompR4g4b4a4 // 16 bpp (4 bit alpha)
|
|
PixelFormatUncompR8g8b8a8 // 32 bpp
|
|
PixelFormatUncompR32 // 32 bpp (1 channel - float)
|
|
PixelFormatUncompR32g32b32 // 32*3 bpp (3 channels - float)
|
|
PixelFormatUncompR32g32b32a32 // 32*4 bpp (4 channels - float)
|
|
PixelFormatCompDxt1Rgb // 4 bpp (no alpha)
|
|
PixelFormatCompDxt1Rgba // 4 bpp (1 bit alpha)
|
|
PixelFormatCompDxt3Rgba // 8 bpp
|
|
PixelFormatCompDxt5Rgba // 8 bpp
|
|
PixelFormatCompEtc1Rgb // 4 bpp
|
|
PixelFormatCompEtc2Rgb // 4 bpp
|
|
PixelFormatCompEtc2EacRgba // 8 bpp
|
|
PixelFormatCompPvrtRgb // 4 bpp
|
|
PixelFormatCompPvrtRgba // 4 bpp
|
|
PixelFormatCompAtsc4x4Rgba // 8 bpp
|
|
PixelFormatCompAtsc8x8Rgba // 2 bpp
|
|
)
|
|
|
|
// VertexAttribute - VRTX: Vertex data attribute
|
|
//
|
|
// NOTE: The expected number of components for every vertex attribute is provided as a property to data,
|
|
// the listed components count are the expected/default ones
|
|
type VertexAttribute int32
|
|
|
|
const (
|
|
VertexAttributePosition VertexAttribute = 0 // Vertex position attribute: [x, y, z]
|
|
VertexAttributeTexcoord1 VertexAttribute = 10 // Vertex texture coordinates attribute: [u, v]
|
|
VertexAttributeTexcoord2 VertexAttribute = 11 // Vertex texture coordinates attribute: [u, v]
|
|
VertexAttributeTexcoord3 VertexAttribute = 12 // Vertex texture coordinates attribute: [u, v]
|
|
VertexAttributeTexcoord4 VertexAttribute = 13 // Vertex texture coordinates attribute: [u, v]
|
|
VertexAttributeNormal VertexAttribute = 20 // Vertex normal attribute: [x, y, z]
|
|
VertexAttributeTangent VertexAttribute = 30 // Vertex tangent attribute: [x, y, z, w]
|
|
VertexAttributeColor VertexAttribute = 40 // Vertex color attribute: [r, g, b, a]
|
|
VertexAttributeIndex VertexAttribute = 100 // Vertex index attribute: [i]
|
|
)
|
|
|
|
// VertexFormat - VRTX: Vertex data format type
|
|
type VertexFormat int32
|
|
|
|
const (
|
|
VertexFormatUbyte VertexFormat = iota // 8 bit unsigned integer data
|
|
VertexFormatByte // 8 bit signed integer data
|
|
VertexFormatUshort // 16 bit unsigned integer data
|
|
VertexFormatShort // 16 bit signed integer data
|
|
VertexFormatUint // 32 bit unsigned integer data
|
|
VertexFormatInt // 32 bit integer data
|
|
VertexFormatHfloat // 16 bit float data
|
|
VertexFormatFloat // 32 bit float data
|
|
)
|
|
|
|
// FontStyle - FNTG: Font style
|
|
type FontStyle int32
|
|
|
|
const (
|
|
FontStyleUndefined FontStyle = iota // Undefined font style
|
|
FontStyleRegular // Regular font style
|
|
FontStyleBold // Bold font style
|
|
FontStyleItalic // Italic font style
|
|
)
|
|
|
|
// LoadResourceChunk - Load one resource chunk for provided id
|
|
func LoadResourceChunk(fileName string, rresId int32) ResourceChunk {
|
|
cfileName := C.CString(fileName)
|
|
defer C.free(unsafe.Pointer(cfileName))
|
|
ret := C.rresLoadResourceChunk(cfileName, C.int(rresId))
|
|
v := *(*ResourceChunk)(unsafe.Pointer(&ret))
|
|
return v
|
|
}
|
|
|
|
// UnloadResourceChunk - Unload resource chunk from memory
|
|
func UnloadResourceChunk(chunk *ResourceChunk) {
|
|
cchunk := *(*C.rresResourceChunk)(unsafe.Pointer(chunk))
|
|
C.rresUnloadResourceChunk(cchunk)
|
|
}
|
|
|
|
// LoadResourceMulti - Load resource for provided id (multiple resource chunks)
|
|
func LoadResourceMulti(fileName string, rresId int32) ResourceMulti {
|
|
cfileName := C.CString(fileName)
|
|
defer C.free(unsafe.Pointer(cfileName))
|
|
ret := C.rresLoadResourceMulti(cfileName, C.int(rresId))
|
|
v := *(*ResourceMulti)(unsafe.Pointer(&ret))
|
|
return v
|
|
}
|
|
|
|
// UnloadResourceMulti - Unload resource from memory (multiple resource chunks)
|
|
func UnloadResourceMulti(multi *ResourceMulti) {
|
|
cmulti := *(*C.rresResourceMulti)(unsafe.Pointer(multi))
|
|
C.rresUnloadResourceMulti(cmulti)
|
|
}
|
|
|
|
// LoadResourceChunkInfo - Load resource chunk info for provided id
|
|
func LoadResourceChunkInfo(fileName string, rresId int32) ResourceChunkInfo {
|
|
cfileName := C.CString(fileName)
|
|
defer C.free(unsafe.Pointer(cfileName))
|
|
ret := C.rresLoadResourceChunkInfo(cfileName, C.int(rresId))
|
|
v := *(*ResourceChunkInfo)(unsafe.Pointer(&ret))
|
|
return v
|
|
}
|
|
|
|
// LoadResourceChunkInfoAll - Load all resource chunks info
|
|
func LoadResourceChunkInfoAll(fileName string) []ResourceChunkInfo {
|
|
// Convert the fileName into a CString and releases the memory afterwards
|
|
cfileName := C.CString(fileName)
|
|
defer C.free(unsafe.Pointer(cfileName))
|
|
|
|
// The length of the resulted array is saved in the chunkCount variable
|
|
var chunkCount C.uint
|
|
cinfos := C.rresLoadResourceChunkInfoAll(cfileName, &chunkCount)
|
|
|
|
// The C array can be released afterwards, because the values are stored in a golang slice
|
|
defer C.free(unsafe.Pointer(cinfos))
|
|
|
|
// Iterate over the C array and store the values in a golang slice
|
|
infos := make([]ResourceChunkInfo, chunkCount)
|
|
for i := 0; i < int(chunkCount); i++ {
|
|
// Get the C value from the C array
|
|
ret := C.GetResourceChunkInfoFromArray(cinfos, C.int(i))
|
|
// Convert the C value into a golang value
|
|
v := *(*ResourceChunkInfo)(unsafe.Pointer(&ret))
|
|
// Save the golang value in the golang slice
|
|
infos[i] = v
|
|
}
|
|
|
|
return infos
|
|
}
|
|
|
|
// LoadCentralDirectory - Load central directory resource chunk from file
|
|
func LoadCentralDirectory(fileName string) CentralDir {
|
|
cfileName := C.CString(fileName)
|
|
defer C.free(unsafe.Pointer(cfileName))
|
|
ret := C.rresLoadCentralDirectory(cfileName)
|
|
v := *(*CentralDir)(unsafe.Pointer(&ret))
|
|
return v
|
|
}
|
|
|
|
// UnloadCentralDirectory - Unload central directory resource chunk
|
|
func UnloadCentralDirectory(dir *CentralDir) {
|
|
cdir := *(*C.rresCentralDir)(unsafe.Pointer(dir))
|
|
C.rresUnloadCentralDirectory(cdir)
|
|
}
|
|
|
|
// GetDataType - Get ResourceDataType from FourCC code
|
|
func GetDataType(fourCC [4]byte) ResourceDataType {
|
|
value := string(fourCC[:])
|
|
switch value {
|
|
case "NULL":
|
|
return DataNull
|
|
case "RAWD":
|
|
return DataRaw
|
|
case "TEXT":
|
|
return DataText
|
|
case "IMGE":
|
|
return DataImage
|
|
case "WAVE":
|
|
return DataWave
|
|
case "VRTX":
|
|
return DataVertex
|
|
case "FNTG":
|
|
return DataFontGlyphs
|
|
case "LINK":
|
|
return DataLink
|
|
case "CDIR":
|
|
return DataDirectory
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
// GetResourceId - Get resource id for a provided filename
|
|
//
|
|
// NOTE: It requires CDIR available in the file (it's optinal by design)
|
|
func GetResourceId(dir CentralDir, fileName string) int32 {
|
|
cfileName := C.CString(fileName)
|
|
defer C.free(unsafe.Pointer(cfileName))
|
|
cdir := *(*C.rresCentralDir)(unsafe.Pointer(&dir))
|
|
ret := C.rresGetResourceId(cdir, cfileName)
|
|
v := int32(ret)
|
|
return v
|
|
}
|
|
|
|
// ComputeCRC32 - Compute CRC32 hash
|
|
//
|
|
// NOTE: CRC32 is used as rres id, generated from original filename
|
|
func ComputeCRC32(data []byte) uint32 {
|
|
return crc32.ChecksumIEEE(data)
|
|
}
|
|
|
|
// SetCipherPassword - Set password to be used on data decryption
|
|
//
|
|
// NOTE: The cipher password is kept as an internal pointer to provided string, it's up to the user to manage that sensible data properly
|
|
//
|
|
// Password should be to allocate and set before loading an encrypted resource and it should be cleaned/wiped after the encrypted resource has been loaded
|
|
//
|
|
// You can use the WipeCipherPassword function to clear the password
|
|
func SetCipherPassword(pass string) {
|
|
cpass := C.CString(pass)
|
|
C.rresSetCipherPassword(cpass)
|
|
}
|
|
|
|
// GetCipherPassword - Get password to be used on data decryption
|
|
func GetCipherPassword() string {
|
|
cpass := C.rresGetCipherPassword()
|
|
return C.GoString(cpass)
|
|
}
|
|
|
|
// WipeCipherPassword - Clears the password from the C memory using explicit_bzero
|
|
//
|
|
// This is an approach but no guarantee
|
|
func WipeCipherPassword() {
|
|
cpass := C.rresGetCipherPassword()
|
|
C.explicit_bzero(unsafe.Pointer(cpass), C.strlen(cpass))
|
|
C.free(unsafe.Pointer(cpass))
|
|
}
|