Commit Graph

24 Commits

Author SHA1 Message Date
7cb2b01403 v0.23.3 — Mode safe : OpenVR sans vtable (Phase 1 stripped down)
Init OpenVR + binding de la vtable IVRSystem_022 fonctionne (confirmé par
les logs : "OpenVR state: Ready, vtable IVRSystem_022 bindée"), mais le
PREMIER appel vtable crashe en AccessViolation natif. Pas une race au
boot — ça plante même 8s après que vrserver est apparu, et probablement
à n'importe quel moment de la session SteamVR.

Diagnostic : les fonctions de la vtable (IsTrackedDeviceConnected,
GetBoolTrackedDeviceProperty, etc.) accèdent à des shared memories avec
vrserver qui peuvent être (re)mappées dynamiquement quand un device se
connecte/déconnecte. L'AccessViolation natif passe au travers du CLR
via Marshal.GetDelegateForFunctionPointer — non rattrapable par
catch (Exception) ni catch (AccessViolationException).

Fix : on retire TOUS les appels vtable. OpenVrService.Query utilise
maintenant uniquement l'API flat C qui ne fait pas de SHM/IPC :

  - VR_IsRuntimeInstalled() — lookup statique dans la DLL
  - VR_IsHmdPresent() — consulte un mutex/event nommé, pas de SHM
  - GetProcessesByName("vrserver") — process check Windows

Résultat : check VR robuste mais réduit à "SteamVR actif + HMD présent
oui/non". Plus de batterie, plus d'info per-device, le device picker du
dialog devient cosmétique (tous les aliases retournent la même réponse).

La batterie + l'info per-device reviendra en Phase 1.5 via une approche
sub-process : un PSLauncher.VrProbe.exe séparé qui fait les vtable calls
et communique par stdout JSON. Si VrProbe crashe en AccessViolation, le
launcher voit juste "exit code != 0" et report "VR query failed" sans
être affecté lui-même.

Le code de la vtable (delegates, BindVTableDelegates, Init/Shutdown) reste
en place dans OpenVrService.cs comme référence pour Phase 1.5 — n'est
juste plus appelé depuis Query.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 20:16:16 +02:00
07ead6011f v0.23.2 — Fix AccessViolation pendant le démarrage de SteamVR
Cause : race condition. Quand l'utilisateur lance SteamVR alors que le
launcher tourne déjà, vrserver.exe apparaît dans la liste des process
avant que ses drivers et ses shared memories soient prêts à servir des
requêtes vtable. La séquence problématique :

  1. tick health loop → IsVrServerRunning() = true (vrserver vient juste
     d'apparaître dans la process list)
  2. VR_InitInternal2 → succeed (init est tolérant, ne lit pas la SHM)
  3. VR_GetGenericInterface → vtable pointer valide
  4. premier appel vtable (IsTrackedDeviceConnected, slot 21) → lit dans
     une shared memory pas encore allouée par vrserver → AccessViolation
  5. SEH passe au travers du CLR via Marshal.GetDelegateForFunctionPointer
     → process killed sans que catch (Exception) ne se déclenche.

Stack confirmée par l'Event Viewer Windows :
  System.AccessViolationException
    at OpenVrService.Query(System.String)
    at SystemHealthService.CheckVrDevice(...)
    at health loop background task

Fix : cooldown de 8 s après la première détection de vrserver. On note
le timestamp UTC de la première apparition, et on refuse l'init tant que
moins de 8 s n'ont passé. Pendant le warmup, la pill reste sur "SteamVR
non lancé" (HmdAbsent), puis bascule sur Ready au tick suivant. Si
vrserver disparaît, on reset le timestamp + on tear-down la session
proprement, prêts pour un prochain cycle de démarrage.

8 s = compromis : sur SSD le ramp-up des drivers SteamVR prend ~3-5 s,
sur HDD ça peut monter à 7 s. La marge couvre les deux.

Bonus diag : flush Serilog avant que le process meure dans le hook
AppDomain.UnhandledException. Sans ça, la trace de la fatale n'arrivait
jamais sur disque (exactement ce qu'on a vu dans les logs : silence
total avant le redémarrage suivant).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 20:08:32 +02:00
c997859750 v0.23.1 — Fix crash OpenVR quand SteamVR non lancé
Deux bugs corrigés :

1. Marshalling bool incorrect sur VR_IsHmdPresent / VR_IsRuntimeInstalled.
   OpenVR renvoie un bool C++ (1 octet) ; sans MarshalAs(I1) le marshaller
   .NET lit 4 octets (Win32 BOOL) et obtient des valeurs random. Conséquence :
   VR_IsHmdPresent passait parfois à "true" même si aucun casque n'était
   détecté, et on tentait alors une init OpenVR sans backend → crash.

2. Garde process avant init. Même avec le bool marshalling correct,
   VR_IsHmdPresent peut renvoyer true si un HMD est branché en USB sans
   que SteamVR soit lancé. VR_InitInternal2 en mode Background tente alors
   de charger les drivers (lighthouse, oculus_link…) qui peuvent crash en
   SEH si vrserver n'a jamais été démarré sur cette session. On ajoute
   une vérification de la présence de vrserver.exe avant de tenter l'init,
   avec cache 2 s pour ne pas faire un GetProcessesByName à chaque tick.

Résultat : si SteamVR est installé mais pas lancé → pill "SteamVR pas lancé"
proprement, sans tenter d'init et donc sans risque de crash.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 19:38:35 +02:00
4d0e287227 v0.23.0 — SteamVR / OpenVR : check VrDevice avec batterie + tracking
Nouveau Kind « VrDevice » dans le bandeau de santé. Pour chaque appareil
SteamVR (HMD, manettes, trackers, base stations) on lit via OpenVR :
batterie %, état de charge, niveau d'activité (actif / veille / inactif)
et serial number. Severity du pill calculée sur le batterie : < 15% rouge,
< 30% orange, sinon vert ; charging force le vert.

Implémentation : P/Invoke direct dans openvr_api.dll (BSD-3) — pas de
NuGet, pas de bundling de la DLL. La runtime est résolue dynamiquement
via DllImportResolver depuis :
  1. %LocalAppData%\openvr\openvrpaths.vrpath (canon écrit par SteamVR),
  2. fallback C:\Program Files (x86)\Steam\steamapps\common\SteamVR\bin\win64\.

Si SteamVR n'est pas installé → pill « SteamVR non installé » (Unknown,
gris). Si SteamVR pas lancé → pill « SteamVR non lancé / aucun HMD »
(Error, rouge). Init en mode VRApplication_Background, donc on lit l'état
sans devenir scene application ni monopoliser le compositor.

Éditeur : nouveau radio « Appareil SteamVR » à côté de Process / Ping ;
quand il est sélectionné, le champ Target devient une ComboBox d'alias
(HMD, Manette gauche/droite, Tracker 1-3, Base station 1-2) au lieu du
TextBox libre. Le mapping alias → TrackedDeviceIndex se fait à chaque
query (controllers via GetTrackedDeviceIndexForControllerRole, trackers
et base stations par énumération avec compteur de classe).

Robustesse : init lazy une fois pour toute la vie du process (init/shutdown
répétés coûtent 100-500 ms). Vtable IVRSystem_022 résolue par index slot
dans une struct contiguë de IntPtr (pas de struct marshaling, donc immune
aux changements de slots qu'on n'utilise pas). Si SteamVR crash en cours
de session, le service détecte au prochain tick et marque la session
broken pour retenter au tick suivant.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 19:29:57 +02:00
211af9dd85 v0.22.0 — Health checks editor : icon picker + per-check refresh
Settings → Avancés → Bandeau de santé : liste éditable des checks (Add /
Edit / Delete) avec dialog modal pour chaque entry. Picker d'emoji parmi
28 icônes curatées (VR / streaming / réseau / système). Édition du nom,
type (Process / Ping), cible, intervalle de refresh en ms (per-check),
et seuils Ping (warn / error / timeout) sous une section Avancé.

La config est persistée dans %LocalAppData%\PSLauncher\config.json donc
elle survit aux auto-updates du launcher. Modifiable aussi à la main
dans le json pour les power-users.

Le bandeau hot-reload après Save : cancel des boucles de polling
existantes, reconstruction des HealthIndicators depuis la config fraîche,
puis relance d'une boucle dédiée par check (chacune cadencée sur son
propre RefreshIntervalMs — Process check rapide peut tourner à 1 s,
Ping reste à 5-10 s).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 19:09:39 +02:00
60358a6cf5 v0.21.0 — System health banner with colored indicators + tooltips
New row between top bar and content showing pill-shaped status indicators
for the system dependencies that PROSERVE needs (SteamVR, Vive Business
Streaming process, VR headset reachable on the network, etc.). Each pill
is colored : 🟢 OK, 🟠 limitation détectée (e.g. ping élevé), 🔴 KO,
gris = non configuré. Hover → tooltip with full check kind, target,
last RTT/process count and timestamp.

Architecture
- LocalConfig.HealthChecksConfig : list of HealthCheckEntry (Kind=Ping
  or Process, Target=IP/host or process name, ping thresholds in ms,
  refresh interval in s).
- ISystemHealthService + SystemHealthService : ping via
  System.Net.NetworkInformation.Ping (no admin required), process
  lookup via System.Diagnostics.Process.GetProcessesByName. Returns
  HealthResult { Severity, Detail }.
- HealthIndicatorViewModel : observable wrapper per entry with
  Severity-driven Brushes (background pill, border, icon colour) and
  composed Tooltip text.
- MainViewModel.InitHealthIndicators + StartHealthLoop : populates
  ObservableCollection<HealthIndicatorViewModel> from config and runs
  parallel checks every RefreshIntervalSeconds (default 10s).
- MainWindow.xaml : new Row 1 (between top bar and body) hosting an
  ItemsControl bound to HealthIndicators. Row collapses cleanly if the
  list is empty (HasHealthIndicators=false).

Defaults shipped (all editable in %LocalAppData%\PSLauncher\config.json)
- 🎮 SteamVR (Process : vrserver)
- 📡 Vive Business Streaming (Process : HtcConnectionUtility)
- 🥽 Casque VR (Ping : empty, user fills in the headset IP)

The Settings UI editor for managing the list is intentionally deferred
to a future iteration — config.json edit is enough to start using it.

Versions bumped to 0.21.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 18:44:15 +02:00
30eceaea2c v0.20.0 — Cancel-from-red-button waits for segments + new Verifying row state
Two related UX bugs fixed.

1) Red "Annuler" button on a row : DL appeared to keep going

The button calls RestartFromZeroAsync which used to fire
_activeDownloadCts.Cancel() then sleep 200 ms then delete the .partial.
With 16 parallel segments mid-write, 200 ms is way too short — the
segments were still flushing buffers when the file got deleted, then
recreated it from their own write streams, making it look like the
download "continued".

Now we keep a reference to the in-flight install Task
(_activeInstallTask) and properly await it (with a 10 s safety timeout)
before discarding state. This guarantees all segment FileStreams are
closed and the .partial deletion is the final word.

2) Progress bar said "Downloading…" during SHA-256 verification

The footer message switched to "🔍 Vérification SHA-256 v…" but the
row's badge stayed on "⬇ Downloading…" because row.State stayed at
Downloading throughout DownloadAsync (which internally chains DL +
verify). New VersionRowState.Verifying inserted between Downloading
and Installing, applied on the first hashProgress callback. Badge now
reads "🔍 Vérification…" / "🔍 Verifying…" during the hash phase.

VersionRowViewModel.IsBusy now also includes Verifying so commands
that gate on busy stay disabled during verification.

Versions bumped to 0.20.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 18:34:18 +02:00
3c8438f3fe v0.19.0 — Bump default parallel segments 8→16 to shorten end-of-DL tail
Issue : on a 14 GB download with 8 segments, when 7 finish there's only
1 connection left handling its assigned 1.75 GB → apparent throughput
collapses to 1/8th of the peak for the final stretch (~5 min visible
slowdown at the end).

Quick fix : double the default segment count.
- 8 segments  : last has 1.75 GB to finish alone
- 16 segments : last has 875 MB → tail ~halved

Tuning surfaced in Settings → Avancés → Serveur as
« Connexions parallèles pour les téléchargements (1-32, défaut 16) ».
Power users on faster servers can push to 24-32. OVH mutualisé tolerates
up to 16-24 ; above that, soft rate-limiting kicks in.

