diff --git a/GitVersion.yml b/GitVersion.yml index 34814080..d1593aa9 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -1,6 +1,6 @@ mode: ContinuousDeployment increment: Patch -next-version: 0.7.0 +next-version: 0.8.0 branches: master: tag: alpha \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index d3000790..daac4161 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,5 +4,5 @@ environment: myget_api_key: secure: 0xJoYAtR6psXCRvk1qm5czDObkeRjHKPjfe5gIExXVFPwA0VVODYv/hBZYUtz2F3 build_script: - - ps: .\build.ps1 + - ps: .\build.ps1 appveyorbuild test: off \ No newline at end of file diff --git a/build/Build.cs b/build/Build.cs index bd2cad86..61f75fb4 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -4,18 +4,17 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; -using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Xml.Linq; using ICSharpCode.SharpZipLib.Zip; -using ILRepacking; using Nuke.Common; using Nuke.Common.CI.AppVeyor; using Nuke.Common.Git; using Nuke.Common.IO; using Nuke.Common.ProjectModel; +using Nuke.Common.Tooling; using Nuke.Common.Tools.DotNet; using Nuke.Common.Tools.GitVersion; using Nuke.Common.Tools.SignTool; @@ -27,7 +26,7 @@ namespace LibHacBuild { partial class Build : NukeBuild { - public static int Main() => Execute(x => x.Results); + public static int Main() => Execute(x => x.Standard); [Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")] public readonly string Configuration = IsLocalBuild ? "Debug" : "Release"; @@ -45,23 +44,21 @@ namespace LibHacBuild AbsolutePath SignedArtifactsDirectory => ArtifactsDirectory / "signed"; AbsolutePath TempDirectory => RootDirectory / ".tmp"; AbsolutePath CliCoreDir => TempDirectory / "hactoolnet_netcoreapp3.0"; - AbsolutePath CliFrameworkDir => TempDirectory / "hactoolnet_net46"; - AbsolutePath CliNativeDir => TempDirectory / "hactoolnet_native"; - AbsolutePath CliFrameworkZip => ArtifactsDirectory / "hactoolnet.zip"; - AbsolutePath CliCoreZip => ArtifactsDirectory / "hactoolnet_netcore.zip"; + AbsolutePath CliNativeDir => TempDirectory / $"hactoolnet_{HostOsName}"; + AbsolutePath CliNativeExe => CliNativeDir / $"hactoolnet_native{NativeProgramExtension}"; + AbsolutePath CliCoreZip => ArtifactsDirectory / $"hactoolnet-{VersionString}-netcore.zip"; + AbsolutePath CliNativeZip => ArtifactsDirectory / $"hactoolnet-{VersionString}-{HostOsName}.zip"; AbsolutePath NugetConfig => RootDirectory / "nuget.config"; - AbsolutePath CliMergedExe => ArtifactsDirectory / "hactoolnet.exe"; - AbsolutePath CliNativeExe => ArtifactsDirectory / NativeProgramFilename; - Project LibHacProject => _solution.GetProject("LibHac").NotNull(); Project LibHacTestProject => _solution.GetProject("LibHac.Tests").NotNull(); Project HactoolnetProject => _solution.GetProject("hactoolnet").NotNull(); private string NativeRuntime { get; set; } - private string NativeProgramFilename { get; set; } + private string HostOsName { get; set; } + private string NativeProgramExtension { get; set; } - string AppVeyorVersion { get; set; } + string VersionString { get; set; } Dictionary VersionProps { get; set; } = new Dictionary(); private const string DotNetFeedSource = "https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json"; @@ -69,20 +66,23 @@ namespace LibHacBuild public Build() { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (EnvironmentInfo.IsWin) { NativeRuntime = "win-x64"; - NativeProgramFilename = "hactoolnet_native.exe"; + NativeProgramExtension = ".exe"; + HostOsName = "win"; } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + else if (EnvironmentInfo.IsLinux) { NativeRuntime = "linux-x64"; - NativeProgramFilename = "hactoolnet_native"; + NativeProgramExtension = ""; + HostOsName = "linux"; } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + else if (EnvironmentInfo.IsOsx) { NativeRuntime = "osx-x64"; - NativeProgramFilename = "hactoolnet_native"; + NativeProgramExtension = ""; + HostOsName = "macos"; } } @@ -92,10 +92,10 @@ namespace LibHacBuild .OnlyWhenStatic(() => _gitRepository != null) .Executes(() => { - AppVeyorVersion = $"{_gitVersion.AssemblySemVer}"; + VersionString = $"{_gitVersion.MajorMinorPatch}"; if (!string.IsNullOrWhiteSpace(_gitVersion.PreReleaseTag)) { - AppVeyorVersion += $"-{_gitVersion.PreReleaseTag}+{_gitVersion.Sha.Substring(0, 8)}"; + VersionString += $"-{_gitVersion.PreReleaseTag}+{_gitVersion.Sha.Substring(0, 8)}"; } string suffix = _gitVersion.PreReleaseTag; @@ -116,11 +116,11 @@ namespace LibHacBuild ["VersionSuffix"] = suffix }; - Console.WriteLine($"Building version {AppVeyorVersion}"); + Logger.Normal($"Building version {VersionString}"); if (Host == HostType.AppVeyor) { - SetAppVeyorVersion(AppVeyorVersion); + SetAppVeyorVersion(VersionString); } }); @@ -137,7 +137,6 @@ namespace LibHacBuild EnsureCleanDirectory(ArtifactsDirectory); EnsureCleanDirectory(CliCoreDir); - EnsureCleanDirectory(CliFrameworkDir); EnsureCleanDirectory(CliNativeDir); }); @@ -175,13 +174,6 @@ namespace LibHacBuild .SetNoBuild(true) .SetProperties(VersionProps)); - DotNetPublish(s => publishSettings - .SetProject(HactoolnetProject) - .SetFramework("net46") - .SetOutput(CliFrameworkDir) - .SetNoBuild(true) - .SetProperties(VersionProps)); - // Hack around OS newline differences if (EnvironmentInfo.IsUnix) { @@ -220,36 +212,6 @@ namespace LibHacBuild } }); - Target Merge => _ => _ - .DependsOn(Compile) - // Merging on Linux blocked by https://github.com/gluck/il-repack/issues/230 - .OnlyWhenStatic(() => !EnvironmentInfo.IsUnix) - .Executes(() => - { - string[] libraries = Directory.GetFiles(CliFrameworkDir, "*.dll"); - var cliList = new List { CliFrameworkDir / "hactoolnet.exe" }; - cliList.AddRange(libraries); - - var cliOptions = new RepackOptions - { - OutputFile = CliMergedExe, - InputAssemblies = cliList.ToArray(), - SearchDirectories = new[] { "." } - }; - - new ILRepack(cliOptions).Repack(); - - foreach (AbsolutePath file in ArtifactsDirectory.GlobFiles("*.exe.config")) - { - File.Delete(file); - } - - if (Host == HostType.AppVeyor) - { - PushArtifact(CliMergedExe); - } - }); - Target Test => _ => _ .DependsOn(Compile) .Executes(() => @@ -266,32 +228,24 @@ namespace LibHacBuild Target Zip => _ => _ .DependsOn(Pack) + .After(Native) .Executes(() => { - string[] namesFx = Directory.EnumerateFiles(CliFrameworkDir, "*.exe") - .Concat(Directory.EnumerateFiles(CliFrameworkDir, "*.dll")) - .ToArray(); - string[] namesCore = Directory.EnumerateFiles(CliCoreDir, "*.json") .Concat(Directory.EnumerateFiles(CliCoreDir, "*.dll")) .ToArray(); - ZipFiles(CliFrameworkZip, namesFx); - Console.WriteLine($"Created {CliFrameworkZip}"); - ZipFiles(CliCoreZip, namesCore); - Console.WriteLine($"Created {CliCoreZip}"); + Logger.Normal($"Created {CliCoreZip}"); if (Host == HostType.AppVeyor) { - PushArtifact(CliFrameworkZip); PushArtifact(CliCoreZip); - PushArtifact(CliMergedExe); } }); Target Publish => _ => _ - .DependsOn(Test) + .DependsOn(Test, Pack) .OnlyWhenStatic(() => AppVeyor.Instance != null && AppVeyor.Instance.PullRequestTitle == null) .Executes(() => { @@ -309,101 +263,115 @@ namespace LibHacBuild DotNetNuGetPush(settings.SetTargetPath(snupkgFile)); }); - [SuppressMessage("ReSharper", "PossibleNullReferenceException")] - Target Native => _ => _ - .DependsOn(SetVersion) - .OnlyWhenStatic(() => AppVeyor.Instance != null && IsMasterBranch) - .Executes(() => - { - AbsolutePath nativeProject = HactoolnetProject.Path.Parent / "hactoolnet_native.csproj"; - - try - { - File.Copy(HactoolnetProject, nativeProject, true); - DotNet("new nuget --force"); - - XDocument doc = XDocument.Load(NugetConfig); - doc.Element("configuration").Element("packageSources").Add(new XElement("add", - new XAttribute("key", "myget"), new XAttribute("value", DotNetFeedSource))); - - doc.Save(NugetConfig); - - DotNet($"add {nativeProject} package Microsoft.DotNet.ILCompiler --version 1.0.0-alpha-*"); - - DotNetPublishSettings publishSettings = new DotNetPublishSettings() - .SetConfiguration(Configuration) - .SetProject(nativeProject) - .SetFramework("netcoreapp3.0") - .SetRuntime(NativeRuntime) - .SetOutput(CliNativeDir) - .SetProperties(VersionProps); - - if (!Untrimmed) - { - publishSettings = publishSettings - .AddProperty("RootAllApplicationAssemblies", false) - .AddProperty("IlcGenerateCompleteTypeMetadata", false) - .AddProperty("IlcGenerateStackTraceData", false) - .AddProperty("IlcFoldIdenticalMethodBodies", true) - ; - } - - DotNetPublish(publishSettings); - - AbsolutePath tempExe = CliNativeDir / NativeProgramFilename; - - File.Copy(tempExe, CliNativeExe, true); - - if (Host == HostType.AppVeyor) - { - AbsolutePath zipFile = CliNativeExe.Parent / "hactoolnet_native.zip"; - ZipFiles(zipFile, new[] { CliNativeExe.ToString() }); - - PushArtifact(zipFile); - } - } - finally - { - File.Delete(nativeProject); - File.Delete(NugetConfig); - } - }); - - Target Results => _ => _ - .DependsOn(Test, Zip, Merge, Sign, Native, Publish) - .Executes(() => - { - Console.WriteLine("SHA-1:"); - using (SHA1 sha = SHA1.Create()) - { - foreach (string filename in Directory.EnumerateFiles(ArtifactsDirectory)) - { - using (var stream = new FileStream(filename, FileMode.Open)) - { - string hash = BitConverter.ToString(sha.ComputeHash(stream)).Replace("-", ""); - Console.WriteLine($"{hash} - {Path.GetFileName(filename)}"); - } - } - } - }); - Target Sign => _ => _ - .DependsOn(Test, Zip, Merge) + .DependsOn(Test, Zip) .OnlyWhenStatic(() => File.Exists(CertFileName)) - .OnlyWhenStatic(() => !EnvironmentInfo.IsUnix) + .OnlyWhenStatic(() => EnvironmentInfo.IsWin) .Executes(() => { string pwd = ReadPassword(); if (pwd == string.Empty) { - Console.WriteLine("Skipping sign task"); + Logger.Normal("Skipping sign task"); return; } SignAndReZip(pwd); }); + Target Native => _ => _ + .DependsOn(SetVersion) + .After(Compile) + .Executes(BuildNative); + + Target AppVeyorBuild => _ => _ + .DependsOn(Zip, Native, Publish) + .Unlisted() + .Executes(PrintResults); + + Target Standard => _ => _ + .DependsOn(Test, Zip) + .Executes(PrintResults); + + Target Full => _ => _ + .DependsOn(Sign, Native) + .Executes(PrintResults); + + public void PrintResults() + { + Logger.Normal("SHA-1:"); + using (SHA1 sha = SHA1.Create()) + { + foreach (string filename in Directory.EnumerateFiles(ArtifactsDirectory)) + { + using (var stream = new FileStream(filename, FileMode.Open)) + { + string hash = BitConverter.ToString(sha.ComputeHash(stream)).Replace("-", ""); + Logger.Normal($"{hash} - {Path.GetFileName(filename)}"); + } + } + } + } + + [SuppressMessage("ReSharper", "PossibleNullReferenceException")] + public void BuildNative() + { + AbsolutePath nativeProject = HactoolnetProject.Path.Parent / "hactoolnet_native.csproj"; + + try + { + File.Copy(HactoolnetProject, nativeProject, true); + DotNet("new nuget --force"); + + XDocument doc = XDocument.Load(NugetConfig); + doc.Element("configuration").Element("packageSources").Add(new XElement("add", + new XAttribute("key", "myget"), new XAttribute("value", DotNetFeedSource))); + + doc.Save(NugetConfig); + + DotNet($"add {nativeProject} package Microsoft.DotNet.ILCompiler --version 1.0.0-alpha-*"); + + DotNetPublishSettings publishSettings = new DotNetPublishSettings() + .SetConfiguration(Configuration) + .SetProject(nativeProject) + .SetFramework("netcoreapp3.0") + .SetRuntime(NativeRuntime) + .SetOutput(CliNativeDir) + .SetProperties(VersionProps); + + if (!Untrimmed) + { + publishSettings = publishSettings + .AddProperty("RootAllApplicationAssemblies", false) + .AddProperty("IlcGenerateCompleteTypeMetadata", false) + .AddProperty("IlcGenerateStackTraceData", false) + .AddProperty("IlcFoldIdenticalMethodBodies", true) + ; + } + + DotNetPublish(publishSettings); + + if (EnvironmentInfo.IsUnix && !Untrimmed) + { + ProcessTasks.StartProcess("strip", CliNativeExe).AssertZeroExitCode(); + } + + ZipFile(CliNativeZip, CliNativeExe, $"hactoolnet{NativeProgramExtension}"); + Logger.Normal($"Created {CliNativeZip}"); + + if (Host == HostType.AppVeyor) + { + PushArtifact(CliNativeZip); + } + } + finally + { + File.Delete(nativeProject); + File.Delete(NugetConfig); + } + } + public static void ZipFiles(string outFile, IEnumerable files) { using (var s = new ZipOutputStream(File.Create(outFile))) @@ -425,6 +393,24 @@ namespace LibHacBuild } } + public static void ZipFile(string outFile, string file, string nameInsideZip) + { + using (var s = new ZipOutputStream(File.Create(outFile))) + { + s.SetLevel(9); + + var entry = new ZipEntry(nameInsideZip); + entry.DateTime = DateTime.UnixEpoch; + + using (FileStream fs = File.OpenRead(file)) + { + entry.Size = fs.Length; + s.PutNextEntry(entry); + fs.CopyTo(s); + } + } + } + public static void ZipDirectory(string outFile, string directory) { using (var s = new ZipOutputStream(File.Create(outFile))) @@ -503,7 +489,7 @@ namespace LibHacBuild { if (!File.Exists(path)) { - Console.WriteLine($"Unable to add artifact {path}"); + Logger.Warn($"Unable to add artifact {path}"); } var psi = new ProcessStartInfo @@ -524,7 +510,7 @@ namespace LibHacBuild proc.WaitForExit(); - Console.WriteLine($"Added AppVeyor artifact {path}"); + Logger.Normal($"Added AppVeyor artifact {path}"); } public static void SetAppVeyorVersion(string version) @@ -572,35 +558,41 @@ namespace LibHacBuild AbsolutePath nupkgFile = ArtifactsDirectory.GlobFiles("*.nupkg").Single(); AbsolutePath snupkgFile = ArtifactsDirectory.GlobFiles("*.snupkg").Single(); AbsolutePath nupkgDir = TempDirectory / ("sign_" + Path.GetFileName(nupkgFile)); - AbsolutePath netFxDir = TempDirectory / ("sign_" + Path.GetFileName(CliFrameworkZip)); AbsolutePath coreFxDir = TempDirectory / ("sign_" + Path.GetFileName(CliCoreZip)); - AbsolutePath signedMergedExe = SignedArtifactsDirectory / Path.GetFileName(CliMergedExe); + AbsolutePath nativeZipDir = TempDirectory / ("sign_" + Path.GetFileName(CliNativeZip)); + + bool signNative = FileExists(CliNativeExe); try { - UnzipFiles(CliFrameworkZip, netFxDir); UnzipFiles(CliCoreZip, coreFxDir); List pkgFileList = UnzipPackage(nupkgFile, nupkgDir); var toSign = new List(); toSign.AddRange(nupkgDir.GlobFiles("**/LibHac.dll")); - toSign.Add(netFxDir / "hactoolnet.exe"); toSign.Add(coreFxDir / "hactoolnet.dll"); - toSign.Add(signedMergedExe); + + if (signNative) + { + UnzipFiles(CliNativeZip, nativeZipDir); + toSign.Add(nativeZipDir / "hactoolnet.exe"); + } Directory.CreateDirectory(SignedArtifactsDirectory); - File.Copy(CliMergedExe, signedMergedExe, true); SignAssemblies(password, toSign.Select(x => x.ToString()).ToArray()); // Avoid having multiple signed versions of the same file - File.Copy(nupkgDir / "lib" / "net46" / "LibHac.dll", netFxDir / "LibHac.dll", true); File.Copy(nupkgDir / "lib" / "netcoreapp3.0" / "LibHac.dll", coreFxDir / "LibHac.dll", true); ZipDirectory(SignedArtifactsDirectory / Path.GetFileName(nupkgFile), nupkgDir, pkgFileList); - ZipDirectory(SignedArtifactsDirectory / Path.GetFileName(CliFrameworkZip), netFxDir); ZipDirectory(SignedArtifactsDirectory / Path.GetFileName(CliCoreZip), coreFxDir); + if (signNative) + { + ZipDirectory(SignedArtifactsDirectory / Path.GetFileName(CliNativeZip), nativeZipDir); + } + File.Copy(snupkgFile, SignedArtifactsDirectory / Path.GetFileName(snupkgFile)); SignNupkg(SignedArtifactsDirectory / Path.GetFileName(nupkgFile), password); @@ -614,7 +606,6 @@ namespace LibHacBuild finally { Directory.Delete(nupkgDir, true); - Directory.Delete(netFxDir, true); Directory.Delete(coreFxDir, true); } } diff --git a/build/_build.csproj b/build/_build.csproj index 4f4db254..77a0d806 100644 --- a/build/_build.csproj +++ b/build/_build.csproj @@ -10,8 +10,7 @@ - - + diff --git a/src/LibHac/Common/SpanHelpers.cs b/src/LibHac/Common/SpanHelpers.cs index aef00193..f63835d4 100644 --- a/src/LibHac/Common/SpanHelpers.cs +++ b/src/LibHac/Common/SpanHelpers.cs @@ -1,26 +1,16 @@ using System; using System.Runtime.CompilerServices; - -#if NETCOREAPP using System.Runtime.InteropServices; -#endif namespace LibHac.Common { public static class SpanHelpers { [MethodImpl(MethodImplOptions.AggressiveInlining)] -#if NETCOREAPP public static Span CreateSpan(ref T reference, int length) { return MemoryMarshal.CreateSpan(ref reference, length); } -#else - public static unsafe Span CreateSpan(ref T reference, int length) - { - return new Span(Unsafe.AsPointer(ref reference), length); - } -#endif [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span AsSpan(ref T reference) where T : unmanaged @@ -43,17 +33,10 @@ namespace LibHac.Common } [MethodImpl(MethodImplOptions.AggressiveInlining)] -#if NETCOREAPP public static ReadOnlySpan CreateReadOnlySpan(ref T reference, int length) { return MemoryMarshal.CreateReadOnlySpan(ref reference, length); } -#else - public static unsafe ReadOnlySpan CreateReadOnlySpan(ref T reference, int length) - { - return new ReadOnlySpan(Unsafe.AsPointer(ref reference), length); - } -#endif [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpan AsReadOnlySpan(ref T reference) where T : unmanaged diff --git a/src/LibHac/Common/StringUtils.cs b/src/LibHac/Common/StringUtils.cs index df4443c9..7bdeca9f 100644 --- a/src/LibHac/Common/StringUtils.cs +++ b/src/LibHac/Common/StringUtils.cs @@ -80,11 +80,7 @@ namespace LibHac.Common public static string Utf8ToString(ReadOnlySpan value) { -#if STRING_SPAN return Encoding.UTF8.GetString(value); -#else - return Encoding.UTF8.GetString(value.ToArray()); -#endif } public static string Utf8ZToString(ReadOnlySpan value) diff --git a/src/LibHac/Compatibility/Env.cs b/src/LibHac/Compatibility/Env.cs deleted file mode 100644 index d80193ae..00000000 --- a/src/LibHac/Compatibility/Env.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace LibHac.Compatibility -{ - /// - /// Contains variables describing runtime environment info - /// needed for compatibility code. - /// - internal static class Env - { - public static bool IsMono { get; } = Type.GetType("Mono.Runtime") != null; - } -} diff --git a/src/LibHac/Compatibility/FileSystemName.cs b/src/LibHac/Compatibility/FileSystemName.cs deleted file mode 100644 index 07f9059a..00000000 --- a/src/LibHac/Compatibility/FileSystemName.cs +++ /dev/null @@ -1,367 +0,0 @@ - -#if !HAS_FILE_SYSTEM_NAME - -// This code was introduced in .NET Core 2.1 - -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -namespace LibHac.Compatibility -{ - /// - /// Provides methods for matching file system names. - /// - internal static class FileSystemName - { - private static readonly char[] WildcardChars = - { - '\"', '<', '>', '*', '?' - }; - - private static readonly char[] SimpleWildcardChars = - { - '*', '?' - }; - - /// - /// Return true if the given expression matches the given name. '*' and '?' are wildcards, '\' escapes. - /// - public static bool MatchesSimpleExpression(ReadOnlySpan expression, ReadOnlySpan name, bool ignoreCase = true) - { - return MatchPattern(expression, name, ignoreCase, useExtendedWildcards: false); - } - - // Matching routine description - // ============================ - // (copied from native impl) - // - // This routine compares a Dbcs name and an expression and tells the caller - // if the name is in the language defined by the expression. The input name - // cannot contain wildcards, while the expression may contain wildcards. - // - // Expression wild cards are evaluated as shown in the nondeterministic - // finite automatons below. Note that ~* and ~? are DOS_STAR and DOS_QM. - // - // ~* is DOS_STAR, ~? is DOS_QM, and ~. is DOS_DOT - // - // S - // <-----< - // X | | e Y - // X * Y == (0)----->-(1)->-----(2)-----(3) - // - // S-. - // <-----< - // X | | e Y - // X ~* Y == (0)----->-(1)->-----(2)-----(3) - // - // X S S Y - // X ?? Y == (0)---(1)---(2)---(3)---(4) - // - // X . . Y - // X ~.~. Y == (0)---(1)----(2)------(3)---(4) - // | |________| - // | ^ | - // |_______________| - // ^EOF or .^ - // - // X S-. S-. Y - // X ~?~? Y == (0)---(1)-----(2)-----(3)---(4) - // | |________| - // | ^ | - // |_______________| - // ^EOF or .^ - // - // where S is any single character - // S-. is any single character except the final . - // e is a null character transition - // EOF is the end of the name string - // - // In words: - // - // * matches 0 or more characters. - // ? matches exactly 1 character. - // DOS_STAR matches 0 or more characters until encountering and matching - // the final . in the name. - // DOS_QM matches any single character, or upon encountering a period or - // end of name string, advances the expression to the end of the - // set of contiguous DOS_QMs. - // DOS_DOT matches either a . or zero characters beyond name string. - - private static bool MatchPattern(ReadOnlySpan expression, ReadOnlySpan name, bool ignoreCase, bool useExtendedWildcards) - { - // The idea behind the algorithm is pretty simple. We keep track of all possible locations - // in the regular expression that are matching the name. When the name has been exhausted, - // if one of the locations in the expression is also just exhausted, the name is in the - // language defined by the regular expression. - - if (expression.Length == 0 || name.Length == 0) - return false; - - if (expression[0] == '*') - { - // Just * matches everything - if (expression.Length == 1) - return true; - - ReadOnlySpan expressionEnd = expression.Slice(1); - if (expressionEnd.IndexOfAny(useExtendedWildcards ? WildcardChars : SimpleWildcardChars) == -1) - { - // Handle the special case of a single starting *, which essentially means "ends with" - - // If the name doesn't have enough characters to match the remaining expression, it can't be a match. - if (name.Length < expressionEnd.Length) - return false; - - // See if we end with the expression - return name.EndsWith(expressionEnd, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); - } - } - - int nameOffset = 0; - int expressionOffset; - - int priorMatch; - int currentMatch; - int priorMatchCount; - int matchCount = 1; - - char nameChar = '\0'; - char expressionChar; - - // ReSharper disable once RedundantAssignment - Span temp = stackalloc int[0]; - Span currentMatches = stackalloc int[16]; - Span priorMatches = stackalloc int[16]; - priorMatches[0] = 0; - - int maxState = expression.Length * 2; - int currentState; - bool nameFinished = false; - - // Walk through the name string, picking off characters. We go one - // character beyond the end because some wild cards are able to match - // zero characters beyond the end of the string. - // - // With each new name character we determine a new set of states that - // match the name so far. We use two arrays that we swap back and forth - // for this purpose. One array lists the possible expression states for - // all name characters up to but not including the current one, and other - // array is used to build up the list of states considering the current - // name character as well. The arrays are then switched and the process - // repeated. - // - // There is not a one-to-one correspondence between state number and - // offset into the expression. State numbering is not continuous. - // This allows a simple conversion between state number and expression - // offset. Each character in the expression can represent one or two - // states. * and DOS_STAR generate two states: expressionOffset * 2 and - // expressionOffset * 2 + 1. All other expression characters can produce - // only a single state. Thus expressionOffset = currentState / 2. - - while (!nameFinished) - { - if (nameOffset < name.Length) - { - // Not at the end of the name. Grab the current character and move the offset forward. - nameChar = name[nameOffset++]; - } - else - { - // At the end of the name. If the expression is exhausted, exit. - if (priorMatches[matchCount - 1] == maxState) - break; - - nameFinished = true; - } - - // Now, for each of the previous stored expression matches, see what - // we can do with this name character. - priorMatch = 0; - currentMatch = 0; - priorMatchCount = 0; - - while (priorMatch < matchCount) - { - // We have to carry on our expression analysis as far as possible for each - // character of name, so we loop here until the expression stops matching. - - expressionOffset = (priorMatches[priorMatch++] + 1) / 2; - - while (expressionOffset < expression.Length) - { - currentState = expressionOffset * 2; - expressionChar = expression[expressionOffset]; - - // We may be about to exhaust the local space for matches, - // so we have to reallocate if this is the case. - if (currentMatch >= currentMatches.Length - 2) - { - int newSize = currentMatches.Length * 2; - temp = new int[newSize]; - currentMatches.CopyTo(temp); - currentMatches = temp; - - temp = new int[newSize]; - priorMatches.CopyTo(temp); - priorMatches = temp; - } - - if (expressionChar == '*') - { - // '*' matches any character zero or more times. - // ReSharper disable once RedundantJumpStatement - goto MatchZeroOrMore; - } - else if (useExtendedWildcards && expressionChar == '<') - { - // '<' (DOS_STAR) matches any character except '.' zero or more times. - - // If we are at a period, determine if we are allowed to - // consume it, i.e. make sure it is not the last one. - - bool notLastPeriod = false; - if (!nameFinished && nameChar == '.') - { - for (int offset = nameOffset; offset < name.Length; offset++) - { - if (name[offset] == '.') - { - notLastPeriod = true; - break; - } - } - } - - if (nameFinished || nameChar != '.' || notLastPeriod) - { - // ReSharper disable once RedundantJumpStatement - goto MatchZeroOrMore; - } - else - { - // We are at a period. We can only match zero - // characters (i.e. the epsilon transition). - goto MatchZero; - } - } - else - { - // The remaining expression characters all match by consuming a character, - // so we need to force the expression and state forward. - currentState += 2; - - if (useExtendedWildcards && expressionChar == '>') - { - // '>' (DOS_QM) is the most complicated. If the name is finished, - // we can match zero characters. If this name is a '.', we - // don't match, but look at the next expression. Otherwise - // we match a single character. - if (nameFinished || nameChar == '.') - goto NextExpressionCharacter; - - currentMatches[currentMatch++] = currentState; - goto ExpressionFinished; - } - else if (useExtendedWildcards && expressionChar == '"') - { - // A '"' (DOS_DOT) can match either a period, or zero characters - // beyond the end of name. - if (nameFinished) - { - goto NextExpressionCharacter; - } - else if (nameChar == '.') - { - currentMatches[currentMatch++] = currentState; - } - goto ExpressionFinished; - } - else - { - if (expressionChar == '\\') - { - // Escape character, try to move the expression forward again and match literally. - if (++expressionOffset == expression.Length) - { - currentMatches[currentMatch++] = maxState; - goto ExpressionFinished; - } - - currentState = expressionOffset * 2 + 2; - expressionChar = expression[expressionOffset]; - } - - // From this point on a name character is required to even - // continue, let alone make a match. - if (nameFinished) goto ExpressionFinished; - - if (expressionChar == '?') - { - // If this expression was a '?' we can match it once. - currentMatches[currentMatch++] = currentState; - } - else if (ignoreCase - ? char.ToUpperInvariant(expressionChar) == char.ToUpperInvariant(nameChar) - : expressionChar == nameChar) - { - // Matched a non-wildcard character - currentMatches[currentMatch++] = currentState; - } - - goto ExpressionFinished; - } - } - - MatchZeroOrMore: - currentMatches[currentMatch++] = currentState; - MatchZero: - currentMatches[currentMatch++] = currentState + 1; - NextExpressionCharacter: - if (++expressionOffset == expression.Length) - currentMatches[currentMatch++] = maxState; - } // while (expressionOffset < expression.Length) - - ExpressionFinished: - - // Prevent duplication in the destination array. - // - // Each of the arrays is monotonically increasing and non-duplicating, thus we skip - // over any source element in the source array if we just added the same element to - // the destination array. This guarantees non-duplication in the destination array. - - if ((priorMatch < matchCount) && (priorMatchCount < currentMatch)) - { - while (priorMatchCount < currentMatch) - { - int previousLength = priorMatches.Length; - while ((priorMatch < previousLength) && (priorMatches[priorMatch] < currentMatches[priorMatchCount])) - { - priorMatch++; - } - priorMatchCount++; - } - } - } // while (sourceCount < matchesCount) - - // If we found no matches in the just finished iteration it's time to bail. - if (currentMatch == 0) - return false; - - // Swap the meaning the two arrays - temp = priorMatches; - priorMatches = currentMatches; - currentMatches = temp; - - matchCount = currentMatch; - } // while (!nameFinished) - - currentState = priorMatches[matchCount - 1]; - - return currentState == maxState; - } - } -} -#endif diff --git a/src/LibHac/Compatibility/Rsa.cs b/src/LibHac/Compatibility/Rsa.cs deleted file mode 100644 index 96b4c729..00000000 --- a/src/LibHac/Compatibility/Rsa.cs +++ /dev/null @@ -1,69 +0,0 @@ -#if NETFRAMEWORK - -using System; -using System.Numerics; -using LibHac.Crypto; - -namespace LibHac.Compatibility -{ - internal static class Rsa - { - public static bool Rsa2048PssVerifyMono(byte[] data, byte[] signature, byte[] modulus) - { - const int rsaLen = 0x100; - const int digestLen = 0x20; - - const int hashOffset = rsaLen - digestLen - 1; - const int saltOffset = hashOffset - digestLen; - const int padEnd = saltOffset - 1; - - var message = new byte[rsaLen]; - - BigInteger decInt = BigInteger.ModPow(CryptoOld.GetBigInteger(signature), new BigInteger(65537), CryptoOld.GetBigInteger(modulus)); - byte[] decBytes = decInt.ToByteArray(); - - if (decBytes[0] != 0xBC) return false; - - Array.Reverse(decBytes); - Array.Copy(decBytes, 0, message, message.Length - decBytes.Length, decBytes.Length); - - var hashBuf = new byte[0x24]; - Array.Copy(message, hashOffset, hashBuf, 0, digestLen); - - ref byte seed = ref hashBuf[0x23]; - - Span digestBuffer = stackalloc byte[Sha256.DigestSize]; - - for (int i = 0; i < hashOffset; i += 0x20) - { - Sha256.GenerateSha256Hash(hashBuf, digestBuffer); - Util.XorArrays(message.AsSpan(i, digestLen), digestBuffer); - seed++; - } - - message[0] &= 0x7F; - - if (!Util.IsEmpty(message.AsSpan(0, padEnd)) || message[padEnd] != 1) - { - return false; - } - - Span prefix = stackalloc byte[8]; - Span digest = stackalloc byte[Sha256.DigestSize]; - - Sha256.GenerateSha256Hash(data, digest); - - IHash sha2 = Sha256.CreateSha256Generator(); - sha2.Initialize(); - - sha2.Update(prefix); - sha2.Update(digest); - sha2.Update(message.AsSpan(saltOffset, digestLen)); - - sha2.GetHash(digest); - - return Util.SpansEqual(hashBuf.AsSpan(0, 0x20), digest); - } - } -} -#endif diff --git a/src/LibHac/CryptoOld.cs b/src/LibHac/CryptoOld.cs index bd61a5ea..bb75f7de 100644 --- a/src/LibHac/CryptoOld.cs +++ b/src/LibHac/CryptoOld.cs @@ -152,20 +152,7 @@ namespace LibHac public static Validity Rsa2048PssVerify(byte[] data, byte[] signature, byte[] modulus) { -#if NETFRAMEWORK - if (!Compatibility.Env.IsMono) - { - return Compatibility.Rsa.Rsa2048PssVerifyMono(data, signature, modulus) - ? Validity.Valid - : Validity.Invalid; - } -#endif - -#if USE_RSA_CNG - using (RSA rsa = new RSACng()) -#else using (RSA rsa = RSA.Create()) -#endif { rsa.ImportParameters(new RSAParameters { Exponent = new byte[] { 1, 0, 1 }, Modulus = modulus }); @@ -177,12 +164,8 @@ namespace LibHac public static byte[] DecryptTitleKey(byte[] titleKeyblock, RSAParameters rsaParams) { - // todo: Does this work on Mono? -#if USE_RSA_CNG - RSA rsa = new RSACng(); -#else RSA rsa = RSA.Create(); -#endif + rsa.ImportParameters(rsaParams); return rsa.Decrypt(titleKeyblock, RSAEncryptionPadding.OaepSHA256); } diff --git a/src/LibHac/FsService/ExternalKeySet.cs b/src/LibHac/FsService/ExternalKeySet.cs index dd6172ec..b380b388 100644 --- a/src/LibHac/FsService/ExternalKeySet.cs +++ b/src/LibHac/FsService/ExternalKeySet.cs @@ -90,18 +90,7 @@ namespace LibHac.FsService lock (_locker) { int newCapacity = Math.Max(capacity, ExternalKeys.Count); -#if NETCOREAPP ExternalKeys.TrimExcess(newCapacity); -#else - var trimmedDict = new Dictionary(newCapacity); - - foreach (KeyValuePair kvp in ExternalKeys) - { - trimmedDict.Add(kvp.Key, kvp.Value); - } - - ExternalKeys = trimmedDict; -#endif } } } diff --git a/src/LibHac/FsSystem/ConcatenationDirectory.cs b/src/LibHac/FsSystem/ConcatenationDirectory.cs index 13b60cc7..f22e72df 100644 --- a/src/LibHac/FsSystem/ConcatenationDirectory.cs +++ b/src/LibHac/FsSystem/ConcatenationDirectory.cs @@ -1,9 +1,7 @@ using System; +using System.Runtime.InteropServices; using LibHac.Common; using LibHac.Fs; -#if CROSS_PLATFORM -using System.Runtime.InteropServices; -#endif namespace LibHac.FsSystem { @@ -99,7 +97,6 @@ namespace LibHac.FsSystem private bool IsConcatenationFile(DirectoryEntry entry) { -#if CROSS_PLATFORM if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return ConcatenationFileSystem.HasConcatenationFileAttribute(entry.Attributes); @@ -111,9 +108,6 @@ namespace LibHac.FsSystem return ParentFileSystem.IsConcatenationFile(fullPath); } -#else - return ConcatenationFileSystem.HasConcatenationFileAttribute(entry.Attributes); -#endif } } } diff --git a/src/LibHac/FsSystem/ConcatenationFileSystem.cs b/src/LibHac/FsSystem/ConcatenationFileSystem.cs index f90045ac..9b732f1f 100644 --- a/src/LibHac/FsSystem/ConcatenationFileSystem.cs +++ b/src/LibHac/FsSystem/ConcatenationFileSystem.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; -using LibHac.Fs; -#if CROSS_PLATFORM using System.Runtime.InteropServices; -#endif +using LibHac.Fs; namespace LibHac.FsSystem { @@ -51,7 +49,6 @@ namespace LibHac.FsSystem // but writing still won't work properly on those platforms internal bool IsConcatenationFile(string path) { -#if CROSS_PLATFORM if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { Result rc = BaseFileSystem.GetFileAttributes(path, out NxFileAttributes attributes); @@ -63,15 +60,8 @@ namespace LibHac.FsSystem { return IsConcatenationFileHeuristic(path); } -#else - Result rc = BaseFileSystem.GetFileAttributes(path, out NxFileAttributes attributes); - if (rc.IsFailure()) return false; - - return HasConcatenationFileAttribute(attributes); -#endif } -#if CROSS_PLATFORM private bool IsConcatenationFileHeuristic(string path) { // Check if the path is a directory @@ -92,7 +82,6 @@ namespace LibHac.FsSystem // Should be enough checks to avoid most false positives. Maybe return true; } -#endif internal static bool HasConcatenationFileAttribute(NxFileAttributes attributes) { diff --git a/src/LibHac/FsSystem/PathTools.cs b/src/LibHac/FsSystem/PathTools.cs index 8d7ee926..837a8e57 100644 --- a/src/LibHac/FsSystem/PathTools.cs +++ b/src/LibHac/FsSystem/PathTools.cs @@ -1,13 +1,10 @@ using System; using System.Diagnostics; +using System.IO.Enumeration; using System.Runtime.CompilerServices; using LibHac.Common; using LibHac.Fs; -#if HAS_FILE_SYSTEM_NAME -using System.IO.Enumeration; -#endif - namespace LibHac.FsSystem { public static class PathTools @@ -455,13 +452,8 @@ namespace LibHac.FsSystem public static bool MatchesPattern(string searchPattern, string name, bool ignoreCase) { -#if HAS_FILE_SYSTEM_NAME return FileSystemName.MatchesSimpleExpression(searchPattern.AsSpan(), name.AsSpan(), ignoreCase); -#else - return Compatibility.FileSystemName.MatchesSimpleExpression(searchPattern.AsSpan(), - name.AsSpan(), ignoreCase); -#endif } private static bool IsValidMountNameChar(char c) diff --git a/src/LibHac/FsSystem/StreamFile.cs b/src/LibHac/FsSystem/StreamFile.cs index 5114af65..12378b77 100644 --- a/src/LibHac/FsSystem/StreamFile.cs +++ b/src/LibHac/FsSystem/StreamFile.cs @@ -2,10 +2,6 @@ using System.IO; using LibHac.Fs; -#if !STREAM_SPAN -using System.Buffers; -#endif - namespace LibHac.FsSystem { /// @@ -32,7 +28,6 @@ namespace LibHac.FsSystem Result rc = ValidateReadParams(out long toRead, offset, destination.Length, Mode); if (rc.IsFailure()) return rc; -#if STREAM_SPAN lock (Locker) { if (BaseStream.Position != offset) @@ -43,26 +38,6 @@ namespace LibHac.FsSystem bytesRead = BaseStream.Read(destination.Slice(0, (int)toRead)); return Result.Success; } -#else - byte[] buffer = ArrayPool.Shared.Rent((int)toRead); - try - { - lock (Locker) - { - if (BaseStream.Position != offset) - { - BaseStream.Position = offset; - } - - bytesRead = BaseStream.Read(buffer, 0, (int)toRead); - } - - new Span(buffer, 0, (int)bytesRead).CopyTo(destination); - - return Result.Success; - } - finally { ArrayPool.Shared.Return(buffer); } -#endif } protected override Result WriteImpl(long offset, ReadOnlySpan source, WriteOption options) @@ -70,26 +45,11 @@ namespace LibHac.FsSystem Result rc = ValidateWriteParams(offset, source.Length, Mode, out _); if (rc.IsFailure()) return rc; -#if STREAM_SPAN lock (Locker) { BaseStream.Position = offset; BaseStream.Write(source); } -#else - byte[] buffer = ArrayPool.Shared.Rent(source.Length); - try - { - source.CopyTo(buffer); - - lock (Locker) - { - BaseStream.Position = offset; - BaseStream.Write(buffer, 0, source.Length); - } - } - finally { ArrayPool.Shared.Return(buffer); } -#endif if (options.HasFlag(WriteOption.Flush)) { diff --git a/src/LibHac/FsSystem/StreamStorage.cs b/src/LibHac/FsSystem/StreamStorage.cs index 85505e87..a5d1c0bc 100644 --- a/src/LibHac/FsSystem/StreamStorage.cs +++ b/src/LibHac/FsSystem/StreamStorage.cs @@ -2,10 +2,6 @@ using System.IO; using LibHac.Fs; -#if !STREAM_SPAN -using System.Buffers; -#endif - namespace LibHac.FsSystem { public class StreamStorage : StorageBase @@ -26,7 +22,6 @@ namespace LibHac.FsSystem protected override Result ReadImpl(long offset, Span destination) { -#if STREAM_SPAN lock (Locker) { if (BaseStream.Position != offset) @@ -36,31 +31,12 @@ namespace LibHac.FsSystem BaseStream.Read(destination); } -#else - byte[] buffer = ArrayPool.Shared.Rent(destination.Length); - try - { - lock (Locker) - { - if (BaseStream.Position != offset) - { - BaseStream.Position = offset; - } - - BaseStream.Read(buffer, 0, destination.Length); - } - - buffer.AsSpan(0, destination.Length).CopyTo(destination); - } - finally { ArrayPool.Shared.Return(buffer); } -#endif return Result.Success; } protected override Result WriteImpl(long offset, ReadOnlySpan source) { -#if STREAM_SPAN lock (Locker) { if (BaseStream.Position != offset) @@ -70,24 +46,6 @@ namespace LibHac.FsSystem BaseStream.Write(source); } -#else - byte[] buffer = ArrayPool.Shared.Rent(source.Length); - try - { - source.CopyTo(buffer); - - lock (Locker) - { - if (BaseStream.Position != offset) - { - BaseStream.Position = offset; - } - - BaseStream.Write(buffer, 0, source.Length); - } - } - finally { ArrayPool.Shared.Return(buffer); } -#endif return Result.Success; } diff --git a/src/LibHac/LibHac.csproj b/src/LibHac/LibHac.csproj index 6a7a758f..6a15bcf3 100644 --- a/src/LibHac/LibHac.csproj +++ b/src/LibHac/LibHac.csproj @@ -2,7 +2,7 @@ Library - netcoreapp3.0;netstandard2.0;net46 + netcoreapp3.0;netstandard2.1 8.0 @@ -26,31 +26,15 @@ true - - $(DefineConstants);USE_RSA_CNG - - - $(DefineConstants);STREAM_SPAN;STRING_SPAN;HAS_FILE_SYSTEM_NAME;CROSS_PLATFORM;HAS_INTRINSICS + $(DefineConstants);HAS_INTRINSICS - - $(DefineConstants);CROSS_PLATFORM - - - - - - - - - - + - diff --git a/src/LibHac/Util.cs b/src/LibHac/Util.cs index ea8a7232..f7b4ce26 100644 --- a/src/LibHac/Util.cs +++ b/src/LibHac/Util.cs @@ -109,11 +109,7 @@ namespace LibHac [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string GetUtf8String(ReadOnlySpan value) { -#if STRING_SPAN return Encoding.UTF8.GetString(value); -#else - return Encoding.UTF8.GetString(value.ToArray()); -#endif } public static string GetUtf8StringNullTerminated(ReadOnlySpan value) @@ -123,11 +119,7 @@ namespace LibHac value = value.Slice(0, i); -#if STRING_SPAN return Encoding.UTF8.GetString(value); -#else - return Encoding.UTF8.GetString(value.ToArray()); -#endif } public static bool IsEmpty(this byte[] array) => ((ReadOnlySpan)array).IsEmpty(); diff --git a/src/hactoolnet/ProcessBench.cs b/src/hactoolnet/ProcessBench.cs index 9c6d8f99..63ecb278 100644 --- a/src/hactoolnet/ProcessBench.cs +++ b/src/hactoolnet/ProcessBench.cs @@ -1,17 +1,14 @@ using System; using System.Diagnostics; using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; using LibHac; using LibHac.Crypto; using LibHac.Fs; using LibHac.FsSystem; -#if NETCOREAPP -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; -#endif - namespace hactoolnet { internal static class ProcessBench @@ -234,7 +231,6 @@ namespace hactoolnet // ReSharper disable once UnusedParameter.Local private static void RegisterAesSingleBlockBenchmarks(MultiBenchmark bench) { -#if NETCOREAPP var input = new byte[SingleBlockCipherBenchSize]; var output = new byte[SingleBlockCipherBenchSize]; @@ -284,7 +280,6 @@ namespace hactoolnet outBlock = ref Unsafe.Add(ref outBlock, Aes.BlockSize); } } -#endif } private static void RegisterShaBenchmarks(MultiBenchmark bench) diff --git a/src/hactoolnet/ProcessSwitchFs.cs b/src/hactoolnet/ProcessSwitchFs.cs index a2fa567d..b60841a3 100644 --- a/src/hactoolnet/ProcessSwitchFs.cs +++ b/src/hactoolnet/ProcessSwitchFs.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using LibHac; using LibHac.Fs; @@ -9,10 +10,6 @@ using LibHac.FsSystem; using LibHac.FsSystem.NcaUtils; using LibHac.FsSystem.Save; -#if NETCOREAPP -using System.Runtime.InteropServices; -#endif - namespace hactoolnet { internal static class ProcessSwitchFs @@ -310,10 +307,8 @@ namespace hactoolnet private static void CheckForNcaFolders(Context ctx, SwitchFs switchFs) { -#if NETCOREAPP // Skip this until Linux gets FAT attribute support if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return; -#endif IFileSystem fs = switchFs.ContentFs; diff --git a/src/hactoolnet/hactoolnet.csproj b/src/hactoolnet/hactoolnet.csproj index 5c89a9e0..74ad0ced 100644 --- a/src/hactoolnet/hactoolnet.csproj +++ b/src/hactoolnet/hactoolnet.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.0;net46 + netcoreapp3.0 8.0 @@ -20,8 +20,4 @@ - - - - diff --git a/tests/LibHac.Tests/LibHac.Tests.csproj b/tests/LibHac.Tests/LibHac.Tests.csproj index e6966950..27b72fc1 100644 --- a/tests/LibHac.Tests/LibHac.Tests.csproj +++ b/tests/LibHac.Tests/LibHac.Tests.csproj @@ -1,7 +1,7 @@  - net46;netcoreapp3.0 + netcoreapp3.0 false @@ -20,8 +20,4 @@ - - - -