Skip to content
VedaruPublic

About

Native Linux installer & updater for Kuro Games (Wuthering Waves, Punishing: Gray Raven) — no wine

Topics

Resources

Stars

4 stars

Watchers

1 watching

Forks

Repository files navigation

kuro

A native Linux installer and updater for Kuro Games titles — Wuthering Waves (鸣潮) and Punishing: Gray Raven (战双帕弥什). No wine, no launcher.exe, no hpatchz — one static Rust binary that talks to the official CDN directly and applies Kuro's KrDiff patches natively. It downloads and updates game files; the game itself is launched via Steam + GE-Proton (see below).

Inspired by ww-manager; the wine dependency is removed by applying patches via hdiffpatch-rs.

┌status─────────────────────────────────────────────┐
│game:    punishing-gray-raven                      │
│server:  global                                    │
│local:   4.7.0                                     │
│remote:  4.7.0                                     │
│up to date                                         │
└───────────────────────────────────────────────────┘
┌task───────────────────────────────────────────────┐
│task: sync                                         │
│files done: 2429   queued: 26511                   │
│[████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░]   9.8%      │
│  3.90/39.92 GiB  overall                         │
│[████████████████████████] 100.0%  25.5 MiB/…      │
└───────────────────────────────────────────────────┘

Features

  • Multi-game TUI — one binary manages WuWa and PGR side by side; installed games are auto-detected in your Steam libraries
  • status — local vs remote version per game/server, live from the CDN
  • predownload — stage the next update (krpdiff patches + full-file fallbacks) in the background, resumable
  • apply — merge KrDiff patches natively (no wine), MD5-verify every output, atomic .bak swap, version bump; full-file fallback if a merge fails
  • sync — parallel full-tree MD5 verify + repair of missing/corrupt files, resumable across restarts; followed by a stale-artifact sweep that only removes files the manifest has dropped (same directory and same extension as manifest entries). Live game state is never deleted: Client/Saved/** (the client's own resource/video channel, settings, saves, local storage, logs), the SDK / anti-cheat / crash-reporter payloads the client writes for itself, and anything in a directory the manifest doesn't ship into
  • persistent MD5 cache — a verified file is never hashed twice: entries in <game>/.kuro_cache/md5.json are keyed by manifest path and validated on (size, mtime) like make/git, so re-verify costs one stat per file. Pure I/O savings — delete .kuro_cache/ any time to force a full re-hash
  • install — from-zero client download for any supported game
  • quality packs (SD / HD / UHD) — WuWa's channel manifest carries the base paks plus one Client/Content/HD body and nothing else, so SD and UHD cannot come from sync. kuro pack <folder> <sd|hd|uhd> installs the tier from the launcher's own pack document (common + sd/hd/uhd, each with its own version, manifest and CDN) and then writes the two records the official launcher reads, so the tier counts as installed instead of as "repair game". --check verifies without downloading. --quality on install / sync stays the narrower thing: it picks which body those fetch from the channel. PGR (Unity) has no quality packs, so this is WuWa-only
  • checkout — CN ⇄ Bilibili channel switch for WuWa (diff-file swap + appId)
  • built-in launcher — kuro play starts the game itself. Since WuWa 3.7 the client refuses to boot without -krqlv=<SD|HD|UHD> on its command line, and that argument can only be given at exec time, so kuro owns the spawn: proton run, or umu-run when no Proton is installed, in a kuro-managed prefix under the install. kuro kill then finds the client again by its recorded process group when it hangs on exit
  • Resumable, MD5-verified downloads with per-file progress bars

Supported games & servers

Game CN 官服 Bilibili Global
Wuthering Waves (G152/G153) ✅ ✅ ✅
Punishing: Gray Raven (G143/G148) ✅ token — ✅

Tokens

WuWa's launcher tokens are public and stable (same ones ww-manager ships), so they're compiled in. PGR's tokens are runtime-only — they rotate, so they live in ~/.config/kuro/tokens.toml (or KURO_PGR_GLOBAL_TOKEN / KURO_PGR_CN_TOKEN), never in the binary:

[pgr]
global = "…token from the launcher's WebView2 storage on a Windows install…"
cn = "…same, from a CN launcher install's cache…"

When PGR status starts failing (stale token), re-copy it from a real launcher install's cache and update the file.

Install

From a release (Linux x86_64):

curl -L -o kuro https://github.com/vedaru/kuro/releases/latest/download/kuro
chmod +x kuro
sudo mv kuro /usr/local/bin/

Or build from source (Rust ≥ 1.75):

cargo build --release
cp target/release/kuro ~/.local/bin/

Usage

kuro                    # TUI — auto-detects installed games in Steam libraries
kuro <folder>...        # or point at game folders explicitly
kuro status <folder>    # CLI: print local/remote versions
kuro sync <folder> [--quality <sd|hd|uhd>]              # CLI: verify + repair
kuro install <wuwa|pgr> <cn|bilibili|global> <folder> [--quality <sd|hd|uhd>]
kuro quality <folder> [sd|hd|uhd]                       # CLI: which pack the client mounts
kuro pack [folder] <sd|hd|uhd> [--check]                # CLI: install the paks + register them

--quality on install / sync is the download side of that choice: only the named Client/Content/<TIER> body is fetched, together with the base paks and binaries every install needs. It defaults to everything the channel serves, and a body the channel does not serve is an error naming what it does serve — so "sd" on an HD-only channel tells you instead of quietly installing 42 GiB of HD. The saved preset (kuro quality <folder> hd) is what a plain sync follows afterwards, best-effort: if the channel currently serves no such body, the run falls back to the full manifest rather than failing. PGR has no quality packs at all, so there --quality is refused rather than ignored.

Getting a tier the channel does not serve is kuro pack's job. It is idempotent — a rerun verifies by MD5 and downloads only what is missing — and it writes the launcher records only after every file of the tier verifies, because a record for a pack the disk does not have is exactly what makes the launcher answer "repair game". The records are withdrawn before the first byte of a reinstall for the same reason: an interrupted run must read as "not installed", not as a broken install. common is reconciled against the channel: where the two disagree about a file (the 69 channel-SDK binaries, CN vs Bilibili), the channel wins and the pack's copy is left alone. The manifest is also checked against the indexFileMd5 the pack document publishes, and an offered version older than the installed one is refused rather than rolled back.

TUI keys

Key Action
Tab cycle focus: status → task → log (focused box is highlighted)
← / → switch game
↑ / ↓, PgUp / PgDn scroll the log (when focused)
r refresh status
d predownload update
a apply predownloaded update
s sync / repair files
c checkout server (CN ⇄ Bilibili)
i install a new game (w/p game · c/b/g server · f quality pack · t path · s Steam default)
Q quality preset (s/h/u pick · i install that tier's paks · Enter pick + install · saved for p)
h / ? help overlay
q quit

Running the game

kuro installs game files only and launches them itself — the official launcher doesn't run under wine, and dropping the game into Steam as a non-Steam shortcut loses the -krqlv= argument the client now demands:

kuro play <folder>              # saved preset / what is on disk / HD
kuro play <folder> --quality hd # this run only
kuro kill <folder>              # a client that hung on exit

It runs under Proton (GE or dwproton, whichever $HOME holds) and falls back to umu-run. Overrides: KURO_PROTON (a directory containing proton), KURO_PREFIX, STEAM_ROOT. Only WuWa takes a tier argument; PGR launches plain. First start is slow (shader compile); updates come from kuro, not Steam.

PGR ships ACE (AntiCheatExpert). Its kernel drivers can't load under Proton; most ACE games run fine with it absent, some refuse — no clean workaround.

How it works

kuro ──► prod[-cn]-alicdn-gamestarter.kurogame.com/launcher/game/<GID>/<appId>_<token>/index.json
          │  weighted CDN list + patchConfig (old→new version transitions)
          ▼
        resource.json ──► {dest, md5, size, chunkInfos}[]
          │                  · .krpdiff entries = native patch payloads (zstd)
          │                  · everything else = full-file fallbacks
          ▼
        download (parallel ranged GETs, per-chunk MD5) → verify → atomic swap
  • Incremental patches are KrDiff (Kuro's HDIFF19 variant) + zstd — applied natively by kuro-patch
  • Official merge keeps originals untouched until outputs are MD5-verified, then swaps with .bak recovery — kuro mirrors that flow
  • Full tree hashing streams in 1 MiB chunks (WuWa ships a 26 GB pak — reading whole files would OOM)

Crate layout

crates/
├── kuro-api    — launcher protocol types + HTTP client (game-agnostic)
├── kuro-patch  — KrDiff / HDiff native patch engine + streaming MD5
├── kuro-core   — GameManager: download / apply / sync / checkout / install
└── kuro-tui    — ratatui frontend (binary: `kuro`)

Adding a new Kuro title = one entry in kuro-api/src/config.rs; the whole pipeline is game-agnostic.

Development

cargo test                      # unit + integration tests
cargo build --release           # static-ish release binary
cargo run -p kuro-core --example pgr_proof   # live CDN smoke test (PGR)
cargo run -p kuro-core --example status -- <folder>   # live status, one install

Formatting and lints are gates, not advice — both CI workflows run them before anything is published, so a PR that fails either never reaches a release:

cargo fmt --check                                        # rustfmt.toml: width 120
cargo clippy --workspace --all-targets -- -D warnings

Neither uses --all: vendor/hdiffpatch-rs is a path dependency, not a workspace member, and its two dead-code warnings are not ours to fix.

Known limitations

  • PGR launcher tokens rotate and are issued by Kuro's private launcher SDK at runtime — kuro cannot fetch them (yet), so both PGR servers need a token you supply yourself (~/.config/kuro/tokens.toml, see Tokens). PGR CN's CDN host is derived from the shared launcher platform, not yet confirmed against a live session
  • ACE anti-cheat doesn't run under Proton (see above)
  • --quality on install / sync only covers the bodies a channel serves: predownload / apply still stage the whole patch set for a version, because a partially-applied patch would record a version the install does not actually hold. Tiers the channel does not serve come from kuro pack
  • kuro pack installs one tier at a time; nothing switches between installed tiers on disk. Each tier is a disjoint ~20–62 GiB body, so keeping more than one installed is a disk-space decision, and the -krqlv= argument is what picks which is mounted
  • PGR has no quality packs to choose from: it is a Unity title whose assets live under PGR_Data/ and whose client takes no -krqlv tier, so --quality is refused for it rather than pretended at
  • The CDN manifest describes the base client only, so it is not an inventory of a WuWa install: the game's own resource channel (Client/Saved/**, i.e. Video/Lang/Resource packs — not the Client/Content/Paks base paks) is fetched by the client itself and is invisible to Kuro
  • WuWa CN hotfixes (e.g. 3.6.0 → 3.6.1) have no krpdiff groups and no fromFolder: predownload stages the changed files as full files, and apply refuses to record the new version unless every target file is already on disk at its target hash — use sync for those, which downloads and verifies them directly

License

MIT — see LICENSE.

About

Native Linux installer & updater for Kuro Games (Wuthering Waves, Punishing: Gray Raven) — no wine

Topics

Resources

Stars

4 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages