forked from katboi01/JapariBypass
Added login token persistence. Optimized update process
This commit is contained in:
parent
50144307ac
commit
ed77fc6b44
114
dmmBypass.py
114
dmmBypass.py
|
@ -2,17 +2,27 @@ import os
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import pathlib
|
||||||
import argparse
|
import argparse
|
||||||
import requests
|
import requests
|
||||||
import subprocess
|
import subprocess
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
from uuid import getnode
|
from uuid import getnode
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description='DMM bypass script')
|
parser = argparse.ArgumentParser(description='DMM bypass script')
|
||||||
parser.add_argument('-g', '--game', type=str, help="DMM code name of the game", default="kfp2g")
|
parser.add_argument('-g', '--game', type=str, help="DMM code name of the game", default="kfp2g")
|
||||||
parser.add_argument('-u', '--update', help="Check for game update before launching", action='store_true')
|
parser.add_argument('-u', '--update', help="Check for game update before launching", action='store_true')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
def load_config(config_name):
|
||||||
|
with open(config_name, "rt", encoding="utf-8") as f:
|
||||||
|
return json.load(f)
|
||||||
|
|
||||||
|
def save_config(config_name, config):
|
||||||
|
with open(config_name, 'wt', encoding="utf-8") as f:
|
||||||
|
json.dump(config, f, indent=1, ensure_ascii=False)
|
||||||
|
|
||||||
config_name = args.game + '.cfg'
|
config_name = args.game + '.cfg'
|
||||||
|
|
||||||
if not os.path.exists(config_name):
|
if not os.path.exists(config_name):
|
||||||
|
@ -24,11 +34,9 @@ if not os.path.exists(config_name):
|
||||||
"dmm_password" : input('DMM Password: '),
|
"dmm_password" : input('DMM Password: '),
|
||||||
"use_proxy" : input('Your login tokens will be sent through my VPN machine.\nIs that okay? (yes/no): ').lower() == "yes"
|
"use_proxy" : input('Your login tokens will be sent through my VPN machine.\nIs that okay? (yes/no): ').lower() == "yes"
|
||||||
}
|
}
|
||||||
with open(config_name, 'wt', encoding="utf-8") as f:
|
save_config(config_name, config)
|
||||||
json.dump(config, f, indent=1, ensure_ascii=False)
|
|
||||||
else:
|
else:
|
||||||
with open(config_name, "rt", encoding="utf-8") as f:
|
config = load_config(config_name)
|
||||||
config = json.load(f)
|
|
||||||
print(f'Loaded settings from {config_name}')
|
print(f'Loaded settings from {config_name}')
|
||||||
|
|
||||||
config["update_game"] = args.update
|
config["update_game"] = args.update
|
||||||
|
@ -47,6 +55,20 @@ def get_mac():
|
||||||
mac = getnode()
|
mac = getnode()
|
||||||
return ':'.join(("%012X" % mac)[i:i+2] for i in range(0, 12, 2)).lower()
|
return ':'.join(("%012X" % mac)[i:i+2] for i in range(0, 12, 2)).lower()
|
||||||
|
|
||||||
|
def get_game_root_path(user_path, game_dir, exe_name):
|
||||||
|
"""Return valid game install directory"""
|
||||||
|
path = pathlib.Path(user_path)
|
||||||
|
parts = list(path.parts)
|
||||||
|
if path.name == exe_name:
|
||||||
|
return str(path.parent.resolve())
|
||||||
|
elif game_dir in parts:
|
||||||
|
#get index of last occurence of game_dir in user path (+1)
|
||||||
|
dir_idx = len(parts) - parts[::-1].index(game_dir)
|
||||||
|
parts = parts[:dir_idx]
|
||||||
|
return str(pathlib.Path(*parts).resolve())
|
||||||
|
else:
|
||||||
|
raise Exception(f"Path to game files is incorrect! Ensure it points to {exe_name} or contains {game_dir}!")
|
||||||
|
|
||||||
def retrieve_login_token(session : requests.Session):
|
def retrieve_login_token(session : requests.Session):
|
||||||
try:
|
try:
|
||||||
print("Retrieving login form")
|
print("Retrieving login form")
|
||||||
|
@ -79,7 +101,7 @@ def retrieve_auth_keys(login, password, token, captcha, session : requests.Sessi
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Failed to log in:", e)
|
print("Failed to log in:", e)
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
def agree_to_game_terms(game_id, use_proxy):
|
def agree_to_game_terms(game_id, use_proxy):
|
||||||
try:
|
try:
|
||||||
print("Accepting updated game terms of use")
|
print("Accepting updated game terms of use")
|
||||||
|
@ -121,52 +143,82 @@ def retrieve_launch_params(game_id, mac_addr, hdd_serial, motherboard, login_sec
|
||||||
elif result_json["result_code"] != 100:
|
elif result_json["result_code"] != 100:
|
||||||
raise Exception(f'{result_json["result_code"]}: {result_json["error"]}')
|
raise Exception(f'{result_json["result_code"]}: {result_json["error"]}')
|
||||||
data = result_json["data"]
|
data = result_json["data"]
|
||||||
if update_game:
|
return data
|
||||||
game_version = data["latest_version"]
|
|
||||||
print("Latest version:", game_version)
|
|
||||||
file_list_params = "?" + data["sign"].replace(";", "&").replace("CloudFront-", "")
|
|
||||||
return data["execute_args"], data["file_list_url"], file_list_params
|
|
||||||
else:
|
|
||||||
return data["execute_args"], None, None
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Failed to retrieve launch arguments:", e)
|
print("Failed to retrieve launch arguments:", e)
|
||||||
|
|
||||||
def main(config):
|
def main(config):
|
||||||
game_id = config["game_id"]
|
#required arguments
|
||||||
|
game_id = config["game_id"]
|
||||||
exe_location = config["file_path"]
|
exe_location = config["file_path"]
|
||||||
login = urllib.parse.quote_plus(config["dmm_login"])
|
login = urllib.parse.quote_plus(config["dmm_login"])
|
||||||
password = urllib.parse.quote_plus(config["dmm_password"])
|
password = urllib.parse.quote_plus(config["dmm_password"])
|
||||||
update_game = config["update_game"] if "update_game" in config else False
|
#optional arguments
|
||||||
use_proxy = config["use_proxy"] if "use_proxy" in config else False
|
update_game = config.get("update_game", False)
|
||||||
|
use_proxy = config.get("use_proxy", False)
|
||||||
|
saved_login = config.get("saved_login", None)
|
||||||
|
#dmm requires these values
|
||||||
mac_addr = get_mac()
|
mac_addr = get_mac()
|
||||||
hdd_serial = get_hash('')
|
hdd_serial = get_hash('') #DMM sends an empty hash as well
|
||||||
#actual moterboard serial is unknown, but this works
|
motherboard = get_hash(getnode()) #actual moterboard serial is unknown, but this works
|
||||||
motherboard = get_hash(getnode())
|
|
||||||
|
current_time = datetime.now()
|
||||||
|
|
||||||
with requests.Session() as session:
|
with requests.Session() as session:
|
||||||
token = retrieve_login_token(session)
|
if saved_login is not None:
|
||||||
captcha = retrieve_captcha_token()
|
login_expiration = datetime.fromtimestamp(saved_login["expiration"])
|
||||||
|
if login_expiration < current_time:
|
||||||
|
print("Login data has expired")
|
||||||
|
saved_login = None
|
||||||
|
|
||||||
if token == None or captcha == None:
|
if saved_login is not None:
|
||||||
return
|
login_secure, login_session = saved_login["login_secure"], saved_login["login_session"]
|
||||||
|
print("Loaded login data from previous session")
|
||||||
|
else:
|
||||||
|
token = retrieve_login_token(session)
|
||||||
|
captcha = retrieve_captcha_token()
|
||||||
|
|
||||||
login_secure, login_session = retrieve_auth_keys(login, password, token, captcha, session)
|
if token == None or captcha == None:
|
||||||
if login_secure == None or login_session == None:
|
return
|
||||||
return
|
|
||||||
|
login_secure, login_session = retrieve_auth_keys(login, password, token, captcha, session)
|
||||||
|
if login_secure == None or login_session == None:
|
||||||
|
return
|
||||||
|
|
||||||
|
login_expiration = int((current_time + timedelta(days=364)).timestamp()) #expire in less than a year
|
||||||
|
config["saved_login"] = saved_login = {"login_secure" : login_secure, "login_session" : login_session, "expiration": login_expiration}
|
||||||
|
del(config["update_game"])
|
||||||
|
save_config(config_name, config)
|
||||||
|
|
||||||
if not use_proxy: input("Enable VPN now and press Enter")
|
if not use_proxy: input("Enable VPN now and press Enter")
|
||||||
|
|
||||||
execute_args, file_list_url, file_access_params = retrieve_launch_params(game_id, mac_addr, hdd_serial, motherboard, login_secure, login_session, use_proxy, update_game)
|
#execute_args, file_list_url, file_access_params
|
||||||
if execute_args == None or (update_game and (file_list_url == None or file_access_params == None)):
|
launch_data : dict = retrieve_launch_params(game_id, mac_addr, hdd_serial, motherboard, login_secure, login_session, use_proxy, update_game)
|
||||||
|
|
||||||
|
execute_args : str = launch_data.get("execute_args", None)
|
||||||
|
if execute_args == None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if update_game:
|
||||||
|
file_list_url = launch_data.get("file_list_url", None)
|
||||||
|
file_list_params : str = launch_data.get("sign", None)
|
||||||
|
if file_list_url == None or file_list_params == None:
|
||||||
|
return
|
||||||
|
|
||||||
|
print("Latest version:", launch_data["latest_version"])
|
||||||
|
file_list_params = "?" + file_list_params.replace(";", "&").replace("CloudFront-", "")
|
||||||
|
|
||||||
|
exec_name = launch_data["exec_file_name"]
|
||||||
|
install_dir = launch_data["install_dir"]
|
||||||
|
game_root_path = get_game_root_path(exe_location, install_dir, exec_name)
|
||||||
|
|
||||||
if not use_proxy: input("Disable VPN now and press Enter")
|
if not use_proxy: input("Disable VPN now and press Enter")
|
||||||
|
|
||||||
if update_game:
|
if update_game:
|
||||||
dmmUpdater.update_game(os.path.dirname(exe_location), file_list_url, file_access_params)
|
dmmUpdater.update_game(game_root_path, file_list_url, file_list_params)
|
||||||
|
|
||||||
print("Starting game")
|
print("Starting game")
|
||||||
args = [exe_location] + execute_args.split()
|
args = [os.path.join(game_root_path, exec_name)] + execute_args.split()
|
||||||
print(args)
|
print(args)
|
||||||
subprocess.Popen(args, start_new_session=True)
|
subprocess.Popen(args, start_new_session=True)
|
||||||
input("Done. Press enter to exit (this will close the game)")
|
input("Done. Press enter to exit (this will close the game)")
|
||||||
|
|
|
@ -4,6 +4,7 @@ import hashlib
|
||||||
import requests
|
import requests
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
from urllib.request import urlretrieve
|
from urllib.request import urlretrieve
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
def get_file_list(url):
|
def get_file_list(url):
|
||||||
url = "https://apidgp-gameplayer.games.dmm.com" + url
|
url = "https://apidgp-gameplayer.games.dmm.com" + url
|
||||||
|
@ -27,33 +28,28 @@ def get_file_hash(file_path):
|
||||||
def update_game(game_path, files_url, files_param):
|
def update_game(game_path, files_url, files_param):
|
||||||
print("Updating game")
|
print("Updating game")
|
||||||
server_url, server_files = get_file_list(files_url)
|
server_url, server_files = get_file_list(files_url)
|
||||||
server_file_dict = {file["local_path"]: file for file in server_files}
|
server_file_dict = {str(Path(game_path, file["local_path"].lstrip('/')).resolve()): file for file in server_files}
|
||||||
|
local_file_dict = {str(Path(dp, f).resolve()): "" for dp, dn, filenames in os.walk(game_path) for f in filenames}
|
||||||
local_files = [os.path.join(dp, f).replace("\\", "/") for dp, dn, filenames in os.walk(game_path) for f in filenames]
|
|
||||||
local_file_dict = {"/" + os.path.relpath(r, game_path).replace("\\", "/"): {"abs_path":r, "hash":""} for r in local_files}
|
|
||||||
|
|
||||||
files_to_download = []
|
files_to_download = []
|
||||||
files_to_delete = []
|
files_to_delete = []
|
||||||
for server_file_key in server_file_dict.keys():
|
for abs_file_path in server_file_dict.keys():
|
||||||
server_file = server_file_dict[server_file_key]
|
server_file = server_file_dict[abs_file_path]
|
||||||
|
|
||||||
if server_file_key in local_file_dict:
|
if abs_file_path in local_file_dict:
|
||||||
local_file = local_file_dict[server_file_key]
|
|
||||||
if server_file["force_delete_flg"]:
|
if server_file["force_delete_flg"]:
|
||||||
files_to_delete.append(local_file["abs_path"])
|
files_to_delete.append(abs_file_path)
|
||||||
else:
|
else:
|
||||||
local_file["hash"] = get_file_hash(local_file["abs_path"])
|
local_file_hash = get_file_hash(abs_file_path)
|
||||||
|
|
||||||
if server_file["check_hash_flg"] and local_file["hash"] == server_file["hash"]:
|
if server_file["check_hash_flg"] and local_file_hash == server_file["hash"]:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
download_url = urllib.parse.urljoin(server_url, server_file["path"]) + files_param
|
download_url = urllib.parse.urljoin(server_url, server_file["path"]) + files_param
|
||||||
download_path = game_path.replace("\\", "/") + server_file_key
|
files_to_download.append({"url":download_url, "path":abs_file_path})
|
||||||
files_to_download.append({"url":download_url, "path":download_path})
|
|
||||||
else:
|
else:
|
||||||
download_url = urllib.parse.urljoin(server_url, server_file["path"]) + files_param
|
download_url = urllib.parse.urljoin(server_url, server_file["path"]) + files_param
|
||||||
download_path = game_path.replace("\\", "/") + server_file_key
|
files_to_download.append({"url":download_url, "path":abs_file_path})
|
||||||
files_to_download.append({"url":download_url, "path":download_path})
|
|
||||||
|
|
||||||
print("Files to download:", len(files_to_download))
|
print("Files to download:", len(files_to_download))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue