LifePlay-Revival/Tools/SplitModules.ps1

162 lines
11 KiB
PowerShell
Raw Permalink Normal View History

<#
.SYNOPSIS
Separe les modules optionnels d'un build empaquete (Docs/Migration/15_Modules_Optionnels.md).
.DESCRIPTION
Les modules de Content/Modules sont livres en fichiers nus a cote du jeu. Ce script garde dans le build les modules
listes comme essentiels dans le fichier de configuration (Tools/modules/<version>.json) et deplace tous les autres
dans une archive zip, avec l'arborescence LifePlay\Content\Modules\<module>\..., a extraire dans le dossier du jeu
(celui de LifePlay.exe). Une notice LISEZMOI / README est ajoutee a l'archive.
Le jeu gere l'absence d'un module : au demarrage, GI.removeDeletedMods retire de GI.ModEnableds tout module dont le
fichier .lpmod est absent, et GI.IsModEnabled repond alors faux.
Controles : chaque module essentiel doit exister ; nombre de fichiers et d'octets deplaces = contenu de l'archive
(7-Zip, test d'integrite) ; aucun fichier de module optionnel ne reste dans le build. Les fichiers deplaces sont
conserves dans <build>\..\<nom>_modules_optionnels (rien n'est supprime).
.PARAMETER (configuration "per_module": true, depuis la publication qui suit la V1.4)
Une archive par module optionnel, <module>-for-LifePlay-<version>.zip (LifePlay\Content\Modules\<module> + notice
bilingue tiree du .lpmod), dans <Out>\<out_dir> ; un index INDEX.txt (nom, archive, taille, SHA-256) sert au message
de diffusion (Discord). Les mods d'assets (LifePlay-Sandbox/Tools/BuildMod.ps1, atelier de mods de l'addon, lot DC3) se deposent dans le meme dossier.
.EXAMPLE
.\Tools\SplitModules.ps1 -Build D:\Game\LifePlay_Build_5.5_Shipping_V1.5 -Config Tools\modules
ext.json -Out D:\Game\LifePlay_Publication
.EXAMPLE
.\Tools\SplitModules.ps1 -Build D:\Game\LifePlay_Build_5.5_Shipping_V1.4 -Config Tools\modules\V1.4.json -Out D:\Game\LifePlay_Publication
#>
param(
[Parameter(Mandatory)] [string] $Build,
[Parameter(Mandatory)] [string] $Config,
[Parameter(Mandatory)] [string] $Out
)
$ErrorActionPreference = 'Stop'
$SevenZip = 'C:\Program Files\7-Zip\7z.exe'
$cfg = Get-Content $Config -Raw | ConvertFrom-Json
$modules = Join-Path $Build 'Windows\LifePlay\Content\Modules'
if (-not (Test-Path $modules)) { throw "Dossier des modules introuvable : $modules" }
# Un build de distribution ne contient aucun mod d'assets installe (sa partie texte serait livree sans ses paquets) :
# ces mods sont livres par LifePlay-Sandbox/Tools/BuildMod.ps1 (addon, lot DC3).
$assetMods = @(Get-ChildItem (Join-Path $Build 'Windows\LifePlay\Mods') -Directory -ErrorAction SilentlyContinue | ForEach-Object Name)
if ($assetMods) { throw "Mod(s) d'assets installe(s) dans le build ($($assetMods -join ', ')) : utiliser un build neuf, ces mods sont livres par BuildMod.ps1" }
foreach ($m in $cfg.essential) {
if (-not (Test-Path (Join-Path $modules "$m\$m.lpmod"))) { throw "Module essentiel absent du build : $m" }
}
$optional = Get-ChildItem $modules -Directory | Where-Object { (Test-Path (Join-Path $_.FullName "$($_.Name).lpmod")) -and $_.Name -notin $cfg.essential }
if (-not $optional) { Write-Host 'Aucun module optionnel dans le build.'; return }
# Arborescence de l'archive : LifePlay\Content\Modules\<module>
$stage = Join-Path (Split-Path $Build -Parent) ((Split-Path $Build -Leaf) + '_modules_optionnels')
$target = Join-Path $stage 'LifePlay\Content\Modules'
New-Item -ItemType Directory -Force $target | Out-Null
$files = 0; $bytes = 0
foreach ($m in $optional) {
$items = Get-ChildItem $m.FullName -Recurse -File
$files += $items.Count; $bytes += ($items | Measure-Object Length -Sum).Sum
Move-Item $m.FullName (Join-Path $target $m.Name)
}
$list = ($optional.Name | Sort-Object) -join ', '
if ($cfg.per_module) {
# Une archive par module (publication suivant la V1.4).
$dir = Join-Path $Out $(if ($cfg.out_dir) { $cfg.out_dir } else { "Modules_$($cfg.version)" })
New-Item -ItemType Directory -Force $dir | Out-Null
$index = @("LifePlay Revival $($cfg.version) - optional modules / modules optionnels", '',
'Install: extract the zip into the game folder (the one containing LifePlay.exe), merging the LifePlay folder, then enable',
'the module in "Mod Manager" (title screen). Installation : extraire le zip dans le dossier du jeu, puis activer le module.',
'Adult content, adults only / contenu adulte, reserve aux adultes.', '')
# Paquets : un groupe de la configuration ("groups" : name, pattern, title, description) reunit ses modules dans une
# seule archive (ex. vin_* = modules de base du jeu, decision du 2026-10-03) ; chaque autre module a la sienne.
$packages = @()
$grouped = @()
foreach ($g in @($cfg.groups)) {
if (-not $g) { continue }
$members = @($optional | Where-Object { $_.Name -like $g.pattern } | Sort-Object Name)
if ($members) { $packages += [pscustomobject]@{ Name = $g.name; Title = $g.title; Description = $g.description; Members = $members }; $grouped += $members.Name }
}
foreach ($m in ($optional | Where-Object { $_.Name -notin $grouped } | Sort-Object Name)) {
$packages += [pscustomobject]@{ Name = $m.Name; Title = ''; Description = ''; Members = @($m) }
}
$zipped = 0
foreach ($p in $packages) {
$one = Join-Path $stage "_zip\$($p.Name)"
New-Item -ItemType Directory -Force (Join-Path $one 'LifePlay\Content\Modules') | Out-Null
$lines = @(); $expected = 0
foreach ($m in $p.Members) {
$src = Join-Path $target $m.Name
$lp = Get-Content (Join-Path $src "$($m.Name).lpmod") -Encoding utf8
$val = { param($k) $l = $lp | Where-Object { $_ -match "^$k\s*:" } | Select-Object -First 1; if ($l) { ($l -split ':', 2)[1].Trim() } else { '' } }
$title = & $val 'MODULE_NAME'; $desc = & $val 'MODULE_DESCRIPTION'; $req = & $val 'MODULE_REQUIREMENTS'
Copy-Item $src (Join-Path $one "LifePlay\Content\Modules\$($m.Name)") -Recurse
$expected += @(Get-ChildItem $src -Recurse -File).Count
$lines += "- $title ($($m.Name))$(if ($req) { ", requires / necessite : $req" })"
if ($p.Members.Count -eq 1) { $p.Title = $title; $p.Description = $desc }
}
$modulesDir = if ($p.Members.Count -eq 1) { "LifePlay\Content\Modules\$($p.Members[0].Name)" } else { 'LifePlay\Content\Modules\<module>' }
$note = @("$($p.Title) - LifePlay Revival $($cfg.version)", '', $p.Description, '') + $lines + @('',
'EN: extract this archive into the game folder (the one containing LifePlay.exe), merging the LifePlay folder, then',
"enable the module(s) in `"Mod Manager`" (title screen). To remove one, delete its folder $modulesDir.",
'FR : extraire cette archive dans le dossier du jeu (celui de LifePlay.exe) en fusionnant le dossier LifePlay, puis',
"activer le ou les modules dans `"Mod Manager`" (ecran titre). Pour en retirer un : supprimer $modulesDir.",
'Adult content, adults only / contenu adulte, reserve aux adultes.')
[IO.File]::WriteAllText((Join-Path $one 'README.txt'), ($note -join "`r`n") + "`r`n", (New-Object Text.UTF8Encoding $false))
$zip = Join-Path $dir "$($p.Name)-for-LifePlay-$($cfg.version).zip"
if (Test-Path $zip) { throw "L'archive existe deja : $zip" }
Push-Location $one
& $SevenZip a -tzip -mx=7 $zip 'LifePlay' 'README.txt' | Out-Null
Pop-Location
& $SevenZip t $zip | Out-Null
if ($LASTEXITCODE -ne 0) { throw "Test 7-Zip en echec : $zip" }
$n = (& $SevenZip l -slt $zip | Select-String '^Folder = -').Count - 1
if ($n -ne $expected) { throw "$($p.Name) : $n fichier(s) dans l'archive pour $expected" }
$zipped += $n
$h = (Get-FileHash $zip -Algorithm SHA256).Hash.ToLower()
$index += ('{0} ({1}{2}) : {3}, {4:N0} bytes, SHA-256 {5}' -f $p.Title, $p.Name, $(if ($p.Members.Count -gt 1) { ': ' + ($p.Members.Name -join ', ') } else { '' }), (Split-Path $zip -Leaf), (Get-Item $zip).Length, $h)
Write-Host " $($p.Name) : $(Split-Path $zip -Leaf) ($($p.Members.Count) module(s), $n fichiers)"
}
[IO.File]::WriteAllText((Join-Path $dir 'INDEX.txt'), ($index -join "`r`n") + "`r`n", (New-Object Text.UTF8Encoding $false))
$left = Get-ChildItem $modules -Directory | Where-Object { $_.Name -in $optional.Name }
Write-Host "Modules gardes dans le build : $($cfg.essential -join ', ')"
Write-Host "Modules optionnels : $($optional.Count) en $($packages.Count) archive(s) dans $dir ; fichiers deplaces $files, archives $zipped ; restes dans le build $(@($left).Count)"
if ($zipped -ne $files -or @($left).Count -ne 0) { throw 'Controle en echec : contenu des archives different des fichiers deplaces' }
return
}
$readme = @"
LifePlay Revival $($cfg.version) - modules optionnels / optional modules
FR : extraire cette archive dans le dossier du jeu (celui qui contient LifePlay.exe), en fusionnant le dossier
LifePlay. Les modules apparaissent ensuite dans le gestionnaire de mods du jeu, ou ils s'activent et se desactivent.
vin_NTR est desactive par defaut. Pour retirer un module : supprimer son dossier LifePlay\Content\Modules\<module>.
EN: extract this archive into the game folder (the one containing LifePlay.exe), merging the LifePlay folder. The
modules then appear in the in-game mod manager, where they can be enabled or disabled. vin_NTR is disabled by
default. To remove a module, delete its LifePlay\Content\Modules\<module> folder.
Modules : $list
Contenu adulte, reserve aux adultes / adult content, adults only.
"@
Set-Content (Join-Path $stage 'LISEZMOI_README.txt') $readme -Encoding utf8
New-Item -ItemType Directory -Force $Out | Out-Null
$zip = Join-Path $Out $cfg.zip
if (Test-Path $zip) { throw "L'archive existe deja : $zip" }
Push-Location $stage
& $SevenZip a -tzip -mx=7 $zip 'LifePlay' 'LISEZMOI_README.txt' | Out-Null
Pop-Location
& $SevenZip t $zip | Out-Null
if ($LASTEXITCODE -ne 0) { throw "Test 7-Zip en echec : $zip" }
$zipFiles = (& $SevenZip l -slt $zip | Select-String '^Folder = -').Count - 1 # moins la notice
$left = Get-ChildItem $modules -Directory | Where-Object { $_.Name -in $optional.Name }
$hash = (Get-FileHash $zip -Algorithm SHA256).Hash.ToLower()
Write-Host "Modules gardes dans le build : $($cfg.essential -join ', ')"
Write-Host "Modules optionnels : $($optional.Count) ($list)"
Write-Host "Fichiers deplaces : $files ($bytes octets) ; dans l'archive : $zipFiles fichier(s) de module"
Write-Host "Restes dans le build : $(@($left).Count)"
Write-Host "Archive : $zip ($((Get-Item $zip).Length) octets), SHA-256 $hash"
if ($zipFiles -ne $files -or @($left).Count -ne 0) { throw 'Controle en echec : contenu de l''archive different des fichiers deplaces' }