rREM - raylib Resource EMbedder

This commit is contained in:
Milan Nikolic 2017-11-27 05:32:39 +01:00
parent d25f6e5b1f
commit 5b59c0b7e9
10 changed files with 911 additions and 166 deletions

View file

@ -1,4 +1,4 @@
PACKAGES= raylib raygui raymath easings physics PACKAGES= raylib raygui raymath easings physics rres
GO?= go GO?= go

View file

@ -0,0 +1,8 @@
#define NUM_RESOURCES 6
#define RES_coin.wav 0x00000000 // Embedded as WAVE
#define RES_raylib_logo.gif 0x00000001 // Embedded as IMAGE
#define RES_raylib_logo.jpg 0x00000002 // Embedded as IMAGE
#define RES_raylib_logo.png 0x00000003 // Embedded as IMAGE
#define RES_raylib_logo.tga 0x00000004 // Embedded as IMAGE
#define RES_tanatana.ogg 0x00000005 // Embedded as VORBIS

Binary file not shown.

View file

@ -0,0 +1,95 @@
package main
import (
//"bytes"
"github.com/gen2brain/raylib-go/raylib"
)
const numTextures = 4
func main() {
screenWidth := int32(800)
screenHeight := int32(450)
raylib.InitWindow(screenWidth, screenHeight, "raylib [core] example - resources loading")
raylib.InitAudioDevice()
// OpenAsset() will also work on Android (reads files from assets/)
reader, err := raylib.OpenAsset("data.rres")
if err != nil {
raylib.TraceLog(raylib.LogWarning, "[%s] rRES raylib resource file could not be opened: %v", "data.rres", err)
}
defer reader.Close()
// bindata
//b := MustAsset("data.rres")
//reader := bytes.NewReader(b)
res := raylib.LoadResource(reader, 0, nil)
wav := raylib.LoadWaveEx(res.Data, int32(res.Param1), int32(res.Param2), int32(res.Param3), int32(res.Param4))
snd := raylib.LoadSoundFromWave(wav)
raylib.UnloadWave(wav)
textures := make([]raylib.Texture2D, numTextures)
for i := 0; i < numTextures; i++ {
r := raylib.LoadResource(reader, i+1, nil)
image := raylib.LoadImagePro(r.Data, int32(r.Param1), int32(r.Param2), raylib.TextureFormat(r.Param3))
textures[i] = raylib.LoadTextureFromImage(image)
raylib.UnloadImage(image)
}
currentTexture := 0
raylib.SetTargetFPS(60)
for !raylib.WindowShouldClose() {
if raylib.IsKeyPressed(raylib.KeySpace) {
raylib.PlaySound(snd)
}
if raylib.IsMouseButtonPressed(raylib.MouseLeftButton) {
currentTexture = (currentTexture + 1) % numTextures // Cycle between the textures
}
raylib.BeginDrawing()
raylib.ClearBackground(raylib.RayWhite)
raylib.DrawTexture(textures[currentTexture], screenWidth/2-textures[currentTexture].Width/2, screenHeight/2-textures[currentTexture].Height/2, raylib.RayWhite)
raylib.DrawText("MOUSE LEFT BUTTON to CYCLE TEXTURES", 40, 410, 10, raylib.Gray)
raylib.DrawText("SPACE to PLAY SOUND", 40, 430, 10, raylib.Gray)
switch currentTexture {
case 0:
raylib.DrawText("GIF", 272, 70, 20, raylib.Gray)
break
case 1:
raylib.DrawText("JPEG", 272, 70, 20, raylib.Gray)
break
case 2:
raylib.DrawText("PNG", 272, 70, 20, raylib.Gray)
break
case 3:
raylib.DrawText("TGA", 272, 70, 20, raylib.Gray)
break
default:
break
}
raylib.EndDrawing()
}
raylib.UnloadSound(snd)
for _, t := range textures {
raylib.UnloadTexture(t)
}
raylib.CloseAudioDevice()
raylib.CloseWindow()
}

View file

