Skip to content

Commit 4c544c6

Browse files
authored
Merge pull request #51 from fa0311/develop-v4
4.6.0
2 parents af66ecd + 1a7460d commit 4c544c6

File tree

10 files changed

+253
-31
lines changed

10 files changed

+253
-31
lines changed

DMMGamePlayerFastLauncher.py

Lines changed: 99 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
import os
1212
import time
1313
from urllib.parse import urlparse
14+
import win32security
15+
import sys
1416

1517

1618
class DgpSession:
@@ -211,6 +213,12 @@ class ErrorManager:
211213
url="https://github.com/fa0311/DMMGamePlayerFastLauncher/issues",
212214
)
213215

216+
elevate_admin_error: ErrorManagerType = ErrorManagerType(
217+
message="Failed to elevate to administrator privileges.",
218+
solution="Report an Issues.",
219+
url="https://github.com/fa0311/DMMGamePlayerFastLauncher/issues",
220+
)
221+
214222
def error(self, error: ErrorManagerType, log: str | None = None):
215223
output = filter(
216224
lambda x: x != None,
@@ -225,7 +233,86 @@ def info(self, text: str):
225233
print(text)
226234

227235

236+
class ProcessManager:
237+
non_request_admin: bool = False
238+
non_bypass_uac: bool = False
239+
error_manager: ErrorManager
240+
241+
def __init__(self, error_manager: ErrorManager) -> None:
242+
self.error_manager = error_manager
243+
244+
def run(
245+
self, args: dict[str], admin: bool = False, force: bool = False
246+
) -> subprocess.Popen[bytes] | None:
247+
print(" ".join(args))
248+
if admin:
249+
if not self.non_bypass_uac and not force:
250+
run_bypass_uac()
251+
elif self.non_request_admin:
252+
self.error_manager.error(error=ErrorManager.permission_error)
253+
else:
254+
args = [f'"{arg}"' for arg in args]
255+
ctypes.windll.shell32.ShellExecuteW(
256+
None, "runas", args[0], " ".join(args[1:]), None, 1
257+
)
258+
else:
259+
return subprocess.Popen(
260+
args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
261+
)
262+
263+
264+
def get_sid() -> str:
265+
desc = win32security.GetFileSecurity(".", win32security.OWNER_SECURITY_INFORMATION)
266+
sid = desc.GetSecurityDescriptorOwner()
267+
sidstr = win32security.ConvertSidToStringSid(sid)
268+
return sidstr
269+
270+
271+
def run_bypass_uac():
272+
schtasks_file = "schtasks_v1_" + os.getlogin()
273+
schtasks_name = f"\Microsoft\Windows\DMMGamePlayerFastLauncher\{schtasks_file}"
274+
275+
run_args = [arg.schtasks_path, "/run", "/tn", schtasks_name]
276+
277+
if process_manager.run(run_args).wait() == 1:
278+
schtasks_xml_path = (
279+
r"{appdata}\DMMGamePlayerFastLauncher\assets\{name}.xml".format(
280+
appdata=os.environ["APPDATA"], name=schtasks_file
281+
)
282+
)
283+
schtasks_task_path = (
284+
r"{appdata}\DMMGamePlayerFastLauncher\Tools\Task.exe".format(
285+
appdata=os.environ["APPDATA"]
286+
)
287+
)
288+
with open("assets/template.xml", "r") as f:
289+
template = f.read()
290+
291+
with open(f"assets/{schtasks_file}.xml", "w") as f:
292+
f.write(
293+
template.replace(r"{{UID}}", schtasks_file)
294+
.replace(r"{{SID}}", get_sid())
295+
.replace(r"{{COMMAND}}", schtasks_task_path)
296+
.replace(r"{{WORKING_DIRECTORY}}", os.getcwd())
297+
)
298+
299+
create_args = [
300+
arg.schtasks_path,
301+
"/create",
302+
"/xml",
303+
schtasks_xml_path,
304+
"/tn",
305+
schtasks_name,
306+
]
307+
process_manager.run(create_args, admin=True, force=True)
308+
time.sleep(3)
309+
process_manager.run(run_args).wait()
310+
time.sleep(5)
311+
sys.exit()
312+
313+
228314
error_manager = ErrorManager()
315+
process_manager = ProcessManager(error_manager)
229316

230317
argpar = argparse.ArgumentParser(
231318
prog="DMMGamePlayerFastLauncher",
@@ -239,9 +326,14 @@ def info(self, text: str):
239326
argpar.add_argument("--skip-exception", action="store_true")
240327
argpar.add_argument("--https-proxy-uri", default=None)
241328
argpar.add_argument("--non-request-admin", action="store_true")
329+
argpar.add_argument("--non-bypass-uac", action="store_true")
330+
argpar.add_argument("--schtasks-path", default="schtasks.exe")
331+
242332
try:
243333
arg = argpar.parse_args()
244334
error_manager.skip = arg.skip_exception
335+
process_manager.non_request_admin = arg.non_request_admin
336+
process_manager.non_bypass_uac = arg.non_bypass_uac
245337
except:
246338
error_manager.error(error=ErrorManager.argument_error)
247339

@@ -324,20 +416,17 @@ def info(self, text: str):
324416
dmm_args = dmm_args + arg.game_args.split(" ")
325417
print(game_path)
326418
start_time = time.time()
327-
process = subprocess.Popen(
328-
[game_path] + dmm_args, shell=True, stdout=subprocess.PIPE
329-
)
419+
process = process_manager.run([game_path] + dmm_args)
330420
for line in process.stdout:
331421
text = line.decode("utf-8").strip()
332422
print(text)
333-
if time.time() - start_time < 2 and not arg.skip_exception:
423+
if time.time() - start_time < 2:
334424
if response["data"]["is_administrator"]:
335-
if not ctypes.windll.shell32.IsUserAnAdmin() and not arg.non_request_admin:
336-
ctypes.windll.shell32.ShellExecuteW(
337-
None, "runas", game_path, response["data"]["execute_args"], None, 1
338-
)
339-
else:
340-
error_manager.error(error=ErrorManager.permission_error)
425+
process = process_manager.run([game_path] + dmm_args, admin=True)
426+
else:
427+
error_manager.error(
428+
error=ErrorManager.startup_error, log=json.dumps(response)
429+
)
341430

342431
elif response["result_code"] == 307:
343432
error_manager.error(error=ErrorManager.auth_device_error)

DMMGamePlayerProductIdChecker.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,5 @@
1717
pd.set_option("display.unicode.east_asian_width", True)
1818

1919
print(df)
20-
print("終了するには何かキーを押してください")
20+
print("Please press any key to exit")
2121
input()

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@ DMM Game Player のゲームを高速かつセキュアに起動できるラン
1111
## 特徴
1212

1313
- **ワンクリックでゲームを起動**
14+
- **高速**
1415
- **最低限の権限で起動**
16+
- **UAC の自動昇格**
1517
- **DMM にハードウェア情報を送信しない**
1618
- **隠されているコマンドライン引数を使用可能**
19+
- **面倒な設定の必要無し**
1720

1821
## インストール
1922

Task.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import psutil
2+
import os
3+
import signal
4+
import ctypes
5+
6+
7+
for p in psutil.process_iter(attrs=("name", "pid", "cmdline")):
8+
if p.info["name"] != "DMMGamePlayerFastLauncher.exe":
9+
continue
10+
11+
cmdline = p.info["cmdline"]
12+
if "--non-bypass-uac" not in cmdline:
13+
cmdline.append("--non-bypass-uac")
14+
cmdline = [f'"{cmd}"' for cmd in cmdline]
15+
os.kill(p.info["pid"], signal.SIGTERM)
16+
print("killed " + " ".join(cmdline))
17+
ctypes.windll.shell32.ShellExecuteW(
18+
None, "runas", cmdline[0], " ".join(cmdline[1:]), None, 1
19+
)
20+
break
21+
else:
22+
print("Error")

build.ps1

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ black *.py
22
pip freeze > requirements.txt
33
pyinstaller DMMGamePlayerFastLauncher.py --onefile --noconsole
44
pyinstaller DMMGamePlayerProductIdChecker.py --onefile
5+
pyinstaller Task.py --onefile --noconsole
56
New-Item "Z:\Project\Python\hack\DMMGamePlayerFastLauncher\windows\tools" -ItemType Directory -Force
67
Copy-Item -Path "Z:\Project\Python\hack\DMMGamePlayerFastLauncher\dist\DMMGamePlayerProductIdChecker.exe" -Destination "Z:\Project\Python\hack\DMMGamePlayerFastLauncher\windows\tools" -Force
8+
Copy-Item -Path "Z:\Project\Python\hack\DMMGamePlayerFastLauncher\dist\Task.exe" -Destination "Z:\Project\Python\hack\DMMGamePlayerFastLauncher\windows\tools" -Force
79
Start-Process "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" "Z:\Project\Python\hack\DMMGamePlayerFastLauncher\setup.iss"

docs/README-advance.md

Lines changed: 72 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,51 @@
1919

2020
`DMMGamePlayerFastLauncher.exe <product_id>`
2121

22-
| オプション | エイリアス | デフォルト | タイプ |
23-
| ------------------- | ---------- | ---------- | ------------------ |
24-
| --help | -h | False | Bool |
25-
| --game-path | | None | String &#124; None |
26-
| --game-args | | None | String &#124; None |
27-
| --login-force | | Flase | Bool |
28-
| --skip-exception | | False | Bool |
29-
| --https-proxy-uri | | None | String &#124; None |
30-
| --non-request-admin | | False | Bool |
22+
| オプション | エイリアス | デフォルト | タイプ | note |
23+
| ------------------- | ---------- | ------------ | ------------------ | ---------- |
24+
| --help | -h | False | Bool | |
25+
| --game-path | | None | String &#124; None | |
26+
| --game-args | | None | String &#124; None | |
27+
| --login-force | | Flase | Bool | deprecated |
28+
| --skip-exception | | False | Bool | |
29+
| --https-proxy-uri | | None | String &#124; None | |
30+
| --non-request-admin | | False | Bool | deprecated |
31+
| --non-bypass-uac | | False | Bool | |
32+
| --schtasks-path | | schtasks.exe | String | |
33+
34+
```mermaid
35+
graph TD;
36+
ゲームのプロセスを開始 --権限不備で起動しない--> non-bypass-uac;
37+
ゲームのプロセスを開始 --Start/Error--> 続行;
38+
non-bypass-uac --false--> 最初のリクエスト;
39+
最初のリクエスト --no-->ゲームに権限与える
40+
最初のリクエスト --yes--> non-request-admin
41+
non-bypass-uac --true--> non-request-admin;
42+
non-request-admin --true--> skip-exception;
43+
non-request-admin --false--> UAC;
44+
UAC --allow--> ゲームに権限与える
45+
UAC --disabled--> 続行
46+
skip-exception --true--> 続行
47+
skip-exception --false--> エラー
48+
```
49+
50+
**最初のリクエスト** - 最初のリクエストは、管理者権限を要求します
51+
これは、タスクスケジューラーに自動的に権限を昇格させるプログラムを登録するために必要です
52+
53+
特にこだわりが無ければ product_id 以外の引数は不要です
54+
ゲームや環境によっては`game-path` `https-proxy-uri` `schtasks-path` などが必要です
3155

3256
### game-path
3357

34-
何も指定していない場合は自動で検出しますがゲームによってはうまくいかない場合があります
58+
ゲームのパスを指定します
59+
何も指定していない場合は自動で検出しますがうまくいかない場合は指定してください
3560

3661
例:
3762
`%AppData%\DMMGamePlayerFastLauncher\DMMGamePlayerFastLauncher.exe umamusume --game-path %UserProfile%/umamusume/umamusume.exe`
3863

3964
### game-args
4065

41-
ゲームに引数を追加したい場合はこれを指定します
66+
ゲームに引数を追加したい場合はこの引数を使用します
4267
通常の DMM を介した起動方法で使用できない隠された引数を使用することができます
4368
`"` で囲む必要があることに注意してください
4469

@@ -55,7 +80,7 @@ Unity 製ゲームの引数はここに詳しく載ってます
5580

5681
### skip-exception
5782

58-
エラーを出力しません
83+
この引数を使用するとエラーを出力しなくなります
5984
これはあくまで応急処置で基本的には使わないで下さい
6085
原因不明なエラーが発生した場合は [issues](https://github.com/fa0311/DMMGamePlayerFastLauncher/issues) に報告して下さい
6186

@@ -79,12 +104,46 @@ Socks5
79104

80105
### non-request-admin
81106

82-
このツールは管理者権限を必要なときのみ要求することがありますがそれを要求しなくなります
107+
この引数を使用すると管理者権限を要求しません
83108
ほとんどの場合、この引数は不要です
84109

85110
例:
86111
`%AppData%\DMMGamePlayerFastLauncher\DMMGamePlayerFastLauncher.exe umamusume --non-request-admin`
87112

113+
### non-bypass-uac
114+
115+
この引数を使用すると UAC の自動昇格を行わなくなります
116+
117+
指定していない場合はタスクスケジューラを使った権限の自動昇格を行います
118+
タスクの詳細はこのコマンドで確認できます
119+
複雑な処理を行うため数秒程度起動速度が遅くなる場合があります
120+
`schtasks.exe /query /tn \Microsoft\Windows\DMMGamePlayerFastLauncher\`
121+
122+
また、このコマンドで削除できます
123+
`Get-ScheduledTask | where TaskPath -eq "\Microsoft\Windows\DMMGamePlayerFastLauncher" | Unregister-ScheduledTask -Confirm:$false`
124+
または `.\tools\refresh.ps1`
125+
126+
### schtasks-path
127+
128+
`schtasks.exe`のパスを指定します
129+
ほとんどの場合、この引数は不要です
130+
131+
## ファイル階層
132+
133+
| ファイル名 | 削除 | 詳細 |
134+
| --------------------------------------- | ---- | ------------------------------------------------------------------------------ |
135+
| DMMGamePlayerFastLauncher.exe | x | 本体 |
136+
| unins000.dat | o | アンインストールする際に必要 |
137+
| unins000.exe | x | アンインストールする際に必要 |
138+
| cookie.bytes | o | セッションのキャッシュファイル, DMM のセッションが取得できなかった際に使用する |
139+
| tools/DMMGamePlayerProductIdChecker.exe | o | プロダクト ID のチェッカー,左ダブルクリックで実行 |
140+
| tools/Task.exe | x | タスクスケジューラから呼び出される |
141+
| tools/refresh.ps1 | o | タスクを削除する,右クリックメニューから PowerShell で実行 |
142+
| sample/ウマ娘.lnk | o | ウマ娘の起動ショートカット,左ダブルクリックで実行 |
143+
| sample/プリコネ R.lnk | o | プリコネ R の起動ショートカット,左ダブルクリックで実行 |
144+
| assets/template.xml | x | タスクスケジューラのテンプレートファイル |
145+
| assets/schtasks_v1\_{username}.xml | o | タスクスケジューラの残骸ファイル |
146+
88147
## ヘルプ
89148

90149
### セットアップする際、「Windows によって PC が保護されました」と表示される

requirements.txt

30 Bytes
Binary file not shown.

setup.iss

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
33

44
#define MyAppName "DMMGamePlayerFastLauncher"
5-
#define MyAppVersion "4.5.2"
5+
#define MyAppVersion "4.6.0"
66
#define MyAppPublisher "yuki"
77
#define MyAppURL "https://github.com/fa0311/DMMGamePlayerFastLauncher"
88
#define MyAppExeName "DMMGamePlayerFastLauncher.exe"
@@ -21,7 +21,7 @@ AppPublisher={#MyAppPublisher}
2121
AppPublisherURL={#MyAppURL}
2222
AppSupportURL={#MyAppURL}
2323
AppUpdatesURL={#MyAppURL}
24-
DefaultDirName={autopf}\{#MyAppName}
24+
DefaultDirName={userappdata}\{#MyAppName}
2525
DisableDirPage=yes
2626
ChangesAssociations=yes
2727
DefaultGroupName={#MyAppName}
@@ -34,23 +34,24 @@ OutputBaseFilename=DMMGamePlayerFastLauncher-Setup
3434
Compression=lzma
3535
SolidCompression=yes
3636
WizardStyle=modern
37+
UninstallFilesDir={userappdata}\{#MyAppName}
3738

3839
[Languages]
3940
Name: "english"; MessagesFile: "compiler:Default.isl"
4041
Name: "japanese"; MessagesFile: "compiler:Languages\Japanese.isl"
4142
Name: "chinesesimplified"; MessagesFile: "compiler:Languages\ChineseSimplified.isl"
4243

4344
[Files]
44-
Source: "Z:\Project\Python\hack\DMMGamePlayerFastLauncher\dist\{#MyAppExeName}"; DestDir: "{userappdata}\{#MyAppName}"; Flags: ignoreversion
45-
Source: "Z:\Project\Python\hack\DMMGamePlayerFastLauncher\windows\*"; DestDir: "{userappdata}\{#MyAppName}"; Flags: ignoreversion recursesubdirs createallsubdirs
45+
Source: "Z:\Project\Python\hack\DMMGamePlayerFastLauncher\dist\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
46+
Source: "Z:\Project\Python\hack\DMMGamePlayerFastLauncher\windows\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
4647
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
4748

4849
[Registry]
4950
Root: HKA; Subkey: "Software\Classes\{#MyAppAssocExt}\OpenWithProgids"; ValueType: string; ValueName: "{#MyAppAssocKey}"; ValueData: ""; Flags: uninsdeletevalue
5051
Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}"; ValueType: string; ValueName: ""; ValueData: "{#MyAppAssocName}"; Flags: uninsdeletekey
51-
Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{userappdata}\{#MyAppName}\{#MyAppExeName},0"
52-
Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{userappdata}\{#MyAppName}\{#MyAppExeName}"" ""%1"""
52+
Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\{#MyAppExeName},0"
53+
Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" ""%1"""
5354
Root: HKA; Subkey: "Software\Classes\Applications\{#MyAppExeName}\SupportedTypes"; ValueType: string; ValueName: ".myp"; ValueData: ""
5455

5556
[Icons]
56-
Name: "{group}\{#MyAppName}"; Filename: "{userappdata}\{#MyAppName}\{#MyAppExeName}"
57+
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"

0 commit comments

Comments
 (0)