Add Linux Python CLI port (linux/ucm.py)#32
Open
captainzonks wants to merge 2 commits into
Open
Conversation
Author
|
Just pulled your latest "main," reviewing any necessary changes for this script real quick. UPDATE: No real changes. Rebasing now and adjusting file paths and some README text. |
Single-file Python port of UltimateCameraMod for Linux users who want to apply presets without running the WPF app under Wine/Proton. Lives under a new linux/ subdirectory so it stays isolated from the main C# tree. Scope (matches upstream output byte-for-byte on the camera archive entry): - All 7 official style presets (Heroic, Panoramic, Close-Up, Low Rider, Knee Cam, Dirt Cam, Survival), read from the existing ucm_presets/ folder at the repo root - Custom distance/height/right-offset + FoV delta, combat pullback, mount-height sync, steadycam toggle, bane/centered - Backup / restore / safe re-apply semantics (always rebuilds from a pristine vanilla snapshot, never compounds on a modded file) - Ports the camera-XML branch of ArchiveWriter (MatchCompressedSize + scattered/inflate/binary-search strategies). The newer HTML/CSS size-matching paths added for Center HUD target a different archive and are out of scope. Out of scope (GUI-only upstream features): UCM Quick / Fine Tune / God Mode, Sacred overrides, JSON / 0.paz / multi-format export, preset catalog browser, live camera previews, Center HUD. Direct C# -> Python mapping (sources under src/UltimateCameraMod.V3/): - Paz/NameHasher.cs -> compute_hash (Jenkins Lookup3) - Paz/StreamTransform.cs -> stream_apply (ChaCha20 20 rounds) - Paz/AssetCodec.cs -> build_codec_params / asset_decode - Paz/PamtReader.cs -> pamt_parse - Paz/ArchiveWriter.cs -> match_compressed_size + strategies - Services/CameraMod.cs -> install_with_mod_set, XML engine - Models/CameraRules.cs -> build_modifications + builders Requirements: - Python >= 3.10 - python-lz4 (only external dep; lazy-imported so `list` works without it) Steam auto-detect covers native ~/.local/share/Steam, ~/.steam/steam, and the Flatpak com.valvesoftware.Steam path, plus every library listed in each libraryfolders.vdf. --game-dir overrides. Vanilla backup defaults to $XDG_DATA_HOME/ultimate_camera_mod/ (i.e. ~/.local/share/ultimate_camera_mod/) so backups never pollute the repo. --backup-dir PATH overrides. Smoke-tested on Arch Linux + native Steam against Crimson Desert: extract(vanilla) -> apply(panoramic) -> extract -> restore -> extract yields a byte-identical vanilla XML dump (verified with diff -q). .gitignore: ignore ucm_backups/, linux/ucm_backups/, __pycache__/, *.pyc to keep runtime state out of the tree. Also adds linux/README.md with install instructions per distro, command examples, auto-detection rules, parity notes, and troubleshooting. Credit: 0xFitz / FitzDegenhub for the entire upstream project, camera rule engine, and preset definitions this port mirrors.
ffe0b79 to
fee7811
Compare
Replaces the previous regex-only heuristic in _validate_vanilla() with a fixture-based check that compares the live camera XML against bundled SHA-256 hashes of every known-vanilla playercamerapreset.xml. The fixtures themselves live upstream at docs/vanilla_xml/ and were added to main in v3.2 (commit 5e59ad0). Why: - The old heuristic only spotted UCM's own fingerprint (Fov="40" on Player_Basic_Default_Run / Runfast). It would happily accept files modded by other tools as "vanilla" and overwrite a legitimate backup. - Hash matching against a known-good fixture is a stronger signal and scales naturally as new game patches land — just hash the new fixture under docs/vanilla_xml/ and add it to the dict. How: 1. _normalize_for_hash() strips any number of leading UTF-8 BOMs and normalizes CRLF -> LF before hashing. This is needed because the live PAZ entry has 1 BOM + CRLF, while the upstream fixture files have 2 BOMs + LF — both decode to the same logical XML, so we collapse the byte differences before comparing. 2. _VANILLA_FIXTURE_SHA256 maps version label -> SHA-256 of the normalized fixture bytes. SHA-256s are baked into the script as constants so the single-file portability promise stays intact — the script does not need to read the docs/vanilla_xml/ folder at runtime. 3. _validate_vanilla() now: - hashes the normalized live XML and looks it up in the dict; a hit logs `Vanilla fixture match: <version>` and returns True - if no fixture hits but the legacy heuristic still says vanilla, logs a fallback WARNING and returns True (lets the script keep working on a brand-new game patch before fixtures are baked in) - otherwise returns False, refusing to overwrite the backup Currently bundled vanilla versions: - v1.02.xx: 2cca7a02587c868ba4e1380fb179444b3ef96bcd25a1019180fc7f591cbd1d0b - v1.03.00: 6664050d5af09a4d7910e3675dc5d9ca4f0210c8c4ea7a7845e5b7aad9ef330a Verified end-to-end on Arch Linux + native Steam against the live PAZ on a v1.03.00 install: - ensure_backup on a clean game logs `Vanilla fixture match: v1.03.00` - apply -> restore -> apply round-trip still produces a byte-identical vanilla XML extract - Empty-fixture-dict simulation triggers the heuristic fallback warning - Modded XML with no fixture hit triggers the refusal path with the existing TO FIX message Adds `import hashlib` to the import block. No new third-party deps. No CLI changes, no behavior change for users on a recognized game version — only the safety net is stronger.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A note from the contributor
Heads up, the following PR and work was done with Claude Code. I'm a hobby programmer, Claude came out and then allowed my ADHD brain to actually finish ideas and projects. I'm also a Linux gamer, and while I dual boot to Windows for some games, I wanted to get this mod over to Linux, as the performance has hit a good parity and I'd prefer to play Crimson Desert with my Linux boot.
I wanted to acknowledge that AI code can be problematic right now, but that I've managed to successfully use it so far for a lot of projects. The python script for this camera mod works well, I'm playing it now in panorama preset, and I wanted to give it back to the authors and Linux gamers out there.
Thanks for the work, hopefully this also helps the dozens of others who wanted this on Linux <3
— Matt
Summary
Adds a single-file Python port of UCM under a new
linux/subfolder so Linux players (native Steam or Proton) can apply the official style presets without running the WPF app under Wine. The port reuses the existingucm_presets/folder at the repo root and ports upstream C# logic module-for-module — no behavior changes on the Windows side.linux/ucm.py(~2,200 lines, pure Python)python-lz4What's in scope
Matches upstream output byte-for-byte on the camera archive entry:
ArchiveWriter.cssize-matching pipeline (scattered comments, inflate strategies, binary search)What's out of scope
UCM Quick / Fine Tune / God Mode editors, Sacred overrides, JSON /
0.paz/ multi-format export, preset catalog browser, live previews, import from mod manager packages. Users who want those stay on the Windows app — this is explicitly the "apply a preset on Linux without Wine" happy path.Direct C# → Python mapping
Sources under
src/UltimateCameraMod.V3/:linux/ucm.pyPaz/NameHasher.cscompute_hash(Jenkins Lookup3)Paz/StreamTransform.csstream_apply(ChaCha20 20 rounds)Paz/AssetCodec.csbuild_codec_params/asset_decodePaz/PamtReader.cspamt_parsePaz/ArchiveWriter.cs—MatchCompressedSizematch_compressed_size+ strategiesServices/CameraMod.csinstall_with_mod_set+ XML engineModels/CameraRules.csbuild_modifications+ buildersOnly the camera-XML branch of
ArchiveWriteris mirrored. The newerMatchCompressedSizeHtml/MatchCompressedSizeCsshelpers added for the Center HUD feature target archive0012and are out of scope for this Linux CLI port.Linux conventions
~/.local/share/Steam,~/.steam/steam, Flatpakcom.valvesoftware.Steam, plus every library listed in eachlibraryfolders.vdf.--game-diroverrides.$XDG_DATA_HOME/ultimate_camera_mod/(i.e.~/.local/share/ultimate_camera_mod/) so it never pollutes the repo.--backup-diroverrides.lz4is lazy-imported solistworks without the dep installed.Test plan
Tested on Arch Linux (kernel 6.19), native Steam install, Python 3.13,
python-lz44.4.5:python linux/ucm.py list— lists all 7 official presetspython linux/ucm.py extract --out /tmp/vanilla.xml— captures clean vanilla XMLpython linux/ucm.py apply --preset panoramic— installs, size-matching succeedspython linux/ucm.py extract --out /tmp/modded.xml— confirms preset-applied XMLpython linux/ucm.py restore— restores vanilladiff -qbetween pre-apply vanilla extract and post-restore extract — byte-identicalFiles
linux/ucm.py(new) — the portlinux/README.md(new) — install, usage, parity notes, troubleshooting.gitignore— ignoreucm_backups/,linux/ucm_backups/,__pycache__/,*.pycso Python runtime state stays out of the treeCredit
Full credit to 0xFitz/FitzDegenhub for the original UCM project — this port is a straight mirror of existing C# logic, not a rewrite. The entire upstream credit chain for PAZ/ChaCha20/LZ4 research and camera rule design applies unchanged; see the root README.
Risk / maintenance notes
linux/.ucmpresetformat; if that format changes upstream,load_presetwill need a corresponding update