diff --git a/README.md b/README.md
index bcd1689..f73d82d 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-
+
# orbisRPC — Discord Rich Presence for PS4 (GoldHEN RPC)
@@ -228,8 +228,6 @@ nanoDNS — then reboot. (This is the step most people miss.)
- Some homebrew (Apollo Save Tool, Cheats Manager, Homebrew Store) isn't
detected yet — under investigation.
-- Hotspot/mobile connections can send oversized frames that Discord
- rejects — use stable Wi-Fi/LAN if presence won't set.
## Building
@@ -273,7 +271,8 @@ Build/test/CI details: [`docs/BUILDING.md`](docs/BUILDING.md).
- **SirHumza** — project founder, daemon core.
- **CyberMask367** — firmware coverage to 13.52, homebrew and retro games resolving, presence flags, tester wrangling.
- Testers in `testers-chat` on DIscord — every log file that made a fix possible.
-- [*PKG-ZONE*](https://pkg-zone.com) — metadata on ps4 hombrew apps used for resolving
+- [OpenOrbis](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain) - SDK/Toolchain used to make setup PKG
+- [*PKG-ZONE*](https://pkg-zone.com) — used for resolving metadata on hombrew apps
- [*PSX DATA CENTER*](https://psxdatacenter.com) — source used to make custom json list in [`config`](config/) for resolving retro games
## License
diff --git a/config/icons/pslogo.png b/config/icons/pslogo.png
deleted file mode 100644
index 14cac60..0000000
Binary files a/config/icons/pslogo.png and /dev/null differ
diff --git a/scripts/add_custom_title.py b/scripts/add_custom_title.py
new file mode 100644
index 0000000..5f0ffda
--- /dev/null
+++ b/scripts/add_custom_title.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python3
+"""Add an entry to config/custom.json (the community title list).
+
+ python3 scripts/add_custom_title.py ask for id, name, art
+ python3 scripts/add_custom_title.py --list show every entry
+ python3 scripts/add_custom_title.py --remove ORPC00042
+
+pkg-zone only carries the apps its own scraper knows about, and the retro
+indexes are gated on retro prefixes. custom.json is the catch-all: a plain
+TITLEID -> name map anyone can contribute to.
+
+Format matches the retro indexes so the payload reuses the same scanner:
+
+ [{"id":"ORPC00042","name":"Some App","cover":"https://.../a.png"}]
+
+Compact separators are required. The scanner matches entries on the literal
+byte run "id":"", so a space after the colon -- what you get from a
+normal pretty-printer -- means the lookup never matches and the payload falls
+back to the raw TITLEID with no error. save() below enforces this.
+
+"cover" is optional; without one the presence falls back to the default art.
+
+custom runs last in the chain, so TMDB stays authoritative for retail titles.
+"""
+import json
+import os
+import sys
+
+PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
+ 'config', 'custom.json')
+
+
+def load():
+ try:
+ with open(PATH, encoding='utf-8') as f:
+ return json.load(f)
+ except (OSError, ValueError):
+ return []
+
+
+def save(entries):
+ """One entry per line so a contribution reads as a one-line diff.
+
+ separators=(',', ':') is not cosmetic: the scanner matches entries on the
+ literal byte run "id":"", so a space after the colon -- what a
+ normal pretty-printer emits -- means the lookup never matches and the
+ payload falls back to the raw TITLEID with no error."""
+ with open(PATH, 'w', encoding='utf-8', newline='\n') as f:
+ f.write('[\n')
+ for i, e in enumerate(entries):
+ tail = '' if i == len(entries) - 1 else ','
+ f.write(' ' + json.dumps(e, ensure_ascii=False,
+ separators=(',', ':')) + tail + '\n')
+ f.write(']\n')
+
+
+def main():
+ argv = sys.argv[1:]
+
+ if argv and argv[0] == '--list':
+ entries = load()
+ for e in entries:
+ art = e.get('cover', '')
+ print(' %-12s %s%s' % (e['id'], e['name'],
+ (' [%s]' % art) if art else ''))
+ print('%d entries' % len(entries))
+ return
+
+ if argv and argv[0] == '--remove':
+ if len(argv) < 2:
+ sys.exit('usage: add_custom_title.py --remove TITLEID')
+ tid = argv[1].upper()
+ entries = load()
+ kept = [e for e in entries if e['id'].upper() != tid]
+ if len(kept) == len(entries):
+ print('%s not in the list' % tid)
+ return
+ save(kept)
+ print('removed %s (%d entries left)' % (tid, len(kept)))
+ return
+
+ if argv:
+ sys.exit('usage: add_custom_title.py [--list | --remove TITLEID]')
+
+ # --- interactive add ---------------------------------------------------
+ tid = input('TITLEID: ').strip().upper()
+ name = input('Name: ').strip()
+ art = input('Cover URL (blank for none): ').strip()
+
+ entry = {'id': tid, 'name': name}
+ if art:
+ entry['cover'] = art
+
+ entries = [e for e in load() if e['id'].upper() != tid]
+ entries.append(entry)
+ save(entries)
+
+ print('%s -> %s' % (tid, name))
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file