87 lines
2.7 KiB
JavaScript
87 lines
2.7 KiB
JavaScript
|
|
import fs from 'node:fs'
|
||
|
|
import path from 'node:path'
|
||
|
|
|
||
|
|
const source = process.argv[2] ?? 'node_modules/@mlightcad/libredwg-web/wasm/libredwg-web.wasm'
|
||
|
|
const target = process.argv[3] ?? 'public/vendor/libredwg/wasm/libredwg-web.wasm'
|
||
|
|
const INITIAL_PAGES = 4096 // 256 MiB; the module can still grow up to its 4 GiB maximum.
|
||
|
|
const MAX_PAGES = 65536
|
||
|
|
|
||
|
|
const buf = fs.readFileSync(source)
|
||
|
|
if (buf.readUInt32LE(0) !== 0x6d736100 || buf.readUInt32LE(4) !== 1) {
|
||
|
|
throw new Error('Not a WebAssembly module')
|
||
|
|
}
|
||
|
|
|
||
|
|
function readU32(data, offset) {
|
||
|
|
let value = 0
|
||
|
|
let shift = 0
|
||
|
|
let pos = offset
|
||
|
|
while (true) {
|
||
|
|
const byte = data[pos++]
|
||
|
|
value += (byte & 0x7f) * 2 ** shift
|
||
|
|
if (!(byte & 0x80)) break
|
||
|
|
shift += 7
|
||
|
|
if (shift > 35) throw new Error('Invalid LEB128 value')
|
||
|
|
}
|
||
|
|
return [value >>> 0, pos]
|
||
|
|
}
|
||
|
|
|
||
|
|
function encodeU32(value) {
|
||
|
|
const bytes = []
|
||
|
|
let v = value >>> 0
|
||
|
|
do {
|
||
|
|
let byte = v & 0x7f
|
||
|
|
v >>>= 7
|
||
|
|
if (v) byte |= 0x80
|
||
|
|
bytes.push(byte)
|
||
|
|
} while (v)
|
||
|
|
return Buffer.from(bytes)
|
||
|
|
}
|
||
|
|
|
||
|
|
function encodeSection(id, payload) {
|
||
|
|
return Buffer.concat([Buffer.from([id]), encodeU32(payload.length), payload])
|
||
|
|
}
|
||
|
|
|
||
|
|
const sections = []
|
||
|
|
let pos = 8
|
||
|
|
while (pos < buf.length) {
|
||
|
|
const id = buf[pos++]
|
||
|
|
const [size, afterSize] = readU32(buf, pos)
|
||
|
|
const payload = buf.subarray(afterSize, afterSize + size)
|
||
|
|
sections.push({ id, payload })
|
||
|
|
pos = afterSize + size
|
||
|
|
}
|
||
|
|
|
||
|
|
let patched = false
|
||
|
|
const output = []
|
||
|
|
output.push(buf.subarray(0, 8))
|
||
|
|
for (const section of sections) {
|
||
|
|
if (section.id === 5 && !patched) {
|
||
|
|
const [count, afterCount] = readU32(section.payload, 0)
|
||
|
|
if (count < 1) throw new Error('Memory section has no memory')
|
||
|
|
const flags = section.payload[afterCount]
|
||
|
|
if ((flags & 0x01) === 0) throw new Error('Memory has no maximum; refusing to patch')
|
||
|
|
const [oldMin, afterMin] = readU32(section.payload, afterCount + 1)
|
||
|
|
const [oldMax] = readU32(section.payload, afterMin)
|
||
|
|
if (oldMin !== 16384) throw new Error(`Unexpected initial memory: ${oldMin}`)
|
||
|
|
const payload = Buffer.concat([
|
||
|
|
encodeU32(count),
|
||
|
|
Buffer.from([flags]),
|
||
|
|
encodeU32(INITIAL_PAGES),
|
||
|
|
encodeU32(MAX_PAGES),
|
||
|
|
section.payload.subarray(afterMin + encodeU32(oldMax).length)
|
||
|
|
])
|
||
|
|
output.push(encodeSection(5, payload))
|
||
|
|
patched = true
|
||
|
|
console.log(
|
||
|
|
`patched memory: ${oldMin} -> ${INITIAL_PAGES} pages (${Math.round((INITIAL_PAGES * 64) / 1024)} MiB)`
|
||
|
|
)
|
||
|
|
} else {
|
||
|
|
output.push(encodeSection(section.id, section.payload))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (!patched) throw new Error('Memory section not found')
|
||
|
|
|
||
|
|
fs.mkdirSync(path.dirname(target), { recursive: true })
|
||
|
|
fs.writeFileSync(target, Buffer.concat(output))
|
||
|
|
console.log('wrote', target, fs.statSync(target).size, 'bytes')
|