Target netstandard2.1 and netcoreapp3.0 (#99)

.NET Core 2.1 introduced some runtime changes to support Span<T> and ByReference. Along with this comes the ability to do things like reinterpret memory as a different type. In .NET Framework the garbage collector couldn't track these references. These features proved useful enough that support for .NET Framework was dropped.

* Target netstandard2.1 and netcoreapp3.0
* Build: Zip native builds. Put version in zip filename
* Always build native exe on AppVeyor
This commit is contained in:
Alex Barney 2019-11-29 13:11:04 -06:00 committed by GitHub
parent f304f664f4
commit 57586d75fd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 170 additions and 827 deletions

View file

@ -1,6 +1,6 @@
mode: ContinuousDeployment mode: ContinuousDeployment
increment: Patch increment: Patch
next-version: 0.7.0 next-version: 0.8.0
branches: branches:
master: master:
tag: alpha tag: alpha

View file

@ -4,5 +4,5 @@ environment:
myget_api_key: myget_api_key:
secure: 0xJoYAtR6psXCRvk1qm5czDObkeRjHKPjfe5gIExXVFPwA0VVODYv/hBZYUtz2F3 secure: 0xJoYAtR6psXCRvk1qm5czDObkeRjHKPjfe5gIExXVFPwA0VVODYv/hBZYUtz2F3
build_script: build_script:
- ps: .\build.ps1 - ps: .\build.ps1 appveyorbuild
test: off test: off

View file

@ -4,18 +4,17 @@ using System.Diagnostics;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Xml.Linq; using System.Xml.Linq;
using ICSharpCode.SharpZipLib.Zip; using ICSharpCode.SharpZipLib.Zip;
using ILRepacking;
using Nuke.Common; using Nuke.Common;
using Nuke.Common.CI.AppVeyor; using Nuke.Common.CI.AppVeyor;
using Nuke.Common.Git; using Nuke.Common.Git;
using Nuke.Common.IO; using Nuke.Common.IO;
using Nuke.Common.ProjectModel; using Nuke.Common.ProjectModel;
using Nuke.Common.Tooling;
using Nuke.Common.Tools.DotNet; using Nuke.Common.Tools.DotNet;
using Nuke.Common.Tools.GitVersion; using Nuke.Common.Tools.GitVersion;
using Nuke.Common.Tools.SignTool; using Nuke.Common.Tools.SignTool;
@ -27,7 +26,7 @@ namespace LibHacBuild
{ {
partial class Build : NukeBuild partial class Build : NukeBuild
{ {
public static int Main() => Execute<Build>(x => x.Results); public static int Main() => Execute<Build>(x => x.Standard);
[Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")] [Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")]
public readonly string Configuration = IsLocalBuild ? "Debug" : "Release"; public readonly string Configuration = IsLocalBuild ? "Debug" : "Release";
@ -45,23 +44,21 @@ namespace LibHacBuild
AbsolutePath SignedArtifactsDirectory => ArtifactsDirectory / "signed"; AbsolutePath SignedArtifactsDirectory => ArtifactsDirectory / "signed";
AbsolutePath TempDirectory => RootDirectory / ".tmp"; AbsolutePath TempDirectory => RootDirectory / ".tmp";
AbsolutePath CliCoreDir => TempDirectory / "hactoolnet_netcoreapp3.0"; AbsolutePath CliCoreDir => TempDirectory / "hactoolnet_netcoreapp3.0";
AbsolutePath CliFrameworkDir => TempDirectory / "hactoolnet_net46"; AbsolutePath CliNativeDir => TempDirectory / $"hactoolnet_{HostOsName}";
AbsolutePath CliNativeDir => TempDirectory / "hactoolnet_native"; AbsolutePath CliNativeExe => CliNativeDir / $"hactoolnet_native{NativeProgramExtension}";
AbsolutePath CliFrameworkZip => ArtifactsDirectory / "hactoolnet.zip"; AbsolutePath CliCoreZip => ArtifactsDirectory / $"hactoolnet-{VersionString}-netcore.zip";
AbsolutePath CliCoreZip => ArtifactsDirectory / "hactoolnet_netcore.zip"; AbsolutePath CliNativeZip => ArtifactsDirectory / $"hactoolnet-{VersionString}-{HostOsName}.zip";
AbsolutePath NugetConfig => RootDirectory / "nuget.config"; AbsolutePath NugetConfig => RootDirectory / "nuget.config";
AbsolutePath CliMergedExe => ArtifactsDirectory / "hactoolnet.exe";
AbsolutePath CliNativeExe => ArtifactsDirectory / NativeProgramFilename;
Project LibHacProject => _solution.GetProject("LibHac").NotNull(); Project LibHacProject => _solution.GetProject("LibHac").NotNull();
Project LibHacTestProject => _solution.GetProject("LibHac.Tests").NotNull(); Project LibHacTestProject => _solution.GetProject("LibHac.Tests").NotNull();
Project HactoolnetProject => _solution.GetProject("hactoolnet").NotNull(); Project HactoolnetProject => _solution.GetProject("hactoolnet").NotNull();
private string NativeRuntime { get; set; } 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<string, object> VersionProps { get; set; } = new Dictionary<string, object>(); Dictionary<string, object> VersionProps { get; set; } = new Dictionary<string, object>();
private const string DotNetFeedSource = "https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json"; private const string DotNetFeedSource = "https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json";
@ -69,20 +66,23 @@ namespace LibHacBuild
public Build() public Build()
{ {
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (EnvironmentInfo.IsWin)
{ {
NativeRuntime = "win-x64"; NativeRuntime = "win-x64";
NativeProgramFilename = "hactoolnet_native.exe"; NativeProgramExtension = ".exe";
HostOsName = "win";
} }
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) else if (EnvironmentInfo.IsLinux)
{ {
NativeRuntime = "linux-x64"; NativeRuntime = "linux-x64";
NativeProgramFilename = "hactoolnet_native"; NativeProgramExtension = "";
HostOsName = "linux";
} }
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) else if (EnvironmentInfo.IsOsx)
{ {
NativeRuntime = "osx-x64"; NativeRuntime = "osx-x64";
NativeProgramFilename = "hactoolnet_native"; NativeProgramExtension = "";
HostOsName = "macos";
} }
} }
@ -92,10 +92,10 @@ namespace LibHacBuild
.OnlyWhenStatic(() => _gitRepository != null) .OnlyWhenStatic(() => _gitRepository != null)
.Executes(() => .Executes(() =>
{ {
AppVeyorVersion = $"{_gitVersion.AssemblySemVer}"; VersionString = $"{_gitVersion.MajorMinorPatch}";
if (!string.IsNullOrWhiteSpace(_gitVersion.PreReleaseTag)) 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; string suffix = _gitVersion.PreReleaseTag;
@ -116,11 +116,11 @@ namespace LibHacBuild
["VersionSuffix"] = suffix ["VersionSuffix"] = suffix
}; };
Console.WriteLine($"Building version {AppVeyorVersion}"); Logger.Normal($"Building version {VersionString}");
if (Host == HostType.AppVeyor) if (Host == HostType.AppVeyor)
{ {
SetAppVeyorVersion(AppVeyorVersion); SetAppVeyorVersion(VersionString);
} }
}); });
@ -137,7 +137,6 @@ namespace LibHacBuild
EnsureCleanDirectory(ArtifactsDirectory); EnsureCleanDirectory(ArtifactsDirectory);
EnsureCleanDirectory(CliCoreDir); EnsureCleanDirectory(CliCoreDir);
EnsureCleanDirectory(CliFrameworkDir);
EnsureCleanDirectory(CliNativeDir); EnsureCleanDirectory(CliNativeDir);
}); });
@ -175,13 +174,6 @@ namespace LibHacBuild
.SetNoBuild(true) .SetNoBuild(true)
.SetProperties(VersionProps)); .SetProperties(VersionProps));
DotNetPublish(s => publishSettings
.SetProject(HactoolnetProject)
.SetFramework("net46")
.SetOutput(CliFrameworkDir)
.SetNoBuild(true)
.SetProperties(VersionProps));
// Hack around OS newline differences // Hack around OS newline differences
if (EnvironmentInfo.IsUnix) 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<string> { 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 => _ => _ Target Test => _ => _
.DependsOn(Compile) .DependsOn(Compile)
.Executes(() => .Executes(() =>
@ -266,32 +228,24 @@ namespace LibHacBuild
Target Zip => _ => _ Target Zip => _ => _
.DependsOn(Pack) .DependsOn(Pack)
.After(Native)
.Executes(() => .Executes(() =>
{ {
string[] namesFx = Directory.EnumerateFiles(CliFrameworkDir, "*.exe")
.Concat(Directory.EnumerateFiles(CliFrameworkDir, "*.dll"))
.ToArray();
string[] namesCore = Directory.EnumerateFiles(CliCoreDir, "*.json") string[] namesCore = Directory.EnumerateFiles(CliCoreDir, "*.json")
.Concat(Directory.EnumerateFiles(CliCoreDir, "*.dll")) .Concat(Directory.EnumerateFiles(CliCoreDir, "*.dll"))
.ToArray(); .ToArray();
ZipFiles(CliFrameworkZip, namesFx);
Console.WriteLine($"Created {CliFrameworkZip}");
ZipFiles(CliCoreZip, namesCore); ZipFiles(CliCoreZip, namesCore);
Console.WriteLine($"Created {CliCoreZip}"); Logger.Normal($"Created {CliCoreZip}");
if (Host == HostType.AppVeyor) if (Host == HostType.AppVeyor)
{ {
PushArtifact(CliFrameworkZip);
PushArtifact(CliCoreZip); PushArtifact(CliCoreZip);
PushArtifact(CliMergedExe);
} }
}); });
Target Publish => _ => _ Target Publish => _ => _
.DependsOn(Test) .DependsOn(Test, Pack)
.OnlyWhenStatic(() => AppVeyor.Instance != null && AppVeyor.Instance.PullRequestTitle == null) .OnlyWhenStatic(() => AppVeyor.Instance != null && AppVeyor.Instance.PullRequestTitle == null)
.Executes(() => .Executes(() =>
{ {
@ -309,101 +263,115 @@ namespace LibHacBuild
DotNetNuGetPush(settings.SetTargetPath(snupkgFile)); 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 => _ => _ Target Sign => _ => _
.DependsOn(Test, Zip, Merge) .DependsOn(Test, Zip)
.OnlyWhenStatic(() => File.Exists(CertFileName)) .OnlyWhenStatic(() => File.Exists(CertFileName))
.OnlyWhenStatic(() => !EnvironmentInfo.IsUnix) .OnlyWhenStatic(() => EnvironmentInfo.IsWin)
.Executes(() => .Executes(() =>
{ {
string pwd = ReadPassword(); string pwd = ReadPassword();
if (pwd == string.Empty) if (pwd == string.Empty)
{ {
Console.WriteLine("Skipping sign task"); Logger.Normal("Skipping sign task");
return; return;
} }
SignAndReZip(pwd); 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<string> files) public static void ZipFiles(string outFile, IEnumerable<string> files)
{ {
using (var s = new ZipOutputStream(File.Create(outFile))) 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) public static void ZipDirectory(string outFile, string directory)
{ {
using (var s = new ZipOutputStream(File.Create(outFile))) using (var s = new ZipOutputStream(File.Create(outFile)))
@ -503,7 +489,7 @@ namespace LibHacBuild
{ {
if (!File.Exists(path)) if (!File.Exists(path))
{ {
Console.WriteLine($"Unable to add artifact {path}"); Logger.Warn($"Unable to add artifact {path}");
} }
var psi = new ProcessStartInfo var psi = new ProcessStartInfo
@ -524,7 +510,7 @@ namespace LibHacBuild
proc.WaitForExit(); proc.WaitForExit();
Console.WriteLine($"Added AppVeyor artifact {path}"); Logger.Normal($"Added AppVeyor artifact {path}");
} }
public static void SetAppVeyorVersion(string version) public static void SetAppVeyorVersion(string version)
@ -572,35 +558,41 @@ namespace LibHacBuild
AbsolutePath nupkgFile = ArtifactsDirectory.GlobFiles("*.nupkg").Single(); AbsolutePath nupkgFile = ArtifactsDirectory.GlobFiles("*.nupkg").Single();
AbsolutePath snupkgFile = ArtifactsDirectory.GlobFiles("*.snupkg").Single(); AbsolutePath snupkgFile = ArtifactsDirectory.GlobFiles("*.snupkg").Single();
AbsolutePath nupkgDir = TempDirectory / ("sign_" + Path.GetFileName(nupkgFile)); AbsolutePath nupkgDir = TempDirectory / ("sign_" + Path.GetFileName(nupkgFile));
AbsolutePath netFxDir = TempDirectory / ("sign_" + Path.GetFileName(CliFrameworkZip));
AbsolutePath coreFxDir = TempDirectory / ("sign_" + Path.GetFileName(CliCoreZip)); 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 try
{ {
UnzipFiles(CliFrameworkZip, netFxDir);
UnzipFiles(CliCoreZip, coreFxDir); UnzipFiles(CliCoreZip, coreFxDir);
List<string> pkgFileList = UnzipPackage(nupkgFile, nupkgDir); List<string> pkgFileList = UnzipPackage(nupkgFile, nupkgDir);
var toSign = new List<AbsolutePath>(); var toSign = new List<AbsolutePath>();
toSign.AddRange(nupkgDir.GlobFiles("**/LibHac.dll")); toSign.AddRange(nupkgDir.GlobFiles("**/LibHac.dll"));
toSign.Add(netFxDir / "hactoolnet.exe");
toSign.Add(coreFxDir / "hactoolnet.dll"); toSign.Add(coreFxDir / "hactoolnet.dll");
toSign.Add(signedMergedExe);
if (signNative)
{
UnzipFiles(CliNativeZip, nativeZipDir);
toSign.Add(nativeZipDir / "hactoolnet.exe");
}
Directory.CreateDirectory(SignedArtifactsDirectory); Directory.CreateDirectory(SignedArtifactsDirectory);
File.Copy(CliMergedExe, signedMergedExe, true);
SignAssemblies(password, toSign.Select(x => x.ToString()).ToArray()); SignAssemblies(password, toSign.Select(x => x.ToString()).ToArray());
// Avoid having multiple signed versions of the same file // 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); File.Copy(nupkgDir / "lib" / "netcoreapp3.0" / "LibHac.dll", coreFxDir / "LibHac.dll", true);
ZipDirectory(SignedArtifactsDirectory / Path.GetFileName(nupkgFile), nupkgDir, pkgFileList); ZipDirectory(SignedArtifactsDirectory / Path.GetFileName(nupkgFile), nupkgDir, pkgFileList);
ZipDirectory(SignedArtifactsDirectory / Path.GetFileName(CliFrameworkZip), netFxDir);
ZipDirectory(SignedArtifactsDirectory / Path.GetFileName(CliCoreZip), coreFxDir); ZipDirectory(SignedArtifactsDirectory / Path.GetFileName(CliCoreZip), coreFxDir);
if (signNative)
{
ZipDirectory(SignedArtifactsDirectory / Path.GetFileName(CliNativeZip), nativeZipDir);
}
File.Copy(snupkgFile, SignedArtifactsDirectory / Path.GetFileName(snupkgFile)); File.Copy(snupkgFile, SignedArtifactsDirectory / Path.GetFileName(snupkgFile));
SignNupkg(SignedArtifactsDirectory / Path.GetFileName(nupkgFile), password); SignNupkg(SignedArtifactsDirectory / Path.GetFileName(nupkgFile), password);
@ -614,7 +606,6 @@ namespace LibHacBuild
finally finally
{ {
Directory.Delete(nupkgDir, true); Directory.Delete(nupkgDir, true);
Directory.Delete(netFxDir, true);
Directory.Delete(coreFxDir, true); Directory.Delete(coreFxDir, true);
} }
} }

View file

@ -10,8 +10,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageDownload Include="GitVersion.Tool" Version="[5.1.2]" /> <PackageDownload Include="GitVersion.Tool" Version="[5.0.1]" />
<PackageReference Include="ILRepack.Lib" Version="2.0.18" NoWarn="NU1701" />
<PackageReference Include="NuGet.CommandLine" Version="5.3.1" /> <PackageReference Include="NuGet.CommandLine" Version="5.3.1" />
<PackageReference Include="Nuke.Common" Version="0.23.4" /> <PackageReference Include="Nuke.Common" Version="0.23.4" />
<PackageReference Include="SharpZipLib" Version="1.2.0" /> <PackageReference Include="SharpZipLib" Version="1.2.0" />

View file

@ -1,26 +1,16 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
#if NETCOREAPP
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#endif
namespace LibHac.Common namespace LibHac.Common
{ {
public static class SpanHelpers public static class SpanHelpers
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
#if NETCOREAPP
public static Span<T> CreateSpan<T>(ref T reference, int length) public static Span<T> CreateSpan<T>(ref T reference, int length)
{ {
return MemoryMarshal.CreateSpan(ref reference, length); return MemoryMarshal.CreateSpan(ref reference, length);
} }
#else
public static unsafe Span<T> CreateSpan<T>(ref T reference, int length)
{
return new Span<T>(Unsafe.AsPointer(ref reference), length);
}
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<T> AsSpan<T>(ref T reference) where T : unmanaged public static Span<T> AsSpan<T>(ref T reference) where T : unmanaged
@ -43,17 +33,10 @@ namespace LibHac.Common
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
#if NETCOREAPP
public static ReadOnlySpan<T> CreateReadOnlySpan<T>(ref T reference, int length) public static ReadOnlySpan<T> CreateReadOnlySpan<T>(ref T reference, int length)
{ {
return MemoryMarshal.CreateReadOnlySpan(ref reference, length); return MemoryMarshal.CreateReadOnlySpan(ref reference, length);
} }
#else
public static unsafe ReadOnlySpan<T> CreateReadOnlySpan<T>(ref T reference, int length)
{
return new ReadOnlySpan<T>(Unsafe.AsPointer(ref reference), length);
}
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<T> AsReadOnlySpan<T>(ref T reference) where T : unmanaged public static ReadOnlySpan<T> AsReadOnlySpan<T>(ref T reference) where T : unmanaged

View file

@ -80,11 +80,7 @@ namespace LibHac.Common
public static string Utf8ToString(ReadOnlySpan<byte> value) public static string Utf8ToString(ReadOnlySpan<byte> value)
{ {
#if STRING_SPAN
return Encoding.UTF8.GetString(value); return Encoding.UTF8.GetString(value);
#else
return Encoding.UTF8.GetString(value.ToArray());
#endif
} }
public static string Utf8ZToString(ReadOnlySpan<byte> value) public static string Utf8ZToString(ReadOnlySpan<byte> value)

View file

@ -1,13 +0,0 @@
using System;
namespace LibHac.Compatibility
{
/// <summary>
/// Contains variables describing runtime environment info
/// needed for compatibility code.
/// </summary>
internal static class Env
{
public static bool IsMono { get; } = Type.GetType("Mono.Runtime") != null;
}
}

View file

@ -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
{
/// <summary>
/// Provides methods for matching file system names.
/// </summary>
internal static class FileSystemName
{
private static readonly char[] WildcardChars =
{
'\"', '<', '>', '*', '?'
};
private static readonly char[] SimpleWildcardChars =
{
'*', '?'
};
/// <summary>
/// Return true if the given expression matches the given name. '*' and '?' are wildcards, '\' escapes.
/// </summary>
public static bool MatchesSimpleExpression(ReadOnlySpan<char> expression, ReadOnlySpan<char> 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<char> expression, ReadOnlySpan<char> 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<char> 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<int> temp = stackalloc int[0];
Span<int> currentMatches = stackalloc int[16];
Span<int> 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

View file

@ -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<byte> 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<byte> prefix = stackalloc byte[8];
Span<byte> 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

View file

@ -152,20 +152,7 @@ namespace LibHac
public static Validity Rsa2048PssVerify(byte[] data, byte[] signature, byte[] modulus) 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()) using (RSA rsa = RSA.Create())
#endif
{ {
rsa.ImportParameters(new RSAParameters { Exponent = new byte[] { 1, 0, 1 }, Modulus = modulus }); 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) 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(); RSA rsa = RSA.Create();
#endif
rsa.ImportParameters(rsaParams); rsa.ImportParameters(rsaParams);
return rsa.Decrypt(titleKeyblock, RSAEncryptionPadding.OaepSHA256); return rsa.Decrypt(titleKeyblock, RSAEncryptionPadding.OaepSHA256);
} }

View file

@ -90,18 +90,7 @@ namespace LibHac.FsService
lock (_locker) lock (_locker)
{ {
int newCapacity = Math.Max(capacity, ExternalKeys.Count); int newCapacity = Math.Max(capacity, ExternalKeys.Count);
#if NETCOREAPP
ExternalKeys.TrimExcess(newCapacity); ExternalKeys.TrimExcess(newCapacity);
#else
var trimmedDict = new Dictionary<RightsId, AccessKey>(newCapacity);
foreach (KeyValuePair<RightsId, AccessKey> kvp in ExternalKeys)
{
trimmedDict.Add(kvp.Key, kvp.Value);
}
ExternalKeys = trimmedDict;
#endif
} }
} }
} }

View file

@ -1,9 +1,7 @@
using System; using System;
using System.Runtime.InteropServices;
using LibHac.Common; using LibHac.Common;
using LibHac.Fs; using LibHac.Fs;
#if CROSS_PLATFORM
using System.Runtime.InteropServices;
#endif
namespace LibHac.FsSystem namespace LibHac.FsSystem
{ {
@ -99,7 +97,6 @@ namespace LibHac.FsSystem
private bool IsConcatenationFile(DirectoryEntry entry) private bool IsConcatenationFile(DirectoryEntry entry)
{ {
#if CROSS_PLATFORM
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{ {
return ConcatenationFileSystem.HasConcatenationFileAttribute(entry.Attributes); return ConcatenationFileSystem.HasConcatenationFileAttribute(entry.Attributes);
@ -111,9 +108,6 @@ namespace LibHac.FsSystem
return ParentFileSystem.IsConcatenationFile(fullPath); return ParentFileSystem.IsConcatenationFile(fullPath);
} }
#else
return ConcatenationFileSystem.HasConcatenationFileAttribute(entry.Attributes);
#endif
} }
} }
} }

View file

@ -1,9 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using LibHac.Fs;
#if CROSS_PLATFORM
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#endif using LibHac.Fs;
namespace LibHac.FsSystem namespace LibHac.FsSystem
{ {
@ -51,7 +49,6 @@ namespace LibHac.FsSystem
// but writing still won't work properly on those platforms // but writing still won't work properly on those platforms
internal bool IsConcatenationFile(string path) internal bool IsConcatenationFile(string path)
{ {
#if CROSS_PLATFORM
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{ {
Result rc = BaseFileSystem.GetFileAttributes(path, out NxFileAttributes attributes); Result rc = BaseFileSystem.GetFileAttributes(path, out NxFileAttributes attributes);
@ -63,15 +60,8 @@ namespace LibHac.FsSystem
{ {
return IsConcatenationFileHeuristic(path); 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) private bool IsConcatenationFileHeuristic(string path)
{ {
// Check if the path is a directory // Check if the path is a directory
@ -92,7 +82,6 @@ namespace LibHac.FsSystem
// Should be enough checks to avoid most false positives. Maybe // Should be enough checks to avoid most false positives. Maybe
return true; return true;
} }
#endif
internal static bool HasConcatenationFileAttribute(NxFileAttributes attributes) internal static bool HasConcatenationFileAttribute(NxFileAttributes attributes)
{ {

View file

@ -1,13 +1,10 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO.Enumeration;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using LibHac.Common; using LibHac.Common;
using LibHac.Fs; using LibHac.Fs;
#if HAS_FILE_SYSTEM_NAME
using System.IO.Enumeration;
#endif
namespace LibHac.FsSystem namespace LibHac.FsSystem
{ {
public static class PathTools public static class PathTools
@ -455,13 +452,8 @@ namespace LibHac.FsSystem
public static bool MatchesPattern(string searchPattern, string name, bool ignoreCase) public static bool MatchesPattern(string searchPattern, string name, bool ignoreCase)
{ {
#if HAS_FILE_SYSTEM_NAME
return FileSystemName.MatchesSimpleExpression(searchPattern.AsSpan(), return FileSystemName.MatchesSimpleExpression(searchPattern.AsSpan(),
name.AsSpan(), ignoreCase); name.AsSpan(), ignoreCase);
#else
return Compatibility.FileSystemName.MatchesSimpleExpression(searchPattern.AsSpan(),
name.AsSpan(), ignoreCase);
#endif
} }
private static bool IsValidMountNameChar(char c) private static bool IsValidMountNameChar(char c)

View file

@ -2,10 +2,6 @@
using System.IO; using System.IO;
using LibHac.Fs; using LibHac.Fs;
#if !STREAM_SPAN
using System.Buffers;
#endif
namespace LibHac.FsSystem namespace LibHac.FsSystem
{ {
/// <summary> /// <summary>
@ -32,7 +28,6 @@ namespace LibHac.FsSystem
Result rc = ValidateReadParams(out long toRead, offset, destination.Length, Mode); Result rc = ValidateReadParams(out long toRead, offset, destination.Length, Mode);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
#if STREAM_SPAN
lock (Locker) lock (Locker)
{ {
if (BaseStream.Position != offset) if (BaseStream.Position != offset)
@ -43,26 +38,6 @@ namespace LibHac.FsSystem
bytesRead = BaseStream.Read(destination.Slice(0, (int)toRead)); bytesRead = BaseStream.Read(destination.Slice(0, (int)toRead));
return Result.Success; return Result.Success;
} }
#else
byte[] buffer = ArrayPool<byte>.Shared.Rent((int)toRead);
try
{
lock (Locker)
{
if (BaseStream.Position != offset)
{
BaseStream.Position = offset;
}
bytesRead = BaseStream.Read(buffer, 0, (int)toRead);
}
new Span<byte>(buffer, 0, (int)bytesRead).CopyTo(destination);
return Result.Success;
}
finally { ArrayPool<byte>.Shared.Return(buffer); }
#endif
} }
protected override Result WriteImpl(long offset, ReadOnlySpan<byte> source, WriteOption options) protected override Result WriteImpl(long offset, ReadOnlySpan<byte> source, WriteOption options)
@ -70,26 +45,11 @@ namespace LibHac.FsSystem
Result rc = ValidateWriteParams(offset, source.Length, Mode, out _); Result rc = ValidateWriteParams(offset, source.Length, Mode, out _);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
#if STREAM_SPAN
lock (Locker) lock (Locker)
{ {
BaseStream.Position = offset; BaseStream.Position = offset;
BaseStream.Write(source); BaseStream.Write(source);
} }
#else
byte[] buffer = ArrayPool<byte>.Shared.Rent(source.Length);
try
{
source.CopyTo(buffer);
lock (Locker)
{
BaseStream.Position = offset;
BaseStream.Write(buffer, 0, source.Length);
}
}
finally { ArrayPool<byte>.Shared.Return(buffer); }
#endif
if (options.HasFlag(WriteOption.Flush)) if (options.HasFlag(WriteOption.Flush))
{ {

View file

@ -2,10 +2,6 @@
using System.IO; using System.IO;
using LibHac.Fs; using LibHac.Fs;
#if !STREAM_SPAN
using System.Buffers;
#endif
namespace LibHac.FsSystem namespace LibHac.FsSystem
{ {
public class StreamStorage : StorageBase public class StreamStorage : StorageBase
@ -26,7 +22,6 @@ namespace LibHac.FsSystem
protected override Result ReadImpl(long offset, Span<byte> destination) protected override Result ReadImpl(long offset, Span<byte> destination)
{ {
#if STREAM_SPAN
lock (Locker) lock (Locker)
{ {
if (BaseStream.Position != offset) if (BaseStream.Position != offset)
@ -36,31 +31,12 @@ namespace LibHac.FsSystem
BaseStream.Read(destination); BaseStream.Read(destination);
} }
#else
byte[] buffer = ArrayPool<byte>.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<byte>.Shared.Return(buffer); }
#endif
return Result.Success; return Result.Success;
} }
protected override Result WriteImpl(long offset, ReadOnlySpan<byte> source) protected override Result WriteImpl(long offset, ReadOnlySpan<byte> source)
{ {
#if STREAM_SPAN
lock (Locker) lock (Locker)
{ {
if (BaseStream.Position != offset) if (BaseStream.Position != offset)
@ -70,24 +46,6 @@ namespace LibHac.FsSystem
BaseStream.Write(source); BaseStream.Write(source);
} }
#else
byte[] buffer = ArrayPool<byte>.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<byte>.Shared.Return(buffer); }
#endif
return Result.Success; return Result.Success;
} }

View file

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<TargetFrameworks>netcoreapp3.0;netstandard2.0;net46</TargetFrameworks> <TargetFrameworks>netcoreapp3.0;netstandard2.1</TargetFrameworks>
<LangVersion>8.0</LangVersion> <LangVersion>8.0</LangVersion>
</PropertyGroup> </PropertyGroup>
@ -26,31 +26,15 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'net46' ">
<DefineConstants>$(DefineConstants);USE_RSA_CNG</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.0' "> <PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.0' ">
<DefineConstants>$(DefineConstants);STREAM_SPAN;STRING_SPAN;HAS_FILE_SYSTEM_NAME;CROSS_PLATFORM;HAS_INTRINSICS</DefineConstants> <DefineConstants>$(DefineConstants);HAS_INTRINSICS</DefineConstants>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' "> <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.1' ">
<DefineConstants>$(DefineConstants);CROSS_PLATFORM</DefineConstants>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net46' ">
<PackageReference Include="System.Buffers" Version="4.5.0" />
<PackageReference Include="System.Memory" Version="4.5.3" />
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="System.Memory" Version="4.5.3" /> <PackageReference Include="System.Memory" Version="4.5.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" PrivateAssets="All" Version="1.0.0-preview.2" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-19554-01" PrivateAssets="All"/> <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-19554-01" PrivateAssets="All"/>
</ItemGroup> </ItemGroup>

View file

@ -109,11 +109,7 @@ namespace LibHac
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string GetUtf8String(ReadOnlySpan<byte> value) public static string GetUtf8String(ReadOnlySpan<byte> value)
{ {
#if STRING_SPAN
return Encoding.UTF8.GetString(value); return Encoding.UTF8.GetString(value);
#else
return Encoding.UTF8.GetString(value.ToArray());
#endif
} }
public static string GetUtf8StringNullTerminated(ReadOnlySpan<byte> value) public static string GetUtf8StringNullTerminated(ReadOnlySpan<byte> value)
@ -123,11 +119,7 @@ namespace LibHac
value = value.Slice(0, i); value = value.Slice(0, i);
#if STRING_SPAN
return Encoding.UTF8.GetString(value); return Encoding.UTF8.GetString(value);
#else
return Encoding.UTF8.GetString(value.ToArray());
#endif
} }
public static bool IsEmpty(this byte[] array) => ((ReadOnlySpan<byte>)array).IsEmpty(); public static bool IsEmpty(this byte[] array) => ((ReadOnlySpan<byte>)array).IsEmpty();

View file

@ -1,17 +1,14 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using LibHac; using LibHac;
using LibHac.Crypto; using LibHac.Crypto;
using LibHac.Fs; using LibHac.Fs;
using LibHac.FsSystem; using LibHac.FsSystem;
#if NETCOREAPP
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
#endif
namespace hactoolnet namespace hactoolnet
{ {
internal static class ProcessBench internal static class ProcessBench
@ -234,7 +231,6 @@ namespace hactoolnet
// ReSharper disable once UnusedParameter.Local // ReSharper disable once UnusedParameter.Local
private static void RegisterAesSingleBlockBenchmarks(MultiBenchmark bench) private static void RegisterAesSingleBlockBenchmarks(MultiBenchmark bench)
{ {
#if NETCOREAPP
var input = new byte[SingleBlockCipherBenchSize]; var input = new byte[SingleBlockCipherBenchSize];
var output = new byte[SingleBlockCipherBenchSize]; var output = new byte[SingleBlockCipherBenchSize];
@ -284,7 +280,6 @@ namespace hactoolnet
outBlock = ref Unsafe.Add(ref outBlock, Aes.BlockSize); outBlock = ref Unsafe.Add(ref outBlock, Aes.BlockSize);
} }
} }
#endif
} }
private static void RegisterShaBenchmarks(MultiBenchmark bench) private static void RegisterShaBenchmarks(MultiBenchmark bench)

View file

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using LibHac; using LibHac;
using LibHac.Fs; using LibHac.Fs;
@ -9,10 +10,6 @@ using LibHac.FsSystem;
using LibHac.FsSystem.NcaUtils; using LibHac.FsSystem.NcaUtils;
using LibHac.FsSystem.Save; using LibHac.FsSystem.Save;
#if NETCOREAPP
using System.Runtime.InteropServices;
#endif
namespace hactoolnet namespace hactoolnet
{ {
internal static class ProcessSwitchFs internal static class ProcessSwitchFs
@ -310,10 +307,8 @@ namespace hactoolnet
private static void CheckForNcaFolders(Context ctx, SwitchFs switchFs) private static void CheckForNcaFolders(Context ctx, SwitchFs switchFs)
{ {
#if NETCOREAPP
// Skip this until Linux gets FAT attribute support // Skip this until Linux gets FAT attribute support
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return; if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return;
#endif
IFileSystem fs = switchFs.ContentFs; IFileSystem fs = switchFs.ContentFs;

View file

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFrameworks>netcoreapp3.0;net46</TargetFrameworks> <TargetFrameworks>netcoreapp3.0</TargetFrameworks>
<LangVersion>8.0</LangVersion> <LangVersion>8.0</LangVersion>
</PropertyGroup> </PropertyGroup>
@ -20,8 +20,4 @@
<ProjectReference Include="..\LibHac\LibHac.csproj" /> <ProjectReference Include="..\LibHac\LibHac.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" PrivateAssets="All" Version="1.0.0-preview.2" />
</ItemGroup>
</Project> </Project>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net46;netcoreapp3.0</TargetFrameworks> <TargetFrameworks>netcoreapp3.0</TargetFrameworks>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
@ -20,8 +20,4 @@
<EmbeddedResource Include="CryptoTests\TestVectors\*.rsp" /> <EmbeddedResource Include="CryptoTests\TestVectors\*.rsp" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" PrivateAssets="All" Version="1.0.0-preview.2" />
</ItemGroup>
</Project> </Project>