bitness detection

This commit is contained in:
Novattz
2026-01-17 17:57:17 +01:00
parent 05e4275962
commit 40b9ec9b01
3 changed files with 160 additions and 0 deletions

View File

@@ -4,6 +4,7 @@
)]
mod cache;
mod utils;
mod dlc_manager;
mod installer;
mod searcher;

View File

@@ -0,0 +1,158 @@
use log::{debug, info, warn};
use std::fs;
use std::path::Path;
use walkdir::WalkDir;
/// Represents the bitness of a binary
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Bitness {
Bit32,
Bit64,
}
/// Detect the bitness of a Linux Binary by reading ELF header
/// ELF format: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
fn detect_binary_bitness(file_path: &Path) -> Option<Bitness> {
// Read first 5 bytes of the file to check ELF header
let bytes = match fs::read(file_path) {
Ok(b) if b.len() >= 5 => b,
_ => return None,
};
// Check for ELF magic number (0x7F 'E' 'L' 'F')
if bytes.len() < 5 || &bytes[0..4] != b"\x7ELF" {
return None;
}
// Byte 4 (EI_CLASS) indicates 32-bit or 64-bit
// 1 = ELFCLASS32 (32-bit)
// 2 = ELFCLASS64 (64-bit)
match bytes[4] {
1 => Some(Bitness::Bit32),
2 => Some(Bitness::Bit64),
_ => None,
}
}
/// Scan game directory for Linux binaries and determine bitness
/// Returns the detected bitness, prioritizing the main game executable
pub fn detect_game_bitness(game_path: &str) -> Result<Bitness, String> {
info!("Detecting bitness for game at: {}", game_path);
let game_path_obj = Path::new(game_path);
if !game_path_obj.exists() {
return Err("Game path does not exist".to_string());
}
// Directories to skip for performance
let skip_dirs = [
"videos",
"video",
"movies",
"movie",
"sound",
"sounds",
"audio",
"textures",
"music",
"localization",
"shaders",
"logs",
"assets",
"_CommonRedist",
];
// Limit scan depth to avoid deep recursion
const MAX_DEPTH: usize = 5;
let mut bit64_binaries = Vec::new();
let mut bit32_binaries = Vec::new();
// Scan for Linux binaries
for entry in WalkDir::new(game_path_obj)
.max_depth(MAX_DEPTH)
.follow_links(false)
.into_iter()
.filter_entry(|e| {
if e.file_type().is_dir() {
let dir_name = e.file_name().to_string_lossy().to_lowercase();
!skip_dirs.iter().any(|&skip| dir_name.contains(skip))
} else {
true
}
})
.filter_map(Result::ok)
{
let path = entry.path();
// Only check files
if !path.is_file() {
continue;
}
// Check if file is executable
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
if let Ok(metadata) = fs::metadata(path) {
let permissions = metadata.permissions();
// Check if executable bit is set
if permissions.mode() & 0o111 == 0 {
continue;
}
} else {
continue;
}
}
// Check for common Linux executable extensions or no extension
let filename = path.file_name().unwrap_or_default().to_string_lossy();
let has_exe_extension = filename.ends_with(".x86")
|| filename.ends_with(".x86_64")
|| filename.ends_with(".bin")
|| filename.contains('.');
// Also check for .so files (shared libraries) as they indicate bitness
let is_shared_lib = filename.ends_with(".so")
|| filename.contains(".so")
|| filename.starts_with("lib");
if !has_exe_extension && !is_shared_lib {
continue;
}
// Detect bitness
if let Some(bitness) = detect_binary_bitness(path) {
debug!("Found {:?} binary: {}", bitness, path.display());
match bitness {
Bitness::Bit64 => bit64_binaries.push(path.to_path_buf()),
Bitness::Bit32 => bit32_binaries.push(path.to_path_buf()),
}
}
}
// Decision logic: prioritize finding the main game executable
// 1. If we found any 64-bit binaries and no 32-bit, it's 64-bit
// 2. If we found any 32-bit binaries and no 64-bit, it's 32-bit
// 3. If we found both, prefer 64-bit (modern games are usually 64-bit)
// 4. If we found neither, return an error
if !bit64_binaries.is_empty() && bit32_binaries.is_empty() {
info!("Detected 64-bit game (Only 64-bit binaries found");
Ok(Bitness::Bit64)
} else if !bit32_binaries.is_empty() && bit64_binaries.is_empty() {
info!("Detected 32-bit game (Only 32-bit binaries found");
Ok(Bitness::Bit32)
} else if !bit64_binaries.is_empty() && !bit32_binaries.is_empty() {
warn!(
"Found both 32-bit and 64-bit binaries, defaulting to 64-bit. 32-bit: {}, 64-bit: {}",
bit32_binaries.len(),
bit64_binaries.len()
);
info!("Detected 64-bit game (mixed binaries, defaulting to 64-bit)");
Ok(Bitness::Bit64)
} else {
Err("Could not detect game bitness: no Linux binaries found".to_string())
}
}

View File

@@ -0,0 +1 @@
pub mod bitness;