Skip to content

Instantly share code, notes, and snippets.

@dend
Created July 26, 2022 06:49
Show Gist options
  • Save dend/9cad0906bc5e1cb82c8c93962c324578 to your computer and use it in GitHub Desktop.
Save dend/9cad0906bc5e1cb82c8c93962c324578 to your computer and use it in GitHub Desktop.
Script to enable all Halo Infinite content (maps and game modes) for your account
# Script by Den Delimarsky - Written in July 2022
# Enables all modes and maps that are available in a given build manifest.
# For details, refer to https://den.dev/blog/halo-infinite-enable-all-content
param (
# Spartan V4 token, used for authentication against the Halo Infinite API.
[Parameter(Mandatory=$true)]
[string]$SpartanV4Token,
# Build ID accessible to the user which is authenticating.
[Parameter(Mandatory=$true)]
[string]$BuildId,
# User XUID used for inclusion in the binary blob
[Parameter(Mandatory=$true)]
[string]$Xuid
)
function ProcessAsset([Guid]$AssetId, [Guid]$AssetVersionId, [string]$AssetName) {
Write-Host "Processing asset with version ${AssetId} and version ${AssetVersionId}"
Write-Host "Asset name:" $AssetName
$reverseAssetGuid = $assetId.ToByteArray()
$reverseVersionGuid = $versionId.ToByteArray()
$properAssetId = [RequestData.Helper]::GetRawGuid($reverseAssetGuid)
$properVersionId = [RequestData.Helper]::GetRawGuid($reverseVersionGuid)
$entity = [RequestData.Entity]::new()
$entity.Admin = $Xuid
$entity.Dummy = [RequestData.DummyEntity]::new()
$entity.AssetDefinition = New-Object System.Collections.Generic.List[RequestData.Asset]
$asset = [RequestData.Asset]::new()
$asset.AssetId = $properAssetId
$asset.AssetVersion = $properVersionId
$entity.AssetDefinition.Add($asset)
$entity.Metadata = New-Object System.Collections.Generic.List[string]
$entity.Metadata.Add($AssetName)
Write-Host "Writing data to file..."
[RequestData.Helper]::WriteToFile($entity)
}
# Only download a new NuGet binary if one doesn't already exist in the current folder.
if (-Not(Test-Path nuget.exe -PathType Leaf)) {
Write-Host "Downloading NuGet (PowerShell package management is messy)..."
Invoke-WebRequest https://dist.nuget.org/win-x86-commandline/latest/nuget.exe -OutFile nuget.exe
} else {
Write-Host "NuGet.exe already exists in current folder."
}
Write-Host "Installing the Microsoft Bond helper package..."
.\nuget.exe install Bond.Core.CSharp -OutputDirectory . -Version 9.0.5
# Loading the Bond assembly, that will be necessary to serialize the data
$bondLibaryPath = Resolve-Path ".\Bond.Core.CSharp.9.0.5\lib\net46\Bond.dll"
$bondAttributesLibaryPath = Resolve-Path ".\Bond.Core.CSharp.9.0.5\lib\net46\Bond.Attributes.dll"
$bondIOLibaryPath = Resolve-Path ".\Bond.Core.CSharp.9.0.5\lib\net46\Bond.IO.dll"
$bondReflectionLibaryPath = Resolve-Path ".\Bond.Core.CSharp.9.0.5\lib\net46\Bond.Reflection.dll"
[String[]]$paths = $bondLibaryPath, $bondAttributesLibaryPath, $bondIOLibaryPath, $bondReflectionLibaryPath
Add-Type -Path $bondLibaryPath
Add-Type -Path $bondAttributesLibaryPath
Add-Type -Path $bondIOLibaryPath
Add-Type -Path $bondReflectionLibaryPath
Write-Host $paths
$entityDefinition = @"
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Bond;
using Bond.Protocols;
using Bond.Tag;
namespace RequestData
{
[Bond.Schema]
public class Entity
{
[Bond.Id(0), Bond.Type(typeof(wstring))]
public string Admin { get; set; }
[Bond.Id(1), Bond.Type(typeof(Bond.Tag.structT))]
public DummyEntity Dummy { get; set; }
[Bond.Id(2)]
public List<Asset> AssetDefinition { get; set; }
[Bond.Id(6), Bond.Type(typeof(List<Bond.Tag.wstring>))]
public List<string> Metadata { get; set; }
}
[Bond.Schema]
public class DummyEntity
{
}
[Bond.Schema]
public class Asset
{
[Bond.Id(0)]
public RawGuidDefinition AssetId { get; set; }
[Bond.Id(1)]
public RawGuidDefinition AssetVersion { get; set; }
}
[Bond.Schema]
public class RawGuidDefinition
{
[Bond.Id(0)]
public UInt32 FragmentA { get; set; }
[Bond.Id(1)]
public UInt16 FragmentB { get; set; }
[Bond.Id(2)]
public UInt16 FragmentC { get; set; }
[Bond.Id(3)]
public UInt64 FragmentD { get; set; }
}
public static class Helper
{
public static RawGuidDefinition GetRawGuid(byte[] data)
{
return new RawGuidDefinition()
{
FragmentA = BitConverter.ToUInt32(data.Take(4).ToArray(), 0),
FragmentB = BitConverter.ToUInt16(data.Skip(4).Take(2).ToArray(), 0),
FragmentC = BitConverter.ToUInt16(data.Skip(6).Take(2).ToArray(), 0),
FragmentD = BitConverter.ToUInt64(data.Skip(8).Take(8).ToArray(), 0)
};
}
public static void WriteToFile(Entity entity)
{
using (var stream = new FileStream("example.bin", FileMode.Create))
{
var output = new Bond.IO.Unsafe.OutputStream(stream);
CompactBinaryWriter<Bond.IO.Unsafe.OutputBuffer> writer = new Bond.Protocols.CompactBinaryWriter<Bond.IO.Unsafe.OutputBuffer>(output, 2);
Serialize.To(writer, entity);
output.Flush();
}
}
}
}
"@
Try {
Add-Type -TypeDefinition $entityDefinition -ReferencedAssemblies $paths
} Catch {
Write-Host "Looks like the type is already loaded."
}
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("X-343-Authorization-Spartan", $SpartanV4Token)
$headers.Add("Accept", "application/json")
# Try and get build metadata.
Try {
$url = "https://discovery-infiniteugc.svc.halowaypoint.com/hi/manifests/builds/${buildId}/game"
Write-Host "Trying to work with the following URL:" $url
$response = Invoke-RestMethod $url -Method 'GET' -Headers $headers
$gameVariants = $response.UgcGameVariantLinks
$maps = $response.MapLinks
Write-Host "Response contains $($maps.Count) map links and $($gameVariants.Count) game variants."
# Process game variant links
$counter = 1
foreach($gameVariant in $gameVariants){
$assetId = [Guid]::new($gameVariant.AssetId)
$versionId = [Guid]::new($gameVariant.VersionId)
ProcessAsset $assetId $versionId $gameVariant.PublicName
Write-Host "Preparing to store..."
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("X-343-Authorization-Spartan", $SpartanV4Token)
$headers.Add("Accept", "application/json")
$headers.Add("Content-Type", "application/x-bond-compact-binary")
$body = [System.IO.File]::ReadAllBytes('example.bin')
$fullPath = [System.IO.Path]::GetFullPath('example.bin')
Write-Host $fullPath
$response = Invoke-WebRequest 'https://authoring-infiniteugc.svc.halowaypoint.com/hi/UgcGameVariants' -Method 'POST' -Headers $headers -Body $body
Write-Host "[${counter}] Storage routine ended with HTTP status code" $response.StatusCode
$counter = $counter + 1
}
Write-Host "Going to be processing map links..."
$counter = 1
foreach($map in $maps){
$assetId = [Guid]::new($map.AssetId)
$versionId = [Guid]::new($map.VersionId)
ProcessAsset $assetId $versionId $map.PublicName
Write-Host "Preparing to store..."
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("X-343-Authorization-Spartan", $SpartanV4Token)
$headers.Add("Accept", "application/json")
$headers.Add("Content-Type", "application/x-bond-compact-binary")
$body = [System.IO.File]::ReadAllBytes('example.bin')
$fullPath = [System.IO.Path]::GetFullPath('example.bin')
Write-Host $fullPath
$response = Invoke-WebRequest 'https://authoring-infiniteugc.svc.halowaypoint.com/hi/maps' -Method 'POST' -Headers $headers -Body $body
Write-Host "[${counter}] Storage routine ended with HTTP status code" $response.StatusCode
$counter = $counter + 1
}
} Catch {
Write-Host "Error processing build metadata."
Write-Warning $Error[0]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment