import DataScanner from "../util/scanner";
|
|
|
|
export enum TileType {
|
|
WALKABLE,
|
|
OBSTRUCTED,
|
|
CLIFF,
|
|
}
|
|
|
|
export enum Corner {
|
|
TOP_LEFT,
|
|
TOP_RIGHT,
|
|
BOTTOM_RIGHT,
|
|
BOTTOM_LEFT,
|
|
}
|
|
|
|
export type TileAltitudes = Record<Corner, number>;
|
|
|
|
export type Tile = {
|
|
altitude: TileAltitudes;
|
|
ty: TileType;
|
|
};
|
|
|
|
export type GAT = {
|
|
height: number;
|
|
tiles: Tile[];
|
|
width: number;
|
|
};
|
|
|
|
export class ParseError extends Error {
|
|
constructor(message: string) {
|
|
super(message);
|
|
}
|
|
}
|
|
|
|
const HEADER_MAGIC = 0x54415247;
|
|
const HEADER_SIZE_BYTES = 14;
|
|
const SUPPORTED_VERSION = "1.2";
|
|
const TILE_SIZE_BYTES = 20;
|
|
const SCALING_FACTOR = -1 / 5;
|
|
|
|
const TERRAIN_TYPE_TO_TILE_TYPE: Record<number, TileType> = {
|
|
0: TileType.WALKABLE,
|
|
1: TileType.OBSTRUCTED,
|
|
5: TileType.CLIFF,
|
|
};
|
|
|
|
export const parseGAT = (data: ArrayBuffer): GAT => {
|
|
const scanner = new DataScanner(data);
|
|
|
|
const magic = scanner.uint32();
|
|
if (magic !== HEADER_MAGIC) {
|
|
throw new ParseError("invalid magic number");
|
|
}
|
|
|
|
const version = `${scanner.uint8()}.${scanner.uint8()}`;
|
|
if (version !== SUPPORTED_VERSION) {
|
|
throw new ParseError(`unsupported file version ${version}`);
|
|
}
|
|
|
|
const width = scanner.uint32();
|
|
const height = scanner.uint32();
|
|
|
|
const expectedSize = HEADER_SIZE_BYTES + width * height * TILE_SIZE_BYTES;
|
|
if (data.byteLength !== expectedSize) {
|
|
throw new ParseError(`unexpected size of data`);
|
|
}
|
|
|
|
const tiles = [];
|
|
for (let i = 0; i < width * height; ++i) {
|
|
tiles[i] = parseTile(scanner);
|
|
}
|
|
|
|
return {
|
|
height,
|
|
tiles,
|
|
width,
|
|
};
|
|
};
|
|
|
|
const parseTile = (scanner: DataScanner): Tile => {
|
|
const bottomLeft = scanner.float32();
|
|
const bottomRight = scanner.float32();
|
|
const topLeft = scanner.float32();
|
|
const topRight = scanner.float32();
|
|
|
|
const terrainType = scanner.uint32() & 0xffff;
|
|
if (typeof TERRAIN_TYPE_TO_TILE_TYPE[terrainType] === "undefined") {
|
|
throw new Error(`unknown terrain type 0x${terrainType.toString(16)}`);
|
|
}
|
|
|
|
return {
|
|
ty: TERRAIN_TYPE_TO_TILE_TYPE[terrainType],
|
|
altitude: {
|
|
[Corner.TOP_LEFT]: topLeft * SCALING_FACTOR,
|
|
[Corner.TOP_RIGHT]: topRight * SCALING_FACTOR,
|
|
[Corner.BOTTOM_RIGHT]: bottomRight * SCALING_FACTOR,
|
|
[Corner.BOTTOM_LEFT]: bottomLeft * SCALING_FACTOR,
|
|
},
|
|
};
|
|
};
|