Skip to main content

soar_package/formats/
mod.rs

1//! Package format detection and handling.
2//!
3//! This module provides functionality for detecting package formats based on
4//! magic bytes and handling format-specific operations like desktop integration.
5
6pub mod appimage;
7pub mod common;
8pub mod wrappe;
9
10use std::io::{BufReader, Read, Seek, SeekFrom};
11
12use crate::error::{PackageError, Result};
13
14/// Magic bytes for ELF executables.
15pub const ELF_MAGIC_BYTES: [u8; 4] = [0x7f, 0x45, 0x4c, 0x46];
16
17/// Magic bytes for AppImage format (at offset 8).
18pub const APPIMAGE_MAGIC_BYTES: [u8; 4] = [0x41, 0x49, 0x02, 0x00];
19
20/// Magic bytes for FlatImage format (at offset 8).
21pub const FLATIMAGE_MAGIC_BYTES: [u8; 4] = [0x46, 0x49, 0x01, 0x00];
22
23/// Magic bytes for RunImage format (at offset 8).
24pub const RUNIMAGE_MAGIC_BYTES: [u8; 4] = [0x52, 0x49, 0x02, 0x00];
25
26/// Magic bytes for Wrappe format (at offset file_size - 801).
27pub const WRAPPE_MAGIC_BYTES: [u8; 8] = [0x50, 0x45, 0x33, 0x44, 0x41, 0x54, 0x41, 0x00];
28
29/// Magic bytes for PNG images.
30pub const PNG_MAGIC_BYTES: [u8; 8] = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
31
32/// Magic bytes for SVG images.
33pub const SVG_MAGIC_BYTES: [u8; 4] = [0x3c, 0x73, 0x76, 0x67];
34
35/// Supported package formats.
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum PackageFormat {
38    /// AppImage format - self-contained Linux application.
39    AppImage,
40    /// FlatImage format.
41    FlatImage,
42    /// RunImage format.
43    RunImage,
44    /// Wrappe format - Windows PE wrapper.
45    Wrappe,
46    /// Standard ELF executable.
47    ELF,
48    /// Unknown or unsupported format.
49    Unknown,
50}
51
52/// Detects the package format by reading magic bytes from the file.
53///
54/// # Arguments
55///
56/// * `file` - A buffered reader with seek capability
57///
58/// # Returns
59///
60/// The detected [`PackageFormat`], or [`PackageFormat::Unknown`] if the format
61/// cannot be determined.
62///
63/// # Errors
64///
65/// Returns [`PackageError`] if reading or seeking fails.
66pub fn get_file_type<T>(file: &mut BufReader<T>) -> Result<PackageFormat>
67where
68    T: Read + Seek,
69{
70    let mut magic_bytes = [0u8; 12];
71    file.read_exact(&mut magic_bytes)
72        .map_err(|_| PackageError::MagicBytesError)?;
73
74    if magic_bytes[8..] == APPIMAGE_MAGIC_BYTES {
75        return Ok(PackageFormat::AppImage);
76    }
77    if magic_bytes[8..] == FLATIMAGE_MAGIC_BYTES {
78        return Ok(PackageFormat::FlatImage);
79    }
80    if magic_bytes[8..] == RUNIMAGE_MAGIC_BYTES {
81        return Ok(PackageFormat::RunImage);
82    }
83
84    // Check for Wrappe format - magic bytes are at offset (file_size - 801)
85    let file_size = file
86        .seek(SeekFrom::End(0))
87        .map_err(|_| PackageError::SeekError)?;
88
89    // Wrappe magic bytes require at least 801 bytes (offset from end) + 8 bytes (magic)
90    if file_size >= 801 {
91        let start = file_size - 801;
92        file.seek(SeekFrom::Start(start))
93            .map_err(|_| PackageError::SeekError)?;
94
95        let mut wrappe_magic = [0u8; 8];
96        file.read_exact(&mut wrappe_magic)
97            .map_err(|_| PackageError::MagicBytesError)?;
98
99        if wrappe_magic == WRAPPE_MAGIC_BYTES {
100            file.rewind().map_err(|_| PackageError::SeekError)?;
101            return Ok(PackageFormat::Wrappe);
102        }
103    }
104
105    file.rewind().map_err(|_| PackageError::SeekError)?;
106
107    if magic_bytes[..4] == ELF_MAGIC_BYTES {
108        return Ok(PackageFormat::ELF);
109    }
110
111    Ok(PackageFormat::Unknown)
112}