mirror of
https://github.com/retroplasma/earth-reverse-engineering.git
synced 2026-01-25 14:16:56 +00:00
141 lines
3.7 KiB
JavaScript
141 lines
3.7 KiB
JavaScript
"use strict";
|
|
|
|
const fs = require('fs-extra');
|
|
const path = require('path');
|
|
const getUrl = require('./get-url');
|
|
const decodeResource = require('./decode-resource');
|
|
|
|
// protobuf decoding constants
|
|
const [CMD_BULK, CMD_NODE] = [0, 3];
|
|
|
|
module.exports = function init(config) {
|
|
const { URL_PREFIX, DUMP_JSON_DIR, DUMP_RAW_DIR, DUMP_JSON, DUMP_RAW } = config;
|
|
|
|
DUMP_JSON && fs.ensureDirSync(DUMP_JSON_DIR);
|
|
DUMP_RAW && fs.ensureDirSync(DUMP_RAW_DIR);
|
|
|
|
const utils = {
|
|
|
|
bulk: {
|
|
// checks if element at index has a node
|
|
hasNodeAtIndex(bulk, index) {
|
|
return !(bulk.flags[index] & 8);
|
|
},
|
|
|
|
// checks if element at index has bulk metadata
|
|
// expects index to point at bulk metadata candidate
|
|
hasBulkMetadataAtIndex(bulk, index) {
|
|
return !!(bulk.flags[index] & 4);
|
|
},
|
|
|
|
// get index by path
|
|
// it accepts the whole or relative octant path
|
|
getIndexByPath(bulk, path) {
|
|
let c = -1;
|
|
for (let e = path, f = (e.length - 1) - ((e.length - 1) % 4); f < e.length; ++f)
|
|
c = bulk.childIndices[8 * (c + 1) + (e.charCodeAt(f) - 48)];
|
|
return c;
|
|
},
|
|
|
|
// get relative octant path by index
|
|
getPathByIndex(bulk, index) {
|
|
let [first] = utils.bulk.allPaths(bulk, i => i === index, true);
|
|
if (first === undefined) {
|
|
first = null;
|
|
}
|
|
return first;
|
|
},
|
|
|
|
// returns all paths in bulk (dirty method)
|
|
// also accepts filter
|
|
allPaths(bulk, filter = _ => true, stopAfterFirst = false) {
|
|
let result = [];
|
|
function next(oct, max) {
|
|
if (oct.length === max) return;
|
|
for (const nxt of [0, 1, 2, 3, 4, 5, 6, 7].map(a => a.toString())) {
|
|
const cur = oct + nxt;
|
|
const i = utils.bulk.getIndexByPath(bulk, cur);
|
|
if (i < 0) continue;
|
|
if (filter(i)) {
|
|
result.push(cur);
|
|
if (stopAfterFirst) return;
|
|
}
|
|
next(cur, max)
|
|
}
|
|
}
|
|
next("", 4);
|
|
return result;
|
|
},
|
|
},
|
|
|
|
async getNode(path, bulk, index) {
|
|
|
|
const nodeEpoch = bulk.epoch[index];
|
|
const nodeImgEpoch = bulk.imageryEpochArray ? bulk.imageryEpochArray[index] : bulk.defaultImageryEpoch;
|
|
const nodeTexFormat = bulk.textureFormatArray ? bulk.textureFormatArray[index] : bulk.defaultTextureFormat;
|
|
const nodeFlags = bulk.flags[index];
|
|
|
|
const imgEpochPart = nodeFlags & 16 ? `!3u${nodeImgEpoch}` : '';
|
|
const url = `!1m2!1s${path}!2u${nodeEpoch}!2e${nodeTexFormat}${imgEpochPart}!4b0`
|
|
|
|
return await decode(CMD_NODE, `NodeData/pb=${url}`, false);
|
|
},
|
|
|
|
async getPlanetoid() {
|
|
return await decode(CMD_BULK, `PlanetoidMetadata`);
|
|
},
|
|
|
|
async getBulk(path, epoch) {
|
|
return await decode(CMD_BULK, `BulkMetadata/pb=!1m2!1s${path}!2u${epoch}`);
|
|
},
|
|
|
|
};
|
|
|
|
const CACHE_ENABLED = true;
|
|
const cache = {};
|
|
const requests = {};
|
|
|
|
// download and decode map data
|
|
async function decode(command, url, useMemoryCache = true) {
|
|
|
|
if (useMemoryCache && CACHE_ENABLED && cache[url]) {
|
|
return cache[url];
|
|
}
|
|
|
|
if (requests[url]) {
|
|
// deduplicate parallel downloads
|
|
return await new Promise(function (resolve, reject) {
|
|
requests[url].push({ resolve, reject });
|
|
});
|
|
};
|
|
|
|
requests[url] = [];
|
|
|
|
let res;
|
|
try {
|
|
const payload = await getUrl(`${URL_PREFIX}${url}`);
|
|
const data = await decodeResource(command, payload);
|
|
res = data.payload;
|
|
|
|
if (useMemoryCache && CACHE_ENABLED) {
|
|
cache[url] = res;
|
|
}
|
|
|
|
const fn = url.replace('/pb=', '');
|
|
DUMP_JSON && fs.writeFileSync(path.join(DUMP_JSON_DIR, `${fn}.json`), JSON.stringify(res, null, 2));
|
|
DUMP_RAW && fs.writeFileSync(path.join(DUMP_RAW_DIR, `${fn}.raw`), payload);
|
|
} catch (ex) {
|
|
requests[url].forEach(p => p.reject(ex));
|
|
delete requests[url];
|
|
throw ex;
|
|
}
|
|
|
|
requests[url].forEach(p => p.resolve(res));
|
|
delete requests[url];
|
|
|
|
return res;
|
|
}
|
|
|
|
return utils;
|
|
}
|