diff --git a/src/cli/build.rs b/src/cli/build.rs index 7bbbfc3..2e3a767 100644 --- a/src/cli/build.rs +++ b/src/cli/build.rs @@ -1,4 +1,6 @@ use console::Style; +use itertools::Itertools; +use num_traits::{FromBytes, ToBytes}; use std::{env, path::Path, process::ExitCode}; use tokio::{ fs::{self, OpenOptions}, @@ -13,6 +15,101 @@ use mlua::Compiler as LuaCompiler; // it is a standalone binary pub const MAGIC: &[u8; 8] = b"cr3sc3nt"; +/// Utility struct to parse and generate bytes to the META chunk of standalone binaries. +#[derive(Debug, Clone)] +pub struct MetaChunk { + /// Compiled lua bytecode of the entrypoint script. + bytecode: Vec, + /// Offset to the the beginning of the bytecode from the start of the lune binary. + bytecode_offset: Option, + /// Number of files present, currently unused. **For future use**. + file_count: Option, +} + +impl MetaChunk { + pub fn new() -> Self { + Self { + bytecode: Vec::new(), + bytecode_offset: None, + file_count: None, + } + } + + pub fn with_bytecode(&mut self, bytecode: Vec) -> Self { + self.bytecode = bytecode; + + self.clone() + } + + pub fn with_bytecode_offset(&mut self, offset: u64) -> Self { + self.bytecode_offset = Some(offset); + + self.clone() + } + + pub fn with_file_count(&mut self, count: u64) -> Self { + self.file_count = Some(count); + + self.clone() + } + + pub fn build(self, endianness: &str) -> Vec { + match endianness { + "big" => self.to_be_bytes(), + "little" => self.to_le_bytes(), + &_ => panic!("unexpected endianness"), + } + } +} + +impl Default for MetaChunk { + fn default() -> Self { + Self { + bytecode: Vec::new(), + bytecode_offset: Some(0), + file_count: Some(1), + } + } +} + +impl ToBytes for MetaChunk { + type Bytes = Vec; + + fn to_be_bytes(&self) -> Self::Bytes { + // We start with the bytecode offset as the first field already filled in + let mut tmp = self.bytecode_offset.unwrap().to_be_bytes().to_vec(); + + // NOTE: The order of the fields here are reversed, which is on purpose + tmp.extend(self.bytecode.len().to_be_bytes()); + tmp.extend(self.file_count.unwrap().to_be_bytes()); + + tmp + } + + fn to_le_bytes(&self) -> Self::Bytes { + // We start with the bytecode offset as the first field already filled in + let mut tmp = self.bytecode_offset.unwrap().to_le_bytes().to_vec(); + + // NOTE: The order of the fields here are reversed, which is on purpose + tmp.extend(self.bytecode.len().to_le_bytes()); + tmp.extend(self.file_count.unwrap().to_le_bytes()); + + tmp + } +} + +impl FromBytes for MetaChunk { + type Bytes = Vec; + + fn from_be_bytes(bytes: &Self::Bytes) -> Self { + todo!() + } + + fn from_le_bytes(bytes: &Self::Bytes) -> Self { + todo!() + } +} + /** Compiles and embeds the bytecode of a requested lua file to form a standalone binary, then writes it to an output file, with the required permissions. @@ -48,13 +145,13 @@ pub async fn build_standalone>( patched_bin.extend(&bytecode); - let mut meta = base_bin_offset.to_le_bytes().to_vec(); // Start with the base bytecode offset + let meta = MetaChunk::new() + .with_bytecode(bytecode) + .with_bytecode_offset(base_bin_offset) + .with_file_count(1_u64); // Start with the base bytecode offset // Include metadata in the META chunk, each field is 8 bytes - meta.extend((bytecode.len() as u64).to_le_bytes()); // Size of bytecode, used to calculate end offset at runtime - meta.extend(1_u64.to_le_bytes()); // Number of files, padded with null bytes - for future use - - patched_bin.extend(meta); + patched_bin.extend(meta.build("little")); // Append the magic signature to the base binary patched_bin.extend(MAGIC);