Skip to content

Commit

Permalink
Merge pull request #51 from fa0311/develop-v4
Browse files Browse the repository at this point in the history
4.6.0
  • Loading branch information
fa0311 authored Feb 1, 2023
2 parents af66ecd + 1a7460d commit 4c544c6
Show file tree
Hide file tree
Showing 10 changed files with 253 additions and 31 deletions.
109 changes: 99 additions & 10 deletions DMMGamePlayerFastLauncher.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import os
import time
from urllib.parse import urlparse
import win32security
import sys


class DgpSession:
Expand Down Expand Up @@ -211,6 +213,12 @@ class ErrorManager:
url="https://github.com/fa0311/DMMGamePlayerFastLauncher/issues",
)

elevate_admin_error: ErrorManagerType = ErrorManagerType(
message="Failed to elevate to administrator privileges.",
solution="Report an Issues.",
url="https://github.com/fa0311/DMMGamePlayerFastLauncher/issues",
)

def error(self, error: ErrorManagerType, log: str | None = None):
output = filter(
lambda x: x != None,
Expand All @@ -225,7 +233,86 @@ def info(self, text: str):
print(text)


class ProcessManager:
non_request_admin: bool = False
non_bypass_uac: bool = False
error_manager: ErrorManager

def __init__(self, error_manager: ErrorManager) -> None:
self.error_manager = error_manager

def run(
self, args: dict[str], admin: bool = False, force: bool = False
) -> subprocess.Popen[bytes] | None:
print(" ".join(args))
if admin:
if not self.non_bypass_uac and not force:
run_bypass_uac()
elif self.non_request_admin:
self.error_manager.error(error=ErrorManager.permission_error)
else:
args = [f'"{arg}"' for arg in args]
ctypes.windll.shell32.ShellExecuteW(
None, "runas", args[0], " ".join(args[1:]), None, 1
)
else:
return subprocess.Popen(
args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)


def get_sid() -> str:
desc = win32security.GetFileSecurity(".", win32security.OWNER_SECURITY_INFORMATION)
sid = desc.GetSecurityDescriptorOwner()
sidstr = win32security.ConvertSidToStringSid(sid)
return sidstr


def run_bypass_uac():
schtasks_file = "schtasks_v1_" + os.getlogin()
schtasks_name = f"\Microsoft\Windows\DMMGamePlayerFastLauncher\{schtasks_file}"

run_args = [arg.schtasks_path, "/run", "/tn", schtasks_name]

if process_manager.run(run_args).wait() == 1:
schtasks_xml_path = (
r"{appdata}\DMMGamePlayerFastLauncher\assets\{name}.xml".format(
appdata=os.environ["APPDATA"], name=schtasks_file
)
)
schtasks_task_path = (
r"{appdata}\DMMGamePlayerFastLauncher\Tools\Task.exe".format(
appdata=os.environ["APPDATA"]
)
)
with open("assets/template.xml", "r") as f:
template = f.read()

with open(f"assets/{schtasks_file}.xml", "w") as f:
f.write(
template.replace(r"{{UID}}", schtasks_file)
.replace(r"{{SID}}", get_sid())
.replace(r"{{COMMAND}}", schtasks_task_path)
.replace(r"{{WORKING_DIRECTORY}}", os.getcwd())
)

create_args = [
arg.schtasks_path,
"/create",
"/xml",
schtasks_xml_path,
"/tn",
schtasks_name,
]
process_manager.run(create_args, admin=True, force=True)
time.sleep(3)
process_manager.run(run_args).wait()
time.sleep(5)
sys.exit()


error_manager = ErrorManager()
process_manager = ProcessManager(error_manager)

argpar = argparse.ArgumentParser(
prog="DMMGamePlayerFastLauncher",
Expand All @@ -239,9 +326,14 @@ def info(self, text: str):
argpar.add_argument("--skip-exception", action="store_true")
argpar.add_argument("--https-proxy-uri", default=None)
argpar.add_argument("--non-request-admin", action="store_true")
argpar.add_argument("--non-bypass-uac", action="store_true")
argpar.add_argument("--schtasks-path", default="schtasks.exe")

try:
arg = argpar.parse_args()
error_manager.skip = arg.skip_exception
process_manager.non_request_admin = arg.non_request_admin
process_manager.non_bypass_uac = arg.non_bypass_uac
except:
error_manager.error(error=ErrorManager.argument_error)

Expand Down Expand Up @@ -324,20 +416,17 @@ def info(self, text: str):
dmm_args = dmm_args + arg.game_args.split(" ")
print(game_path)
start_time = time.time()
process = subprocess.Popen(
[game_path] + dmm_args, shell=True, stdout=subprocess.PIPE
)
process = process_manager.run([game_path] + dmm_args)
for line in process.stdout:
text = line.decode("utf-8").strip()
print(text)
if time.time() - start_time < 2 and not arg.skip_exception:
if time.time() - start_time < 2:
if response["data"]["is_administrator"]:
if not ctypes.windll.shell32.IsUserAnAdmin() and not arg.non_request_admin:
ctypes.windll.shell32.ShellExecuteW(
None, "runas", game_path, response["data"]["execute_args"], None, 1
)
else:
error_manager.error(error=ErrorManager.permission_error)
process = process_manager.run([game_path] + dmm_args, admin=True)
else:
error_manager.error(
error=ErrorManager.startup_error, log=json.dumps(response)
)

elif response["result_code"] == 307:
error_manager.error(error=ErrorManager.auth_device_error)
Expand Down
2 changes: 1 addition & 1 deletion DMMGamePlayerProductIdChecker.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@
pd.set_option("display.unicode.east_asian_width", True)

print(df)
print("終了するには何かキーを押してください")
print("Please press any key to exit")
input()
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@ DMM Game Player のゲームを高速かつセキュアに起動できるラン
## 特徴

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

## インストール

Expand Down
22 changes: 22 additions & 0 deletions Task.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import psutil
import os
import signal
import ctypes


for p in psutil.process_iter(attrs=("name", "pid", "cmdline")):
if p.info["name"] != "DMMGamePlayerFastLauncher.exe":
continue

cmdline = p.info["cmdline"]
if "--non-bypass-uac" not in cmdline:
cmdline.append("--non-bypass-uac")
cmdline = [f'"{cmd}"' for cmd in cmdline]
os.kill(p.info["pid"], signal.SIGTERM)
print("killed " + " ".join(cmdline))
ctypes.windll.shell32.ShellExecuteW(
None, "runas", cmdline[0], " ".join(cmdline[1:]), None, 1
)
break
else:
print("Error")
2 changes: 2 additions & 0 deletions build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ black *.py
pip freeze > requirements.txt
pyinstaller DMMGamePlayerFastLauncher.py --onefile --noconsole
pyinstaller DMMGamePlayerProductIdChecker.py --onefile
pyinstaller Task.py --onefile --noconsole
New-Item "Z:\Project\Python\hack\DMMGamePlayerFastLauncher\windows\tools" -ItemType Directory -Force
Copy-Item -Path "Z:\Project\Python\hack\DMMGamePlayerFastLauncher\dist\DMMGamePlayerProductIdChecker.exe" -Destination "Z:\Project\Python\hack\DMMGamePlayerFastLauncher\windows\tools" -Force
Copy-Item -Path "Z:\Project\Python\hack\DMMGamePlayerFastLauncher\dist\Task.exe" -Destination "Z:\Project\Python\hack\DMMGamePlayerFastLauncher\windows\tools" -Force
Start-Process "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" "Z:\Project\Python\hack\DMMGamePlayerFastLauncher\setup.iss"
85 changes: 72 additions & 13 deletions docs/README-advance.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,51 @@

`DMMGamePlayerFastLauncher.exe <product_id>`

| オプション | エイリアス | デフォルト | タイプ |
| ------------------- | ---------- | ---------- | ------------------ |
| --help | -h | False | Bool |
| --game-path | | None | String &#124; None |
| --game-args | | None | String &#124; None |
| --login-force | | Flase | Bool |
| --skip-exception | | False | Bool |
| --https-proxy-uri | | None | String &#124; None |
| --non-request-admin | | False | Bool |
| オプション | エイリアス | デフォルト | タイプ | note |
| ------------------- | ---------- | ------------ | ------------------ | ---------- |
| --help | -h | False | Bool | |
| --game-path | | None | String &#124; None | |
| --game-args | | None | String &#124; None | |
| --login-force | | Flase | Bool | deprecated |
| --skip-exception | | False | Bool | |
| --https-proxy-uri | | None | String &#124; None | |
| --non-request-admin | | False | Bool | deprecated |
| --non-bypass-uac | | False | Bool | |
| --schtasks-path | | schtasks.exe | String | |

```mermaid
graph TD;
ゲームのプロセスを開始 --権限不備で起動しない--> non-bypass-uac;
ゲームのプロセスを開始 --Start/Error--> 続行;
non-bypass-uac --false--> 最初のリクエスト;
最初のリクエスト --no-->ゲームに権限与える
最初のリクエスト --yes--> non-request-admin
non-bypass-uac --true--> non-request-admin;
non-request-admin --true--> skip-exception;
non-request-admin --false--> UAC;
UAC --allow--> ゲームに権限与える
UAC --disabled--> 続行
skip-exception --true--> 続行
skip-exception --false--> エラー
```

**最初のリクエスト** - 最初のリクエストは、管理者権限を要求します
これは、タスクスケジューラーに自動的に権限を昇格させるプログラムを登録するために必要です

特にこだわりが無ければ product_id 以外の引数は不要です
ゲームや環境によっては`game-path` `https-proxy-uri` `schtasks-path` などが必要です

### game-path

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

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

### game-args

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

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

### skip-exception

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

Expand All @@ -79,12 +104,46 @@ Socks5

### non-request-admin

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

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

### non-bypass-uac

この引数を使用すると UAC の自動昇格を行わなくなります

指定していない場合はタスクスケジューラを使った権限の自動昇格を行います
タスクの詳細はこのコマンドで確認できます
複雑な処理を行うため数秒程度起動速度が遅くなる場合があります
`schtasks.exe /query /tn \Microsoft\Windows\DMMGamePlayerFastLauncher\`

また、このコマンドで削除できます
`Get-ScheduledTask | where TaskPath -eq "\Microsoft\Windows\DMMGamePlayerFastLauncher" | Unregister-ScheduledTask -Confirm:$false`
または `.\tools\refresh.ps1`

### schtasks-path

`schtasks.exe`のパスを指定します
ほとんどの場合、この引数は不要です

## ファイル階層

| ファイル名 | 削除 | 詳細 |
| --------------------------------------- | ---- | ------------------------------------------------------------------------------ |
| DMMGamePlayerFastLauncher.exe | x | 本体 |
| unins000.dat | o | アンインストールする際に必要 |
| unins000.exe | x | アンインストールする際に必要 |
| cookie.bytes | o | セッションのキャッシュファイル, DMM のセッションが取得できなかった際に使用する |
| tools/DMMGamePlayerProductIdChecker.exe | o | プロダクト ID のチェッカー,左ダブルクリックで実行 |
| tools/Task.exe | x | タスクスケジューラから呼び出される |
| tools/refresh.ps1 | o | タスクを削除する,右クリックメニューから PowerShell で実行 |
| sample/ウマ娘.lnk | o | ウマ娘の起動ショートカット,左ダブルクリックで実行 |
| sample/プリコネ R.lnk | o | プリコネ R の起動ショートカット,左ダブルクリックで実行 |
| assets/template.xml | x | タスクスケジューラのテンプレートファイル |
| assets/schtasks_v1\_{username}.xml | o | タスクスケジューラの残骸ファイル |

## ヘルプ

### セットアップする際、「Windows によって PC が保護されました」と表示される
Expand Down
Binary file modified requirements.txt
Binary file not shown.
15 changes: 8 additions & 7 deletions setup.iss
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!

#define MyAppName "DMMGamePlayerFastLauncher"
#define MyAppVersion "4.5.2"
#define MyAppVersion "4.6.0"
#define MyAppPublisher "yuki"
#define MyAppURL "https://github.com/fa0311/DMMGamePlayerFastLauncher"
#define MyAppExeName "DMMGamePlayerFastLauncher.exe"
Expand All @@ -21,7 +21,7 @@ AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={autopf}\{#MyAppName}
DefaultDirName={userappdata}\{#MyAppName}
DisableDirPage=yes
ChangesAssociations=yes
DefaultGroupName={#MyAppName}
Expand All @@ -34,23 +34,24 @@ OutputBaseFilename=DMMGamePlayerFastLauncher-Setup
Compression=lzma
SolidCompression=yes
WizardStyle=modern
UninstallFilesDir={userappdata}\{#MyAppName}

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
Name: "japanese"; MessagesFile: "compiler:Languages\Japanese.isl"
Name: "chinesesimplified"; MessagesFile: "compiler:Languages\ChineseSimplified.isl"

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

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

[Icons]
Name: "{group}\{#MyAppName}"; Filename: "{userappdata}\{#MyAppName}\{#MyAppExeName}"
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Loading

0 comments on commit 4c544c6

Please sign in to comment.