OpenParable
Skip to content
← Back to skills

pst-codebase

Development

PalworldSaveTools (PST) architecture map — repo layout, entry chain, the palsav SAV<->GVAS<->JSON pipeline, the palworld_aio GUI structure, game data locations, and the custom pytest dynamic-import registry. Load FIRST whenever working anywhere in this repo so you orient on real structure, not guesses.

View on GitHub ↗
QUICK START

How to use this skill

Bring this guide into your coding agent with a prompt tailored to the tool you use.

  1. Open your project in Codex.
  2. Copy and paste the prompt below.
  3. Review files and risks before approving any action.
Prompt to paste
Inspect the Agent Skill at https://github.com/deafdudecomputers/PalworldSaveTools/blob/HEAD/.opencode/skills/pst-codebase/SKILL.md and all supporting files. Report provenance, permissions, scripts, dependencies, and security risks. Propose an exact installation into .agents/skills/pst-codebase, but wait for my approval before writing files or running scripts.

Copying this prompt does not install or run the skill. Review third-party files before use.

PalworldSaveTools (PST) — Codebase Map

Maintain this as the source of truth. When the layout changes, patch this skill. Project: PalworldSaveTools v2.0.0, GitHub deadafdudecomputers/PalworldSaveTools, author Pylar. MIT. PySide6 desktop GUI + CLI save tools for Palworld.

Active branch (cyrix's dev): upd/ProjRefactor. Python >=3.11. Package mgr: uv (workspace). venv at .venv.

Entry chain

  1. start.py (repo root) — creates/ensures uv venv at .venv, deletes stale uv.lock, then runs src/bootup.py with the venv python.
  2. src/bootup.py — splash screen + dependency/version checks; launches the GUI. GUI deps are optional: set env PST_NO_GUI=1 (or any truthy value) to skip PySide6 import — essential for headless/test/CI runs. PST_DEBUG=1 enables verbose bootup logging.
  3. src/palworld_aio/main.py — the actual PySide6 "All-in-One" app + ui/main_window.py.

Bootup path constants live in src/boot_paths.py: ROOT_DIR, CONFIG_DIR, RESOURCES_DIR, GUI_DIR.

Source layout (src/)

src/
  palsav/            # SERIALIZATION ENGINE (workspace pkg "palsav-flex")
    palsav/
      core.py            # decompress_sav_to_gvas / compress_gvas_to_sav
      gvas.py            # GvasFile.read/load/write/dump  (UE GVAS container)
      archive.py         # FArchiveReader / FArchiveWriter (UE binary archive)
      paltypes.py        # PALWORLD_CUSTOM_PROPERTIES, PALWORLD_TYPE_HINTS
      json_tools.py      # orjson-backed dump/load
      _cityhash.py       # city hash for save integrity
      compressor/        # oozlib (Oodle, default) + zlib backends, enums
      rawdata/           # PER-ENTITY raw decoders (character, group, base_camp,
                         #   map_data, map_model, item_container, work*, etc.)
      commands/          # auto_update, backup, convert, diag, resave_test,
                         #   roundtrip_validation  (CLI subcommands)
      cli.py             # `palsav` CLI entry  (SAV<->JSON)
  palworld_aio/      # THE GUI APP ("All-in-One Tools") — RESTRUCTURED into sub-packages
    main.py             # app entry  ->  ui/main_window.py
    constants.py        # global state hub (50+ importers, STAYS at root)
    utils.py            # shared helpers (STAYS at root, external importers)
    updater.py          # update checking
    managers/           # ★ business logic (operates on constants.loaded_level_json)
      save_manager, player_manager, guild_manager, base_manager,
      data_manager, func_manager, zone_manager, backup_manager
    inventory/          # ★ inventory + container subsystem
      inventory_manager, base_inventory_manager, standardized_container,
      dynamic_item_manager, dynamic_item, container_ownership
    editor/             # ★ pal + world editing engine
      pal_editor/ (17 modules + shim, partitioned from edit_pals.py), dialogs, worldoption_editor
    map/                # map generation
      map_generator
    ui/                 # views (sub-organized)
      main_window.py    # QMainWindow god-class (2144 lines)
      tabs/             # tools_tab, base_inventory_tab, inventory_tab, pal_editor_tab, map_tab, container_ids_tab
      dialogs/          # container_selector_dialog, player_item_dialog, player_pal_dialog, player_technology_dialog, skill_picker, tab_guide_dialog
      chrome/           # header_widget, sidebar_widget, results_widget, styled_combo, styles (ThemeManager), menus
      map_view/         # map_view (MapGraphicsView), map_markers, map_items, map_effects
    widgets/            # reusable Qt widgets (unchanged): search_panel, stats_panel, menu_popup, scrollable_context_menu, hover overlays, collapsible_splitter, loading_popup, tree_widgets
  palworld_toolsets/    # CLI tools: fix_host_save, game_pass_save_fix,
                        #   character_transfer, convert_generic, convertids,
                        #   modify_save, restore_map, slot_injector, xgp_save_extract
  palworld_xgp_import/  # Xbox Game Pass save import (gamepass_manager, container_types)
  palworld_coord/       # coordinate transforms
  (top-level) bootup.py, boot_paths.py, common.py, palobject.py,
            qt_imports.py, resource_resolver.py, path_setup.py,
            loading_manager.py, import_libs.py, i18n/__init__.py, nerd_btn.py

The palsav pipeline (the heart of the project)

SAV bytes <-> GVAS (UE struct) <-> Python dict/JSON

  • SAV->JSON: decompress_sav_to_gvas(raw) -> GvasFile.read(gvas, PALWORLD_TYPE_HINTS, PALWORLD_CUSTOM_PROPERTIES) -> .dump() -> json_tools.dump.
  • JSON->SAV: json_tools.load -> GvasFile.load(data) -> .write(PALWORLD_CUSTOM_PROPERTIES) -> compress_gvas_to_sav(gvas_bytes, compression_level).
  • PALWORLD_CUSTOM_PROPERTIES maps property type names -> (read_fn, write_fn) decoders. The rawdata/ modules implement the per-entity decode/encode for each Palworld property type (CharacterSaveParameterMap, GroupSaveDataMap, MapObjectSaveData, ItemContainerSaveData, work collections, base camps, map models, etc.).
  • Default compression is Oodle (compressor/oozlib.py, via pyoozle). zlib is the fallback (--library zlib).
  • orjson for speed; GC is disabled during parsing then re-enabled.

Resources & data

  • resources/game_data/*.json — static game reference: characters (pal data + passives), items, skills, boss_mapping, pal_exp_table, friendship, relic_data, reference_unlock_data, uidata, world, world_map_areas.
  • resources/i18n/*.json — 8 langs (en_US, zh_CN, de_DE, es_ES, fr_FR, ru_RU, ja_JP, ko_KR); resources/i18n/config.json is the lang registry. i18n loader at src/i18n/__init__.py.
  • src/data/configs/*.json — app presets: base_inventory_loadouts, equipment_loadouts, inventory_loadouts, passive_loadouts, zone_exclusions, plus config.json.
  • resources/tab_guide/ — in-app help. resources/readme/ — localized READMEs.

Testing (cyrix's preferred route)

Custom dynamic-import registry — tests NEVER hardcode import paths:

  • tests/test_registry.py -> MODULE_MAP maps logical name -> {import_as, parent} (or {installed: True} for installed pkgs like palsav).
  • Tests do: from tests.dynamic_importer import import_from then X = import_from('palsav.archive', 'FArchiveReader').
  • tests/conftest.py -> pytest_sessionstart injects parent dirs into sys.path.
  • To move a source module: update only MODULE_MAP. Zero test files change. This is the restructure-safe design of this repo — respect it.

Commands (run from repo root, venv active):

  • pytest — fast suite (~186 tests, ~0.6s; excludes @pytest.mark.slow).
  • pytest -m slow — only the save-file roundtrip (~40s, decompresses/recompresses a 3.2MB V1 beta save in tests/save_test/).
  • pytest -m "" — everything (~206 tests).
  • python scripts/scrs/test_interactively.py — menu runner (--quick / --all).

Structural audit auto-runs at session start via conftest (tests/harness/: file_pairer, graph_validator, resource_auditor, structural_cache). It gates the suite: file pairing + import graph + resource path integrity + relative-import resolution check (catches broken from .X/from ..X that lazy/in-function code hides from runtime tests). Flags:

  • --skip-structural — skip ALL structural checks.
  • --no-deep-audit — skip file pairing + import graph (keep resource audit).
  • --no-strict-paths — skip the deep AST resource-path audit.
  • --dump-structural — print full report without aborting.

Test layout: tests/unit/{core_logic,palsav_core,palworld_aio_tests,scripts}, tests/integration/{test_imports,test_palworld_save,test_resource_integrity}.

Repo composition (measured)

~2900 tracked files, but 2467 are in resources/game_data (mostly 2470 .webp icons — pals/npcs/items/passives/structures/technologies/elements/ui). Real source: 213 files in src/, 35 tests, 31 scripts. Total Python LOC ~50,643. Heaviest files: pal_editor/ (partitioned from edit_pals.py, now 17 modules + shim), func_manager.py (2657), base_inventory_tab.py (2606), update_game_data.py (2362), inventory_tab.py (2203), main_window.py (2144), map_tab.py (1916). palsav engine (archive.py 809) is dwarfed by the GUI layer.

Structural audit findings (known issues)

  1. STALE .gitattributes: references src/palsav/pyooz/ (deleted in 1e3eff6, renamed to palooz). The 15 SIMDe .h headers now under palooz are NOT marked linguist-vendored → GitHub C++ language stats inflated. Fix: update path to src/palsav/palooz/ooz/dep/ooz/simde/**.
  2. Icon storage split 3 ways: resources/assets/icons (16, app icons), resources/game_data/icons (2456, game entity icons), src/data/icon/ (2 .ico — paladius/xenolord, look misplaced/orphaned).
  3. src/ top-level flat namespace: 10 loose .py files (boot_paths, bootup, common, import_libs, loading_manager, nerd_btn, palobject, path_setup, qt_imports, resource_resolver) mixing boot/Qt/app concerns. import_libs.py is a star-import re-export hub (namespace pollution risk).
  4. .gitignore lib/ is global: could shadow a future legit lib dir. palsav/palsav/lib/ (compiled palooz .so, platform-specific) is gitignored — on-disk build output in the source tree.
  5. 9 .cmd wrappers in scripts/ duplicate the uv-venv bootstrap logic each (Windows launcher pattern).

Gotchas

  • The repo has a dist/ and build/ dir full of Nuitka/cx_Freeze build artifacts and a .venv — always exclude these when scanning files (find without filters drowns in noise). Scan only real source: exclude .git, .venv, dist, build, __pycache__, *.egg-info, .pytest_cache.
  • There is no AGENTS.md/CLAUDE.md/.cursorrules yet — repo conventions live in README + tests/README.md + this skill.
  • palsav (the engine) and palooz are installed packages; everything else under src/ is path-imported.
  • Save format versioning: tests target V1 beta saves. Mind format changes across Palworld updates (see commands/auto_update.py, commands/diag.py).

How to grow this knowledge

Build focused per-section skills as we actually work there, e.g. pst-save-pipeline (deep palsav internals), pst-pal-editor (pal_editor_tab + edit_pals + characters.json schema), pst-game-data-json (the resources/game_data schemas), pst-map-tab, pst-inventory. Patch THIS skill's layout map whenever a module moves or a new package appears.