Skip to content

Instantly share code, notes, and snippets.

@SuperFlue
Created October 11, 2022 09:36
Show Gist options
  • Save SuperFlue/b8c811a61b90d5ab4bb6ea04a2938a4e to your computer and use it in GitHub Desktop.
Save SuperFlue/b8c811a61b90d5ab4bb6ea04a2938a4e to your computer and use it in GitHub Desktop.
Terra Invicta PowerShell save editor
# Quick save editor for Terra Invicta in PowerShell
# Released under MIT license by <Superflue@github>
# Use PowerShell ISE or VSCode to more easily tweak stuff.
# Just place a breakpoint (F9) on the line pointed out at the bottom of the script
# And then you can use the examples in the if statement as a start
# This script handles the compressed saves (gzip)
# For simplicity we just hardcode the name of the save to edit (just call it "editsave")
# And the output is called "modifiedsave"
# This is just a simple way to avoid accidentally breaking an important save
$TargetSave = "$env:userprofile\Documents\My Games\TerraInvicta\Saves\editsave"
$ModifiedSave = "$env:userprofile\Documents\My Games\TerraInvicta\Saves\modifiedsave"
# We unpack the gzipped savefile here
$TempFile = "$env:temp\terrainvictasavetemp"
# Functions to unpack a GZIP
function Unpack-Gzip ([string]$CompressedFileName, [string]$DecompressedFileName) {
$CompressedFile = [System.IO.FileStream]::new($CompressedFileName, [System.IO.FileMode]::Open)
$DecompressedFile = [System.IO.FileStream]::new($DecompressedFileName, [System.IO.FileMode]::Create)
$Decompressor = [System.IO.Compression.GZipStream]::new($CompressedFile, [System.IO.Compression.CompressionMode]::Decompress)
$Decompressor.CopyTo($DecompressedFile)
$Decompressor.Dispose()
$CompressedFile.Close()
$DecompressedFile.Close()
}
# Function to pack a GZIP
function Pack-Gzip ([string]$TargetFileName, [string]$CompressedFileName) {
$TargetFile = [System.IO.FileStream]::new($TargetFileName, [System.IO.FileMode]::Open)
$CompressedFile = [System.IO.FileStream]::new($CompressedFileName, [System.IO.FileMode]::OpenOrCreate)
$Compressor = [System.IO.Compression.GZipStream]::new($CompressedFile, [System.IO.Compression.CompressionMode]::Compress)
$TargetFile.CopyTo($Compressor)
$Compressor.Dispose()
$TargetFile.Close()
$CompressedFile.Close()
}
# PowerShell 6 and upwards handles saving files without Byte Order Mark (BOM)
# But Powershell 5 does not, so we make a quick function to save properly
function Save-Utf8NoBom ([string]$textcontent,[string]$TargetFile) {
[System.IO.File]::WriteAllLines($TargetFile, $textcontent, [System.Text.UTF8Encoding]::new($False))
}
# Quick function to just convert daily income to year values
# This is meant for the "baseIncomes_year" value
# So if you want an income of about 10 cash per day, you can use this
function YearValue ([float]$DayIncome) {
return [float]([float]$DayIncome * 365)
}
# Unpack savegame to temp file
Unpack-Gzip "$TargetSave.gz" "$TempFile"
# Convert savedata
$SaveData = Get-Content -Path $TempFile | ConvertFrom-Json
# Get correct player faction ID
$PlayerfactionID = $SaveData.gamestates.'PavonisInteractive.TerraInvicta.TIPlayerState'.Where({ $_.value -match "isAI=False" }).value.faction
# Get Faction data
$FactionData = $SaveData.gamestates.'PavonisInteractive.TerraInvicta.TIFactionState'.Where({ $_.Key -match $PlayerfactionID }).value
# Councilors get the player's councilors
$CouncilID = $FactionData.councilors.Value -join "|"
$PlayerCouncilors = $SaveData.gamestates.'PavonisInteractive.TerraInvicta.TICouncilorState'.Where({ $_.Key -match $CouncilID })
# Putting the edit code in a if tag just to make it easier to work with
# But at the same time avoiding accitentally running commands your dont want
# When you are in debug (hitting the breakpoint) you can select a line (or multiple) and press F8 to run
if ($false) {
# List attributes for council
$PlayerCouncilors.Value.attributes
# Simple way to set all councilors specific stat to a specified level
$PlayerCouncilors.Value.attributes.ForEach({$_.Loyalty = 25})
$PlayerCouncilors.Value.attributes.ForEach({$_.Administration = 25})
# set councilors XP value
$PlayerCouncilors.Value.ForEach({$_.xp = 900})
# This block below sets all couniclors stats to a minimum level
# If the stat level is above the minimum, they wont be changed
$minlvl = 25
$Attributes = @(
"Persuasion"
"Espionage"
"Command"
"Investigation"
"Science"
"Administration"
"Security"
"Loyalty"
"ApparentLoyalty"
)
$MinimumLevel = {
if($psitem.$attrib -lt $minlvl){
$psitem.$attrib = $minlvl
}
}
foreach ($attrib in $Attributes){
$PlayerCouncilors.Value.attributes.ForEach($MinimumLevel)
}
# Resources stuff
# Modify income
# Most values are per year income.
# You can use the "YearValue" function to quickly get the right value for your wanted daily income
# MC and Projects are "constant" values and are not calculated by year
$FactionData.baseIncomes_year
$FactionData.baseIncomes_year.Influence = (YearValue 2)
$FactionData.baseIncomes_year.Money = (YearValue 1)
$FactionData.baseIncomes_year.Operations = (YearValue 1)
$FactionData.baseIncomes_year.Research = (YearValue 90)
$FactionData.baseIncomes_year.Boost = (YearValue 1)
$FactionData.baseIncomes_year.Projects = 4
$FactionData.baseIncomes_year.MissionControl = 40
$FactionData.baseIncomes_year
# Set resources to specific level
$FactionData.resources
$FactionData.resources.Money = 9000000
$FactionData.resources.Influence = 80000
$FactionData.resources.Boost = 700
$FactionData.resources.Operations = 200
$FactionData.resources
}
# Place breakpoint on line below with F9 and press F5 to run
#$SaveData | ConvertTo-Json -Depth 100 | Out-File $TempFile -Encoding utf8NoBOM
Save-Utf8NoBom -textcontent ($SaveData | ConvertTo-Json -Depth 100) -TargetFile $TempFile
# When you done modifying, just press F5 again to finish
# Pack the save up in gzip again
Pack-Gzip "$TempFile" "$ModifiedSave.gz"
# Remove temp file
#Remove-Item $TempFile
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment