import zipfile, io, json, tomllib, re, pickle, os, glob def lenient_json(b): s=b.decode("utf-8-sig",errors="replace") try: return json.loads(s) except Exception: s2=re.sub(r'//[^\n]*','',s); s2=re.sub(r',(\s*[}\]])',r'\1',s2) try: return json.loads(s2) except Exception: return None mods={} # modid -> dict(jar, version, deps, nested) files=[] # (jar, path, json) for data recipe/tags + lang lang={}; models=set(); errors=[] def walk(z, jarname, nested=False): names=z.namelist() for t in ("META-INF/neoforge.mods.toml","META-INF/mods.toml"): if t in names: raw=z.read(t).decode("utf-8",errors="replace") try: m=tomllib.loads(raw) except Exception as e: errors.append((jarname,"toml",str(e))); m={} for md in m.get("mods",[]): mid=md.get("modId"); ver=str(md.get("version")) if ver.startswith("${"): mf=z.read("META-INF/MANIFEST.MF").decode(errors="replace") if "META-INF/MANIFEST.MF" in names else "" mm=re.search(r'Implementation-Version: *(\S+)',mf); ver=mm.group(1) if mm else ver deps=m.get("dependencies",{}).get(mid,[]) mods.setdefault(mid,[]).append(dict(jar=jarname,version=ver,deps=deps,nested=nested,file=t)) break for n in names: if n.startswith("META-INF/jarjar/") and n.endswith(".jar"): try: walk(zipfile.ZipFile(io.BytesIO(z.read(n))), jarname+"!"+n.split("/")[-1], True) except Exception as e: errors.append((jarname,n,str(e))) elif n.startswith("data/") and n.endswith(".json"): p=n.split("/") if len(p)>=4 and (p[2] in("recipe","recipes") or (p[2]=="tags" and p[3] in("item","items","block","blocks"))): j=lenient_json(z.read(n)) if j is None: errors.append((jarname,n,"badjson")) files.append((jarname,n,j)) elif n.startswith("assets/") and n.endswith("lang/en_us.json"): j=lenient_json(z.read(n)) if isinstance(j,dict): for k,v in j.items(): if k.startswith(("item.","block.")) and k.count(".")==2: lang.setdefault(k,[]).append((jarname,v)) elif n.startswith("assets/") and "/models/item/" in n and n.endswith(".json"): p=n.split("/"); models.add(p[1]+":"+"/".join(p[4:])[:-5]) jars=["base_minecraft.jar","base_neoforge.jar"]+sorted(glob.glob("jars/*.jar")) for j in jars: walk(zipfile.ZipFile(j), os.path.basename(j)) pickle.dump(dict(mods=mods,files=files,lang=lang,models=models,errors=errors),open("data.pkl","wb")) print(len(mods),len(files),len(lang),len(models),len(errors)) for e in errors[:20]: print(e)