Skip to content

Instantly share code, notes, and snippets.

@Still34
Last active July 18, 2024 23:57
Show Gist options
  • Save Still34/a1e34f24ff0a26deed52fddc60999435 to your computer and use it in GitHub Desktop.
Save Still34/a1e34f24ff0a26deed52fddc60999435 to your computer and use it in GitHub Desktop.
PowerShell script for generating template for submission info for NX titles
#Requires -Version 7
$Config = [PSCustomObject]@{
NXGameInfoPath = "E:\tools\NXGameInfo-CLI\nxgameinfo_cli.exe"
HashMyFiles = "hashmyfiles.exe"
HacToolNet = "E:\tools\hactoolnet\hactoolnet.exe"
}
if ("NXNativeWin32" -as [type]) {}else {
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
public class NXNativeWin32 {
[DllImport("ntdll.dll", SetLastError = true)]
public static extern uint RtlComputeCrc32(uint initialCrc, byte[] buffer, int length);
}
"@
}
function Get-FileCRC32 {
# works well for items smaller than int32 size - not so much after that, especially for large XCIs
param (
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[ValidateNotNullOrEmpty()]
[Alias('PSPath', 'LP')]
[string]$LiteralPath
)
begin {
if (-Not (Test-Path -LiteralPath $LiteralPath)) {
throw [System.IO.FileNotFoundException]::new("File not found ($LiteralPath).")
}
}
process {
$item = Get-Item -LiteralPath $LiteralPath
$bytes = [System.IO.File]::ReadAllBytes($item.FullName)
$crc32 = [NXNativeWin32]::RtlComputeCrc32(0, $bytes, $bytes.Length)
return $crc32.ToString("X8")
}
}
function Split-StringIntoChunks {
param (
[string]$InputString,
[int]$ChunkSize
)
$chunks = @()
for ($i = 0; $i -lt $InputString.Length; $i += $ChunkSize) {
$chunks += $InputString.Substring($i, [Math]::Min($ChunkSize, $InputString.Length - $i))
}
return $chunks
}
function Get-CardIDSet {
param (
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[ValidateNotNullOrEmpty()]
[Alias('PSPath', 'LP')]
[string]$LiteralPath
)
begin {
if (-Not (Test-Path -LiteralPath $LiteralPath)) {
throw [System.IO.FileNotFoundException]::new("File not found ($LiteralPath).")
}
if (((Get-Item -LiteralPath $LiteralPath).Length) -ne 12) {
throw [System.InvalidOperationException]::new("File is abnormally large for Card ID Set.")
}
}
process {
$sb = [System.Text.StringBuilder]::new()
[void]$sb.AppendLine("Format: Card ID Sets")
$content = Get-Content -LiteralPath $LiteralPath -AsByteStream
$hexString = [System.Convert]::ToHexString($content)
$sets = Split-StringIntoChunks -InputString $hexString -ChunkSize 8
$i = 1
foreach ($set in $sets) {
[void]$sb.AppendLine("Card ID $($i): $set")
$i++
}
$crc32 = Get-FileCRC32 -LiteralPath $LiteralPath
[void]$sb.AppendLine("CRC32: $crc32")
}
end {
return $sb.ToString()
}
}
function Get-NXGameInfo {
param (
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[ValidateNotNullOrEmpty()]
[Alias('PSPath', 'LP')]
[string]$LiteralPath
)
begin {
if (-Not (Test-Path -LiteralPath $LiteralPath)) {
throw [System.IO.FileNotFoundException]::new("File not found ($LiteralPath).")
}
}
process {
return Invoke-Command -ScriptBlock { . $Config.NXGameInfoPath $args[0] } -ArgumentList (gi -LiteralPath $LiteralPath).FullName
}
}
function Get-HacToolNetInfo {
param (
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[ValidateNotNullOrEmpty()]
[Alias('PSPath', 'LP')]
[string]$LiteralPath
)
begin {
if (-Not (Test-Path -LiteralPath $LiteralPath)) {
throw [System.IO.FileNotFoundException]::new("File not found ($LiteralPath).")
}
}
process {
return Invoke-Command -ScriptBlock {. $Config.HacToolNet -t xci $args[0]} -ArgumentList (gi -LiteralPath $LiteralPath).FullName
}
}
function Get-FileMetadata {
param (
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[ValidateNotNullOrEmpty()]
[Alias('PSPath', 'LP')]
[string]$LiteralPath
)
begin {
if (-Not (Test-Path -LiteralPath $LiteralPath)) {
throw [System.IO.FileNotFoundException]::new("File not found ($LiteralPath).")
}
}
process {
$item = gi -LiteralPath $LiteralPath
$tempFile = [System.IO.Path]::GetTempFileName()
[void](Start-Process -FilePath $Config.HashMyFiles -NoNewWindow -Wait -PassThru -ArgumentList "/file","`"$($item.Fullname)`"", "/md5", "1", "/sha1", "1", "/sha256", "1", "/sha512", "0", "/sha384", "0", "/crc32", "1", "/stext", $tempFile)
}
end {
return (Get-Content $tempFile)
}
}
function Get-ContentBase64 {
param (
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[ValidateNotNullOrEmpty()]
[Alias('PSPath', 'LP')]
[string]$LiteralPath
)
begin {
if (-Not (Test-Path -LiteralPath $LiteralPath)) {
throw [System.IO.FileNotFoundException]::new("File not found ($LiteralPath).")
}
}
process{
$item = gi -LiteralPath $LiteralPath
$bytes = [System.IO.File]::ReadAllBytes($item.FullName)
return [System.Convert]::ToBase64String($bytes)
}
}
function New-NXMetadata {
param (
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[ValidateNotNullOrEmpty()]
[Alias('PSPath', 'LP')]
[string]$LiteralPath
)
begin {
if (-Not (Test-Path -LiteralPath $LiteralPath -PathType Container)) {
throw [System.IO.FileNotFoundException]::new("Directory not found or path is not a directory ($LiteralPath).")
}
$items = gci -LiteralPath $LiteralPath
$initialData = $items| ? { $_.Name -like "*Initial Data*"}
$cardIdSet = $items| ? { $_.Name -like "*Card ID Set*"}
$xci = $items| ? { $_.Extension-match"xci"}
$sb = [System.Text.StringBuilder]::new()
$activity = "Generating metadata for $($LiteralPath.Name)..."
}
process{
[void]$sb.AppendLine("[size=150]Game & Cart Info:[/size]")
[void]$sb.AppendLine("Game Name: ")
[void]$sb.AppendLine("Region: ")
[void]$sb.AppendLine("Card Code: LA-H-")
[void]$sb.AppendLine("Dump Tool: nxdt_rw_poc v2.0.0 (rewrite-[CHANGEME]-dirty)")
[void]$sb.AppendLine()
[void]$sb.AppendLine("Barcode: ")
[void]$sb.AppendLine("Cart Back Serial Code: ")
[void]$sb.AppendLine("Box Serials: ")
[void]$sb.AppendLine()
[void]$sb.AppendLine("[size=150]Card ID Set:[/size]")
[void]$sb.AppendLine("[code]")
Write-Progress -Activity $activity -Status "Generating Card ID Set data..." -PercentComplete 20
[void]$sb.AppendJoin([System.Environment]::NewLine, (Get-CardIDSet $cardIdSet))
[void]$sb.AppendLine("[/code]")
[void]$sb.AppendLine()
[void]$sb.AppendLine("[size=150]Hash Info for Card:[/size]")
[void]$sb.AppendLine("[code]")
Write-Progress -Activity $activity -Status "Generating XCI hash..." -PercentComplete 30
[void]$sb.AppendJoin([System.Environment]::NewLine, (Get-FileMetadata $xci))
Write-Progress -Activity $activity -Status "Generating initial data hash..." -PercentComplete 40
[void]$sb.AppendJoin([System.Environment]::NewLine, (Get-FileMetadata $initialData))
[void]$sb.AppendLine("[/code]")
[void]$sb.AppendLine()
[void]$sb.AppendLine("[size=150]Initial Area Base64 Dump:[/size]")
[void]$sb.AppendLine("[code]")
Write-Progress -Activity $activity -Status "Generating initial data Base64..." -PercentComplete 50
[void]$sb.AppendJoin([System.Environment]::NewLine, (Get-ContentBase64 $initialData))
[void]$sb.AppendLine("[/code]")
[void]$sb.AppendLine()
[void]$sb.AppendLine("[size=150]Hactoolnet Info:[/size]")
[void]$sb.AppendLine("[code]")
Write-Progress -Activity $activity -Status "Generating HacToolNet data..." -PercentComplete 70
[void]$sb.AppendJoin([System.Environment]::NewLine, (Get-HacToolNetInfo $xci))
[void]$sb.AppendLine("[/code]")
[void]$sb.AppendLine()
[void]$sb.AppendLine("[size=150]NX Game Info:[/size]")
[void]$sb.AppendLine("[code]")
Write-Progress -Activity $activity -Status "Generating NXGameInfo data..." -PercentComplete 90
[void]$sb.AppendJoin([System.Environment]::NewLine, (Get-NXGameInfo $xci))
[void]$sb.AppendLine("[/code]")
}
end {
$output = $sb.ToString()
Set-Content -LiteralPath (Join-Path $LiteralPath "submissioninfo.txt") -Value $output
Write-Progress -Activity $activity -Completed
return $output
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment