210 lines
4.6 KiB
Go
210 lines
4.6 KiB
Go
package raylib
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"crypto/des"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"unsafe"
|
|
|
|
"github.com/dsnet/compress/bzip2"
|
|
"github.com/klauspost/compress/flate"
|
|
"github.com/pierrec/lz4"
|
|
"github.com/rootlch/encrypt"
|
|
"github.com/ulikunitz/xz"
|
|
|
|
"github.com/gen2brain/raylib-go/rres"
|
|
)
|
|
|
|
// LoadResource - Load resource from file by id
|
|
// NOTE: Returns uncompressed data with parameters, search resource by id
|
|
func LoadResource(reader io.ReadSeeker, rresID int, key []byte) (data rres.Data) {
|
|
var fileHeader rres.FileHeader
|
|
var infoHeader rres.InfoHeader
|
|
|
|
reader.Seek(0, 0)
|
|
|
|
// Read rres file header
|
|
err := binary.Read(reader, binary.LittleEndian, &fileHeader)
|
|
if err != nil {
|
|
TraceLog(LogWarning, err.Error())
|
|
return
|
|
}
|
|
|
|
// Verify "rRES" identifier
|
|
id := fmt.Sprintf("%c", fileHeader.ID)
|
|
if id != "[r R E S]" {
|
|
TraceLog(LogWarning, "not a valid raylib resource file")
|
|
return
|
|
}
|
|
|
|
reader.Seek(int64(unsafe.Sizeof(fileHeader)), os.SEEK_CUR)
|
|
|
|
for i := 0; i < int(fileHeader.Count); i++ {
|
|
// Read resource info and parameters
|
|
err = binary.Read(reader, binary.LittleEndian, &infoHeader)
|
|
if err != nil {
|
|
TraceLog(LogWarning, err.Error())
|
|
return
|
|
}
|
|
|
|
reader.Seek(int64(unsafe.Sizeof(infoHeader)), os.SEEK_CUR)
|
|
|
|
if int(infoHeader.ID) == rresID {
|
|
data.Type = uint32(infoHeader.DataType)
|
|
data.Param1 = infoHeader.Param1
|
|
data.Param2 = infoHeader.Param2
|
|
data.Param3 = infoHeader.Param3
|
|
data.Param4 = infoHeader.Param4
|
|
|
|
// Read resource data block
|
|
b := make([]byte, infoHeader.DataSize)
|
|
reader.Read(b)
|
|
|
|
// Uncompress data
|
|
switch infoHeader.CompType {
|
|
case rres.CompNone:
|
|
data.Data = b
|
|
case rres.CompDeflate:
|
|
r := flate.NewReader(bytes.NewReader(b))
|
|
|
|
u := make([]byte, infoHeader.UncompSize)
|
|
r.Read(u)
|
|
|
|
data.Data = u
|
|
|
|
r.Close()
|
|
case rres.CompLZ4:
|
|
r := lz4.NewReader(bytes.NewReader(b))
|
|
|
|
u := make([]byte, infoHeader.UncompSize)
|
|
r.Read(u)
|
|
|
|
data.Data = u
|
|
case rres.CompLZMA2:
|
|
r, err := xz.NewReader(bytes.NewReader(b))
|
|
if err != nil {
|
|
TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err)
|
|
}
|
|
|
|
u := make([]byte, infoHeader.UncompSize)
|
|
r.Read(u)
|
|
|
|
data.Data = u
|
|
case rres.CompBZIP2:
|
|
r, err := bzip2.NewReader(bytes.NewReader(b), &bzip2.ReaderConfig{})
|
|
if err != nil {
|
|
TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err)
|
|
}
|
|
|
|
u := make([]byte, infoHeader.UncompSize)
|
|
r.Read(u)
|
|
|
|
data.Data = u
|
|
}
|
|
|
|
// Decrypt data
|
|
switch infoHeader.CryptoType {
|
|
case rres.CryptoXOR:
|
|
c, err := encrypt.NewXor(string(key))
|
|
if err != nil {
|
|
TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err)
|
|
}
|
|
|
|
b := c.Encode(data.Data)
|
|
data.Data = b
|
|
case rres.CryptoAES:
|
|
b, err := decryptAES(key, data.Data)
|
|
if err != nil {
|
|
TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err)
|
|
}
|
|
data.Data = b
|
|
case rres.Crypto3DES:
|
|
b, err := decrypt3DES(key, data.Data)
|
|
if err != nil {
|
|
TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err)
|
|
}
|
|
data.Data = b
|
|
}
|
|
|
|
if data.Data != nil {
|
|
TraceLog(LogInfo, "[ID %d] Resource data loaded successfully", infoHeader.ID)
|
|
}
|
|
} else {
|
|
// Skip required data to read next resource infoHeader
|
|
reader.Seek(int64(infoHeader.DataSize), os.SEEK_CUR)
|
|
}
|
|
}
|
|
|
|
if data.Data == nil {
|
|
TraceLog(LogInfo, "[ID %d] Requested resource could not be found", rresID)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// unpad
|
|
func unpad(src []byte) ([]byte, error) {
|
|
length := len(src)
|
|
unpadding := int(src[length-1])
|
|
|
|
if unpadding > length {
|
|
return nil, fmt.Errorf("unpad error. This can happen when incorrect encryption key is used.")
|
|
}
|
|
|
|
return src[:(length - unpadding)], nil
|
|
}
|
|
|
|
// decryptAES
|
|
func decryptAES(key, text []byte) ([]byte, error) {
|
|
block, err := aes.NewCipher(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if (len(text) % aes.BlockSize) != 0 {
|
|
return nil, fmt.Errorf("blocksize must be multiple of decoded message length")
|
|
}
|
|
|
|
iv := text[:aes.BlockSize]
|
|
msg := text[aes.BlockSize:]
|
|
|
|
cfb := cipher.NewCFBDecrypter(block, iv)
|
|
cfb.XORKeyStream(msg, msg)
|
|
|
|
unpadMsg, err := unpad(msg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return unpadMsg, nil
|
|
}
|
|
|
|
// decrypt3DES
|
|
func decrypt3DES(key, text []byte) ([]byte, error) {
|
|
block, err := des.NewCipher(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if (len(text) % des.BlockSize) != 0 {
|
|
return nil, fmt.Errorf("blocksize must be multiple of decoded message length")
|
|
}
|
|
|
|
iv := text[:des.BlockSize]
|
|
msg := text[des.BlockSize:]
|
|
|
|
cbc := cipher.NewCBCDecrypter(block, iv)
|
|
cbc.CryptBlocks(msg, msg)
|
|
|
|
unpadMsg, err := unpad(msg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return unpadMsg, nil
|
|
}
|