"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var firmware_exports = {};
__export(firmware_exports, {
  extractFirmware: () => extractFirmware,
  guessFirmwareFileFormat: () => guessFirmwareFileFormat,
  tryUnzipFirmwareFile: () => tryUnzipFirmwareFile
});
module.exports = __toCommonJS(firmware_exports);
var import_shared = require("@zwave-js/shared");
var import_fflate = require("fflate");
var import_crypto = require("../crypto/index.js");
var import_ZWaveError = require("../error/ZWaveError.js");
var import_crc = require("./crc.js");
var import_nrf_intel_hex = __toESM(require("nrf-intel-hex"), 1);
const MemoryMap = import_nrf_intel_hex.default;
const firmwareIndicators = {
  // All aeotec updater exes contain this text
  aeotec: import_shared.Bytes.from("Zensys.ZWave", "utf8"),
  // This seems to be the standard beginning of a gecko bootloader firmware
  gecko: 3944195587,
  // Encrypted HEC firmware files
  hec: import_shared.Bytes.from("HSENC2", "ascii")
};
function guessFirmwareFileFormat(filename, rawData) {
  filename = filename.toLowerCase();
  const rawBuffer = import_shared.Bytes.view(rawData);
  if (filename.endsWith(".bin")) {
    return "bin";
  } else if ((filename.endsWith(".exe") || filename.endsWith(".ex_")) && rawBuffer.includes(firmwareIndicators.aeotec)) {
    return "aeotec";
  } else if (/\.(hex|ota|otz)$/.test(filename)) {
    return filename.slice(-3);
  } else if (filename.endsWith(".gbl") && rawBuffer.readUInt32BE(0) === firmwareIndicators.gecko) {
    return "gecko";
  } else if (filename.endsWith(".hec") && rawBuffer.subarray(0, firmwareIndicators.hec.length).equals(firmwareIndicators.hec)) {
    return "hec";
  }
  throw new import_ZWaveError.ZWaveError("Could not detect firmware format", import_ZWaveError.ZWaveErrorCodes.Invalid_Firmware_File);
}
__name(guessFirmwareFileFormat, "guessFirmwareFileFormat");
function tryUnzipFirmwareFile(zipData) {
  const unzipped = (0, import_fflate.unzipSync)(zipData, {
    filter: /* @__PURE__ */ __name((file) => {
      return /\.(hex|exe|ex_|ota|otz|hec|gbl|bin)$/.test(file.name);
    }, "filter")
  });
  if (Object.keys(unzipped).length === 1) {
    const filename = Object.keys(unzipped)[0];
    const rawData = unzipped[filename];
    try {
      const format = guessFirmwareFileFormat(filename, rawData);
      return { filename, format, rawData };
    } catch {
      return;
    }
  }
}
__name(tryUnzipFirmwareFile, "tryUnzipFirmwareFile");
async function extractFirmware(rawData, format) {
  switch (format) {
    case "aeotec":
      return extractFirmwareAeotec(rawData);
    case "otz":
    case "ota":
      if (rawData.every((b) => b <= 127)) {
        try {
          return extractFirmwareHEX(rawData);
        } catch (e) {
          if (e instanceof import_ZWaveError.ZWaveError && e.code === import_ZWaveError.ZWaveErrorCodes.Argument_Invalid) {
          } else {
            throw e;
          }
        }
      }
      return extractFirmwareRAW(rawData);
    case "hex":
      return extractFirmwareHEX(rawData);
    case "hec":
      return extractFirmwareHEC(rawData);
    case "gecko":
      return extractFirmwareRAW(rawData);
    case "bin":
      return extractFirmwareRAW(rawData);
  }
}
__name(extractFirmware, "extractFirmware");
function extractFirmwareRAW(data) {
  return { data };
}
__name(extractFirmwareRAW, "extractFirmwareRAW");
function extractFirmwareAeotec(data) {
  const buffer = import_shared.Bytes.view(data);
  if (buffer.readUInt16BE(0) !== 19802) {
    throw new import_ZWaveError.ZWaveError("This does not appear to be a valid Aeotec updater (not an executable)!", import_ZWaveError.ZWaveErrorCodes.Argument_Invalid);
  }
  const firmwareStart = buffer.readUInt32BE(buffer.length - 8);
  const firmwareLength = buffer.readUInt32BE(buffer.length - 4);
  let numControlBytes = 8;
  if (buffer.includes(import_shared.Bytes.from("ImageCalcCrc16", "ascii"))) {
    numControlBytes += 2;
  }
  switch (true) {
    case firmwareStart + firmwareLength === buffer.length - 256 - numControlBytes:
      break;
    case firmwareStart + firmwareLength === buffer.length - 256 - 8:
      numControlBytes = 8;
      break;
    default:
      throw new import_ZWaveError.ZWaveError("This does not appear to be a valid Aeotec updater (invalid firmware length)!", import_ZWaveError.ZWaveErrorCodes.Argument_Invalid);
  }
  const firmwareData = buffer.subarray(firmwareStart, firmwareStart + firmwareLength);
  const firmwareNameBytes = buffer.subarray(buffer.length - 256 - numControlBytes).subarray(0, 256);
  if (numControlBytes === 10) {
    const checksum = buffer.readUInt16BE(buffer.length - 10);
    const actualChecksum = (0, import_crc.CRC16_CCITT)(import_shared.Bytes.concat([firmwareData, firmwareNameBytes]), 65173);
    if (checksum !== actualChecksum) {
      throw new import_ZWaveError.ZWaveError("This does not appear to be a valid Aeotec updater (invalid checksum)!", import_ZWaveError.ZWaveErrorCodes.Argument_Invalid);
    }
  }
  const firmwareTarget = firmwareNameBytes[0] < 32 ? firmwareNameBytes[0] : void 0;
  const firmwareNameOffset = firmwareTarget == void 0 ? 0 : 1;
  const firmwareName = firmwareNameBytes.subarray(firmwareNameOffset, firmwareNameBytes.indexOf(0, firmwareNameOffset)).toString("utf8");
  if (!/^[a-zA-Z0-9_ -]+$/.test(firmwareName)) {
    throw new import_ZWaveError.ZWaveError("This does not appear to be a valid Aeotec updater (invalid firmware name)!", import_ZWaveError.ZWaveErrorCodes.Argument_Invalid);
  }
  const ret = {
    data: firmwareData
  };
  if (firmwareTarget != void 0) {
    ret.firmwareTarget = firmwareTarget;
  }
  if (/__TargetZwave__/.test(firmwareName)) {
    ret.firmwareTarget = 0;
  } else {
    const match = /__TargetMcu(\d)__/.exec(firmwareName);
    if (match)
      ret.firmwareTarget = +match[1];
  }
  return ret;
}
__name(extractFirmwareAeotec, "extractFirmwareAeotec");
function extractFirmwareHEX(dataHEX) {
  try {
    if ((0, import_shared.isUint8Array)(dataHEX)) {
      dataHEX = import_shared.Bytes.view(dataHEX).toString("ascii");
    }
    const memMap = MemoryMap.fromHex(dataHEX);
    let data = new import_shared.Bytes();
    for (const [offset, chunk] of memMap.entries()) {
      data = import_shared.Bytes.concat([
        data,
        import_shared.Bytes.alloc(offset - data.length, 255),
        chunk
      ]);
    }
    return { data };
  } catch (e) {
    if (/Malformed/.test((0, import_shared.getErrorMessage)(e))) {
      throw new import_ZWaveError.ZWaveError("Could not parse HEX firmware file!", import_ZWaveError.ZWaveErrorCodes.Argument_Invalid);
    } else {
      throw e;
    }
  }
}
__name(extractFirmwareHEX, "extractFirmwareHEX");
async function extractFirmwareHEC(data) {
  const key = "d7a68def0f4a1241940f6cb8017121d15f0e2682e258c9f7553e706e834923b7";
  const iv = "0e6519297530583708612a2823663844";
  const ciphertext = import_shared.Bytes.from(import_shared.Bytes.view(data.subarray(6)).toString("ascii"), "base64");
  const plaintext = import_shared.Bytes.view(await (0, import_crypto.decryptAES256CBC)(ciphertext, import_shared.Bytes.from(key, "hex"), import_shared.Bytes.from(iv, "hex"))).toString("ascii").replaceAll(" ", "\n");
  return extractFirmwareHEX(plaintext);
}
__name(extractFirmwareHEC, "extractFirmwareHEC");
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  extractFirmware,
  guessFirmwareFileFormat,
  tryUnzipFirmwareFile
});
//# sourceMappingURL=firmware.js.map
