33import argparse
44import json
55import glob
6- import win32crypt
76import ctypes
87import random
98import hashlib
109import sqlite3
1110import os
1211import time
13- import re
14-
15-
16- def gen_rand_hex ():
17- return hashlib .sha256 (str (random .random ()).encode ()).hexdigest ()
18-
12+ from urllib .parse import urlparse
13+
14+
15+ class DgpSession :
16+ DGP5_PATH : str
17+ HEADERS : dict [str , str ]
18+ PROXY : dict [str , str | None ]
19+ DGP5_HEADERS : dict [str , str ]
20+ DGP5_LAUNCH_PARAMS : dict [str , str ]
21+ db : sqlite3 .Connection
22+ session : requests .Session
23+ cookies : requests .cookies .RequestsCookieJar
24+
25+ def __init__ (self , https_proxy_uri : str | None = None ):
26+ requests .packages .urllib3 .disable_warnings ()
27+ self .DGP5_PATH = os .environ ["APPDATA" ] + "\\ dmmgameplayer5\\ "
28+ self .PROXY = {"https" : https_proxy_uri }
29+ self .HEADERS = {
30+ "Accept" : "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" ,
31+ "Upgrade-Insecure-Requests" : "1" ,
32+ "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.84 Safari/537.36" ,
33+ }
34+ self .DGP5_HEADERS = {
35+ "Host" : "apidgp-gameplayer.games.dmm.com" ,
36+ "Connection" : "keep-alive" ,
37+ "User-Agent" : "DMMGamePlayer5-Win/17.1.2 Electron/17.1.2" ,
38+ "Client-App" : "DMMGamePlayer5" ,
39+ "Client-version" : "17.1.2" ,
40+ }
41+ self .DGP5_LAUNCH_PARAMS = {
42+ "game_type" : "GCL" ,
43+ "game_os" : "win" ,
44+ "launch_type" : "LIB" ,
45+ "mac_address" : self .gen_rand_address (),
46+ "hdd_serial" : self .gen_rand_hex (),
47+ "motherboard" : self .gen_rand_hex (),
48+ "user_os" : "win" ,
49+ }
50+ self .open ()
51+
52+ def gen_rand_hex (self ):
53+ return hashlib .sha256 (str (random .random ()).encode ()).hexdigest ()
54+
55+ def gen_rand_address (self ):
56+ hex = self .gen_rand_hex ()
57+ address = ""
58+ for x in range (12 ):
59+ address += hex [x ]
60+ if x % 2 == 1 :
61+ address += ":"
62+ return address [:- 1 ]
63+
64+ def write (self ):
65+ for cookie_row in self .db .cursor ().execute ("select * from cookies" ):
66+ name = cookie_row [3 ]
67+ value = self .session .cookies .get (name )
68+ if value != None :
69+ self .db .execute (
70+ f"update cookies set value = '{ value } ' where name = '{ name } '"
71+ )
72+ self .db .commit ()
73+
74+ def read (self ):
75+ for cookie_row in self .db .cursor ().execute ("select * from cookies" ):
76+ cookie_data = {
77+ "name" : cookie_row [3 ],
78+ "value" : cookie_row [4 ],
79+ "domain" : cookie_row [1 ],
80+ "path" : cookie_row [6 ],
81+ "secure" : cookie_row [8 ],
82+ }
83+ self .session .cookies .set_cookie (
84+ requests .cookies .create_cookie (** cookie_data )
85+ )
1986
20- def gen_rand_address ():
21- hex = gen_rand_hex ()
22- address = ""
23- for x in range (12 ):
24- address += hex [x ]
25- if x % 2 == 1 :
26- address += ":"
27- return address [:- 1 ]
87+ def get (self , url : str ) -> requests .Response :
88+ return self .session .get (url , headers = self .HEADERS , proxies = self .PROXY )
2889
90+ def post (self , url : str ) -> requests .Response :
91+ return self .session .post (url , headers = self .HEADERS , proxies = self .PROXY )
2992
30- def get_dgp5_session (dgp5_path ):
31- db = sqlite3 .connect (dgp5_path + "Network/Cookies" ).cursor ()
32- session = requests .session ()
33- for cookie_row in db .execute ("select * from cookies" ):
34- cookie_data = {
35- "name" : cookie_row [3 ],
36- "value" : cookie_row [4 ],
37- "domain" : cookie_row [1 ],
38- "path" : cookie_row [6 ],
39- "secure" : cookie_row [8 ],
40- }
41- session .cookies .set_cookie (requests .cookies .create_cookie (** cookie_data ))
42- return session
93+ def lunch (self , url : str , product_id : str ) -> requests .Response :
94+ json = {"product_id" : product_id }
95+ json .update (self .DGP5_LAUNCH_PARAMS )
96+ return self .session .post (
97+ url ,
98+ headers = self .DGP5_HEADERS ,
99+ proxies = self .PROXY ,
100+ json = json ,
101+ verify = False ,
102+ )
43103
104+ def get_config (self ):
105+ with open (self .DGP5_PATH + "dmmgame.cnf" , "r" , encoding = "utf-8" ) as f :
106+ config = f .read ()
107+ return json .loads (config )
44108
45- def get_dpg5_config ( dgp5_path ):
46- with open ( dgp5_path + "dmmgame.cnf" , "r" , encoding = "utf-8" ) as f :
47- config = f . read ()
48- return json . loads ( config )
109+ def open ( self ):
110+ self . db = sqlite3 . connect ( self . DGP5_PATH + "Network/Cookies" )
111+ self . session = requests . session ()
112+ self . cookies = self . session . cookies
49113
114+ def close (self ):
115+ self .db .close ()
50116
51- requests .packages .urllib3 .disable_warnings ()
52117
53118argpar = argparse .ArgumentParser (
54119 prog = "DMMGamePlayerFastLauncher" ,
@@ -58,81 +123,36 @@ def get_dpg5_config(dgp5_path):
58123argpar .add_argument ("product_id" , default = None )
59124argpar .add_argument ("--game-path" , default = None )
60125argpar .add_argument ("--game-args" , default = None )
61- argpar .add_argument ("--login-force" , action = "store_true" )
62126argpar .add_argument ("--skip-exception" , action = "store_true" )
63127argpar .add_argument ("--https-proxy-uri" , default = None )
64128argpar .add_argument ("--non-request-admin" , action = "store_true" )
65129arg = argpar .parse_args ()
66130
67- HEADERS = {
68- "Accept" : "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" ,
69- "Upgrade-Insecure-Requests" : "1" ,
70- "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.84 Safari/537.36" ,
71- }
72-
73- PROXY = {"https" : arg .https_proxy_uri }
74-
75- DGP5_HEADERS = {
76- "Host" : "apidgp-gameplayer.games.dmm.com" ,
77- "Connection" : "keep-alive" ,
78- "User-Agent" : "DMMGamePlayer5-Win/17.1.2 Electron/17.1.2" ,
79- "Client-App" : "DMMGamePlayer5" ,
80- "Client-version" : "17.1.2" ,
81- }
82- DGP5_LAUNCH_PARAMS = {
83- "product_id" : arg .product_id ,
84- "game_type" : "GCL" ,
85- "game_os" : "win" ,
86- "launch_type" : "LIB" ,
87- "mac_address" : gen_rand_address (),
88- "hdd_serial" : gen_rand_hex (),
89- "motherboard" : gen_rand_hex (),
90- "user_os" : "win" ,
91- }
92- DGP5_PATH = os .environ ["APPDATA" ] + "\\ dmmgameplayer5\\ "
93-
94-
95- open ("cookie.bytes" , "a+" )
96- with open ("cookie.bytes" , "rb" ) as f :
97- blob = f .read ()
98- if blob == b"" or arg .login_force :
99- session = get_dgp5_session (DGP5_PATH )
100-
101- response = session .get (
102- "https://www.dmm.com/" ,
103- headers = HEADERS ,
104- proxies = PROXY ,
105- ).text
106-
107- reg = '<script{any}id="auto-login"{any}data-encoded="{data}"{any}></script>' .format (any = ".*?" ,data = "([0-9a-zA-Z_]*)" )
108- data_encoded = re .findall (reg , response )[0 ]
109-
110- response = session .get (
111- f"https://accounts.dmm.com/service/login/token/=/path={ data_encoded } " ,
112- headers = HEADERS ,
113- proxies = PROXY ,
114- )
115- if session .cookies .get ("login_session_id" ) == None :
116- if not arg .skip_exception :
117- raise Exception (
118- "ログインに失敗しました\n DMMGamePlayerを起動してログインし直して下さい"
119- )
120- contents = json .dumps (
121- {
122- "login_session_id" : session .cookies .get ("login_session_id" ),
123- "login_secure_id" : session .cookies .get ("login_secure_id" ),
124- }
125- )
126- new_blob = win32crypt .CryptProtectData (
127- contents .encode (), "DMMGamePlayerFastLauncher"
128- )
129- with open ("cookie.bytes" , "wb" ) as f :
130- f .write (new_blob )
131- else :
132- _ , contents = win32crypt .CryptUnprotectData (blob )
133- cookie = json .loads (contents )
134131
135- dpg5_config = get_dpg5_config (DGP5_PATH )
132+ session = DgpSession (arg .https_proxy_uri )
133+
134+ session .read ()
135+ url = session .get ("https://apidgp-gameplayer.games.dmm.com/v5/loginurl" ).json ()["data" ][
136+ "url"
137+ ]
138+ token = urlparse (url ).path .split ("path=" )[- 1 ]
139+
140+
141+ session .get (url )
142+ res = session .get (
143+ f"https://accounts.dmm.com/service/login/token/=/path={ token } /is_app=false"
144+ )
145+
146+ if session .cookies .get ("login_session_id" ) == None :
147+ if not arg .skip_exception :
148+ raise Exception (
149+ "\n " .join (["Login failed." , "Please start DMMGamePlayer and login again." ])
150+ )
151+
152+ session .write ()
153+ session .close ()
154+
155+ dpg5_config = session .get_config ()
136156if arg .game_path is None :
137157 for contents in dpg5_config ["contents" ]:
138158 if contents ["productId" ] == arg .product_id :
@@ -156,28 +176,33 @@ def get_dpg5_config(dgp5_path):
156176 break
157177 else :
158178 if not arg .skip_exception :
159- raise Exception ("ゲームのパスの検出に失敗しました" )
179+ raise Exception (
180+ "\n " .join (
181+ [
182+ "Game path detection failed." ,
183+ "Try using --game_path." ,
184+ "https://github.com/fa0311/DMMGamePlayerFastLauncher/blob/master/docs/README-advance.md#game-path" ,
185+ ]
186+ )
187+ )
160188 break
161189 else :
162190 if not arg .skip_exception :
163- raise Exception (
164- "product_id が無効です\n "
165- + " " .join (
191+ message = [
192+ "product_id is invalid." ,
193+ "Please select:" ,
194+ " " .join (
166195 [contents ["productId" ] for contents in dpg5_config ["contents" ]]
167- )
168- + "から選択して下さい"
169- )
196+ ),
197+ ]
198+ raise Exception ( " \n " . join ( message ) )
170199else :
171200 game_path = arg .game_path
172201
173- response = requests .post (
174- "https://apidgp-gameplayer.games.dmm.com/v5/launch/cl" ,
175- cookies = cookie ,
176- headers = DGP5_HEADERS ,
177- json = DGP5_LAUNCH_PARAMS ,
178- verify = False ,
179- proxies = PROXY ,
202+ response = session .lunch (
203+ "https://apidgp-gameplayer.games.dmm.com/v5/launch/cl" , arg .product_id
180204).json ()
205+
181206if response ["result_code" ] == 100 :
182207 dmm_args = response ["data" ]["execute_args" ].split (" " )
183208 if arg .game_args is not None :
@@ -197,17 +222,23 @@ def get_dpg5_config(dgp5_path):
197222 None , "runas" , game_path , response ["data" ]["execute_args" ], None , 1
198223 )
199224 else :
200- raise Exception ("ゲームが起動しませんでした。管理者権限を許可してください。" )
225+ raise Exception (
226+ "Game did not start. Please allow administrative privileges."
227+ )
201228 elif response ["data" ]["is_administrator" ]:
202- raise Exception ("ゲームが起動しませんでした。管理者権限を許可してください。" )
229+ raise Exception (
230+ "Game did not start. Please allow administrative privileges."
231+ )
203232 else :
204233 if not arg .skip_exception :
205- raise Exception ("ゲームが起動しませんでした。ゲームにアップデートがある可能性があります。" )
234+ raise Exception (
235+ "Game did not start. There may be an update to the game."
236+ )
206237elif response ["result_code" ] == 801 :
207238 if not arg .skip_exception :
208- raise Exception ("日本国外からのアクセスは禁止されています\n " + json .dumps (response ))
239+ raise Exception (
240+ "\n " .join (["Access from outside Japan is prohibited" , json .dumps (response )])
241+ )
209242else :
210- with open ("cookie.bytes" , "wb" ) as f :
211- f .write (b"" )
212243 if not arg .skip_exception :
213- raise Exception ("起動にエラーが発生したため修復プログラムを実行しました \n " + json .dumps (response ))
244+ raise Exception ("\n " . join ([ "Error in startup." , json .dumps (response )] ))
0 commit comments