From b30982f9e8a081b8ea60bfaa1044a8c1db2fd550 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 2 Nov 2025 03:34:49 +0300 Subject: [PATCH] finally, dxvk fix --- .gitignore | 3 +- assets/dxvk.reg | 8 ++ prepare.sh | 6 ++ run.sh | 5 +- src/config.py | 2 + src/installationthread.py | 187 +++++++++++++++++++++++++------------- src/mainwindow.py | 14 +++ translations/en_US.py | 5 +- translations/ru_RU.py | 5 +- 9 files changed, 164 insertions(+), 71 deletions(-) create mode 100644 assets/dxvk.reg diff --git a/.gitignore b/.gitignore index 239744d..1f54103 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ bin/* assets/wine assets/vcr.zip assets/msxml3.zip -assets/gdiplus.dll \ No newline at end of file +assets/gdiplus.dll +assets/dxvk.tar.gz \ No newline at end of file diff --git a/assets/dxvk.reg b/assets/dxvk.reg new file mode 100644 index 0000000..1eedc9b --- /dev/null +++ b/assets/dxvk.reg @@ -0,0 +1,8 @@ +Windows Registry Editor Version 5.00 + +[HKEY_CURRENT_USER\Software\Wine\DllOverrides] +"d3d10core"="native,builtin" +"d3d11"="native,builtin" +"d3d8"="native,builtin" +"d3d9"="native,builtin" +"dxgi"="native,builtin" \ No newline at end of file diff --git a/prepare.sh b/prepare.sh index d71f2d1..52978ba 100755 --- a/prepare.sh +++ b/prepare.sh @@ -52,5 +52,11 @@ echo Downloading gdiplus.dll... curl -LO https://github.com/relativemodder/aegnux/releases/download/vcrbin/gdiplus.dll mv gdiplus.dll ./assets/ + +# Download dxvk +echo Downloading dxvk... +curl -LO https://github.com/doitsujin/dxvk/releases/download/v2.7.1/dxvk-2.7.1.tar.gz +mv dxvk-2.7.1.tar.gz ./assets/dxvk.tar.gz + echo -------------------------------------------- echo Done! \ No newline at end of file diff --git a/run.sh b/run.sh index db6b130..dc3ea8a 100755 --- a/run.sh +++ b/run.sh @@ -1,8 +1,5 @@ #!/bin/sh -SCRIPT_PATH=$(readlink -f "$0") -SCRIPT_DIR=$(dirname "SCRIPT_PATH") - -cd "$SCRIPT_DIR" +cd "$(dirname "$0")" python main.py \ No newline at end of file diff --git a/src/config.py b/src/config.py index 3670da1..3f01e5a 100644 --- a/src/config.py +++ b/src/config.py @@ -13,6 +13,8 @@ WINETRICKS_BIN = BASE_DIR + '/bin/winetricks' CABEXTRACT_BIN = BASE_DIR + '/bin/cabextract' GDIPLUS_DLL = BASE_DIR + '/assets/gdiplus.dll' FONTSMOOTH_REG = BASE_DIR + '/assets/fontsmooth.reg' +DXVK_TAR = BASE_DIR + '/assets/dxvk.tar.gz' +DXVK_REG = BASE_DIR + '/assets/dxvk.reg' VCR_ZIP = BASE_DIR + '/assets/vcr.zip' MSXML_ZIP = BASE_DIR + '/assets/msxml3.zip' diff --git a/src/installationthread.py b/src/installationthread.py index 6c6bd6e..d4af3f1 100644 --- a/src/installationthread.py +++ b/src/installationthread.py @@ -1,11 +1,13 @@ import os from pathlib import Path import shutil +import tarfile +import traceback from src.config import ( - AE_DOWNLOAD_URL, AE_FILENAME, FONTSMOOTH_REG, + AE_DOWNLOAD_URL, AE_FILENAME, DXVK_REG, FONTSMOOTH_REG, WINE_RUNNER_DIR, WINETRICKS_BIN, CABEXTRACT_BIN, WINE_STYLE_REG, - VCR_ZIP, MSXML_ZIP, GDIPLUS_DLL + VCR_ZIP, MSXML_ZIP, GDIPLUS_DLL, DXVK_TAR ) from src.processthread import ProcessThread from src.utils import ( @@ -46,35 +48,7 @@ class InstallationThread(ProcessThread): self.progress_signal.emit(15) - self.log_signal.emit(f'[DEBUG] Unpacking AE from {self.ae_filename}...') - aegnux_install_dir = get_aegnux_installation_dir() - self.unpack_zip(self.ae_filename, aegnux_install_dir.as_posix()) - - root_path = Path(aegnux_install_dir) - target_dir = Path(get_ae_install_dir()) - source_folder_to_delete = None - - self.log_signal.emit('[DEBUG] Searching for AfterFX.exe...') - - for exe_path in root_path.rglob('AfterFX.exe'): - source_dir_to_move = exe_path.parent - self.log_signal.emit(f'[DEBUG] Found installation folder: {source_dir_to_move}') - for item in source_dir_to_move.iterdir(): - shutil.move(item.as_posix(), target_dir.joinpath(item.name).as_posix()) - self.log_signal.emit(f'[DEBUG] All contents moved to {target_dir}') - source_folder_to_delete = source_dir_to_move.parent - break - - if source_folder_to_delete and source_folder_to_delete != root_path: - self.log_signal.emit(f'[DEBUG] Removing temporary folder: {source_folder_to_delete}') - try: - shutil.rmtree(source_folder_to_delete.as_posix()) - self.log_signal.emit('[DEBUG] Temporary folder removed successfully.') - except OSError as e: - self.log_signal.emit(f'[ERROR] Failed to remove temporary folder {source_folder_to_delete}: {e}') - else: - self.log_signal.emit('[WARNING] Installation folder not found or matched root path. No folder was deleted.') - + self.unpack_ae() self.progress_signal.emit(20) @@ -108,50 +82,23 @@ class InstallationThread(ProcessThread): self.progress_signal.emit(60) - tweaks = ['dxvk', 'corefonts'] + tweaks = ['corefonts'] for tweak in tweaks: self.log_signal.emit(f'[DEBUG] Installing {tweak} with winetricks') self.run_command(['winetricks', '-q', tweak], in_prefix=True) self.progress_signal.emit(60 + tweaks.index(tweak) * 2) + self.install_dxvk() + self.progress_signal.emit(70) - self.log_signal.emit(f'[DEBUG] Unpacking VCR to {get_vcr_dir_path()}...') - self.unpack_zip(VCR_ZIP, get_vcr_dir_path().as_posix()) - - self.progress_signal.emit(80) - - self.log_signal.emit(f'[DEBUG] Installing VCR') - self.run_command(['wine', get_vcr_dir_path().joinpath('install_all.bat').as_posix()], in_prefix=True) + self.install_vcr() self.progress_signal.emit(85) - self.log_signal.emit(f'[DEBUG] Unpacking MSXML3 to {get_msxml_dir_path()}...') - self.unpack_zip(MSXML_ZIP, get_msxml_dir_path().as_posix()) - - self.progress_signal.emit(90) + self.install_msxml3() - self.log_signal.emit(f'[DEBUG] Overriding MSXML3 DLL...') - system32_dir = get_wineprefix_dir().joinpath('drive_c/windows/system32') - shutil.copy(get_msxml_dir_path().joinpath('msxml3.dll'), system32_dir.joinpath('msxml3.dll')) - shutil.copy(get_msxml_dir_path().joinpath('msxml3r.dll'), system32_dir.joinpath('msxml3r.dll')) - - self.run_command( - ['wine', 'reg', 'add', - 'HKCU\\Software\\Wine\\DllOverrides', '/v', - 'msxml3', '/d', 'native,builtin', '/f'], - in_prefix=True - ) - - self.log_signal.emit(f'[DEBUG] Overriding gdiplus DLL...') - shutil.copy(GDIPLUS_DLL, system32_dir.joinpath('gdiplus.dll')) - - self.run_command( - ['wine', 'reg', 'add', - 'HKCU\\Software\\Wine\\DllOverrides', '/v', - 'gdiplus', '/d', 'native,builtin', '/f'], - in_prefix=True - ) + self.install_gdiplus() self.log_signal.emit(f'[DEBUG] Applying fontsmooth settings') self.run_command( @@ -178,8 +125,120 @@ class InstallationThread(ProcessThread): self.finished_signal.emit(True) except Exception as e: + traceback.print_exc() self.log_signal.emit(f'[ERROR] {e}') self.finished_signal.emit(False) + + def install_vcr(self): + self.log_signal.emit(f'[DEBUG] Unpacking VCR to {get_vcr_dir_path()}...') + self.unpack_zip(VCR_ZIP, get_vcr_dir_path().as_posix()) + + self.progress_signal.emit(80) + + self.log_signal.emit(f'[DEBUG] Installing VCR') + self.run_command(['wine', get_vcr_dir_path().joinpath('install_all.bat').as_posix()], in_prefix=True) + + def install_msxml3(self): + self.log_signal.emit(f'[DEBUG] Unpacking MSXML3 to {get_msxml_dir_path()}...') + self.unpack_zip(MSXML_ZIP, get_msxml_dir_path().as_posix()) + + self.progress_signal.emit(90) + + self.log_signal.emit(f'[DEBUG] Overriding MSXML3 DLL...') + system32_dir = get_wineprefix_dir().joinpath('drive_c/windows/system32') + shutil.copy(get_msxml_dir_path().joinpath('msxml3.dll'), system32_dir.joinpath('msxml3.dll')) + shutil.copy(get_msxml_dir_path().joinpath('msxml3r.dll'), system32_dir.joinpath('msxml3r.dll')) + + self.run_command( + ['wine', 'reg', 'add', + 'HKCU\\Software\\Wine\\DllOverrides', '/v', + 'msxml3', '/d', 'native,builtin', '/f'], + in_prefix=True + ) + + def install_gdiplus(self): + system32_dir = get_wineprefix_dir().joinpath('drive_c/windows/system32') + self.log_signal.emit(f'[DEBUG] Overriding gdiplus DLL...') + shutil.copy(GDIPLUS_DLL, system32_dir.joinpath('gdiplus.dll')) + + self.run_command( + ['wine', 'reg', 'add', + 'HKCU\\Software\\Wine\\DllOverrides', '/v', + 'gdiplus', '/d', 'native,builtin', '/f'], + in_prefix=True + ) + + def get_tar_root_dir_name(self, tar_path) -> str | None: + with tarfile.open(tar_path, 'r') as tar: + member_names = tar.getnames() + if not member_names: + return None + + first_member = member_names[0] + root_name = first_member.split(os.path.sep)[0] + + return root_name + + def install_dxvk(self): + system32_dir = get_wineprefix_dir().joinpath('drive_c/windows/system32') + syswow64_dir = get_wineprefix_dir().joinpath('drive_c/windows/syswow64') + self.unpack_tar(DXVK_TAR, get_wineprefix_dir()) + + dxvk_name = self.get_tar_root_dir_name(DXVK_TAR) + dxvk_root_dir = get_wineprefix_dir().joinpath(dxvk_name) + + self.log_signal.emit(f'[DEBUG] DXVK root dir is {dxvk_root_dir}') + + source_x64 = dxvk_root_dir.joinpath('x64') + source_x32 = dxvk_root_dir.joinpath('x32') + + for source in [source_x64, source_x32]: + for item in source.iterdir(): + system_dir = system32_dir if 'x64' in source.as_posix() else syswow64_dir + + if item.is_file(): + shutil.copy2(item, system_dir.joinpath(item.name)) + elif item.is_dir(): + dest = system_dir.joinpath(item.name) + if dest.exists(): + shutil.rmtree(dest) + shutil.copytree(item, dest) + + self.log_signal.emit(f'[DEBUG] Overriding DXVK dlls') + self.run_command( + ['wine', 'regedit', DXVK_REG], + in_prefix=True + ) + + def unpack_ae(self): + self.log_signal.emit(f'[DEBUG] Unpacking AE from {self.ae_filename}...') + aegnux_install_dir = get_aegnux_installation_dir() + self.unpack_zip(self.ae_filename, aegnux_install_dir.as_posix()) + + root_path = Path(aegnux_install_dir) + target_dir = Path(get_ae_install_dir()) + source_folder_to_delete = None + + self.log_signal.emit('[DEBUG] Searching for AfterFX.exe...') + + for exe_path in root_path.rglob('AfterFX.exe'): + source_dir_to_move = exe_path.parent + self.log_signal.emit(f'[DEBUG] Found installation folder: {source_dir_to_move}') + for item in source_dir_to_move.iterdir(): + shutil.move(item.as_posix(), target_dir.joinpath(item.name).as_posix()) + self.log_signal.emit(f'[DEBUG] All contents moved to {target_dir}') + source_folder_to_delete = source_dir_to_move.parent + break + + if source_folder_to_delete and source_folder_to_delete != root_path: + self.log_signal.emit(f'[DEBUG] Removing temporary folder: {source_folder_to_delete}') + try: + shutil.rmtree(source_folder_to_delete.as_posix()) + self.log_signal.emit('[DEBUG] Temporary folder removed successfully.') + except OSError as e: + self.log_signal.emit(f'[ERROR] Failed to remove temporary folder {source_folder_to_delete}: {e}') + else: + self.log_signal.emit('[WARNING] Installation folder not found or matched root path. No folder was deleted.') def install_nvidia_libs(self): download_url = "https://github.com/SveSop/nvidia-libs/releases/download/v0.8.5/nvidia-libs-v0.8.5.tar.xz" diff --git a/src/mainwindow.py b/src/mainwindow.py index 70444c6..a239efc 100644 --- a/src/mainwindow.py +++ b/src/mainwindow.py @@ -122,6 +122,14 @@ class MainWindow(MainWindowUI): self.progress_bar.hide() self.init_installation() + if not success: + QMessageBox.critical( + self, + gls('error'), + self.logs_edit.toPlainText().split('\n')[-2] + ) + return + if check_aegnux_installed() and not check_aegnux_tip_marked(): QMessageBox.information(self, '', gls('tip_alt_t')) mark_aegnux_tip_as_shown() @@ -143,6 +151,12 @@ class MainWindow(MainWindowUI): self.install_thread.set_download_method(method) if method == DownloadMethod.OFFLINE: + QMessageBox.warning( + self, + gls('offline_note'), + gls('offline_note_text') + ) + filename, _ = QFileDialog.getOpenFileName( self, gls('offline_ae_zip_title'), diff --git a/translations/en_US.py b/translations/en_US.py index 5af0b84..e7646ac 100644 --- a/translations/en_US.py +++ b/translations/en_US.py @@ -29,5 +29,8 @@ STRINGS = { 'kill_action': 'Kill AE', 'log_action': 'Toggle log', 'term_action': 'Open Terminal', - 'cep_action': 'CEP directory' + 'cep_action': 'CEP directory', + 'offline_note': 'Offline note', + 'offline_note_text': 'Note that the .zip archive should contain After Effects from Program Files (including AfterFX.exe).', + 'error': 'Error' } \ No newline at end of file diff --git a/translations/ru_RU.py b/translations/ru_RU.py index 8c4edf6..aed4f35 100644 --- a/translations/ru_RU.py +++ b/translations/ru_RU.py @@ -29,5 +29,8 @@ STRINGS = { 'kill_action': 'Остановить AE', 'log_action': 'Вкл/выкл логи', 'term_action': 'Открыть терминал', - 'cep_action': 'Папка с CEP' + 'cep_action': 'Папка с CEP', + 'offline_note': 'Внимание, оффлайн', + 'offline_note_text': 'Учтите, что .zip-архив должен содержать After Effects из Program Files (включая AfterFX.exe).', + 'error': 'Ошибка' } \ No newline at end of file