Source: memorykit.js

const path = require('path');
const nodeGypBuild = require('node-gyp-build');
const nativeAddon = nodeGypBuild(path.resolve(__dirname, '../'));

/**
 * Enum for supported types.
 * @readonly
 * @private
 * @enum {string}
 */
const TYPE = {
  BYTE: 'BYTE',
  INT: 'INT',
  INT32: 'INT32',
  UINT32: 'UINT32',
  INT64: 'INT64',
  UINT64: 'UINT64',
  SHORT: 'SHORT',
  LONG: 'LONG',
  FLOAT: 'FLOAT',
  DOUBLE: 'DOUBLE',
  POINTER: 'POINTER',
  BOOL: 'BOOL',
  DWORD: 'DWORD',
  STRING: 'STRING',
};

/**
 * @typedef {number | BigInt} Addr Address
 */

/**
 * @typedef {Object} BasicProcessInfo Basic Process Info
 * @property {number} pid PID
 * @property {number | undefined} ppid PPID
 * @property {string} name Name
 */

/**
 * Get basic information about all the processes that currently running
 * *Note: `root` is required on Linux and MacOS.*
 * @returns {Array<BasicProcessInfo>}
 */
function getProcesses() {
  return nativeAddon.getProcesses();
}

/**
 * Process class
 */
class Process {
  /**
   * For internal use only.
   */
  #nativeProcess;

  /**
   * Base Address of the Process
   * @readonly
   * @returns {BigInt} Base Address
   */
  get baseAddr() {
    return this.#nativeProcess.getBaseAddr();
  }

  /**
   * Create a new Process by PID
   * @param {number} pid PID
   */
  constructor(pid) {
    if (typeof pid === 'number') {
      if (
        getProcesses()
          .map((i) => i.pid)
          .includes(pid)
      ) {
        this.#nativeProcess = new nativeAddon.Process(pid);
      } else {
        throw new Error(`Process with PID ${pid} not found`);
      }
    } else {
      throw new Error('PID is expected to create Process');
    }
  }

  /**
   * Read `byte` from virtual memory at addr.
   * @param {Addr} addr Address
   * @returns {number} Value
   */
  readByte(addr) {
    return this.#nativeProcess.readMemory(addr, TYPE.BYTE);
  }

  /**
   * Read `int` from virtual memory at addr.
   * @param {Addr} addr Address
   * @returns {number} Value
   */
  readInt(addr) {
    return this.#nativeProcess.readMemory(addr, TYPE.INT);
  }

  /**
   * Read `int32` from virtual memory at addr.
   * @param {Addr} addr Address
   * @returns {number} Result
   */
  readInt32(addr) {
    return this.#nativeProcess.readMemory(addr, TYPE.INT32);
  }

  /**
   * Read `uint32` from virtual memory at addr.
   * @param {Addr} addr Address
   * @returns {number} Result
   */
  readUint32(addr) {
    return this.#nativeProcess.readMemory(addr, TYPE.UINT32);
  }

  /**
   * Read `int64` from virtual memory at addr.
   * @param {Addr} addr Address
   * @returns {BigInt} Result
   */
  readInt64(addr) {
    return this.#nativeProcess.readMemory(addr, TYPE.INT64);
  }

  /**
   * Read `uint64` from virtual memory at addr.
   * @param {Addr} addr Address
   * @returns {BigInt} Result
   */
  readUint64(addr) {
    return this.#nativeProcess.readMemory(addr, TYPE.UINT64);
  }

  /**
   * Read `short` from virtual memory at addr.
   * @param {Addr} addr Address
   * @returns {number} Result
   */
  readShort(addr) {
    return this.#nativeProcess.readMemory(addr, TYPE.SHORT);
  }

  /**
   * Read `long` from virtual memory at addr.
   * @param {Addr} addr Address
   * @returns {BigInt} Result
   */
  readLong(addr) {
    return this.#nativeProcess.readMemory(addr, TYPE.LONG);
  }

  /**
   * Read `float` from virtual memory at addr.
   * @param {Addr} addr Address
   * @returns {number} Result
   */
  readFloat(addr) {
    return this.#nativeProcess.readMemory(addr, TYPE.FLOAT);
  }

  /**
   * Read `double` from virtual memory at addr.
   * @param {Addr} addr Address
   * @returns {number} Result
   */
  readDouble(addr) {
    return this.#nativeProcess.readMemory(addr, TYPE.DOUBLE);
  }

  /**
   * Read pointer from virtual memory at addr.
   * @param {Addr} addr Address
   * @returns {BigInt} Result
   */
  readPointer(addr) {
    return this.#nativeProcess.readMemory(addr, TYPE.POINTER);
  }

