Source

loaders/MapLoader.js

/**
 * @typedef Tile
 * @property {number} height Used to construct a heightmap
 * @property {number} attrOpcode
 * @property {number} overlayId Overlay Definition ID
 * @property {number} overlayPath
 * @property {number} overlayRotation
 * @property {number} settings
 * @property {number} underlayId Underlay Definition ID
 */

/**
 * @typedef Location
 * @property {number} id ObjectDefinition ID
 * @property {number} type 
 * @property {number} orientation Rotation
 * @property {{x,y,z}} position 
*/

/**
* @class MapDefinition
* @category Definitions
* @hideconstructor
*/
export class MapDefinition {
    /** @type {number} */
    id;

    /** @type {number} */
    regionX;

    /** @type {number} */
    regionY;

    /** 
     * Tile info including Overlay/Underlay and height
     * @type {Tile}
     */
    tiles = [];
}

/**
* @class LocationDefinition
* @category Definitions
* @hideconstructor
*/
export class LocationDefinition {

    /** @type {number} */
    id;

    /** @type {number} */
    regionX;

    /** @type {number} */
    regionY;

    /** 
     * Objects on the map
     * @type {Location}
     */
    locations = [];
}
export class EmptyMapDefinition {

}
export default class MapLoader {

    /*
    for(let i=0;i<32768;i++){
        let x = i >> 8;
        let y = i & 0xFF;
        if(hash("l"+x+"_"+y) == hashVal){
            console.log("l"+x+"_"+y);
            break;
        }
        if(hash("m"+x+"_"+y) == hashVal){
            console.log("m"+x+"_"+y);
            break;
        }
    }
    */
    hash(str) {
        let h = 0;
        for (let i = 0; i < str.length; i++) {
            h = h * 31 + str.charCodeAt(i);
        }
        return (new Int32Array([h]))[0];
    }



    load(bytes, id, rscache) {
        if (bytes == undefined) return new MapDefinition();
        let x, y;
        let mapInfo = rscache.cacheRequester.xteas[id];
        //if there is xteas then its a location def
        if (mapInfo != undefined) {
            x = mapInfo.mapsquare >> 8;
            y = mapInfo.mapsquare & 0xFF;
            return this.loadLocationDef(bytes, id, x, y);
        } else {
            let hashVal = rscache.indicies[5].archives[id].nameHash;
            for (let i = 0; i < 32768; i++) {
                let x = i >> 8;
                let y = i & 0xFF;

                //no xteas and its a terrain map
                if (this.hash("m" + x + "_" + y) == hashVal) {
                    return this.loadMapDef(bytes, id, x, y);
                } //no xteas and its a location def
                if (this.hash("l" + x + "_" + y) == hashVal) {
                    //not much we can do here without xteas
                    return new LocationDefinition();
                }

            }
        }

        //no other case matched
        return new EmptyMapDefinition();
    }
    loadLocationDef(bytes, defId, x, y) {
        let def = new LocationDefinition();
        def.id = defId;
        def.regionX = x;
        def.regionY = y;
        def.locations = [];
        let dataview = new DataView(bytes.buffer);

        let id = -1;
        let idOffset;

        while ((idOffset = dataview.readUnsignedIntSmartShortCompat()) != 0) {
            id += idOffset;

            let position = 0;
            let positionOffset;

            while ((positionOffset = dataview.readUnsignedShortSmart()) != 0) {
                position += positionOffset - 1;

                let localY = position & 0x3F;
                let localX = position >> 6 & 0x3F;
                let height = position >> 12 & 0x3;

                let attributes = dataview.readUint8();
                let type = attributes >> 2;
                let orientation = attributes & 0x3;

                def.locations.push({ id, type, orientation, position: { localX, localY, height } });
            }
        }

        return def;
    }

    loadMapDef(bytes, defId, x, y) {
        let X = 64;
        let Y = 64;
        let Z = 4;
        let def = new MapDefinition();
        def.id = defId;
        def.regionX = x;
        def.regionY = y;
        let dataview = new DataView(bytes.buffer);

        def.tiles = [];

        for (let z = 0; z < Z; z++) {
            def.tiles[z] = [];
            for (let x = 0; x < X; x++) {
                def.tiles[z][x] = [];
                for (let y = 0; y < Y; y++) {
                    def.tiles[z][x][y] = {};
                    let tile = def.tiles[z][x][y];
                    while (true) {
                        let attribute = dataview.readUint16();
                        if (attribute == 0) {
                            break;
                        }
                        else if (attribute == 1) {
                            tile.height = dataview.readUint8();
                            break;
                        }
                        else if (attribute <= 49) {
                            tile.attrOpcode = attribute;
                            tile.overlayId = dataview.readInt16();
                            tile.overlayPath = ((attribute - 2) / 4);
                            tile.overlayRotation = (attribute - 2 & 3);
                        }
                        else if (attribute <= 81) {
                            tile.settings = (attribute - 49);
                        }
                        else {
                            tile.underlayId = (attribute - 81);
                        }
                    }

                }
            }
        }

        return def;
    }

}