diff --git a/.editorconfig b/.editorconfig
index 9e9cc12..8232ce9 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -131,7 +131,7 @@ csharp_style_throw_expression = true:warning
csharp_style_conditional_delegate_call = true:warning
### Code-block preferences
-csharp_prefer_braces = when_multiline:warning
+csharp_prefer_braces = when_multiline:suggestion
dotnet_diagnostic.SA1503.severity = warning # Doesn't follow above value
csharp_prefer_simple_using_statement = true:suggestion
csharp_style_prefer_tuple_swap = true:suggestion
@@ -216,6 +216,7 @@ dotnet_diagnostic.IDE1006.severity = warning
### .NET SDK
dotnet_diagnostic.CA1303.severity = none # We don't translate exception and log messages from English
dotnet_diagnostic.SA1025.severity = none # Allow spaces in comments to structure info
+dotnet_diagnostic.IDE0019.severity = suggestion # Use pattern matching
dotnet_diagnostic.IDE0045.severity = suggestion # Simplify ifs
dotnet_diagnostic.IDE0046.severity = suggestion # Simplify ifs
dotnet_diagnostic.IDE0057.severity = suggestion # Slice can be simplified
diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml
index 91d132c..3dbabbf 100644
--- a/.github/workflows/build-and-release.yml
+++ b/.github/workflows/build-and-release.yml
@@ -11,9 +11,8 @@ on:
tags: [ "v*" ]
env:
- DOTNET_VERSIONS: |
- 8.0.420
- 10.0.202
+ TEST_RESOURCES_NAME: 'resources-0.zip'
+ DOTNET_VERSIONS: 10.0.203
DOTNET_INSTALL_DIR: '/tmp/dotnet/'
jobs:
@@ -32,12 +31,28 @@ jobs:
with:
dotnet-version: ${{ env.DOTNET_VERSIONS }}
+ # Full test for master builds and PR of repo owners
- name: "Build, test, and bundle"
+ if: ${{ env.TEST_RESOURCES_URI != '' }}
run: >
dotnet run build.cs --
--target=Default
--target=Bundle
--dotnet-configuration=Release
+ --resource-uri=${{ env.TEST_RESOURCES_URI }}
+ --resource-name=${{ env.TEST_RESOURCES_NAME }}
+ --resource-usr=${{ env.TEST_RESOURCES_USR }}
+ --resource-pwd=${{ env.TEST_RESOURCES_PWD }}
+ env:
+ TEST_RESOURCES_URI: ${{ secrets.TEST_RESOURCES_URI }}
+ TEST_RESOURCES_USR: ${{ secrets.TEST_RESOURCES_USR }}
+ TEST_RESOURCES_PWD: ${{ secrets.TEST_RESOURCES_PWD }}
+
+ # Tests without resources for PR of external contributors
+ - name: "Build, test, and bundle"
+ if: ${{ env.TEST_RESOURCES_URI == '' }}
+ run: >
+ dotnet run build.cs -- --target=Default --target=Bundle --dotnet-configuration=Release
- name: "Publish artifacts to CI"
uses: actions/upload-artifact@v7
diff --git a/.idea/.idea.JUSToolkit/.idea/.gitignore b/.idea/.idea.JUSToolkit/.idea/.gitignore
new file mode 100644
index 0000000..f7634cc
--- /dev/null
+++ b/.idea/.idea.JUSToolkit/.idea/.gitignore
@@ -0,0 +1,15 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Rider ignored files
+/projectSettingsUpdater.xml
+/.idea.JUSToolkit.iml
+/contentModel.xml
+/modules.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Ignored default folder with query files
+/queries/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/.idea.JUSToolkit/.idea/indexLayout.xml b/.idea/.idea.JUSToolkit/.idea/indexLayout.xml
new file mode 100644
index 0000000..7b08163
--- /dev/null
+++ b/.idea/.idea.JUSToolkit/.idea/indexLayout.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.JUSToolkit/.idea/vcs.xml b/.idea/.idea.JUSToolkit/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/.idea.JUSToolkit/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/JUSToolkit.sln.DotSettings b/JUSToolkit.sln.DotSettings
new file mode 100644
index 0000000..58e4866
--- /dev/null
+++ b/JUSToolkit.sln.DotSettings
@@ -0,0 +1,2 @@
+
+ True
\ No newline at end of file
diff --git a/JUSToolkit.slnx b/JUSToolkit.slnx
new file mode 100644
index 0000000..4594463
--- /dev/null
+++ b/JUSToolkit.slnx
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build.cs b/build.cs
index 422ee7e..e8d715b 100644
--- a/build.cs
+++ b/build.cs
@@ -1,6 +1,6 @@
-#!/usr/bin/env dotnet run
+#!/usr/bin/env dotnet run
#:property PublishAot=false
-#:package Cake.Frosting.PleOps.Recipe@1.0.4-preview.33
+#:package Cake.Frosting.PleOps.Recipe@1.0.4-preview.37
using System.Diagnostics.CodeAnalysis;
using Cake.Common.IO;
@@ -57,7 +57,7 @@ public override void Setup(BuildContext context, ISetupContext info)
new ProjectPublicationInfo(
"./src/JUS.CLI",
[ "win-x64", "linux-x64", "osx-x64" ],
- "net8.0"));
+ "net10.0"));
context.ReadArguments();
@@ -108,11 +108,7 @@ public override bool ShouldRun(BuildContext context) =>
public override void Run(BuildContext context)
{
- string resourcesPath = Path.GetFullPath(Path.Combine("resources", "tests"));
- if (Directory.Exists(resourcesPath)) {
- context.Log.Information("Test files already exists, skipping download.");
- return;
- }
+ string resourcesPath = Path.GetFullPath(Path.Combine("src", "JUS.Tests"));
string resourceUri = string.Format(context.TestResourceUri!, context.TestResourceName);
var downloadSettings = new DownloadFileSettings {
@@ -128,6 +124,6 @@ public override void Run(BuildContext context)
var compressedResources = context.DownloadFile(resourceUri, downloadSettings);
context.Log.Debug("Unzipping resource");
- context.Unzip(compressedResources, resourcesPath);
+ context.Unzip(compressedResources, resourcesPath, true);
}
}
diff --git a/nuget.config b/nuget.config
index 8292bd9..9cc47fe 100644
--- a/nuget.config
+++ b/nuget.config
@@ -14,7 +14,7 @@
-
+
diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index d61b8e2..d3fb91f 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -1,24 +1,21 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/JUS.CLI/JUS.CLI.csproj b/src/JUS.CLI/JUS.CLI.csproj
index 8014701..6200e3f 100644
--- a/src/JUS.CLI/JUS.CLI.csproj
+++ b/src/JUS.CLI/JUS.CLI.csproj
@@ -3,7 +3,7 @@
Exe
- net8.0
+ net10.0
diff --git a/src/JUS.SceneGatePlugin/JUS.SceneGatePlugin.csproj b/src/JUS.SceneGatePlugin/JUS.SceneGatePlugin.csproj
new file mode 100644
index 0000000..3984ea7
--- /dev/null
+++ b/src/JUS.SceneGatePlugin/JUS.SceneGatePlugin.csproj
@@ -0,0 +1,21 @@
+
+
+
+ SceneGate UI plugin for the JUS game formats.
+
+ net10.0
+
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/JUS.SceneGatePlugin/JusBinaryHeaderConverterMatcher.cs b/src/JUS.SceneGatePlugin/JusBinaryHeaderConverterMatcher.cs
new file mode 100644
index 0000000..4b341b8
--- /dev/null
+++ b/src/JUS.SceneGatePlugin/JusBinaryHeaderConverterMatcher.cs
@@ -0,0 +1,88 @@
+using System.Diagnostics.CodeAnalysis;
+using JUS.Tool.Containers.Converters;
+using JUS.Tool.Graphics.Converters;
+using SceneGate.UI.Formats.Discovery;
+using Yarhl.FileSystem;
+using Yarhl.IO;
+
+namespace JUS.SceneGatePlugin;
+
+[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
+public sealed class JusBinaryHeaderConverterMatcher : IConverterMatcher
+{
+ private static readonly HeaderFormat[] SupportedFormats = [
+ // Containers
+ HeaderFormat.Create("ALAR", 2, null),
+ HeaderFormat.Create("ALAR", 3, null),
+ // Graphics
+ HeaderFormat.Create("ALMT", null, null),
+ HeaderFormat.Create("DSIG", null, null),
+ HeaderFormat.Create("DSTX", 1, 3),
+ HeaderFormat.Create("DSTX", 1, 4),
+ ];
+
+ public ConverterMatcherResult Match(Node input, ConverterMatcherContext context)
+ {
+ if (input.Format is not IBinary) {
+ return ConverterMatcherResult.Incompatible();
+ }
+
+ // Validate the header data
+ HeaderFormat? compatibleFormat = SupportedFormats
+ .FirstOrDefault(f => MatchFormat(context, f));
+ if (compatibleFormat is null) {
+ return ConverterMatcherResult.Incompatible();
+ }
+
+ // header data match... validate this is a supported game
+ bool? compatibleSoftware = SupportedSoftware.IsFromCompatibleSoftware(input, out _);
+ if (compatibleSoftware == false) {
+ return ConverterMatcherResult.Incompatible();
+ }
+
+ // "should be" if header match, but we can't verify the game code
+ MatchingConfidence level = compatibleSoftware == true ? MatchingConfidence.Confident : MatchingConfidence.ShouldBe;
+ object converter = compatibleFormat.ConverterFactory();
+ return new ConverterMatcherResult(level, compatibleFormat.ConverterType, converter);
+ }
+
+ private static bool MatchFormat(ConverterMatcherContext context, HeaderFormat format)
+ {
+ int requiredHeaderData = format switch {
+ { Flags: not null } => 6,
+ { Version: not null } => 5,
+ _ => 4
+ };
+ if (context.Header.Length < requiredHeaderData) {
+ return false;
+ }
+
+ if (context.Header.ReadAsciiString(0, 4) != format.Id) {
+ return false;
+ }
+
+ if (format.Version.HasValue && context.Header.Span[4] != format.Version.Value) {
+ return false;
+ }
+
+ if (format.Flags.HasValue && context.Header.Span[5] != format.Flags.Value) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private sealed record HeaderFormat(
+ string Id,
+ byte? Version,
+ byte? Flags,
+ Type ConverterType,
+ Func