2022-02-13 19:39:28 +00:00
|
|
|
use std::collections::HashMap;
|
2022-02-10 09:21:28 +00:00
|
|
|
use std::io::{BufRead, BufReader};
|
|
|
|
use std::iter::Peekable;
|
|
|
|
use std::str::Chars;
|
|
|
|
|
2022-11-19 17:20:03 +00:00
|
|
|
use crate::framework::context::Context;
|
|
|
|
use crate::framework::error::GameResult;
|
2022-02-10 09:21:28 +00:00
|
|
|
use crate::framework::filesystem;
|
2022-03-15 22:18:25 +00:00
|
|
|
use crate::mod_requirements::ModRequirements;
|
2022-02-10 09:21:28 +00:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct ModInfo {
|
|
|
|
pub id: String,
|
|
|
|
pub requirement: Requirement,
|
|
|
|
pub priority: u32,
|
2022-02-18 00:54:22 +00:00
|
|
|
pub save_slot: i32,
|
2022-02-10 09:21:28 +00:00
|
|
|
pub path: String,
|
2022-02-13 19:39:28 +00:00
|
|
|
pub name: String,
|
|
|
|
pub description: String,
|
2022-05-22 13:17:55 +00:00
|
|
|
pub valid: bool,
|
2022-02-10 09:21:28 +00:00
|
|
|
}
|
|
|
|
|
2022-03-15 22:18:25 +00:00
|
|
|
impl ModInfo {
|
|
|
|
pub fn satisfies_requirement(&self, mod_requirements: &ModRequirements) -> bool {
|
|
|
|
match self.requirement {
|
|
|
|
Requirement::Unlocked => true,
|
|
|
|
Requirement::Locked => false,
|
|
|
|
Requirement::RequireHell => mod_requirements.beat_hell,
|
|
|
|
Requirement::RequireItem(item_id) => mod_requirements.has_item(item_id),
|
|
|
|
Requirement::RequireWeapon(weapon_id) => mod_requirements.has_weapon(weapon_id),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-10 09:21:28 +00:00
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
|
|
pub enum Requirement {
|
|
|
|
/// R+
|
|
|
|
Unlocked,
|
|
|
|
/// R-
|
|
|
|
Locked,
|
|
|
|
/// RI##
|
|
|
|
RequireItem(u16),
|
|
|
|
/// RA##
|
|
|
|
RequireWeapon(u16),
|
|
|
|
/// RG
|
|
|
|
RequireHell,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct ModList {
|
|
|
|
pub mods: Vec<ModInfo>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ModList {
|
2022-02-13 19:39:28 +00:00
|
|
|
pub fn load(ctx: &mut Context, string_table: &HashMap<String, String>) -> GameResult<ModList> {
|
2022-02-10 09:21:28 +00:00
|
|
|
let mut mods = Vec::new();
|
|
|
|
|
|
|
|
if let Ok(file) = filesystem::open(ctx, "/mods.txt") {
|
|
|
|
let reader = BufReader::new(file);
|
|
|
|
let mut lines = reader.lines();
|
|
|
|
|
|
|
|
while let Some(Ok(line)) = lines.next() {
|
|
|
|
if line == "=MOD LIST START=" {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while let Some(Ok(line)) = lines.next() {
|
|
|
|
let mut id = String::new();
|
|
|
|
let mut requirement = Requirement::Unlocked;
|
|
|
|
let mut priority = 1000u32;
|
|
|
|
let mut path = String::new();
|
|
|
|
let mut chars = line.chars().peekable();
|
|
|
|
|
|
|
|
fn consume_spaces(chars: &mut Peekable<Chars>) {
|
|
|
|
while let Some(&c) = chars.peek() {
|
|
|
|
if c != ' ' {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
chars.next();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
consume_spaces(&mut chars);
|
|
|
|
id.push_str("csmod_");
|
|
|
|
for c in &mut chars {
|
|
|
|
if c == ' ' {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
id.push(c);
|
|
|
|
}
|
|
|
|
consume_spaces(&mut chars);
|
|
|
|
|
|
|
|
loop {
|
|
|
|
if let Some(c) = chars.next() {
|
|
|
|
if c == 'R' {
|
|
|
|
if let Some(c) = chars.next() {
|
|
|
|
if c == '+' {
|
|
|
|
requirement = Requirement::Unlocked;
|
|
|
|
} else if c == '-' {
|
|
|
|
requirement = Requirement::Locked;
|
|
|
|
} else if c == 'I' {
|
|
|
|
let mut item_id = String::new();
|
|
|
|
for c in &mut chars {
|
|
|
|
if c == ' ' {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
item_id.push(c);
|
|
|
|
}
|
|
|
|
requirement = Requirement::RequireItem(item_id.parse().unwrap_or(0));
|
|
|
|
} else if c == 'A' {
|
|
|
|
let mut weapon_id = String::new();
|
|
|
|
for c in &mut chars {
|
|
|
|
if c == ' ' {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
weapon_id.push(c);
|
|
|
|
}
|
|
|
|
requirement = Requirement::RequireWeapon(weapon_id.parse().unwrap_or(0));
|
|
|
|
} else if c == 'G' {
|
|
|
|
requirement = Requirement::RequireHell;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if c == 'P' {
|
|
|
|
priority = 0;
|
|
|
|
|
|
|
|
for c in &mut chars {
|
|
|
|
if c == ' ' {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
priority = priority.saturating_mul(10).saturating_add(c.to_digit(10).unwrap_or(0));
|
|
|
|
}
|
|
|
|
} else if c == '/' {
|
|
|
|
path.push(c);
|
|
|
|
while let Some(&c) = chars.peek() {
|
|
|
|
path.push(c);
|
|
|
|
chars.next();
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
} else if c == ' ' {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-22 13:17:55 +00:00
|
|
|
let mut valid = false;
|
2022-02-13 19:39:28 +00:00
|
|
|
let mut name = String::new();
|
|
|
|
let mut description = String::new();
|
2022-02-18 00:54:22 +00:00
|
|
|
let mut save_slot = -1;
|
2022-02-13 19:39:28 +00:00
|
|
|
|
|
|
|
if let Ok(file) = filesystem::open(ctx, [&path, "/mod.txt"].join("")) {
|
2022-05-22 13:17:55 +00:00
|
|
|
valid = true;
|
2022-02-13 19:39:28 +00:00
|
|
|
let reader = BufReader::new(file);
|
|
|
|
let mut lines = reader.lines();
|
2022-02-18 00:54:22 +00:00
|
|
|
if let Some(line) = lines.nth(1) {
|
|
|
|
save_slot = line.unwrap_or("-1".to_string()).parse::<i32>().unwrap_or(-1);
|
|
|
|
}
|
|
|
|
if let Some(line) = lines.next() {
|
2022-02-13 19:39:28 +00:00
|
|
|
let read_name = line.unwrap_or("No Mod Name".to_string()).to_string();
|
|
|
|
name = string_table.get(&read_name).unwrap_or(&read_name).to_string();
|
|
|
|
}
|
|
|
|
if let Some(line) = lines.next() {
|
|
|
|
description = line.unwrap_or("No Description".to_string()).to_string();
|
|
|
|
}
|
2022-05-22 13:17:55 +00:00
|
|
|
} else {
|
|
|
|
name = path.clone();
|
|
|
|
description = "mod.txt not found".to_string();
|
2022-02-13 19:39:28 +00:00
|
|
|
}
|
|
|
|
|
2022-05-22 13:17:55 +00:00
|
|
|
mods.push(ModInfo { id, requirement, priority, save_slot, path, name, description, valid })
|
2022-02-10 09:21:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mods.sort_by(|a, b| a.priority.cmp(&b.priority));
|
|
|
|
|
|
|
|
Ok(ModList { mods })
|
|
|
|
}
|
2022-02-23 00:46:49 +00:00
|
|
|
|
|
|
|
pub fn get_save_from_path(&self, mod_path: String) -> i32 {
|
|
|
|
if let Some(mod_sel) = self.mods.iter().find(|x| x.path == mod_path) {
|
|
|
|
mod_sel.save_slot
|
|
|
|
} else {
|
|
|
|
-1
|
|
|
|
}
|
|
|
|
}
|
2022-02-25 22:00:14 +00:00
|
|
|
|
|
|
|
pub fn get_name_from_path(&self, mod_path: String) -> &str {
|
|
|
|
if let Some(mod_sel) = self.mods.iter().find(|x| x.path == mod_path) {
|
|
|
|
&mod_sel.name
|
|
|
|
} else {
|
|
|
|
"NoName"
|
|
|
|
}
|
|
|
|
}
|
2022-02-10 09:21:28 +00:00
|
|
|
}
|