gma/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//! Crate for reading and writing gma files, the file format of garrys mod's addons.
//! This crate currently does not support opening compressed archives.

mod addon_metadata;
mod binary;
mod error;
mod gma_builder;
mod gma_reader;
mod result;

pub use error::Error;
pub use gma_builder::GMABuilder;
pub use gma_reader::{FileEntry, GMAFile};
pub use result::Result;
use std::convert::TryFrom;

use gma_reader::GMAFileReader;

use std::io::BufReader;
use std::{
    io::{BufRead, Cursor, Seek},
    path::Path,
};

const IDENT: [u8; 4] = [b'G', b'M', b'A', b'D'];
const VALID_VERSIONS: [u8; 3] = [1, 2, 3];

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum AddonType {
    Gamemode,
    Map,
    Weapon,
    Vehicle,
    NPC,
    Entity,
    Tool,
    Effects,
    Model,
    ServerContent,
}

impl TryFrom<&str> for AddonType {
    type Error = Error;

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        let value_lower = value.to_lowercase();
        match value_lower.as_str() {
            "gamemode" => Ok(AddonType::Gamemode),
            "map" => Ok(AddonType::Map),
            "weapon" => Ok(AddonType::Weapon),
            "vehicle" => Ok(AddonType::Vehicle),
            "npc" => Ok(AddonType::NPC),
            "entity" => Ok(AddonType::Entity),
            "tool" => Ok(AddonType::Tool),
            "effects" => Ok(AddonType::Effects),
            "model" => Ok(AddonType::Model),
            "servercontent" => Ok(AddonType::ServerContent),
            _ => Err(Self::Error::InvalidAddonType(value_lower)),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum AddonTag {
    Fun,
    Roleplay,
    Scenic,
    Movie,
    Realism,
    Cartoon,
    Water,
    Comic,
    Build,
}

impl TryFrom<&str> for AddonTag {
    type Error = Error;

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        let value_lower = value.to_lowercase();
        match value_lower.as_str() {
            "fun" => Ok(AddonTag::Fun),
            "roleplay" => Ok(AddonTag::Roleplay),
            "scenic" => Ok(AddonTag::Scenic),
            "movie" => Ok(AddonTag::Movie),
            "realism" => Ok(AddonTag::Realism),
            "cartoon" => Ok(AddonTag::Cartoon),
            "water" => Ok(AddonTag::Water),
            "comic" => Ok(AddonTag::Comic),
            "build" => Ok(AddonTag::Build),
            _ => Err(Self::Error::InvalidAddonTag(value_lower)),
        }
    }
}

/// Opens a file from disk with the given path and tries to read it as a gma archive
pub fn open<P>(path: P) -> Result<GMAFile<BufReader<std::fs::File>>>
where
    P: AsRef<Path>,
{
    let file = std::fs::File::open(path)?;
    let reader = BufReader::new(file);
    load(reader)
}

/// Loads a gma file from a reader
pub fn load<ReaderType>(r: ReaderType) -> Result<GMAFile<ReaderType>>
where
    ReaderType: BufRead + Seek,
{
    GMAFileReader::new(r)?.read_gma()
}

/// Loads a gma file from memory
pub fn load_from_memory(data: &[u8]) -> Result<GMAFile<Cursor<&[u8]>>> {
    load(Cursor::new(data))
}