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), fichefiches/REL-SAV-T4.md.- Code lu en entier :
Source/LifePlay/LPSaveRelaunch.cpp(391 l.),LPSaveRelaunch.h(9 l.),LifePlay.cpp(24 l.) ;git diffdeLifePlay.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.cpp391 l. (Readetwc -lconcordent),LPSaveRelaunch.h9 l. L'IMPL annonce 10 l. pour l'en-tête : écart de comptage sans effet (réserve R5). git status --shortsur les 3 chemins :M LifePlay.cpp,?? LPSaveRelaunch.cpp,?? LPSaveRelaunch.h. Aucun autre fichier deSource/LifePlay/LP*du lot n'a été touché par cette IMPL (les autresMdu 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 àSaveDirn'est pas refusé et fermerait le guillemet de-UserDir="…"(cpp:315). Risque faible (ini local de l'utilisateur). Correctif : annulerGTargetsi elle contient". Corrigée, voir T4b. - R2
LPSaveRelaunch.cpp:197:GetPrivateProfileStringWlit un ini ANSI (page de code) ou UTF-16 ; unSaveDirnon 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 = 0sansSTARTF_USESTDHANDLES: une redirection de sortie du parent (> fichier,-logsur console redirigée) ne passe pas à l'enfant. Sans effet pour un jeu fenêtré ; à noter pourTools\debug\*. - R4
LPSaveRelaunch.cpp:242-245: racine de lecteur, cibleX:\.: à 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)