Skip to content

Commit

Permalink
refactor: use typescript
Browse files Browse the repository at this point in the history
- Fix possible incorrect share code string detection
- prettify code
  • Loading branch information
akiver committed Aug 29, 2018
1 parent 6e70a17 commit d590091
Show file tree
Hide file tree
Showing 10 changed files with 232 additions and 162 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ yarn-error.log*
.npm
.idea
.DS_Store
dist/
6 changes: 6 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"semi": false,
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "es5"
}
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Changelog

##### 1.1.0

* Fix possible incorrect share code string detection
* TypeScript support

##### 1.0.0

Initial release
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ Node module to decode / encode the CSGO share codes used to share game replays b

# Installation

`npm install csgo-sharecode`
`npm install csgo-sharecode` or `yarn install csgo-sharecode`

TypeScript definitions provided.

# Usage

Expand Down
22 changes: 12 additions & 10 deletions example.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// const ShareCode = require('csgo-sharecode');
// OR using ES modules
// import ShareCode from 'csgo-sharecode';
const ShareCode = require('./index');

// import { encode, decode } from 'csgo-sharecode';
// When using TS you can import the ShareCode (object representation of a full share code) and TwoComplementInteger64 types
// import { ShareCode, TwoComplementInteger64 } from 'csgo-sharecode';
const ShareCode = require('./dist')

// sample example:
// share code: CSGO-GADqf-jjyJ8-cSP2r-smZRo-TO2xK
Expand All @@ -20,13 +22,13 @@ const match = {
low: 143,
},
tvPort: 599906796,
};
}

console.log('encoding share code:\n', match);
const code = ShareCode.encode(match.matchId, match.reservationId, match.tvPort);
console.log('result:\n', code);
console.log('encoding share code:\n', match)
const code = ShareCode.encode(match.matchId, match.reservationId, match.tvPort)
console.log('result:\n', code)

const shareCode = 'CSGO-GADqf-jjyJ8-cSP2r-smZRo-TO2xK';
console.log('decoding share code:\n', shareCode);
const info = ShareCode.decode(shareCode);
console.log('result:\n', info);
const shareCode = 'CSGO-GADqf-jjyJ8-cSP2r-smZRo-TO2xK'
console.log('decoding share code:\n', shareCode)
const info = ShareCode.decode(shareCode)
console.log('result:\n', info)
147 changes: 0 additions & 147 deletions index.js

This file was deleted.

143 changes: 143 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import BigNumber from 'bignumber.js'

const DICTIONARY = 'ABCDEFGHJKLMNOPQRSTUVWXYZabcdefhijkmnopqrstuvwxyz23456789'
const DICTIONARY_LENGTH = DICTIONARY.length
const SHARECODE_PATTERN = /CSGO(-?[\w]{5}){5}$/

export interface TwoComplementInteger64 {
low: number
high: number
unsigned?: boolean
}

export interface Sharecode {
matchId: TwoComplementInteger64
reservationId: TwoComplementInteger64
tvPort: number
}

/**
* Convert a byte array into a hexadecimal string
*/
function bytesToHex(bytes: number[]) {
return Array.from(bytes, byte => {
return ('0' + (byte & 0xff).toString(16)).slice(-2)
}).join('')
}

/**
* Convert a hexadecimal string into a byte array
*/
function hexToBytes(str: string) {
let array = []
for (var i = 0, j = 0; i < str.length; i += 2, j++) {
array[j] = parseInt('0x' + str.substr(i, 2))
}

return array
}

/**
* Convert a 64 bit 2 complement integer into a byte array (big endian byte representation)
*/
function longToBytesBE(high: number, low: number) {
return [
(high >>> 24) & 0xff,
(high >>> 16) & 0xff,
(high >>> 8) & 0xff,
high & 0xff,
(low >>> 24) & 0xff,
(low >>> 16) & 0xff,
(low >>> 8) & 0xff,
low & 0xff,
]
}

/**
* Convert an int into a byte array (low bits only)
*/
function int16ToBytes(number: number) {
return [(number & 0x0000ff00) >> 8, number & 0x000000ff]
}

/**
* Convert a byte array into an int32
*/
function bytesToInt32(bytes: number[]) {
let number = 0
for (let i = 0; i < bytes.length; i++) {
number += bytes[i]
if (i < bytes.length - 1) {
number = number << 8
}
}

return number
}

/**
* Encode a share code from its ShareCode object type and return its string representation.
* Required fields should come from a CDataGCCStrike15_v2_MatchInfo protobuf message.
* https://github.com/SteamRE/SteamKit/blob/master/Resources/Protobufs/csgo/cstrike15_gcmessages.proto#L785
*/
const encode = (
matchId: TwoComplementInteger64,
reservationId: TwoComplementInteger64,
tvPort: number
): string => {
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)

let c = ''
let rem: BigNumber = 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)}`
}

/**
* Decode a CSGO share code from its string and return it as a ShareCode object type.
* Share code format excepted: CSGO-xxxxx-xxxxx-xxxxx-xxxxx-xxxxx
*/
const decode = (shareCode: string): 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]))
}

const bytes = hexToBytes(big.toString(16))

return {
matchId: {
low: bytesToInt32(bytes.slice(0, 4).reverse()),
high: bytesToInt32(bytes.slice(4, 8).reverse()),
},
reservationId: {
low: bytesToInt32(bytes.slice(8, 12).reverse()),
high: bytesToInt32(bytes.slice(12, 16).reverse()),
},
tvPort: bytesToInt32(bytes.slice(16, 18).reverse()),
}
}

export { encode, decode }
25 changes: 25 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit d590091

Please sign in to comment.