mirror of
https://github.com/SabreTools/NDecrypt.git
synced 2026-07-08 18:06:38 +00:00
Modernize NDecrypt
This commit is contained in:
53
.github/workflows/build_program.yml
vendored
Normal file
53
.github/workflows/build_program.yml
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
name: Build Program
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
project: [NDecrypt]
|
||||
runtime: [win-x86, win-x64, win-arm64, linux-x64, linux-arm64, osx-x64]
|
||||
framework: [net8.0] #[net20, net35, net40, net452, net472, net48, netcoreapp3.1, net5.0, net6.0, net7.0, net8.0]
|
||||
conf: [Release, Debug]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
|
||||
- name: Build
|
||||
run: dotnet publish ${{ matrix.project }}/${{ matrix.project }}.csproj -f ${{ matrix.framework }} -r ${{ matrix.runtime }} -c ${{ matrix.conf == 'Release' && 'Release -p:DebugType=None -p:DebugSymbols=false' || 'Debug'}} --self-contained true --version-suffix ${{ github.sha }} ${{ (startsWith(matrix.framework, 'net5') || startsWith(matrix.framework, 'net6') || startsWith(matrix.framework, 'net7') || startsWith(matrix.framework, 'net8')) && '-p:PublishSingleFile=true' || ''}}
|
||||
|
||||
- name: Archive build
|
||||
run: zip -r ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip ${{ matrix.project }}/bin/${{ matrix.conf }}/${{ matrix.framework }}/${{ matrix.runtime }}/publish/
|
||||
|
||||
- name: Upload build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}
|
||||
path: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip
|
||||
|
||||
- name: Upload to rolling
|
||||
uses: ncipollo/release-action@v1.14.0
|
||||
with:
|
||||
allowUpdates: True
|
||||
artifacts: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip
|
||||
body: 'Last built commit: ${{ github.sha }}'
|
||||
name: 'Rolling Release'
|
||||
prerelease: True
|
||||
replacesArtifacts: True
|
||||
tag: "rolling"
|
||||
updateOnlyUnreleased: True
|
||||
17
.github/workflows/check_pr.yml
vendored
Normal file
17
.github/workflows/check_pr.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
name: Build PR
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Build
|
||||
run: dotnet build
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2018-2023 Matt Nadareski
|
||||
Copyright (c) 2018-2024 Matt Nadareski
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using NDecrypt.Core.Tools;
|
||||
using SabreTools.IO.Readers;
|
||||
|
||||
namespace NDecrypt.Core
|
||||
{
|
||||
@@ -37,7 +37,7 @@ namespace NDecrypt.Core
|
||||
/// <summary>
|
||||
/// Path to the keyfile [3DS only]
|
||||
/// </summary>
|
||||
public string KeyFile { get; set; }
|
||||
public string? KeyFile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Flag to indicate keyfile format to use [3DS only]
|
||||
@@ -119,9 +119,9 @@ namespace NDecrypt.Core
|
||||
/// Setup all of the necessary constants from aes_keys.txt
|
||||
/// </summary>
|
||||
/// <param name="keyfile">Path to aes_keys.txt</param>
|
||||
private void InitAesKeysTxt(string keyfile)
|
||||
private void InitAesKeysTxt(string? keyfile)
|
||||
{
|
||||
if (!File.Exists(keyfile))
|
||||
if (keyfile == null || !File.Exists(keyfile))
|
||||
{
|
||||
IsReady = false;
|
||||
return;
|
||||
@@ -129,7 +129,7 @@ namespace NDecrypt.Core
|
||||
|
||||
try
|
||||
{
|
||||
using (IniReader reader = new IniReader(keyfile))
|
||||
using (var reader = new IniReader(keyfile))
|
||||
{
|
||||
// This is required to preserve sign for BigInteger
|
||||
byte[] signByte = new byte[] { 0x00 };
|
||||
@@ -142,7 +142,7 @@ namespace NDecrypt.Core
|
||||
if (reader.KeyValuePair == null || string.IsNullOrWhiteSpace(reader.KeyValuePair?.Key))
|
||||
break;
|
||||
|
||||
var kvp = reader.KeyValuePair.Value;
|
||||
var kvp = reader.KeyValuePair!.Value;
|
||||
byte[] value = StringToByteArray(kvp.Value).Reverse().ToArray();
|
||||
byte[] valueWithSign = value.Concat(signByte).ToArray();
|
||||
|
||||
@@ -240,9 +240,9 @@ namespace NDecrypt.Core
|
||||
/// </summary>
|
||||
/// <param name="keyfile">Path to keys.bin</param>
|
||||
/// <remarks>keys.bin should be in little endian format</remarks>
|
||||
private void InitKeysBin(string keyfile)
|
||||
private void InitKeysBin(string? keyfile)
|
||||
{
|
||||
if (!File.Exists(keyfile))
|
||||
if (keyfile == null || !File.Exists(keyfile))
|
||||
{
|
||||
IsReady = false;
|
||||
return;
|
||||
|
||||
@@ -1,21 +1,54 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;net6.0</TargetFrameworks>
|
||||
<!-- Assembly Properties -->
|
||||
<TargetFrameworks>net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<VersionPrefix>0.2.5</VersionPrefix>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski</Authors>
|
||||
<Copyright>Copyright (c)2018-2023 Matt Nadareski</Copyright>
|
||||
<Description>Common code for all NDecrypt processors</Description>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2019-2024</Copyright>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/SabreTools/NDecrypt</RepositoryUrl>
|
||||
<Version>0.2.5</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<FileVersion>$(Version)</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Support All Frameworks -->
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net4`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(RuntimeIdentifier.StartsWith(`osx-arm`))">
|
||||
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Support for old .NET versions -->
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net40`))">
|
||||
<PackageReference Include="MinAsyncBridge" Version="0.12.4" />
|
||||
<PackageReference Include="MinTasksExtensionsBridge" Version="0.3.4" />
|
||||
<PackageReference Include="MinThreadingBridge" Version="0.11.4" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith(`net4`)) AND !$(TargetFramework.StartsWith(`net40`))">
|
||||
<PackageReference Include="IndexRange" Version="1.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BouncyCastle.NetCore" Version="1.9.0" />
|
||||
<PackageReference Include="SabreTools.Hashing" Version="1.2.1" />
|
||||
<PackageReference Include="SabreTools.IO" Version="1.4.11" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace NDecrypt.Core.Tools
|
||||
{
|
||||
/// <summary>
|
||||
/// Available hashing types
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum Hash
|
||||
{
|
||||
CRC = 1 << 0,
|
||||
MD5 = 1 << 1,
|
||||
SHA1 = 1 << 2,
|
||||
SHA256 = 1 << 3,
|
||||
SHA384 = 1 << 4,
|
||||
SHA512 = 1 << 5,
|
||||
|
||||
// Special combinations
|
||||
Standard = CRC | MD5 | SHA1,
|
||||
DeepHashes = SHA256 | SHA384 | SHA512,
|
||||
SecureHashes = MD5 | SHA1 | SHA256 | SHA384 | SHA512,
|
||||
All = CRC | MD5 | SHA1 | SHA256 | SHA384 | SHA512,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Different types of INI rows being parsed
|
||||
/// </summary>
|
||||
public enum IniRowType
|
||||
{
|
||||
None,
|
||||
SectionHeader,
|
||||
KeyValue,
|
||||
Comment,
|
||||
Invalid,
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace NDecrypt.Core.Tools
|
||||
{
|
||||
/// <summary>
|
||||
/// Async hashing class wraper
|
||||
/// </summary>
|
||||
public class Hasher
|
||||
{
|
||||
public Hash HashType { get; private set; }
|
||||
private IDisposable _hasher;
|
||||
|
||||
public Hasher(Hash hashType)
|
||||
{
|
||||
this.HashType = hashType;
|
||||
GetHasher();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate the correct hashing class based on the hash type
|
||||
/// </summary>
|
||||
private void GetHasher()
|
||||
{
|
||||
switch (HashType)
|
||||
{
|
||||
case Hash.CRC:
|
||||
_hasher = new OptimizedCRC.OptimizedCRC();
|
||||
break;
|
||||
|
||||
case Hash.MD5:
|
||||
_hasher = MD5.Create();
|
||||
break;
|
||||
|
||||
case Hash.SHA1:
|
||||
_hasher = SHA1.Create();
|
||||
break;
|
||||
|
||||
case Hash.SHA256:
|
||||
_hasher = SHA256.Create();
|
||||
break;
|
||||
|
||||
case Hash.SHA384:
|
||||
_hasher = SHA384.Create();
|
||||
break;
|
||||
|
||||
case Hash.SHA512:
|
||||
_hasher = SHA512.Create();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_hasher.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process a buffer of some length with the internal hash algorithm
|
||||
/// </summary>
|
||||
public void Process(byte[] buffer, int size)
|
||||
{
|
||||
switch (HashType)
|
||||
{
|
||||
case Hash.CRC:
|
||||
(_hasher as OptimizedCRC.OptimizedCRC).Update(buffer, 0, size);
|
||||
break;
|
||||
|
||||
case Hash.MD5:
|
||||
case Hash.SHA1:
|
||||
case Hash.SHA256:
|
||||
case Hash.SHA384:
|
||||
case Hash.SHA512:
|
||||
(_hasher as HashAlgorithm).TransformBlock(buffer, 0, size, null, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Terminate the internal hash algorigthm
|
||||
/// </summary>
|
||||
public void Terminate()
|
||||
{
|
||||
byte[] emptyBuffer = new byte[0];
|
||||
switch (HashType)
|
||||
{
|
||||
case Hash.CRC:
|
||||
(_hasher as OptimizedCRC.OptimizedCRC).Update(emptyBuffer, 0, 0);
|
||||
break;
|
||||
|
||||
case Hash.MD5:
|
||||
case Hash.SHA1:
|
||||
case Hash.SHA256:
|
||||
case Hash.SHA384:
|
||||
case Hash.SHA512:
|
||||
(_hasher as HashAlgorithm).TransformFinalBlock(emptyBuffer, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get internal hash as a byte array
|
||||
/// </summary>
|
||||
public byte[] GetHash()
|
||||
{
|
||||
switch (HashType)
|
||||
{
|
||||
case Hash.CRC:
|
||||
return BitConverter.GetBytes((_hasher as OptimizedCRC.OptimizedCRC).Value).Reverse().ToArray();
|
||||
|
||||
case Hash.MD5:
|
||||
case Hash.SHA1:
|
||||
case Hash.SHA256:
|
||||
case Hash.SHA384:
|
||||
case Hash.SHA512:
|
||||
return (_hasher as HashAlgorithm).Hash;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace NDecrypt.Core.Tools
|
||||
{
|
||||
public class IniReader : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal stream reader for inputting
|
||||
/// </summary>
|
||||
private readonly StreamReader sr;
|
||||
|
||||
/// <summary>
|
||||
/// Get if at end of stream
|
||||
/// </summary>
|
||||
public bool EndOfStream
|
||||
{
|
||||
get
|
||||
{
|
||||
return sr?.EndOfStream ?? true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contents of the currently read line as a key value pair
|
||||
/// </summary>
|
||||
public KeyValuePair<string, string>? KeyValuePair { get; private set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Contents of the current line, unprocessed
|
||||
/// </summary>
|
||||
public string CurrentLine { get; private set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Get the current line number
|
||||
/// </summary>
|
||||
public long LineNumber { get; private set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Current row type
|
||||
/// </summary>
|
||||
public IniRowType RowType { get; private set; } = IniRowType.None;
|
||||
|
||||
/// <summary>
|
||||
/// Current section being read
|
||||
/// </summary>
|
||||
public string Section { get; private set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Validate that rows are in key=value format
|
||||
/// </summary>
|
||||
public bool ValidateRows { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for reading from a file
|
||||
/// </summary>
|
||||
public IniReader(string filename)
|
||||
{
|
||||
sr = new StreamReader(filename);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for reading from a stream
|
||||
/// </summary>
|
||||
public IniReader(Stream stream, Encoding encoding)
|
||||
{
|
||||
sr = new StreamReader(stream, encoding);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read the next line in the INI file
|
||||
/// </summary>
|
||||
public bool ReadNextLine()
|
||||
{
|
||||
if (!(sr.BaseStream?.CanRead ?? false) || sr.EndOfStream)
|
||||
return false;
|
||||
|
||||
CurrentLine = sr.ReadLine().Trim();
|
||||
LineNumber++;
|
||||
ProcessLine();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the current line and extract out values
|
||||
/// </summary>
|
||||
private void ProcessLine()
|
||||
{
|
||||
// Comment
|
||||
if (CurrentLine.StartsWith(";") || CurrentLine.StartsWith("#"))
|
||||
{
|
||||
KeyValuePair = null;
|
||||
RowType = IniRowType.Comment;
|
||||
}
|
||||
|
||||
// Section
|
||||
else if (CurrentLine.StartsWith("[") && CurrentLine.EndsWith("]"))
|
||||
{
|
||||
KeyValuePair = null;
|
||||
RowType = IniRowType.SectionHeader;
|
||||
Section = CurrentLine.TrimStart('[').TrimEnd(']');
|
||||
}
|
||||
|
||||
// KeyValuePair
|
||||
else if (CurrentLine.Contains("="))
|
||||
{
|
||||
// Split the line by '=' for key-value pairs
|
||||
string[] data = CurrentLine.Split('=');
|
||||
|
||||
// If the value field contains an '=', we need to put them back in
|
||||
string key = data[0].Trim();
|
||||
string value = string.Join("=", data.Skip(1)).Trim();
|
||||
|
||||
KeyValuePair = new KeyValuePair<string, string>(key, value);
|
||||
RowType = IniRowType.KeyValue;
|
||||
}
|
||||
|
||||
// Empty
|
||||
else if (string.IsNullOrEmpty(CurrentLine))
|
||||
{
|
||||
KeyValuePair = null;
|
||||
CurrentLine = string.Empty;
|
||||
RowType = IniRowType.None;
|
||||
}
|
||||
|
||||
// Invalid
|
||||
else
|
||||
{
|
||||
KeyValuePair = null;
|
||||
RowType = IniRowType.Invalid;
|
||||
|
||||
if (ValidateRows)
|
||||
throw new InvalidDataException($"Invalid INI row found, cannot continue: {CurrentLine}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose of the underlying reader
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
sr.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2012-2015 Eugene Larchenko (spct@mail.ru)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace OptimizedCRC
|
||||
{
|
||||
internal class OptimizedCRC : IDisposable
|
||||
{
|
||||
private const uint kCrcPoly = 0xEDB88320;
|
||||
private const uint kInitial = 0xFFFFFFFF;
|
||||
private const int CRC_NUM_TABLES = 8;
|
||||
private static readonly uint[] Table;
|
||||
|
||||
static OptimizedCRC()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
Table = new uint[256 * CRC_NUM_TABLES];
|
||||
int i;
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
uint r = (uint)i;
|
||||
for (int j = 0; j < 8; j++)
|
||||
{
|
||||
r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1));
|
||||
}
|
||||
Table[i] = r;
|
||||
}
|
||||
for (; i < 256 * CRC_NUM_TABLES; i++)
|
||||
{
|
||||
uint r = Table[i - 256];
|
||||
Table[i] = Table[r & 0xFF] ^ (r >> 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public uint UnsignedValue;
|
||||
|
||||
public OptimizedCRC()
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset CRC
|
||||
/// </summary>
|
||||
public void Init()
|
||||
{
|
||||
UnsignedValue = kInitial;
|
||||
}
|
||||
|
||||
public int Value
|
||||
{
|
||||
get { return (int)~UnsignedValue; }
|
||||
}
|
||||
|
||||
public void Update(byte[] data, int offset, int count)
|
||||
{
|
||||
new ArraySegment<byte>(data, offset, count); // check arguments
|
||||
if (count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var table = OptimizedCRC.Table;
|
||||
|
||||
uint crc = UnsignedValue;
|
||||
|
||||
for (; (offset & 7) != 0 && count != 0; count--)
|
||||
{
|
||||
crc = (crc >> 8) ^ table[(byte)crc ^ data[offset++]];
|
||||
}
|
||||
|
||||
if (count >= 8)
|
||||
{
|
||||
/*
|
||||
* Idea from 7-zip project sources (http://7-zip.org/sdk.html)
|
||||
*/
|
||||
|
||||
int end = (count - 8) & ~7;
|
||||
count -= end;
|
||||
end += offset;
|
||||
|
||||
while (offset != end)
|
||||
{
|
||||
crc ^= (uint)(data[offset] + (data[offset + 1] << 8) + (data[offset + 2] << 16) + (data[offset + 3] << 24));
|
||||
uint high = (uint)(data[offset + 4] + (data[offset + 5] << 8) + (data[offset + 6] << 16) + (data[offset + 7] << 24));
|
||||
offset += 8;
|
||||
|
||||
crc = table[(byte)crc + 0x700]
|
||||
^ table[(byte)(crc >>= 8) + 0x600]
|
||||
^ table[(byte)(crc >>= 8) + 0x500]
|
||||
^ table[/*(byte)*/(crc >> 8) + 0x400]
|
||||
^ table[(byte)(high) + 0x300]
|
||||
^ table[(byte)(high >>= 8) + 0x200]
|
||||
^ table[(byte)(high >>= 8) + 0x100]
|
||||
^ table[/*(byte)*/(high >> 8) + 0x000];
|
||||
}
|
||||
}
|
||||
|
||||
while (count-- != 0)
|
||||
{
|
||||
crc = (crc >> 8) ^ table[(byte)crc ^ data[offset++]];
|
||||
}
|
||||
|
||||
UnsignedValue = crc;
|
||||
}
|
||||
|
||||
static public int Compute(byte[] data, int offset, int count)
|
||||
{
|
||||
var crc = new OptimizedCRC();
|
||||
crc.Update(data, offset, count);
|
||||
return crc.Value;
|
||||
}
|
||||
|
||||
static public int Compute(byte[] data)
|
||||
{
|
||||
return Compute(data, 0, data.Length);
|
||||
}
|
||||
|
||||
static public int Compute(ArraySegment<byte> block)
|
||||
{
|
||||
return Compute(block.Array, block.Offset, block.Count);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
UnsignedValue = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ namespace NDecrypt.N3DS
|
||||
using (BinaryReader reader = new BinaryReader(File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
|
||||
using (BinaryWriter writer = new BinaryWriter(File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)))
|
||||
{
|
||||
CIAHeader header = CIAHeader.Read(reader);
|
||||
CIAHeader? header = CIAHeader.Read(reader);
|
||||
if (header == null)
|
||||
{
|
||||
Console.WriteLine("Error: Not a 3DS CIA!");
|
||||
@@ -77,7 +77,7 @@ namespace NDecrypt.N3DS
|
||||
private void ProcessAllPartitions(CIAHeader ciaHeader, BinaryReader reader, BinaryWriter writer)
|
||||
{
|
||||
// Iterate over all NCCH partitions
|
||||
for (int p = 0; p < ciaHeader.Partitions.Length; p++)
|
||||
for (int p = 0; p < ciaHeader.Partitions!.Length; p++)
|
||||
{
|
||||
NCCHHeader ncchHeader = ciaHeader.Partitions[0];
|
||||
ProcessPartition(ciaHeader, ncchHeader, reader, writer);
|
||||
@@ -99,7 +99,7 @@ namespace NDecrypt.N3DS
|
||||
Console.WriteLine($"Partition {ncchHeader.PartitionNumber} is not verified due to force flag being set.");
|
||||
}
|
||||
// If we're not forcing the operation, check if the 'NoCrypto' bit is set
|
||||
else if (ncchHeader.Flags.PossblyDecrypted ^ decryptArgs.Encrypt)
|
||||
else if (ncchHeader.Flags!.PossblyDecrypted ^ decryptArgs.Encrypt)
|
||||
{
|
||||
Console.WriteLine($"Partition {ncchHeader.PartitionNumber}: Already " + (decryptArgs.Encrypt ? "Encrypted" : "Decrypted") + "?...");
|
||||
return;
|
||||
@@ -159,7 +159,7 @@ namespace NDecrypt.N3DS
|
||||
}
|
||||
else
|
||||
{
|
||||
masks = ncchHeader.Flags.BitMasks;
|
||||
masks = ncchHeader.Flags!.BitMasks;
|
||||
method = ncchHeader.Flags.CryptoMethod;
|
||||
}
|
||||
|
||||
@@ -209,12 +209,12 @@ namespace NDecrypt.N3DS
|
||||
|
||||
if (ncchHeader.ExtendedHeaderSizeInBytes > 0)
|
||||
{
|
||||
reader.BaseStream.Seek((ncchHeader.Entry.Offset * mediaUnitSize) + 0x200, SeekOrigin.Begin);
|
||||
reader.BaseStream.Seek((ncchHeader.Entry!.Offset * mediaUnitSize) + 0x200, SeekOrigin.Begin);
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset * mediaUnitSize) + 0x200, SeekOrigin.Begin);
|
||||
|
||||
Console.WriteLine($"Partition {ncchHeader.PartitionNumber} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + ": ExHeader");
|
||||
|
||||
var cipher = CreateAESCipher(ncchHeader.NormalKey2C, ncchHeader.PlainIV, decryptArgs.Encrypt);
|
||||
var cipher = CreateAESCipher(ncchHeader.NormalKey2C, ncchHeader.PlainIV!, decryptArgs.Encrypt);
|
||||
writer.Write(cipher.ProcessBytes(reader.ReadBytes(Constants.CXTExtendedDataHeaderLength)));
|
||||
writer.Flush();
|
||||
return true;
|
||||
@@ -224,8 +224,6 @@ namespace NDecrypt.N3DS
|
||||
Console.WriteLine($"Partition {ncchHeader.PartitionNumber} ExeFS: No Extended Header... Skipping...");
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -239,8 +237,8 @@ namespace NDecrypt.N3DS
|
||||
// TODO: Determine how to figure out the MediaUnitSize without an NCSD header. Is it a default value?
|
||||
uint mediaUnitSize = 0x200; // mediaUnitSize;
|
||||
|
||||
reader.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin);
|
||||
ExeFSHeader exefsHeader = ExeFSHeader.Read(reader);
|
||||
reader.BaseStream.Seek((ncchHeader.Entry!.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin);
|
||||
ExeFSHeader? exefsHeader = ExeFSHeader.Read(reader);
|
||||
|
||||
// If the header failed to read, log and return
|
||||
if (exefsHeader == null)
|
||||
@@ -249,7 +247,7 @@ namespace NDecrypt.N3DS
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (ExeFSFileHeader fileHeader in exefsHeader.FileHeaders)
|
||||
foreach (ExeFSFileHeader fileHeader in exefsHeader.FileHeaders!)
|
||||
{
|
||||
// Only decrypt a file if it's a code binary
|
||||
if (!fileHeader.IsCodeBinary)
|
||||
@@ -259,7 +257,7 @@ namespace NDecrypt.N3DS
|
||||
uint datalenB = ((fileHeader.FileSize) % (1024 * 1024));
|
||||
uint ctroffset = ((fileHeader.FileOffset + mediaUnitSize) / 0x10);
|
||||
|
||||
byte[] exefsIVWithOffsetForHeader = AddToByteArray(ncchHeader.ExeFSIV, (int)ctroffset);
|
||||
byte[] exefsIVWithOffsetForHeader = AddToByteArray(ncchHeader.ExeFSIV!, (int)ctroffset);
|
||||
|
||||
var firstCipher = CreateAESCipher(ncchHeader.NormalKey, exefsIVWithOffsetForHeader, decryptArgs.Encrypt);
|
||||
var secondCipher = CreateAESCipher(ncchHeader.NormalKey2C, exefsIVWithOffsetForHeader, !decryptArgs.Encrypt);
|
||||
@@ -298,12 +296,12 @@ namespace NDecrypt.N3DS
|
||||
// TODO: Determine how to figure out the MediaUnitSize without an NCSD header. Is it a default value?
|
||||
uint mediaUnitSize = 0x200; // mediaUnitSize;
|
||||
|
||||
reader.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin);
|
||||
reader.BaseStream.Seek((ncchHeader.Entry!.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin);
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin);
|
||||
|
||||
Console.WriteLine($"Partition {ncchHeader.PartitionNumber} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + $": ExeFS Filename Table");
|
||||
|
||||
var exeFSFilenameTable = CreateAESCipher(ncchHeader.NormalKey2C, ncchHeader.ExeFSIV, decryptArgs.Encrypt);
|
||||
var exeFSFilenameTable = CreateAESCipher(ncchHeader.NormalKey2C, ncchHeader.ExeFSIV!, decryptArgs.Encrypt);
|
||||
writer.Write(exeFSFilenameTable.ProcessBytes(reader.ReadBytes((int)mediaUnitSize)));
|
||||
writer.Flush();
|
||||
}
|
||||
@@ -323,11 +321,11 @@ namespace NDecrypt.N3DS
|
||||
int exefsSizeB = (int)((long)((ncchHeader.ExeFSSizeInMediaUnits - 1) * mediaUnitSize) % (1024 * 1024));
|
||||
int ctroffsetE = (int)(mediaUnitSize / 0x10);
|
||||
|
||||
byte[] exefsIVWithOffset = AddToByteArray(ncchHeader.ExeFSIV, ctroffsetE);
|
||||
byte[] exefsIVWithOffset = AddToByteArray(ncchHeader.ExeFSIV!, ctroffsetE);
|
||||
|
||||
var exeFS = CreateAESCipher(ncchHeader.NormalKey2C, exefsIVWithOffset, decryptArgs.Encrypt);
|
||||
|
||||
reader.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.ExeFSOffsetInMediaUnits + 1) * mediaUnitSize, SeekOrigin.Begin);
|
||||
reader.BaseStream.Seek((ncchHeader.Entry!.Offset + ncchHeader.ExeFSOffsetInMediaUnits + 1) * mediaUnitSize, SeekOrigin.Begin);
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.ExeFSOffsetInMediaUnits + 1) * mediaUnitSize, SeekOrigin.Begin);
|
||||
if (exefsSizeM > 0)
|
||||
{
|
||||
@@ -370,7 +368,7 @@ namespace NDecrypt.N3DS
|
||||
ProcessExeFSFilenameTable(ncchHeader, reader, writer);
|
||||
|
||||
// For all but the original crypto method, process each of the files in the table
|
||||
if (ncchHeader.Flags.CryptoMethod != CryptoMethod.Original)
|
||||
if (ncchHeader.Flags!.CryptoMethod != CryptoMethod.Original)
|
||||
ProcessExeFSFileEntries(ncchHeader, reader, writer);
|
||||
|
||||
// Decrypt the rest of the ExeFS
|
||||
@@ -399,9 +397,9 @@ namespace NDecrypt.N3DS
|
||||
long romfsSizeM = (int)((long)(ncchHeader.RomFSSizeInMediaUnits * mediaUnitSize) / (1024 * 1024));
|
||||
int romfsSizeB = (int)((long)(ncchHeader.RomFSSizeInMediaUnits * mediaUnitSize) % (1024 * 1024));
|
||||
|
||||
var cipher = CreateAESCipher(ncchHeader.NormalKey, ncchHeader.RomFSIV, decryptArgs.Encrypt);
|
||||
var cipher = CreateAESCipher(ncchHeader.NormalKey, ncchHeader.RomFSIV!, decryptArgs.Encrypt);
|
||||
|
||||
reader.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin);
|
||||
reader.BaseStream.Seek((ncchHeader.Entry!.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin);
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin);
|
||||
if (romfsSizeM > 0)
|
||||
{
|
||||
@@ -432,13 +430,13 @@ namespace NDecrypt.N3DS
|
||||
uint mediaUnitSize = 0x200; // ncsdHeader.MediaUnitSize;
|
||||
|
||||
// Write the new CryptoMethod
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset * mediaUnitSize) + 0x18B, SeekOrigin.Begin);
|
||||
writer.BaseStream.Seek((ncchHeader.Entry!.Offset * mediaUnitSize) + 0x18B, SeekOrigin.Begin);
|
||||
writer.Write((byte)CryptoMethod.Original);
|
||||
writer.Flush();
|
||||
|
||||
// Write the new BitMasks flag
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset * mediaUnitSize) + 0x18F, SeekOrigin.Begin);
|
||||
BitMasks flag = ncchHeader.Flags.BitMasks;
|
||||
BitMasks flag = ncchHeader.Flags!.BitMasks;
|
||||
flag &= (BitMasks)((byte)(BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator) ^ 0xFF);
|
||||
flag |= BitMasks.NoCrypto;
|
||||
writer.Write((byte)flag);
|
||||
@@ -513,9 +511,9 @@ namespace NDecrypt.N3DS
|
||||
//}
|
||||
}
|
||||
|
||||
var cipher = CreateAESCipher(ncchHeader.NormalKey, ncchHeader.RomFSIV, decryptArgs.Encrypt);
|
||||
var cipher = CreateAESCipher(ncchHeader.NormalKey, ncchHeader.RomFSIV!, decryptArgs.Encrypt);
|
||||
|
||||
reader.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin);
|
||||
reader.BaseStream.Seek((ncchHeader.Entry!.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin);
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin);
|
||||
if (romfsSizeM > 0)
|
||||
{
|
||||
@@ -547,7 +545,7 @@ namespace NDecrypt.N3DS
|
||||
uint mediaUnitSize = 0x200; // ncsdHeader.MediaUnitSize;
|
||||
|
||||
// Write the new CryptoMethod
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset * mediaUnitSize) + 0x18B, SeekOrigin.Begin);
|
||||
writer.BaseStream.Seek((ncchHeader.Entry!.Offset * mediaUnitSize) + 0x18B, SeekOrigin.Begin);
|
||||
|
||||
// For partitions 1 and up, set crypto-method to 0x00
|
||||
if (ncchHeader.PartitionNumber > 0)
|
||||
@@ -562,7 +560,7 @@ namespace NDecrypt.N3DS
|
||||
|
||||
// Write the new BitMasks flag
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset * mediaUnitSize) + 0x18F, SeekOrigin.Begin);
|
||||
BitMasks flag = ncchHeader.Flags.BitMasks;
|
||||
BitMasks flag = ncchHeader.Flags!.BitMasks;
|
||||
flag &= (BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator | BitMasks.NoCrypto) ^ (BitMasks)0xFF;
|
||||
|
||||
// TODO: Determine how to figure out the original crypto method, if possible
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
internal class Constants
|
||||
{
|
||||
// Setup Keys and IVs
|
||||
public static byte[] PlainCounter = new byte[] { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
public static byte[] ExefsCounter = new byte[] { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
public static byte[] RomfsCounter = new byte[] { 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
public static byte[] PlainCounter = [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
public static byte[] ExefsCounter = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
public static byte[] RomfsCounter = [0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
|
||||
public const int CXTExtendedDataHeaderLength = 0x800;
|
||||
}
|
||||
|
||||
@@ -31,21 +31,21 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// 12 Special memory
|
||||
/// 13 Process has access to CPU core 2 (New3DS only)
|
||||
/// </summary>
|
||||
public byte[][] Descriptors { get; private set; }
|
||||
public byte[][]? Descriptors { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved { get; private set; }
|
||||
public byte[]? Reserved { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get ARM11 kernel capabilities, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>ARM11 kernel capabilities object, null on error</returns>
|
||||
public static ARM11KernelCapabilities Read(BinaryReader reader)
|
||||
public static ARM11KernelCapabilities? Read(BinaryReader reader)
|
||||
{
|
||||
ARM11KernelCapabilities kc = new ARM11KernelCapabilities();
|
||||
var kc = new ARM11KernelCapabilities();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Program ID
|
||||
/// </summary>
|
||||
public byte[] ProgramID { get; private set; }
|
||||
public byte[]? ProgramID { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Core version (The Title ID low of the required FIRM)
|
||||
@@ -37,27 +37,27 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Resource limit descriptors. The first byte here controls the maximum allowed CpuTime.
|
||||
/// </summary>
|
||||
public byte[][] ResourceLimitDescriptors { get; private set; }
|
||||
public byte[][]? ResourceLimitDescriptors { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Storage info
|
||||
/// </summary>
|
||||
public StorageInfo StorageInfo { get; private set; }
|
||||
public StorageInfo? StorageInfo { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Service access control
|
||||
/// </summary>
|
||||
public byte[][] ServiceAccessControl { get; private set; }
|
||||
public byte[][]? ServiceAccessControl { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Extended service access control, support for this was implemented with 9.3.0-X.
|
||||
/// </summary>
|
||||
public byte[][] ExtendedServiceAccessControl { get; private set; }
|
||||
public byte[][]? ExtendedServiceAccessControl { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved { get; private set; }
|
||||
public byte[]? Reserved { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Resource limit category. (0 = APPLICATION, 1 = SYS_APPLET, 2 = LIB_APPLET, 3 = OTHER (sysmodules running under the BASE memregion))
|
||||
@@ -69,9 +69,9 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>ARM11 local system capabilities object, null on error</returns>
|
||||
public static ARM11LocalSystemCapabilities Read(BinaryReader reader)
|
||||
public static ARM11LocalSystemCapabilities? Read(BinaryReader reader)
|
||||
{
|
||||
ARM11LocalSystemCapabilities lsc = new ARM11LocalSystemCapabilities();
|
||||
var lsc = new ARM11LocalSystemCapabilities();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Descriptors
|
||||
/// </summary>
|
||||
public ARM9AccessControlDescriptors[] Descriptors { get; private set; }
|
||||
public ARM9AccessControlDescriptors[]? Descriptors { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM9 Descriptor Version. Originally this value had to be ≥ 2. Starting with 9.3.0-X this value has to be either value 2 or value 3.
|
||||
@@ -19,9 +19,9 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>ARM9 access control object, null on error</returns>
|
||||
public static ARM9AccessControl Read(BinaryReader reader)
|
||||
public static ARM9AccessControl? Read(BinaryReader reader)
|
||||
{
|
||||
ARM9AccessControl ac = new ARM9AccessControl();
|
||||
var ac = new ARM9AccessControl();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -7,26 +7,26 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// ARM11 local system capabilities
|
||||
/// </summary>
|
||||
public ARM11LocalSystemCapabilities ARM11LocalSystemCapabilities { get; private set; }
|
||||
public ARM11LocalSystemCapabilities? ARM11LocalSystemCapabilities { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM11 kernel capabilities
|
||||
/// </summary>
|
||||
public ARM11KernelCapabilities ARM11KernelCapabilities { get; private set; }
|
||||
public ARM11KernelCapabilities? ARM11KernelCapabilities { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM9 access control
|
||||
/// </summary>
|
||||
public ARM9AccessControl ARM9AccessControl { get; private set; }
|
||||
public ARM9AccessControl? ARM9AccessControl { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get access control info, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>Access control info object, null on error</returns>
|
||||
public static AccessControlInfo Read(BinaryReader reader)
|
||||
public static AccessControlInfo? Read(BinaryReader reader)
|
||||
{
|
||||
AccessControlInfo aci = new AccessControlInfo();
|
||||
var aci = new AccessControlInfo();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Content Index
|
||||
/// </summary>
|
||||
public byte[] ContentIndex { get; private set; }
|
||||
public byte[]? ContentIndex { get; private set; }
|
||||
|
||||
#region Content Index
|
||||
|
||||
@@ -59,27 +59,27 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <remarks>
|
||||
/// https://www.3dbrew.org/wiki/CIA#Certificate_Chain
|
||||
/// </remarks>
|
||||
public Certificate[] CertificateChain { get; set; }
|
||||
public Certificate[]? CertificateChain { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ticket
|
||||
/// </summary>
|
||||
public Ticket Ticket { get; set; }
|
||||
public Ticket? Ticket { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// TMD file data
|
||||
/// </summary>
|
||||
public TitleMetadata TMDFileData { get; set; }
|
||||
public TitleMetadata? TMDFileData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Content file data
|
||||
/// </summary>
|
||||
public NCCHHeader[] Partitions { get; set; }
|
||||
public NCCHHeader[]? Partitions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Meta file data (Not a necessary component)
|
||||
/// </summary>
|
||||
public MetaFile MetaFileData { get; set; }
|
||||
public MetaFile? MetaFileData { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -88,9 +88,9 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>CIA header object, null on error</returns>
|
||||
public static CIAHeader Read(BinaryReader reader)
|
||||
public static CIAHeader? Read(BinaryReader reader)
|
||||
{
|
||||
CIAHeader header = new CIAHeader();
|
||||
var header = new CIAHeader();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -107,9 +107,9 @@ namespace NDecrypt.N3DS.Headers
|
||||
reader.BaseStream.Seek(64 - (reader.BaseStream.Position % 64), SeekOrigin.Current);
|
||||
|
||||
header.CertificateChain = new Certificate[3];
|
||||
header.CertificateChain[0] = Certificate.Read(reader); // CA
|
||||
header.CertificateChain[1] = Certificate.Read(reader); // Ticket
|
||||
header.CertificateChain[2] = Certificate.Read(reader); // TMD
|
||||
header.CertificateChain[0] = Certificate.Read(reader)!; // CA
|
||||
header.CertificateChain[1] = Certificate.Read(reader)!; // Ticket
|
||||
header.CertificateChain[2] = Certificate.Read(reader)!; // TMD
|
||||
if (reader.BaseStream.Position % 64 != 0)
|
||||
reader.BaseStream.Seek(64 - (reader.BaseStream.Position % 64), SeekOrigin.Current);
|
||||
|
||||
@@ -126,7 +126,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
while (reader.BaseStream.Position < startingPosition + header.ContentSize)
|
||||
{
|
||||
long initPosition = reader.BaseStream.Position;
|
||||
NCCHHeader ncchHeader = NCCHHeader.Read(reader, readSignature: true);
|
||||
NCCHHeader? ncchHeader = NCCHHeader.Read(reader, readSignature: true);
|
||||
if (ncchHeader == null)
|
||||
break;
|
||||
|
||||
|
||||
@@ -7,36 +7,36 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// SCI
|
||||
/// </summary>
|
||||
public SystemControlInfo SCI { get; private set; }
|
||||
public SystemControlInfo? SCI { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ACI
|
||||
/// </summary>
|
||||
public AccessControlInfo ACI { get; private set; }
|
||||
public AccessControlInfo? ACI { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// AccessDesc signature (RSA-2048-SHA256)
|
||||
/// </summary>
|
||||
public byte[] AccessDescSignature { get; private set; }
|
||||
public byte[]? AccessDescSignature { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// NCCH HDR RSA-2048 public key
|
||||
/// </summary>
|
||||
public byte[] NCCHHDRPublicKey { get; private set; }
|
||||
public byte[]? NCCHHDRPublicKey { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ACI (for limitation of first ACI)
|
||||
/// </summary>
|
||||
public AccessControlInfo ACIForLimitations { get; private set; }
|
||||
public AccessControlInfo? ACIForLimitations { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get a CXI extended header, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>CXI extended header object, null on error</returns>
|
||||
public static CXIExtendedHeader Read(BinaryReader reader)
|
||||
public static CXIExtendedHeader? Read(BinaryReader reader)
|
||||
{
|
||||
CXIExtendedHeader header = new CXIExtendedHeader();
|
||||
var header = new CXIExtendedHeader();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -24,17 +24,17 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Signature
|
||||
/// </summary>
|
||||
public byte[] Signature { get; private set; }
|
||||
public byte[]? Signature { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Issuer
|
||||
/// </summary>
|
||||
public byte[] Issuer { get; private set; }
|
||||
public byte[]? Issuer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Issuer as a trimmed string
|
||||
/// </summary>
|
||||
public string IssuerString => Issuer != null && Issuer.Length > 0
|
||||
public string? IssuerString => Issuer != null && Issuer.Length > 0
|
||||
? Encoding.ASCII.GetString(Issuer)?.TrimEnd('\0')
|
||||
: null;
|
||||
|
||||
@@ -46,12 +46,12 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Name
|
||||
/// </summary>
|
||||
public byte[] Name { get; private set; }
|
||||
public byte[]? Name { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name as a trimmed string
|
||||
/// </summary>
|
||||
public string NameString => Name != null && Name.Length > 0
|
||||
public string? NameString => Name != null && Name.Length > 0
|
||||
? Encoding.ASCII.GetString(Name)?.TrimEnd('\0')
|
||||
: null;
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Modulus
|
||||
/// </summary>
|
||||
public byte[] Modulus { get; private set; }
|
||||
public byte[]? Modulus { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Public Exponent
|
||||
@@ -81,7 +81,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Public Key
|
||||
/// </summary>
|
||||
public byte[] PublicKey { get; private set; }
|
||||
public byte[]? PublicKey { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -90,9 +90,9 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>Certificate object, null on error</returns>
|
||||
public static Certificate Read(BinaryReader reader)
|
||||
public static Certificate? Read(BinaryReader reader)
|
||||
{
|
||||
Certificate ct = new Certificate();
|
||||
var ct = new Certificate();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Address
|
||||
/// </summary>
|
||||
public byte[] Address { get; private set; }
|
||||
public byte[]? Address { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Physical region size (in page-multiples)
|
||||
@@ -24,9 +24,9 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>Code set info object, null on error</returns>
|
||||
public static CodeSetInfo Read(BinaryReader reader)
|
||||
public static CodeSetInfo? Read(BinaryReader reader)
|
||||
{
|
||||
CodeSetInfo csi = new CodeSetInfo();
|
||||
var csi = new CodeSetInfo();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -30,16 +30,16 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// SHA-256 hash
|
||||
/// </summary>
|
||||
public byte[] SHA256Hash { get; private set; }
|
||||
public byte[]? SHA256Hash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get content chunk record, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>Content chunk record object, null on error</returns>
|
||||
public static ContentChunkRecord Read(BinaryReader reader)
|
||||
public static ContentChunkRecord? Read(BinaryReader reader)
|
||||
{
|
||||
ContentChunkRecord ccr = new ContentChunkRecord();
|
||||
var ccr = new ContentChunkRecord();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -17,16 +17,16 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// SHA-256 hash of the next k content records that have not been hashed yet
|
||||
/// </summary>
|
||||
public byte[] UnhashedContentRecordsSHA256Hash { get; private set; }
|
||||
public byte[]? UnhashedContentRecordsSHA256Hash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get content info record, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>Content info record object, null on error</returns>
|
||||
public static ContentInfoRecord Read(BinaryReader reader)
|
||||
public static ContentInfoRecord? Read(BinaryReader reader)
|
||||
{
|
||||
ContentInfoRecord cir = new ContentInfoRecord();
|
||||
var cir = new ContentInfoRecord();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -7,14 +7,14 @@ namespace NDecrypt.N3DS.Headers
|
||||
internal class ExeFSFileHeader
|
||||
{
|
||||
// .code\0\0\0
|
||||
private readonly byte[] codeSegmentBytes = new byte[] { 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x00, 0x00, 0x00 };
|
||||
private readonly byte[] codeSegmentBytes = [0x2e, 0x63, 0x6f, 0x64, 0x65, 0x00, 0x00, 0x00];
|
||||
|
||||
/// <summary>
|
||||
/// File name
|
||||
/// </summary>
|
||||
public byte[] FileName { get; private set; }
|
||||
public string ReadableFileName { get { return Encoding.ASCII.GetString(FileName); } }
|
||||
public bool IsCodeBinary { get { return Enumerable.SequenceEqual(FileName, codeSegmentBytes); } }
|
||||
public byte[]? FileName { get; private set; }
|
||||
public string ReadableFileName { get { return Encoding.ASCII.GetString(FileName!); } }
|
||||
public bool IsCodeBinary { get { return Enumerable.SequenceEqual(FileName!, codeSegmentBytes); } }
|
||||
|
||||
/// <summary>
|
||||
/// File offset
|
||||
@@ -29,16 +29,16 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// SHA256 hash calculated over the entire file contents
|
||||
/// </summary>
|
||||
public byte[] FileHash { get; set; }
|
||||
public byte[]? FileHash { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get an ExeFS file header, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>ExeFS file header object, null on error</returns>
|
||||
public static ExeFSFileHeader Read(BinaryReader reader)
|
||||
public static ExeFSFileHeader? Read(BinaryReader reader)
|
||||
{
|
||||
ExeFSFileHeader header = new ExeFSFileHeader();
|
||||
var header = new ExeFSFileHeader();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -7,27 +7,27 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// File headers (10 headers maximum, 16 bytes each)
|
||||
/// </summary>
|
||||
public ExeFSFileHeader[] FileHeaders { get; private set; }
|
||||
public ExeFSFileHeader[]? FileHeaders { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved { get; private set; }
|
||||
public byte[]? Reserved { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get an ExeFS header, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>ExeFS header object, null on error</returns>
|
||||
public static ExeFSHeader Read(BinaryReader reader)
|
||||
public static ExeFSHeader? Read(BinaryReader reader)
|
||||
{
|
||||
ExeFSHeader header = new ExeFSHeader();
|
||||
var header = new ExeFSHeader();
|
||||
|
||||
try
|
||||
{
|
||||
header.FileHeaders = new ExeFSFileHeader[10];
|
||||
for (int i = 0; i < 10; i++)
|
||||
header.FileHeaders[i] = ExeFSFileHeader.Read(reader);
|
||||
header.FileHeaders[i] = ExeFSFileHeader.Read(reader)!;
|
||||
|
||||
header.Reserved = reader.ReadBytes(0x20);
|
||||
|
||||
|
||||
@@ -7,12 +7,12 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Title ID dependency list - Taken from the application's ExHeader
|
||||
/// </summary>
|
||||
public byte[] TitleIDDependencyList { get; private set; }
|
||||
public byte[]? TitleIDDependencyList { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved1 { get; private set; }
|
||||
public byte[]? Reserved1 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Core Version
|
||||
@@ -22,21 +22,21 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved2 { get; private set; }
|
||||
public byte[]? Reserved2 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Icon Data(.ICN) - Taken from the application's ExeFS
|
||||
/// </summary>
|
||||
public byte[] IconData { get; private set; }
|
||||
public byte[]? IconData { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get the Metafile data, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>Metafile data object, null on error</returns>
|
||||
public static MetaFile Read(BinaryReader reader)
|
||||
public static MetaFile? Read(BinaryReader reader)
|
||||
{
|
||||
MetaFile metaFile = new MetaFile();
|
||||
var metaFile = new MetaFile();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -16,12 +16,12 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Partition table entry for the current partition
|
||||
/// </summary>
|
||||
public PartitionTableEntry Entry { get; set; }
|
||||
public PartitionTableEntry? Entry { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// RSA-2048 signature of the NCCH header, using SHA-256.
|
||||
/// </summary>
|
||||
public byte[] RSA2048Signature { get; private set; }
|
||||
public byte[]? RSA2048Signature { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Content size, in media units (1 media unit = 0x200 bytes)
|
||||
@@ -31,10 +31,10 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Partition ID
|
||||
/// </summary>
|
||||
public byte[] PartitionId { get; private set; }
|
||||
public byte[] PlainIV { get { return PartitionId.Concat(Constants.PlainCounter).ToArray(); } }
|
||||
public byte[] ExeFSIV { get { return PartitionId.Concat(Constants.ExefsCounter).ToArray(); } }
|
||||
public byte[] RomFSIV { get { return PartitionId.Concat(Constants.RomfsCounter).ToArray(); } }
|
||||
public byte[]? PartitionId { get; private set; }
|
||||
public byte[]? PlainIV { get { return [.. PartitionId, .. Constants.PlainCounter]; } }
|
||||
public byte[]? ExeFSIV { get { return [.. PartitionId, .. Constants.ExefsCounter]; } }
|
||||
public byte[]? RomFSIV { get { return [.. PartitionId, .. Constants.RomfsCounter]; } }
|
||||
|
||||
/// <summary>
|
||||
/// Boot rom key
|
||||
@@ -82,27 +82,27 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Program ID
|
||||
/// </summary>
|
||||
public byte[] ProgramId { get; private set; }
|
||||
public byte[]? ProgramId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved1 { get; private set; }
|
||||
public byte[]? Reserved1 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Logo Region SHA-256 hash. (For applications built with SDK 5+) (Supported from firmware: 5.0.0-11)
|
||||
/// </summary>
|
||||
public byte[] LogoRegionHash { get; private set; }
|
||||
public byte[]? LogoRegionHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Product code
|
||||
/// </summary>
|
||||
public byte[] ProductCode { get; private set; }
|
||||
public byte[]? ProductCode { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Extended header SHA-256 hash (SHA256 of 2x Alignment Size, beginning at 0x0 of ExHeader)
|
||||
/// </summary>
|
||||
public byte[] ExtendedHeaderHash { get; private set; }
|
||||
public byte[]? ExtendedHeaderHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Extended header size, in bytes
|
||||
@@ -112,12 +112,12 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved2 { get; private set; }
|
||||
public byte[]? Reserved2 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Flags
|
||||
/// </summary>
|
||||
public NCCHHeaderFlags Flags { get; private set; }
|
||||
public NCCHHeaderFlags? Flags { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Plain region offset, in media units
|
||||
@@ -157,7 +157,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved3 { get; private set; }
|
||||
public byte[]? Reserved3 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// RomFS offset, in media units
|
||||
@@ -177,19 +177,19 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved4 { get; private set; }
|
||||
public byte[]? Reserved4 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ExeFS superblock SHA-256 hash - (SHA-256 hash, starting at 0x0 of the ExeFS over the number of
|
||||
/// media units specified in the ExeFS hash region size)
|
||||
/// </summary>
|
||||
public byte[] ExeFSSuperblockHash { get; private set; }
|
||||
public byte[]? ExeFSSuperblockHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// RomFS superblock SHA-256 hash - (SHA-256 hash, starting at 0x0 of the RomFS over the number
|
||||
/// of media units specified in the RomFS hash region size)
|
||||
/// </summary>
|
||||
public byte[] RomFSSuperblockHash { get; private set; }
|
||||
public byte[]? RomFSSuperblockHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get an NCCH header, if possible
|
||||
@@ -197,9 +197,9 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <param name="readSignature">True if the RSA signature is read, false otherwise</param>
|
||||
/// <returns>NCCH header object, null on error</returns>
|
||||
public static NCCHHeader Read(BinaryReader reader, bool readSignature)
|
||||
public static NCCHHeader? Read(BinaryReader reader, bool readSignature)
|
||||
{
|
||||
NCCHHeader header = new NCCHHeader();
|
||||
var header = new NCCHHeader();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -57,9 +57,9 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>NCCH header flags object, null on error</returns>
|
||||
public static NCCHHeaderFlags Read(BinaryReader reader)
|
||||
public static NCCHHeaderFlags? Read(BinaryReader reader)
|
||||
{
|
||||
NCCHHeaderFlags flags = new NCCHHeaderFlags();
|
||||
var flags = new NCCHHeaderFlags();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// RSA-2048 SHA-256 signature of the NCSD header
|
||||
/// </summary>
|
||||
public byte[] RSA2048Signature { get; private set; }
|
||||
public byte[]? RSA2048Signature { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Size of the NCSD image, in media units (1 media unit = 0x200 bytes)
|
||||
@@ -22,7 +22,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Media ID
|
||||
/// </summary>
|
||||
public byte[] MediaId { get; private set; }
|
||||
public byte[]? MediaId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Partitions FS type (0=None, 1=Normal, 3=FIRM, 4=AGB_FIRM save)
|
||||
@@ -32,37 +32,37 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Partitions crypt type (each byte corresponds to a partition in the partition table)
|
||||
/// </summary>
|
||||
public byte[] PartitionsCryptType { get; private set; }
|
||||
public byte[]? PartitionsCryptType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Offset & Length partition table, in media units
|
||||
/// </summary>
|
||||
public PartitionTableEntry[] PartitionsTable { get; private set; }
|
||||
public PartitionTableEntry[]? PartitionsTable { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Partition table entry for Executable Content (CXI)
|
||||
/// </summary>
|
||||
public PartitionTableEntry ExecutableContent { get { return PartitionsTable[0]; } }
|
||||
public PartitionTableEntry ExecutableContent { get { return PartitionsTable![0]; } }
|
||||
|
||||
/// <summary>
|
||||
/// Partition table entry for E-Manual (CFA)
|
||||
/// </summary>
|
||||
public PartitionTableEntry EManual { get { return PartitionsTable[1]; } }
|
||||
public PartitionTableEntry EManual { get { return PartitionsTable![1]; } }
|
||||
|
||||
/// <summary>
|
||||
/// Partition table entry for Download Play Child container (CFA)
|
||||
/// </summary>
|
||||
public PartitionTableEntry DownloadPlayChildContainer { get { return PartitionsTable[2]; } }
|
||||
public PartitionTableEntry DownloadPlayChildContainer { get { return PartitionsTable![2]; } }
|
||||
|
||||
/// <summary>
|
||||
/// Partition table entry for New3DS Update Data (CFA)
|
||||
/// </summary>
|
||||
public PartitionTableEntry New3DSUpdateData { get { return PartitionsTable[6]; } }
|
||||
public PartitionTableEntry New3DSUpdateData { get { return PartitionsTable![6]; } }
|
||||
|
||||
/// <summary>
|
||||
/// Partition table entry for Update Data (CFA)
|
||||
/// </summary>
|
||||
public PartitionTableEntry UpdateData { get { return PartitionsTable[7]; } }
|
||||
public PartitionTableEntry UpdateData { get { return PartitionsTable![7]; } }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Exheader SHA-256 hash
|
||||
/// </summary>
|
||||
public byte[] ExheaderHash { get; private set; }
|
||||
public byte[]? ExheaderHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Additional header size
|
||||
@@ -86,53 +86,53 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Partition Flags
|
||||
/// </summary>
|
||||
public byte[] PartitionFlags { get; private set; }
|
||||
public byte[]? PartitionFlags { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Backup Write Wait Time (The time to wait to write save to backup after the card is recognized (0-255
|
||||
/// seconds)).NATIVE_FIRM loads this flag from the gamecard NCSD header starting with 6.0.0-11.
|
||||
/// </summary>
|
||||
public byte BackupWriteWaitTime { get { return PartitionFlags[(int)NCSDFlags.BackupWriteWaitTime]; } }
|
||||
public byte BackupWriteWaitTime { get { return PartitionFlags![(int)NCSDFlags.BackupWriteWaitTime]; } }
|
||||
|
||||
/// <summary>
|
||||
/// Media Card Device (1 = NOR Flash, 2 = None, 3 = BT) (SDK 3.X+)
|
||||
/// </summary>
|
||||
public MediaCardDeviceType MediaCardDevice3X { get { return (MediaCardDeviceType)PartitionFlags[(int)NCSDFlags.MediaCardDevice3X]; } }
|
||||
public MediaCardDeviceType MediaCardDevice3X { get { return (MediaCardDeviceType)PartitionFlags![(int)NCSDFlags.MediaCardDevice3X]; } }
|
||||
|
||||
/// <summary>
|
||||
/// Media Platform Index (1 = CTR)
|
||||
/// </summary>
|
||||
public MediaPlatformIndex MediaPlatformIndex { get { return (MediaPlatformIndex)PartitionFlags[(int)NCSDFlags.MediaPlatformIndex]; } }
|
||||
public MediaPlatformIndex MediaPlatformIndex { get { return (MediaPlatformIndex)PartitionFlags![(int)NCSDFlags.MediaPlatformIndex]; } }
|
||||
|
||||
/// <summary>
|
||||
/// Media Type Index (0 = Inner Device, 1 = Card1, 2 = Card2, 3 = Extended Device)
|
||||
/// </summary>
|
||||
public MediaTypeIndex MediaTypeIndex { get { return (MediaTypeIndex)PartitionFlags[(int)NCSDFlags.MediaTypeIndex]; } }
|
||||
public MediaTypeIndex MediaTypeIndex { get { return (MediaTypeIndex)PartitionFlags![(int)NCSDFlags.MediaTypeIndex]; } }
|
||||
|
||||
/// <summary>
|
||||
/// Media Unit Size i.e. u32 MediaUnitSize = 0x200*2^flags[6];
|
||||
/// </summary>
|
||||
public uint MediaUnitSize { get { return (uint)(0x200 * Math.Pow(2, PartitionFlags[(int)NCSDFlags.MediaUnitSize])); } }
|
||||
public uint MediaUnitSize { get { return (uint)(0x200 * Math.Pow(2, PartitionFlags![(int)NCSDFlags.MediaUnitSize])); } }
|
||||
|
||||
/// <summary>
|
||||
/// Media Card Device (1 = NOR Flash, 2 = None, 3 = BT) (Only SDK 2.X)
|
||||
/// </summary>
|
||||
public MediaCardDeviceType MediaCardDevice2X { get { return (MediaCardDeviceType)PartitionFlags[(int)NCSDFlags.MediaCardDevice2X]; } }
|
||||
public MediaCardDeviceType MediaCardDevice2X { get { return (MediaCardDeviceType)PartitionFlags![(int)NCSDFlags.MediaCardDevice2X]; } }
|
||||
|
||||
/// <summary>
|
||||
/// Partition ID table
|
||||
/// </summary>
|
||||
public byte[][] PartitionIdTable { get; private set; }
|
||||
public byte[][]? PartitionIdTable { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved1 { get; private set; }
|
||||
public byte[]? Reserved1 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved?
|
||||
/// </summary>
|
||||
public byte[] Reserved2 { get; private set; }
|
||||
public byte[]? Reserved2 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Support for this was implemented with 9.6.0-X FIRM. Bit0=1 enables using bits 1-2, it's unknown
|
||||
@@ -154,12 +154,12 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Unknown
|
||||
/// </summary>
|
||||
public byte[] Unknown { get; private set; }
|
||||
public byte[]? Unknown { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Encrypted MBR partition-table, for the TWL partitions(key-data used for this keyslot is console-unique).
|
||||
/// </summary>
|
||||
public byte[] EncryptedMBR { get; private set; }
|
||||
public byte[]? EncryptedMBR { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -168,17 +168,17 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// CARD2: Writable Address In Media Units (For 'On-Chip' Savedata). CARD1: Always 0xFFFFFFFF.
|
||||
/// </summary>
|
||||
public byte[] CARD2WritableAddressMediaUnits { get; private set; }
|
||||
public byte[]? CARD2WritableAddressMediaUnits { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Card Info Bitmask
|
||||
/// </summary>
|
||||
public byte[] CardInfoBytemask { get; private set; }
|
||||
public byte[]? CardInfoBytemask { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved1
|
||||
/// </summary>
|
||||
public byte[] Reserved3 { get; private set; }
|
||||
public byte[]? Reserved3 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Title version
|
||||
@@ -193,36 +193,37 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Reserved2
|
||||
/// </summary>
|
||||
public byte[] Reserved4 { get; private set; }
|
||||
public byte[]? Reserved4 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Card seed keyY (first u64 is Media ID (same as first NCCH partitionId))
|
||||
/// </summary>
|
||||
public byte[] CardSeedKeyY { get; private set; }
|
||||
public byte[]? CardSeedKeyY { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Encrypted card seed (AES-CCM, keyslot 0x3B for retail cards, see CTRCARD_SECSEED) /// </summary>
|
||||
public byte[] EncryptedCardSeed { get; private set; }
|
||||
/// Encrypted card seed (AES-CCM, keyslot 0x3B for retail cards, see CTRCARD_SECSEED)
|
||||
/// </summary>
|
||||
public byte[]? EncryptedCardSeed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Card seed AES-MAC
|
||||
/// </summary>
|
||||
public byte[] CardSeedAESMAC { get; private set; }
|
||||
public byte[]? CardSeedAESMAC { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Card seed nonce
|
||||
/// </summary>
|
||||
public byte[] CardSeedNonce { get; private set; }
|
||||
public byte[]? CardSeedNonce { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved3
|
||||
/// </summary>
|
||||
public byte[] Reserved5 { get; private set; }
|
||||
public byte[]? Reserved5 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Copy of first NCCH header (excluding RSA signature)
|
||||
/// </summary>
|
||||
public NCCHHeader BackupHeader { get; private set; }
|
||||
public NCCHHeader? BackupHeader { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -231,17 +232,17 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// CardDeviceReserved1
|
||||
/// </summary>
|
||||
public byte[] CardDeviceReserved1 { get; private set; }
|
||||
public byte[]? CardDeviceReserved1 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// TitleKey
|
||||
/// </summary>
|
||||
public byte[] TitleKey { get; private set; }
|
||||
public byte[]? TitleKey { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// CardDeviceReserved2
|
||||
/// </summary>
|
||||
public byte[] CardDeviceReserved2 { get; private set; }
|
||||
public byte[]? CardDeviceReserved2 { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -251,9 +252,9 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <param name="development">True if development cart, false otherwise</param>
|
||||
/// <returns>NCSD header object, null on error</returns>
|
||||
public static NCSDHeader Read(BinaryReader reader, bool development)
|
||||
public static NCSDHeader? Read(BinaryReader reader, bool development)
|
||||
{
|
||||
NCSDHeader header = new NCSDHeader();
|
||||
var header = new NCSDHeader();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -269,7 +270,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
|
||||
header.PartitionsTable = new PartitionTableEntry[8];
|
||||
for (int i = 0; i < 8; i++)
|
||||
header.PartitionsTable[i] = PartitionTableEntry.Read(reader);
|
||||
header.PartitionsTable[i] = PartitionTableEntry.Read(reader)!;
|
||||
|
||||
if (header.PartitionsFSType == FilesystemType.Normal
|
||||
|| header.PartitionsFSType == FilesystemType.None)
|
||||
|
||||
@@ -19,9 +19,9 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>Partition table entry object, null on error</returns>
|
||||
public static PartitionTableEntry Read(BinaryReader reader)
|
||||
public static PartitionTableEntry? Read(BinaryReader reader)
|
||||
{
|
||||
PartitionTableEntry entry = new PartitionTableEntry();
|
||||
var entry = new PartitionTableEntry();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved1 { get; private set; }
|
||||
public byte[]? Reserved1 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Level 2 logical offset
|
||||
@@ -51,7 +51,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved2 { get; private set; }
|
||||
public byte[]? Reserved2 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Level 3 logical offset
|
||||
@@ -63,7 +63,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// </summary>
|
||||
public ulong Level3HashdataSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// <summary>s
|
||||
/// Level 3 block size, in log2
|
||||
/// </summary>
|
||||
public uint Level3BlockSizeLog2 { get; private set; }
|
||||
@@ -71,12 +71,12 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved3 { get; private set; }
|
||||
public byte[]? Reserved3 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved4 { get; private set; }
|
||||
public byte[]? Reserved4 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional info size.
|
||||
@@ -88,9 +88,9 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>RomFS header object, null on error</returns>
|
||||
public static RomFSHeader Read(BinaryReader reader)
|
||||
public static RomFSHeader? Read(BinaryReader reader)
|
||||
{
|
||||
RomFSHeader header = new RomFSHeader();
|
||||
var header = new RomFSHeader();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -7,22 +7,22 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Extdata ID
|
||||
/// </summary>
|
||||
public byte[] ExtdataID { get; private set; }
|
||||
public byte[]? ExtdataID { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// System savedata IDs
|
||||
/// </summary>
|
||||
public byte[] SystemSavedataIDs { get; private set; }
|
||||
public byte[]? SystemSavedataIDs { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Storage accessible unique IDs
|
||||
/// </summary>
|
||||
public byte[] StorageAccessibleUniqueIDs { get; private set; }
|
||||
public byte[]? StorageAccessibleUniqueIDs { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Filesystem access info
|
||||
/// </summary>
|
||||
public byte[] FilesystemAccessInfo { get; private set; }
|
||||
public byte[]? FilesystemAccessInfo { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Other attributes
|
||||
@@ -34,9 +34,9 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>Storage info object, null on error</returns>
|
||||
public static StorageInfo Read(BinaryReader reader)
|
||||
public static StorageInfo? Read(BinaryReader reader)
|
||||
{
|
||||
StorageInfo si = new StorageInfo();
|
||||
var si = new StorageInfo();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -7,12 +7,12 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Application title (default is "CtrApp")
|
||||
/// </summary>
|
||||
public char[] ApplicationTitle { get; private set; }
|
||||
public char[]? ApplicationTitle { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved1 { get; private set; }
|
||||
public byte[]? Reserved1 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Flag (bit 0: CompressExefsCode, bit 1: SDApplication)
|
||||
@@ -22,12 +22,12 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Remaster version
|
||||
/// </summary>
|
||||
public byte[] RemasterVersion { get; private set; }
|
||||
public byte[]? RemasterVersion { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Text code set info
|
||||
/// </summary>
|
||||
public CodeSetInfo TextCodesetInfo { get; private set; }
|
||||
public CodeSetInfo? TextCodesetInfo { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Stack size
|
||||
@@ -37,17 +37,17 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Read-only code set info
|
||||
/// </summary>
|
||||
public CodeSetInfo ReadOnlyCodeSetInfo { get; private set; }
|
||||
public CodeSetInfo? ReadOnlyCodeSetInfo { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved2 { get; private set; }
|
||||
public byte[]? Reserved2 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Data code set info
|
||||
/// </summary>
|
||||
public CodeSetInfo DataCodeSetInfo { get; private set; }
|
||||
public CodeSetInfo? DataCodeSetInfo { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// BSS size
|
||||
@@ -57,21 +57,21 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Dependency module (program ID) list
|
||||
/// </summary>
|
||||
public byte[][] DependencyModuleList { get; private set; }
|
||||
public byte[][]? DependencyModuleList { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// SystemInfo
|
||||
/// </summary>
|
||||
public SystemInfo SystemInfo { get; private set; }
|
||||
public SystemInfo? SystemInfo { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get system control info, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>System control info object, null on error</returns>
|
||||
public static SystemControlInfo Read(BinaryReader reader)
|
||||
public static SystemControlInfo? Read(BinaryReader reader)
|
||||
{
|
||||
SystemControlInfo sci = new SystemControlInfo();
|
||||
var sci = new SystemControlInfo();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -12,21 +12,21 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Jump ID
|
||||
/// </summary>
|
||||
public byte[] JumpID { get; private set; }
|
||||
public byte[]? JumpID { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved { get; private set; }
|
||||
public byte[]? Reserved { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get system info, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>System info object, null on error</returns>
|
||||
public static SystemInfo Read(BinaryReader reader)
|
||||
public static SystemInfo? Read(BinaryReader reader)
|
||||
{
|
||||
SystemInfo si = new SystemInfo();
|
||||
var si = new SystemInfo();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -26,24 +26,24 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Signature
|
||||
/// </summary>
|
||||
public byte[] Signature { get; private set; }
|
||||
public byte[]? Signature { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Issuer
|
||||
/// </summary>
|
||||
public byte[] Issuer { get; private set; }
|
||||
public byte[]? Issuer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Issuer as a trimmed string
|
||||
/// </summary>
|
||||
public string IssuerString => Issuer != null && Issuer.Length > 0
|
||||
public string? IssuerString => Issuer != null && Issuer.Length > 0
|
||||
? Encoding.ASCII.GetString(Issuer)?.TrimEnd('\0')
|
||||
: null;
|
||||
|
||||
/// <summary>
|
||||
/// ECC PublicKey
|
||||
/// </summary>
|
||||
public byte[] ECCPublicKey { get; private set; }
|
||||
public byte[]? ECCPublicKey { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Version (For 3DS this is always 1)
|
||||
@@ -74,7 +74,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// The titlekey is used to decrypt content downloaded from the CDN using 128-bit AES-CBC with
|
||||
/// the content index (as big endian u16, padded with trailing zeroes) as the IV.
|
||||
/// </remarks>
|
||||
public byte[] TitleKey { get; private set; }
|
||||
public byte[]? TitleKey { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
@@ -129,7 +129,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved4 { get; private set; }
|
||||
public byte[]? Reserved4 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// eShop Account ID?
|
||||
@@ -149,7 +149,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved6 { get; private set; }
|
||||
public byte[]? Reserved6 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Limits
|
||||
@@ -157,7 +157,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <remarks>
|
||||
/// In demos, the first u32 in the "Limits" section is 0x4, then the second u32 is the max-playcount.
|
||||
/// </remarks>
|
||||
public int[] Limits { get; private set; }
|
||||
public int[]? Limits { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Denotes if the ticket denotes a demo or not
|
||||
@@ -179,7 +179,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Content Index
|
||||
/// </summary>
|
||||
public byte[] ContentIndex { get; private set; }
|
||||
public byte[]? ContentIndex { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Certificate chain
|
||||
@@ -187,7 +187,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <remarks>
|
||||
/// https://www.3dbrew.org/wiki/Ticket#Certificate_Chain
|
||||
/// </remarks>
|
||||
public Certificate[] CertificateChain { get; set; }
|
||||
public Certificate[]? CertificateChain { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get ticket, if possible
|
||||
@@ -195,9 +195,9 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <param name="ticketSize">Ticket size from the header</param>
|
||||
/// <returns>Ticket object, null on error</returns>
|
||||
public static Ticket Read(BinaryReader reader, int ticketSize)
|
||||
public static Ticket? Read(BinaryReader reader, int ticketSize)
|
||||
{
|
||||
Ticket tk = new Ticket();
|
||||
var tk = new Ticket();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -264,8 +264,8 @@ namespace NDecrypt.N3DS.Headers
|
||||
if (ticketSize > (reader.BaseStream.Position - startingPosition) + (2 * 0x200))
|
||||
{
|
||||
tk.CertificateChain = new Certificate[2];
|
||||
tk.CertificateChain[0] = Certificate.Read(reader); // Ticket
|
||||
tk.CertificateChain[1] = Certificate.Read(reader); // CA
|
||||
tk.CertificateChain[0] = Certificate.Read(reader)!; // Ticket
|
||||
tk.CertificateChain[1] = Certificate.Read(reader)!; // CA
|
||||
}
|
||||
|
||||
return tk;
|
||||
|
||||
@@ -26,17 +26,17 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Signature
|
||||
/// </summary>
|
||||
public byte[] Signature { get; private set; }
|
||||
public byte[]? Signature { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Signature Issuer
|
||||
/// </summary>
|
||||
public byte[] SignatureIssuer { get; private set; }
|
||||
public byte[]? SignatureIssuer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Signature Issuer as a trimmed string
|
||||
/// </summary>
|
||||
public string SignatureIssuerString => SignatureIssuer != null && SignatureIssuer.Length > 0
|
||||
public string? SignatureIssuerString => SignatureIssuer != null && SignatureIssuer.Length > 0
|
||||
? Encoding.ASCII.GetString(SignatureIssuer)?.TrimEnd('\0')
|
||||
: null;
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved3 { get; private set; }
|
||||
public byte[]? Reserved3 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Access Rights
|
||||
@@ -133,18 +133,18 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// SHA-256 Hash of the Content Info Records
|
||||
/// </summary>
|
||||
public byte[] SHA256HashContentInfoRecords { get; private set; }
|
||||
public byte[]? SHA256HashContentInfoRecords { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// There are 64 of these records, usually only the first is used.
|
||||
/// </summary>
|
||||
public ContentInfoRecord[] ContentInfoRecords { get; private set; }
|
||||
public ContentInfoRecord[]? ContentInfoRecords { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// There is one of these for each content contained in this title.
|
||||
/// (Determined by "Content Count" in the TMD Header).
|
||||
/// </summary>
|
||||
public ContentChunkRecord[] ContentChunkRecords { get; private set; }
|
||||
public ContentChunkRecord[]? ContentChunkRecords { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Certificate chain
|
||||
@@ -152,7 +152,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <remarks>
|
||||
/// https://www.3dbrew.org/wiki/Title_metadata#Certificate_Chain
|
||||
/// </remarks>
|
||||
public Certificate[] CertificateChain { get; set; }
|
||||
public Certificate[]? CertificateChain { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get ticket metadata, if possible
|
||||
@@ -160,9 +160,9 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <param name="metadataSize">Metadata size from the header</param>
|
||||
/// <returns>Title metadata object, null on error</returns>
|
||||
public static TitleMetadata Read(BinaryReader reader, int metadataSize)
|
||||
public static TitleMetadata? Read(BinaryReader reader, int metadataSize)
|
||||
{
|
||||
TitleMetadata tm = new TitleMetadata();
|
||||
var tm = new TitleMetadata();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -214,20 +214,20 @@ namespace NDecrypt.N3DS.Headers
|
||||
tm.ContentInfoRecords = new ContentInfoRecord[64];
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
tm.ContentInfoRecords[i] = ContentInfoRecord.Read(reader);
|
||||
tm.ContentInfoRecords[i] = ContentInfoRecord.Read(reader)!;
|
||||
}
|
||||
|
||||
tm.ContentChunkRecords = new ContentChunkRecord[tm.ContentCount];
|
||||
for (int i = 0; i < tm.ContentCount; i++)
|
||||
{
|
||||
tm.ContentChunkRecords[i] = ContentChunkRecord.Read(reader);
|
||||
tm.ContentChunkRecords[i] = ContentChunkRecord.Read(reader)!;
|
||||
}
|
||||
|
||||
if (metadataSize > (reader.BaseStream.Position - startingPosition) + (2 * 0x200))
|
||||
{
|
||||
tm.CertificateChain = new Certificate[2];
|
||||
tm.CertificateChain[0] = Certificate.Read(reader); // TMD
|
||||
tm.CertificateChain[1] = Certificate.Read(reader); // CA
|
||||
tm.CertificateChain[0] = Certificate.Read(reader)!; // TMD
|
||||
tm.CertificateChain[1] = Certificate.Read(reader)!; // CA
|
||||
}
|
||||
|
||||
return tm;
|
||||
|
||||
@@ -1,17 +1,38 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;net6.0</TargetFrameworks>
|
||||
<!-- Assembly Properties -->
|
||||
<TargetFrameworks>net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<VersionPrefix>0.2.5</VersionPrefix>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski</Authors>
|
||||
<Copyright>Copyright (c)2018-2023 Matt Nadareski</Copyright>
|
||||
<Description>3DS encryption and decryption code</Description>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2019-2024</Copyright>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/SabreTools/NDecrypt</RepositoryUrl>
|
||||
<Version>0.2.5</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<FileVersion>$(Version)</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Support All Frameworks -->
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net4`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(RuntimeIdentifier.StartsWith(`osx-arm`))">
|
||||
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace NDecrypt.N3DS
|
||||
using (BinaryReader reader = new BinaryReader(File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
|
||||
using (BinaryWriter writer = new BinaryWriter(File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)))
|
||||
{
|
||||
NCSDHeader header = NCSDHeader.Read(reader, decryptArgs.Development);
|
||||
NCSDHeader? header = NCSDHeader.Read(reader, decryptArgs.Development);
|
||||
if (header == null)
|
||||
{
|
||||
Console.WriteLine("Error: Not a 3DS cart image!");
|
||||
@@ -78,7 +78,7 @@ namespace NDecrypt.N3DS
|
||||
// Iterate over all 8 NCCH partitions
|
||||
for (int p = 0; p < 8; p++)
|
||||
{
|
||||
NCCHHeader ncchHeader = GetPartitionHeader(ncsdHeader, reader, p);
|
||||
NCCHHeader? ncchHeader = GetPartitionHeader(ncsdHeader, reader, p);
|
||||
if (ncchHeader == null)
|
||||
continue;
|
||||
|
||||
@@ -93,9 +93,9 @@ namespace NDecrypt.N3DS
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <param name="partitionNumber">Partition number to attempt to retrieve</param>
|
||||
/// <returns>NCCH header for the partition requested, null on error</returns>
|
||||
private NCCHHeader GetPartitionHeader(NCSDHeader ncsdHeader, BinaryReader reader, int partitionNumber)
|
||||
private NCCHHeader? GetPartitionHeader(NCSDHeader ncsdHeader, BinaryReader reader, int partitionNumber)
|
||||
{
|
||||
if (!ncsdHeader.PartitionsTable[partitionNumber].IsValid())
|
||||
if (!ncsdHeader.PartitionsTable![partitionNumber].IsValid())
|
||||
{
|
||||
Console.WriteLine($"Partition {partitionNumber} Not found... Skipping...");
|
||||
return null;
|
||||
@@ -104,7 +104,7 @@ namespace NDecrypt.N3DS
|
||||
// Seek to the beginning of the NCCH partition
|
||||
reader.BaseStream.Seek((ncsdHeader.PartitionsTable[partitionNumber].Offset * ncsdHeader.MediaUnitSize), SeekOrigin.Begin);
|
||||
|
||||
NCCHHeader partitionHeader = NCCHHeader.Read(reader, readSignature: true);
|
||||
NCCHHeader? partitionHeader = NCCHHeader.Read(reader, readSignature: true);
|
||||
if (partitionHeader == null)
|
||||
{
|
||||
Console.WriteLine($"Partition {partitionNumber} Unable to read NCCH header");
|
||||
@@ -131,7 +131,7 @@ namespace NDecrypt.N3DS
|
||||
Console.WriteLine($"Partition {ncchHeader.PartitionNumber} is not verified due to force flag being set.");
|
||||
}
|
||||
// If we're not forcing the operation, check if the 'NoCrypto' bit is set
|
||||
else if (ncchHeader.Flags.PossblyDecrypted ^ decryptArgs.Encrypt)
|
||||
else if (ncchHeader.Flags!.PossblyDecrypted ^ decryptArgs.Encrypt)
|
||||
{
|
||||
Console.WriteLine($"Partition {ncchHeader.PartitionNumber}: Already " + (decryptArgs.Encrypt ? "Encrypted" : "Decrypted") + "?...");
|
||||
return;
|
||||
@@ -184,12 +184,12 @@ namespace NDecrypt.N3DS
|
||||
CryptoMethod method;
|
||||
if (decryptArgs.Encrypt)
|
||||
{
|
||||
masks = ncsdHeader.BackupHeader.Flags.BitMasks;
|
||||
masks = ncsdHeader.BackupHeader!.Flags!.BitMasks;
|
||||
method = ncsdHeader.BackupHeader.Flags.CryptoMethod;
|
||||
}
|
||||
else
|
||||
{
|
||||
masks = ncchHeader.Flags.BitMasks;
|
||||
masks = ncchHeader.Flags!.BitMasks;
|
||||
method = ncchHeader.Flags.CryptoMethod;
|
||||
}
|
||||
|
||||
@@ -237,12 +237,12 @@ namespace NDecrypt.N3DS
|
||||
{
|
||||
if (ncchHeader.ExtendedHeaderSizeInBytes > 0)
|
||||
{
|
||||
reader.BaseStream.Seek((ncchHeader.Entry.Offset * ncsdHeader.MediaUnitSize) + 0x200, SeekOrigin.Begin);
|
||||
reader.BaseStream.Seek((ncchHeader.Entry!.Offset * ncsdHeader.MediaUnitSize) + 0x200, SeekOrigin.Begin);
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset * ncsdHeader.MediaUnitSize) + 0x200, SeekOrigin.Begin);
|
||||
|
||||
Console.WriteLine($"Partition {ncchHeader.PartitionNumber} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + ": ExHeader");
|
||||
|
||||
var cipher = CreateAESCipher(ncchHeader.NormalKey2C, ncchHeader.PlainIV, decryptArgs.Encrypt);
|
||||
var cipher = CreateAESCipher(ncchHeader.NormalKey2C, ncchHeader.PlainIV!, decryptArgs.Encrypt);
|
||||
byte[] readBytes = reader.ReadBytes(Constants.CXTExtendedDataHeaderLength);
|
||||
byte[] processedBytes = cipher.ProcessBytes(readBytes);
|
||||
writer.Write(processedBytes);
|
||||
@@ -265,8 +265,8 @@ namespace NDecrypt.N3DS
|
||||
/// <param name="writer">BinaryWriter representing the output stream</param>
|
||||
private void ProcessExeFSFileEntries(NCSDHeader ncsdHeader, NCCHHeader ncchHeader, BinaryReader reader, BinaryWriter writer)
|
||||
{
|
||||
reader.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * ncsdHeader.MediaUnitSize, SeekOrigin.Begin);
|
||||
ExeFSHeader exefsHeader = ExeFSHeader.Read(reader);
|
||||
reader.BaseStream.Seek((ncchHeader.Entry!.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * ncsdHeader.MediaUnitSize, SeekOrigin.Begin);
|
||||
ExeFSHeader? exefsHeader = ExeFSHeader.Read(reader);
|
||||
|
||||
// If the header failed to read, log and return
|
||||
if (exefsHeader == null)
|
||||
@@ -275,7 +275,7 @@ namespace NDecrypt.N3DS
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (ExeFSFileHeader fileHeader in exefsHeader.FileHeaders)
|
||||
foreach (ExeFSFileHeader fileHeader in exefsHeader.FileHeaders!)
|
||||
{
|
||||
// Only decrypt a file if it's a code binary
|
||||
if (!fileHeader.IsCodeBinary)
|
||||
@@ -285,7 +285,7 @@ namespace NDecrypt.N3DS
|
||||
uint datalenB = ((fileHeader.FileSize) % (1024 * 1024));
|
||||
uint ctroffset = ((fileHeader.FileOffset + ncsdHeader.MediaUnitSize) / 0x10);
|
||||
|
||||
byte[] exefsIVWithOffsetForHeader = AddToByteArray(ncchHeader.ExeFSIV, (int)ctroffset);
|
||||
byte[] exefsIVWithOffsetForHeader = AddToByteArray(ncchHeader.ExeFSIV!, (int)ctroffset);
|
||||
|
||||
var firstCipher = CreateAESCipher(ncchHeader.NormalKey, exefsIVWithOffsetForHeader, decryptArgs.Encrypt);
|
||||
var secondCipher = CreateAESCipher(ncchHeader.NormalKey2C, exefsIVWithOffsetForHeader, !decryptArgs.Encrypt);
|
||||
@@ -328,12 +328,12 @@ namespace NDecrypt.N3DS
|
||||
/// <param name="writer">BinaryWriter representing the output stream</param>
|
||||
private void ProcessExeFSFilenameTable(NCSDHeader ncsdHeader, NCCHHeader ncchHeader, BinaryReader reader, BinaryWriter writer)
|
||||
{
|
||||
reader.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * ncsdHeader.MediaUnitSize, SeekOrigin.Begin);
|
||||
reader.BaseStream.Seek((ncchHeader.Entry!.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * ncsdHeader.MediaUnitSize, SeekOrigin.Begin);
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * ncsdHeader.MediaUnitSize, SeekOrigin.Begin);
|
||||
|
||||
Console.WriteLine($"Partition {ncchHeader.PartitionNumber} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + $": ExeFS Filename Table");
|
||||
|
||||
var cipher = CreateAESCipher(ncchHeader.NormalKey2C, ncchHeader.ExeFSIV, decryptArgs.Encrypt);
|
||||
var cipher = CreateAESCipher(ncchHeader.NormalKey2C, ncchHeader.ExeFSIV!, decryptArgs.Encrypt);
|
||||
byte[] readBytes = reader.ReadBytes((int)ncsdHeader.MediaUnitSize);
|
||||
byte[] processedBytes = cipher.ProcessBytes(readBytes);
|
||||
writer.Write(processedBytes);
|
||||
@@ -358,11 +358,11 @@ namespace NDecrypt.N3DS
|
||||
int exefsSizeB = (int)((long)((ncchHeader.ExeFSSizeInMediaUnits - 1) * ncsdHeader.MediaUnitSize) % (1024 * 1024));
|
||||
int ctroffsetE = (int)(ncsdHeader.MediaUnitSize / 0x10);
|
||||
|
||||
byte[] exefsIVWithOffset = AddToByteArray(ncchHeader.ExeFSIV, ctroffsetE);
|
||||
byte[] exefsIVWithOffset = AddToByteArray(ncchHeader.ExeFSIV!, ctroffsetE);
|
||||
|
||||
var cipher = CreateAESCipher(ncchHeader.NormalKey2C, exefsIVWithOffset, decryptArgs.Encrypt);
|
||||
|
||||
reader.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.ExeFSOffsetInMediaUnits + 1) * ncsdHeader.MediaUnitSize, SeekOrigin.Begin);
|
||||
reader.BaseStream.Seek((ncchHeader.Entry!.Offset + ncchHeader.ExeFSOffsetInMediaUnits + 1) * ncsdHeader.MediaUnitSize, SeekOrigin.Begin);
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.ExeFSOffsetInMediaUnits + 1) * ncsdHeader.MediaUnitSize, SeekOrigin.Begin);
|
||||
if (exefsSizeM > 0)
|
||||
{
|
||||
@@ -410,7 +410,7 @@ namespace NDecrypt.N3DS
|
||||
ProcessExeFSFilenameTable(ncsdHeader, ncchHeader, reader, writer);
|
||||
|
||||
// For all but the original crypto method, process each of the files in the table
|
||||
if (ncchHeader.Flags.CryptoMethod != CryptoMethod.Original)
|
||||
if (ncchHeader.Flags!.CryptoMethod != CryptoMethod.Original)
|
||||
ProcessExeFSFileEntries(ncsdHeader, ncchHeader, reader, writer);
|
||||
|
||||
// Decrypt the rest of the ExeFS
|
||||
@@ -437,9 +437,9 @@ namespace NDecrypt.N3DS
|
||||
long romfsSizeM = (int)((long)(ncchHeader.RomFSSizeInMediaUnits * ncsdHeader.MediaUnitSize) / (1024 * 1024));
|
||||
int romfsSizeB = (int)((long)(ncchHeader.RomFSSizeInMediaUnits * ncsdHeader.MediaUnitSize) % (1024 * 1024));
|
||||
|
||||
var cipher = CreateAESCipher(ncchHeader.NormalKey, ncchHeader.RomFSIV, decryptArgs.Encrypt);
|
||||
var cipher = CreateAESCipher(ncchHeader.NormalKey, ncchHeader.RomFSIV!, decryptArgs.Encrypt);
|
||||
|
||||
reader.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * ncsdHeader.MediaUnitSize, SeekOrigin.Begin);
|
||||
reader.BaseStream.Seek((ncchHeader.Entry!.Offset + ncchHeader.RomFSOffsetInMediaUnits) * ncsdHeader.MediaUnitSize, SeekOrigin.Begin);
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * ncsdHeader.MediaUnitSize, SeekOrigin.Begin);
|
||||
if (romfsSizeM > 0)
|
||||
{
|
||||
@@ -472,13 +472,13 @@ namespace NDecrypt.N3DS
|
||||
private void UpdateDecryptCryptoAndMasks(NCSDHeader ncsdHeader, NCCHHeader ncchHeader, BinaryWriter writer)
|
||||
{
|
||||
// Write the new CryptoMethod
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset * ncsdHeader.MediaUnitSize) + 0x18B, SeekOrigin.Begin);
|
||||
writer.BaseStream.Seek((ncchHeader.Entry!.Offset * ncsdHeader.MediaUnitSize) + 0x18B, SeekOrigin.Begin);
|
||||
writer.Write((byte)CryptoMethod.Original);
|
||||
writer.Flush();
|
||||
|
||||
// Write the new BitMasks flag
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset * ncsdHeader.MediaUnitSize) + 0x18F, SeekOrigin.Begin);
|
||||
BitMasks flag = ncchHeader.Flags.BitMasks;
|
||||
BitMasks flag = ncchHeader.Flags!.BitMasks;
|
||||
flag &= (BitMasks)((byte)(BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator) ^ 0xFF);
|
||||
flag |= BitMasks.NoCrypto;
|
||||
writer.Write((byte)flag);
|
||||
@@ -506,7 +506,7 @@ namespace NDecrypt.N3DS
|
||||
}
|
||||
|
||||
// For all but the original crypto method, process each of the files in the table
|
||||
if (ncsdHeader.BackupHeader.Flags.CryptoMethod != CryptoMethod.Original)
|
||||
if (ncsdHeader.BackupHeader!.Flags!.CryptoMethod != CryptoMethod.Original)
|
||||
ProcessExeFSFileEntries(ncsdHeader, ncchHeader, reader, writer);
|
||||
|
||||
// Encrypt the filename table
|
||||
@@ -539,7 +539,7 @@ namespace NDecrypt.N3DS
|
||||
// Encrypting RomFS for partitions 1 and up always use Key0x2C
|
||||
if (ncchHeader.PartitionNumber > 0)
|
||||
{
|
||||
if (ncsdHeader.BackupHeader.Flags?.BitMasks.HasFlag(BitMasks.FixedCryptoKey) == true) // except if using zero-key
|
||||
if (ncsdHeader.BackupHeader!.Flags?.BitMasks.HasFlag(BitMasks.FixedCryptoKey) == true) // except if using zero-key
|
||||
{
|
||||
ncchHeader.NormalKey = 0x00;
|
||||
}
|
||||
@@ -550,9 +550,9 @@ namespace NDecrypt.N3DS
|
||||
}
|
||||
}
|
||||
|
||||
var cipher = CreateAESCipher(ncchHeader.NormalKey, ncchHeader.RomFSIV, decryptArgs.Encrypt);
|
||||
var cipher = CreateAESCipher(ncchHeader.NormalKey, ncchHeader.RomFSIV!, decryptArgs.Encrypt);
|
||||
|
||||
reader.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * ncsdHeader.MediaUnitSize, SeekOrigin.Begin);
|
||||
reader.BaseStream.Seek((ncchHeader.Entry!.Offset + ncchHeader.RomFSOffsetInMediaUnits) * ncsdHeader.MediaUnitSize, SeekOrigin.Begin);
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * ncsdHeader.MediaUnitSize, SeekOrigin.Begin);
|
||||
if (romfsSizeM > 0)
|
||||
{
|
||||
@@ -585,7 +585,7 @@ namespace NDecrypt.N3DS
|
||||
private void UpdateEncryptCryptoAndMasks(NCSDHeader ncsdHeader, NCCHHeader ncchHeader, BinaryWriter writer)
|
||||
{
|
||||
// Write the new CryptoMethod
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset * ncsdHeader.MediaUnitSize) + 0x18B, SeekOrigin.Begin);
|
||||
writer.BaseStream.Seek((ncchHeader.Entry!.Offset * ncsdHeader.MediaUnitSize) + 0x18B, SeekOrigin.Begin);
|
||||
|
||||
// For partitions 1 and up, set crypto-method to 0x00
|
||||
if (ncchHeader.PartitionNumber > 0)
|
||||
@@ -593,15 +593,15 @@ namespace NDecrypt.N3DS
|
||||
|
||||
// If partition 0, restore crypto-method from backup flags
|
||||
else
|
||||
writer.Write((byte)ncsdHeader.BackupHeader.Flags.CryptoMethod);
|
||||
writer.Write((byte)ncsdHeader.BackupHeader!.Flags!.CryptoMethod);
|
||||
|
||||
writer.Flush();
|
||||
|
||||
// Write the new BitMasks flag
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset * ncsdHeader.MediaUnitSize) + 0x18F, SeekOrigin.Begin);
|
||||
BitMasks flag = ncchHeader.Flags.BitMasks;
|
||||
BitMasks flag = ncchHeader.Flags!.BitMasks;
|
||||
flag &= (BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator | BitMasks.NoCrypto) ^ (BitMasks)0xFF;
|
||||
flag |= (BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator) & ncsdHeader.BackupHeader.Flags.BitMasks;
|
||||
flag |= (BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator) & ncsdHeader.BackupHeader!.Flags!.BitMasks;
|
||||
writer.Write((byte)flag);
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
{
|
||||
internal class Constants
|
||||
{
|
||||
public static byte[] NDSEncryptionData = new byte[]
|
||||
{
|
||||
public static byte[] NDSEncryptionData =
|
||||
[
|
||||
0x99,0xD5,0x20,0x5F,0x57,0x44,0xF5,0xB9,0x6E,0x19,0xA4,0xD9,0x9E,0x6A,0x5A,0x94,
|
||||
0xD8,0xAE,0xF1,0xEB,0x41,0x75,0xE2,0x3A,0x93,0x82,0xD0,0x32,0x33,0xEE,0x31,0xD5,
|
||||
0xCC,0x57,0x61,0x9A,0x37,0x06,0xA2,0x1B,0x79,0x39,0x72,0xF5,0x55,0xAE,0xF6,0xBE,
|
||||
@@ -265,7 +265,7 @@
|
||||
0x6B,0x3A,0xA6,0x6B,0x35,0xD2,0x2F,0x43,0xCD,0x02,0xFD,0xB5,0xE9,0xBC,0x5B,0xAA,
|
||||
0xD8,0xA4,0x19,0x7E,0x0E,0x5D,0x94,0x81,0x9E,0x6F,0x77,0xAD,0xD6,0x0E,0x74,0x93,
|
||||
0x96,0xE7,0xC4,0x18,0x5F,0xAD,0xF5,0x19,
|
||||
};
|
||||
];
|
||||
|
||||
#region ARM9 decryption check values
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ namespace NDecrypt.Nitro
|
||||
|
||||
#region Encryption process variables
|
||||
|
||||
private uint[] cardHash = new uint[0x412];
|
||||
private uint[] arg2 = new uint[3];
|
||||
private uint[] _cardHash = new uint[0x412];
|
||||
private uint[] _arg2 = new uint[3];
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace NDecrypt.Nitro
|
||||
using (BinaryReader reader = new BinaryReader(File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
|
||||
using (BinaryWriter writer = new BinaryWriter(File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)))
|
||||
{
|
||||
NDSHeader header = NDSHeader.Read(reader);
|
||||
NDSHeader? header = NDSHeader.Read(reader);
|
||||
if (header == null)
|
||||
{
|
||||
Console.WriteLine("Error: Not a DS or DSi Rom!");
|
||||
@@ -79,7 +79,7 @@ namespace NDecrypt.Nitro
|
||||
// If we're not forcing the operation, check to see if we should be proceeding
|
||||
else
|
||||
{
|
||||
bool? isDecrypted = CheckIfDecrypted(reader);
|
||||
bool? isDecrypted = DSTool.CheckIfDecrypted(reader);
|
||||
if (isDecrypted == null)
|
||||
{
|
||||
Console.WriteLine("File has an empty secure area, cannot proceed");
|
||||
@@ -102,7 +102,7 @@ namespace NDecrypt.Nitro
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>True if the file has known values for a decrypted file, null if it's empty, false otherwise</returns>
|
||||
private bool? CheckIfDecrypted(BinaryReader reader)
|
||||
private static bool? CheckIfDecrypted(BinaryReader reader)
|
||||
{
|
||||
reader.BaseStream.Seek(0x4000, SeekOrigin.Begin);
|
||||
uint firstValue = reader.ReadUInt32();
|
||||
@@ -175,8 +175,8 @@ namespace NDecrypt.Nitro
|
||||
// Perform the initialization steps
|
||||
Init1(ndsHeader);
|
||||
if (!decryptArgs.Encrypt) Decrypt(ref p1, ref p0);
|
||||
arg2[1] <<= 1;
|
||||
arg2[2] >>= 1;
|
||||
_arg2[1] <<= 1;
|
||||
_arg2[2] >>= 1;
|
||||
Init2();
|
||||
|
||||
// If we're decrypting, set the proper flags
|
||||
@@ -245,8 +245,8 @@ namespace NDecrypt.Nitro
|
||||
/// <param name="ndsHeader">NDS header representing the DS file</param>
|
||||
private void Init1(NDSHeader ndsHeader)
|
||||
{
|
||||
Buffer.BlockCopy(Constants.NDSEncryptionData, 0, cardHash, 0, 4 * (1024 + 18));
|
||||
arg2 = new uint[] { ndsHeader.Gamecode, ndsHeader.Gamecode >> 1, ndsHeader.Gamecode << 1 };
|
||||
Buffer.BlockCopy(Constants.NDSEncryptionData, 0, _cardHash, 0, 4 * (1024 + 18));
|
||||
_arg2 = [ndsHeader.Gamecode, ndsHeader.Gamecode >> 1, ndsHeader.Gamecode << 1];
|
||||
Init2();
|
||||
Init2();
|
||||
}
|
||||
@@ -256,12 +256,12 @@ namespace NDecrypt.Nitro
|
||||
/// </summary>
|
||||
private void Init2()
|
||||
{
|
||||
Encrypt(ref arg2[2], ref arg2[1]);
|
||||
Encrypt(ref arg2[1], ref arg2[0]);
|
||||
Encrypt(ref _arg2[2], ref _arg2[1]);
|
||||
Encrypt(ref _arg2[1], ref _arg2[0]);
|
||||
|
||||
byte[] allBytes = BitConverter.GetBytes(arg2[0])
|
||||
.Concat(BitConverter.GetBytes(arg2[1]))
|
||||
.Concat(BitConverter.GetBytes(arg2[2]))
|
||||
byte[] allBytes = BitConverter.GetBytes(_arg2[0])
|
||||
.Concat(BitConverter.GetBytes(_arg2[1]))
|
||||
.Concat(BitConverter.GetBytes(_arg2[2]))
|
||||
.ToArray();
|
||||
|
||||
UpdateHashtable(allBytes);
|
||||
@@ -278,13 +278,13 @@ namespace NDecrypt.Nitro
|
||||
uint b = arg2;
|
||||
for (int i = 17; i > 1; i--)
|
||||
{
|
||||
uint c = cardHash[i] ^ a;
|
||||
uint c = _cardHash[i] ^ a;
|
||||
a = b ^ Lookup(c);
|
||||
b = c;
|
||||
}
|
||||
|
||||
arg1 = b ^ cardHash[0];
|
||||
arg2 = a ^ cardHash[1];
|
||||
arg1 = b ^ _cardHash[0];
|
||||
arg2 = a ^ _cardHash[1];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -298,13 +298,13 @@ namespace NDecrypt.Nitro
|
||||
uint b = arg2;
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
uint c = cardHash[i] ^ a;
|
||||
uint c = _cardHash[i] ^ a;
|
||||
a = b ^ Lookup(c);
|
||||
b = c;
|
||||
}
|
||||
|
||||
arg2 = a ^ cardHash[16];
|
||||
arg1 = b ^ cardHash[17];
|
||||
arg2 = a ^ _cardHash[16];
|
||||
arg1 = b ^ _cardHash[17];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -319,10 +319,10 @@ namespace NDecrypt.Nitro
|
||||
uint c = (v >> 8) & 0xFF;
|
||||
uint d = (v >> 0) & 0xFF;
|
||||
|
||||
a = cardHash[a + 18 + 0];
|
||||
b = cardHash[b + 18 + 256];
|
||||
c = cardHash[c + 18 + 512];
|
||||
d = cardHash[d + 18 + 768];
|
||||
a = _cardHash[a + 18 + 0];
|
||||
b = _cardHash[b + 18 + 256];
|
||||
c = _cardHash[c + 18 + 512];
|
||||
d = _cardHash[d + 18 + 768];
|
||||
|
||||
return d + (c ^ (b + a));
|
||||
}
|
||||
@@ -342,7 +342,7 @@ namespace NDecrypt.Nitro
|
||||
r3 |= arg1[(j * 4 + i) & 7];
|
||||
}
|
||||
|
||||
cardHash[j] ^= r3;
|
||||
_cardHash[j] ^= r3;
|
||||
}
|
||||
|
||||
uint tmp1 = 0;
|
||||
@@ -350,14 +350,14 @@ namespace NDecrypt.Nitro
|
||||
for (int i = 0; i < 18; i += 2)
|
||||
{
|
||||
Encrypt(ref tmp1, ref tmp2);
|
||||
cardHash[i + 0] = tmp1;
|
||||
cardHash[i + 1] = tmp2;
|
||||
_cardHash[i + 0] = tmp1;
|
||||
_cardHash[i + 1] = tmp2;
|
||||
}
|
||||
for (int i = 0; i < 0x400; i += 2)
|
||||
{
|
||||
Encrypt(ref tmp1, ref tmp2);
|
||||
cardHash[i + 18 + 0] = tmp1;
|
||||
cardHash[i + 18 + 1] = tmp2;
|
||||
_cardHash[i + 18 + 0] = tmp1;
|
||||
_cardHash[i + 18 + 1] = tmp2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
|
||||
namespace NDecrypt.Nitro
|
||||
namespace NDecrypt.Nitro
|
||||
{
|
||||
internal enum NDSUnitcode : byte
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace NDecrypt.Nitro.Headers
|
||||
/// <summary>
|
||||
/// Game Title
|
||||
/// </summary>
|
||||
public char[] GameTitle { get; private set; }
|
||||
public char[]? GameTitle { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gamecode
|
||||
@@ -19,7 +19,7 @@ namespace NDecrypt.Nitro.Headers
|
||||
/// <summary>
|
||||
/// Makercode
|
||||
/// </summary>
|
||||
public char[] Makercode { get; private set; }
|
||||
public char[]? Makercode { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Unitcode
|
||||
@@ -40,7 +40,7 @@ namespace NDecrypt.Nitro.Headers
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved1 { get; private set; }
|
||||
public byte[]? Reserved1 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Game Revision (used by DSi titles)
|
||||
@@ -140,12 +140,12 @@ namespace NDecrypt.Nitro.Headers
|
||||
/// <summary>
|
||||
/// Normal card control register settings (0x00416657 for OneTimePROM)
|
||||
/// </summary>
|
||||
public byte[] NormalCardControlRegisterSettings { get; private set; }
|
||||
public byte[]? NormalCardControlRegisterSettings { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Secure card control register settings (0x081808F8 for OneTimePROM)
|
||||
/// </summary>
|
||||
public byte[] SecureCardControlRegisterSettings { get; private set; }
|
||||
public byte[]? SecureCardControlRegisterSettings { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Icon Banner offset (NDSi same as NDS, but with new extra entries)
|
||||
@@ -165,17 +165,17 @@ namespace NDecrypt.Nitro.Headers
|
||||
/// <summary>
|
||||
/// ARM9 autoload
|
||||
/// </summary>
|
||||
public byte[] ARM9Autoload { get; private set; }
|
||||
public byte[]? ARM9Autoload { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM7 autoload
|
||||
/// </summary>
|
||||
public byte[] ARM7Autoload { get; private set; }
|
||||
public byte[]? ARM7Autoload { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Secure disable
|
||||
/// </summary>
|
||||
public byte[] SecureDisable { get; private set; }
|
||||
public byte[]? SecureDisable { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// NTR region ROM size (excluding DSi area)
|
||||
@@ -190,12 +190,12 @@ namespace NDecrypt.Nitro.Headers
|
||||
/// <summary>
|
||||
///Reserved (0x88, 0x8C, 0x90 = Unknown, used by DSi)
|
||||
/// </summary>
|
||||
public byte[] Reserved2 { get; private set; }
|
||||
public byte[]? Reserved2 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Nintendo Logo
|
||||
/// </summary>
|
||||
public byte[] NintendoLogo { get; private set; }
|
||||
public byte[]? NintendoLogo { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Nintendo Logo CRC
|
||||
@@ -210,7 +210,7 @@ namespace NDecrypt.Nitro.Headers
|
||||
/// <summary>
|
||||
/// Debugger reserved
|
||||
/// </summary>
|
||||
public byte[] DebuggerReserved { get; private set; }
|
||||
public byte[]? DebuggerReserved { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -219,42 +219,42 @@ namespace NDecrypt.Nitro.Headers
|
||||
/// <summary>
|
||||
/// Global MBK1..MBK5 Settings
|
||||
/// </summary>
|
||||
public byte[] GlobalMBK15Settings { get; private set; }
|
||||
public byte[]? GlobalMBK15Settings { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Local MBK6..MBK8 Settings for ARM9
|
||||
/// </summary>
|
||||
public byte[] LocalMBK68SettingsARM9 { get; private set; }
|
||||
public byte[]? LocalMBK68SettingsARM9 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Local MBK6..MBK8 Settings for ARM7
|
||||
/// </summary>
|
||||
public byte[] LocalMBK68SettingsARM7 { get; private set; }
|
||||
public byte[]? LocalMBK68SettingsARM7 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Global MBK9 Setting
|
||||
/// </summary>
|
||||
public byte[] GlobalMBK9Setting { get; private set; }
|
||||
public byte[]? GlobalMBK9Setting { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public byte[] RegionFlags { get; private set; }
|
||||
public byte[]? RegionFlags { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Access control
|
||||
/// </summary>
|
||||
public byte[] AccessControl { get; private set; }
|
||||
public byte[]? AccessControl { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM7 SCFG EXT mask (controls which devices to enable)
|
||||
/// </summary>
|
||||
public byte[] ARM7SCFGEXTMask { get; private set; }
|
||||
public byte[]? ARM7SCFGEXTMask { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved/flags? When bit2 of byte 0x1bf is set, usage of banner.sav from the title data dir is enabled.(additional banner data)
|
||||
/// </summary>
|
||||
public byte[] ReservedFlags { get; private set; }
|
||||
public byte[]? ReservedFlags { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM9i rom offset
|
||||
@@ -264,7 +264,7 @@ namespace NDecrypt.Nitro.Headers
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved3 { get; private set; }
|
||||
public byte[]? Reserved3 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM9i load address
|
||||
@@ -284,7 +284,7 @@ namespace NDecrypt.Nitro.Headers
|
||||
/// <summary>
|
||||
/// Pointer to base address where various structures and parameters are passed to the title - what is that???
|
||||
/// </summary>
|
||||
public byte[] Reserved4 { get; private set; }
|
||||
public byte[]? Reserved4 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM7i load address
|
||||
@@ -354,7 +354,7 @@ namespace NDecrypt.Nitro.Headers
|
||||
/// <summary>
|
||||
/// Unknown (used by DSi)
|
||||
/// </summary>
|
||||
public byte[] Unknown1 { get; private set; }
|
||||
public byte[]? Unknown1 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// NTR+TWL region ROM size (total size including DSi area)
|
||||
@@ -364,7 +364,7 @@ namespace NDecrypt.Nitro.Headers
|
||||
/// <summary>
|
||||
/// Unknown (used by DSi)
|
||||
/// </summary>
|
||||
public byte[] Unknown2 { get; private set; }
|
||||
public byte[]? Unknown2 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Modcrypt area 1 offset
|
||||
@@ -389,7 +389,7 @@ namespace NDecrypt.Nitro.Headers
|
||||
/// <summary>
|
||||
/// Title ID
|
||||
/// </summary>
|
||||
public byte[] TitleID { get; private set; }
|
||||
public byte[]? TitleID { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// DSiWare: "public.sav" size
|
||||
@@ -404,67 +404,67 @@ namespace NDecrypt.Nitro.Headers
|
||||
/// <summary>
|
||||
/// Reserved (zero)
|
||||
/// </summary>
|
||||
public byte[] ReservedZero { get; private set; }
|
||||
public byte[]? ReservedZero { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Unknown (used by DSi)
|
||||
/// </summary>
|
||||
public byte[] Unknown3 { get; private set; }
|
||||
public byte[]? Unknown3 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM9 (with encrypted secure area) SHA1 HMAC hash
|
||||
/// </summary>
|
||||
public byte[] ARM9WithSecureAreaSHA1HMACHash { get; private set; }
|
||||
public byte[]? ARM9WithSecureAreaSHA1HMACHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM7 SHA1 HMAC hash
|
||||
/// </summary>
|
||||
public byte[] ARM7SHA1HMACHash { get; private set; }
|
||||
public byte[]? ARM7SHA1HMACHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Digest master SHA1 HMAC hash
|
||||
/// </summary>
|
||||
public byte[] DigestMasterSHA1HMACHash { get; private set; }
|
||||
public byte[]? DigestMasterSHA1HMACHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Banner SHA1 HMAC hash
|
||||
/// </summary>
|
||||
public byte[] BannerSHA1HMACHash { get; private set; }
|
||||
public byte[]? BannerSHA1HMACHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM9i (decrypted) SHA1 HMAC hash
|
||||
/// </summary>
|
||||
public byte[] ARM9iDecryptedSHA1HMACHash { get; private set; }
|
||||
public byte[]? ARM9iDecryptedSHA1HMACHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM7i (decrypted) SHA1 HMAC hash
|
||||
/// </summary>
|
||||
public byte[] ARM7iDecryptedSHA1HMACHash { get; private set; }
|
||||
public byte[]? ARM7iDecryptedSHA1HMACHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved5 { get; private set; }
|
||||
public byte[]? Reserved5 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM9 (without secure area) SHA1 HMAC hash
|
||||
/// </summary>
|
||||
public byte[] ARM9NoSecureAreaSHA1HMACHash { get; private set; }
|
||||
public byte[]? ARM9NoSecureAreaSHA1HMACHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved6 { get; private set; }
|
||||
public byte[]? Reserved6 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved and unchecked region, always zero. Used for passing arguments in debug environment.
|
||||
/// </summary>
|
||||
public byte[] ReservedAndUnchecked { get; private set; }
|
||||
public byte[]? ReservedAndUnchecked { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// RSA signature (the first 0xE00 bytes of the header are signed with an 1024-bit RSA signature).
|
||||
/// </summary>
|
||||
public byte[] RSASignature { get; private set; }
|
||||
public byte[]? RSASignature { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -473,9 +473,9 @@ namespace NDecrypt.Nitro.Headers
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>NDS/NDSi header object, null on error</returns>
|
||||
public static NDSHeader Read(BinaryReader reader)
|
||||
public static NDSHeader? Read(BinaryReader reader)
|
||||
{
|
||||
NDSHeader header = new NDSHeader();
|
||||
var header = new NDSHeader();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -1,17 +1,38 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;net6.0</TargetFrameworks>
|
||||
<!-- Assembly Properties -->
|
||||
<TargetFrameworks>net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<VersionPrefix>0.2.5</VersionPrefix>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski</Authors>
|
||||
<Copyright>Copyright (c)2018-2023 Matt Nadareski</Copyright>
|
||||
<Description>NDS encryption and decryption code</Description>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2019-2024</Copyright>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/SabreTools/NDecrypt</RepositoryUrl>
|
||||
<Version>0.2.5</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<FileVersion>$(Version)</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Support All Frameworks -->
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net4`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(RuntimeIdentifier.StartsWith(`osx-arm`))">
|
||||
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NDecrypt.Core.Tools;
|
||||
using SabreTools.Hashing;
|
||||
|
||||
namespace NDecrypt
|
||||
{
|
||||
@@ -14,106 +11,24 @@ namespace NDecrypt
|
||||
/// </summary>
|
||||
/// <param name="input">Filename to get information from</param>
|
||||
/// <returns>Formatted string representing the hashes, null on error</returns>
|
||||
public static string GetInfo(string input)
|
||||
public static string? GetInfo(string input)
|
||||
{
|
||||
// If the file doesn't exist, return null
|
||||
if (!File.Exists(input))
|
||||
return null;
|
||||
|
||||
// Get the file length
|
||||
long size = new FileInfo(input).Length;
|
||||
|
||||
// Open the file
|
||||
Stream inputStream = File.OpenRead(input);
|
||||
|
||||
try
|
||||
{
|
||||
// Get a list of hashers to run over the buffer
|
||||
List<Hasher> hashers = new List<Hasher>
|
||||
{
|
||||
new Hasher(Hash.CRC),
|
||||
new Hasher(Hash.MD5),
|
||||
new Hasher(Hash.SHA1),
|
||||
new Hasher(Hash.SHA256),
|
||||
};
|
||||
|
||||
// Initialize the hashing helpers
|
||||
int buffersize = 3 * 1024 * 1024;
|
||||
byte[] buffer = new byte[buffersize];
|
||||
|
||||
/*
|
||||
Please note that some of the following code is adapted from
|
||||
RomVault. This is a modified version of how RomVault does
|
||||
threaded hashing. As such, some of the terminology and code
|
||||
is the same, though variable names and comments may have
|
||||
been tweaked to better fit this code base.
|
||||
*/
|
||||
|
||||
// Pre load the buffer
|
||||
int next = buffersize > size ? (int)size : buffersize;
|
||||
int current = inputStream.Read(buffer, 0, next);
|
||||
long refsize = size;
|
||||
|
||||
while (refsize > 0)
|
||||
{
|
||||
// Run hashes in parallel
|
||||
if (current > 0)
|
||||
Parallel.ForEach(hashers, h => h.Process(buffer, current));
|
||||
|
||||
// Load the next buffer
|
||||
refsize -= current;
|
||||
next = buffersize > refsize ? (int)refsize : buffersize;
|
||||
|
||||
if (next > 0)
|
||||
current = inputStream.Read(buffer, 0, next);
|
||||
}
|
||||
|
||||
// Finalize all hashing helpers
|
||||
Parallel.ForEach(hashers, h => h.Terminate());
|
||||
|
||||
// Get the results
|
||||
string result = $"Size: {size}\n"
|
||||
+ $"CRC32: {ByteArrayToString(hashers.First(h => h.HashType == Hash.CRC).GetHash()) ?? ""}\n"
|
||||
+ $"MD5: {ByteArrayToString(hashers.First(h => h.HashType == Hash.MD5).GetHash()) ?? ""}\n"
|
||||
+ $"SHA1: {ByteArrayToString(hashers.First(h => h.HashType == Hash.SHA1).GetHash()) ?? ""}\n"
|
||||
+ $"SHA256: {ByteArrayToString(hashers.First(h => h.HashType == Hash.SHA256).GetHash()) ?? ""}\n";
|
||||
|
||||
// Dispose of the hashers
|
||||
hashers.ForEach(h => h.Dispose());
|
||||
|
||||
return result;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
inputStream.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a byte array to a hex string
|
||||
/// </summary>
|
||||
/// <param name="bytes">Byte array to convert</param>
|
||||
/// <returns>Hex string representing the byte array</returns>
|
||||
/// <link>http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa</link>
|
||||
private static string ByteArrayToString(byte[] bytes)
|
||||
{
|
||||
// If we get null in, we send null out
|
||||
if (bytes == null)
|
||||
// Get the file information, if possible
|
||||
HashType[] hashTypes = [HashType.CRC32, HashType.MD5, HashType.SHA1, HashType.SHA256];
|
||||
var hashDict = HashTool.GetFileHashesAndSize(input, hashTypes, out long size);
|
||||
if (hashDict == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
string hex = BitConverter.ToString(bytes);
|
||||
return hex.Replace("-", string.Empty).ToLowerInvariant();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
// Get the results
|
||||
return $"Size: {size}\n"
|
||||
+ $"CRC32: {hashDict.First(h => h.Key == HashType.CRC32).Value ?? string.Empty}\n"
|
||||
+ $"MD5: {hashDict.First(h => h.Key == HashType.MD5).Value ?? string.Empty}\n"
|
||||
+ $"SHA1: {hashDict.First(h => h.Key == HashType.SHA1).Value ?? string.Empty}\n"
|
||||
+ $"SHA256: {hashDict.First(h => h.Key == HashType.SHA256).Value ?? string.Empty}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,40 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;net6.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win-x86;win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<!-- Assembly Properties -->
|
||||
<TargetFrameworks>net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
<OutputType>Exe</OutputType>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<VersionPrefix>0.2.5</VersionPrefix>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Title>NDecrypt</Title>
|
||||
<AssemblyName>NDecrypt</AssemblyName>
|
||||
<Description>DS/3DS Encryption Tool</Description>
|
||||
<Authors>Matt Nadareski</Authors>
|
||||
<Product>NDecrypt</Product>
|
||||
<Copyright>Copyright (c)2018-2023 Matt Nadareski</Copyright>
|
||||
<Description>DS/3DS Encryption Tool</Description>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2018-2024</Copyright>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/SabreTools/NDecrypt</RepositoryUrl>
|
||||
<Version>0.2.5</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<FileVersion>$(Version)</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Support All Frameworks -->
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net4`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(RuntimeIdentifier.StartsWith(`osx-arm`))">
|
||||
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using NDecrypt.Core;
|
||||
using NDecrypt.N3DS;
|
||||
using NDecrypt.Nitro;
|
||||
@@ -31,7 +30,7 @@ namespace NDecrypt
|
||||
return;
|
||||
}
|
||||
|
||||
var decryptArgs = new DecryptArgs();
|
||||
var decryptArgs = new DecryptArgs();
|
||||
if (args[0] == "decrypt" || args[0] == "d")
|
||||
{
|
||||
decryptArgs.Encrypt = false;
|
||||
@@ -48,7 +47,7 @@ namespace NDecrypt
|
||||
|
||||
bool outputHashes = false;
|
||||
int start = 1;
|
||||
for ( ; start < args.Length; start++)
|
||||
for (; start < args.Length; start++)
|
||||
{
|
||||
if (args[start] == "-c" || args[start] == "--citra")
|
||||
{
|
||||
@@ -75,7 +74,7 @@ namespace NDecrypt
|
||||
string tempPath = args[start];
|
||||
if (string.IsNullOrWhiteSpace(tempPath))
|
||||
Console.WriteLine($"Invalid keyfile path: null or empty path found!");
|
||||
|
||||
|
||||
tempPath = Path.GetFullPath(tempPath);
|
||||
if (!File.Exists(tempPath))
|
||||
Console.WriteLine($"Invalid keyfile path: file {tempPath} not found!");
|
||||
@@ -91,10 +90,13 @@ namespace NDecrypt
|
||||
// Derive the keyfile path based on the runtime folder if not already set
|
||||
if (string.IsNullOrWhiteSpace(decryptArgs.KeyFile))
|
||||
{
|
||||
using var processModule = System.Diagnostics.Process.GetCurrentProcess().MainModule;
|
||||
string applicationDirectory = Path.GetDirectoryName(processModule?.FileName) ?? string.Empty;
|
||||
|
||||
if (decryptArgs.UseCitraKeyFile)
|
||||
decryptArgs.KeyFile = Path.Combine(Path.GetDirectoryName(AppContext.BaseDirectory), "aes_keys.txt");
|
||||
decryptArgs.KeyFile = Path.Combine(applicationDirectory, "aes_keys.txt");
|
||||
else
|
||||
decryptArgs.KeyFile = Path.Combine(Path.GetDirectoryName(AppContext.BaseDirectory), "keys.bin");
|
||||
decryptArgs.KeyFile = Path.Combine(applicationDirectory, "keys.bin");
|
||||
}
|
||||
|
||||
// If we are using a Citra keyfile, there are no development keys
|
||||
@@ -103,7 +105,7 @@ namespace NDecrypt
|
||||
Console.WriteLine("Citra keyfiles don't contain development keys; disabling the option...");
|
||||
decryptArgs.Development = false;
|
||||
}
|
||||
|
||||
|
||||
// Initialize the constants, if possible
|
||||
decryptArgs.Initialize();
|
||||
|
||||
@@ -136,7 +138,7 @@ namespace NDecrypt
|
||||
private static void ProcessPath(string path, DecryptArgs decryptArgs, bool outputHashes)
|
||||
{
|
||||
Console.WriteLine(path);
|
||||
ITool tool = DeriveTool(path, decryptArgs);
|
||||
ITool? tool = DeriveTool(path, decryptArgs);
|
||||
if (tool?.ProcessFile() != true)
|
||||
Console.WriteLine("Processing failed!");
|
||||
else if (outputHashes)
|
||||
@@ -147,7 +149,7 @@ namespace NDecrypt
|
||||
/// Display a basic help text
|
||||
/// </summary>
|
||||
/// <param name="err">Additional error text to display, can be null to ignore</param>
|
||||
private static void DisplayHelp(string err = null)
|
||||
private static void DisplayHelp(string? err = null)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(err))
|
||||
Console.WriteLine($"Error: {err}");
|
||||
@@ -175,10 +177,10 @@ More than one path can be specified at a time.");
|
||||
/// <param name="filename">Filename to derive the tool from</param>
|
||||
/// <param name="decryptArgs">Arguments to pass to the tools on creation</param>
|
||||
/// <returns></returns>
|
||||
private static ITool DeriveTool(string filename, DecryptArgs decryptArgs)
|
||||
private static ITool? DeriveTool(string filename, DecryptArgs decryptArgs)
|
||||
{
|
||||
FileType type = DetermineFileType(filename);
|
||||
switch(type)
|
||||
switch (type)
|
||||
{
|
||||
case FileType.NDS:
|
||||
Console.WriteLine("File recognized as Nintendo DS");
|
||||
@@ -222,13 +224,13 @@ More than one path can be specified at a time.");
|
||||
|
||||
else if (filename.EndsWith(".3ds", StringComparison.OrdinalIgnoreCase))
|
||||
return FileType.N3DS;
|
||||
|
||||
|
||||
else if (filename.EndsWith(".cia", StringComparison.OrdinalIgnoreCase))
|
||||
return FileType.N3DSCIA;
|
||||
|
||||
return FileType.NULL;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Write out the hashes of a file to a named file
|
||||
/// </summary>
|
||||
@@ -240,10 +242,10 @@ More than one path can be specified at a time.");
|
||||
return;
|
||||
|
||||
// Get the hash string from the file
|
||||
string hashString = HashingHelper.GetInfo(filename);
|
||||
string? hashString = HashingHelper.GetInfo(filename);
|
||||
if (hashString == null)
|
||||
return;
|
||||
|
||||
|
||||
// Open the output file and write the hashes
|
||||
using (var fs = File.Create(Path.GetFullPath(filename) + ".hash"))
|
||||
using (var sw = new StreamWriter(fs))
|
||||
|
||||
34
README.md
34
README.md
@@ -1,6 +1,7 @@
|
||||
# NDecrypt
|
||||
|
||||
[](https://ci.appveyor.com/project/mnadareski/ndecrypt)
|
||||
[](https://github.com/SabreTools/NDecrypt/actions/workflows/build_program.yml)
|
||||
|
||||
A simple tool for simple people.
|
||||
|
||||
@@ -16,24 +17,29 @@ This is a code port of 3 different programs:
|
||||
|
||||
This tool allows you to encrypt and decrypt your personally dumped NDS and N3DS roms with minimal hassle. The only caveat right now is that you need a `keys.bin` file for your personally obtained encryption keys.
|
||||
|
||||
## Where do I find it?
|
||||
|
||||
For the most recent stable build, download the latest release here: [Releases Page](https://github.com/SabreTools/NDecrypt/releases)
|
||||
|
||||
For the latest WIP build here: [Rolling Release](https://github.com/SabreTools/NDecrypt/releases/tag/rolling)
|
||||
|
||||
## So how do I use this?
|
||||
|
||||
NDecrypt.exe <operation> [flags] <path> ...
|
||||
NDecrypt.exe <operation> [flags] <path> ...
|
||||
|
||||
Possible values for <operation>:
|
||||
e, encrypt - Encrypt the input files
|
||||
d, decrypt - Decrypt the input files
|
||||
Possible values for <operation>:
|
||||
e, encrypt - Encrypt the input files
|
||||
d, decrypt - Decrypt the input files
|
||||
|
||||
Possible values for [flags] (one or more can be used):
|
||||
-c, --citra - Enable using aes_keys.txt instead of keys.bin
|
||||
-dev, --development - Enable using development keys, if available
|
||||
-f, --force - Force operation by avoiding sanity checks
|
||||
-h, --hash - Output size and hashes to a companion file
|
||||
-k, --keyfile <path> - Path to keys.bin or aes_keys.txt
|
||||
|
||||
<path> can be any file or folder that contains uncompressed items.
|
||||
More than one path can be specified at a time.
|
||||
Possible values for [flags] (one or more can be used):
|
||||
-c, --citra - Enable using aes_keys.txt instead of keys.bin
|
||||
-dev, --development - Enable using development keys, if available
|
||||
-f, --force - Force operation by avoiding sanity checks
|
||||
-h, --hash - Output size and hashes to a companion file
|
||||
-k, --keyfile <path> - Path to keys.bin or aes_keys.txt
|
||||
|
||||
<path> can be any file or folder that contains uncompressed items.
|
||||
More than one path can be specified at a time.
|
||||
|
||||
**Note:** This overwrites the input files, so make backups if you're working on your original, personal dumps.
|
||||
|
||||
@@ -74,4 +80,4 @@ I'd like to thank the developers of the original programs for doing the actual h
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This program is **ONLY** for use with personally dumped files and keys and is not meant for enabling illegal activity. I do not condone using this program for anything other than personal use and research. If this program is used for anything other than that, I cannot be held liable for anything that happens.
|
||||
This program is **ONLY** for use with personally dumped files and keys and is not meant for enabling illegal activity. I do not condone using this program for anything other than personal use and research. If this program is used for anything other than that, I cannot be held liable for anything that happens.
|
||||
|
||||
39
appveyor.yml
39
appveyor.yml
@@ -8,10 +8,6 @@ pull_requests:
|
||||
# vm template
|
||||
image: Visual Studio 2022
|
||||
|
||||
# msbuild configuration
|
||||
configuration:
|
||||
- Debug
|
||||
|
||||
# install dependencies
|
||||
install:
|
||||
- cd %APPVEYOR_BUILD_FOLDER%
|
||||
@@ -19,25 +15,7 @@ install:
|
||||
|
||||
# build step
|
||||
build_script:
|
||||
- cmd: dotnet restore
|
||||
- cmd: dotnet build NDecrypt\NDecrypt.csproj --framework net48
|
||||
- cmd: dotnet publish NDecrypt\NDecrypt.csproj --framework net6.0 --runtime win-x86 --self-contained true -p:PublishSingleFile=true
|
||||
- cmd: dotnet publish NDecrypt\NDecrypt.csproj --framework net6.0 --runtime win-x64 --self-contained true -p:PublishSingleFile=true
|
||||
- cmd: dotnet publish NDecrypt\NDecrypt.csproj --framework net6.0 --runtime linux-x64 --self-contained true -p:PublishSingleFile=true
|
||||
- cmd: dotnet publish NDecrypt\NDecrypt.csproj --framework net6.0 --runtime osx-x64 --self-contained true -p:PublishSingleFile=true
|
||||
|
||||
# post-build script
|
||||
after_build:
|
||||
- cmd: cd %APPVEYOR_BUILD_FOLDER%\NDecrypt\bin\Debug\net48
|
||||
- cmd: 7z a -tzip %APPVEYOR_BUILD_FOLDER%\NDecrypt-%APPVEYOR_REPO_COMMIT%_net48.zip *
|
||||
- cmd: cd %APPVEYOR_BUILD_FOLDER%\NDecrypt\bin\Debug\net6.0\win-x86\publish\
|
||||
- cmd: 7z a -tzip %APPVEYOR_BUILD_FOLDER%\NDecrypt-%APPVEYOR_REPO_COMMIT%_net6.0_win-x86.zip *
|
||||
- cmd: cd %APPVEYOR_BUILD_FOLDER%\NDecrypt\bin\Debug\net6.0\win-x64\publish\
|
||||
- cmd: 7z a -tzip %APPVEYOR_BUILD_FOLDER%\NDecrypt-%APPVEYOR_REPO_COMMIT%_net6.0_win-x64.zip *
|
||||
- cmd: cd %APPVEYOR_BUILD_FOLDER%\NDecrypt\bin\Debug\net6.0\linux-x64\publish\
|
||||
- cmd: 7z a -tzip %APPVEYOR_BUILD_FOLDER%\NDecrypt-%APPVEYOR_REPO_COMMIT%_net6.0_linux-x64.zip *
|
||||
- cmd: cd %APPVEYOR_BUILD_FOLDER%\NDecrypt\bin\Debug\net6.0\osx-x64\publish\
|
||||
- cmd: 7z a -tzip %APPVEYOR_BUILD_FOLDER%\NDecrypt-%APPVEYOR_REPO_COMMIT%_net6.0_osx-x64.zip *
|
||||
- dotnet build
|
||||
|
||||
# success/failure tracking
|
||||
on_success:
|
||||
@@ -45,17 +23,4 @@ on_success:
|
||||
- ps: ./send.ps1 success $env:WEBHOOK_URL
|
||||
on_failure:
|
||||
- ps: Invoke-RestMethod https://raw.githubusercontent.com/DiscordHooks/appveyor-discord-webhook/master/send.ps1 -o send.ps1
|
||||
- ps: ./send.ps1 failure $env:WEBHOOK_URL
|
||||
|
||||
# artifact linking
|
||||
artifacts:
|
||||
- path: NDecrypt-%APPVEYOR_REPO_COMMIT%_net48.zip
|
||||
name: NDecrypt (.NET Framework 4.8)
|
||||
- path: NDecrypt-%APPVEYOR_REPO_COMMIT%_net6.0_win-x86.zip
|
||||
name: NDecrypt (.NET 6.0, Windows x86)
|
||||
- path: NDecrypt-%APPVEYOR_REPO_COMMIT%_net6.0_win-x64.zip
|
||||
name: NDecrypt (.NET 6.0, Windows x64)
|
||||
- path: NDecrypt-%APPVEYOR_REPO_COMMIT%_net6.0_linux-x64.zip
|
||||
name: NDecrypt (.NET 6.0, Linux x64)
|
||||
- path: NDecrypt-%APPVEYOR_REPO_COMMIT%_net6.0_osx-x64.zip
|
||||
name: NDecrypt (.NET 6.0, OSX x64)
|
||||
- ps: ./send.ps1 failure $env:WEBHOOK_URL
|
||||
143
publish-nix.sh
Normal file
143
publish-nix.sh
Normal file
@@ -0,0 +1,143 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This batch file assumes the following:
|
||||
# - .NET 8.0 (or newer) SDK is installed and in PATH
|
||||
# - zip is installed and in PATH
|
||||
# - Git is installed and in PATH
|
||||
#
|
||||
# If any of these are not satisfied, the operation may fail
|
||||
# in an unpredictable way and result in an incomplete output.
|
||||
|
||||
# Optional parameters
|
||||
USE_ALL=false
|
||||
NO_BUILD=false
|
||||
NO_ARCHIVE=false
|
||||
while getopts "uba" OPTION; do
|
||||
case $OPTION in
|
||||
u)
|
||||
USE_ALL=true
|
||||
;;
|
||||
b)
|
||||
NO_BUILD=true
|
||||
;;
|
||||
a)
|
||||
NO_ARCHIVE=true
|
||||
;;
|
||||
*)
|
||||
echo "Invalid option provided"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Set the current directory as a variable
|
||||
BUILD_FOLDER=$PWD
|
||||
|
||||
# Set the current commit hash
|
||||
COMMIT=$(git log --pretty=%H -1)
|
||||
|
||||
# Output the selected options
|
||||
echo "Selected Options:"
|
||||
echo " Use all frameworks (-u) $USE_ALL"
|
||||
echo " No build (-b) $NO_BUILD"
|
||||
echo " No archive (-a) $NO_ARCHIVE"
|
||||
echo " "
|
||||
|
||||
# Create the build matrix arrays
|
||||
FRAMEWORKS=("net8.0")
|
||||
RUNTIMES=("win-x86" "win-x64" "win-arm64" "linux-x64" "linux-arm64" "osx-x64" "osx-arm64")
|
||||
|
||||
# Use expanded lists, if requested
|
||||
if [ $USE_ALL = true ]; then
|
||||
FRAMEWORKS=("net40" "net452" "net462" "net472" "net48" "netcoreapp3.1" "net5.0" "net6.0" "net7.0" "net8.0")
|
||||
fi
|
||||
|
||||
# Create the filter arrays
|
||||
SINGLE_FILE_CAPABLE=("net5.0" "net6.0" "net7.0" "net8.0")
|
||||
VALID_APPLE_FRAMEWORKS=("net6.0" "net7.0" "net8.0")
|
||||
VALID_CROSS_PLATFORM_FRAMEWORKS=("netcoreapp3.1" "net5.0" "net6.0" "net7.0" "net8.0")
|
||||
VALID_CROSS_PLATFORM_RUNTIMES=("win-arm64" "linux-x64" "linux-arm64" "osx-x64" "osx-arm64")
|
||||
|
||||
# Only build if requested
|
||||
if [ $NO_BUILD = false ]; then
|
||||
# Restore Nuget packages for all builds
|
||||
echo "Restoring Nuget packages"
|
||||
dotnet restore
|
||||
|
||||
# Build Program
|
||||
for FRAMEWORK in "${FRAMEWORKS[@]}"; do
|
||||
for RUNTIME in "${RUNTIMES[@]}"; do
|
||||
# Output the current build
|
||||
echo "===== Build Program - $FRAMEWORK, $RUNTIME ====="
|
||||
|
||||
# If we have an invalid combination of framework and runtime
|
||||
if [[ ! $(echo ${VALID_CROSS_PLATFORM_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
|
||||
if [[ $(echo ${VALID_CROSS_PLATFORM_RUNTIMES[@]} | fgrep -w $RUNTIME) ]]; then
|
||||
echo "Skipped due to invalid combination"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# If we have Apple silicon but an unsupported framework
|
||||
if [[ ! $(echo ${VALID_APPLE_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
|
||||
if [ $RUNTIME = "osx-arm64" ]; then
|
||||
echo "Skipped due to no Apple Silicon support"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# Only .NET 5 and above can publish to a single file
|
||||
if [[ $(echo ${SINGLE_FILE_CAPABLE[@]} | fgrep -w $FRAMEWORK) ]]; then
|
||||
# Only include Debug if building all
|
||||
if [ $USE_ALL = true ]; then
|
||||
dotnet publish NDecrypt/NDecrypt.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
fi
|
||||
dotnet publish NDecrypt/NDecrypt.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
else
|
||||
# Only include Debug if building all
|
||||
if [ $USE_ALL = true ]; then
|
||||
dotnet publish NDecrypt/NDecrypt.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT
|
||||
fi
|
||||
dotnet publish NDecrypt/NDecrypt.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false
|
||||
fi
|
||||
done
|
||||
done
|
||||
fi
|
||||
|
||||
# Only create archives if requested
|
||||
if [ $NO_ARCHIVE = false ]; then
|
||||
# Create Test archives
|
||||
for FRAMEWORK in "${FRAMEWORKS[@]}"; do
|
||||
for RUNTIME in "${RUNTIMES[@]}"; do
|
||||
# Output the current build
|
||||
echo "===== Archive Program - $FRAMEWORK, $RUNTIME ====="
|
||||
|
||||
# If we have an invalid combination of framework and runtime
|
||||
if [[ ! $(echo ${VALID_CROSS_PLATFORM_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
|
||||
if [[ $(echo ${VALID_CROSS_PLATFORM_RUNTIMES[@]} | fgrep -w $RUNTIME) ]]; then
|
||||
echo "Skipped due to invalid combination"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# If we have Apple silicon but an unsupported framework
|
||||
if [[ ! $(echo ${VALID_APPLE_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
|
||||
if [ $RUNTIME = "osx-arm64" ]; then
|
||||
echo "Skipped due to no Apple Silicon support"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# Only include Debug if building all
|
||||
if [ $USE_ALL = true ]; then
|
||||
cd $BUILD_FOLDER/NDecrypt/bin/Debug/${FRAMEWORK}/${RUNTIME}/publish/
|
||||
zip -r $BUILD_FOLDER/NDecrypt_${FRAMEWORK}_${RUNTIME}_debug.zip .
|
||||
fi
|
||||
cd $BUILD_FOLDER/NDecrypt/bin/Release/${FRAMEWORK}/${RUNTIME}/publish/
|
||||
zip -r $BUILD_FOLDER/NDecrypt_${FRAMEWORK}_${RUNTIME}_release.zip .
|
||||
done
|
||||
done
|
||||
|
||||
# Reset the directory
|
||||
cd $BUILD_FOLDER
|
||||
fi
|
||||
128
publish-win.ps1
Normal file
128
publish-win.ps1
Normal file
@@ -0,0 +1,128 @@
|
||||
# This batch file assumes the following:
|
||||
# - .NET 8.0 (or newer) SDK is installed and in PATH
|
||||
# - 7-zip commandline (7z.exe) is installed and in PATH
|
||||
# - Git for Windows is installed and in PATH
|
||||
#
|
||||
# If any of these are not satisfied, the operation may fail
|
||||
# in an unpredictable way and result in an incomplete output.
|
||||
|
||||
# Optional parameters
|
||||
param(
|
||||
[Parameter(Mandatory = $false)]
|
||||
[Alias("UseAll")]
|
||||
[switch]$USE_ALL,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[Alias("NoBuild")]
|
||||
[switch]$NO_BUILD,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[Alias("NoArchive")]
|
||||
[switch]$NO_ARCHIVE
|
||||
)
|
||||
|
||||
# Set the current directory as a variable
|
||||
$BUILD_FOLDER = $PSScriptRoot
|
||||
|
||||
# Set the current commit hash
|
||||
$COMMIT = git log --pretty=format:"%H" -1
|
||||
|
||||
# Output the selected options
|
||||
Write-Host "Selected Options:"
|
||||
Write-Host " Use all frameworks (-UseAll) $USE_ALL"
|
||||
Write-Host " No build (-NoBuild) $NO_BUILD"
|
||||
Write-Host " No archive (-NoArchive) $NO_ARCHIVE"
|
||||
Write-Host " "
|
||||
|
||||
# Create the build matrix arrays
|
||||
$FRAMEWORKS = @('net8.0')
|
||||
$RUNTIMES = @('win-x86', 'win-x64', 'win-arm64', 'linux-x64', 'linux-arm64', 'osx-x64', 'osx-arm64')
|
||||
|
||||
# Use expanded lists, if requested
|
||||
if ($USE_ALL.IsPresent) {
|
||||
$FRAMEWORKS = @('net40', 'net452', 'net462', 'net472', 'net48', 'netcoreapp3.1', 'net5.0', 'net6.0', 'net7.0', 'net8.0')
|
||||
}
|
||||
|
||||
# Create the filter arrays
|
||||
$SINGLE_FILE_CAPABLE = @('net5.0', 'net6.0', 'net7.0', 'net8.0')
|
||||
$VALID_APPLE_FRAMEWORKS = @('net6.0', 'net7.0', 'net8.0')
|
||||
$VALID_CROSS_PLATFORM_FRAMEWORKS = @('netcoreapp3.1', 'net5.0', 'net6.0', 'net7.0', 'net8.0')
|
||||
$VALID_CROSS_PLATFORM_RUNTIMES = @('win-arm64', 'linux-x64', 'linux-arm64', 'osx-x64', 'osx-arm64')
|
||||
|
||||
# Only build if requested
|
||||
if (!$NO_BUILD.IsPresent) {
|
||||
# Restore Nuget packages for all builds
|
||||
Write-Host "Restoring Nuget packages"
|
||||
dotnet restore
|
||||
|
||||
# Build Program
|
||||
foreach ($FRAMEWORK in $FRAMEWORKS) {
|
||||
foreach ($RUNTIME in $RUNTIMES) {
|
||||
# Output the current build
|
||||
Write-Host "===== Build Program - $FRAMEWORK, $RUNTIME ====="
|
||||
|
||||
# If we have an invalid combination of framework and runtime
|
||||
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME) {
|
||||
Write-Host "Skipped due to invalid combination"
|
||||
continue
|
||||
}
|
||||
|
||||
# If we have Apple silicon but an unsupported framework
|
||||
if ($VALID_APPLE_FRAMEWORKS -notcontains $FRAMEWORK -and $RUNTIME -eq 'osx-arm64') {
|
||||
Write-Host "Skipped due to no Apple Silicon support"
|
||||
continue
|
||||
}
|
||||
|
||||
# Only .NET 5 and above can publish to a single file
|
||||
if ($SINGLE_FILE_CAPABLE -contains $FRAMEWORK) {
|
||||
# Only include Debug if building all
|
||||
if ($USE_ALL.IsPresent) {
|
||||
dotnet publish NDecrypt\NDecrypt.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
}
|
||||
dotnet publish NDecrypt\NDecrypt.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
}
|
||||
else {
|
||||
# Only include Debug if building all
|
||||
if ($USE_ALL.IsPresent) {
|
||||
dotnet publish NDecrypt\NDecrypt.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT
|
||||
}
|
||||
dotnet publish NDecrypt\NDecrypt.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Only create archives if requested
|
||||
if (!$NO_ARCHIVE.IsPresent) {
|
||||
# Create Program archives
|
||||
foreach ($FRAMEWORK in $FRAMEWORKS) {
|
||||
foreach ($RUNTIME in $RUNTIMES) {
|
||||
# Output the current build
|
||||
Write-Host "===== Archive Program - $FRAMEWORK, $RUNTIME ====="
|
||||
|
||||
# If we have an invalid combination of framework and runtime
|
||||
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME) {
|
||||
Write-Host "Skipped due to invalid combination"
|
||||
continue
|
||||
}
|
||||
|
||||
# If we have Apple silicon but an unsupported framework
|
||||
if ($VALID_APPLE_FRAMEWORKS -notcontains $FRAMEWORK -and $RUNTIME -eq 'osx-arm64') {
|
||||
Write-Host "Skipped due to no Apple Silicon support"
|
||||
continue
|
||||
}
|
||||
|
||||
# Only include Debug if building all
|
||||
if ($USE_ALL.IsPresent) {
|
||||
Set-Location -Path $BUILD_FOLDER\Test\bin\Debug\${FRAMEWORK}\${RUNTIME}\publish\
|
||||
7z a -tzip $BUILD_FOLDER\NDecrypt_${FRAMEWORK}_${RUNTIME}_debug.zip *
|
||||
}
|
||||
|
||||
Set-Location -Path $BUILD_FOLDER\Test\bin\Release\${FRAMEWORK}\${RUNTIME}\publish\
|
||||
7z a -tzip $BUILD_FOLDER\NDecrypt_${FRAMEWORK}_${RUNTIME}_release.zip *
|
||||
}
|
||||
}
|
||||
|
||||
# Reset the directory
|
||||
Set-Location -Path $PSScriptRoot
|
||||
}
|
||||
Reference in New Issue
Block a user