Home Reference Source

src/Civ5SaveStringToBoolMap.js

import Civ5SaveBoolProperty from './Civ5SaveBoolProperty';
import Civ5SaveDataView from './Civ5SaveDataView';
import Civ5SaveIntProperty from './Civ5SaveIntProperty';
import Civ5SaveStringProperty from './Civ5SaveStringProperty';

/**
 * @ignore
 */
export default class Civ5SaveStringToBoolMap {
  /**
   * @ignore
   */
  constructor(byteOffset, saveData) {
    /**
     * @ignore
     */
    this.byteOffset = byteOffset;
    /**
     * @ignore
     */
    this.length = 4;
    /**
     * @private
     */
    this._items = new Map();
    /**
     * @private
     */
    this._size = new Civ5SaveIntProperty(this.byteOffset, 4, saveData);

    if (this._getSize(saveData) > 0) {
      let currentByteOffset = this.byteOffset + 4;
      for (let i = 0; i < this._getSize(saveData); i++) {
        currentByteOffset = this._addItemToMap(saveData, currentByteOffset);
      }
    }
  }

  /**
   * @private
   */
  _addItemToMap(saveData, byteOffset) {
    let itemKeyProperty = new Civ5SaveStringProperty(byteOffset, null, saveData);
    byteOffset += itemKeyProperty.length;
    let itemValueProperty = new Civ5SaveBoolProperty(byteOffset, 4, saveData);
    byteOffset += itemValueProperty.length;

    this._items.set(itemKeyProperty.getValue(saveData), itemValueProperty);
    this.length = byteOffset - this.byteOffset;

    return byteOffset;
  }

  /**
   * @private
   */
  _getSize(saveData) {
    return this._size.getValue(saveData);
  }

  /**
   * @private
   */
  _setSize(saveData, newValue) {
    this._size.setValue(saveData, newValue);
  }

  /**
   * @ignore
   */
  getValue(saveData, itemKey) {
    if (this._items.has(itemKey)) {
      return this._items.get(itemKey).getValue(saveData);
    } else {
      return false;
    }
  }

  /**
   * @ignore
   */
  setValue(saveData, itemKey, newItemValue) {
    if (this._items.has(itemKey)) {
      this._items.get(itemKey).setValue(saveData, newItemValue);

    } else {
      return this._addItemToSaveData(saveData, itemKey, newItemValue);
    }
  }

  /**
   * @private
   */
  _addItemToSaveData(saveData, itemKey, newItemValue) {
    this._setSize(saveData, this._getSize(saveData) + 1);

    let itemKeyLengthArray = this._int32ToUint8Array(itemKey.length);
    let itemKeyArray = this._stringToUint8Array(itemKey);
    let itemValueArray = this._int32ToUint8Array(Number(newItemValue));
    let arrayToInsert = this._concatTypedArrays(
      this._concatTypedArrays(
        itemKeyLengthArray,
        itemKeyArray
      ),
      itemValueArray
    );

    let newSaveDataTypedArray = this._insertIntoTypedArray(
      new Uint8Array(saveData.buffer),
      arrayToInsert,
      this.byteOffset + this.length);
    let newSaveData = new Civ5SaveDataView(newSaveDataTypedArray.buffer);

    this._addItemToMap(newSaveData, this.byteOffset + this.length);

    return newSaveData;
  }

  /**
   * @private
   */
  // Inspired by https://stackoverflow.com/a/12965194/399105
  _int32ToUint8Array(int32) {
    let int32Array = new Uint8Array(4);
    for (let i = 0; i < int32Array.length; i++) {
      let byte = int32 & 0xff;
      int32Array[i] = byte;
      int32 = (int32 - byte) / 256;
    }
    return int32Array;
  }

  /**
   * @private
   */
  _stringToUint8Array(string) {
    let stringArray = new Uint8Array(string.length);
    for (let i = 0; i < string.length; i++) {
      stringArray[i] = string.charCodeAt(i);
    }
    return stringArray;
  }

  /**
   * @private
   */
  // https://stackoverflow.com/a/33703102/399105
  _concatTypedArrays(a, b) {
    var c = new (a.constructor)(a.length + b.length);
    c.set(a, 0);
    c.set(b, a.length);
    return c;
  }

  /**
   * @private
   */
  _insertIntoTypedArray(array, arrayToInsert, insertAtByteOffset) {
    return this._concatTypedArrays(
      this._concatTypedArrays(
        array.slice(0, insertAtByteOffset),
        arrayToInsert
      ),
      array.slice(insertAtByteOffset, array.length)
    );
  }
}