def get_game_install_dir_path(target_app_id): logger.info('Get install dir path') # レジストリを見て、Steamのインストール先を探す # 32bitを見て、なければ64bitのキーを探しに行く。それでもなければそもそもインストールされていないと判断する try: steam_install_key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Valve\\Steam") except OSError: try: steam_install_key = winreg.OpenKey( winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\WOW6432Node\\Valve\\Steam") except OSError: raise Exception(_("ERR_NOT_FIND_STEAM_REGKEY")) logger.info('Find steam_install_key in reg') try: steam_install_path, key_type = winreg.QueryValueEx( steam_install_key, "InstallPath") except FileNotFoundError: raise Exception(_("ERR_NOT_FIND_INSTALLPATH_IN_STEAM_REGKEY")) steam_install_key.Close() logger.info('steam_install_path=%s, key_type=%s', steam_install_path, key_type) # 基本問題ないと思うが念の為 if key_type != winreg.REG_SZ: raise Exception("invald value") # デフォルトのsteamappsフォルダを探す steam_apps_path = __(steam_install_path, "steamapps") if os.path.exists(steam_apps_path) is False: raise Exception(_("ERR_NOT_EXIST_DEFAULT_STEAMAPPS_DIR")) logger.info('steam_apps_path=%s', steam_apps_path) # acfがあるフォルダを列挙 acf_dir_paths = [steam_apps_path] acf_dir_paths.extend(get_lib_folders_from_vdf(steam_apps_path)) logger.info('acf_dir_paths=%s', acf_dir_paths) # 各ディレクトリについて処理 game_install_dir_path = None for dir_path in acf_dir_paths: game_install_dir_path = get_game_install_dir(dir_path, target_app_id) if game_install_dir_path is None: continue else: break logger.info('game_install_dir_path=%s', game_install_dir_path) if game_install_dir_path is None: raise Exception(_("ERR_NOT_FIND_TARGET_GAME_ON_YOUR_PC")) return game_install_dir_path
def done(self): try: self.result() r['text'] = _('TASK_FINISH') time.sleep(1) except Exception as exp: messagebox.showerror(_('ERROR_BOX_TITLE'), _('ERROR_BOX_MESSAGE') + ":\n" + str(exp)) finally: r['text'] = original_text
def uninstaller(uninstall_info_list): logger.info('uninstall') for info in uninstall_info_list: final_check_file = info['final_check_file'] remove_target_paths = info['remove_target_paths'] logger.info('remove_target_paths=%s', remove_target_paths) base_path = '.' if 'app_id' in info: base_path = get_game_install_dir_path(info['app_id']) if os.path.exists(__(base_path, "claes.exe")): logger.info('claes uninstall mode') sb.call(__(base_path, "claes.exe /uninstall-all")) elif 'game_dir_name' in info: base_path = __(get_my_documents_folder(), "Paradox Interactive", info['game_dir_name']) # Modダウンローダーをuninstallモードで起動 if os.path.exists(__(base_path, "claes.exe")): logger.info('claes uninstall mode') sb.call(__(base_path, "claes.exe /uninstall-all")) logger.info('base_path=%s', base_path) # 最終チェックファイルがあるかを確認する。このファイルがあるかどうかで本当にインストールされているか判断する if os.path.exists(__(base_path, final_check_file)) is False: raise Exception(_('ERR_NOT_EXIST_FINAL_CHECK_FILE')) for path in remove_target_paths: remove_util(__(base_path, path)) logger.info('finish')
def get_lib_folders_from_vdf(steam_apps_path): logger.info('get lib folders from vdf') # vdfファイルを探す library_folders_vdf_path = __(steam_apps_path, "libraryfolders.vdf") if os.path.exists(library_folders_vdf_path) is False: raise Exception(_("ERR_NOT_FIND_LIBRARYFOLDERS_VDF")) logger.info('library_folders_vdf_path=%s', library_folders_vdf_path) # vdfファイルにある"[数字]" "xxxx"をさがす install_dir_pattern = re.compile(r'\s*"[0-9a-zA-Z]+"\s+"(.*)') logger.info('install_dir_pattern=%s', install_dir_pattern) game_libs_paths = [] with open(library_folders_vdf_path, mode='r', encoding="utf8", errors='ignore') as target_vdf_file: logger.info('open %s', library_folders_vdf_path) for line in target_vdf_file: result = install_dir_pattern.match(line) if result is not None: game_libs_paths.append( __( result.group(1).strip("\"").replace("\\\\", "\\"), "steamapps")) logger.info('game_libs_paths=%s', game_libs_paths) return game_libs_paths
def get_my_documents_folder(): logger.info('get my documents folder') # APIを使って探す buf = ctypes.create_unicode_buffer(MAX_PATH + 1) if ctypes.windll.shell32.SHGetSpecialFolderPathW(None, buf, 0x1005, False): logger.info('search done. buf.value=%s', buf.value) return buf.value else: raise Exception(_("ERR_SHGetSpecialFolderPathW"))
def get_game_install_dir(dir_path, target_app_id): logger.info('get game install dir') # インストールディレクトリにあるsteamapps/appmanifest_[APPID].acfを探す target_app_acf_path = __(dir_path, "appmanifest_{}.acf".format(target_app_id)) logger.info('target_app_acf_path=%s', target_app_acf_path) # なければ終了 if os.path.exists(target_app_acf_path) is False: return None # acfファイルにある"installdir" "xxxx"をさがす install_dir_pattern = re.compile(r'\s*"installdir"\s+"(.*)') game_install_dir_name = None with open(target_app_acf_path, mode='r', encoding="utf8", errors='ignore') as target_app_acf_file: logger.info('open %s', target_app_acf_path) for line in target_app_acf_file: result = install_dir_pattern.match(line) if result is not None: game_install_dir_name = result.group(1).strip("\"") break logger.info('game_install_dir_name=%s', game_install_dir_name) if game_install_dir_name is None: raise Exception(_("ERR_INVALID_ACF")) # パスを確認 game_install_dir_path = __(dir_path, "common", game_install_dir_name) if os.path.exists(game_install_dir_path) is False: raise Exception(_("ERR_NOT_EXIST_GAME_INSTALL_DIR")) logger.info('game_install_dir_path=%s', game_install_dir_path) return game_install_dir_path
def threader(r, func): original_text = r['text'] r['text'] = _('TASK_DO') feature = executor.submit(func) def done(self): try: self.result() r['text'] = _('TASK_FINISH') time.sleep(1) except Exception as exp: messagebox.showerror(_('ERROR_BOX_TITLE'), _('ERROR_BOX_MESSAGE') + ":\n" + str(exp)) finally: r['text'] = original_text feature.add_done_callback(done)
def install(install_dir_path, target_zip_url, final_check_file): logger.info('install') final_check_file_path = __(install_dir_path, final_check_file) logger.info('final_check_file_path=%s', final_check_file_path) # 最終チェックファイルがあるかを確認する if os.path.exists(final_check_file_path) is False: raise Exception(_('ERR_NOT_EXIST_FINAL_CHECK_FILE')) # 最終チェックファイルがある場所に対してファイルを展開する final_check_file_dir = os.path.dirname(final_check_file_path) logger.info('final_check_file_dir=%s', final_check_file_dir) path, headers = urllib.request.urlretrieve(target_zip_url) logger.info('path=%s,headers=%s', path, headers) with zipfile.ZipFile(path) as existing_zip: existing_zip.extractall(final_check_file_dir) logger.info('zip unpacked')
def about(): logger.info('open About dialog') messagebox.showinfo(_('ABOUT_BOX_TITLE'), _('ABOUT_BOX_MESSAGE'))
# ログ設定 # https://beenje.github.io/blog/posts/logging-to-a-tkinter-scrolledtext-widget/ log_queue = queue.Queue() queue_handler = QueueHandler(log_queue) logger.addHandler(queue_handler) formatter = logging.Formatter('%(asctime)s: %(message)s') queue_handler.setFormatter(formatter) logger.info('GUI setup') repo_url = "https://raw.githubusercontent.com/matanki-saito/SimpleInstaller/master/" executor = concurrent.futures.ThreadPoolExecutor(max_workers=2) root = tkinter.Tk() root.title(_('TITLE')) root.geometry("300x300") # ノートブック style = ttk.Style() style.configure("BW.TLabel", foreground="black", background="white") nb = ttk.Notebook(width=300, height=200, style="BW.TLabel") # タブの作成 tab2 = tkinter.Frame(nb, pady=0, relief='flat') tab3 = tkinter.Frame(nb, pady=0, relief='flat') tab4 = tkinter.Frame(nb, pady=0, relief='flat') nb.add(tab2, text=_('TAB_JPMOD'), padding=0) nb.add(tab3, text=_('TAB_INFO'), padding=0)