forked from g-js-api/G.js
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathreader.js
More file actions
120 lines (111 loc) · 4.81 KB
/
Copy pathreader.js
File metadata and controls
120 lines (111 loc) · 4.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
const {
parse
} = require("node-html-parser"); // not exactly used for HTML lmao, similar enough though
const { Readable } = require('stream');
const zlib = require("zlib");
const fs = require("fs");
let encode_level = (level_string) => {
let gzipped = zlib.gzipSync(level_string);
let base64_encoded = gzipped
.toString("base64")
.replaceAll("/", "_")
.replaceAll("+", "-");
return base64_encoded;
};
let decode_level = (data) => {
const base64_decoded = Buffer.from(
data.replaceAll("_", "/").replaceAll("-", "+"),
"base64"
);
const decompressed = zlib.gunzipSync(base64_decoded);
return decompressed.toString();
};
class LevelReader {
constructor(
level_name,
filename = `${process.env.localappdata}\\GeometryDash\\CCLocalLevels.dat`
) {
return new Promise((resolve, reject) => {
const size = fs.statSync(filename).size;
let output = Buffer.alloc(size);
// savefile reading stuff
const readStream = fs.createReadStream(filename, { highWaterMark: 1024 * 1024 });
let offset = 0; // Offset for buffer
readStream.on('data', buffer => {
buffer.copy(output, offset);
offset += buffer.length;
});
readStream.on("end", async () => {
let last_level;
let add_to_level, set_level;
for (let i = 0; i < output.length; i++) {
output[i] = output[i] ^ 11;
}
let b64out = Buffer.from(output.toString(), "base64");
output = zlib
.unzipSync(b64out)
.toString();
output = parse(output); // Base64 decodes savefile, then unzips savefile and parses XML
let info = output.childNodes[1].childNodes[0].childNodes[1].childNodes;
for (let i in info) {
let curr = info[i];
if (curr.rawTagName == "d") {
let tags = curr.childNodes; // tags reference
let dat = {};
tags.forEach((tag, i) => {
if (tag.rawTagName == "k") {
if (tag.childNodes[0]._rawText == "k4") {
let lval = tags[i + 1].childNodes[0]._rawText;
let dec = decode_level(lval);
dat.levelstring = dec;
dat.raw = lval;
add_to_level = (lvlstr) => {
tags[i + 1].childNodes[0]._rawText = encode_level(
dec + lvlstr
);
dat.levelstring = dec + lvlstr;
dat.raw = tags[i + 1].childNodes[0]._rawText;
};
set_level = (lvlstr) => {
tags[i + 1].childNodes[0]._rawText = encode_level(lvlstr);
dat.levelstring = lvlstr;
dat.raw = tags[i + 1].childNodes[0]._rawText;
};
}
if (tag.childNodes[0]._rawText == "k2") dat.name = tags[i + 1].childNodes[0]._rawText;
}
});
if (!level_name) {
last_level = dat;
break;
} else {
if (dat.name == level_name) {
last_level = dat;
break;
}
}
}
}
if (!last_level)
reject(`Level "${level_name}" not found in savefile!`);
resolve({
data: last_level,
add: add_to_level,
set: set_level,
save: async () => {
let outstr = output.toString();
let alm = zlib.gzipSync(outstr);
alm = Buffer.from(
alm.toString("base64").replaceAll("/", "_").replaceAll("+", "-")
);
for (let i = 0; i < alm.length; i++) {
alm[i] = alm[i] ^ 11; // XOR decrypts buffer
}
fs.writeFileSync(filename, alm);
},
});
});
});
}
}
module.exports = LevelReader;