Aligned :
- LocalConfig default : 8 → 16
- DownloadManager clamp : Math.Clamp(_, 1, 16) → 1, 32
- HttpClient handler MaxConnectionsPerServer : 16 → 32 (matches new ceiling)

Versions bumped to 0.19.0.

Existing config.json files will keep their previous value (8) untouched ;
delete config.json to pick up the new default, or just bump it in
Settings → Avancés.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 18:28:35 +02:00
8366ca2669 v0.18.0 — Bluer dark palette to match ASTERION VR identity
Shifts the launcher background from pure black to deep navy and adds a
subtle blue tint over the body bitmap so the overall feel is closer to
the brand's blue/cyan accent rather than neutral black-on-grey.

Theme.xaml palette tweaks
- Brush.Bg.Window  : #000000 → #0A1220 (deep navy, ~95% black)
- Brush.Bg.Sidebar : #0E1218 → #0E1422
- Brush.Bg.Card    : #161B23 → #161D2C
- Brush.Bg.Footer  : #050709 → #060A14
- New Brush.Bg.BlueTint : #3050A0 @ 8% opacity, used as overlay

MainWindow.xaml
- Outer Grid Background : hardcoded "Black" → Brush.Bg.Window
  (so future tweaks of the brush propagate)
- New Rectangle on top of the body Background.png with the blue tint
  brush, IsHitTestVisible=False so it doesn't block clicks.

Versions bumped to 0.18.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 11:30:24 +02:00
e608a5d29d v0.17.0 — Doc tool auto-deploy + revert (mirror of Report)
Adds the same deploy + backup/revert mechanism as Report, but for the
local Documentation web tool :
  PROSERVE-source/_doc/  →  C:\xampp\htdocs\ProserveDoc\
  http://localhost/ProserveDoc/

Default DocumentationUrl in the launcher set to http://localhost/ProserveDoc/
so the Documentation sidebar tab works out of the box on a fresh install
without any manual config.

Implementation
- LocalConfig.DocToolConfig (HtdocsRoot/FolderName/AutoDeploy/MaxBackups,
  defaults to ProserveDoc folder, 3 backups kept).
- IDocToolDeployer + DocToolDeployer : near-duplicate of the Report
  deployer with SourceSubdir = "_doc". Reuses ReportTool's BackupInfo /
  DeployStatus / DeployResult / DeployProgress types so we don't fork
  the data model.
- MainViewModel : new install step 7 right after the Report deploy step,
  mirrors its progress reporting + error dialogs (silent on
  XamppNotFound since the Report step already warned for the same root).
- SettingsViewModel : DocBackupViewModel + Doc properties + RedeployDoc
  + Revert commands. Loads the doc backups list in background like
  the Report ones.
- SettingsDialog.xaml : new « OUTIL DOCUMENTATION » card under the
  « OUTIL REPORT » one in Avancés, with the same fields and backup table.
- Strings.cs : doc-specific status / progress / error labels in 5 langs ;
  reuses some labels from Report where the wording is identical.

Versions bumped to 0.17.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 11:18:13 +02:00
886245cc61 v0.16.0 — ThemedMessageBox replacing native MessageBox everywhere
The Win32 MessageBox uses the system white-on-gray look that clashes
with the launcher's dark theme (jarring transition every time we
confirm a cancel, show an error, etc.). Replaced with a custom WPF
dialog that:

- Drops in : same Show(message, title, button, icon, default) API as
  System.Windows.MessageBox, returns the same MessageBoxResult.
- Looks native to the launcher : dark Brush.Bg.Window background,
  Brush.Border outline, Brush.Text.Primary content, AccentButton for
  the default action and SecondaryButton for the others.
- Iconography by emoji + colour code :  red (Error), ⚠ amber (Warning),
   neutral (Question), ℹ blue (Information). Mapped from MessageBoxImage.
- Buttons localised via Strings.ActionOk / ActionYes / ActionNo /
  ActionCancel — works in fr/en/zh/th/ar like the rest of the UI.
- Owner = currently active window (falls back to MainWindow then
  CenterScreen) so it positions correctly even from a child dialog.
- Esc resolves to Cancel/No matching MessageBox semantics.

