Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into docs-update
Browse files Browse the repository at this point in the history
  • Loading branch information
Pickysaurus committed Oct 14, 2024
2 parents 38dbb82 + 9d425b4 commit f9e65f4
Show file tree
Hide file tree
Showing 18 changed files with 3,540 additions and 25 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
namespace NexusMods.Abstractions.Activities;
using System.Globalization;

/// <summary>
/// Represents a percentage; used for reporting progress of various operations
Expand Down Expand Up @@ -152,7 +153,10 @@ public int CompareTo(object? obj)
/// <returns></returns>
public static bool TryParse(string str, out Percent p)
{
// TODO: This will not parse a value like 3.33%, as `TryParse` will not accept % suffix. https://github.com/Nexus-Mods/NexusMods.App/issues/209
var strSpan = str.AsSpan();
if (strSpan[^1] == NumberFormatInfo.CurrentInfo.PercentSymbol[0])
strSpan = strSpan[..^1];

if (double.TryParse(str, out var d))
{
d /= 100;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

namespace NexusMods.Games.Larian.BaldursGate3.Utils.PakParsing;

/// <summary>
/// Static class containing definitions for the Larian Package (LSPK, `.pak`) format.
/// </summary>
public static class LspkPackageFormat
{
#region Enums
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
using System.Text;
using System.Xml;

namespace NexusMods.Games.Larian.BaldursGate3.Utils.PakParsing;

/// <summary>
/// Class containing definitions for the Larian Xml (LSX) format.
/// </summary>
public static class LsxXmlFormat
{

Expand Down Expand Up @@ -35,32 +39,19 @@ public struct ModuleShortDesc
/// </summary>
public static string SerializeModuleShortDesc(ModuleShortDesc moduleShortDesc)
{
var settings = new XmlWriterSettings
{
Indent = true,
OmitXmlDeclaration = true,
};

using var stringWriter = new StringWriter();
using (var xmlWriter = XmlWriter.Create(stringWriter, new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true}))
using (var xmlWriter = XmlWriter.Create(stringWriter, settings))
{
xmlWriter.WriteStartElement("node");
xmlWriter.WriteAttributeString("id", "ModuleShortDesc");

WriteAttribute(xmlWriter, "Folder", "LSString", moduleShortDesc.Folder);
WriteAttribute(xmlWriter, "MD5", "LSString", moduleShortDesc.Md5);
WriteAttribute(xmlWriter, "Name", "LSString", moduleShortDesc.Name);
WriteAttribute(xmlWriter, "PublishHandle", "uint64", moduleShortDesc.PublishHandle);
WriteAttribute(xmlWriter, "UUID", "guid", moduleShortDesc.Uuid);
WriteAttribute(xmlWriter, "Version64", "int64", moduleShortDesc.Version);

xmlWriter.WriteEndElement();
ModsettingsFileFormat.WriteModuleShortDesc(xmlWriter, moduleShortDesc);
}

return stringWriter.ToString();

static void WriteAttribute(XmlWriter xmlWriter, string id, string type, string value)
{
xmlWriter.WriteStartElement("attribute");
xmlWriter.WriteAttributeString("id", id);
xmlWriter.WriteAttributeString("type", type);
xmlWriter.WriteAttributeString("value", value);
xmlWriter.WriteEndElement();
}
}


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
using System.Text;
using System.Xml;

namespace NexusMods.Games.Larian.BaldursGate3.Utils.PakParsing;

/// <summary>
/// Class to parse and write the `modsettings.lsx` xml file format used for the BG3 Load Order.
/// </summary>
public static class ModsettingsFileFormat
{
public static string SerializeModsettingsLoadOrder(LsxXmlFormat.ModuleShortDesc[] moduleShortDescs)
{
var settings = new XmlWriterSettings
{
Indent = true,
OmitXmlDeclaration = false,
};

using var stringWriter = new StringWriter();
using (var xmlWriter = XmlWriter.Create(stringWriter, settings))
{
xmlWriter.WriteStartDocument();
xmlWriter.WriteStartElement("save");

WriteVersion(xmlWriter);
WriteRegion(xmlWriter, moduleShortDescs);

xmlWriter.WriteEndElement(); // save
xmlWriter.WriteEndDocument();
}

return stringWriter.ToString();
}

public static LsxXmlFormat.ModuleShortDesc[] DeserializeModsettingsLoadOrder(string modsettingsXml)
{
var modulesLoadOrder = new List<LsxXmlFormat.ModuleShortDesc>();
var skipFirstGustavDev = true;

using (var stringReader = new StringReader(modsettingsXml))
using (var xmlReader = XmlReader.Create(stringReader))
{
while (xmlReader.Read())
{
if (xmlReader is not { NodeType: XmlNodeType.Element, Name: "node" } ||
xmlReader.GetAttribute("id") != "ModuleShortDesc")
{
continue;
}

var moduleShortDesc = new LsxXmlFormat.ModuleShortDesc();

while (xmlReader.Read() && xmlReader is not { NodeType: XmlNodeType.EndElement, Name: "node" })
{
if (xmlReader is { NodeType: XmlNodeType.Element, Name: "attribute" })
{
var id = xmlReader.GetAttribute("id");
var value = xmlReader.GetAttribute("value");

switch (id)
{
case "Folder":
moduleShortDesc.Folder = value ?? string.Empty;
break;
case "MD5":
moduleShortDesc.Md5 = value ?? string.Empty;
break;
case "Name":
moduleShortDesc.Name = value ?? string.Empty;
break;
case "PublishHandle":
moduleShortDesc.PublishHandle = value ?? string.Empty;
break;
case "UUID":
moduleShortDesc.Uuid = value ?? string.Empty;
break;
case "Version64":
moduleShortDesc.Version = value ?? string.Empty;
break;
}
}
}

// Skip the GustavDev entry
if (skipFirstGustavDev && moduleShortDesc is { Name: "GustavDev" })
{
skipFirstGustavDev = false;
continue;
}

modulesLoadOrder.Add(moduleShortDesc);
}
}

return modulesLoadOrder.ToArray();
}

#region private methods

private static void WriteVersion(XmlWriter xmlWriter)
{
xmlWriter.WriteStartElement("version");
xmlWriter.WriteAttributeString("major", "4");
xmlWriter.WriteAttributeString("minor", "7");
xmlWriter.WriteAttributeString("revision", "1");
xmlWriter.WriteAttributeString("build", "200");
xmlWriter.WriteEndElement();
}

private static void WriteRegion(XmlWriter xmlWriter, LsxXmlFormat.ModuleShortDesc[] moduleShortDescs)
{
xmlWriter.WriteStartElement("region");
xmlWriter.WriteAttributeString("id", "ModuleSettings");

xmlWriter.WriteStartElement("node");
xmlWriter.WriteAttributeString("id", "root");

xmlWriter.WriteStartElement("children");
xmlWriter.WriteStartElement("node");
xmlWriter.WriteAttributeString("id", "Mods");

xmlWriter.WriteStartElement("children");

// Add default GustavDev entry
WriteModuleShortDesc(xmlWriter,
new LsxXmlFormat.ModuleShortDesc
{
Folder = "GustavDev",
Name = "GustavDev",
PublishHandle = "0",
Version = "36028797018963968",
Uuid = "28ac9ce2-2aba-8cda-b3b5-6e922f71b6b8",
Md5 = ""
}
);

// Add pak mod entries
foreach (var moduleShortDesc in moduleShortDescs)
{
WriteModuleShortDesc(xmlWriter, moduleShortDesc);
}

xmlWriter.WriteEndElement(); // children
xmlWriter.WriteEndElement(); // node Mods
xmlWriter.WriteEndElement(); // children
xmlWriter.WriteEndElement(); // node root
xmlWriter.WriteEndElement(); // region
}

internal static void WriteModuleShortDesc(XmlWriter xmlWriter, LsxXmlFormat.ModuleShortDesc moduleShortDesc)
{
xmlWriter.WriteStartElement("node");
xmlWriter.WriteAttributeString("id", "ModuleShortDesc");

WriteAttribute(xmlWriter,
"Folder",
"LSString",
moduleShortDesc.Folder
);
WriteAttribute(xmlWriter,
"MD5",
"LSString",
moduleShortDesc.Md5
);
WriteAttribute(xmlWriter,
"Name",
"LSString",
moduleShortDesc.Name
);
WriteAttribute(xmlWriter,
"PublishHandle",
"uint64",
moduleShortDesc.PublishHandle
);
WriteAttribute(xmlWriter,
"UUID",
"guid",
moduleShortDesc.Uuid
);
WriteAttribute(xmlWriter,
"Version64",
"int64",
moduleShortDesc.Version
);

xmlWriter.WriteEndElement();
}

private static void WriteAttribute(XmlWriter xmlWriter, string id, string type, string value)
{
xmlWriter.WriteStartElement("attribute");
xmlWriter.WriteAttributeString("id", id);
xmlWriter.WriteAttributeString("type", type);
xmlWriter.WriteAttributeString("value", value);
xmlWriter.WriteEndElement();
}

#endregion private methods
}
19 changes: 19 additions & 0 deletions src/NexusMods.App.UI/Controls/StandardButton.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:NexusMods.App.UI.Controls"
xmlns:icons="clr-namespace:NexusMods.Icons;assembly=NexusMods.Icons"
>

<Design.PreviewWith>
<Border Padding="8">
<StackPanel Spacing="10">
<controls:StandardButton />
</StackPanel>
</Border>
</Design.PreviewWith>

<!-- Control theme is located at src/Themes/NexusMods.Themes.NexusFluentDark/Resources/ControlThemes/StandardButtonControlTheme.axaml
Default style is found there as well as the default template -->

</Styles>
Loading

0 comments on commit f9e65f4

Please sign in to comment.