Metadata-Version: 2.4
Name: pysage-tools
Version: 0.2.0
Summary: Python tools for the data formats of the SAGE engine (Battle for Middle-earth): ini game data, binary maps, replays, and UI movies.
Author-email: Clement Julia <clement.julia13@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/ClementJ18/pySAGE
Project-URL: Repository, https://github.com/ClementJ18/pySAGE
Project-URL: Issues, https://github.com/ClementJ18/pySAGE/issues
Keywords: sage,bfme,ini,parser,linter,map,replay,modding
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: End Users/Desktop
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Games/Entertainment
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: reversebox>=0.84; sys_platform == "win32"
Provides-Extra: ui
Requires-Dist: PyQt6>=6.6; extra == "ui"
Requires-Dist: pybig>=0.6; extra == "ui"
Requires-Dist: pillow>=10; extra == "ui"
Provides-Extra: lint-ui
Requires-Dist: PyQt6>=6.6; extra == "lint-ui"
Provides-Extra: wiki
Requires-Dist: mwclient>=0.11; extra == "wiki"
Requires-Dist: mwparserfromhell>=0.7; extra == "wiki"
Requires-Dist: PyQt6>=6.6; extra == "wiki"
Requires-Dist: pybig>=0.6; extra == "wiki"
Requires-Dist: keyring>=24; extra == "wiki"
Provides-Extra: edain-ui
Requires-Dist: PyQt6>=6.6; extra == "edain-ui"
Provides-Extra: asset-ui
Requires-Dist: PyQt6>=6.6; extra == "asset-ui"
Provides-Extra: apt
Requires-Dist: pybig>=0.6; extra == "apt"
Provides-Extra: w3d-view
Requires-Dist: PyQt6>=6.6; extra == "w3d-view"
Requires-Dist: PyOpenGL>=3.1; extra == "w3d-view"
Requires-Dist: numpy>=2; extra == "w3d-view"
Requires-Dist: pillow>=10; extra == "w3d-view"
Provides-Extra: patch
Requires-Dist: capstone>=5; extra == "patch"
Requires-Dist: pefile>=2023; extra == "patch"
Provides-Extra: all
Requires-Dist: pysage-tools[ui]; extra == "all"
Requires-Dist: pysage-tools[lint-ui]; extra == "all"
Requires-Dist: pysage-tools[wiki]; extra == "all"
Requires-Dist: pysage-tools[edain-ui]; extra == "all"
Requires-Dist: pysage-tools[asset-ui]; extra == "all"
Requires-Dist: pysage-tools[apt]; extra == "all"
Requires-Dist: pysage-tools[w3d-view]; extra == "all"
Requires-Dist: pysage-tools[patch]; extra == "all"
Dynamic: license-file

# pySAGE

A collection of Python tools for reading, editing, linting and visualising the data
formats of the **SAGE engine** - the engine behind *Command & Conquer: Generals* and
*The Battle for Middle-earth*. It grew out of an ini parser and now spans ini game data,
binary maps, replays, and UI, with a domain overlay for the Edain mod.

Everything installs as one package with optional extras (see [Install](#install)). Each
subproject has its own README with the details; this page is the map.

Can be downloaded pre-packaged from here: https://www.moddb.com/games/battle-for-middle-earth-ii/downloads/pysage-tools

## Projects

### Ini game data

| Project | What it is |
| --- | --- |
| [`sage_ini`](sage_ini/README.md) | The foundation: a typed, comment-preserving `.ini` parser, a whole-game loader, the cross-reference graph, and a lossless AST. Everything else builds on it. |
| [`sage_lint`](sage_lint/README.md) | Formatter and linter over `sage_ini` - canonical reprint plus judgment rules (dangling references, out-of-range values, duplicates, unused definitions) and meta-analysis. |

### Binary formats

| Project | What it is |
| --- | --- |
| [`sage_map`](sage_map/README.md) | Reader/writer for BFME `.map` files, plus a game-aware overlay that resolves script arguments and object references and lints maps. |
| [`sage_asset`](sage_asset/README.md) | Reader/writer, builder and combiner for `asset.dat`, the BFME2/RotWK art cache index - parse, build one from an art tree, merge a base with a mod overlay, and check it against the art on disk. |
| [`sage_w3d`](sage_w3d/README.md) | Lossless reader/writer for `.w3d` model files - meshes, materials, skeletons, uncompressed and compressed animation, HLOD level-of-detail data, collision boxes, and dazzles - plus a scene-assembly + PyQt6/OpenGL viewer (`sage-w3d view`, the `w3d-view` extra). |
| [`sage_cah`](sage_cah/README.md) | Reader/writer for `.cah`, the BFME2/RotWK Create-a-Hero file - identity, class, colors, powers, "bling" customization/attributes, GUID, and the validating checksum. |
| [`sage_replay`](sage_replay/README.md) | Reader for SAGE replay files (Generals `.rep`, BFME / BFME2 / RotWK) - the recorded order stream, decoded into build orders, APM and command timing. |
| [`sage_apt`](sage_apt/README.md) | Converter, viewer and editor for `.apt` UI movies (the Flash-derived format behind BFME's menus and HUD). **Work in progress**, not yet fully functional. |
| [`sage_patch`](sage_patch/README.md) | Binary patches for the ROTWK `game.dat` engine, with the reverse-engineering behind them - the shipped one raises the `CommandSet` button limit from 33 to 64, and `verify` re-checks a patched binary without a disassembler. |

### Domain overlays & apps

| Project | What it is |
| --- | --- |
| [`sage_mods.edain`](sage_mods/edain/README.md) | Edain-mod overlay: builds a faction ownership graph (spellbook → base → structures → units/heroes/upgrades) and renders, diffs or serves it. |
| [`sage_wiki`](sage_wiki/README.md) | Desktop tool that updates Edain wiki infoboxes from parsed game data through the MediaWiki API. |
| [`sage_ui`](sage_ui/README.md) | PyQt6 desktop browser for SAGE game data: load sources, search an object, see its resolved stats. |

### Shared

| Project | What it is |
| --- | --- |
| [`sage_utils`](sage_utils/README.md) | Helpers shared by more than one front end: the Qt-free data layer (sources, textures, views, the faction-graph types) and the shared Qt chrome. |

## Install

Requires Python ≥ 3.12. The project is **pySAGE**; on PyPI it is published as **`pysage-tools`**.

> **Note:** the bare `pysage` name on PyPI is an unrelated, abandoned messaging library last
> released in 2011, and `py-sage` is likewise taken by another project. `pip install pysage` will
> *not* get you this project - install `pysage-tools`.

```sh
pip install pysage-tools             # core library + linter
pip install "pysage-tools[ui]"       # + the PyQt6 desktop apps (sage-ui)
pip install "pysage-tools[wiki]"     # + the wiki updater
pip install "pysage-tools[edain-ui]" # + the Edain Linter desktop app
pip install "pysage-tools[apt]"      # + reading .const/.apt out of .big archives
pip install "pysage-tools[asset-ui]" # + the SAGE Asset desktop app (build/combine asset.dat)
pip install "pysage-tools[w3d-view]" # + the OpenGL model viewer (sage-w3d view)
pip install "pysage-tools[patch]"    # + the game.dat disassembly/RE tools
pip install "pysage-tools[all]"      # + everything above at once
```

From a clone, for development, swap the name for an editable install of the checkout:

```sh
pip install -e ".[ui]"
```

The extras (`ui`, `lint-ui`, `wiki`, `edain-ui`, `apt`, `asset-ui`, `w3d-view`, `patch`, and
`all` for the lot) pull in the optional dependencies each peripheral tool needs. The ini, map,
replay, asset, w3d, cah and patch layers are stdlib-only and always ship, so no extra is needed
to parse, lint or diff a map, to build and combine an asset.dat from the command line, or to
apply and verify a game.dat patch. The one non-optional dependency is `reversebox` on
Windows, the native RefPack compressor that makes saving large maps fast (its DLL is Windows-only,
so other platforms use the byte-identical pure-Python compressor).

Console scripts are installed for the CLI tools: `sage-ini`, `sage-lint`, `sage-edain`,
`sage-replay`, `sage-apt`, `sage-map`, `sage-save`, `sage-asset`, `sage-w3d`, `sage-cah`,
`sage-patch` (and the GUI scripts `sage-ui`,
`sage-wiki`, `sage-lint-ui`, `sage-edain-lint` - the Edain Linter, which combines the ini and map
checks in one window - and `sage-asset-ui`, the SAGE Asset builder/combiner window).

## Tests

```sh
pytest            # fast, data-free core suite
pytest --full     # + corpus acceptance gates and peripheral-package suites
```

## Contributing

Contributions are welcome - bug reports, fixes, new checks and rules, format coverage, and
documentation all help. See **[CONTRIBUTING.md](CONTRIBUTING.md)** to get set up and
**[CONVENTIONS.md](CONVENTIONS.md)** for the coding rules. AI-assisted contributions are
welcome too, with one expectation: you have read, understood, and can stand behind every
line you submit.