@ -132,12 +132,13 @@ func LoadWave(fileName string) Wave {
} }
// LoadWaveEx - Load wave data from float array data (32bit) // LoadWaveEx - Load wave data from float array data (32bit)
func LoadWaveEx(data unsafe.Pointer, sampleCount int32, sampleRate int32, sampleSize int32, channels int32) Wave { func LoadWaveEx(data []byte, sampleCount int32, sampleRate int32, sampleSize int32, channels int32) Wave {
cdata := unsafe.Pointer(&data[0])
csampleCount := (C.int)(sampleCount) csampleCount := (C.int)(sampleCount)
csampleRate := (C.int)(sampleRate) csampleRate := (C.int)(sampleRate)
csampleSize := (C.int)(sampleSize) csampleSize := (C.int)(sampleSize)
cchannels := (C.int)(channels) cchannels := (C.int)(channels)
ret := C.LoadWaveEx(data, csampleCount, csampleRate, csampleSize, cchannels) ret := C.LoadWaveEx(cdata, csampleCount, csampleRate, csampleSize, cchannels)
v := NewWaveFromPointer(unsafe.Pointer(&ret)) v := NewWaveFromPointer(unsafe.Pointer(&ret))
return v return v
} }

View file

@ -2,212 +2,209 @@ package raylib
import ( import (
"bytes" "bytes"
"compress/flate" "crypto/aes"
"crypto/cipher"
"crypto/des"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io"
"os" "os"
"unsafe" "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"
) )
// RRESFileHeader - rRES file header (8 byte) // LoadResource - Load resource from file by id
type RRESFileHeader struct {
// File identifier: rRES (4 byte)
ID [4]int8
// File version and subversion (2 byte)
Version uint16
// Number of resources in this file (2 byte)
Count uint16
}
// RRESInfoHeader - rRES info header, every resource includes this header (16 byte + 16 byte)
type RRESInfoHeader struct {
// Resource unique identifier (4 byte)
ID uint32
// Resource data type (1 byte)
DataType uint8
// Resource data compression type (1 byte)
CompType uint8
// Resource data encryption type (1 byte)
CryptoType uint8
// Resource data parts count, used for splitted data (1 byte)
PartsCount uint8
// Resource data size (compressed or not, only DATA) (4 byte)
DataSize uint32
// Resource data size (uncompressed, only DATA) (4 byte)
UncompSize uint32
// Resouce parameter 1 (4 byte)
Param1 uint32
// Resouce parameter 2 (4 byte)
Param2 uint32
// Resouce parameter 3 (4 byte)
Param3 uint32
// Resouce parameter 4 (4 byte)
Param4 uint32
}
// rRES data types
const (
RRESTypeRaw = iota
RRESTypeImage
RRESTypeWave
RRESTypeVertex
RRESTypeText
RRESTypeFontImage
RRESTypeFontData
RRESTypeDirectory
)
// Compression types
const (
// No data compression
RRESCompNone = iota
// DEFLATE compression
RRESCompDeflate
// LZ4 compression
RRESCompLz4
// LZMA compression
RRESCompLzma
// BROTLI compression
RRESCompBrotli
)
// Image formats
const (
// 8 bit per pixel (no alpha)
RRESImUncompGrayscale = iota + 1
// 16 bpp (2 channels)
RRESImUncompGrayAlpha
// 16 bpp
RRESImUncompR5g6b5
// 24 bpp
RRESImUncompR8g8b8
// 16 bpp (1 bit alpha)
RRESImUncompR5g5b5a1
// 16 bpp (4 bit alpha)
RRESImUncompR4g4b4a4
// 32 bpp
RRESImUncompR8g8b8a8
// 4 bpp (no alpha)
RRESImCompDxt1Rgb
// 4 bpp (1 bit alpha)
RRESImCompDxt1Rgba
// 8 bpp
RRESImCompDxt3Rgba
// 8 bpp
RRESImCompDxt5Rgba
// 4 bpp
RRESImCompEtc1Rgb
// 4 bpp
RRESImCompEtc2Rgb
// 8 bpp
RRESImCompEtc2EacRgba
// 4 bpp
RRESImCompPvrtRgb
// 4 bpp
RRESImCompPvrtRgba
// 8 bpp
RRESImCompAstc4x4Rgba
// 2 bpp
RRESImCompAstc8x8Rgba
)
// RRESVert
const (
RRESVertPosition = iota
RRESVertTexcoord1
RRESVertTexcoord2
RRESVertTexcoord3
RRESVertTexcoord4
RRESVertNormal
RRESVertTangent
RRESVertColor
RRESVertIndex
)
// RRESVert
const (
RRESVertByte = iota
RRESVertShort
RRESVertInt
RRESVertHfloat
RRESVertFloat
)
// LoadResource - Load resource from file (only one)
// NOTE: Returns uncompressed data with parameters, only first resource found
func LoadResource(fileName string) []byte {
return LoadResourceByID(fileName, 0)
}
// LoadResourceByID - Load resource from file by id
// NOTE: Returns uncompressed data with parameters, search resource by id // NOTE: Returns uncompressed data with parameters, search resource by id
func LoadResourceByID(fileName string, rresID int) (data []byte) { func LoadResource(reader io.ReadSeeker, rresID int, key []byte) (data rres.Data) {
file, err := OpenAsset(fileName) var fileHeader rres.FileHeader
if err != nil { var infoHeader rres.InfoHeader
TraceLog(LogWarning, "[%s] rRES raylib resource file could not be opened", fileName)
return
}
defer file.Close()
fileHeader := RRESFileHeader{} reader.Seek(0, 0)
infoHeader := RRESInfoHeader{}
// Read rres file header // Read rres file header
err = binary.Read(file, binary.LittleEndian, &fileHeader) err := binary.Read(reader, binary.LittleEndian, &fileHeader)
if err != nil { if err != nil {
TraceLog(LogWarning, err.Error()) TraceLog(LogWarning, err.Error())
return return
} }
//fmt.Printf("%+v\n", fileHeader)
// Verify "rRES" identifier // Verify "rRES" identifier
id := fmt.Sprintf("%c", fileHeader.ID) id := fmt.Sprintf("%c", fileHeader.ID)
if id != "[r R E S]" { if id != "[r R E S]" {
TraceLog(LogWarning, "[%s] is not a valid raylib resource file", fileName) TraceLog(LogWarning, "not a valid raylib resource file")
return return
} }
file.Seek(int64(unsafe.Sizeof(fileHeader)), os.SEEK_CUR) reader.Seek(int64(unsafe.Sizeof(fileHeader)), os.SEEK_CUR)
for i := 0; i < int(fileHeader.Count); i++ { for i := 0; i < int(fileHeader.Count); i++ {
// Read resource info and parameters // Read resource info and parameters
err = binary.Read(file, binary.LittleEndian, &infoHeader) err = binary.Read(reader, binary.LittleEndian, &infoHeader)
if err != nil { if err != nil {
TraceLog(LogWarning, err.Error()) TraceLog(LogWarning, err.Error())
return return
} }
//fmt.Printf("%+v\n", infoHeader) reader.Seek(int64(unsafe.Sizeof(infoHeader)), os.SEEK_CUR)
file.Seek(int64(unsafe.Sizeof(infoHeader)), os.SEEK_CUR)
if int(infoHeader.ID) == rresID { 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 // Read resource data block
data = make([]byte, infoHeader.DataSize) b := make([]byte, infoHeader.DataSize)
file.Read(data) reader.Read(b)
if infoHeader.CompType == RRESCompDeflate { // Uncompress data
// Uncompress data switch infoHeader.CompType {
b := bytes.NewReader(data) case rres.CompNone:
r := flate.NewReader(b) data.Data = b
case rres.CompDeflate:
r := flate.NewReader(bytes.NewReader(b))
data = make([]byte, infoHeader.UncompSize) u := make([]byte, infoHeader.UncompSize)
r.Read(data) 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
} }
if len(data) > 0 { // Decrypt data
TraceLog(LogInfo, "[%s][ID %d] Resource data loaded successfully", fileName, infoHeader.ID) 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 { } else {
// Skip required data to read next resource infoHeader // Skip required data to read next resource infoHeader
file.Seek(int64(infoHeader.DataSize), os.SEEK_CUR) reader.Seek(int64(infoHeader.DataSize), os.SEEK_CUR)
} }
} }
if len(data) == 0 { if data.Data == nil {
TraceLog(LogInfo, "[%s][ID %d] Requested resource could not be found", fileName, rresID) TraceLog(LogInfo, "[ID %d] Requested resource could not be found", rresID)
} }
return 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
}

3
rres/README.md Normal file
View file

@ -0,0 +1,3 @@
## rres [![GoDoc](https://godoc.org/github.com/gen2brain/raylib-go/rres?status.svg)](https://godoc.org/github.com/gen2brain/raylib-go/rres)
raylib resources.

25
rres/cmd/rrem/README.md Normal file
View file

@ -0,0 +1,25 @@
## rrem
rREM - raylib Resource EMbedder.
### Usage
```
Usage of ./rrem:
-base string
Resources file basename (default "data")
-bin
Generate Go bindata (.go file)
-comp int
Compression type, 0=None, 1=Deflate, 2=LZ4, 5=LZMA2 (XZ), 6=BZIP2 (default 5)
-enc int
Encryption type, 0=None, 1=XOR, 2=AES, 3=3DES
-header
Generate C header (.h file)
-key string
Encryption key
```
### Example
[Example](https://github.com/gen2brain/raylib-go/tree/master/examples/others/resources).

453
rres/cmd/rrem/main.go Normal file
View file

@ -0,0 +1,453 @@
// rREM - raylib Resource EMbedder
package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/des"
"crypto/rand"
"encoding/binary"
"flag"
"fmt"
"image"
"image/color"
"image/draw"
"io"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"strings"
"unsafe"
_ "image/gif"
_ "image/jpeg"
_ "image/png"
"github.com/blezek/tga"
_ "github.com/jbuchbinder/gopnm"
_ "golang.org/x/image/bmp"
"github.com/dsnet/compress/bzip2"
"github.com/jfreymuth/oggvorbis"
"github.com/jteeuwen/go-bindata"
"github.com/klauspost/compress/flate"
"github.com/moutend/go-wav"
"github.com/pierrec/lz4"
"github.com/rootlch/encrypt"
"github.com/ulikunitz/xz"
"github.com/gen2brain/raylib-go/rres"
)
func init() {
tga.RegisterFormat()
}
func main() {
base := flag.String("base", "data", "Resources file basename")
comp := flag.Int("comp", rres.CompLZMA2, "Compression type, 0=None, 1=Deflate, 2=LZ4, 5=LZMA2 (XZ), 6=BZIP2")
enc := flag.Int("enc", rres.CryptoNone, "Encryption type, 0=None, 1=XOR, 2=AES, 3=3DES")
key := flag.String("key", "", "Encryption key")
header := flag.Bool("header", false, "Generate C header (.h file)")
bin := flag.Bool("bin", false, "Generate Go bindata (.go file)")
flag.Parse()
if len(flag.Args()) == 0 {
flag.Usage()
os.Exit(1)
}
switch *comp {
case rres.CompNone:
case rres.CompDeflate:
case rres.CompLZ4:
case rres.CompLZMA2:
case rres.CompBZIP2:
default:
fmt.Printf("compression type %d not implemented\n", *comp)
os.Exit(1)
}
switch *enc {
case rres.CryptoNone:
case rres.CryptoXOR:
case rres.CryptoAES:
case rres.Crypto3DES:
default:
fmt.Printf("encryption type %d not implemented\n", *enc)
os.Exit(1)
}
if *enc != 0 {
if *key == "" {
fmt.Printf("encryption requires key (-k)\n")
os.Exit(1)
}
if len(*key) != 16 && len(*key) != 24 {
fmt.Printf("wrong key length, it should be 16 or 24\n")
os.Exit(1)
}
}
rresFile, err := os.Create(fmt.Sprintf("%s.rres", *base))
if err != nil {
fmt.Printf("%v\n", err)
os.Exit(1)
}
defer rresFile.Close()
var headerFile *os.File
if *header {
headerFile, err = os.Create(fmt.Sprintf("%s.h", *base))
if err != nil {
fmt.Printf("%v\n", err)
os.Exit(1)
}
defer headerFile.Close()
}
var fileHeader rres.FileHeader
// "rRES" identifier
copy(fileHeader.ID[:], "rRES")
fileHeader.Count = uint16(len(flag.Args()))
fileHeader.Version = 1
// Write file header
err = binary.Write(rresFile, binary.LittleEndian, &fileHeader)
if err != nil {
fmt.Printf("%v\n", err)
os.Exit(1)
}
rresFile.Seek(int64(unsafe.Sizeof(fileHeader)), os.SEEK_CUR)
if *header {
// Write C header file
_, err = headerFile.Write([]byte(fmt.Sprintf("#define NUM_RESOURCES %d\n\n", flag.NArg())))
if err != nil {
fmt.Printf("%v\n", err)
os.Exit(1)
}
}
for id, filename := range flag.Args() {
var data []byte
var infoHeader rres.InfoHeader
file, err := os.Open(filename)
if err != nil {
fmt.Printf("%s: %v\n", filename, err)
continue
}
data, err = ioutil.ReadAll(file)
if err != nil {
fmt.Printf("%s: %v\n", filename, err)
}
file.Close()
infoHeader.ID = uint32(id)
infoHeader.CompType = uint8(*comp)
infoHeader.CryptoType = uint8(*enc)
infoHeader.DataType = uint8(fileType(filename))
infoHeader.PartsCount = uint8(1)
// Params
switch infoHeader.DataType {
case rres.TypeImage:
img, _, err := image.Decode(bytes.NewReader(data))
if err != nil {
fmt.Printf("%s: %v\n", filename, err)
continue
}
rect := img.Bounds()
width, height := rect.Dx(), rect.Dy()
infoHeader.Param1 = uint32(width)
infoHeader.Param2 = uint32(height)
switch img.ColorModel() {
case color.GrayModel:
infoHeader.Param3 = rres.ImUncompGrayscale
i := image.NewGray(rect)
draw.Draw(i, rect, img, rect.Min, draw.Src)
data = i.Pix
case color.Gray16Model:
infoHeader.Param3 = rres.ImUncompGrayAlpha
i := image.NewGray16(rect)
draw.Draw(i, rect, img, rect.Min, draw.Src)
data = i.Pix
default:
infoHeader.Param3 = rres.ImUncompR8g8b8a8
i := image.NewNRGBA(rect)
draw.Draw(i, rect, img, rect.Min, draw.Src)
data = i.Pix
}
case rres.TypeWave:
a := &wav.File{}
err := wav.Unmarshal(data, a)
if err != nil {
fmt.Printf("%s: %v\n", filename, err)
}
data, err = ioutil.ReadAll(a)
if err != nil {
fmt.Printf("%s: %v\n", filename, err)
}
infoHeader.Param1 = uint32(a.Samples())
infoHeader.Param2 = uint32(a.SamplesPerSec())
infoHeader.Param3 = uint32(a.BitsPerSample())
infoHeader.Param4 = uint32(a.Channels())
case rres.TypeVorbis:
r, err := oggvorbis.NewReader(bytes.NewReader(data))
if err != nil {
fmt.Printf("%s: %v\n", filename, err)
}
d, _, err := oggvorbis.ReadAll(bytes.NewReader(data))
if err != nil {
fmt.Printf("%s: %v\n", filename, err)
}
// Convert []float32 to []byte
header := *(*reflect.SliceHeader)(unsafe.Pointer(&d))
header.Len *= 4
header.Cap *= 4
data = *(*[]byte)(unsafe.Pointer(&header))
infoHeader.Param1 = uint32(r.SampleRate())
infoHeader.Param2 = uint32(r.Bitrate().Nominal)
infoHeader.Param3 = uint32(r.Channels())
case rres.TypeVertex:
// TODO https://github.com/sheenobu/go-obj
case rres.TypeText, rres.TypeRaw:
}
// Encryption
switch infoHeader.CryptoType {
case rres.CryptoXOR:
c, err := encrypt.NewXor(*key)
if err != nil {
fmt.Printf("%v\n", err)
}
b := c.Encode(data)
data = b
case rres.CryptoAES:
b, err := encryptAES([]byte(*key), data)
if err != nil {
fmt.Printf("%v\n", err)
}
data = b
case rres.Crypto3DES:
b, err := encrypt3DES([]byte(*key), data)
if err != nil {
fmt.Printf("%v\n", err)
}
data = b
}
infoHeader.UncompSize = uint32(len(data))
// Compression
switch infoHeader.CompType {
case rres.CompNone:
infoHeader.DataSize = uint32(len(data))
case rres.CompDeflate:
buf := new(bytes.Buffer)
w, err := flate.NewWriter(buf, flate.BestCompression)
if err != nil {
fmt.Printf("%v\n", err)
}
_, err = w.Write(data)
if err != nil {
fmt.Printf("%v\n", err)
}
w.Close()
infoHeader.DataSize = uint32(len(buf.Bytes()))
data = buf.Bytes()
case rres.CompLZ4:
buf := new(bytes.Buffer)
w := lz4.NewWriter(buf)
if err != nil {
fmt.Printf("%v\n", err)
}
_, err = w.Write(data)
if err != nil {
fmt.Printf("%v\n", err)
}
w.Close()
infoHeader.DataSize = uint32(len(buf.Bytes()))
data = buf.Bytes()
case rres.CompLZMA2:
buf := new(bytes.Buffer)
w, err := xz.NewWriter(buf)
if err != nil {
fmt.Printf("%v\n", err)
}
_, err = w.Write(data)
if err != nil {
fmt.Printf("%v\n", err)
}
w.Close()
infoHeader.DataSize = uint32(len(buf.Bytes()))
data = buf.Bytes()
case rres.CompBZIP2:
buf := new(bytes.Buffer)
w, err := bzip2.NewWriter(buf, &bzip2.WriterConfig{Level: bzip2.BestCompression})
if err != nil {
fmt.Printf("%v\n", err)
}
_, err = w.Write(data)
if err != nil {
fmt.Printf("%v\n", err)
}
w.Close()
infoHeader.DataSize = uint32(len(buf.Bytes()))
data = buf.Bytes()
}
// Write resource info and parameters
err = binary.Write(rresFile, binary.LittleEndian, &infoHeader)
if err != nil {
fmt.Printf("%v\n", err)
}
rresFile.Seek(int64(unsafe.Sizeof(infoHeader)), os.SEEK_CUR)
// Write resource data
_, err = rresFile.Write(data)
if err != nil {
fmt.Printf("%v\n", err)
}
var typeName string
switch infoHeader.DataType {
case rres.TypeImage:
typeName = "IMAGE"
case rres.TypeWave:
typeName = "WAVE"
case rres.TypeVorbis:
typeName = "VORBIS"
case rres.TypeText:
typeName = "TEXT"
default:
typeName = "RAW"
}
fmt.Printf("%s %d // Embedded as %s\n", filepath.Base(filename), id, typeName)
if *header {
headerFile.Write([]byte(fmt.Sprintf("#define RES_%s 0x%08x\t\t// Embedded as %s\n", filepath.Base(filename), id, typeName)))
}
}
// Generate bindata
if *bin {
cfg := bindata.NewConfig()
cfg.NoCompress = true
cfg.Output = fmt.Sprintf("%s.go", *base)
cfg.Input = make([]bindata.InputConfig, 1)
cfg.Input[0] = bindata.InputConfig{Path: fmt.Sprintf("%s.rres", *base), Recursive: false}
err := bindata.Translate(cfg)
if err != nil {
fmt.Printf("%v\n", err)
}
}
}
// fileType returns resource file type
func fileType(f string) int {
switch strings.ToLower(filepath.Ext(f)) {
case ".jpg", ".jpeg", ".png", ".bmp", ".tga", ".gif":
return rres.TypeImage
case ".txt", ".csv", ".info", ".md":
return rres.TypeText
case ".wav":
return rres.TypeWave
case ".ogg":
return rres.TypeVorbis
case ".obj":
return rres.TypeVertex
default:
return rres.TypeRaw
}
}
// pad to block size
func pad(src []byte, blockSize int) []byte {
padding := blockSize - len(src)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(src, padtext...)
}
// encryptAES
func encryptAES(key, text []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
msg := pad(text, aes.BlockSize)
ciphertext := make([]byte, aes.BlockSize+len(msg))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return nil, err
}
cfb := cipher.NewCFBEncrypter(block, iv)
cfb.XORKeyStream(ciphertext[aes.BlockSize:], msg)
return ciphertext, nil
}
// encrypt3DES
func encrypt3DES(key, text []byte) ([]byte, error) {
block, err := des.NewTripleDESCipher([]byte(key))
if err != nil {
return nil, err
}
msg := pad(text, des.BlockSize)
ciphertext := make([]byte, des.BlockSize+len(msg))
iv := ciphertext[:des.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return nil, err
}
cbc := cipher.NewCBCEncrypter(block, iv)
cbc.CryptBlocks(ciphertext[des.BlockSize:], msg)
return ciphertext, nil
}

