1
0
Fork 0

Compare commits

..

3 Commits

2 changed files with 117 additions and 63 deletions

View File

@ -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")
@ -80,59 +102,79 @@ def retrieve_auth_keys(login, password, token, captcha, session : requests.Sessi
print("Failed to log in:", e) print("Failed to log in:", e)
return None, None return None, None
def retrieve_update_params(game_id, login_secure, login_session, use_proxy): def agree_to_game_terms(game_id, use_proxy):
try: try:
print("Retrieving update file list") print("Accepting updated game terms of use")
data = {"product_id":game_id,"game_type":"GCL","game_os":"win"}
headers = {"User-Agent": "DMMGamePlayer5-Win/5.3.12 Electron/32.1.0", "Client-App": "DMMGamePlayer5", "Client-version": "5.3.12", "Content-Type": "application/json"} headers = {"User-Agent": "DMMGamePlayer5-Win/5.3.12 Electron/32.1.0", "Client-App": "DMMGamePlayer5", "Client-version": "5.3.12", "Content-Type": "application/json"}
cookies = {"login_secure_id":login_secure, "login_session_id":login_session} url = "" if use_proxy else "https://apidgp-gameplayer.games.dmm.com/v5/agreement/confirm/client"
url = "https://katworks.sytes.net/KF/Api/DMM/filelist" if use_proxy else "https://apidgp-gameplayer.games.dmm.com/v5/r2/filelist/cl" data = {"product_id":game_id,"is_notification":False,"is_myapp":False}
result = requests.post(url, cookies=cookies, headers=headers, json=data) result = requests.post(url, headers=headers, json=data)
result.raise_for_status() result.raise_for_status()
result_json = result.json() result_json = result.json()
if result_json["result_code"] != 100: if 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"] return True
game_version = data["latest_version"]
print("Latest version:", game_version)
file_list_url = data["file_list_url"]
file_list_params = data["sign"]
file_list_params = "?" + file_list_params.replace(";", "&").replace("CloudFront-", "")
return file_list_url, file_list_params
except Exception as e: except Exception as e:
print("Failed to retrieve update file list:", e) print("Failed to accept terms of use:", e)
return None, None return False
def retrieve_launch_params(game_id, mac_addr, hdd_serial, motherboard, login_secure, login_session, use_proxy): def retrieve_launch_params(game_id, mac_addr, hdd_serial, motherboard, login_secure, login_session, use_proxy, update_game):
try: try:
print("Retrieving launch arguments") print("Retrieving launch arguments")
data = {"product_id":game_id,"game_type":"GCL","game_os":"win","launch_type":"LIB","mac_address":mac_addr,"hdd_serial":hdd_serial,"motherboard":motherboard,"user_os":"win"} data = {"product_id":game_id,"game_type":"GCL","game_os":"win","launch_type":"LIB","mac_address":mac_addr,"hdd_serial":hdd_serial,"motherboard":motherboard,"user_os":"win"}
headers = {"User-Agent": "DMMGamePlayer5-Win/5.3.12 Electron/32.1.0", "Client-App": "DMMGamePlayer5", "Client-version": "5.3.12", "Content-Type": "application/json"} headers = {"User-Agent": "DMMGamePlayer5-Win/5.3.12 Electron/32.1.0", "Client-App": "DMMGamePlayer5", "Client-version": "5.3.12", "Content-Type": "application/json"}
cookies = {"login_secure_id":login_secure, "login_session_id":login_session} cookies = {"login_secure_id":login_secure, "login_session_id":login_session}
url = "https://katworks.sytes.net/KF/Api/DMM/launch" if use_proxy else "https://apidgp-gameplayer.games.dmm.com/v5/r2/launch/cl" if update_game:
url = "https://katworks.sytes.net/KF/Api/DMM/update" if use_proxy else "https://apidgp-gameplayer.games.dmm.com/v5/r2/launch/cl"
else:
url = "https://katworks.sytes.net/KF/Api/DMM/launch" if use_proxy else "https://apidgp-gameplayer.games.dmm.com/v5/launch/cl"
result = requests.post(url, cookies=cookies, headers=headers, json=data)
result.raise_for_status()
result_json = result.json()
if result_json["result_code"] == 308:
if agree_to_game_terms(game_id, use_proxy):
result = requests.post(url, cookies=cookies, headers=headers, json=data) result = requests.post(url, cookies=cookies, headers=headers, json=data)
result.raise_for_status() result.raise_for_status()
result_json = result.json() result_json = result.json()
if result_json["result_code"] != 100: if 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"]}')
else:
raise Exception("Failed to agree to updated game terms of use. Use the DMM app to confirm.")
elif result_json["result_code"] != 100:
raise Exception(f'{result_json["result_code"]}: {result_json["error"]}')
data = result_json["data"] data = result_json["data"]
return data["execute_args"] return data
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):
#required arguments
game_id = config["game_id"] 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:
if saved_login is not None:
login_expiration = datetime.fromtimestamp(saved_login["expiration"])
if login_expiration < current_time:
print("Login data has expired")
saved_login = None
if saved_login is not None:
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) token = retrieve_login_token(session)
captcha = retrieve_captcha_token() captcha = retrieve_captcha_token()
@ -143,24 +185,40 @@ def main(config):
if login_secure == None or login_session == None: if login_secure == None or login_session == None:
return 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")
if update_game: #execute_args, file_list_url, file_access_params
file_list_url, file_access_params = retrieve_update_params(game_id, login_secure, login_session, use_proxy) launch_data : dict = retrieve_launch_params(game_id, mac_addr, hdd_serial, motherboard, login_secure, login_session, use_proxy, update_game)
if file_list_url == None or file_access_params == None:
return
execute_args = retrieve_launch_params(game_id, mac_addr, hdd_serial, motherboard, login_secure, login_session, use_proxy) execute_args : str = launch_data.get("execute_args", None)
if 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)")

View File

@ -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))