diff --git a/README.md b/README.md index 64632c4..52f9805 100644 --- a/README.md +++ b/README.md @@ -1 +1,32 @@ -# PS4-Database-Rebuilder-Extended-2.0 \ No newline at end of file +# PS4_db_Rebuilder_EXT + +Ps4 built-in database rebuilder has the tendency to remove fpkg from the database. This will repopulate the database with them! + +This repository is a modification of the aizenar repository (https://github.com/aizenar/PS4_db_Rebuilder_EXT) + +## Requirement + +- Python (3.0+) + +## Instructions + +1) Recursively clone (`git clone --recursive`) this repo or downdload a release. +2) Start FTP server on the PS4 +3) Run the python script through terminal/cmdline: + + | Version | Command | + |--|--| + | 5.05 | `python3 fix_db.py [PS4_IP] --port [PS4_FTP_PORT] --fw 5.05` | + | 6.72 - 9.00 | `python3 fix_db.py [PS4_IP] --port [PS4_FTP_PORT] --fw 6.72` | + | 11.00 | `python3 fix_db.py [PS4_IP] --port [PS4_FTP_PORT]` | + +4) Wait for the script to finish, then logout of the PS4 user without closing the browser + +5) Log back in and all your games should be there again + +Note: For 11.00 you still need to use wifi you cant use pi-pwn for FTP. + +## Changes + +1) Original Repo https://github.com/aizenar/PS4_db_Rebuilder_EXT +2) Update fix_db.py so now it supports 11.00 firmware. diff --git a/appinfo.py b/appinfo.py new file mode 100644 index 0000000..3a671fb --- /dev/null +++ b/appinfo.py @@ -0,0 +1,66 @@ +_appinfo = {} + +_appinfo["#_access_index"] = "67" +_appinfo["#_last_access_time"] = "2018-07-27 15:04:39.822" +_appinfo["#_contents_status"] = "0" +_appinfo["#_mtime"] = "2018-07-27 15:04:40.635" +_appinfo["#_update_index"] = "74" +_appinfo["#exit_type"] = "0" +_appinfo["ATTRIBUTE_INTERNAL"] = "0" +_appinfo["DISP_LOCATION_1"] = "0" +_appinfo["DISP_LOCATION_2"] = "0" +_appinfo["DOWNLOAD_DATA_SIZE"] = "0" +_appinfo["FORMAT"] = "obs" +_appinfo["PARENTAL_LEVEL"] = "1" +_appinfo["SERVICE_ID_ADDCONT_ADD_1"] = "0" +_appinfo["SERVICE_ID_ADDCONT_ADD_2"] = "0" +_appinfo["SERVICE_ID_ADDCONT_ADD_3"] = "0" +_appinfo["SERVICE_ID_ADDCONT_ADD_4"] = "0" +_appinfo["SERVICE_ID_ADDCONT_ADD_5"] = "0" +_appinfo["SERVICE_ID_ADDCONT_ADD_6"] = "0" +_appinfo["SERVICE_ID_ADDCONT_ADD_7"] = "0" +_appinfo["SYSTEM_VER"] = "33751040" +_appinfo["USER_DEFINED_PARAM_1"] = "0" +_appinfo["USER_DEFINED_PARAM_2"] = "0" +_appinfo["USER_DEFINED_PARAM_3"] = "0" +_appinfo["USER_DEFINED_PARAM_4"] = "0" +_appinfo["_contents_ext_type"] = "0" +_appinfo["_contents_location"] = "0" +_appinfo["_current_slot"] = "0" +_appinfo["_disable_live_detail"] = "0" +_appinfo["_hdd_location"] = "0" +_appinfo["_path_info"] = "3113537756987392" +_appinfo["_path_info_2"] = "0" +_appinfo["_size_other_hdd"] = "0" +_appinfo["_sort_priority"] = "100" +_appinfo["_uninstallable"] = "1" +_appinfo["_view_category"] = "0" +_appinfo["_working_status"] = "0" + +def get_pseudo_appinfo(sfo, size) : + copy__appinfo = _appinfo + + copy__appinfo["#_size"] = "%d" % size + copy__appinfo["_org_path"] = "/user/app/%s" % sfo["TITLE_ID"] + copy__appinfo["_metadata_path"] = "/user/appmeta/%s" % sfo["TITLE_ID"] + + copy__appinfo["APP_TYPE"] = sfo["APP_TYPE"] + copy__appinfo["APP_VER"] = sfo["APP_VER"] + copy__appinfo["ATTRIBUTE"] = sfo["ATTRIBUTE"] + copy__appinfo["CATEGORY"] = sfo["CATEGORY"] + copy__appinfo["CONTENT_ID"] = sfo["CONTENT_ID"] + copy__appinfo["TITLE"] = sfo["TITLE"] + copy__appinfo["TITLE_ID"] = sfo["TITLE_ID"] + copy__appinfo["VERSION"] = sfo["VERSION"] + + if(False) : + print(" %s" % copy__appinfo["APP_TYPE"]) + print(" %s" % copy__appinfo["APP_VER"]) + print(" %s" % copy__appinfo["ATTRIBUTE"]) + print(" %s" % copy__appinfo["CATEGORY"]) + print(" %s" % copy__appinfo["CONTENT_ID"]) + print(" %s" % copy__appinfo["TITLE"]) + print(" %s" % copy__appinfo["TITLE_ID"]) + print(" %s" % copy__appinfo["VERSION"]) + + return copy__appinfo \ No newline at end of file diff --git a/appinfo.pyc b/appinfo.pyc new file mode 100644 index 0000000..1c25dec Binary files /dev/null and b/appinfo.pyc differ diff --git a/fix_db.py b/fix_db.py new file mode 100644 index 0000000..161e868 --- /dev/null +++ b/fix_db.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from __future__ import print_function +from ftplib import FTP +import sqlite3 +import appinfo +import io +import os +import re +from sfo.sfo import SfoFile as SfoFile +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument("PS4_IP", help="PS4 ftp ip address") +parser.add_argument('--fw', default="11.00", help='currently support 5.05, 6.72, 7.02, 7.55, 9.0(?), 11.00') +parser.add_argument('--port', default="2121", help='PS4 FTP Port Number') +args = parser.parse_args() +app_db = "tmp/app.db" +PS4_IP = args.PS4_IP + +port = 2121 +port = int(args.port) + +value_format = "" +if(args.fw == "5.05"): + value_format = """("%s", "%s", "%s", "/user/appmeta/%s", "2018-07-27 15:06:46.822", "0", "0", "5", "1", "100", "0", "151", "5", "1", "gd", "0", "0", "0", "0", NULL, NULL, NULL, "%d", "2018-07-27 15:06:46.802", "0", "game", NULL, "0", "0", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "0", NULL, NULL, NULL, NULL, NULL, "0", "0", NULL, "2018-07-27 15:06:46.757")""" +elif(args.fw == "6.72"): + value_format = """("%s", "%s", "%s", "/user/appmeta/%s", "2018-07-27 15:06:46.822", "0", "0", "5", "1", "100", "0", "151","5", "1", "gd", "0", "0", "0", "0",NULL, NULL, NULL, "%d", "2018-07-27 15:06:46.802", "0", "game", NULL, "0", "0", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "0", NULL,NULL, NULL, NULL, NULL, "0", "0", NULL, "2018-07-27 15:06:46.757","0","0","0","0","0",NULL)""" +elif(args.fw == "11.00"): + value_format = """("%s", "%s", "%s", "/user/appmeta/%s", "2018-07-27 15:06:46.822", "0", "0", "5", "1", "100", "0", "151","5", "1", "gd", "0", "0", "0", "0",NULL, NULL, NULL, "%d", "2018-07-27 15:06:46.802", "0", "game", NULL, "0", "0", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "0", NULL,NULL, NULL, NULL, NULL, "0", "0", NULL, "2018-07-27 15:06:46.757","0","0","0","0","0",NULL,"0",NULL,NULL,NULL)""" +else: + value_format = """("%s", "%s", "%s", "/user/appmeta/%s", "2018-07-27 15:06:46.822", "0", "0", "5", "1", "100", "0", "151","5", "1", "gd", "0", "0", "0", "0",NULL, NULL, NULL, "%d", "2018-07-27 15:06:46.802", "0", "game", NULL, "0", "0", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "0", NULL,NULL, NULL, NULL, NULL, "0", "0", NULL, "2018-07-27 15:06:46.757","0","0","0","0","0",NULL)""" + +if not os.path.exists('tmp'): + os.makedirs('tmp') + +class CUSA : + sfo = None + size = 10000000 + is_usable = False + +info = {} +files = [] + +def sort_files(file) : + if re.search("^[A-Z]", file[-9]): + files.append("'%s'" % file[-9:]) + +def get_game_info_by_id(GameID) : + if(GameID not in info) : + info[GameID] = CUSA() + + try: + buffer = io.BytesIO() + ftp.cwd('/system_data/priv/appmeta/%s/' % GameID) + ftp.retrbinary("RETR param.sfo" , buffer.write) + buffer.seek(0) + sfo = SfoFile.from_reader(buffer) + info[GameID].sfo = sfo + info[GameID].size = ftp.size("/user/app/%s/app.pkg" % GameID) + info[GameID].is_usable = True + except Exception as e: + print("Error processing %s, ignorining..." % GameID) + print("type error: " + str(e)) + + return info[GameID] + + +ftp = FTP() +ftp.connect(PS4_IP, port, timeout=30) +ftp.login(user='username', passwd = 'password') +if(len(files) == 0) : + ftp.cwd('/user/app/') + ftp.dir(sort_files) + print(files) + + +ftp.cwd('/system_data/priv/mms/') +lf = open(app_db, "wb") +ftp.retrbinary("RETR app.db" , lf.write) +lf.close() + +conn = sqlite3.connect(app_db) + +cursor = conn.cursor() +cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'tbl_appbrowse_%%';") +tables = cursor.fetchall() + +files_joined = "SELECT %s AS titleid " % ' AS titleid UNION SELECT '.join(files) +tbl_appbrowse = [] +for tbl in tables : + tbl_appbrowse.append(tbl[0]) + print("Processing table: %s" % tbl[0]) + cursor.execute("SELECT T.titleid FROM (%s) T WHERE T.titleid NOT IN (SELECT titleid FROM %s);" % (files_joined, tbl[0])) + list_id = cursor.fetchall() + sql_list = [] + for tmp_GameID in list_id : + GameID = tmp_GameID[0].replace("'", "") + print(" Processing GameID: %s... " % GameID, end='') + cusa = get_game_info_by_id(GameID) + if(cusa.is_usable == True) : + sql_list.append(value_format + % (cusa.sfo['TITLE_ID'], cusa.sfo['CONTENT_ID'], cusa.sfo['TITLE'], cusa.sfo['TITLE_ID'], cusa.size)) + print("Completed %d" % cusa.size) + else : + print("Ignoring") + + if(len(sql_list) > 0) : + cursor.execute("INSERT INTO %s VALUES %s;" % (tbl[0], ', '.join(sql_list))) + +print('') +print('') +print('') + +print("Processing table: tbl_appinfo") + +cursor.execute("SELECT DISTINCT T.titleid FROM (SELECT titleid FROM %s) T WHERE T.titleid NOT IN (SELECT DISTINCT titleid FROM tbl_appinfo);" % (" UNION SELECT titleid FROM ".join(tbl_appbrowse))) +missing_appinfo_cusa_id = cursor.fetchall() +for tmp_cusa_id in missing_appinfo_cusa_id : + game_id = tmp_cusa_id[0] + print(" Processing GameID: %s... " % game_id, end='') + cusa = get_game_info_by_id(game_id) + if(cusa.is_usable == True) : + sql_items = appinfo.get_pseudo_appinfo(cusa.sfo, cusa.size) + for key, value in sql_items.items(): + cursor.execute("INSERT INTO tbl_appinfo (titleid, key, val) VALUES (?, ?, ?);", [game_id, key, value]) + print("Completed") + else : + print("Skipped") + +conn.commit() + +conn.close() + +ftp.cwd('/system_data/priv/mms/') +file = open(app_db,'rb') +ftp.storbinary('STOR app.db', file) +file.close()