Overview
GRPGTILE is the binary format for defining tile types in GRPG. Each tile represents a terrain or floor type (grass, water, stone, etc.) and references a texture from the GRPGTEX atlas. Tiles are used to build the ground layer of game maps.
[8 bytes: Magic Header "GRPGTILE"]
[2 bytes: Tile Count (uint16)]
[Tile Array:]
For each tile:
[4 bytes: Name Length (uint32)]
[N bytes: Name (UTF-8 string)]
[2 bytes: Tile ID (uint16)]
[2 bytes: Texture ID (uint16)]
Type Definitions
type Header struct {
Magic [8]byte
}
Magic number identifier: "GRPGTILE" (8 ASCII characters)
Tile
type Tile struct {
Name string
TileId uint16
TexId uint16
}
Human-readable name of the tile (e.g., “grass”, “water”, “stone_floor”)
Unique identifier for this tile type (0-65535)
Reference to a texture in the GRPGTEX atlas by its InternalIdInt
Functions
func WriteHeader(buf *gbuf.GBuf)
Writes the GRPGTILE magic header to the buffer.
The buffer to write the header to
Binary Output:
[0x47, 0x52, 0x50, 0x47, 0x54, 0x49, 0x4C, 0x45]
"G R P G T I L E"
Example:
buf := gbuf.NewEmptyGBuf()
grpgtile.WriteHeader(buf)
// Buffer contains: []byte{'G', 'R', 'P', 'G', 'T', 'I', 'L', 'E'}
func ReadHeader(buf *gbuf.GBuf) (Header, error)
Reads and parses the GRPGTILE header from the buffer.
The buffer to read the header from
Returns:
Header - The parsed header structure
error - Error if insufficient data is available
Example:
data, _ := os.ReadFile("tiles.grpgtile")
buf := gbuf.NewGBuf(data)
header, err := grpgtile.ReadHeader(buf)
if err != nil {
log.Fatal("Invalid GRPGTILE file")
}
WriteTiles
func WriteTiles(buf *gbuf.GBuf, tiles []Tile)
Writes an array of tile definitions to the buffer.
The buffer to write tiles to
Array of tile definitions to serialize
Binary Output Format:
[2 bytes: uint16 count]
For each tile:
[4 bytes: uint32 name length]
[N bytes: UTF-8 name]
[2 bytes: uint16 tile ID]
[2 bytes: uint16 texture ID]
Example:
tiles := []grpgtile.Tile{
{
Name: "grass",
TileId: 1,
TexId: 2,
},
{
Name: "water",
TileId: 2,
TexId: 6,
},
}
buf := gbuf.NewEmptyGBuf()
grpgtile.WriteHeader(buf)
grpgtile.WriteTiles(buf, tiles)
os.WriteFile("tiles.grpgtile", buf.Bytes(), 0644)
ReadTiles
func ReadTiles(buf *gbuf.GBuf) ([]Tile, error)
Reads an array of tile definitions from the buffer.
The buffer to read tiles from (positioned after the header)
Returns:
[]Tile - Array of parsed tile definitions
error - Error if data is malformed or incomplete
Example:
data, _ := os.ReadFile("tiles.grpgtile")
buf := gbuf.NewGBuf(data)
// Read header
header, _ := grpgtile.ReadHeader(buf)
// Read tiles
tiles, err := grpgtile.ReadTiles(buf)
if err != nil {
log.Fatal("Failed to read tiles")
}
for _, tile := range tiles {
fmt.Printf("Tile %d: %s (Texture: %d)\n", tile.TileId, tile.Name, tile.TexId)
}
Complete Usage Example
Creating Tile Definitions
package main
import (
"grpg/data-go/gbuf"
"grpg/data-go/grpgtile"
"os"
)
func main() {
// Define tiles
tiles := []grpgtile.Tile{
{
Name: "grass",
TileId: 0,
TexId: 0, // References texture with InternalIdInt = 0
},
{
Name: "stone",
TileId: 1,
TexId: 1, // References texture with InternalIdInt = 1
},
{
Name: "water",
TileId: 2,
TexId: 2,
},
{
Name: "sand",
TileId: 3,
TexId: 3,
},
}
// Write to buffer
buf := gbuf.NewEmptyGBuf()
grpgtile.WriteHeader(buf)
grpgtile.WriteTiles(buf, tiles)
// Save to file
os.WriteFile("tiles.grpgtile", buf.Bytes(), 0644)
}
Loading Tile Definitions
package main
import (
"fmt"
"grpg/data-go/gbuf"
"grpg/data-go/grpgtile"
"os"
)
func main() {
// Load file
data, err := os.ReadFile("tiles.grpgtile")
if err != nil {
panic(err)
}
buf := gbuf.NewGBuf(data)
// Read and validate header
header, err := grpgtile.ReadHeader(buf)
if err != nil {
panic("Invalid GRPGTILE file")
}
expectedMagic := [8]byte{'G', 'R', 'P', 'G', 'T', 'I', 'L', 'E'}
if header.Magic != expectedMagic {
panic("Invalid magic number")
}
// Read tiles
tiles, err := grpgtile.ReadTiles(buf)
if err != nil {
panic("Failed to read tiles")
}
// Build lookup maps
tilesByID := make(map[uint16]grpgtile.Tile)
tilesByName := make(map[string]grpgtile.Tile)
for _, tile := range tiles {
tilesByID[tile.TileId] = tile
tilesByName[tile.Name] = tile
fmt.Printf("Loaded tile: %s (ID: %d, Tex: %d)\n",
tile.Name, tile.TileId, tile.TexId)
}
// Look up tile by ID
if tile, exists := tilesByID[1]; exists {
fmt.Printf("Tile ID 1 is: %s\n", tile.Name)
}
}
File Extension
.grpgtile
Magic Number
GRPGTILE (ASCII: 0x47 0x52 0x50 0x47 0x54 0x49 0x4C 0x45)
Tile Count Encoding
The tile count is stored as a uint16, limiting the maximum number of tile types to 65,535. This is sufficient for most games while keeping the header compact.
String Encoding
Tile names use the GBuf length-prefixed string format:
- 4-byte uint32 length
- N bytes of UTF-8 encoded string data
This allows for international characters in tile names.
Texture References
The TexId field references textures from the GRPGTEX format:
- Must match a texture’s
InternalIdInt value
- Invalid references will cause rendering errors
- Texture atlas must be loaded before tile definitions
Example Binary Layout
Offset | Bytes | Description
-------|----------------------------------|---------------------------
0x00 | 47 52 50 47 54 49 4C 45 | Magic "GRPGTILE"
0x08 | 00 02 | Count: 2 tiles
0x0A | 00 00 00 05 | Name length: 5
0x0E | 67 72 61 73 73 | Name: "grass"
0x13 | 00 01 | Tile ID: 1
0x15 | 00 02 | Texture ID: 2
0x17 | 00 00 00 05 | Name length: 5
0x1B | 77 61 74 65 72 | Name: "water"
0x20 | 00 02 | Tile ID: 2
0x22 | 00 06 | Texture ID: 6
Size Limits
- Maximum Tiles: 65,535 (uint16 count)
- Maximum Tile ID: 65,535 (uint16 range)
- Maximum Texture ID: 65,535 (uint16 range)
- Maximum Name Length: 4,294,967,295 bytes (uint32 length, impractical)
Usage in Maps
Tiles are referenced in GRPGMAP files by their TileId:
// Map zone uses tile IDs
type Zone struct {
Tiles [256]Tile // Each element is a uint16 tile ID
Objs [256]Obj
}
Example: A zone filled with grass (tile ID 1):
zone := grpgmap.Zone{}
for i := range 256 {
zone.Tiles[i] = grpgmap.Tile(1) // All grass
}
Error Handling
Common errors when reading GRPGTILE files:
- Invalid magic number: File is not a GRPGTILE file
- Insufficient data: File is truncated
- Invalid tile count: Corrupted count field
- String length mismatch: Name length exceeds remaining data
header, err := grpgtile.ReadHeader(buf)
if err != nil {
// Handle: file too small, not enough bytes for header
}
tiles, err := grpgtile.ReadTiles(buf)
if err != nil {
// Handle: corrupted tile data, invalid string lengths
}
- GRPGTEX - Texture atlas referenced by
TexId
- GRPGMAP - Map files that use tile IDs to build terrain
- GBuf - Binary buffer used for serialization