modde_games/stardew/
mod.rs1pub 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
13pub 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}