Skip to content

Commit c724955

Browse files
feat: update data & scripts
1 parent 3b4adc3 commit c724955

File tree

10 files changed

+1068
-1111
lines changed

10 files changed

+1068
-1111
lines changed

scripts/.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
*
22
!shell.nix
33
!Makefile
4-
!.gitignore
4+
!.gitignore
5+
!extract_translations.py
6+
!extract_item_names.py
7+
!extract_twitch_rewards.py
8+
!extract_season_rewards.py

scripts/Makefile

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
1-
.PHONY: install-deps all mapping.json expeditions.json twitch.json
1+
.PHONY: clean install-deps all mapping.json rewards translations.json item_to_name_mappings.json season_rewards.json twitch_rewards.json
22

3-
all: install-deps mapping.json expeditions.json twitch.json
3+
VERSION := 6.12-BREACH-28.October.2025
4+
5+
all: clean install-deps mapping.json rewards
6+
7+
clean:
8+
rm -f ./season_rewards.json ./twitch_rewards.json ./item_to_name_mappings.json ./translations.json ./mapping.json
9+
rm -rf ./EXTRACTED
10+
rm -f ./MBINCompiler-linux
11+
rm -rf ./HGPAKtool
12+
rm -f ./*.log
13+
rm -f ./*.MXML
414

515
install-deps:
6-
curl -sSL https://github.com/monkeyman192/MBINCompiler/releases/latest/download/MBINCompiler-linux -o ./MBINCompiler-linux
16+
curl -sSL https://github.com/monkeyman192/MBINCompiler/releases/download/v6.13.0-pre1/MBINCompiler-linux-dotnet6 -o ./MBINCompiler-linux
717
git clone https://github.com/monkeyman192/HGPAKtool.git ./HGPAKtool
818
chmod +x ./MBINCompiler-linux
919

@@ -12,10 +22,28 @@ mapping.json:
1222
jq '.Mapping | map({(.Key): .Value}) | add' ./mapping.json > ../src/data/mapping.json
1323
npx prettier --write ../src/data/mapping.json --config ../.prettierrc
1424

15-
twitch.json:
16-
curl -sSL "https://github.com/NMSCD/nms-archive/raw/refs/heads/main/6.01-VOYAGERS-28.August.2025/METADATA/REALITY/TABLES/UNLOCKABLETWITCHREWARDS.MBIN" -o ./UNLOCKABLETWITCHREWARDS.MBIN
17-
./MBINCompiler-linux -d ./ ./UNLOCKABLETWITCHREWARDS.MBIN -y
25+
translations.json:
26+
python ./HGPAKtool/HGPAKTool/hgpaktool.py ./PCBANKS/NMSARC.MetadataEtc.pak
27+
for f in EXTRACTED/language/*_usenglish.mbin; do \
28+
echo "Decoding $$f"; \
29+
./MBINCompiler-linux -y -d ./EXTRACTED/language/ "$$f"; \
30+
done
31+
python ./extract_translations.py
32+
33+
item_to_name_mappings.json: translations.json
34+
curl -sSL "https://github.com/NMSCD/nms-archive/raw/refs/heads/main/$(VERSION)/METADATA/REALITY/TABLES/NMS_REALITY_GCPRODUCTTABLE.MXML" -o ./NMS_REALITY_GCPRODUCTTABLE.MXML
35+
python ./extract_item_names.py
36+
37+
season_rewards.json: item_to_name_mappings.json
38+
curl -sSL "https://github.com/NMSCD/nms-archive/raw/refs/heads/main/$(VERSION)/METADATA/REALITY/TABLES/UNLOCKABLESEASONREWARDS.MXML" -o ./UNLOCKABLESEASONREWARDS.MXML
39+
python ./extract_season_rewards.py
40+
cp ./season_rewards.json ../src/data/rewards/season.json
41+
npx prettier --write ../src/data/rewards/season.json --config ../.prettierrc
42+
43+
twitch_rewards.json: item_to_name_mappings.json
44+
curl -sSL "https://github.com/NMSCD/nms-archive/raw/refs/heads/main/$(VERSION)/METADATA/REALITY/TABLES/UNLOCKABLETWITCHREWARDS.MXML" -o ./UNLOCKABLETWITCHREWARDS.MXML
45+
python ./extract_twitch_rewards.py
46+
cp ./twitch_rewards.json ../src/data/rewards/twitch.json
47+
npx prettier --write ../src/data/rewards/twitch.json --config ../.prettierrc
1848

19-
season.json:
20-
curl -sSL "https://github.com/NMSCD/nms-archive/raw/refs/heads/main/6.01-VOYAGERS-28.August.2025/METADATA/REALITY/TABLES/UNLOCKABLESEASONREWARDS.MBIN" -o ./UNLOCKABLESEASONREWARDS.MBIN
21-
./MBINCompiler-linux -d ./ ./UNLOCKABLESEASONREWARDS.MBIN -y
49+
rewards: season_rewards.json twitch_rewards.json

scripts/extract_item_names.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import xml.etree.ElementTree as ET
2+
import json
3+
4+
xml_path = "./NMS_REALITY_GCPRODUCTTABLE.MXML"
5+
translations_path = "./translations.json"
6+
output_json = "item_to_name_mappings.json"
7+
8+
# Load translation strings
9+
with open(translations_path, "r", encoding="utf-8") as f:
10+
translations = json.load(f)
11+
12+
tree = ET.parse(xml_path)
13+
root = tree.getroot()
14+
15+
mapping = {}
16+
17+
# Find all product entries
18+
for product in root.findall(".//Property[@name='Table'][@value='GcProductData']"):
19+
prod_id = None
20+
name_lower_key = None
21+
22+
for prop in product.findall("Property"):
23+
if prop.get("name") == "ID":
24+
prod_id = prop.get("value")
25+
if prop.get("name") == "NameLower":
26+
name_lower_key = prop.get("value")
27+
28+
if prod_id and name_lower_key:
29+
translated_value = translations.get(name_lower_key, name_lower_key)
30+
mapping[prod_id] = translated_value
31+
32+
with open(output_json, "w", encoding="utf-8") as f:
33+
json.dump(mapping, f, indent=4, ensure_ascii=False)
34+
35+
print(f"Saved {len(mapping)} entries to {output_json}")
36+

scripts/extract_season_rewards.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import xml.etree.ElementTree as ET
2+
import json
3+
import re
4+
5+
xml_path = "./UNLOCKABLESEASONREWARDS.MXML"
6+
mapping_path = "./item_to_name_mappings.json"
7+
output_path = "./season_rewards.json"
8+
9+
# Load product name mapping
10+
with open(mapping_path, "r", encoding="utf-8") as f:
11+
name_map = json.load(f)
12+
13+
tree = ET.parse(xml_path)
14+
root = tree.getroot()
15+
16+
results = []
17+
18+
for entry in root.findall(".//Property[@name='Table'][@value='GcUnlockableSeasonReward']"):
19+
entry_id = None
20+
expedition = None
21+
22+
# Traverse properties
23+
for prop in entry.findall("Property"):
24+
# ID field
25+
if prop.get("name") == "ID":
26+
entry_id = prop.get("value")
27+
28+
# SeasonIds nested property
29+
if prop.get("name") == "SeasonIds":
30+
season_prop = prop.find("Property")
31+
if season_prop is not None:
32+
season_value = season_prop.get("value")
33+
if season_value and season_value.isdigit():
34+
expedition = int(season_value)
35+
36+
if entry_id is None:
37+
continue
38+
39+
# Resolve name from mapping (fallback: reuse ID)
40+
name = name_map.get(entry_id, entry_id)
41+
42+
results.append({
43+
"name": name,
44+
"id": f"^{entry_id}",
45+
"expedition": expedition if expedition is not None else 0
46+
})
47+
48+
# Output JSON
49+
with open(output_path, "w", encoding="utf-8") as f:
50+
json.dump(results, f, indent=2, ensure_ascii=False)
51+
52+
print(f"Saved {len(results)} entries to {output_path}")
53+

scripts/extract_translations.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import os
2+
import json
3+
import xml.etree.ElementTree as ET
4+
5+
LANGDIR = "./EXTRACTED/language"
6+
PREFIX = "english"
7+
OUTPUT = "translations.json"
8+
9+
translations = {}
10+
11+
def get_value(prop):
12+
"""Extracts text from either <Property value=""> or inner text."""
13+
if prop is None:
14+
return ""
15+
# Support attributes
16+
if "value" in prop.attrib:
17+
return prop.attrib.get("value", "")
18+
# Support inner text
19+
if prop.text:
20+
return prop.text.strip()
21+
return ""
22+
23+
for fname in os.listdir(LANGDIR):
24+
if PREFIX.lower() in fname.lower() and fname.lower().endswith(".mxml"):
25+
path = os.path.join(LANGDIR, fname)
26+
try:
27+
tree = ET.parse(path)
28+
root = tree.getroot()
29+
30+
# Matches both your structure and other NMS localisation formats
31+
# //Data/Property[@name='Table']/Property[@name='Table']
32+
entries = root.findall(".//Property[@name='Table']/Property[@name='Table']")
33+
if not entries:
34+
# Try alternate structure used in other versions:
35+
entries = root.findall(".//Property[@name='Table']")
36+
37+
for entry in entries:
38+
key = entry.get("_id")
39+
if not key:
40+
continue
41+
42+
eng = entry.find("Property[@name='English']")
43+
us_eng = entry.find("Property[@name='USEnglish']")
44+
45+
value = get_value(eng)
46+
if not value and us_eng is not None:
47+
value = get_value(us_eng)
48+
49+
translations[key] = value
50+
51+
except ET.ParseError:
52+
print(f"Skipping malformed XML: {fname}")
53+
54+
with open(OUTPUT, "w", encoding="utf-8") as f:
55+
json.dump(translations, f, indent=2, ensure_ascii=False)
56+
57+
print(f"Extracted {len(translations)} entries to {OUTPUT}")

scripts/extract_twitch_rewards.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import xml.etree.ElementTree as ET
2+
import json
3+
4+
xml_path = "./UNLOCKABLETWITCHREWARDS.MXML"
5+
mapping_path = "./item_to_name_mappings.json"
6+
output_path = "twitch_rewards.json"
7+
8+
# Load product id --> localized name mapping
9+
with open(mapping_path, "r", encoding="utf-8") as f:
10+
name_map = json.load(f)
11+
12+
tree = ET.parse(xml_path)
13+
root = tree.getroot()
14+
15+
rewards = []
16+
17+
# Locate entries in the unlockable twitch rewards table
18+
for reward in root.findall(".//Property[@name='Table'][@value='GcUnlockableTwitchReward']"):
19+
twitch_id = None
20+
product_id = None
21+
22+
for prop in reward.findall("Property"):
23+
if prop.get("name") == "TwitchId":
24+
twitch_id = prop.get("value")
25+
if prop.get("name") == "ProductId":
26+
product_id = prop.get("value")
27+
28+
if twitch_id and product_id:
29+
display_name = name_map.get(product_id, product_id)
30+
rewards.append({
31+
"id": f"^{twitch_id}",
32+
"name": display_name
33+
})
34+
35+
# Write output JSON
36+
with open(output_path, "w", encoding="utf-8") as f:
37+
json.dump(rewards, f, indent=2, ensure_ascii=False)
38+
39+
print(f"Saved {len(rewards)} entries to {output_path}")

src/data/expeditions.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@
1717
"16": "Boundary Redux",
1818
"17": "Titan Redux",
1919
"18": "Relics",
20-
"19": "Corvette"
20+
"19": "Corvette",
21+
"20": "Breach"
2122
}

0 commit comments

Comments
 (0)