mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-14 23:10:33 +02:00
- Fixes #18091 The upstream part hadn't been synced since 2023. Tools/update_gamecontrollerdb.py replaces it with the current upstream file and keeps PPSSPP's own mappings, dropping those whose GUID upstream now has (after SDL's old-GUID conversion), since a later line overrides an earlier one. 193 of 207 were covered, including the PS3 and 8BitDo Ultimate 2C one-offs. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
136 lines
4.6 KiB
Python
136 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
|
# Copyright (c) 2012- PPSSPP Project.
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, version 2.0 or later versions.
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License 2.0 for more details.
|
|
|
|
# A copy of the GPL 2.0 should have been included with the program.
|
|
# If not, see http://www.gnu.org/licenses/
|
|
|
|
# Official git repository and contact information can be found at
|
|
# https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
|
|
"""Re-sync assets/gamecontrollerdb.txt with the community SDL_GameControllerDB.
|
|
|
|
The file is upstream's database followed by PPSSPP's own mappings, which start at
|
|
PPSSPP_MARKER. This replaces the upstream part with the current upstream file and
|
|
keeps the PPSSPP mappings, except those whose GUID (on the same platform) upstream
|
|
now has. SDL lets a later mapping replace an earlier one, so a stale PPSSPP line
|
|
would otherwise override upstream's.
|
|
|
|
Usage: python Tools/update_gamecontrollerdb.py [--upstream FILE]
|
|
"""
|
|
|
|
import argparse
|
|
import os
|
|
import sys
|
|
import urllib.request
|
|
|
|
UPSTREAM_URL = "https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/master/gamecontrollerdb.txt"
|
|
PPSSPP_MARKER = "# The below is from PPSSPP's old gamecontrollerdb.txt."
|
|
|
|
DB_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "assets", "gamecontrollerdb.txt")
|
|
|
|
# Section comments in the PPSSPP part, for lines without a platform: field.
|
|
SECTION_PLATFORMS = {
|
|
"# Windows": "Windows",
|
|
"# OS X": "Mac OS X",
|
|
"# Mac OS X": "Mac OS X",
|
|
"# Linux": "Linux",
|
|
}
|
|
|
|
|
|
def normalize_guid(guid, platform):
|
|
"""Converts pre-2.0.5 GUIDs the way SDL_PrivateGetGamepadGUIDFromMappingString() does."""
|
|
guid = guid.lower()
|
|
if len(guid) != 32:
|
|
return guid
|
|
if platform == "Windows" and guid[20:] == "504944564944":
|
|
return "03000000" + guid[0:4] + "0000" + guid[4:8] + "000000000000"
|
|
if platform == "Mac OS X" and guid[4:16] == "000000000000" and guid[20:] == "000000000000":
|
|
return "03000000" + guid[0:4] + "0000" + guid[16:20] + "000000000000"
|
|
return guid
|
|
|
|
|
|
def parse_mapping(line, section_platform):
|
|
"""Returns (guid, platform) for a mapping line, or None for comments and blanks."""
|
|
line = line.strip()
|
|
if not line or line.startswith("#"):
|
|
return None
|
|
fields = line.split(",")
|
|
platform = section_platform
|
|
for field in fields[2:]:
|
|
if field.startswith("platform:"):
|
|
platform = field[len("platform:"):]
|
|
return (normalize_guid(fields[0], platform), platform)
|
|
|
|
|
|
def section_platform_of(line, current):
|
|
for prefix, platform in SECTION_PLATFORMS.items():
|
|
if line.startswith(prefix):
|
|
return platform
|
|
return current
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
parser.add_argument("--upstream", help="Use this local copy of upstream's gamecontrollerdb.txt instead of downloading it")
|
|
args = parser.parse_args()
|
|
|
|
with open(DB_PATH, "r", encoding="utf-8", newline="") as f:
|
|
current = f.read()
|
|
newline = "\r\n" if "\r\n" in current else "\n"
|
|
current_lines = current.splitlines()
|
|
|
|
if PPSSPP_MARKER not in current_lines:
|
|
print("Marker line not found in %s, giving up" % DB_PATH, file=sys.stderr)
|
|
return 1
|
|
ppsspp_lines = current_lines[current_lines.index(PPSSPP_MARKER):]
|
|
|
|
if args.upstream:
|
|
with open(args.upstream, "r", encoding="utf-8") as f:
|
|
upstream = f.read()
|
|
else:
|
|
with urllib.request.urlopen(UPSTREAM_URL) as response:
|
|
upstream = response.read().decode("utf-8")
|
|
upstream_lines = upstream.splitlines()
|
|
while upstream_lines and not upstream_lines[-1].strip():
|
|
upstream_lines.pop()
|
|
|
|
upstream_keys = set()
|
|
platform = None
|
|
for line in upstream_lines:
|
|
platform = section_platform_of(line, platform)
|
|
key = parse_mapping(line, platform)
|
|
if key:
|
|
upstream_keys.add(key)
|
|
|
|
kept = []
|
|
dropped = []
|
|
platform = None
|
|
for line in ppsspp_lines:
|
|
platform = section_platform_of(line, platform)
|
|
key = parse_mapping(line, platform)
|
|
if key and key in upstream_keys:
|
|
dropped.append(line)
|
|
else:
|
|
kept.append(line)
|
|
|
|
with open(DB_PATH, "w", encoding="utf-8", newline="") as f:
|
|
f.write(newline.join(upstream_lines + [""] + kept) + newline)
|
|
|
|
print("Upstream: %d mappings. PPSSPP: kept %d lines, dropped %d now covered upstream:" % (len(upstream_keys), len(kept), len(dropped)))
|
|
for line in dropped:
|
|
print(" " + line.split(",")[0] + " " + line.split(",")[1])
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|