Compare commits

..

No commits in common. "master" and "master" have entirely different histories.

2 changed files with 62 additions and 116 deletions

View File

@ -2,27 +2,17 @@ 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):
@ -34,9 +24,11 @@ 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"
} }
save_config(config_name, config) with open(config_name, 'wt', encoding="utf-8") as f:
json.dump(config, f, indent=1, ensure_ascii=False)
else: else:
config = load_config(config_name) with open(config_name, "rt", encoding="utf-8") as f:
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
@ -55,20 +47,6 @@ 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")
@ -102,123 +80,87 @@ 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 agree_to_game_terms(game_id, use_proxy): def retrieve_update_params(game_id, login_secure, login_session, use_proxy):
try: try:
print("Accepting updated game terms of use") print("Retrieving update file list")
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"}
url = "" if use_proxy else "https://apidgp-gameplayer.games.dmm.com/v5/agreement/confirm/client" cookies = {"login_secure_id":login_secure, "login_session_id":login_session}
data = {"product_id":game_id,"is_notification":False,"is_myapp":False} url = "https://katworks.sytes.net/KF/Api/DMM/filelist" if use_proxy else "https://apidgp-gameplayer.games.dmm.com/v5/r2/filelist/cl"
result = requests.post(url, 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"]}')
return True data = result_json["data"]
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 accept terms of use:", e) print("Failed to retrieve update file list:", e)
return False return None, None
def retrieve_launch_params(game_id, mac_addr, hdd_serial, motherboard, login_secure, login_session, use_proxy, update_game): def retrieve_launch_params(game_id, mac_addr, hdd_serial, motherboard, login_secure, login_session, use_proxy):
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}
if update_game: url = "https://katworks.sytes.net/KF/Api/DMM/launch" if use_proxy else "https://apidgp-gameplayer.games.dmm.com/v5/r2/launch/cl"
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 = 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"] == 308: if result_json["result_code"] != 100:
if agree_to_game_terms(game_id, use_proxy):
result = requests.post(url, cookies=cookies, headers=headers, json=data)
result.raise_for_status()
result_json = result.json()
if result_json["result_code"] != 100:
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"]}') raise Exception(f'{result_json["result_code"]}: {result_json["error"]}')
data = result_json["data"] data = result_json["data"]
return data return data["execute_args"]
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"])
#optional arguments update_game = config["update_game"] if "update_game" in config else False
update_game = config.get("update_game", False) use_proxy = config["use_proxy"] if "use_proxy" in config else 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('') #DMM sends an empty hash as well hdd_serial = get_hash('')
motherboard = get_hash(getnode()) #actual moterboard serial is unknown, but this works #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: token = retrieve_login_token(session)
login_expiration = datetime.fromtimestamp(saved_login["expiration"]) captcha = retrieve_captcha_token()
if login_expiration < current_time:
print("Login data has expired")
saved_login = None
if saved_login is not None: if token == None or captcha == None:
login_secure, login_session = saved_login["login_secure"], saved_login["login_session"] return
print("Loaded login data from previous session")
else:
token = retrieve_login_token(session)
captcha = retrieve_captcha_token()
if token == None or captcha == None: login_secure, login_session = retrieve_auth_keys(login, password, token, captcha, session)
return if login_secure == None or login_session == None:
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
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
if update_game: if update_game:
file_list_url = launch_data.get("file_list_url", None) file_list_url, file_access_params = retrieve_update_params(game_id, login_secure, login_session, use_proxy)
file_list_params : str = launch_data.get("sign", None) if file_list_url == None or file_access_params == None:
if file_list_url == None or file_list_params == None:
return return
print("Latest version:", launch_data["latest_version"]) execute_args = retrieve_launch_params(game_id, mac_addr, hdd_serial, motherboard, login_secure, login_session, use_proxy)
file_list_params = "?" + file_list_params.replace(";", "&").replace("CloudFront-", "") if execute_args == None:
return
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(game_root_path, file_list_url, file_list_params) dmmUpdater.update_game(os.path.dirname(exe_location), file_list_url, file_access_params)
print("Starting game") print("Starting game")
args = [os.path.join(game_root_path, exec_name)] + execute_args.split() args = [exe_location] + 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,7 +4,6 @@ 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
@ -28,28 +27,33 @@ 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 = {str(Path(game_path, file["local_path"].lstrip('/')).resolve()): file for file in server_files} server_file_dict = {file["local_path"]: 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 abs_file_path in server_file_dict.keys(): for server_file_key in server_file_dict.keys():
server_file = server_file_dict[abs_file_path] server_file = server_file_dict[server_file_key]
if abs_file_path in local_file_dict: if server_file_key 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(abs_file_path) files_to_delete.append(local_file["abs_path"])
else: else:
local_file_hash = get_file_hash(abs_file_path) local_file["hash"] = get_file_hash(local_file["abs_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
files_to_download.append({"url":download_url, "path":abs_file_path}) download_path = game_path.replace("\\", "/") + server_file_key
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
files_to_download.append({"url":download_url, "path":abs_file_path}) download_path = game_path.replace("\\", "/") + server_file_key
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))