-
Notifications
You must be signed in to change notification settings - Fork 2
/
Program.cs
172 lines (127 loc) · 6.08 KB
/
Program.cs
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
using NLog;
using System.CommandLine;
using System.Diagnostics;
using System.Text;
using Ionic.Zlib;
namespace StarfieldSaveTool;
class Program
{
private static Logger logger;
private const int BufferSize = 65536; // 64 KB
static async Task<int> Main(string[] args)
{
var fileArgument = new Argument<FileInfo>(name: "file", description: "Starfield Save File (.sfs) to read");
var jsonOutputOption = new Option<bool>(new[] { "--output-json-file", "-j" }, () => false, "Write JSON output to file");
var rawOutputOption = new Option<bool>(new[] { "--output-raw-file", "-r" }, () => false, "Write raw output to file");
var changeFileOption = new Option<bool>(new[] { "--change-file", "-c" }, () => false, "Test change and write back to file");
var rootCommand = new RootCommand("Decompress Starfield Save file")
{
fileArgument,
jsonOutputOption,
rawOutputOption,
changeFileOption
};
rootCommand.SetHandler(Start, fileArgument, jsonOutputOption, rawOutputOption, changeFileOption);
// logging stuff
// create a configuration instance
var config = new NLog.Config.LoggingConfiguration();
// create a console logging target
//var logConsole = new NLog.Targets.ConsoleTarget();
var debugConsole = new NLog.Targets.DebugSystemTarget();
// send logs with levels from Info to Fatal to the console
//config.AddRule(NLog.LogLevel.Warn, NLog.LogLevel.Fatal, logConsole);
// send logs with levels from Debug to Fatal to the console
config.AddRule(NLog.LogLevel.Debug, NLog.LogLevel.Fatal, debugConsole);
// apply the configuration
LogManager.Configuration = config;
// create a logger
logger = LogManager.GetCurrentClassLogger();
// need a bigger stdout buffer for large files
// Create a new StreamWriter with the desired buffer size
var stdoutWriter = new StreamWriter(Console.OpenStandardOutput(), new UTF8Encoding(false), BufferSize);
// Set the custom StreamWriter as the Console's output
stdoutWriter.AutoFlush = true;
Console.SetOut(stdoutWriter);
return await rootCommand.InvokeAsync(args);
}
private static void Start(FileInfo file, bool jsonOutputOption, bool rawOutputOption, bool changeFileOption)
{
Stopwatch sw = new Stopwatch();
sw.Start();
logger.Info("StarfieldSaveTool Started");
logger.Debug($"fileArgument={file.FullName}");
try
{
var sfs = new SfsFile(file);
sfs.ProcessFile();
var decompressedBytes = sfs.DecompressedChunks;
logger.Debug($"Decompressed {decompressedBytes.Length} bytes in {sw.Elapsed:ss\\.ffff} seconds.");
// write decompressed data to disk if option is set
if (rawOutputOption)
{
var path = Path.ChangeExtension(file.FullName, ".dat");
logger.Debug($"Writing Raw to {path}...");
File.WriteAllBytes(path, decompressedBytes);
logger.Info($"Raw output written to {path}");
}
// read the decompressed data into a new file
var dat = new DatFile(decompressedBytes);
dat.ProcessFile();
// write decompressed data to disk if option is set
if (changeFileOption)
{
sw.Restart();
// test change and write back to file?
//dat.ChangeFile();
//logger.Debug($"Compressed {dat.Data.Length} bytes in {sw.Elapsed:ss\\.ffff} seconds.");
// test write back to file
var path = AddSuffixToFilePath(file.FullName, "_modified");
sfs.WriteFile(path, dat.Data);
logger.Info($"sfs written to {path}");
}
var json = dat.ToJson();
if (jsonOutputOption)
{
var path = Path.ChangeExtension(file.FullName, ".json");
logger.Debug($"Writing JSON to {path}...");
File.WriteAllText(path, json);
logger.Info($"JSON output written to {path}");
}
// always write to console
Console.WriteLine(json);
logger.Debug($"Processed in {sw.Elapsed:s\\.ffff} seconds");
}
catch (FileNotFoundException fnfe)
{
// Exception handler for FileNotFoundException
// We just inform the user that there is no such file
logger.Error(fnfe.Message);
Console.Error.WriteLine(fnfe.Message);
}
catch (IOException ioe)
{
// Exception handler for other input/output exceptions
logger.Error(ioe.StackTrace);
Console.Error.WriteLine(ioe.Message);
}
catch (Exception ex)
{
// Exception handler for any other exception that may occur and was not already handled specifically
logger.Error(ex.ToString());
Console.Error.WriteLine(ex.Message);
}
//Console.ReadKey();
}
public static string AddSuffixToFilePath(string originalFilePath, string suffix)
{
// Extract the directory path
var directoryPath = Path.GetDirectoryName(originalFilePath);
// Extract the file name without the extension
var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(originalFilePath);
// Extract the file extension
var fileExtension = Path.GetExtension(originalFilePath);
// Concatenate the new file path with the suffix before the extension
var newFilePath = Path.Combine(directoryPath, $"{fileNameWithoutExtension}{suffix}{fileExtension}");
return newFilePath;
}
}