Skip to content

Commit

Permalink
Merge branch 'version-check'
Browse files Browse the repository at this point in the history
  • Loading branch information
insomnious committed Jul 24, 2024
2 parents 9a404ab + 06822cf commit 3f67923
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 76 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.

The format is based on [Common Changelog](https://common-changelog.org/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.2.0] - 2024-07-24

- Increased stdout buffer size to 64 KB to handle larger save files
- De-prettied (uglied? uglified?! made ugly!?!) the JSON output to save buffer size
- Packaging with NativeAOT to improve performance

## [1.1.0] - 2024-07-15

- Added `JsonVersion` in case we have to change the output format in the future
Expand All @@ -18,6 +24,7 @@ The format is based on [Common Changelog](https://common-changelog.org/) and thi

- Initial release

[1.2.0]: https://github.com/Nexus-Mods/StarfieldSaveTool/releases/tag/v1.2.0
[1.1.0]: https://github.com/Nexus-Mods/StarfieldSaveTool/releases/tag/v1.1.0
[1.0.1]: https://github.com/Nexus-Mods/StarfieldSaveTool/releases/tag/v1.0.1
[1.0.0]: https://github.com/Nexus-Mods/StarfieldSaveTool/releases/tag/v1.0.0
48 changes: 26 additions & 22 deletions DatFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,14 +112,14 @@ public void ProcessFile()
HeaderSize = br.ReadUInt32();

Header = ReadHeader(br);

SaveVersion = br.ReadByte();
CurrentGameVersionSize = br.ReadUInt16();
CurrentGameVersion = Encoding.ASCII.GetString(br.ReadBytes(CurrentGameVersionSize));
CreatedGameVersionSize = br.ReadUInt16();
CreatedGameVersion = Encoding.ASCII.GetString(br.ReadBytes(CreatedGameVersionSize));
PluginInfoSize = br.ReadUInt16();

PluginInfo = ReadPluginInfo(br, SaveVersion);
}

Expand Down Expand Up @@ -170,9 +170,9 @@ public string ToJson()
{
var options = new JsonSerializerOptions
{
WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
TypeInfoResolver = SourceGenerationContext.Default
TypeInfoResolver = SourceGenerationContext.Default,

};
return JsonSerializer.Serialize(this, options);

Check warning on line 177 in DatFile.cs

View workflow job for this annotation

GitHub Actions / build

Using member 'System.Text.Json.JsonSerializer.Serialize<TValue>(TValue, JsonSerializerOptions)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications.

Check warning on line 177 in DatFile.cs

View workflow job for this annotation

GitHub Actions / build

Using member 'System.Text.Json.JsonSerializer.Serialize<TValue>(TValue, JsonSerializerOptions)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.

Check warning on line 177 in DatFile.cs

View workflow job for this annotation

GitHub Actions / publish

Using member 'System.Text.Json.JsonSerializer.Serialize<TValue>(TValue, JsonSerializerOptions)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications.

Check warning on line 177 in DatFile.cs

View workflow job for this annotation

GitHub Actions / publish

Using member 'System.Text.Json.JsonSerializer.Serialize<TValue>(TValue, JsonSerializerOptions)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.

Check warning on line 177 in DatFile.cs

View workflow job for this annotation

GitHub Actions / publish

Using member 'System.Text.Json.JsonSerializer.Serialize<TValue>(TValue, JsonSerializerOptions)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications.

Check warning on line 177 in DatFile.cs

View workflow job for this annotation

GitHub Actions / publish

Using member 'System.Text.Json.JsonSerializer.Serialize<TValue>(TValue, JsonSerializerOptions)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.
}
Expand All @@ -192,15 +192,15 @@ private FilePluginInfo ReadPluginInfo(BinaryReader br, byte infoSaveVersion)
// loop through normal plugins
for (int i = 0; i < pluginInfo.PluginCount; i++)
{
pluginInfo.Plugins.Add(ReadPlugin(br));
pluginInfo.Plugins.Add(ReadPlugin(br, infoSaveVersion));
}

pluginInfo.LightPluginCount = br.ReadUInt16();

// loop through light plugins
for (int i = 0; i < pluginInfo.LightPluginCount; i++)
{
pluginInfo.LightPlugins.Add(ReadPlugin(br));
pluginInfo.LightPlugins.Add(ReadPlugin(br, infoSaveVersion));
}

// previous save versions didn't have medium plugins
Expand All @@ -211,7 +211,7 @@ private FilePluginInfo ReadPluginInfo(BinaryReader br, byte infoSaveVersion)
// loop through medium plugins
for (int i = 0; i < pluginInfo.MediumPluginCount; i++)
{
pluginInfo.MediumPlugins.Add(ReadPlugin(br));
pluginInfo.MediumPlugins.Add(ReadPlugin(br, infoSaveVersion));
}
}