163
rres/rres.go Normal file
View file

@ -0,0 +1,163 @@
package rres
type Data struct {
// Resource type (4 byte)
Type uint32
// Resource parameter 1 (4 byte)
Param1 uint32
// Resource parameter 2 (4 byte)
Param2 uint32
// Resource parameter 3 (4 byte)
Param3 uint32
// Resource parameter 4 (4 byte)
Param4 uint32
// Resource data
Data []byte
}
// FileHeader - rRES file header (8 byte)
type FileHeader struct {
// File identifier: rRES (4 byte)
ID [4]byte
// File version and subversion (2 byte)
Version uint16
// Number of resources in this file (2 byte)
Count uint16
}
// InfoHeader - rRES info header, every resource includes this header (16 byte + 16 byte)
type InfoHeader struct {
// Resource unique identifier (4 byte)
ID uint32
// Resource data type (1 byte)
DataType uint8
// Resource data compression type (1 byte)
CompType uint8
// Resource data encryption type (1 byte)
CryptoType uint8
// Resource data parts count, used for splitted data (1 byte)
PartsCount uint8
// Resource data size (compressed or not, only DATA) (4 byte)
DataSize uint32
// Resource data size (uncompressed, only DATA) (4 byte)
UncompSize uint32
// Resource parameter 1 (4 byte)
Param1 uint32
// Resource parameter 2 (4 byte)
Param2 uint32
// Resource parameter 3 (4 byte)
Param3 uint32
// Resource parameter 4 (4 byte)
Param4 uint32
}
// rRES data types
const (
TypeRaw = iota
TypeImage
TypeWave
TypeVertex
TypeText
TypeFontImage
TypeFontCharData
TypeDirectory
TypeVorbis
)
// Compression types
const (
// No data compression
CompNone = iota
// DEFLATE compression
CompDeflate
// LZ4 compression
CompLZ4
// LZMA compression
CompLZMA
// BROTLI compression
CompBrotli
// LZMA2 (XZ) compression
CompLZMA2
// BZIP2 compression
CompBZIP2
)
// Encryption types
const (
// No data encryption
CryptoNone = iota
// XOR (128 bit) encryption
CryptoXOR
// RIJNDAEL (128 bit) encryption (AES)
CryptoAES
// Triple DES encryption
Crypto3DES
// Blowfish encryption
CryptoBlowfish
// Extended TEA encryption
CryptoXTEA
)
// Image formats
const (
// 8 bit per pixel (no alpha)
ImUncompGrayscale = iota + 1
// 16 bpp (2 channels)
ImUncompGrayAlpha
// 16 bpp
ImUncompR5g6b5
// 24 bpp
ImUncompR8g8b8
// 16 bpp (1 bit alpha)
ImUncompR5g5b5a1
// 16 bpp (4 bit alpha)
ImUncompR4g4b4a4
// 32 bpp
ImUncompR8g8b8a8
// 4 bpp (no alpha)
ImCompDxt1Rgb
// 4 bpp (1 bit alpha)
ImCompDxt1Rgba
// 8 bpp
ImCompDxt3Rgba
// 8 bpp
ImCompDxt5Rgba
// 4 bpp
ImCompEtc1Rgb
// 4 bpp
ImCompEtc2Rgb
// 8 bpp
ImCompEtc2EacRgba
// 4 bpp
ImCompPvrtRgb
// 4 bpp
ImCompPvrtRgba
// 8 bpp
ImCompAstc4x4Rgba
// 2 bpp
ImCompAstc8x8Rgba
)
// Vert
const (
VertPosition = iota
VertTexcoord1
VertTexcoord2
VertTexcoord3
VertTexcoord4
VertNormal
VertTangent
VertColor
VertIndex
)
// Vert
const (
VertByte = iota
VertShort
VertInt
VertHfloat
VertFloat
)