{"id":34049676,"url":"https://github.com/juk0de/mtf2json","last_synced_at":"2026-04-02T02:12:27.239Z","repository":{"id":244570799,"uuid":"815434300","full_name":"juk0de/mtf2json","owner":"juk0de","description":"Convert MegaMek's MTF format to JSON.","archived":false,"fork":false,"pushed_at":"2025-07-14T14:06:03.000Z","size":449,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-12-15T18:36:28.379Z","etag":null,"topics":["battletech","json","megamek","mtf"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/juk0de.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-15T06:38:34.000Z","updated_at":"2025-01-09T15:26:54.000Z","dependencies_parsed_at":"2024-06-21T06:46:06.684Z","dependency_job_id":"bcf1d974-9ff7-48f1-add3-cd12599044ee","html_url":"https://github.com/juk0de/mtf2json","commit_stats":null,"previous_names":["juk0de/mtf2json"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/juk0de/mtf2json","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juk0de%2Fmtf2json","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juk0de%2Fmtf2json/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juk0de%2Fmtf2json/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juk0de%2Fmtf2json/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/juk0de","download_url":"https://codeload.github.com/juk0de/mtf2json/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juk0de%2Fmtf2json/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31294422,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T01:43:37.129Z","status":"online","status_checked_at":"2026-04-02T02:00:08.535Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["battletech","json","megamek","mtf"],"created_at":"2025-12-14T00:54:49.075Z","updated_at":"2026-04-02T02:12:27.229Z","avatar_url":"https://github.com/juk0de.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# mtf2json\n\n`mtf2json` converts [MegaMek](https://github.com/MegaMek/)'s MTF file format to JSON.\n\n## Project Goals and Features\n\n- Simplify the parsing of MTF files across all modern programming languages by\n  utilizing a widely-recognized and well-documented format (JSON).\n- Provide a comprehensive JSON structure that includes all data from the MTF\n  files, ensuring it is well-organized and clearly arranged.\n- Address common issues with the MTF format, such as duplicate keys,\n  inconsistent delimiters, and varying encodings.\n\n## Why mtf2json?\n\nMegaMek is a great project, but the MTF format is difficult to work with. It is\nnot standardized and there is no official specification (at least none that I\ncould find). Additionally, MTF files do not contain all the information\nrequired to create an actual record sheet.\n\n`mtf2json` does not simply create a 1:1 JSON version of the MTF data but\nrestructures the data and adds some information, such as structure pips. The\nexamples below illustrate the differences between various sections in the MTF\nand JSON formats.\n\n## Current State\n### Conversion Rates\n\n| Chassis Type | Conversion Rate |\n|--------------|---------------|\n| Biped | 100% (3954 / 3954) |\n| Quad | Not supported |\n| Tripod | Not supported |\n| LAM | Not supported |\n\n### Latest Supported MegaMek Commit\n\nThe most recent commit of the [MegaMek](https://github.com/MegaMek/megamek)\nrepository that `mtf2json` has been tested with can be displayd by running\n`mtf2json --mm-commit`.\n\n### Limitations\n\nThe MTF files are missing important information that is currenty not available in JSON either:\n- Weapon damage, heat and ranges\n- Ammunition quantity per slot\n\nWhile it makes sense not to duplicate this data in each MTF file, I hoped that\nit would be available in some easy-to-import format, e.g. CSV files. However,\nI have not been able to find anything like that. It seems that much of this\ninformation is hardcoded in JAVA.\n\n### Testing\n\nTesting mtf2json is challenging, primarily because the MTF format is so loosely\nspecified. I'm still not sure if I've encountered all possible keys and\nunderstood all supported value syntaxes. Since there are over 4000 MTF files in\nMegaMek, I cannot manually verify all conversion results. If you encounter\ntrouble converting an MTF file, please create a GitHub issue and attach the\nfile so I can verify and fix the issue.\n\n## Installation\n\n### PyPi\n\n```sh\npipx install mtf2json\n```\n\n### Manual\nClone the repository and install the dependencies:\n\n```sh\ngit clone https://github.com/juk0de/mtf2json.git\ncd mtf2json\npipx install .\n```\n\n## Usage\n\n### CLI\nTo convert a single MTF file to JSON, use the following command:\n\n```sh\nmtf2json --mtf-file \u003cpath_to_mtf_file\u003e [--convert] [--json-file \u003cpath_to_json_file\u003e]\n```\n\nTo query JSON data in the terminal (e.g. armor pips of left arm), pipe the output into `jq`:\n```sh\nmtf2json --mtf-file \u003cpath_to_mtf_file\u003e | jq .armor.left_arm.pips\n```\n\nTo convert all MTF files in a directory, including subdirectories, use the following command:\n\n```sh\nmtf2json --mtf-dir \u003cpath_to_mtf_dir\u003e --recursive [--json-dir \u003cpath_to_json_dir\u003e]\n```\n\nIf you want to convert all current MTF files, use the MegaMek Github repository\nwith the latest supported commit. You can clone it like this:\n\n```\ngit clone git@github.com:MegaMek/megamek.git \u0026\u0026 cd megamek \u0026\u0026 git reset --hard $(mtf2json --mm-commit)\n```\n\nThen use `mtf2json` with the `--mtf-dir` option as described above.\n\n### Library\n```python\nfrom mtf2json import read_mtf\nfrom pathlib import Path\njson_data = read_mtf(Path('/my/file.mtf'))\n```\n\n## JSON Structure and Examples\n\nHere are some comparisons of sections from an MTF file and their JSON counterparts:\n\n### The Basic Stuff\n\nMTF:\n```\nchassis:Atlas\nmodel:AS7-K\nmul id:144\n```\n\nJSON:\n```json\n\"chassis\": \"Atlas\",\n\"model\": \"AS7-K\",\n\"mul_id\": 144,\n```\n\nMost of the \"flat\" `key:value` pairs are also stored as `key:value` pairs in\nJSON. However, numeric values are stored as `int` if appropriate (they remain\nstrings if they are used as names or model designation).\n\n### Rules Level\nMTF:\n```\nRules Level:2\n```\n\nJSON:\n```json\n\"rules_level\": 2,\n\"rules_level_str\": \"Standard\",\n```\n\nA string version of the rules level is automatically added. It's intended to be used in record sheets and determined using the same algorithm\nas [MegaMek](https://github.com/juk0de/megamek/blob/78fb6e4616dd469dcb781c7da37d1bae748c45ce/megamek/src/megamek/common/SimpleTechLevel.java#L92).\n\n### Quirks\nMTF:\n```\nquirk:battle_fists_la\nquirk:battle_fists_ra\nquirk:command_mech\nquirk:distracting\nquirk:imp_com\n```\nJSON:\n```json\n\"quirks\": [\n    \"battle_fists_la\",\n    \"battle_fists_ra\",\n    \"command_mech\",\n    \"distracting\",\n    \"imp_com\"\n],\n```\nNo more dealing with multiple identical keys - just a simple JSON list.\n\n### Heat Sinks\nMTF:\n```\nHeat Sinks:20 Single\n```\nJSON:\n```json\n\"heat_sinks\": {\n    \"quantity\": 20,\n    \"type\": \"Single\"\n},\n```\n\nThe type and quantity of heat sinks are separate keys, eliminating the need for additional parsing.\n\n### Movement Points\nMTF:\n```\nWalk MP:3\nJump MP:0\n```\nJSON:\n```json\n\"walk_mp\": 3,\n\"run_mp\": 5,\n\"jump_mp\": 0,\n```\n\nThe movement points for running are calculated automatically for your convenience.\n\n### Armor\nMTF:\n```  \narmor:Standard(Inner Sphere)\nLA armor:34\nRA armor:34\nLT armor:32\nRT armor:32\nCT armor:47\nHD armor:9\nLL armor:41\nRL armor:41\nRTL armor:10\nRTR armor:10\nRTC armor:14\n```\nJSON:\n```json\n\"armor\": {\n    \"type\": \"Standard\",\n    \"tech_base\": \"IS\",\n    \"left_arm\": {\n        \"pips\": 34\n    },\n    \"right_arm\": {\n        \"pips\": 34\n    },\n    \"left_torso\": {\n        \"front\": {\n            \"pips\": 32\n        },\n        \"rear\": {\n            \"pips\": 10\n        }\n    },\n    \"right_torso\": {\n        \"front\": {\n            \"pips\": 32\n        },\n        \"rear\": {\n            \"pips\": 10\n        }\n    },\n    \"center_torso\": {\n        \"front\": {\n            \"pips\": 47\n        },\n        \"rear\": {\n            \"pips\": 14\n        }\n    },\n    \"head\": {\n        \"pips\": 9\n    },\n    \"left_leg\": {\n        \"pips\": 41\n    },\n    \"right_leg\": {\n        \"pips\": 41\n    }\n},\n```\n\nArmor type, tech base and pips are stored in the `armor` section. In case of\npatchwork armor, each location will have a `type` key:\n\n```json\n\"armor\": {\n    \"type\": \"Patchwork\",\n    \"left_arm\": {\n        \"pips\": 26,\n        \"type\": \"Reactive(Inner Sphere)\"\n    },\n}\n```\n\nNote that the tech base is not always available in the MTF file (i.e. sometimes\nit's \"Standard Armor\", sometimes \"Standard(Inner Sphere))\".\n\n### Weapons\nMTF:\n```\nWeapons:3\n1 ISGaussRifle, Right Torso, Ammo:16\n1 ISERLargeLaser, Left Arm\n2 ISMediumPulseLaser, Center Torso (R)\n```\nJSON:\n```json\n\"weapons\": [\n    {\n        \"name\": \"ISGaussRifle\",\n        \"location\": \"right_torso\",\n        \"facing\": \"front\",\n        \"quantity\": 1,\n        \"ammo\": 16\n    },\n    {\n        \"name\": \"ISERLargeLaser\",\n        \"location\": \"left_arm\",\n        \"facing\": \"front\",\n        \"quantity\": 1\n    },\n    {\n        \"name\": \"ISMediumPulseLaser\",\n        \"location\": \"center_torso\",\n        \"facing\": \"rear\",\n        \"quantity\": 2\n    },\n],\n```\n\nLocation, facing, quantity and ammo are all individual keys for each weapon.\nSome MTF files contain individual entries for identical weapons in the same\nlocation (i.e., no quantity at the beginning of the line). These entries are\nautomatically merged.\n\n### Structure\nMTF:\n```\nstructure:IS Standard\n```\nJSON:\n```json\n\"structure\": {\n    \"type\": \"Standard\",\n    \"head\": {\n        \"pips\": 3\n    },\n    \"center_torso\": {\n        \"pips\": 31\n    },\n    \"left_torso\": {\n        \"pips\": 21\n    },\n    \"right_torso\": {\n        \"pips\": 21\n    },\n    \"left_arm\": {\n        \"pips\": 17\n    },\n    \"right_arm\": {\n        \"pips\": 17\n    },\n    \"left_leg\": {\n        \"pips\": 21\n    },\n    \"right_leg\": {\n        \"pips\": 21\n    }\n},\n```\n\nThe structure type and tech base are stored in the `structure` section. The\npips are added afterward, based on the mech's tonnage.\n\n### Critical Slots\nMTF:\n```\nLeft Arm:\nShoulder\nUpper Arm Actuator\nLower Arm Actuator\nHand Actuator\nHeat Sink\nHeat Sink\nISERLargeLaser\nISERLargeLaser\nISAntiMissileSystem\n-Empty-\n-Empty-\n-Empty-\n```\nJSON:\n```json\n\"critical_slots\": {\n  \"left_arm\": {\n    \"1\": \"Shoulder\",\n    \"2\": \"Upper Arm Actuator\",\n    \"3\": \"Lower Arm Actuator\",\n    \"4\": \"Hand Actuator\",\n    \"5\": \"Heat Sink\",\n    \"6\": \"Heat Sink\",\n    \"7\": \"ISERLargeLaser\",\n    \"8\": \"ISERLargeLaser\",\n    \"9\": \"ISAntiMissileSystem\",\n    \"10\": null,\n    \"11\": null,\n    \"12\": null\n  },\n}\n```\n\nAll critical slot data is stored in the `critical_slots` section, with a separate\nsection for each location. Each slot uses its slot number as the key, making it\nvery easy to check the content of each slot directly. The `-Empty-` string is\nreplaced by an actual `null` (Python `None`).\n\n### Fluff\nMTF:\n```\noverview:The Banshee, introduced in 2445 by the Terran Hegemony, is a 'Mech that was designed specifically for close-assault operations during the early years of BattleMech warfare.\ncapabilities:A contemporary of the Mackie and Emperor, the Banshee is undeniably fast for one of the heaviest 'Mechs ever produced, and with fifteen tons of armor it is better protected than most other assault 'Mechs. However the use of a massive GM 380 fusion engine left little room for weaponry, and even at the time of its introduction the Banshee was considered undergunned compared to its privately-built competition. As critics noted, what use was impressive armor and mobility if a better-armed enemy could overwhelm and destroy it? The result was a mediocre 'Mech which even the lowly Rifleman could beat as long as it prevented the Banshee's mass from coming into play.\ndeployment:The two primary weapons on the Banshee, a Magna Hellstar PPC matched up with an Imperator-A Autocannon/5 with one ton of ammo, are mounted in the right and left sides of its torso. The two systems complement each other with similar range profiles and would provide good firepower for a 'Mech weighing at half the tonnage of the Banshee. Perversely for a close-combat 'Mech their minimum ranges means both weapons present targeting difficulties as the Banshee closes in on its enemy, and a Magna Mk I Small Laser mounted in its head appears to have been installed as an afterthought. Sixteen heat sinks help keep the 'Mech cool though, and thanks to its prodigious size the Banshee can turn lighter 'Mechs to scrap in hand-to-hand fighting.\nhistory: The Hegemony soldiered on though, producing five thousand Banshees in a decade before its poor combat performance finally forced them to stop production in 2455 and the 'Mech was relegated to training and militia units for the next few centuries. Even during the Succession Wars the 'Mech's poor reputation meant many commanders saw it as a liability rather than an asset; unless they were fighting on a backwater Periphery world or as part of a poorly-equipped outfit, most Banshees were placed in second-line reserves to provide fire support or fulfill the role of brawlers so that better-armed 'Mechs need not engage in hand-to-hand combat. In this way fully a third of the original Banshee production run remained operational by the dawn of the thirty-first century and could be found throughout the Inner Sphere and Periphery, with the lion's share residing within the Lyran Commonwealth. Though a few attempts had been made over the years to improve the Banshee, Defiance Industries was the first to produce a variant which markedly improved on the original and was suggested by many of its original critics. With help from the Federated Suns and access to blueprints from the original Hesperus II factories, the BNC-3S proved to be such a success the Lyrans began fielding them in front line units, surprising their enemies who ignored them to focus on \"real\" threats with thoroughly unexpected firepower. Under the Federated Commonwealth improvements would continue to be made, though not at the same priority level as other projects, and the much-improved BNC-5S was active in time to meet the Clan Invasion.\nmanufacturer:Diplass BattleMechs,Witten Industries,Defiance Industries,Star League Weapons Research\nprimaryfactory:Apollo,Hesperus II,Hesperus II,New Earth\nsystemmanufacturer:CHASSIS:Star League\nsystemmode:CHASSIS:XT\nsystemmanufacturer:ENGINE:GM\nsystemmode:ENGINE:380\nsystemmanufacturer:ARMOR:Starshield\nsystemmanufacturer:COMMUNICATIONS:Dalban\nsystemmode:COMMUNICATIONS:Commline\nsystemmanufacturer:TARGETING:Dalban\nsystemmode:TARGETING:HiRez-B\n```\nJSON:\n```json\n\"fluff\": {\n  \"overview\": \"The Banshee, introduced in 2445 by the Terran Hegemony, is a 'Mech that was designed specifically for close-assault operations during the early years of BattleMech warfare.\",\n  \"capabilities\": \"A contemporary of the Mackie and Emperor, the Banshee is undeniably fast for one of the heaviest 'Mechs ever produced, and with fifteen tons of armor it is better protected than most other assault 'Mechs. However the use of a massive GM 380 fusion engine left little room for weaponry, and even at the time of its introduction the Banshee was considered undergunned compared to its privately-built competition. As critics noted, what use was impressive armor and mobility if a better-armed enemy could overwhelm and destroy it? The result was a mediocre 'Mech which even the lowly Rifleman could beat as long as it prevented the Banshee's mass from coming into play.\",\n  \"deployment\": \"The two primary weapons on the Banshee, a Magna Hellstar PPC matched up with an Imperator-A Autocannon/5 with one ton of ammo, are mounted in the right and left sides of its torso. The two systems complement each other with similar range profiles and would provide good firepower for a 'Mech weighing at half the tonnage of the Banshee. Perversely for a close-combat 'Mech their minimum ranges means both weapons present targeting difficulties as the Banshee closes in on its enemy, and a Magna Mk I Small Laser mounted in its head appears to have been installed as an afterthought. Sixteen heat sinks help keep the 'Mech cool though, and thanks to its prodigious size the Banshee can turn lighter 'Mechs to scrap in hand-to-hand fighting.\",\n  \"history\": \"The Hegemony soldiered on though, producing five thousand Banshees in a decade before its poor combat performance finally forced them to stop production in 2455 and the 'Mech was relegated to training and militia units for the next few centuries. Even during the Succession Wars the 'Mech's poor reputation meant many commanders saw it as a liability rather than an asset; unless they were fighting on a backwater Periphery world or as part of a poorly-equipped outfit, most Banshees were placed in second-line reserves to provide fire support or fulfill the role of brawlers so that better-armed 'Mechs need not engage in hand-to-hand combat. In this way fully a third of the original Banshee production run remained operational by the dawn of the thirty-first century and could be found throughout the Inner Sphere and Periphery, with the lion's share residing within the Lyran Commonwealth. Though a few attempts had been made over the years to improve the Banshee, Defiance Industries was the first to produce a variant which markedly improved on the original and was suggested by many of its original critics. With help from the Federated Suns and access to blueprints from the original Hesperus II factories, the BNC-3S proved to be such a success the Lyrans began fielding them in front line units, surprising their enemies who ignored them to focus on \\\"real\\\" threats with thoroughly unexpected firepower. Under the Federated Commonwealth improvements would continue to be made, though not at the same priority level as other projects, and the much-improved BNC-5S was active in time to meet the Clan Invasion.\",\n  \"manufacturer\": [\n    \"Diplass BattleMechs\",\n    \"Witten Industries\",\n    \"Defiance Industries\",\n    \"Star League Weapons Research\"\n  ],\n  \"primaryfactory\": [\n    \"Apollo\",\n    \"Hesperus II\",\n    \"Hesperus II\",\n    \"New Earth\"\n  ],\n  \"systemmanufacturer\": {\n    \"chassis\": \"Star League\",\n    \"engine\": \"GM\",\n    \"armor\": \"Starshield\",\n    \"communications\": \"Dalban\",\n    \"targeting\": \"Dalban\"\n  },\n  \"systemmode\": {\n    \"chassis\": \"XT\",\n    \"engine\": \"380\",\n    \"communications\": \"Commline\",\n    \"targeting\": \"HiRez-B\"\n  }\n}\n```\n\nI created a new section `fluff` that contains all the additional information\nusually not required for playing or creating record sheets.  As you can\nsee, the MTF keys `systemmanufacturer` and `systemmode` can appear multiple\ntimes in an MTF file and contain a \"subkey\" (i.e. a nested `key:value` pair\nwithin the value). I separated all these \"subkeys\" into individual\n`key:value` pairs and organized them neatly. Also, the `manufacturer` and\n`primaryfactory` values are always lists, as they often contain multiple\nvalues.\n\n## Development\n* Install [poetry](https://python-poetry.org/docs/)\n* Clone repository and `cd` into it\n* Execute `poetry install`\n* To run tests, execute `poetry run pytest`\n* To run `mtf2json`, execute `poetry run mtf2json`\n* Before creating a pull request, make sure to run `ruff check` and `ruff format`\n\n## License\n\nAll source code in this project is licensed under the [GPLv3 license](https://www.gnu.org/licenses/gpl-3.0.en.html).\nThe included MTF files are part of the MegaMek project and are, therefore, licensed under the GPLv2 license (see\nhttps://github.com/MegaMek/#current-project-status). These files are provided unmodified and solely for testing purposes.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuk0de%2Fmtf2json","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjuk0de%2Fmtf2json","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuk0de%2Fmtf2json/lists"}