The 21 MessageBox.Show call sites across MainViewModel, SettingsViewModel,
LicenseDetailsDialog and MainWindow now use ThemedMessageBox.Show with
no signature change — full grep replace + tiny `using` adjustments.

Versions bumped to 0.16.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 10:46:53 +02:00
24d1841844 v0.15.0 — release notes Markdown : readable inline code
Bumps to consolidate the dark-theme fix for inline `code` in the
release notes viewer (commit 6d11b43) into a versioned client.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 10:33:23 +02:00
c706d09e72 Drop legacy PSLauncher.exe backward compat (not in prod yet)
Simplifies the rename done in ce3979d : PS_Launcher.exe is the only
name. Removes the dual-path lookup in LauncherSelfUpdater, the
duplicated taskkill blocks in the .bat scripts, the legacy patterns
in .gitignore, and the explanatory comments about the migration.

Cleaner code, single source of truth for the binary name.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 10:08:23 +02:00
ce3979d131 v0.14.0 — rename PSLauncher.exe → PS_Launcher.exe (with backward compat)
Aligns the executable name with the repo / product naming. Backward-
compatible: existing installs that have PSLauncher.exe on disk continue
to work — the self-updater overwrites the file at the running .exe path
without renaming, so the historical filename persists on those machines.

Changes
- AssemblyName : PSLauncher → PS_Launcher (both App and Updater csproj)
- Inno Setup : MyAppExeName, MyUpdaterExeName, OutputBaseFilename all
  now use PS_Launcher prefix
- LauncherSelfUpdater : looks for PS_Launcher.Updater.exe first, falls
  back to legacy PSLauncher.Updater.exe so old installs keep updating
- Build scripts (build-launcher / build-updater / build-installer /
  build-all) : taskkill both legacy AND new names; output paths printed
  with new names
- .gitignore : added PS_Launcher.exe / PS_Launcher.Updater.exe /
  PS_Launcher-*.exe patterns alongside the legacy ones; also ignored
  the WebView2 user-data folder and Office ~$ lock files
- Server admin/launcher.php : URL pattern now generates
  PS_Launcher-{ver}.exe ; SignManifest's existing tolerant glob
  *{ver}*.exe still matches both names
- Versions bumped to 0.14.0 (App + Updater + installer .iss)

Migration story for clients
- Brand-new install via PS_Launcher-Setup-0.14.0.exe → PS_Launcher.exe
  on disk
- Existing install (PSLauncher.exe) auto-updates to v0.14 → file stays
  named PSLauncher.exe but contains v0.14 code; self-updater fallback
  ensures future updates keep working

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 09:55:41 +02:00
562b4e5ed0 v0.13.0 — Report tool : timestamped backups + manual revert
The previous deploy mechanism kept the old version under {target}.old
just long enough to swap, then deleted it — no way back if the new
report had a runtime bug only visible at use time.

Now each deploy keeps the previous active version under
{FolderName}.backup-yyyyMMdd-HHmmss/. The N most recent are kept
(MaxBackups, default 3) and older ones are pruned in best-effort.

UI in Settings → Avancés → Outil Report :
- "Conserver N backups" input (0 = no history, back to v0.12 behavior)
- List of backups with date + size + cryptic folder name + per-row "↶ Revert"
- Confirmation dialog: "Revert to {date}? The currently deployed
  version will itself be saved as a new backup, so you can switch back."

Implementation
- IReportToolDeployer: + ListBackupsAsync, + RevertAsync, + BackupInfo,
  + DeployStatus.BackupNotFound.
- ReportToolDeployer: deploy renames {target} → backup-{ts}; PruneOldBackups
  trims to MaxBackups; RevertAsync atomically swaps current ↔ chosen
  backup, the previous current becoming itself a fresh backup.
- ReportBackupViewModel + DataTemplate for the list rows.
- Strings: backup labels, "↶ Revert", confirmation message.

Caveats documented in the design discussion:
- Backups cover ONLY the report tool files. DB migrations are not reverted
  (irreversible). If a migration broke the schema, that's a separate fix.
- No HTTP-HEAD post-deploy auto-revert: too fragile for false positives.
  Revert stays a deliberate manual action.

