diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 83623cd..4a11b6e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,3 +15,11 @@ jobs: - run: npm publish --access public env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - run: scripts/build.exe + + - uses: softprops/action-gh-release@v1 + with: + token: "${{ secrets.GITHUB_TOKEN }}" + prerelease: true + files: siemens-memory-dumper.exe.zip diff --git a/bin/siemens-memory-dumper.js b/bin/siemens-memory-dumper.js index 8f1a15d..afbf74f 100755 --- a/bin/siemens-memory-dumper.js +++ b/bin/siemens-memory-dumper.js @@ -12,54 +12,56 @@ const USB_DEVICES = [ "11F5:1004", // DCA-540 ]; -const DEFAULT_PORT = await getDefaultPort(); +(async () => { + const DEFAULT_PORT = await getDefaultPort(); -program - .description('CLI memory dumper for Siemens phones.') - .option('-p, --port ', 'serial port name', DEFAULT_PORT) - .option('-b, --baudrate ', 'limit maximum baudrate (0 - use maximum)', '0'); + program + .description('CLI memory dumper for Siemens phones.') + .option('-p, --port ', 'serial port name', DEFAULT_PORT) + .option('-b, --baudrate ', 'limit maximum baudrate (0 - use maximum)', '0'); -program.command('read') - .description('Read memory region by address and size and save to file.') - .argument('', 'memory address (0xHEX)') - .argument('', 'memory size (0xHEX or 1M, 128k, 256...)') - .argument('[file|dir]', 'write memory to this file or dir') - .action(async function (addr, size, output) { - await readMemoryToFile({addr, size, output, ...this.optsWithGlobals()}); - }); + program.command('read') + .description('Read memory region by address and size and save to file.') + .argument('', 'memory address (0xHEX)') + .argument('', 'memory size (0xHEX or 1M, 128k, 256...)') + .argument('[file|dir]', 'write memory to this file or dir') + .action(async function (addr, size, output) { + await readMemoryToFile({addr, size, output, ...this.optsWithGlobals()}); + }); -program.command('read-region') - .description('Read memory region by name and save to file.') - .argument('', 'memory region name (SRAM, RAM, ...)') - .argument('[file|dir]', 'write memory to this file or dir') - .action(async function (name, output) { - await readMemoryToFile({name, output, ...this.optsWithGlobals()}); - }); + program.command('read-region') + .description('Read memory region by name and save to file.') + .argument('', 'memory region name (SRAM, RAM, ...)') + .argument('[file|dir]', 'write memory to this file or dir') + .action(async function (name, output) { + await readMemoryToFile({name, output, ...this.optsWithGlobals()}); + }); -program.command('read-all') - .description('Read all available memory regions from phone and save to dir.') - .argument('[dir]', 'write memory regions to this dir') - .action(async function (output) { - await readAllMemory({output, ...this.optsWithGlobals()}); - }); + program.command('read-all') + .description('Read all available memory regions from phone and save to dir.') + .argument('[dir]', 'write memory regions to this dir') + .action(async function (output) { + await readAllMemory({output, ...this.optsWithGlobals()}); + }); -program.command('list') - .description('List available memory regions for dump.') - .action(async function () { - await listAvailableMemory(this.optsWithGlobals()); - }); + program.command('list') + .description('List available memory regions for dump.') + .action(async function () { + await listAvailableMemory(this.optsWithGlobals()); + }); -program.command('list-ports') - .description('List available serial ports.') - .action(async function () { - for (let p of await SerialPort.list()) { - if (p.productId != null) - console.log(p.path, `${p.vendorId}:${p.productId}`, p.manufacturer); - } - }); + program.command('list-ports') + .description('List available serial ports.') + .action(async function () { + for (let p of await SerialPort.list()) { + if (p.productId != null) + console.log(p.path, `${p.vendorId}:${p.productId}`, p.manufacturer); + } + }); -program.showHelpAfterError(); -program.parse(); + program.showHelpAfterError(); + program.parse(); +})(); async function getDefaultPort() { let availablePorts = (await SerialPort.list()).filter((d) => { diff --git a/package-lock.json b/package-lock.json index 799bedf..34258a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "table": "^6.8.2" }, "bin": { - "siemens-memory-dumper": "bin/memory-dumper.js" + "siemens-memory-dumper": "bin/siemens-memory-dumper.js" } }, "../node-sie-serial": { @@ -92,6 +92,12 @@ "url": "https://opencollective.com/serialport/donate" } }, + "node_modules/@serialport/bindings-cpp/node_modules/node-addon-api": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.0.0.tgz", + "integrity": "sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA==", + "license": "MIT" + }, "node_modules/@serialport/bindings-interface": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@serialport/bindings-interface/-/bindings-interface-1.2.2.tgz", @@ -404,12 +410,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "license": "MIT" }, - "node_modules/node-addon-api": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.0.0.tgz", - "integrity": "sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA==", - "license": "MIT" - }, "node_modules/node-gyp-build": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", diff --git a/package.json b/package.json index 951edbd..abd69a6 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "@sie-js/siemens-memory-dumper", "version": "1.0.3", - "type": "module", "main": "./bin/siemens-memory-dumper.js", + "type": "module", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "link-local-deps": "npm link -S ../node-sie-serial/", diff --git a/pkg.json b/pkg.json new file mode 100644 index 0000000..14322f4 --- /dev/null +++ b/pkg.json @@ -0,0 +1,10 @@ +{ + "pkg": { + "targets": [ + "latest-win-x64" + ], + "assets": [ + "./node_modules/@serialport/bindings-cpp/prebuilds" + ] + } +} diff --git a/scripts/build-exe.sh b/scripts/build-exe.sh new file mode 100755 index 0000000..5cc407d --- /dev/null +++ b/scripts/build-exe.sh @@ -0,0 +1,8 @@ +#!/bin/bash +set -e +set -x +cd "$(dirname $0)/../" +rm -f *.exe *.exe.zip +npx -y esbuild bin/siemens-memory-dumper.js --bundle --outfile=./siemens-memory-dumper.js --format=cjs --platform=node --loader:.node=file --external:@serialport/bindings-cpp +npx -y pkg -c ./pkg.json ./siemens-memory-dumper.js +zip siemens-memory-dumper.exe.zip siemens-memory-dumper.exe diff --git a/src/index.js b/src/index.js index ec76236..7267025 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,5 @@ import fs from 'fs'; -import "chalk"; +import chalk from "chalk"; import { table as asciiTable } from 'table'; import { CGSN, serialWaitForOpen } from "@sie-js/serial"; import cliProgress from "cli-progress"; @@ -19,7 +19,7 @@ export async function readMemoryToFile(argv) { let info = await getPhoneInfo(cgsn); if (!info) { - console.error(`Can't get phone information!`); + showError(`Can't get phone information!`); await cgsnClose(cgsn, port); return; } @@ -27,7 +27,7 @@ export async function readMemoryToFile(argv) { if (argv.name) { let region = getMemoryRegionByName(info.memoryRegions, argv.name); if (!region) { - console.error(`Memory region ${argv.name} not found.`); + showError(`Memory region ${argv.name} not found.`); await cgsnClose(cgsn, port); return; } @@ -84,8 +84,8 @@ export async function readAllMemory(argv) { try { fs.mkdirSync(argv.output, true); } catch (e) { - console.error(e.message); - console.error(`Output dir not found: ${argv.output}`); + showError(e.message); + showError(`Output dir not found: ${argv.output}`); return; } } @@ -96,7 +96,7 @@ export async function readAllMemory(argv) { let info = await getPhoneInfo(cgsn); if (!info) { - console.error(`Can't get phone information!`); + showError(`Can't get phone information!`); await cgsnClose(cgsn, port); return; } @@ -135,7 +135,7 @@ export async function readAllMemory(argv) { i++; if (!result.success) { - console.error(`Error: ${result.error}`); + showError(`Error: ${result.error}`); break; } @@ -158,7 +158,7 @@ export async function listAvailableMemory(argv) { let info = await getPhoneInfo(cgsn); if (!info) { - console.error(`Can't get phone information!`); + showError(`Can't get phone information!`); await cgsnClose(cgsn, port); return; } @@ -293,7 +293,7 @@ async function getPhoneInfo(cgsn) { descr: 'External RAM.', }); } else { - console.error(`Detected unknown phone! Memory regions maybe incorret.`); + showError(`Detected unknown phone! Memory regions maybe incorret.`); memoryRegions.push({ name: "RAM", addr: 0xA8000000, @@ -316,7 +316,7 @@ async function cgsnConnect(portName, limitBaudrate) { console.info(`Connected using ${port.baudRate} baudrate.`); return [cgsn, port]; } else { - console.error('Phone not found or CGSN patch is not installed.'); + showError('Phone not found or CGSN patch is not installed.'); await cgsnClose(cgsn, port); return [null, null]; } @@ -359,3 +359,7 @@ function formatSize(size) { return +(size / 1024).toFixed(2) + " kB"; } } + +function showError(message) { + console.error(chalk.red(message)) +}