Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- const fs = require('fs');
- const path = require('path');
- const luamin = require('luamin');
- function printUsage() {
- console.log('Usage: node index.js <exe_file> <config_file>');
- console.log('');
- console.log('Config file format: lua_file,unique_string');
- console.log('Each line contains a LUA file path and unique string separated by comma');
- }
- function parseConfig(configPath) {
- if (!fs.existsSync(configPath)) {
- throw new Error(`Config file not found: ${configPath}`);
- }
- const content = fs.readFileSync(configPath, 'utf8');
- const lines = content.split('\n').filter(line => line.trim());
- const configs = [];
- for (const line of lines) {
- const parts = line.trim().split(',');
- if (parts.length !== 2) {
- throw new Error(`Invalid config line format: ${line}`);
- }
- const luaFile = parts[0].trim();
- const uniqueString = parts[1].trim();
- if (!fs.existsSync(luaFile)) {
- throw new Error(`LUA file not found: ${luaFile}`);
- }
- configs.push({ luaFile, uniqueString });
- }
- return configs;
- }
- function simplifyLua(luaCode) {
- return luaCode
- .split('\n')
- .map(line => {
- const commentIndex = line.indexOf('-- ');
- if (commentIndex >= 0) {
- line = line.substring(0, commentIndex);
- }
- return line.replace(/^[ \t]+/, '').replace(/[ \t]+$/, '');
- })
- .join('\n');
- }
- function minifyLua(luaCode, availableSpace) {
- const simplified = simplifyLua(luaCode);
- const simplifiedBuffer = Buffer.from(simplified, 'utf8');
- if (simplifiedBuffer.length <= availableSpace) {
- console.log(`Using simplified version: ${simplifiedBuffer.length} bytes (saved ${Buffer.from(luaCode, 'utf8').length - simplifiedBuffer.length} bytes)`);
- return simplified;
- }
- console.log(`Simplified version still too large (${simplifiedBuffer.length} bytes), using luamin...`);
- try {
- const minified = luamin.minify(luaCode);
- console.log(`Using luamin minified version: ${Buffer.from(minified, 'utf8').length} bytes`);
- return minified;
- } catch (error) {
- throw new Error(`Failed to minify LUA: ${error.message}`);
- }
- }
- function findUniqueString(buffer, searchString) {
- const searchBuffer = Buffer.from(searchString, 'utf8');
- const positions = [];
- for (let i = 0; i <= buffer.length - searchBuffer.length; i++) {
- if (buffer.subarray(i, i + searchBuffer.length).equals(searchBuffer)) {
- positions.push(i);
- }
- }
- return positions;
- }
- function findNullBoundaries(buffer, position) {
- let start = position;
- let end = position;
- while (start > 0 && buffer[start - 1] !== 0) {
- start--;
- }
- while (end < buffer.length && buffer[end] !== 0) {
- end++;
- }
- // Check for double dash separator pattern within the boundaries
- const doubleDashEnd = findDoubleDashEnd(buffer, start, end);
- if (doubleDashEnd !== -1) {
- end = doubleDashEnd;
- }
- return { start, end };
- }
- function findDoubleDashEnd(buffer, start, end) {
- // Look for pattern: 120 dashes + 0x0d + 0x0a + 120 dashes
- const dashLine = Buffer.alloc(120, 0x2D); // 120 dashes
- const crLf = Buffer.from([0x0D, 0x0A]); // \r\n
- for (let i = start; i < end - 244; i++) { // 120+2+120 = 242 bytes minimum
- // Check for first dash line
- if (buffer.subarray(i, i + 120).equals(dashLine)) {
- // Check for \r\n after first dash line
- if (buffer.subarray(i + 120, i + 122).equals(crLf)) {
- // Check for second dash line
- if (buffer.subarray(i + 122, i + 242).equals(dashLine)) {
- // Found double dash pattern, return position after first \n (end of script)
- return i + 122;
- }
- }
- }
- }
- return -1; // Pattern not found
- }
- function loadCache(exePath) {
- const cacheFile = exePath + '.cache';
- if (!fs.existsSync(cacheFile)) {
- return null;
- }
- try {
- const content = fs.readFileSync(cacheFile, 'utf8');
- const lines = content.trim().split('\n');
- if (lines.length === 0) return null;
- const exeSize = parseInt(lines[0]);
- const currentExeSize = fs.statSync(exePath).size;
- if (exeSize !== currentExeSize) {
- console.log('Cache invalid: executable size changed');
- return null;
- }
- const cache = { exeSize, entries: {} };
- for (let i = 1; i < lines.length; i++) {
- const parts = lines[i].split(',');
- if (parts.length === 4) {
- const [luaFile, uniqueString, start, end] = parts;
- cache.entries[`${luaFile},${uniqueString}`] = {
- start: parseInt(start),
- end: parseInt(end)
- };
- }
- }
- console.log(`Loaded cache with ${Object.keys(cache.entries).length} entries`);
- return cache;
- } catch (error) {
- console.log('Cache file corrupted, ignoring');
- return null;
- }
- }
- function saveCache(exePath, cacheData) {
- const cacheFile = exePath + '.cache';
- let content = `${cacheData.exeSize}\n`;
- for (const [key, value] of Object.entries(cacheData.entries)) {
- const [luaFile, uniqueString] = key.split(',');
- content += `${luaFile},${uniqueString},${value.start},${value.end}\n`;
- }
- fs.writeFileSync(cacheFile, content);
- console.log(`Cache saved with ${Object.keys(cacheData.entries).length} entries`);
- }
- function patchExe(exePath, configs) {
- if (!fs.existsSync(exePath)) {
- throw new Error(`Executable file not found: ${exePath}`);
- }
- let exeBuffer = fs.readFileSync(exePath);
- const cache = loadCache(exePath);
- const newCache = { exeSize: exeBuffer.length, entries: {} };
- for (const config of configs) {
- console.log(`Processing: ${config.luaFile} -> "${config.uniqueString}"`);
- const cacheKey = `${config.luaFile},${config.uniqueString}`;
- let boundaries;
- if (cache && cache.entries[cacheKey]) {
- boundaries = cache.entries[cacheKey];
- console.log(`Using cached boundaries: ${boundaries.start}-${boundaries.end}`);
- } else {
- const positions = findUniqueString(exeBuffer, config.uniqueString);
- if (positions.length === 0) {
- throw new Error(`Unique string not found in exe: "${config.uniqueString}"`);
- }
- if (positions.length > 1) {
- throw new Error(`Multiple instances of unique string found: "${config.uniqueString}" (${positions.length} occurrences)`);
- }
- const position = positions[0];
- boundaries = findNullBoundaries(exeBuffer, position);
- console.log(`Found boundaries: ${boundaries.start}-${boundaries.end}`);
- }
- newCache.entries[cacheKey] = boundaries;
- const availableSpace = boundaries.end - boundaries.start;
- const originalContent = exeBuffer.subarray(boundaries.start, boundaries.end);
- const originalString = originalContent.toString('utf8').replace(/\0+$/, '').replace(/\s+$/, '');
- if (originalString.trim()) {
- const originalDir = 'original';
- if (!fs.existsSync(originalDir)) {
- fs.mkdirSync(originalDir);
- }
- const originalPath = path.join(originalDir, config.luaFile);
- if (!fs.existsSync(originalPath)) {
- fs.writeFileSync(originalPath, originalString);
- console.log(`Saved original content to: ${originalPath}`);
- }
- }
- const luaContent = fs.readFileSync(config.luaFile, 'utf8');
- const minifiedLua = minifyLua(luaContent, availableSpace);
- const luaBuffer = Buffer.from(minifiedLua, 'utf8');
- if (luaBuffer.length > availableSpace) {
- throw new Error(`Minified LUA file too large: ${luaBuffer.length} bytes > ${availableSpace} bytes available`);
- }
- const paddedBuffer = Buffer.alloc(availableSpace, 0x20);
- luaBuffer.copy(paddedBuffer, 0);
- paddedBuffer.copy(exeBuffer, boundaries.start);
- console.log(`Successfully patched: ${luaBuffer.length}/${availableSpace} bytes used`);
- }
- saveCache(exePath, newCache);
- const parsedPath = path.parse(exePath);
- const newExePath = path.join(parsedPath.dir, 'Maschine 3.exe');
- fs.writeFileSync(newExePath, exeBuffer);
- console.log(`Executable patched successfully: ${newExePath}`);
- return newExePath;
- }
- function main() {
- const args = process.argv.slice(2);
- if (args.length !== 2) {
- printUsage();
- process.exit(1);
- }
- const [exePath, configPath] = args;
- try {
- const configs = parseConfig(configPath);
- patchExe(exePath, configs);
- console.log('Patching completed successfully!');
- } catch (error) {
- console.error('Error:', error.message);
- process.exit(1);
- }
- }
- if (require.main === module) {
- main();
- }
Advertisement
Add Comment
Please, Sign In to add comment