-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathpatch-epipe-log-asar.cjs
More file actions
112 lines (103 loc) · 3.48 KB
/
Copy pathpatch-epipe-log-asar.cjs
File metadata and controls
112 lines (103 loc) · 3.48 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
/**
* Hotpatch installed asar: fix EPIPE→log infinite loop that filled main.log to multi-GB.
*/
const fs = require("fs");
const path = require("path");
const { Readable } = require("stream");
const { Pickle } = require("@electron/asar/lib/pickle");
const { getFileIntegrity } = require("@electron/asar/lib/integrity");
const { readArchiveHeaderSync } = require("@electron/asar/lib/disk");
const ROOT = path.resolve(__dirname, "..");
const ASAR =
process.env.HSP_ASAR ||
"C:/Program Files/Hyperlinks Space Program/current/resources/app.asar";
const OUT = path.join(ROOT, ".tmp-app.asar.epipe");
const REL = "windows/build.cjs";
const SRC = path.join(ROOT, "windows", "build.cjs");
function ensureFileNode(root, relPath) {
const parts = relPath.split("/");
let node = root;
for (let i = 0; i < parts.length; i++) {
const part = parts[i];
const isLast = i === parts.length - 1;
if (!node.files) node.files = {};
if (isLast) {
if (!node.files[part] || node.files[part].files) node.files[part] = {};
return node.files[part];
}
if (!node.files[part]) node.files[part] = { files: {} };
if (!node.files[part].files) node.files[part] = { files: {} };
node = node.files[part];
}
throw new Error("empty path");
}
function payloadSizeFromHeader(header) {
let max = 0;
function walk(node) {
if (!node || typeof node !== "object") return;
if (node.files) {
for (const child of Object.values(node.files)) walk(child);
return;
}
if (typeof node.size === "number" && node.offset != null) {
const end = Number(node.offset) + node.size;
if (end > max) max = end;
}
}
walk(header);
return max;
}
async function main() {
const patched = fs.readFileSync(SRC);
if (!patched.includes("EPIPE") || !patched.includes("log truncated")) {
throw new Error("source build.cjs missing EPIPE/log-cap fix");
}
const { header, headerSize } = readArchiveHeaderSync(ASAR);
const newHeader = JSON.parse(JSON.stringify(header));
const srcStat = fs.statSync(ASAR);
const payloadStart = 8 + headerSize;
const oldPayloadSize = srcStat.size - payloadStart;
const appendAt = Math.max(oldPayloadSize, payloadSizeFromHeader(header));
const integrity = await getFileIntegrity(Readable.from(patched));
const node = ensureFileNode(newHeader, REL);
delete node.files;
node.size = patched.length;
node.offset = String(appendAt);
node.integrity = integrity;
const headerPickle = Pickle.createEmpty();
headerPickle.writeString(JSON.stringify(newHeader));
const headerBuf = headerPickle.toBuffer();
const sizePickle = Pickle.createEmpty();
sizePickle.writeUInt32(headerBuf.length);
const sizeBuf = sizePickle.toBuffer();
if (fs.existsSync(OUT)) fs.unlinkSync(OUT);
const out = fs.createWriteStream(OUT);
const write = (buf) =>
new Promise((resolve, reject) => {
const ok = out.write(buf);
if (ok) resolve();
else out.once("drain", resolve);
out.once("error", reject);
});
await write(sizeBuf);
await write(headerBuf);
await new Promise((resolve, reject) => {
const rs = fs.createReadStream(ASAR, {
start: payloadStart,
end: srcStat.size - 1,
});
rs.on("error", reject);
rs.on("end", resolve);
rs.pipe(out, { end: false });
});
await write(patched);
await new Promise((resolve, reject) => {
out.end(() => resolve());
out.on("error", reject);
});
console.log("packed", fs.statSync(OUT).size);
}
main().catch((e) => {
console.error(e);
process.exit(1);
});