code:
# EraElectron_Android_Engine

## Introduction
This is the [EraElectron](https://gitgud.io/umaera/era-electron/) by [@SLeader](https://gitgud.io/SLeader) unofficial port on Android Cordova platform through [nodejs-mobile-cordova](https://github.com/nodejs-mobile/nodejs-mobile-cordova)
Current Version: [v3.0.0](https://gitgud.io/umaera/era-electron/-/tree/3.0.0?ref_type=tags)

## Installation
1. Create a new Cordova project. Currently Cordova version >= 8 shall be supported, however only cordova 9 and 10 is tested.
2. Setup JDK 1.8 and Android buildtools. You may follow the instruction at [Here](https://cordova.apache.org/docs/en/9.x/guide/platforms/android/index.html)
3. `cordova platform add android@7`
4. `cordova plugins add cordova-plugin-chooser cordova-plugin-console cordova-plugin-exit nodejs-mobile-cordova@0.4.2 cordova-plugin-save-dialog`
5. Get the latest release (or clone and compile yourself) of [EraElectron_Android_Render](https://gitgud.io/erauma_android_unofficial_port/eraelectron_android_render), then unpack the whole project into www/ under your cordova project
6. Copy this project into www/nodejs-project under your cordova project
7. Last, Copy EraElectron-compatiable game under www/nodejs-project/game

## Note
Currently, limited by the Nodejs version of nodejs-mobile-cordova, only games with Nodejs v12 compatible syntax could be run without any modification. Else, you shall first replace all incompatiable syntax first.\n
One of the convenience way is to use babel tools. You may follow the following instruction in order to transform any game into Nodejs v12 compatiable syntax.\n
Assume that you hold all the game assets and code inside 'Game' folder under current working directory
1. `npm install --save-dev @babel/core @babel/cli @babel/preset-env babel-plugin-transform-remove-strict-mode`
2. Create a file call babel.config.json, which contains the following content:
```json
{
    "targets": {
      "node": "12.0"
    },
    "presets": [
        [
          "@babel/preset-env"
        ]
    ],
    "plugins": ["transform-remove-strict-mode"]
}
```
3. `npx babel Game --out-dir Output_Dir --copy-files --config-file ./babel.config.json`
4. The syntax-translated game is now in Output_Dir folder.

## Build
`cordova build android --verbose --device`

license: mit


code:
var cordova = require('cordova-bridge');

var home_path = __dirname;
console.log(home_path);

const { join } = require('path');

const { ipcMain } = require('@/ipcPoly')

const log4js = require('log4js');

const createEra = require('@/era/era-electron');
const { engineCommand } = require('@/renderer/utils/embedded');

const https = require("https");
const http = require("http");

// const isDevelopment = process.env.NODE_ENV !== 'production';
const isDevelopment = true;

log4js.configure({
    appenders: {
        console: { type: 'console' }
    },
    categories: {
        default: { appenders: ['console'], level: 'info' }
    }
});

const logger = log4js.getLogger('background');
const engineLogger = log4js.getLogger('engine');

if (isDevelopment) {
    logger.level = 'debug';
    engineLogger.level = 'debug';
}

async function createWindow(refer) {
    var fetchScreenSize = function () {
        return new Promise((resolve, reject) => {
            var currentTime = Date.now();
            cordova.channel.post("secret_channel_2", { event: "screen_size", id: currentTime });
            cordova.channel.on(`secret_channel_t_${currentTime}`, function (msg) {
                resolve(msg);
            });
        });
    }

    var size = [0, 0];
    cordova.channel.on("size_secret", function (msg) {
        size[0] = msg[0];
        size[1] = msg[1];
    });

    cordova.channel.post("size_secret", "init");

    const win = {
        webContents: {
            send: function (key, value) {
                ipcMain.send(key, value);
            }
        },
        on: function (event, func) {
            cordova.channel.on("secret_channel", function (msg) {
                if (msg.event == event) {
                    func();
                }
            });
        },
        getContentSize: function () {
            console.log(size);
            return size;
        }
    };

    function setContentHeight() {
        win.webContents.send('engine', {
            action: engineCommand.resize,
            arg: win.getContentSize()[1],
        });
    }
    win.on('resize', setContentHeight);

    var gamePath = join(home_path, './game');

    var screenSize = await fetchScreenSize();
    var setIconCb = (_image) => { };

    function connect(action, data){
        win.webContents.send('connector', {action, data});
    }

    function error(e){
        engineLogger.error(e.stack);
        connect('error', {message: e.message, stack: e.stack});
    }

    function setMenu() { };

    const era = createEra(
        gamePath,
        connect,
        (key, cb) => {
            ipcMain.on(key, cb);
        },
        (key) => {
            ipcMain.removeAllListeners(key);
        },
        () => {
            setContentHeight();
        },
        (name, _path, remove, _gamebase) => {
            if (!remove){
                try {
                    var url = new URL(_gamebase['versionCheck']);
                    var client = (url.protocol == "http:") ? http : https;
                    client.get(_gamebase['versionCheck'], res => {
                        res.setEncoding("utf8");
                        let body = "";
                        res.on("data", data => {
                            body += data;
                        });
                        res.on("end", () => {
                            const newVersion = Number(body);
                            if (newVersion > _gamebase.version){
                                win.webContents.send('connector', {
                                    action: 'findNewVersion',
                                    data: {
                                        new: newVersion,
                                        now: _gamebase.version,
                                        update: _gamebase.site
                                    }
                                });
                            }
                        });
                    });
                } catch (e) {
                    logger.warn('checking game version failed, traceback: \n'+e.stack);
                }
            }
        },
        (_image) => {
        },
        (progress) => { },
        () => { cordova.channel.post("secret_channel", "exit") },
        engineLogger,
        isDevelopment,
    );

    ipcMain.removeAllListeners();
    ipcMain.on('engine', (_, msg) => {
        switch (msg.action) {
            case engineCommand.restart:
                era.restart();
                break;
            case engineCommand.start:
                era.start().catch(error);
                win.webContents.send('engine', {
                    action: engineCommand.version,
                    arg: "2.0.1",
                });
                break;
            case engineCommand.saveConfig:
                era.config = msg.data;
                era.saveConfig();
                win.webContents.send('connector', {
                    action: 'setConfig',
                    data: {
                        config: era.config,
                        fixed: era.fixedConfig,
                    },
                });
        }
    });

    ipcMain.on('era', (_, msg) => {
        switch (msg.action) {
            case 'add':
                win.webContents.send('connector', {
                    action: 'log',
                    data: { message: era.api.add(msg.key, msg.val) },
                });
                break;
            case 'cleanSave':
                logger.info(era.api.rmData(msg.val));
                break;
            case 'get':
                win.webContents.send('connector', {
                    action: 'log',
                    data: { message: era.api.get(msg.key) },
                });
                break;
            case 'logData':
                era.api.logData();
                break;
            case 'logStatic':
                era.api.logStatic();
                break;
            case 'set':
                win.webContents.send('connector', {
                    action: 'log',
                    data: { message: era.api.set(msg.key, msg.val) },
                });
                break;
            case 'toggleDebug':
                win.webContents.send('connector', {
                    action: 'log',
                    data: { message: era.api.toggleDebug() },
                });
        }
    });
    if (refer.push) {
        refer.push(era);
    }
    return era;
}

module.exports = { createWindow };


code:
'use strict';

const {
  copyFileSync,
  existsSync,
  lstatSync,
  mkdirSync,
  readFileSync,
  readdirSync,
  rmSync,
  statSync,
  writeFileSync,
} = require('fs');

const { dirname, extname, join, relative, resolve } = require('path');

const { gzip } = require('compressing');
// const { app } = require('electron');
const getImageSize = require('image-size');

// const parseCSV = require('@/era/csv-utils');
const nameMapping = require('@/era/name-mapping.json');
const parseDataFile = require('@/era/static-file-utils');
const {
  fuckOffUtf8Bom,
  getGameVersion,
  getNumber,
  safeUndefinedCheck,
  safelyGetConfigEntry,
  toLowerCase,
} = require('@/renderer/utils/value-utils');

const getterHandlers = [Object.keys, Object.values, Object.entries];
const safeLibs = { crypto: true };
const staticFormatPriority = ['yml', 'json', 'csv'];
const staticFormatRegex = [
  /chara[^/]+.yml/,
  /chara[^/]+.json/,
  /chara[^/]+.csv/,
];
const embeddedTableNames = {
  abl: 1,
  base: 1,
  maxbase: 1,
  cflag: 1,
  cstr: 1,
  equip: 1,
  ex: 1,
  nowex: 1,
  exp: 1,
  flag: 1,
  global: 1,
  item: 1,
  mark: 1,
  palam: 1,
  juel: 1,
  gotjuel: 1,
  stain: 1,
  talent: 1,
  tcvar: 1,
  tequip: 1,
  tflag: 1,
};

/**
 * @param {string} path game path
 * @param {function} connect callback
 * @param {function} listen callback
 * @param {function} cleanListener callback
 * @param {function} resizeWindow callback
 * @param {function} configPath callback
 * @param {function} setIcon callback
 * @param {function} setProgressBar callback
 * @param {function} quit callback
 * @param {{debug:function,error:function,info:function,warn:function,level:string}} logger logger
 * @param {boolean} isDevelopment
  * @returns {{api:{add:function(string,*):*,get:function(string):*,logData:function,logStatic:function,rmData:function(*),set:function(string,*):*,toggleDebug:function()},config:Record<string,*>,data:*,fixedConfig:Record<string,*>,reloadFiles:function(string),restart:function(),logCache:function(),saveConfig:function(),setPath:function(string),start:function(),staticData:*,fieldNames:*}} era
 */
module.exports = (
  path,
  connect,
  listen,
  cleanListener,
  resizeWindow,
  configPath,
  setIcon,
  setProgressBar,
  quit,
  logger,
  isDevelopment,
) => {
  const era = {
    api: {
      add: () => {},
      get: () => {},
      logData: () => {},
      logStatic: () => {},
      rmData: () => {},
      set: () => {},
      toggleDebug: () => {},
    },
    config: {},
    data: {},
    debug: false,
    fieldNames: {},
    fixedConfig: {},
    global: {},
    /** @type {Record<string,
     * {path:string,[x]:number,[y]:number,[width]:number,[height]:number,
     * [posX]:number,[posY]:number}>} */
    images: {},
    logCache: () => {},
    reloadFiles: () => {},
    restart: () => {},
    saveConfig: () => {},
    setPath: () => {},
    start: () => {},
    staticData: {
      gamebase: {},
    },
  };

  let allowWait = true,
    clearKey = undefined,
    extendedCharaTables,
    extendedNormalTables,
    inputKey = undefined,
    isContinue = false,
    gameMain = undefined,
    gamePath = resolve(path),
    oldPath = 'old-path',
    totalLines = 0;

  function addTotalLines() {
    allowWait = true;
    return totalLines++;
  }

  /**
   * @param {number} [lineCount]
   * @returns {Promise<number>}
   */
  function clearScreen(lineCount) {
    clearKey = new Date().getTime().toString();
    connect('clear', { clearKey, lineCount });
    return new Promise((resolve) => {
      listen(clearKey, (_, ret) => {
        cleanListener(clearKey);
        clearKey = undefined;
        setTotalLines(ret);
        resolve(ret);
      });
    });
  }

  function error(message, stack) {
    if (stack) {
      logger.error(stack);
    } else {
      logger.error(message);
    }
    connect('error', { message, stack });
  }

  function getImageObject(names) {
    return (names instanceof Array ? names : [names])
      .map((e) => {
        const ret = (typeof e === 'string' ? { names: e } : e) || {};
        const altImages = (ret.names || '').split('\t').map(toLowerCase);
        delete ret.names;
        for (const img of altImages) {
          if (era.images[img]) {
            ret.src = era.images[img].path || '';
            ret.x = era.images[img].x || 0;
            ret.y = era.images[img].y || 0;
            ret.width = era.images[img].width;
            ret.height = era.images[img].height;
            ret.posX = era.images[img].posX || 0;
            ret.posY = era.images[img].posY || 0;
            return ret;
          }
        }
      })
      .filter((e) => e && e.src);
  }

  function getWholeImagesFromCache(names) {
    return (names instanceof Array ? names : [names])
      .map((e) => {
        const ret = (typeof e === 'string' ? { names: e } : e) || {};
        const altImages = (ret.names || '').split('\t').map(toLowerCase);
        delete ret.names;
        for (const img of altImages) {
          if (era.images[img]) {
            ret.height = era.images[img].height;
            ret.src = era.images[img].path || '';
            ret.width = era.images[img].width;
            return ret;
          }
        }
      })
      .filter((e) => e && e.src);
  }

  function inColRows(func, ...columnObjects) {
    connect(func, {
      columns: columnObjects.map((x) => {
        const obj = x instanceof Array ? { columns: x, config: {} } : x;
        return {
          columns: obj.columns.map((y) => {
            if (y.type === 'image') {
              return {
                type: 'image',
                images: getImageObject(y.names),
                config: y.config || {},
              };
            } else if (y.type === 'image.whole') {
              return {
                type: 'image.whole',
                images: getWholeImagesFromCache(y.names),
                config: y.config || {},
              };
            }
            return y;
          }),
          config: obj.config || {},
        };
      }),
    });
    return addTotalLines();
  }

  function log(info, stack) {
    connect('log', { message: info, stack });
  }

  function resetParams() {
    if (inputKey) {
      cleanListener(inputKey);
      inputKey = undefined;
    }
    if (clearKey) {
      cleanListener(clearKey);
      clearKey = undefined;
    }
    isContinue = false;
  }

  function setTotalLines(val) {
    if (totalLines !== val) {
      allowWait = true;
      return (totalLines = val);
    }
    return totalLines;
  }

  function text(func, content, config) {
    if (
      era.staticData.gamebase &&
      era.staticData.gamebase['collapseBlankLines'] &&
      (content === undefined || content === '' || content.length === 0)
    ) {
      return -1;
    }
    connect(func, { content, config: config || {} });
    return addTotalLines();
  }

  era.api.add = (key, val) => {
    if (!val) {
      return era.api.get(key);
    }
    return era.api.set(key, val, true);
  };

  era.api.addCharacter = era.api.resetCharacter = (...ids) => {
    const result = ids.map((charaId) => {
      let src = charaId,
        dst = charaId;
      if (charaId.length === 2) {
        dst = charaId[0];
        src = charaId[1];
      } else if (typeof src !== 'number') {
        return false;
      }
      if (!era.staticData.chara[src]) {
        return false;
      }
      era.data.no[era.data.newCharaIndex++] = dst;
      era.data.maxbase[dst] = {};
      era.data.base[dst] = {};
      era.data.abl[dst] = {};
      era.data.talent[dst] = {};
      era.data.cflag[dst] = {};
      era.data.cstr[dst] = {};
      era.data.equip[dst] = {};
      era.data.mark[dst] = {};
      era.data.exp[dst] = {};
      era.data.juel[dst] = {};
      era.data.callname[dst] = {};
      era.data.relation[dst] = {};
      Object.keys(extendedCharaTables).forEach(
        (table) => (era.data[toLowerCase(table)][dst] = {}),
      );

      // init
      Object.entries(era.staticData.chara[src])
        .filter((kv) => typeof kv[1] === 'object')
        .forEach(
          /** @param {[string, {}]} kv */
          (kv) =>
            Object.entries(kv[1]).forEach(
              (kv1) => (era.data[kv[0]][dst][kv1[0]] = kv1[1]),
            ),
        );
      Object.entries(era.staticData)
        .filter(
          (kv) =>
            era.data[kv[0]] instanceof Object &&
            era.data[kv[0]][dst] instanceof Object &&
            kv[0] !== 'cstr' &&
            kv[0] !== 'item' &&
            kv[0] !== 'flag' &&
            kv[0] !== 'global' &&
            !extendedNormalTables[kv[0]],
        )
        .forEach(
          /** @param {[string, object]} kv */
          (kv) =>
            Object.entries(kv[1])
              .filter((kv1) => typeof kv1[1] !== 'object')
              .forEach((kv1) => {
                if (era.data[kv[0]][dst][kv1[1]] === undefined) {
                  era.data[kv[0]][dst][kv1[1]] = 0;
                }
              }),
        );
      Object.values(era.staticData.cstr || {}).forEach(
        (v) => era.data.cstr[dst][v] || (era.data.cstr[dst][v] = ''),
      );
      era.data.callname[dst][-2] =
        era.staticData.chara[src].callname || era.staticData.chara[src].name;
      era.data.callname[dst][-1] = era.staticData.chara[src].name;
      Object.entries(era.staticData.relationship.callname)
        .map((e) => {
          return { k: e[0], v: e[1] };
        })
        .filter(({ k }) => k.startsWith(`${dst}|`) || k.endsWith(`|${dst}`))
        .forEach(({ k, v }) => {
          const idArr = k.split('|'),
            _from = idArr[0],
            _to = idArr[1];
          if (era.data.callname[_from]) {
            era.data.callname[_from][_to] = v;
          }
        });
      Object.entries(era.staticData.relationship.relation)
        .map((e) => {
          return { k: e[0], v: e[1] };
        })
        .filter(({ k }) => k.startsWith(`${dst}|`) || k.endsWith(`|${dst}`))
        .forEach(({ k, v }) => {
          const idArr = k.split('|'),
            _from = idArr[0],
            _to = idArr[1];
          if (era.data.relation[_from]) {
            era.data.relation[_from][_to] = v;
            if (
              (era.staticData.gamebase['defaultChara'] === undefined ||
                _to !== era.staticData.gamebase['defaultChara']) &&
              era.staticData.relationship.relation[`${_to}|${_from}`] ===
                undefined &&
              era.data.relation[_to]
            ) {
              era.data.relation[_to][_from] = v;
            }
          }
        });
      era.data.love[dst] = 0;
      era.data.base &&
        Object.keys(era.data.base[dst]).forEach(
          (k) => (era.data.maxbase[dst][k] = era.data.base[dst][k]),
        );
      return true;
    });
    if (ids.length === 1) {
      return result[0];
    }
    return result;
  };

  era.api.addCharacterForTrain = (...charaId) => {
    if (charaId.length) {
      charaId
        .filter((id) => !era.data.tequip[id])
        .forEach((id) => {
          era.data.tequip[id] = {};
          era.data.tcvar[id] = {};
          era.data.palam[id] = {};
          era.data.gotjuel[id] = {};
          era.data.stain[id] = {};
          era.data.ex[id] = {};
          era.data.nowex[id] = {};
          Object.values(era.staticData.tcvar).forEach(
            (v) => (era.data.tcvar[id][v] = 0),
          );
          Object.values(era.staticData.juel).forEach(
            (v) => (era.data.palam[id][v] = era.data.gotjuel[id][v] = 0),
          );
          Object.values(era.staticData.stain).forEach(
            (v) => (era.data.stain[id][v] = 0),
          );
          Object.values(era.staticData.ex).forEach(
            (v) => (era.data.ex[id][v] = era.data.nowex[id][v] = 0),
          );
        });
    }
  };

  era.api.beginTrain = (...charaId) => {
    if (!era.data.tequip) {
      era.data.tequip = {};
      era.data.tflag = {};
      era.data.tcvar = {};
      era.data.palam = {};
      era.data.gotjuel = {};
      era.data.stain = {};
      era.data.ex = {};
      era.data.nowex = {};

      Object.values(era.staticData.tflag).forEach(
        (v) => (era.data.tflag[v] = 0),
      );
    }
    era.api.addCharacterForTrain(...charaId);
  };

  era.api.checkImage = (...name) => {
    if (name.length === 0) {
      return false;
    }
    const ret = name.map((e) => era.images[e] !== undefined);
    if (name.length === 1) {
      return ret[0];
    }
    return ret;
  };

  era.api.clear = async (lineCount) => {
    if (safelyGetConfigEntry(era.config, 'system.disableClear')) {
      return totalLines;
    }
    if (isContinue) {
      await era.api.waitAnyKey(true);
      return await clearScreen(lineCount);
    } else {
      return await clearScreen(lineCount);
    }
  };

  era.api.drawLine = (config) => {
    connect('drawLine', {
      config: config || {},
    });
    return addTotalLines();
  };

  era.api.endTrain = () => {
    delete era.data.tequip;
    delete era.data.tflag;
    delete era.data.tcvar;
    delete era.data.palam;
    delete era.data.gotjuel;
    delete era.data.stain;
    delete era.data.ex;
    delete era.data.nowex;
  };

  era.api.get = era.api.set = (key, val, isAdd) => {
    if (!key) {
      return undefined;
    }
    const keyArr = key.split(':').map((x) => x.toLocaleLowerCase());
    let tableName, charaIndex, valueIndex, type;
    switch (keyArr.length) {
      case 1:
        tableName = keyArr[0];
        switch (tableName) {
          case 'no':
            return era.data.no;
          case 'gamebase':
            return era.staticData.gamebase;
          case 'version':
            if (typeof val == 'number'){
                era.data.version = val;
            }
            return era.data.version;
          case 'chara':
            return Object.keys(era.staticData.chara)
              .map(Number)
              .sort((a, b) => a - b);
          default:
            if (tableName.endsWith('names')) {
              type = 0;
              tableName = tableName.substring(0, tableName.length - 5);
            } else if (tableName.endsWith('keys')) {
              type = 1;
              tableName = tableName.substring(0, tableName.length - 4);
            } else if (tableName.endsWith('entries')) {
              type = 2;
              tableName = tableName.substring(0, tableName.length - 7);
            }
            if (type !== undefined) {
              switch (tableName) {
                case 'palam':
                  return getterHandlers[type](era.staticData.juel);
                case 'item':
                  return getterHandlers[type](era.staticData.item.name);
                default:
                  if (era.staticData[tableName]) {
                    return getterHandlers[type](era.staticData[tableName]);
                  }
              }
            }
        }
        break;
      case 2:
        tableName = keyArr[0];
        valueIndex = keyArr[1];
        switch (tableName) {
          case 'callname':
            return era.data.callname[valueIndex] || {};
          case 'love':
            if (val !== undefined) {
              if (isAdd) {
                era.data.love[valueIndex] += val;
              } else {
                era.data.love[valueIndex] = val;
              }
            }
            return era.data.love[valueIndex];
          case 'global':
            valueIndex = safeUndefinedCheck(
              era.staticData.global[valueIndex],
              valueIndex,
            );
            if (val !== undefined) {
              if (isAdd) {
                era.global[valueIndex] += val;
              } else {
                era.global[valueIndex] = val;
              }
            }
            return era.global[valueIndex];
          case 'palamname':
            return era.fieldNames.juel[valueIndex].n;
          case 'relation':
            return era.data.relation[valueIndex] || {};
          default:
            if (tableName.endsWith('name')) {
              tableName = tableName.substring(0, tableName.length - 4);
              if (era.fieldNames[tableName]) {
                return era.fieldNames[tableName][valueIndex].n;
              }
            } else if (tableName.startsWith('item')) {
              if (valueIndex === 'bought') {
                return era.data.item.bought;
              }
              valueIndex = safeUndefinedCheck(
                era.staticData.item.name[valueIndex],
                valueIndex,
              );
              switch (tableName.substring(4)) {
                case 'price':
                  tableName = 'price';
                  break;
                case 'sales':
                  tableName = 'sales';
                  break;
                default:
                  tableName = 'hold';
              }
              if (val !== undefined) {
                if (isAdd) {
                  era.data.item[tableName][valueIndex] += val;
                } else {
                  era.data.item[tableName][valueIndex] = val;
                }
              }
              return era.data.item[tableName][valueIndex];
            } else if (era.data[tableName]) {
              valueIndex = safeUndefinedCheck(
                era.staticData[tableName][valueIndex],
                valueIndex,
              );
              if (val !== undefined) {
                if (isAdd) {
                  era.data[tableName][valueIndex] += val;
                } else {
                  era.data[tableName][valueIndex] = val;
                }
              }
              return era.data[tableName][valueIndex];
            }
        }
        break;
      case 3:
        tableName = keyArr[0];
        charaIndex = keyArr[1];
        valueIndex = keyArr[2];
        switch (tableName) {
          case 'callname':
            if (!era.data.callname[charaIndex]) {
              return '-';
            }
            if (val !== undefined) {
              era.data.callname[charaIndex][valueIndex] = val;
            }
            return safeUndefinedCheck(
              era.data.callname[charaIndex][valueIndex],
              era.data.callname[valueIndex]
                ? era.data.callname[valueIndex][-2]
                : '-',
            );
          case 'relation':
            if (!era.data.relation[charaIndex]) {
              return undefined;
            }
            if (val !== undefined) {
              if (isAdd) {
                era.data.relation[charaIndex][valueIndex] += val;
              } else {
                era.data.relation[charaIndex][valueIndex] = val;
              }
            }
            return era.data.relation[charaIndex][valueIndex];
          case 'global':
            if (!era.global[charaIndex]) {
              return undefined;
            }
            if (val !== undefined) {
              if (isAdd) {
                era.global[charaIndex][valueIndex] += val;
              } else {
                era.global[charaIndex][valueIndex] = val;
              }
            }
            return era.global[charaIndex][valueIndex];
          case 'base':
            if (!era.data.base[charaIndex]) {
              return undefined;
            }
            valueIndex = safeUndefinedCheck(
              era.staticData.base[valueIndex],
              valueIndex,
            );
            if (val !== undefined) {
              if (isAdd) {
                era.data.base[charaIndex][valueIndex] += val;
              } else {
                era.data.base[charaIndex][valueIndex] = val;
              }
              if (era.data.maxbase[charaIndex][valueIndex]) {
                era.data.base[charaIndex][valueIndex] = Math.min(
                  era.data.maxbase[charaIndex][valueIndex],
                  Math.max(0, era.data.base[charaIndex][valueIndex]),
                );
              }
            }
            return era.data.base[charaIndex][valueIndex];
          case 'maxbase':
            if (!era.data.maxbase[charaIndex]) {
              return undefined;
            }
            valueIndex = safeUndefinedCheck(
              era.staticData.base[valueIndex],
              valueIndex,
            );
            if (val !== undefined) {
              if (isAdd) {
                era.data.maxbase[charaIndex][valueIndex] += val;
              } else {
                era.data.maxbase[charaIndex][valueIndex] = val;
              }
            }
            return era.data.maxbase[charaIndex][valueIndex];
          default:
            if (tableName.startsWith('static')) {
              tableName = tableName.substring(6);
              if (!era.staticData.chara[charaIndex]) {
                return undefined;
              }
              if (!era.staticData[tableName]) {
                return era.staticData.chara[charaIndex][valueIndex];
              }
              valueIndex = safeUndefinedCheck(
                era.staticData[tableName][valueIndex],
                valueIndex,
              );
              return era.staticData.chara[charaIndex][tableName][valueIndex];
            }
            if (!era.data[tableName] || !era.data[tableName][charaIndex]) {
              return undefined;
            }
            switch (tableName) {
              case 'palam':
              case 'gotjuel':
                valueIndex = safeUndefinedCheck(
                  era.staticData.juel[valueIndex],
                  valueIndex,
                );
                break;
              case 'nowex':
                valueIndex = safeUndefinedCheck(
                  era.staticData.ex[valueIndex],
                  valueIndex,
                );
                break;
              default:
                valueIndex = safeUndefinedCheck(
                  era.staticData[tableName][valueIndex],
                  valueIndex,
                );
            }
            if (val !== undefined) {
              if (isAdd) {
                era.data[tableName][charaIndex][valueIndex] += val;
              } else {
                era.data[tableName][charaIndex][valueIndex] = val;
              }
            }
            return era.data[tableName][charaIndex][valueIndex];
        }
    }
    error(`key error in getter/setter! key (${key})`, new Error().stack);
    return undefined;
  };

  era.api.getAllCharacters = () => {
    return Object.keys(era.staticData.chara).map(Number);
  };

  era.api.getAddedCharacters = () => {
    return Object.keys(era.data.base).map(Number);
  };

  era.api.getCharactersInTrain = () => {
    if (!era.data.tequip) {
      return [];
    }
    return Object.keys(era.data.tequip).map(Number);
  };

  era.api.getLineCount = () => totalLines;

  /**
   * @param {Record<string,any>} [config]
   * @returns {Promise<string|number>}
   */
  era.api.input = async (config) => {
    inputKey = new Date().getTime().toString();
    const _config = config || {};
    connect('input', { config: _config, inputKey });
    return new Promise((resolve) => {
      listen(inputKey, (_, obj) => {
        cleanListener(inputKey);
        inputKey = undefined;
        const retVal = getNumber(obj.val);
        isContinue = obj.continue;
        if (
          !safelyGetConfigEntry(era.config, 'system.hideUserInput') &&
          !_config.hideInput &&
          !_config.any
        ) {
          era.api.print(retVal);
        }
        resolve(retVal);
      });
    });
  };

  era.api.loadData = async (savId) => {
    const savPath = join(gamePath, `../../save/save${savId}.sav`);
    try {
      let tmp;
      if (safelyGetConfigEntry(era.config, 'system.saveCompressedData')) {
        const tmpPath = join(gamePath, `ere-tmp-sav-${new Date().getTime()}`);
        await gzip.uncompress(savPath, tmpPath);
        tmp = JSON.parse(fuckOffUtf8Bom(readFileSync(tmpPath, 'utf-8')));
        rmSync(tmpPath);
      } else {
        tmp = JSON.parse(fuckOffUtf8Bom(readFileSync(savPath, 'utf-8')));
      }
      if (tmp.code && tmp.code !== era.staticData.gamebase.gameCode) {
        error(
          `存档文件所属游戏ID（${tmp.code}）与GameBase（${era.staticData.gamebase.gameCode}）不匹配！请检查sav文件夹！`,
        );
      } else if (
        !tmp.version ||
        tmp.version < era.staticData.gamebase['allowVersion']
      ) {
        error(`save${savId}.sav版本过低（${tmp.version / 1000}）！`);
      } else {
        era.data = tmp;
        era.staticData.item &&
          Object.values(era.staticData.item.name).forEach((num) => {
            era.data.item.hold[num] === undefined &&
              (era.data.item.hold[num] = 0);
            era.data.item.sales[num] === undefined &&
              (era.data.item.sales[num] = 0);
            era.data.item.price[num] === undefined &&
              (era.data.item.price[num] = era.staticData.item.price[num]);
          });
          Object.keys(extendedNormalTables).forEach((k) => {
            if (!era.data[k]) {
              era.data[k] = {};
              Object.values(era.staticData[k]).forEach(
                (num) => (era.data[k][num] = 0),

            );
        }
    });
    Object.keys(extendedCharaTables).forEach((k) => {
      const key = toLowerCase(k);
      if (!era.data[key]) {
        era.data[key] = {};
      }
      era.data.no.forEach(
        (charaId) =>
          !era.data[key][charaId] && (era.data[key][charaId] = {}),
      );
    });
        return true;
      }
    } catch (e) {
      error(e.message, e.stack);
    }
    return false;
  };

  era.api.loadGlobal = () => {
    const globalPath = join(gamePath, '../../save/global.sav');
    if (existsSync(globalPath)) {
      let invalid_code = false;
      try {
        const tmp = JSON.parse(readFileSync(globalPath, 'utf-8'));
        if (tmp.code && tmp.code !== era.staticData.gamebase.gameCode) {
            invalid_code = true;
            error(
              `global.sav所属游戏ID（${tmp.code}）与GameBase（${era.staticData.gamebase.gameCode}）不匹配！请检查sav文件夹！`,
            );
        } else if (
          !tmp.version ||
          tmp.version < era.staticData.gamebase['allowVersion']
        ) {
          error(`global.sav版本过低（${tmp.version}）！已重新生成`);
          era.api.resetGlobal();
        } else {
          era.global = tmp;
          Object.values(era.staticData.global).forEach(
            (k) => !era.global[k] && (era.global[k] = 0),
          );
        }
      } catch (_) {
        era.api.resetGlobal();
      }
      if (invalid_code) {
        throw new Error("Invalid Code");
      }
    } else {
      era.api.resetGlobal();
    }
    const maxSavFiles =
      safelyGetConfigEntry(era.config, 'system.saveFiles') || 10;
    new Array(maxSavFiles + 1).fill(0).forEach((_, i) => {
      const existing = existsSync(join(gamePath, `../../save/save${i}.sav`));
      if (existing && !era.global.saves[i]) {
        era.global.saves[i] = 'UNNAMED SAVE FILE';
      } else if (
        era.global.saves[i] &&
        !existing &&
        !era.global.saves[i].startsWith('(FILE LOST)')
      ) {
        era.global.saves[i] = `(FILE LOST) ${era.global.saves[i]}`;
      }
    });
    return era.api.saveGlobal();
  };

  era.api.isDebug = () => era.debug;

  era.api.log = log;

  era.api.logData = () => {
    log({ data: era.data, global: era.global });
  };

  era.api.logger = {
    assert(checkVal, aimVal){
        if (checkVal !== aimVal){
            const message = `assert failed! aim val: ${aimVal}, got val: ${checkVal}`;
            logger.warn(new Error(message).stack);
            connect('warn', { message, stack: new Error().stack.split('\n')[2] });
        }
    },
    debug(info) {
      if (era.debug) {
        log(info, new Error().stack.split('\n')[2]);
      }
    },
    error,
    info: log,
  };

  era.api.logStatic = () => {
    log({
      images: era.images,
      names: era.fieldNames,
      static: era.staticData,
    });
  };

  era.api.notify = (content, title, type) =>
    connect('notify', { content, title, type });
  era.api.print = (...params) => text('print', ...params);

  era.api.printAndWait = async (content, config) => {
    if (era.api.print(content, config) !== -1) {
      await era.api.waitAnyKey();
      return totalLines - 1;
    }
  };

  era.api.printButton = (content, accelerator, config) => {
    connect('printButton', {
      content,
      accelerator,
      config: config || {},
    });
    return addTotalLines();
  };

  era.api.printImage = (...names) => {
    connect('printImage', {
      images: getImageObject(names),
      config: {},
    });
    return addTotalLines();
  };

  era.api.printInColRows = (...columnObjects) =>
    inColRows('printInColRows', ...columnObjects);

  era.api.printLineChart = (data) => {
    connect('printLineChart', data);
    return addTotalLines();
  };

  era.api.println = () => {
    connect('println');
    return addTotalLines();
  };

  era.api.printMultiColumns = (columnObjects, config) => {
    connect('printMultiCols', {
      columns: columnObjects.map((x) => {
        if (x.type === 'image') {
          return {
            type: 'image',
            images: getImageObject(x.names),
            config: x.config || {},
          };
        } else if (x.type === 'image.whole') {
          return {
            type: 'image.whole',
            images: getWholeImagesFromCache(x.names),
            config: x.config || {},
          };
        }
        return x;
      }),
      config: config || {},
    });
    return addTotalLines();
  };

  era.api.printProgress = (percentage, inContent, outContent, config) => {
    connect('printProgress', {
      config: config || {},
      inContent,
      outContent,
      percentage,
    });
    return addTotalLines();
  };

  era.api.printWholeImage = (name, config) => {
    connect('printWholeImage', {
      config: config || {},
      images: getWholeImagesFromCache(name),
    });
    return addTotalLines();
  };

  era.api.println = () => {
    connect('println');
    return addTotalLines();
  };

  era.api.quit = () => {
    quit();
    throw new Error('quit');
  };

  era.api.replaceInColRows = (...columnObjects) =>
    inColRows('replaceInColRows', ...columnObjects);

  era.api.replaceText = (...params) => text('replaceText', ...params);

  era.api.resetAllExceptGlobal = () => {
    Object.keys(era.data).forEach((tableName) => (era.data[tableName] = {}));
    return true;
  };

  era.api.resetData = () => {
    era.data = {
      abl: {},
      base: {},
      callname: {},
      cflag: {},
      cstr: {},
      equip: {},
      exp: {},
      flag: {},
      item: {
        bought: -1,
        hold: {},
        price: {},
        sales: {},
      },
      juel: {},
      love: {},
      mark: {},
      maxbase: {},
      newCharaIndex: 0,
      no: [],
      relation: {},
      talent: {},
      version: era.staticData.gamebase.version,
    };
    Object.values(era.staticData.flag || {}).forEach(
      (num) => (era.data.flag[num] = 0),
    );
    era.staticData.item &&
      Object.values(era.staticData.item.name).forEach((num) => {
        era.data.item.hold[num] = 0;
        era.data.item.sales[num] = 0;
        era.data.item.price[num] = era.staticData.item.price[num];
      });
      Object.keys(extendedNormalTables).forEach((k) => {
        era.data[k] = {};
        Object.values(era.staticData[k]).forEach((num) => (era.data[k][num] = 0));
      });
      Object.keys(extendedCharaTables).forEach((k) => (era.data[k] = {}));
    if (era.staticData.gamebase['defaultChara'] !== undefined) {
      era.api.addCharacter(era.staticData.gamebase['defaultChara']);
    }
  };

  era.api.resetGlobal = () => {
    era.global = {
      saves: {},
      version: era.staticData.gamebase.version,
    };
    Object.values(era.staticData.global || {}).forEach(
      (k) => (era.global[k] = 0),
    );
    return era.api.saveGlobal();
  };

  era.api.rmData = (savId) => {
    try {
      rmSync(join(join(gamePath, `../../save/save${savId}.sav`)));
      delete era.global.saves[savId];
      era.api.saveGlobal();
    } catch (e) {
      error(e.message, e.stack);
      return false;
    }
    return true;
  };

  era.api.saveData = async (savId, comment) => {
    const savDirPath = join(gamePath, '../../save');
    if (!existsSync(savDirPath)) {
      mkdirSync(savDirPath);
    }
    try {
      era.data.version = era.staticData.gamebase.version;
      const data = JSON.stringify(era.data),
        dataPath = join(savDirPath, `./save${savId}.sav`);
      if (safelyGetConfigEntry(era.config, 'system.saveCompressedData')) {
        await gzip.compressFile(Buffer.from(data, 'utf-8'), dataPath);
      } else {
        writeFileSync(dataPath, data);
      }
      era.global.saves[savId] = comment;
      era.api.saveGlobal();
      return true;
    } catch (e) {
      error(e.message, e.stack);
      return false;
    }
  };

  era.api.saveGlobal = () => {
    const savDirPath = join(gamePath, '../../save');
    if (!existsSync(savDirPath)) {
      mkdirSync(savDirPath);
    }
    try {
      era.global.version = era.staticData.gamebase.version;
      writeFileSync(
        join(savDirPath, './global.sav'),
        JSON.stringify(era.global),
      );
      return true;
    } catch (e) {
      error(e.message, e.stack);
    }
    return false;
  };

  era.api.setAlign = (textAlign) => {
    connect('setAlign', textAlign);
  };

  era.api.setHorizontalAlign = (align) => {
    connect('setHorizontalAlign', align);
  };

  era.api.setMask = (maskName, opacity) => {
    const imageObj = getWholeImagesFromCache(maskName)[0];
    connect('setMask', { url: imageObj ? imageObj.src : undefined, opacity });
    allowWait = true;
  };

  era.api.setOffset = (offset) => {
    connect('setOffset', offset);
  };

  era.api.setTitle = (title) => {
    connect('setTitle', title);
  };

  era.api.setToBottom = () => {
    connect('setToBottom');
    return addTotalLines();
  };

  era.api.setVerticalAlign = (align) => {
    connect('setVerticalAlign', align);
  };

  era.api.setWidth = (width) => {
    connect('setWidth', width);
  };

  era.api.toggleDebug = () => {
    era.debug = !era.debug;
    if (era.debug) {
      logger.level = 'debug';
    } else {
      logger.level = 'info';
    }
    return era.debug;
  };

  /**
   * @param {boolean} [fromClear]
   * @returns {Promise<void>}
   */
  era.api.waitAnyKey = async (fromClear) => {
    if (allowWait || fromClear) {
      allowWait = false;
      await era.api.input({
        any: true,
        fromClear,
        useRule: false,
      });
    }
  };

  const module = require('module');
  const include = module.prototype.require;

  era.logCache = () => {
    const cache = Object.keys(module['_cache']).filter((e) =>
      e.startsWith(oldPath),
    );
    if (cache.length) {
      cache.forEach((e) => logger.info(e));
    } else {
      logger.info('none');
    }
  };

  // eslint-disable-next-line no-unused-vars
  era.reloadFiles = (_path) =>
    Object.keys(module['_cache'])
      .filter((e) => e.startsWith(_path))
      .forEach((e) => delete module['_cache'][e]);

  era.restart = () => {
    if (!gameMain) {
      era.api.print(`路径${gamePath}不正确！请选择待载入游戏文件夹！`);
      return;
    }
    resetParams();
    era.api.resetData();
    era.api.loadGlobal();
    configPath(
      `${
        era.staticData.gamebase.title
          ? era.staticData.gamebase.title.substring(0, 20)
          : ''
      } v${getGameVersion(era.staticData.gamebase.version || 1)}`,
      dirname(oldPath),
      false,
      era.staticData.gamebase,
    );

    era.api
      .clear()
      .then(gameMain)
      .catch((e) => {
        if (e.message !== 'quit') {
          error(e.message, e.stack);
        }
      });
  };

  era.saveConfig = () =>
    writeFileSync(
      join(gamePath, './ere.config.json'),
      JSON.stringify(era.config),
    );

  era.setPath = (_path) => (gamePath = resolve(_path));

  era.start = async () => {
    resetParams();
    await era.api.clear();
    setIcon();
    let current = new Date().getTime();
    if (!existsSync(gamePath)) {
      era.api.print(`路径${gamePath}不正确！请选择待载入游戏文件夹！`);
      configPath(undefined, gamePath, true);
      return;
    }

    // load resource csv
    const resPath = join(gamePath, './res'),
      isResDirExists = existsSync(resPath) && lstatSync(resPath).isDirectory();

      let staticPath, suffix;

      /** @param {string} [preferred] */
      function getSuffix(preferred) {
        let formatIndex = preferred ? staticFormatPriority.indexOf(preferred) : 0;
        if (formatIndex < 0) {
          formatIndex = 0;
        }
        while (formatIndex < staticFormatPriority.length) {
          staticPath = join(gamePath, `./${staticFormatPriority[formatIndex]}`);
          if (existsSync(staticPath) && statSync(staticPath).isDirectory()) {
            break;
          }
          formatIndex++;
        }
        suffix = staticFormatPriority[formatIndex];
      }

      getSuffix();
      if (suffix === undefined) {
        era.api.print(
          '静态数据文件夹（yml、json、csv）不存在！游戏数据载入失败！',
        );
        gameMain = undefined;
        return;
      }

    // load config file
    if (!existsSync(join(gamePath, './ere.config.json'))) {
      if (existsSync(join(staticPath, './_config.json'))) {
        try {
          JSON.parse(
            fuckOffUtf8Bom(
              readFileSync(join(staticPath, './_config.json'), 'utf-8'),
            ),
          );
          copyFileSync(
            join(staticPath, './_config.json'),
            join(gamePath, './ere.config.json'),
          );
        } catch (_) {
          // eslint-disable-next-line no-empty
        }
      } else {
        writeFileSync(
          join(gamePath, './ere.config.json'),
          JSON.stringify({ system: {}, window: {} }),
        );
      }
    }
    era.config = JSON.parse(
      fuckOffUtf8Bom(
        readFileSync(join(gamePath, './ere.config.json'), 'utf-8'),
      ),
    );
    getSuffix(era.config.system.static);
    era.fixedConfig = {};
    if (existsSync(join(staticPath, './_fixed.json'))) {
      try {
        era.fixedConfig = JSON.parse(
          fuckOffUtf8Bom(
            readFileSync(join(staticPath, './_fixed.json'), 'utf-8'),
          ),
        );
        Object.entries(era.fixedConfig).forEach(
          /** @param {[string, any]} kv */
          (kv) => {
            if (typeof kv[1] === 'object') {
              Object.entries(kv[1]).forEach((kv1) => {
                (era.config[kv[0]] || (era.config[kv[0]] = {}))[kv1[0]] =
                kv1[1];
              });
            } else {
              era.config[kv[0]] = kv[1];
            }
          },
        );
        writeFileSync(
          join(gamePath, './ere.config.json'),
          JSON.stringify(era.config),
        );
        getSuffix(era.config.system.static);
      } catch (e) {
        error(e.message, e.stack);
      }
    }
    resizeWindow();
    connect('setConfig', { config: era.config, fixed: era.fixedConfig });
    logger.info(
      `loaded game config in ${-(
        current - (current = new Date().getTime())
      )}ms`,
    );
    extendedCharaTables = {};
    (
      safelyGetConfigEntry(era.config, 'system.extendedCharaTables') || []
    ).forEach((e) => (extendedCharaTables[e.toLowerCase()] = 1));
    setProgressBar(0.01);

    // load static data
    let fileList = {};
    era.fieldNames = {};
    era.images = {};
    era.staticData = {
      relationship: {
        callname: {},
        relation: {},
      },
    };

    function loadPath(_path) {
      const l = readdirSync(_path);
      l.forEach((f) => {
        const filePath = join(_path, `./${f}`);
        if (statSync(filePath).isDirectory()) {
          loadPath(filePath);
        } else if (toLowerCase(extname(filePath)) === `.${suffix}`) {
          fileList[filePath] = toLowerCase(f.replace(/\..*$/, ''));
        }
      });
    }

    loadPath(staticPath);
    const normalDataList = [],
      charaDataList = [];
    const charaReg = staticFormatRegex[staticFormatPriority.indexOf(suffix)];
    // load _replace
    Object.keys(fileList).forEach((_path) => {
      const x = toLowerCase(_path);
      if (
        x.endsWith(`_replace.${suffix}`) &&
        safelyGetConfigEntry(era.config, 'system._replace')
      ) {
        const content_list = parseDataFile(
            fuckOffUtf8Bom(readFileSync(_path, 'utf-8')),
            suffix,
            '_replace',
          );
        era.staticData['_replace'] = {};
        content_list.forEach(
          (a) =>
            (era.staticData['_replace'][
              nameMapping['_replace'][a[0]]
                ? nameMapping['_replace'][a[0]]
                : a[0]
            ] = a[1]),
        );
      } else if (charaReg.test(x)) {
        charaDataList.push(_path);
      } else {
        normalDataList.push(_path);
      }
    });
    let showInfo = true;
    if (
      era.staticData['_replace'] &&
      era.staticData['_replace']['briefInformationOnLoading']
    ) {
      era.api.print(era.staticData['_replace']['briefInformationOnLoading']);
      showInfo = false;
    }
    // load normal data
    showInfo &&
    era.api.print(`loading static data files (from ${staticPath}) ...`);

    function generateUniqueKey(table, originKey, tableName, strKey) {
      let key = originKey;
      while (table[key]) {
        key++;
      }
      if (originKey !== key) {
        log(
            `[WARNING (ENGINE)]\n\tduplicate key in ${tableName}.${suffix}! index ${originKey} of ${strKey} has been allocated to ${table[originKey].n}! reset to ${key}`,
        );
      }
      return key;
    }

    setProgressBar(0.03);
    const phaseProgress = isResDirExists ? 0.32 : 0.48;

    extendedNormalTables = {};
    normalDataList.forEach((_path, i) => {
      const k = toLowerCase(fileList[_path]);
      let content_list;
      switch (k) {
        case 'variablesize':
        case 'str':
        case 'strname':
        case 'globals':
          log(`[WARNING (ENGINE)]\n\tdeprecated table: ${k}!`);
          break;
        case 'maxbase':
        case 'juel':
        case 'gotjuel':
        case 'nowex':
        case 'no':
        case 'static':
        case 'callname':
        case 'love':
        case 'relation':
          log(`[WARNING (ENGINE)]\n\tprotected table name: ${k}!`);
          break;
        case '_replace':
          break;
        default:
          if (
            k.endsWith('name') &&
            era.staticData[k.substring(0, k.length - 4)]
          ) {
            log(`[WARNING (ENGINE)]\n\tprotected table name: ${k}!`);
            break;
          }
          content_list = parseDataFile(
            fuckOffUtf8Bom(readFileSync(_path, 'utf-8')),
            suffix,
            k,
          );
          switch (k) {
            case '_rename':
            case 'gamebase':
              era.staticData[k] = {};
              content_list.forEach(
                (a) =>
                  (era.staticData[k][
                    nameMapping[k]
                      ? nameMapping.gamebase[a[0]]
                      : toLowerCase(a[0])
                  ] = a[1]),
              );
              break;
            default:
              content_list = content_list.map((a) => {
                return a.map(toLowerCase);
              });
              switch (k) {
                case 'palam':
                  era.staticData.juel = {};
                  era.fieldNames.juel = {};
                  content_list.forEach((a) => {
                    let numKey = a[0],
                      strKey = a[1];
                    numKey = generateUniqueKey(
                      era.fieldNames.juel,
                      numKey,
                      k,
                      strKey,
                    );
                    era.staticData.juel[strKey] = numKey;
                    era.fieldNames.juel[numKey] = {
                        n: strKey,
                        k: a[3],
                        t: a[4],
                      };
                  });
                  break;
                case 'item':
                  era.staticData.item = { name: {}, price: {} };
                  era.fieldNames[k] = {};
                  content_list.forEach((a) => {
                    let numKey = a[0],
                      strKey = a[1],
                      val = a[2];
                    numKey = generateUniqueKey(
                      era.fieldNames[k],
                      numKey,
                      k,
                      strKey,
                    );
                    era.staticData.item.name[strKey] = numKey;
                    era.staticData.item.price[numKey] = val;
                    era.fieldNames[k][numKey] = {
                        n: strKey,
                        k: a[3],
                        t: 'number',
                      };
                  });
                  break;
                default:
                  era.staticData[k] = {};
                  era.fieldNames[k] = {};
                  content_list.forEach((a) => {
                    let numKey = a[0],
                      strKey = a[1];
                    numKey = generateUniqueKey(
                      era.fieldNames[k],
                      numKey,
                      k,
                      strKey,
                    );
                    era.staticData[k][strKey] = numKey;
                    era.fieldNames[k][numKey] = { n: strKey, k: a[3], t: a[4] };
                  });
              }
          }
          showInfo && era.api.print(`loaded ${k}`);
      }
      setProgressBar(0.15 + (phaseProgress * (i + 1)) / normalDataList.length);
    });
    if (!era.staticData.gamebase) {
      era.api.print(`GameBase.${suffix}不存在！游戏数据载入失败！`);
      gameMain = undefined;
      return;
    }
    connect('setGameBase', era.staticData.gamebase);
    setIcon(join(gamePath, era.staticData.gamebase['icon'] || ''));
    setProgressBar(0.15 + phaseProgress);

    showInfo && era.api.print(`\nloading chara files (from ${csvPath}) ...`);
    era.staticData.chara = {};
     // load chara data
     charaDataList.forEach((_path, i) => {
      const tmp = {};
      let tableName, valueIndex, value;
      parseDataFile(
        fuckOffUtf8Bom(readFileSync(_path, 'utf-8')),
        suffix,
        'chara',
      ).forEach((a) => {
        a[0] = toLowerCase(a[0]);
        a[1] = toLowerCase(a[1]);
        tableName = safeUndefinedCheck(nameMapping.chara[a[0]], a[0]);
        if (
          tableName === 'item' ||
          tableName === 'flag' ||
          tableName === 'global' ||
          extendedNormalTables[tableName]
        ) {
          throw new Error(
            `角色定义中使用了错误的表名 ${tableName}（${a[0]}）！`,
          );
        }
        switch (a.length) {
          case 2:
            value = a[1];
            tmp[tableName] = value;
            break;
          case 3:
            valueIndex = a[1];
            value = a[2];
            if (tableName === 'relation' || tableName === 'callname') {
              era.staticData.relationship[tableName][
                `${tmp['id']}|${valueIndex}`
              ] = value;
            } else if (era.staticData[tableName]) {
              valueIndex = safeUndefinedCheck(
                era.staticData[tableName][valueIndex],
                a[1],
              );
              (tmp[tableName] || (tmp[tableName] = {}))[valueIndex] = value;
            } else {
              error(`no such chara table: ${tableName}!`);
            }
        }
      });
      if (tmp['id'] !== undefined) {
        if (!era.staticData.chara[tmp['id']]) {
          era.staticData.chara[tmp['id']] = tmp;
        } else {
          const charaTable = era.staticData.chara[tmp['id']];
          Object.keys(tmp).forEach((k) => {
            if (typeof tmp[k] === 'object') {
              if (!charaTable[k]) {
                charaTable[k] = tmp[k];
              } else {
                Object.keys(tmp[k]).forEach((k2) => {
                  charaTable[k][k2] = tmp[k][k2];
                });
              }
            } else {
              charaTable[k] = tmp[k];
            }
          });
        }
      }
      showInfo && era.api.print(`loaded ${relative(staticPath, _path)}`);
      setProgressBar(
        0.15 + phaseProgress + (phaseProgress * (i + 1)) / charaDataList.length,
      );
    });
    logger.info(
      `loaded csv files in ${-(current - (current = new Date().getTime()))}ms`,
    );
    setProgressBar(0.15 + phaseProgress * 2);

    // load resource csv
    if (isResDirExists) {
      fileList = {};
      loadPath(resPath);
      showInfo &&
        era.api.print(`\nloading resource files (from ${resPath}) ...`);
      Object.keys(fileList).forEach((_path, i) => {
        const parent = dirname(_path);
        let count = 0;
        parseDataFile(
            fuckOffUtf8Bom(readFileSync(_path, 'utf-8')),
            'csv',
          ).forEach((a, i) => {
          if (a[0]) {
            if (era.images[toLowerCase(a[0])]) {
              log(
                `[WARNING (ENGINE)]\n\tduplicate key ${toLowerCase(
                  a[0],
                )} of line ${i + 1} in ${_path}!`,
              );
            } else {
              let temp;
              if (a.length >= 2) {
                temp = era.images[toLowerCase(a[0])] = {
                  path: a[1].startsWith('http')
                    ? a[1]
                    : `file://${join(parent, a[1])}`,
                  x: 0,
                  y: 0,
                  width: 0,
                  height: 0,
                  posX: 0,
                  posY: 0,
                };
                if (a.length >= 4) {
                  temp.x = a[2];
                  temp.y = a[3];
                  if (a.length >= 6) {
                    temp.width = a[4];
                    temp.height = a[5];
                    if (a.length >= 8) {
                      temp.posX = a[6];
                      temp.posY = a[7];
                    }
                  }
                }
                if (!temp.width && temp.path.startsWith('file://')) {
                  try {
                    temp = getImageSize(temp.path.substring(7));
                    era.images[toLowerCase(a[0])].width = temp.width;
                    era.images[toLowerCase(a[0])].height = temp.height;
                  } catch (e) {
                    logger.error(e.message);
                  }
                }
              }
              if (era.images[toLowerCase(a[0])]) {
                count++;
              }
            }
          }
        });
        showInfo &&
          era.api.print(
            `loaded ${count} images from ${relative(resPath, _path)}`,
          );
        setProgressBar(
          0.15 +
            phaseProgress * 2 +
            (phaseProgress * (i + 1)) / charaDataList.length,
        );
      });
      logger.info(
        `loaded resources in ${-(
          current - (current = new Date().getTime())
        )}ms`,
      );
    } else {
      showInfo && era.api.print('no valid resource path...');
    }

    if (isDevelopment) {
      era.api.logStatic();
    }

    setProgressBar(0.99);

    // load ere
    let eraModule;
    try {
      showInfo && era.api.print(`\ngame loaded from ${gamePath} .`);

      const cache_dict = {},
        erePath = join(gamePath, 'ere');
      if (
        !existsSync(erePath) ||
        !lstatSync(erePath).isDirectory() ||
        !existsSync(join(erePath, './main.js'))
      ) {
        era.api.print('没有合法的ERE入口文件（ere/main.js）！游戏载入失败！');
        gameMain = undefined;
        return;
      }
      module.prototype.require = function (path) {
        let dir = path;
        const cache_flag = cache_dict[path];
        switch (cache_flag) {
          case 1:
            logger.warn('circular! requiring ' + path);
            break;
          case undefined:
            logger.debug('requiring ' + path);
            cache_dict[path] = 1;
        }
        if (path[0] === '.') {
          return include.call(this, path);
        }
        if (path[0] === '#') {
          dir = `${erePath}${path.substring(1)}`;
        } else if (!safeLibs[path]) {
          error(
            `游戏在试图加载内置库！请确认游戏来自安全的来源或了解游戏的行为！库名：${path}`,
          );
          // return {};
          return include.call(this, path);
        }
        let ret;
        try {
          ret = include.call(this, dir);
        } finally {
          if (cache_dict[path] === 1) {
            cache_dict[path] = 2;
          }
        }
        return ret;
      };

      // clear cache, load game, and find era module
      Object.keys(module['_cache']).forEach((e) => {
        var _module$_cache$e$expo;
        if (e.startsWith(oldPath) || (_module$_cache$e$expo = module['_cache'][e].exports) === null || _module$_cache$e$expo === void 0 ? void 0 : _module$_cache$e$expo.isEra) {
          // logger.debug('delete cache: ' + e);
          delete module['_cache'][e];
        }
      });
      eval("gameMain = require('#/main');");
      eraModule = Object.values(module['_cache']).find(m => {
        var _m$exports;
        return (_m$exports = m.exports) === null || _m$exports === void 0 ? void 0 : _m$exports.isEra;
      });
      oldPath = dirname(
        Object.keys(module['_cache']).filter(
          (x) => x.endsWith('ere/main.js') || x.endsWith('ere\\main.js'),
        )[0],
      );

      if (eraModule) {
        // inject era.api
        Object.keys(era.api).forEach((k) => {
          if (eraModule.exports[k]) {
            eraModule.exports[k]._s = true;
          } else {
            log(
              `[WARNING (ENGINE)] sdk doesn't have api ${k}! Please check the sdk version!`,
            );
          }
          eraModule.exports[k] = era.api[k];
          eraModule.exports[k]._s = true;
        });
        eraModule.exports.version.engine = "1.3.0";
      } else {
        gameMain = undefined;
        era.api.print(`游戏文件夹没有引入正确的ere sdk（era-electron.js）！`);
        return;
      }
      logger.info(
        `loaded ere from ${oldPath} in ${new Date().getTime() - current}ms`,
      );
      setProgressBar(1.0);
      connect('prepare');
      await era.api.printAndWait('\nloading success!');
    } catch (e) {
      error(e.message, e.stack);
    }
    setProgressBar(-1);

    era.restart();
  };

  if (isDevelopment) {
    era.debug = true;
    logger.debug(Object.keys(era.api).sort());
  }

  return era;
};


番号: id

角色编号: id

名前: name

姓名: name

呼び名: callname

默认称呼: callname

称呼: callname

基础属性: base

基礎: base

技能: abl

能力: abl

特性: talent

特质: talent

素質: talent

角色flag: cflag

フラグ: cflag

角色str: cstr

信赖: relation

相性: relation

装备: equip

装着物: equip

刻印: mark

经验: exp

経験: exp

珠: juel

作者: author

追加信息: info

追加情報: info

发布时间: year

製作年: year

游戏名称: title

タイトル: title

游戏标识: gameCode

コード: gameCode

初始角色编号: defaultChara

最初からいるキャラ: defaultChara

版本: version

バージョン: version

最低支持版本: allowVersion

バージョン違い認める: allowVersion

发布页: site

游戏图标: icon

版本检查: versionCheck

程序更新: baseZip

起動時簡略表示: briefInformationOnLoading


code:
const YAML = require('yamljs');

const { getNumber } = require('@/renderer/utils/value-utils');

/** @param {string} content */
function parseCSV(content) {
  const toParsed = content.replace(/\s*;[^\n]*/g, '');
  const ret = [];
  for (const line of toParsed.split('\n')) {
    const arr = line
      .split(',')
      .map((x) => getNumber(x.replace(/(^\s+|\s+$)/, '')));
    if (arr.length > 1) {
      ret.push(arr);
    }
  }
  return ret;
}

/**
 * @param {Record<string,any>} content
 * @param {string} table
 * @returns {*[][]}
 */
function parseObject(content, table) {
  const ret = [];
  switch (table) {
    case '_rename':
    case '_replace':
    case 'gamebase':
      Object.entries(content).forEach((e) => ret.push(e));
      break;
    case 'chara':
      Object.entries(content).forEach((e0) => {
        if (e0[1] instanceof Object) {
          Object.entries(e0[1]).forEach((e1) => {
            ret.push([e0[0], ...e1]);
          });
        } else {
          ret.push(e0);
        }
      });
      break;
    default:
      Object.entries(content).forEach((e) =>
        ret.push([e[1].id, e[0], e[1].price, e[1].name, e[1].type]),
      );
  }
  return ret.map((entry) =>
    entry.map((e) => (typeof e === 'string' ? getNumber(e) : e)),
  );
}

/**
 * @param {string} content
 * @param {string} format
 * @param {string} [table]
 * @returns {*[][]}
 */
function parseDataFile(content, format, table) {
  switch (format) {
    case 'csv':
      return parseCSV(content);
    case 'json':
      try {
        return parseObject(JSON.parse(content), table);
      } catch (_) {
        return parseObject({}, table);
      }
    case 'yml':
      return parseObject(YAML.parse(content) || {}, table);
  }
}

module.exports = parseDataFile;


code:
var cordova = require('cordova-bridge');

var f = function (){
    var listeners = {
        listeners: {}
    };
    cordova.channel.on("ipc", function(msg){
        var key = msg.key;
        if (listeners.listeners[key]){
            for (var i = 0; i < listeners.listeners[key].length; i++){
                listeners.listeners[key][i](msg.key, msg.msg);
            }
        }
    });
    return {
        on: (key, cb) => {
            if (!(listeners.listeners[key])){
                listeners.listeners[key] = [cb];
            } else {
                listeners.listeners[key].push(cb);
            }
        },
        send: (key, msg) => {
            cordova.channel.post("ipc", {key: key, msg: msg});
        },
        removeAllListeners: function (arg){
            if (arg == undefined){
                listeners.listeners = {};
            } else {
                listeners.listeners[arg] = [];
            }
        }
    }
}

module.exports = {
    ipcMain: f()
}


code:
const modules = require('module');
const cordova = require('cordova-bridge');
const include = modules.prototype.require;

const { createHash } = require('crypto');

const zl = require("zip-lib");
const {v4: uuidv4} = require("uuid");
const yauzl = require("yauzl");

const { join } = require("path");

var fs = require("fs");
fs.rmSync = fs.unlinkSync;

require("core-js");

modules.prototype.require = function (path) {
    if (path.slice(0, 2) === '@/') {
        return include.call(this, join(__dirname, "."+path.slice(1)))
    }
    return include.call(this, path);
};

const oldconsole = console;
console = new Proxy({}, {
    get(target, name) {
        return function(...args){
            var newarr = args.map((m) => {
                try {
                    JSON.stringify(m);
                    return m;
                } catch (e) {
                    return m + "";
                }
            });
            cordova.channel.post("console", [name, newarr]);
            return oldconsole[name](...args);
        }
    }
});

const { createWindow, error } = require('android');
const { DataAnalysis } = require('@element-plus/icons-vue');

function registerHelperFunction(eraArray){
    cordova.channel.on("helper", function(data){
        switch (data.action){
            case "reload":
                if (eraArray[0]){
                    eraArray[0].restart();
                }
                break;
            case "exit":
                if (eraArray[0]){
                    eraArray[0].api.quit();
                }
                break;
            case "save":
                if (!data.id) break;
                async function saveExport(id){
                    function helper(){
                        return new Promise((resolve, reject) => {
                            var uuid = uuidv4();
                            zl.archiveFolder(join(__dirname, "../save"), join(__dirname, uuid + ".zip")).then(() => resolve(join(__dirname, uuid + ".zip")), reject)
                        });
                    }
                    var filename = await helper();
                    function helper2(){
                        return new Promise((resolve, reject) => {
                            var fileContent = fs.readFileSync(filename);
                            var base64String = fileContent.toString("base64");
                            var uuid = uuidv4();
                            cordova.channel.on("save_file", function(msg){
                                if (msg.id == uuid){
                                    resolve(msg.data);
                                }
                            });
                            cordova.channel.post("save_file", {mime: "application/zip", id: uuid, content: base64String, name: `${(new Date().toJSON().slice(0,19)).replaceAll(":", "_")}_sav.zip`, });
                        });
                    }
                    var result = await helper2();
                    cordova.channel.post("helper", {id: id, ret: result});
                    fs.unlinkSync(filename);
                }
                saveExport(data.id);
                break;
            case "import":
                if (!data.id) break;
                async function importSave(req_id){
                    var uuid = uuidv4();
                    function helper3() {
                        return new Promise((resolve, reject) => {
                            cordova.channel.on("open_file", function (data) {
                                if (data.id == uuid) {
                                    resolve(data.data);
                                }
                            });
                            cordova.channel.post("open_file", {
                                id: uuid,
                                accept: "application/zip"
                            });
                        });
                    }
                    var data = await helper3();
                    if (data.code == -1) {
                        cordova.channel.post("helper", {id: req_id, ret: {code: 0, code2: 1}});
                        return;
                    } else if (data.code == 1) {
                        cordova.channel.post("helper", {id: req_id, ret: {code: 1, reason: data.reason}});
                        return;
                    } else {
                        function wrap(...args) {
                            return new Promise((resolve, reject) => {
                                yauzl.open(...args, function (err, zipfile) {
                                    if (err) {
                                        reject(err);
                                    } else {
                                        resolve(zipfile);
                                    }
                                });
                            });
                        }
                        var uuid = uuidv4();
                        while (fs.existsSync(join(__dirname, uuid + ".zip"))) {
                            uuid = uuidv4();
                        }
                        fs.writeFileSync(join(__dirname, uuid + ".zip"), data.data, {
                            encoding: 'base64'
                        });
                        try {
                            var zipfile = await wrap(join(__dirname, uuid + ".zip"), {
                                lazyEntries: true
                            });
                            fs.rmdirSync(join(__dirname, "../save"), {
                                recursive: true
                            });
                            fs.mkdirSync(join(__dirname, "../save"));
                            var processCount = 0;
                            function unzipfunc() {
                                return new Promise((resolve, reject) => {
                                    zipfile.once("end", function () {
                                        zipfile.close();
                                        resolve(join(__dirname, uuid + ".zip"));
                                    });
                                    zipfile.on("entry", function (entry) {
                                        console.log("ZIP: " + entry);
                                        if (/\/$/.test(entry.fileName)) {
                                            zipfile.readEntry();
                                        } else {
                                            zipfile.openReadStream(entry, function (err, readStream) {
                                                if (err) reject(err);
                                                var writeStream = fs.createWriteStream(join(__dirname, "../save", entry.fileName));
                                                processCount++;
                                                readStream.on("end", function () {
                                                    zipfile.readEntry();
                                                    writeStream.close();
                                                    processCount--;
                                                });
                                                readStream.pipe(writeStream);
                                            });
                                        }
                                    });
                                    zipfile.readEntry();
                                });
                            }
                            function waitProcess() {
                                return new Promise((resolve, reject) => {
                                    function test() {
                                        if (processCount == 0) {
                                            resolve();
                                        } else {
                                            setTimeout(test, 10);
                                        }
                                    }
                                    test();
                                });
                            }
                            await unzipfunc();
                            await waitProcess();
                            cordova.channel.post("helper", {id: req_id, ret: {code: 0, code2: 0}});
                            return;
                        } catch (e){
                            cordova.channel.post("helper", {id: req_id, ret: {code: 1, reason: e.toString()}});
                            console.error(e);
                        }
                    }
                }
                importSave(data.id).then((filename) => {
                    fs.unlinkSync(filename);
                });
                break;
            case "clear":
                if (!data.id) break;
                try {
                    fs.rmdirSync(join(__dirname, "../save"), {recursive: true});
                    fs.mkdirSync(join(__dirname, "../save"));
                    cordova.channel.post("helper", {id: data.id, ret: {code: 0}});
                } catch (e) {
                    cordova.channel.post("helper", {id: data.id, ret: {code: 1, reason: e.toString()}});
                }
        }
    });
}

cordova.channel.on('message', function (msg) {
    if (msg.msg == "load") {
        if (!fs.existsSync(join(__dirname, "game/passwd"))){
            cordova.channel.post('goforward', {msg: ""});
        } else {
            var tocheck = !fs.existsSync(join(__dirname, "../lastpwd"));
            if (!tocheck){
                var lastpwd = fs.readFileSync(join(__dirname, "../lastpwd"), 'utf8');
                var topwd = fs.readFileSync(join(__dirname, "game/passwd"), "utf8");
                var hashpwd = createHash('sha256').update(lastpwd).digest('hex');
                tocheck = (hashpwd != topwd);
            }
            if (!tocheck){
                cordova.channel.post('goforward', {msg: ""});
            } else {
                cordova.channel.post('requirepwd', {msg: ""});
            }
        }
    } else if (msg.msg == "validate"){
        var key = msg.cb;
        var receivePas = msg.pass;
        var firstHash = createHash('sha256').update(receivePas).digest('hex');
        fs.writeFileSync(join(__dirname, "../lastpwd"), firstHash);
        var secondHash = createHash('sha256').update(firstHash).digest('hex');
        var compare = fs.readFileSync(join(__dirname, "game/passwd"), "utf8");
        var flag = secondHash == compare;
        cordova.channel.post("passwordcb", {
            key: key,
            res: flag
        });
        if (flag){
            cordova.channel.post('goforward', {msg: ""});
        }
    } else if (msg.msg == "prepared"){
        try {
            var refer = [];
            registerHelperFunction(refer);
            cordova.channel.post("helper_init", "helper");
            createWindow(refer).then((era) => era.start()).then((e) => {
                cordova.channel.post("helper_init", "era");
            }).catch(errors => {
                cordova.channel.post("error", errors.stack);
                error(errors);
            });
            console.log("PASS");
        } catch (e){
            cordova.channel.post("error", e.stack);
        }
    }
});

try {
    cordova.channel.on('eval', function(code){
        var result = eval(code);
        try {
            cordova.channel.post("eval", result);
        } catch (error){
            cordova.channel.post("eval", result + "");
        }
    });
} catch (error) {
    cordova.channel.post("error", error.stack);
}


code:
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")

case `uname` in
    *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
esac

if [ -x "$basedir/node" ]; then
  exec "$basedir/node"  "$basedir/../acorn/bin/acorn" "$@"
else
  exec node  "$basedir/../acorn/bin/acorn" "$@"
fi


code:
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0

IF EXIST "%dp0%\node.exe" (
  SET "_prog=%dp0%\node.exe"
) ELSE (
  SET "_prog=node"
  SET PATHEXT=%PATHEXT:;.JS;=;%
)

endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\..\acorn\bin\acorn" %*


code:
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent

$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
  # Fix case when both the Windows and Linux builds of Node
  # are installed in the same directory
  $exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "$basedir/node$exe"  "$basedir/../acorn/bin/acorn" $args
  } else {
    & "$basedir/node$exe"  "$basedir/../acorn/bin/acorn" $args
  }
  $ret=$LASTEXITCODE
} else {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "node$exe"  "$basedir/../acorn/bin/acorn" $args
  } else {
    & "node$exe"  "$basedir/../acorn/bin/acorn" $args
  }
  $ret=$LASTEXITCODE
}
exit $ret


code:
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")

case `uname` in
    *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
esac

if [ -x "$basedir/node" ]; then
  exec "$basedir/node"  "$basedir/../ansi-html-community/bin/ansi-html" "$@"
else
  exec node  "$basedir/../ansi-html-community/bin/ansi-html" "$@"
fi


code:
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0

IF EXIST "%dp0%\node.exe" (
  SET "_prog=%dp0%\node.exe"
) ELSE (
  SET "_prog=node"
  SET PATHEXT=%PATHEXT:;.JS;=;%
)

endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\..\ansi-html-community\bin\ansi-html" %*


code:
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent

$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
  # Fix case when both the Windows and Linux builds of Node
  # are installed in the same directory
  $exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "$basedir/node$exe"  "$basedir/../ansi-html-community/bin/ansi-html" $args
  } else {
    & "$basedir/node$exe"  "$basedir/../ansi-html-community/bin/ansi-html" $args
  }
  $ret=$LASTEXITCODE
} else {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "node$exe"  "$basedir/../ansi-html-community/bin/ansi-html" $args
  } else {
    & "node$exe"  "$basedir/../ansi-html-community/bin/ansi-html" $args
  }
  $ret=$LASTEXITCODE
}
exit $ret


code:
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")

case `uname` in
    *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
esac

if [ -x "$basedir/node" ]; then
  exec "$basedir/node"  "$basedir/../autoprefixer/bin/autoprefixer" "$@"
else
  exec node  "$basedir/../autoprefixer/bin/autoprefixer" "$@"
fi


code:
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0

IF EXIST "%dp0%\node.exe" (
  SET "_prog=%dp0%\node.exe"
) ELSE (
  SET "_prog=node"
  SET PATHEXT=%PATHEXT:;.JS;=;%
)

endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\..\autoprefixer\bin\autoprefixer" %*


code:
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent

$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
  # Fix case when both the Windows and Linux builds of Node
  # are installed in the same directory
  $exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "$basedir/node$exe"  "$basedir/../autoprefixer/bin/autoprefixer" $args
  } else {
    & "$basedir/node$exe"  "$basedir/../autoprefixer/bin/autoprefixer" $args
  }
  $ret=$LASTEXITCODE
} else {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "node$exe"  "$basedir/../autoprefixer/bin/autoprefixer" $args
  } else {
    & "node$exe"  "$basedir/../autoprefixer/bin/autoprefixer" $args
  }
  $ret=$LASTEXITCODE
}
exit $ret


code:
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")

case `uname` in
    *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
esac

if [ -x "$basedir/node" ]; then
  exec "$basedir/node"  "$basedir/../browserslist/cli.js" "$@"
else
  exec node  "$basedir/../browserslist/cli.js" "$@"
fi


code:
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0

IF EXIST "%dp0%\node.exe" (
  SET "_prog=%dp0%\node.exe"
) ELSE (
  SET "_prog=node"
  SET PATHEXT=%PATHEXT:;.JS;=;%
)

endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\..\browserslist\cli.js" %*


code:
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent

$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
  # Fix case when both the Windows and Linux builds of Node
  # are installed in the same directory
  $exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "$basedir/node$exe"  "$basedir/../browserslist/cli.js" $args
  } else {
    & "$basedir/node$exe"  "$basedir/../browserslist/cli.js" $args
  }
  $ret=$LASTEXITCODE
} else {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "node$exe"  "$basedir/../browserslist/cli.js" $args
  } else {
    & "node$exe"  "$basedir/../browserslist/cli.js" $args
  }
  $ret=$LASTEXITCODE
}
exit $ret


code:
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")

case `uname` in
    *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
esac

if [ -x "$basedir/node" ]; then
  exec "$basedir/node"  "$basedir/../cssesc/bin/cssesc" "$@"
else
  exec node  "$basedir/../cssesc/bin/cssesc" "$@"
fi


code:
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0

IF EXIST "%dp0%\node.exe" (
  SET "_prog=%dp0%\node.exe"
) ELSE (
  SET "_prog=node"
  SET PATHEXT=%PATHEXT:;.JS;=;%
)

endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\..\cssesc\bin\cssesc" %*


code:
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent

$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
  # Fix case when both the Windows and Linux builds of Node
  # are installed in the same directory
  $exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "$basedir/node$exe"  "$basedir/../cssesc/bin/cssesc" $args
  } else {
    & "$basedir/node$exe"  "$basedir/../cssesc/bin/cssesc" $args
  }
  $ret=$LASTEXITCODE
} else {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "node$exe"  "$basedir/../cssesc/bin/cssesc" $args
  } else {
    & "node$exe"  "$basedir/../cssesc/bin/cssesc" $args
  }
  $ret=$LASTEXITCODE
}
exit $ret


code:
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")

case `uname` in
    *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
esac

if [ -x "$basedir/node" ]; then
  exec "$basedir/node"  "$basedir/../eslint/bin/eslint.js" "$@"
else
  exec node  "$basedir/../eslint/bin/eslint.js" "$@"
fi


code:
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")

case `uname` in
    *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
esac

if [ -x "$basedir/node" ]; then
  exec "$basedir/node"  "$basedir/../eslint-config-prettier/bin/cli.js" "$@"
else
  exec node  "$basedir/../eslint-config-prettier/bin/cli.js" "$@"
fi


code:
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0

IF EXIST "%dp0%\node.exe" (
  SET "_prog=%dp0%\node.exe"
) ELSE (
  SET "_prog=node"
  SET PATHEXT=%PATHEXT:;.JS;=;%
)

endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\..\eslint-config-prettier\bin\cli.js" %*


code:
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent

$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
  # Fix case when both the Windows and Linux builds of Node
  # are installed in the same directory
  $exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "$basedir/node$exe"  "$basedir/../eslint-config-prettier/bin/cli.js" $args
  } else {
    & "$basedir/node$exe"  "$basedir/../eslint-config-prettier/bin/cli.js" $args
  }
  $ret=$LASTEXITCODE
} else {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "node$exe"  "$basedir/../eslint-config-prettier/bin/cli.js" $args
  } else {
    & "node$exe"  "$basedir/../eslint-config-prettier/bin/cli.js" $args
  }
  $ret=$LASTEXITCODE
}
exit $ret


code:
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0

IF EXIST "%dp0%\node.exe" (
  SET "_prog=%dp0%\node.exe"
) ELSE (
  SET "_prog=node"
  SET PATHEXT=%PATHEXT:;.JS;=;%
)

endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\..\eslint\bin\eslint.js" %*


code:
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent

$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
  # Fix case when both the Windows and Linux builds of Node
  # are installed in the same directory
  $exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "$basedir/node$exe"  "$basedir/../eslint/bin/eslint.js" $args
  } else {
    & "$basedir/node$exe"  "$basedir/../eslint/bin/eslint.js" $args
  }
  $ret=$LASTEXITCODE
} else {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "node$exe"  "$basedir/../eslint/bin/eslint.js" $args
  } else {
    & "node$exe"  "$basedir/../eslint/bin/eslint.js" $args
  }
  $ret=$LASTEXITCODE
}
exit $ret


code:
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")

case `uname` in
    *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
esac

if [ -x "$basedir/node" ]; then
  exec "$basedir/node"  "$basedir/../flat/cli.js" "$@"
else
  exec node  "$basedir/../flat/cli.js" "$@"
fi


code:
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0

IF EXIST "%dp0%\node.exe" (
  SET "_prog=%dp0%\node.exe"
) ELSE (
  SET "_prog=node"
  SET PATHEXT=%PATHEXT:;.JS;=;%
)

endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\..\flat\cli.js" %*


code:
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent

$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
  # Fix case when both the Windows and Linux builds of Node
  # are installed in the same directory
  $exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "$basedir/node$exe"  "$basedir/../flat/cli.js" $args
  } else {
    & "$basedir/node$exe"  "$basedir/../flat/cli.js" $args
  }
  $ret=$LASTEXITCODE
} else {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "node$exe"  "$basedir/../flat/cli.js" $args
  } else {
    & "node$exe"  "$basedir/../flat/cli.js" $args
  }
  $ret=$LASTEXITCODE
}
exit $ret


code:
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")

case `uname` in
    *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
esac

if [ -x "$basedir/node" ]; then
  exec "$basedir/node"  "$basedir/../he/bin/he" "$@"
else
  exec node  "$basedir/../he/bin/he" "$@"
fi


code:
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0

IF EXIST "%dp0%\node.exe" (
  SET "_prog=%dp0%\node.exe"
) ELSE (
  SET "_prog=node"
  SET PATHEXT=%PATHEXT:;.JS;=;%
)

endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\..\he\bin\he" %*


code:
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent

$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
  # Fix case when both the Windows and Linux builds of Node
  # are installed in the same directory
  $exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "$basedir/node$exe"  "$basedir/../he/bin/he" $args
  } else {
    & "$basedir/node$exe"  "$basedir/../he/bin/he" $args
  }
  $ret=$LASTEXITCODE
} else {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "node$exe"  "$basedir/../he/bin/he" $args
  } else {
    & "node$exe"  "$basedir/../he/bin/he" $args
  }
  $ret=$LASTEXITCODE
}
exit $ret


code:
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")

case `uname` in
    *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
esac

if [ -x "$basedir/node" ]; then
  exec "$basedir/node"  "$basedir/../cli-highlight/bin/highlight" "$@"
else
  exec node  "$basedir/../cli-highlight/bin/highlight" "$@"
fi


code:
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0

IF EXIST "%dp0%\node.exe" (
  SET "_prog=%dp0%\node.exe"
) ELSE (
  SET "_prog=node"
  SET PATHEXT=%PATHEXT:;.JS;=;%
)

endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\..\cli-highlight\bin\highlight" %*


code:
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent

$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
  # Fix case when both the Windows and Linux builds of Node
  # are installed in the same directory
  $exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "$basedir/node$exe"  "$basedir/../cli-highlight/bin/highlight" $args
  } else {
    & "$basedir/node$exe"  "$basedir/../cli-highlight/bin/highlight" $args
  }
  $ret=$LASTEXITCODE
} else {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "node$exe"  "$basedir/../cli-highlight/bin/highlight" $args
  } else {
    & "node$exe"  "$basedir/../cli-highlight/bin/highlight" $args
  }
  $ret=$LASTEXITCODE
}
exit $ret


code:
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")

case `uname` in
    *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
esac

if [ -x "$basedir/node" ]; then
  exec "$basedir/node"  "$basedir/../html-minifier-terser/cli.js" "$@"
else
  exec node  "$basedir/../html-minifier-terser/cli.js" "$@"
fi


code:
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0

IF EXIST "%dp0%\node.exe" (
  SET "_prog=%dp0%\node.exe"
) ELSE (
  SET "_prog=node"
  SET PATHEXT=%PATHEXT:;.JS;=;%
)

endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\..\html-minifier-terser\cli.js" %*


code:
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent

$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
  # Fix case when both the Windows and Linux builds of Node
  # are installed in the same directory
  $exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "$basedir/node$exe"  "$basedir/../html-minifier-terser/cli.js" $args
  } else {
    & "$basedir/node$exe"  "$basedir/../html-minifier-terser/cli.js" $args
  }
  $ret=$LASTEXITCODE
} else {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "node$exe"  "$basedir/../html-minifier-terser/cli.js" $args
  } else {
    & "node$exe"  "$basedir/../html-minifier-terser/cli.js" $args
  }
  $ret=$LASTEXITCODE
}
exit $ret


code:
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")

case `uname` in
    *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
esac

if [ -x "$basedir/node" ]; then
  exec "$basedir/node"  "$basedir/../husky/bin.js" "$@"
else
  exec node  "$basedir/../husky/bin.js" "$@"
fi


code:
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0

IF EXIST "%dp0%\node.exe" (
  SET "_prog=%dp0%\node.exe"
) ELSE (
  SET "_prog=node"
  SET PATHEXT=%PATHEXT:;.JS;=;%
)

endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\..\husky\bin.js" %*


code:
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent

$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
  # Fix case when both the Windows and Linux builds of Node
  # are installed in the same directory
  $exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "$basedir/node$exe"  "$basedir/../husky/bin.js" $args
  } else {
    & "$basedir/node$exe"  "$basedir/../husky/bin.js" $args
  }
  $ret=$LASTEXITCODE
} else {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "node$exe"  "$basedir/../husky/bin.js" $args
  } else {
    & "node$exe"  "$basedir/../husky/bin.js" $args
  }
  $ret=$LASTEXITCODE
}
exit $ret


code:
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")

case `uname` in
    *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
esac

if [ -x "$basedir/node" ]; then
  exec "$basedir/node"  "$basedir/../image-size/bin/image-size.js" "$@"
else
  exec node  "$basedir/../image-size/bin/image-size.js" "$@"
fi


code:
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0

IF EXIST "%dp0%\node.exe" (
  SET "_prog=%dp0%\node.exe"
) ELSE (
  SET "_prog=node"
  SET PATHEXT=%PATHEXT:;.JS;=;%
)

endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\..\image-size\bin\image-size.js" %*


code:
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent

$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
  # Fix case when both the Windows and Linux builds of Node
  # are installed in the same directory
  $exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "$basedir/node$exe"  "$basedir/../image-size/bin/image-size.js" $args
  } else {
    & "$basedir/node$exe"  "$basedir/../image-size/bin/image-size.js" $args
  }
  $ret=$LASTEXITCODE
} else {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "node$exe"  "$basedir/../image-size/bin/image-size.js" $args
  } else {
    & "node$exe"  "$basedir/../image-size/bin/image-size.js" $args
  }
  $ret=$LASTEXITCODE
}
exit $ret


code:
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")

case `uname` in
    *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
esac

if [ -x "$basedir/node" ]; then
  exec "$basedir/node"  "$basedir/../is-ci/bin.js" "$@"
else
  exec node  "$basedir/../is-ci/bin.js" "$@"
fi


code:
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0

IF EXIST "%dp0%\node.exe" (
  SET "_prog=%dp0%\node.exe"
) ELSE (
  SET "_prog=node"
  SET PATHEXT=%PATHEXT:;.JS;=;%
)

endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\..\is-ci\bin.js" %*


code:
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent

$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
  # Fix case when both the Windows and Linux builds of Node
  # are installed in the same directory
  $exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "$basedir/node$exe"  "$basedir/../is-ci/bin.js" $args
  } else {
    & "$basedir/node$exe"  "$basedir/../is-ci/bin.js" $args
  }
  $ret=$LASTEXITCODE
} else {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "node$exe"  "$basedir/../is-ci/bin.js" $args
  } else {
    & "node$exe"  "$basedir/../is-ci/bin.js" $args
  }
  $ret=$LASTEXITCODE
}
exit $ret


code:
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")

case `uname` in
    *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
esac

if [ -x "$basedir/node" ]; then
  exec "$basedir/node"  "$basedir/../is-docker/cli.js" "$@"
else
  exec node  "$basedir/../is-docker/cli.js" "$@"
fi


code:
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0

IF EXIST "%dp0%\node.exe" (
  SET "_prog=%dp0%\node.exe"
) ELSE (
  SET "_prog=node"
  SET PATHEXT=%PATHEXT:;.JS;=;%
)

endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\..\is-docker\cli.js" %*


code:
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent

$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
  # Fix case when both the Windows and Linux builds of Node
  # are installed in the same directory
  $exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "$basedir/node$exe"  "$basedir/../is-docker/cli.js" $args
  } else {
    & "$basedir/node$exe"  "$basedir/../is-docker/cli.js" $args
  }
  $ret=$LASTEXITCODE
} else {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "node$exe"  "$basedir/../is-docker/cli.js" $args
  } else {
    & "node$exe"  "$basedir/../is-docker/cli.js" $args
  }
  $ret=$LASTEXITCODE
}
exit $ret