node-artifact-api
Version:
A node module wrapper for the Valve official Artifact API
192 lines (191 loc) • 6.95 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const buffer_1 = require("buffer");
class ArtifactDeckEncoder {
constructor() {
this.version = 2;
this.EncodedPrefix = 'ADC';
this.MaxBytesForVarUint32 = 5;
this.knHeaderSize = 3;
}
encodeDeck(deckContents) {
if (!deckContents) {
throw new Error('Error encoding deck');
}
const bytes = this.encodeBytes(deckContents);
if (!bytes) {
throw new Error('Error encoding deck');
}
const deckCode = this.encodeBytesToString(bytes);
return deckCode;
}
encodeBytes(deckContents) {
if (!deckContents.heroes || !deckContents.cards) {
throw new Error('Error encoding deck');
}
this.sortDeckByID(deckContents);
const countHeroes = deckContents.heroes.length;
const allCards = [...deckContents.heroes, ...deckContents.cards];
const bytes = [];
const version = this.version << 4 | this.extractNBitsWithCarry(countHeroes, 3);
if (!this.addByte(bytes, version)) {
throw new Error('Error encoding deck');
}
const nDummyChecksum = 0;
const nChecksumByte = bytes.length;
if (!this.addByte(bytes, nDummyChecksum)) {
throw new Error('Error encoding deck');
}
let nameLen = 0;
let name;
if (deckContents.name) {
name = deckContents.name;
let trimLen = name.length;
while (trimLen > 63) {
let amountToTrim = Math.floor((trimLen - 63) / 4);
amountToTrim = (amountToTrim > 1) ? amountToTrim : 1;
name = name.substring(0, name.length - amountToTrim);
trimLen = name.length;
}
nameLen = name.length;
}
if (!this.addByte(bytes, nameLen)) {
throw new Error('Error encoding deck');
}
if (!this.addRemainingNumberToBuffer(countHeroes, 3, bytes)) {
throw new Error('Error encoding deck');
}
let prevCardId = 0;
for (let unCurrHero = 0; unCurrHero < countHeroes; unCurrHero++) {
const card = allCards[unCurrHero];
if (card.turn === 0) {
throw new Error('Error encoding deck');
}
if (!this.addCardToBuffer(card.turn, card.id - prevCardId, bytes)) {
throw new Error('Error encoding deck');
}
prevCardId = card.id;
}
prevCardId = 0;
for (let nCurrCard = countHeroes; nCurrCard < allCards.length; nCurrCard++) {
const card = allCards[nCurrCard];
if (card.count === 0) {
throw new Error('Error encoding deck');
}
if (card.id <= 0) {
throw new Error('Error encoding deck');
}
if (!this.addCardToBuffer(card.count, card.id - prevCardId, bytes)) {
throw new Error('Error encoding deck');
}
prevCardId = card.id;
}
const preStringByteCount = bytes.length;
{
const nameBytes = this.unpack(name);
for (const nameByte of nameBytes) {
if (!this.addByte(bytes, nameByte)) {
throw new Error('Error encoding deck');
}
}
}
const unFullChecksum = this.computeChecksum(bytes, preStringByteCount - this.knHeaderSize);
const unSmallChecksum = (unFullChecksum & 0x0FF);
bytes[nChecksumByte] = unSmallChecksum;
return bytes;
}
encodeBytesToString(bytes) {
const byteCount = bytes.length;
if (byteCount === 0) {
throw new Error('Error encoding deck');
}
const packedAndEncoded = this.packAndEncode(bytes);
let deckString = this.EncodedPrefix + packedAndEncoded;
deckString = deckString.replace(/\//g, '-');
deckString = deckString.replace(/=/g, '_');
return deckString;
}
sortDeckByID(deck) {
function sortByID(a, b) {
return a.id <= b.id ? -1 : 1;
}
deck.heroes.sort(sortByID);
deck.cards.sort(sortByID);
}
extractNBitsWithCarry(value, numBits) {
const unLimitBit = 1 << numBits;
let unResult = (value & (unLimitBit - 1));
if (value >= unLimitBit) {
unResult |= unLimitBit;
}
return unResult;
}
addByte(bytes, byte) {
if (byte > 255) {
throw new Error('Error encoding deck');
}
bytes.push(byte);
return true;
}
addRemainingNumberToBuffer(unValue, unAlreadyWrittenBits, bytes) {
unValue >>= unAlreadyWrittenBits;
let unNumBytes = 0;
while (unValue > 0) {
const unNextByte = this.extractNBitsWithCarry(unValue, 7);
unValue >>= 7;
if (!this.addByte(bytes, unNextByte)) {
throw new Error('Error encoding deck');
}
unNumBytes++;
}
return true;
}
addCardToBuffer(unCount, unValue, bytes) {
if (unCount === 0) {
throw new Error('Error encoding deck');
}
const countBytesStart = bytes.length;
const knFirstByteMaxCount = 0x03;
const bExtendedCount = (unCount - 1) >= knFirstByteMaxCount;
const unFirstByteCount = bExtendedCount ? knFirstByteMaxCount : (unCount - 1);
let unFirstByte = (unFirstByteCount << 6);
unFirstByte |= this.extractNBitsWithCarry(unValue, 5);
if (!this.addByte(bytes, unFirstByte)) {
throw new Error('Error encoding deck');
}
if (!this.addRemainingNumberToBuffer(unValue, 5, bytes)) {
throw new Error('Error encoding deck');
}
if (bExtendedCount) {
if (!this.addRemainingNumberToBuffer(unCount, 0, bytes)) {
throw new Error('Error encoding deck');
}
}
const countBytesEnd = bytes.length;
if (countBytesEnd - countBytesStart > 11) {
throw new Error('Error encoding deck');
}
return true;
}
unpack(str) {
const binArr = [];
for (let i = 0; i < str.length; i++) {
binArr.push(str.charCodeAt(i));
}
return binArr;
}
packAndEncode(binArr) {
const buff = buffer_1.Buffer.from(binArr);
const encoded = buff.toString('base64');
return encoded;
}
computeChecksum(bytes, unNumBytes) {
let unChecksum = 0;
for (let unAddCheck = this.knHeaderSize; unAddCheck < unNumBytes + this.knHeaderSize; unAddCheck++) {
const byte = bytes[unAddCheck];
unChecksum += byte;
}
return unChecksum;
}
}
exports.ArtifactDeckEncoder = ArtifactDeckEncoder;