lib/api/Side.js

const path = require('path');
const Map = require('./Map');
const { children, dictAsEncodedData, loadDataAsDict } = require('../utils');

const decoderBlacklist = [ 'Filler', 'Style', 'levels' ];
const validExt = '.bin';
const validSides = [ 'B', 'C' ];

/**
 * @class
 * @property {Map} map - The map for this side
 * @property {Object} data - The metadata for the side
 * @property {String} sideName - The name of this side, either `A`, `B`, or `C`
 */
class Side {
	/**
	 * Creates a Side instance
	 * @constructor
	 * @param {Object} [data={}] - Optional object that contains properties for the constructor
	 * @param {Map} [data.map=new Map()] - The map for this side
 	 * @param {Object} [data.data={}] - The metadata for the side
 	 * @param {String} [data.sideName='A'] - The name of this side, either `A`, `B`, or `C`
	 * @example
	 * // returns a Side
	 * const { Side } = require('lucid-dream');
	 * const side = new Side();
	 */
	constructor(data = {}) {
		Object.assign(this, {
			map: new Map(),
			data: {},
			sideName: 'A'
		}, data);
	}

	/**
	 * Load the map data and metadata for a side from a `.bin` file
	 * @param {String} file - The path to the `.bin` file to load
	 * @example
	 * const { Side } = require('lucid-dream');
	 * const side = new Side();
	 * await side.decode('/path/to/my-map.bin');
	 * @returns {null}
	 */
	async decode(file) {

		// ensure we received a proper file argument
		if (!file) {
			throw new Error('must provide a map file');
		}
		if (typeof file !== 'string') {
			throw new Error('map file argument must be a string');
		}

		// make sure file is an actual bin file
		const filename = path.basename(file);
		if (path.extname(file) !== validExt) {
			throw new Error(`invalid side file "${filename}", must have a .bin extension"`);
		}
		const basename = path.basename(file, validExt);
		const sideName = basename.split('-').pop();
		this.sideName = validSides.includes(sideName) ? sideName : 'A';

		// decode map data
		const mapData = await this.map.decode(file);
		this.data = loadMetadata.bind(this)(mapData);
	}

	/**
	 * Encodes a side to a file
	 * @param {String} file - The file to which to write the encoded side
	 * @example
	 * const { Side } = require('lucid-dream');
	 * const side = new Side();
	 * await side.decode('/path/to/my-map.bin');
	 * // make changes to side/map/meta data
	 * await side.encode('/path/to/my-new-map.bin');
	 */
	async encode(file) {
		const data = this.map;
		encodeMetadata.bind(this)(data);
		await this.map.encode(data, file);
	}

}


/*********************/
/* private functions */
/*********************/

function loadMetadata(data) {
	const res = {};
	for (let child of children(data)) {
		if (!decoderBlacklist.includes(child.__name)) {
			loadDataAsDict(res, child);
		}
	}
	return res;
}

function encodeMetadata(target) {
	for (let [ key, value ] of Object.entries(this.data)) {
		target.__children.push(dictAsEncodedData(key, value));
	}
}

module.exports = Side;