Skip to content

Instantly share code, notes, and snippets.

@TheRadziu
Last active April 2, 2023 16:51
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save TheRadziu/b7321fdf2672197d14b87eeb2a5bd919 to your computer and use it in GitHub Desktop.
Save TheRadziu/b7321fdf2672197d14b87eeb2a5bd919 to your computer and use it in GitHub Desktop.
Easy Fake DLC / DLC without Extra Data Generator
## Easy Fake DLC / DLC without Extra Data Generator
## Version 2.1.0 (24.07.20)
## NOW UPDATED TO USE OPEN SOURCED PKGTOOL.EXE (from LibOrbisPkg) AND WITH PSS URL SUPPORT
## Written in python 3.5 by TheRadziu
import os, os.path
import sys
import json
import urllib.request
import errno
import shutil
import datetime
current_dir = os.path.dirname(os.path.abspath(__file__))
gen_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
contentid = "EP0002-CUSA00000_00-0000000000000000"
name = "NO DLC NAME"
titleid = "CUSA00000"
def mkdir_p(path):
try:
os.makedirs(path)
except OSError as exc:
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else: raise
def safe_open_w(path):
mkdir_p(os.path.dirname(path))
return open(path, 'w')
def parse_store_url(url):
url = url.replace("https://", "")
urls = url.split("/")
contentid = urls[3]
chihiro_url = 'https://store.playstation.com/store/api/chihiro/00_09_000/container/'+urls[1][-2:]+'/'+urls[1][:2]+'/999/'+urls[3]+'/'
chihiro_response = urllib.request.urlopen(chihiro_url)
chihiro_json = json.loads(chihiro_response.read().decode('utf-8'))
if chihiro_json['skus'][0]['entitlements'][0]['packageType'] != 'PS4AL':
print('!!!!!!!! WARNING !!!!!!!!\nPackage seems to be DLC WITH EXTRA DATA or a BUNDLE. Created fpkg is most likely to be unusable.\n!!!!!!!! WARNING !!!!!!!!')
if yes_no_prompt("Do you wish to continue anyway?") == False:
print('Aborting!')
sys.exit(2)
print('----------------------------------------')
contentid = chihiro_json['id']
name = chihiro_json['name']
urllib.request.urlretrieve(chihiro_json['images'][len(chihiro_json['images'])-1]['url'], "fake_dlc_temp/sce_sys/icon0.png")
return name, contentid
if not os.path.exists('PkgTool.exe') or not os.path.exists('LibOrbisPkg.dll'):
print("File or files \'PkgTool.exe.exe\', \'LibOrbisPkg.dll'\ are missing from current directory!!")
sys.exit(2)
def yes_no_prompt(question, default="yes"):
valid = {"yes": True, "y": True, "ye": True,
"no": False, "n": False}
if default is None:
prompt = " [y/n] "
elif default == "yes":
prompt = " [Y/n] "
elif default == "no":
prompt = " [y/N] "
else:
raise ValueError("invalid default answer: '%s'" % default)
while True:
print(question + prompt)
choice = input('Choice: ').lower()
if default is not None and choice == '':
return valid[default]
elif choice in valid:
return valid[choice]
else:
print('Please respond with \'yes\' or \'no\'\n(or \'y\' or \'n\').')
## Precreate some directories if needed
mkdir_p(os.path.dirname('fake_dlc_temp/sce_sys/'))
if os.path.isdir("fake_dlc_pkg"):
pass
else:
os.mkdir('fake_dlc_pkg')
## decide if from url or not:
if len(sys.argv) > 1 and "https://store.playstation.com" in sys.argv[1]:
print("!! STORE URL PARSING MODE !!\n")
res = parse_store_url(sys.argv[1])
name = res[0]
contentid = res[1]
else:
try:
contentid = sys.argv[1]
name = sys.argv[2]
urllib.request.urlretrieve("https://i.imgur.com/JeaTFEX.png", "fake_dlc_temp/sce_sys/icon0.png")
print("!! LOCAL METADATA MODE !!\n")
if len(contentid) != 36:
print("DLC CID IS TOO LONG OR TOO SHORT, IT HAS TO BE 36 CHARACTERS LONG, FOR EXAMPLE 'UP9000-CUSA00900_00-SPEXPANSIONDLC03'")
shutil.rmtree('fake_dlc_temp')
sys.exit(2)
except:
print("Usage for local metadata mode: {} {} {}\n".format(sys.argv[0], 'DLC_CID', '\"DLC_NAME\"'))
print("Usage for store url parsing mode: {} {}".format(sys.argv[0], 'https://store.playstation.com/pl-pl/product/EP0002-CUSA07399_00-CRASHNSANELEVEL2'))
shutil.rmtree('fake_dlc_temp')
sys.exit(2)
GP4_template = """<?xml version="1.0"?>
<psproject xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" fmt="gp4" version="1000">
<volume>
<volume_type>pkg_ps4_ac_data</volume_type>
<volume_ts>%s</volume_ts>
<package content_id="%s" passcode="00000000000000000000000000000000" entitlement_key="00000000000000000000000000000000" />
</volume>
<files img_no="0">
<file targ_path="sce_sys/icon0.png" orig_path="sce_sys\icon0.png" />
<file targ_path="sce_sys/param.sfo" orig_path="sce_sys\param.sfo" />
</files>
<rootdir>
<dir targ_name="sce_sys" />
</rootdir>
</psproject>""" % (gen_time, contentid)
## save template as a project file
x = safe_open_w('fake_dlc_temp/fake_dlc_project.gp4')
x.write(GP4_template)
x.close()
## make and populate param.sfo
print('SFO:')
os.system('PkgTool.exe sfo_new fake_dlc_temp\sce_sys\param.sfo')
os.system('PkgTool.exe sfo_setentry --value 0x00000000 --type integer --maxsize 4 fake_dlc_temp\sce_sys\param.sfo ATTRIBUTE')
os.system('PkgTool.exe sfo_setentry --value ac --type utf8 --maxsize 4 fake_dlc_temp\sce_sys\param.sfo CATEGORY')
os.system('PkgTool.exe sfo_setentry --value ' + contentid + ' --type utf8 --maxsize 48 fake_dlc_temp\sce_sys\param.sfo CONTENT_ID')
os.system('PkgTool.exe sfo_setentry --value obs --type utf8 --maxsize 4 fake_dlc_temp\sce_sys\param.sfo FORMAT')
os.system('PkgTool.exe sfo_setentry --value \"' + name + '\" --type utf8 --maxsize 128 fake_dlc_temp\sce_sys\param.sfo TITLE')
os.system('PkgTool.exe sfo_setentry --value ' + contentid[7:16] + ' --type utf8 --maxsize 12 fake_dlc_temp\sce_sys\param.sfo TITLE_ID')
os.system('PkgTool.exe sfo_setentry --value 01.00 --type utf8 --maxsize 8 fake_dlc_temp\sce_sys\param.sfo VERSION')
print('----------------------------------------')
## build fpkg out of generated PG4 project file
os.system('PkgTool.exe pkg_build fake_dlc_temp\\fake_dlc_project.gp4 fake_dlc_pkg')
## be a good boy and clean up after
shutil.rmtree('fake_dlc_temp')
print('----------------------------------------')
print('Everything is finished, quitting :)')
@agret
Copy link

agret commented Jun 1, 2018

Thanks for creating this, it's a huge time saver having it done with those magic command lines :)

I ported it to PowerShell and made it automatically get the DLC name from PSN server and also check the entitlements to make sure it's a DLC without exdata, you just put a list of content IDs into download_list.txt in the same folder and it will automatically create the PKGs with correct naming. (Right click it and hit Run With Powershell)

https://gist.github.com/agret/1dffa44adec2b8838f966e2f42438e6c

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment