将数值编码为字母数字代码(反向解码功能)
Encoding numerical values into alphanumeric code (reversing decoding function)
我有一个函数将 CS:GO 十字准线代码解码为键值对象。
(之前我问过关于如何从 CS:GO 中解码共享代码的问题)
如何将这些值解码成由字母数字字符组成的“共享代码”?
函数解码分享代码:
const BigNumber = require("bignumber.js");
// Intentionally no 0 and 1 number in DICTIONARY
const DICTIONARY = "ABCDEFGHJKLMNOPQRSTUVWXYZabcdefhijkmnopqrstuvwxyz23456789";
const DICTIONARY_LENGTH = DICTIONARY.length;
const SHARECODE_PATTERN = /CSGO(-?[\w]{5}){5}$/;
const bigNumberToByteArray = big => {
const str = big.toString(16).padStart(36, "0");
const bytes = [];
for (let i = 0; i < str.length; i += 2) {
bytes.push(parseInt(str.slice(i, i + 2), 16));
}
return bytes;
}
const parseBytes = bytes => {
return {
cl_crosshairgap: Int8Array.of(bytes[2])[0] / 10.0,
cl_crosshair_outlinethickness: (bytes[3] & 7) / 2.0,
cl_crosshaircolor_r: bytes[4],
cl_crosshaircolor_g: bytes[5],
cl_crosshaircolor_b: bytes[6],
cl_crosshairalpha: bytes[7],
cl_crosshair_dynamic_splitdist: bytes[8],
cl_fixedcrosshairgap: Int8Array.of(bytes[9])[0] / 10.0,
cl_crosshaircolor: bytes[10] & 7,
cl_crosshair_drawoutline: bytes[10] & 8 ? 1 : 0,
cl_crosshair_dynamic_splitalpha_innermod: ((bytes[10] & 0xF0) >> 4) / 10.0,
cl_crosshair_dynamic_splitalpha_outermod: (bytes[11] & 0xF) / 10.0,
cl_crosshair_dynamic_maxdist_splitratio: ((bytes[11] & 0xF0) >> 4) / 10.0,
cl_crosshairthickness: (bytes[12] & 0x3F) / 10.0,
cl_crosshairstyle: (bytes[13] & 0xE) >> 1,
cl_crosshairdot: bytes[13] & 0x10 ? 1 : 0,
cl_crosshairgap_useweaponvalue: bytes[13] & 0x20 ? 1 : 0,
cl_crosshairusealpha: bytes[13] & 0x40 ? 1 : 0,
cl_crosshair_t: bytes[13] & 0x80 ? 1 : 0,
cl_crosshairsize: (((bytes[15] & 0x1f) << 8) + bytes[14]) / 10.0
};
}
const decode = shareCode => {
if (!shareCode.match(SHARECODE_PATTERN)) {
throw new Error('Invalid share code');
}
shareCode = shareCode.replace(/CSGO|-/g, '');
const chars = Array.from(shareCode).reverse();
let big = new BigNumber(0);
for (let i = 0; i < chars.length; i++) {
big = big.multipliedBy(DICTIONARY_LENGTH).plus(DICTIONARY.indexOf(chars[i]));
}
return parseBytes(bigNumberToByteArray(big));
}
console.log(decode('CSGO-O4Jsi-V36wY-rTMGK-9w7qF-jQ8WB'))
// OUTPUT:
// {
// cl_crosshairgap: 1,
// cl_crosshair_outlinethickness: 1.5,
// cl_crosshaircolor_r: 50,
// cl_crosshaircolor_g: 250,
// cl_crosshaircolor_b: 84,
// cl_crosshairalpha: 200,
// cl_crosshair_dynamic_splitdist: 127,
// cl_fixedcrosshairgap: -10,
// cl_crosshaircolor: 5,
// cl_crosshair_drawoutline: 0,
// cl_crosshair_dynamic_splitalpha_innermod: 0.6,
// cl_crosshair_dynamic_splitalpha_outermod: 0.8,
// cl_crosshair_dynamic_maxdist_splitratio: 0.3,
// cl_crosshairthickness: 4.1,
// cl_crosshairstyle: 2,
// cl_crosshairdot: 1,
// cl_crosshairgap_useweaponvalue: 0,
// cl_crosshairusealpha: 0,
// cl_crosshair_t: 1,
// cl_crosshairsize: 33
// }
因此值如下:
{
cl_crosshairgap: 1,
cl_crosshair_outlinethickness: 1.5,
cl_crosshaircolor_r: 50,
cl_crosshaircolor_g: 250,
cl_crosshaircolor_b: 84,
cl_crosshairalpha: 200,
cl_crosshair_dynamic_splitdist: 127,
cl_fixedcrosshairgap: -10,
cl_crosshaircolor: 5,
cl_crosshair_drawoutline: 0,
cl_crosshair_dynamic_splitalpha_innermod: 0.6,
cl_crosshair_dynamic_splitalpha_outermod: 0.8,
cl_crosshair_dynamic_maxdist_splitratio: 0.3,
cl_crosshairthickness: 4.1,
cl_crosshairstyle: 2,
cl_crosshairdot: 1,
cl_crosshairgap_useweaponvalue: 0,
cl_crosshairusealpha: 0,
cl_crosshair_t: 1,
cl_crosshairsize: 33
}
应编码为:CSGO-O4Jsi-V36wY-rTMGK-9w7qF-jQ8WB
功能编码匹配共享代码,这可能是编码十字准线代码的基础:
const BigNumber = require("bignumber.js");
// Intentionally no 0 and 1 number in DICTIONARY
const DICTIONARY = "ABCDEFGHJKLMNOPQRSTUVWXYZabcdefhijkmnopqrstuvwxyz23456789";
const DICTIONARY_LENGTH = DICTIONARY.length;
const SHARECODE_PATTERN = /CSGO(-?[\w]{5}){5}$/;
function bytesToHex(bytes) {
return Array.from(bytes, (byte) => {
return ('0' + (byte & 0xff).toString(16)).slice(-2);
}).join('');
}
function bigNumberToByteArray(big) {
const str = big.toString(16).padStart(36, '0');
const bytes = [];
for (let i = 0; i < str.length; i += 2) {
bytes.push(parseInt(str.slice(i, i + 2), 16));
}
return bytes;
}
function longToBytesBE(high, low) {
return [
(high >>> 24) & 0xff,
(high >>> 16) & 0xff,
(high >>> 8) & 0xff,
high & 0xff,
(low >>> 24) & 0xff,
(low >>> 16) & 0xff,
(low >>> 8) & 0xff,
low & 0xff,
];
}
function int16ToBytes(number) {
return [(number & 0x0000ff00) >> 8, number & 0x000000ff];
}
function bytesToInt32(bytes) {
let number = 0;
for (let i = 0; i < bytes.length; i++) {
number += bytes[i];
if (i < bytes.length - 1) {
number = number << 8;
}
}
return number;
}
function bigNumberToByteArray(big) {
const str = big.toString(16).padStart(36, "0");
const bytes = [];
for (let i = 0; i < str.length; i += 2) {
bytes.push(parseInt(str.slice(i, i + 2), 16));
}
return bytes;
}
const encode = (matchId, reservationId, tvPort) => {
const matchBytes = longToBytesBE(matchId.high, matchId.low).reverse();
const reservationBytes = longToBytesBE(reservationId.high, reservationId.low).reverse();
const tvBytes = int16ToBytes(tvPort).reverse();
const bytes = Array.prototype.concat(matchBytes, reservationBytes, tvBytes);
const bytesHex = bytesToHex(bytes);
let total = new BigNumber(bytesHex, 16);
// This part would probably be identical
let c = '';
let rem = new BigNumber(0);
for (let i = 0; i < 25; i++) {
rem = total.mod(DICTIONARY_LENGTH);
c += DICTIONARY[rem.integerValue(BigNumber.ROUND_FLOOR).toNumber()];
total = total.div(DICTIONARY_LENGTH);
}
return `CSGO-${c.substr(0, 5)}-${c.substr(5, 5)}-${c.substr(10, 5)}-${c.substr(15, 5)}-${c.substr(20, 5)}`;
};
console.log(encode(
{
low: -2147483492, high: 752192506
},
{
low: 143, high: 752193760
},
55788
));
// OUTPUT:
// CSGO-GADqf-jjyJ8-cSP2r-smZRo-TO2xK
我还发现 Python 代码做同样的事情(使用“匹配代码”,包含较少的编码值)——我知道这是 JS 问题,包括这个只是为了识别相似之处
import re
dictionary = "ABCDEFGHJKLMNOPQRSTUVWXYZabcdefhijkmnopqrstuvwxyz23456789"
def _swap_endianness(number):
result = 0
for n in range(0, 144, 8):
result = (result << 8) + ((number >> n) & 0xFF)
return result
def encode(matchid, outcomeid, token):
a = _swap_endianness((token << 128) | (outcomeid << 64) | matchid)
code = ''
for _ in range(25):
a, r = divmod(a, len(dictionary))
code += dictionary[r]
return "CSGO-%s-%s-%s-%s-%s" % (code[:5], code[5:10], code[10:15], code[15:20], code[20:])
print(encode(250, 34, 10))
# CSGO-t4kTW-mcVyA-TcReG-hviRe-pXNtQ
根据所提供的信息,创建编码器将是困难甚至不可能的,因为编码位比您指定的源要多得多。代码足够大,可以容纳 18 个字节。您解码仅使用其中的 14 个,甚至不是全部。最有趣的(对我来说)是字节 13 的最低有效位下落不明。
即使您为其中的大多数设置了合理的默认值,您也必须弄清楚如何计算字节 0,它似乎是某种校验和。
以下似乎有效。我在 中解码的对象上进行了尝试,并且所有对象都是无损往返。
const DICTIONARY = "ABCDEFGHJKLMNOPQRSTUVWXYZabcdefhijkmnopqrstuvwxyz23456789";
const DICTIONARY_LENGTH = BigInt(DICTIONARY.length);
const serializeToBytes = info => {
const bytes = [
0,
1,
(info.cl_crosshairgap * 10) & 0xff,
(info.cl_crosshair_outlinethickness * 2) & 7,
info.cl_crosshaircolor_r,
info.cl_crosshaircolor_g,
info.cl_crosshaircolor_b,
info.cl_crosshairalpha,
info.cl_crosshair_dynamic_splitdist,
(info.cl_fixedcrosshairgap * 10) & 0xff,
(info.cl_crosshaircolor & 7) |
(info.cl_crosshair_drawoutline ? 8 : 0) |
(info.cl_crosshair_dynamic_splitalpha_innermod * 10) << 4,
((info.cl_crosshair_dynamic_splitalpha_outermod * 10) & 0xf) |
((info.cl_crosshair_dynamic_maxdist_splitratio * 10) << 4),
(info.cl_crosshairthickness * 10) & 0x3f,
((info.cl_crosshairstyle << 1) & 0xe) |
(info.cl_crosshairdot ? 0x10 : 0) |
(info.cl_crosshairgap_useweaponvalue ? 0x20 : 0) |
(info.cl_crosshairusealpha ? 0x40 : 0) |
(info.cl_crosshair_t ? 0x80 : 0),
(info.cl_crosshairsize * 10) & 0xff,
((info.cl_crosshairsize * 10) >> 8) & 0x1f,
0,
0
];
let sum = 0;
for (let i = 1; i < bytes.length; ++i) {
sum += bytes[i];
}
bytes[0] = sum & 0xff;
return bytes;
};
const encode = info => {
const bytes = serializeToBytes(info);
let acc = 0n;
let pos = 1n;
for (let i = bytes.length; i --> 0;) {
acc += BigInt(bytes[i]) * pos;
pos *= 256n;
}
let result = '';
for (let i = 0; i < 25; ++i) {
const digit = acc % DICTIONARY_LENGTH;
acc = acc / DICTIONARY_LENGTH;
result += DICTIONARY.charAt(Number(digit));
}
return `CSGO-${result.slice(0, 5)}-${result.slice(5, 10)}-${result.slice(10, 15)}-${result.slice(15, 20)}-${result.slice(20, 25)}`;
};
我有一个函数将 CS:GO 十字准线代码解码为键值对象。
(之前我问过关于如何从 CS:GO
如何将这些值解码成由字母数字字符组成的“共享代码”?
函数解码分享代码:
const BigNumber = require("bignumber.js");
// Intentionally no 0 and 1 number in DICTIONARY
const DICTIONARY = "ABCDEFGHJKLMNOPQRSTUVWXYZabcdefhijkmnopqrstuvwxyz23456789";
const DICTIONARY_LENGTH = DICTIONARY.length;
const SHARECODE_PATTERN = /CSGO(-?[\w]{5}){5}$/;
const bigNumberToByteArray = big => {
const str = big.toString(16).padStart(36, "0");
const bytes = [];
for (let i = 0; i < str.length; i += 2) {
bytes.push(parseInt(str.slice(i, i + 2), 16));
}
return bytes;
}
const parseBytes = bytes => {
return {
cl_crosshairgap: Int8Array.of(bytes[2])[0] / 10.0,
cl_crosshair_outlinethickness: (bytes[3] & 7) / 2.0,
cl_crosshaircolor_r: bytes[4],
cl_crosshaircolor_g: bytes[5],
cl_crosshaircolor_b: bytes[6],
cl_crosshairalpha: bytes[7],
cl_crosshair_dynamic_splitdist: bytes[8],
cl_fixedcrosshairgap: Int8Array.of(bytes[9])[0] / 10.0,
cl_crosshaircolor: bytes[10] & 7,
cl_crosshair_drawoutline: bytes[10] & 8 ? 1 : 0,
cl_crosshair_dynamic_splitalpha_innermod: ((bytes[10] & 0xF0) >> 4) / 10.0,
cl_crosshair_dynamic_splitalpha_outermod: (bytes[11] & 0xF) / 10.0,
cl_crosshair_dynamic_maxdist_splitratio: ((bytes[11] & 0xF0) >> 4) / 10.0,
cl_crosshairthickness: (bytes[12] & 0x3F) / 10.0,
cl_crosshairstyle: (bytes[13] & 0xE) >> 1,
cl_crosshairdot: bytes[13] & 0x10 ? 1 : 0,
cl_crosshairgap_useweaponvalue: bytes[13] & 0x20 ? 1 : 0,
cl_crosshairusealpha: bytes[13] & 0x40 ? 1 : 0,
cl_crosshair_t: bytes[13] & 0x80 ? 1 : 0,
cl_crosshairsize: (((bytes[15] & 0x1f) << 8) + bytes[14]) / 10.0
};
}
const decode = shareCode => {
if (!shareCode.match(SHARECODE_PATTERN)) {
throw new Error('Invalid share code');
}
shareCode = shareCode.replace(/CSGO|-/g, '');
const chars = Array.from(shareCode).reverse();
let big = new BigNumber(0);
for (let i = 0; i < chars.length; i++) {
big = big.multipliedBy(DICTIONARY_LENGTH).plus(DICTIONARY.indexOf(chars[i]));
}
return parseBytes(bigNumberToByteArray(big));
}
console.log(decode('CSGO-O4Jsi-V36wY-rTMGK-9w7qF-jQ8WB'))
// OUTPUT:
// {
// cl_crosshairgap: 1,
// cl_crosshair_outlinethickness: 1.5,
// cl_crosshaircolor_r: 50,
// cl_crosshaircolor_g: 250,
// cl_crosshaircolor_b: 84,
// cl_crosshairalpha: 200,
// cl_crosshair_dynamic_splitdist: 127,
// cl_fixedcrosshairgap: -10,
// cl_crosshaircolor: 5,
// cl_crosshair_drawoutline: 0,
// cl_crosshair_dynamic_splitalpha_innermod: 0.6,
// cl_crosshair_dynamic_splitalpha_outermod: 0.8,
// cl_crosshair_dynamic_maxdist_splitratio: 0.3,
// cl_crosshairthickness: 4.1,
// cl_crosshairstyle: 2,
// cl_crosshairdot: 1,
// cl_crosshairgap_useweaponvalue: 0,
// cl_crosshairusealpha: 0,
// cl_crosshair_t: 1,
// cl_crosshairsize: 33
// }
因此值如下:
{
cl_crosshairgap: 1,
cl_crosshair_outlinethickness: 1.5,
cl_crosshaircolor_r: 50,
cl_crosshaircolor_g: 250,
cl_crosshaircolor_b: 84,
cl_crosshairalpha: 200,
cl_crosshair_dynamic_splitdist: 127,
cl_fixedcrosshairgap: -10,
cl_crosshaircolor: 5,
cl_crosshair_drawoutline: 0,
cl_crosshair_dynamic_splitalpha_innermod: 0.6,
cl_crosshair_dynamic_splitalpha_outermod: 0.8,
cl_crosshair_dynamic_maxdist_splitratio: 0.3,
cl_crosshairthickness: 4.1,
cl_crosshairstyle: 2,
cl_crosshairdot: 1,
cl_crosshairgap_useweaponvalue: 0,
cl_crosshairusealpha: 0,
cl_crosshair_t: 1,
cl_crosshairsize: 33
}
应编码为:CSGO-O4Jsi-V36wY-rTMGK-9w7qF-jQ8WB
功能编码匹配共享代码,这可能是编码十字准线代码的基础:
const BigNumber = require("bignumber.js");
// Intentionally no 0 and 1 number in DICTIONARY
const DICTIONARY = "ABCDEFGHJKLMNOPQRSTUVWXYZabcdefhijkmnopqrstuvwxyz23456789";
const DICTIONARY_LENGTH = DICTIONARY.length;
const SHARECODE_PATTERN = /CSGO(-?[\w]{5}){5}$/;
function bytesToHex(bytes) {
return Array.from(bytes, (byte) => {
return ('0' + (byte & 0xff).toString(16)).slice(-2);
}).join('');
}
function bigNumberToByteArray(big) {
const str = big.toString(16).padStart(36, '0');
const bytes = [];
for (let i = 0; i < str.length; i += 2) {
bytes.push(parseInt(str.slice(i, i + 2), 16));
}
return bytes;
}
function longToBytesBE(high, low) {
return [
(high >>> 24) & 0xff,
(high >>> 16) & 0xff,
(high >>> 8) & 0xff,
high & 0xff,
(low >>> 24) & 0xff,
(low >>> 16) & 0xff,
(low >>> 8) & 0xff,
low & 0xff,
];
}
function int16ToBytes(number) {
return [(number & 0x0000ff00) >> 8, number & 0x000000ff];
}
function bytesToInt32(bytes) {
let number = 0;
for (let i = 0; i < bytes.length; i++) {
number += bytes[i];
if (i < bytes.length - 1) {
number = number << 8;
}
}
return number;
}
function bigNumberToByteArray(big) {
const str = big.toString(16).padStart(36, "0");
const bytes = [];
for (let i = 0; i < str.length; i += 2) {
bytes.push(parseInt(str.slice(i, i + 2), 16));
}
return bytes;
}
const encode = (matchId, reservationId, tvPort) => {
const matchBytes = longToBytesBE(matchId.high, matchId.low).reverse();
const reservationBytes = longToBytesBE(reservationId.high, reservationId.low).reverse();
const tvBytes = int16ToBytes(tvPort).reverse();
const bytes = Array.prototype.concat(matchBytes, reservationBytes, tvBytes);
const bytesHex = bytesToHex(bytes);
let total = new BigNumber(bytesHex, 16);
// This part would probably be identical
let c = '';
let rem = new BigNumber(0);
for (let i = 0; i < 25; i++) {
rem = total.mod(DICTIONARY_LENGTH);
c += DICTIONARY[rem.integerValue(BigNumber.ROUND_FLOOR).toNumber()];
total = total.div(DICTIONARY_LENGTH);
}
return `CSGO-${c.substr(0, 5)}-${c.substr(5, 5)}-${c.substr(10, 5)}-${c.substr(15, 5)}-${c.substr(20, 5)}`;
};
console.log(encode(
{
low: -2147483492, high: 752192506
},
{
low: 143, high: 752193760
},
55788
));
// OUTPUT:
// CSGO-GADqf-jjyJ8-cSP2r-smZRo-TO2xK
我还发现 Python 代码做同样的事情(使用“匹配代码”,包含较少的编码值)——我知道这是 JS 问题,包括这个只是为了识别相似之处
import re
dictionary = "ABCDEFGHJKLMNOPQRSTUVWXYZabcdefhijkmnopqrstuvwxyz23456789"
def _swap_endianness(number):
result = 0
for n in range(0, 144, 8):
result = (result << 8) + ((number >> n) & 0xFF)
return result
def encode(matchid, outcomeid, token):
a = _swap_endianness((token << 128) | (outcomeid << 64) | matchid)
code = ''
for _ in range(25):
a, r = divmod(a, len(dictionary))
code += dictionary[r]
return "CSGO-%s-%s-%s-%s-%s" % (code[:5], code[5:10], code[10:15], code[15:20], code[20:])
print(encode(250, 34, 10))
# CSGO-t4kTW-mcVyA-TcReG-hviRe-pXNtQ
根据所提供的信息,创建编码器将是困难甚至不可能的,因为编码位比您指定的源要多得多。代码足够大,可以容纳 18 个字节。您解码仅使用其中的 14 个,甚至不是全部。最有趣的(对我来说)是字节 13 的最低有效位下落不明。
即使您为其中的大多数设置了合理的默认值,您也必须弄清楚如何计算字节 0,它似乎是某种校验和。
以下似乎有效。我在
const DICTIONARY = "ABCDEFGHJKLMNOPQRSTUVWXYZabcdefhijkmnopqrstuvwxyz23456789";
const DICTIONARY_LENGTH = BigInt(DICTIONARY.length);
const serializeToBytes = info => {
const bytes = [
0,
1,
(info.cl_crosshairgap * 10) & 0xff,
(info.cl_crosshair_outlinethickness * 2) & 7,
info.cl_crosshaircolor_r,
info.cl_crosshaircolor_g,
info.cl_crosshaircolor_b,
info.cl_crosshairalpha,
info.cl_crosshair_dynamic_splitdist,
(info.cl_fixedcrosshairgap * 10) & 0xff,
(info.cl_crosshaircolor & 7) |
(info.cl_crosshair_drawoutline ? 8 : 0) |
(info.cl_crosshair_dynamic_splitalpha_innermod * 10) << 4,
((info.cl_crosshair_dynamic_splitalpha_outermod * 10) & 0xf) |
((info.cl_crosshair_dynamic_maxdist_splitratio * 10) << 4),
(info.cl_crosshairthickness * 10) & 0x3f,
((info.cl_crosshairstyle << 1) & 0xe) |
(info.cl_crosshairdot ? 0x10 : 0) |
(info.cl_crosshairgap_useweaponvalue ? 0x20 : 0) |
(info.cl_crosshairusealpha ? 0x40 : 0) |
(info.cl_crosshair_t ? 0x80 : 0),
(info.cl_crosshairsize * 10) & 0xff,
((info.cl_crosshairsize * 10) >> 8) & 0x1f,
0,
0
];
let sum = 0;
for (let i = 1; i < bytes.length; ++i) {
sum += bytes[i];
}
bytes[0] = sum & 0xff;
return bytes;
};
const encode = info => {
const bytes = serializeToBytes(info);
let acc = 0n;
let pos = 1n;
for (let i = bytes.length; i --> 0;) {
acc += BigInt(bytes[i]) * pos;
pos *= 256n;
}
let result = '';
for (let i = 0; i < 25; ++i) {
const digit = acc % DICTIONARY_LENGTH;
acc = acc / DICTIONARY_LENGTH;
result += DICTIONARY.charAt(Number(digit));
}
return `CSGO-${result.slice(0, 5)}-${result.slice(5, 10)}-${result.slice(10, 15)}-${result.slice(15, 20)}-${result.slice(20, 25)}`;
};