Skip to main content

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.

Binary Format Structure

[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
[8]byte
Magic number identifier: "GRPGTILE" (8 ASCII characters)

Tile

type Tile struct {
    Name   string
    TileId uint16
    TexId  uint16
}
Name
string
Human-readable name of the tile (e.g., “grass”, “water”, “stone_floor”)
TileId
uint16
Unique identifier for this tile type (0-65535)
TexId
uint16
Reference to a texture in the GRPGTEX atlas by its InternalIdInt

Functions

WriteHeader

func WriteHeader(buf *gbuf.GBuf)
Writes the GRPGTILE magic header to the buffer.
buf
*gbuf.GBuf
required
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'}

ReadHeader

func ReadHeader(buf *gbuf.GBuf) (Header, error)
Reads and parses the GRPGTILE header from the buffer.
buf
*gbuf.GBuf
required
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.
buf
*gbuf.GBuf
required
The buffer to write tiles to
tiles
[]Tile
required
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.
buf
*gbuf.GBuf
required
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)
    }
}

Binary Format Details

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

Build docs developers (and LLMs) love