use std::ffi::CString;
use std::fs::File;
use std::io::{BufReader, BufWriter, Read, Seek};
use std::path::Path;
use anyhow::Context;
use serde::Serialize;
use serde::de::DeserializeOwned;
use zip::ZipArchive;
pub mod files;
pub mod java;
pub mod logs;
pub mod minecraft;
pub mod persistent;
pub mod update;
pub fn json_from_file<D: DeserializeOwned>(path: impl AsRef<Path>) -> anyhow::Result<D> {
let file = BufReader::new(File::open(path).context("Failed to open file")?);
Ok(simd_json::from_reader(file)?)
}
pub fn json_to_file<S: Serialize>(path: impl AsRef<Path>, data: &S) -> anyhow::Result<()> {
let file = BufWriter::new(File::create(path).context("Failed to open file")?);
simd_json::to_writer(file, data).context("Failed to serialize data to file")?;
Ok(())
}
pub fn json_to_file_pretty<S: Serialize>(path: impl AsRef<Path>, data: &S) -> anyhow::Result<()> {
let file = BufWriter::new(File::create(path).context("Failed to open file")?);
serde_json::to_writer_pretty(file, data).context("Failed to serialize data to file")?;
Ok(())
}
pub fn json_to_file_pretty_fast<S: Serialize>(
path: impl AsRef<Path>,
data: &S,
) -> anyhow::Result<()> {
let file = BufWriter::new(File::create(path).context("Failed to open file")?);
simd_json::to_writer_pretty(file, data).context("Failed to serialize data to file")?;
Ok(())
}
pub fn extract_zip_dir<R: Read + Seek>(
zip: &mut ZipArchive<R>,
zip_dir: &str,
target_dir: impl AsRef<Path>,
) -> anyhow::Result<()> {
let _ = std::fs::create_dir_all(target_dir.as_ref());
for index in 0..zip.len() {
let mut file = zip.by_index(index)?;
if file.is_dir() {
continue;
}
let Some(filename) = file.enclosed_name() else {
continue;
};
let Ok(filename) = filename.strip_prefix(zip_dir) else {
continue;
};
let out_path = target_dir.as_ref().join(filename);
let _ = files::create_leading_dirs(&out_path);
let mut out_file = File::create(out_path).context("Failed to create output file")?;
std::io::copy(&mut file, &mut out_file).context("Failed to copy file from zip")?;
}
Ok(())
}
#[cfg(target_family = "unix")]
unsafe extern "C" {
fn mkfifo(path: *const i8, mode: u32) -> i32;
fn open(path: *const i8, flags: i32, mode: u32) -> i32;
fn fcntl(file: i32, op: i32, mode: i32) -> i32;
}
#[cfg(target_os = "windows")]
use std::os::windows::prelude::RawHandle;
#[cfg(target_os = "windows")]
#[link(name = "kernel32")]
unsafe extern "system" {
fn CreateNamedPipeA(
lpName: *const i8,
dwOpenMode: u32,
dwPipeMode: u32,
dwMaxInstances: u32,
dwOutBufferSize: u32,
dwInBufferSize: u32,
dwDefaultTimeOut: u32,
lpSecurityAttributes: *mut std::ffi::c_void,
) -> RawHandle;
}
#[cfg(target_os = "windows")]
#[repr(C)]
#[allow(non_snake_case)]
struct SECURITY_ATTRIBUTES {
nLength: u32,
lpSecurityDescriptor: *mut std::ffi::c_void,
bInheritHandle: i32,
}
pub fn create_named_pipe(path: impl AsRef<Path>) -> std::io::Result<std::process::Stdio> {
#[cfg(target_family = "unix")]
{
use std::os::fd::FromRawFd;
const O_NONBLOCK: i32 = 0o4000;
let c_path = CString::new(path.as_ref().to_string_lossy().as_bytes()).unwrap();
unsafe {
if mkfifo(c_path.as_ptr(), 0o600) != 0 {
return Err(std::io::Error::last_os_error());
}
}
let open_flags = O_NONBLOCK;
let file = unsafe { open(c_path.as_ptr(), open_flags, 0) };
if file == -1 {
return Err(std::io::Error::last_os_error());
}
unsafe { fcntl(file, 4, !O_NONBLOCK) };
let out = unsafe { std::process::Stdio::from_raw_fd(file) };
Ok(out)
}
#[cfg(target_os = "windows")]
{
use std::os::windows::io::FromRawHandle;
const BUFFER_SIZE: u32 = 512;
let path = format!(
"\\\\.\\pipe\\{}",
path.as_ref()
.file_name()
.unwrap_or_default()
.to_string_lossy()
);
let c_path = CString::new(path).unwrap();
let mut security_attributes = SECURITY_ATTRIBUTES {
nLength: std::mem::size_of::<SECURITY_ATTRIBUTES>() as u32,
lpSecurityDescriptor: std::ptr::null_mut(),
bInheritHandle: 1,
};
let pipe_handle = unsafe {
CreateNamedPipeA(
c_path.as_ptr(),
0x00000003, 0x00000000, 1, BUFFER_SIZE,
BUFFER_SIZE,
0,
&mut security_attributes as *mut _ as *mut _,
)
};
if pipe_handle.is_null() {
return Err(std::io::Error::last_os_error());
}
unsafe { Ok(std::process::Stdio::from_raw_handle(pipe_handle)) }
}
}
pub fn open_named_pipe(path: impl AsRef<Path>) -> std::io::Result<File> {
std::fs::OpenOptions::new()
.append(true)
.create(true)
.open(path)
}