Skip to content

Commit

Permalink
Add .mhwslot support and add backup feature
Browse files Browse the repository at this point in the history
  • Loading branch information
Fusion86 committed Jan 29, 2024
1 parent 3420edb commit d5e186f
Show file tree
Hide file tree
Showing 9 changed files with 277 additions and 36 deletions.
1 change: 1 addition & 0 deletions src/MHWSaveTransfer/FodyWeavers.xml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<PropertyChanged />
<Costura/>
</Weavers>
116 changes: 116 additions & 0 deletions src/MHWSaveTransfer/FodyWeavers.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,122 @@
</xs:attribute>
</xs:complexType>
</xs:element>
<xs:element name="Costura" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:all>
<xs:element minOccurs="0" maxOccurs="1" name="ExcludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="IncludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="ExcludeRuntimeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="IncludeRuntimeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="Unmanaged32Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="Unmanaged64Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="PreloadOrder" type="xs:string">
<xs:annotation>
<xs:documentation>The order of preloaded assemblies, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:all>
<xs:attribute name="CreateTemporaryAssemblies" type="xs:boolean">
<xs:annotation>
<xs:documentation>This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeDebugSymbols" type="xs:boolean">
<xs:annotation>
<xs:documentation>Controls if .pdbs for reference assemblies are also embedded.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeRuntimeReferences" type="xs:boolean">
<xs:annotation>
<xs:documentation>Controls if runtime assemblies are also embedded.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="UseRuntimeReferencePaths" type="xs:boolean">
<xs:annotation>
<xs:documentation>Controls whether the runtime assemblies are embedded with their full path or only with their assembly name.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="DisableCompression" type="xs:boolean">
<xs:annotation>
<xs:documentation>Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="DisableCleanup" type="xs:boolean">
<xs:annotation>
<xs:documentation>As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="LoadAtModuleInit" type="xs:boolean">
<xs:annotation>
<xs:documentation>Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IgnoreSatelliteAssemblies" type="xs:boolean">
<xs:annotation>
<xs:documentation>Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="ExcludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with |</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="ExcludeRuntimeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with |</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeRuntimeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="Unmanaged32Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="Unmanaged64Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="PreloadOrder" type="xs:string">
<xs:annotation>
<xs:documentation>The order of preloaded assemblies, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
Expand Down
103 changes: 103 additions & 0 deletions src/MHWSaveTransfer/Helpers/BackupHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
using Cirilla.Core.Models;
using Serilog;
using System;
using System.IO;
using System.Linq;

