LifePlay-Revival/Docs/Migration/etat/REL/SAV/REL-SAV-VAL.md
edujiro5972 8084622450 LifePlay UE 5.8 : état de développement au 2026-10-19
Tickets : REP1-T4

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 22:57:42 +02:00

11 KiB

Fiche REL-SAV-T4 — Niveau N1 — Modèle prévu sonnet | utilisé claude-sonnet-5-5 — Agent lp-validator — Skills sk-protocol, sk-recount, lp-validate

REL-SAV-VAL : validation statique de la garde B+ (2026-10-13)

Verdict : PASS-STATIC (aucun KO ; 5 réserves mineures, section 5). Rien n'a été compilé ni lancé ; rien n'a été modifié hors ce livrable. Complément T4b en fin de document (R1 corrigée, PASS-STATIC).

1. Fiches amont relues

  • REL-SAV-SPEC.md (T2, §§ 0-8), REL-SAV-IMPL.md (T3, écarts E1-E4), fiche fiches/REL-SAV-T4.md.
  • Code lu en entier : Source/LifePlay/LPSaveRelaunch.cpp (391 l.), LPSaveRelaunch.h (9 l.), LifePlay.cpp (24 l.) ; git diff de LifePlay.cpp.
  • Moteur 5.8 : Core/Public/Windows/{WindowsHWrapper,AllowWindowsPlatformTypes,HideWindowsPlatformTypes,MinWindows,PostWindowsApi}.h, Misc/Build.h, WindowsPlatformProcess.cpp:30-45.

2. Points à confirmer des IMPL (REL-SAV-IMPL.md)

L'IMPL ne numérote pas de section « points à confirmer » ; reprises ici : ses écarts, ses lectures moteur et ses « Chaîne à chercher ».

Point Résultat
E1 (ajout de LPSaveRelaunch.h) PASS : idiome de LPModGuard.h ; en-tête sous instantané (manifest.csv : Existed=False)
E2 (LP_SAVE_RELAUNCHED = source, pas 1) PASS : le verrou teste « non vide » (cpp:279), LogStartupState relit la valeur (cpp:377-379) ; cohérent
E3 (WindowsHWrapper.h avant ShlObj, FALSE remplacé par 0) PASS : PostWindowsApi.h:45 retire GetEnvironmentVariable ; windows.h déjà inclus par MinWindows.h:80, ShlObj ne le réintroduit pas ; Hide… retire TRUE/FALSE, cpp:332 utilise 0 ; même motif que WindowsPlatformProcess.cpp:38-45
E4 (motifs non-compile, non-execute) PASS : cpp:382, 387
shell32/ole32/uuid reliées, UE_GAME reporté (non rejoué ici : UEBuildWindows.cs, TargetRules.cs:3479 non relus) ; Build.h:22-23 relu : UE_GAME vaut 0 par défaut, d'où le defined(UE_GAME) && UE_GAME correct
Nom d'exe Development LifePlay.exe PASS-STATIC : la garde n'utilise que GetModuleFileNameW (cpp:252)
Shipping sans journal reporté à l'essai COMMUN-4 (E8)
Chaînes à chercher reporté (exige un build)

3. Critères rejoués (lecture ; aucune compilation)

# Critère Résultat Preuve
a Diff LifePlay.cpp OK git diff : +1 include (l.5), +2 lignes (commentaire l.17, appel l.18), rien d'autre. MD5 : LPSaveRelaunch.cpp b5b37ce8…, .h f31173f6…, LifePlay.cpp 3f7ffa30… = IMPL. LF (CR = 0 par tr), non-ASCII = 0 pour les 3 ; git ls-files --eol : i/lf w/lf. Instantané FB62EEAC… 734 o présent.
b Garde de compilation OK cpp:27 PLATFORM_WINDOWS && IS_MONOLITHIC && !WITH_EDITOR && defined(UE_GAME) && UE_GAME. Toutes les macros sont définies par Build.h / UBT (IS_MONOLITHIC : Build.h:133-135) ; UE_GAME protégée par defined. UE_BUILD_SHIPPING (cpp:288) défini. Pas de C4668 attendu.
c Aucune API moteur dans le chemin statique OK FGuard::FGuard (cpp:351) → Run (249-347) → Len, Append, Lower, HasToken, FileExists, DirExists, CutLastComponent, SkipProgramName, IsAbsolute, ComputeTarget : uniquement Win32 (GetModuleFileNameW, GetFileAttributesW, GetCommandLineW, GetEnvironmentVariableW, GetPrivateProfileStringW, ExpandEnvironmentStringsW, SHGetKnownFolderPath, SHCreateDirectoryExW, CreateProcessW…) et tampons globaux wchar_t[32768] (cpp:57-62). Pas de new, std::, FString, FPaths, UE_LOG, TArray, check dans cette zone. FString/FPaths/UE_LOG/FPlatformMisc n'apparaissent qu'en LogStartupState (cpp:360-391), appelée depuis StartupModule.
d Transmission brute OK SkipProgramName (cpp:153-180) : guillemets ou premier blanc, puis blancs. Queue recopiée par Append (cpp:314) sans retouche ; -UserDir="<cible>" en fin (cpp:315). Avec queue vide : un seul blanc (cpp:313-315). Aucun \ final : cpp:237-241 retire \ et /, racine X: → X:\. (cpp:242-245).
e Anti-boucle OK cpp:279 : userdir= précédé de - ou /, sans casse (Lower, cpp:90-93, mot en minuscules) ou variable non vide. Posée cpp:331 avant CreateProcessW (332), retirée cpp:334 en cas d'échec.
f Échec = démarrage normal OK Sorties de Run : exe illisible/trop long 253-257 ; pas de pak 260-270 ; désactivé 274-278 ; verrous 279-283 ; Development sans ini 288-293 ; cible incalculable 298-302 ; dossier non créé 304-309 ; ligne trop longue 316-320 ; CreateProcessW en échec 332-338 : toutes return sans relance ni sortie. TerminateProcess (346) atteint seulement après CreateProcessW réussi, CloseHandle(hThread) et WaitForSingleObject (342).
g Détection OK 3 coupes de GExe (cpp:260) puis \Content\Paks\LifePlay-Windows.pak (266). Shipping : ini facultatif (le #if !UE_BUILD_SHIPPING ne couvre que 288-294) ; autrement ini exigé. GetPrivateProfileStringW sur [SaveLocation] SaveDir= (197) ; chemin absolu seul (IsAbsolute 182-188, test 211) ; sinon SHGetKnownFolderPath(FOLDERID_LocalAppData) + \LifePlay-Revival (223-225).
h Compilabilité (estimée) OK, estimé Ordre WindowsHWrapper.h → AllowWindowsPlatformTypes.h → <shlobj.h> → HideWindowsPlatformTypes.h (cpp:34-37) = schéma de WindowsPlatformProcess.cpp:36-45 (qui emploie aussi SHGetKnownFolderPath/CoTaskMemFree). TRUE/FALSE absents du code. Types Win32 (DWORD, PWSTR, STARTUPINFOW, PROCESS_INFORMATION, WORD) valides après Hide (DWORD redevient le typedef). LogStartupState : FString ternaires avec FString(TEXT(…)) des deux côtés (cpp:378-379), pas de type mixte. FPlatformMisc::GetEnvironmentVariable(const TCHAR*) existe (GenericPlatformMisc.h:619, WindowsPlatformMisc.h:126) et la macro est retirée. LogLifePlay : LifePlay.h:9. Espace de noms LPSaveRelaunchPrivate : pas de collision en unity. Pas d'ombrage de variable ni de comparaison signée/non signée relevés. Non compilé : à confirmer par COMMUN-4.
i SPEC §§ 1-6 et E1-E4 OK § 1 : objet statique GGuard (cpp:356), tampons fixes. § 2 : pak, -LPNoSaveRelaunch, verrous, Shipping/Development/DebugGame. § 3 : ini, ExpandEnvironmentStringsW, repli, création du dossier. § 4 : GetStartupInfoW pour wShowWindow (322-328), bInheritHandles 0, environnement et dossier hérités, attente, GetExitCodeProcess, TerminateProcess. § 6 : format de la ligne (389-390) et motifs. Seuls écarts : E1-E4, déclarés. Voir l'observation O1 (section 5).

(Numéros de ligne de cette section : version b5b37ce8… ; décalés de +5 après ComputeTarget dans la version a0cd6687…, voir T4b.)

4. Diff lu en entier

  • Suivi par git : Source/LifePlay/LifePlay.cpp, +3 lignes, 0 retrait (voir a).
  • Non suivis, lus en entier : LPSaveRelaunch.cpp 391 l. (Read et wc -l concordent), LPSaveRelaunch.h 9 l. L'IMPL annonce 10 l. pour l'en-tête : écart de comptage sans effet (réserve R5).
  • git status --short sur les 3 chemins : M LifePlay.cpp, ?? LPSaveRelaunch.cpp, ?? LPSaveRelaunch.h. Aucun autre fichier de Source/LifePlay/LP* du lot n'a été touché par cette IMPL (les autres M du statut initial appartiennent à d'autres lots).

5. Verdict : PASS-STATIC

Contrôles a-i : OK. Réserves (aucune ne bloque ; correctifs proposés, non appliqués) :

  • R1 LPSaveRelaunch.cpp:208-216 : un " intérieur à SaveDir n'est pas refusé et fermerait le guillemet de -UserDir="…" (cpp:315). Risque faible (ini local de l'utilisateur). Correctif : annuler GTarget si elle contient ". Corrigée, voir T4b.
  • R2 LPSaveRelaunch.cpp:197 : GetPrivateProfileStringW lit un ini ANSI (page de code) ou UTF-16 ; un SaveDir non ASCII écrit en UTF-8 par le futur menu (U4) serait mal lu. À fixer dans la fiche du menu (écrire en UTF-16 ou imposer l'ASCII).
  • R3 LPSaveRelaunch.cpp:332 : bInheritHandles = 0 sans STARTF_USESTDHANDLES : une redirection de sortie du parent (> fichier, -log sur console redirigée) ne passe pas à l'enfant. Sans effet pour un jeu fenêtré ; à noter pour Tools\debug\*.
  • R4 LPSaveRelaunch.cpp:242-245 : racine de lecteur, cible X:\. : à observer à l'essai (le moteur normalise le chemin ?). Cas improbable.
  • R5 IMPL : l'en-tête compte 9 lignes, non 10.
  • O1 (observation, pas un écart) : SPEC § 2.3 dit « précédé d'un espace ou d'un guillemet », la fiche T4 et le code disent « précédé de - ou / » ; le code est le plus juste (le - fait partie du jeton).

6. Points à confirmer par l'utilisateur (cases)

  • Compilation (COMMUN-4, Development) sans nouvel avertissement : non rejouée ici.
  • Essai en jeu E1-E7 (SPEC § 7) ; E8 Shipping sur accord de Big Boss.
  • R1 : corrigée (T4b).
  • R2 : encodage de l'ini pour la fiche du menu U4.

Non vérifié

Compilation, unité Unity, exécution, UEBuildWindows.cs:2098-2101 et TargetRules.cs:3479 (cités par l'IMPL, non relus), comportement du bootstrap, effet de X:\. sur le moteur.

Tours : 5 / 30

T4b : complément après correction de R1 (E5)

Fiche REL-SAV-T4b — mêmes règles que T4. Version relue : LPSaveRelaunch.cpp 396 l., MD5 a0cd668771ff5893ed2867f24de1df3a (relevé par md5sum, = valeur annoncée).

# Contrôle Résultat Preuve
1 Diff limité à la boucle bHasQuote OK, estimé L'ancienne version n'est plus sur disque (aucune copie de b5b37ce8…, l'instantané ne contient que LifePlay.cpp) : diff non rejouable. Recoupement : +5 lignes exactement (391 → 396) ; lignes 211-215 = commentaire, bool bHasQuote, boucle for et ses 3 lignes ; condition l.216 étendue de `
2 Boucle bornée OK cpp:212 : I < BufferLen && GTarget[I] ; GTarget a BufferLen éléments (cpp:61) : l'indice maximal lu est 32767, jamais hors tableau, même si ExpandEnvironmentStringsW échoue ou rend Needed > BufferLen (tampon alors non terminé). Dans ce cas la condition de cpp:216 (Needed > BufferLen) remet GTarget[0] = 0. Arrêt dès le premier " (!bHasQuote).
3 Aucune API moteur dans le chemin statique OK Les 5 lignes n'emploient que DWORD, bool, wchar_t, BufferLen, GTarget ; pas de FString/FPaths/UE_LOG/TArray/check/new/std:: ajouté.
4 CR et ASCII OK tr -cd '\r' | wc -c = 0 ; LC_ALL=C tr -d '\000-\177' | wc -c = 0 ; wc -l = 396.

Effet sur la SPEC : conforme (SPEC § 3.2 : repli sur le défaut quand le résultat est invalide). Comportement : un SaveDir contenant " donne GTarget[0] = 0, donc la cible par défaut %LOCALAPPDATA%\LifePlay-Revival (source defaut). Un " de bord reste retiré avant (cpp:199-207). Écart E5 de l'IMPL : cohérent avec ce contrôle ; non relu dans l'IMPL mise à jour (hors demande).

Verdict T4b : PASS-STATIC. R1 levée. Réserves R2 à R5 inchangées. MD5 à reporter au commit : a0cd668771ff5893ed2867f24de1df3a.

Tours : 2 / 30 (T4b)