Versions bumped to 0.13.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 09:15:09 +02:00
67f422539d v0.12.0 — Report tool auto-deploy from PROSERVE ZIP
Each PROSERVE ZIP can ship a _report/ subfolder alongside _migrations/.
After install + DB migration, the launcher copies _report/ to the local
XAMPP htdocs (default C:\xampp\htdocs\ProserveReport\) so the Reports
tab in the sidebar always serves the matching version.

Service (PSLauncher.Core/ReportTool)
- IReportToolDeployer + ReportToolDeployer.
- Atomic deploy: copy to {target}.new/ → rename current to .old/ →
  rename .new → final → cleanup .old. Apache never sees a half-state.
- Skip silencieux si _report/ absent du ZIP (PROSERVE release qui ne
  touche pas au Report).
- DeployStatus enum (SkippedNoSourceFolder, XamppNotFound, Failed,
  Deployed) renvoyé pour gestion UI claire.

Config (LocalConfig)
- ReportToolConfig (HtdocsRoot, FolderName, AutoDeploy). Defaults
  matchent l'install standard ASTERION (C:\xampp\htdocs\ProserveReport).

Install pipeline (MainViewModel)
- Étape 6 après extraction + migrations. Progress reporté via le footer
  Library : « 📂 Déploiement Report : 47/120 — assets/main.js ».
  XAMPP introuvable → dialog avec lien vers Settings → Avancés.

Settings UI (SettingsDialog → Avancés)
- Nouveau bloc « OUTIL REPORT (XAMPP htdocs) » : 2 champs path + checkbox
  AutoDeploy + bouton « 📂 Re-déployer maintenant » qui rejoue le deploy
  sur la dernière version installée. Utile après changement de chemin
  htdocs ou si l'auto-deploy avait foiré (XAMPP éteint).

Versions bumped to 0.12.0 (App + Updater + installer .iss).

Côté release : tu ajoutes _report/ dans le source de PROSERVE, tu zippes,
tu uploades. Les clients récupèrent ZIP + migrations + Report en une
seule install.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 09:05:40 +02:00
6d251e9eac v0.11.0 — right sidebar nav with Reports / Documentation tabs
User-visible feature change (sidebar appears on every screen) deserves
a minor bump rather than a patch. WebView2 dependency adds ~5 MB to the
single-file exe (the Edge runtime itself is system-installed, not
shipped).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 08:53:14 +02:00
1473e5185c v0.10.0 — DB migration support
Bumps version across the three places it lives:
- PSLauncher.App.csproj : 0.9.0 → 0.10.0
- PSLauncher.Updater.csproj : 0.8.0 → 0.10.0 (catch-up; the updater
  hadn't been bumped since 0.8 — version doesn't gate behavior, just
  matches the .exe metadata)
- installer/PSLauncher.iss : 0.8.0 → 0.10.0 (installer artifact name
  PSLauncher-Setup-0.10.0.exe)

This release adds bundled SQL migrations applied automatically after
each PROSERVE install, so a new build that needs schema changes ships
the .sql alongside its binaries and the launcher rolls them out without
manual intervention.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 08:29:02 +02:00
f5f1fe4d36 docs: PowerPoint deck for internal admin + end-user guide, embedded in installer
Two presentations generated from pptxgenjs scripts in docs/, both using
the launcher's dark palette (black bg, #161B23 cards, #3B82F6 blue
accent for internal, #16A34A green accent for the user guide):

- PS_Launcher-Documentation-Interne.pptx (17 slides)
  Cover, sommaire, architecture client/serveur/MySQL, setup OVH initial,
  config.php, génération secrets, backoffice (Dashboard / Licenses /
  Versions / Launcher / Audit), workflow release Proserve, workflow
  release launcher, security (Ed25519 / HMAC / DPAPI), file locations
  + logs, troubleshooting, closing.

- PS_Launcher-Guide-Utilisateur.pptx (14 slides)
  Cover, présentation du launcher, prérequis Windows, installation
  setup.exe (avec gestion SmartScreen), activation license PRSRV-,
  bibliothèque (featured + autres), lancer une version, installer une
  nouvelle version, suivi du DL avec resume, menu ⋯ par version,
  paramètres ⚙, auto-update du launcher, FAQ (license expirée /
  offline / changement PC / SHA-256 KO), support.

