Skip to main content

modde_games/stardew/
mod.rs

1//! The Stardew Valley (SMAPI) game plugin and its mod-layout policies.
2
3pub mod saves;
4pub mod scanner;
5
6use std::path::{Path, PathBuf};
7
8use modde_core::installer::InstallMethod;
9
10use crate::policies::{BareLayoutPolicy, ContentPolicy};
11use crate::traits::{ContentCategory, GamePlugin, ModSafety};
12
13/// [`GamePlugin`] for Stardew Valley, modded via the SMAPI loader.
14pub struct SmapiGame;
15
16pub static STARDEW_VALLEY: SmapiGame = SmapiGame;
17
18const STARDEW_SAVE_BREAKING_EXT: &[&str] = &["dll", "json", "xnb", "tmx", "tbin"];
19const STARDEW_COSMETIC_EXT: &[&str] = &["png", "jpg", "xnb"];
20const STARDEW_CONTENT_CATEGORIES: &[(&str, ContentCategory)] = &[
21    ("dll", ContentCategory::Binary),
22    ("json", ContentCategory::Config),
23    ("xnb", ContentCategory::Archive),
24    ("tmx", ContentCategory::Config),
25    ("tbin", ContentCategory::Config),
26    ("png", ContentCategory::Texture),
27    ("jpg", ContentCategory::Texture),
28];
29
30const STARDEW_CONTENT_POLICY: ContentPolicy = ContentPolicy {
31    save_breaking_ext: STARDEW_SAVE_BREAKING_EXT,
32    cosmetic_ext: STARDEW_COSMETIC_EXT,
33    save_breaking_dirs: &[],
34    categories: STARDEW_CONTENT_CATEGORIES,
35};
36
37const STARDEW_BARE_LAYOUT_POLICY: BareLayoutPolicy = BareLayoutPolicy {
38    root_dirs: &["mods"],
39    root_file_exts: &["json", "dll"],
40    case_insensitive_dirs: true,
41};
42
43impl GamePlugin for SmapiGame {
44    fn game_id(&self) -> &'static str {
45        "stardew-valley"
46    }
47
48    fn display_name(&self) -> &'static str {
49        "Stardew Valley"
50    }
51
52    fn mod_directory(&self, install: &Path) -> PathBuf {
53        install.join("Mods")
54    }
55
56    fn save_directory(&self) -> Option<PathBuf> {
57        Some(modde_core::paths::config_dir().join("StardewValley/Saves"))
58    }
59
60    fn supports_save_profiles(&self) -> bool {
61        true
62    }
63
64    fn classify_mod(&self, mod_dir: &Path) -> ModSafety {
65        STARDEW_CONTENT_POLICY.classify_mod(mod_dir)
66    }
67
68    fn classify_extension(&self, ext: &str) -> ContentCategory {
69        STARDEW_CONTENT_POLICY.classify_extension(ext)
70    }
71
72    fn executable_dir(&self, install: &Path) -> PathBuf {
73        install.to_path_buf()
74    }
75
76    fn steam_app_id_u32(&self) -> Option<u32> {
77        Some(413150)
78    }
79
80    fn nexus_game_domain(&self) -> Option<&str> {
81        Some("stardewvalley")
82    }
83
84    fn analyze_mod_archive(&self, extracted_dir: &Path) -> Option<InstallMethod> {
85        if extracted_dir.join("manifest.json").is_file() {
86            return Some(InstallMethod::DirectoryMod {
87                directory_name: None,
88            });
89        }
90        extracted_dir
91            .join("Mods")
92            .is_dir()
93            .then(|| InstallMethod::StripContentRoot {
94                root: "Mods".to_string(),
95            })
96    }
97
98    fn recognizes_bare_layout(&self, extracted_dir: &Path) -> bool {
99        extracted_dir.join("manifest.json").is_file()
100            || STARDEW_BARE_LAYOUT_POLICY.recognizes(extracted_dir)
101    }
102}