Edit 3/27: Apparently Avarice V and IV were swapped (I got the friendly name mapping from reddit - not sure where a real source is) -- I confirmed in my save and two others did as well
Edit 6/2/23: Thanks jojorne for catching an issue related to the GUI for compressed saves
Here is a bat file that runs the powershell inside it (sneaky.. but effective) :
Here is the same script that you can copy and past and save into a .bat file (use notepad and save as UTF-8 or ANSI (do not select the with BOM ones) and run to avoid the prompts that you get from windows about it being safe or not..
Code: Select all
@findstr/v "^@f.*&" "%~f0"|powershell -noprofile -executionpolicy bypass -&goto:eof
#Fist line stolen from https://stackoverflow.com/a/2611394
#This uses a fair amount of memory - if you have limited ram exit X4 first...
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Messagebox]::Show("This uses a fair amount of memory - if you have limited ram exit X4 first...")
#stolen from https://4sysops.com/archives/how-to-create-an-open-file-folder-dialog-box-with-powershell/
$FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property @{
InitialDirectory = [Environment]::GetFolderPath('MyDocuments') + "\Egosoft\X4"
Filter = 'Save file (*.xml; *.xml.gz)|*.xml;*.xml.gz'
Title = "Select savegame file"
}
while([string]::IsNullOrWhiteSpace($FileBrowser.FileName)){
$null = $FileBrowser.ShowDialog()
sleep 1
}
#from https://social.technet.microsoft.com/Forums/windowsserver/en-US/5aa53fef-5229-4313-a035-8b3a38ab93f5/unzip-gz-files-using-powershell?forum=winserverpowershell
Function Expand-GZip{
Param(
$infile,
$outfile = ($infile -replace '\.gz$','')
)
$input = New-Object System.IO.FileStream $inFile, ([IO.FileMode]::Open), ([IO.FileAccess]::Read), ([IO.FileShare]::Read)
$output = New-Object System.IO.FileStream $outFile, ([IO.FileMode]::Create), ([IO.FileAccess]::Write), ([IO.FileShare]::None)
$gzipStream = New-Object System.IO.Compression.GzipStream $input, ([IO.Compression.CompressionMode]::Decompress)
$buffer = New-Object byte[](1024)
while($true){
$read = $gzipstream.Read($buffer, 0, 1024)
if ($read -le 0){break}
$output.Write($buffer, 0, $read)
}
$gzipStream.Close()
$output.Close()
$input.Close()
}
$gzip = $false
if($FileBrowser.FileName -match "\.gz$"){
Write-Host "Extracting compressed save, this may take a while"
$gzip = $true
Expand-GZip -infile $FileBrowser.FileName
}
if($gzip) {
$file = ($FileBrowser.FileName -replace '\.gz$','')
} else {
$file = $FileBrowser.FileName
}
Write-Host "Loading XML, this may take a while and will use lots of ram"
$Xml=New-Object Xml
#unzip .gz file with 7-zip or gunzip first or use uncompressed saves
$Xml.Load($file)
Write-Host "Finding Erlking Data Vaults"
$components=Select-Xml -Xml $Xml -XPath "/savegame/universe//component"
$erl=$components | ?{$_.Node.macro -and $_.Node.macro.Contains("erlking")}
$vaults = $erl | ?{$_.Node.macro -and $_.Node.macro.Contains("landmarks_erlking_vault")}
#From hatevampire at https://www.reddit.com/r/X4Foundations/comments/thjbik/how_to_read_save_file/
#Not sure where the real source of friendlyname is..
$locationtoname = @{
cluster_500_sector001_macro="Avarice I"
cluster_500_sector002_macro="Avarice V"
cluster_500_sector003_macro="Avarice IV"
cluster_501_macro="Windfall I"
cluster_502_macro="Windfall III"
cluster_503_macro="Windfall IV"
}
$macrotocoords=@{
"Zone004_Cluster_503_Sector001_macro"="139680,0,-3215.59"
"Zone003_Cluster_500_Sector003_macro"="-115796.9,0,-96109.38"
"Zone002_Cluster_503_Sector001_macro"="-2269.199,0,165758.4"
"Zone006_Cluster_500_Sector002_macro"="-184409.83,0,-3343.0"
"Zone003_Cluster_501_Sector001_macro"="-97898.44,0,-23632.81"
"Zone003_Cluster_500_Sector001_macro"="-97898.44,0,-23632.81"
"Zone005_Cluster_501_Sector001_macro"="-23023.44,0,-160132.8"
"Zone004_Cluster_504_Sector001_macro"="157616.8,0,-84263.63"
"tzoneCluster_500_Sector002SHCon9_GateZone_macro"="-39827.2,0,200000"
"tzoneCluster_500_Sector002SHCon5_GateZone_macro"="-50000,0,200000"
"Zone001_Cluster_500_Sector003_macro"="-117046.9,0,82593.75"
"Zone001_Cluster_501_Sector001_macro"="-127062.5,0,133718.8"
"Zone005_Cluster_504_Sector001_macro"="-23023.44,0,-160132.8"
"Zone004_Cluster_500_Sector001_macro"="103164.1,0,-32492.19"
"Zone002_Cluster_500_Sector001_macro"="-10960.94,0,112070.3"
"Zone003_Cluster_500_Sector002_macro"="-7132.69,0,531.25"
"Zone002_Cluster_500_Sector002_macro"="-118489.4,0,-131687.5"
"tzoneCluster_500_Sector003SHCon9_GateZone_macro"="50000,0,200000"
"Zone005_Cluster_500_Sector002_macro"="98694.83,0,-126406.3"
"tzoneCluster_500_Sector001SHCon2_GateZone_macro"="66960.9,0,-207957"
"tzoneCluster_500_Sector003SHCon5_GateZone_macro"="40054.7,0,200000"
"tzoneCluster_500_Sector001SHCon10_GateZone_macro"="-45738.28,0,-200000"
"Zone005_Cluster_500_Sector003_macro"="34671.88,0,-152515.6"
"Zone001_Cluster_504_Sector001_macro"="-127062.5,0,133718.8"
"Zone003_Cluster_502_Sector001_macro"="-97898.44,0,-23632.81"
"Zone002_Cluster_504_Sector001_macro"="137904.1,0,135199.3"
"Zone005_Cluster_503_Sector001_macro"="-23023.44,0,-160132.8"
"Zone001_Cluster_500_Sector001_macro"="-127062.5,0,133718.8"
"Zone004_Cluster_501_Sector001_macro"="103164.1,0,-32492.19"
"tzoneCluster_500_Sector001SHCon4_GateZone_macro"="76886.7,0,-207957"
"Zone006_Cluster_502_Sector001_macro"="47000.0,0,172000.0"
"Zone002_Cluster_500_Sector003_macro"="44015.63,0,158125"
"tzoneCluster_500_Sector001SHCon6_GateZone_macro"="-55785.2,0,-200000"
"Zone002_Cluster_501_Sector001_macro"="-10960.94,0,112070.3"
"Zone001_Cluster_500_Sector002_macro"="-78593.63,0,157937.5"
"Zone003_Cluster_503_Sector001_macro"="-199365.3,0,-55454.45"
"Zone001_Cluster_502_Sector001_macro"="-127062.5,0,133718.8"
"Zone003_Cluster_504_Sector001_macro"="-226443.3,0,-97194.67"
"Zone005_Cluster_500_Sector001_macro"="-23023.44,0,-160132.8"
"Zone004_Cluster_500_Sector003_macro"="110359.4,0,7250"
"Zone001_Cluster_503_Sector001_macro"="-190039.3,0,180073.9"
"Zone005_Cluster_502_Sector001_macro"="-23023.44,0,-160132.8"
"Zone004_Cluster_500_Sector002_macro"="101721.7,0,100656.3"
"Zone002_Cluster_502_Sector001_macro"="108009.3,0,115933.7"
"Zone004_Cluster_502_Sector001_macro"="103164.1,0,-32492.19"
}
#Get offsets up the tree
function Recurse-Offsets($node){
if($node.class -and $node.class -eq "galaxy"){
return @()
}
if($node.offset -ne $null){
if($node.offset.position){
$x=$node.offset.position.x
$y=$node.offset.position.y
$z=$node.offset.position.z
} else {
$x,$y,$z=0,0,0
}
if($node.offset.rotation){
if($node.offset.rotation.yaw){$yaw=$node.offset.rotation.yaw} else {$yaw=0}
if($node.offset.rotation.pitch){$pitch=$node.offset.rotation.pitch} else {$pitch=0}
if($node.offset.rotation.roll){$roll=$node.offset.rotation.roll} else {$roll=0}
} else {
$pitch,$roll,$yaw=0,0,0
}
if($macrotocoords[$node.macro]){
$x,$y,$z=$macrotocoords[$node.macro].Split(",")
}
$obj = [pscustomobject]@{
x=$x
y=$y
z=$z
pitch=$pitch
roll=$roll
yaw=$yaw
code=$node.code
macro=$node.macro
class=$node.class
}
return @($obj) + (Recurse-Offsets -node $node.ParentNode)
} else {
return Recurse-Offsets -node $node.ParentNode
}
}
$data=. {
foreach($vault in $vaults){
$data=Recurse-Offsets $vault.Node
$sums = $data | Measure-Object -Sum -Property x,y,z
$location = $data |?{$_.class -eq "sector"} | select -ExpandProperty macro
if([string]::IsNullOrWhiteSpace($location)){
$location = $data |?{$_.class -eq "cluster"} | select -ExpandProperty macro
}
[pscustomobject]@{
location=$locationtoname[$location]
Xkm=($sums |?{$_.property -eq "x"}).sum/1000
Ykm=($sums |?{$_.property -eq "y"}).sum/1000
Zkm=($sums |?{$_.property -eq "z"}).sum/1000
blueprint=$vault.Node.connections.connection.component.blueprints |?{$_ -ne $null}
vault=$vault.Node.macro
code=$vault.Node.code
}
}
}
$data | ft -AutoSize
$data | Out-GridView
if($gzip){
if([System.Windows.Forms.MessageBox]::Show("Remove the extracted save $file" , "Remove Save?" , 4, 64) -eq "Yes"){
Remove-Item -Path $file
}
}
[System.Windows.Forms.Messagebox]::Show("Done, you can copy out of the cmd window or grid view before closing this if you want.")
<#
#Some other useful snippets
#detailed output
. {
foreach($vault in $vaults){
Recurse-Offsets $vault.Node
"-----"
}
} | ft -AutoSize
#parse out zone info
$zoneinfo = [xml](gc c:\path\to\dlc_pirate_sectors.xml)
$hash = @{}
$zoneinfo.macros.macro | %{
$_.connections.connection | %{
$hash += @{$_.macro.ref = $_.offset.position }
}
}
# Generate a powershell hash for the above xml and put it on the clipboard
"@{`n" + ($hash.GetEnumerator() |%{" `"$($_.key)`"=`"$(@($_.value.x,$_.value.y,$_.value.z) -join ",")`"`n"}) + "}" | clip
$components=Select-Xml -Xml $Xml -XPath "/savegame/universe//component"
# Get all classes
$components.Node.class | select -Unique | sort
order of map related:
galaxy
cluster
sector
zone
#>