Overview
GRPGTEX is the binary format for storing texture atlases in GRPG. Each texture contains an identifier (both string and integer) and compressed image data in JXL (JPEG XL) format. This format is used to package all game textures into a single loadable asset.
[8 bytes: Magic Header "GRPGTEX\0"]
[4 bytes: Texture Count (uint32)]
[Texture Array:]
For each texture:
[4 bytes: ID String Length (uint32)]
[N bytes: ID String (UTF-8)]
[2 bytes: ID Integer (uint16)]
[4 bytes: Image Data Length (uint32)]
[M bytes: Image Data (JXL format)]
Type Definitions
type Header struct {
Magic [8]byte
}
Magic number identifier: "GRPGTEX\0" (GRPGTEX followed by null byte)
Texture
type Texture struct {
InternalIdString []byte
InternalIdInt uint16
ImageBytes []byte
}
UTF-8 encoded string identifier for the texture (e.g., “grass”, “stone”)
Numeric identifier for the texture, used for efficient lookups
Compressed image data in JXL (JPEG XL) format
Methods
Equals
func (t Texture) Equals(other Texture) bool
Compares two textures for equality based on their ID string and image bytes.
The texture to compare against
Returns: bool - True if both textures have identical ID strings and image data
Functions
func WriteHeader(buf *gbuf.GBuf)
Writes the GRPGTEX magic header to the buffer.
The buffer to write the header to
Binary Output:
[0x47, 0x52, 0x50, 0x47, 0x54, 0x45, 0x58, 0x00]
"G R P G T E X \0"
Example:
buf := gbuf.NewEmptyGBuf()
grpgtex.WriteHeader(buf)
// Buffer contains magic: []byte{'G', 'R', 'P', 'G', 'T', 'E', 'X', 0}
func ReadHeader(buf *gbuf.GBuf) (Header, error)
Reads and validates the GRPGTEX header from the buffer.
The buffer to read the header from
Returns:
Header - The parsed header structure
error - Error if insufficient data or invalid format
Example:
data, _ := os.ReadFile("textures.grpgtex")
buf := gbuf.NewGBuf(data)
header, err := grpgtex.ReadHeader(buf)
if err != nil {
log.Fatal("Invalid GRPGTEX file")
}
WriteTextures
func WriteTextures(buf *gbuf.GBuf, textures []Texture)
Writes an array of textures to the buffer.
The buffer to write textures to
Array of textures to serialize
Binary Output Format:
[4 bytes: uint32 count]
For each texture:
[4 bytes: uint32 string ID length]
[N bytes: string ID]
[2 bytes: uint16 integer ID]
[4 bytes: uint32 image data length]
[M bytes: image data]
Example:
textures := []grpgtex.Texture{
{
InternalIdString: []byte("grass"),
InternalIdInt: 0,
ImageBytes: grassJxlBytes,
},
{
InternalIdString: []byte("stone"),
InternalIdInt: 1,
ImageBytes: stoneJxlBytes,
},
}
buf := gbuf.NewEmptyGBuf()
grpgtex.WriteHeader(buf)
grpgtex.WriteTextures(buf, textures)
// Save to file
os.WriteFile("textures.grpgtex", buf.Bytes(), 0644)
ReadTextures
func ReadTextures(buf *gbuf.GBuf) ([]Texture, error)
Reads an array of textures from the buffer.
The buffer to read textures from (positioned after the header)
Returns:
[]Texture - Array of parsed texture objects
error - Error if data is malformed or incomplete
Example:
data, _ := os.ReadFile("textures.grpgtex")
buf := gbuf.NewGBuf(data)
// Read header first
header, _ := grpgtex.ReadHeader(buf)
// Read textures
textures, err := grpgtex.ReadTextures(buf)
if err != nil {
log.Fatal("Failed to read textures")
}
for _, tex := range textures {
fmt.Printf("Texture: %s (ID: %d)\n", tex.InternalIdString, tex.InternalIdInt)
}
Complete Usage Example
Creating a Texture Atlas
package main
import (
"grpg/data-go/gbuf"
"grpg/data-go/grpgtex"
"os"
)
func main() {
// Load image files
grassJxl, _ := os.ReadFile("assets/grass.jxl")
stoneJxl, _ := os.ReadFile("assets/stone.jxl")
waterJxl, _ := os.ReadFile("assets/water.jxl")
// Create texture array
textures := []grpgtex.Texture{
{
InternalIdString: []byte("grass"),
InternalIdInt: 0,
ImageBytes: grassJxl,
},
{
InternalIdString: []byte("stone"),
InternalIdInt: 1,
ImageBytes: stoneJxl,
},
{
InternalIdString: []byte("water"),
InternalIdInt: 2,
ImageBytes: waterJxl,
},
}
// Write to buffer
buf := gbuf.NewEmptyGBuf()
grpgtex.WriteHeader(buf)
grpgtex.WriteTextures(buf, textures)
// Save to file
os.WriteFile("textures.grpgtex", buf.Bytes(), 0644)
}
Loading a Texture Atlas
package main
import (
"fmt"
"grpg/data-go/gbuf"
"grpg/data-go/grpgtex"
"os"
)
func main() {
// Load file
data, err := os.ReadFile("textures.grpgtex")
if err != nil {
panic(err)
}
buf := gbuf.NewGBuf(data)
// Read header
header, err := grpgtex.ReadHeader(buf)
if err != nil {
panic("Invalid GRPGTEX file")
}
// Validate magic
expectedMagic := [8]byte{'G', 'R', 'P', 'G', 'T', 'E', 'X', 0}
if header.Magic != expectedMagic {
panic("Invalid magic number")
}
// Read textures
textures, err := grpgtex.ReadTextures(buf)
if err != nil {
panic("Failed to read textures")
}
// Process textures
fmt.Printf("Loaded %d textures:\n", len(textures))
for _, tex := range textures {
fmt.Printf(" - %s (ID: %d, Size: %d bytes)\n",
string(tex.InternalIdString),
tex.InternalIdInt,
len(tex.ImageBytes))
}
}
File Extension
.grpgtex
Magic Number
GRPGTEX\0 (ASCII: 0x47 0x52 0x50 0x47 0x54 0x45 0x58 0x00)
Textures use JXL (JPEG XL) format for image compression:
- Modern, efficient compression
- Lossless and lossy modes
- Better compression than PNG/JPEG
- Native support in modern browsers and tools
Texture Indexing
Each texture has two identifiers:
-
String ID: Human-readable name (e.g., “grass”, “stone_brick”)
- Used during development and debugging
- UTF-8 encoded for internationalization
-
Integer ID: Numeric identifier (uint16)
- Used for efficient runtime lookups
- Range: 0-65535 unique textures
- Referenced by tiles, objects, NPCs, and items
Size Limits
- Maximum Textures: 4,294,967,295 (uint32 count)
- Maximum String ID Length: 4,294,967,295 bytes (uint32 length)
- Maximum Image Size: 4,294,967,295 bytes (uint32 length)
Practical limits are much lower due to memory and performance constraints.
Error Handling
Common errors when reading GRPGTEX files:
- Invalid magic number: File is not a GRPGTEX file or is corrupted
- Insufficient data: File is truncated or incomplete
- Invalid texture count: Corrupted texture count field
- String/image length mismatch: Declared length exceeds remaining data
header, err := grpgtex.ReadHeader(buf)
if err != nil {
// Handle invalid header
}
textures, err := grpgtex.ReadTextures(buf)
if err != nil {
// Handle texture read error
}
- GRPGTILE - References textures by integer ID
- GRPGOBJ - References textures for object states
- GRPGNPC - References textures for NPC sprites
- GRPGITEM - References textures for item icons