Field file format

From OpenKore Wiki
Jump to navigation Jump to search

What are FLD files?

In order for OpenKore to walk around on a map, it must know how the map looks like. Otherwise it can't calculate a path. An FLD file contains information about how a map looks like.

Relation with .GAT/.RSW/.GND/.DIST

If you open your Ragnarok Online client's GRF file (the RO client's resource archive file), you will not find FLD files, but only GAT/RSW/GND files. GAT (Ground Altitude File) and RSW (Resource World File) files also contain information about how the maps look like, but they contain too much information. OpenKore is only interested in the information about which parts of a map are walkable, but GAT files also contain information such as the height of a specific block in a map. So GAT and RSW files are converted to FLD files to save space.

I'm not sure what GND and RSW files really are. But it seems that offset 166-169 (32-bit x86 floating point) in the RSW file represents the water level of the map.

DIST files contain information about the distance to the closest non-walkable block. They're used by OpenKore's pathfinding algorithm for wall avoidance. DIST files are automatically generated by OpenKore at runtime, from FLD files.

Creating FLD files

There is a tool called gat_to_fld.pl located in fields/tools/. You can use that script to convert GAT files to FLD files. Copy GAT and RSW files to the same folder as gat_to_fld.pl, and run the script. The easy guide can be found here.

The FLD file format

The FLD file format is described by the follow C structure, assuming that the stucture is packed:

struct FLDFile {
   uint16 width;   // The width of the field.
   uint16 height;  // The height of the field.
   unsigned char data[width * height];  // The raw field data.
};

All integers are in little-endian.

Each byte in the raw field data describes a block on the map. The following byte values (in decimal) have a known meaning:

  • 0 = walkable block
  • 1 = non-walkable block
  • 2 = non-walkable water (not snipable)
  • 3 = walkable water
  • 4 = non-walkable water (snipable)
  • 5 = cliff (snipable)
  • 6 = cliff (not snipable)
  • 7 = unknown

The raw 1D array represents the 2D map, just like many raster image formats do. Therefore, to get information about a position (x, y) on the map, you query FLDFile.data[y * FLDFile.width + x]

The GAT file format

struct GATFile {                // Offset
   unsigned char magic[6];     // 0
   uint32 width;               // 6
   uint32 height;              // 10
   struct GATBlock blocks[];   // 14
};

All integers are in little-endian.

magic
The value is "GRAT\x01\x02"
width
The width of the map.
height
The height of the map.
blocks
An array of GATBlock structures. This extends until EOF. Each structure represents a block in the map.
// Total size: 20 bytes
struct GATBlock {                 // Offset
   float upperLeftHeight;        // 0
   float upperRightHeight;       // 4
   float lowerLeftHeight;        // 8
   float lowerRightHeight;       // 12
   unsigned char type;           // 16
   unsigned char unknown[3];     // 17
};

'float' is a 32-bit x86 floating point number.

(upper|lower)(left|right)height
These fields specify the height of the upper left, upper right, lower left and lower right corner of the block.
type
Information about this block's type.
  • 0 = walkable block
  • 1 = non-walkable block
  • 2 = non-walkable water (not snipable)
  • 3 = walkable water
  • 4 = non-walkable water (snipable)
  • 5 = cliff (snipable)
  • 6 = cliff (not snipable)
  • Everything else = unknown

It is also important to keep the water level* in mind. When the current block is below water level, the type field must be interpreted differently, as follows:

  • 0 = walkable water
  • 1 = non-walkable water (not snipable)
  • 3 = walkable water
  • 5 = non-walkable water (snipable)
  • 6 = non-walkable water (not snipable)
  • Everything else = unknown

See the RSW note in 2nd paragraph of this page.

To check whether a block is below water level, use the following pseudo code:

float averageDepth = (gat_block.upperLeftHeight + gat_block.upperRightHeight
   + gat_block.lowerLeftHeight + gat_block.lowerRightHeight) / 4;
float waterLevel = readWaterLevelFromRSWFile();

// The '>' is NOT a typo!
if (averageDepth > waterLevel)
   // Block is below water level
else
   // Block is above water level

Credits

  • Alkie (Odin project) - information about that GAT files use floating-point numbers.
  • Skipperbot - parts of this page is derived from Skipperbot's source code.
  • amacc_boy
  • [GAT file specification] Note that his specification is slightly different from ours. Apart from whether the first 4 (height) fields in GATBlock are correct, I believe that our specification more correct, based on various test results when I wrote the OpenKore map viewer and GAT conversion tool.
  • [RSW file specification]

Source: http://web.archive.org/web/20080529162301/http://www.openkore.com/wiki/index.php/Field_file_format