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; 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 = { 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, }, }; };