Both decks use cards with colored left-accent strips, numbered step
flows, code blocks (Consolas on #0A0E14), and consistent footer
branding "© 2026 ASTERION VR".

installer/PSLauncher.iss now copies the user guide into {app}\docs\
and creates a "Guide utilisateur" Start Menu shortcut next to the
launcher. The internal doc stays in the repo, never shipped to clients.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 12:11:35 +02:00
2dc9c98d1e Bump version 0.7.0 / 0.5.0 → 0.8.0 (synced across launcher, updater, installer)
After the auto-update test loop the App was at 0.7.0 and Updater /
Inno Setup were still at 0.5.0. Sync everything to 0.8.0 so a single
version covers the recent batch of branding + UX changes:

- Default installRoot is C:\ASTERION_VR
- ASTERION favicon (window icons + .exe icon + Setup icon)
- ASTERION wordmark left of PROSERVE in the top bar
- Centered, readable "© 2026 ASTERION VR — Tous droits réservés" pill
- Window control hover in blue (was unreadable yellow)
- Background image anchored bottom-right so the brand mark doesn't
  get cropped on resize or hidden by the download footer
- Launcher window minimizes when a Proserve version is launched

Next time the operator pushes a release, "Définir" 0.8.0 in the
backoffice → upload PSLauncher-0.8.0.exe → click the blue Sync.
Existing 0.7.0 launchers will then auto-update to 0.8.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 12:01:55 +02:00
dfad967eae branding: ASTERION VR favicon + copyright everywhere
Icon
----
src/favicon64.jpg converted to favicon.ico (single-resolution 64x64)
via PowerShell + System.Drawing.Icon.FromHandle. The .ico is now an
EmbeddedResource of PSLauncher.App and referenced as:
- <ApplicationIcon> on PSLauncher.App.csproj → .exe icon in
  Explorer / taskbar / Alt-Tab
- Window.Icon on every WPF window: MainWindow, SettingsDialog,
  OnboardingDialog, LicenseDetailsDialog, UpdateAvailableDialog,
  ReleaseNotesViewerDialog, LauncherUpdateDialog
- <ApplicationIcon> on PSLauncher.Updater.csproj → updater also
  carries the brand icon
- SetupIconFile in installer/PSLauncher.iss → the Inno Setup .exe
  installer shows the icon too

Copyright
---------
- <Company>, <Product>, <Copyright> assembly attributes set in
  both csprojs → properties dialog on the .exe shows "© ASTERION VR".
- MainWindow body has a discreet floating "© ASTERION VR" text in the
  bottom-right, mirroring the "Vérifier les MAJ" button on the left.
  Opacity 0.7 + secondary color so it doesn't compete with the cards.
- SettingsDialog "Logs & Application" section gains a
  "© ASTERION VR — Tous droits réservés" line under the version.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 11:44:28 +02:00
f2a1de9aac Self-update: UAC-aware, de-elevated relaunch, user-mode installer
Three coordinated changes so the auto-update flow works on every
existing install location.

LauncherSelfUpdater.cs
----------------------
- Probe the target directory with a write/delete of a temp file. If
  it fails (e.g. install in Program Files without admin), set
  UseShellExecute=true + Verb=runas on the Updater process so UAC
  prompts the user once for elevation. If the directory is writable
  (user-mode install or portable layout), skip elevation entirely.
- Switched from ProcessStartInfo.ArgumentList to a quoted Arguments
  string because Verb=runas requires UseShellExecute=true, which
  ignores ArgumentList. Paths get explicit quotes to survive spaces.

Updater Program.cs
------------------
After the file swap, the relaunch was a direct Process.Start(target).
With UAC elevation that propagates admin to the new launcher, then to
its child PROSERVE_UE_5_5.exe — undesirable. Replace with
`explorer.exe "<target>"`: explorer is always running in the user's
normal token, opens the exe via shell, the new process inherits the
non-elevated session. Standard de-elevation trick.

installer/PSLauncher.iss
------------------------
Switch from system-wide install (Program Files, requires admin) to
per-user (DefaultDirName={localappdata}\Programs\..., PrivilegesRequired=
lowest). Auto-update then never needs UAC at all on fresh installs.
Existing installs in Program Files keep working thanks to the runas
fallback above.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 10:57:32 +02:00
6bc5381562 installer: use x64 (Inno Setup 6.0+) instead of x64compatible (6.3+)
The value x64compatible was added in Inno Setup 6.3 — earlier 6.x
versions reject it with "Value of [Setup] section directive
ArchitecturesAllowed is invalid". Using plain x64 keeps compatibility
with the whole 6.x line. Modern users on 6.3+ are still happy since
x64 still resolves correctly there.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 09:54:05 +02:00
b7de228bc9 v0.6 + v1.0: HMAC download URLs, launcher self-update, Inno Setup installer
Three deliverables shipped together so the next deployment cycle has a
clean distribution story.

(1) Auto-update of the launcher itself
---------------------------------------
- Models: RemoteManifest gains an optional `launcher` section
  (LauncherInfo: version, minRequired, download {url,size,sha256},
   releaseNotesUrl). Server-side, sign-manifest.php passes it through
  unchanged; admins edit versions.json with the new launcher entry +
  upload PSLauncher-X.Y.Z.exe to /builds/launcher/.
- Core: ILauncherSelfUpdater compares assembly version against
  manifest.launcher.version using the existing SemVer parser, and
  reuses DownloadManager (Range/resume/sha256 already proven on the
  game ZIPs) to download the new exe into
  %LocalAppData%/PSLauncher/selfupdate/.
- New project PSLauncher.Updater (~34 MB self-contained console exe):
  spawned by the main app with --target / --source / --pid / --launch.
  Waits for the main process to exit (or for the file lock to release),
  backs up the current exe to .bak, copies the new file in place, and
  restarts. .bak survives the swap so the user can roll back manually.
- App.csproj now declares Version=0.5.0 — currently shipped baseline.
  PSLauncher.App.csproj sets a fixed AssemblyVersion so reflection-based
  comparison works deterministically.
- MainViewModel.PromptLauncherUpdate: dialog after CheckForUpdates if
  the manifest advertises a newer launcher. Download with progress in
  the existing footer, then Application.Shutdown() so the Updater can
  do its job.

(2) Inno Setup installer
------------------------
installer/PSLauncher.iss + build-installer.ps1 produce a single
PSLauncher-Setup-X.Y.Z.exe (~80 MB) that installs into
Program Files\ASTERION VR\PSLauncher\, drops both PSLauncher.exe and
PSLauncher.Updater.exe side by side (the updater MUST live next to
the target), creates Start Menu + optional Desktop shortcuts, and
registers a clean uninstall entry. The user's %LocalAppData%
(license, logs, cache) is intentionally untouched on uninstall — same
license survives a reinstall.

build-installer.ps1 chains dotnet publish for both projects and ISCC
in one command. README explains the bump-version workflow.

(3) HMAC-signed download URLs
-----------------------------
- New PHP route GET /api/download-url/{version} (Authorization: Bearer
  <licenseKey> or ?key=...). Validates the license, checks
  download_entitlement_until >= minLicenseDate of the version, and
  returns a HMAC-signed URL (path|exp|licId, hash_hmac SHA-256, valid
  1 h) + sha256 + sizeBytes for verification.
- /builds/.htaccess routes every *.zip request to gate.php. gate.php
  validates exp, lic, sig (constant-time hash_equals), then streams
  the file with Range: support so the launcher's resume keeps working.
  Audit log gets a download_url_issued entry per request.
- Client-side wired transparently: LicenseService gains
  GetSignedDownloadUrlAsync(version) that GETs the endpoint with the
  decrypted license key from DPAPI. MainViewModel calls it before
  every download; if the endpoint returns 404/401/network-error, the
  client falls back to the manifest's plain download.url (graceful
  degradation for setups that haven't deployed gate.php yet).

Note on PHP streaming for 14 GB ZIPs: gate.php uses set_time_limit(0)
+ ignore_user_abort(true) + 1 MiB chunked fread with periodic flush.
Works on OVH mutualisé but holds a PHP-FPM slot for the duration. If
parallel downloads scale past a few clients, switch to
mod_xsendfile or migrate /builds/ to Cloudflare R2 with native
S3-presigned URLs and remove the gate entirely.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 09:38:13 +02:00