Skip to content

Commit

Permalink
Merge pull request #23 from tolik518/refactorings
Browse files Browse the repository at this point in the history
refactorings
  • Loading branch information
tolik518 authored Oct 22, 2023
2 parents a8f8984 + c52cf33 commit 6485600
Show file tree
Hide file tree
Showing 22 changed files with 1,917 additions and 1,546 deletions.
30 changes: 26 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,37 @@
# SoG Savegame Editor ![Icon](https://returnnull.de/images/_64.png)
![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/tolik518/SoG_SGreader?label=latest%20version&style=flat-square) ![GitHub all releases](https://img.shields.io/github/downloads/tolik518/SoG_SGreader/total?style=flat-square)

## Download
Head over to the releases if you are just here for the [download link](https://github.com/tolik518/SoG_SGreader/releases), then press on `Assets`
on the latest version and download the `SoG_SGreader.exe` file
on the latest version and download the `SoG_SGreader.exe` file.

Or just [press here](https://github.com/tolik518/SoG_SGreader/releases/latest/download/SoG_SGreader.exe) to download the latest version.

## How to use
![SoG_SGreader](https://returnnull.de/images/SoG_SGreader7.png)

**SoG Savegame Editor** is a work in progress tool for editing [Secrets of Grindea](https://store.steampowered.com/app/269770/Secrets_of_Grindea/) savegames.
Currenty it can only edit character files.
You can edit your nickname, skill points, gold, equipment, inventory, your pets and much more is comming!
You can just run the exe and it will open a file dialog where you can select your savegame file.
After that you can edit your savegame and save it back to the same file.

Alternatively you can drag and drop your savegame file on the exe and it will open the savegame directly.

You can also use the command line to open a savegame file directly.
```
SoG_SGreader.exe <savegame path> # Open UI with savegame, path to savegame is optional
SoG_SGreader.exe --help # Show help
SoG_SGreader.exe --patch # Show the supported game version
SoG_SGreader.exe --version # Show the version of the program
SoG_SGreader.exe --json <savegame path> # Print json of the savegame to console
SoG_SGreader.exe --text <savegame path> # Show a short summary of the savegame
```

## Description


**SoG Savegame Editor** is a tool for editing [Secrets of Grindea](https://store.steampowered.com/app/269770/Secrets_of_Grindea/) savegames.
You can edit your nickname, skill points, gold, equipment, inventory, your pets and much more is coming!

If you are interested how the save game file is build, you can find the structure
of the save game on the [wiki page](https://github.com/tolik518/SoG_SGreader/wiki/Savegame-File-Structure) (not actively maintained).
Expand Down
42 changes: 21 additions & 21 deletions SoG_SGreader.Test/DataReaderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Linq;
using Xunit;
using Moq;
using SoG_SGreader.Wrapper;

namespace SoG_SGreader.Test
{
Expand All @@ -13,8 +14,7 @@ public class DataReaderTests

private static Player GetSaveGame(int saveGameNumber)
{
DataReader dataReader = new DataReader();
var txtConsoleMock = new Mock<ITextBoxWrapper>().Object;
var fakeTextBox = new FakeTextBox();

string projectDirectory = Directory.GetParent(AppDomain.CurrentDomain.BaseDirectory).Parent.Parent.Parent.FullName;

Expand All @@ -25,7 +25,7 @@ private static Player GetSaveGame(int saveGameNumber)

string filePath = Path.Combine(projectDirectory, "SoG_SGreader.Test", "SaveGames", saveGameNumber + ".cha");

return dataReader.ReadFromFile(filePath, txtConsoleMock);
return DataReader.ReadFromFile(filePath, fakeTextBox);
}

private static IEnumerable<object[]> Nicknames()
Expand Down Expand Up @@ -91,15 +91,15 @@ private void TestCanReadBirthday(int birthdayDay, int birthdayMonth, Player play

private static IEnumerable<object[]> FirstItem()
{
yield return new object[] { new Item(SogItems._Usable_CardAlbum, 1, 189755), GetSaveGame(0).Inventory };
yield return new object[] { new Item(SogItems._KeyItem_DivaMirror, 0, 158319), GetSaveGame(1).Inventory };
yield return new object[] { new Item(SogItems._Usable_CardAlbum, 1, 46623), GetSaveGame(2).Inventory };
yield return new object[] { new Item(SogItems._Usable_CardAlbum, 1, 16756), GetSaveGame(3).Inventory };
yield return new object[] { new Item(SogItems._Usable_CardAlbum, 1, 25026), GetSaveGame(4).Inventory };
yield return new object[] { new Item(SogItems._Usable_CardAlbum, 1, 18895), GetSaveGame(5).Inventory };
yield return new object[] { new Item(SogItems._Usable_CardAlbum, 1, 16634), GetSaveGame(6).Inventory };
yield return new object[] { new Item(SogItems._Usable_CardAlbum, 1, 14455), GetSaveGame(7).Inventory };
yield return new object[] { new Item(SogItems._Usable_CardAlbum, 1, 96617), GetSaveGame(8).Inventory };
yield return new object[] { new Item(SogItems.Usable_CardAlbum, 1, 189755), GetSaveGame(0).Inventory };
yield return new object[] { new Item(SogItems.KeyItem_DivaMirror, 0, 158319), GetSaveGame(1).Inventory };
yield return new object[] { new Item(SogItems.Usable_CardAlbum, 1, 46623), GetSaveGame(2).Inventory };
yield return new object[] { new Item(SogItems.Usable_CardAlbum, 1, 16756), GetSaveGame(3).Inventory };
yield return new object[] { new Item(SogItems.Usable_CardAlbum, 1, 25026), GetSaveGame(4).Inventory };
yield return new object[] { new Item(SogItems.Usable_CardAlbum, 1, 18895), GetSaveGame(5).Inventory };
yield return new object[] { new Item(SogItems.Usable_CardAlbum, 1, 16634), GetSaveGame(6).Inventory };
yield return new object[] { new Item(SogItems.Usable_CardAlbum, 1, 14455), GetSaveGame(7).Inventory };
yield return new object[] { new Item(SogItems.Usable_CardAlbum, 1, 96617), GetSaveGame(8).Inventory };
}

[Theory]
Expand All @@ -111,15 +111,15 @@ private void TestCanGetFirstItemFromInventory(Item firstItem, List<Item> invento

private static IEnumerable<object[]> LastItem()
{
yield return new object[] { new Item(SogItems._Furniture_Decoration_ArcadeChallengeTrophyF08, 1, 189760), GetSaveGame(0).Inventory };
yield return new object[] { new Item(SogItems._KeyItem_CatalystOfPower, 1, 159243), GetSaveGame(1).Inventory };
yield return new object[] { new Item(SogItems._KeyItem_CatalystOfPower, 1, 46457), GetSaveGame(2).Inventory };
yield return new object[] { new Item(SogItems._Shoes_MushroomSlippers, 1, 16738), GetSaveGame(3).Inventory };
yield return new object[] { new Item(SogItems._TwoHanded_BugNet, 0, 25025), GetSaveGame(4).Inventory };
yield return new object[] { new Item(SogItems._TwoHanded_BugNet, 0, 18894), GetSaveGame(5).Inventory };
yield return new object[] { new Item(SogItems._Furniture_Carpet_MasterHQRed, 0, 16614), GetSaveGame(6).Inventory };
yield return new object[] { new Item(SogItems._TwoHanded_BugNet, 0, 14454), GetSaveGame(7).Inventory };
yield return new object[] { new Item(SogItems._OneHanded_UgrasScroll, 1, 96375), GetSaveGame(8).Inventory };
yield return new object[] { new Item(SogItems.Furniture_Decoration_ArcadeChallengeTrophyF08, 1, 189760), GetSaveGame(0).Inventory };
yield return new object[] { new Item(SogItems.KeyItem_CatalystOfPower, 1, 159243), GetSaveGame(1).Inventory };
yield return new object[] { new Item(SogItems.KeyItem_CatalystOfPower, 1, 46457), GetSaveGame(2).Inventory };
yield return new object[] { new Item(SogItems.Shoes_MushroomSlippers, 1, 16738), GetSaveGame(3).Inventory };
yield return new object[] { new Item(SogItems.TwoHanded_BugNet, 0, 25025), GetSaveGame(4).Inventory };
yield return new object[] { new Item(SogItems.TwoHanded_BugNet, 0, 18894), GetSaveGame(5).Inventory };
yield return new object[] { new Item(SogItems.Furniture_Carpet_MasterHQRed, 0, 16614), GetSaveGame(6).Inventory };
yield return new object[] { new Item(SogItems.TwoHanded_BugNet, 0, 14454), GetSaveGame(7).Inventory };
yield return new object[] { new Item(SogItems.OneHanded_UgrasScroll, 1, 96375), GetSaveGame(8).Inventory };
}

[Theory]
Expand Down
142 changes: 142 additions & 0 deletions SoG_SGreader.Test/IntegrationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
using System;
using System.Diagnostics;
using System.IO;
using Xunit;

namespace SoG_SGreader.Test
{
public class IntegrationTests
{
private static string GetExePath()
{
string projectDirectory = Directory.GetParent(AppDomain.CurrentDomain.BaseDirectory).Parent.Parent.Parent.FullName;

// Exception for GitHub Actions Test Runner
if (Environment.GetEnvironmentVariable("GITHUB_WORKSPACE") != null) {
projectDirectory = Environment.GetEnvironmentVariable("GITHUB_WORKSPACE");
return Path.Combine(projectDirectory, "SoG_SGreader", "bin", "Release", "SoG_SGreader.exe");
}

return Path.Combine(projectDirectory, "SoG_SGreader", "bin", "Debug", "SoG_SGreader.exe");
}

private static string GetSaveGamePath(string saveGameNumber)
{
string projectDirectory = Directory.GetParent(AppDomain.CurrentDomain.BaseDirectory).Parent.Parent.Parent.FullName;

// Exception for GitHub Actions Test Runner
if (Environment.GetEnvironmentVariable("GITHUB_WORKSPACE") != null) {
projectDirectory = Environment.GetEnvironmentVariable("GITHUB_WORKSPACE");
}

return Path.Combine(projectDirectory, "SoG_SGreader.Test", "SaveGames", saveGameNumber + ".cha");
}

[Fact]
public void TestBadPath()
{
string arguments = "-t " + GetSaveGamePath("doesntexist");

// Start the process
using var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = GetExePath(),
Arguments = arguments,
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true,
}
};

process.Start();
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();

Assert.Contains("Could not find file", output);
Assert.Contains(Path.Combine("SaveGames", "doesntexist.cha"), output);
}

[Fact]
public void TestTextOutput()
{
string arguments = "-t " + GetSaveGamePath("1");

// Start the process
using var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = GetExePath(),
Arguments = arguments,
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true,
}
};

process.Start();
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();

Assert.Contains("Filesize: 4494", output);
Assert.Contains("Birthday: 24.6.1081", output);
Assert.Contains("ItemsMetCount: 124", output);
Assert.Contains("KilledEnemiesCount: 58", output);
}

[Fact]
public void TestJsonOutput()
{
string arguments = "-j " + GetSaveGamePath("1");

// Start the process
using var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = GetExePath(),
Arguments = arguments,
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true,
}
};

process.Start();
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();

Assert.Contains("\"MagicByte\": 116", output);
Assert.Contains("\"PlayTimeTotal\": 1645950", output);
Assert.Contains("\"UniquePlayerId\": 451873", output);
Assert.Contains("\"Cash\": 6873538", output);
}

[Fact]
public void TestPatchOutput()
{
string arguments = "--patch";

// Start the process
using var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = GetExePath(),
Arguments = arguments,
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true,
}
};

process.Start();
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();

Assert.Contains(FrmMain.CurrentPatch, output);
}
}
}
2 changes: 2 additions & 0 deletions SoG_SGreader.Test/SoG_SGreader.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<AssemblyName>SoG_SGreader.Test</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<LangVersion>8</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
Expand Down Expand Up @@ -67,6 +68,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="DataReaderTests.cs" />
<Compile Include="IntegrationTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs"/>
</ItemGroup>
<ItemGroup>
Expand Down
22 changes: 22 additions & 0 deletions SoG_SGreader/ComandLineOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using CommandLine;

namespace SoG_SGreader
{
class ComandLineOptions
{
[Value(0, MetaName = "savegame path", Required = false, HelpText = "Path to the savegame")]
public string SavegamePath { get; set; }

[Option('t', "text", HelpText = "Show a short summary of the savegame")]
public bool ShowText { get; set; }

[Option('j', "json", HelpText = "Print json of the savegame to console")]
public bool ShowJson { get; set; }

[Option('h', "help", HelpText = "Show help")]
public bool ShowHelp { get; set; }

[Option('p', "patch", HelpText = "Show the current supported patch version")]
public bool ShowPatch { get; set; }
}
}
19 changes: 13 additions & 6 deletions SoG_SGreader/DataReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ namespace SoG_SGreader
{
public class DataReader
{
private readonly Player playerObject = new Player();

public Player ReadFromFile(string fileName, ITextBoxWrapper txtConsole)
public static Player ReadFromFile(string fileName, ITextBoxWrapper txtConsole)
{
Player playerObject = new Player();

using (FileStream fileStream = new FileStream(fileName, FileMode.Open))
{
BinaryReader readBinary = new BinaryReader(fileStream);
Expand Down Expand Up @@ -64,10 +64,12 @@ public Player ReadFromFile(string fileName, ITextBoxWrapper txtConsole)
playerObject.Style.PantsColor = readBinary.ReadByte();

playerObject.Style.Sex = readBinary.ReadByte(); // female = 0; male = 1
txtConsole.AppendText("\r\nGender: " + playerObject.Style.Sex);

playerObject.NicknameLength = readBinary.ReadByte();

playerObject.Nickname = new string(readBinary.ReadChars(playerObject.NicknameLength));
txtConsole.AppendText("\r\nNickname: " + playerObject.Nickname);

playerObject.ItemsCount = readBinary.ReadInt32();

Expand Down Expand Up @@ -145,6 +147,7 @@ public Player ReadFromFile(string fileName, ITextBoxWrapper txtConsole)
txtConsole.AppendText("\r\nSkillCount: " + playerObject.SkillsCount);

playerObject.Level = readBinary.ReadInt16(); //Level
txtConsole.AppendText("\r\nLevel: " + playerObject.Level);

playerObject.ExpCurrent = readBinary.ReadInt32(); //currentexp
playerObject.ExpUnknown0 = readBinary.ReadInt32(); //something exp ?
Expand All @@ -154,6 +157,7 @@ public Player ReadFromFile(string fileName, ITextBoxWrapper txtConsole)
playerObject.SkillSilverPoints = readBinary.ReadInt16(); //Silver Skill Points
playerObject.SkillGoldPoints = readBinary.ReadInt16(); //Gold Skill Points
playerObject.Cash = readBinary.ReadInt32(); //cash
txtConsole.AppendText("\r\nCash: " + playerObject.Cash);

playerObject.PetsCount = readBinary.ReadByte();
txtConsole.AppendText("\r\nPetsCount: " + playerObject.PetsCount);
Expand Down Expand Up @@ -285,7 +289,8 @@ public Player ReadFromFile(string fileName, ITextBoxWrapper txtConsole)
txtConsole.AppendText("\r\n" + "KilledEnemiesCount: " + playerObject.KilledEnemiesCount);

playerObject.PotionsMax = readBinary.ReadByte();
txtConsole.AppendText("\r\n" + " PotionsMax: " + playerObject.PotionsMax);
txtConsole.AppendText("\r\n" + "PotionsMax: " + playerObject.PotionsMax);

playerObject.PotionsEquipped = readBinary.ReadByte();
txtConsole.AppendText("\r\n" + "PotionsEquipped: " + playerObject.PotionsEquipped);

Expand All @@ -306,7 +311,7 @@ public Player ReadFromFile(string fileName, ITextBoxWrapper txtConsole)
playerObject.UnknownVariable04 = readBinary.ReadInt32();
playerObject.UnknownVariable05 = readBinary.ReadInt32();
playerObject.PlayTimeTotal = readBinary.ReadInt32();
txtConsole.AppendText("\r\nPlayTimeTotal: " + (double)playerObject.PlayTimeTotal / 60 / 60 / 60 + " hours");
txtConsole.AppendText("\r\nPlayTimeTotal: " + Math.Round((double)playerObject.PlayTimeTotal / 60 / 60 / 60, 2) + " hours");
playerObject.UnknownVariable06 = readBinary.ReadByte();

playerObject.UnknownVariable07Count = readBinary.ReadInt16();
Expand Down Expand Up @@ -357,11 +362,13 @@ public Player ReadFromFile(string fileName, ITextBoxWrapper txtConsole)

txtConsole.AppendText("\r\nLength: " + readBinary.BaseStream.Length);
txtConsole.AppendText("\r\nPosition: " + readBinary.BaseStream.Position);
txtConsole.AppendText("\r\nIs Position and length the same?: " + (readBinary.BaseStream.Position == readBinary.BaseStream.Length).ToString());

readBinary.Close();
fileStream.Close();
}

txtConsole.AppendText("\r\n");

return playerObject;
}
}
Expand Down
Loading

0 comments on commit 6485600

Please sign in to comment.