Skip to content

Commit 1b97fe8

Browse files
committed
RC.13 updates
1 parent 61e9648 commit 1b97fe8

File tree

11 files changed

+196
-12
lines changed

11 files changed

+196
-12
lines changed

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
{
22
"name": "@runejs/common",
3-
"version": "2.0.0-rc.9",
3+
"version": "2.0.0-rc.13",
44
"description": "Common logging, networking, compression, and other functionality for RuneJS applications.",
55
"main": "./index.js",
66
"types": "./index.d.ts",
77
"exports": {
88
".": "./index.js",
99
"./buffer": "./buffer/index.js",
1010
"./color": "./color/index.js",
11-
"./compression": "./compression/index.js",
11+
"./compress": "./compress/index.js",
12+
"./encrypt": "./encrypt/index.js",
1213
"./fs": "./fs/index.js",
1314
"./logger": "./logger/index.js",
1415
"./net": "./net/index.js",
File renamed without changes.

src/compress/compression-method.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export enum FileCompression {
2+
none = 0,
3+
bzip = 1,
4+
gzip = 2
5+
}
6+
7+
export type CompressionMethod = 'none' | 'bzip' | 'gzip';
8+
9+
export const getCompressionMethod = (compression: FileCompression | number): CompressionMethod => {
10+
if(compression === 0 || compression > 2) {
11+
return 'none';
12+
}
13+
14+
return compression === 1 ? 'bzip' : 'gzip';
15+
};
File renamed without changes.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
export * from './bzip2';
22
export * from './gzip';
3-
export * from './file-compression';
3+
export * from './compression-method';

src/compression/file-compression.ts

Lines changed: 0 additions & 7 deletions
This file was deleted.

src/encrypt/encryption-method.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export enum FileEncryption {
2+
none = 0,
3+
xtea = 1
4+
}
5+
6+
export type EncryptionMethod = 'none' | 'xtea';
7+
8+
export const getEncryptionMethod = (encryption: FileEncryption | number): EncryptionMethod => encryption === 1 ? 'xtea' : 'none';

src/encrypt/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './xtea';
2+
export * from './encryption-method';

src/encrypt/xtea.ts

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import path from 'path';
2+
import fs from 'fs';
3+
4+
import { logger } from '../logger';
5+
import { ByteBuffer } from '../buffer';
6+
7+
8+
export type XteaKey = [ number, number, number, number ];
9+
10+
11+
export interface XteaConfig {
12+
archive: number;
13+
group: number;
14+
name_hash: number;
15+
name: string;
16+
mapsquare: number;
17+
key: XteaKey;
18+
}
19+
20+
21+
export interface XteaKeys {
22+
gameVersion: number;
23+
key: XteaKey;
24+
}
25+
26+
27+
const toInt = value => value | 0;
28+
29+
30+
export class Xtea {
31+
32+
public static loadKeys(xteaConfigPath: string): Map<string, XteaKeys[]> {
33+
if(!fs.existsSync(xteaConfigPath)) {
34+
logger.error(`Error loading XTEA keys: ${xteaConfigPath} was not found.`);
35+
return null;
36+
}
37+
38+
const stats = fs.statSync(xteaConfigPath);
39+
40+
if(!stats.isDirectory()) {
41+
logger.error(`Error loading XTEA keys: ${xteaConfigPath} is not a directory.`);
42+
return null;
43+
}
44+
45+
const xteaKeys: Map<string, XteaKeys[]> = new Map<string, XteaKeys[]>();
46+
const xteaFileNames = fs.readdirSync(xteaConfigPath);
47+
for(const fileName of xteaFileNames) {
48+
try {
49+
const gameVersionString = fileName.substring(0, fileName.indexOf('.json'));
50+
if(!gameVersionString) {
51+
logger.error(`Error loading XTEA config file ${fileName}: No game version supplied.`);
52+
continue;
53+
}
54+
55+
const gameVersion: number = Number(gameVersionString);
56+
if(!gameVersion || isNaN(gameVersion)) {
57+
logger.error(`Error loading XTEA config file ${fileName}: Invalid game version supplied.`);
58+
continue;
59+
}
60+
61+
const fileContent = fs.readFileSync(path.join(xteaConfigPath, fileName), 'utf-8');
62+
const xteaConfigList = JSON.parse(fileContent) as XteaConfig[];
63+
64+
if(!xteaConfigList?.length) {
65+
logger.error(`Error loading XTEA config file ${fileName}: File is empty.`);
66+
continue;
67+
}
68+
69+
for(const xteaConfig of xteaConfigList) {
70+
if(!xteaConfig?.name || !xteaConfig?.key?.length) {
71+
continue;
72+
}
73+
74+
const { name: fileName, key } = xteaConfig;
75+
let fileKeys: XteaKeys[] = [];
76+
77+
if(xteaKeys.has(fileName)) {
78+
fileKeys = xteaKeys.get(fileName);
79+
}
80+
81+
fileKeys.push({ gameVersion, key });
82+
xteaKeys.set(fileName, fileKeys);
83+
}
84+
} catch(error) {
85+
logger.error(`Error loading XTEA config file ${fileName}:`, error);
86+
}
87+
}
88+
89+
return xteaKeys;
90+
}
91+
92+
public static validKeys(keys?: number[] | undefined): boolean {
93+
if(!keys) {
94+
return false;
95+
}
96+
97+
return keys?.length === 4 && (keys[0] !== 0 || keys[1] !== 0 || keys[2] !== 0 || keys[3] !== 0);
98+
}
99+
100+
// @TODO unit testing
101+
public static encrypt(input: ByteBuffer, keys: number[], length: number): ByteBuffer {
102+
const encryptedBuffer = new ByteBuffer(length);
103+
const chunks = length / 8;
104+
input.readerIndex = 0;
105+
106+
for(let i = 0; i < chunks; i++) {
107+
let v0 = input.get('int');
108+
let v1 = input.get('int');
109+
let sum = 0;
110+
const delta = -0x61c88647;
111+
112+
let rounds = 32;
113+
while(rounds-- > 0) {
114+
v0 += ((sum + keys[sum & 3]) ^ (v1 + ((v1 >>> 5) ^ (v1 << 4))));
115+
sum += delta
116+
v1 += ((v0 + ((v0 >>> 5) ^ (v0 << 4))) ^ (keys[(sum >>> 11) & 3] + sum));
117+
}
118+
119+
encryptedBuffer.put(v0, 'int');
120+
encryptedBuffer.put(v1, 'int');
121+
}
122+
123+
return encryptedBuffer.flipWriter();
124+
}
125+
126+
// @TODO unit testing
127+
public static decrypt(input: ByteBuffer, keys: number[], length: number): ByteBuffer {
128+
if(!keys?.length) {
129+
return input;
130+
}
131+
132+
const output = new ByteBuffer(length);
133+
const numBlocks = Math.floor(length / 8);
134+
135+
for(let block = 0; block < numBlocks; block++) {
136+
let v0 = input.get('int');
137+
let v1 = input.get('int');
138+
let sum = 0x9E3779B9 * 32;
139+
140+
for(let i = 0; i < 32; i++) {
141+
v1 -= ((toInt(v0 << 4) ^ toInt(v0 >>> 5)) + v0) ^ (sum + keys[(sum >>> 11) & 3]);
142+
v1 = toInt(v1);
143+
144+
sum -= 0x9E3779B9;
145+
146+
v0 -= ((toInt(v1 << 4) ^ toInt(v1 >>> 5)) + v1) ^ (sum + keys[sum & 3]);
147+
v0 = toInt(v0);
148+
}
149+
150+
output.put(v0, 'int');
151+
output.put(v1, 'int');
152+
}
153+
154+
input.copy(output, output.writerIndex, input.readerIndex);
155+
return output;
156+
}
157+
158+
}

0 commit comments

Comments
 (0)