use std::{env, path::PathBuf, sync::LazyLock};
use anyhow::{Result, bail};
use async_fs as fs;
use mlua::Compiler as LuaCompiler;
pub static CURRENT_EXE: LazyLock<PathBuf> =
LazyLock::new(|| env::current_exe().expect("failed to get current exe"));
const MAGIC: &[u8; 8] = b"cr3sc3nt";
#[derive(Debug, Clone)]
pub struct Metadata {
pub bytecode: Vec<u8>,
}
impl Metadata {
pub async fn check_env() -> (bool, Vec<u8>) {
let contents = fs::read(CURRENT_EXE.to_path_buf())
.await
.unwrap_or_default();
let is_standalone = contents.ends_with(MAGIC);
(is_standalone, contents)
}
pub async fn create_env_patched_bin(
base_exe_path: PathBuf,
script_contents: impl Into<Vec<u8>>,
) -> Result<Vec<u8>> {
let compiler = LuaCompiler::new()
.set_optimization_level(2)
.set_coverage_level(0)
.set_debug_level(1);
let mut patched_bin = fs::read(base_exe_path).await?;
let bytecode = compiler.compile(script_contents.into())?;
let meta = Self { bytecode };
patched_bin.extend_from_slice(&meta.to_bytes());
Ok(patched_bin)
}
pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self> {
let bytes = bytes.as_ref();
if bytes.len() < 16 || !bytes.ends_with(MAGIC) {
bail!("not a standalone binary")
}
let bytecode_size_bytes = &bytes[bytes.len() - 16..bytes.len() - 8];
let bytecode_size =
usize::try_from(u64::from_be_bytes(bytecode_size_bytes.try_into().unwrap()))?;
let bytecode = bytes[bytes.len() - 16 - bytecode_size..].to_vec();
Ok(Self { bytecode })
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.extend_from_slice(&self.bytecode);
bytes.extend_from_slice(&(self.bytecode.len() as u64).to_be_bytes());
bytes.extend_from_slice(MAGIC);
bytes
}
}