summaryrefslogtreecommitdiff
path: root/src/ModBuildConfig/build/smapi.targets
blob: a1b6aab37a61cfd8c70c6f970091ec81ff2b3e49 (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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!--*********************************************
  ** Define build tasks
  **********************************************-->
  <!--######
  ## create a release zip file for a mod (CodeTaskFactory only available on Windows?)
  #######-->
  <UsingTask TaskName="CreateModReleaseZip" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" Condition="'$(OS)' == 'Windows_NT'">
    <ParameterGroup>
      <ModName ParameterType="System.String" Required="true" />
      <Files ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
      <OutputFolderPath ParameterType="System.String" Required="true" />
    </ParameterGroup>
    <Task>
      <Reference Include="System.IO" />
      <Reference Include="System.IO.Compression" />
      <Reference Include="System.Web.Extensions"/>
      <Code Type="Class" Language="cs">
        <![CDATA[
        using System;
        using System.Collections.Generic;
        using System.IO;
        using System.IO.Compression;
        using System.Web.Script.Serialization;
        using Microsoft.Build.Framework;
        using Microsoft.Build.Utilities;

        /// <summary>A build task which packs mod files into a conventional release zip.</summary>
        public class CreateModReleaseZip : Task, ITask
        {
           /*********
           ** Accessors
           *********/
           /// <summary>The mod files to pack.</summary>
           public ITaskItem[] Files { get; set; }

           /// <summary>The name of the mod.</param>
           public string ModName { get; set; }

           /// <summary>The absolute or relative path to the folder which should contain the generated zip file.</summary>
           public string OutputFolderPath { get; set; }


           /*********
           ** Public methods
           *********/
           public override bool Execute()
           {
              try
              {
                 // create output path if needed
                 Directory.CreateDirectory(OutputFolderPath);

                 // get zip filename
                 string fileName = string.Format("{0}-{1}.zip", this.ModName, this.GetManifestVersion());

                 // clear old zip file if present
                 string zipPath = Path.Combine(OutputFolderPath, fileName);
                 if (File.Exists(zipPath))
                    File.Delete(zipPath);

                 // create zip file
                 using (Stream zipStream = new FileStream(zipPath, FileMode.Create, FileAccess.Write))
                 using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create))
                 {
                    foreach (ITaskItem file in Files)
                    {
                       // get file info
                       string filePath = file.ItemSpec;
                       string entryName = ModName + '/' + file.GetMetadata("RecursiveDir") + file.GetMetadata("Filename") + file.GetMetadata("Extension");
                       if (new FileInfo(filePath).Directory.Name.Equals("i18n", StringComparison.InvariantCultureIgnoreCase))
                          entryName = Path.Combine("i18n", entryName);

                       // add to zip
                       using (Stream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
                       using (Stream fileStreamInZip = archive.CreateEntry(entryName).Open())
                       {
                          fileStream.CopyTo(fileStreamInZip);
                       }
                     }
                 }

                 return true;
              }
              catch (Exception ex)
              {
                 Log.LogErrorFromException(ex);
                 return false;
              }
           }

           /// <summary>Get a semantic version from the mod manifest (if available).</summary>
           public string GetManifestVersion()
           {
              // Get the file JSON string
              string json = "";
              foreach(ITaskItem file in Files)
              {
                 if(Path.GetFileName(file.ItemSpec).ToLower() != "manifest.json")
                    continue;
                 json = File.ReadAllText(file.ItemSpec);
                 break;
              }

              // Serialize the manifest json into a data object, then get a version object from that.
              IDictionary<string, object> data = (IDictionary<string, object>)new JavaScriptSerializer().DeserializeObject(json);
              IDictionary<string, object> version = (IDictionary<string, object>)data["Version"];

              // Store our version numbers for ease of use
              int major = (int)version["MajorVersion"];
              int minor = (int)version["MinorVersion"];
              int patch = (int)version["PatchVersion"];

              return String.Format("{0}.{1}.{2}", major, minor, patch);
           }
        }
        ]]>
      </Code>
    </Task>
  </UsingTask>


  <!--*********************************************
  ** Find the basic mod metadata
  **********************************************-->
  <!--######
  ## import developer's custom settings (if any)
  #######-->
  <Import Condition="$(OS) != 'Windows_NT' AND Exists('$(HOME)\stardewvalley.targets')" Project="$(HOME)\stardewvalley.targets" />
  <Import Condition="$(OS) == 'Windows_NT' AND Exists('$(USERPROFILE)\stardewvalley.targets')" Project="$(USERPROFILE)\stardewvalley.targets" />

  <!--######
  ## find platform + game path
  #######-->
  <Choose>
    <When Condition="$(OS) == 'Unix' OR $(OS) == 'OSX'">
      <PropertyGroup>
        <!-- Linux -->
        <GamePath Condition="!Exists('$(GamePath)')">$(HOME)/GOG Games/Stardew Valley/game</GamePath>
        <GamePath Condition="!Exists('$(GamePath)')">$(HOME)/.local/share/Steam/steamapps/common/Stardew Valley</GamePath>

        <!-- Mac (may be 'Unix' or 'OSX') -->
        <GamePath Condition="!Exists('$(GamePath)')">/Applications/Stardew Valley.app/Contents/MacOS</GamePath>
        <GamePath Condition="!Exists('$(GamePath)')">$(HOME)/Library/Application Support/Steam/steamapps/common/Stardew Valley/Contents/MacOS</GamePath>
      </PropertyGroup>
    </When>
    <When Condition="$(OS) == 'Windows_NT'">
      <PropertyGroup>
        <GamePath Condition="!Exists('$(GamePath)')">C:\Program Files (x86)\GalaxyClient\Games\Stardew Valley</GamePath>
        <GamePath Condition="!Exists('$(GamePath)')">C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley</GamePath>
        <GamePath Condition="!Exists('$(GamePath)')">$([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\GOG.com\Games\1453375253', 'PATH', null, RegistryView.Registry32))</GamePath>
        <GamePath Condition="!Exists('$(GamePath)')">$([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 413150', 'InstallLocation', null, RegistryView.Registry64, RegistryView.Registry32))</GamePath>
      </PropertyGroup>
    </When>
  </Choose>


  <!--*********************************************
  ** Inject the assembly references and debugging configuration
  **********************************************-->
  <Choose>
    <When Condition="$(OS) == 'Windows_NT'">
      <!-- references -->
      <ItemGroup>
        <Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
          <Private>false</Private>
        </Reference>
        <Reference Include="Microsoft.Xna.Framework.Game, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
          <Private>false</Private>
        </Reference>
        <Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
          <Private>false</Private>
        </Reference>
        <Reference Include="Microsoft.Xna.Framework.Xact, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
          <Private>false</Private>
        </Reference>
        <Reference Include="Stardew Valley">
          <HintPath>$(GamePath)\Stardew Valley.exe</HintPath>
          <Private>false</Private>
        </Reference>
        <Reference Include="StardewModdingAPI">
          <HintPath>$(GamePath)\StardewModdingAPI.exe</HintPath>
          <Private>false</Private>
        </Reference>
        <Reference Include="xTile, Version=2.0.4.0, Culture=neutral, processorArchitecture=x86">
          <HintPath>$(GamePath)\xTile.dll</HintPath>
          <Private>false</Private>
          <SpecificVersion>False</SpecificVersion>
        </Reference>
      </ItemGroup>

      <!-- launch game for debugging -->
      <PropertyGroup>
        <StartAction>Program</StartAction>
        <StartProgram>$(GamePath)\StardewModdingAPI.exe</StartProgram>
        <StartWorkingDirectory>$(GamePath)</StartWorkingDirectory>
      </PropertyGroup>
    </When>
    <Otherwise>
      <!-- references -->
      <ItemGroup>
        <Reference Include="MonoGame.Framework">
          <HintPath>$(GamePath)\MonoGame.Framework.dll</HintPath>
          <Private>false</Private>
          <SpecificVersion>False</SpecificVersion>
        </Reference>
        <Reference Include="StardewValley">
          <HintPath>$(GamePath)\StardewValley.exe</HintPath>
          <Private>false</Private>
        </Reference>
        <Reference Include="StardewModdingAPI">
          <HintPath>$(GamePath)\StardewModdingAPI.exe</HintPath>
          <Private>false</Private>
        </Reference>
        <Reference Include="xTile">
          <HintPath>$(GamePath)\xTile.dll</HintPath>
          <Private>false</Private>
        </Reference>
      </ItemGroup>
    </Otherwise>
  </Choose>


  <!--*********************************************
  ** Perform build logic
  **********************************************-->
  <!--######
  ## validate metadata before build
  #######-->
  <Target Name="BeforeBuild">
    <!-- show error for unknown platform -->
    <Error Condition="'$(OS)' != 'OSX' AND '$(OS)' != 'Unix' AND '$(OS)' != 'Windows_NT'" Text="The build config package doesn't recognise OS type '$(OS)'." />

    <!-- if game path is invalid, show one user-friendly error instead of a slew of reference errors -->
    <Error Condition="!Exists('$(GamePath)')" Text="Failed to find the game install path. See https://github.com/Pathoschild/Stardew.ModBuildConfig#troubleshoot for help." />
    <Error Condition="'$(OS)' == 'Windows_NT' AND !Exists('$(GamePath)\Stardew Valley.exe')" Text="Found a game folder at $(GamePath), but it doesn't contain Stardew Valley. You should delete this folder if it's empty." />
    <Error Condition="'$(OS)' != 'Windows_NT' AND !Exists('$(GamePath)\StardewValley.exe')" Text="Found a game folder at $(GamePath), but it doesn't contain Stardew Valley. You should delete this folder if it's empty." />
    <Error Condition="!Exists('$(GamePath)\StardewModdingAPI.exe')" Text="Found a game folder at $(GamePath), but it doesn't contain SMAPI." />
  </Target>

  <!--######
  ## Deploy files after build
  #######-->
  <Target Name="AfterBuild" Condition="'$(DeployModFolderName)' != '' OR '$(DeployModZipTo)' != ''">
    <!--collect file paths-->
    <PropertyGroup>
      <ModDeployPath>$(GamePath)\Mods\$(DeployModFolderName)</ModDeployPath>
      <DeployModZipTo Condition="'$(OS)' != 'Windows_NT'"><!--disable on Linux/Mac where CodeTaskFactory doesn't seem to be available--></DeployModZipTo>
    </PropertyGroup>
    <ItemGroup>
      <BuildFiles Include="$(TargetDir)\**\*.*" Exclude="$(TargetDir)\manifest.json;$(TargetDir)\i18n\**\*.*" />

      <BuildFiles Include="$(ProjectDir)\manifest.json" Condition="'@(BuildFiles)' != ''" />
      <BuildFiles Include="$(TargetDir)\manifest.json" Condition="'@(BuildFiles)' != '' AND !EXISTS('$(ProjectDir)\manifest.json')" />

      <I18nFiles Include="$(ProjectDir)\i18n\*.json" Condition="'@(BuildFiles)' != ''" />
      <I18nFiles Include="$(TargetDir)\i18n\*.json" Condition="'@(BuildFiles)' != '' AND !EXISTS('$(ProjectDir)\i18n')" />
    </ItemGroup>

    <!--validate paths-->
    <Error Text="Could not deploy mod automatically because no build output was found." Condition="'@(BuildFiles)' == ''" />
    <Error Text="Could not deploy mod automatically because no manifest.json was found in the project or build output." Condition="!Exists('$(TargetDir)\manifest.json') AND !Exists('$(ProjectDir)\manifest.json')" />

    <!-- copy mod files into mod folder if <DeployModFolderName> property is set -->
    <Message Text="Deploying mod to $(ModDeployPath)..." Importance="high" Condition="'$(DeployModFolderName)' != ''" />
    <Copy SourceFiles="@(BuildFiles)" DestinationFolder="$(ModDeployPath)\%(RecursiveDir)" SkipUnchangedFiles="true" Condition="'$(DeployModFolderName)' != ''" />
    <Copy SourceFiles="@(I18nFiles)" DestinationFolder="$(ModDeployPath)\i18n" SkipUnchangedFiles="true" Condition="'$(DeployModFolderName)' != ''" />

    <!-- create release zip if <DeployModZipTo> property is set -->
    <Message Text="Generating mod release at $(DeployModZipTo)\$(MSBuildProjectName).zip..." Importance="high" Condition="'$(DeployModZipTo)' != ''" />
    <CreateModReleaseZip ModName="$(MSBuildProjectName)" Files="@(BuildFiles);@(I18nFiles)" OutputFolderPath="$(DeployModZipTo)" Condition="'$(DeployModZipTo)' != ''" />
  </Target>
</Project>