  /**
   * Read `bool` from virtual memory at addr.
   * @param {Addr} addr Address
   * @returns {boolean} Result
   */
  readBool(addr) {
    return this.#nativeProcess.readMemory(addr, TYPE.BOOL);
  }

  /**
   * Read `DWORD` from virtual memory at addr.
   * @param {Addr} addr Address
   * @returns {BigInt} Result
   */
  readDword(addr) {
    return this.#nativeProcess.readMemory(addr, TYPE.DWORD);
  }

  /**
   * Read string from virtual memory at addr.
   * @param {Addr} addr Address
   * @returns {string} Value
   */
  readString(addr) {
    return this.#nativeProcess.readMemory(addr, TYPE.STRING);
  }

  /**
   * Write `byte` to virtual memory at addr.
   * @param {BigInt} addr Address
   * @param {number} val Value
   */
  writeByte(addr, val) {
    this.#nativeProcess.writeMemory(addr, TYPE.BYTE, val);
  }

  /**
   * Write `int` to virtual memory at addr.
   * @param {BigInt} addr Address
   * @param {number} val Value
   */
  writeInt(addr, val) {
    this.#nativeProcess.writeMemory(addr, TYPE.INT, val);
  }

  /**
   * Write `int32` to virtual memory at addr.
   * @param {BigInt} addr Address
   * @param {number} val Value
   */
  writeInt32(addr, val) {
    this.#nativeProcess.writeMemory(addr, TYPE.INT32, val);
  }

  /**
   * Write `uint32` to virtual memory at addr.
   * @param {BigInt} addr Address
   * @param {number} val Value
   */
  writeUint32(addr, val) {
    this.#nativeProcess.writeMemory(addr, TYPE.UINT32, val);
  }

  /**
   * Write `int64` to virtual memory at addr.
   * @param {BigInt} addr Address
   * @param {BigInt} val Value
   */
  writeInt64(addr, val) {
    this.#nativeProcess.writeMemory(addr, TYPE.INT64, val);
  }

  /**
   * Write `uint64` to virtual memory at addr.
   * @param {BigInt} addr Address
   * @param {BigInt} val Value
   */
  writeUint64(addr, val) {
    this.#nativeProcess.writeMemory(addr, TYPE.UINT64, val);
  }

  /**
   * Write `short` to virtual memory at addr.
   * @param {BigInt} addr Address
   * @param {number} val Value
   */
  writeShort(addr, val) {
    this.#nativeProcess.writeMemory(addr, TYPE.SHORT, val);
  }

  /**
   * Write `long` to virtual memory at addr.
   * @param {BigInt} addr Address
   * @param {BigInt} val Value
   */
  writeLong(addr, val) {
    this.#nativeProcess.writeMemory(addr, TYPE.LONG, val);
  }

  /**
   * Write `float` to virtual memory at addr.
   * @param {BigInt} addr Address
   * @param {number} val Value
   */
  writeFloat(addr, val) {
    this.#nativeProcess.writeMemory(addr, TYPE.FLOAT, val);
  }

  /**
   * Write `double` to virtual memory at addr.
   * @param {BigInt} addr Address
   * @param {number} val Value
   */
  writeDouble(addr, val) {
    this.#nativeProcess.writeMemory(addr, TYPE.DOUBLE, val);
  }

  /**
   * Write pointer to virtual memory at addr.
   * @param {BigInt} addr Address
   * @param {BigInt} val Value
   */
  writePointer(addr, val) {
    this.#nativeProcess.writeMemory(addr, TYPE.POINTER, val);
  }

  /**
   * Write `bool` to virtual memory at addr.
   * @param {BigInt} addr Address
   * @param {boolean} val Value
   */
  writeBool(addr, val) {
    this.#nativeProcess.writeMemory(addr, TYPE.BOOL, val);
  }

  /**
   * Write `DWORD` to virtual memory at addr.
   * @param {BigInt} addr Address
   * @param {BigInt} val Value
   */
  writeDword(addr, val) {
    this.#nativeProcess.writeMemory(addr, TYPE.DWORD, val);
  }

  /**
   * Write `string` to virtual memory at addr.
   * @param {BigInt} addr Address
   * @param {string} val Value
   */
  writeString(addr, val) {
    this.#nativeProcess.writeMemory(addr, TYPE.STRING, val);
  }

  /**
   * Release process handle / task port
   */
  release() {
    this.#nativeProcess.release();
  }

  /**
   * Check if process has released handle / task port
   * @returns {bool} Result
   */
  hasReleased() {
    return this.#nativeProcess.hasReleased();
  }
}

module.exports = {
  getProcesses,
  Process,
};