Fixed some Bugs
This commit is contained in:
parent
68ef350777
commit
dcd6a84279
64
.github/workflows/release.yml
vendored
64
.github/workflows/release.yml
vendored
|
@ -5,8 +5,8 @@ name: New Release
|
|||
|
||||
on:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
types: [ created ]
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
@ -15,40 +15,40 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.8'
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install setuptools wheel twine
|
||||
- name: Build and publish
|
||||
env:
|
||||
TWINE_USERNAME: __token__
|
||||
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
|
||||
run: |
|
||||
python setup.py sdist bdist_wheel
|
||||
twine upload dist/*
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.8'
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install setuptools wheel twine
|
||||
- name: Build and publish
|
||||
env:
|
||||
TWINE_USERNAME: __token__
|
||||
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
|
||||
run: |
|
||||
python setup.py sdist bdist_wheel
|
||||
twine upload dist/*
|
||||
|
||||
aur-publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Generate PKGBUILD
|
||||
run:
|
||||
./.github/GenPKG.sh
|
||||
|
||||
- name: Upload
|
||||
uses: KSXGitHub/github-actions-deploy-aur@v2.2.3
|
||||
with:
|
||||
pkgname: rare
|
||||
pkgbuild: ./.github/rare/PKGBUILD
|
||||
commit_username: Dummerle
|
||||
commit_email: ${{ secrets.MAIL }}
|
||||
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
||||
commit_message: Update AUR package
|
||||
- uses: actions/checkout@v2
|
||||
- name: Generate PKGBUILD
|
||||
run:
|
||||
./.github/GenPKG.sh
|
||||
|
||||
- name: Upload
|
||||
uses: KSXGitHub/github-actions-deploy-aur@v2.2.3
|
||||
with:
|
||||
pkgname: rare
|
||||
pkgbuild: ./.github/rare/PKGBUILD
|
||||
commit_username: Dummerle
|
||||
commit_email: ${{ secrets.MAIL }}
|
||||
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
||||
commit_message: Update AUR package
|
||||
|
||||
|
||||
|
||||
|
|
16
README.md
16
README.md
|
@ -2,8 +2,9 @@
|
|||
|
||||
## A frontend for legendary, the open source Epic Games Launcher alternative
|
||||
|
||||
Rare is currently considered beta software and in no way feature-complete. You will probably run into issues, so it is recommend to make a backup. If you have features you want to have in this app, create an issue on github, contact me on Discord (Dummerle#7419) or build it yourself. Please report bugs so I can fix them.
|
||||
|
||||
Rare is currently considered beta software and in no way feature-complete. You will probably run into issues, so it is
|
||||
recommend to make a backup. If you have features you want to have in this app, create an issue on github, contact me on
|
||||
Discord (Dummerle#7419) or build it yourself. Please report bugs so I can fix them.
|
||||
|
||||
### Requirements
|
||||
|
||||
|
@ -19,16 +20,18 @@ Rare is currently considered beta software and in no way feature-complete. You w
|
|||
|
||||
Execute *pip install Rare* for all users Or *pip install Rare --user* for only one user
|
||||
|
||||
**Note**: On Linux must be /home/user/.local/bin in PATH
|
||||
|
||||
### Windows Simple
|
||||
|
||||
Download Rare.exe and place it somewhere in PATH
|
||||
|
||||
**Note**
|
||||
Using the exe-file could cause an error with the stylesheets
|
||||
Using the exe file could cause errors
|
||||
|
||||
## Linux
|
||||
|
||||
- For Arch Linux is an AUR package available: [rare-git](https://aur.archlinux.org/packages/rare-git)
|
||||
- For Arch Linux is an AUR package available: [rare-git](https://aur.archlinux.org/packages/rare-git) or [rare](https://aur.archlinux.org/packages/rare)
|
||||
- Other distributions have to install it with pip or clone the repo and install it manually: *python3 setup.py install*
|
||||
|
||||
## Implemented
|
||||
|
@ -37,12 +40,13 @@ Using the exe-file could cause an error with the stylesheets
|
|||
- Authentication(Import from existing installation and via Browser)
|
||||
- Download progress bar
|
||||
- Settings (Legendary and games)
|
||||
- Sync Cloud Saves
|
||||
- Translations (English and German)
|
||||
|
||||
## Planned
|
||||
- Sync Cloud Saves
|
||||
|
||||
- Offline mode
|
||||
- More Translations
|
||||
- More Translations (Need help)
|
||||
|
||||
## Images
|
||||
|
||||
|
|
|
@ -53,7 +53,8 @@ class InstallInfoDialog(QDialog):
|
|||
super(InstallInfoDialog, self).__init__()
|
||||
self.layout = QVBoxLayout()
|
||||
self.infos = QLabel(self.tr(
|
||||
"Download size: {}GB\nInstall size: {}GB").format(round(dl_size / 1024 ** 3, 2), round(install_size / 1024 ** 3, 2)))
|
||||
"Download size: {}GB\nInstall size: {}GB").format(round(dl_size / 1024 ** 3, 2),
|
||||
round(install_size / 1024 ** 3, 2)))
|
||||
self.layout.addWidget(self.infos)
|
||||
|
||||
self.btn_layout = QHBoxLayout()
|
||||
|
|
|
@ -3,6 +3,7 @@ from logging import getLogger
|
|||
|
||||
from PyQt5.QtCore import pyqtSignal
|
||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QLabel, QLineEdit
|
||||
|
||||
from custom_legendary.core import LegendaryCore
|
||||
|
||||
logger = getLogger("BrowserLogin")
|
||||
|
@ -21,7 +22,8 @@ class BrowserLogin(QWidget):
|
|||
self.layout.addWidget(self.back)
|
||||
|
||||
self.info_text = QLabel(self.tr(
|
||||
"Opens a browser. You login and copy the json code in the field below. Click <a href='{}'>here</a> to open Browser").format(self.url))
|
||||
"Opens a browser. You login and copy the json code in the field below. Click <a href='{}'>here</a> to open Browser").format(
|
||||
self.url))
|
||||
self.info_text.setWordWrap(True)
|
||||
self.info_text.setOpenExternalLinks(True)
|
||||
self.layout.addWidget(self.info_text)
|
||||
|
|
|
@ -4,6 +4,7 @@ from logging import getLogger
|
|||
|
||||
from PyQt5.QtCore import pyqtSignal
|
||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QLabel, QButtonGroup, QRadioButton
|
||||
|
||||
from custom_legendary.core import LegendaryCore
|
||||
|
||||
logger = getLogger("Import")
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QLabel, QStackedLayout, QWidget, QPushButton
|
||||
from custom_legendary.core import LegendaryCore
|
||||
|
||||
# Login Opportunities: Browser, Import
|
||||
from Rare.Components.Dialogs.Login.BrowserLogin import BrowserLogin
|
||||
from Rare.Components.Dialogs.Login.ImportWidget import ImportWidget
|
||||
from custom_legendary.core import LegendaryCore
|
||||
|
||||
|
||||
class LoginDialog(QDialog):
|
||||
|
|
|
@ -41,4 +41,4 @@ class PathInputDialog(QDialog):
|
|||
|
||||
def ok(self):
|
||||
self.path = self.input.text()
|
||||
self.close()
|
||||
self.close()
|
||||
|
|
|
@ -2,10 +2,10 @@ from logging import getLogger
|
|||
|
||||
from PyQt5.QtCore import QThread, pyqtSignal
|
||||
from PyQt5.QtWidgets import QDialog, QLabel, QProgressBar, QVBoxLayout
|
||||
from custom_legendary.core import LegendaryCore
|
||||
|
||||
from Rare.Components.Dialogs.Login.LoginDialog import LoginDialog
|
||||
from Rare.utils.utils import download_images
|
||||
from custom_legendary.core import LegendaryCore
|
||||
|
||||
logger = getLogger("Login")
|
||||
|
||||
|
@ -73,7 +73,7 @@ class LaunchDialog(QDialog):
|
|||
exit(0)
|
||||
|
||||
def launch(self):
|
||||
#self.core = core
|
||||
# self.core = core
|
||||
self.info_pb.setMaximum(len(self.core.get_game_list()))
|
||||
self.info_text.setText(self.tr("Downloading Images"))
|
||||
self.thread = LaunchThread(self.core, self)
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
from PyQt5.QtCore import QSize
|
||||
from PyQt5.QtGui import QFont
|
||||
from PyQt5.QtWidgets import QTabWidget, QTabBar, QWidget, QToolButton, QWidgetAction, QMenu
|
||||
from Rare.Components.Tabs.CloudSaves.CloudSaves import SyncSaves
|
||||
from custom_legendary.core import LegendaryCore
|
||||
from qtawesome import icon
|
||||
|
||||
# from Rare.Components.Tabs.Account.AccountWidget import MiniWidget
|
||||
from Rare.Components.Tabs.Account.AccountWidget import MiniWidget
|
||||
from Rare.Components.Tabs.CloudSaves.CloudSaves import SyncSaves
|
||||
from Rare.Components.Tabs.Downloads.DownloadTab import DownloadTab
|
||||
from Rare.Components.Tabs.Games.GamesTab import GameTab
|
||||
from Rare.Components.Tabs.Settings.SettingsTab import SettingsTab
|
||||
from Rare.utils.Models import InstallOptions
|
||||
from custom_legendary.core import LegendaryCore
|
||||
|
||||
|
||||
class TabWidget(QTabWidget):
|
||||
|
|
|
@ -2,6 +2,7 @@ import webbrowser
|
|||
|
||||
from PyQt5.QtCore import QCoreApplication
|
||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QMessageBox, QLabel, QPushButton
|
||||
|
||||
from custom_legendary.core import LegendaryCore
|
||||
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ class _DownloadThread(QThread):
|
|||
|
||||
class SyncWidget(QWidget):
|
||||
reload = pyqtSignal()
|
||||
|
||||
def __init__(self, igame: InstalledGame, save, core: LegendaryCore):
|
||||
super(SyncWidget, self).__init__()
|
||||
self.layout = QVBoxLayout()
|
||||
|
@ -152,6 +153,7 @@ class SyncWidget(QWidget):
|
|||
self.core.lgd.set_installed_game(self.igame.app_name, self.igame)
|
||||
self.save_path_text.setText(self.igame.save_path)
|
||||
self.reload.emit()
|
||||
|
||||
def upload(self):
|
||||
self.logger.info("Uploading Saves for game " + self.igame.title)
|
||||
self.info_text.setText(self.tr("Uploading..."))
|
||||
|
@ -182,4 +184,4 @@ class SyncWidget(QWidget):
|
|||
self.upload_button.setDisabled(True)
|
||||
self.download_button.setDisabled(True)
|
||||
self.download_button.setStyleSheet("QPushButton{background-color: black}")
|
||||
self.reload.emit()
|
||||
self.reload.emit()
|
||||
|
|
|
@ -9,17 +9,14 @@ from PyQt5.QtCore import QThread, pyqtSignal, Qt
|
|||
from PyQt5.QtWidgets import QWidget, QMessageBox, QVBoxLayout, QLabel, QGridLayout, QProgressBar, QPushButton, QDialog, \
|
||||
QListWidget, QHBoxLayout
|
||||
|
||||
|
||||
|
||||
from Rare.Components.Dialogs.InstallDialog import InstallInfoDialog
|
||||
from Rare.utils.Models import InstallOptions, KillDownloadException
|
||||
from custom_legendary.core import LegendaryCore
|
||||
from custom_legendary.downloader.manager import DLManager
|
||||
from custom_legendary.models.downloading import UIUpdate
|
||||
from custom_legendary.models.game import Game
|
||||
from custom_legendary.utils.selective_dl import games
|
||||
|
||||
from Rare.Components.Dialogs.InstallDialog import InstallInfoDialog
|
||||
from Rare.utils.Models import InstallOptions, KillDownloadException
|
||||
|
||||
logger = getLogger("Download")
|
||||
|
||||
|
||||
|
@ -46,7 +43,7 @@ class DownloadThread(QThread):
|
|||
time.sleep(1)
|
||||
while self.dlm.is_alive():
|
||||
if self.kill:
|
||||
#raise KillDownloadException()
|
||||
# raise KillDownloadException()
|
||||
# TODO kill download queue, workers
|
||||
pass
|
||||
try:
|
||||
|
|
|
@ -4,8 +4,6 @@ from PyQt5.QtCore import Qt, pyqtSignal
|
|||
from PyQt5.QtGui import QPixmap, QKeyEvent
|
||||
from PyQt5.QtWidgets import QWidget, QPushButton, QVBoxLayout, QLabel, QHBoxLayout, QTabWidget, QMessageBox, \
|
||||
QProgressBar, QStackedWidget
|
||||
from custom_legendary.core import LegendaryCore
|
||||
from custom_legendary.models.game import InstalledGame, Game
|
||||
from qtawesome import icon
|
||||
|
||||
from Rare.Components.Tabs.Games.GameInfo.GameSettings import GameSettings
|
||||
|
@ -13,6 +11,8 @@ from Rare.utils import LegendaryApi
|
|||
from Rare.utils.LegendaryApi import VerifyThread
|
||||
from Rare.utils.QtExtensions import SideTabBar
|
||||
from Rare.utils.utils import IMAGE_DIR
|
||||
from custom_legendary.core import LegendaryCore
|
||||
from custom_legendary.models.game import InstalledGame, Game
|
||||
|
||||
|
||||
class InfoTabs(QTabWidget):
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import os
|
||||
|
||||
from PyQt5.QtWidgets import QWidget, QLabel, QVBoxLayout, QComboBox, QFileDialog, QPushButton, QMessageBox
|
||||
from custom_legendary.core import LegendaryCore
|
||||
from custom_legendary.models.game import InstalledGame, Game
|
||||
|
||||
from Rare.Components.Tabs.Settings.Linux import LinuxSettings
|
||||
from Rare.Components.Tabs.Settings.SettingsWidget import SettingsWidget
|
||||
from Rare.utils.QtExtensions import PathEdit
|
||||
from custom_legendary.core import LegendaryCore
|
||||
from custom_legendary.models.game import InstalledGame, Game
|
||||
|
||||
|
||||
class GameSettings(QWidget):
|
||||
|
@ -44,7 +44,7 @@ class GameSettings(QWidget):
|
|||
for i in os.listdir(os.path.expanduser("~/.steam/steam/steamapps/common")):
|
||||
if i.startswith("Proton"):
|
||||
wrapper = '"' + os.path.join(os.path.expanduser("~/.steam/steam/steamapps/common"), i,
|
||||
"proton") + '" run'
|
||||
"proton") + '" run'
|
||||
self.possible_proton_wrappers.append(wrapper)
|
||||
except FileNotFoundError as e:
|
||||
print("Unable to find Proton:", e)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
from PyQt5.QtCore import Qt, pyqtSignal, QSettings
|
||||
from PyQt5.QtWidgets import *
|
||||
from custom_legendary.core import LegendaryCore
|
||||
|
||||
from Rare.Components.Tabs.Games.GameWidgetInstalled import GameWidgetInstalled
|
||||
from Rare.Components.Tabs.Games.GameWidgetListUninstalled import UninstalledGameWidget
|
||||
|
@ -8,6 +7,7 @@ from Rare.Components.Tabs.Games.GameWidgetUninstalled import GameWidgetUninstall
|
|||
from Rare.Components.Tabs.Games.InstalledListWidget import GameWidget
|
||||
from Rare.utils.Models import InstallOptions
|
||||
from Rare.utils.QtExtensions import FlowLayout
|
||||
from custom_legendary.core import LegendaryCore
|
||||
|
||||
|
||||
class GameList(QScrollArea):
|
||||
|
@ -89,4 +89,4 @@ class GameList(QScrollArea):
|
|||
self.update()
|
||||
|
||||
def import_game(self):
|
||||
pass
|
||||
pass
|
||||
|
|
|
@ -4,12 +4,12 @@ from logging import getLogger
|
|||
from PyQt5.QtCore import QEvent, pyqtSignal, QSettings, QSize, Qt
|
||||
from PyQt5.QtGui import QPixmap, QMouseEvent
|
||||
from PyQt5.QtWidgets import *
|
||||
from custom_legendary.core import LegendaryCore
|
||||
from custom_legendary.models.game import InstalledGame
|
||||
from qtawesome import icon
|
||||
|
||||
from Rare.utils import LegendaryApi
|
||||
from Rare.utils.QtExtensions import ClickableLabel
|
||||
from custom_legendary.core import LegendaryCore
|
||||
from custom_legendary.models.game import InstalledGame
|
||||
|
||||
logger = getLogger("GameWidgetInstalled")
|
||||
|
||||
|
@ -87,9 +87,9 @@ class GameWidgetInstalled(QWidget):
|
|||
self.info_label.setObjectName("info_label")
|
||||
self.layout.addWidget(self.info_label)
|
||||
|
||||
#p = self.palette()
|
||||
#p.setColor(self.backgroundRole(), Qt.red)
|
||||
#self.setPalette(p)
|
||||
# p = self.palette()
|
||||
# p.setColor(self.backgroundRole(), Qt.red)
|
||||
# self.setPalette(p)
|
||||
|
||||
self.setLayout(self.layout)
|
||||
self.setFixedWidth(self.sizeHint().width())
|
||||
|
@ -108,7 +108,7 @@ class GameWidgetInstalled(QWidget):
|
|||
else:
|
||||
self.info_label.setText(self.info_text)
|
||||
|
||||
def mousePressEvent(self, e:QMouseEvent):
|
||||
def mousePressEvent(self, e: QMouseEvent):
|
||||
# left button
|
||||
if e.button() == 1:
|
||||
if self.update_available:
|
||||
|
@ -119,7 +119,6 @@ class GameWidgetInstalled(QWidget):
|
|||
elif e.button() == 2:
|
||||
pass
|
||||
|
||||
|
||||
def launch(self, offline=False, skip_version_check=False):
|
||||
if not self.running:
|
||||
logger.info("Launching " + self.game.title)
|
||||
|
|
|
@ -4,13 +4,13 @@ from logging import getLogger
|
|||
from PyQt5.QtCore import pyqtSignal, QSettings
|
||||
from PyQt5.QtGui import QPixmap
|
||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel
|
||||
from custom_legendary.core import LegendaryCore
|
||||
from custom_legendary.models.game import Game
|
||||
|
||||
from Rare.Components.Dialogs.InstallDialog import InstallDialog
|
||||
from Rare.utils.Models import InstallOptions
|
||||
from Rare.utils.QtExtensions import ClickableLabel
|
||||
from Rare.utils.utils import download_image
|
||||
from custom_legendary.core import LegendaryCore
|
||||
from custom_legendary.models.game import Game
|
||||
|
||||
logger = getLogger("Uninstalled")
|
||||
|
||||
|
|
|
@ -4,10 +4,10 @@ from logging import getLogger
|
|||
from PyQt5.QtCore import QProcess, pyqtSignal, QSettings
|
||||
from PyQt5.QtGui import QPixmap
|
||||
from PyQt5.QtWidgets import QWidget, QHBoxLayout, QLabel, QPushButton, QStyle, QVBoxLayout
|
||||
from custom_legendary.core import LegendaryCore
|
||||
from custom_legendary.models.game import InstalledGame
|
||||
|
||||
from Rare.utils import LegendaryApi
|
||||
from custom_legendary.core import LegendaryCore
|
||||
from custom_legendary.models.game import InstalledGame
|
||||
|
||||
logger = getLogger("GameWidget")
|
||||
|
||||
|
|
|
@ -9,12 +9,12 @@ class About(QWidget):
|
|||
self.title = QLabel("<h2>About</h2>")
|
||||
self.layout.addWidget(self.title)
|
||||
|
||||
self.dev = QLabel(self.tr("Developer:")+"<a href='https://github.com/Dummerle'>Dummerle</a>")
|
||||
self.dev = QLabel(self.tr("Developer:") + "<a href='https://github.com/Dummerle'>Dummerle</a>")
|
||||
self.dev.setToolTip("Github")
|
||||
self.dev.setOpenExternalLinks(True)
|
||||
self.dev.setWordWrap(True)
|
||||
self.layout.addWidget(self.dev)
|
||||
self.lgd_dev = QLabel(self.tr("Legendary developer:")+"<a href='https://github.com/derrod/'>derrod</a>")
|
||||
self.lgd_dev = QLabel(self.tr("Legendary developer:") + "<a href='https://github.com/derrod/'>derrod</a>")
|
||||
self.lgd_dev.setOpenExternalLinks(True)
|
||||
self.lgd_dev.setToolTip("Github")
|
||||
self.layout.addWidget(self.lgd_dev)
|
||||
|
|
|
@ -2,6 +2,7 @@ from logging import getLogger
|
|||
|
||||
from PyQt5.QtCore import pyqtSignal
|
||||
from PyQt5.QtWidgets import QWidget, QCheckBox, QVBoxLayout, QWidgetAction, QMenu, QToolButton, QHBoxLayout, QLabel
|
||||
|
||||
from custom_legendary.core import LegendaryCore
|
||||
|
||||
logger = getLogger("DXVK Settings")
|
||||
|
|
|
@ -2,10 +2,10 @@ from logging import getLogger
|
|||
|
||||
from PyQt5.QtGui import QIntValidator
|
||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QFileDialog, QPushButton, QLineEdit
|
||||
from custom_legendary.core import LegendaryCore
|
||||
|
||||
from Rare.Components.Tabs.Settings.SettingsWidget import SettingsWidget
|
||||
from Rare.utils.QtExtensions import PathEdit
|
||||
from custom_legendary.core import LegendaryCore
|
||||
|
||||
logger = getLogger("LegendarySettings")
|
||||
|
||||
|
@ -15,7 +15,7 @@ class LegendarySettings(QWidget):
|
|||
super(LegendarySettings, self).__init__()
|
||||
self.layout = QVBoxLayout()
|
||||
self.core = core
|
||||
self.title = QLabel("<h2>"+self.tr("Legendary settings")+"</h2>")
|
||||
self.title = QLabel("<h2>" + self.tr("Legendary settings") + "</h2>")
|
||||
self.layout.addWidget(self.title)
|
||||
|
||||
# Default installation directory
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
from logging import getLogger
|
||||
|
||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QPushButton, QFileDialog, QLineEdit
|
||||
from custom_legendary.core import LegendaryCore
|
||||
|
||||
from Rare.Components.Tabs.Settings.DXVK.Dxvk import DxvkWidget
|
||||
from Rare.Components.Tabs.Settings.SettingsWidget import SettingsWidget
|
||||
from Rare.utils.QtExtensions import PathEdit
|
||||
from custom_legendary.core import LegendaryCore
|
||||
|
||||
logger = getLogger("LinuxSettings")
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ class RareSettings(QWidget):
|
|||
def __init__(self):
|
||||
super(RareSettings, self).__init__()
|
||||
self.layout = QVBoxLayout()
|
||||
self.title = QLabel("<h2>"+self.tr("Rare settings")+"</h2>")
|
||||
self.title = QLabel("<h2>" + self.tr("Rare settings") + "</h2>")
|
||||
self.layout.addWidget(self.title)
|
||||
settings = QSettings()
|
||||
img_dir = settings.value("img_dir", type=str)
|
||||
|
|
11
Rare/Main.py
11
Rare/Main.py
|
@ -1,19 +1,16 @@
|
|||
import logging
|
||||
import os
|
||||
|
||||
from PyQt5.QtCore import QSettings, QTranslator
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
from Rare.Components.MainWindow import MainWindow
|
||||
|
||||
from Rare.Components.Launch.LaunchDialog import LaunchDialog
|
||||
|
||||
from Rare import lang_path, style_path
|
||||
|
||||
from Rare.Components.Launch.LaunchDialog import LaunchDialog
|
||||
from Rare.Components.MainWindow import MainWindow
|
||||
from Rare.utils.utils import get_lang
|
||||
from custom_legendary.core import LegendaryCore
|
||||
|
||||
import os
|
||||
|
||||
logging.basicConfig(
|
||||
format='[%(name)s] %(levelname)s: %(message)s',
|
||||
level=logging.INFO
|
||||
|
@ -45,4 +42,4 @@ def main():
|
|||
launch_dialog.exec_()
|
||||
mainwindow = MainWindow(core)
|
||||
|
||||
app.exec_()
|
||||
app.exec_()
|
||||
|
|
|
@ -7,11 +7,12 @@ QLabel {
|
|||
background-color: transparent;
|
||||
}
|
||||
|
||||
QTabBar#main_tab_bar{
|
||||
QTabBar#main_tab_bar {
|
||||
border-bottom: none;
|
||||
background-color: #2b2b2c;
|
||||
}
|
||||
QTabBar::tab#main_tab_bar{
|
||||
|
||||
QTabBar::tab#main_tab_bar {
|
||||
border-bottom: noneq;
|
||||
}
|
||||
|
||||
|
@ -19,7 +20,8 @@ QTabBar::tab#main_tab_bar {
|
|||
border-bottom: none;
|
||||
padding: 5px
|
||||
}
|
||||
QTabBar::tab:selected#main_tab_bar{
|
||||
|
||||
QTabBar::tab:selected#main_tab_bar {
|
||||
background-color: gray;
|
||||
}
|
||||
|
||||
|
@ -45,7 +47,8 @@ QPushButton {
|
|||
background-color: #3c3f41;
|
||||
padding: 3px;
|
||||
}
|
||||
QPushButton:hover{
|
||||
|
||||
QPushButton:hover {
|
||||
background-color: #223;
|
||||
}
|
||||
|
||||
|
@ -62,11 +65,12 @@ QPushButton#menu {
|
|||
border-style: none;
|
||||
}
|
||||
|
||||
QPushButton#menu_button{
|
||||
QPushButton#menu_button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
QPushButton:hover#menu_button{
|
||||
|
||||
QPushButton:hover#menu_button {
|
||||
background-color: #334;
|
||||
}
|
||||
|
||||
|
@ -74,12 +78,11 @@ QLineEdit {
|
|||
border: 1px solid white;
|
||||
}
|
||||
|
||||
QCheckBox{
|
||||
QCheckBox {
|
||||
color: #F0F0F0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#list_widget {
|
||||
border-top: 2px solid white;
|
||||
}
|
||||
|
@ -100,7 +103,7 @@ QTabBar::tab::selected#settings_bar {
|
|||
background-color: darkslategrey;
|
||||
}
|
||||
|
||||
#search_bar{
|
||||
#search_bar {
|
||||
padding: 3px;
|
||||
border-radius: 5px;
|
||||
background-color: #334;
|
||||
|
|
|
@ -2,4 +2,4 @@ import os
|
|||
|
||||
__version__ = "0.9.4"
|
||||
style_path = os.path.join(os.path.dirname(__file__), "Styles/")
|
||||
lang_path = os.path.join(os.path.dirname(__file__), "languages/")
|
||||
lang_path = os.path.join(os.path.dirname(__file__), "languages/")
|
||||
|
|
|
@ -8,7 +8,6 @@ if __name__ == '__main__':
|
|||
exit(0)
|
||||
|
||||
from Rare.Main import main
|
||||
|
||||
main()
|
||||
|
||||
"""
|
||||
|
|
2031
Rare/languages/de.ts
2031
Rare/languages/de.ts
File diff suppressed because it is too large
Load diff
|
@ -4,7 +4,7 @@ from logging import getLogger
|
|||
from sys import stdout
|
||||
|
||||
from PyQt5.QtCore import QProcess, QProcessEnvironment, QThread, pyqtSignal
|
||||
from PyQt5.QtWidgets import QMessageBox, QWidget
|
||||
|
||||
from custom_legendary.core import LegendaryCore
|
||||
from custom_legendary.models.game import VerifyResult
|
||||
from custom_legendary.utils.lfs import validate_files
|
||||
|
@ -125,7 +125,7 @@ class VerifyThread(QThread):
|
|||
|
||||
if not missing and not failed:
|
||||
logger.info('Verification finished successfully.')
|
||||
self.summary.emit((0,0))
|
||||
self.summary.emit((0, 0))
|
||||
|
||||
else:
|
||||
logger.error(f'Verification failed, {len(failed)} file(s) corrupted, {len(missing)} file(s) are missing.')
|
||||
|
|
|
@ -6,14 +6,15 @@ from logging import getLogger
|
|||
import requests
|
||||
from PIL import Image
|
||||
from PyQt5.QtCore import pyqtSignal, QLocale, QSettings
|
||||
from custom_legendary.core import LegendaryCore
|
||||
|
||||
from Rare import lang_path
|
||||
from custom_legendary.core import LegendaryCore
|
||||
|
||||
logger = getLogger("Utils")
|
||||
s = QSettings("Rare","Rare")
|
||||
s = QSettings("Rare", "Rare")
|
||||
IMAGE_DIR = s.value("img_dir", os.path.expanduser("~/.cache/rare"), type=str)
|
||||
logger.info("IMAGE DIRECTORY: "+IMAGE_DIR)
|
||||
logger.info("IMAGE DIRECTORY: " + IMAGE_DIR)
|
||||
|
||||
|
||||
def download_images(signal: pyqtSignal, core: LegendaryCore):
|
||||
if not os.path.isdir(IMAGE_DIR):
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
# !/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
import requests
|
||||
import logging
|
||||
|
||||
import requests
|
||||
from requests.auth import HTTPBasicAuth
|
||||
|
||||
from custom_legendary.models.exceptions import InvalidCredentialsError
|
||||
|
@ -157,7 +157,7 @@ class EPCAPI:
|
|||
f'{user_id}/{app_name}')
|
||||
r.raise_for_status()
|
||||
return r.json()
|
||||
|
||||
|
||||
def create_game_cloud_saves(self, app_name, filenames):
|
||||
return self.get_user_cloud_saves(app_name, filenames=filenames)
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ import shlex
|
|||
import subprocess
|
||||
import time
|
||||
import webbrowser
|
||||
|
||||
from distutils.util import strtobool
|
||||
from getpass import getuser
|
||||
from logging.handlers import QueueListener
|
||||
|
@ -22,7 +21,7 @@ from sys import exit, stdout
|
|||
from custom_legendary import __version__, __codename__
|
||||
from custom_legendary.core import LegendaryCore
|
||||
from custom_legendary.models.exceptions import InvalidCredentialsError
|
||||
from custom_legendary.models.game import SaveGameStatus, VerifyResult
|
||||
from custom_legendary.models.game import SaveGameStatus
|
||||
from custom_legendary.utils.cli import get_boolean_choice, sdl_prompt
|
||||
from custom_legendary.utils.custom_parser import AliasedSubParsersAction
|
||||
|
||||
|
@ -220,7 +219,7 @@ class LegendaryCLI:
|
|||
self.core.install_game(game)
|
||||
|
||||
print(f' * {game.title} (App name: {game.app_name} | Version: {game.version} | '
|
||||
f'{game.install_size / (1024*1024*1024):.02f} GiB)')
|
||||
f'{game.install_size / (1024 * 1024 * 1024):.02f} GiB)')
|
||||
if args.include_dir:
|
||||
print(f' + Location: {game.install_path}')
|
||||
if not os.path.exists(game.install_path):
|
||||
|
@ -570,7 +569,7 @@ class LegendaryCLI:
|
|||
exit(0)
|
||||
|
||||
logger.info('Downloads are resumable, you can interrupt the download with '
|
||||
'CTRL-C and resume it using the same command later on.')
|
||||
'CTRL-C and resume it using the same command later on.')
|
||||
|
||||
start_t = time.time()
|
||||
|
||||
|
@ -584,7 +583,8 @@ class LegendaryCLI:
|
|||
while dlm.is_alive():
|
||||
try:
|
||||
status = status_queue.get(timeout=0.1)
|
||||
logger.info(f'= Progress: {status.progress:.02f}% ({status.processed_chunks}/{status.chunk_tasks}), '
|
||||
logger.info(
|
||||
f'= Progress: {status.progress:.02f}% ({status.processed_chunks}/{status.chunk_tasks}), '
|
||||
f'Running for {str(datetime.timedelta(seconds=status.runtime))}, '
|
||||
f'ETA: {str(datetime.timedelta(seconds=status.estimated_time_left))}')
|
||||
logger.info(f' - Downloaded: {status.total_downloaded / 1024 / 1024:.02f} MiB, '
|
||||
|
@ -937,7 +937,7 @@ class LegendaryCLI:
|
|||
self.core.lgd.clean_tmp_data()
|
||||
|
||||
after = self.core.lgd.get_dir_size()
|
||||
logger.info(f'Cleanup complete! Removed {(before - after)/1024/1024:.02f} MiB.')
|
||||
logger.info(f'Cleanup complete! Removed {(before - after) / 1024 / 1024:.02f} MiB.')
|
||||
|
||||
|
||||
def main():
|
||||
|
|
|
@ -5,35 +5,35 @@ import logging
|
|||
import os
|
||||
import shlex
|
||||
import shutil
|
||||
|
||||
from base64 import b64decode
|
||||
from collections import defaultdict
|
||||
from datetime import datetime, timezone
|
||||
from locale import getdefaultlocale
|
||||
from multiprocessing import Queue
|
||||
from random import choice as randchoice
|
||||
from requests import session
|
||||
from requests.exceptions import HTTPError
|
||||
from typing import List, Dict, Callable
|
||||
from uuid import uuid4
|
||||
|
||||
from requests import session
|
||||
from requests.exceptions import HTTPError
|
||||
|
||||
from custom_legendary.api.egs import EPCAPI
|
||||
from custom_legendary.downloader.manager import DLManager
|
||||
from custom_legendary.lfs.egl import EPCLFS
|
||||
from custom_legendary.lfs.lgndry import LGDLFS
|
||||
from custom_legendary.utils.lfs import clean_filename, delete_folder, delete_filelist, validate_files
|
||||
from custom_legendary.models.chunk import Chunk
|
||||
from custom_legendary.models.downloading import AnalysisResult, ConditionCheckResult
|
||||
from custom_legendary.models.egl import EGLManifest
|
||||
from custom_legendary.models.exceptions import InvalidCredentialsError
|
||||
from custom_legendary.models.game import GameAsset, Game, InstalledGame, SaveGameFile, SaveGameStatus, VerifyResult
|
||||
from custom_legendary.models.json_manifest import JSONManifest
|
||||
from custom_legendary.models.manifest import Manifest, ManifestMeta
|
||||
from custom_legendary.models.chunk import Chunk
|
||||
from custom_legendary.models.manifest import Manifest
|
||||
from custom_legendary.utils.game_workarounds import is_opt_enabled
|
||||
from custom_legendary.utils.savegame_helper import SaveGameHelper
|
||||
from custom_legendary.utils.lfs import clean_filename, delete_folder, delete_filelist, validate_files
|
||||
from custom_legendary.utils.manifests import combine_manifests
|
||||
from custom_legendary.utils.wine_helpers import read_registry, get_shell_folders
|
||||
from custom_legendary.utils.savegame_helper import SaveGameHelper
|
||||
from custom_legendary.utils.selective_dl import get_sdl_appname
|
||||
from custom_legendary.utils.wine_helpers import read_registry, get_shell_folders
|
||||
|
||||
|
||||
# ToDo: instead of true/false return values for success/failure actually raise an exception that the CLI/GUI
|
||||
|
@ -349,11 +349,11 @@ class LegendaryCore:
|
|||
params.extend(shlex.split(install.launch_parameters, posix=False))
|
||||
|
||||
params.extend([
|
||||
'-AUTH_LOGIN=unused',
|
||||
f'-AUTH_PASSWORD={game_token}',
|
||||
'-AUTH_TYPE=exchangecode',
|
||||
f'-epicapp={app_name}',
|
||||
'-epicenv=Prod'])
|
||||
'-AUTH_LOGIN=unused',
|
||||
f'-AUTH_PASSWORD={game_token}',
|
||||
'-AUTH_TYPE=exchangecode',
|
||||
f'-epicapp={app_name}',
|
||||
'-epicenv=Prod'])
|
||||
|
||||
if install.requires_ot and not offline:
|
||||
self.log.info('Getting ownership token.')
|
||||
|
@ -370,10 +370,10 @@ class LegendaryCore:
|
|||
language_code = self.language_code
|
||||
|
||||
params.extend([
|
||||
'-EpicPortal',
|
||||
f'-epicusername={user_name}',
|
||||
f'-epicuserid={account_id}',
|
||||
f'-epiclocale={language_code}'
|
||||
'-EpicPortal',
|
||||
f'-epicusername={user_name}',
|
||||
f'-epicuserid={account_id}',
|
||||
f'-epiclocale={language_code}'
|
||||
])
|
||||
|
||||
if extra_args:
|
||||
|
@ -431,7 +431,7 @@ class LegendaryCore:
|
|||
'{appdata}': os.path.expandvars('%APPDATA%'),
|
||||
'{userdir}': os.path.expandvars('%userprofile%/documents'),
|
||||
# '{userprofile}': os.path.expandvars('%userprofile%'), # possibly wrong
|
||||
'{usersavedgames}': os.path.expandvars('%userprofile%/Saved Games')
|
||||
'{usersavedgames}': os.path.expandvars('%userprofile%/Saved Games')
|
||||
})
|
||||
else:
|
||||
# attempt to get WINE prefix from config
|
||||
|
@ -589,7 +589,7 @@ class LegendaryCore:
|
|||
self.log.debug(f'Writing "{fpath}"...')
|
||||
with open(fpath, 'wb') as fh:
|
||||
for cp in fm.chunk_parts:
|
||||
fh.write(chunks[cp.guid_num][cp.offset:cp.offset+cp.size])
|
||||
fh.write(chunks[cp.guid_num][cp.offset:cp.offset + cp.size])
|
||||
|
||||
# set modified time to savegame creation timestamp
|
||||
m_date = datetime.strptime(f_parts[4], '%Y.%m.%d-%H.%M.%S.manifest')
|
||||
|
@ -696,7 +696,7 @@ class LegendaryCore:
|
|||
else:
|
||||
return None
|
||||
|
||||
def verify_game(self, app_name: str, callback: Callable[[int, int], None]=print):
|
||||
def verify_game(self, app_name: str, callback: Callable[[int, int], None] = print):
|
||||
if not self.is_installed(app_name):
|
||||
self.log.error(f'Game "{app_name}" is not installed')
|
||||
return
|
||||
|
@ -747,7 +747,8 @@ class LegendaryCore:
|
|||
if not missing and not failed:
|
||||
self.log.info('Verification finished successfully.')
|
||||
else:
|
||||
raise RuntimeError(f'Verification failed, {len(failed)} file(s) corrupted, {len(missing)} file(s) are missing.')
|
||||
raise RuntimeError(
|
||||
f'Verification failed, {len(failed)} file(s) corrupted, {len(missing)} file(s) are missing.')
|
||||
|
||||
def prepare_download(self, app_name: str, base_path: str = '', no_install: bool = False,
|
||||
status_q: Queue = None, max_shm: int = 0, max_workers: int = 0,
|
||||
|
@ -761,7 +762,8 @@ class LegendaryCore:
|
|||
ignore_space_req: bool = False,
|
||||
disable_delta: bool = False, override_delta_manifest: str = '',
|
||||
egl_guid: str = '', reset_sdl: bool = False,
|
||||
sdl_prompt: Callable[[str, str], List[str]] = list) -> (DLManager, AnalysisResult, Game, InstalledGame, bool, str):
|
||||
sdl_prompt: Callable[[str, str], List[str]] = list) -> (
|
||||
DLManager, AnalysisResult, Game, InstalledGame, bool, str):
|
||||
if self.is_installed(app_name):
|
||||
igame = self.get_installed_game(app_name)
|
||||
if igame.needs_verification and not repair:
|
||||
|
@ -784,7 +786,7 @@ class LegendaryCore:
|
|||
|
||||
if not game:
|
||||
raise RuntimeError(f'Could not find "{app_name}" in list of available games,'
|
||||
f'did you type the name correctly?')
|
||||
f'did you type the name correctly?')
|
||||
|
||||
if game.is_dlc:
|
||||
self.log.info('Install candidate is DLC')
|
||||
|
@ -889,10 +891,10 @@ class LegendaryCore:
|
|||
else:
|
||||
if not game_folder:
|
||||
if game.is_dlc:
|
||||
game_folder = base_game.metadata.get('customAttributes', {}).\
|
||||
game_folder = base_game.metadata.get('customAttributes', {}). \
|
||||
get('FolderName', {}).get('value', base_game.app_name)
|
||||
else:
|
||||
game_folder = game.metadata.get('customAttributes', {}).\
|
||||
game_folder = game.metadata.get('customAttributes', {}). \
|
||||
get('FolderName', {}).get('value', game.app_name)
|
||||
|
||||
if not base_path:
|
||||
|
@ -992,8 +994,8 @@ class LegendaryCore:
|
|||
raise RuntimeError('Nothing to do.')
|
||||
|
||||
res = self.check_installation_conditions(analysis=anlres, install=igame, game=game,
|
||||
updating=self.is_installed(app_name),
|
||||
ignore_space_req=ignore_space_req)
|
||||
updating=self.is_installed(app_name),
|
||||
ignore_space_req=ignore_space_req)
|
||||
|
||||
if res.warnings or res.failures:
|
||||
self.log.info('Installation requirements check returned the following results:')
|
||||
|
@ -1179,8 +1181,8 @@ class LegendaryCore:
|
|||
# If there's no in-progress installation assume the game doesn't need to be verified
|
||||
if mf and not os.path.exists(os.path.join(app_path, '.egstore', 'bps')):
|
||||
needs_verify = False
|
||||
if os.path.exists(os.path.join(app_path, '.egstore', 'Pending')):
|
||||
if os.listdir(os.path.join(app_path, '.egstore', 'Pending')):
|
||||
if os.path.exists(os.path.join(app_path, '.egstore', 'Pending')):
|
||||
if os.listdir(os.path.join(app_path, '.egstore', 'Pending')):
|
||||
needs_verify = True
|
||||
|
||||
if not needs_verify:
|
||||
|
@ -1304,13 +1306,13 @@ class LegendaryCore:
|
|||
os.makedirs(egstore_folder)
|
||||
|
||||
# copy manifest and create mancpn file in .egstore folder
|
||||
with open(os.path.join(egstore_folder, f'{egl_game.installation_guid}.manifest',), 'wb') as mf:
|
||||
with open(os.path.join(egstore_folder, f'{egl_game.installation_guid}.manifest', ), 'wb') as mf:
|
||||
mf.write(manifest_data)
|
||||
|
||||
mancpn = dict(FormatVersion=0, AppName=app_name,
|
||||
CatalogItemId=lgd_game.asset_info.catalog_item_id,
|
||||
CatalogNamespace=lgd_game.asset_info.namespace)
|
||||
with open(os.path.join(egstore_folder, f'{egl_game.installation_guid}.mancpn',), 'w') as mcpnf:
|
||||
with open(os.path.join(egstore_folder, f'{egl_game.installation_guid}.mancpn', ), 'w') as mcpnf:
|
||||
json.dump(mancpn, mcpnf, indent=4, sort_keys=True)
|
||||
|
||||
# And finally, write the file for EGL
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
import logging
|
||||
import os
|
||||
import time
|
||||
|
||||
from collections import Counter, defaultdict, deque
|
||||
from logging.handlers import QueueHandler
|
||||
from multiprocessing import cpu_count, Process, Queue as MPQueue
|
||||
|
@ -172,7 +171,7 @@ class DLManager(Process):
|
|||
|
||||
file_prefix_filter = [f.lower() for f in file_prefix_filter]
|
||||
files_to_skip = set(i.filename for i in manifest.file_manifest_list.elements if not
|
||||
any(i.filename.lower().startswith(pfx) for pfx in file_prefix_filter))
|
||||
any(i.filename.lower().startswith(pfx) for pfx in file_prefix_filter))
|
||||
self.log.info(f'Found {len(files_to_skip)} files to skip based on include prefix(es)')
|
||||
mc.added -= files_to_skip
|
||||
mc.changed -= files_to_skip
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
# coding: utf-8
|
||||
|
||||
import os
|
||||
import requests
|
||||
import time
|
||||
import logging
|
||||
|
||||
import os
|
||||
import time
|
||||
from logging.handlers import QueueHandler
|
||||
from multiprocessing import Process
|
||||
from multiprocessing.shared_memory import SharedMemory
|
||||
from queue import Empty
|
||||
|
||||
import requests
|
||||
|
||||
from custom_legendary.models.chunk import Chunk
|
||||
from custom_legendary.models.downloading import DownloaderTaskResult, WriterTaskResult
|
||||
|
||||
|
@ -252,13 +252,13 @@ class FileWorker(Process):
|
|||
chunk_guid=j.chunk_guid,
|
||||
release_memory=j.release_memory,
|
||||
shm=j.shm, size=j.chunk_size,
|
||||
time_delta=post_write-pre_write))
|
||||
time_delta=post_write - pre_write))
|
||||
else:
|
||||
self.o_q.put(WriterTaskResult(success=True, filename=j.filename,
|
||||
chunk_guid=j.chunk_guid,
|
||||
release_memory=j.release_memory,
|
||||
shm=j.shm, size=j.chunk_size,
|
||||
time_delta=post_write-pre_write))
|
||||
time_delta=post_write - pre_write))
|
||||
except Exception as e:
|
||||
logger.warning(f'Job {j.filename} failed with: {e!r}, fetching next one...')
|
||||
self.o_q.put(WriterTaskResult(success=False, filename=j.filename, chunk_guid=j.chunk_guid))
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
import configparser
|
||||
import json
|
||||
import os
|
||||
|
||||
from typing import List
|
||||
|
||||
from custom_legendary.models.egl import EGLManifest
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
# coding: utf-8
|
||||
|
||||
import json
|
||||
import os
|
||||
import configparser
|
||||
import json
|
||||
import logging
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from custom_legendary.models.game import *
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
import struct
|
||||
import zlib
|
||||
|
||||
from hashlib import sha1
|
||||
from io import BytesIO
|
||||
from uuid import uuid4
|
||||
|
@ -50,7 +49,7 @@ class Chunk:
|
|||
|
||||
@data.setter
|
||||
def data(self, value: bytes):
|
||||
if len(value) > 1024*1024:
|
||||
if len(value) > 1024 * 1024:
|
||||
raise ValueError('Provided data is too large (> 1 MiB)!')
|
||||
# data is now uncompressed
|
||||
if self.compressed:
|
||||
|
|
|
@ -169,6 +169,7 @@ class AnalysisResult:
|
|||
|
||||
class ConditionCheckResult:
|
||||
"""Result object used in Core to identify problems that would prevent an installation from succeeding"""
|
||||
|
||||
def __init__(self, failures=None, warnings=None):
|
||||
self.failures = failures
|
||||
self.warnings = warnings
|
||||
|
|
|
@ -3,7 +3,6 @@ from distutils.util import strtobool
|
|||
|
||||
from custom_legendary.models.game import InstalledGame, Game
|
||||
|
||||
|
||||
_template = {
|
||||
'AppCategories': ['public', 'games', 'applications'],
|
||||
'AppName': '',
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
import json
|
||||
import struct
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
from custom_legendary.models.manifest import (
|
||||
|
@ -37,6 +36,7 @@ class JSONManifest(Manifest):
|
|||
Manifest-compatible reader for JSON based manifests
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.json_data = None
|
||||
|
@ -127,7 +127,7 @@ class JSONCDL(CDL):
|
|||
_ci.hash = blob_to_num(chl.pop(guid))
|
||||
_ci.sha_hash = bytes.fromhex(csl.pop(guid))
|
||||
_ci.group_num = blob_to_num(dgl.pop(guid))
|
||||
_ci.window_size = 1024*1024
|
||||
_ci.window_size = 1024 * 1024
|
||||
_cdl.elements.append(_ci)
|
||||
|
||||
for _dc in (cfl, chl, csl, dgl):
|
||||
|
@ -149,7 +149,7 @@ class JSONFML(FML):
|
|||
for _fmj in json_data.pop('FileManifestList'):
|
||||
_fm = FileManifest()
|
||||
_fm.filename = _fmj.pop('Filename', '')
|
||||
_fm.hash = blob_to_num(_fmj.pop('FileHash')).to_bytes(160//8, 'little')
|
||||
_fm.hash = blob_to_num(_fmj.pop('FileHash')).to_bytes(160 // 8, 'little')
|
||||
_fm.flags |= int(_fmj.pop('bIsReadOnly', False))
|
||||
_fm.flags |= int(_fmj.pop('bIsCompressed', False)) << 1
|
||||
_fm.flags |= int(_fmj.pop('bIsUnixExecutable', False)) << 2
|
||||
|
|
|
@ -602,9 +602,9 @@ class FileManifest:
|
|||
|
||||
return '<FileManifest (filename="{}", symlink_target="{}", hash={}, flags={}, ' \
|
||||
'install_tags=[{}], chunk_parts=[{}], file_size={})>'.format(
|
||||
self.filename, self.symlink_target, self.hash.hex(), self.flags,
|
||||
', '.join(self.install_tags), cp_repr, self.file_size
|
||||
)
|
||||
self.filename, self.symlink_target, self.hash.hex(), self.flags,
|
||||
', '.join(self.install_tags), cp_repr, self.file_size
|
||||
)
|
||||
|
||||
|
||||
class ChunkPart:
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import argparse
|
||||
|
||||
|
||||
# reference: https://gist.github.com/sampsyo/471779#gistcomment-2886157
|
||||
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
# coding: utf-8
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import hashlib
|
||||
import logging
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from typing import List, Iterator
|
||||
|
||||
from custom_legendary.models.game import VerifyResult
|
||||
|
@ -37,7 +36,7 @@ def delete_filelist(path: str, filenames: List[str],
|
|||
_dir, _fn = os.path.split(filename)
|
||||
if _dir:
|
||||
dirs.add(_dir)
|
||||
|
||||
|
||||
try:
|
||||
os.remove(os.path.join(path, _dir, _fn))
|
||||
except Exception as e:
|
||||
|
@ -63,14 +62,14 @@ def delete_filelist(path: str, filenames: List[str],
|
|||
if not silent:
|
||||
logger.error(f'Failed removing directory "{_dir}" with {e!r}')
|
||||
no_error = False
|
||||
|
||||
|
||||
if delete_root_directory:
|
||||
try:
|
||||
os.rmdir(path)
|
||||
except Exception as e:
|
||||
if not silent:
|
||||
logger.error(f'Removing game directory failed with {e!r}')
|
||||
|
||||
|
||||
return no_error
|
||||
|
||||
|
||||
|
@ -101,7 +100,7 @@ def validate_files(base_path: str, filelist: List[tuple], hash_type='sha1') -> I
|
|||
try:
|
||||
with open(full_path, 'rb') as f:
|
||||
real_file_hash = hashlib.new(hash_type)
|
||||
while chunk := f.read(1024*1024):
|
||||
while chunk := f.read(1024 * 1024):
|
||||
real_file_hash.update(chunk)
|
||||
|
||||
result_hash = real_file_hash.hexdigest()
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import logging
|
||||
import os
|
||||
|
||||
from datetime import datetime
|
||||
from fnmatch import fnmatch
|
||||
from hashlib import sha1
|
||||
|
@ -130,7 +129,7 @@ class SaveGameHelper:
|
|||
self.log.warning(f'Got EOF for "{f.filename}" with {remaining} bytes remaining! '
|
||||
f'File may have been corrupted/modified.')
|
||||
break
|
||||
|
||||
|
||||
cur_buffer.write(_tmp)
|
||||
fhash.update(_tmp) # update sha1 hash with new data
|
||||
f.chunk_parts.append(cp)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import os
|
||||
import configparser
|
||||
import os
|
||||
|
||||
|
||||
def read_registry(wine_pfx):
|
||||
|
|
Loading…
Reference in a new issue