Expand All @@ -226,7 +226,7 @@ string ReadString(BinaryReader br)
return Encoding.ASCII.GetString(br.ReadBytes(size));
}

private FilePlugin ReadPlugin(BinaryReader br)
private FilePlugin ReadPlugin(BinaryReader br, byte infoSaveVersion)
{
// record the current position
// var offset = br.BaseStream.Position;
Expand Down Expand Up @@ -254,20 +254,24 @@ private FilePlugin ReadPlugin(BinaryReader br)
*/

// non-native plugin so we are expecting some extra info and possibly creation info

// creation name not always here
plugin.CreationNameSize = br.ReadUInt16();
if (plugin.CreationNameSize != 0)
plugin.CreationName = Encoding.ASCII.GetString(br.ReadBytes(plugin.CreationNameSize));

// creation id not always here
plugin.CreationIdSize = br.ReadUInt16();
if (plugin.CreationIdSize != 0)
plugin.CreationId = Encoding.ASCII.GetString(br.ReadBytes(plugin.CreationIdSize));

plugin.FlagsSize = br.ReadUInt16();
plugin.Flags = br.ReadBytes(plugin.FlagsSize);
plugin.AchievementFriendly = br.ReadByte();

// previous save versions doesn't have this extra data
if (infoSaveVersion >= 122)
{
// creation name not always here
plugin.CreationNameSize = br.ReadUInt16();
if (plugin.CreationNameSize != 0)
plugin.CreationName = Encoding.ASCII.GetString(br.ReadBytes(plugin.CreationNameSize));

// creation id not always here
plugin.CreationIdSize = br.ReadUInt16();
if (plugin.CreationIdSize != 0)
plugin.CreationId = Encoding.ASCII.GetString(br.ReadBytes(plugin.CreationIdSize));

plugin.FlagsSize = br.ReadUInt16();
plugin.Flags = br.ReadBytes(plugin.FlagsSize);
plugin.AchievementFriendly = br.ReadByte();
}

_logger.Info($"{plugin.PluginName} is a normal plugin ({plugin.CreationName}).");
return plugin;
Expand Down
138 changes: 113 additions & 25 deletions Example.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,79 +3,167 @@
"Header": {
"EngineVersion": 27,
"SaveVersion": 122,
"SaveNumber": 8,
"PlayerName": "Simone",
"SaveNumber": 1,
"PlayerName": "Player",
"PlayerLevel": 1,
"PlayerLocation": "Vectera - Vectera",
"Playtime": "0d.0h.30m.0 days.0 hours.30 minutes",
"PlayerLocation": "Volii Alpha - Madame Sauvage\u0027s Place",
"Playtime": "0d.0h.4m.0 days.0 hours.4 minutes",
"RaceName": "HumanRace",
"Gender": 1,
"Gender": 0,
"Experience": 40,
"ExperienceRequired": 200,
"DateTime": "2024-07-09T15:06:32.6400615Z"
"DateTime": "2024-06-27T22:57:26.0368839Z"
},
"SaveVersion": 122,
"CurrentGameVersion": "1.12.36.0",
"CreatedGameVersion": "1.12.36.0",
"CurrentGameVersion": "1.12.32.0",
"CreatedGameVersion": "1.12.32.0",
"PluginInfo": {
"PluginCount": 4,
"LightPluginCount": 8,
"MediumPluginCount": 3,
"PluginCount": 15,
"LightPluginCount": 17,
"MediumPluginCount": 7,
"Plugins": [
{
"PluginName": "Starfield.esm"
},
{
"PluginName": "G231_XenoMaster.esm"
"PluginName": "BlueprintShips-Starfield.esm"
},
{
"PluginName": "MCRemodel.esm"
"PluginName": "tankgirlsmodelhome.esm",
"CreationName": "TGs Luxury Homes Volume I",
"CreationId": "TM_3232667a-c443-48a1-8719-1d3978bc4cd9"
},
{
"PluginName": "BlueprintShips-Starfield.esm"
"PluginName": "StarfieldCommunityPatch.esm"
},
{
"PluginName": "SFCP_1043.esm"
},
{
"PluginName": "SKKFastStartNewGame.esm"
},
{
"PluginName": "SH_Armor.esm"
},
{
"PluginName": "Lovers Utility Library.esm"
},
{
"PluginName": "SKKStalkersFollowers.esm"
},
{
"PluginName": "Ship Power Fix.esm"
},
{
"PluginName": "Starfarer\u0027s Hairstyles.esm"
},
{
"PluginName": "DK-Follower.esp"
},
{
"PluginName": "DK-NPC.esp"
},
{
"PluginName": "oltTest_DialogueNPCNew.esp"
},
{
"PluginName": "DKCassimaWu.esp"
}
],
"LightPlugins": [
{
"PluginName": "DonnaCrew.esm"
"PluginName": "Constellation.esm"
},
{
"PluginName": "OldMars.esm"
},
{
"PluginName": "SFBGS007.esm"
},
{
"PluginName": "SFBGS008.esm"
},
{
"PluginName": "sfbgs00a_a.esm",
"CreationName": "Blackout Drumbeat Skin",
"CreationId": "TM_31ccf130-4852-417b-842a-9d82672028e4"
},
{
"PluginName": "sfbgs021.esm",
"CreationName": "Observatory",
"CreationId": "TM_e8ff65fc-7c13-44b3-b8a5-27df6b28007d"
},
{
"PluginName": "sfbgs023.esm",
"CreationName": "Starborn Gravis Suit",
"CreationId": "TM_beefc7ae-59f4-4934-b2a5-d04e5264f029"
},
{
"PluginName": "starfieldcompendium.esm",
"CreationName": "Starfield Compendium",
"CreationId": "TM_2fccc6a3-0368-4b40-bbd4-6a1551d44e41"
"PluginName": "unocucflygirl.esm",
"CreationName": "Robin Locke - UC Fly Girl Companion",
"CreationId": "TM_31bc272e-edc6-4d27-a420-2d6d5fd99f79"
},
{
"PluginName": "Constellation.esm"
"PluginName": "ringobungo_apexelectronicsex.esm",
"CreationName": "Desktop Speaker (SSNN Broadcast)",
"CreationId": "TM_9abcd3bf-ba88-4140-9746-8d02b68b3474"
},
{
"PluginName": "OldMars.esm"
"PluginName": "neonsigns.esm",
"CreationName": "Realistic Neon Signs",
"CreationId": "TM_7c0ecaca-8647-4f2f-833a-ff2e76aadd5c"
},
{
"PluginName": "SFBGS007.esm"
"PluginName": "betterqasmoke.esm",
"CreationName": "Qasmoke has workbenches",
"CreationId": "TM_0a88c365-4a7e-4377-ae18-fbe0e37df40b"
},
{
"PluginName": "SFBGS008.esm"
"PluginName": "GalBankPlus.esm"
},
{
"PluginName": "ImprovedFollowerBehavior.esm"
},
{
"PluginName": "gnStaggeredSQs.esm"
},
{
"PluginName": "Better Explosives.esm"
},
{
"PluginName": "Better Mines - Fixed Explosion Delay.esm"
},
{
"PluginName": "Starfield Survival Sandwich.esm"
}
],
"MediumPlugins": [
{
"PluginName": "Neon_Vertigo.esm"
},
{
"PluginName": "SFBGS006.esm"
},
{
"PluginName": "SFBGS003.esm"
},
{
"PluginName": "sfta01.esm",
"CreationName": "Trackers Alliance: The Vulture",
"CreationId": "TM_4bf1a31f-46d5-47bf-a5e6-d0f9e2496d0c"
},
{
"PluginName": "qog-miningconglomerate.esm",
"CreationName": "StarSim: Mining Conglomerate",
"CreationId": "TM_e0004076-6d5f-4b96-b7c9-c257a0e3b892"
},
{
"PluginName": "FaceRedesign.esm",
"CreationName": "FaceReDesign",
"CreationId": "TM_78cf4166-6c37-4653-a363-9a23c8d261cd"
},
{
"PluginName": "StarValor.esm"
},
{
"PluginName": "Better Locational Damage.esm"
}
]
}
Expand Down
20 changes: 17 additions & 3 deletions Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace StarfieldSaveTool;
class Program
{
private static Logger logger;

Check warning on line 11 in Program.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable field 'logger' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 11 in Program.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable field 'logger' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 11 in Program.cs

View workflow job for this annotation

GitHub Actions / publish

Non-nullable field 'logger' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 11 in Program.cs

View workflow job for this annotation

GitHub Actions / publish

Non-nullable field 'logger' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 11 in Program.cs

View workflow job for this annotation

GitHub Actions / publish

Non-nullable field 'logger' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.
private const int BufferSize = 65536; // 64 KB

static async Task<int> Main(string[] args)
{
Expand All @@ -35,12 +36,12 @@ static async Task<int> Main(string[] args)
var config = new NLog.Config.LoggingConfiguration();

// create a console logging target
var logConsole = new NLog.Targets.ConsoleTarget();
//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);
//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);

Expand All @@ -49,6 +50,16 @@ static async Task<int> Main(string[] args)

// 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);
}
Expand Down Expand Up @@ -117,17 +128,20 @@ private static void Start(FileInfo file, bool jsonOutputOption, bool rawOutputOp
{
// Exception handler for FileNotFoundException
// We just inform the user that there is no such file
logger.Error($"The file {file} is not found.");
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();
Expand Down
Loading

0 comments on commit 3f67923

Please sign in to comment.