diff --git a/data_from_portwine/plugins/pp-games-lib b/data_from_portwine/plugins/pp-games-lib
deleted file mode 100755
index 69fcb8ee..00000000
--- a/data_from_portwine/plugins/pp-games-lib
+++ /dev/null
@@ -1,484 +0,0 @@
-#!/usr/bin/env python3
-
-import os
-import re
-import shlex
-import shutil
-import logging
-from configparser import RawConfigParser
-from pathlib import Path
-from subprocess import run
-from types import SimpleNamespace
-try:
- from PyQt6.QtCore import * # type: ignore
- from PyQt6.QtGui import * # type: ignore
- from PyQt6.QtWidgets import * # type: ignore
-except ModuleNotFoundError:
- from PyQt5.QtCore import * # type: ignore
- from PyQt5.QtGui import * # type: ignore
- from PyQt5.QtWidgets import * # type: ignore
-
-settings = QSettings('PPGL', 'PortProtonGamesLib')
-g = SimpleNamespace(locale = '')
-
-class MainWindow(QMainWindow):
- def __init__(self):
- super().__init__()
-
- self.resize(QSize(800, 600))
- geometry = settings.value('geometry_main')
- if geometry:
- self.restoreGeometry(geometry)
-
- shortcut = RawConfigParser()
- shortcut.read(os.getenv('HOME') + '/.local/share/applications/PortProton.desktop')
- scripts_dir = shortcut.get('Desktop Entry', 'Path', fallback=os.getenv('HOME') + '/.local/share/PortWINE/PortProton/data/scripts')
- if not scripts_dir or not Path(scripts_dir).is_dir():
- QMessageBox.critical(self, 'Error', 'Can not find installed PortProton')
- exit(1)
- g.scripts_dir = scripts_dir.rstrip('/')
- g.pp_icon = shortcut.get('Desktop Entry', 'Icon', fallback='/usr/share/pixmaps/portproton.png')
- pp_icon = QIcon(g.pp_icon)
- self.setWindowIcon(pp_icon)
- self.setWindowTitle('PortProton games library')
-
- g.base_dir = str(Path(scripts_dir + '/../..').resolve())
- g.install_pfx = g.base_dir + '/data/prefixes/INSTALL'
- g.shortcuts_dir = g.base_dir + '/shortcuts'
- g.games_dir = g.base_dir + '/games'
-
- loc_path = Path(g.base_dir + '/data/tmp/PortProton_loc')
- if loc_path.exists():
- g.locale = loc_path.read_text().strip()
-
- Path(g.shortcuts_dir).mkdir(parents=True, exist_ok=True)
- Path(g.games_dir).mkdir(parents=True, exist_ok=True)
-
- sep = QFrame(self)
- sep.setFrameShape(QFrame.Shape.VLine)
- sep.setFrameShadow(QFrame.Shadow.Sunken)
- self._status_size = QLabel(self)
- self._status_dir = QLabel(self)
- self._status_wine = QLabel(self)
- self.statusBar().setVisible(False)
- self.statusBar().addWidget(self._status_dir, 1)
- self.statusBar().addWidget(self._status_wine)
- self.statusBar().addWidget(sep)
- self.statusBar().addWidget(self._status_size)
-
-
- self.game_list = GameList(self)
- self.setCentralWidget(self.game_list)
-
- self.toolbar = self.addToolBar('Main')
- self.toolbar.setIconSize(QSize(32, 32))
- self.toolbar.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon)
- self.toolbar.setMovable(False)
- action = QAction(self.style().standardIcon(QStyle.StandardPixmap.SP_FileDialogNewFolder), _tr('Install new game'), self)
- action.triggered.connect(self.install_game)
- self.toolbar.addAction(action)
- action = QAction(self.style().standardIcon(QStyle.StandardPixmap.SP_FileLinkIcon), _tr('Add game entry'), self)
- action.triggered.connect(self.add_game)
- self.toolbar.addAction(action)
- action = QAction(self.style().standardIcon(QStyle.StandardPixmap.SP_BrowserReload), _tr('Reload list'), self)
- action.triggered.connect(self.reload_list)
- self.toolbar.addAction(action)
- action = QAction(self.style().standardIcon(QStyle.StandardPixmap.SP_TrashIcon), _tr('Drop install prefix'), self)
- action.triggered.connect(self.drop_prefix)
- self.toolbar.addAction(action)
- spacer = QWidget(self)
- spacer.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
- self.toolbar.addWidget(spacer)
- action = QAction(pp_icon, 'PortProton', self)
- action.triggered.connect(self.run_pp)
- self.toolbar.addAction(action)
-
- def install_game(self):
- InstallGame(self)
-
- def add_game(self):
- InstallGame(self, False)
-
- def reload_list(self):
- self.game_list.reload()
-
- def drop_prefix(self):
- res = QMessageBox.question(self, _tr('Are you sure ?'), _tr('Do you really want to remove
{0} ?', g.install_pfx))
- if res == QMessageBox.StandardButton.Yes:
- shutil.rmtree(g.install_pfx, True)
-
- def run_pp(self):
- self.setDisabled(True)
- app.processEvents()
- run([g.scripts_dir + '/start.sh'])
- self.setDisabled(False)
-
- def set_status(self, item):
- self.statusBar().setVisible(bool(item))
- if item:
- self._status_size.setText('Size: ' + item.dir_size_human)
- self._status_dir.setText(' ' + item.game_dir)
- self._status_wine.setText(item.wine_use)
-
- def closeEvent(self, event):
- geometry = self.saveGeometry()
- settings.setValue('geometry_main', geometry)
- super().closeEvent(event)
-
-class LoadListThread(QThread):
- completed = pyqtSignal(list)
- def __init__(self, parent, install_dir):
- super().__init__(parent)
- self.install_dir = install_dir
- def run(self):
- exe_list = list(Path(self.install_dir).glob('**/*.exe'))
- self.completed.emit(exe_list)
-
-class InstallGame(QDialog):
- def __init__(self, parent, installing=True):
- super().__init__(parent)
- self._installing = installing
- self.install_dir = g.install_pfx + '/drive_c/Games' if installing else g.games_dir
- self._exe_list_widget = QListWidget(self)
- self._exe_list_widget.setIconSize(QSize(16, 16))
- self._exe_list_widget.itemDoubleClicked.connect(self._handleDoubleClick)
- layout = QVBoxLayout()
- layout.addWidget(self._exe_list_widget)
-
- self._pbar = QProgressBar(self)
- self._pbar.setMaximum(0)
- layout.addWidget(self._pbar)
- thread = LoadListThread(self, self.install_dir)
- thread.completed.connect(self.load)
- thread.start()
-
- if self._installing:
- setup_btn = QPushButton(self)
- setup_btn.setIcon(self.style().standardIcon(QStyle.StandardPixmap.SP_FileDialogStart))
- setup_btn.setText(_tr('Run another setup'))
- setup_btn.clicked.connect(self._runSetup)
- layout.addWidget(setup_btn)
- self.setLayout(layout)
- self.resize(400, 300)
- self.setModal(True)
- self.setWindowTitle(_tr('Select game exe file'))
- geometry = settings.value('geometry_install')
- if geometry:
- self.restoreGeometry(geometry)
- self.show()
-
- def load(self, exe_list):
- if self._installing and len(exe_list) == 0:
- self._runSetup()
- exe_list = list(Path(self.install_dir).glob('**/*.exe'))
- if len(exe_list) == 0:
- return self.close()
- def render_list():
- pixmap = QPixmap(16, 16)
- pixmap.fill(Qt.GlobalColor.transparent)
- empty_icon = QIcon(pixmap)
- for exe in sorted(exe_list):
- ico_file = str(exe) + '.ico'
- item = QListWidgetItem(self._exe_list_widget)
- item.setText(str(exe)[len(self.install_dir)+1:])
- try:
- if not Path(ico_file).exists():
- run(['wrestool', '-x', '-t14', '-o', ico_file, exe], capture_output=True)
- item.setIcon(QIcon(ico_file))
- except Exception:
- pass
- if item.icon().pixmap(16, 16).isNull():
- item.setIcon(empty_icon)
- self._exe_list_widget.addItem(item)
- self._pbar.setVisible(False)
- thread = QThread(self)
- thread.run = render_list
- thread.start()
-
- def _runSetup(self):
- downloads_dir = QStandardPaths.writableLocation(QStandardPaths.StandardLocation.DownloadLocation)
- exe_file, _ = QFileDialog.getOpenFileName(self, caption=_tr('Choose setup file'), filter='Exe files (*.exe)', directory=downloads_dir)
- if not exe_file:
- return
- ppdb = shlex.quote(exe_file + '.ppdb')
- script = f"""
- mkdir -p {shlex.quote(g.install_pfx + '/drive_c/Games')}
- echo '
- export PW_VULKAN_USE=1
- export PW_GUI_DISABLED_CS=1
- export PW_PREFIX_NAME=INSTALL
- export PW_DLL_INSTALL=mfc42
- ' > {ppdb}
- {shlex.quote(g.scripts_dir + '/start.sh')} {shlex.quote(exe_file)}
- rm -f {ppdb}
- """
- self.setDisabled(True)
- app.processEvents()
- run(['bash', '-c', script])
- self.setDisabled(False)
-
- def _handleDoubleClick(self, item):
- game_dir = item.text().split('/')[0]
- dlg = QInputDialog(self)
- dlg.setWindowTitle(_tr('Please enter game entry name'))
- dlg.setLabelText(_tr('New game entry'))
- dlg.setTextValue(game_dir)
- dlg.resize(300, 0)
- ok = dlg.exec()
- shortcut_name = dlg.textValue()
- if not ok or not shortcut_name:
- return
- file_name = re.sub(r'[<>:/\\|?*]', '_', shortcut_name)
- shortcut = f"{g.shortcuts_dir}/{file_name}.desktop"
- if Path(shortcut).exists():
- res = QMessageBox.question(self, _tr('Shortcut already exists'), _tr('Shortcut {0} already exists. Overwrite ?', file_name))
- if res != QMessageBox.StandardButton.Yes:
- return
- src_dir = self.install_dir + '/' + game_dir
- dst_dir = g.games_dir + '/' + game_dir
- exe_file = shlex.quote(g.games_dir + '/' + item.text())
- ppdb = shlex.quote(g.games_dir + '/' + item.text() + '.ppdb')
- self.setDisabled(True)
- if self._installing and Path(dst_dir).exists():
- res = QMessageBox.question(self, _tr('Dir already exists'), _tr('Dir {0} already exists. Overwrite ?', game_dir))
- if res != QMessageBox.StandardButton.Yes:
- return
- if self._installing:
- os.rename(src_dir, dst_dir)
- script = f"""
- export INSTALLING_PORT=1
- export portwine_exe={exe_file}
- cd {shlex.quote(g.scripts_dir)}
- . {shlex.quote(g.scripts_dir + '/runlib')}
- pw_init_db
- [ -f {ppdb} ] && . {ppdb}
- echo -e "export PW_VULKAN_USE=${{PW_VULKAN_USE:-1}}\nexport PW_GUI_DISABLED_CS=1" >> {ppdb}
- """
- run(['bash', '-c', script])
- icon_path = g.games_dir + '/' + item.text() + '.ico'
- if not Path(icon_path).exists():
- icon_path = g.pp_icon
- Path(shortcut).write_text(f"""[Desktop Entry]
-Name={shortcut_name}
-Exec=env {shlex.quote(g.scripts_dir + '/start.sh')} {exe_file}
-Type=Application
-Categories=Game
-StartupNotify=true
-Path={shlex.quote(g.scripts_dir)}
-Icon={icon_path}
-""", encoding='utf-8')
- os.chmod(shortcut, 0o755)
- win.reload_list()
- self.close()
-
- def closeEvent(self, event):
- geometry = self.saveGeometry()
- settings.setValue('geometry_install', geometry)
- super().closeEvent(event)
-
-
-class GameList(QListWidget):
- def __init__(self, parent):
- super().__init__(parent)
- self.itemActivated.connect(self.runGame)
- self.currentItemChanged.connect(self.selectItem)
- self.setViewMode(QListWidget.ViewMode.IconMode)
- self.setResizeMode(QListWidget.ResizeMode.Adjust)
- self.setIconSize(QSize(64, 64))
- self.setWordWrap(True)
- self.setSpacing(3)
- self.reload()
-
- def reload(self):
- self.clear()
- shortcuts = list(Path(g.shortcuts_dir).glob('*.desktop'))
- shortcuts += list(Path(g.base_dir).glob('*.desktop'))
- for shortcut in shortcuts:
- try:
- item = GameItem(self, shortcut)
- self.addItem(item)
- except ValueError:
- pass
- except:
- logging.exception('Error while parse "%s"', shortcut)
- self.sortItems()
- self.setCurrentIndex(QModelIndex())
-
- def runGame(self, item):
- win.setDisabled(True)
- app.processEvents()
- run(['bash', '-c', item.get('Exec')])
- win.setDisabled(False)
-
- def selectItem(self, item):
- win.set_status(item)
-
- def contextMenuEvent(self, event):
- selected = self.selectedItems()
- if len(selected) == 0:
- return
- selected = selected[0]
- menu = QMenu(self)
- desktop = menu.addAction(self.style().standardIcon(QStyle.StandardPixmap.SP_DesktopIcon), _tr('Add to desktop'))
- restore_gui = menu.addAction(self.style().standardIcon(QStyle.StandardPixmap.SP_DialogResetButton), _tr('Restore PortProton GUI'))
- default_wine = menu.addAction(self.style().standardIcon(QStyle.StandardPixmap.SP_DialogOkButton), _tr('Set default wine'))
- remove = menu.addAction(self.style().standardIcon(QStyle.StandardPixmap.SP_TrashIcon), _tr('Remove game entry'))
- uninstall = menu.addAction(self.style().standardIcon(QStyle.StandardPixmap.SP_DialogCloseButton), _tr('Uninstall game'))
- if not selected.pp_gui_disabled:
- restore_gui.setVisible(False)
- if not selected.wine_use:
- default_wine.setVisible(False)
- if not selected.game_dir.startswith(g.games_dir):
- uninstall.setVisible(False)
- action = menu.exec(self.mapToGlobal(event.pos()))
- desktop_shortcut = QStandardPaths.writableLocation(QStandardPaths.StandardLocation.DesktopLocation) + '/' + Path(selected.desktop_file).name
- if action == desktop:
- if Path(desktop_shortcut).exists():
- res = QMessageBox.question(self, _tr('Shortcut already exists'), _tr('Shortcut {0} already exists. Overwrite ?', desktop_shortcut))
- if res != QMessageBox.StandardButton.Yes:
- return
- shutil.copy(selected.desktop_file, desktop_shortcut)
- if action == restore_gui or action == default_wine:
- ignore_line = 'PW_GUI_DISABLED_CS' if action == restore_gui else 'PW_WINE_USE'
- ppdb = shlex.split(selected.get('Exec'))[-1] + '.ppdb'
- if not Path(ppdb).exists():
- return
- with open(ppdb, 'r') as read:
- with open(ppdb + '.new', 'w') as write:
- while (line := read.readline()):
- if ignore_line not in line:
- write.write(line)
- os.rename(ppdb + '.new', ppdb)
- if action == restore_gui:
- selected.pp_gui_disabled = False
- if action == default_wine:
- selected.wine_use = None
- self.selectItem(selected)
- def remove_shortcut():
- Path(desktop_shortcut).unlink(True)
- Path(selected.desktop_file).unlink(True)
- def_icon_path = g.base_dir + '/data/img/' + Path(shlex.split(selected.get('Exec'))[-1]).stem + '.png'
- Path(def_icon_path).unlink(True)
- if action == remove:
- remove_shortcut()
- self.reload()
- if action == uninstall:
- res = QMessageBox.question(self,
- _tr('Are you sure ?'),
- _tr('Do you really want to uninstall {0}
located in "{1}" ?', selected.get('Name'), selected.game_dir)
- )
- if res != QMessageBox.StandardButton.Yes:
- return
- remove_shortcut()
- if selected.game_dir.startswith(g.games_dir):
- shutil.rmtree(selected.game_dir, True)
- self.reload()
-
-
-def human_size(num):
- if not num:
- return "-"
- for unit in ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]:
- if abs(num) < 1024.0:
- return f"{num:.2f} {unit}B"
- num /= 1024.0
- return f"{num:.2f} YiB"
-
-class GameItem(QListWidgetItem):
- def __init__(self, parent, desktop_file):
- self.desktop_file = desktop_file
- self.config = RawConfigParser()
- self.config.read(desktop_file)
- text = self.get('Name', Path(desktop_file).stem)
- if not self.get('Exec') or text == 'PortProton':
- raise ValueError('Validation fail')
- exe_file = shlex.split(self.get('Exec'))[-1]
- if exe_file.startswith(g.games_dir):
- self.game_dir = g.games_dir + '/' + exe_file[len(g.games_dir)+1:].split('/')[0]
- else:
- self.game_dir = str(Path(exe_file).parent)
- if self.game_dir == '.':
- raise ValueError('Can not determine game dir')
- self.pp_gui_disabled = False
- self.wine_use = None
- ppdb = exe_file + '.ppdb'
- if Path(ppdb).exists():
- ppdb_conf = RawConfigParser(strict=False)
- with open(ppdb) as f:
- ppdb_conf.read_string('[dummy]\n' + f.read())
- pp_gui_disabled = ppdb_conf.get('dummy', 'export PW_GUI_DISABLED_CS', fallback='').strip('"')
- try: self.pp_gui_disabled = bool(int(pp_gui_disabled))
- except: self.pp_gui_disabled = bool(pp_gui_disabled)
- self.wine_use = ppdb_conf.get('dummy', 'export PW_WINE_USE', fallback='').strip('"')
-
- super().__init__(parent)
-
- self.setToolTip(text)
- self.setText(text)
- icon_path = self.get('Icon') if Path(self.get('Icon')).exists() else g.pp_icon
- qicon = QIcon(icon_path)
- self.setIcon(qicon)
- self.setTextAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignTop)
- self.setSizeHint(QSize(100, 105))
-
- self._set_dir_size(None)
- dir_size_cache = self.game_dir + '/.size'
- if Path(dir_size_cache).exists():
- self._set_dir_size(int(Path(dir_size_cache).read_text()))
- else:
- def calc_dir_size():
- if not Path(self.game_dir).exists():
- return
- dir_size = sum(p.stat(follow_symlinks=False).st_size for p in Path(self.game_dir).rglob('*'))
- self._set_dir_size(dir_size)
- Path(dir_size_cache).write_text(str(dir_size))
- thread = QThread(parent)
- thread.run = calc_dir_size
- thread.start()
-
- def get(self, name, fallback=None):
- return self.config.get('Desktop Entry', name, fallback=fallback)
-
- def _set_dir_size(self, size):
- self.dir_size = size
- self.dir_size_human = human_size(size)
-
-import signal
-signal.signal(signal.SIGINT, signal.SIG_DFL)
-
-lang = {
- 'RUS': {
- 'Install new game': 'Установить игру',
- 'Add game entry': 'Добавить в список',
- 'Reload list': 'Обновить список',
- 'Drop install prefix': 'Удалить установочный префикс',
- 'Are you sure ?': 'Вы уверены ?',
- 'Do you really want to remove
{0} ?': 'Вы действительно хотите удалить
{0} ?',
- 'Run another setup': 'Запустить установку',
- 'Select game exe file': 'Выберите exe файл игры',
- 'Choose setup file': 'Выберите установочный файл',
- 'Please enter game entry name': 'Введите название игры',
- 'New game entry': 'Название игры',
- 'Shortcut already exists': 'Ярлык уже существует',
- 'Shortcut {0} already exists. Overwrite ?': 'Ярлык {0} уже существует. Перезаписать ?',
- 'Dir already exists': 'Директория уже существует',
- 'Dir {0} already exists. Overwrite ?': 'Директория {0} уже существует. Перезаписать ?',
- 'Add to desktop': 'Добавить на рабочий стол',
- 'Restore PortProton GUI': 'Восстановить PortProton GUI',
- 'Set default wine': 'Выбрать дефолтный wine',
- 'Remove game entry': 'Убрать из списка',
- 'Uninstall game': 'Удалить игру',
- 'Do you really want to uninstall {0}
located in "{1}" ?': 'Вы действительно хотите удалить {0}
расположеную в "{1}" ?'
- }
-}
-def _tr(text, *fmt):
- res = lang.get(g.locale, {}).get(text, text)
- if fmt:
- res = res.format(*fmt)
- return res
-
-app = QApplication([])
-app.setDesktopFileName('PortProton')
-win = MainWindow()
-win.show()
-app.exec()