Sign in Sign up
kretrod/lodestone Public
Branches
master
172 lines (156 loc) · 7.1 KB Raw
//! Saving and loading the menu's settings.
//!
//! A plain text file, one `key = value` per line, so it can be read and edited
//! without the client. Module names come from the same registry the menu and
//! the keybinds use, so a module cannot appear in one and be missing here.

use crate::state::{BindTarget, ModuleId, Shared};

fn path() -> std::path::PathBuf {
    crate::client_dir().join("lodestone-config.txt")
}

pub fn save(shared: &Shared) -> Result<String, String> {
    let mut out = String::from("# lodestone settings\n");

    for m in ModuleId::ALL {
        out.push_str(&format!("{} = {}\n", m.key(), m.get(&shared.cfg)));
    }

    let c = &shared.cfg;
    let mut num = |k: &str, v: f32| out.push_str(&format!("{k} = {v}\n"));
    num("fly_speed", c.movement.fly_speed);
    num("fly_step", c.movement.fly_step);
    num("fly_step_interval", c.movement.fly_step_interval);
    num("speed_value", c.movement.speed_value);
    num("jetpack_power", c.movement.jetpack_power);
    num("step_height", c.movement.step_height);
    num("jump_multiplier", c.movement.jump_multiplier);
    num("spider_power", c.movement.spider_power);
    num("freecam_speed", c.movement.freecam_speed);
    num("aura_range", c.combat.aura_range);
    num("aura_cps", c.combat.aura_cps);
    num("aim_fov", c.combat.aim_fov);
    num("aim_speed", c.combat.aim_speed);
    num("trigger_delay", c.combat.trigger_delay);
    num("reach_distance", c.combat.reach_distance);
    num("click_cps", c.combat.click_cps);
    num("click_jitter", c.combat.click_jitter);
    num("place_delay", c.building.place_delay);
    num("kb_horizontal", c.combat.kb_horizontal);
    num("kb_vertical", c.combat.kb_vertical);
    num("esp_distance", c.esp.distance);
    num("fov_value", c.visuals.fov_value);
    num("ui_scale", c.ui_scale);

    out.push_str(&format!("fly_mode = {}\n", c.movement.fly_mode.label()));
    out.push_str(&format!("speed_mode = {}\n", c.movement.speed_mode.label()));
    out.push_str(&format!("aim_mode = {}\n", c.combat.aim_mode.label()));
    out.push_str(&format!("aura_players = {}\n", c.combat.aura_players));
    out.push_str(&format!("aura_mobs = {}\n", c.combat.aura_mobs));
    out.push_str(&format!("aura_animals = {}\n", c.combat.aura_animals));

    for b in &shared.binds {
        let name = match b.target {
            BindTarget::Module(m) => m.key(),
            BindTarget::Panic => "panic",
        };
        out.push_str(&format!("bind.{name} = {}\n", b.key));
    }

    let path = path();
    std::fs::write(&path, out).map_err(|e| e.to_string())?;
    Ok(path.display().to_string())
}

/// Load at startup, when there is no `Shared` borrow to hand.
/// Returns false when there is simply no config yet.
pub fn load_into_shared() -> Result<bool, String> {
    if !path().exists() {
        return Ok(false);
    }
    crate::state::with(|s| load(s))
        .unwrap_or(Err("state lock poisoned".into()))
        .map(|_| true)
}

pub fn load(shared: &mut Shared) -> Result<(), String> {
    let text = std::fs::read_to_string(path()).map_err(|e| e.to_string())?;
    shared.binds.clear();

    for line in text.lines() {
        let line = line.trim();
        if line.is_empty() || line.starts_with('#') {
            continue;
        }
        let Some((key, value)) = line.split_once('=') else {
            continue;
        };
        let (key, value) = (key.trim(), value.trim());

        if let Some(name) = key.strip_prefix("bind.") {
            if let Ok(vk) = value.parse::<u32>() {
                let target = if name == "panic" {
                    Some(BindTarget::Panic)
                } else {
                    ModuleId::from_key(name).map(BindTarget::Module)
                };
                if let Some(target) = target {
                    shared.binds.push(crate::state::Bind { key: vk, target });
                }
            }
            continue;
        }

        if let Some(m) = ModuleId::from_key(key) {
            m.set(&mut shared.cfg, value == "true");
            continue;
        }

        apply_setting(shared, key, value);
    }

    // Anything the window-capture switch turned on has to be re-applied, since
    // it is Windows state rather than ours.
    crate::capture::apply(shared.cfg.misc.hide_from_capture);
    Ok(())
}

fn apply_setting(shared: &mut Shared, key: &str, value: &str) {
    use crate::state::{AimMode, FlyMode, SpeedMode};
    let c = &mut shared.cfg;
    let f = || value.parse::<f32>().ok();
    let b = value == "true";
    match key {
        "fly_speed" => c.movement.fly_speed = f().unwrap_or(c.movement.fly_speed),
        "fly_step" => c.movement.fly_step = f().unwrap_or(c.movement.fly_step),
        "fly_step_interval" => {
            c.movement.fly_step_interval = f().unwrap_or(c.movement.fly_step_interval)
        }
        "speed_value" => c.movement.speed_value = f().unwrap_or(c.movement.speed_value),
        "jetpack_power" => c.movement.jetpack_power = f().unwrap_or(c.movement.jetpack_power),
        "step_height" => c.movement.step_height = f().unwrap_or(c.movement.step_height),
        "jump_multiplier" => {
            c.movement.jump_multiplier = f().unwrap_or(c.movement.jump_multiplier)
        }
        "spider_power" => c.movement.spider_power = f().unwrap_or(c.movement.spider_power),
        "freecam_speed" => c.movement.freecam_speed = f().unwrap_or(c.movement.freecam_speed),
        "aura_range" => c.combat.aura_range = f().unwrap_or(c.combat.aura_range),
        "aura_cps" => c.combat.aura_cps = f().unwrap_or(c.combat.aura_cps),
        "aim_fov" => c.combat.aim_fov = f().unwrap_or(c.combat.aim_fov),
        "aim_speed" => c.combat.aim_speed = f().unwrap_or(c.combat.aim_speed),
        "trigger_delay" => c.combat.trigger_delay = f().unwrap_or(c.combat.trigger_delay),
        "reach_distance" => c.combat.reach_distance = f().unwrap_or(c.combat.reach_distance),
        "click_cps" => c.combat.click_cps = f().unwrap_or(c.combat.click_cps),
        "click_jitter" => c.combat.click_jitter = f().unwrap_or(c.combat.click_jitter),
        "place_delay" => c.building.place_delay = f().unwrap_or(c.building.place_delay),
        "kb_horizontal" => c.combat.kb_horizontal = f().unwrap_or(c.combat.kb_horizontal),
        "kb_vertical" => c.combat.kb_vertical = f().unwrap_or(c.combat.kb_vertical),
        "esp_distance" => c.esp.distance = f().unwrap_or(c.esp.distance),
        "fov_value" => c.visuals.fov_value = f().unwrap_or(c.visuals.fov_value),
        "ui_scale" => c.ui_scale = f().unwrap_or(c.ui_scale),
        "aura_players" => c.combat.aura_players = b,
        "aura_mobs" => c.combat.aura_mobs = b,
        "aura_animals" => c.combat.aura_animals = b,
        "fly_mode" => {
            if let Some(m) = FlyMode::ALL.into_iter().find(|m| m.label() == value) {
                c.movement.fly_mode = m;
            }
        }
        "speed_mode" => {
            if let Some(m) = SpeedMode::ALL.into_iter().find(|m| m.label() == value) {
                c.movement.speed_mode = m;
            }
        }
        "aim_mode" => {
            if let Some(m) = AimMode::ALL.into_iter().find(|m| m.label() == value) {
                c.combat.aim_mode = m;
            }
        }
        _ => {}
    }
}