Skip to content

Commit 1d16c29

Browse files
committed
WinterPatch update
1 parent 5aa6560 commit 1d16c29

27 files changed

+513
-67
lines changed

GRUnlocker/ByteUtils.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ public static int GetPrevNewLineAnchor(byte[] arr, int current) {
1919
return 0;
2020
}
2121

22+
public static void ReplaceByteRange(ref byte[] bytes, byte[] newRange, int index) {
23+
var lst = bytes.ToList();
24+
lst.RemoveRange(index, newRange.Count());
25+
lst.InsertRange(index, newRange);
26+
bytes = lst.ToArray();
27+
}
28+
2229
public static void ReplaceByteRange(ref byte[] bytes, byte[] newRange, string fromString, string toString, int fromIndex = 0) {
2330

2431
List<byte> lst = bytes.ToList();

GRUnlocker/Config.cs

Lines changed: 80 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,23 @@ namespace GRUnlocker {
66
class Config {
77

88
// Global settings
9-
public string SaveDirectory { get; private set; } = "";
9+
private string SaveDirectory;
1010
public string GameDirectory { get; private set; } = "";
1111

12-
public bool IsLocalSavePath = true;
13-
public bool ValidGameDirectory = false;
12+
// save file
13+
public bool DisplayPath { get; private set; } = false;
14+
public enum SaveType { None, SteamOrGOG, EGS }
15+
public enum SaveLocation { None, Local, Remote, Auto }
16+
17+
public SaveType saveType = SaveType.None;
18+
public SaveLocation saveLocation = SaveLocation.None;
19+
public string SaveFilePath { get; private set; } = "";
20+
1421

1522
// Private settings
16-
private const string ConfigFileName = "config.json";
23+
private const string FILE_NAME_CONFIG = "config.json";
24+
private const string FILE_NAME_Steam = "Ghostrunner.sav";
25+
private const string FILE_NAME_EGS = "GhostrunnerSave.sav";
1726

1827
// single instance
1928
private static Config Instance;
@@ -24,18 +33,29 @@ public static Config getInstance() {
2433
}
2534

2635
// Load config file
27-
public void Load() {
28-
if(File.Exists(ConfigFileName)) {
36+
public void Load(string[] args) {
37+
// ARGS
38+
if(args.Contains("-displaypath"))
39+
DisplayPath = true;
40+
41+
// config file found?
42+
if(File.Exists(FILE_NAME_CONFIG)) {
2943
if(!ParseManually()) {
3044
Console.WriteLine("Error: config.json file\n -Invalid/missing directories or corrupted config.json file");
3145
InputHandler.ExitProgram();
3246
}
3347
} else {
3448
// no config file, local path file
35-
SaveDirectory = "";
36-
GameDirectory = "";
37-
IsLocalSavePath = true;
49+
SaveDirectory = Directory.GetCurrentDirectory();
50+
if(DetectFile()) { // file found in same directory, flag as local
51+
saveLocation = SaveLocation.Local;
52+
}
3853
}
54+
55+
56+
// failed to find local + remote(config file), try auto detect
57+
if(saveLocation == SaveLocation.None)
58+
AutoDetect();
3959
}
4060

4161
// Note: creating and parsing manually to avoid 3'rd party .dll dependencies like Newtonsoft-Json,
@@ -47,15 +67,48 @@ public bool SaveManually() {
4767
$" \"{nameof(SaveDirectory)}\": \"\",\n" +
4868
$" \"{nameof(GameDirectory)}\": \"\"\n" +
4969
"}";
50-
File.WriteAllText(ConfigFileName, str);
70+
File.WriteAllText(FILE_NAME_CONFIG, str);
5171
return true;
5272
} catch(Exception) {}
5373
return false;
5474
}
5575

76+
private bool AutoDetect() {
77+
// attempts to auto find .sav file
78+
string savePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Ghostrunner\Saved\SaveGames\");
79+
if(!Directory.Exists(savePath)) return false;
80+
81+
82+
// EGS VERSION
83+
if(File.Exists(Path.Combine(savePath, FILE_NAME_EGS))) {
84+
// found it - EGS
85+
SaveFilePath = Path.Combine(savePath, FILE_NAME_EGS);
86+
saveType = SaveType.EGS;
87+
saveLocation = SaveLocation.Auto;
88+
return true;
89+
}
90+
91+
// STEAM VERSION
92+
var folders = Directory.EnumerateDirectories(savePath).ToList();
93+
for(int i = 0; i < folders.Count(); i++) {
94+
long steamUserID = -1;
95+
if(Path.GetFileName(folders[i]).Length > 15 && Path.GetFileName(folders[i]).Length < 20 && long.TryParse(Path.GetFileName(folders[i]), out steamUserID) && steamUserID > 0) {
96+
savePath = Path.Combine(savePath, "" + steamUserID, FILE_NAME_Steam);
97+
if(File.Exists(savePath)) {
98+
// found it - STEAM/GOG
99+
SaveFilePath = savePath;
100+
saveType = SaveType.SteamOrGOG;
101+
saveLocation = SaveLocation.Auto;
102+
return true;
103+
}
104+
}
105+
}
106+
return false;
107+
}
108+
56109
private bool ParseManually() {
57-
if(!File.Exists(ConfigFileName)) return false;
58-
var result = File.ReadAllText(ConfigFileName)
110+
if(!File.Exists(FILE_NAME_CONFIG)) return false;
111+
var result = File.ReadAllText(FILE_NAME_CONFIG)
59112
.Split(new[] { '\n', '\r', ',', '\"' }, StringSplitOptions.RemoveEmptyEntries)
60113
.Where(x => !string.IsNullOrWhiteSpace(x.Trim())).ToArray();
61114

@@ -68,12 +121,12 @@ private bool ParseManually() {
68121
// check save directory
69122
if(Directory.Exists(result[1])) {
70123
SaveDirectory = result[1];
71-
IsLocalSavePath = false;
124+
saveLocation = SaveLocation.Remote;
125+
DetectFile();
72126
}
73127
// check game directory
74128
if(Directory.Exists(result[3]) && GameDirectoryStructureValid(result[3])) {
75129
GameDirectory = result[3];
76-
ValidGameDirectory = true;
77130
}
78131
return true;
79132
}
@@ -82,6 +135,19 @@ private bool ParseManually() {
82135
return false;
83136
}
84137

138+
private bool DetectFile() {
139+
if(File.Exists(Path.Combine(SaveDirectory, FILE_NAME_Steam))) {
140+
SaveFilePath = Path.Combine(SaveDirectory, FILE_NAME_Steam);
141+
saveType = SaveType.SteamOrGOG;
142+
return true;
143+
} else if(File.Exists(Path.Combine(SaveDirectory, FILE_NAME_EGS))) {
144+
SaveFilePath = Path.Combine(SaveDirectory, FILE_NAME_EGS);
145+
saveType = SaveType.SteamOrGOG;
146+
return true;
147+
}
148+
return false;
149+
}
150+
85151
private bool GameDirectoryStructureValid(string path) {
86152
// 2 directories "Engine" and "Ghostrunner", 1 file "Ghostrunner.exe"
87153
return (Directory.Exists(Path.Combine(path, "Engine")) && Directory.Exists(Path.Combine(path, "Ghostrunner")) && File.Exists(Path.Combine(path, "Ghostrunner.exe")));

GRUnlocker/GRUnlocker.csproj

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@
6666
<PropertyGroup>
6767
<ApplicationManifest>Properties\app.manifest</ApplicationManifest>
6868
</PropertyGroup>
69+
<PropertyGroup>
70+
<ApplicationIcon>icon%281%29.ico</ApplicationIcon>
71+
</PropertyGroup>
6972
<ItemGroup>
7073
<Reference Include="System" />
7174
<Reference Include="System.Core" />
@@ -98,6 +101,22 @@
98101
<None Include="Resources\defaultLevelDetails.sav" />
99102
<None Include="Resources\defaultLevelDetails_2.sav" />
100103
<None Include="Resources\EmptyUnlockedList.sav" />
104+
<None Include="Resources\Ghostrunner_hc_1.sav" />
105+
<None Include="Resources\Ghostrunner_hc_10.sav" />
106+
<None Include="Resources\Ghostrunner_hc_11.sav" />
107+
<None Include="Resources\Ghostrunner_hc_12.sav" />
108+
<None Include="Resources\Ghostrunner_hc_13.sav" />
109+
<None Include="Resources\Ghostrunner_hc_14.sav" />
110+
<None Include="Resources\Ghostrunner_hc_15.sav" />
111+
<None Include="Resources\Ghostrunner_hc_16.sav" />
112+
<None Include="Resources\Ghostrunner_hc_2.sav" />
113+
<None Include="Resources\Ghostrunner_hc_3.sav" />
114+
<None Include="Resources\Ghostrunner_hc_4.sav" />
115+
<None Include="Resources\Ghostrunner_hc_5.sav" />
116+
<None Include="Resources\Ghostrunner_hc_6.sav" />
117+
<None Include="Resources\Ghostrunner_hc_7.sav" />
118+
<None Include="Resources\Ghostrunner_hc_8.sav" />
119+
<None Include="Resources\Ghostrunner_hc_9.sav" />
101120
<None Include="Resources\Ghostrunner_lvl1.sav" />
102121
<None Include="Resources\Ghostrunner_lvl10.sav" />
103122
<None Include="Resources\Ghostrunner_lvl11.sav" />
@@ -115,6 +134,7 @@
115134
<None Include="Resources\Ghostrunner_lvl8.sav" />
116135
<None Include="Resources\Ghostrunner_lvl9.sav" />
117136
<None Include="Resources\Hundo.sav" />
137+
<None Include="Resources\hundoHC.sav" />
118138
<None Include="Resources\Levels.sav" />
119139
<None Include="Resources\newgame.sav" />
120140
<None Include="Resources\Newgame_collectiables.sav" />
@@ -152,6 +172,8 @@
152172
<Install>false</Install>
153173
</BootstrapperPackage>
154174
</ItemGroup>
155-
<ItemGroup />
175+
<ItemGroup>
176+
<Content Include="icon%281%29.ico" />
177+
</ItemGroup>
156178
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
157179
</Project>

GRUnlocker/InputHandler.cs

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,32 @@ public class InputHandler {
99
public InputHandler() {
1010
// print intro
1111
Console.WriteLine("════Ghostrunner Unlocker [by DmgVol]════");
12-
Console.WriteLine($"Save path: {(Config.getInstance().IsLocalSavePath ? "local(same directory)" : "remote(from config)")}");
12+
Console.WriteLine($"Save Path: {Config.getInstance().saveLocation}\nSave Type: {Config.getInstance().saveType}");
13+
if(Config.getInstance().DisplayPath) Console.WriteLine($"Path: {Config.getInstance().SaveFilePath}");
14+
Console.WriteLine();
1315
bool SavFileFound = CheckSaveExists(false);
14-
if(SavFileFound) Console.WriteLine($"Save version: {(Unlocker.IsSteam ? $"Steam ({Unlocker.FILE_NAME_Steam})" : $"EGS ({Unlocker.FILE_NAME_EGS})")}\n");
15-
16+
1617
if(SavFileFound) {
1718
// ==unlocking==
1819
options.Add(new Option_UnlockAllCollectibles());
1920
options.Add(new Option_UnlockAllLevels());
20-
options.Add(new Option_UnlockEverything());
21-
options.Add(new Option_UnlockUpToLevel());
22-
options.Add(new Option_NewGameCollectibles());
23-
options.Add(new Option_NewGameSword());
24-
options.Add(new Option_ReplaceSelectedSword());
21+
options.Add(new Option_UnlockAllLevelsHC());
22+
options.Add(new Option_UnlockEverything());
23+
options.Add(new Option_UnlockEverythingHC());
24+
options.Add(new Option_UnlockUpToLevel());
25+
options.Add(new Option_UnlockUpToLevelHC());
26+
options.Add(new Option_NewGameCollectibles());
27+
options.Add(new Option_NewGameSword());
28+
options.Add(new Option_ReplaceSelectedSword());
2529
// ==resetting==
2630
options.Add(new Option_ResetLevelDetails());
2731
options.Add(new Option_ResetCollectibles());
2832

2933
// ==game related==
30-
if(!Config.getInstance().IsLocalSavePath)
34+
if(Config.getInstance().saveLocation == Config.SaveLocation.Remote)
3135
options.Add(new Option_ToggleIntros());
3236
options.Add(new Option_RemoveKevin());
37+
options.Add(new Option_WinterDLC());
3338
}
3439

3540
// == GRUnlocker related==
@@ -45,23 +50,18 @@ public void Handle() {
4550
}
4651
Console.WriteLine("─────────────────────────────────");
4752

48-
// read input or args if any
49-
string input = "";
50-
if(Program.ARGS != null && Program.ARGS.Length == 1) {
51-
input += Program.ARGS[0][0];
52-
Console.WriteLine("Using args: " + input);
53-
} else {
54-
Console.Write("\nOption:\t");
55-
input = Console.ReadLine();
56-
}
53+
// read input
54+
Console.Write("\nOption:\t");
55+
string input = Console.ReadLine();
56+
5757

5858
// valid?
5959
bool successFlag = true;
6060
int optionNumber;
6161
if(Int32.TryParse(input, out optionNumber) && optionNumber > 0 && optionNumber < options.Count + 1) {
6262
int i = optionNumber - 1;
6363
// special case - unlock up to level, requires more input
64-
if(options[i] is Option_UnlockUpToLevel) {
64+
if(options[i] is Option_UnlockUpToLevel || options[i] is Option_UnlockUpToLevelHC) {
6565
Console.WriteLine("Up to which level to unlock? (1-16)");
6666
Console.WriteLine("─────────────────────────────────");
6767
Console.Write(">");
@@ -85,7 +85,7 @@ public void Handle() {
8585
}
8686

8787
// print results
88-
if(successFlag)
88+
if(successFlag)
8989
Console.WriteLine("Patched successfully!");
9090
else
9191
Console.WriteLine("Error: failed to patch!\n-Something went wrong, missing permissions/files or corrupted .sav file");
@@ -107,12 +107,22 @@ private int GetOptionNumber(int min, int max) {
107107

108108
public static bool CheckSaveExists(bool exitIfMissing = true) {
109109
if(!Unlocker.FileExists()) {
110-
if(Config.getInstance().IsLocalSavePath)
111-
Console.WriteLine("Error: Missing .sav file!\n-Make sure the save file is in the same directory as the executable");
112-
else
113-
Console.WriteLine("Error: Missing .sav file!\n-Save file is missing from the given path (config file)");
110+
Console.WriteLine("Error: Missing .sav file!");
114111

115-
if(exitIfMissing) ExitProgram();
112+
// error msg based on save location
113+
switch(Config.getInstance().saveLocation) {
114+
case Config.SaveLocation.Local:
115+
Console.WriteLine("-Make sure the save file is in the same directory as the executable.");
116+
break;
117+
case Config.SaveLocation.Remote:
118+
Console.WriteLine("-Save file is missing from the given path (config file).");
119+
break;
120+
case Config.SaveLocation.Auto:
121+
Console.WriteLine("-Can't find save file.");
122+
break;
123+
}
124+
125+
if(exitIfMissing) ExitProgram();
116126
return false;
117127
}
118128
return true;
@@ -123,4 +133,4 @@ public static void ExitProgram() {
123133
Environment.Exit(0);
124134
}
125135
}
126-
}
136+
}

GRUnlocker/OptionCore.cs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,36 @@ public Option_UnlockAllCollectibles() : base("Unlock all collectibles") { }
1717
}
1818

1919
public class Option_UnlockAllLevels : OptionCore {
20-
public Option_UnlockAllLevels() : base("Unlock all levels") { }
20+
public Option_UnlockAllLevels() : base("Unlock all levels (classic levels)") { }
2121
public override bool Patch(int index) => unlocker.UnlockLevels();
2222
}
2323

24+
public class Option_UnlockAllLevelsHC : OptionCore {
25+
public Option_UnlockAllLevelsHC() : base("Unlock all levels (classic + hardcore levels)") { }
26+
public override bool Patch(int index) => unlocker.UnlockLevelsHC();
27+
}
28+
29+
2430
public class Option_UnlockEverything : OptionCore {
25-
public Option_UnlockEverything() : base("Unlock everything (100%)") { }
31+
public Option_UnlockEverything() : base("Unlock everything (100%: classic levels)") { }
2632
public override bool Patch(int index) => unlocker.UnlockAll();
2733
}
2834

35+
public class Option_UnlockEverythingHC : OptionCore {
36+
public Option_UnlockEverythingHC() : base("Unlock everything (100%: classic + hardcore levels)") { }
37+
public override bool Patch(int index) => unlocker.UnlockAllHC();
38+
}
39+
2940
public class Option_UnlockUpToLevel : OptionCore {
30-
public Option_UnlockUpToLevel() : base("Unlock up to a specific level (1-16)") { }
41+
public Option_UnlockUpToLevel() : base("Unlock up to a specific level (1-16) (classic)") { }
3142
public override bool Patch(int index) => unlocker.UnlockUpToLevel(index);
3243
}
3344

45+
public class Option_UnlockUpToLevelHC : OptionCore {
46+
public Option_UnlockUpToLevelHC() : base("Unlock up to a specific level (1-16) (hardcore)") { }
47+
public override bool Patch(int index) => unlocker.UnlockUpToLevelHC(index);
48+
}
49+
3450
public class Option_NewGameCollectibles : OptionCore {
3551
public Option_NewGameCollectibles() : base("New-game with collectibles unlocked") { }
3652
public override bool Patch(int index) => unlocker.NewGameCollectiables();
@@ -73,4 +89,14 @@ public Option_RemoveKevin() : base("Remove Kevin (the drone)") { }
7389
// Don't worry, nothing is being changed, just a silly little easteregg.
7490
public override bool Patch(int index) => true; // 'LOL NOPE'
7591
}
92+
93+
public class Option_WinterDLC : OptionCore {
94+
public Option_WinterDLC() : base("Unlock Winter DLC (sword+gloves)") { }
95+
// You really think I'll unlock the dlc for free!?? NO! go buy and support the devs! ($1.99)
96+
// Don't worry, nothing is being changed.
97+
public override bool Patch(int index) {
98+
System.Console.WriteLine("What did you expect? go buy it!\nhttps://store.steampowered.com/app/1480500/Ghostrunner__Winter_Pack/\n");
99+
return true;
100+
}
101+
}
76102
}

0 commit comments

Comments
 (0)