Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Extract param.json from the PS5 pkg link and display some information.
#!/usr/bin/env python3
### Extract param.json from the PS5 pkg link and display some information.
###
### [Usage] show_ps5_pkg_metadata.py [-h] [--output] [URL of PS5 pkg (version.xml / .json / .DP.pkg / .sc.pkg)]
### [Examples]
### show_ps5_pkg_metadata.py https://sgst.prod.dl.playstation.net/sgst/prod/00/np/PPSA01280_00/d8ec167a-59da-4e54-8e2c-1161c706516a-version.xml
### show_ps5_pkg_metadata.py http://gst.prod.dl.playstation.net/gst/prod/00/PPSA01280_00/app/pkg/14/f_79cafe1a822dd62c55ebb4d08844deafe89d9e42366ddd8ade1d54de8f2f8eac/IP9100-PPSA01280_00-SFSRELE000000100-DP.pkg
### show_ps5_pkg_metadata.py https://sgst.prod.dl.playstation.net/sgst/prod/00/PPSA01280_00/app/info/13/f_504bd9d060d0861ae60bc680146dba2093041c29da446558392446d6eecc7330/IP9100-PPSA01280_00-SFSRELE000000100_sc.pkg
### show_ps5_pkg_metadata.py --output https://sgst.prod.dl.playstation.net/sgst/prod/00/np/PPSA01280_00/d8ec167a-59da-4e54-8e2c-1161c706516a-version.xml
import json
import urllib.request
import urllib.error
import sys
import ssl
import pprint
import argparse
import xml.etree.ElementTree as ET
ssl._create_default_https_context = ssl._create_unverified_context
def get_param_json(url, output=False):
# URLの正当性を検証
url = url.strip()
#if 'gst.prod.dl.playstation.net' not in url:
# print(f'ERROR! Not PS5 pkg link')
# sys.exit(-1)
if 'version.xml' in url:
try:
with urllib.request.urlopen(url) as res:
xml_data = res.read()
except urllib.error.HTTPError as err:
if err.code == 404:
print(f'error {err.code}')
return
elif err.code == 403:
snoretoast('PS5 XML Check', f'ERROR! http_code: {err.code}')
print(f'ERROR! http_code: {err.code}')
sys.exit(-1)
except urllib.error.URLError as err:
print(f'error {err}')
return
url = parse_ps5_xml(xml_data)[2]
url = url.replace('.json', '_sc.pkg')
if '.json' in url:
url = url.replace('.json', '_sc.pkg')
if 'DP.pkg' not in url and 'sc.pkg' not in url:
print(f'ERROR! Not PS5 pkg link')
sys.exit(-1)
# ファイルを64KBのみダウンロード
chunk_size = 1024 * 64
try:
with urllib.request.urlopen(url) as res:
chunk = res.read(chunk_size)
except urllib.error.HTTPError as err:
if err.code == 404:
print(f'error {err.code}')
elif err.code == 403:
snoretoast('PS5 XML Check', f'ERROR! http_code: {err.code}')
print(f'ERROR! http_code: {err.code}')
sys.exit(-1)
except urllib.error.URLError as err:
print(f'error {err}')
json_data = extract_param_json(chunk)
if output:
out_file = 'param.json'
with open(out_file, mode='wb') as f:
f.write(json_data)
return json.loads(json_data)
def parse_ps5_xml(xml_data):
root = ET.fromstring(xml_data)
tag_index = len(root[0]) - 1
content_id = root[0].attrib['content_id']
content_ver = root[0][tag_index].attrib['content_ver']
manifest_url = root[0][tag_index].attrib['manifest_url']
system_ver = root[0][tag_index].attrib['system_ver']
try:
delta_url = root[0][0].attrib['delta_url']
delta_url_titileId = delta_url.split('/')[-1][7:19]
except KeyError:
delta_url = None
delta_url_titileId = None
system_ver_hex = f'{int(system_ver):x}'
fw_version = '0' + '.'.join([system_ver_hex[0], system_ver_hex[1:3], system_ver_hex[3:5], system_ver_hex[5:7]])
return content_id, content_ver, manifest_url, fw_version, delta_url, delta_url_titileId
def extract_param_json(data):
# "param.json" の位置を探す
find_str = 'param.json'.encode()
temp_position = data.find(find_str)
find_str = b'\x7b\x0d\x0a'
start_position = data.find(find_str, temp_position)
find_str = 'version.xml'.encode()
temp_position = data.find(find_str)
# 0x7D 0D 0A json終端部分を探す
find_str = b'\x7d\x0d\x0a'
end_position = data.find(find_str, temp_position) + 3
json_data = data[start_position:end_position]
return json_data
def print_param(param_json):
print('-'*100)
pprint.pprint(param_json)
param_json = adjust_param_value(param_json)
target_keys = ['titleId',
'contentId',
'defaultLanguage',
'titleName',
'applicationCategoryType',
'applicationDrmType',
'attribute',
'attribute2',
'attribute3',
'userDefinedParam1',
'userDefinedParam2',
'userDefinedParam3',
'userDefinedParam4',
'contentVersion',
'targetContentVersion',
'masterVersion',
'requiredSystemSoftwareVersion',
'sdkVersion',
'creationDate',
'versionFileUri']
print('-'*100)
for key in target_keys:
try:
value = param_json[key]
except KeyError:
value = None
print(f'{key: <32} = {value}')
print('-'*100)
def adjust_param_value(param_json):
# Sub Keyの取得
param_json['defaultLanguage'] = param_json['localizedParameters']['defaultLanguage']
param_json['titleName'] = param_json['localizedParameters'][param_json['defaultLanguage']]['titleName']
param_json['creationDate'] = param_json['pubtools']['creationDate']
param_json['toolVersion'] = param_json['pubtools']['toolVersion']
# 値の変換
param_json['versionFileUri'] = param_json['versionFileUri'].strip()
param_json['titleId'] = param_json['titleId'] + '_00'
fw = param_json['requiredSystemSoftwareVersion']
param_json['requiredSystemSoftwareVersion'] = '.'.join([fw[2:4], fw[4:6], fw[6:8], fw[8:10]]) + '-' + '.'.join([fw[10:12], fw[12:14], fw[14:16], fw[16:17], fw[17:18]])
try:
param_json['attribute'] = hex(param_json['attribute'])
param_json['attribute2'] = hex(param_json['attribute2'])
param_json['attribute3'] = hex(param_json['attribute3'])
sdk = param_json['sdkVersion']
param_json['sdkVersion'] = '.'.join([sdk[2:4], sdk[4:6], sdk[6:8], sdk[8:10]]) + '-' + '.'.join([sdk[10:12], sdk[12:14], sdk[14:16], sdk[16:17], sdk[17:18]])
except Exception:
pass
return param_json
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Show PS5 Pkg Metadata')
parser.add_argument('url', help='URL of PS5 pkg (version.xml / .json / .DP.pkg / _sc.pkg)')
parser.add_argument('--output', action='store_true', help='Output param.json file to the same folder in the script')
args = parser.parse_args()
param_json = get_param_json(args.url, args.output)
print_param(param_json)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment