Guest User

Untitled

a guest
Aug 9th, 2025
47
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.54 KB | None | 0 0
  1. const fs = require('fs');
  2. const path = require('path');
  3. const luamin = require('luamin');
  4.  
  5. function printUsage() {
  6. console.log('Usage: node index.js <exe_file> <config_file>');
  7. console.log('');
  8. console.log('Config file format: lua_file,unique_string');
  9. console.log('Each line contains a LUA file path and unique string separated by comma');
  10. }
  11.  
  12. function parseConfig(configPath) {
  13. if (!fs.existsSync(configPath)) {
  14. throw new Error(`Config file not found: ${configPath}`);
  15. }
  16.  
  17. const content = fs.readFileSync(configPath, 'utf8');
  18. const lines = content.split('\n').filter(line => line.trim());
  19. const configs = [];
  20.  
  21. for (const line of lines) {
  22. const parts = line.trim().split(',');
  23. if (parts.length !== 2) {
  24. throw new Error(`Invalid config line format: ${line}`);
  25. }
  26.  
  27. const luaFile = parts[0].trim();
  28. const uniqueString = parts[1].trim();
  29.  
  30. if (!fs.existsSync(luaFile)) {
  31. throw new Error(`LUA file not found: ${luaFile}`);
  32. }
  33.  
  34. configs.push({ luaFile, uniqueString });
  35. }
  36.  
  37. return configs;
  38. }
  39.  
  40. function simplifyLua(luaCode) {
  41. return luaCode
  42. .split('\n')
  43. .map(line => {
  44. const commentIndex = line.indexOf('-- ');
  45. if (commentIndex >= 0) {
  46. line = line.substring(0, commentIndex);
  47. }
  48. return line.replace(/^[ \t]+/, '').replace(/[ \t]+$/, '');
  49. })
  50. .join('\n');
  51. }
  52.  
  53. function minifyLua(luaCode, availableSpace) {
  54. const simplified = simplifyLua(luaCode);
  55. const simplifiedBuffer = Buffer.from(simplified, 'utf8');
  56.  
  57. if (simplifiedBuffer.length <= availableSpace) {
  58. console.log(`Using simplified version: ${simplifiedBuffer.length} bytes (saved ${Buffer.from(luaCode, 'utf8').length - simplifiedBuffer.length} bytes)`);
  59. return simplified;
  60. }
  61.  
  62. console.log(`Simplified version still too large (${simplifiedBuffer.length} bytes), using luamin...`);
  63. try {
  64. const minified = luamin.minify(luaCode);
  65. console.log(`Using luamin minified version: ${Buffer.from(minified, 'utf8').length} bytes`);
  66. return minified;
  67. } catch (error) {
  68. throw new Error(`Failed to minify LUA: ${error.message}`);
  69. }
  70. }
  71.  
  72. function findUniqueString(buffer, searchString) {
  73. const searchBuffer = Buffer.from(searchString, 'utf8');
  74. const positions = [];
  75.  
  76. for (let i = 0; i <= buffer.length - searchBuffer.length; i++) {
  77. if (buffer.subarray(i, i + searchBuffer.length).equals(searchBuffer)) {
  78. positions.push(i);
  79. }
  80. }
  81.  
  82. return positions;
  83. }
  84.  
  85. function findNullBoundaries(buffer, position) {
  86. let start = position;
  87. let end = position;
  88.  
  89. while (start > 0 && buffer[start - 1] !== 0) {
  90. start--;
  91. }
  92.  
  93. while (end < buffer.length && buffer[end] !== 0) {
  94. end++;
  95. }
  96.  
  97. // Check for double dash separator pattern within the boundaries
  98. const doubleDashEnd = findDoubleDashEnd(buffer, start, end);
  99. if (doubleDashEnd !== -1) {
  100. end = doubleDashEnd;
  101. }
  102.  
  103. return { start, end };
  104. }
  105.  
  106. function findDoubleDashEnd(buffer, start, end) {
  107. // Look for pattern: 120 dashes + 0x0d + 0x0a + 120 dashes
  108. const dashLine = Buffer.alloc(120, 0x2D); // 120 dashes
  109. const crLf = Buffer.from([0x0D, 0x0A]); // \r\n
  110.  
  111. for (let i = start; i < end - 244; i++) { // 120+2+120 = 242 bytes minimum
  112. // Check for first dash line
  113. if (buffer.subarray(i, i + 120).equals(dashLine)) {
  114. // Check for \r\n after first dash line
  115. if (buffer.subarray(i + 120, i + 122).equals(crLf)) {
  116. // Check for second dash line
  117. if (buffer.subarray(i + 122, i + 242).equals(dashLine)) {
  118. // Found double dash pattern, return position after first \n (end of script)
  119. return i + 122;
  120. }
  121. }
  122. }
  123. }
  124.  
  125. return -1; // Pattern not found
  126. }
  127.  
  128. function loadCache(exePath) {
  129. const cacheFile = exePath + '.cache';
  130. if (!fs.existsSync(cacheFile)) {
  131. return null;
  132. }
  133.  
  134. try {
  135. const content = fs.readFileSync(cacheFile, 'utf8');
  136. const lines = content.trim().split('\n');
  137.  
  138. if (lines.length === 0) return null;
  139.  
  140. const exeSize = parseInt(lines[0]);
  141. const currentExeSize = fs.statSync(exePath).size;
  142.  
  143. if (exeSize !== currentExeSize) {
  144. console.log('Cache invalid: executable size changed');
  145. return null;
  146. }
  147.  
  148. const cache = { exeSize, entries: {} };
  149.  
  150. for (let i = 1; i < lines.length; i++) {
  151. const parts = lines[i].split(',');
  152. if (parts.length === 4) {
  153. const [luaFile, uniqueString, start, end] = parts;
  154. cache.entries[`${luaFile},${uniqueString}`] = {
  155. start: parseInt(start),
  156. end: parseInt(end)
  157. };
  158. }
  159. }
  160.  
  161. console.log(`Loaded cache with ${Object.keys(cache.entries).length} entries`);
  162. return cache;
  163. } catch (error) {
  164. console.log('Cache file corrupted, ignoring');
  165. return null;
  166. }
  167. }
  168.  
  169. function saveCache(exePath, cacheData) {
  170. const cacheFile = exePath + '.cache';
  171. let content = `${cacheData.exeSize}\n`;
  172.  
  173. for (const [key, value] of Object.entries(cacheData.entries)) {
  174. const [luaFile, uniqueString] = key.split(',');
  175. content += `${luaFile},${uniqueString},${value.start},${value.end}\n`;
  176. }
  177.  
  178. fs.writeFileSync(cacheFile, content);
  179. console.log(`Cache saved with ${Object.keys(cacheData.entries).length} entries`);
  180. }
  181.  
  182. function patchExe(exePath, configs) {
  183. if (!fs.existsSync(exePath)) {
  184. throw new Error(`Executable file not found: ${exePath}`);
  185. }
  186.  
  187. let exeBuffer = fs.readFileSync(exePath);
  188. const cache = loadCache(exePath);
  189. const newCache = { exeSize: exeBuffer.length, entries: {} };
  190.  
  191. for (const config of configs) {
  192. console.log(`Processing: ${config.luaFile} -> "${config.uniqueString}"`);
  193.  
  194. const cacheKey = `${config.luaFile},${config.uniqueString}`;
  195. let boundaries;
  196.  
  197. if (cache && cache.entries[cacheKey]) {
  198. boundaries = cache.entries[cacheKey];
  199. console.log(`Using cached boundaries: ${boundaries.start}-${boundaries.end}`);
  200. } else {
  201. const positions = findUniqueString(exeBuffer, config.uniqueString);
  202.  
  203. if (positions.length === 0) {
  204. throw new Error(`Unique string not found in exe: "${config.uniqueString}"`);
  205. }
  206.  
  207. if (positions.length > 1) {
  208. throw new Error(`Multiple instances of unique string found: "${config.uniqueString}" (${positions.length} occurrences)`);
  209. }
  210.  
  211. const position = positions[0];
  212. boundaries = findNullBoundaries(exeBuffer, position);
  213. console.log(`Found boundaries: ${boundaries.start}-${boundaries.end}`);
  214. }
  215.  
  216. newCache.entries[cacheKey] = boundaries;
  217. const availableSpace = boundaries.end - boundaries.start;
  218.  
  219. const originalContent = exeBuffer.subarray(boundaries.start, boundaries.end);
  220. const originalString = originalContent.toString('utf8').replace(/\0+$/, '').replace(/\s+$/, '');
  221.  
  222. if (originalString.trim()) {
  223. const originalDir = 'original';
  224. if (!fs.existsSync(originalDir)) {
  225. fs.mkdirSync(originalDir);
  226. }
  227.  
  228. const originalPath = path.join(originalDir, config.luaFile);
  229. if (!fs.existsSync(originalPath)) {
  230. fs.writeFileSync(originalPath, originalString);
  231. console.log(`Saved original content to: ${originalPath}`);
  232. }
  233. }
  234.  
  235. const luaContent = fs.readFileSync(config.luaFile, 'utf8');
  236. const minifiedLua = minifyLua(luaContent, availableSpace);
  237. const luaBuffer = Buffer.from(minifiedLua, 'utf8');
  238.  
  239. if (luaBuffer.length > availableSpace) {
  240. throw new Error(`Minified LUA file too large: ${luaBuffer.length} bytes > ${availableSpace} bytes available`);
  241. }
  242.  
  243. const paddedBuffer = Buffer.alloc(availableSpace, 0x20);
  244. luaBuffer.copy(paddedBuffer, 0);
  245.  
  246. paddedBuffer.copy(exeBuffer, boundaries.start);
  247.  
  248. console.log(`Successfully patched: ${luaBuffer.length}/${availableSpace} bytes used`);
  249. }
  250.  
  251. saveCache(exePath, newCache);
  252.  
  253. const parsedPath = path.parse(exePath);
  254. const newExePath = path.join(parsedPath.dir, 'Maschine 3.exe');
  255.  
  256. fs.writeFileSync(newExePath, exeBuffer);
  257. console.log(`Executable patched successfully: ${newExePath}`);
  258. return newExePath;
  259. }
  260.  
  261. function main() {
  262. const args = process.argv.slice(2);
  263.  
  264. if (args.length !== 2) {
  265. printUsage();
  266. process.exit(1);
  267. }
  268.  
  269. const [exePath, configPath] = args;
  270.  
  271. try {
  272. const configs = parseConfig(configPath);
  273. patchExe(exePath, configs);
  274. console.log('Patching completed successfully!');
  275. } catch (error) {
  276. console.error('Error:', error.message);
  277. process.exit(1);
  278. }
  279. }
  280.  
  281. if (require.main === module) {
  282. main();
  283. }
Advertisement
Add Comment
Please, Sign In to add comment