Skip to content

Detect retail installations by archive presence, not executable name #330

Description

@bobtista

Detection-side complement to #315 — both turn on sentinel .big existence and should share constants.

Deliberately excluded: the *.exe glob in GameClientDetector.cs:566,742 that hides extensionless
binaries from client detection. That belongs with #325 and should consume its magic-byte
classification rather than add a second heuristic.

Problem

GameInstallation.HasValidExecutable (GameInstallation.cs:286) accepts only game.dat and
generals.exe, and it gates all detection on every platform: InstallationExtensions.ToDomain
(:86) calls SetPaths, discarding each platform detector's own HasGenerals/HasZeroHour.

So the canonical native macOS layout is undetectable. The engine's macOS deploy produces a flat
directory — engine binary, bundled dylibs, source-controlled Data//Window//Art/, and the user's
own .big archives merged into one tree, by default ~/TheSuperHackers/GeneralsZH/. That is not a
search root, not a name-matched child, and its binary is extensionless.

Alpha 5 launches the native client against the user's retail data, but the archive roots come from
GameInstallation.ZeroHourPath, so there is nothing to supply them.

generals.exe was only ever a proxy for "the archives are here." The archives are the direct signal
and the thing $CNC_ZH_INSTALLPATH actually needs.

Approach

All three parts are needed together:

  1. Identify retail data by archive presence instead of executable name.
    Zero Hour is any *zh.big; Generals is any of the canonical setaudio.big,
    audioenglish.big, english.big, gensec.big, ini.big, maps.big, music.big,
    shaders.big, speech.big, speechenglish.big, terrain.big, textures.big, w3d.big,
    window.big. Share constants with Validate retail archive roots before launch #315.
    These are retail filenames — verified present in a retail Zero Hour installation, which is
    what GenHub is identifying. Deliberately not derived from any engine build's loading code,
    so the rule stays valid for a user running a stock retail install with no community client.
    Check the directory root only, not recursively: Data/INI/INIZH.big is a duplicate shipped in
    the English, Chinese and Korean SKUs and must not be counted twice.
  2. MacOSInstallationDetector must test the candidate directory itself, not only name-matched
    immediate children.
  3. Keep GameInstallation.Fetch() in step — it is a second implementation of the same question.

Main hazard: a combined directory has both INI.big and INIZH.big, so both flags become true —
an unexercised state. ManifestProvider.cs:182,236 does HasZeroHour ? ZeroHour : Generals, so
Generals silently never gets a manifest. Also affects GameClientDetector.cs:58,87 (same directory
scanned twice), WindowsInstallationDetector.cs:209-269 (dedup does not collapse this case), and
InstallationPathResolver.cs:99-108.

Acceptance

  • A flat directory with INI.big and INIZH.big yields one installation with both paths set, and
    both manifests generated.
  • No duplicate GameClient entries when both paths are equal.
  • Steam, EA App, Wine, Lutris and CD/ISO detection unchanged on Windows and Linux.
  • ~/TheSuperHackers/GeneralsZH detected with no environment overrides, producing correct
    CNC_ZH_INSTALLPATH/CNC_GENERALS_INSTALLPATH including the mandatory trailing separator.

Own PR off development — not folded into the mac stack, since the ManifestProvider change is
user-visible on Windows retail and needs deliberate review.

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugSomething isn't working rightFixIs fixing somethingMajorSeverity: minor < major < criticalWIP

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions