summaryrefslogtreecommitdiff
path: root/src/SMAPI/Framework/ContentPack.cs
blob: a1d977e4a4c866054bc49566376727b85c996a26 (plain)
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
118
119
120
121
122
123
124
125
126
127
128
129
130
using System;
using System.IO;
using StardewModdingAPI.Framework.ModHelpers;
using StardewModdingAPI.Toolkit.Serialization;
using StardewModdingAPI.Toolkit.Utilities;
using StardewModdingAPI.Toolkit.Utilities.PathLookups;

namespace StardewModdingAPI.Framework
{
    /// <summary>Manages access to a content pack's metadata and files.</summary>
    internal class ContentPack : IContentPack
    {
        /*********
        ** Fields
        *********/
        /// <summary>Encapsulates SMAPI's JSON file parsing.</summary>
        private readonly JsonHelper JsonHelper;

        /// <summary>A lookup for files within the <see cref="DirectoryPath"/>.</summary>
        private readonly IFileLookup FileLookup;


        /*********
        ** Accessors
        *********/
        /// <inheritdoc />
        public string DirectoryPath { get; }

        /// <inheritdoc />
        public IManifest Manifest { get; }

        /// <inheritdoc />
        public ITranslationHelper Translation => this.TranslationImpl;

        /// <inheritdoc />
        public IModContentHelper ModContent { get; }

        /// <summary>The underlying translation helper.</summary>
        internal TranslationHelper TranslationImpl { get; set; }


        /*********
        ** Public methods
        *********/
        /// <summary>Construct an instance.</summary>
        /// <param name="directoryPath">The full path to the content pack's folder.</param>
        /// <param name="manifest">The content pack's manifest.</param>
        /// <param name="content">Provides an API for loading content assets from the content pack's folder.</param>
        /// <param name="translation">Provides translations stored in the content pack's <c>i18n</c> folder.</param>
        /// <param name="jsonHelper">Encapsulates SMAPI's JSON file parsing.</param>
        /// <param name="fileLookup">A lookup for files within the <paramref name="directoryPath"/>.</param>
        public ContentPack(string directoryPath, IManifest manifest, IModContentHelper content, TranslationHelper translation, JsonHelper jsonHelper, IFileLookup fileLookup)
        {
            this.DirectoryPath = directoryPath;
            this.Manifest = manifest;
            this.ModContent = content;
            this.TranslationImpl = translation;
            this.JsonHelper = jsonHelper;
            this.FileLookup = fileLookup;
        }

        /// <inheritdoc />
        public bool HasFile(string path)
        {
            path = PathUtilities.NormalizePath(path);

            return this.GetFile(path).Exists;
        }

        /// <inheritdoc />
        public TModel? ReadJsonFile<TModel>(string path) where TModel : class
        {
            path = PathUtilities.NormalizePath(path);

            FileInfo file = this.GetFile(path);
            return file.Exists && this.JsonHelper.ReadJsonFileIfExists(file.FullName, out TModel? model)
                ? model
                : null;
        }

        /// <inheritdoc />
        public void WriteJsonFile<TModel>(string path, TModel data) where TModel : class
        {
            path = PathUtilities.NormalizePath(path);

            FileInfo file = this.GetFile(path);
            bool didExist = file.Exists;

            this.JsonHelper.WriteJsonFile(file.FullName, data);

            if (!didExist)
            {
                this.FileLookup.Add(
                    Path.GetRelativePath(this.DirectoryPath, file.FullName)
                );
            }
        }

#if SMAPI_DEPRECATED
        /// <inheritdoc />
        [Obsolete($"Use {nameof(IContentPack.ModContent)}.{nameof(IModContentHelper.Load)} instead. This method will be removed in SMAPI 4.0.0.")]
        public T LoadAsset<T>(string key)
            where T : notnull
        {
            return this.ModContent.Load<T>(key);
        }

        /// <inheritdoc />
        [Obsolete($"Use {nameof(IContentPack.ModContent)}.{nameof(IModContentHelper.GetInternalAssetName)} instead. This method will be removed in SMAPI 4.0.0.")]
        public string GetActualAssetKey(string key)
        {
            return this.ModContent.GetInternalAssetName(key).Name;
        }
#endif


        /*********
        ** Private methods
        *********/
        /// <summary>Get the underlying file info.</summary>
        /// <param name="relativePath">The normalized file path relative to the content pack directory.</param>
        private FileInfo GetFile(string relativePath)
        {
            if (!PathUtilities.IsSafeRelativePath(relativePath))
                throw new InvalidOperationException($"You must call {nameof(IContentPack)} methods with a relative path.");

            return this.FileLookup.GetFile(relativePath);
        }
    }
}