namespace MHWSaveTransfer.Helpers
{
// Mostly copy-pasted from https://github.com/Fusion86/MHWAppearanceEditor/blob/master/src/MHWAppearanceEditor/Services/BackupService.cs
internal class BackupHelper
{
private static readonly int MaxBackupCount = 10;
private static readonly ILogger log = Log.ForContext<BackupHelper>();

private readonly string backupDir;

public BackupHelper()
{
backupDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "MHWSaveTransfer", "backup");

try
{
if (!Directory.Exists(backupDir))
{
Directory.CreateDirectory(backupDir);

string readme = "MHWSaveTransfer uses this folder for its automated backup feature.\n";
readme += "You can disable this feature by setting ' \"MaxBackupCount\": 0 ' in ../config.json\n\n";
readme += "Do NOT manually add files to this folder, MHWSaveTransfer may delete them at any time to make more space!";
File.WriteAllText(Path.Combine(backupDir, "README.txt"), readme);
}
}
catch (Exception ex)
{
log.Error(ex.Message);
}
}

public void CreateBackup(SaveData saveData)
{
if (MaxBackupCount == 0) return;

string fileName = "SAVEDATA_" + DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
string backupPath = Path.Combine(backupDir, fileName);
string infoPath = Path.Combine(backupDir, fileName + ".txt");

// Write SAVEDATA
File.Copy(saveData.Filepath, backupPath);

// Write info
string info = "";
for (int i = 0; i < saveData.SaveSlots.Length; i++)
{
info += $"SaveSlot {i + 1}:\n";

var timespan = TimeSpan.FromSeconds(saveData.SaveSlots[i].PlayTime);
string timeStr = (int)timespan.TotalHours + ":" + timespan.ToString(@"mm\:ss");

// TODO: This used the **edited** huntername, rank, etc but the actual backup contains the **unedited** values.
info += $"TimePlayed: {timeStr}\n";
info += $"HunterName: {saveData.SaveSlots[i].HunterName}\n";
info += $"HunterRank: {saveData.SaveSlots[i].HunterRank}\n";
info += $"MasterRank: {saveData.SaveSlots[i].MasterRank}\n";
info += $"HunterXp: {saveData.SaveSlots[i].HunterXp}\n";
info += $"MasterXp: {saveData.SaveSlots[i].MasterXp}\n";
info += $"PalicoName: {saveData.SaveSlots[i].PalicoName}\n\n";
}

File.WriteAllText(infoPath, info);

log.Information($"Created a backup at '{backupPath}'");

RemoveExcessiveBackups();
}

public void RemoveExcessiveBackups()
{
var files = Directory.GetFiles(backupDir, "*")
.Where(x => !x.EndsWith(".txt"))
.OrderByDescending(x => x)
.Skip(MaxBackupCount)
.ToList();

if (files.Count > 0)
{
log.Information($"Deleting {files.Count} backup(s) because they exceed the MaxBackupCount");

foreach (var file in files)
{
try
{
File.Delete(file);
File.Delete(file + ".txt");
}
catch (Exception ex)
{
log.Error("Couldn't delete backup: " + ex.Message);
}
}
}
}
}
}
2 changes: 0 additions & 2 deletions src/MHWSaveTransfer/Helpers/SteamUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace MHWSaveTransfer.Helpers
{
Expand Down
17 changes: 10 additions & 7 deletions src/MHWSaveTransfer/MHWSaveTransfer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,30 @@

<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net5.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<Version>0.3.0</Version>
<TargetFramework>net48</TargetFramework>
<UseWPF>true</UseWPF>
<Version>0.4.0</Version>
<Nullable>enable</Nullable>
<LangVersion>8.0</LangVersion>
<ApplicationIcon>icon.ico</ApplicationIcon>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AngleSharp" Version="0.14.0" />
<PackageReference Include="Costura.Fody" Version="5.7.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Gameloop.Vdf" Version="0.6.1" />
<PackageReference Include="Microsoft.CSharp" Version="4.*" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Serilog.Formatting.Compact" Version="1.1.0" />
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
<PackageReference Include="System.Net.Http" Version="4.*" />
<PackageReference Include="Fody" Version="6.3.0">
<PackageReference Include="Fody" Version="6.8.0">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="gong-wpf-dragdrop" Version="2.3.2" />
<PackageReference Include="PropertyChanged.Fody" Version="3.3.2">
<PackageReference Include="PropertyChanged.Fody" Version="4.1.0">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
Expand Down
6 changes: 3 additions & 3 deletions src/MHWSaveTransfer/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@
<MenuItem Header="Change hunter name" Command="{Binding ChangeHunterNameCommand}"/>
<MenuItem Header="Change palico name" Command="{Binding ChangePalicoNameCommand}"/>
<MenuItem Header="Toggle gender" Command="{Binding ToggleGenderCommand}"/>
<!--<Separator/>
<MenuItem Header="Export as .mhwslot"/>-->
<Separator/>
<MenuItem Header="Export as .mhwslot" Command="{Binding ExportMhwslotCommand}"/>
</ContextMenu>
</uc:SaveSlotItem.ContextMenu>
</uc:SaveSlotItem>
Expand Down Expand Up @@ -112,7 +112,7 @@
</ListView.ItemContainerStyle>
</ListView>

<Button Grid.Column="2" Grid.Row="4" Content="Import Profiles from another SaveData file" Command="{Binding ImportSaveDataCommand}"/>
<Button Grid.Column="2" Grid.Row="4" Content="Import Profiles from another SaveData or .mhwslot file" Command="{Binding ImportSaveDataCommand}"/>
</Grid>
</DockPanel>
</Window>

This file was deleted.

Loading

0 comments on commit d5e186f

Please sign in to comment.