pysage-tools 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pysage_tools-0.1.0/LICENSE +21 -0
- pysage_tools-0.1.0/PKG-INFO +127 -0
- pysage_tools-0.1.0/README.md +87 -0
- pysage_tools-0.1.0/pyproject.toml +184 -0
- pysage_tools-0.1.0/pysage_tools.egg-info/PKG-INFO +127 -0
- pysage_tools-0.1.0/pysage_tools.egg-info/SOURCES.txt +289 -0
- pysage_tools-0.1.0/pysage_tools.egg-info/dependency_links.txt +1 -0
- pysage_tools-0.1.0/pysage_tools.egg-info/entry_points.txt +14 -0
- pysage_tools-0.1.0/pysage_tools.egg-info/requires.txt +24 -0
- pysage_tools-0.1.0/pysage_tools.egg-info/top_level.txt +10 -0
- pysage_tools-0.1.0/sage_apt/__init__.py +19 -0
- pysage_tools-0.1.0/sage_apt/__main__.py +164 -0
- pysage_tools-0.1.0/sage_apt/actions.py +852 -0
- pysage_tools-0.1.0/sage_apt/aptfile.py +1907 -0
- pysage_tools-0.1.0/sage_apt/assets/editor.html +1329 -0
- pysage_tools-0.1.0/sage_apt/check.py +99 -0
- pysage_tools-0.1.0/sage_apt/editor.py +149 -0
- pysage_tools-0.1.0/sage_apt/flags.py +173 -0
- pysage_tools-0.1.0/sage_apt/geometry.py +107 -0
- pysage_tools-0.1.0/sage_apt/imagemap.py +69 -0
- pysage_tools-0.1.0/sage_apt/py.typed +0 -0
- pysage_tools-0.1.0/sage_apt/textures.py +187 -0
- pysage_tools-0.1.0/sage_apt/viewer.py +556 -0
- pysage_tools-0.1.0/sage_ini/__init__.py +64 -0
- pysage_tools-0.1.0/sage_ini/__main__.py +610 -0
- pysage_tools-0.1.0/sage_ini/brief.py +231 -0
- pysage_tools-0.1.0/sage_ini/diff.py +384 -0
- pysage_tools-0.1.0/sage_ini/loader.py +164 -0
- pysage_tools-0.1.0/sage_ini/macro_merge.py +298 -0
- pysage_tools-0.1.0/sage_ini/manifest.py +447 -0
- pysage_tools-0.1.0/sage_ini/merge.py +491 -0
- pysage_tools-0.1.0/sage_ini/model/__init__.py +38 -0
- pysage_tools-0.1.0/sage_ini/model/behaviors.py +3740 -0
- pysage_tools-0.1.0/sage_ini/model/data_blocks.py +2688 -0
- pysage_tools-0.1.0/sage_ini/model/definitions.py +16 -0
- pysage_tools-0.1.0/sage_ini/model/draw.py +430 -0
- pysage_tools-0.1.0/sage_ini/model/enums.py +2196 -0
- pysage_tools-0.1.0/sage_ini/model/fxlist.py +168 -0
- pysage_tools-0.1.0/sage_ini/model/game.py +328 -0
- pysage_tools-0.1.0/sage_ini/model/ini_objects.py +1397 -0
- pysage_tools-0.1.0/sage_ini/model/livingworld.py +351 -0
- pysage_tools-0.1.0/sage_ini/model/misc_blocks.py +480 -0
- pysage_tools-0.1.0/sage_ini/model/nuggets.py +244 -0
- pysage_tools-0.1.0/sage_ini/model/objects.py +621 -0
- pysage_tools-0.1.0/sage_ini/model/particles.py +183 -0
- pysage_tools-0.1.0/sage_ini/model/state.py +902 -0
- pysage_tools-0.1.0/sage_ini/model/types.py +1432 -0
- pysage_tools-0.1.0/sage_ini/model/ui.py +55 -0
- pysage_tools-0.1.0/sage_ini/model/xref.py +190 -0
- pysage_tools-0.1.0/sage_ini/modindex.py +114 -0
- pysage_tools-0.1.0/sage_ini/parser/__init__.py +47 -0
- pysage_tools-0.1.0/sage_ini/parser/ast.py +109 -0
- pysage_tools-0.1.0/sage_ini/parser/blockparser.py +347 -0
- pysage_tools-0.1.0/sage_ini/parser/diagnostics.py +73 -0
- pysage_tools-0.1.0/sage_ini/parser/io.py +90 -0
- pysage_tools-0.1.0/sage_ini/parser/keywords.py +118 -0
- pysage_tools-0.1.0/sage_ini/parser/lexer.py +97 -0
- pysage_tools-0.1.0/sage_ini/parser/location.py +32 -0
- pysage_tools-0.1.0/sage_ini/parser/printer.py +206 -0
- pysage_tools-0.1.0/sage_ini/player_diff.py +556 -0
- pysage_tools-0.1.0/sage_ini/primer.py +291 -0
- pysage_tools-0.1.0/sage_ini/py.typed +0 -0
- pysage_tools-0.1.0/sage_ini/skill_assets/bfme-ini/SKILL.md +94 -0
- pysage_tools-0.1.0/sage_ini/skill_install.py +18 -0
- pysage_tools-0.1.0/sage_ini/stats.py +221 -0
- pysage_tools-0.1.0/sage_ini/strings.py +149 -0
- pysage_tools-0.1.0/sage_ini/subsystems.py +257 -0
- pysage_tools-0.1.0/sage_ini/suggest.py +96 -0
- pysage_tools-0.1.0/sage_ini/walk.py +52 -0
- pysage_tools-0.1.0/sage_lint/__init__.py +3 -0
- pysage_tools-0.1.0/sage_lint/__main__.py +4 -0
- pysage_tools-0.1.0/sage_lint/analysis.py +115 -0
- pysage_tools-0.1.0/sage_lint/baseline.py +118 -0
- pysage_tools-0.1.0/sage_lint/cli.py +566 -0
- pysage_tools-0.1.0/sage_lint/commands/__init__.py +3 -0
- pysage_tools-0.1.0/sage_lint/commands/common.py +167 -0
- pysage_tools-0.1.0/sage_lint/commands/diff.py +112 -0
- pysage_tools-0.1.0/sage_lint/commands/duplicates.py +116 -0
- pysage_tools-0.1.0/sage_lint/commands/format.py +192 -0
- pysage_tools-0.1.0/sage_lint/commands/init.py +44 -0
- pysage_tools-0.1.0/sage_lint/commands/lint.py +552 -0
- pysage_tools-0.1.0/sage_lint/commands/manifest.py +44 -0
- pysage_tools-0.1.0/sage_lint/commands/serve.py +423 -0
- pysage_tools-0.1.0/sage_lint/config.py +260 -0
- pysage_tools-0.1.0/sage_lint/duplicates.py +292 -0
- pysage_tools-0.1.0/sage_lint/fixer.py +272 -0
- pysage_tools-0.1.0/sage_lint/formatter.py +153 -0
- pysage_tools-0.1.0/sage_lint/linter.py +390 -0
- pysage_tools-0.1.0/sage_lint/plugins/sublime/generate_syntax.py +132 -0
- pysage_tools-0.1.0/sage_lint/plugins/sublime/sage_lint.py +1985 -0
- pysage_tools-0.1.0/sage_lint/plugins/ui/__init__.py +4 -0
- pysage_tools-0.1.0/sage_lint/plugins/ui/__main__.py +5 -0
- pysage_tools-0.1.0/sage_lint/plugins/ui/app.py +24 -0
- pysage_tools-0.1.0/sage_lint/plugins/ui/icon.ico +0 -0
- pysage_tools-0.1.0/sage_lint/plugins/ui/runner.py +188 -0
- pysage_tools-0.1.0/sage_lint/plugins/ui/window.py +474 -0
- pysage_tools-0.1.0/sage_lint/ruleconfig.py +59 -0
- pysage_tools-0.1.0/sage_lint/rules/__init__.py +61 -0
- pysage_tools-0.1.0/sage_lint/rules/assets.py +203 -0
- pysage_tools-0.1.0/sage_lint/rules/base.py +55 -0
- pysage_tools-0.1.0/sage_lint/rules/commandset.py +114 -0
- pysage_tools-0.1.0/sage_lint/rules/definitions.py +150 -0
- pysage_tools-0.1.0/sage_lint/rules/macros.py +94 -0
- pysage_tools-0.1.0/sage_lint/rules/map_ini.py +51 -0
- pysage_tools-0.1.0/sage_lint/rules/modifier_fx.py +114 -0
- pysage_tools-0.1.0/sage_lint/rules/module_ops.py +204 -0
- pysage_tools-0.1.0/sage_lint/rules/module_refs.py +72 -0
- pysage_tools-0.1.0/sage_lint/rules/modules.py +55 -0
- pysage_tools-0.1.0/sage_lint/rules/references.py +185 -0
- pysage_tools-0.1.0/sage_lint/rules/respawn.py +85 -0
- pysage_tools-0.1.0/sage_lint/rules/schema.py +151 -0
- pysage_tools-0.1.0/sage_lint/rules/strings.py +152 -0
- pysage_tools-0.1.0/sage_lint/suppressions.py +86 -0
- pysage_tools-0.1.0/sage_map/__init__.py +93 -0
- pysage_tools-0.1.0/sage_map/__main__.py +107 -0
- pysage_tools-0.1.0/sage_map/assets/__init__.py +72 -0
- pysage_tools-0.1.0/sage_map/assets/asset_list.py +58 -0
- pysage_tools-0.1.0/sage_map/assets/blend_tile_data.py +441 -0
- pysage_tools-0.1.0/sage_map/assets/camera_animation_list.py +234 -0
- pysage_tools-0.1.0/sage_map/assets/castle_templates.py +171 -0
- pysage_tools-0.1.0/sage_map/assets/environment_data.py +77 -0
- pysage_tools-0.1.0/sage_map/assets/fog_settings.py +31 -0
- pysage_tools-0.1.0/sage_map/assets/global_lighting.py +233 -0
- pysage_tools-0.1.0/sage_map/assets/global_version.py +30 -0
- pysage_tools-0.1.0/sage_map/assets/height_map.py +118 -0
- pysage_tools-0.1.0/sage_map/assets/library_map_lists.py +73 -0
- pysage_tools-0.1.0/sage_map/assets/mission_hotspots.py +60 -0
- pysage_tools-0.1.0/sage_map/assets/mission_objectives.py +78 -0
- pysage_tools-0.1.0/sage_map/assets/mp_positions.py +95 -0
- pysage_tools-0.1.0/sage_map/assets/named_cameras.py +85 -0
- pysage_tools-0.1.0/sage_map/assets/object_list.py +83 -0
- pysage_tools-0.1.0/sage_map/assets/player_scripts.py +570 -0
- pysage_tools-0.1.0/sage_map/assets/polygon_triggers.py +200 -0
- pysage_tools-0.1.0/sage_map/assets/post_effects_chunk.py +154 -0
- pysage_tools-0.1.0/sage_map/assets/river_areas.py +143 -0
- pysage_tools-0.1.0/sage_map/assets/sides_list.py +254 -0
- pysage_tools-0.1.0/sage_map/assets/skipped_asset.py +31 -0
- pysage_tools-0.1.0/sage_map/assets/skybox_settings.py +43 -0
- pysage_tools-0.1.0/sage_map/assets/standing_water_area.py +105 -0
- pysage_tools-0.1.0/sage_map/assets/standing_waves_area.py +177 -0
- pysage_tools-0.1.0/sage_map/assets/teams.py +53 -0
- pysage_tools-0.1.0/sage_map/assets/trigger_areas.py +82 -0
- pysage_tools-0.1.0/sage_map/assets/water_settings.py +36 -0
- pysage_tools-0.1.0/sage_map/assets/waypoint_list.py +40 -0
- pysage_tools-0.1.0/sage_map/assets/world_info.py +32 -0
- pysage_tools-0.1.0/sage_map/checks/__init__.py +20 -0
- pysage_tools-0.1.0/sage_map/checks/height_utils.py +200 -0
- pysage_tools-0.1.0/sage_map/checks/linter.py +54 -0
- pysage_tools-0.1.0/sage_map/context.py +238 -0
- pysage_tools-0.1.0/sage_map/diff.py +810 -0
- pysage_tools-0.1.0/sage_map/linter.py +264 -0
- pysage_tools-0.1.0/sage_map/map.py +416 -0
- pysage_tools-0.1.0/sage_map/model.py +219 -0
- pysage_tools-0.1.0/sage_map/properties.py +39 -0
- pysage_tools-0.1.0/sage_map/py.typed +0 -0
- pysage_tools-0.1.0/sage_map/scripts.py +172 -0
- pysage_tools-0.1.0/sage_mods/__init__.py +7 -0
- pysage_tools-0.1.0/sage_mods/edain/__init__.py +59 -0
- pysage_tools-0.1.0/sage_mods/edain/__main__.py +391 -0
- pysage_tools-0.1.0/sage_mods/edain/diff.py +271 -0
- pysage_tools-0.1.0/sage_mods/edain/graph.py +70 -0
- pysage_tools-0.1.0/sage_mods/edain/icons/Angmar_frontpage.webp +0 -0
- pysage_tools-0.1.0/sage_mods/edain/icons/Arnor_frontpage.webp +0 -0
- pysage_tools-0.1.0/sage_mods/edain/icons/Belfalas_frontpage.webp +0 -0
- pysage_tools-0.1.0/sage_mods/edain/icons/Erebor_frontpage.webp +0 -0
- pysage_tools-0.1.0/sage_mods/edain/icons/Ered-Luin_frontpage.webp +0 -0
- pysage_tools-0.1.0/sage_mods/edain/icons/Gondor_frontpage.webp +0 -0
- pysage_tools-0.1.0/sage_mods/edain/icons/Imladris_frontpage.webp +0 -0
- pysage_tools-0.1.0/sage_mods/edain/icons/Iron_Hills_frontpage.webp +0 -0
- pysage_tools-0.1.0/sage_mods/edain/icons/Isengard_frontpage.webp +0 -0
- pysage_tools-0.1.0/sage_mods/edain/icons/Lothlorien_frontpage.webp +0 -0
- pysage_tools-0.1.0/sage_mods/edain/icons/Misty_Mountains_frontpage.webp +0 -0
- pysage_tools-0.1.0/sage_mods/edain/icons/Mordor_frontpage.webp +0 -0
- pysage_tools-0.1.0/sage_mods/edain/icons/Rohan_frontpage.webp +0 -0
- pysage_tools-0.1.0/sage_mods/edain/map_checks/__init__.py +67 -0
- pysage_tools-0.1.0/sage_mods/edain/map_checks/findings.py +122 -0
- pysage_tools-0.1.0/sage_mods/edain/map_checks/linter.py +353 -0
- pysage_tools-0.1.0/sage_mods/edain/map_checks/ui/__init__.py +5 -0
- pysage_tools-0.1.0/sage_mods/edain/map_checks/ui/__main__.py +5 -0
- pysage_tools-0.1.0/sage_mods/edain/map_checks/ui/app.py +29 -0
- pysage_tools-0.1.0/sage_mods/edain/map_checks/ui/icon.ico +0 -0
- pysage_tools-0.1.0/sage_mods/edain/map_checks/ui/runner.py +115 -0
- pysage_tools-0.1.0/sage_mods/edain/map_checks/ui/window.py +645 -0
- pysage_tools-0.1.0/sage_mods/edain/replay.py +463 -0
- pysage_tools-0.1.0/sage_mods/edain/report.py +315 -0
- pysage_tools-0.1.0/sage_mods/edain/schema.py +97 -0
- pysage_tools-0.1.0/sage_mods/edain/server.py +57 -0
- pysage_tools-0.1.0/sage_mods/edain/skill_assets/bfme-faction/SKILL.md +95 -0
- pysage_tools-0.1.0/sage_mods/edain/skill_install.py +19 -0
- pysage_tools-0.1.0/sage_mods/edain/ui/app.js +494 -0
- pysage_tools-0.1.0/sage_mods/edain/ui/index.html +34 -0
- pysage_tools-0.1.0/sage_mods/edain/ui/style.css +218 -0
- pysage_tools-0.1.0/sage_mods/py.typed +0 -0
- pysage_tools-0.1.0/sage_replay/__init__.py +90 -0
- pysage_tools-0.1.0/sage_replay/__main__.py +1099 -0
- pysage_tools-0.1.0/sage_replay/aggregate.py +3361 -0
- pysage_tools-0.1.0/sage_replay/build_orders.py +328 -0
- pysage_tools-0.1.0/sage_replay/cache.py +127 -0
- pysage_tools-0.1.0/sage_replay/coverage.py +605 -0
- pysage_tools-0.1.0/sage_replay/heroes.py +107 -0
- pysage_tools-0.1.0/sage_replay/ids.py +239 -0
- pysage_tools-0.1.0/sage_replay/narrate.py +726 -0
- pysage_tools-0.1.0/sage_replay/py.typed +0 -0
- pysage_tools-0.1.0/sage_replay/replay.py +942 -0
- pysage_tools-0.1.0/sage_replay/retarget.py +312 -0
- pysage_tools-0.1.0/sage_replay/serialize.py +172 -0
- pysage_tools-0.1.0/sage_replay/sidecar.py +216 -0
- pysage_tools-0.1.0/sage_replay/stats.py +519 -0
- pysage_tools-0.1.0/sage_replay/translated.py +701 -0
- pysage_tools-0.1.0/sage_replay/winner.py +222 -0
- pysage_tools-0.1.0/sage_save/__init__.py +246 -0
- pysage_tools-0.1.0/sage_save/__main__.py +654 -0
- pysage_tools-0.1.0/sage_save/chunks.py +1821 -0
- pysage_tools-0.1.0/sage_save/coverage.py +76 -0
- pysage_tools-0.1.0/sage_save/diagnose.py +257 -0
- pysage_tools-0.1.0/sage_save/edit.py +170 -0
- pysage_tools-0.1.0/sage_save/export.py +262 -0
- pysage_tools-0.1.0/sage_save/players.py +87 -0
- pysage_tools-0.1.0/sage_save/py.typed +0 -0
- pysage_tools-0.1.0/sage_save/reversing.py +128 -0
- pysage_tools-0.1.0/sage_save/save.py +160 -0
- pysage_tools-0.1.0/sage_save/serialize.py +169 -0
- pysage_tools-0.1.0/sage_save/xfer.py +97 -0
- pysage_tools-0.1.0/sage_save/xref.py +263 -0
- pysage_tools-0.1.0/sage_ui/__init__.py +3 -0
- pysage_tools-0.1.0/sage_ui/app.py +22 -0
- pysage_tools-0.1.0/sage_ui/browser.py +987 -0
- pysage_tools-0.1.0/sage_ui/extract.py +243 -0
- pysage_tools-0.1.0/sage_ui/icon.ico +0 -0
- pysage_tools-0.1.0/sage_ui/layout.py +11 -0
- pysage_tools-0.1.0/sage_ui/object_browser.py +386 -0
- pysage_tools-0.1.0/sage_ui/registry.py +106 -0
- pysage_tools-0.1.0/sage_ui/unit_panel.py +1263 -0
- pysage_tools-0.1.0/sage_utils/__init__.py +4 -0
- pysage_tools-0.1.0/sage_utils/assets/active_overlay.png +0 -0
- pysage_tools-0.1.0/sage_utils/assets/background.png +0 -0
- pysage_tools-0.1.0/sage_utils/assets/passive_overlay.png +0 -0
- pysage_tools-0.1.0/sage_utils/cli.py +110 -0
- pysage_tools-0.1.0/sage_utils/clock.py +18 -0
- pysage_tools-0.1.0/sage_utils/config.py +40 -0
- pysage_tools-0.1.0/sage_utils/extras.py +39 -0
- pysage_tools-0.1.0/sage_utils/factiongraph/__init__.py +77 -0
- pysage_tools-0.1.0/sage_utils/factiongraph/bases.py +149 -0
- pysage_tools-0.1.0/sage_utils/factiongraph/graph.py +637 -0
- pysage_tools-0.1.0/sage_utils/factiongraph/model.py +268 -0
- pysage_tools-0.1.0/sage_utils/factiongraph/powers.py +135 -0
- pysage_tools-0.1.0/sage_utils/factiongraph/profile.py +123 -0
- pysage_tools-0.1.0/sage_utils/findings.py +187 -0
- pysage_tools-0.1.0/sage_utils/gameroot.py +46 -0
- pysage_tools-0.1.0/sage_utils/refpack.py +354 -0
- pysage_tools-0.1.0/sage_utils/skill.py +41 -0
- pysage_tools-0.1.0/sage_utils/sources.py +203 -0
- pysage_tools-0.1.0/sage_utils/stream.py +410 -0
- pysage_tools-0.1.0/sage_utils/styles.py +106 -0
- pysage_tools-0.1.0/sage_utils/textures.py +212 -0
- pysage_tools-0.1.0/sage_utils/views/__init__.py +130 -0
- pysage_tools-0.1.0/sage_utils/views/base.py +52 -0
- pysage_tools-0.1.0/sage_utils/views/buttons.py +194 -0
- pysage_tools-0.1.0/sage_utils/views/factions.py +169 -0
- pysage_tools-0.1.0/sage_utils/views/objects.py +55 -0
- pysage_tools-0.1.0/sage_utils/views/powers.py +209 -0
- pysage_tools-0.1.0/sage_utils/views/text.py +113 -0
- pysage_tools-0.1.0/sage_utils/views/weapons.py +334 -0
- pysage_tools-0.1.0/sage_utils/widgets.py +481 -0
- pysage_tools-0.1.0/sage_wiki/__init__.py +4 -0
- pysage_tools-0.1.0/sage_wiki/app.py +286 -0
- pysage_tools-0.1.0/sage_wiki/archetypes.py +205 -0
- pysage_tools-0.1.0/sage_wiki/armorsets.py +94 -0
- pysage_tools-0.1.0/sage_wiki/cards/__init__.py +4 -0
- pysage_tools-0.1.0/sage_wiki/cards/category_card.py +278 -0
- pysage_tools-0.1.0/sage_wiki/cards/diff_card.py +428 -0
- pysage_tools-0.1.0/sage_wiki/cards/images_card.py +580 -0
- pysage_tools-0.1.0/sage_wiki/cards/pagegen_card.py +204 -0
- pysage_tools-0.1.0/sage_wiki/cards/tools_card.py +334 -0
- pysage_tools-0.1.0/sage_wiki/credentials.py +61 -0
- pysage_tools-0.1.0/sage_wiki/diff.py +137 -0
- pysage_tools-0.1.0/sage_wiki/icon.ico +0 -0
- pysage_tools-0.1.0/sage_wiki/images.py +166 -0
- pysage_tools-0.1.0/sage_wiki/infobox.py +124 -0
- pysage_tools-0.1.0/sage_wiki/launcher.py +21 -0
- pysage_tools-0.1.0/sage_wiki/links.py +93 -0
- pysage_tools-0.1.0/sage_wiki/mapping.py +554 -0
- pysage_tools-0.1.0/sage_wiki/meta.py +7 -0
- pysage_tools-0.1.0/sage_wiki/names.py +15 -0
- pysage_tools-0.1.0/sage_wiki/pagegen.py +682 -0
- pysage_tools-0.1.0/sage_wiki/versions.py +35 -0
- pysage_tools-0.1.0/sage_wiki/weapons.py +105 -0
- pysage_tools-0.1.0/sage_wiki/wiki/__init__.py +5 -0
- pysage_tools-0.1.0/sage_wiki/wiki/client.py +217 -0
- pysage_tools-0.1.0/setup.cfg +4 -0
- pysage_tools-0.1.0/tests/test_packaging.py +82 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Clement Julia
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pysage-tools
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python tools for the data formats of the SAGE engine (Battle for Middle-earth): ini game data, binary maps, replays, and UI movies.
|
|
5
|
+
Author-email: Clement Julia <clement.julia13@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/ClementJ18/pySAGE
|
|
8
|
+
Project-URL: Repository, https://github.com/ClementJ18/pySAGE
|
|
9
|
+
Project-URL: Issues, https://github.com/ClementJ18/pySAGE/issues
|
|
10
|
+
Keywords: sage,bfme,ini,parser,linter,map,replay,modding
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
17
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
18
|
+
Classifier: Topic :: Games/Entertainment
|
|
19
|
+
Requires-Python: >=3.12
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: reversebox>=0.84; sys_platform == "win32"
|
|
23
|
+
Provides-Extra: ui
|
|
24
|
+
Requires-Dist: PyQt6>=6.6; extra == "ui"
|
|
25
|
+
Requires-Dist: pybig>=0.6; extra == "ui"
|
|
26
|
+
Requires-Dist: pillow>=10; extra == "ui"
|
|
27
|
+
Provides-Extra: lint-ui
|
|
28
|
+
Requires-Dist: PyQt6>=6.6; extra == "lint-ui"
|
|
29
|
+
Provides-Extra: wiki
|
|
30
|
+
Requires-Dist: mwclient>=0.11; extra == "wiki"
|
|
31
|
+
Requires-Dist: mwparserfromhell>=0.7; extra == "wiki"
|
|
32
|
+
Requires-Dist: PyQt6>=6.6; extra == "wiki"
|
|
33
|
+
Requires-Dist: pybig>=0.6; extra == "wiki"
|
|
34
|
+
Requires-Dist: keyring>=24; extra == "wiki"
|
|
35
|
+
Provides-Extra: edain-ui
|
|
36
|
+
Requires-Dist: PyQt6>=6.6; extra == "edain-ui"
|
|
37
|
+
Provides-Extra: apt
|
|
38
|
+
Requires-Dist: pybig>=0.6; extra == "apt"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
# pySAGE
|
|
42
|
+
|
|
43
|
+
A collection of Python tools for reading, editing, linting and visualising the data
|
|
44
|
+
formats of the **SAGE engine** - the engine behind *Command & Conquer: Generals* and
|
|
45
|
+
*The Battle for Middle-earth*. It grew out of an ini parser and now spans ini game data,
|
|
46
|
+
binary maps, replays, and UI, with a domain overlay for the Edain mod.
|
|
47
|
+
|
|
48
|
+
Everything installs as one package with optional extras (see [Install](#install)). Each
|
|
49
|
+
subproject has its own README with the details; this page is the map.
|
|
50
|
+
|
|
51
|
+
## Projects
|
|
52
|
+
|
|
53
|
+
### Ini game data
|
|
54
|
+
|
|
55
|
+
| Project | What it is |
|
|
56
|
+
| --- | --- |
|
|
57
|
+
| [`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. |
|
|
58
|
+
| [`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. |
|
|
59
|
+
|
|
60
|
+
### Binary formats
|
|
61
|
+
|
|
62
|
+
| Project | What it is |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| [`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. |
|
|
65
|
+
| [`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. |
|
|
66
|
+
| [`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. |
|
|
67
|
+
|
|
68
|
+
### Domain overlays & apps
|
|
69
|
+
|
|
70
|
+
| Project | What it is |
|
|
71
|
+
| --- | --- |
|
|
72
|
+
| [`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. |
|
|
73
|
+
| [`sage_wiki`](sage_wiki/README.md) | Desktop tool that updates Edain wiki infoboxes from parsed game data through the MediaWiki API. |
|
|
74
|
+
| [`sage_ui`](sage_ui/README.md) | PyQt6 desktop browser for SAGE game data: load sources, search an object, see its resolved stats. |
|
|
75
|
+
|
|
76
|
+
### Shared
|
|
77
|
+
|
|
78
|
+
| Project | What it is |
|
|
79
|
+
| --- | --- |
|
|
80
|
+
| [`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. |
|
|
81
|
+
|
|
82
|
+
## Install
|
|
83
|
+
|
|
84
|
+
Requires Python ≥ 3.12. The project is **pySAGE**; on PyPI it is published as **`pysage-tools`**.
|
|
85
|
+
|
|
86
|
+
> **Note:** the bare `pysage` name on PyPI is an unrelated, abandoned messaging library last
|
|
87
|
+
> released in 2011, and `py-sage` is likewise taken by another project. `pip install pysage` will
|
|
88
|
+
> *not* get you this project - install `pysage-tools`.
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
pip install pysage-tools # core library + linter
|
|
92
|
+
pip install "pysage-tools[ui]" # + the PyQt6 desktop apps (sage-ui)
|
|
93
|
+
pip install "pysage-tools[wiki]" # + the wiki updater
|
|
94
|
+
pip install "pysage-tools[edain-ui]" # + the Edain Linter desktop app
|
|
95
|
+
pip install "pysage-tools[apt]" # + reading .const/.apt out of .big archives
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
From a clone, for development, swap the name for an editable install of the checkout:
|
|
99
|
+
|
|
100
|
+
```sh
|
|
101
|
+
pip install -e ".[ui]"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The extras (`ui`, `lint-ui`, `wiki`, `edain-ui`, `apt`) pull in the optional dependencies each
|
|
105
|
+
peripheral tool needs. The ini, map and replay layers are stdlib-only and always ship, so no
|
|
106
|
+
extra is needed to parse, lint or diff a map. The one non-optional dependency is `reversebox` on
|
|
107
|
+
Windows, the native RefPack compressor that makes saving large maps fast (its DLL is Windows-only,
|
|
108
|
+
so other platforms use the byte-identical pure-Python compressor).
|
|
109
|
+
|
|
110
|
+
Console scripts are installed for the CLI tools: `sage-ini`, `sage-lint`, `sage-edain`,
|
|
111
|
+
`sage-replay`, `sage-apt` (and the GUI scripts `sage-ui`, `sage-wiki`, `sage-lint-ui`,
|
|
112
|
+
`sage-edain-lint` - the Edain Linter, which combines the ini and map checks in one window).
|
|
113
|
+
|
|
114
|
+
## Tests
|
|
115
|
+
|
|
116
|
+
```sh
|
|
117
|
+
pytest # fast, data-free core suite
|
|
118
|
+
pytest --full # + corpus acceptance gates and peripheral-package suites
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Contributing
|
|
122
|
+
|
|
123
|
+
Contributions are welcome - bug reports, fixes, new checks and rules, format coverage, and
|
|
124
|
+
documentation all help. See **[CONTRIBUTING.md](CONTRIBUTING.md)** to get set up and
|
|
125
|
+
**[CONVENTIONS.md](CONVENTIONS.md)** for the coding rules. AI-assisted contributions are
|
|
126
|
+
welcome too, with one expectation: you have read, understood, and can stand behind every
|
|
127
|
+
line you submit.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# pySAGE
|
|
2
|
+
|
|
3
|
+
A collection of Python tools for reading, editing, linting and visualising the data
|
|
4
|
+
formats of the **SAGE engine** - the engine behind *Command & Conquer: Generals* and
|
|
5
|
+
*The Battle for Middle-earth*. It grew out of an ini parser and now spans ini game data,
|
|
6
|
+
binary maps, replays, and UI, with a domain overlay for the Edain mod.
|
|
7
|
+
|
|
8
|
+
Everything installs as one package with optional extras (see [Install](#install)). Each
|
|
9
|
+
subproject has its own README with the details; this page is the map.
|
|
10
|
+
|
|
11
|
+
## Projects
|
|
12
|
+
|
|
13
|
+
### Ini game data
|
|
14
|
+
|
|
15
|
+
| Project | What it is |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| [`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. |
|
|
18
|
+
| [`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. |
|
|
19
|
+
|
|
20
|
+
### Binary formats
|
|
21
|
+
|
|
22
|
+
| Project | What it is |
|
|
23
|
+
| --- | --- |
|
|
24
|
+
| [`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. |
|
|
25
|
+
| [`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. |
|
|
26
|
+
| [`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. |
|
|
27
|
+
|
|
28
|
+
### Domain overlays & apps
|
|
29
|
+
|
|
30
|
+
| Project | What it is |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| [`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. |
|
|
33
|
+
| [`sage_wiki`](sage_wiki/README.md) | Desktop tool that updates Edain wiki infoboxes from parsed game data through the MediaWiki API. |
|
|
34
|
+
| [`sage_ui`](sage_ui/README.md) | PyQt6 desktop browser for SAGE game data: load sources, search an object, see its resolved stats. |
|
|
35
|
+
|
|
36
|
+
### Shared
|
|
37
|
+
|
|
38
|
+
| Project | What it is |
|
|
39
|
+
| --- | --- |
|
|
40
|
+
| [`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. |
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
Requires Python ≥ 3.12. The project is **pySAGE**; on PyPI it is published as **`pysage-tools`**.
|
|
45
|
+
|
|
46
|
+
> **Note:** the bare `pysage` name on PyPI is an unrelated, abandoned messaging library last
|
|
47
|
+
> released in 2011, and `py-sage` is likewise taken by another project. `pip install pysage` will
|
|
48
|
+
> *not* get you this project - install `pysage-tools`.
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
pip install pysage-tools # core library + linter
|
|
52
|
+
pip install "pysage-tools[ui]" # + the PyQt6 desktop apps (sage-ui)
|
|
53
|
+
pip install "pysage-tools[wiki]" # + the wiki updater
|
|
54
|
+
pip install "pysage-tools[edain-ui]" # + the Edain Linter desktop app
|
|
55
|
+
pip install "pysage-tools[apt]" # + reading .const/.apt out of .big archives
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
From a clone, for development, swap the name for an editable install of the checkout:
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
pip install -e ".[ui]"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The extras (`ui`, `lint-ui`, `wiki`, `edain-ui`, `apt`) pull in the optional dependencies each
|
|
65
|
+
peripheral tool needs. The ini, map and replay layers are stdlib-only and always ship, so no
|
|
66
|
+
extra is needed to parse, lint or diff a map. The one non-optional dependency is `reversebox` on
|
|
67
|
+
Windows, the native RefPack compressor that makes saving large maps fast (its DLL is Windows-only,
|
|
68
|
+
so other platforms use the byte-identical pure-Python compressor).
|
|
69
|
+
|
|
70
|
+
Console scripts are installed for the CLI tools: `sage-ini`, `sage-lint`, `sage-edain`,
|
|
71
|
+
`sage-replay`, `sage-apt` (and the GUI scripts `sage-ui`, `sage-wiki`, `sage-lint-ui`,
|
|
72
|
+
`sage-edain-lint` - the Edain Linter, which combines the ini and map checks in one window).
|
|
73
|
+
|
|
74
|
+
## Tests
|
|
75
|
+
|
|
76
|
+
```sh
|
|
77
|
+
pytest # fast, data-free core suite
|
|
78
|
+
pytest --full # + corpus acceptance gates and peripheral-package suites
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Contributing
|
|
82
|
+
|
|
83
|
+
Contributions are welcome - bug reports, fixes, new checks and rules, format coverage, and
|
|
84
|
+
documentation all help. See **[CONTRIBUTING.md](CONTRIBUTING.md)** to get set up and
|
|
85
|
+
**[CONVENTIONS.md](CONVENTIONS.md)** for the coding rules. AI-assisted contributions are
|
|
86
|
+
welcome too, with one expectation: you have read, understood, and can stand behind every
|
|
87
|
+
line you submit.
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"] # >=77 for the PEP 639 SPDX `license` field below
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pysage-tools"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python tools for the data formats of the SAGE engine (Battle for Middle-earth): ini game data, binary maps, replays, and UI movies."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [
|
|
14
|
+
{ name = "Clement Julia", email = "clement.julia13@gmail.com" },
|
|
15
|
+
]
|
|
16
|
+
keywords = ["sage", "bfme", "ini", "parser", "linter", "map", "replay", "modding"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Intended Audience :: End Users/Desktop",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Topic :: Software Development :: Libraries",
|
|
24
|
+
"Topic :: Software Development :: Quality Assurance",
|
|
25
|
+
"Topic :: Games/Entertainment",
|
|
26
|
+
]
|
|
27
|
+
dependencies = [
|
|
28
|
+
# Native RefPack compressor for map saving (`sage_utils.refpack`). reversebox ships a
|
|
29
|
+
# Windows-only DLL, so the marker installs it exactly where it can load; every other
|
|
30
|
+
# platform resolves to no dependencies at all and uses the pure-Python compressor.
|
|
31
|
+
"reversebox>=0.84; sys_platform == 'win32'",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://github.com/ClementJ18/pySAGE"
|
|
36
|
+
Repository = "https://github.com/ClementJ18/pySAGE"
|
|
37
|
+
Issues = "https://github.com/ClementJ18/pySAGE/issues"
|
|
38
|
+
|
|
39
|
+
[project.scripts]
|
|
40
|
+
sage-lint = "sage_lint.cli:main"
|
|
41
|
+
sage-ini = "sage_ini.__main__:main"
|
|
42
|
+
sage-edain = "sage_mods.edain.__main__:main"
|
|
43
|
+
sage-replay = "sage_replay.__main__:main"
|
|
44
|
+
sage-apt = "sage_apt.__main__:main"
|
|
45
|
+
sage-save = "sage_save.__main__:main"
|
|
46
|
+
sage-map = "sage_map.__main__:main"
|
|
47
|
+
|
|
48
|
+
# Desktop apps; each needs its extra installed (ui / wiki / lint-ui / edain-ui).
|
|
49
|
+
[project.gui-scripts]
|
|
50
|
+
sage-ui = "sage_ui.app:main"
|
|
51
|
+
sage-wiki = "sage_wiki.launcher:main" # guards the [wiki] extra; app.py imports Qt at module scope
|
|
52
|
+
sage-lint-ui = "sage_lint.plugins.ui.app:main"
|
|
53
|
+
sage-edain-lint = "sage_mods.edain.map_checks.ui.app:main"
|
|
54
|
+
|
|
55
|
+
[project.optional-dependencies]
|
|
56
|
+
ui = [
|
|
57
|
+
"PyQt6>=6.6",
|
|
58
|
+
"pybig>=0.6",
|
|
59
|
+
"pillow>=10",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
lint-ui = [
|
|
63
|
+
"PyQt6>=6.6",
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
wiki = [
|
|
67
|
+
"mwclient>=0.11",
|
|
68
|
+
"mwparserfromhell>=0.7",
|
|
69
|
+
"PyQt6>=6.6",
|
|
70
|
+
"pybig>=0.6",
|
|
71
|
+
"keyring>=24",
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
edain-ui = [
|
|
75
|
+
"PyQt6>=6.6",
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
apt = [
|
|
79
|
+
"pybig>=0.6", # reading a .const (or .apt) out of a .big archive; the core stays stdlib-only
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
[dependency-groups]
|
|
83
|
+
dev = [
|
|
84
|
+
"pytest",
|
|
85
|
+
"ruff",
|
|
86
|
+
"mypy",
|
|
87
|
+
"pyinstaller>=6", # builds the desktop .exe bundles (the *.spec files); not a runtime dep
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
[tool.setuptools.packages.find]
|
|
91
|
+
include = ["sage_apt*", "sage_ini*", "sage_lint*", "sage_map*", "sage_replay*", "sage_save*", "sage_utils*", "sage_wiki*", "sage_ui*", "sage_mods*"]
|
|
92
|
+
|
|
93
|
+
[tool.setuptools.package-data]
|
|
94
|
+
# Anything a package opens at runtime must be listed here, or it is absent from the wheel. The
|
|
95
|
+
# loaders degrade silently (a missing asset returns None), so an omission surfaces as a feature
|
|
96
|
+
# quietly not working rather than an error; tests/test_packaging.py asserts every runtime asset
|
|
97
|
+
# below is shipped.
|
|
98
|
+
sage_apt = ["assets/*.html", "py.typed"]
|
|
99
|
+
sage_ini = ["py.typed", "skill_assets/**/*"]
|
|
100
|
+
sage_map = ["py.typed"]
|
|
101
|
+
sage_save = ["py.typed"]
|
|
102
|
+
sage_replay = ["py.typed"]
|
|
103
|
+
sage_ui = ["*.ico"]
|
|
104
|
+
sage_wiki = ["*.ico"]
|
|
105
|
+
sage_utils = ["assets/*.png"] # portrait background + overlays; sage_utils/textures.py
|
|
106
|
+
"sage_lint.plugins.ui" = ["*.ico"]
|
|
107
|
+
sage_mods = ["py.typed"] # PEP 561 marker belongs on the top-level package, not sage_mods.edain
|
|
108
|
+
"sage_mods.edain" = [
|
|
109
|
+
"ui/*",
|
|
110
|
+
"skill_assets/**/*",
|
|
111
|
+
"map_checks/ui/*.ico",
|
|
112
|
+
"icons/*.webp", # faction emblems; the public ICONS_DIR / FACTION_ICONS in edain/replay.py
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
[tool.pytest.ini_options]
|
|
116
|
+
testpaths = ["tests"]
|
|
117
|
+
pythonpath = ["."]
|
|
118
|
+
markers = [
|
|
119
|
+
"full: part of the full suite (corpus gates + peripheral packages); run with --full",
|
|
120
|
+
]
|
|
121
|
+
|
|
122
|
+
[tool.ruff]
|
|
123
|
+
target-version = "py312" # matches requires-python, so pyupgrade cannot suggest 3.13-only syntax
|
|
124
|
+
line-length = 100
|
|
125
|
+
extend-exclude = [
|
|
126
|
+
"data", # game-data corpus
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
[tool.ruff.lint]
|
|
130
|
+
select = [
|
|
131
|
+
"E", # pycodestyle errors (E722: no bare `except:`)
|
|
132
|
+
"W", # pycodestyle warnings
|
|
133
|
+
"F", # pyflakes
|
|
134
|
+
"I", # isort - deterministic import order
|
|
135
|
+
"B", # bugbear (B009/B010: no getattr/setattr with constant attribute names)
|
|
136
|
+
"UP", # pyupgrade
|
|
137
|
+
"BLE", # blind-except: no `except Exception:` swallowing
|
|
138
|
+
"PLC0415", # all imports at module top level; no function-local imports
|
|
139
|
+
]
|
|
140
|
+
|
|
141
|
+
[tool.ruff.lint.per-file-ignores]
|
|
142
|
+
"sage_ini/model/behaviors.py" = ["F821"]
|
|
143
|
+
"sage_ini/model/nuggets.py" = ["F821"]
|
|
144
|
+
"sage_ini/model/data_blocks.py" = ["F821"]
|
|
145
|
+
# The embedded single-page HTML/CSS/JS template carries lines longer than the Python limit.
|
|
146
|
+
"sage_apt/viewer.py" = ["E501"]
|
|
147
|
+
"sage_replay/aggregate.py" = ["E501"]
|
|
148
|
+
|
|
149
|
+
[tool.ruff.format]
|
|
150
|
+
|
|
151
|
+
[tool.mypy]
|
|
152
|
+
python_version = "3.12" # matches requires-python: catch 3.13-only API use on the lowest floor
|
|
153
|
+
files = ["sage_apt", "sage_ini", "sage_map", "sage_mods", "sage_replay", "sage_save", "sage_utils"]
|
|
154
|
+
disable_error_code = ["annotation-unchecked"]
|
|
155
|
+
|
|
156
|
+
[[tool.mypy.overrides]]
|
|
157
|
+
module = ["reversebox.*", "PyQt6.*", "pyBIG.*", "PIL.*"]
|
|
158
|
+
ignore_missing_imports = true
|
|
159
|
+
|
|
160
|
+
# The Qt half of sage_utils (and the texture/source layer built on optional deps) is outside
|
|
161
|
+
# the type-checked set: it needs the [ui] extras installed and predates annotations. The
|
|
162
|
+
# Qt-free helpers (config, views, skill, cli) are checked like the core packages.
|
|
163
|
+
[[tool.mypy.overrides]]
|
|
164
|
+
module = [
|
|
165
|
+
"sage_utils.widgets",
|
|
166
|
+
"sage_utils.styles",
|
|
167
|
+
"sage_utils.textures",
|
|
168
|
+
"sage_utils.sources",
|
|
169
|
+
"sage_utils.findings",
|
|
170
|
+
]
|
|
171
|
+
ignore_errors = true
|
|
172
|
+
|
|
173
|
+
# Same for the Qt half of the Edain Linter window; its Qt-free runner stays checked.
|
|
174
|
+
[[tool.mypy.overrides]]
|
|
175
|
+
module = ["sage_mods.edain.map_checks.ui.window", "sage_mods.edain.map_checks.ui.app"]
|
|
176
|
+
ignore_errors = true
|
|
177
|
+
|
|
178
|
+
# sage_lint is a tool package, not in the type-checked `files` set. sage_mods.edain.map_checks imports
|
|
179
|
+
# its shared map-lint runner (add_map_lint_arguments / run_map_lint), which pulls sage_lint into
|
|
180
|
+
# reach: follow the import so sage_mods.edain still sees real types, but don't gate on sage_lint's own
|
|
181
|
+
# (pre-existing) errors here - it is checked on its own terms, not through this overlay.
|
|
182
|
+
[[tool.mypy.overrides]]
|
|
183
|
+
module = ["sage_lint.*"]
|
|
184
|
+
follow_imports = "silent"
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pysage-tools
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python tools for the data formats of the SAGE engine (Battle for Middle-earth): ini game data, binary maps, replays, and UI movies.
|
|
5
|
+
Author-email: Clement Julia <clement.julia13@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/ClementJ18/pySAGE
|
|
8
|
+
Project-URL: Repository, https://github.com/ClementJ18/pySAGE
|
|
9
|
+
Project-URL: Issues, https://github.com/ClementJ18/pySAGE/issues
|
|
10
|
+
Keywords: sage,bfme,ini,parser,linter,map,replay,modding
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
17
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
18
|
+
Classifier: Topic :: Games/Entertainment
|
|
19
|
+
Requires-Python: >=3.12
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: reversebox>=0.84; sys_platform == "win32"
|
|
23
|
+
Provides-Extra: ui
|
|
24
|
+
Requires-Dist: PyQt6>=6.6; extra == "ui"
|
|
25
|
+
Requires-Dist: pybig>=0.6; extra == "ui"
|
|
26
|
+
Requires-Dist: pillow>=10; extra == "ui"
|
|
27
|
+
Provides-Extra: lint-ui
|
|
28
|
+
Requires-Dist: PyQt6>=6.6; extra == "lint-ui"
|
|
29
|
+
Provides-Extra: wiki
|
|
30
|
+
Requires-Dist: mwclient>=0.11; extra == "wiki"
|
|
31
|
+
Requires-Dist: mwparserfromhell>=0.7; extra == "wiki"
|
|
32
|
+
Requires-Dist: PyQt6>=6.6; extra == "wiki"
|
|
33
|
+
Requires-Dist: pybig>=0.6; extra == "wiki"
|
|
34
|
+
Requires-Dist: keyring>=24; extra == "wiki"
|
|
35
|
+
Provides-Extra: edain-ui
|
|
36
|
+
Requires-Dist: PyQt6>=6.6; extra == "edain-ui"
|
|
37
|
+
Provides-Extra: apt
|
|
38
|
+
Requires-Dist: pybig>=0.6; extra == "apt"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
# pySAGE
|
|
42
|
+
|
|
43
|
+
A collection of Python tools for reading, editing, linting and visualising the data
|
|
44
|
+
formats of the **SAGE engine** - the engine behind *Command & Conquer: Generals* and
|
|
45
|
+
*The Battle for Middle-earth*. It grew out of an ini parser and now spans ini game data,
|
|
46
|
+
binary maps, replays, and UI, with a domain overlay for the Edain mod.
|
|
47
|
+
|
|
48
|
+
Everything installs as one package with optional extras (see [Install](#install)). Each
|
|
49
|
+
subproject has its own README with the details; this page is the map.
|
|
50
|
+
|
|
51
|
+
## Projects
|
|
52
|
+
|
|
53
|
+
### Ini game data
|
|
54
|
+
|
|
55
|
+
| Project | What it is |
|
|
56
|
+
| --- | --- |
|
|
57
|
+
| [`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. |
|
|
58
|
+
| [`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. |
|
|
59
|
+
|
|
60
|
+
### Binary formats
|
|
61
|
+
|
|
62
|
+
| Project | What it is |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| [`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. |
|
|
65
|
+
| [`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. |
|
|
66
|
+
| [`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. |
|
|
67
|
+
|
|
68
|
+
### Domain overlays & apps
|
|
69
|
+
|
|
70
|
+
| Project | What it is |
|
|
71
|
+
| --- | --- |
|
|
72
|
+
| [`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. |
|
|
73
|
+
| [`sage_wiki`](sage_wiki/README.md) | Desktop tool that updates Edain wiki infoboxes from parsed game data through the MediaWiki API. |
|
|
74
|
+
| [`sage_ui`](sage_ui/README.md) | PyQt6 desktop browser for SAGE game data: load sources, search an object, see its resolved stats. |
|
|
75
|
+
|
|
76
|
+
### Shared
|
|
77
|
+
|
|
78
|
+
| Project | What it is |
|
|
79
|
+
| --- | --- |
|
|
80
|
+
| [`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. |
|
|
81
|
+
|
|
82
|
+
## Install
|
|
83
|
+
|
|
84
|
+
Requires Python ≥ 3.12. The project is **pySAGE**; on PyPI it is published as **`pysage-tools`**.
|
|
85
|
+
|
|
86
|
+
> **Note:** the bare `pysage` name on PyPI is an unrelated, abandoned messaging library last
|
|
87
|
+
> released in 2011, and `py-sage` is likewise taken by another project. `pip install pysage` will
|
|
88
|
+
> *not* get you this project - install `pysage-tools`.
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
pip install pysage-tools # core library + linter
|
|
92
|
+
pip install "pysage-tools[ui]" # + the PyQt6 desktop apps (sage-ui)
|
|
93
|
+
pip install "pysage-tools[wiki]" # + the wiki updater
|
|
94
|
+
pip install "pysage-tools[edain-ui]" # + the Edain Linter desktop app
|
|
95
|
+
pip install "pysage-tools[apt]" # + reading .const/.apt out of .big archives
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
From a clone, for development, swap the name for an editable install of the checkout:
|
|
99
|
+
|
|
100
|
+
```sh
|
|
101
|
+
pip install -e ".[ui]"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The extras (`ui`, `lint-ui`, `wiki`, `edain-ui`, `apt`) pull in the optional dependencies each
|
|
105
|
+
peripheral tool needs. The ini, map and replay layers are stdlib-only and always ship, so no
|
|
106
|
+
extra is needed to parse, lint or diff a map. The one non-optional dependency is `reversebox` on
|
|
107
|
+
Windows, the native RefPack compressor that makes saving large maps fast (its DLL is Windows-only,
|
|
108
|
+
so other platforms use the byte-identical pure-Python compressor).
|
|
109
|
+
|
|
110
|
+
Console scripts are installed for the CLI tools: `sage-ini`, `sage-lint`, `sage-edain`,
|
|
111
|
+
`sage-replay`, `sage-apt` (and the GUI scripts `sage-ui`, `sage-wiki`, `sage-lint-ui`,
|
|
112
|
+
`sage-edain-lint` - the Edain Linter, which combines the ini and map checks in one window).
|
|
113
|
+
|
|
114
|
+
## Tests
|
|
115
|
+
|
|
116
|
+
```sh
|
|
117
|
+
pytest # fast, data-free core suite
|
|
118
|
+
pytest --full # + corpus acceptance gates and peripheral-package suites
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Contributing
|
|
122
|
+
|
|
123
|
+
Contributions are welcome - bug reports, fixes, new checks and rules, format coverage, and
|
|
124
|
+
documentation all help. See **[CONTRIBUTING.md](CONTRIBUTING.md)** to get set up and
|
|
125
|
+
**[CONVENTIONS.md](CONVENTIONS.md)** for the coding rules. AI-assisted contributions are
|
|
126
|
+
welcome too, with one expectation: you have read, understood, and can stand behind every
|
|
127
|
+
line you submit.
|