#!/usr/bin/env python3 """Scans the workshop library and writes a feature inventory. scan.py [ids...] (no ids = whole library) Writes features.json (per item: info + feature counter) and FEATURES.md (feature -> items index, for picking regression targets) into $LWE_LIBRARY_DIR (default ~/.local/share/lwe-library). Reads scene.pkg in memory, nothing unpacked. """ import collections, json, os, re, struct, sys W = os.environ.get('LWE_WORKSHOP_DIR', '/workspace/SteamLibrary/steamapps/workshop/content/431960') ASSETS = os.environ.get('LWE_ASSETS_DIR', '/workspace/SteamLibrary/steamapps/common/wallpaper_engine/assets') HERE = os.environ.get('LWE_LIBRARY_DIR', os.path.expanduser('~/.local/share/lwe-library')) def parse_json(data): if isinstance(data, bytes): data = data.decode('utf-8-sig', errors='replace') data = re.sub(r',(\s*[}\]])', r'\1', data) try: return json.loads(data) except Exception: return None class Source: """Files of one item: scene.pkg entries, then loose files, then its dependency, then assets.""" def __init__(self, root, dep_root=None): self.roots = [root] + ([dep_root] if dep_root else []) + [ASSETS] self.pkgs = [] for r in self.roots[:-1]: for name in ('scene.pkg', 'gifscene.pkg'): p = os.path.join(r, name) if os.path.exists(p): self.pkgs.append(self._index(p)) @staticmethod def _index(path): with open(path, 'rb') as f: def u32(): return struct.unpack(' items (rarest first; `fx:name*` in features.json = effect shipped by the wallpaper)\n\n') for k, v in sorted(index.items(), key=lambda kv: (len(kv[1]), kv[0])): f.write(f'- **{k}** ({len(v)}): {" ".join(sorted(v))}\n') f.write('\n## Items\n\n') for id_, d in sorted(db.items()): i = d['info'] extra = ' '.join(f'{k}={i[k]}' for k in ('version', 'objs', 'size', 'dep') if i.get(k) is not None) f.write(f'- {id_} [{i.get("type")}] {i.get("title")} ({extra})\n ' + ', '.join(f'{k}{"x" + str(v) if v > 1 else ""}' for k, v in d['features'].items()) + '\n') if __name__ == '__main__': main()