UNPKG

node-artifact-api

Version:

A node module wrapper for the Valve official Artifact API

192 lines (191 loc) 6.95 kB
"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;