param( [string]$ModsPath = "" ) $ErrorActionPreference = "Stop" $modName = "FS25_Mine" $projectRoot = Split-Path -Parent $MyInvocation.MyCommand.Path $projectZip = Join-Path $projectRoot "$modName.zip" $tempZip = Join-Path ([System.IO.Path]::GetTempPath()) "$modName-build.zip" function Format-ProductionAmount { param( [double]$Amount, [string]$CultureName ) $culture = [System.Globalization.CultureInfo]::GetCultureInfo($CultureName) $roundedAmount = [Math]::Round($Amount) if ([Math]::Abs($Amount - $roundedAmount) -lt 0.0001) { return ([int64]$roundedAmount).ToString("N0", $culture) } return $Amount.ToString("N1", $culture) } function Set-L10nText { param( [xml]$ModDesc, [string]$Name, [string]$EnglishText, [string]$GermanText ) $textNode = $ModDesc.SelectSingleNode("/modDesc/l10n/text[@name='$Name']") if ($null -eq $textNode) { throw "l10n Eintrag fehlt: $Name" } $englishNode = $textNode.SelectSingleNode("en") $germanNode = $textNode.SelectSingleNode("de") if ($null -eq $englishNode -or $null -eq $germanNode) { throw "l10n Eintrag ist unvollstaendig: $Name" } $englishNode.InnerText = $EnglishText $germanNode.InnerText = $GermanText } function Update-ProductionDescriptions { param( [string]$ProjectRoot ) $modDescPath = Join-Path $ProjectRoot "modDesc.xml" $products = @{ "PAYDIRT" = @{ En = "paydirt"; De = "Paydirt" } "STONE" = @{ En = "stone"; De = "Stein" } "DIRT" = @{ En = "dirt"; De = "Erde" } "MININGCOAL" = @{ En = "mining coal"; De = "Bergbau-Kohle" } "IRONORE" = @{ En = "iron ore"; De = "Eisenerz" } "SILVERORE" = @{ En = "silver ore"; De = "Silbererz" } } $productionItems = @( @{ XmlPath = "placeables\mine\mine.xml"; L10nName = "storeItem_paydirtMine_function" }, @{ XmlPath = "placeables\stoneQuarry\stoneQuarry.xml"; L10nName = "storeItem_stoneQuarry_function" }, @{ XmlPath = "placeables\dirt\dirt.xml"; L10nName = "storeItem_dirtPit_function" }, @{ XmlPath = "placeables\coalmine\coalmine.xml"; L10nName = "storeItem_coalMine_function" }, @{ XmlPath = "placeables\ironore\iron.xml"; L10nName = "storeItem_ironMine_function" }, @{ XmlPath = "placeables\silverore\silver.xml"; L10nName = "storeItem_silverMine_function" } ) $modDesc = New-Object System.Xml.XmlDocument $modDesc.PreserveWhitespace = $true $modDesc.Load($modDescPath) foreach ($item in $productionItems) { $placeablePath = Join-Path $ProjectRoot $item.XmlPath $placeable = New-Object System.Xml.XmlDocument $placeable.PreserveWhitespace = $true $placeable.Load($placeablePath) $productionNode = $placeable.SelectSingleNode("/placeable/produktion") if ($null -eq $productionNode) { throw "produktion Eintrag fehlt: $($item.XmlPath)" } $fillType = $productionNode.GetAttribute("fillType") $amountText = $productionNode.GetAttribute("amountPerHour") if ([string]::IsNullOrWhiteSpace($fillType) -or [string]::IsNullOrWhiteSpace($amountText)) { throw "produktion Eintrag ist unvollstaendig: $($item.XmlPath)" } $amountPerHour = [double]::Parse($amountText, [System.Globalization.CultureInfo]::InvariantCulture) if ($products.ContainsKey($fillType)) { $product = $products[$fillType] } else { $product = @{ En = $fillType.ToLowerInvariant(); De = $fillType } } $amountEn = Format-ProductionAmount -Amount $amountPerHour -CultureName "en-US" $amountDe = Format-ProductionAmount -Amount $amountPerHour -CultureName "de-DE" Set-L10nText -ModDesc $modDesc -Name $item.L10nName ` -EnglishText "Produces $amountEn l $($product.En) per in-game hour." ` -GermanText "Produziert $amountDe l $($product.De) pro Ingame-Stunde." } $settings = New-Object System.Xml.XmlWriterSettings $settings.Encoding = New-Object System.Text.UTF8Encoding($false) $settings.Indent = $false $settings.NewLineChars = "`r`n" $writer = [System.Xml.XmlWriter]::Create($modDescPath, $settings) try { $modDesc.Save($writer) } finally { $writer.Close() } } if ([string]::IsNullOrWhiteSpace($ModsPath)) { $candidates = @( (Join-Path $env:USERPROFILE "OneDrive\Dokumente\My Games\FarmingSimulator2025\mods"), (Join-Path $env:USERPROFILE "Documents\My Games\FarmingSimulator2025\mods") ) foreach ($candidate in $candidates) { if (Test-Path -LiteralPath $candidate) { $ModsPath = $candidate break } } } if ([string]::IsNullOrWhiteSpace($ModsPath)) { throw "Kein FarmingSimulator2025 mods Ordner gefunden. Starte z.B.: .\build.ps1 -ModsPath 'C:\Users\pasca\OneDrive\Dokumente\My Games\FarmingSimulator2025\mods'" } if (-not (Test-Path -LiteralPath $ModsPath)) { New-Item -ItemType Directory -Force -Path $ModsPath | Out-Null } Update-ProductionDescriptions -ProjectRoot $projectRoot Add-Type -AssemblyName System.IO.Compression Add-Type -AssemblyName System.IO.Compression.FileSystem if (Test-Path -LiteralPath $tempZip) { Remove-Item -LiteralPath $tempZip -Force } if (Test-Path -LiteralPath $projectZip) { Remove-Item -LiteralPath $projectZip -Force } $zip = [System.IO.Compression.ZipFile]::Open($tempZip, [System.IO.Compression.ZipArchiveMode]::Create) try { $files = Get-ChildItem -LiteralPath $projectRoot -Recurse -File -Force | Where-Object { $relative = $_.FullName.Substring($projectRoot.Length + 1) -not ($relative -like ".git\*") -and -not ($relative -like ".vscode\*") -and $relative -ne "$modName.zip" -and $relative -ne "build.ps1" -and $relative -ne "build.cmd" -and -not ($relative -like "*.xcf") } foreach ($file in $files) { $relative = $file.FullName.Substring($projectRoot.Length + 1).Replace("\", "/") [System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($zip, $file.FullName, $relative, [System.IO.Compression.CompressionLevel]::Optimal) | Out-Null } } finally { $zip.Dispose() } Move-Item -LiteralPath $tempZip -Destination $projectZip -Force Copy-Item -LiteralPath $projectZip -Destination (Join-Path $ModsPath "$modName.zip") -Force Write-Host "Build fertig: $projectZip" Write-Host "Kopiert nach: $(Join-Path $ModsPath "$modName.zip")"