Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into netjob_retry
This commit is contained in:
		| @@ -1,16 +1,17 @@ | |||||||
| --- | --- | ||||||
| Language:        Cpp | BasedOnStyle: Chromium | ||||||
| BasedOnStyle:  Chromium |  | ||||||
| IndentWidth: 4 | IndentWidth: 4 | ||||||
| AlignConsecutiveMacros: false |  | ||||||
| AlignConsecutiveAssignments: false |  | ||||||
| AllowShortIfStatementsOnASingleLine: false | AllowShortIfStatementsOnASingleLine: false | ||||||
|  | ColumnLimit: 140 | ||||||
|  | --- | ||||||
|  | Language: Cpp | ||||||
|  | AlignConsecutiveMacros: None | ||||||
|  | AlignConsecutiveAssignments: None | ||||||
| BraceWrapping: | BraceWrapping: | ||||||
|   AfterFunction:   true |   AfterFunction: true | ||||||
|   SplitEmptyFunction: false |   SplitEmptyFunction: false | ||||||
|   SplitEmptyRecord: false |   SplitEmptyRecord: false | ||||||
|   SplitEmptyNamespace: false |   SplitEmptyNamespace: false | ||||||
| BreakBeforeBraces: Custom | BreakBeforeBraces: Custom | ||||||
| BreakConstructorInitializers: BeforeComma | BreakConstructorInitializers: BeforeComma | ||||||
| ColumnLimit:     140 |  | ||||||
| Cpp11BracedListStyle: false | Cpp11BracedListStyle: false | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | # EditorConfig specs and documentation: https://EditorConfig.org | ||||||
|  |  | ||||||
|  | # top-most EditorConfig file | ||||||
|  | root = true | ||||||
|  |  | ||||||
|  | # C++ Code Style settings | ||||||
|  | [*.{c++,cc,cpp,cppm,cxx,h,h++,hh,hpp,hxx,inl,ipp,ixx,tlh,tli}] | ||||||
|  | cpp_generate_documentation_comments = doxygen_slash_star | ||||||
							
								
								
									
										32
									
								
								.github/workflows/backport.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								.github/workflows/backport.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | name: Backport | ||||||
|  | on: | ||||||
|  |   pull_request_target: | ||||||
|  |     types: [closed, labeled] | ||||||
|  |  | ||||||
|  | # WARNING: | ||||||
|  | # When extending this action, be aware that $GITHUB_TOKEN allows write access to | ||||||
|  | # the GitHub repository. This means that it should not evaluate user input in a | ||||||
|  | # way that allows code injection. | ||||||
|  |  | ||||||
|  | permissions: | ||||||
|  |   contents: read | ||||||
|  |  | ||||||
|  | jobs: | ||||||
|  |   backport: | ||||||
|  |     permissions: | ||||||
|  |       contents: write # for korthout/backport-action to create branch | ||||||
|  |       pull-requests: write # for korthout/backport-action to create PR to backport | ||||||
|  |     name: Backport Pull Request | ||||||
|  |     if: github.repository_owner == 'PrismLauncher' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name)) | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     steps: | ||||||
|  |       - uses: actions/checkout@v3 | ||||||
|  |         with: | ||||||
|  |           ref: ${{ github.event.pull_request.head.sha }} | ||||||
|  |       - name: Create backport PRs | ||||||
|  |         uses: korthout/backport-action@v1.4.0 | ||||||
|  |         with: | ||||||
|  |           # Config README: https://github.com/korthout/backport-action#backport-action | ||||||
|  |           pull_description: |- | ||||||
|  |             Bot-based backport to `${target_branch}`, triggered by a label in #${pull_number}. | ||||||
|  |  | ||||||
							
								
								
									
										104
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										104
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @@ -24,6 +24,12 @@ on: | |||||||
|       CACHIX_AUTH_TOKEN: |       CACHIX_AUTH_TOKEN: | ||||||
|         description: Private token for authenticating against Cachix cache |         description: Private token for authenticating against Cachix cache | ||||||
|         required: false |         required: false | ||||||
|  |       GPG_PRIVATE_KEY: | ||||||
|  |         description: Private key for AppImage signing | ||||||
|  |         required: false | ||||||
|  |       GPG_PRIVATE_KEY_ID: | ||||||
|  |         description: ID for the GPG_PRIVATE_KEY, to select the signing key | ||||||
|  |         required: false | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|   build: |   build: | ||||||
| @@ -68,7 +74,7 @@ jobs: | |||||||
|             qt_ver: 6 |             qt_ver: 6 | ||||||
|             qt_host: windows |             qt_host: windows | ||||||
|             qt_arch: '' |             qt_arch: '' | ||||||
|             qt_version: '6.5.1' |             qt_version: '6.5.2' | ||||||
|             qt_modules: 'qt5compat qtimageformats' |             qt_modules: 'qt5compat qtimageformats' | ||||||
|             qt_tools: '' |             qt_tools: '' | ||||||
|  |  | ||||||
| @@ -80,7 +86,7 @@ jobs: | |||||||
|             qt_ver: 6 |             qt_ver: 6 | ||||||
|             qt_host: windows |             qt_host: windows | ||||||
|             qt_arch: 'win64_msvc2019_arm64' |             qt_arch: 'win64_msvc2019_arm64' | ||||||
|             qt_version: '6.5.1' |             qt_version: '6.5.2' | ||||||
|             qt_modules: 'qt5compat qtimageformats' |             qt_modules: 'qt5compat qtimageformats' | ||||||
|             qt_tools: '' |             qt_tools: '' | ||||||
|  |  | ||||||
| @@ -90,7 +96,7 @@ jobs: | |||||||
|             qt_ver: 6 |             qt_ver: 6 | ||||||
|             qt_host: mac |             qt_host: mac | ||||||
|             qt_arch: '' |             qt_arch: '' | ||||||
|             qt_version: '6.5.0' |             qt_version: '6.5.2' | ||||||
|             qt_modules: 'qt5compat qtimageformats' |             qt_modules: 'qt5compat qtimageformats' | ||||||
|             qt_tools: '' |             qt_tools: '' | ||||||
|  |  | ||||||
| @@ -152,7 +158,7 @@ jobs: | |||||||
|  |  | ||||||
|       - name: Setup ccache |       - name: Setup ccache | ||||||
|         if: (runner.os != 'Windows' || matrix.msystem == '') && inputs.build_type == 'Debug' |         if: (runner.os != 'Windows' || matrix.msystem == '') && inputs.build_type == 'Debug' | ||||||
|         uses: hendrikmuhs/ccache-action@v1.2.9 |         uses: hendrikmuhs/ccache-action@v1.2.10 | ||||||
|         with: |         with: | ||||||
|           key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}-${{ matrix.architecture }} |           key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}-${{ matrix.architecture }} | ||||||
|  |  | ||||||
| @@ -249,6 +255,8 @@ jobs: | |||||||
|           wget "https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage" |           wget "https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage" | ||||||
|           wget "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage" |           wget "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage" | ||||||
|  |  | ||||||
|  |           wget "https://github.com/AppImageCommunity/AppImageUpdate/releases/download/continuous/AppImageUpdate-x86_64.AppImage" | ||||||
|  |  | ||||||
|           ${{ github.workspace }}/.github/scripts/prepare_JREs.sh |           ${{ github.workspace }}/.github/scripts/prepare_JREs.sh | ||||||
|           sudo apt install libopengl0 |           sudo apt install libopengl0 | ||||||
|  |  | ||||||
| @@ -264,23 +272,23 @@ jobs: | |||||||
|       - name: Configure CMake (macOS) |       - name: Configure CMake (macOS) | ||||||
|         if: runner.os == 'macOS' && matrix.qt_ver == 6 |         if: runner.os == 'macOS' && matrix.qt_ver == 6 | ||||||
|         run: | |         run: | | ||||||
|           cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja |           cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja | ||||||
|  |  | ||||||
|       - name: Configure CMake (macOS-Legacy) |       - name: Configure CMake (macOS-Legacy) | ||||||
|         if: runner.os == 'macOS' && matrix.qt_ver == 5 |         if: runner.os == 'macOS' && matrix.qt_ver == 5 | ||||||
|         run: | |         run: | | ||||||
|           cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -G Ninja |           cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -G Ninja | ||||||
|  |  | ||||||
|       - name: Configure CMake (Windows MinGW-w64) |       - name: Configure CMake (Windows MinGW-w64) | ||||||
|         if: runner.os == 'Windows' && matrix.msystem != '' |         if: runner.os == 'Windows' && matrix.msystem != '' | ||||||
|         shell: msys2 {0} |         shell: msys2 {0} | ||||||
|         run: | |         run: | | ||||||
|           cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -G Ninja |           cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -G Ninja | ||||||
|  |  | ||||||
|       - name: Configure CMake (Windows MSVC) |       - name: Configure CMake (Windows MSVC) | ||||||
|         if: runner.os == 'Windows' && matrix.msystem == '' |         if: runner.os == 'Windows' && matrix.msystem == '' | ||||||
|         run: | |         run: | | ||||||
|           cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON |           cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON | ||||||
|           # https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix) |           # https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix) | ||||||
|           if ("${{ env.CCACHE_VAR }}") |           if ("${{ env.CCACHE_VAR }}") | ||||||
|           { |           { | ||||||
| @@ -295,7 +303,7 @@ jobs: | |||||||
|       - name: Configure CMake (Linux) |       - name: Configure CMake (Linux) | ||||||
|         if: runner.os == 'Linux' |         if: runner.os == 'Linux' | ||||||
|         run: | |         run: | | ||||||
|           cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=Linux -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja |           cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja | ||||||
|  |  | ||||||
|       ## |       ## | ||||||
|       # BUILD |       # BUILD | ||||||
| @@ -387,8 +395,8 @@ jobs: | |||||||
|           cd ${{ env.INSTALL_DIR }} |           cd ${{ env.INSTALL_DIR }} | ||||||
|           if ("${{ matrix.qt_ver }}" -eq "5") |           if ("${{ matrix.qt_ver }}" -eq "5") | ||||||
|           { |           { | ||||||
|             Copy-Item D:/a/PrismLauncher/Qt/Tools/OpenSSL/Win_x86/bin/libcrypto-1_1.dll -Destination libcrypto-1_1.dll |             Copy-Item ${{ runner.workspace }}/Qt/Tools/OpenSSL/Win_x86/bin/libcrypto-1_1.dll -Destination libcrypto-1_1.dll | ||||||
|             Copy-Item D:/a/PrismLauncher/Qt/Tools/OpenSSL/Win_x86/bin/libssl-1_1.dll -Destination libssl-1_1.dll |             Copy-Item ${{ runner.workspace }}/Qt/Tools/OpenSSL/Win_x86/bin/libssl-1_1.dll -Destination libssl-1_1.dll | ||||||
|           } |           } | ||||||
|           cd ${{ github.workspace }} |           cd ${{ github.workspace }} | ||||||
|  |  | ||||||
| @@ -425,7 +433,7 @@ jobs: | |||||||
|         run: | |         run: | | ||||||
|           cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }}  # cmake install on Windows is slow, let's just copy instead |           cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }}  # cmake install on Windows is slow, let's just copy instead | ||||||
|           cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable |           cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable | ||||||
|            |  | ||||||
|           Get-ChildItem ${{ env.INSTALL_PORTABLE_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_PORTABLE_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt |           Get-ChildItem ${{ env.INSTALL_PORTABLE_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_PORTABLE_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt | ||||||
|  |  | ||||||
|       - name: Package (Windows, installer) |       - name: Package (Windows, installer) | ||||||
| @@ -466,11 +474,15 @@ jobs: | |||||||
|       - name: Package AppImage (Linux) |       - name: Package AppImage (Linux) | ||||||
|         if: runner.os == 'Linux' && matrix.qt_ver != 5 |         if: runner.os == 'Linux' && matrix.qt_ver != 5 | ||||||
|         shell: bash |         shell: bash | ||||||
|  |         env: | ||||||
|  |           GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} | ||||||
|         run: | |         run: | | ||||||
|           cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr |           cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr | ||||||
|  |  | ||||||
|           mv ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.metainfo.xml ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.appdata.xml |           mv ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.metainfo.xml ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.appdata.xml | ||||||
|           export "NO_APPSTREAM=1" # we have to skip appstream checking because appstream on ubuntu 20.04 is outdated |           export "NO_APPSTREAM=1" # we have to skip appstream checking because appstream on ubuntu 20.04 is outdated | ||||||
|           export OUTPUT="PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage" |  | ||||||
|  |           export OUTPUT="PrismLauncher-Linux-x86_64.AppImage" | ||||||
|  |  | ||||||
|           chmod +x linuxdeploy-*.AppImage |           chmod +x linuxdeploy-*.AppImage | ||||||
|  |  | ||||||
| @@ -481,8 +493,8 @@ jobs: | |||||||
|  |  | ||||||
|           cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk |           cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk | ||||||
|  |  | ||||||
|           cp -r /home/runner/work/PrismLauncher/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines |           cp -r ${{ runner.workspace }}/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines | ||||||
|            |  | ||||||
|           cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/ |           cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/ | ||||||
|           cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/ |           cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/ | ||||||
|           cp /usr/lib/x86_64-linux-gnu/libOpenGL.so.0* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/ |           cp /usr/lib/x86_64-linux-gnu/libOpenGL.so.0* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/ | ||||||
| @@ -494,8 +506,33 @@ jobs: | |||||||
|           LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib" |           LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib" | ||||||
|           export LD_LIBRARY_PATH |           export LD_LIBRARY_PATH | ||||||
|  |  | ||||||
|  |           chmod +x AppImageUpdate-x86_64.AppImage | ||||||
|  |           ./AppImageUpdate-x86_64.AppImage --appimage-extract | ||||||
|  |  | ||||||
|  |           mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/optional | ||||||
|  |           mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins | ||||||
|  |  | ||||||
|  |           cp -r squashfs-root/usr/bin/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/bin | ||||||
|  |           cp -r squashfs-root/usr/lib/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib | ||||||
|  |           cp -r squashfs-root/usr/optional/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/optional | ||||||
|  |           cp -r squashfs-root/usr/optional/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins | ||||||
|  |  | ||||||
|  |           export UPDATE_INFORMATION="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-Linux-x86_64.AppImage.zsync"  | ||||||
|  |  | ||||||
|  |           if [ '${{ secrets.GPG_PRIVATE_KEY_ID }}' != '' ]; then | ||||||
|  |             export SIGN=1 | ||||||
|  |             export SIGN_KEY=${{ secrets.GPG_PRIVATE_KEY_ID }} | ||||||
|  |             mkdir -p ~/.gnupg/ | ||||||
|  |             printf "$GPG_PRIVATE_KEY" | base64 --decode > ~/.gnupg/private.key | ||||||
|  |             gpg --import ~/.gnupg/private.key | ||||||
|  |           else | ||||||
|  |             echo ":warning: Skipped code signing for Linux AppImage, as gpg key was not present." >> $GITHUB_STEP_SUMMARY | ||||||
|  |           fi | ||||||
|  |  | ||||||
|           ./linuxdeploy-x86_64.AppImage --appdir ${{ env.INSTALL_APPIMAGE_DIR }} --output appimage --plugin qt -i ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/icons/hicolor/scalable/apps/org.prismlauncher.PrismLauncher.svg |           ./linuxdeploy-x86_64.AppImage --appdir ${{ env.INSTALL_APPIMAGE_DIR }} --output appimage --plugin qt -i ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/icons/hicolor/scalable/apps/org.prismlauncher.PrismLauncher.svg | ||||||
|  |  | ||||||
|  |           mv "PrismLauncher-Linux-x86_64.AppImage" "PrismLauncher-Linux-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage" | ||||||
|  |  | ||||||
|       ## |       ## | ||||||
|       # UPLOAD BUILDS |       # UPLOAD BUILDS | ||||||
|       ## |       ## | ||||||
| @@ -562,6 +599,13 @@ jobs: | |||||||
|         with: |         with: | ||||||
|           name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage |           name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage | ||||||
|           path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage |           path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage | ||||||
|  |        | ||||||
|  |       - name: Upload AppImage Zsync (Linux) | ||||||
|  |         if: runner.os == 'Linux' && matrix.qt_ver != 5 | ||||||
|  |         uses: actions/upload-artifact@v3 | ||||||
|  |         with: | ||||||
|  |           name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage.zsync | ||||||
|  |           path: PrismLauncher-Linux-x86_64.AppImage.zsync | ||||||
|  |  | ||||||
|       - name: ccache stats (Windows MinGW-w64) |       - name: ccache stats (Windows MinGW-w64) | ||||||
|         if: runner.os == 'Windows' && matrix.msystem != '' |         if: runner.os == 'Windows' && matrix.msystem != '' | ||||||
| @@ -586,33 +630,3 @@ jobs: | |||||||
|         with: |         with: | ||||||
|           bundle: "Prism Launcher.flatpak" |           bundle: "Prism Launcher.flatpak" | ||||||
|           manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml  |           manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml  | ||||||
|  |  | ||||||
|   nix: |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|     strategy: |  | ||||||
|       matrix: |  | ||||||
|         package: |  | ||||||
|           - prismlauncher |  | ||||||
|           - prismlauncher-qt5 |  | ||||||
|     steps: |  | ||||||
|       - name: Clone repository |  | ||||||
|         if: inputs.build_type == 'Debug' |  | ||||||
|         uses: actions/checkout@v3 |  | ||||||
|         with: |  | ||||||
|           submodules: 'true' |  | ||||||
|       - name: Install nix |  | ||||||
|         if: inputs.build_type == 'Debug' |  | ||||||
|         uses: cachix/install-nix-action@v22 |  | ||||||
|         with: |  | ||||||
|           install_url: https://nixos.org/nix/install |  | ||||||
|           extra_nix_config: | |  | ||||||
|             auto-optimise-store = true |  | ||||||
|             experimental-features = nix-command flakes |  | ||||||
|       - uses: cachix/cachix-action@v12 |  | ||||||
|         if: inputs.build_type == 'Debug' |  | ||||||
|         with: |  | ||||||
|           name: prismlauncher |  | ||||||
|           authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' |  | ||||||
|       - name: Build |  | ||||||
|         if: inputs.build_type == 'Debug' |  | ||||||
|         run: nix build .#${{ matrix.package }} --print-build-logs |  | ||||||
|   | |||||||
							
								
								
									
										9
									
								
								.github/workflows/trigger_release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.github/workflows/trigger_release.yml
									
									
									
									
										vendored
									
									
								
							| @@ -43,7 +43,8 @@ jobs: | |||||||
|           mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz           |           mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz           | ||||||
|           mv PrismLauncher-Linux-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz |           mv PrismLauncher-Linux-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz | ||||||
|           mv PrismLauncher-Linux*/PrismLauncher.tar.gz PrismLauncher-Linux-${{ env.VERSION }}.tar.gz |           mv PrismLauncher-Linux*/PrismLauncher.tar.gz PrismLauncher-Linux-${{ env.VERSION }}.tar.gz | ||||||
|           mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage |           mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-x86_64.AppImage | ||||||
|  |           mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync | ||||||
|           mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz |           mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz | ||||||
|           mv PrismLauncher-macOS*/PrismLauncher.tar.gz PrismLauncher-macOS-${{ env.VERSION }}.tar.gz |           mv PrismLauncher-macOS*/PrismLauncher.tar.gz PrismLauncher-macOS-${{ env.VERSION }}.tar.gz | ||||||
|  |  | ||||||
| @@ -78,9 +79,8 @@ jobs: | |||||||
|       - name: Create release |       - name: Create release | ||||||
|         id: create_release |         id: create_release | ||||||
|         uses: softprops/action-gh-release@v1 |         uses: softprops/action-gh-release@v1 | ||||||
|         env: |  | ||||||
|           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |  | ||||||
|         with: |         with: | ||||||
|  |           token: ${{ secrets.GITHUB_TOKEN }} | ||||||
|           tag_name: ${{ github.ref }} |           tag_name: ${{ github.ref }} | ||||||
|           name: Prism Launcher ${{ env.VERSION }} |           name: Prism Launcher ${{ env.VERSION }} | ||||||
|           draft: true |           draft: true | ||||||
| @@ -88,7 +88,8 @@ jobs: | |||||||
|           files: | |           files: | | ||||||
|             PrismLauncher-Linux-${{ env.VERSION }}.tar.gz |             PrismLauncher-Linux-${{ env.VERSION }}.tar.gz | ||||||
|             PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz |             PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz | ||||||
|             PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage |             PrismLauncher-Linux-x86_64.AppImage | ||||||
|  |             PrismLauncher-Linux-x86_64.AppImage.zsync | ||||||
|             PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz |             PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz | ||||||
|             PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz |             PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz | ||||||
|             PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip |             PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								.github/workflows/update-flake.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/update-flake.yml
									
									
									
									
										vendored
									
									
								
							| @@ -25,4 +25,6 @@ jobs: | |||||||
|           pr-title: "chore(nix): update lockfile" |           pr-title: "chore(nix): update lockfile" | ||||||
|           pr-labels: | |           pr-labels: | | ||||||
|             Linux |             Linux | ||||||
|  |             packaging | ||||||
|             simple change |             simple change | ||||||
|  |             changelog:omit | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| @@ -19,3 +19,6 @@ | |||||||
| [submodule "libraries/cmark"] | [submodule "libraries/cmark"] | ||||||
| 	path = libraries/cmark | 	path = libraries/cmark | ||||||
| 	url = https://github.com/commonmark/cmark.git | 	url = https://github.com/commonmark/cmark.git | ||||||
|  | [submodule "flatpak/shared-modules"] | ||||||
|  | 	path = flatpak/shared-modules | ||||||
|  | 	url = https://github.com/flathub/shared-modules.git | ||||||
|   | |||||||
| @@ -33,6 +33,13 @@ if(MSVC) | |||||||
|     # Use /W4 as /Wall includes unnesserey warnings such as added padding to structs |     # Use /W4 as /Wall includes unnesserey warnings such as added padding to structs | ||||||
|     set(CMAKE_CXX_FLAGS "/GS /permissive- /W4 ${CMAKE_CXX_FLAGS}") |     set(CMAKE_CXX_FLAGS "/GS /permissive- /W4 ${CMAKE_CXX_FLAGS}") | ||||||
|  |  | ||||||
|  |     # /EHs Enables stack unwind semantics for standard C++ exceptions to ensure stackframes are unwound | ||||||
|  |     # and object deconstructors are called when an exception is caught. | ||||||
|  |     # without it memory leaks and a warning is printed | ||||||
|  |     # /EHc tells the compiler to assume that functions declared as extern "C" never throw a C++ exception | ||||||
|  |     # This appears to not always be a defualt compiler option in CMAKE | ||||||
|  |     set(CMAKE_CXX_FLAGS "/EHsc ${CMAKE_CXX_FLAGS}") | ||||||
|  |  | ||||||
|     # LINK accepts /SUBSYSTEM whics sets if we are a WINDOWS (gui) or a CONSOLE programs |     # LINK accepts /SUBSYSTEM whics sets if we are a WINDOWS (gui) or a CONSOLE programs | ||||||
|     # This implicitly selects an entrypoint specific to the subsystem selected |     # This implicitly selects an entrypoint specific to the subsystem selected | ||||||
|     # qtmain/QtEntryPointLib provides the correct entrypoint (wWinMain) for gui programs |     # qtmain/QtEntryPointLib provides the correct entrypoint (wWinMain) for gui programs | ||||||
| @@ -85,38 +92,39 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTOML_ENABLE_FLOAT16=0") | |||||||
| # set CXXFLAGS for build targets | # set CXXFLAGS for build targets | ||||||
| set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}") | set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}") | ||||||
|  |  | ||||||
| option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" on) | option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" OFF) | ||||||
|  |  | ||||||
| # If this is a Debug build turn on address sanitiser | # If this is a Debug build turn on address sanitiser | ||||||
| if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND DEBUG_ADDRESS_SANITIZER) | if ((CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") AND DEBUG_ADDRESS_SANITIZER) | ||||||
|     message(STATUS "Address Sanitizer enabled for Debug builds, Turn it off with -DDEBUG_ADDRESS_SANITIZER=off") |     message(STATUS "Address Sanitizer enabled for Debug builds, Turn it off with -DDEBUG_ADDRESS_SANITIZER=off") | ||||||
|     if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") |     if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") | ||||||
|         if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") |         if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") | ||||||
|             # using clang with clang-cl front end  |             # using clang with clang-cl front end  | ||||||
|             message(STATUS "Address Sanitizer available on Clang MSVC frontend") |             message(STATUS "Address Sanitizer available on Clang MSVC frontend") | ||||||
|             set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /O1 /Oy-") |             set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-") | ||||||
|             set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /O1 /Oy-") |             set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-") | ||||||
|         else() |         else() | ||||||
|             # AppleClang and Clang |             # AppleClang and Clang | ||||||
|             message(STATUS "Address Sanitizer available on Clang") |             message(STATUS "Address Sanitizer available on Clang") | ||||||
|             set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer") |             set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") | ||||||
|             set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer") |             set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer") | ||||||
|         endif() |         endif() | ||||||
|     elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") |     elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") | ||||||
|         # GCC |         # GCC | ||||||
|         message(STATUS "Address Sanitizer available on GCC") |         message(STATUS "Address Sanitizer available on GCC") | ||||||
|         set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer") |         set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") | ||||||
|         set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer") |         set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer") | ||||||
|         link_libraries("asan") |         link_libraries("asan") | ||||||
|     elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") |     elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") | ||||||
|         message(STATUS "Address Sanitizer available on MSVC") |         message(STATUS "Address Sanitizer available on MSVC") | ||||||
|         set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /O1 /Oy-") |         set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-") | ||||||
|         set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /O1 /Oy-") |         set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-") | ||||||
|     else() |     else() | ||||||
|         message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}") |         message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}") | ||||||
|     endif() |     endif() | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
|  |  | ||||||
| option(ENABLE_LTO "Enable Link Time Optimization" off) | option(ENABLE_LTO "Enable Link Time Optimization" off) | ||||||
|  |  | ||||||
| if(ENABLE_LTO) | if(ENABLE_LTO) | ||||||
| @@ -178,7 +186,7 @@ set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}. | |||||||
| set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},0,0") | set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},0,0") | ||||||
|  |  | ||||||
| # Build platform. | # Build platform. | ||||||
| set(Launcher_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.") | set(Launcher_BUILD_PLATFORM "unknown" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.") | ||||||
|  |  | ||||||
| # Channel list URL | # Channel list URL | ||||||
| set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.") | set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.") | ||||||
| @@ -318,6 +326,8 @@ add_subdirectory(program_info) | |||||||
|  |  | ||||||
| ####################################### Install layout ####################################### | ####################################### Install layout ####################################### | ||||||
|  |  | ||||||
|  | set(Launcher_ENABLE_UPDATER NO) | ||||||
|  |  | ||||||
| if(NOT (UNIX AND APPLE)) | if(NOT (UNIX AND APPLE)) | ||||||
|     # Install "portable.txt" if selected component is "portable" |     # Install "portable.txt" if selected component is "portable" | ||||||
|     install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_Portable_File}" DESTINATION "." COMPONENT portable EXCLUDE_FROM_ALL) |     install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_Portable_File}" DESTINATION "." COMPONENT portable EXCLUDE_FROM_ALL) | ||||||
| @@ -342,9 +352,9 @@ if(UNIX AND APPLE) | |||||||
|     set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_NAME}") |     set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_NAME}") | ||||||
|     set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_NAME}") |     set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_NAME}") | ||||||
|     set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns) |     set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns) | ||||||
|     set(MACOSX_BUNDLE_COPYRIGHT "© 2022 ${Launcher_Copyright_Mac}") |     set(MACOSX_BUNDLE_COPYRIGHT "© 2022-2023 ${Launcher_Copyright_Mac}") | ||||||
|     set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=") |     set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=" CACHE STRING "Public key for Sparkle update feed") | ||||||
|     set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://prismlauncher.org/feed/appcast.xml") |     set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://prismlauncher.org/feed/appcast.xml" CACHE STRING "URL for Sparkle update feed") | ||||||
|  |  | ||||||
|     set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.1.0/Sparkle-2.1.0.tar.xz" CACHE STRING "URL to Sparkle release archive") |     set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.1.0/Sparkle-2.1.0.tar.xz" CACHE STRING "URL to Sparkle release archive") | ||||||
|     set(MACOSX_SPARKLE_SHA256 "bf6ac1caa9f8d321d5784859c88da874f28412f37fb327bc21b7b14c5d61ef94" CACHE STRING "SHA256 checksum for Sparkle release archive") |     set(MACOSX_SPARKLE_SHA256 "bf6ac1caa9f8d321d5784859c88da874f28412f37fb327bc21b7b14c5d61ef94" CACHE STRING "SHA256 checksum for Sparkle release archive") | ||||||
| @@ -353,8 +363,12 @@ if(UNIX AND APPLE) | |||||||
|     # directories to look for dependencies |     # directories to look for dependencies | ||||||
|     set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ${MACOSX_SPARKLE_DIR}) |     set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ${MACOSX_SPARKLE_DIR}) | ||||||
|  |  | ||||||
|  |     if(NOT MACOSX_SPARKLE_UPDATE_PUBLIC_KEY STREQUAL "" AND NOT MACOSX_SPARKLE_UPDATE_FEED_URL STREQUAL "") | ||||||
|  |         set(Launcher_ENABLE_UPDATER YES) | ||||||
|  |     endif() | ||||||
|  |  | ||||||
|     # install as bundle |     # install as bundle | ||||||
|     set(INSTALL_BUNDLE "full") |     set(INSTALL_BUNDLE "full" CACHE STRING "Use fixup_bundle to bundle dependencies") | ||||||
|  |  | ||||||
|     # Add the icon |     # Add the icon | ||||||
|     install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns) |     install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns) | ||||||
| @@ -367,7 +381,7 @@ elseif(UNIX) | |||||||
|     set(JARS_DEST_DIR "share/${Launcher_Name}") |     set(JARS_DEST_DIR "share/${Launcher_Name}") | ||||||
|  |  | ||||||
|     # install as bundle with no dependencies included |     # install as bundle with no dependencies included | ||||||
|     set(INSTALL_BUNDLE "nodeps") |     set(INSTALL_BUNDLE "nodeps" CACHE STRING "Use fixup_bundle to bundle dependencies") | ||||||
|  |  | ||||||
|     # Set RPATH |     # Set RPATH | ||||||
|     SET(Launcher_BINARY_RPATH "$ORIGIN/") |     SET(Launcher_BINARY_RPATH "$ORIGIN/") | ||||||
| @@ -401,7 +415,7 @@ elseif(WIN32) | |||||||
|     set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) |     set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) | ||||||
|  |  | ||||||
|     # install as bundle |     # install as bundle | ||||||
|     set(INSTALL_BUNDLE "full") |     set(INSTALL_BUNDLE "full" CACHE STRING "Use fixup_bundle to bundle dependencies") | ||||||
| else() | else() | ||||||
|     message(FATAL_ERROR "Platform not supported") |     message(FATAL_ERROR "Platform not supported") | ||||||
| endif() | endif() | ||||||
|   | |||||||
| @@ -19,7 +19,7 @@ In an effort to ensure that the code you contribute is actually compatible with | |||||||
|  |  | ||||||
| This can be done by appending `-s` to your `git commit` call, or by manually appending the following text to your commit message: | This can be done by appending `-s` to your `git commit` call, or by manually appending the following text to your commit message: | ||||||
|  |  | ||||||
| ``` | ```text | ||||||
| <commit message> | <commit message> | ||||||
|  |  | ||||||
| Signed-off-by: Author name <Author email> | Signed-off-by: Author name <Author email> | ||||||
| @@ -27,7 +27,7 @@ Signed-off-by: Author name <Author email> | |||||||
|  |  | ||||||
| By signing off your work, you agree to the terms below: | By signing off your work, you agree to the terms below: | ||||||
|  |  | ||||||
| ``` | ```text | ||||||
| Developer's Certificate of Origin 1.1 | Developer's Certificate of Origin 1.1 | ||||||
|  |  | ||||||
| By making a contribution to this project, I certify that: | By making a contribution to this project, I certify that: | ||||||
| @@ -61,3 +61,9 @@ As a bonus, you can also [cryptographically sign your commits][gh-signing-commit | |||||||
|  |  | ||||||
| [gh-signing-commits]: https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits | [gh-signing-commits]: https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits | ||||||
| [gh-vigilant-mode]: https://docs.github.com/en/authentication/managing-commit-signature-verification/displaying-verification-statuses-for-all-of-your-commits | [gh-vigilant-mode]: https://docs.github.com/en/authentication/managing-commit-signature-verification/displaying-verification-statuses-for-all-of-your-commits | ||||||
|  |  | ||||||
|  | ## Backporting to Release Branches | ||||||
|  |  | ||||||
|  | We use [automated backports](https://github.com/PrismLauncher/PrismLauncher/blob/develop/.github/workflows/backport.yml) to merge specific contributions from develop into `release` branches. | ||||||
|  |  | ||||||
|  | This is done when pull requests are merged and have labels such as `backport release-7.x` - which should be added along with the milestone for the release. | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								README.md
									
									
									
									
									
								
							| @@ -42,7 +42,7 @@ Feel free to create a GitHub issue if you find a bug or want to suggest a new fe | |||||||
|  |  | ||||||
| - **Our Matrix space:** | - **Our Matrix space:** | ||||||
|  |  | ||||||
| [](https://prismlauncher.org/matrix) | [](https://prismlauncher.org/matrix) | ||||||
|  |  | ||||||
| - **Our Subreddit:** | - **Our Subreddit:** | ||||||
|  |  | ||||||
| @@ -50,7 +50,7 @@ Feel free to create a GitHub issue if you find a bug or want to suggest a new fe | |||||||
|  |  | ||||||
| ## Translations | ## Translations | ||||||
|  |  | ||||||
| The translation effort for PrismLauncher is hosted on [Weblate](https://hosted.weblate.org/projects/prismlauncher/launcher/) and information about translating Prism Launcher is available at <https://github.com/PrismLauncher/Translations> | The translation effort for Prism Launcher is hosted on [Weblate](https://hosted.weblate.org/projects/prismlauncher/launcher/) and information about translating Prism Launcher is available at <https://github.com/PrismLauncher/Translations> | ||||||
|  |  | ||||||
| ## Building | ## Building | ||||||
|  |  | ||||||
| @@ -82,14 +82,16 @@ Thanks to the awesome people over at [MacStadium](https://www.macstadium.com/), | |||||||
|  |  | ||||||
| ## Forking/Redistributing/Custom builds policy | ## Forking/Redistributing/Custom builds policy | ||||||
|  |  | ||||||
| We don't care what you do with your fork/custom build as long as you follow the terms of the [license](LICENSE) (this is a legal responsibility), and if you made code changes rather than just packaging a custom build, please do the following as a basic courtesy: | You are free to fork, redistribute and provide custom builds as long as you follow the terms of the [license](LICENSE) (this is a legal responsibility), and if you made code changes rather than just packaging a custom build, please do the following as a basic courtesy: | ||||||
|  |  | ||||||
| - Make it clear that your fork is not PrismLauncher and is not endorsed by or affiliated with the PrismLauncher project (<https://prismlauncher.org>). | - Make it clear that your fork is not Prism Launcher and is not endorsed by or affiliated with the Prism Launcher project (<https://prismlauncher.org>). | ||||||
| - Go through [CMakeLists.txt](CMakeLists.txt) and change PrismLauncher's API keys to your own or set them to empty strings (`""`) to disable them (this way the program will still compile but the functionality requiring those keys will be disabled). | - Go through [CMakeLists.txt](CMakeLists.txt) and change Prism Launcher's API keys to your own or set them to empty strings (`""`) to disable them (this way the program will still compile but the functionality requiring those keys will be disabled). | ||||||
|  |  | ||||||
| If you have any questions or want any clarification on the above conditions please make an issue and ask us. | If you have any questions or want any clarification on the above conditions please make an issue and ask us. | ||||||
|  |  | ||||||
| Be aware that if you build this software without removing the provided API keys in [CMakeLists.txt](CMakeLists.txt) you are accepting the following terms and conditions: | If you are just building Prism Launcher for your distribution, please make sure to set the `Launcher_BUILD_PLATFORM` to a slug representing your distribution. Examples are `archlinux`, `fedora` and `nixpkgs`. | ||||||
|  |  | ||||||
|  | Note that if you build this software without removing the provided API keys in [CMakeLists.txt](CMakeLists.txt) you are accepting the following terms and conditions: | ||||||
|  |  | ||||||
| - [Microsoft Identity Platform Terms of Use](https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use) | - [Microsoft Identity Platform Terms of Use](https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use) | ||||||
| - [CurseForge 3rd Party API Terms and Conditions](https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions) | - [CurseForge 3rd Party API Terms and Conditions](https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions) | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  PolyMC - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
| @@ -65,7 +65,7 @@ Config::Config() | |||||||
|     MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@"; |     MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@"; | ||||||
|     MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@"; |     MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@"; | ||||||
|  |  | ||||||
|     if (BUILD_PLATFORM == "macOS" && !MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty()) |     if (!MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty()) | ||||||
|     { |     { | ||||||
|         UPDATER_ENABLED = true; |         UPDATER_ENABLED = true; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -36,8 +36,8 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
| #include <QString> |  | ||||||
| #include <QList> | #include <QList> | ||||||
|  | #include <QString> | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * \brief The Config class holds all the build-time information passed from the build system. |  * \brief The Config class holds all the build-time information passed from the build system. | ||||||
| @@ -68,7 +68,7 @@ class Config { | |||||||
|  |  | ||||||
|     bool UPDATER_ENABLED = false; |     bool UPDATER_ENABLED = false; | ||||||
|  |  | ||||||
|     /// A short string identifying this build's platform. For example, "lin64" or "win32". |     /// A short string identifying this build's platform or distribution. | ||||||
|     QString BUILD_PLATFORM; |     QString BUILD_PLATFORM; | ||||||
|  |  | ||||||
|     /// A string containing the build timestamp |     /// A string containing the build timestamp | ||||||
| @@ -145,7 +145,7 @@ class Config { | |||||||
|     QString AUTH_BASE = "https://authserver.mojang.com/"; |     QString AUTH_BASE = "https://authserver.mojang.com/"; | ||||||
|     QString IMGUR_BASE_URL = "https://api.imgur.com/3/"; |     QString IMGUR_BASE_URL = "https://api.imgur.com/3/"; | ||||||
|     QString FMLLIBS_BASE_URL = "https://files.prismlauncher.org/fmllibs/";  // FIXME: move into CMakeLists |     QString FMLLIBS_BASE_URL = "https://files.prismlauncher.org/fmllibs/";  // FIXME: move into CMakeLists | ||||||
|     QString TRANSLATIONS_BASE_URL = "https://i18n.prismlauncher.org/";  // FIXME: move into CMakeLists |     QString TRANSLATIONS_BASE_URL = "https://i18n.prismlauncher.org/";      // FIXME: move into CMakeLists | ||||||
|  |  | ||||||
|     QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/"; |     QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/"; | ||||||
|  |  | ||||||
| @@ -162,7 +162,7 @@ class Config { | |||||||
|  |  | ||||||
|     QString MODRINTH_STAGING_URL = "https://staging-api.modrinth.com/v2"; |     QString MODRINTH_STAGING_URL = "https://staging-api.modrinth.com/v2"; | ||||||
|     QString MODRINTH_PROD_URL = "https://api.modrinth.com/v2"; |     QString MODRINTH_PROD_URL = "https://api.modrinth.com/v2"; | ||||||
|     QStringList MODRINTH_MRPACK_HOSTS{"cdn.modrinth.com", "github.com", "raw.githubusercontent.com", "gitlab.com"}; |     QStringList MODRINTH_MRPACK_HOSTS{ "cdn.modrinth.com", "github.com", "raw.githubusercontent.com", "gitlab.com" }; | ||||||
|  |  | ||||||
|     QString FLAME_BASE_URL = "https://api.curseforge.com/v1"; |     QString FLAME_BASE_URL = "https://api.curseforge.com/v1"; | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										152
									
								
								cmake/CompilerWarnings.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								cmake/CompilerWarnings.cmake
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | |||||||
|  | #  | ||||||
|  | # Function to set compiler warnings with reasonable defaults at the project level. | ||||||
|  | # Taken from https://github.com/aminya/project_options/blob/main/src/CompilerWarnings.cmake | ||||||
|  | # under the folowing license: | ||||||
|  | #  | ||||||
|  | # MIT License | ||||||
|  | # | ||||||
|  | # Copyright (c) 2022-2100 Amin Yahyaabadi | ||||||
|  | # | ||||||
|  | # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  | # of this software and associated documentation files (the "Software"), to deal | ||||||
|  | # in the Software without restriction, including without limitation the rights | ||||||
|  | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  | # copies of the Software, and to permit persons to whom the Software is | ||||||
|  | # furnished to do so, subject to the following conditions: | ||||||
|  | # | ||||||
|  | # The above copyright notice and this permission notice shall be included in all | ||||||
|  | # copies or substantial portions of the Software. | ||||||
|  | # | ||||||
|  | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||||
|  | # SOFTWARE. | ||||||
|  | # | ||||||
|  |  | ||||||
|  | include_guard() | ||||||
|  |  | ||||||
|  | function(_set_project_warnings_add_target_link_option TARGET OPTIONS) | ||||||
|  |   target_link_options(${_project_name} INTERFACE ${OPTIONS}) | ||||||
|  | endfunction() | ||||||
|  |  | ||||||
|  | # Set the compiler warnings | ||||||
|  | # | ||||||
|  | # https://clang.llvm.org/docs/DiagnosticsReference.html | ||||||
|  | # https://github.com/lefticus/cppbestpractices/blob/master/02-Use_the_Tools_Available.md | ||||||
|  | function( | ||||||
|  |   set_project_warnings | ||||||
|  |   _project_name | ||||||
|  |   MSVC_WARNINGS | ||||||
|  |   CLANG_WARNINGS | ||||||
|  |   GCC_WARNINGS | ||||||
|  | ) | ||||||
|  |   if("${MSVC_WARNINGS}" STREQUAL "") | ||||||
|  |     set(MSVC_WARNINGS | ||||||
|  |         /W4 # Baseline reasonable warnings | ||||||
|  |         /w14242 # 'identifier': conversion from 'type1' to 'type1', possible loss of data | ||||||
|  |         /w14254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data | ||||||
|  |         /w14263 # 'function': member function does not override any base class virtual member function | ||||||
|  |         /w14265 # 'classname': class has virtual functions, but destructor is not virtual instances of this class may not | ||||||
|  |                 # be destructed correctly | ||||||
|  |         /w14287 # 'operator': unsigned/negative constant mismatch | ||||||
|  |         /we4289 # nonstandard extension used: 'variable': loop control variable declared in the for-loop is used outside | ||||||
|  |                 # the for-loop scope | ||||||
|  |         /w14296 # 'operator': expression is always 'boolean_value' | ||||||
|  |         /w14311 # 'variable': pointer truncation from 'type1' to 'type2' | ||||||
|  |         /w14545 # expression before comma evaluates to a function which is missing an argument list | ||||||
|  |         /w14546 # function call before comma missing argument list | ||||||
|  |         /w14547 # 'operator': operator before comma has no effect; expected operator with side-effect | ||||||
|  |         /w14549 # 'operator': operator before comma has no effect; did you intend 'operator'? | ||||||
|  |         /w14555 # expression has no effect; expected expression with side- effect | ||||||
|  |         /w14619 # pragma warning: there is no warning number 'number' | ||||||
|  |         /w14640 # Enable warning on thread un-safe static member initialization | ||||||
|  |         /w14826 # Conversion from 'type1' to 'type_2' is sign-extended. This may cause unexpected runtime behavior. | ||||||
|  |         /w14905 # wide string literal cast to 'LPSTR' | ||||||
|  |         /w14906 # string literal cast to 'LPWSTR' | ||||||
|  |         /w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied | ||||||
|  |         /permissive- # standards conformance mode for MSVC compiler. | ||||||
|  |     ) | ||||||
|  |   endif() | ||||||
|  |  | ||||||
|  |   if("${CLANG_WARNINGS}" STREQUAL "") | ||||||
|  |     set(CLANG_WARNINGS | ||||||
|  |         -Wall | ||||||
|  |         -Wextra # reasonable and standard | ||||||
|  |         -Wextra-semi # Warn about semicolon after in-class function definition. | ||||||
|  |         -Wshadow # warn the user if a variable declaration shadows one from a parent context | ||||||
|  |         -Wnon-virtual-dtor # warn the user if a class with virtual functions has a non-virtual destructor. This helps | ||||||
|  |         # catch hard to track down memory errors | ||||||
|  |         -Wold-style-cast # warn for c-style casts | ||||||
|  |         -Wcast-align # warn for potential performance problem casts | ||||||
|  |         -Wunused # warn on anything being unused | ||||||
|  |         -Woverloaded-virtual # warn if you overload (not override) a virtual function | ||||||
|  |         -Wpedantic # warn if non-standard C++ is used | ||||||
|  |         -Wconversion # warn on type conversions that may lose data | ||||||
|  |         -Wsign-conversion # warn on sign conversions | ||||||
|  |         -Wnull-dereference # warn if a null dereference is detected | ||||||
|  |         -Wdouble-promotion # warn if float is implicit promoted to double | ||||||
|  |         -Wformat=2 # warn on security issues around functions that format output (ie printf) | ||||||
|  |         -Wimplicit-fallthrough # warn on statements that fallthrough without an explicit annotation | ||||||
|  |     ) | ||||||
|  |   endif() | ||||||
|  |  | ||||||
|  |   if("${GCC_WARNINGS}" STREQUAL "") | ||||||
|  |     set(GCC_WARNINGS | ||||||
|  |         ${CLANG_WARNINGS} | ||||||
|  |         -Wmisleading-indentation # warn if indentation implies blocks where blocks do not exist | ||||||
|  |         -Wduplicated-cond # warn if if / else chain has duplicated conditions | ||||||
|  |         -Wduplicated-branches # warn if if / else branches have duplicated code | ||||||
|  |         -Wlogical-op # warn about logical operations being used where bitwise were probably wanted | ||||||
|  |         -Wuseless-cast # warn if you perform a cast to the same type | ||||||
|  |     ) | ||||||
|  |   endif() | ||||||
|  |  | ||||||
|  |   if(MSVC) | ||||||
|  |     set(PROJECT_WARNINGS_CXX ${MSVC_WARNINGS}) | ||||||
|  |   elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") | ||||||
|  |     set(PROJECT_WARNINGS_CXX ${CLANG_WARNINGS}) | ||||||
|  |   elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") | ||||||
|  |     set(PROJECT_WARNINGS_CXX ${GCC_WARNINGS}) | ||||||
|  |   else() | ||||||
|  |     message(AUTHOR_WARNING "No compiler warnings set for CXX compiler: '${CMAKE_CXX_COMPILER_ID}'") | ||||||
|  |     # TODO support Intel compiler | ||||||
|  |   endif() | ||||||
|  |  | ||||||
|  |   # Add C warnings | ||||||
|  |   set(PROJECT_WARNINGS_C "${PROJECT_WARNINGS_CXX}") | ||||||
|  |   list( | ||||||
|  |     REMOVE_ITEM | ||||||
|  |     PROJECT_WARNINGS_C | ||||||
|  |     -Wnon-virtual-dtor | ||||||
|  |     -Wold-style-cast | ||||||
|  |     -Woverloaded-virtual | ||||||
|  |     -Wuseless-cast | ||||||
|  |     -Wextra-semi | ||||||
|  |   ) | ||||||
|  |  | ||||||
|  |   target_compile_options( | ||||||
|  |     ${_project_name} | ||||||
|  |     INTERFACE # C++ warnings | ||||||
|  |               $<$<COMPILE_LANGUAGE:CXX>:${PROJECT_WARNINGS_CXX}> | ||||||
|  |               # C warnings | ||||||
|  |               $<$<COMPILE_LANGUAGE:C>:${PROJECT_WARNINGS_C}> | ||||||
|  |    ) | ||||||
|  |  | ||||||
|  |   # If we are using the compiler as a linker driver pass the warnings to it | ||||||
|  |   # (most useful when using LTO or warnings as errors) | ||||||
|  |   if(CMAKE_CXX_LINK_EXECUTABLE MATCHES "^<CMAKE_CXX_COMPILER>") | ||||||
|  |     _set_project_warnings_add_target_link_option( | ||||||
|  |       ${_project_name} "$<$<COMPILE_LANGUAGE:CXX>:${PROJECT_WARNINGS_CXX}>" | ||||||
|  |     ) | ||||||
|  |   endif() | ||||||
|  |  | ||||||
|  |   if(CMAKE_C_LINK_EXECUTABLE MATCHES "^<CMAKE_C_COMPILER>") | ||||||
|  |     _set_project_warnings_add_target_link_option( | ||||||
|  |       ${_project_name} "$<$<COMPILE_LANGUAGE:C>:${PROJECT_WARNINGS_C}>" | ||||||
|  |     ) | ||||||
|  |   endif() | ||||||
|  |  | ||||||
|  |  endfunction() | ||||||
| @@ -67,5 +67,16 @@ | |||||||
|             <string>Alternate</string> |             <string>Alternate</string> | ||||||
|         </dict> |         </dict> | ||||||
|     </array> |     </array> | ||||||
|  |     <key>CFBundleURLTypes</key> | ||||||
|  |     <array> | ||||||
|  |         <dict> | ||||||
|  |             <key>CFBundleURLName</key> | ||||||
|  |             <string>Curseforge</string> | ||||||
|  |             <key>CFBundleURLSchemes</key> | ||||||
|  |             <array> | ||||||
|  |             <string>curseforge</string> | ||||||
|  |             </array> | ||||||
|  |         </dict> | ||||||
|  |     </array> | ||||||
| </dict> | </dict> | ||||||
| </plist> | </plist> | ||||||
|   | |||||||
							
								
								
									
										30
									
								
								flake.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										30
									
								
								flake.lock
									
									
									
										generated
									
									
									
								
							| @@ -21,11 +21,11 @@ | |||||||
|         "nixpkgs-lib": "nixpkgs-lib" |         "nixpkgs-lib": "nixpkgs-lib" | ||||||
|       }, |       }, | ||||||
|       "locked": { |       "locked": { | ||||||
|         "lastModified": 1688254665, |         "lastModified": 1690933134, | ||||||
|         "narHash": "sha256-8FHEgBrr7gYNiS/NzCxIO3m4hvtLRW9YY1nYo1ivm3o=", |         "narHash": "sha256-ab989mN63fQZBFrkk4Q8bYxQCktuHmBIBqUG1jl6/FQ=", | ||||||
|         "owner": "hercules-ci", |         "owner": "hercules-ci", | ||||||
|         "repo": "flake-parts", |         "repo": "flake-parts", | ||||||
|         "rev": "267149c58a14d15f7f81b4d737308421de9d7152", |         "rev": "59cf3f1447cfc75087e7273b04b31e689a8599fb", | ||||||
|         "type": "github" |         "type": "github" | ||||||
|       }, |       }, | ||||||
|       "original": { |       "original": { | ||||||
| @@ -76,11 +76,11 @@ | |||||||
|     "libnbtplusplus": { |     "libnbtplusplus": { | ||||||
|       "flake": false, |       "flake": false, | ||||||
|       "locked": { |       "locked": { | ||||||
|         "lastModified": 1650031308, |         "lastModified": 1690036783, | ||||||
|         "narHash": "sha256-TvVOjkUobYJD9itQYueELJX3wmecvEdCbJ0FinW2mL4=", |         "narHash": "sha256-A5kTgICnx+Qdq3Fir/bKTfdTt/T1NQP2SC+nhN1ENug=", | ||||||
|         "owner": "PrismLauncher", |         "owner": "PrismLauncher", | ||||||
|         "repo": "libnbtplusplus", |         "repo": "libnbtplusplus", | ||||||
|         "rev": "2203af7eeb48c45398139b583615134efd8d407f", |         "rev": "a5e8fd52b8bf4ab5d5bcc042b2a247867589985f", | ||||||
|         "type": "github" |         "type": "github" | ||||||
|       }, |       }, | ||||||
|       "original": { |       "original": { | ||||||
| @@ -91,11 +91,11 @@ | |||||||
|     }, |     }, | ||||||
|     "nixpkgs": { |     "nixpkgs": { | ||||||
|       "locked": { |       "locked": { | ||||||
|         "lastModified": 1688221086, |         "lastModified": 1691853136, | ||||||
|         "narHash": "sha256-cdW6qUL71cNWhHCpMPOJjlw0wzSRP0pVlRn2vqX/VVg=", |         "narHash": "sha256-wTzDsRV4HN8A2Sl0SVQY0q8ILs90CD43Ha//7gNZE+E=", | ||||||
|         "owner": "nixos", |         "owner": "nixos", | ||||||
|         "repo": "nixpkgs", |         "repo": "nixpkgs", | ||||||
|         "rev": "cd99c2b3c9f160cd004318e0697f90bbd5960825", |         "rev": "f0451844bbdf545f696f029d1448de4906c7f753", | ||||||
|         "type": "github" |         "type": "github" | ||||||
|       }, |       }, | ||||||
|       "original": { |       "original": { | ||||||
| @@ -108,11 +108,11 @@ | |||||||
|     "nixpkgs-lib": { |     "nixpkgs-lib": { | ||||||
|       "locked": { |       "locked": { | ||||||
|         "dir": "lib", |         "dir": "lib", | ||||||
|         "lastModified": 1688049487, |         "lastModified": 1690881714, | ||||||
|         "narHash": "sha256-100g4iaKC9MalDjUW9iN6Jl/OocTDtXdeAj7pEGIRh4=", |         "narHash": "sha256-h/nXluEqdiQHs1oSgkOOWF+j8gcJMWhwnZ9PFabN6q0=", | ||||||
|         "owner": "NixOS", |         "owner": "NixOS", | ||||||
|         "repo": "nixpkgs", |         "repo": "nixpkgs", | ||||||
|         "rev": "4bc72cae107788bf3f24f30db2e2f685c9298dc9", |         "rev": "9e1960bc196baf6881340d53dccb203a951745a2", | ||||||
|         "type": "github" |         "type": "github" | ||||||
|       }, |       }, | ||||||
|       "original": { |       "original": { | ||||||
| @@ -138,11 +138,11 @@ | |||||||
|         ] |         ] | ||||||
|       }, |       }, | ||||||
|       "locked": { |       "locked": { | ||||||
|         "lastModified": 1688386108, |         "lastModified": 1691747570, | ||||||
|         "narHash": "sha256-Vffto9QaVonzYAcPlAzd0soqWYpPpKk60dfNLSIXcFA=", |         "narHash": "sha256-J3fnIwJtHVQ0tK2JMBv4oAmII+1mCdXdpeCxtIsrL2A=", | ||||||
|         "owner": "cachix", |         "owner": "cachix", | ||||||
|         "repo": "pre-commit-hooks.nix", |         "repo": "pre-commit-hooks.nix", | ||||||
|         "rev": "42587d3414d1747999a5f71e92a83cf6547b62da", |         "rev": "c5ac3aa3324bd8aebe8622a3fc92eeb3975d317a", | ||||||
|         "type": "github" |         "type": "github" | ||||||
|       }, |       }, | ||||||
|       "original": { |       "original": { | ||||||
|   | |||||||
							
								
								
									
										22
									
								
								flatpak/libdecor.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								flatpak/libdecor.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | { | ||||||
|  |     "name": "libdecor", | ||||||
|  |     "buildsystem": "meson", | ||||||
|  |     "config-opts": [ | ||||||
|  |         "-Ddemo=false" | ||||||
|  |     ], | ||||||
|  |     "sources": [ | ||||||
|  |         { | ||||||
|  |             "type": "git", | ||||||
|  |             "url": "https://gitlab.freedesktop.org/libdecor/libdecor.git", | ||||||
|  |             "commit": "73260393a97291c887e1074ab7f318e031be0ac6" | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             "type": "patch", | ||||||
|  |             "path": "patches/weird_libdecor.patch" | ||||||
|  |         } | ||||||
|  |     ], | ||||||
|  |     "cleanup": [ | ||||||
|  |         "/include", | ||||||
|  |         "/lib/pkgconfig" | ||||||
|  |     ] | ||||||
|  | } | ||||||
| @@ -5,13 +5,6 @@ sdk: org.kde.Sdk | |||||||
| sdk-extensions: | sdk-extensions: | ||||||
|   - org.freedesktop.Sdk.Extension.openjdk17 |   - org.freedesktop.Sdk.Extension.openjdk17 | ||||||
|   - org.freedesktop.Sdk.Extension.openjdk8 |   - org.freedesktop.Sdk.Extension.openjdk8 | ||||||
| add-extensions: |  | ||||||
|   com.valvesoftware.Steam.Utility.gamescope: |  | ||||||
|     version: stable |  | ||||||
|     add-ld-path: lib |  | ||||||
|     no-autodownload: true |  | ||||||
|     autodelete: false |  | ||||||
|     directory: utils/gamescope |  | ||||||
|  |  | ||||||
| command: prismlauncher | command: prismlauncher | ||||||
| finish-args: | finish-args: | ||||||
| @@ -25,22 +18,34 @@ finish-args: | |||||||
|   - --filesystem=xdg-run/app/com.discordapp.Discord:create |   - --filesystem=xdg-run/app/com.discordapp.Discord:create | ||||||
|     # Mod drag&drop |     # Mod drag&drop | ||||||
|   - --filesystem=xdg-download:ro |   - --filesystem=xdg-download:ro | ||||||
|  |     # FTBApp import | ||||||
|  |   - --filesystem=~/.ftba:ro | ||||||
|  |  | ||||||
|  | cleanup: | ||||||
|  |   - /lib/libGLU* | ||||||
|  |  | ||||||
| modules: | modules: | ||||||
|  |   # Might be needed by some Controller mods (see https://github.com/isXander/Controlify/issues/31) | ||||||
|  |   - shared-modules/libusb/libusb.json | ||||||
|  |  | ||||||
|  |   # Needed for proper Wayland support | ||||||
|  |   - libdecor.json | ||||||
|  |  | ||||||
|   - name: prismlauncher |   - name: prismlauncher | ||||||
|     buildsystem: cmake-ninja |     buildsystem: cmake-ninja | ||||||
|  |     builddir: true | ||||||
|     config-opts: |     config-opts: | ||||||
|       - -DLauncher_BUILD_PLATFORM=flatpak |       - -DLauncher_BUILD_PLATFORM=flatpak | ||||||
|       - -DCMAKE_BUILD_TYPE=Debug |       - -DCMAKE_BUILD_TYPE=RelWithDebInfo | ||||||
|       - -DLauncher_QT_VERSION_MAJOR=5 |       - -DLauncher_QT_VERSION_MAJOR=5 | ||||||
|     build-options: |     build-options: | ||||||
|       env: |       env: | ||||||
|         JAVA_HOME: /usr/lib/sdk/openjdk17/jvm/openjdk-17 |         JAVA_HOME: /usr/lib/sdk/openjdk17/jvm/openjdk-17 | ||||||
|         JAVA_COMPILER: /usr/lib/sdk/openjdk17/jvm/openjdk-17/bin/javac |         JAVA_COMPILER: /usr/lib/sdk/openjdk17/jvm/openjdk-17/bin/javac | ||||||
|     sources: |     sources: | ||||||
|         - type: dir |       - type: dir | ||||||
|           path: ../ |         path: ../ | ||||||
|     builddir: true |  | ||||||
|   - name: openjdk |   - name: openjdk | ||||||
|     buildsystem: simple |     buildsystem: simple | ||||||
|     build-commands: |     build-commands: | ||||||
| @@ -49,14 +54,45 @@ modules: | |||||||
|       - mv /app/jre /app/jdk/17 |       - mv /app/jre /app/jdk/17 | ||||||
|       - /usr/lib/sdk/openjdk8/install.sh |       - /usr/lib/sdk/openjdk8/install.sh | ||||||
|       - mv /app/jre /app/jdk/8 |       - mv /app/jre /app/jdk/8 | ||||||
|     cleanup: [/jre] |     cleanup: | ||||||
|  |       - /jre | ||||||
|  |  | ||||||
|  |   - name: glfw | ||||||
|  |     buildsystem: cmake-ninja | ||||||
|  |     config-opts: | ||||||
|  |       - -DCMAKE_BUILD_TYPE=RelWithDebInfo | ||||||
|  |       - -DBUILD_SHARED_LIBS:BOOL=ON | ||||||
|  |       - -DGLFW_USE_WAYLAND=ON | ||||||
|  |     sources: | ||||||
|  |       - type: git | ||||||
|  |         url: https://github.com/glfw/glfw.git | ||||||
|  |         commit: 3fa2360720eeba1964df3c0ecf4b5df8648a8e52 | ||||||
|  |       - type: patch | ||||||
|  |         path: patches/0003-Don-t-crash-on-calls-to-focus-or-icon.patch | ||||||
|  |       - type: patch | ||||||
|  |         path: patches/0005-Add-warning-about-being-an-unofficial-patch.patch | ||||||
|  |       - type: patch | ||||||
|  |         path: patches/0007-Platform-Prefer-Wayland-over-X11.patch | ||||||
|  |     cleanup: | ||||||
|  |       - /include | ||||||
|  |       - /lib/cmake | ||||||
|  |       - /lib/pkgconfig | ||||||
|  |  | ||||||
|   - name: xrandr |   - name: xrandr | ||||||
|     buildsystem: autotools |     buildsystem: autotools | ||||||
|     sources: |     sources: | ||||||
|       - type: archive |       - type: archive | ||||||
|         url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.1.tar.xz |         url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.2.tar.xz | ||||||
|         sha256: 7bc76daf9d72f8aff885efad04ce06b90488a1a169d118dea8a2b661832e8762 |         sha256: c8bee4790d9058bacc4b6246456c58021db58a87ddda1a9d0139bf5f18f1f240 | ||||||
|     cleanup: [/share/man, /bin/xkeystone] |         x-checker-data: | ||||||
|  |           type: anitya | ||||||
|  |           project-id: 14957 | ||||||
|  |           stable-only: true | ||||||
|  |           url-template: https://xorg.freedesktop.org/archive/individual/app/xrandr-$version.tar.xz | ||||||
|  |     cleanup: | ||||||
|  |       - /share/man | ||||||
|  |       - /bin/xkeystone | ||||||
|  |  | ||||||
|   - name: gamemode |   - name: gamemode | ||||||
|     buildsystem: meson |     buildsystem: meson | ||||||
|     config-opts: |     config-opts: | ||||||
| @@ -67,19 +103,56 @@ modules: | |||||||
|       # post-install is running inside the build dir, we need it from the source though |       # post-install is running inside the build dir, we need it from the source though | ||||||
|       - install -Dm755 ../data/gamemoderun -t /app/bin |       - install -Dm755 ../data/gamemoderun -t /app/bin | ||||||
|     sources: |     sources: | ||||||
|       - type: git |       - type: archive | ||||||
|         url: https://github.com/FeralInteractive/gamemode |         archive-type: tar-gzip | ||||||
|         tag: "1.7" |         url: https://api.github.com/repos/FeralInteractive/gamemode/tarball/1.7 | ||||||
|         commit: 4dc99dff76218718763a6b07fc1900fa6d1dafd9 |         sha256: 57ce73ba605d1cf12f8d13725006a895182308d93eba0f69f285648449641803 | ||||||
|  |         x-checker-data: | ||||||
|  |           type: json | ||||||
|  |           url: https://api.github.com/repos/FeralInteractive/gamemode/releases/latest | ||||||
|  |           version-query: .tag_name | ||||||
|  |           url-query: .tarball_url | ||||||
|  |           timestamp-query: .published_at | ||||||
|  |     cleanup: | ||||||
|  |       - /include | ||||||
|  |       - /lib/pkgconfig | ||||||
|  |       - /lib/libgamemodeauto.a | ||||||
|  |  | ||||||
|  |   - name: glxinfo | ||||||
|  |     buildsystem: meson | ||||||
|  |     config-opts: | ||||||
|  |       - --bindir=/app/mesa-demos | ||||||
|  |       - -Degl=disabled | ||||||
|  |       - -Dglut=disabled | ||||||
|  |       - -Dosmesa=disabled | ||||||
|  |       - -Dvulkan=disabled | ||||||
|  |       - -Dwayland=disabled | ||||||
|  |     post-install: | ||||||
|  |       - mv -v /app/mesa-demos/glxinfo /app/bin | ||||||
|  |     sources: | ||||||
|  |       - type: archive | ||||||
|  |         url: https://archive.mesa3d.org/demos/mesa-demos-9.0.0.tar.xz | ||||||
|  |         sha256: 3046a3d26a7b051af7ebdd257a5f23bfeb160cad6ed952329cdff1e9f1ed496b | ||||||
|  |         x-checker-data: | ||||||
|  |           type: anitya | ||||||
|  |           project-id: 16781 | ||||||
|  |           stable-only: true | ||||||
|  |           url-template: https://archive.mesa3d.org/demos/mesa-demos-$version.tar.xz | ||||||
|  |     cleanup: | ||||||
|  |       - /include | ||||||
|  |       - /mesa-demos | ||||||
|  |       - /share | ||||||
|  |     modules: | ||||||
|  |       - shared-modules/glu/glu-9.json | ||||||
|  |  | ||||||
|   - name: enhance |   - name: enhance | ||||||
|     buildsystem: simple |     buildsystem: simple | ||||||
|     build-commands: |     build-commands: | ||||||
|       - mkdir -p /app/utils/gamescope |  | ||||||
|       - install -Dm755 prime-run /app/bin/prime-run |       - install -Dm755 prime-run /app/bin/prime-run | ||||||
|       - mv /app/bin/prismlauncher /app/bin/prismrun |       - mv /app/bin/prismlauncher /app/bin/prismrun | ||||||
|       - install -Dm755 prismlauncher /app/bin/prismlauncher |       - install -Dm755 prismlauncher /app/bin/prismlauncher | ||||||
|     sources: |     sources: | ||||||
|       - type: file |       - type: file | ||||||
|         path: ../flatpak/prime-run |         path: prime-run | ||||||
|       - type: file |       - type: file | ||||||
|         path: ../flatpak/prismlauncher |         path: prismlauncher | ||||||
|   | |||||||
| @@ -0,0 +1,24 @@ | |||||||
|  | diff --git a/src/wl_window.c b/src/wl_window.c | ||||||
|  | index 52d3b9eb..4ac4eb5d 100644 | ||||||
|  | --- a/src/wl_window.c | ||||||
|  | +++ b/src/wl_window.c | ||||||
|  | @@ -2117,8 +2117,7 @@ void _glfwSetWindowTitleWayland(_GLFWwindow* window, const char* title) | ||||||
|  |  void _glfwSetWindowIconWayland(_GLFWwindow* window, | ||||||
|  |                                 int count, const GLFWimage* images) | ||||||
|  |  { | ||||||
|  | -    _glfwInputError(GLFW_FEATURE_UNAVAILABLE, | ||||||
|  | -                    "Wayland: The platform does not support setting the window icon"); | ||||||
|  | +    fprintf(stderr, "!!! Ignoring Error: Wayland: The platform does not support setting the window icon\n"); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  void _glfwGetWindowPosWayland(_GLFWwindow* window, int* xpos, int* ypos) | ||||||
|  | @@ -2361,8 +2360,7 @@ void _glfwRequestWindowAttentionWayland(_GLFWwindow* window) | ||||||
|  |   | ||||||
|  |  void _glfwFocusWindowWayland(_GLFWwindow* window) | ||||||
|  |  { | ||||||
|  | -    _glfwInputError(GLFW_FEATURE_UNAVAILABLE, | ||||||
|  | -                    "Wayland: The platform does not support setting the input focus"); | ||||||
|  | +    fprintf(stderr, "!!! Ignoring Error: Wayland: The platform does not support setting the input focus\n"); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  void _glfwSetWindowMonitorWayland(_GLFWwindow* window, | ||||||
| @@ -0,0 +1,17 @@ | |||||||
|  | diff --git a/src/init.c b/src/init.c | ||||||
|  | index 06dbb3f2..a7c6da86 100644 | ||||||
|  | --- a/src/init.c | ||||||
|  | +++ b/src/init.c | ||||||
|  | @@ -449,6 +449,12 @@ GLFWAPI int glfwInit(void) | ||||||
|  |      _glfw.initialized = GLFW_TRUE; | ||||||
|  |   | ||||||
|  |      glfwDefaultWindowHints(); | ||||||
|  | + | ||||||
|  | +    fprintf(stderr, "!!! Patched GLFW from https://github.com/Admicos/minecraft-wayland\n" | ||||||
|  | +         "!!! If any issues with the window, or some issues with rendering, occur, " | ||||||
|  | +         "first try with the built-in GLFW, and if that solves the issue, report there first.\n" | ||||||
|  | +         "!!! Use outside Minecraft is untested, and things might break.\n"); | ||||||
|  | + | ||||||
|  |      return GLFW_TRUE; | ||||||
|  |  } | ||||||
|  |   | ||||||
							
								
								
									
										20
									
								
								flatpak/patches/0007-Platform-Prefer-Wayland-over-X11.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								flatpak/patches/0007-Platform-Prefer-Wayland-over-X11.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | diff --git a/src/platform.c b/src/platform.c | ||||||
|  | index c5966ae7..3e7442f9 100644 | ||||||
|  | --- a/src/platform.c | ||||||
|  | +++ b/src/platform.c | ||||||
|  | @@ -49,12 +49,12 @@ static const struct | ||||||
|  |  #if defined(_GLFW_COCOA) | ||||||
|  |      { GLFW_PLATFORM_COCOA, _glfwConnectCocoa }, | ||||||
|  |  #endif | ||||||
|  | -#if defined(_GLFW_X11) | ||||||
|  | -    { GLFW_PLATFORM_X11, _glfwConnectX11 }, | ||||||
|  | -#endif | ||||||
|  |  #if defined(_GLFW_WAYLAND) | ||||||
|  |      { GLFW_PLATFORM_WAYLAND, _glfwConnectWayland }, | ||||||
|  |  #endif | ||||||
|  | +#if defined(_GLFW_X11) | ||||||
|  | +    { GLFW_PLATFORM_X11, _glfwConnectX11 }, | ||||||
|  | +#endif | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  GLFWbool _glfwSelectPlatform(int desiredID, _GLFWplatform* platform) | ||||||
							
								
								
									
										40
									
								
								flatpak/patches/weird_libdecor.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								flatpak/patches/weird_libdecor.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | diff --git a/src/libdecor.c b/src/libdecor.c | ||||||
|  | index a9c1106..1aa38b3 100644 | ||||||
|  | --- a/src/libdecor.c | ||||||
|  | +++ b/src/libdecor.c | ||||||
|  | @@ -1391,22 +1391,32 @@ calculate_priority(const struct libdecor_plugin_description *plugin_description) | ||||||
|  |  static bool | ||||||
|  |  check_symbol_conflicts(const struct libdecor_plugin_description *plugin_description) | ||||||
|  |  { | ||||||
|  | +	bool ret = true; | ||||||
|  |  	char * const *symbol; | ||||||
|  | +	void* main_prog = dlopen(NULL, RTLD_LAZY); | ||||||
|  | +	if (!main_prog) { | ||||||
|  | +		fprintf(stderr, "Plugin \"%s\" couldn't check conflicting symbols: \"%s\".\n", | ||||||
|  | +					plugin_description->description, dlerror()); | ||||||
|  | +		return false; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  |   | ||||||
|  |  	symbol = plugin_description->conflicting_symbols; | ||||||
|  |  	while (*symbol) { | ||||||
|  |  		dlerror(); | ||||||
|  | -		dlsym (RTLD_DEFAULT, *symbol); | ||||||
|  | +		dlsym (main_prog, *symbol); | ||||||
|  |  		if (!dlerror()) { | ||||||
|  |  			fprintf(stderr, "Plugin \"%s\" uses conflicting symbol \"%s\".\n", | ||||||
|  |  					plugin_description->description, *symbol); | ||||||
|  | -			return false; | ||||||
|  | +			ret = false; | ||||||
|  | +			break; | ||||||
|  |  		} | ||||||
|  |   | ||||||
|  |  		symbol++; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | -	return true; | ||||||
|  | +	dlclose(main_prog); | ||||||
|  | +	return ret; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static struct plugin_loader * | ||||||
| @@ -5,7 +5,7 @@ for i in {0..9}; do | |||||||
|     test -S "$XDG_RUNTIME_DIR"/discord-ipc-"$i" || ln -sf {app/com.discordapp.Discord,"$XDG_RUNTIME_DIR"}/discord-ipc-"$i"; |     test -S "$XDG_RUNTIME_DIR"/discord-ipc-"$i" || ln -sf {app/com.discordapp.Discord,"$XDG_RUNTIME_DIR"}/discord-ipc-"$i"; | ||||||
| done | done | ||||||
|  |  | ||||||
| export PATH="${PATH}${PATH:+:}/app/utils/gamescope/bin:/usr/lib/extensions/vulkan/MangoHud/bin" | export PATH="${PATH}${PATH:+:}/usr/lib/extensions/vulkan/gamescope/bin:/usr/lib/extensions/vulkan/MangoHud/bin" | ||||||
| export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}${LD_LIBRARY_PATH:+:}/usr/lib/extensions/vulkan/MangoHud/\$LIB/" | export VK_LAYER_PATH="/usr/lib/extensions/vulkan/share/vulkan/implicit_layer.d/" | ||||||
|  |  | ||||||
| exec /app/bin/prismrun "$@" | exec /app/bin/prismrun "$@" | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								flatpak/shared-modules
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								flatpak/shared-modules
									
									
									
									
									
										Submodule
									
								
							 Submodule flatpak/shared-modules added at 45094ca570
									
								
							
							
								
								
									
										6
									
								
								garnix.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								garnix.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | builds: | ||||||
|  |   exclude: [] | ||||||
|  |   include: | ||||||
|  |     - "checks.x86_64-linux.*" | ||||||
|  |     - "devShells.*.*" | ||||||
|  |     - "packages.*.*" | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -2,7 +2,7 @@ | |||||||
| /* | /* | ||||||
|  *  Prism Launcher - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  *  Copyright (C) 2022 Tayou <tayou@gmx.net> |  *  Copyright (C) 2022 Tayou <git@tayou.org> | ||||||
|  *  Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> |  *  Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
| @@ -38,16 +38,17 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <QApplication> | #include <QApplication> | ||||||
| #include <memory> | #include <QDateTime> | ||||||
| #include <QDebug> | #include <QDebug> | ||||||
| #include <QFlag> | #include <QFlag> | ||||||
| #include <QIcon> | #include <QIcon> | ||||||
| #include <QDateTime> |  | ||||||
| #include <QUrl> | #include <QUrl> | ||||||
|  | #include <memory> | ||||||
|  |  | ||||||
| #include <BaseInstance.h> | #include <BaseInstance.h> | ||||||
|  |  | ||||||
| #include "minecraft/launch/MinecraftServerTarget.h" | #include "minecraft/launch/MinecraftServerTarget.h" | ||||||
|  | #include "ui/themes/CatPack.h" | ||||||
|  |  | ||||||
| class LaunchController; | class LaunchController; | ||||||
| class LocalPeer; | class LocalPeer; | ||||||
| @@ -70,27 +71,22 @@ class TranslationsModel; | |||||||
| class ITheme; | class ITheme; | ||||||
| class MCEditTool; | class MCEditTool; | ||||||
| class ThemeManager; | class ThemeManager; | ||||||
|  | class IconTheme; | ||||||
|  |  | ||||||
| namespace Meta { | namespace Meta { | ||||||
|     class Index; | class Index; | ||||||
| } | } | ||||||
|  |  | ||||||
| #if defined(APPLICATION) | #if defined(APPLICATION) | ||||||
| #undef APPLICATION | #undef APPLICATION | ||||||
| #endif | #endif | ||||||
| #define APPLICATION (static_cast<Application *>(QCoreApplication::instance())) | #define APPLICATION (static_cast<Application*>(QCoreApplication::instance())) | ||||||
|  |  | ||||||
| class Application : public QApplication | class Application : public QApplication { | ||||||
| { |  | ||||||
|     // friends for the purpose of limiting access to deprecated stuff |     // friends for the purpose of limiting access to deprecated stuff | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| public: |    public: | ||||||
|     enum Status { |     enum Status { StartingUp, Failed, Succeeded, Initialized }; | ||||||
|         StartingUp, |  | ||||||
|         Failed, |  | ||||||
|         Succeeded, |  | ||||||
|         Initialized |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     enum Capability { |     enum Capability { | ||||||
|         None = 0, |         None = 0, | ||||||
| @@ -102,33 +98,21 @@ public: | |||||||
|     }; |     }; | ||||||
|     Q_DECLARE_FLAGS(Capabilities, Capability) |     Q_DECLARE_FLAGS(Capabilities, Capability) | ||||||
|  |  | ||||||
| public: |    public: | ||||||
|     Application(int &argc, char **argv); |     Application(int& argc, char** argv); | ||||||
|     virtual ~Application(); |     virtual ~Application(); | ||||||
|  |  | ||||||
|     bool event(QEvent* event) override; |     bool event(QEvent* event) override; | ||||||
|  |  | ||||||
|     std::shared_ptr<SettingsObject> settings() const { |     std::shared_ptr<SettingsObject> settings() const { return m_settings; } | ||||||
|         return m_settings; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     qint64 timeSinceStart() const { |     qint64 timeSinceStart() const { return startTime.msecsTo(QDateTime::currentDateTime()); } | ||||||
|         return startTime.msecsTo(QDateTime::currentDateTime()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     QIcon getThemedIcon(const QString& name); |     QIcon getThemedIcon(const QString& name); | ||||||
|  |  | ||||||
|     void setIconTheme(const QString& name); |     ThemeManager* themeManager() { return m_themeManager.get(); } | ||||||
|  |  | ||||||
|     void applyCurrentlySelectedTheme(bool initial = false); |     shared_qobject_ptr<ExternalUpdater> updater() { return m_updater; } | ||||||
|  |  | ||||||
|     QList<ITheme*> getValidApplicationThemes(); |  | ||||||
|  |  | ||||||
|     void setApplicationTheme(const QString& name); |  | ||||||
|  |  | ||||||
|     shared_qobject_ptr<ExternalUpdater> updater() { |  | ||||||
|         return m_updater; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void triggerUpdateCheck(); |     void triggerUpdateCheck(); | ||||||
|  |  | ||||||
| @@ -136,29 +120,17 @@ public: | |||||||
|  |  | ||||||
|     std::shared_ptr<JavaInstallList> javalist(); |     std::shared_ptr<JavaInstallList> javalist(); | ||||||
|  |  | ||||||
|     std::shared_ptr<InstanceList> instances() const { |     std::shared_ptr<InstanceList> instances() const { return m_instances; } | ||||||
|         return m_instances; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     std::shared_ptr<IconList> icons() const { |     std::shared_ptr<IconList> icons() const { return m_icons; } | ||||||
|         return m_icons; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     MCEditTool *mcedit() const { |     MCEditTool* mcedit() const { return m_mcedit.get(); } | ||||||
|         return m_mcedit.get(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     shared_qobject_ptr<AccountList> accounts() const { |     shared_qobject_ptr<AccountList> accounts() const { return m_accounts; } | ||||||
|         return m_accounts; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     Status status() const { |     Status status() const { return m_status; } | ||||||
|         return m_status; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     const QMap<QString, std::shared_ptr<BaseProfilerFactory>> &profilers() const { |     const QMap<QString, std::shared_ptr<BaseProfilerFactory>>& profilers() const { return m_profilers; } | ||||||
|         return m_profilers; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password); |     void updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password); | ||||||
|  |  | ||||||
| @@ -183,35 +155,31 @@ public: | |||||||
|     QString getUserAgentUncached(); |     QString getUserAgentUncached(); | ||||||
|  |  | ||||||
|     /// this is the root of the 'installation'. Used for automatic updates |     /// this is the root of the 'installation'. Used for automatic updates | ||||||
|     const QString &root() { |     const QString& root() { return m_rootPath; } | ||||||
|         return m_rootPath; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bool isPortable() { |     bool isPortable() { return m_portable; } | ||||||
|         return m_portable; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     const Capabilities capabilities() { |     const Capabilities capabilities() { return m_capabilities; } | ||||||
|         return m_capabilities; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /*! |     /*! | ||||||
|      * Opens a json file using either a system default editor, or, if not empty, the editor |      * Opens a json file using either a system default editor, or, if not empty, the editor | ||||||
|      * specified in the settings |      * specified in the settings | ||||||
|      */ |      */ | ||||||
|     bool openJsonEditor(const QString &filename); |     bool openJsonEditor(const QString& filename); | ||||||
|  |  | ||||||
|     InstanceWindow *showInstanceWindow(InstancePtr instance, QString page = QString()); |     InstanceWindow* showInstanceWindow(InstancePtr instance, QString page = QString()); | ||||||
|     MainWindow *showMainWindow(bool minimized = false); |     MainWindow* showMainWindow(bool minimized = false); | ||||||
|  |  | ||||||
|     void updateIsRunning(bool running); |     void updateIsRunning(bool running); | ||||||
|     bool updatesAreAllowed(); |     bool updatesAreAllowed(); | ||||||
|  |  | ||||||
|     void ShowGlobalSettings(class QWidget * parent, QString open_page = QString()); |     void ShowGlobalSettings(class QWidget* parent, QString open_page = QString()); | ||||||
|  |  | ||||||
|     int suitableMaxMem(); |     int suitableMaxMem(); | ||||||
|  |  | ||||||
| signals: |     QUrl normalizeImportUrl(QString const& url); | ||||||
|  |  | ||||||
|  |    signals: | ||||||
|     void updateAllowedChanged(bool status); |     void updateAllowedChanged(bool status); | ||||||
|     void globalSettingsAboutToOpen(); |     void globalSettingsAboutToOpen(); | ||||||
|     void globalSettingsClosed(); |     void globalSettingsClosed(); | ||||||
| @@ -221,39 +189,37 @@ signals: | |||||||
|     void clickedOnDock(); |     void clickedOnDock(); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| public slots: |    public slots: | ||||||
|     bool launch( |     bool launch(InstancePtr instance, | ||||||
|         InstancePtr instance, |                 bool online = true, | ||||||
|         bool online = true, |                 bool demo = false, | ||||||
|         bool demo = false, |                 BaseProfilerFactory* profiler = nullptr, | ||||||
|         BaseProfilerFactory *profiler = nullptr, |                 MinecraftServerTargetPtr serverToJoin = nullptr, | ||||||
|         MinecraftServerTargetPtr serverToJoin = nullptr, |                 MinecraftAccountPtr accountToUse = nullptr); | ||||||
|         MinecraftAccountPtr accountToUse = nullptr |  | ||||||
|     ); |  | ||||||
|     bool kill(InstancePtr instance); |     bool kill(InstancePtr instance); | ||||||
|     void closeCurrentWindow(); |     void closeCurrentWindow(); | ||||||
|  |  | ||||||
| private slots: |    private slots: | ||||||
|     void on_windowClose(); |     void on_windowClose(); | ||||||
|     void messageReceived(const QByteArray & message); |     void messageReceived(const QByteArray& message); | ||||||
|     void controllerSucceeded(); |     void controllerSucceeded(); | ||||||
|     void controllerFailed(const QString & error); |     void controllerFailed(const QString& error); | ||||||
|     void setupWizardFinished(int status); |     void setupWizardFinished(int status); | ||||||
|  |  | ||||||
| private: |    private: | ||||||
|     bool handleDataMigration(const QString & currentData, const QString & oldData, const QString & name, const QString & configFile) const; |     bool handleDataMigration(const QString& currentData, const QString& oldData, const QString& name, const QString& configFile) const; | ||||||
|     bool createSetupWizard(); |     bool createSetupWizard(); | ||||||
|     void performMainStartupAction(); |     void performMainStartupAction(); | ||||||
|  |  | ||||||
|     // sets the fatal error message and m_status to Failed. |     // sets the fatal error message and m_status to Failed. | ||||||
|     void showFatalErrorMessage(const QString & title, const QString & content); |     void showFatalErrorMessage(const QString& title, const QString& content); | ||||||
|  |  | ||||||
| private: |    private: | ||||||
|     void addRunningInstance(); |     void addRunningInstance(); | ||||||
|     void subRunningInstance(); |     void subRunningInstance(); | ||||||
|     bool shouldExitNow() const; |     bool shouldExitNow() const; | ||||||
|  |  | ||||||
| private: |    private: | ||||||
|     QDateTime startTime; |     QDateTime startTime; | ||||||
|  |  | ||||||
|     shared_qobject_ptr<QNetworkAccessManager> m_network; |     shared_qobject_ptr<QNetworkAccessManager> m_network; | ||||||
| @@ -279,7 +245,7 @@ private: | |||||||
|     QString m_rootPath; |     QString m_rootPath; | ||||||
|     Status m_status = Application::StartingUp; |     Status m_status = Application::StartingUp; | ||||||
|     Capabilities m_capabilities; |     Capabilities m_capabilities; | ||||||
|     bool m_portable = false;  |     bool m_portable = false; | ||||||
|  |  | ||||||
| #ifdef Q_OS_MACOS | #ifdef Q_OS_MACOS | ||||||
|     Qt::ApplicationState m_prevAppState = Qt::ApplicationInactive; |     Qt::ApplicationState m_prevAppState = Qt::ApplicationInactive; | ||||||
| @@ -292,7 +258,7 @@ private: | |||||||
|  |  | ||||||
|     // FIXME: attach to instances instead. |     // FIXME: attach to instances instead. | ||||||
|     struct InstanceXtras { |     struct InstanceXtras { | ||||||
|         InstanceWindow * window = nullptr; |         InstanceWindow* window = nullptr; | ||||||
|         shared_qobject_ptr<LaunchController> controller; |         shared_qobject_ptr<LaunchController> controller; | ||||||
|     }; |     }; | ||||||
|     std::map<QString, InstanceXtras> m_instanceExtras; |     std::map<QString, InstanceXtras> m_instanceExtras; | ||||||
| @@ -303,18 +269,19 @@ private: | |||||||
|     bool m_updateRunning = false; |     bool m_updateRunning = false; | ||||||
|  |  | ||||||
|     // main window, if any |     // main window, if any | ||||||
|     MainWindow * m_mainWindow = nullptr; |     MainWindow* m_mainWindow = nullptr; | ||||||
|  |  | ||||||
|     // peer launcher instance connector - used to implement single instance launcher and signalling |     // peer launcher instance connector - used to implement single instance launcher and signalling | ||||||
|     LocalPeer * m_peerInstance = nullptr; |     LocalPeer* m_peerInstance = nullptr; | ||||||
|  |  | ||||||
|     SetupWizard * m_setupWizard = nullptr; |     SetupWizard* m_setupWizard = nullptr; | ||||||
| public: |  | ||||||
|  |    public: | ||||||
|     QString m_instanceIdToLaunch; |     QString m_instanceIdToLaunch; | ||||||
|     QString m_serverToJoin; |     QString m_serverToJoin; | ||||||
|     QString m_profileToUse; |     QString m_profileToUse; | ||||||
|     bool m_liveCheck = false; |     bool m_liveCheck = false; | ||||||
|     QList<QUrl> m_zipsToImport; |     QList<QUrl> m_urlsToImport; | ||||||
|     QString m_instanceIdToShowWindowOf; |     QString m_instanceIdToShowWindowOf; | ||||||
|     std::unique_ptr<QFile> logFile; |     std::unique_ptr<QFile> logFile; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  PolyMC - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
| @@ -39,7 +39,8 @@ | |||||||
| #include <QJsonObject> | #include <QJsonObject> | ||||||
| #include "Json.h" | #include "Json.h" | ||||||
|  |  | ||||||
| void ApplicationMessage::parse(const QByteArray & input) { | void ApplicationMessage::parse(const QByteArray& input) | ||||||
|  | { | ||||||
|     auto doc = Json::requireDocument(input, "ApplicationMessage"); |     auto doc = Json::requireDocument(input, "ApplicationMessage"); | ||||||
|     auto root = Json::requireObject(doc, "ApplicationMessage"); |     auto root = Json::requireObject(doc, "ApplicationMessage"); | ||||||
|  |  | ||||||
| @@ -47,12 +48,13 @@ void ApplicationMessage::parse(const QByteArray & input) { | |||||||
|     args.clear(); |     args.clear(); | ||||||
|  |  | ||||||
|     auto parsedArgs = root.value("args").toObject(); |     auto parsedArgs = root.value("args").toObject(); | ||||||
|     for(auto iter = parsedArgs.constBegin(); iter != parsedArgs.constEnd(); iter++) { |     for (auto iter = parsedArgs.constBegin(); iter != parsedArgs.constEnd(); iter++) { | ||||||
|         args.insert(iter.key(), iter.value().toString()); |         args.insert(iter.key(), iter.value().toString()); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| QByteArray ApplicationMessage::serialize() { | QByteArray ApplicationMessage::serialize() | ||||||
|  | { | ||||||
|     QJsonObject root; |     QJsonObject root; | ||||||
|     root.insert("command", command); |     root.insert("command", command); | ||||||
|     QJsonObject outArgs; |     QJsonObject outArgs; | ||||||
|   | |||||||
| @@ -1,13 +1,13 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <QString> |  | ||||||
| #include <QHash> |  | ||||||
| #include <QByteArray> | #include <QByteArray> | ||||||
|  | #include <QHash> | ||||||
|  | #include <QString> | ||||||
|  |  | ||||||
| struct ApplicationMessage { | struct ApplicationMessage { | ||||||
|     QString command; |     QString command; | ||||||
|     QHash<QString, QString> args; |     QHash<QString, QString> args; | ||||||
|  |  | ||||||
|     QByteArray serialize(); |     QByteArray serialize(); | ||||||
|     void parse(const QByteArray & input); |     void parse(const QByteArray& input); | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -18,27 +18,21 @@ | |||||||
| #include "BaseInstaller.h" | #include "BaseInstaller.h" | ||||||
| #include "minecraft/MinecraftInstance.h" | #include "minecraft/MinecraftInstance.h" | ||||||
|  |  | ||||||
| BaseInstaller::BaseInstaller() | BaseInstaller::BaseInstaller() {} | ||||||
| { |  | ||||||
|  |  | ||||||
| } | bool BaseInstaller::isApplied(MinecraftInstance* on) | ||||||
|  |  | ||||||
| bool BaseInstaller::isApplied(MinecraftInstance *on) |  | ||||||
| { | { | ||||||
|     return QFile::exists(filename(on->instanceRoot())); |     return QFile::exists(filename(on->instanceRoot())); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool BaseInstaller::add(MinecraftInstance *to) | bool BaseInstaller::add(MinecraftInstance* to) | ||||||
| { | { | ||||||
|     if (!patchesDir(to->instanceRoot()).exists()) |     if (!patchesDir(to->instanceRoot()).exists()) { | ||||||
|     { |  | ||||||
|         QDir(to->instanceRoot()).mkdir("patches"); |         QDir(to->instanceRoot()).mkdir("patches"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (isApplied(to)) |     if (isApplied(to)) { | ||||||
|     { |         if (!remove(to)) { | ||||||
|         if (!remove(to)) |  | ||||||
|         { |  | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -46,16 +40,16 @@ bool BaseInstaller::add(MinecraftInstance *to) | |||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool BaseInstaller::remove(MinecraftInstance *from) | bool BaseInstaller::remove(MinecraftInstance* from) | ||||||
| { | { | ||||||
|     return QFile::remove(filename(from->instanceRoot())); |     return QFile::remove(filename(from->instanceRoot())); | ||||||
| } | } | ||||||
|  |  | ||||||
| QString BaseInstaller::filename(const QString &root) const | QString BaseInstaller::filename(const QString& root) const | ||||||
| { | { | ||||||
|     return patchesDir(root).absoluteFilePath(id() + ".json"); |     return patchesDir(root).absoluteFilePath(id() + ".json"); | ||||||
| } | } | ||||||
| QDir BaseInstaller::patchesDir(const QString &root) const | QDir BaseInstaller::patchesDir(const QString& root) const | ||||||
| { | { | ||||||
|     return QDir(root + "/patches/"); |     return QDir(root + "/patches/"); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -26,20 +26,19 @@ class QObject; | |||||||
| class Task; | class Task; | ||||||
| class BaseVersion; | class BaseVersion; | ||||||
|  |  | ||||||
| class BaseInstaller | class BaseInstaller { | ||||||
| { |    public: | ||||||
| public: |  | ||||||
|     BaseInstaller(); |     BaseInstaller(); | ||||||
|     virtual ~BaseInstaller(){}; |     virtual ~BaseInstaller(){}; | ||||||
|     bool isApplied(MinecraftInstance *on); |     bool isApplied(MinecraftInstance* on); | ||||||
|  |  | ||||||
|     virtual bool add(MinecraftInstance *to); |     virtual bool add(MinecraftInstance* to); | ||||||
|     virtual bool remove(MinecraftInstance *from); |     virtual bool remove(MinecraftInstance* from); | ||||||
|  |  | ||||||
|     virtual Task *createInstallTask(MinecraftInstance *instance, BaseVersion::Ptr version, QObject *parent) = 0; |     virtual Task* createInstallTask(MinecraftInstance* instance, BaseVersion::Ptr version, QObject* parent) = 0; | ||||||
|  |  | ||||||
| protected: |    protected: | ||||||
|     virtual QString id() const = 0; |     virtual QString id() const = 0; | ||||||
|     QString filename(const QString &root) const; |     QString filename(const QString& root) const; | ||||||
|     QDir patchesDir(const QString &root) const; |     QDir patchesDir(const QString& root) const; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  PolyMC - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  *  Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org> |  *  Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org> | ||||||
|  * |  * | ||||||
| @@ -36,23 +36,22 @@ | |||||||
|  |  | ||||||
| #include "BaseInstance.h" | #include "BaseInstance.h" | ||||||
|  |  | ||||||
| #include <QFileInfo> |  | ||||||
| #include <QDir> |  | ||||||
| #include <QDebug> | #include <QDebug> | ||||||
| #include <QRegularExpression> | #include <QDir> | ||||||
|  | #include <QFileInfo> | ||||||
| #include <QJsonDocument> | #include <QJsonDocument> | ||||||
| #include <QJsonObject> | #include <QJsonObject> | ||||||
|  | #include <QRegularExpression> | ||||||
|  |  | ||||||
| #include "settings/INISettingsObject.h" | #include "settings/INISettingsObject.h" | ||||||
| #include "settings/Setting.h" |  | ||||||
| #include "settings/OverrideSetting.h" | #include "settings/OverrideSetting.h" | ||||||
|  | #include "settings/Setting.h" | ||||||
|  |  | ||||||
| #include "FileSystem.h" |  | ||||||
| #include "Commandline.h" |  | ||||||
| #include "BuildConfig.h" | #include "BuildConfig.h" | ||||||
|  | #include "Commandline.h" | ||||||
|  | #include "FileSystem.h" | ||||||
|  |  | ||||||
| BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir) | BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir) : QObject() | ||||||
|     : QObject() |  | ||||||
| { | { | ||||||
|     m_settings = settings; |     m_settings = settings; | ||||||
|     m_global_settings = globalSettings; |     m_global_settings = globalSettings; | ||||||
| @@ -79,7 +78,7 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s | |||||||
|         m_settings->registerSetting("InstanceType", ""); |         m_settings->registerSetting("InstanceType", ""); | ||||||
|  |  | ||||||
|     // Custom Commands |     // Custom Commands | ||||||
|     auto commandSetting = m_settings->registerSetting({"OverrideCommands","OverrideLaunchCmd"}, false); |     auto commandSetting = m_settings->registerSetting({ "OverrideCommands", "OverrideLaunchCmd" }, false); | ||||||
|     m_settings->registerOverride(globalSettings->getSetting("PreLaunchCommand"), commandSetting); |     m_settings->registerOverride(globalSettings->getSetting("PreLaunchCommand"), commandSetting); | ||||||
|     m_settings->registerOverride(globalSettings->getSetting("WrapperCommand"), commandSetting); |     m_settings->registerOverride(globalSettings->getSetting("WrapperCommand"), commandSetting); | ||||||
|     m_settings->registerOverride(globalSettings->getSetting("PostExitCommand"), commandSetting); |     m_settings->registerOverride(globalSettings->getSetting("PostExitCommand"), commandSetting); | ||||||
| @@ -148,7 +147,11 @@ QString BaseInstance::getManagedPackVersionName() const | |||||||
|     return m_settings->get("ManagedPackVersionName").toString(); |     return m_settings->get("ManagedPackVersionName").toString(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void BaseInstance::setManagedPack(const QString& type, const QString& id, const QString& name, const QString& versionId, const QString& version) | void BaseInstance::setManagedPack(const QString& type, | ||||||
|  |                                   const QString& id, | ||||||
|  |                                   const QString& name, | ||||||
|  |                                   const QString& versionId, | ||||||
|  |                                   const QString& version) | ||||||
| { | { | ||||||
|     m_settings->set("ManagedPack", true); |     m_settings->set("ManagedPack", true); | ||||||
|     m_settings->set("ManagedPackType", type); |     m_settings->set("ManagedPackType", type); | ||||||
| @@ -173,8 +176,7 @@ int BaseInstance::getConsoleMaxLines() const | |||||||
|     auto lineSetting = m_settings->getSetting("ConsoleMaxLines"); |     auto lineSetting = m_settings->getSetting("ConsoleMaxLines"); | ||||||
|     bool conversionOk = false; |     bool conversionOk = false; | ||||||
|     int maxLines = lineSetting->get().toInt(&conversionOk); |     int maxLines = lineSetting->get().toInt(&conversionOk); | ||||||
|     if(!conversionOk) |     if (!conversionOk) { | ||||||
|     { |  | ||||||
|         maxLines = lineSetting->defValue().toInt(); |         maxLines = lineSetting->defValue().toInt(); | ||||||
|         qWarning() << "ConsoleMaxLines has nonsensical value, defaulting to" << maxLines; |         qWarning() << "ConsoleMaxLines has nonsensical value, defaulting to" << maxLines; | ||||||
|     } |     } | ||||||
| @@ -220,8 +222,7 @@ bool BaseInstance::isLinkedToInstanceId(const QString& id) const | |||||||
|  |  | ||||||
| void BaseInstance::iconUpdated(QString key) | void BaseInstance::iconUpdated(QString key) | ||||||
| { | { | ||||||
|     if(iconKey() == key) |     if (iconKey() == key) { | ||||||
|     { |  | ||||||
|         emit propertiesChanged(this); |         emit propertiesChanged(this); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -235,8 +236,7 @@ void BaseInstance::invalidate() | |||||||
| void BaseInstance::changeStatus(BaseInstance::Status newStatus) | void BaseInstance::changeStatus(BaseInstance::Status newStatus) | ||||||
| { | { | ||||||
|     Status status = currentStatus(); |     Status status = currentStatus(); | ||||||
|     if(status != newStatus) |     if (status != newStatus) { | ||||||
|     { |  | ||||||
|         m_status = newStatus; |         m_status = newStatus; | ||||||
|         emit statusChanged(status, newStatus); |         emit statusChanged(status, newStatus); | ||||||
|     } |     } | ||||||
| @@ -259,23 +259,19 @@ bool BaseInstance::isRunning() const | |||||||
|  |  | ||||||
| void BaseInstance::setRunning(bool running) | void BaseInstance::setRunning(bool running) | ||||||
| { | { | ||||||
|     if(running == m_isRunning) |     if (running == m_isRunning) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|     m_isRunning = running; |     m_isRunning = running; | ||||||
|  |  | ||||||
|     if(!m_settings->get("RecordGameTime").toBool()) |     if (!m_settings->get("RecordGameTime").toBool()) { | ||||||
|     { |  | ||||||
|         emit runningStatusChanged(running); |         emit runningStatusChanged(running); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if(running) |     if (running) { | ||||||
|     { |  | ||||||
|         m_timeStarted = QDateTime::currentDateTime(); |         m_timeStarted = QDateTime::currentDateTime(); | ||||||
|     } |     } else { | ||||||
|     else |  | ||||||
|     { |  | ||||||
|         QDateTime timeEnded = QDateTime::currentDateTime(); |         QDateTime timeEnded = QDateTime::currentDateTime(); | ||||||
|  |  | ||||||
|         qint64 current = settings()->get("totalTimePlayed").toLongLong(); |         qint64 current = settings()->get("totalTimePlayed").toLongLong(); | ||||||
| @@ -291,8 +287,7 @@ void BaseInstance::setRunning(bool running) | |||||||
| int64_t BaseInstance::totalTimePlayed() const | int64_t BaseInstance::totalTimePlayed() const | ||||||
| { | { | ||||||
|     qint64 current = m_settings->get("totalTimePlayed").toLongLong(); |     qint64 current = m_settings->get("totalTimePlayed").toLongLong(); | ||||||
|     if(m_isRunning) |     if (m_isRunning) { | ||||||
|     { |  | ||||||
|         QDateTime timeNow = QDateTime::currentDateTime(); |         QDateTime timeNow = QDateTime::currentDateTime(); | ||||||
|         return current + m_timeStarted.secsTo(timeNow); |         return current + m_timeStarted.secsTo(timeNow); | ||||||
|     } |     } | ||||||
| @@ -301,8 +296,7 @@ int64_t BaseInstance::totalTimePlayed() const | |||||||
|  |  | ||||||
| int64_t BaseInstance::lastTimePlayed() const | int64_t BaseInstance::lastTimePlayed() const | ||||||
| { | { | ||||||
|     if(m_isRunning) |     if (m_isRunning) { | ||||||
|     { |  | ||||||
|         QDateTime timeNow = QDateTime::currentDateTime(); |         QDateTime timeNow = QDateTime::currentDateTime(); | ||||||
|         return m_timeStarted.secsTo(timeNow); |         return m_timeStarted.secsTo(timeNow); | ||||||
|     } |     } | ||||||
| @@ -349,14 +343,14 @@ qint64 BaseInstance::lastLaunch() const | |||||||
|  |  | ||||||
| void BaseInstance::setLastLaunch(qint64 val) | void BaseInstance::setLastLaunch(qint64 val) | ||||||
| { | { | ||||||
|     //FIXME: if no change, do not set. setting involves saving a file. |     // FIXME: if no change, do not set. setting involves saving a file. | ||||||
|     m_settings->set("lastLaunchTime", val); |     m_settings->set("lastLaunchTime", val); | ||||||
|     emit propertiesChanged(this); |     emit propertiesChanged(this); | ||||||
| } | } | ||||||
|  |  | ||||||
| void BaseInstance::setNotes(QString val) | void BaseInstance::setNotes(QString val) | ||||||
| { | { | ||||||
|     //FIXME: if no change, do not set. setting involves saving a file. |     // FIXME: if no change, do not set. setting involves saving a file. | ||||||
|     m_settings->set("notes", val); |     m_settings->set("notes", val); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -367,7 +361,7 @@ QString BaseInstance::notes() const | |||||||
|  |  | ||||||
| void BaseInstance::setIconKey(QString val) | void BaseInstance::setIconKey(QString val) | ||||||
| { | { | ||||||
|     //FIXME: if no change, do not set. setting involves saving a file. |     // FIXME: if no change, do not set. setting involves saving a file. | ||||||
|     m_settings->set("iconKey", val); |     m_settings->set("iconKey", val); | ||||||
|     emit propertiesChanged(this); |     emit propertiesChanged(this); | ||||||
| } | } | ||||||
| @@ -379,7 +373,7 @@ QString BaseInstance::iconKey() const | |||||||
|  |  | ||||||
| void BaseInstance::setName(QString val) | void BaseInstance::setName(QString val) | ||||||
| { | { | ||||||
|     //FIXME: if no change, do not set. setting involves saving a file. |     // FIXME: if no change, do not set. setting involves saving a file. | ||||||
|     m_settings->set("name", val); |     m_settings->set("name", val); | ||||||
|     emit propertiesChanged(this); |     emit propertiesChanged(this); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  PolyMC - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  *  Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org> |  *  Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org> | ||||||
|  * |  * | ||||||
| @@ -37,24 +37,24 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include <cassert> | #include <cassert> | ||||||
|  |  | ||||||
| #include <QObject> |  | ||||||
| #include "QObjectPtr.h" |  | ||||||
| #include <QDateTime> | #include <QDateTime> | ||||||
| #include <QSet> | #include <QObject> | ||||||
| #include <QProcess> | #include <QProcess> | ||||||
|  | #include <QSet> | ||||||
|  | #include "QObjectPtr.h" | ||||||
|  |  | ||||||
| #include "settings/SettingsObject.h" | #include "settings/SettingsObject.h" | ||||||
|  |  | ||||||
| #include "settings/INIFile.h" |  | ||||||
| #include "BaseVersionList.h" | #include "BaseVersionList.h" | ||||||
| #include "minecraft/auth/MinecraftAccount.h" |  | ||||||
| #include "MessageLevel.h" | #include "MessageLevel.h" | ||||||
|  | #include "minecraft/auth/MinecraftAccount.h" | ||||||
| #include "pathmatcher/IPathMatcher.h" | #include "pathmatcher/IPathMatcher.h" | ||||||
|  | #include "settings/INIFile.h" | ||||||
|  |  | ||||||
| #include "net/Mode.h" | #include "net/Mode.h" | ||||||
|  |  | ||||||
| #include "minecraft/launch/MinecraftServerTarget.h" |  | ||||||
| #include "RuntimeContext.h" | #include "RuntimeContext.h" | ||||||
|  | #include "minecraft/launch/MinecraftServerTarget.h" | ||||||
|  |  | ||||||
| class QDir; | class QDir; | ||||||
| class Task; | class Task; | ||||||
| @@ -72,23 +72,21 @@ typedef std::shared_ptr<BaseInstance> InstancePtr; | |||||||
|  * To create a new instance type, create a new class inheriting from this class |  * To create a new instance type, create a new class inheriting from this class | ||||||
|  * and implement the pure virtual functions. |  * and implement the pure virtual functions. | ||||||
|  */ |  */ | ||||||
| class BaseInstance : public QObject, public std::enable_shared_from_this<BaseInstance> | class BaseInstance : public QObject, public std::enable_shared_from_this<BaseInstance> { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| protected: |    protected: | ||||||
|     /// no-touchy! |     /// no-touchy! | ||||||
|     BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir); |     BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir); | ||||||
|  |  | ||||||
| public: /* types */ |    public: /* types */ | ||||||
|     enum class Status |     enum class Status { | ||||||
|     { |  | ||||||
|         Present, |         Present, | ||||||
|         Gone // either nuked or invalidated |         Gone  // either nuked or invalidated | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
| public: |    public: | ||||||
|     /// virtual destructor to make sure the destruction is COMPLETE |     /// virtual destructor to make sure the destruction is COMPLETE | ||||||
|     virtual ~BaseInstance() {}; |     virtual ~BaseInstance() {} | ||||||
|  |  | ||||||
|     virtual void saveNow() = 0; |     virtual void saveNow() = 0; | ||||||
|  |  | ||||||
| @@ -117,10 +115,7 @@ public: | |||||||
|     QString instanceRoot() const; |     QString instanceRoot() const; | ||||||
|  |  | ||||||
|     /// Path to the instance's game root directory. |     /// Path to the instance's game root directory. | ||||||
|     virtual QString gameRoot() const |     virtual QString gameRoot() const { return instanceRoot(); } | ||||||
|     { |  | ||||||
|         return instanceRoot(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// Path to the instance's mods directory. |     /// Path to the instance's mods directory. | ||||||
|     virtual QString modsRoot() const = 0; |     virtual QString modsRoot() const = 0; | ||||||
| @@ -151,15 +146,12 @@ public: | |||||||
|     void copyManagedPack(BaseInstance& other); |     void copyManagedPack(BaseInstance& other); | ||||||
|  |  | ||||||
|     /// guess log level from a line of game log |     /// guess log level from a line of game log | ||||||
|     virtual MessageLevel::Enum guessLevel([[maybe_unused]] const QString &line, MessageLevel::Enum level) |     virtual MessageLevel::Enum guessLevel([[maybe_unused]] const QString& line, MessageLevel::Enum level) { return level; } | ||||||
|     { |  | ||||||
|         return level; |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     virtual QStringList extraArguments(); |     virtual QStringList extraArguments(); | ||||||
|  |  | ||||||
|     /// Traits. Normally inside the version, depends on instance implementation. |     /// Traits. Normally inside the version, depends on instance implementation. | ||||||
|     virtual QSet <QString> traits() const = 0; |     virtual QSet<QString> traits() const = 0; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Gets the time that the instance was last launched. |      * Gets the time that the instance was last launched. | ||||||
| @@ -189,8 +181,7 @@ public: | |||||||
|     virtual Task::Ptr createUpdateTask(Net::Mode mode) = 0; |     virtual Task::Ptr createUpdateTask(Net::Mode mode) = 0; | ||||||
|  |  | ||||||
|     /// returns a valid launcher (task container) |     /// returns a valid launcher (task container) | ||||||
|     virtual shared_qobject_ptr<LaunchTask> createLaunchTask( |     virtual shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) = 0; | ||||||
|             AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) = 0; |  | ||||||
|  |  | ||||||
|     /// returns the current launch task (if any) |     /// returns the current launch task (if any) | ||||||
|     shared_qobject_ptr<LaunchTask> getLaunchTask(); |     shared_qobject_ptr<LaunchTask> getLaunchTask(); | ||||||
| @@ -222,45 +213,30 @@ public: | |||||||
|     virtual QString typeName() const = 0; |     virtual QString typeName() const = 0; | ||||||
|  |  | ||||||
|     void updateRuntimeContext(); |     void updateRuntimeContext(); | ||||||
|     RuntimeContext runtimeContext() const |     RuntimeContext runtimeContext() const { return m_runtimeContext; } | ||||||
|     { |  | ||||||
|         return m_runtimeContext; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bool hasVersionBroken() const |     bool hasVersionBroken() const { return m_hasBrokenVersion; } | ||||||
|     { |  | ||||||
|         return m_hasBrokenVersion; |  | ||||||
|     } |  | ||||||
|     void setVersionBroken(bool value) |     void setVersionBroken(bool value) | ||||||
|     { |     { | ||||||
|         if(m_hasBrokenVersion != value) |         if (m_hasBrokenVersion != value) { | ||||||
|         { |  | ||||||
|             m_hasBrokenVersion = value; |             m_hasBrokenVersion = value; | ||||||
|             emit propertiesChanged(this); |             emit propertiesChanged(this); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool hasUpdateAvailable() const |     bool hasUpdateAvailable() const { return m_hasUpdate; } | ||||||
|     { |  | ||||||
|         return m_hasUpdate; |  | ||||||
|     } |  | ||||||
|     void setUpdateAvailable(bool value) |     void setUpdateAvailable(bool value) | ||||||
|     { |     { | ||||||
|         if(m_hasUpdate != value) |         if (m_hasUpdate != value) { | ||||||
|         { |  | ||||||
|             m_hasUpdate = value; |             m_hasUpdate = value; | ||||||
|             emit propertiesChanged(this); |             emit propertiesChanged(this); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool hasCrashed() const |     bool hasCrashed() const { return m_crashed; } | ||||||
|     { |  | ||||||
|         return m_crashed; |  | ||||||
|     } |  | ||||||
|     void setCrashed(bool value) |     void setCrashed(bool value) | ||||||
|     { |     { | ||||||
|         if(m_crashed != value) |         if (m_crashed != value) { | ||||||
|         { |  | ||||||
|             m_crashed = value; |             m_crashed = value; | ||||||
|             emit propertiesChanged(this); |             emit propertiesChanged(this); | ||||||
|         } |         } | ||||||
| @@ -288,19 +264,19 @@ public: | |||||||
|     bool removeLinkedInstanceId(const QString& id); |     bool removeLinkedInstanceId(const QString& id); | ||||||
|     bool isLinkedToInstanceId(const QString& id) const; |     bool isLinkedToInstanceId(const QString& id) const; | ||||||
|  |  | ||||||
| protected: |    protected: | ||||||
|     void changeStatus(Status newStatus); |     void changeStatus(Status newStatus); | ||||||
|  |  | ||||||
|     SettingsObjectPtr globalSettings() const { return m_global_settings.lock(); }; |     SettingsObjectPtr globalSettings() const { return m_global_settings.lock(); } | ||||||
|  |  | ||||||
|     bool isSpecificSettingsLoaded() const { return m_specific_settings_loaded; } |     bool isSpecificSettingsLoaded() const { return m_specific_settings_loaded; } | ||||||
|     void setSpecificSettingsLoaded(bool loaded) { m_specific_settings_loaded = loaded; } |     void setSpecificSettingsLoaded(bool loaded) { m_specific_settings_loaded = loaded; } | ||||||
|  |  | ||||||
| signals: |    signals: | ||||||
|     /*! |     /*! | ||||||
|      * \brief Signal emitted when properties relevant to the instance view change |      * \brief Signal emitted when properties relevant to the instance view change | ||||||
|      */ |      */ | ||||||
|     void propertiesChanged(BaseInstance *inst); |     void propertiesChanged(BaseInstance* inst); | ||||||
|  |  | ||||||
|     void launchTaskChanged(shared_qobject_ptr<LaunchTask>); |     void launchTaskChanged(shared_qobject_ptr<LaunchTask>); | ||||||
|  |  | ||||||
| @@ -308,10 +284,10 @@ signals: | |||||||
|  |  | ||||||
|     void statusChanged(Status from, Status to); |     void statusChanged(Status from, Status to); | ||||||
|  |  | ||||||
| protected slots: |    protected slots: | ||||||
|     void iconUpdated(QString key); |     void iconUpdated(QString key); | ||||||
|  |  | ||||||
| protected: /* data */ |    protected: /* data */ | ||||||
|     QString m_rootDir; |     QString m_rootDir; | ||||||
|     SettingsObjectPtr m_settings; |     SettingsObjectPtr m_settings; | ||||||
|     // InstanceFlags m_flags; |     // InstanceFlags m_flags; | ||||||
| @@ -320,7 +296,7 @@ protected: /* data */ | |||||||
|     QDateTime m_timeStarted; |     QDateTime m_timeStarted; | ||||||
|     RuntimeContext m_runtimeContext; |     RuntimeContext m_runtimeContext; | ||||||
|  |  | ||||||
| private: /* data */ |    private: /* data */ | ||||||
|     Status m_status = Status::Present; |     Status m_status = Status::Present; | ||||||
|     bool m_crashed = false; |     bool m_crashed = false; | ||||||
|     bool m_hasUpdate = false; |     bool m_hasUpdate = false; | ||||||
| @@ -328,9 +304,8 @@ private: /* data */ | |||||||
|  |  | ||||||
|     SettingsObjectWeakPtr m_global_settings; |     SettingsObjectWeakPtr m_global_settings; | ||||||
|     bool m_specific_settings_loaded = false; |     bool m_specific_settings_loaded = false; | ||||||
|  |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| Q_DECLARE_METATYPE(shared_qobject_ptr<BaseInstance>) | Q_DECLARE_METATYPE(shared_qobject_ptr<BaseInstance>) | ||||||
| //Q_DECLARE_METATYPE(BaseInstance::InstanceFlag) | // Q_DECLARE_METATYPE(BaseInstance::InstanceFlag) | ||||||
| //Q_DECLARE_OPERATORS_FOR_FLAGS(BaseInstance::InstanceFlags) | // Q_DECLARE_OPERATORS_FOR_FLAGS(BaseInstance::InstanceFlags) | ||||||
|   | |||||||
| @@ -15,16 +15,15 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <memory> |  | ||||||
| #include <QString> |  | ||||||
| #include <QMetaType> | #include <QMetaType> | ||||||
|  | #include <QString> | ||||||
|  | #include <memory> | ||||||
|  |  | ||||||
| /*! | /*! | ||||||
|  * An abstract base class for versions. |  * An abstract base class for versions. | ||||||
|  */ |  */ | ||||||
| class BaseVersion | class BaseVersion { | ||||||
| { |    public: | ||||||
| public: |  | ||||||
|     using Ptr = std::shared_ptr<BaseVersion>; |     using Ptr = std::shared_ptr<BaseVersion>; | ||||||
|     virtual ~BaseVersion() {} |     virtual ~BaseVersion() {} | ||||||
|     /*! |     /*! | ||||||
| @@ -44,15 +43,8 @@ public: | |||||||
|      * the kind of version this is (Stable, Beta, Snapshot, whatever) |      * the kind of version this is (Stable, Beta, Snapshot, whatever) | ||||||
|      */ |      */ | ||||||
|     virtual QString typeString() const = 0; |     virtual QString typeString() const = 0; | ||||||
|  |     virtual bool operator<(BaseVersion& a) { return name() < a.name(); } | ||||||
|     virtual bool operator<(BaseVersion &a) |     virtual bool operator>(BaseVersion& a) { return name() > a.name(); } | ||||||
|     { |  | ||||||
|         return name() < a.name(); |  | ||||||
|     }; |  | ||||||
|     virtual bool operator>(BaseVersion &a) |  | ||||||
|     { |  | ||||||
|         return name() > a.name(); |  | ||||||
|     }; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| Q_DECLARE_METATYPE(BaseVersion::Ptr) | Q_DECLARE_METATYPE(BaseVersion::Ptr) | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  PolyMC - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
| @@ -36,14 +36,11 @@ | |||||||
| #include "BaseVersionList.h" | #include "BaseVersionList.h" | ||||||
| #include "BaseVersion.h" | #include "BaseVersion.h" | ||||||
|  |  | ||||||
| BaseVersionList::BaseVersionList(QObject *parent) : QAbstractListModel(parent) | BaseVersionList::BaseVersionList(QObject* parent) : QAbstractListModel(parent) {} | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| BaseVersion::Ptr BaseVersionList::findVersion(const QString &descriptor) | BaseVersion::Ptr BaseVersionList::findVersion(const QString& descriptor) | ||||||
| { | { | ||||||
|     for (int i = 0; i < count(); i++) |     for (int i = 0; i < count(); i++) { | ||||||
|     { |  | ||||||
|         if (at(i)->descriptor() == descriptor) |         if (at(i)->descriptor() == descriptor) | ||||||
|             return at(i); |             return at(i); | ||||||
|     } |     } | ||||||
| @@ -58,7 +55,7 @@ BaseVersion::Ptr BaseVersionList::getRecommended() const | |||||||
|         return at(0); |         return at(0); | ||||||
| } | } | ||||||
|  |  | ||||||
| QVariant BaseVersionList::data(const QModelIndex &index, int role) const | QVariant BaseVersionList::data(const QModelIndex& index, int role) const | ||||||
| { | { | ||||||
|     if (!index.isValid()) |     if (!index.isValid()) | ||||||
|         return QVariant(); |         return QVariant(); | ||||||
| @@ -68,37 +65,36 @@ QVariant BaseVersionList::data(const QModelIndex &index, int role) const | |||||||
|  |  | ||||||
|     BaseVersion::Ptr version = at(index.row()); |     BaseVersion::Ptr version = at(index.row()); | ||||||
|  |  | ||||||
|     switch (role) |     switch (role) { | ||||||
|     { |         case VersionPointerRole: | ||||||
|     case VersionPointerRole: |             return QVariant::fromValue(version); | ||||||
|         return QVariant::fromValue(version); |  | ||||||
|  |  | ||||||
|     case VersionRole: |         case VersionRole: | ||||||
|         return version->name(); |             return version->name(); | ||||||
|  |  | ||||||
|     case VersionIdRole: |         case VersionIdRole: | ||||||
|         return version->descriptor(); |             return version->descriptor(); | ||||||
|  |  | ||||||
|     case TypeRole: |         case TypeRole: | ||||||
|         return version->typeString(); |             return version->typeString(); | ||||||
|  |  | ||||||
|     default: |         default: | ||||||
|         return QVariant(); |             return QVariant(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| BaseVersionList::RoleList BaseVersionList::providesRoles() const | BaseVersionList::RoleList BaseVersionList::providesRoles() const | ||||||
| { | { | ||||||
|     return {VersionPointerRole, VersionRole, VersionIdRole, TypeRole}; |     return { VersionPointerRole, VersionRole, VersionIdRole, TypeRole }; | ||||||
| } | } | ||||||
|  |  | ||||||
| int BaseVersionList::rowCount(const QModelIndex &parent) const | int BaseVersionList::rowCount(const QModelIndex& parent) const | ||||||
| { | { | ||||||
|     // Return count |     // Return count | ||||||
|     return parent.isValid() ? 0 : count(); |     return parent.isValid() ? 0 : count(); | ||||||
| } | } | ||||||
|  |  | ||||||
| int BaseVersionList::columnCount(const QModelIndex &parent) const | int BaseVersionList::columnCount(const QModelIndex& parent) const | ||||||
| { | { | ||||||
|     return parent.isValid() ? 0 : 1; |     return parent.isValid() ? 0 : 1; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -15,13 +15,13 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
|  | #include <QAbstractListModel> | ||||||
| #include <QObject> | #include <QObject> | ||||||
| #include <QVariant> | #include <QVariant> | ||||||
| #include <QAbstractListModel> |  | ||||||
|  |  | ||||||
| #include "BaseVersion.h" | #include "BaseVersion.h" | ||||||
| #include "tasks/Task.h" |  | ||||||
| #include "QObjectPtr.h" | #include "QObjectPtr.h" | ||||||
|  | #include "tasks/Task.h" | ||||||
|  |  | ||||||
| /*! | /*! | ||||||
|  * \brief Class that each instance type's version list derives from. |  * \brief Class that each instance type's version list derives from. | ||||||
| @@ -35,12 +35,10 @@ | |||||||
|  * all have a default implementation, but they can be overridden by plugins to |  * all have a default implementation, but they can be overridden by plugins to | ||||||
|  * change the behavior of the list. |  * change the behavior of the list. | ||||||
|  */ |  */ | ||||||
| class BaseVersionList : public QAbstractListModel | class BaseVersionList : public QAbstractListModel { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| public: |    public: | ||||||
|     enum ModelRoles |     enum ModelRoles { | ||||||
|     { |  | ||||||
|         VersionPointerRole = Qt::UserRole, |         VersionPointerRole = Qt::UserRole, | ||||||
|         VersionRole, |         VersionRole, | ||||||
|         VersionIdRole, |         VersionIdRole, | ||||||
| @@ -55,7 +53,7 @@ public: | |||||||
|     }; |     }; | ||||||
|     typedef QList<int> RoleList; |     typedef QList<int> RoleList; | ||||||
|  |  | ||||||
|     explicit BaseVersionList(QObject *parent = 0); |     explicit BaseVersionList(QObject* parent = 0); | ||||||
|  |  | ||||||
|     /*! |     /*! | ||||||
|      * \brief Gets a task that will reload the version list. |      * \brief Gets a task that will reload the version list. | ||||||
| @@ -66,7 +64,7 @@ public: | |||||||
|     virtual Task::Ptr getLoadTask() = 0; |     virtual Task::Ptr getLoadTask() = 0; | ||||||
|  |  | ||||||
|     //! Checks whether or not the list is loaded. If this returns false, the list should be |     //! Checks whether or not the list is loaded. If this returns false, the list should be | ||||||
|     //loaded. |     // loaded. | ||||||
|     virtual bool isLoaded() = 0; |     virtual bool isLoaded() = 0; | ||||||
|  |  | ||||||
|     //! Gets the version at the given index. |     //! Gets the version at the given index. | ||||||
| @@ -76,9 +74,9 @@ public: | |||||||
|     virtual int count() const = 0; |     virtual int count() const = 0; | ||||||
|  |  | ||||||
|     //////// List Model Functions //////// |     //////// List Model Functions //////// | ||||||
|     QVariant data(const QModelIndex &index, int role) const override; |     QVariant data(const QModelIndex& index, int role) const override; | ||||||
|     int rowCount(const QModelIndex &parent) const override; |     int rowCount(const QModelIndex& parent) const override; | ||||||
|     int columnCount(const QModelIndex &parent) const override; |     int columnCount(const QModelIndex& parent) const override; | ||||||
|     QHash<int, QByteArray> roleNames() const override; |     QHash<int, QByteArray> roleNames() const override; | ||||||
|  |  | ||||||
|     //! which roles are provided by this version list? |     //! which roles are provided by this version list? | ||||||
| @@ -90,7 +88,7 @@ public: | |||||||
|      * \return A const pointer to the version with the given descriptor. NULL if |      * \return A const pointer to the version with the given descriptor. NULL if | ||||||
|      * one doesn't exist. |      * one doesn't exist. | ||||||
|      */ |      */ | ||||||
|     virtual BaseVersion::Ptr findVersion(const QString &descriptor); |     virtual BaseVersion::Ptr findVersion(const QString& descriptor); | ||||||
|  |  | ||||||
|     /*! |     /*! | ||||||
|      * \brief Gets the recommended version from this list |      * \brief Gets the recommended version from this list | ||||||
| @@ -103,8 +101,7 @@ public: | |||||||
|      */ |      */ | ||||||
|     virtual void sortVersions() = 0; |     virtual void sortVersions() = 0; | ||||||
|  |  | ||||||
| protected |    protected slots: | ||||||
| slots: |  | ||||||
|     /*! |     /*! | ||||||
|      * Updates this list with the given list of versions. |      * Updates this list with the given list of versions. | ||||||
|      * This is done by copying each version in the given list and inserting it |      * This is done by copying each version in the given list and inserting it | ||||||
|   | |||||||
| @@ -136,6 +136,15 @@ set(NET_SOURCES | |||||||
|     net/Validator.h |     net/Validator.h | ||||||
|     net/Upload.cpp |     net/Upload.cpp | ||||||
|     net/Upload.h |     net/Upload.h | ||||||
|  |     net/HeaderProxy.h | ||||||
|  |     net/RawHeaderProxy.h | ||||||
|  |     net/ApiHeaderProxy.h | ||||||
|  |     net/ApiDownload.h | ||||||
|  |     net/ApiDownload.cpp | ||||||
|  |     net/ApiUpload.cpp | ||||||
|  |     net/ApiUpload.h | ||||||
|  |     net/NetRequest.cpp | ||||||
|  |     net/NetRequest.h | ||||||
| ) | ) | ||||||
|  |  | ||||||
| # Game launch logic | # Game launch logic | ||||||
| @@ -262,8 +271,6 @@ set(MINECRAFT_SOURCES | |||||||
|     minecraft/launch/CreateGameFolders.h |     minecraft/launch/CreateGameFolders.h | ||||||
|     minecraft/launch/ModMinecraftJar.cpp |     minecraft/launch/ModMinecraftJar.cpp | ||||||
|     minecraft/launch/ModMinecraftJar.h |     minecraft/launch/ModMinecraftJar.h | ||||||
|     minecraft/launch/DirectJavaLaunch.cpp |  | ||||||
|     minecraft/launch/DirectJavaLaunch.h |  | ||||||
|     minecraft/launch/ExtractNatives.cpp |     minecraft/launch/ExtractNatives.cpp | ||||||
|     minecraft/launch/ExtractNatives.h |     minecraft/launch/ExtractNatives.h | ||||||
|     minecraft/launch/LauncherPartLaunch.cpp |     minecraft/launch/LauncherPartLaunch.cpp | ||||||
| @@ -487,6 +494,9 @@ set(API_SOURCES | |||||||
|     modplatform/helpers/HashUtils.cpp |     modplatform/helpers/HashUtils.cpp | ||||||
|     modplatform/helpers/OverrideUtils.h |     modplatform/helpers/OverrideUtils.h | ||||||
|     modplatform/helpers/OverrideUtils.cpp |     modplatform/helpers/OverrideUtils.cpp | ||||||
|  |  | ||||||
|  |     modplatform/helpers/ExportToModList.h | ||||||
|  |     modplatform/helpers/ExportToModList.cpp | ||||||
| ) | ) | ||||||
|  |  | ||||||
| set(FTB_SOURCES | set(FTB_SOURCES | ||||||
| @@ -498,6 +508,11 @@ set(FTB_SOURCES | |||||||
|     modplatform/legacy_ftb/PrivatePackManager.cpp |     modplatform/legacy_ftb/PrivatePackManager.cpp | ||||||
|  |  | ||||||
|     modplatform/legacy_ftb/PackHelpers.h |     modplatform/legacy_ftb/PackHelpers.h | ||||||
|  |  | ||||||
|  |     modplatform/import_ftb/PackInstallTask.h | ||||||
|  |     modplatform/import_ftb/PackInstallTask.cpp | ||||||
|  |     modplatform/import_ftb/PackHelpers.h | ||||||
|  |     modplatform/import_ftb/PackHelpers.cpp | ||||||
| ) | ) | ||||||
|  |  | ||||||
| set(FLAME_SOURCES | set(FLAME_SOURCES | ||||||
| @@ -560,6 +575,9 @@ set(ATLAUNCHER_SOURCES | |||||||
| ) | ) | ||||||
|  |  | ||||||
| set(LINKEXE_SOURCES | set(LINKEXE_SOURCES | ||||||
|  |     WindowsConsole.cpp | ||||||
|  |     WindowsConsole.h | ||||||
|  |  | ||||||
|     filelink/FileLink.h |     filelink/FileLink.h | ||||||
|     filelink/FileLink.cpp |     filelink/FileLink.cpp | ||||||
|     FileSystem.h |     FileSystem.h | ||||||
| @@ -665,7 +683,7 @@ set(LOGIC_SOURCES | |||||||
|     ${ATLAUNCHER_SOURCES} |     ${ATLAUNCHER_SOURCES} | ||||||
| ) | ) | ||||||
|  |  | ||||||
| if(APPLE) | if(APPLE AND Launcher_ENABLE_UPDATER) | ||||||
|     set (LOGIC_SOURCES ${LOGIC_SOURCES} ${MAC_UPDATE_SOURCES}) |     set (LOGIC_SOURCES ${LOGIC_SOURCES} ${MAC_UPDATE_SOURCES}) | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
| @@ -756,8 +774,12 @@ SET(LAUNCHER_SOURCES | |||||||
|     ui/themes/ITheme.h |     ui/themes/ITheme.h | ||||||
|     ui/themes/SystemTheme.cpp |     ui/themes/SystemTheme.cpp | ||||||
|     ui/themes/SystemTheme.h |     ui/themes/SystemTheme.h | ||||||
|  |     ui/themes/IconTheme.cpp | ||||||
|  |     ui/themes/IconTheme.h | ||||||
|     ui/themes/ThemeManager.cpp |     ui/themes/ThemeManager.cpp | ||||||
|     ui/themes/ThemeManager.h |     ui/themes/ThemeManager.h | ||||||
|  |     ui/themes/CatPack.cpp | ||||||
|  |     ui/themes/CatPack.h | ||||||
|  |  | ||||||
|     # Processes |     # Processes | ||||||
|     LaunchController.h |     LaunchController.h | ||||||
| @@ -867,6 +889,11 @@ SET(LAUNCHER_SOURCES | |||||||
|     ui/pages/modplatform/legacy_ftb/ListModel.h |     ui/pages/modplatform/legacy_ftb/ListModel.h | ||||||
|     ui/pages/modplatform/legacy_ftb/ListModel.cpp |     ui/pages/modplatform/legacy_ftb/ListModel.cpp | ||||||
|  |  | ||||||
|  |     ui/pages/modplatform/import_ftb/ImportFTBPage.cpp | ||||||
|  |     ui/pages/modplatform/import_ftb/ImportFTBPage.h | ||||||
|  |     ui/pages/modplatform/import_ftb/ListModel.h | ||||||
|  |     ui/pages/modplatform/import_ftb/ListModel.cpp | ||||||
|  |  | ||||||
|     ui/pages/modplatform/flame/FlameModel.cpp |     ui/pages/modplatform/flame/FlameModel.cpp | ||||||
|     ui/pages/modplatform/flame/FlameModel.h |     ui/pages/modplatform/flame/FlameModel.h | ||||||
|     ui/pages/modplatform/flame/FlamePage.cpp |     ui/pages/modplatform/flame/FlamePage.cpp | ||||||
| @@ -911,6 +938,8 @@ SET(LAUNCHER_SOURCES | |||||||
|     ui/dialogs/ExportInstanceDialog.h |     ui/dialogs/ExportInstanceDialog.h | ||||||
|     ui/dialogs/ExportPackDialog.cpp |     ui/dialogs/ExportPackDialog.cpp | ||||||
|     ui/dialogs/ExportPackDialog.h |     ui/dialogs/ExportPackDialog.h | ||||||
|  |     ui/dialogs/ExportToModListDialog.cpp | ||||||
|  |     ui/dialogs/ExportToModListDialog.h | ||||||
|     ui/dialogs/IconPickerDialog.cpp |     ui/dialogs/IconPickerDialog.cpp | ||||||
|     ui/dialogs/IconPickerDialog.h |     ui/dialogs/IconPickerDialog.h | ||||||
|     ui/dialogs/ImportResourceDialog.cpp |     ui/dialogs/ImportResourceDialog.cpp | ||||||
| @@ -947,6 +976,8 @@ SET(LAUNCHER_SOURCES | |||||||
|     ui/dialogs/ChooseProviderDialog.cpp |     ui/dialogs/ChooseProviderDialog.cpp | ||||||
|     ui/dialogs/ModUpdateDialog.cpp |     ui/dialogs/ModUpdateDialog.cpp | ||||||
|     ui/dialogs/ModUpdateDialog.h |     ui/dialogs/ModUpdateDialog.h | ||||||
|  |     ui/dialogs/InstallLoaderDialog.cpp | ||||||
|  |     ui/dialogs/InstallLoaderDialog.h | ||||||
|  |  | ||||||
|     # GUI - widgets |     # GUI - widgets | ||||||
|     ui/widgets/Common.cpp |     ui/widgets/Common.cpp | ||||||
| @@ -1011,6 +1042,14 @@ SET(LAUNCHER_SOURCES | |||||||
|     ui/instanceview/VisualGroup.h |     ui/instanceview/VisualGroup.h | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | if(WIN32) | ||||||
|  |     set(LAUNCHER_SOURCES | ||||||
|  |         WindowsConsole.cpp | ||||||
|  |         WindowsConsole.h | ||||||
|  |         ${LAUNCHER_SOURCES} | ||||||
|  |     ) | ||||||
|  | endif() | ||||||
|  |  | ||||||
| qt_wrap_ui(LAUNCHER_UI | qt_wrap_ui(LAUNCHER_UI | ||||||
|     ui/MainWindow.ui |     ui/MainWindow.ui | ||||||
|     ui/setupwizard/PasteWizardPage.ui |     ui/setupwizard/PasteWizardPage.ui | ||||||
| @@ -1039,6 +1078,7 @@ qt_wrap_ui(LAUNCHER_UI | |||||||
|     ui/pages/modplatform/ResourcePage.ui |     ui/pages/modplatform/ResourcePage.ui | ||||||
|     ui/pages/modplatform/flame/FlamePage.ui |     ui/pages/modplatform/flame/FlamePage.ui | ||||||
|     ui/pages/modplatform/legacy_ftb/Page.ui |     ui/pages/modplatform/legacy_ftb/Page.ui | ||||||
|  |     ui/pages/modplatform/import_ftb/ImportFTBPage.ui | ||||||
|     ui/pages/modplatform/ImportPage.ui |     ui/pages/modplatform/ImportPage.ui | ||||||
|     ui/pages/modplatform/modrinth/ModrinthPage.ui |     ui/pages/modplatform/modrinth/ModrinthPage.ui | ||||||
|     ui/pages/modplatform/technic/TechnicPage.ui |     ui/pages/modplatform/technic/TechnicPage.ui | ||||||
| @@ -1058,6 +1098,7 @@ qt_wrap_ui(LAUNCHER_UI | |||||||
|     ui/dialogs/SkinUploadDialog.ui |     ui/dialogs/SkinUploadDialog.ui | ||||||
|     ui/dialogs/ExportInstanceDialog.ui |     ui/dialogs/ExportInstanceDialog.ui | ||||||
|     ui/dialogs/ExportPackDialog.ui |     ui/dialogs/ExportPackDialog.ui | ||||||
|  |     ui/dialogs/ExportToModListDialog.ui | ||||||
|     ui/dialogs/IconPickerDialog.ui |     ui/dialogs/IconPickerDialog.ui | ||||||
|     ui/dialogs/ImportResourceDialog.ui |     ui/dialogs/ImportResourceDialog.ui | ||||||
|     ui/dialogs/MSALoginDialog.ui |     ui/dialogs/MSALoginDialog.ui | ||||||
| @@ -1092,8 +1133,15 @@ if(WIN32) | |||||||
|     set(LAUNCHER_RCS ${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_WindowsRC}) |     set(LAUNCHER_RCS ${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_WindowsRC}) | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
|  | include(CompilerWarnings) | ||||||
|  |  | ||||||
| # Add executable | # Add executable | ||||||
| add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES}) | add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES}) | ||||||
|  | set_project_warnings(Launcher_logic | ||||||
|  |     "${Launcher_MSVC_WARNINGS}" | ||||||
|  |     "${Launcher_CLANG_WARNINGS}" | ||||||
|  |     "${Launcher_GCC_WARNINGS}") | ||||||
|  | target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION) | ||||||
| target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) | target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) | ||||||
| target_link_libraries(Launcher_logic | target_link_libraries(Launcher_logic | ||||||
|     systeminfo |     systeminfo | ||||||
| @@ -1133,17 +1181,23 @@ if(APPLE) | |||||||
|     set(CMAKE_MACOSX_RPATH 1) |     set(CMAKE_MACOSX_RPATH 1) | ||||||
|     set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks/") |     set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks/") | ||||||
|  |  | ||||||
|     file(DOWNLOAD ${MACOSX_SPARKLE_DOWNLOAD_URL} ${CMAKE_BINARY_DIR}/Sparkle.tar.xz EXPECTED_HASH SHA256=${MACOSX_SPARKLE_SHA256}) |     if(Launcher_ENABLE_UPDATER) | ||||||
|     file(ARCHIVE_EXTRACT INPUT ${CMAKE_BINARY_DIR}/Sparkle.tar.xz DESTINATION ${CMAKE_BINARY_DIR}/frameworks/Sparkle) |       file(DOWNLOAD ${MACOSX_SPARKLE_DOWNLOAD_URL} ${CMAKE_BINARY_DIR}/Sparkle.tar.xz EXPECTED_HASH SHA256=${MACOSX_SPARKLE_SHA256}) | ||||||
|  |       file(ARCHIVE_EXTRACT INPUT ${CMAKE_BINARY_DIR}/Sparkle.tar.xz DESTINATION ${CMAKE_BINARY_DIR}/frameworks/Sparkle) | ||||||
|  |  | ||||||
|  |       find_library(SPARKLE_FRAMEWORK Sparkle "${CMAKE_BINARY_DIR}/frameworks/Sparkle") | ||||||
|  |       add_compile_definitions(SPARKLE_ENABLED) | ||||||
|  |     endif() | ||||||
|  |  | ||||||
|     find_library(SPARKLE_FRAMEWORK Sparkle "${CMAKE_BINARY_DIR}/frameworks/Sparkle") |  | ||||||
|     target_link_libraries(Launcher_logic |     target_link_libraries(Launcher_logic | ||||||
|         "-framework AppKit" |         "-framework AppKit" | ||||||
|         "-framework Carbon" |         "-framework Carbon" | ||||||
|         "-framework Foundation" |         "-framework Foundation" | ||||||
|         "-framework ApplicationServices" |         "-framework ApplicationServices" | ||||||
|     ) |     ) | ||||||
|     target_link_libraries(Launcher_logic ${SPARKLE_FRAMEWORK}) |     if(Launcher_ENABLE_UPDATER) | ||||||
|  |       target_link_libraries(Launcher_logic ${SPARKLE_FRAMEWORK}) | ||||||
|  |     endif() | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
| target_link_libraries(Launcher_logic) | target_link_libraries(Launcher_logic) | ||||||
| @@ -1172,6 +1226,11 @@ install(TARGETS ${Launcher_Name} | |||||||
|  |  | ||||||
| if(WIN32) | if(WIN32) | ||||||
|     add_library(filelink_logic STATIC ${LINKEXE_SOURCES}) |     add_library(filelink_logic STATIC ${LINKEXE_SOURCES}) | ||||||
|  |     set_project_warnings(filelink_logic | ||||||
|  |     "${Launcher_MSVC_WARNINGS}" | ||||||
|  |     "${Launcher_CLANG_WARNINGS}" | ||||||
|  |     "${Launcher_GCC_WARNINGS}") | ||||||
|  |  | ||||||
|     target_include_directories(filelink_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) |     target_include_directories(filelink_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) | ||||||
|     target_link_libraries(filelink_logic |     target_link_libraries(filelink_logic | ||||||
|         systeminfo |         systeminfo | ||||||
| @@ -1205,7 +1264,7 @@ if(WIN32) | |||||||
|     ) |     ) | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
| if (UNIX AND APPLE) | if (UNIX AND APPLE AND Launcher_ENABLE_UPDATER) | ||||||
|     # Add Sparkle updater |     # Add Sparkle updater | ||||||
|     # It has to be copied here instead of just allowing fixup_bundle to install it, otherwise essential parts of |     # It has to be copied here instead of just allowing fixup_bundle to install it, otherwise essential parts of | ||||||
|     # the framework aren't installed |     # the framework aren't installed | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  PolyMC - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
| @@ -41,8 +41,7 @@ | |||||||
|  * @file libutil/src/cmdutils.cpp |  * @file libutil/src/cmdutils.cpp | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| namespace Commandline | namespace Commandline { | ||||||
| { |  | ||||||
|  |  | ||||||
| // commandline splitter | // commandline splitter | ||||||
| QStringList splitArgs(QString args) | QStringList splitArgs(QString args) | ||||||
| @@ -51,19 +50,15 @@ QStringList splitArgs(QString args) | |||||||
|     QString current; |     QString current; | ||||||
|     bool escape = false; |     bool escape = false; | ||||||
|     QChar inquotes; |     QChar inquotes; | ||||||
|     for (int i = 0; i < args.length(); i++) |     for (int i = 0; i < args.length(); i++) { | ||||||
|     { |  | ||||||
|         QChar cchar = args.at(i); |         QChar cchar = args.at(i); | ||||||
|  |  | ||||||
|         // \ escaped |         // \ escaped | ||||||
|         if (escape) |         if (escape) { | ||||||
|         { |  | ||||||
|             current += cchar; |             current += cchar; | ||||||
|             escape = false; |             escape = false; | ||||||
|             // in "quotes" |             // in "quotes" | ||||||
|         } |         } else if (!inquotes.isNull()) { | ||||||
|         else if (!inquotes.isNull()) |  | ||||||
|         { |  | ||||||
|             if (cchar == '\\') |             if (cchar == '\\') | ||||||
|                 escape = true; |                 escape = true; | ||||||
|             else if (cchar == inquotes) |             else if (cchar == inquotes) | ||||||
| @@ -71,18 +66,13 @@ QStringList splitArgs(QString args) | |||||||
|             else |             else | ||||||
|                 current += cchar; |                 current += cchar; | ||||||
|             // otherwise |             // otherwise | ||||||
|         } |         } else { | ||||||
|         else |             if (cchar == ' ') { | ||||||
|         { |                 if (!current.isEmpty()) { | ||||||
|             if (cchar == ' ') |  | ||||||
|             { |  | ||||||
|                 if (!current.isEmpty()) |  | ||||||
|                 { |  | ||||||
|                     argv << current; |                     argv << current; | ||||||
|                     current.clear(); |                     current.clear(); | ||||||
|                 } |                 } | ||||||
|             } |             } else if (cchar == '"' || cchar == '\'') | ||||||
|             else if (cchar == '"' || cchar == '\'') |  | ||||||
|                 inquotes = cchar; |                 inquotes = cchar; | ||||||
|             else |             else | ||||||
|                 current += cchar; |                 current += cchar; | ||||||
| @@ -92,4 +82,4 @@ QStringList splitArgs(QString args) | |||||||
|         argv << current; |         argv << current; | ||||||
|     return argv; |     return argv; | ||||||
| } | } | ||||||
| } | }  // namespace Commandline | ||||||
|   | |||||||
| @@ -25,8 +25,7 @@ | |||||||
|  * @brief commandline parsing and processing utilities |  * @brief commandline parsing and processing utilities | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| namespace Commandline | namespace Commandline { | ||||||
| { |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * @brief split a string into argv items like a shell would do |  * @brief split a string into argv items like a shell would do | ||||||
| @@ -34,4 +33,4 @@ namespace Commandline | |||||||
|  * @return a QStringList containing all arguments |  * @return a QStringList containing all arguments | ||||||
|  */ |  */ | ||||||
| QStringList splitArgs(QString args); | QStringList splitArgs(QString args); | ||||||
| } | }  // namespace Commandline | ||||||
|   | |||||||
| @@ -1,33 +1,21 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| template <typename T> | template <typename T> | ||||||
| class DefaultVariable | class DefaultVariable { | ||||||
| { |    public: | ||||||
| public: |     DefaultVariable(const T& value) { defaultValue = value; } | ||||||
|     DefaultVariable(const T & value) |     DefaultVariable<T>& operator=(const T& value) | ||||||
|     { |  | ||||||
|         defaultValue = value; |  | ||||||
|     } |  | ||||||
|     DefaultVariable<T> & operator =(const T & value) |  | ||||||
|     { |     { | ||||||
|         currentValue = value; |         currentValue = value; | ||||||
|         is_default = currentValue == defaultValue; |         is_default = currentValue == defaultValue; | ||||||
|         is_explicit = true; |         is_explicit = true; | ||||||
|         return *this; |         return *this; | ||||||
|     } |     } | ||||||
|     operator const T &() const |     operator const T&() const { return is_default ? defaultValue : currentValue; } | ||||||
|     { |     bool isDefault() const { return is_default; } | ||||||
|         return is_default ? defaultValue : currentValue; |     bool isExplicit() const { return is_explicit; } | ||||||
|     } |  | ||||||
|     bool isDefault() const |    private: | ||||||
|     { |  | ||||||
|         return is_default; |  | ||||||
|     } |  | ||||||
|     bool isExplicit() const |  | ||||||
|     { |  | ||||||
|         return is_explicit; |  | ||||||
|     } |  | ||||||
| private: |  | ||||||
|     T currentValue; |     T currentValue; | ||||||
|     T defaultValue; |     T defaultValue; | ||||||
|     bool is_default = true; |     bool is_default = true; | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  PolyMC - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 dada513 <dada513@protonmail.com> |  *  Copyright (C) 2022 dada513 <dada513@protonmail.com> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
| @@ -33,40 +33,37 @@ | |||||||
|  *      limitations under the License. |  *      limitations under the License. | ||||||
|  */ |  */ | ||||||
| #include "DesktopServices.h" | #include "DesktopServices.h" | ||||||
| #include <QDir> |  | ||||||
| #include <QDesktopServices> |  | ||||||
| #include <QProcess> |  | ||||||
| #include <QDebug> | #include <QDebug> | ||||||
|  | #include <QDesktopServices> | ||||||
|  | #include <QDir> | ||||||
|  | #include <QProcess> | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * This shouldn't exist, but until QTBUG-9328 and other unreported bugs are fixed, it needs to be a thing. |  * This shouldn't exist, but until QTBUG-9328 and other unreported bugs are fixed, it needs to be a thing. | ||||||
|  */ |  */ | ||||||
| #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) | #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) | ||||||
|  |  | ||||||
| #include <unistd.h> |  | ||||||
| #include <errno.h> | #include <errno.h> | ||||||
| #include <sys/types.h> | #include <sys/types.h> | ||||||
| #include <sys/wait.h> | #include <sys/wait.h> | ||||||
|  | #include <unistd.h> | ||||||
|  |  | ||||||
| template <typename T> | template <typename T> | ||||||
| bool IndirectOpen(T callable, qint64 *pid_forked = nullptr) | bool IndirectOpen(T callable, qint64* pid_forked = nullptr) | ||||||
| { | { | ||||||
|     auto pid = fork(); |     auto pid = fork(); | ||||||
|     if(pid_forked) |     if (pid_forked) { | ||||||
|     { |         if (pid > 0) | ||||||
|         if(pid > 0) |  | ||||||
|             *pid_forked = pid; |             *pid_forked = pid; | ||||||
|         else |         else | ||||||
|             *pid_forked = 0; |             *pid_forked = 0; | ||||||
|     } |     } | ||||||
|     if(pid == -1) |     if (pid == -1) { | ||||||
|     { |  | ||||||
|         qWarning() << "IndirectOpen failed to fork: " << errno; |         qWarning() << "IndirectOpen failed to fork: " << errno; | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|     // child - do the stuff |     // child - do the stuff | ||||||
|     if(pid == 0) |     if (pid == 0) { | ||||||
|     { |  | ||||||
|         // unset all this garbage so it doesn't get passed to the child process |         // unset all this garbage so it doesn't get passed to the child process | ||||||
|         qunsetenv("LD_PRELOAD"); |         qunsetenv("LD_PRELOAD"); | ||||||
|         qunsetenv("LD_LIBRARY_PATH"); |         qunsetenv("LD_LIBRARY_PATH"); | ||||||
| @@ -82,19 +79,14 @@ bool IndirectOpen(T callable, qint64 *pid_forked = nullptr) | |||||||
|  |  | ||||||
|         // die. now. do not clean up anything, it would just hang forever. |         // die. now. do not clean up anything, it would just hang forever. | ||||||
|         _exit(status ? 0 : 1); |         _exit(status ? 0 : 1); | ||||||
|     } |     } else { | ||||||
|     else |         // parent - assume it worked. | ||||||
|     { |  | ||||||
|         //parent - assume it worked. |  | ||||||
|         int status; |         int status; | ||||||
|         while (waitpid(pid, &status, 0)) |         while (waitpid(pid, &status, 0)) { | ||||||
|         { |             if (WIFEXITED(status)) { | ||||||
|             if(WIFEXITED(status)) |  | ||||||
|             { |  | ||||||
|                 return WEXITSTATUS(status) == 0; |                 return WEXITSTATUS(status) == 0; | ||||||
|             } |             } | ||||||
|             if(WIFSIGNALED(status)) |             if (WIFSIGNALED(status)) { | ||||||
|             { |  | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -104,26 +96,19 @@ bool IndirectOpen(T callable, qint64 *pid_forked = nullptr) | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
| namespace DesktopServices { | namespace DesktopServices { | ||||||
| bool openDirectory(const QString &path, bool ensureExists) | bool openDirectory(const QString& path, [[maybe_unused]] bool ensureExists) | ||||||
| { | { | ||||||
|     qDebug() << "Opening directory" << path; |     qDebug() << "Opening directory" << path; | ||||||
|     QDir parentPath; |     QDir parentPath; | ||||||
|     QDir dir(path); |     QDir dir(path); | ||||||
|     if (!dir.exists()) |     if (ensureExists && !dir.exists()) { | ||||||
|     { |  | ||||||
|         parentPath.mkpath(dir.absolutePath()); |         parentPath.mkpath(dir.absolutePath()); | ||||||
|     } |     } | ||||||
|     auto f = [&]() |     auto f = [&]() { return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath())); }; | ||||||
|     { |  | ||||||
|         return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath())); |  | ||||||
|     }; |  | ||||||
| #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) | #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) | ||||||
|     if(!isFlatpak()) |     if (!isSandbox()) { | ||||||
|     { |  | ||||||
|         return IndirectOpen(f); |         return IndirectOpen(f); | ||||||
|     } |     } else { | ||||||
|     else |  | ||||||
|     { |  | ||||||
|         return f(); |         return f(); | ||||||
|     } |     } | ||||||
| #else | #else | ||||||
| @@ -131,20 +116,14 @@ bool openDirectory(const QString &path, bool ensureExists) | |||||||
| #endif | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| bool openFile(const QString &path) | bool openFile(const QString& path) | ||||||
| { | { | ||||||
|     qDebug() << "Opening file" << path; |     qDebug() << "Opening file" << path; | ||||||
|     auto f = [&]() |     auto f = [&]() { return QDesktopServices::openUrl(QUrl::fromLocalFile(path)); }; | ||||||
|     { |  | ||||||
|         return QDesktopServices::openUrl(QUrl::fromLocalFile(path)); |  | ||||||
|     }; |  | ||||||
| #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) | #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) | ||||||
|     if(!isFlatpak()) |     if (!isSandbox()) { | ||||||
|     { |  | ||||||
|         return IndirectOpen(f); |         return IndirectOpen(f); | ||||||
|     } |     } else { | ||||||
|     else |  | ||||||
|     { |  | ||||||
|         return f(); |         return f(); | ||||||
|     } |     } | ||||||
| #else | #else | ||||||
| @@ -152,41 +131,29 @@ bool openFile(const QString &path) | |||||||
| #endif | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| bool openFile(const QString &application, const QString &path, const QString &workingDirectory, qint64 *pid) | bool openFile(const QString& application, const QString& path, const QString& workingDirectory, qint64* pid) | ||||||
| { | { | ||||||
|     qDebug() << "Opening file" << path << "using" << application; |     qDebug() << "Opening file" << path << "using" << application; | ||||||
| #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) | #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) | ||||||
|     // FIXME: the pid here is fake. So if something depends on it, it will likely misbehave |     // FIXME: the pid here is fake. So if something depends on it, it will likely misbehave | ||||||
|     if(!isFlatpak()) |     if (!isSandbox()) { | ||||||
|     { |         return IndirectOpen([&]() { return QProcess::startDetached(application, QStringList() << path, workingDirectory); }, pid); | ||||||
|         return IndirectOpen([&]() |     } else { | ||||||
|         { |         return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid); | ||||||
|             return QProcess::startDetached(application, QStringList() << path, workingDirectory); |  | ||||||
|         }, pid); |  | ||||||
|     } |  | ||||||
|     else |  | ||||||
|     { |  | ||||||
|       return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid);   |  | ||||||
|     } |     } | ||||||
| #else | #else | ||||||
|     return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid); |     return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid); | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| bool run(const QString &application, const QStringList &args, const QString &workingDirectory, qint64 *pid) | bool run(const QString& application, const QStringList& args, const QString& workingDirectory, qint64* pid) | ||||||
| { | { | ||||||
|     qDebug() << "Running" << application << "with args" << args.join(' '); |     qDebug() << "Running" << application << "with args" << args.join(' '); | ||||||
| #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) | #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) | ||||||
|     if(!isFlatpak()) |     if (!isSandbox()) { | ||||||
|     { |         // FIXME: the pid here is fake. So if something depends on it, it will likely misbehave | ||||||
|     // FIXME: the pid here is fake. So if something depends on it, it will likely misbehave |         return IndirectOpen([&]() { return QProcess::startDetached(application, args, workingDirectory); }, pid); | ||||||
|     return IndirectOpen([&]() |     } else { | ||||||
|     { |  | ||||||
|         return QProcess::startDetached(application, args, workingDirectory); |  | ||||||
|     }, pid); |  | ||||||
|     } |  | ||||||
|     else |  | ||||||
|     { |  | ||||||
|         return QProcess::startDetached(application, args, workingDirectory, pid); |         return QProcess::startDetached(application, args, workingDirectory, pid); | ||||||
|     } |     } | ||||||
| #else | #else | ||||||
| @@ -194,20 +161,14 @@ bool run(const QString &application, const QStringList &args, const QString &wor | |||||||
| #endif | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| bool openUrl(const QUrl &url) | bool openUrl(const QUrl& url) | ||||||
| { | { | ||||||
|     qDebug() << "Opening URL" << url.toString(); |     qDebug() << "Opening URL" << url.toString(); | ||||||
|     auto f = [&]() |     auto f = [&]() { return QDesktopServices::openUrl(url); }; | ||||||
|     { |  | ||||||
|         return QDesktopServices::openUrl(url); |  | ||||||
|     }; |  | ||||||
| #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) | #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) | ||||||
|     if(!isFlatpak()) |     if (!isSandbox()) { | ||||||
|     { |  | ||||||
|         return IndirectOpen(f); |         return IndirectOpen(f); | ||||||
|     } |     } else { | ||||||
|     else |  | ||||||
|     { |  | ||||||
|         return f(); |         return f(); | ||||||
|     } |     } | ||||||
| #else | #else | ||||||
| @@ -224,4 +185,18 @@ bool isFlatpak() | |||||||
| #endif | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
|  | bool isSnap() | ||||||
|  | { | ||||||
|  | #ifdef Q_OS_LINUX | ||||||
|  |     return getenv("SNAP"); | ||||||
|  | #else | ||||||
|  |     return false; | ||||||
|  | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
|  | bool isSandbox() | ||||||
|  | { | ||||||
|  |     return isSnap() || isFlatpak(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace DesktopServices | ||||||
|   | |||||||
| @@ -1,38 +1,50 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <QUrl> |  | ||||||
| #include <QString> | #include <QString> | ||||||
|  | #include <QUrl> | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * This wraps around QDesktopServices and adds workarounds where needed |  * This wraps around QDesktopServices and adds workarounds where needed | ||||||
|  * Use this instead of QDesktopServices! |  * Use this instead of QDesktopServices! | ||||||
|  */ |  */ | ||||||
| namespace DesktopServices | namespace DesktopServices { | ||||||
| { | /** | ||||||
|     /** |  * Open a file in whatever application is applicable | ||||||
|      * Open a file in whatever application is applicable |  */ | ||||||
|      */ | bool openFile(const QString& path); | ||||||
|     bool openFile(const QString &path); |  | ||||||
|  |  | ||||||
|     /** | /** | ||||||
|      * Open a file in the specified application |  * Open a file in the specified application | ||||||
|      */ |  */ | ||||||
|     bool openFile(const QString &application, const QString &path, const QString & workingDirectory = QString(), qint64 *pid = 0); | bool openFile(const QString& application, const QString& path, const QString& workingDirectory = QString(), qint64* pid = 0); | ||||||
|  |  | ||||||
|     /** | /** | ||||||
|      * Run an application |  * Run an application | ||||||
|      */ |  */ | ||||||
|     bool run(const QString &application,const QStringList &args, const QString & workingDirectory = QString(), qint64 *pid = 0); | bool run(const QString& application, const QStringList& args, const QString& workingDirectory = QString(), qint64* pid = 0); | ||||||
|  |  | ||||||
|     /** | /** | ||||||
|      * Open a directory |  * Open a directory | ||||||
|      */ |  */ | ||||||
|     bool openDirectory(const QString &path, bool ensureExists = false); | bool openDirectory(const QString& path, bool ensureExists = false); | ||||||
|  |  | ||||||
|     /** | /** | ||||||
|      * Open the URL, most likely in a browser. Maybe. |  * Open the URL, most likely in a browser. Maybe. | ||||||
|      */ |  */ | ||||||
|     bool openUrl(const QUrl &url); | bool openUrl(const QUrl& url); | ||||||
|  |  | ||||||
|     bool isFlatpak(); | /** | ||||||
| } |  * Determine whether the launcher is running in a Flatpak environment | ||||||
|  |  */ | ||||||
|  | bool isFlatpak(); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Determine whether the launcher is running in a Snap environment | ||||||
|  |  */ | ||||||
|  | bool isSnap(); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Determine whether the launcher is running in a sandboxed (Flatpak or Snap) environment | ||||||
|  |  */ | ||||||
|  | bool isSandbox(); | ||||||
|  | }  // namespace DesktopServices | ||||||
|   | |||||||
| @@ -2,31 +2,18 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <QString> |  | ||||||
| #include <QDebug> | #include <QDebug> | ||||||
|  | #include <QString> | ||||||
| #include <exception> | #include <exception> | ||||||
|  |  | ||||||
| class Exception : public std::exception | class Exception : public std::exception { | ||||||
| { |    public: | ||||||
| public: |     Exception(const QString& message) : std::exception(), m_message(message) { qCritical() << "Exception:" << message; } | ||||||
|     Exception(const QString &message) : std::exception(), m_message(message) |     Exception(const Exception& other) : std::exception(), m_message(other.cause()) {} | ||||||
|     { |  | ||||||
|         qCritical() << "Exception:" << message; |  | ||||||
|     } |  | ||||||
|     Exception(const Exception &other) |  | ||||||
|         : std::exception(), m_message(other.cause()) |  | ||||||
|     { |  | ||||||
|     } |  | ||||||
|     virtual ~Exception() noexcept {} |     virtual ~Exception() noexcept {} | ||||||
|     const char *what() const noexcept |     const char* what() const noexcept { return m_message.toLatin1().constData(); } | ||||||
|     { |     QString cause() const { return m_message; } | ||||||
|         return m_message.toLatin1().constData(); |  | ||||||
|     } |  | ||||||
|     QString cause() const |  | ||||||
|     { |  | ||||||
|         return m_message; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| private: |    private: | ||||||
|     QString m_message; |     QString m_message; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -4,31 +4,24 @@ | |||||||
| template <typename T> | template <typename T> | ||||||
| inline void clamp(T& current, T min, T max) | inline void clamp(T& current, T min, T max) | ||||||
| { | { | ||||||
|     if (current < min) |     if (current < min) { | ||||||
|     { |  | ||||||
|         current = min; |         current = min; | ||||||
|     } |     } else if (current > max) { | ||||||
|     else if(current > max) |  | ||||||
|     { |  | ||||||
|         current = max; |         current = max; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| // List of numbers from min to max. Next is exponent times bigger than previous. | // List of numbers from min to max. Next is exponent times bigger than previous. | ||||||
|  |  | ||||||
| class ExponentialSeries | class ExponentialSeries { | ||||||
| { |    public: | ||||||
| public: |  | ||||||
|     ExponentialSeries(unsigned min, unsigned max, unsigned exponent = 2) |     ExponentialSeries(unsigned min, unsigned max, unsigned exponent = 2) | ||||||
|     { |     { | ||||||
|         m_current = m_min = min; |         m_current = m_min = min; | ||||||
|         m_max = max; |         m_max = max; | ||||||
|         m_exponent = exponent; |         m_exponent = exponent; | ||||||
|     } |     } | ||||||
|     void reset() |     void reset() { m_current = m_min; } | ||||||
|     { |  | ||||||
|         m_current = m_min; |  | ||||||
|     } |  | ||||||
|     unsigned operator()() |     unsigned operator()() | ||||||
|     { |     { | ||||||
|         unsigned retval = m_current; |         unsigned retval = m_current; | ||||||
|   | |||||||
| @@ -779,7 +779,8 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri | |||||||
|     } |     } | ||||||
| #if defined(Q_OS_MACOS) | #if defined(Q_OS_MACOS) | ||||||
|     // Create the Application |     // Create the Application | ||||||
|     QDir applicationDirectory = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation) + "/" + BuildConfig.LAUNCHER_NAME + " Instances/"; |     QDir applicationDirectory = | ||||||
|  |         QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation) + "/" + BuildConfig.LAUNCHER_NAME + " Instances/"; | ||||||
|  |  | ||||||
|     if (!applicationDirectory.mkpath(".")) { |     if (!applicationDirectory.mkpath(".")) { | ||||||
|         qWarning() << "Couldn't create application directory"; |         qWarning() << "Couldn't create application directory"; | ||||||
| @@ -843,7 +844,9 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri | |||||||
|                   "    <key>CFBundleIconFile</key>\n" |                   "    <key>CFBundleIconFile</key>\n" | ||||||
|                   "    <string>Icon.icns</string>\n" |                   "    <string>Icon.icns</string>\n" | ||||||
|                   "    <key>CFBundleName</key>\n" |                   "    <key>CFBundleName</key>\n" | ||||||
|                   "    <string>" << name << "</string>\n"  // Name of the application |                   "    <string>" | ||||||
|  |                << name | ||||||
|  |                << "</string>\n"  // Name of the application | ||||||
|                   "    <key>CFBundlePackageType</key>\n" |                   "    <key>CFBundlePackageType</key>\n" | ||||||
|                   "    <string>APPL</string>\n" |                   "    <string>APPL</string>\n" | ||||||
|                   "    <key>CFBundleShortVersionString</key>\n" |                   "    <key>CFBundleShortVersionString</key>\n" | ||||||
|   | |||||||
| @@ -43,10 +43,10 @@ | |||||||
| #include <system_error> | #include <system_error> | ||||||
|  |  | ||||||
| #include <QDir> | #include <QDir> | ||||||
| #include <QPair> |  | ||||||
| #include <QFlags> | #include <QFlags> | ||||||
| #include <QLocalServer> | #include <QLocalServer> | ||||||
| #include <QObject> | #include <QObject> | ||||||
|  | #include <QPair> | ||||||
| #include <QThread> | #include <QThread> | ||||||
|  |  | ||||||
| namespace FS { | namespace FS { | ||||||
| @@ -112,8 +112,8 @@ class copy : public QObject { | |||||||
|  |  | ||||||
|     bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); } |     bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); } | ||||||
|  |  | ||||||
|     int totalCopied() { return m_copied; } |     qsizetype totalCopied() { return m_copied; } | ||||||
|     int totalFailed() { return m_failedPaths.length(); } |     qsizetype totalFailed() { return m_failedPaths.length(); } | ||||||
|     QStringList failed() { return m_failedPaths; } |     QStringList failed() { return m_failedPaths; } | ||||||
|  |  | ||||||
|    signals: |    signals: | ||||||
| @@ -130,7 +130,7 @@ class copy : public QObject { | |||||||
|     bool m_whitelist = false; |     bool m_whitelist = false; | ||||||
|     QDir m_src; |     QDir m_src; | ||||||
|     QDir m_dst; |     QDir m_dst; | ||||||
|     int m_copied; |     qsizetype m_copied; | ||||||
|     QStringList m_failedPaths; |     QStringList m_failedPaths; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -365,25 +365,24 @@ enum class FilesystemType { | |||||||
|  * QMap is ordered |  * QMap is ordered | ||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
| static const QMap<FilesystemType, QStringList> s_filesystem_type_names = { | static const QMap<FilesystemType, QStringList> s_filesystem_type_names = { { FilesystemType::FAT, { "FAT" } }, | ||||||
|     {FilesystemType::FAT,        { "FAT" }}, |                                                                            { FilesystemType::NTFS, { "NTFS" } }, | ||||||
|     {FilesystemType::NTFS,       { "NTFS" }}, |                                                                            { FilesystemType::REFS, { "REFS" } }, | ||||||
|     {FilesystemType::REFS,       { "REFS" }}, |                                                                            { FilesystemType::EXT_2_OLD, { "EXT_2_OLD", "EXT2_OLD" } }, | ||||||
|     {FilesystemType::EXT_2_OLD,  { "EXT_2_OLD", "EXT2_OLD" }}, |                                                                            { FilesystemType::EXT_2_3_4, | ||||||
|     {FilesystemType::EXT_2_3_4,  { "EXT2/3/4", "EXT_2_3_4", "EXT2", "EXT3", "EXT4" }}, |                                                                              { "EXT2/3/4", "EXT_2_3_4", "EXT2", "EXT3", "EXT4" } }, | ||||||
|     {FilesystemType::EXT,        { "EXT" }}, |                                                                            { FilesystemType::EXT, { "EXT" } }, | ||||||
|     {FilesystemType::XFS,        { "XFS" }}, |                                                                            { FilesystemType::XFS, { "XFS" } }, | ||||||
|     {FilesystemType::BTRFS,      { "BTRFS" }}, |                                                                            { FilesystemType::BTRFS, { "BTRFS" } }, | ||||||
|     {FilesystemType::NFS,        { "NFS" }}, |                                                                            { FilesystemType::NFS, { "NFS" } }, | ||||||
|     {FilesystemType::ZFS,        { "ZFS" }}, |                                                                            { FilesystemType::ZFS, { "ZFS" } }, | ||||||
|     {FilesystemType::APFS,       { "APFS" }}, |                                                                            { FilesystemType::APFS, { "APFS" } }, | ||||||
|     {FilesystemType::HFS,        { "HFS" }}, |                                                                            { FilesystemType::HFS, { "HFS" } }, | ||||||
|     {FilesystemType::HFSPLUS,    { "HFSPLUS" }}, |                                                                            { FilesystemType::HFSPLUS, { "HFSPLUS" } }, | ||||||
|     {FilesystemType::HFSX,       { "HFSX" }}, |                                                                            { FilesystemType::HFSX, { "HFSX" } }, | ||||||
|     {FilesystemType::FUSEBLK,    { "FUSEBLK" }}, |                                                                            { FilesystemType::FUSEBLK, { "FUSEBLK" } }, | ||||||
|     {FilesystemType::F2FS,       { "F2FS" }}, |                                                                            { FilesystemType::F2FS, { "F2FS" } }, | ||||||
|     {FilesystemType::UNKNOWN,    { "UNKNOWN" }} |                                                                            { FilesystemType::UNKNOWN, { "UNKNOWN" } } }; | ||||||
| }; |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * @brief Get the string name of Filesystem enum object |  * @brief Get the string name of Filesystem enum object | ||||||
| @@ -475,8 +474,8 @@ class clone : public QObject { | |||||||
|  |  | ||||||
|     bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); } |     bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); } | ||||||
|  |  | ||||||
|     int totalCloned() { return m_cloned; } |     qsizetype totalCloned() { return m_cloned; } | ||||||
|     int totalFailed() { return m_failedClones.length(); } |     qsizetype totalFailed() { return m_failedClones.length(); } | ||||||
|  |  | ||||||
|     QList<QPair<QString, QString>> failed() { return m_failedClones; } |     QList<QPair<QString, QString>> failed() { return m_failedClones; } | ||||||
|  |  | ||||||
| @@ -492,7 +491,7 @@ class clone : public QObject { | |||||||
|     bool m_whitelist = false; |     bool m_whitelist = false; | ||||||
|     QDir m_src; |     QDir m_src; | ||||||
|     QDir m_dst; |     QDir m_dst; | ||||||
|     int m_cloned; |     qsizetype m_cloned; | ||||||
|     QList<QPair<QString, QString>> m_failedClones; |     QList<QPair<QString, QString>> m_failedClones; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,28 +1,33 @@ | |||||||
| #include "Filter.h" | #include "Filter.h" | ||||||
|  |  | ||||||
| Filter::~Filter(){} | Filter::~Filter() {} | ||||||
|  |  | ||||||
| ContainsFilter::ContainsFilter(const QString& pattern) : pattern(pattern){} | ContainsFilter::ContainsFilter(const QString& pattern) : pattern(pattern) {} | ||||||
| ContainsFilter::~ContainsFilter(){} | ContainsFilter::~ContainsFilter() {} | ||||||
| bool ContainsFilter::accepts(const QString& value) | bool ContainsFilter::accepts(const QString& value) | ||||||
| { | { | ||||||
|     return value.contains(pattern); |     return value.contains(pattern); | ||||||
| } | } | ||||||
|  |  | ||||||
| ExactFilter::ExactFilter(const QString& pattern) : pattern(pattern){} | ExactFilter::ExactFilter(const QString& pattern) : pattern(pattern) {} | ||||||
| ExactFilter::~ExactFilter(){} | ExactFilter::~ExactFilter() {} | ||||||
| bool ExactFilter::accepts(const QString& value) | bool ExactFilter::accepts(const QString& value) | ||||||
| { | { | ||||||
|     return value == pattern; |     return value == pattern; | ||||||
| } | } | ||||||
|  |  | ||||||
| RegexpFilter::RegexpFilter(const QString& regexp, bool invert) | ExactIfPresentFilter::ExactIfPresentFilter(const QString& pattern) : pattern(pattern) {} | ||||||
|     :invert(invert) | bool ExactIfPresentFilter::accepts(const QString& value) | ||||||
|  | { | ||||||
|  |     return value.isEmpty() || value == pattern; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | RegexpFilter::RegexpFilter(const QString& regexp, bool invert) : invert(invert) | ||||||
| { | { | ||||||
|     pattern.setPattern(regexp); |     pattern.setPattern(regexp); | ||||||
|     pattern.optimize(); |     pattern.optimize(); | ||||||
| } | } | ||||||
| RegexpFilter::~RegexpFilter(){} | RegexpFilter::~RegexpFilter() {} | ||||||
| bool RegexpFilter::accepts(const QString& value) | bool RegexpFilter::accepts(const QString& value) | ||||||
| { | { | ||||||
|     auto match = pattern.match(value); |     auto match = pattern.match(value); | ||||||
|   | |||||||
| @@ -1,42 +1,51 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <QString> |  | ||||||
| #include <QRegularExpression> | #include <QRegularExpression> | ||||||
|  | #include <QString> | ||||||
|  |  | ||||||
| class Filter | class Filter { | ||||||
| { |    public: | ||||||
| public: |  | ||||||
|     virtual ~Filter(); |     virtual ~Filter(); | ||||||
|     virtual bool accepts(const QString & value) = 0; |     virtual bool accepts(const QString& value) = 0; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class ContainsFilter: public Filter | class ContainsFilter : public Filter { | ||||||
| { |    public: | ||||||
| public: |     ContainsFilter(const QString& pattern); | ||||||
|     ContainsFilter(const QString &pattern); |  | ||||||
|     virtual ~ContainsFilter(); |     virtual ~ContainsFilter(); | ||||||
|     bool accepts(const QString & value) override; |     bool accepts(const QString& value) override; | ||||||
| private: |  | ||||||
|  |    private: | ||||||
|     QString pattern; |     QString pattern; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class ExactFilter: public Filter | class ExactFilter : public Filter { | ||||||
| { |    public: | ||||||
| public: |     ExactFilter(const QString& pattern); | ||||||
|     ExactFilter(const QString &pattern); |  | ||||||
|     virtual ~ExactFilter(); |     virtual ~ExactFilter(); | ||||||
|     bool accepts(const QString & value) override; |     bool accepts(const QString& value) override; | ||||||
| private: |  | ||||||
|  |    private: | ||||||
|     QString pattern; |     QString pattern; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class RegexpFilter: public Filter | class ExactIfPresentFilter : public Filter { | ||||||
| { |    public: | ||||||
| public: |     ExactIfPresentFilter(const QString& pattern); | ||||||
|     RegexpFilter(const QString ®exp, bool invert); |     ~ExactIfPresentFilter() override = default; | ||||||
|  |     bool accepts(const QString& value) override; | ||||||
|  |  | ||||||
|  |    private: | ||||||
|  |     QString pattern; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class RegexpFilter : public Filter { | ||||||
|  |    public: | ||||||
|  |     RegexpFilter(const QString& regexp, bool invert); | ||||||
|     virtual ~RegexpFilter(); |     virtual ~RegexpFilter(); | ||||||
|     bool accepts(const QString & value) override; |     bool accepts(const QString& value) override; | ||||||
| private: |  | ||||||
|  |    private: | ||||||
|     QRegularExpression pattern; |     QRegularExpression pattern; | ||||||
|     bool invert = false; |     bool invert = false; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  PolyMC - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
| @@ -37,10 +37,9 @@ | |||||||
| #include <zlib.h> | #include <zlib.h> | ||||||
| #include <QByteArray> | #include <QByteArray> | ||||||
|  |  | ||||||
| bool GZip::unzip(const QByteArray &compressedBytes, QByteArray &uncompressedBytes) | bool GZip::unzip(const QByteArray& compressedBytes, QByteArray& uncompressedBytes) | ||||||
| { | { | ||||||
|     if (compressedBytes.size() == 0) |     if (compressedBytes.size() == 0) { | ||||||
|     { |  | ||||||
|         uncompressedBytes = compressedBytes; |         uncompressedBytes = compressedBytes; | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| @@ -51,42 +50,37 @@ bool GZip::unzip(const QByteArray &compressedBytes, QByteArray &uncompressedByte | |||||||
|  |  | ||||||
|     z_stream strm; |     z_stream strm; | ||||||
|     memset(&strm, 0, sizeof(strm)); |     memset(&strm, 0, sizeof(strm)); | ||||||
|     strm.next_in = (Bytef *)compressedBytes.data(); |     strm.next_in = (Bytef*)compressedBytes.data(); | ||||||
|     strm.avail_in = compressedBytes.size(); |     strm.avail_in = compressedBytes.size(); | ||||||
|  |  | ||||||
|     bool done = false; |     bool done = false; | ||||||
|  |  | ||||||
|     if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK) |     if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK) { | ||||||
|     { |  | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     int err = Z_OK; |     int err = Z_OK; | ||||||
|  |  | ||||||
|     while (!done) |     while (!done) { | ||||||
|     { |  | ||||||
|         // If our output buffer is too small |         // If our output buffer is too small | ||||||
|         if (strm.total_out >= uncompLength) |         if (strm.total_out >= uncompLength) { | ||||||
|         { |  | ||||||
|             uncompressedBytes.resize(uncompLength * 2); |             uncompressedBytes.resize(uncompLength * 2); | ||||||
|             uncompLength *= 2; |             uncompLength *= 2; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         strm.next_out = reinterpret_cast<Bytef *>((uncompressedBytes.data() + strm.total_out)); |         strm.next_out = reinterpret_cast<Bytef*>((uncompressedBytes.data() + strm.total_out)); | ||||||
|         strm.avail_out = uncompLength - strm.total_out; |         strm.avail_out = uncompLength - strm.total_out; | ||||||
|  |  | ||||||
|         // Inflate another chunk. |         // Inflate another chunk. | ||||||
|         err = inflate(&strm, Z_SYNC_FLUSH); |         err = inflate(&strm, Z_SYNC_FLUSH); | ||||||
|         if (err == Z_STREAM_END) |         if (err == Z_STREAM_END) | ||||||
|             done = true; |             done = true; | ||||||
|         else if (err != Z_OK) |         else if (err != Z_OK) { | ||||||
|         { |  | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (inflateEnd(&strm) != Z_OK || !done) |     if (inflateEnd(&strm) != Z_OK || !done) { | ||||||
|     { |  | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -94,10 +88,9 @@ bool GZip::unzip(const QByteArray &compressedBytes, QByteArray &uncompressedByte | |||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes) | bool GZip::zip(const QByteArray& uncompressedBytes, QByteArray& compressedBytes) | ||||||
| { | { | ||||||
|     if (uncompressedBytes.size() == 0) |     if (uncompressedBytes.size() == 0) { | ||||||
|     { |  | ||||||
|         compressedBytes = uncompressedBytes; |         compressedBytes = uncompressedBytes; | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| @@ -109,8 +102,7 @@ bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes) | |||||||
|     z_stream zs; |     z_stream zs; | ||||||
|     memset(&zs, 0, sizeof(zs)); |     memset(&zs, 0, sizeof(zs)); | ||||||
|  |  | ||||||
|     if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (16 + MAX_WBITS), 8, Z_DEFAULT_STRATEGY) != Z_OK) |     if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (16 + MAX_WBITS), 8, Z_DEFAULT_STRATEGY) != Z_OK) { | ||||||
|     { |  | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -122,11 +114,9 @@ bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes) | |||||||
|  |  | ||||||
|     unsigned offset = 0; |     unsigned offset = 0; | ||||||
|     unsigned temp = 0; |     unsigned temp = 0; | ||||||
|     do |     do { | ||||||
|     { |  | ||||||
|         auto remaining = compressedBytes.size() - offset; |         auto remaining = compressedBytes.size() - offset; | ||||||
|         if(remaining < 1) |         if (remaining < 1) { | ||||||
|         { |  | ||||||
|             compressedBytes.resize(compressedBytes.size() * 2); |             compressedBytes.resize(compressedBytes.size() * 2); | ||||||
|         } |         } | ||||||
|         zs.next_out = reinterpret_cast<Bytef*>((compressedBytes.data() + offset)); |         zs.next_out = reinterpret_cast<Bytef*>((compressedBytes.data() + offset)); | ||||||
| @@ -137,13 +127,11 @@ bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes) | |||||||
|  |  | ||||||
|     compressedBytes.resize(offset); |     compressedBytes.resize(offset); | ||||||
|  |  | ||||||
|     if (deflateEnd(&zs) != Z_OK) |     if (deflateEnd(&zs) != Z_OK) { | ||||||
|     { |  | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (ret != Z_STREAM_END) |     if (ret != Z_STREAM_END) { | ||||||
|     { |  | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|     return true; |     return true; | ||||||
|   | |||||||
| @@ -1,10 +1,8 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include <QByteArray> | #include <QByteArray> | ||||||
|  |  | ||||||
| class GZip | class GZip { | ||||||
| { |    public: | ||||||
| public: |     static bool unzip(const QByteArray& compressedBytes, QByteArray& uncompressedBytes); | ||||||
|     static bool unzip(const QByteArray &compressedBytes, QByteArray &uncompressedBytes); |     static bool zip(const QByteArray& uncompressedBytes, QByteArray& compressedBytes); | ||||||
|     static bool zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes); |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,17 +6,10 @@ | |||||||
|  |  | ||||||
| bool InstanceCopyPrefs::allTrue() const | bool InstanceCopyPrefs::allTrue() const | ||||||
| { | { | ||||||
|     return copySaves && |     return copySaves && keepPlaytime && copyGameOptions && copyResourcePacks && copyShaderPacks && copyServers && copyMods && | ||||||
|         keepPlaytime && |            copyScreenshots; | ||||||
|         copyGameOptions && |  | ||||||
|         copyResourcePacks && |  | ||||||
|         copyShaderPacks && |  | ||||||
|         copyServers && |  | ||||||
|         copyMods && |  | ||||||
|         copyScreenshots; |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| // Returns a single RegEx string of the selected folders/files to filter out (ex: ".minecraft/saves|.minecraft/server.dat") | // Returns a single RegEx string of the selected folders/files to filter out (ex: ".minecraft/saves|.minecraft/server.dat") | ||||||
| QString InstanceCopyPrefs::getSelectedFiltersAsRegex() const | QString InstanceCopyPrefs::getSelectedFiltersAsRegex() const | ||||||
| { | { | ||||||
| @@ -26,25 +19,30 @@ QString InstanceCopyPrefs::getSelectedFiltersAsRegex(const QStringList& addition | |||||||
| { | { | ||||||
|     QStringList filters; |     QStringList filters; | ||||||
|  |  | ||||||
|     if(!copySaves) |     if (!copySaves) | ||||||
|         filters << "saves"; |         filters << "saves"; | ||||||
|  |  | ||||||
|     if(!copyGameOptions) |     if (!copyGameOptions) | ||||||
|         filters << "options.txt"; |         filters << "options.txt"; | ||||||
|  |  | ||||||
|     if(!copyResourcePacks) |     if (!copyResourcePacks) | ||||||
|         filters << "resourcepacks" << "texturepacks"; |         filters << "resourcepacks" | ||||||
|  |                 << "texturepacks"; | ||||||
|  |  | ||||||
|     if(!copyShaderPacks) |     if (!copyShaderPacks) | ||||||
|         filters << "shaderpacks"; |         filters << "shaderpacks"; | ||||||
|  |  | ||||||
|     if(!copyServers) |     if (!copyServers) | ||||||
|         filters << "servers.dat" << "servers.dat_old" << "server-resource-packs"; |         filters << "servers.dat" | ||||||
|  |                 << "servers.dat_old" | ||||||
|  |                 << "server-resource-packs"; | ||||||
|  |  | ||||||
|     if(!copyMods) |     if (!copyMods) | ||||||
|         filters << "coremods" << "mods" << "config"; |         filters << "coremods" | ||||||
|  |                 << "mods" | ||||||
|  |                 << "config"; | ||||||
|  |  | ||||||
|     if(!copyScreenshots) |     if (!copyScreenshots) | ||||||
|         filters << "screenshots"; |         filters << "screenshots"; | ||||||
|  |  | ||||||
|     for (auto filter : additionalFilters) { |     for (auto filter : additionalFilters) { | ||||||
|   | |||||||
| @@ -40,7 +40,7 @@ struct InstanceCopyPrefs { | |||||||
|     void enableDontLinkSaves(bool b); |     void enableDontLinkSaves(bool b); | ||||||
|     void enableUseClone(bool b); |     void enableUseClone(bool b); | ||||||
|  |  | ||||||
|    protected: // data |    protected:  // data | ||||||
|     bool copySaves = true; |     bool copySaves = true; | ||||||
|     bool keepPlaytime = true; |     bool keepPlaytime = true; | ||||||
|     bool copyGameOptions = true; |     bool copyGameOptions = true; | ||||||
|   | |||||||
| @@ -156,8 +156,9 @@ void InstanceCopyTask::copyFinished() | |||||||
|         allowed_symlinks.append(m_origInstance->gameRoot().toUtf8()); |         allowed_symlinks.append(m_origInstance->gameRoot().toUtf8()); | ||||||
|         allowed_symlinks.append("\n"); |         allowed_symlinks.append("\n"); | ||||||
|         if (allowed_symlinks_file.isSymLink()) |         if (allowed_symlinks_file.isSymLink()) | ||||||
|             FS::deletePath(allowed_symlinks_file |             FS::deletePath( | ||||||
|                                .filePath());  // we dont want to modify the original. also make sure the resulting file is not itself a link. |                 allowed_symlinks_file | ||||||
|  |                     .filePath());  // we dont want to modify the original. also make sure the resulting file is not itself a link. | ||||||
|  |  | ||||||
|         FS::write(allowed_symlinks_file.filePath(), allowed_symlinks); |         FS::write(allowed_symlinks_file.filePath(), allowed_symlinks); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -11,19 +11,18 @@ | |||||||
| #include "settings/SettingsObject.h" | #include "settings/SettingsObject.h" | ||||||
| #include "tasks/Task.h" | #include "tasks/Task.h" | ||||||
|  |  | ||||||
| class InstanceCopyTask : public InstanceTask | class InstanceCopyTask : public InstanceTask { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| public: |    public: | ||||||
|     explicit InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs); |     explicit InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs); | ||||||
|  |  | ||||||
| protected: |    protected: | ||||||
|     //! Entry point for tasks. |     //! Entry point for tasks. | ||||||
|     virtual void executeTask() override; |     virtual void executeTask() override; | ||||||
|     void copyFinished(); |     void copyFinished(); | ||||||
|     void copyAborted(); |     void copyAborted(); | ||||||
|  |  | ||||||
| private: |    private: | ||||||
|     /* data */ |     /* data */ | ||||||
|     InstancePtr m_origInstance; |     InstancePtr m_origInstance; | ||||||
|     QFuture<bool> m_copyFuture; |     QFuture<bool> m_copyFuture; | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  PolyMC - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  *  Copyright (c) 2022 flowln <flowlnlnln@gmail.com> |  *  Copyright (c) 2022 flowln <flowlnlnln@gmail.com> | ||||||
|  * |  * | ||||||
| @@ -45,11 +45,14 @@ | |||||||
| #include "icons/IconList.h" | #include "icons/IconList.h" | ||||||
| #include "icons/IconUtils.h" | #include "icons/IconUtils.h" | ||||||
|  |  | ||||||
| #include "modplatform/technic/TechnicPackProcessor.h" |  | ||||||
| #include "modplatform/modrinth/ModrinthInstanceCreationTask.h" |  | ||||||
| #include "modplatform/flame/FlameInstanceCreationTask.h" | #include "modplatform/flame/FlameInstanceCreationTask.h" | ||||||
|  | #include "modplatform/modrinth/ModrinthInstanceCreationTask.h" | ||||||
|  | #include "modplatform/technic/TechnicPackProcessor.h" | ||||||
|  |  | ||||||
| #include "settings/INISettingsObject.h" | #include "settings/INISettingsObject.h" | ||||||
|  | #include "tasks/Task.h" | ||||||
|  |  | ||||||
|  | #include "net/ApiDownload.h" | ||||||
|  |  | ||||||
| #include <QtConcurrentRun> | #include <QtConcurrentRun> | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
| @@ -88,25 +91,27 @@ void InstanceImportTask::executeTask() | |||||||
|         setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString())); |         setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString())); | ||||||
|         m_downloadRequired = true; |         m_downloadRequired = true; | ||||||
|  |  | ||||||
|         const QString path(m_sourceUrl.host() + '/' + m_sourceUrl.path()); |         downloadFromUrl(); | ||||||
|  |  | ||||||
|         auto entry = APPLICATION->metacache()->resolveEntry("general", path); |  | ||||||
|         entry->setStale(true); |  | ||||||
|         m_archivePath = entry->getFullPath(); |  | ||||||
|  |  | ||||||
|         m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network())); |  | ||||||
|         m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry)); |  | ||||||
|  |  | ||||||
|         connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded); |  | ||||||
|         connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged); |  | ||||||
|         connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propogateStepProgress); |  | ||||||
|         connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed); |  | ||||||
|         connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted); |  | ||||||
|  |  | ||||||
|         m_filesNetJob->start(); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void InstanceImportTask::downloadFromUrl() | ||||||
|  | { | ||||||
|  |     const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path(); | ||||||
|  |     auto entry = APPLICATION->metacache()->resolveEntry("general", path); | ||||||
|  |     entry->setStale(true); | ||||||
|  |     m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network())); | ||||||
|  |     m_filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry)); | ||||||
|  |     m_archivePath = entry->getFullPath(); | ||||||
|  |  | ||||||
|  |     connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded); | ||||||
|  |     connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged); | ||||||
|  |     connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress); | ||||||
|  |     connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed); | ||||||
|  |     connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted); | ||||||
|  |     m_filesNetJob->start(); | ||||||
|  | } | ||||||
|  |  | ||||||
| void InstanceImportTask::downloadSucceeded() | void InstanceImportTask::downloadSucceeded() | ||||||
| { | { | ||||||
|     processZipPack(); |     processZipPack(); | ||||||
| @@ -138,8 +143,7 @@ void InstanceImportTask::processZipPack() | |||||||
|  |  | ||||||
|     // open the zip and find relevant files in it |     // open the zip and find relevant files in it | ||||||
|     m_packZip.reset(new QuaZip(m_archivePath)); |     m_packZip.reset(new QuaZip(m_archivePath)); | ||||||
|     if (!m_packZip->open(QuaZip::mdUnzip)) |     if (!m_packZip->open(QuaZip::mdUnzip)) { | ||||||
|     { |  | ||||||
|         emitFailed(tr("Unable to open supplied modpack zip file.")); |         emitFailed(tr("Unable to open supplied modpack zip file.")); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @@ -153,44 +157,40 @@ void InstanceImportTask::processZipPack() | |||||||
|  |  | ||||||
|     // NOTE: Prioritize modpack platforms that aren't searched for recursively. |     // NOTE: Prioritize modpack platforms that aren't searched for recursively. | ||||||
|     // Especially Flame has a very common filename for its manifest, which may appear inside overrides for example |     // Especially Flame has a very common filename for its manifest, which may appear inside overrides for example | ||||||
|     if(modrinthFound) |     if (modrinthFound) { | ||||||
|     { |  | ||||||
|         // process as Modrinth pack |         // process as Modrinth pack | ||||||
|         qDebug() << "Modrinth:" << modrinthFound; |         qDebug() << "Modrinth:" << modrinthFound; | ||||||
|         m_modpackType = ModpackType::Modrinth; |         m_modpackType = ModpackType::Modrinth; | ||||||
|     } |     } else if (technicFound) { | ||||||
|     else if (technicFound) |  | ||||||
|     { |  | ||||||
|         // process as Technic pack |         // process as Technic pack | ||||||
|         qDebug() << "Technic:" << technicFound; |         qDebug() << "Technic:" << technicFound; | ||||||
|         extractDir.mkpath(".minecraft"); |         extractDir.mkpath(".minecraft"); | ||||||
|         extractDir.cd(".minecraft"); |         extractDir.cd(".minecraft"); | ||||||
|         m_modpackType = ModpackType::Technic; |         m_modpackType = ModpackType::Technic; | ||||||
|     } |     } else { | ||||||
|     else |         QStringList paths_to_ignore{ "overrides/" }; | ||||||
|     { |  | ||||||
|         QStringList paths_to_ignore { "overrides/" }; |  | ||||||
|  |  | ||||||
|         if (QString mmcRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg", paths_to_ignore); !mmcRoot.isNull()) { |         if (QString mmcRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg", paths_to_ignore); !mmcRoot.isNull()) { | ||||||
|             // process as MultiMC instance/pack |             // process as MultiMC instance/pack | ||||||
|             qDebug() << "MultiMC:" << mmcRoot; |             qDebug() << "MultiMC:" << mmcRoot; | ||||||
|             root = mmcRoot; |             root = mmcRoot; | ||||||
|             m_modpackType = ModpackType::MultiMC; |             m_modpackType = ModpackType::MultiMC; | ||||||
|         } else if (QString flameRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json", paths_to_ignore); !flameRoot.isNull()) { |         } else if (QString flameRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json", paths_to_ignore); | ||||||
|  |                    !flameRoot.isNull()) { | ||||||
|             // process as Flame pack |             // process as Flame pack | ||||||
|             qDebug() << "Flame:" << flameRoot; |             qDebug() << "Flame:" << flameRoot; | ||||||
|             root = flameRoot; |             root = flameRoot; | ||||||
|             m_modpackType = ModpackType::Flame; |             m_modpackType = ModpackType::Flame; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     if(m_modpackType == ModpackType::Unknown) |     if (m_modpackType == ModpackType::Unknown) { | ||||||
|     { |  | ||||||
|         emitFailed(tr("Archive does not contain a recognized modpack type.")); |         emitFailed(tr("Archive does not contain a recognized modpack type.")); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // make sure we extract just the pack |     // make sure we extract just the pack | ||||||
|     m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath()); |     m_extractFuture = | ||||||
|  |         QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath()); | ||||||
|     connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &InstanceImportTask::extractFinished); |     connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &InstanceImportTask::extractFinished); | ||||||
|     m_extractFutureWatcher.setFuture(m_extractFuture); |     m_extractFutureWatcher.setFuture(m_extractFuture); | ||||||
| } | } | ||||||
| @@ -210,37 +210,28 @@ void InstanceImportTask::extractFinished() | |||||||
|  |  | ||||||
|     qDebug() << "Fixing permissions for extracted pack files..."; |     qDebug() << "Fixing permissions for extracted pack files..."; | ||||||
|     QDirIterator it(extractDir, QDirIterator::Subdirectories); |     QDirIterator it(extractDir, QDirIterator::Subdirectories); | ||||||
|     while (it.hasNext()) |     while (it.hasNext()) { | ||||||
|     { |  | ||||||
|         auto filepath = it.next(); |         auto filepath = it.next(); | ||||||
|         QFileInfo file(filepath); |         QFileInfo file(filepath); | ||||||
|         auto permissions = QFile::permissions(filepath); |         auto permissions = QFile::permissions(filepath); | ||||||
|         auto origPermissions = permissions; |         auto origPermissions = permissions; | ||||||
|         if(file.isDir()) |         if (file.isDir()) { | ||||||
|         { |  | ||||||
|             // Folder +rwx for current user |             // Folder +rwx for current user | ||||||
|             permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser; |             permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser; | ||||||
|         } |         } else { | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             // File +rw for current user |             // File +rw for current user | ||||||
|             permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser; |             permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser; | ||||||
|         } |         } | ||||||
|         if(origPermissions != permissions) |         if (origPermissions != permissions) { | ||||||
|         { |             if (!QFile::setPermissions(filepath, permissions)) { | ||||||
|             if(!QFile::setPermissions(filepath, permissions)) |  | ||||||
|             { |  | ||||||
|                 logWarning(tr("Could not fix permissions for %1").arg(filepath)); |                 logWarning(tr("Could not fix permissions for %1").arg(filepath)); | ||||||
|             } |             } else { | ||||||
|             else |  | ||||||
|             { |  | ||||||
|                 qDebug() << "Fixed" << filepath; |                 qDebug() << "Fixed" << filepath; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     switch(m_modpackType) |     switch (m_modpackType) { | ||||||
|     { |  | ||||||
|         case ModpackType::MultiMC: |         case ModpackType::MultiMC: | ||||||
|             processMultiMC(); |             processMultiMC(); | ||||||
|             return; |             return; | ||||||
| @@ -276,7 +267,8 @@ void InstanceImportTask::processFlame() | |||||||
|         if (original_instance_id_it != m_extra_info.constEnd()) |         if (original_instance_id_it != m_extra_info.constEnd()) | ||||||
|             original_instance_id = original_instance_id_it.value(); |             original_instance_id = original_instance_id_it.value(); | ||||||
|  |  | ||||||
|         inst_creation_task = makeShared<FlameCreationTask>(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id); |         inst_creation_task = | ||||||
|  |             makeShared<FlameCreationTask>(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id); | ||||||
|     } else { |     } else { | ||||||
|         // FIXME: Find a way to get IDs in directly imported ZIPs |         // FIXME: Find a way to get IDs in directly imported ZIPs | ||||||
|         inst_creation_task = makeShared<FlameCreationTask>(m_stagingPath, m_globalSettings, m_parent, QString(), QString()); |         inst_creation_task = makeShared<FlameCreationTask>(m_stagingPath, m_globalSettings, m_parent, QString(), QString()); | ||||||
| @@ -286,14 +278,14 @@ void InstanceImportTask::processFlame() | |||||||
|     inst_creation_task->setIcon(m_instIcon); |     inst_creation_task->setIcon(m_instIcon); | ||||||
|     inst_creation_task->setGroup(m_instGroup); |     inst_creation_task->setGroup(m_instGroup); | ||||||
|     inst_creation_task->setConfirmUpdate(shouldConfirmUpdate()); |     inst_creation_task->setConfirmUpdate(shouldConfirmUpdate()); | ||||||
|      |  | ||||||
|     connect(inst_creation_task.get(), &Task::succeeded, this, [this, inst_creation_task] { |     connect(inst_creation_task.get(), &Task::succeeded, this, [this, inst_creation_task] { | ||||||
|         setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID()); |         setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID()); | ||||||
|         emitSucceeded(); |         emitSucceeded(); | ||||||
|     }); |     }); | ||||||
|     connect(inst_creation_task.get(), &Task::failed, this, &InstanceImportTask::emitFailed); |     connect(inst_creation_task.get(), &Task::failed, this, &InstanceImportTask::emitFailed); | ||||||
|     connect(inst_creation_task.get(), &Task::progress, this, &InstanceImportTask::setProgress); |     connect(inst_creation_task.get(), &Task::progress, this, &InstanceImportTask::setProgress); | ||||||
|     connect(inst_creation_task.get(), &Task::stepProgress, this, &InstanceImportTask::propogateStepProgress); |     connect(inst_creation_task.get(), &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress); | ||||||
|     connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus); |     connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus); | ||||||
|     connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails); |     connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails); | ||||||
|  |  | ||||||
| @@ -362,7 +354,8 @@ void InstanceImportTask::processModrinth() | |||||||
|         if (original_instance_id_it != m_extra_info.constEnd()) |         if (original_instance_id_it != m_extra_info.constEnd()) | ||||||
|             original_instance_id = original_instance_id_it.value(); |             original_instance_id = original_instance_id_it.value(); | ||||||
|  |  | ||||||
|         inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id); |         inst_creation_task = | ||||||
|  |             new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id); | ||||||
|     } else { |     } else { | ||||||
|         QString pack_id; |         QString pack_id; | ||||||
|         if (!m_sourceUrl.isEmpty()) { |         if (!m_sourceUrl.isEmpty()) { | ||||||
| @@ -378,14 +371,14 @@ void InstanceImportTask::processModrinth() | |||||||
|     inst_creation_task->setIcon(m_instIcon); |     inst_creation_task->setIcon(m_instIcon); | ||||||
|     inst_creation_task->setGroup(m_instGroup); |     inst_creation_task->setGroup(m_instGroup); | ||||||
|     inst_creation_task->setConfirmUpdate(shouldConfirmUpdate()); |     inst_creation_task->setConfirmUpdate(shouldConfirmUpdate()); | ||||||
|      |  | ||||||
|     connect(inst_creation_task, &Task::succeeded, this, [this, inst_creation_task] { |     connect(inst_creation_task, &Task::succeeded, this, [this, inst_creation_task] { | ||||||
|         setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID()); |         setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID()); | ||||||
|         emitSucceeded(); |         emitSucceeded(); | ||||||
|     }); |     }); | ||||||
|     connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed); |     connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed); | ||||||
|     connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress); |     connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress); | ||||||
|     connect(inst_creation_task, &Task::stepProgress, this, &InstanceImportTask::propogateStepProgress); |     connect(inst_creation_task, &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress); | ||||||
|     connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus); |     connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus); | ||||||
|     connect(inst_creation_task, &Task::details, this, &InstanceImportTask::setDetails); |     connect(inst_creation_task, &Task::details, this, &InstanceImportTask::setDetails); | ||||||
|     connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater); |     connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater); | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  PolyMC - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
| @@ -35,54 +35,49 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "InstanceTask.h" |  | ||||||
| #include "net/NetJob.h" |  | ||||||
| #include <QUrl> |  | ||||||
| #include <QFuture> | #include <QFuture> | ||||||
| #include <QFutureWatcher> | #include <QFutureWatcher> | ||||||
| #include "settings/SettingsObject.h" | #include <QUrl> | ||||||
|  | #include "InstanceTask.h" | ||||||
| #include "QObjectPtr.h" | #include "QObjectPtr.h" | ||||||
| #include "modplatform/flame/PackManifest.h" | #include "modplatform/flame/PackManifest.h" | ||||||
|  | #include "net/NetJob.h" | ||||||
|  | #include "settings/SettingsObject.h" | ||||||
|  |  | ||||||
| #include <optional> | #include <optional> | ||||||
|  |  | ||||||
| class QuaZip; | class QuaZip; | ||||||
| namespace Flame | namespace Flame { | ||||||
| { | class FileResolvingTask; | ||||||
|     class FileResolvingTask; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| class InstanceImportTask : public InstanceTask | class InstanceImportTask : public InstanceTask { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| public: |    public: | ||||||
|     explicit InstanceImportTask(const QUrl sourceUrl, QWidget* parent = nullptr, QMap<QString, QString>&& extra_info = {}); |     explicit InstanceImportTask(const QUrl sourceUrl, QWidget* parent = nullptr, QMap<QString, QString>&& extra_info = {}); | ||||||
|  |  | ||||||
|     bool abort() override; |     bool abort() override; | ||||||
|     const QVector<Flame::File> &getBlockedFiles() const |     const QVector<Flame::File>& getBlockedFiles() const { return m_blockedMods; } | ||||||
|     { |  | ||||||
|         return m_blockedMods; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| protected: |    protected: | ||||||
|     //! Entry point for tasks. |     //! Entry point for tasks. | ||||||
|     virtual void executeTask() override; |     virtual void executeTask() override; | ||||||
|  |  | ||||||
| private: |    private: | ||||||
|     void processZipPack(); |     void processZipPack(); | ||||||
|     void processMultiMC(); |     void processMultiMC(); | ||||||
|     void processTechnic(); |     void processTechnic(); | ||||||
|     void processFlame(); |     void processFlame(); | ||||||
|     void processModrinth(); |     void processModrinth(); | ||||||
|  |  | ||||||
| private slots: |    private slots: | ||||||
|     void downloadSucceeded(); |     void downloadSucceeded(); | ||||||
|     void downloadFailed(QString reason); |     void downloadFailed(QString reason); | ||||||
|     void downloadProgressChanged(qint64 current, qint64 total); |     void downloadProgressChanged(qint64 current, qint64 total); | ||||||
|     void downloadAborted(); |     void downloadAborted(); | ||||||
|     void extractFinished(); |     void extractFinished(); | ||||||
|  |  | ||||||
| private: /* data */ |    private: /* data */ | ||||||
|     NetJob::Ptr m_filesNetJob; |     NetJob::Ptr m_filesNetJob; | ||||||
|     shared_qobject_ptr<Flame::FileResolvingTask> m_modIdResolver; |     shared_qobject_ptr<Flame::FileResolvingTask> m_modIdResolver; | ||||||
|     QUrl m_sourceUrl; |     QUrl m_sourceUrl; | ||||||
| @@ -92,7 +87,7 @@ private: /* data */ | |||||||
|     QFuture<std::optional<QStringList>> m_extractFuture; |     QFuture<std::optional<QStringList>> m_extractFuture; | ||||||
|     QFutureWatcher<std::optional<QStringList>> m_extractFutureWatcher; |     QFutureWatcher<std::optional<QStringList>> m_extractFutureWatcher; | ||||||
|     QVector<Flame::File> m_blockedMods; |     QVector<Flame::File> m_blockedMods; | ||||||
|     enum class ModpackType{ |     enum class ModpackType { | ||||||
|         Unknown, |         Unknown, | ||||||
|         MultiMC, |         MultiMC, | ||||||
|         Technic, |         Technic, | ||||||
| @@ -104,6 +99,7 @@ private: /* data */ | |||||||
|     // the source URL / the resource it points to alone. |     // the source URL / the resource it points to alone. | ||||||
|     QMap<QString, QString> m_extra_info; |     QMap<QString, QString> m_extra_info; | ||||||
|  |  | ||||||
|     //FIXME: nuke |     // FIXME: nuke | ||||||
|     QWidget* m_parent; |     QWidget* m_parent; | ||||||
|  |     void downloadFromUrl(); | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  PolyMC - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
| @@ -41,9 +41,9 @@ | |||||||
| #include <QJsonArray> | #include <QJsonArray> | ||||||
| #include <QJsonDocument> | #include <QJsonDocument> | ||||||
| #include <QMimeData> | #include <QMimeData> | ||||||
|  | #include <QPair> | ||||||
| #include <QSet> | #include <QSet> | ||||||
| #include <QStack> | #include <QStack> | ||||||
| #include <QPair> |  | ||||||
| #include <QTextStream> | #include <QTextStream> | ||||||
| #include <QThread> | #include <QThread> | ||||||
| #include <QTimer> | #include <QTimer> | ||||||
| @@ -96,7 +96,11 @@ Qt::DropActions InstanceList::supportedDropActions() const | |||||||
|     return Qt::MoveAction; |     return Qt::MoveAction; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool InstanceList::canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const | bool InstanceList::canDropMimeData(const QMimeData* data, | ||||||
|  |                                    [[maybe_unused]] Qt::DropAction action, | ||||||
|  |                                    [[maybe_unused]] int row, | ||||||
|  |                                    [[maybe_unused]] int column, | ||||||
|  |                                    [[maybe_unused]] const QModelIndex& parent) const | ||||||
| { | { | ||||||
|     if (data && data->hasFormat("application/x-instanceid")) { |     if (data && data->hasFormat("application/x-instanceid")) { | ||||||
|         return true; |         return true; | ||||||
| @@ -104,7 +108,11 @@ bool InstanceList::canDropMimeData(const QMimeData* data, Qt::DropAction action, | |||||||
|     return false; |     return false; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool InstanceList::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) | bool InstanceList::dropMimeData(const QMimeData* data, | ||||||
|  |                                 [[maybe_unused]] Qt::DropAction action, | ||||||
|  |                                 [[maybe_unused]] int row, | ||||||
|  |                                 [[maybe_unused]] int column, | ||||||
|  |                                 [[maybe_unused]] const QModelIndex& parent) | ||||||
| { | { | ||||||
|     if (data && data->hasFormat("application/x-instanceid")) { |     if (data && data->hasFormat("application/x-instanceid")) { | ||||||
|         return true; |         return true; | ||||||
| @@ -129,7 +137,7 @@ QMimeData* InstanceList::mimeData(const QModelIndexList& indexes) const | |||||||
|     return mimeData; |     return mimeData; | ||||||
| } | } | ||||||
|  |  | ||||||
| QStringList InstanceList::getLinkedInstancesById(const QString &id) const | QStringList InstanceList::getLinkedInstancesById(const QString& id) const | ||||||
| { | { | ||||||
|     QStringList linkedInstances; |     QStringList linkedInstances; | ||||||
|     for (auto inst : m_instances) { |     for (auto inst : m_instances) { | ||||||
| @@ -158,42 +166,34 @@ QVariant InstanceList::data(const QModelIndex& index, int role) const | |||||||
|     if (!index.isValid()) { |     if (!index.isValid()) { | ||||||
|         return QVariant(); |         return QVariant(); | ||||||
|     } |     } | ||||||
|     BaseInstance *pdata = static_cast<BaseInstance *>(index.internalPointer()); |     BaseInstance* pdata = static_cast<BaseInstance*>(index.internalPointer()); | ||||||
|     switch (role) |     switch (role) { | ||||||
|     { |         case InstancePointerRole: { | ||||||
|     case InstancePointerRole: |             QVariant v = QVariant::fromValue((void*)pdata); | ||||||
|     { |             return v; | ||||||
|         QVariant v = QVariant::fromValue((void *)pdata); |         } | ||||||
|         return v; |         case InstanceIDRole: { | ||||||
|     } |             return pdata->id(); | ||||||
|     case InstanceIDRole: |         } | ||||||
|     { |         case Qt::EditRole: | ||||||
|         return pdata->id(); |         case Qt::DisplayRole: { | ||||||
|     } |             return pdata->name(); | ||||||
|     case Qt::EditRole: |         } | ||||||
|     case Qt::DisplayRole: |         case Qt::AccessibleTextRole: { | ||||||
|     { |             return tr("%1 Instance").arg(pdata->name()); | ||||||
|         return pdata->name(); |         } | ||||||
|     } |         case Qt::ToolTipRole: { | ||||||
|     case Qt::AccessibleTextRole: |             return pdata->instanceRoot(); | ||||||
|     { |         } | ||||||
|         return tr("%1 Instance").arg(pdata->name()); |         case Qt::DecorationRole: { | ||||||
|     } |             return pdata->iconKey(); | ||||||
|     case Qt::ToolTipRole: |         } | ||||||
|     { |         // HACK: see InstanceView.h in gui! | ||||||
|         return pdata->instanceRoot(); |         case GroupRole: { | ||||||
|     } |             return getInstanceGroup(pdata->id()); | ||||||
|     case Qt::DecorationRole: |         } | ||||||
|     { |         default: | ||||||
|         return pdata->iconKey(); |             break; | ||||||
|     } |  | ||||||
|     // HACK: see InstanceView.h in gui! |  | ||||||
|     case GroupRole: |  | ||||||
|     { |  | ||||||
|         return getInstanceGroup(pdata->id()); |  | ||||||
|     } |  | ||||||
|     default: |  | ||||||
|         break; |  | ||||||
|     } |     } | ||||||
|     return QVariant(); |     return QVariant(); | ||||||
| } | } | ||||||
| @@ -320,16 +320,18 @@ bool InstanceList::trashInstance(const InstanceId& id) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     qDebug() << "Instance" << id << "has been trashed by the launcher."; |     qDebug() << "Instance" << id << "has been trashed by the launcher."; | ||||||
|     m_trashHistory.push({id, inst->instanceRoot(), trashedLoc, cachedGroupId}); |     m_trashHistory.push({ id, inst->instanceRoot(), trashedLoc, cachedGroupId }); | ||||||
|      |  | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool InstanceList::trashedSomething() { | bool InstanceList::trashedSomething() | ||||||
|  | { | ||||||
|     return !m_trashHistory.empty(); |     return !m_trashHistory.empty(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void InstanceList::undoTrashInstance() { | void InstanceList::undoTrashInstance() | ||||||
|  | { | ||||||
|     if (m_trashHistory.empty()) { |     if (m_trashHistory.empty()) { | ||||||
|         qWarning() << "Nothing to recover from trash."; |         qWarning() << "Nothing to recover from trash."; | ||||||
|         return; |         return; | ||||||
| @@ -558,7 +560,7 @@ InstancePtr InstanceList::getInstanceByManagedName(const QString& managed_name) | |||||||
|     return {}; |     return {}; | ||||||
| } | } | ||||||
|  |  | ||||||
| QModelIndex InstanceList::getInstanceIndexById(const QString &id) const | QModelIndex InstanceList::getInstanceIndexById(const QString& id) const | ||||||
| { | { | ||||||
|     return index(getInstIndex(getInstanceById(id).get())); |     return index(getInstIndex(getInstanceById(id).get())); | ||||||
| } | } | ||||||
| @@ -597,13 +599,11 @@ InstancePtr InstanceList::loadInstance(const InstanceId& id) | |||||||
|  |  | ||||||
|     QString inst_type = instanceSettings->get("InstanceType").toString(); |     QString inst_type = instanceSettings->get("InstanceType").toString(); | ||||||
|  |  | ||||||
|     // NOTE: Some PolyMC versions didn't save the InstanceType properly. We will just bank on the probability that this is probably a OneSix instance |     // NOTE: Some PolyMC versions didn't save the InstanceType properly. We will just bank on the probability that this is probably a OneSix | ||||||
|     if (inst_type == "OneSix" || inst_type.isEmpty()) |     // instance | ||||||
|     { |     if (inst_type == "OneSix" || inst_type.isEmpty()) { | ||||||
|         inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot)); |         inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot)); | ||||||
|     } |     } else { | ||||||
|     else |  | ||||||
|     { |  | ||||||
|         inst.reset(new NullInstance(m_globalSettings, instanceSettings, instanceRoot)); |         inst.reset(new NullInstance(m_globalSettings, instanceSettings, instanceRoot)); | ||||||
|     } |     } | ||||||
|     qDebug() << "Loaded instance " << inst->name() << " from " << inst->instanceRoot(); |     qDebug() << "Loaded instance " << inst->name() << " from " << inst->instanceRoot(); | ||||||
| @@ -759,7 +759,7 @@ void InstanceList::instanceDirContentsChanged(const QString& path) | |||||||
|     emit instancesChanged(); |     emit instancesChanged(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void InstanceList::on_InstFolderChanged(const Setting& setting, QVariant value) | void InstanceList::on_InstFolderChanged([[maybe_unused]] const Setting& setting, QVariant value) | ||||||
| { | { | ||||||
|     QString newInstDir = QDir(value.toString()).canonicalPath(); |     QString newInstDir = QDir(value.toString()).canonicalPath(); | ||||||
|     if (newInstDir != m_instDir) { |     if (newInstDir != m_instDir) { | ||||||
| @@ -787,20 +787,25 @@ class InstanceStaging : public Task { | |||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
|     const unsigned minBackoff = 1; |     const unsigned minBackoff = 1; | ||||||
|     const unsigned maxBackoff = 16; |     const unsigned maxBackoff = 16; | ||||||
|  |  | ||||||
|    public: |    public: | ||||||
|     InstanceStaging(InstanceList* parent, InstanceTask* child, QString stagingPath, InstanceName const& instanceName, QString groupName) |     InstanceStaging(InstanceList* parent, InstanceTask* child, QString stagingPath, InstanceName const& instanceName, QString groupName) | ||||||
|         : m_parent(parent), backoff(minBackoff, maxBackoff), m_stagingPath(std::move(stagingPath)), m_instance_name(std::move(instanceName)), m_groupName(std::move(groupName)) |         : m_parent(parent) | ||||||
|  |         , backoff(minBackoff, maxBackoff) | ||||||
|  |         , m_stagingPath(std::move(stagingPath)) | ||||||
|  |         , m_instance_name(std::move(instanceName)) | ||||||
|  |         , m_groupName(std::move(groupName)) | ||||||
|     { |     { | ||||||
|         m_child.reset(child); |         m_child.reset(child); | ||||||
|         connect(child, &Task::succeeded, this, &InstanceStaging::childSucceded); |         connect(child, &Task::succeeded, this, &InstanceStaging::childSucceeded); | ||||||
|         connect(child, &Task::failed, this, &InstanceStaging::childFailed); |         connect(child, &Task::failed, this, &InstanceStaging::childFailed); | ||||||
|         connect(child, &Task::aborted, this, &InstanceStaging::childAborted); |         connect(child, &Task::aborted, this, &InstanceStaging::childAborted); | ||||||
|         connect(child, &Task::abortStatusChanged, this, &InstanceStaging::setAbortable); |         connect(child, &Task::abortStatusChanged, this, &InstanceStaging::setAbortable); | ||||||
|         connect(child, &Task::status, this, &InstanceStaging::setStatus); |         connect(child, &Task::status, this, &InstanceStaging::setStatus); | ||||||
|         connect(child, &Task::details, this, &InstanceStaging::setDetails); |         connect(child, &Task::details, this, &InstanceStaging::setDetails); | ||||||
|         connect(child, &Task::progress, this, &InstanceStaging::setProgress); |         connect(child, &Task::progress, this, &InstanceStaging::setProgress); | ||||||
|         connect(child, &Task::stepProgress, this, &InstanceStaging::propogateStepProgress); |         connect(child, &Task::stepProgress, this, &InstanceStaging::propagateStepProgress); | ||||||
|         connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceded); |         connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceeded); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     virtual ~InstanceStaging(){}; |     virtual ~InstanceStaging(){}; | ||||||
| @@ -815,21 +820,17 @@ class InstanceStaging : public Task { | |||||||
|  |  | ||||||
|         return Task::abort(); |         return Task::abort(); | ||||||
|     } |     } | ||||||
|     bool canAbort() const override |     bool canAbort() const override { return (m_child && m_child->canAbort()); } | ||||||
|     { |  | ||||||
|         return (m_child && m_child->canAbort()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|    protected: |    protected: | ||||||
|     virtual void executeTask() override { m_child->start(); } |     virtual void executeTask() override { m_child->start(); } | ||||||
|     QStringList warnings() const override { return m_child->warnings(); } |     QStringList warnings() const override { return m_child->warnings(); } | ||||||
|  |  | ||||||
|    private slots: |    private slots: | ||||||
|     void childSucceded() |     void childSucceeded() | ||||||
|     { |     { | ||||||
|         unsigned sleepTime = backoff(); |         unsigned sleepTime = backoff(); | ||||||
|         if (m_parent->commitStagedInstance(m_stagingPath, m_instance_name, m_groupName, *m_child.get())) |         if (m_parent->commitStagedInstance(m_stagingPath, m_instance_name, m_groupName, *m_child.get())) { | ||||||
|         { |  | ||||||
|             emitSucceeded(); |             emitSucceeded(); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| @@ -847,13 +848,10 @@ class InstanceStaging : public Task { | |||||||
|         emitFailed(reason); |         emitFailed(reason); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void childAborted() |     void childAborted() { emitAborted(); } | ||||||
|     { |  | ||||||
|         emitAborted(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| private: |    private: | ||||||
|     InstanceList * m_parent; |     InstanceList* m_parent; | ||||||
|     /* |     /* | ||||||
|      * WHY: the whole reason why this uses an exponential backoff retry scheme is antivirus on Windows. |      * WHY: the whole reason why this uses an exponential backoff retry scheme is antivirus on Windows. | ||||||
|      * Basically, it starts messing things up while the launcher is extracting/creating instances |      * Basically, it starts messing things up while the launcher is extracting/creating instances | ||||||
| @@ -892,7 +890,10 @@ QString InstanceList::getStagedInstancePath() | |||||||
|     return path; |     return path; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool InstanceList::commitStagedInstance(const QString& path, InstanceName const& instanceName, const QString& groupName, InstanceTask const& commiting) | bool InstanceList::commitStagedInstance(const QString& path, | ||||||
|  |                                         InstanceName const& instanceName, | ||||||
|  |                                         const QString& groupName, | ||||||
|  |                                         InstanceTask const& commiting) | ||||||
| { | { | ||||||
|     QDir dir; |     QDir dir; | ||||||
|     QString instID; |     QString instID; | ||||||
|   | |||||||
| @@ -15,12 +15,12 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <QObject> |  | ||||||
| #include <QAbstractListModel> | #include <QAbstractListModel> | ||||||
| #include <QSet> |  | ||||||
| #include <QList> | #include <QList> | ||||||
| #include <QStack> | #include <QObject> | ||||||
| #include <QPair> | #include <QPair> | ||||||
|  | #include <QSet> | ||||||
|  | #include <QStack> | ||||||
|  |  | ||||||
| #include "BaseInstance.h" | #include "BaseInstance.h" | ||||||
|  |  | ||||||
| @@ -32,21 +32,9 @@ using InstanceId = QString; | |||||||
| using GroupId = QString; | using GroupId = QString; | ||||||
| using InstanceLocator = std::pair<InstancePtr, int>; | using InstanceLocator = std::pair<InstancePtr, int>; | ||||||
|  |  | ||||||
| enum class InstCreateError | enum class InstCreateError { NoCreateError = 0, NoSuchVersion, UnknownCreateError, InstExists, CantCreateDir }; | ||||||
| { |  | ||||||
|     NoCreateError = 0, |  | ||||||
|     NoSuchVersion, |  | ||||||
|     UnknownCreateError, |  | ||||||
|     InstExists, |  | ||||||
|     CantCreateDir |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| enum class GroupsState | enum class GroupsState { NotLoaded, Steady, Dirty }; | ||||||
| { |  | ||||||
|     NotLoaded, |  | ||||||
|     Steady, |  | ||||||
|     Dirty |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct TrashHistoryItem { | struct TrashHistoryItem { | ||||||
|     QString id; |     QString id; | ||||||
| @@ -55,48 +43,36 @@ struct TrashHistoryItem { | |||||||
|     QString groupName; |     QString groupName; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class InstanceList : public QAbstractListModel | class InstanceList : public QAbstractListModel { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
|  |  | ||||||
| public: |    public: | ||||||
|     explicit InstanceList(SettingsObjectPtr settings, const QString & instDir, QObject *parent = 0); |     explicit InstanceList(SettingsObjectPtr settings, const QString& instDir, QObject* parent = 0); | ||||||
|     virtual ~InstanceList(); |     virtual ~InstanceList(); | ||||||
|  |  | ||||||
| public: |    public: | ||||||
|     QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const override; |     QModelIndex index(int row, int column = 0, const QModelIndex& parent = QModelIndex()) const override; | ||||||
|     int rowCount(const QModelIndex &parent = QModelIndex()) const override; |     int rowCount(const QModelIndex& parent = QModelIndex()) const override; | ||||||
|     QVariant data(const QModelIndex &index, int role) const override; |     QVariant data(const QModelIndex& index, int role) const override; | ||||||
|     Qt::ItemFlags flags(const QModelIndex &index) const override; |     Qt::ItemFlags flags(const QModelIndex& index) const override; | ||||||
|  |  | ||||||
|     bool setData(const QModelIndex & index, const QVariant & value, int role) override; |     bool setData(const QModelIndex& index, const QVariant& value, int role) override; | ||||||
|  |  | ||||||
|     enum AdditionalRoles |     enum AdditionalRoles { | ||||||
|     { |  | ||||||
|         GroupRole = Qt::UserRole, |         GroupRole = Qt::UserRole, | ||||||
|         InstancePointerRole = 0x34B1CB48, ///< Return pointer to real instance |         InstancePointerRole = 0x34B1CB48,  ///< Return pointer to real instance | ||||||
|         InstanceIDRole = 0x34B1CB49 ///< Return id if the instance |         InstanceIDRole = 0x34B1CB49        ///< Return id if the instance | ||||||
|     }; |     }; | ||||||
|     /*! |     /*! | ||||||
|      * \brief Error codes returned by functions in the InstanceList class. |      * \brief Error codes returned by functions in the InstanceList class. | ||||||
|      * NoError Indicates that no error occurred. |      * NoError Indicates that no error occurred. | ||||||
|      * UnknownError indicates that an unspecified error occurred. |      * UnknownError indicates that an unspecified error occurred. | ||||||
|      */ |      */ | ||||||
|     enum InstListError |     enum InstListError { NoError = 0, UnknownError }; | ||||||
|     { |  | ||||||
|         NoError = 0, |  | ||||||
|         UnknownError |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     InstancePtr at(int i) const |     InstancePtr at(int i) const { return m_instances.at(i); } | ||||||
|     { |  | ||||||
|         return m_instances.at(i); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     int count() const |     int count() const { return m_instances.count(); } | ||||||
|     { |  | ||||||
|         return m_instances.count(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     InstListError loadList(); |     InstListError loadList(); | ||||||
|     void saveNow(); |     void saveNow(); | ||||||
| @@ -105,21 +81,21 @@ public: | |||||||
|     InstancePtr getInstanceById(QString id) const; |     InstancePtr getInstanceById(QString id) const; | ||||||
|     /* O(n) */ |     /* O(n) */ | ||||||
|     InstancePtr getInstanceByManagedName(const QString& managed_name) const; |     InstancePtr getInstanceByManagedName(const QString& managed_name) const; | ||||||
|     QModelIndex getInstanceIndexById(const QString &id) const; |     QModelIndex getInstanceIndexById(const QString& id) const; | ||||||
|     QStringList getGroups(); |     QStringList getGroups(); | ||||||
|     bool isGroupCollapsed(const QString &groupName); |     bool isGroupCollapsed(const QString& groupName); | ||||||
|  |  | ||||||
|     GroupId getInstanceGroup(const InstanceId & id) const; |     GroupId getInstanceGroup(const InstanceId& id) const; | ||||||
|     void setInstanceGroup(const InstanceId & id, const GroupId& name); |     void setInstanceGroup(const InstanceId& id, const GroupId& name); | ||||||
|  |  | ||||||
|     void deleteGroup(const GroupId & name); |     void deleteGroup(const GroupId& name); | ||||||
|     bool trashInstance(const InstanceId &id); |     bool trashInstance(const InstanceId& id); | ||||||
|     bool trashedSomething(); |     bool trashedSomething(); | ||||||
|     void undoTrashInstance(); |     void undoTrashInstance(); | ||||||
|     void deleteInstance(const InstanceId & id); |     void deleteInstance(const InstanceId& id); | ||||||
|  |  | ||||||
|     // Wrap an instance creation task in some more task machinery and make it ready to be used |     // Wrap an instance creation task in some more task machinery and make it ready to be used | ||||||
|     Task * wrapInstanceTask(InstanceTask * task); |     Task* wrapInstanceTask(InstanceTask* task); | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Create a new empty staging area for instance creation and @return a path/key top commit it later. |      * Create a new empty staging area for instance creation and @return a path/key top commit it later. | ||||||
| @@ -139,7 +115,7 @@ public: | |||||||
|      * Destroy a previously created staging area given by @keyPath - used when creation fails. |      * Destroy a previously created staging area given by @keyPath - used when creation fails. | ||||||
|      * Used by instance manipulation tasks. |      * Used by instance manipulation tasks. | ||||||
|      */ |      */ | ||||||
|     bool destroyStagingPath(const QString & keyPath); |     bool destroyStagingPath(const QString& keyPath); | ||||||
|  |  | ||||||
|     int getTotalPlayTime(); |     int getTotalPlayTime(); | ||||||
|  |  | ||||||
| @@ -147,42 +123,42 @@ public: | |||||||
|  |  | ||||||
|     Qt::DropActions supportedDropActions() const override; |     Qt::DropActions supportedDropActions() const override; | ||||||
|  |  | ||||||
|     bool canDropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) const override; |     bool canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const override; | ||||||
|  |  | ||||||
|     bool dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) override; |     bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override; | ||||||
|  |  | ||||||
|     QStringList mimeTypes() const override; |     QStringList mimeTypes() const override; | ||||||
|     QMimeData *mimeData(const QModelIndexList &indexes) const override; |     QMimeData* mimeData(const QModelIndexList& indexes) const override; | ||||||
|  |  | ||||||
|     QStringList getLinkedInstancesById(const QString &id) const; |     QStringList getLinkedInstancesById(const QString& id) const; | ||||||
|  |  | ||||||
| signals: |    signals: | ||||||
|     void dataIsInvalid(); |     void dataIsInvalid(); | ||||||
|     void instancesChanged(); |     void instancesChanged(); | ||||||
|     void instanceSelectRequest(QString instanceId); |     void instanceSelectRequest(QString instanceId); | ||||||
|     void groupsChanged(QSet<QString> groups); |     void groupsChanged(QSet<QString> groups); | ||||||
|  |  | ||||||
| public slots: |    public slots: | ||||||
|     void on_InstFolderChanged(const Setting &setting, QVariant value); |     void on_InstFolderChanged(const Setting& setting, QVariant value); | ||||||
|     void on_GroupStateChanged(const QString &group, bool collapsed); |     void on_GroupStateChanged(const QString& group, bool collapsed); | ||||||
|  |  | ||||||
| private slots: |    private slots: | ||||||
|     void propertiesChanged(BaseInstance *inst); |     void propertiesChanged(BaseInstance* inst); | ||||||
|     void providerUpdated(); |     void providerUpdated(); | ||||||
|     void instanceDirContentsChanged(const QString &path); |     void instanceDirContentsChanged(const QString& path); | ||||||
|  |  | ||||||
| private: |    private: | ||||||
|     int getInstIndex(BaseInstance *inst) const; |     int getInstIndex(BaseInstance* inst) const; | ||||||
|     void updateTotalPlayTime(); |     void updateTotalPlayTime(); | ||||||
|     void suspendWatch(); |     void suspendWatch(); | ||||||
|     void resumeWatch(); |     void resumeWatch(); | ||||||
|     void add(const QList<InstancePtr> &list); |     void add(const QList<InstancePtr>& list); | ||||||
|     void loadGroupList(); |     void loadGroupList(); | ||||||
|     void saveGroupList(); |     void saveGroupList(); | ||||||
|     QList<InstanceId> discoverInstances(); |     QList<InstanceId> discoverInstances(); | ||||||
|     InstancePtr loadInstance(const InstanceId& id); |     InstancePtr loadInstance(const InstanceId& id); | ||||||
|  |  | ||||||
| private: |    private: | ||||||
|     int m_watchLevel = 0; |     int m_watchLevel = 0; | ||||||
|     int totalPlayTime = 0; |     int totalPlayTime = 0; | ||||||
|     bool m_dirty = false; |     bool m_dirty = false; | ||||||
| @@ -191,7 +167,7 @@ private: | |||||||
|  |  | ||||||
|     SettingsObjectPtr m_globalSettings; |     SettingsObjectPtr m_globalSettings; | ||||||
|     QString m_instDir; |     QString m_instDir; | ||||||
|     QFileSystemWatcher * m_watcher; |     QFileSystemWatcher* m_watcher; | ||||||
|     // FIXME: this is so inefficient that looking at it is almost painful. |     // FIXME: this is so inefficient that looking at it is almost painful. | ||||||
|     QSet<QString> m_collapsedGroups; |     QSet<QString> m_collapsedGroups; | ||||||
|     QMap<InstanceId, GroupId> m_instanceGroupIndex; |     QMap<InstanceId, GroupId> m_instanceGroupIndex; | ||||||
|   | |||||||
| @@ -1,36 +1,31 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include "minecraft/MinecraftInstance.h" |  | ||||||
| #include <FileSystem.h> | #include <FileSystem.h> | ||||||
|  | #include "minecraft/MinecraftInstance.h" | ||||||
| #include "ui/pages/BasePage.h" | #include "ui/pages/BasePage.h" | ||||||
| #include "ui/pages/BasePageProvider.h" | #include "ui/pages/BasePageProvider.h" | ||||||
|  | #include "ui/pages/instance/InstanceSettingsPage.h" | ||||||
| #include "ui/pages/instance/LogPage.h" | #include "ui/pages/instance/LogPage.h" | ||||||
| #include "ui/pages/instance/VersionPage.h" |  | ||||||
| #include "ui/pages/instance/ManagedPackPage.h" | #include "ui/pages/instance/ManagedPackPage.h" | ||||||
| #include "ui/pages/instance/ModFolderPage.h" | #include "ui/pages/instance/ModFolderPage.h" | ||||||
| #include "ui/pages/instance/ResourcePackPage.h" |  | ||||||
| #include "ui/pages/instance/TexturePackPage.h" |  | ||||||
| #include "ui/pages/instance/ShaderPackPage.h" |  | ||||||
| #include "ui/pages/instance/NotesPage.h" | #include "ui/pages/instance/NotesPage.h" | ||||||
| #include "ui/pages/instance/ScreenshotsPage.h" |  | ||||||
| #include "ui/pages/instance/InstanceSettingsPage.h" |  | ||||||
| #include "ui/pages/instance/OtherLogsPage.h" | #include "ui/pages/instance/OtherLogsPage.h" | ||||||
| #include "ui/pages/instance/WorldListPage.h" | #include "ui/pages/instance/ResourcePackPage.h" | ||||||
|  | #include "ui/pages/instance/ScreenshotsPage.h" | ||||||
| #include "ui/pages/instance/ServersPage.h" | #include "ui/pages/instance/ServersPage.h" | ||||||
| #include "ui/pages/instance/GameOptionsPage.h" | #include "ui/pages/instance/ShaderPackPage.h" | ||||||
|  | #include "ui/pages/instance/TexturePackPage.h" | ||||||
|  | #include "ui/pages/instance/VersionPage.h" | ||||||
|  | #include "ui/pages/instance/WorldListPage.h" | ||||||
|  |  | ||||||
| class InstancePageProvider : public QObject, public BasePageProvider | class InstancePageProvider : protected QObject, public BasePageProvider { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| public: |    public: | ||||||
|     explicit InstancePageProvider(InstancePtr parent) |     explicit InstancePageProvider(InstancePtr parent) { inst = parent; } | ||||||
|     { |  | ||||||
|         inst = parent; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     virtual ~InstancePageProvider() {}; |     virtual ~InstancePageProvider(){}; | ||||||
|     virtual QList<BasePage *> getPages() override |     virtual QList<BasePage*> getPages() override | ||||||
|     { |     { | ||||||
|         QList<BasePage *> values; |         QList<BasePage*> values; | ||||||
|         values.append(new LogPage(inst)); |         values.append(new LogPage(inst)); | ||||||
|         std::shared_ptr<MinecraftInstance> onesix = std::dynamic_pointer_cast<MinecraftInstance>(inst); |         std::shared_ptr<MinecraftInstance> onesix = std::dynamic_pointer_cast<MinecraftInstance>(inst); | ||||||
|         values.append(new VersionPage(onesix.get())); |         values.append(new VersionPage(onesix.get())); | ||||||
| @@ -50,18 +45,14 @@ public: | |||||||
|         values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots"))); |         values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots"))); | ||||||
|         values.append(new InstanceSettingsPage(onesix.get())); |         values.append(new InstanceSettingsPage(onesix.get())); | ||||||
|         auto logMatcher = inst->getLogFileMatcher(); |         auto logMatcher = inst->getLogFileMatcher(); | ||||||
|         if(logMatcher) |         if (logMatcher) { | ||||||
|         { |  | ||||||
|             values.append(new OtherLogsPage(inst->getLogFileRoot(), logMatcher)); |             values.append(new OtherLogsPage(inst->getLogFileRoot(), logMatcher)); | ||||||
|         } |         } | ||||||
|         return values; |         return values; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     virtual QString dialogTitle() override |     virtual QString dialogTitle() override { return tr("Edit Instance (%1)").arg(inst->name()); } | ||||||
|     { |  | ||||||
|         return tr("Edit Instance (%1)").arg(inst->name()); |    protected: | ||||||
|     } |  | ||||||
| protected: |  | ||||||
|     InstancePtr inst; |     InstancePtr inst; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -18,13 +18,14 @@ InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& ol | |||||||
|     return InstanceNameChange::ShouldKeep; |     return InstanceNameChange::ShouldKeep; | ||||||
| } | } | ||||||
|  |  | ||||||
| ShouldUpdate askIfShouldUpdate(QWidget *parent, QString original_version_name) | ShouldUpdate askIfShouldUpdate(QWidget* parent, QString original_version_name) | ||||||
| { | { | ||||||
|     auto info = CustomMessageBox::selectable( |     auto info = CustomMessageBox::selectable( | ||||||
|         parent, QObject::tr("Similar modpack was found!"), |         parent, QObject::tr("Similar modpack was found!"), | ||||||
|         QObject::tr("One or more of your instances are from this same modpack%1. Do you want to create a " |         QObject::tr( | ||||||
|            "separate instance, or update the existing one?\n\nNOTE: Make sure you made a backup of your important instance data before " |             "One or more of your instances are from this same modpack%1. Do you want to create a " | ||||||
|            "updating, as worlds can be corrupted and some configuration may be lost (due to pack overrides).") |             "separate instance, or update the existing one?\n\nNOTE: Make sure you made a backup of your important instance data before " | ||||||
|  |             "updating, as worlds can be corrupted and some configuration may be lost (due to pack overrides).") | ||||||
|             .arg(original_version_name), |             .arg(original_version_name), | ||||||
|         QMessageBox::Information, QMessageBox::Ok | QMessageBox::Reset | QMessageBox::Abort); |         QMessageBox::Information, QMessageBox::Ok | QMessageBox::Reset | QMessageBox::Abort); | ||||||
|     info->setButtonText(QMessageBox::Ok, QObject::tr("Update existing instance")); |     info->setButtonText(QMessageBox::Ok, QObject::tr("Update existing instance")); | ||||||
| @@ -38,7 +39,6 @@ ShouldUpdate askIfShouldUpdate(QWidget *parent, QString original_version_name) | |||||||
|     if (info->clickedButton() == info->button(QMessageBox::Abort)) |     if (info->clickedButton() == info->button(QMessageBox::Abort)) | ||||||
|         return ShouldUpdate::SkipUpdating; |         return ShouldUpdate::SkipUpdating; | ||||||
|     return ShouldUpdate::Cancel; |     return ShouldUpdate::Cancel; | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
| QString InstanceName::name() const | QString InstanceName::name() const | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  PolyMC - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
| @@ -39,43 +39,39 @@ | |||||||
|  |  | ||||||
| #include <QRegularExpression> | #include <QRegularExpression> | ||||||
|  |  | ||||||
| bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent) | bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget* parent) | ||||||
| { | { | ||||||
|     if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(QRegularExpression("-Xm[sx]")) |     if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(QRegularExpression("-Xm[sx]")) || jvmargs.contains("-XX-MaxHeapSize") || | ||||||
|         || jvmargs.contains("-XX-MaxHeapSize") || jvmargs.contains("-XX:InitialHeapSize")) |         jvmargs.contains("-XX:InitialHeapSize")) { | ||||||
|     { |  | ||||||
|         auto warnStr = QObject::tr( |         auto warnStr = QObject::tr( | ||||||
|             "You tried to manually set a JVM memory option (using \"-XX:PermSize\", \"-XX-MaxHeapSize\", \"-XX:InitialHeapSize\", \"-Xmx\" or \"-Xms\").\n" |             "You tried to manually set a JVM memory option (using \"-XX:PermSize\", \"-XX-MaxHeapSize\", \"-XX:InitialHeapSize\", \"-Xmx\" " | ||||||
|  |             "or \"-Xms\").\n" | ||||||
|             "There are dedicated boxes for these in the settings (Java tab, in the Memory group at the top).\n" |             "There are dedicated boxes for these in the settings (Java tab, in the Memory group at the top).\n" | ||||||
|             "This message will be displayed until you remove them from the JVM arguments."); |             "This message will be displayed until you remove them from the JVM arguments."); | ||||||
|         CustomMessageBox::selectable( |         CustomMessageBox::selectable(parent, QObject::tr("JVM arguments warning"), warnStr, QMessageBox::Warning)->exec(); | ||||||
|             parent, QObject::tr("JVM arguments warning"), |  | ||||||
|             warnStr, |  | ||||||
|             QMessageBox::Warning)->exec(); |  | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|     // block lunacy with passing required version to the JVM |     // block lunacy with passing required version to the JVM | ||||||
|     if (jvmargs.contains(QRegularExpression("-version:.*"))) { |     if (jvmargs.contains(QRegularExpression("-version:.*"))) { | ||||||
|         auto warnStr = QObject::tr( |         auto warnStr = QObject::tr( | ||||||
|             "You tried to pass required Java version argument to the JVM (using \"-version:xxx\"). This is not safe and will not be allowed.\n" |             "You tried to pass required Java version argument to the JVM (using \"-version:xxx\"). This is not safe and will not be " | ||||||
|  |             "allowed.\n" | ||||||
|             "This message will be displayed until you remove this from the JVM arguments."); |             "This message will be displayed until you remove this from the JVM arguments."); | ||||||
|         CustomMessageBox::selectable( |         CustomMessageBox::selectable(parent, QObject::tr("JVM arguments warning"), warnStr, QMessageBox::Warning)->exec(); | ||||||
|             parent, QObject::tr("JVM arguments warning"), |  | ||||||
|             warnStr, |  | ||||||
|             QMessageBox::Warning)->exec(); |  | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| void JavaCommon::javaWasOk(QWidget *parent, JavaCheckResult result) | void JavaCommon::javaWasOk(QWidget* parent, const JavaCheckResult& result) | ||||||
| { | { | ||||||
|     QString text; |     QString text; | ||||||
|     text += QObject::tr("Java test succeeded!<br />Platform reported: %1<br />Java version " |     text += QObject::tr( | ||||||
|         "reported: %2<br />Java vendor " |                 "Java test succeeded!<br />Platform reported: %1<br />Java version " | ||||||
|         "reported: %3<br />").arg(result.realPlatform, result.javaVersion.toString(), result.javaVendor); |                 "reported: %2<br />Java vendor " | ||||||
|     if (result.errorLog.size()) |                 "reported: %3<br />") | ||||||
|     { |                 .arg(result.realPlatform, result.javaVersion.toString(), result.javaVendor); | ||||||
|  |     if (result.errorLog.size()) { | ||||||
|         auto htmlError = result.errorLog; |         auto htmlError = result.errorLog; | ||||||
|         htmlError.replace('\n', "<br />"); |         htmlError.replace('\n', "<br />"); | ||||||
|         text += QObject::tr("<br />Warnings:<br /><font color=\"orange\">%1</font>").arg(htmlError); |         text += QObject::tr("<br />Warnings:<br /><font color=\"orange\">%1</font>").arg(htmlError); | ||||||
| @@ -83,7 +79,7 @@ void JavaCommon::javaWasOk(QWidget *parent, JavaCheckResult result) | |||||||
|     CustomMessageBox::selectable(parent, QObject::tr("Java test success"), text, QMessageBox::Information)->show(); |     CustomMessageBox::selectable(parent, QObject::tr("Java test success"), text, QMessageBox::Information)->show(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void JavaCommon::javaArgsWereBad(QWidget *parent, JavaCheckResult result) | void JavaCommon::javaArgsWereBad(QWidget* parent, const JavaCheckResult& result) | ||||||
| { | { | ||||||
|     auto htmlError = result.errorLog; |     auto htmlError = result.errorLog; | ||||||
|     QString text; |     QString text; | ||||||
| @@ -93,7 +89,7 @@ void JavaCommon::javaArgsWereBad(QWidget *parent, JavaCheckResult result) | |||||||
|     CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show(); |     CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void JavaCommon::javaBinaryWasBad(QWidget *parent, JavaCheckResult result) | void JavaCommon::javaBinaryWasBad(QWidget* parent, const JavaCheckResult& result) | ||||||
| { | { | ||||||
|     QString text; |     QString text; | ||||||
|     text += QObject::tr( |     text += QObject::tr( | ||||||
| @@ -102,7 +98,7 @@ void JavaCommon::javaBinaryWasBad(QWidget *parent, JavaCheckResult result) | |||||||
|     CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show(); |     CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void JavaCommon::javaCheckNotFound(QWidget *parent) | void JavaCommon::javaCheckNotFound(QWidget* parent) | ||||||
| { | { | ||||||
|     QString text; |     QString text; | ||||||
|     text += QObject::tr("Java checker library could not be found. Please check your installation."); |     text += QObject::tr("Java checker library could not be found. Please check your installation."); | ||||||
| @@ -111,8 +107,7 @@ void JavaCommon::javaCheckNotFound(QWidget *parent) | |||||||
|  |  | ||||||
| void JavaCommon::TestCheck::run() | void JavaCommon::TestCheck::run() | ||||||
| { | { | ||||||
|     if (!JavaCommon::checkJVMArgs(m_args, m_parent)) |     if (!JavaCommon::checkJVMArgs(m_args, m_parent)) { | ||||||
|     { |  | ||||||
|         emit finished(); |         emit finished(); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @@ -129,8 +124,7 @@ void JavaCommon::TestCheck::run() | |||||||
|  |  | ||||||
| void JavaCommon::TestCheck::checkFinished(JavaCheckResult result) | void JavaCommon::TestCheck::checkFinished(JavaCheckResult result) | ||||||
| { | { | ||||||
|     if (result.validity != JavaCheckResult::Validity::Valid) |     if (result.validity != JavaCheckResult::Validity::Valid) { | ||||||
|     { |  | ||||||
|         javaBinaryWasBad(m_parent, result); |         javaBinaryWasBad(m_parent, result); | ||||||
|         emit finished(); |         emit finished(); | ||||||
|         return; |         return; | ||||||
| @@ -141,8 +135,7 @@ void JavaCommon::TestCheck::checkFinished(JavaCheckResult result) | |||||||
|     checker->m_args = m_args; |     checker->m_args = m_args; | ||||||
|     checker->m_minMem = m_minMem; |     checker->m_minMem = m_minMem; | ||||||
|     checker->m_maxMem = m_maxMem; |     checker->m_maxMem = m_maxMem; | ||||||
|     if (result.javaVersion.requiresPermGen()) |     if (result.javaVersion.requiresPermGen()) { | ||||||
|     { |  | ||||||
|         checker->m_permGen = m_permGen; |         checker->m_permGen = m_permGen; | ||||||
|     } |     } | ||||||
|     checker->performCheck(); |     checker->performCheck(); | ||||||
| @@ -150,8 +143,7 @@ void JavaCommon::TestCheck::checkFinished(JavaCheckResult result) | |||||||
|  |  | ||||||
| void JavaCommon::TestCheck::checkFinishedWithArgs(JavaCheckResult result) | void JavaCommon::TestCheck::checkFinishedWithArgs(JavaCheckResult result) | ||||||
| { | { | ||||||
|     if (result.validity == JavaCheckResult::Validity::Valid) |     if (result.validity == JavaCheckResult::Validity::Valid) { | ||||||
|     { |  | ||||||
|         javaWasOk(m_parent, result); |         javaWasOk(m_parent, result); | ||||||
|         emit finished(); |         emit finished(); | ||||||
|         return; |         return; | ||||||
| @@ -159,4 +151,3 @@ void JavaCommon::TestCheck::checkFinishedWithArgs(JavaCheckResult result) | |||||||
|     javaArgsWereBad(m_parent, result); |     javaArgsWereBad(m_parent, result); | ||||||
|     emit finished(); |     emit finished(); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,45 +6,42 @@ class QWidget; | |||||||
| /** | /** | ||||||
|  * Common UI bits for the java pages to use. |  * Common UI bits for the java pages to use. | ||||||
|  */ |  */ | ||||||
| namespace JavaCommon | namespace JavaCommon { | ||||||
| { | bool checkJVMArgs(QString args, QWidget* parent); | ||||||
|     bool checkJVMArgs(QString args, QWidget *parent); |  | ||||||
|  |  | ||||||
|     // Show a dialog saying that the Java binary was usable | // Show a dialog saying that the Java binary was usable | ||||||
|     void javaWasOk(QWidget *parent, JavaCheckResult result); | void javaWasOk(QWidget* parent, const JavaCheckResult& result); | ||||||
|     // Show a dialog saying that the Java binary was not usable because of bad options | // Show a dialog saying that the Java binary was not usable because of bad options | ||||||
|     void javaArgsWereBad(QWidget *parent, JavaCheckResult result); | void javaArgsWereBad(QWidget* parent, const JavaCheckResult& result); | ||||||
|     // Show a dialog saying that the Java binary was not usable | // Show a dialog saying that the Java binary was not usable | ||||||
|     void javaBinaryWasBad(QWidget *parent, JavaCheckResult result); | void javaBinaryWasBad(QWidget* parent, const JavaCheckResult& result); | ||||||
|     // Show a dialog if we couldn't find Java Checker | // Show a dialog if we couldn't find Java Checker | ||||||
|     void javaCheckNotFound(QWidget *parent); | void javaCheckNotFound(QWidget* parent); | ||||||
|  |  | ||||||
|     class TestCheck : public QObject | class TestCheck : public QObject { | ||||||
|     { |     Q_OBJECT | ||||||
|         Q_OBJECT |    public: | ||||||
|     public: |     TestCheck(QWidget* parent, QString path, QString args, int minMem, int maxMem, int permGen) | ||||||
|         TestCheck(QWidget *parent, QString path, QString args, int minMem, int maxMem, int permGen) |         : m_parent(parent), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen) | ||||||
|             :m_parent(parent), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen) |     {} | ||||||
|         { |     virtual ~TestCheck(){}; | ||||||
|         } |  | ||||||
|         virtual ~TestCheck() {}; |  | ||||||
|  |  | ||||||
|         void run(); |     void run(); | ||||||
|  |  | ||||||
|     signals: |    signals: | ||||||
|         void finished(); |     void finished(); | ||||||
|  |  | ||||||
|     private slots: |    private slots: | ||||||
|         void checkFinished(JavaCheckResult result); |     void checkFinished(JavaCheckResult result); | ||||||
|         void checkFinishedWithArgs(JavaCheckResult result); |     void checkFinishedWithArgs(JavaCheckResult result); | ||||||
|  |  | ||||||
|     private: |    private: | ||||||
|         std::shared_ptr<JavaChecker> checker; |     std::shared_ptr<JavaChecker> checker; | ||||||
|         QWidget *m_parent = nullptr; |     QWidget* m_parent = nullptr; | ||||||
|         QString m_path; |     QString m_path; | ||||||
|         QString m_args; |     QString m_args; | ||||||
|         int m_minMem = 0; |     int m_minMem = 0; | ||||||
|         int m_maxMem = 0; |     int m_maxMem = 0; | ||||||
|         int m_permGen = 64; |     int m_permGen = 64; | ||||||
|     }; | }; | ||||||
| } | }  // namespace JavaCommon | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  PolyMC - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
| @@ -37,257 +37,246 @@ | |||||||
|  |  | ||||||
| #include <QFile> | #include <QFile> | ||||||
|  |  | ||||||
| #include "FileSystem.h" |  | ||||||
| #include <math.h> | #include <math.h> | ||||||
|  | #include "FileSystem.h" | ||||||
|  |  | ||||||
| namespace Json | namespace Json { | ||||||
| { | void write(const QJsonDocument& doc, const QString& filename) | ||||||
| void write(const QJsonDocument &doc, const QString &filename) |  | ||||||
| { | { | ||||||
|     FS::write(filename, doc.toJson()); |     FS::write(filename, doc.toJson()); | ||||||
| } | } | ||||||
| void write(const QJsonObject &object, const QString &filename) | void write(const QJsonObject& object, const QString& filename) | ||||||
| { | { | ||||||
|     write(QJsonDocument(object), filename); |     write(QJsonDocument(object), filename); | ||||||
| } | } | ||||||
| void write(const QJsonArray &array, const QString &filename) | void write(const QJsonArray& array, const QString& filename) | ||||||
| { | { | ||||||
|     write(QJsonDocument(array), filename); |     write(QJsonDocument(array), filename); | ||||||
| } | } | ||||||
|  |  | ||||||
| QByteArray toText(const QJsonObject &obj) | QByteArray toText(const QJsonObject& obj) | ||||||
| { | { | ||||||
|     return QJsonDocument(obj).toJson(QJsonDocument::Compact); |     return QJsonDocument(obj).toJson(QJsonDocument::Compact); | ||||||
| } | } | ||||||
| QByteArray toText(const QJsonArray &array) | QByteArray toText(const QJsonArray& array) | ||||||
| { | { | ||||||
|     return QJsonDocument(array).toJson(QJsonDocument::Compact); |     return QJsonDocument(array).toJson(QJsonDocument::Compact); | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool isBinaryJson(const QByteArray &data) | static bool isBinaryJson(const QByteArray& data) | ||||||
| { | { | ||||||
|     decltype(QJsonDocument::BinaryFormatTag) tag = QJsonDocument::BinaryFormatTag; |     decltype(QJsonDocument::BinaryFormatTag) tag = QJsonDocument::BinaryFormatTag; | ||||||
|     return memcmp(data.constData(), &tag, sizeof(QJsonDocument::BinaryFormatTag)) == 0; |     return memcmp(data.constData(), &tag, sizeof(QJsonDocument::BinaryFormatTag)) == 0; | ||||||
| } | } | ||||||
| QJsonDocument requireDocument(const QByteArray &data, const QString &what) | QJsonDocument requireDocument(const QByteArray& data, const QString& what) | ||||||
| { | { | ||||||
|     if (isBinaryJson(data)) |     if (isBinaryJson(data)) { | ||||||
|     { |  | ||||||
|         // FIXME: Is this needed? |         // FIXME: Is this needed? | ||||||
|         throw JsonException(what + ": Invalid JSON. Binary JSON unsupported"); |         throw JsonException(what + ": Invalid JSON. Binary JSON unsupported"); | ||||||
|     } |     } else { | ||||||
|     else |  | ||||||
|     { |  | ||||||
|         QJsonParseError error; |         QJsonParseError error; | ||||||
|         QJsonDocument doc = QJsonDocument::fromJson(data, &error); |         QJsonDocument doc = QJsonDocument::fromJson(data, &error); | ||||||
|         if (error.error != QJsonParseError::NoError) |         if (error.error != QJsonParseError::NoError) { | ||||||
|         { |  | ||||||
|             throw JsonException(what + ": Error parsing JSON: " + error.errorString()); |             throw JsonException(what + ": Error parsing JSON: " + error.errorString()); | ||||||
|         } |         } | ||||||
|         return doc; |         return doc; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| QJsonDocument requireDocument(const QString &filename, const QString &what) | QJsonDocument requireDocument(const QString& filename, const QString& what) | ||||||
| { | { | ||||||
|     return requireDocument(FS::read(filename), what); |     return requireDocument(FS::read(filename), what); | ||||||
| } | } | ||||||
| QJsonObject requireObject(const QJsonDocument &doc, const QString &what) | QJsonObject requireObject(const QJsonDocument& doc, const QString& what) | ||||||
| { | { | ||||||
|     if (!doc.isObject()) |     if (!doc.isObject()) { | ||||||
|     { |  | ||||||
|         throw JsonException(what + " is not an object"); |         throw JsonException(what + " is not an object"); | ||||||
|     } |     } | ||||||
|     return doc.object(); |     return doc.object(); | ||||||
| } | } | ||||||
| QJsonArray requireArray(const QJsonDocument &doc, const QString &what) | QJsonArray requireArray(const QJsonDocument& doc, const QString& what) | ||||||
| { | { | ||||||
|     if (!doc.isArray()) |     if (!doc.isArray()) { | ||||||
|     { |  | ||||||
|         throw JsonException(what + " is not an array"); |         throw JsonException(what + " is not an array"); | ||||||
|     } |     } | ||||||
|     return doc.array(); |     return doc.array(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void writeString(QJsonObject &to, const QString &key, const QString &value) | void writeString(QJsonObject& to, const QString& key, const QString& value) | ||||||
| { | { | ||||||
|     if (!value.isEmpty()) |     if (!value.isEmpty()) { | ||||||
|     { |  | ||||||
|         to.insert(key, value); |         to.insert(key, value); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void writeStringList(QJsonObject &to, const QString &key, const QStringList &values) | void writeStringList(QJsonObject& to, const QString& key, const QStringList& values) | ||||||
| { | { | ||||||
|     if (!values.isEmpty()) |     if (!values.isEmpty()) { | ||||||
|     { |  | ||||||
|         QJsonArray array; |         QJsonArray array; | ||||||
|         for(auto value: values) |         for (auto value : values) { | ||||||
|         { |  | ||||||
|             array.append(value); |             array.append(value); | ||||||
|         } |         } | ||||||
|         to.insert(key, array); |         to.insert(key, array); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| template<> | template <> | ||||||
| QJsonValue toJson<QUrl>(const QUrl &url) | QJsonValue toJson<QUrl>(const QUrl& url) | ||||||
| { | { | ||||||
|     return QJsonValue(url.toString(QUrl::FullyEncoded)); |     return QJsonValue(url.toString(QUrl::FullyEncoded)); | ||||||
| } | } | ||||||
| template<> | template <> | ||||||
| QJsonValue toJson<QByteArray>(const QByteArray &data) | QJsonValue toJson<QByteArray>(const QByteArray& data) | ||||||
| { | { | ||||||
|     return QJsonValue(QString::fromLatin1(data.toHex())); |     return QJsonValue(QString::fromLatin1(data.toHex())); | ||||||
| } | } | ||||||
| template<> | template <> | ||||||
| QJsonValue toJson<QDateTime>(const QDateTime &datetime) | QJsonValue toJson<QDateTime>(const QDateTime& datetime) | ||||||
| { | { | ||||||
|     return QJsonValue(datetime.toString(Qt::ISODate)); |     return QJsonValue(datetime.toString(Qt::ISODate)); | ||||||
| } | } | ||||||
| template<> | template <> | ||||||
| QJsonValue toJson<QDir>(const QDir &dir) | QJsonValue toJson<QDir>(const QDir& dir) | ||||||
| { | { | ||||||
|     return QDir::current().relativeFilePath(dir.absolutePath()); |     return QDir::current().relativeFilePath(dir.absolutePath()); | ||||||
| } | } | ||||||
| template<> | template <> | ||||||
| QJsonValue toJson<QUuid>(const QUuid &uuid) | QJsonValue toJson<QUuid>(const QUuid& uuid) | ||||||
| { | { | ||||||
|     return uuid.toString(); |     return uuid.toString(); | ||||||
| } | } | ||||||
| template<> | template <> | ||||||
| QJsonValue toJson<QVariant>(const QVariant &variant) | QJsonValue toJson<QVariant>(const QVariant& variant) | ||||||
| { | { | ||||||
|     return QJsonValue::fromVariant(variant); |     return QJsonValue::fromVariant(variant); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | template <> | ||||||
| template<> QByteArray requireIsType<QByteArray>(const QJsonValue &value, const QString &what) | QByteArray requireIsType<QByteArray>(const QJsonValue& value, const QString& what) | ||||||
| { | { | ||||||
|     const QString string = ensureIsType<QString>(value, what); |     const QString string = ensureIsType<QString>(value, what); | ||||||
|     // ensure that the string can be safely cast to Latin1 |     // ensure that the string can be safely cast to Latin1 | ||||||
|     if (string != QString::fromLatin1(string.toLatin1())) |     if (string != QString::fromLatin1(string.toLatin1())) { | ||||||
|     { |  | ||||||
|         throw JsonException(what + " is not encodable as Latin1"); |         throw JsonException(what + " is not encodable as Latin1"); | ||||||
|     } |     } | ||||||
|     return QByteArray::fromHex(string.toLatin1()); |     return QByteArray::fromHex(string.toLatin1()); | ||||||
| } | } | ||||||
|  |  | ||||||
| template<> QJsonArray requireIsType<QJsonArray>(const QJsonValue &value, const QString &what) | template <> | ||||||
|  | QJsonArray requireIsType<QJsonArray>(const QJsonValue& value, const QString& what) | ||||||
| { | { | ||||||
|     if (!value.isArray()) |     if (!value.isArray()) { | ||||||
|     { |  | ||||||
|         throw JsonException(what + " is not an array"); |         throw JsonException(what + " is not an array"); | ||||||
|     } |     } | ||||||
|     return value.toArray(); |     return value.toArray(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | template <> | ||||||
| template<> QString requireIsType<QString>(const QJsonValue &value, const QString &what) | QString requireIsType<QString>(const QJsonValue& value, const QString& what) | ||||||
| { | { | ||||||
|     if (!value.isString()) |     if (!value.isString()) { | ||||||
|     { |  | ||||||
|         throw JsonException(what + " is not a string"); |         throw JsonException(what + " is not a string"); | ||||||
|     } |     } | ||||||
|     return value.toString(); |     return value.toString(); | ||||||
| } | } | ||||||
|  |  | ||||||
| template<> bool requireIsType<bool>(const QJsonValue &value, const QString &what) | template <> | ||||||
|  | bool requireIsType<bool>(const QJsonValue& value, const QString& what) | ||||||
| { | { | ||||||
|     if (!value.isBool()) |     if (!value.isBool()) { | ||||||
|     { |  | ||||||
|         throw JsonException(what + " is not a bool"); |         throw JsonException(what + " is not a bool"); | ||||||
|     } |     } | ||||||
|     return value.toBool(); |     return value.toBool(); | ||||||
| } | } | ||||||
|  |  | ||||||
| template<> double requireIsType<double>(const QJsonValue &value, const QString &what) | template <> | ||||||
|  | double requireIsType<double>(const QJsonValue& value, const QString& what) | ||||||
| { | { | ||||||
|     if (!value.isDouble()) |     if (!value.isDouble()) { | ||||||
|     { |  | ||||||
|         throw JsonException(what + " is not a double"); |         throw JsonException(what + " is not a double"); | ||||||
|     } |     } | ||||||
|     return value.toDouble(); |     return value.toDouble(); | ||||||
| } | } | ||||||
|  |  | ||||||
| template<> int requireIsType<int>(const QJsonValue &value, const QString &what) | template <> | ||||||
|  | int requireIsType<int>(const QJsonValue& value, const QString& what) | ||||||
| { | { | ||||||
|     const double doubl = requireIsType<double>(value, what); |     const double doubl = requireIsType<double>(value, what); | ||||||
|     if (fmod(doubl, 1) != 0) |     if (fmod(doubl, 1) != 0) { | ||||||
|     { |  | ||||||
|         throw JsonException(what + " is not an integer"); |         throw JsonException(what + " is not an integer"); | ||||||
|     } |     } | ||||||
|     return int(doubl); |     return int(doubl); | ||||||
| } | } | ||||||
|  |  | ||||||
| template<> QDateTime requireIsType<QDateTime>(const QJsonValue &value, const QString &what) | template <> | ||||||
|  | QDateTime requireIsType<QDateTime>(const QJsonValue& value, const QString& what) | ||||||
| { | { | ||||||
|     const QString string = requireIsType<QString>(value, what); |     const QString string = requireIsType<QString>(value, what); | ||||||
|     const QDateTime datetime = QDateTime::fromString(string, Qt::ISODate); |     const QDateTime datetime = QDateTime::fromString(string, Qt::ISODate); | ||||||
|     if (!datetime.isValid()) |     if (!datetime.isValid()) { | ||||||
|     { |  | ||||||
|         throw JsonException(what + " is not a ISO formatted date/time value"); |         throw JsonException(what + " is not a ISO formatted date/time value"); | ||||||
|     } |     } | ||||||
|     return datetime; |     return datetime; | ||||||
| } | } | ||||||
|  |  | ||||||
| template<> QUrl requireIsType<QUrl>(const QJsonValue &value, const QString &what) | template <> | ||||||
|  | QUrl requireIsType<QUrl>(const QJsonValue& value, const QString& what) | ||||||
| { | { | ||||||
|     const QString string = ensureIsType<QString>(value, what); |     const QString string = ensureIsType<QString>(value, what); | ||||||
|     if (string.isEmpty()) |     if (string.isEmpty()) { | ||||||
|     { |  | ||||||
|         return QUrl(); |         return QUrl(); | ||||||
|     } |     } | ||||||
|     const QUrl url = QUrl(string, QUrl::StrictMode); |     const QUrl url = QUrl(string, QUrl::StrictMode); | ||||||
|     if (!url.isValid()) |     if (!url.isValid()) { | ||||||
|     { |  | ||||||
|         throw JsonException(what + " is not a correctly formatted URL"); |         throw JsonException(what + " is not a correctly formatted URL"); | ||||||
|     } |     } | ||||||
|     return url; |     return url; | ||||||
| } | } | ||||||
|  |  | ||||||
| template<> QDir requireIsType<QDir>(const QJsonValue &value, const QString &what) | template <> | ||||||
|  | QDir requireIsType<QDir>(const QJsonValue& value, const QString& what) | ||||||
| { | { | ||||||
|     const QString string = requireIsType<QString>(value, what); |     const QString string = requireIsType<QString>(value, what); | ||||||
|     // FIXME: does not handle invalid characters! |     // FIXME: does not handle invalid characters! | ||||||
|     return QDir::current().absoluteFilePath(string); |     return QDir::current().absoluteFilePath(string); | ||||||
| } | } | ||||||
|  |  | ||||||
| template<> QUuid requireIsType<QUuid>(const QJsonValue &value, const QString &what) | template <> | ||||||
|  | QUuid requireIsType<QUuid>(const QJsonValue& value, const QString& what) | ||||||
| { | { | ||||||
|     const QString string = requireIsType<QString>(value, what); |     const QString string = requireIsType<QString>(value, what); | ||||||
|     const QUuid uuid = QUuid(string); |     const QUuid uuid = QUuid(string); | ||||||
|     if (uuid.toString() != string) // converts back => valid |     if (uuid.toString() != string)  // converts back => valid | ||||||
|     { |     { | ||||||
|         throw JsonException(what + " is not a valid UUID"); |         throw JsonException(what + " is not a valid UUID"); | ||||||
|     } |     } | ||||||
|     return uuid; |     return uuid; | ||||||
| } | } | ||||||
|  |  | ||||||
| template<> QJsonObject requireIsType<QJsonObject>(const QJsonValue &value, const QString &what) | template <> | ||||||
|  | QJsonObject requireIsType<QJsonObject>(const QJsonValue& value, const QString& what) | ||||||
| { | { | ||||||
|     if (!value.isObject()) |     if (!value.isObject()) { | ||||||
|     { |  | ||||||
|         throw JsonException(what + " is not an object"); |         throw JsonException(what + " is not an object"); | ||||||
|     } |     } | ||||||
|     return value.toObject(); |     return value.toObject(); | ||||||
| } | } | ||||||
|  |  | ||||||
| template<> QVariant requireIsType<QVariant>(const QJsonValue &value, const QString &what) | template <> | ||||||
|  | QVariant requireIsType<QVariant>(const QJsonValue& value, const QString& what) | ||||||
| { | { | ||||||
|     if (value.isNull() || value.isUndefined()) |     if (value.isNull() || value.isUndefined()) { | ||||||
|     { |  | ||||||
|         throw JsonException(what + " is null or undefined"); |         throw JsonException(what + " is null or undefined"); | ||||||
|     } |     } | ||||||
|     return value.toVariant(); |     return value.toVariant(); | ||||||
| } | } | ||||||
|  |  | ||||||
| template<> QJsonValue requireIsType<QJsonValue>(const QJsonValue &value, const QString &what) | template <> | ||||||
|  | QJsonValue requireIsType<QJsonValue>(const QJsonValue& value, const QString& what) | ||||||
| { | { | ||||||
|     if (value.isNull() || value.isUndefined()) |     if (value.isNull() || value.isUndefined()) { | ||||||
|     { |  | ||||||
|         throw JsonException(what + " is null or undefined"); |         throw JsonException(what + " is null or undefined"); | ||||||
|     } |     } | ||||||
|     return value; |     return value; | ||||||
| } | } | ||||||
|  |  | ||||||
| } | }  // namespace Json | ||||||
|   | |||||||
							
								
								
									
										204
									
								
								launcher/Json.h
									
									
									
									
									
								
							
							
						
						
									
										204
									
								
								launcher/Json.h
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  PolyMC - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
| @@ -35,74 +35,71 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <QJsonDocument> |  | ||||||
| #include <QJsonArray> |  | ||||||
| #include <QJsonObject> |  | ||||||
| #include <QDateTime> | #include <QDateTime> | ||||||
| #include <QUrl> |  | ||||||
| #include <QDir> | #include <QDir> | ||||||
|  | #include <QJsonArray> | ||||||
|  | #include <QJsonDocument> | ||||||
|  | #include <QJsonObject> | ||||||
|  | #include <QUrl> | ||||||
| #include <QUuid> | #include <QUuid> | ||||||
| #include <QVariant> | #include <QVariant> | ||||||
| #include <memory> | #include <memory> | ||||||
|  |  | ||||||
| #include "Exception.h" | #include "Exception.h" | ||||||
|  |  | ||||||
| namespace Json | namespace Json { | ||||||
| { | class JsonException : public ::Exception { | ||||||
| class JsonException : public ::Exception |    public: | ||||||
| { |     JsonException(const QString& message) : Exception(message) {} | ||||||
| public: |  | ||||||
|     JsonException(const QString &message) : Exception(message) {} |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /// @throw FileSystemException | /// @throw FileSystemException | ||||||
| void write(const QJsonDocument &doc, const QString &filename); | void write(const QJsonDocument& doc, const QString& filename); | ||||||
| /// @throw FileSystemException | /// @throw FileSystemException | ||||||
| void write(const QJsonObject &object, const QString &filename); | void write(const QJsonObject& object, const QString& filename); | ||||||
| /// @throw FileSystemException | /// @throw FileSystemException | ||||||
| void write(const QJsonArray &array, const QString &filename); | void write(const QJsonArray& array, const QString& filename); | ||||||
|  |  | ||||||
| QByteArray toText(const QJsonObject &obj); | QByteArray toText(const QJsonObject& obj); | ||||||
| QByteArray toText(const QJsonArray &array); | QByteArray toText(const QJsonArray& array); | ||||||
|  |  | ||||||
| /// @throw JsonException | /// @throw JsonException | ||||||
| QJsonDocument requireDocument(const QByteArray &data, const QString &what = "Document"); | QJsonDocument requireDocument(const QByteArray& data, const QString& what = "Document"); | ||||||
| /// @throw JsonException | /// @throw JsonException | ||||||
| QJsonDocument requireDocument(const QString &filename, const QString &what = "Document"); | QJsonDocument requireDocument(const QString& filename, const QString& what = "Document"); | ||||||
| /// @throw JsonException | /// @throw JsonException | ||||||
| QJsonObject requireObject(const QJsonDocument &doc, const QString &what = "Document"); | QJsonObject requireObject(const QJsonDocument& doc, const QString& what = "Document"); | ||||||
| /// @throw JsonException | /// @throw JsonException | ||||||
| QJsonArray requireArray(const QJsonDocument &doc, const QString &what = "Document"); | QJsonArray requireArray(const QJsonDocument& doc, const QString& what = "Document"); | ||||||
|  |  | ||||||
| /////////////////// WRITING //////////////////// | /////////////////// WRITING //////////////////// | ||||||
|  |  | ||||||
| void writeString(QJsonObject & to, const QString &key, const QString &value); | void writeString(QJsonObject& to, const QString& key, const QString& value); | ||||||
| void writeStringList(QJsonObject & to, const QString &key, const QStringList &values); | void writeStringList(QJsonObject& to, const QString& key, const QStringList& values); | ||||||
|  |  | ||||||
| template<typename T> | template <typename T> | ||||||
| QJsonValue toJson(const T &t) | QJsonValue toJson(const T& t) | ||||||
| { | { | ||||||
|     return QJsonValue(t); |     return QJsonValue(t); | ||||||
| } | } | ||||||
| template<> | template <> | ||||||
| QJsonValue toJson<QUrl>(const QUrl &url); | QJsonValue toJson<QUrl>(const QUrl& url); | ||||||
| template<> | template <> | ||||||
| QJsonValue toJson<QByteArray>(const QByteArray &data); | QJsonValue toJson<QByteArray>(const QByteArray& data); | ||||||
| template<> | template <> | ||||||
| QJsonValue toJson<QDateTime>(const QDateTime &datetime); | QJsonValue toJson<QDateTime>(const QDateTime& datetime); | ||||||
| template<> | template <> | ||||||
| QJsonValue toJson<QDir>(const QDir &dir); | QJsonValue toJson<QDir>(const QDir& dir); | ||||||
| template<> | template <> | ||||||
| QJsonValue toJson<QUuid>(const QUuid &uuid); | QJsonValue toJson<QUuid>(const QUuid& uuid); | ||||||
| template<> | template <> | ||||||
| QJsonValue toJson<QVariant>(const QVariant &variant); | QJsonValue toJson<QVariant>(const QVariant& variant); | ||||||
|  |  | ||||||
| template<typename T> | template <typename T> | ||||||
| QJsonArray toJsonArray(const QList<T> &container) | QJsonArray toJsonArray(const QList<T>& container) | ||||||
| { | { | ||||||
|     QJsonArray array; |     QJsonArray array; | ||||||
|     for (const T item : container) |     for (const T item : container) { | ||||||
|     { |  | ||||||
|         array.append(toJson<T>(item)); |         array.append(toJson<T>(item)); | ||||||
|     } |     } | ||||||
|     return array; |     return array; | ||||||
| @@ -112,106 +109,110 @@ QJsonArray toJsonArray(const QList<T> &container) | |||||||
|  |  | ||||||
| /// @throw JsonException | /// @throw JsonException | ||||||
| template <typename T> | template <typename T> | ||||||
| T requireIsType(const QJsonValue &value, const QString &what = "Value"); | T requireIsType(const QJsonValue& value, const QString& what = "Value"); | ||||||
|  |  | ||||||
| /// @throw JsonException | /// @throw JsonException | ||||||
| template<> double requireIsType<double>(const QJsonValue &value, const QString &what); | template <> | ||||||
|  | double requireIsType<double>(const QJsonValue& value, const QString& what); | ||||||
| /// @throw JsonException | /// @throw JsonException | ||||||
| template<> bool requireIsType<bool>(const QJsonValue &value, const QString &what); | template <> | ||||||
|  | bool requireIsType<bool>(const QJsonValue& value, const QString& what); | ||||||
| /// @throw JsonException | /// @throw JsonException | ||||||
| template<> int requireIsType<int>(const QJsonValue &value, const QString &what); | template <> | ||||||
|  | int requireIsType<int>(const QJsonValue& value, const QString& what); | ||||||
| /// @throw JsonException | /// @throw JsonException | ||||||
| template<> QJsonObject requireIsType<QJsonObject>(const QJsonValue &value, const QString &what); | template <> | ||||||
|  | QJsonObject requireIsType<QJsonObject>(const QJsonValue& value, const QString& what); | ||||||
| /// @throw JsonException | /// @throw JsonException | ||||||
| template<> QJsonArray requireIsType<QJsonArray>(const QJsonValue &value, const QString &what); | template <> | ||||||
|  | QJsonArray requireIsType<QJsonArray>(const QJsonValue& value, const QString& what); | ||||||
| /// @throw JsonException | /// @throw JsonException | ||||||
| template<> QJsonValue requireIsType<QJsonValue>(const QJsonValue &value, const QString &what); | template <> | ||||||
|  | QJsonValue requireIsType<QJsonValue>(const QJsonValue& value, const QString& what); | ||||||
| /// @throw JsonException | /// @throw JsonException | ||||||
| template<> QByteArray requireIsType<QByteArray>(const QJsonValue &value, const QString &what); | template <> | ||||||
|  | QByteArray requireIsType<QByteArray>(const QJsonValue& value, const QString& what); | ||||||
| /// @throw JsonException | /// @throw JsonException | ||||||
| template<> QDateTime requireIsType<QDateTime>(const QJsonValue &value, const QString &what); | template <> | ||||||
|  | QDateTime requireIsType<QDateTime>(const QJsonValue& value, const QString& what); | ||||||
| /// @throw JsonException | /// @throw JsonException | ||||||
| template<> QVariant requireIsType<QVariant>(const QJsonValue &value, const QString &what); | template <> | ||||||
|  | QVariant requireIsType<QVariant>(const QJsonValue& value, const QString& what); | ||||||
| /// @throw JsonException | /// @throw JsonException | ||||||
| template<> QString requireIsType<QString>(const QJsonValue &value, const QString &what); | template <> | ||||||
|  | QString requireIsType<QString>(const QJsonValue& value, const QString& what); | ||||||
| /// @throw JsonException | /// @throw JsonException | ||||||
| template<> QUuid requireIsType<QUuid>(const QJsonValue &value, const QString &what); | template <> | ||||||
|  | QUuid requireIsType<QUuid>(const QJsonValue& value, const QString& what); | ||||||
| /// @throw JsonException | /// @throw JsonException | ||||||
| template<> QDir requireIsType<QDir>(const QJsonValue &value, const QString &what); | template <> | ||||||
|  | QDir requireIsType<QDir>(const QJsonValue& value, const QString& what); | ||||||
| /// @throw JsonException | /// @throw JsonException | ||||||
| template<> QUrl requireIsType<QUrl>(const QJsonValue &value, const QString &what); | template <> | ||||||
|  | QUrl requireIsType<QUrl>(const QJsonValue& value, const QString& what); | ||||||
|  |  | ||||||
| // the following functions are higher level functions, that make use of the above functions for | // the following functions are higher level functions, that make use of the above functions for | ||||||
| // type conversion | // type conversion | ||||||
| template <typename T> | template <typename T> | ||||||
| T ensureIsType(const QJsonValue &value, const T default_ = T(), const QString &what = "Value") | T ensureIsType(const QJsonValue& value, const T default_ = T(), const QString& what = "Value") | ||||||
| { | { | ||||||
|     if (value.isUndefined() || value.isNull()) |     if (value.isUndefined() || value.isNull()) { | ||||||
|     { |  | ||||||
|         return default_; |         return default_; | ||||||
|     } |     } | ||||||
|     try |     try { | ||||||
|     { |  | ||||||
|         return requireIsType<T>(value, what); |         return requireIsType<T>(value, what); | ||||||
|     } |     } catch (const JsonException&) { | ||||||
|     catch (const JsonException &) |  | ||||||
|     { |  | ||||||
|         return default_; |         return default_; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| /// @throw JsonException | /// @throw JsonException | ||||||
| template <typename T> | template <typename T> | ||||||
| T requireIsType(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__") | T requireIsType(const QJsonObject& parent, const QString& key, const QString& what = "__placeholder__") | ||||||
| { | { | ||||||
|     const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); |     const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); | ||||||
|     if (!parent.contains(key)) |     if (!parent.contains(key)) { | ||||||
|     { |  | ||||||
|         throw JsonException(localWhat + "s parent does not contain " + localWhat); |         throw JsonException(localWhat + "s parent does not contain " + localWhat); | ||||||
|     } |     } | ||||||
|     return requireIsType<T>(parent.value(key), localWhat); |     return requireIsType<T>(parent.value(key), localWhat); | ||||||
| } | } | ||||||
|  |  | ||||||
| template <typename T> | template <typename T> | ||||||
| T ensureIsType(const QJsonObject &parent, const QString &key, const T default_ = T(), const QString &what = "__placeholder__") | T ensureIsType(const QJsonObject& parent, const QString& key, const T default_ = T(), const QString& what = "__placeholder__") | ||||||
| { | { | ||||||
|     const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); |     const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); | ||||||
|     if (!parent.contains(key)) |     if (!parent.contains(key)) { | ||||||
|     { |  | ||||||
|         return default_; |         return default_; | ||||||
|     } |     } | ||||||
|     return ensureIsType<T>(parent.value(key), default_, localWhat); |     return ensureIsType<T>(parent.value(key), default_, localWhat); | ||||||
| } | } | ||||||
|  |  | ||||||
| template <typename T> | template <typename T> | ||||||
| QVector<T> requireIsArrayOf(const QJsonDocument &doc) | QVector<T> requireIsArrayOf(const QJsonDocument& doc) | ||||||
| { | { | ||||||
|     const QJsonArray array = requireArray(doc); |     const QJsonArray array = requireArray(doc); | ||||||
|     QVector<T> out; |     QVector<T> out; | ||||||
|     for (const QJsonValue val : array) |     for (const QJsonValue val : array) { | ||||||
|     { |  | ||||||
|         out.append(requireIsType<T>(val, "Document")); |         out.append(requireIsType<T>(val, "Document")); | ||||||
|     } |     } | ||||||
|     return out; |     return out; | ||||||
| } | } | ||||||
|  |  | ||||||
| template <typename T> | template <typename T> | ||||||
| QVector<T> ensureIsArrayOf(const QJsonValue &value, const QString &what = "Value") | QVector<T> ensureIsArrayOf(const QJsonValue& value, const QString& what = "Value") | ||||||
| { | { | ||||||
|     const QJsonArray array = ensureIsType<QJsonArray>(value, QJsonArray(), what); |     const QJsonArray array = ensureIsType<QJsonArray>(value, QJsonArray(), what); | ||||||
|     QVector<T> out; |     QVector<T> out; | ||||||
|     for (const QJsonValue val : array) |     for (const QJsonValue val : array) { | ||||||
|     { |  | ||||||
|         out.append(requireIsType<T>(val, what)); |         out.append(requireIsType<T>(val, what)); | ||||||
|     } |     } | ||||||
|     return out; |     return out; | ||||||
| } | } | ||||||
|  |  | ||||||
| template <typename T> | template <typename T> | ||||||
| QVector<T> ensureIsArrayOf(const QJsonValue &value, const QVector<T> default_, const QString &what = "Value") | QVector<T> ensureIsArrayOf(const QJsonValue& value, const QVector<T> default_, const QString& what = "Value") | ||||||
| { | { | ||||||
|     if (value.isUndefined()) |     if (value.isUndefined()) { | ||||||
|     { |  | ||||||
|         return default_; |         return default_; | ||||||
|     } |     } | ||||||
|     return ensureIsArrayOf<T>(value, what); |     return ensureIsArrayOf<T>(value, what); | ||||||
| @@ -219,45 +220,46 @@ QVector<T> ensureIsArrayOf(const QJsonValue &value, const QVector<T> default_, c | |||||||
|  |  | ||||||
| /// @throw JsonException | /// @throw JsonException | ||||||
| template <typename T> | template <typename T> | ||||||
| QVector<T> requireIsArrayOf(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__") | QVector<T> requireIsArrayOf(const QJsonObject& parent, const QString& key, const QString& what = "__placeholder__") | ||||||
| { | { | ||||||
|     const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); |     const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); | ||||||
|     if (!parent.contains(key)) |     if (!parent.contains(key)) { | ||||||
|     { |  | ||||||
|         throw JsonException(localWhat + "s parent does not contain " + localWhat); |         throw JsonException(localWhat + "s parent does not contain " + localWhat); | ||||||
|     } |     } | ||||||
|     return ensureIsArrayOf<T>(parent.value(key), localWhat); |     return ensureIsArrayOf<T>(parent.value(key), localWhat); | ||||||
| } | } | ||||||
|  |  | ||||||
| template <typename T> | template <typename T> | ||||||
| QVector<T> ensureIsArrayOf(const QJsonObject &parent, const QString &key, | QVector<T> ensureIsArrayOf(const QJsonObject& parent, | ||||||
|                          const QVector<T> &default_ = QVector<T>(), const QString &what = "__placeholder__") |                            const QString& key, | ||||||
|  |                            const QVector<T>& default_ = QVector<T>(), | ||||||
|  |                            const QString& what = "__placeholder__") | ||||||
| { | { | ||||||
|     const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); |     const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); | ||||||
|     if (!parent.contains(key)) |     if (!parent.contains(key)) { | ||||||
|     { |  | ||||||
|         return default_; |         return default_; | ||||||
|     } |     } | ||||||
|     return ensureIsArrayOf<T>(parent.value(key), default_, localWhat); |     return ensureIsArrayOf<T>(parent.value(key), default_, localWhat); | ||||||
| } | } | ||||||
|  |  | ||||||
| // this macro part could be replaced by variadic functions that just pass on their arguments, but that wouldn't work well with IDE helpers | // this macro part could be replaced by variadic functions that just pass on their arguments, but that wouldn't work well with IDE helpers | ||||||
| #define JSON_HELPERFUNCTIONS(NAME, TYPE) \ | #define JSON_HELPERFUNCTIONS(NAME, TYPE)                                                                              \ | ||||||
|     inline TYPE require##NAME(const QJsonValue &value, const QString &what = "Value") \ |     inline TYPE require##NAME(const QJsonValue& value, const QString& what = "Value")                                 \ | ||||||
|     { \ |     {                                                                                                                 \ | ||||||
|         return requireIsType<TYPE>(value, what); \ |         return requireIsType<TYPE>(value, what);                                                                      \ | ||||||
|     } \ |     }                                                                                                                 \ | ||||||
|     inline TYPE ensure##NAME(const QJsonValue &value, const TYPE default_ = TYPE(), const QString &what = "Value") \ |     inline TYPE ensure##NAME(const QJsonValue& value, const TYPE default_ = TYPE(), const QString& what = "Value")    \ | ||||||
|     { \ |     {                                                                                                                 \ | ||||||
|         return ensureIsType<TYPE>(value, default_, what); \ |         return ensureIsType<TYPE>(value, default_, what);                                                             \ | ||||||
|     } \ |     }                                                                                                                 \ | ||||||
|     inline TYPE require##NAME(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__") \ |     inline TYPE require##NAME(const QJsonObject& parent, const QString& key, const QString& what = "__placeholder__") \ | ||||||
|     { \ |     {                                                                                                                 \ | ||||||
|         return requireIsType<TYPE>(parent, key, what); \ |         return requireIsType<TYPE>(parent, key, what);                                                                \ | ||||||
|     } \ |     }                                                                                                                 \ | ||||||
|     inline TYPE ensure##NAME(const QJsonObject &parent, const QString &key, const TYPE default_ = TYPE(), const QString &what = "__placeholder") \ |     inline TYPE ensure##NAME(const QJsonObject& parent, const QString& key, const TYPE default_ = TYPE(),             \ | ||||||
|     { \ |                              const QString& what = "__placeholder")                                                   \ | ||||||
|         return ensureIsType<TYPE>(parent, key, default_, what); \ |     {                                                                                                                 \ | ||||||
|  |         return ensureIsType<TYPE>(parent, key, default_, what);                                                       \ | ||||||
|     } |     } | ||||||
|  |  | ||||||
| JSON_HELPERFUNCTIONS(Array, QJsonArray) | JSON_HELPERFUNCTIONS(Array, QJsonArray) | ||||||
| @@ -276,5 +278,5 @@ JSON_HELPERFUNCTIONS(Variant, QVariant) | |||||||
|  |  | ||||||
| #undef JSON_HELPERFUNCTIONS | #undef JSON_HELPERFUNCTIONS | ||||||
|  |  | ||||||
| } | }  // namespace Json | ||||||
| using JSONValidationError = Json::JsonException; | using JSONValidationError = Json::JsonException; | ||||||
|   | |||||||
| @@ -1,42 +1,26 @@ | |||||||
| #include "KonamiCode.h" | #include "KonamiCode.h" | ||||||
|  |  | ||||||
| #include <array> |  | ||||||
| #include <QDebug> | #include <QDebug> | ||||||
|  | #include <array> | ||||||
|  |  | ||||||
| namespace { | namespace { | ||||||
| const std::array<Qt::Key, 10> konamiCode = | const std::array<Qt::Key, 10> konamiCode = { { Qt::Key_Up, Qt::Key_Up, Qt::Key_Down, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, | ||||||
| { |                                                Qt::Key_Left, Qt::Key_Right, Qt::Key_B, Qt::Key_A } }; | ||||||
|     { |  | ||||||
|         Qt::Key_Up, Qt::Key_Up, |  | ||||||
|         Qt::Key_Down, Qt::Key_Down, |  | ||||||
|         Qt::Key_Left, Qt::Key_Right, |  | ||||||
|         Qt::Key_Left, Qt::Key_Right, |  | ||||||
|         Qt::Key_B, Qt::Key_A |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| KonamiCode::KonamiCode(QObject* parent) : QObject(parent) |  | ||||||
| { |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | KonamiCode::KonamiCode(QObject* parent) : QObject(parent) {} | ||||||
|  |  | ||||||
| void KonamiCode::input(QEvent* event) | void KonamiCode::input(QEvent* event) | ||||||
| { | { | ||||||
|     if( event->type() == QEvent::KeyPress ) |     if (event->type() == QEvent::KeyPress) { | ||||||
|     { |         QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event); | ||||||
|         QKeyEvent *keyEvent = static_cast<QKeyEvent*>( event ); |  | ||||||
|         auto key = Qt::Key(keyEvent->key()); |         auto key = Qt::Key(keyEvent->key()); | ||||||
|         if(key == konamiCode[m_progress]) |         if (key == konamiCode[m_progress]) { | ||||||
|         { |             m_progress++; | ||||||
|             m_progress ++; |         } else { | ||||||
|         } |  | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             m_progress = 0; |             m_progress = 0; | ||||||
|         } |         } | ||||||
|         if(m_progress == static_cast<int>(konamiCode.size())) |         if (m_progress == static_cast<int>(konamiCode.size())) { | ||||||
|         { |  | ||||||
|             m_progress = 0; |             m_progress = 0; | ||||||
|             emit triggered(); |             emit triggered(); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -2,16 +2,15 @@ | |||||||
|  |  | ||||||
| #include <QKeyEvent> | #include <QKeyEvent> | ||||||
|  |  | ||||||
| class KonamiCode : public QObject | class KonamiCode : public QObject { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| public: |    public: | ||||||
|     KonamiCode(QObject *parent = 0); |     KonamiCode(QObject* parent = 0); | ||||||
|     void input(QEvent *event); |     void input(QEvent* event); | ||||||
|  |  | ||||||
| signals: |    signals: | ||||||
|     void triggered(); |     void triggered(); | ||||||
|  |  | ||||||
| private: |    private: | ||||||
|     int m_progress = 0; |     int m_progress = 0; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  PolyMC - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
| @@ -34,44 +34,41 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "LaunchController.h" | #include "LaunchController.h" | ||||||
| #include "minecraft/auth/AccountList.h" |  | ||||||
| #include "Application.h" | #include "Application.h" | ||||||
|  | #include "minecraft/auth/AccountList.h" | ||||||
|  |  | ||||||
| #include "ui/MainWindow.h" |  | ||||||
| #include "ui/InstanceWindow.h" | #include "ui/InstanceWindow.h" | ||||||
|  | #include "ui/MainWindow.h" | ||||||
| #include "ui/dialogs/CustomMessageBox.h" | #include "ui/dialogs/CustomMessageBox.h" | ||||||
| #include "ui/dialogs/ProfileSelectDialog.h" |  | ||||||
| #include "ui/dialogs/ProgressDialog.h" |  | ||||||
| #include "ui/dialogs/EditAccountDialog.h" | #include "ui/dialogs/EditAccountDialog.h" | ||||||
|  | #include "ui/dialogs/ProfileSelectDialog.h" | ||||||
| #include "ui/dialogs/ProfileSetupDialog.h" | #include "ui/dialogs/ProfileSetupDialog.h" | ||||||
|  | #include "ui/dialogs/ProgressDialog.h" | ||||||
|  |  | ||||||
| #include <QLineEdit> |  | ||||||
| #include <QInputDialog> |  | ||||||
| #include <QStringList> |  | ||||||
| #include <QHostInfo> |  | ||||||
| #include <QList> |  | ||||||
| #include <QHostAddress> | #include <QHostAddress> | ||||||
|  | #include <QHostInfo> | ||||||
|  | #include <QInputDialog> | ||||||
|  | #include <QLineEdit> | ||||||
|  | #include <QList> | ||||||
| #include <QPushButton> | #include <QPushButton> | ||||||
|  | #include <QStringList> | ||||||
|  |  | ||||||
| #include "BuildConfig.h" | #include "BuildConfig.h" | ||||||
| #include "JavaCommon.h" | #include "JavaCommon.h" | ||||||
| #include "tasks/Task.h" |  | ||||||
| #include "minecraft/auth/AccountTask.h" |  | ||||||
| #include "launch/steps/TextPrint.h" | #include "launch/steps/TextPrint.h" | ||||||
|  | #include "minecraft/auth/AccountTask.h" | ||||||
|  | #include "tasks/Task.h" | ||||||
|  |  | ||||||
| LaunchController::LaunchController(QObject *parent) : Task(parent) | LaunchController::LaunchController(QObject* parent) : Task(parent) {} | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void LaunchController::executeTask() | void LaunchController::executeTask() | ||||||
| { | { | ||||||
|     if (!m_instance) |     if (!m_instance) { | ||||||
|     { |  | ||||||
|         emitFailed(tr("No instance specified!")); |         emitFailed(tr("No instance specified!")); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if(!JavaCommon::checkJVMArgs(m_instance->settings()->get("JvmArgs").toString(), m_parentWidget)) { |     if (!JavaCommon::checkJVMArgs(m_instance->settings()->get("JvmArgs").toString(), m_parentWidget)) { | ||||||
|         emitFailed(tr("Invalid Java arguments specified. Please fix this first.")); |         emitFailed(tr("Invalid Java arguments specified. Please fix this first.")); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @@ -81,32 +78,25 @@ void LaunchController::executeTask() | |||||||
|  |  | ||||||
| void LaunchController::decideAccount() | void LaunchController::decideAccount() | ||||||
| { | { | ||||||
|     if(m_accountToUse) { |     if (m_accountToUse) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Find an account to use. |     // Find an account to use. | ||||||
|     auto accounts = APPLICATION->accounts(); |     auto accounts = APPLICATION->accounts(); | ||||||
|     if (accounts->count() <= 0) |     if (accounts->count() <= 0) { | ||||||
|     { |  | ||||||
|         // Tell the user they need to log in at least one account in order to play. |         // Tell the user they need to log in at least one account in order to play. | ||||||
|         auto reply = CustomMessageBox::selectable( |         auto reply = CustomMessageBox::selectable(m_parentWidget, tr("No Accounts"), | ||||||
|             m_parentWidget, |                                                   tr("In order to play Minecraft, you must have at least one Microsoft or Mojang " | ||||||
|             tr("No Accounts"), |                                                      "account logged in. Mojang accounts can only be used offline. " | ||||||
|             tr("In order to play Minecraft, you must have at least one Microsoft or Mojang " |                                                      "Would you like to open the account manager to add an account now?"), | ||||||
|                "account logged in. Mojang accounts can only be used offline. " |                                                   QMessageBox::Information, QMessageBox::Yes | QMessageBox::No) | ||||||
|                "Would you like to open the account manager to add an account now?"), |                          ->exec(); | ||||||
|             QMessageBox::Information, |  | ||||||
|             QMessageBox::Yes | QMessageBox::No |  | ||||||
|         )->exec(); |  | ||||||
|  |  | ||||||
|         if (reply == QMessageBox::Yes) |         if (reply == QMessageBox::Yes) { | ||||||
|         { |  | ||||||
|             // Open the account manager. |             // Open the account manager. | ||||||
|             APPLICATION->ShowGlobalSettings(m_parentWidget, "accounts"); |             APPLICATION->ShowGlobalSettings(m_parentWidget, "accounts"); | ||||||
|         } |         } else if (reply == QMessageBox::No) { | ||||||
|         else if (reply == QMessageBox::No) |  | ||||||
|         { |  | ||||||
|             // Do not open "profile select" dialog. |             // Do not open "profile select" dialog. | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| @@ -121,14 +111,10 @@ void LaunchController::decideAccount() | |||||||
|         m_accountToUse = accounts->at(instanceAccountIndex); |         m_accountToUse = accounts->at(instanceAccountIndex); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!m_accountToUse) |     if (!m_accountToUse) { | ||||||
|     { |  | ||||||
|         // If no default account is set, ask the user which one to use. |         // If no default account is set, ask the user which one to use. | ||||||
|         ProfileSelectDialog selectDialog( |         ProfileSelectDialog selectDialog(tr("Which account would you like to use?"), ProfileSelectDialog::GlobalDefaultCheckbox, | ||||||
|             tr("Which account would you like to use?"), |                                          m_parentWidget); | ||||||
|             ProfileSelectDialog::GlobalDefaultCheckbox, |  | ||||||
|             m_parentWidget |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         selectDialog.exec(); |         selectDialog.exec(); | ||||||
|  |  | ||||||
| @@ -142,13 +128,12 @@ void LaunchController::decideAccount() | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void LaunchController::login() | ||||||
| void LaunchController::login() { | { | ||||||
|     decideAccount(); |     decideAccount(); | ||||||
|  |  | ||||||
|     // if no account is selected, we bail |     // if no account is selected, we bail | ||||||
|     if (!m_accountToUse) |     if (!m_accountToUse) { | ||||||
|     { |  | ||||||
|         emitFailed(tr("No account selected for launch.")); |         emitFailed(tr("No account selected for launch.")); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @@ -157,15 +142,11 @@ void LaunchController::login() { | |||||||
|     bool tryagain = true; |     bool tryagain = true; | ||||||
|     unsigned int tries = 0; |     unsigned int tries = 0; | ||||||
|  |  | ||||||
|     while (tryagain) |     while (tryagain) { | ||||||
|     { |  | ||||||
|         if (tries > 0 && tries % 3 == 0) { |         if (tries > 0 && tries % 3 == 0) { | ||||||
|             auto result = QMessageBox::question( |             auto result = | ||||||
|                 m_parentWidget, |                 QMessageBox::question(m_parentWidget, tr("Continue launch?"), | ||||||
|                 tr("Continue launch?"), |                                       tr("It looks like we couldn't launch after %1 tries. Do you want to continue trying?").arg(tries)); | ||||||
|                 tr("It looks like we couldn't launch after %1 tries. Do you want to continue trying?") |  | ||||||
|                     .arg(tries) |  | ||||||
|             ); |  | ||||||
|  |  | ||||||
|             if (result == QMessageBox::No) { |             if (result == QMessageBox::No) { | ||||||
|                 emitAborted(); |                 emitAborted(); | ||||||
| @@ -179,60 +160,48 @@ void LaunchController::login() { | |||||||
|         m_accountToUse->fillSession(m_session); |         m_accountToUse->fillSession(m_session); | ||||||
|  |  | ||||||
|         // Launch immediately in true offline mode |         // Launch immediately in true offline mode | ||||||
|         if(m_accountToUse->isOffline()) { |         if (m_accountToUse->isOffline()) { | ||||||
|             launchInstance(); |             launchInstance(); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         switch(m_accountToUse->accountState()) { |         switch (m_accountToUse->accountState()) { | ||||||
|             case AccountState::Offline: { |             case AccountState::Offline: { | ||||||
|                 m_session->wants_online = false; |                 m_session->wants_online = false; | ||||||
|             } |             } | ||||||
|             /* fallthrough */ |             /* fallthrough */ | ||||||
|             case AccountState::Online: { |             case AccountState::Online: { | ||||||
|                 if(!m_session->wants_online) { |                 if (!m_session->wants_online) { | ||||||
|                     // we ask the user for a player name |                     // we ask the user for a player name | ||||||
|                     bool ok = false; |                     bool ok = false; | ||||||
|  |  | ||||||
|                     QString message = tr("Choose your offline mode player name."); |                     QString message = tr("Choose your offline mode player name."); | ||||||
|                     if(m_session->demo) { |                     if (m_session->demo) { | ||||||
|                         message = tr("Choose your demo mode player name."); |                         message = tr("Choose your demo mode player name."); | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
|                     QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString(); |                     QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString(); | ||||||
|                     QString usedname = lastOfflinePlayerName.isEmpty() ? m_session->player_name : lastOfflinePlayerName; |                     QString usedname = lastOfflinePlayerName.isEmpty() ? m_session->player_name : lastOfflinePlayerName; | ||||||
|                     QString name = QInputDialog::getText( |                     QString name = QInputDialog::getText(m_parentWidget, tr("Player name"), message, QLineEdit::Normal, usedname, &ok); | ||||||
|                         m_parentWidget, |                     if (!ok) { | ||||||
|                         tr("Player name"), |  | ||||||
|                         message, |  | ||||||
|                         QLineEdit::Normal, |  | ||||||
|                         usedname, |  | ||||||
|                         &ok |  | ||||||
|                     ); |  | ||||||
|                     if (!ok) |  | ||||||
|                     { |  | ||||||
|                         tryagain = false; |                         tryagain = false; | ||||||
|                         break; |                         break; | ||||||
|                     } |                     } | ||||||
|                     if (name.length()) |                     if (name.length()) { | ||||||
|                     { |  | ||||||
|                         usedname = name; |                         usedname = name; | ||||||
|                         APPLICATION->settings()->set("LastOfflinePlayerName", usedname); |                         APPLICATION->settings()->set("LastOfflinePlayerName", usedname); | ||||||
|                     } |                     } | ||||||
|                     m_session->MakeOffline(usedname); |                     m_session->MakeOffline(usedname); | ||||||
|                     // offline flavored game from here :3 |                     // offline flavored game from here :3 | ||||||
|                 } |                 } | ||||||
|                 if(m_accountToUse->ownsMinecraft()) { |                 if (m_accountToUse->ownsMinecraft()) { | ||||||
|                     if(!m_accountToUse->hasProfile()) { |                     if (!m_accountToUse->hasProfile()) { | ||||||
|                         // Now handle setting up a profile name here... |                         // Now handle setting up a profile name here... | ||||||
|                         ProfileSetupDialog dialog(m_accountToUse, m_parentWidget); |                         ProfileSetupDialog dialog(m_accountToUse, m_parentWidget); | ||||||
|                         if (dialog.exec() == QDialog::Accepted) |                         if (dialog.exec() == QDialog::Accepted) { | ||||||
|                         { |  | ||||||
|                             tryagain = true; |                             tryagain = true; | ||||||
|                             continue; |                             continue; | ||||||
|                         } |                         } else { | ||||||
|                         else |  | ||||||
|                         { |  | ||||||
|                             emitFailed(tr("Received undetermined session status during login.")); |                             emitFailed(tr("Received undetermined session status during login.")); | ||||||
|                             return; |                             return; | ||||||
|                         } |                         } | ||||||
| @@ -240,24 +209,24 @@ void LaunchController::login() { | |||||||
|                     // we own Minecraft, there is a profile, it's all ready to go! |                     // we own Minecraft, there is a profile, it's all ready to go! | ||||||
|                     launchInstance(); |                     launchInstance(); | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } else { | ||||||
|                 else { |  | ||||||
|                     // play demo ? |                     // play demo ? | ||||||
|                     QMessageBox box(m_parentWidget); |                     QMessageBox box(m_parentWidget); | ||||||
|                     box.setWindowTitle(tr("Play demo?")); |                     box.setWindowTitle(tr("Play demo?")); | ||||||
|                     box.setText(tr("This account does not own Minecraft.\nYou need to purchase the game first to play it.\n\nDo you want to play the demo?")); |                     box.setText( | ||||||
|  |                         tr("This account does not own Minecraft.\nYou need to purchase the game first to play it.\n\nDo you want to play " | ||||||
|  |                            "the demo?")); | ||||||
|                     box.setIcon(QMessageBox::Warning); |                     box.setIcon(QMessageBox::Warning); | ||||||
|                     auto demoButton = box.addButton(tr("Play Demo"), QMessageBox::ButtonRole::YesRole); |                     auto demoButton = box.addButton(tr("Play Demo"), QMessageBox::ButtonRole::YesRole); | ||||||
|                     auto cancelButton = box.addButton(tr("Cancel"), QMessageBox::ButtonRole::NoRole); |                     auto cancelButton = box.addButton(tr("Cancel"), QMessageBox::ButtonRole::NoRole); | ||||||
|                     box.setDefaultButton(cancelButton); |                     box.setDefaultButton(cancelButton); | ||||||
|  |  | ||||||
|                     box.exec(); |                     box.exec(); | ||||||
|                     if(box.clickedButton() == demoButton) { |                     if (box.clickedButton() == demoButton) { | ||||||
|                         // play demo here |                         // play demo here | ||||||
|                         m_session->MakeDemo(); |                         m_session->MakeDemo(); | ||||||
|                         launchInstance(); |                         launchInstance(); | ||||||
|                     } |                     } else { | ||||||
|                     else { |  | ||||||
|                         emitFailed(tr("Launch cancelled - account does not own Minecraft.")); |                         emitFailed(tr("Launch cancelled - account does not own Minecraft.")); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
| @@ -272,8 +241,7 @@ void LaunchController::login() { | |||||||
|             case AccountState::Working: { |             case AccountState::Working: { | ||||||
|                 // refresh is in progress, we need to wait for it to finish to proceed. |                 // refresh is in progress, we need to wait for it to finish to proceed. | ||||||
|                 ProgressDialog progDialog(m_parentWidget); |                 ProgressDialog progDialog(m_parentWidget); | ||||||
|                 if (m_online) |                 if (m_online) { | ||||||
|                 { |  | ||||||
|                     progDialog.setSkipButton(true, tr("Play Offline")); |                     progDialog.setSkipButton(true, tr("Play Offline")); | ||||||
|                 } |                 } | ||||||
|                 auto task = m_accountToUse->currentTask(); |                 auto task = m_accountToUse->currentTask(); | ||||||
| @@ -288,37 +256,24 @@ void LaunchController::login() { | |||||||
|             */ |             */ | ||||||
|             case AccountState::Expired: { |             case AccountState::Expired: { | ||||||
|                 auto errorString = tr("The account has expired and needs to be logged into manually again."); |                 auto errorString = tr("The account has expired and needs to be logged into manually again."); | ||||||
|                 QMessageBox::warning( |                 QMessageBox::warning(m_parentWidget, tr("Account refresh failed"), errorString, QMessageBox::StandardButton::Ok, | ||||||
|                     m_parentWidget, |                                      QMessageBox::StandardButton::Ok); | ||||||
|                     tr("Account refresh failed"), |  | ||||||
|                     errorString, |  | ||||||
|                     QMessageBox::StandardButton::Ok, |  | ||||||
|                     QMessageBox::StandardButton::Ok |  | ||||||
|                 ); |  | ||||||
|                 emitFailed(errorString); |                 emitFailed(errorString); | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|             case AccountState::Disabled: { |             case AccountState::Disabled: { | ||||||
|                 auto errorString = tr("The launcher's client identification has changed. Please remove this account and add it again."); |                 auto errorString = tr("The launcher's client identification has changed. Please remove this account and add it again."); | ||||||
|                 QMessageBox::warning( |                 QMessageBox::warning(m_parentWidget, tr("Client identification changed"), errorString, QMessageBox::StandardButton::Ok, | ||||||
|                         m_parentWidget, |                                      QMessageBox::StandardButton::Ok); | ||||||
|                         tr("Client identification changed"), |  | ||||||
|                         errorString, |  | ||||||
|                         QMessageBox::StandardButton::Ok, |  | ||||||
|                         QMessageBox::StandardButton::Ok |  | ||||||
|                 ); |  | ||||||
|                 emitFailed(errorString); |                 emitFailed(errorString); | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|             case AccountState::Gone: { |             case AccountState::Gone: { | ||||||
|                 auto errorString = tr("The account no longer exists on the servers. It may have been migrated, in which case please add the new account you migrated this one to."); |                 auto errorString = | ||||||
|                 QMessageBox::warning( |                     tr("The account no longer exists on the servers. It may have been migrated, in which case please add the new account " | ||||||
|                     m_parentWidget, |                        "you migrated this one to."); | ||||||
|                     tr("Account gone"), |                 QMessageBox::warning(m_parentWidget, tr("Account gone"), errorString, QMessageBox::StandardButton::Ok, | ||||||
|                     errorString, |                                      QMessageBox::StandardButton::Ok); | ||||||
|                     QMessageBox::StandardButton::Ok, |  | ||||||
|                     QMessageBox::StandardButton::Ok |  | ||||||
|                 ); |  | ||||||
|                 emitFailed(errorString); |                 emitFailed(errorString); | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
| @@ -332,48 +287,45 @@ void LaunchController::launchInstance() | |||||||
|     Q_ASSERT_X(m_instance != NULL, "launchInstance", "instance is NULL"); |     Q_ASSERT_X(m_instance != NULL, "launchInstance", "instance is NULL"); | ||||||
|     Q_ASSERT_X(m_session.get() != nullptr, "launchInstance", "session is NULL"); |     Q_ASSERT_X(m_session.get() != nullptr, "launchInstance", "session is NULL"); | ||||||
|  |  | ||||||
|     if(!m_instance->reloadSettings()) |     if (!m_instance->reloadSettings()) { | ||||||
|     { |  | ||||||
|         QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Couldn't load the instance profile.")); |         QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Couldn't load the instance profile.")); | ||||||
|         emitFailed(tr("Couldn't load the instance profile.")); |         emitFailed(tr("Couldn't load the instance profile.")); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     m_launcher = m_instance->createLaunchTask(m_session, m_serverToJoin); |     m_launcher = m_instance->createLaunchTask(m_session, m_serverToJoin); | ||||||
|     if (!m_launcher) |     if (!m_launcher) { | ||||||
|     { |  | ||||||
|         emitFailed(tr("Couldn't instantiate a launcher.")); |         emitFailed(tr("Couldn't instantiate a launcher.")); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     auto console = qobject_cast<InstanceWindow *>(m_parentWidget); |     auto console = qobject_cast<InstanceWindow*>(m_parentWidget); | ||||||
|     auto showConsole = m_instance->settings()->get("ShowConsole").toBool(); |     auto showConsole = m_instance->settings()->get("ShowConsole").toBool(); | ||||||
|     if(!console && showConsole) |     if (!console && showConsole) { | ||||||
|     { |  | ||||||
|         APPLICATION->showInstanceWindow(m_instance); |         APPLICATION->showInstanceWindow(m_instance); | ||||||
|     } |     } | ||||||
|     connect(m_launcher.get(), &LaunchTask::readyForLaunch, this, &LaunchController::readyForLaunch); |     connect(m_launcher.get(), &LaunchTask::readyForLaunch, this, &LaunchController::readyForLaunch); | ||||||
|     connect(m_launcher.get(), &LaunchTask::succeeded, this, &LaunchController::onSucceeded); |     connect(m_launcher.get(), &LaunchTask::succeeded, this, &LaunchController::onSucceeded); | ||||||
|     connect(m_launcher.get(), &LaunchTask::failed, this,  &LaunchController::onFailed); |     connect(m_launcher.get(), &LaunchTask::failed, this, &LaunchController::onFailed); | ||||||
|     connect(m_launcher.get(), &LaunchTask::requestProgress, this, &LaunchController::onProgressRequested); |     connect(m_launcher.get(), &LaunchTask::requestProgress, this, &LaunchController::onProgressRequested); | ||||||
|  |  | ||||||
|     // Prepend Online and Auth Status |     // Prepend Online and Auth Status | ||||||
|     QString online_mode; |     QString online_mode; | ||||||
|     if(m_session->wants_online) { |     if (m_session->wants_online) { | ||||||
|         online_mode = "online"; |         online_mode = "online"; | ||||||
|  |  | ||||||
|         // Prepend Server Status |         // Prepend Server Status | ||||||
|         QStringList servers = {"authserver.mojang.com", "session.minecraft.net", "textures.minecraft.net", "api.mojang.com"}; |         QStringList servers = { "authserver.mojang.com", "session.minecraft.net", "textures.minecraft.net", "api.mojang.com" }; | ||||||
|         QString resolved_servers = ""; |         QString resolved_servers = ""; | ||||||
|         QHostInfo host_info; |         QHostInfo host_info; | ||||||
|  |  | ||||||
|         for(QString server : servers) { |         for (QString server : servers) { | ||||||
|             host_info = QHostInfo::fromName(server); |             host_info = QHostInfo::fromName(server); | ||||||
|             resolved_servers = resolved_servers + server + " resolves to:\n    ["; |             resolved_servers = resolved_servers + server + " resolves to:\n    ["; | ||||||
|             if(!host_info.addresses().isEmpty()) { |             if (!host_info.addresses().isEmpty()) { | ||||||
|                 for(QHostAddress address : host_info.addresses()) { |                 for (QHostAddress address : host_info.addresses()) { | ||||||
|                     resolved_servers = resolved_servers + address.toString(); |                     resolved_servers = resolved_servers + address.toString(); | ||||||
|                     if(!host_info.addresses().endsWith(address)) { |                     if (!host_info.addresses().endsWith(address)) { | ||||||
|                         resolved_servers = resolved_servers + ", "; |                         resolved_servers = resolved_servers + ", "; | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
| @@ -387,37 +339,40 @@ void LaunchController::launchInstance() | |||||||
|         online_mode = m_demo ? "demo" : "offline"; |         online_mode = m_demo ? "demo" : "offline"; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher)); |     m_launcher->prependStep( | ||||||
|  |         makeShared<TextPrint>(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher)); | ||||||
|  |  | ||||||
|     // Prepend Version |     // Prepend Version | ||||||
|     m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), BuildConfig.LAUNCHER_DISPLAYNAME + " version: " + BuildConfig.printableVersionString() + "\n\n", MessageLevel::Launcher)); |     { | ||||||
|  |         auto versionString = QString("%1 version: %2 (%3)") | ||||||
|  |                                  .arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString(), BuildConfig.BUILD_PLATFORM); | ||||||
|  |         m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), versionString + "\n\n", MessageLevel::Launcher)); | ||||||
|  |     } | ||||||
|     m_launcher->start(); |     m_launcher->start(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void LaunchController::readyForLaunch() | void LaunchController::readyForLaunch() | ||||||
| { | { | ||||||
|     if (!m_profiler) |     if (!m_profiler) { | ||||||
|     { |  | ||||||
|         m_launcher->proceed(); |         m_launcher->proceed(); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     QString error; |     QString error; | ||||||
|     if (!m_profiler->check(&error)) |     if (!m_profiler->check(&error)) { | ||||||
|     { |  | ||||||
|         m_launcher->abort(); |         m_launcher->abort(); | ||||||
|         QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Couldn't start profiler: %1").arg(error)); |         QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Couldn't start profiler: %1").arg(error)); | ||||||
|         emitFailed("Profiler startup failed!"); |         emitFailed("Profiler startup failed!"); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     BaseProfiler *profilerInstance = m_profiler->createProfiler(m_launcher->instance(), this); |     BaseProfiler* profilerInstance = m_profiler->createProfiler(m_launcher->instance(), this); | ||||||
|  |  | ||||||
|     connect(profilerInstance, &BaseProfiler::readyToLaunch, [this](const QString & message) |     connect(profilerInstance, &BaseProfiler::readyToLaunch, [this](const QString& message) { | ||||||
|     { |  | ||||||
|         QMessageBox msg; |         QMessageBox msg; | ||||||
|         msg.setText(tr("The game launch is delayed until you press the " |         msg.setText(tr("The game launch is delayed until you press the " | ||||||
|                         "button. This is the right time to setup the profiler, as the " |                        "button. This is the right time to setup the profiler, as the " | ||||||
|                         "profiler server is running now.\n\n%1").arg(message)); |                        "profiler server is running now.\n\n%1") | ||||||
|  |                         .arg(message)); | ||||||
|         msg.setWindowTitle(tr("Waiting.")); |         msg.setWindowTitle(tr("Waiting.")); | ||||||
|         msg.setIcon(QMessageBox::Information); |         msg.setIcon(QMessageBox::Information); | ||||||
|         msg.addButton(tr("Launch"), QMessageBox::AcceptRole); |         msg.addButton(tr("Launch"), QMessageBox::AcceptRole); | ||||||
| @@ -425,8 +380,7 @@ void LaunchController::readyForLaunch() | |||||||
|         msg.exec(); |         msg.exec(); | ||||||
|         m_launcher->proceed(); |         m_launcher->proceed(); | ||||||
|     }); |     }); | ||||||
|     connect(profilerInstance, &BaseProfiler::abortLaunch, [this](const QString & message) |     connect(profilerInstance, &BaseProfiler::abortLaunch, [this](const QString& message) { | ||||||
|     { |  | ||||||
|         QMessageBox msg; |         QMessageBox msg; | ||||||
|         msg.setText(tr("Couldn't start the profiler: %1").arg(message)); |         msg.setText(tr("Couldn't start the profiler: %1").arg(message)); | ||||||
|         msg.setWindowTitle(tr("Error")); |         msg.setWindowTitle(tr("Error")); | ||||||
| @@ -447,8 +401,7 @@ void LaunchController::onSucceeded() | |||||||
|  |  | ||||||
| void LaunchController::onFailed(QString reason) | void LaunchController::onFailed(QString reason) | ||||||
| { | { | ||||||
|     if(m_instance->settings()->get("ShowConsoleOnError").toBool()) |     if (m_instance->settings()->get("ShowConsoleOnError").toBool()) { | ||||||
|     { |  | ||||||
|         APPLICATION->showInstanceWindow(m_instance, "console"); |         APPLICATION->showInstanceWindow(m_instance, "console"); | ||||||
|     } |     } | ||||||
|     emitFailed(reason); |     emitFailed(reason); | ||||||
| @@ -464,21 +417,18 @@ void LaunchController::onProgressRequested(Task* task) | |||||||
|  |  | ||||||
| bool LaunchController::abort() | bool LaunchController::abort() | ||||||
| { | { | ||||||
|     if(!m_launcher) |     if (!m_launcher) { | ||||||
|     { |  | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|     if(!m_launcher->canAbort()) |     if (!m_launcher->canAbort()) { | ||||||
|     { |  | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|     auto response = CustomMessageBox::selectable( |     auto response = CustomMessageBox::selectable(m_parentWidget, tr("Kill Minecraft?"), | ||||||
|             m_parentWidget, tr("Kill Minecraft?"), |                                                  tr("This can cause the instance to get corrupted and should only be used if Minecraft " | ||||||
|             tr("This can cause the instance to get corrupted and should only be used if Minecraft " |                                                     "is frozen for some reason"), | ||||||
|             "is frozen for some reason"), |                                                  QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) | ||||||
|             QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)->exec(); |                         ->exec(); | ||||||
|     if (response == QMessageBox::Yes) |     if (response == QMessageBox::Yes) { | ||||||
|     { |  | ||||||
|         return m_launcher->abort(); |         return m_launcher->abort(); | ||||||
|     } |     } | ||||||
|     return false; |     return false; | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  PolyMC - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
| @@ -34,81 +34,61 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
| #include <QObject> |  | ||||||
| #include <BaseInstance.h> | #include <BaseInstance.h> | ||||||
| #include <tools/BaseProfiler.h> | #include <tools/BaseProfiler.h> | ||||||
|  | #include <QObject> | ||||||
|  |  | ||||||
| #include "minecraft/launch/MinecraftServerTarget.h" |  | ||||||
| #include "minecraft/auth/MinecraftAccount.h" | #include "minecraft/auth/MinecraftAccount.h" | ||||||
|  | #include "minecraft/launch/MinecraftServerTarget.h" | ||||||
|  |  | ||||||
| class InstanceWindow; | class InstanceWindow; | ||||||
| class LaunchController: public Task | class LaunchController : public Task { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| public: |    public: | ||||||
|     void executeTask() override; |     void executeTask() override; | ||||||
|  |  | ||||||
|     LaunchController(QObject * parent = nullptr); |     LaunchController(QObject* parent = nullptr); | ||||||
|     virtual ~LaunchController(){}; |     virtual ~LaunchController(){}; | ||||||
|  |  | ||||||
|     void setInstance(InstancePtr instance) { |     void setInstance(InstancePtr instance) { m_instance = instance; } | ||||||
|         m_instance = instance; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     InstancePtr instance() { |     InstancePtr instance() { return m_instance; } | ||||||
|         return m_instance; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void setOnline(bool online) { |     void setOnline(bool online) { m_online = online; } | ||||||
|         m_online = online; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void setDemo(bool demo) { |     void setDemo(bool demo) { m_demo = demo; } | ||||||
|         m_demo = demo; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void setProfiler(BaseProfilerFactory *profiler) { |     void setProfiler(BaseProfilerFactory* profiler) { m_profiler = profiler; } | ||||||
|         m_profiler = profiler; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void setParentWidget(QWidget * widget) { |     void setParentWidget(QWidget* widget) { m_parentWidget = widget; } | ||||||
|         m_parentWidget = widget; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void setServerToJoin(MinecraftServerTargetPtr serverToJoin) { |     void setServerToJoin(MinecraftServerTargetPtr serverToJoin) { m_serverToJoin = std::move(serverToJoin); } | ||||||
|         m_serverToJoin = std::move(serverToJoin); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void setAccountToUse(MinecraftAccountPtr accountToUse) { |     void setAccountToUse(MinecraftAccountPtr accountToUse) { m_accountToUse = std::move(accountToUse); } | ||||||
|         m_accountToUse = std::move(accountToUse); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     QString id() |     QString id() { return m_instance->id(); } | ||||||
|     { |  | ||||||
|         return m_instance->id(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bool abort() override; |     bool abort() override; | ||||||
|  |  | ||||||
| private: |    private: | ||||||
|     void login(); |     void login(); | ||||||
|     void launchInstance(); |     void launchInstance(); | ||||||
|     void decideAccount(); |     void decideAccount(); | ||||||
|  |  | ||||||
| private slots: |    private slots: | ||||||
|     void readyForLaunch(); |     void readyForLaunch(); | ||||||
|  |  | ||||||
|     void onSucceeded(); |     void onSucceeded(); | ||||||
|     void onFailed(QString reason); |     void onFailed(QString reason); | ||||||
|     void onProgressRequested(Task *task); |     void onProgressRequested(Task* task); | ||||||
|  |  | ||||||
| private: |    private: | ||||||
|     BaseProfilerFactory *m_profiler = nullptr; |     BaseProfilerFactory* m_profiler = nullptr; | ||||||
|     bool m_online = true; |     bool m_online = true; | ||||||
|     bool m_demo = false; |     bool m_demo = false; | ||||||
|     InstancePtr m_instance; |     InstancePtr m_instance; | ||||||
|     QWidget * m_parentWidget = nullptr; |     QWidget* m_parentWidget = nullptr; | ||||||
|     InstanceWindow *m_console = nullptr; |     InstanceWindow* m_console = nullptr; | ||||||
|     MinecraftAccountPtr m_accountToUse = nullptr; |     MinecraftAccountPtr m_accountToUse = nullptr; | ||||||
|     AuthSessionPtr m_session; |     AuthSessionPtr m_session; | ||||||
|     shared_qobject_ptr<LaunchTask> m_launcher; |     shared_qobject_ptr<LaunchTask> m_launcher; | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
| /* | /* | ||||||
|  *  Prism Launcher - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022,2023 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022,2023 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  *  Copyright (c) 2023 flowln <flowlnlnln@gmail.com>  |  *  Copyright (c) 2023 flowln <flowlnlnln@gmail.com> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  *  it under the terms of the GNU General Public License as published by |  *  it under the terms of the GNU General Public License as published by | ||||||
| @@ -39,7 +39,7 @@ | |||||||
| #include <QTextDecoder> | #include <QTextDecoder> | ||||||
| #include "MessageLevel.h" | #include "MessageLevel.h" | ||||||
|  |  | ||||||
| LoggedProcess::LoggedProcess(QObject *parent) : QProcess(parent) | LoggedProcess::LoggedProcess(QObject* parent) : QProcess(parent) | ||||||
| { | { | ||||||
|     // QProcess has a strange interface... let's map a lot of those into a few. |     // QProcess has a strange interface... let's map a lot of those into a few. | ||||||
|     connect(this, &QProcess::readyReadStandardOutput, this, &LoggedProcess::on_stdOut); |     connect(this, &QProcess::readyReadStandardOutput, this, &LoggedProcess::on_stdOut); | ||||||
| @@ -51,8 +51,7 @@ LoggedProcess::LoggedProcess(QObject *parent) : QProcess(parent) | |||||||
|  |  | ||||||
| LoggedProcess::~LoggedProcess() | LoggedProcess::~LoggedProcess() | ||||||
| { | { | ||||||
|     if(m_is_detachable) |     if (m_is_detachable) { | ||||||
|     { |  | ||||||
|         setProcessState(QProcess::NotRunning); |         setProcessState(QProcess::NotRunning); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -66,14 +65,9 @@ QStringList LoggedProcess::reprocess(const QByteArray& data, QTextDecoder& decod | |||||||
|         m_leftover_line = ""; |         m_leftover_line = ""; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) |     auto lines = str.remove(QChar::CarriageReturn).split(QChar::LineFeed); | ||||||
|     auto lines = str.remove(QChar::CarriageReturn).split(QChar::LineFeed, QString::SkipEmptyParts); |  | ||||||
| #else |  | ||||||
|     auto lines = str.remove(QChar::CarriageReturn).split(QChar::LineFeed, Qt::SkipEmptyParts); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     if (!str.endsWith(QChar::LineFeed)) |     m_leftover_line = lines.takeLast(); | ||||||
|         m_leftover_line = lines.takeLast(); |  | ||||||
|     return lines; |     return lines; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -95,39 +89,31 @@ void LoggedProcess::on_exit(int exit_code, QProcess::ExitStatus status) | |||||||
|     m_exit_code = exit_code; |     m_exit_code = exit_code; | ||||||
|  |  | ||||||
|     // based on state, send signals |     // based on state, send signals | ||||||
|     if (!m_is_aborting) |     if (!m_is_aborting) { | ||||||
|     { |         if (status == QProcess::NormalExit) { | ||||||
|         if (status == QProcess::NormalExit) |  | ||||||
|         { |  | ||||||
|             //: Message displayed on instance exit |             //: Message displayed on instance exit | ||||||
|             emit log({tr("Process exited with code %1.").arg(exit_code)}, MessageLevel::Launcher); |             emit log({ tr("Process exited with code %1.").arg(exit_code) }, MessageLevel::Launcher); | ||||||
|             changeState(LoggedProcess::Finished); |             changeState(LoggedProcess::Finished); | ||||||
|         } |         } else { | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             //: Message displayed on instance crashed |             //: Message displayed on instance crashed | ||||||
|             if(exit_code == -1) |             if (exit_code == -1) | ||||||
|                 emit log({tr("Process crashed.")}, MessageLevel::Launcher); |                 emit log({ tr("Process crashed.") }, MessageLevel::Launcher); | ||||||
|             else |             else | ||||||
|                 emit log({tr("Process crashed with exitcode %1.").arg(exit_code)}, MessageLevel::Launcher); |                 emit log({ tr("Process crashed with exitcode %1.").arg(exit_code) }, MessageLevel::Launcher); | ||||||
|             changeState(LoggedProcess::Crashed); |             changeState(LoggedProcess::Crashed); | ||||||
|         } |         } | ||||||
|     } |     } else { | ||||||
|     else |  | ||||||
|     { |  | ||||||
|         //: Message displayed after the instance exits due to kill request |         //: Message displayed after the instance exits due to kill request | ||||||
|         emit log({tr("Process was killed by user.")}, MessageLevel::Error); |         emit log({ tr("Process was killed by user.") }, MessageLevel::Error); | ||||||
|         changeState(LoggedProcess::Aborted); |         changeState(LoggedProcess::Aborted); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void LoggedProcess::on_error(QProcess::ProcessError error) | void LoggedProcess::on_error(QProcess::ProcessError error) | ||||||
| { | { | ||||||
|     switch(error) |     switch (error) { | ||||||
|     { |         case QProcess::FailedToStart: { | ||||||
|         case QProcess::FailedToStart: |             emit log({ tr("The process failed to start.") }, MessageLevel::Fatal); | ||||||
|         { |  | ||||||
|             emit log({tr("The process failed to start.")}, MessageLevel::Fatal); |  | ||||||
|             changeState(LoggedProcess::FailedToStart); |             changeState(LoggedProcess::FailedToStart); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| @@ -154,7 +140,7 @@ int LoggedProcess::exitCode() const | |||||||
|  |  | ||||||
| void LoggedProcess::changeState(LoggedProcess::State state) | void LoggedProcess::changeState(LoggedProcess::State state) | ||||||
| { | { | ||||||
|     if(state == m_state) |     if (state == m_state) | ||||||
|         return; |         return; | ||||||
|     m_state = state; |     m_state = state; | ||||||
|     emit stateChanged(m_state); |     emit stateChanged(m_state); | ||||||
| @@ -167,24 +153,19 @@ LoggedProcess::State LoggedProcess::state() const | |||||||
|  |  | ||||||
| void LoggedProcess::on_stateChange(QProcess::ProcessState state) | void LoggedProcess::on_stateChange(QProcess::ProcessState state) | ||||||
| { | { | ||||||
|     switch(state) |     switch (state) { | ||||||
|     { |  | ||||||
|         case QProcess::NotRunning: |         case QProcess::NotRunning: | ||||||
|             break; // let's not - there are too many that handle this already. |             break;  // let's not - there are too many that handle this already. | ||||||
|         case QProcess::Starting: |         case QProcess::Starting: { | ||||||
|         { |             if (m_state != LoggedProcess::NotRunning) { | ||||||
|             if(m_state != LoggedProcess::NotRunning) |                 qWarning() << "Wrong state change for process from state" << m_state << "to" << (int)LoggedProcess::Starting; | ||||||
|             { |  | ||||||
|                 qWarning() << "Wrong state change for process from state" << m_state << "to" << (int) LoggedProcess::Starting; |  | ||||||
|             } |             } | ||||||
|             changeState(LoggedProcess::Starting); |             changeState(LoggedProcess::Starting); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         case QProcess::Running: |         case QProcess::Running: { | ||||||
|         { |             if (m_state != LoggedProcess::Starting) { | ||||||
|             if(m_state != LoggedProcess::Starting) |                 qWarning() << "Wrong state change for process from state" << m_state << "to" << (int)LoggedProcess::Running; | ||||||
|             { |  | ||||||
|                 qWarning() << "Wrong state change for process from state" << m_state << "to" << (int) LoggedProcess::Running; |  | ||||||
|             } |             } | ||||||
|             changeState(LoggedProcess::Running); |             changeState(LoggedProcess::Running); | ||||||
|             return; |             return; | ||||||
|   | |||||||
| @@ -43,22 +43,12 @@ | |||||||
|  * This is a basic process. |  * This is a basic process. | ||||||
|  * It has line-based logging support and hides some of the nasty bits. |  * It has line-based logging support and hides some of the nasty bits. | ||||||
|  */ |  */ | ||||||
| class LoggedProcess : public QProcess | class LoggedProcess : public QProcess { | ||||||
| { |     Q_OBJECT | ||||||
| Q_OBJECT |    public: | ||||||
| public: |     enum State { NotRunning, Starting, FailedToStart, Running, Finished, Crashed, Aborted }; | ||||||
|     enum State |  | ||||||
|     { |  | ||||||
|         NotRunning, |  | ||||||
|         Starting, |  | ||||||
|         FailedToStart, |  | ||||||
|         Running, |  | ||||||
|         Finished, |  | ||||||
|         Crashed, |  | ||||||
|         Aborted |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
| public: |    public: | ||||||
|     explicit LoggedProcess(QObject* parent = 0); |     explicit LoggedProcess(QObject* parent = 0); | ||||||
|     virtual ~LoggedProcess(); |     virtual ~LoggedProcess(); | ||||||
|  |  | ||||||
| @@ -67,30 +57,29 @@ public: | |||||||
|  |  | ||||||
|     void setDetachable(bool detachable); |     void setDetachable(bool detachable); | ||||||
|  |  | ||||||
| signals: |    signals: | ||||||
|     void log(QStringList lines, MessageLevel::Enum level); |     void log(QStringList lines, MessageLevel::Enum level); | ||||||
|     void stateChanged(LoggedProcess::State state); |     void stateChanged(LoggedProcess::State state); | ||||||
|  |  | ||||||
| public slots: |    public slots: | ||||||
|     /** |     /** | ||||||
|      * @brief kill the process - equivalent to kill -9 |      * @brief kill the process - equivalent to kill -9 | ||||||
|      */ |      */ | ||||||
|     void kill(); |     void kill(); | ||||||
|  |  | ||||||
|  |    private slots: | ||||||
| private slots: |  | ||||||
|     void on_stdErr(); |     void on_stdErr(); | ||||||
|     void on_stdOut(); |     void on_stdOut(); | ||||||
|     void on_exit(int exit_code, QProcess::ExitStatus status); |     void on_exit(int exit_code, QProcess::ExitStatus status); | ||||||
|     void on_error(QProcess::ProcessError error); |     void on_error(QProcess::ProcessError error); | ||||||
|     void on_stateChange(QProcess::ProcessState); |     void on_stateChange(QProcess::ProcessState); | ||||||
|  |  | ||||||
| private: |    private: | ||||||
|     void changeState(LoggedProcess::State state); |     void changeState(LoggedProcess::State state); | ||||||
|  |  | ||||||
|     QStringList reprocess(const QByteArray& data, QTextDecoder& decoder); |     QStringList reprocess(const QByteArray& data, QTextDecoder& decoder); | ||||||
|  |  | ||||||
| private: |    private: | ||||||
|     QTextDecoder m_err_decoder = QTextDecoder(QTextCodec::codecForLocale()); |     QTextDecoder m_err_decoder = QTextDecoder(QTextCodec::codecForLocale()); | ||||||
|     QTextDecoder m_out_decoder = QTextDecoder(QTextCodec::codecForLocale()); |     QTextDecoder m_out_decoder = QTextDecoder(QTextCodec::codecForLocale()); | ||||||
|     QString m_leftover_line; |     QString m_leftover_line; | ||||||
|   | |||||||
| @@ -17,30 +17,29 @@ | |||||||
|  |  | ||||||
| #include <MMCTime.h> | #include <MMCTime.h> | ||||||
|  |  | ||||||
| #include <QObject> |  | ||||||
| #include <QDateTime> | #include <QDateTime> | ||||||
|  | #include <QObject> | ||||||
| #include <QTextStream> | #include <QTextStream> | ||||||
|  |  | ||||||
| QString Time::prettifyDuration(int64_t duration) { | QString Time::prettifyDuration(int64_t duration) | ||||||
|     int seconds = (int) (duration % 60); | { | ||||||
|  |     int seconds = (int)(duration % 60); | ||||||
|     duration /= 60; |     duration /= 60; | ||||||
|     int minutes = (int) (duration % 60); |     int minutes = (int)(duration % 60); | ||||||
|     duration /= 60; |     duration /= 60; | ||||||
|     int hours = (int) (duration % 24); |     int hours = (int)(duration % 24); | ||||||
|     int days = (int) (duration / 24); |     int days = (int)(duration / 24); | ||||||
|     if((hours == 0)&&(days == 0)) |     if ((hours == 0) && (days == 0)) { | ||||||
|     { |  | ||||||
|         return QObject::tr("%1min %2s").arg(minutes).arg(seconds); |         return QObject::tr("%1min %2s").arg(minutes).arg(seconds); | ||||||
|     } |     } | ||||||
|     if (days == 0) |     if (days == 0) { | ||||||
|     { |  | ||||||
|         return QObject::tr("%1h %2min").arg(hours).arg(minutes); |         return QObject::tr("%1h %2min").arg(hours).arg(minutes); | ||||||
|     } |     } | ||||||
|     return QObject::tr("%1d %2h %3min").arg(days).arg(hours).arg(minutes); |     return QObject::tr("%1d %2h %3min").arg(days).arg(hours).arg(minutes); | ||||||
| } | } | ||||||
|  |  | ||||||
| QString Time::humanReadableDuration(double duration, int precision) { | QString Time::humanReadableDuration(double duration, int precision) | ||||||
|  | { | ||||||
|     using days = std::chrono::duration<int, std::ratio<86400>>; |     using days = std::chrono::duration<int, std::ratio<86400>>; | ||||||
|  |  | ||||||
|     QString outStr; |     QString outStr; | ||||||
| @@ -48,10 +47,10 @@ QString Time::humanReadableDuration(double duration, int precision) { | |||||||
|  |  | ||||||
|     bool neg = false; |     bool neg = false; | ||||||
|     if (duration < 0) { |     if (duration < 0) { | ||||||
|         neg = true; // flag |         neg = true;      // flag | ||||||
|         duration  *= -1; // invert |         duration *= -1;  // invert | ||||||
|     } |     } | ||||||
|          |  | ||||||
|     auto std_duration = std::chrono::duration<double>(duration); |     auto std_duration = std::chrono::duration<double>(duration); | ||||||
|     auto d = std::chrono::duration_cast<days>(std_duration); |     auto d = std::chrono::duration_cast<days>(std_duration); | ||||||
|     std_duration -= d; |     std_duration -= d; | ||||||
| @@ -78,22 +77,22 @@ QString Time::humanReadableDuration(double duration, int precision) { | |||||||
|     if (hc) { |     if (hc) { | ||||||
|         if (dc) |         if (dc) | ||||||
|             os << " "; |             os << " "; | ||||||
|         os << qSetFieldWidth(2) << hc << QObject::tr("h"); // hours |         os << qSetFieldWidth(2) << hc << QObject::tr("h");  // hours | ||||||
|     } |     } | ||||||
|     if (mc) { |     if (mc) { | ||||||
|         if (dc || hc) |         if (dc || hc) | ||||||
|             os << " "; |             os << " "; | ||||||
|         os << qSetFieldWidth(2) << mc << QObject::tr("m"); // minutes |         os << qSetFieldWidth(2) << mc << QObject::tr("m");  // minutes | ||||||
|     } |     } | ||||||
|     if (dc || hc || mc || sc) { |     if (dc || hc || mc || sc) { | ||||||
|         if (dc || hc || mc) |         if (dc || hc || mc) | ||||||
|             os << " "; |             os << " "; | ||||||
|         os << qSetFieldWidth(2) << sc << QObject::tr("s"); // seconds |         os << qSetFieldWidth(2) << sc << QObject::tr("s");  // seconds | ||||||
|     } |     } | ||||||
|     if ((msc && (precision > 0)) || !(dc || hc || mc || sc)) { |     if ((msc && (precision > 0)) || !(dc || hc || mc || sc)) { | ||||||
|         if (dc || hc || mc || sc) |         if (dc || hc || mc || sc) | ||||||
|             os << " "; |             os << " "; | ||||||
|         os << qSetFieldWidth(0) << qSetRealNumberPrecision(precision) << msc << QObject::tr("ms"); // miliseconds |         os << qSetFieldWidth(0) << qSetRealNumberPrecision(precision) << msc << QObject::tr("ms");  // miliseconds | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     os.flush(); |     os.flush(); | ||||||
|   | |||||||
| @@ -25,10 +25,10 @@ QString prettifyDuration(int64_t duration); | |||||||
| /** | /** | ||||||
|  * @brief Returns a string with short form time duration ie. `2days 1h3m4s56.0ms`. |  * @brief Returns a string with short form time duration ie. `2days 1h3m4s56.0ms`. | ||||||
|  * miliseconds are only included if `precision` is greater than 0. |  * miliseconds are only included if `precision` is greater than 0. | ||||||
|  *  |  * | ||||||
|  * @param duration a number of seconds as floating point |  * @param duration a number of seconds as floating point | ||||||
|  * @param precision number of decmial points to display on fractons of a second, defualts to 0. |  * @param precision number of decmial points to display on fractons of a second, defualts to 0. | ||||||
|  * @return QString  |  * @return QString | ||||||
|  */ |  */ | ||||||
| QString humanReadableDuration(double duration, int precision = 0); | QString humanReadableDuration(double duration, int precision = 0); | ||||||
| } | }  // namespace Time | ||||||
|   | |||||||
| @@ -1,7 +1,8 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  PolyMC - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  |  *  Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  *  it under the terms of the GNU General Public License as published by |  *  it under the terms of the GNU General Public License as published by | ||||||
| @@ -33,56 +34,50 @@ | |||||||
|  *      limitations under the License. |  *      limitations under the License. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|  | #include "MMCZip.h" | ||||||
| #include <quazip/quazip.h> | #include <quazip/quazip.h> | ||||||
| #include <quazip/quazipdir.h> | #include <quazip/quazipdir.h> | ||||||
| #include <quazip/quazipfile.h> | #include <quazip/quazipfile.h> | ||||||
| #include "MMCZip.h" |  | ||||||
| #include "FileSystem.h" | #include "FileSystem.h" | ||||||
|  |  | ||||||
| #include <QCoreApplication> | #include <QCoreApplication> | ||||||
| #include <QDebug> | #include <QDebug> | ||||||
|  | #include <QtConcurrentRun> | ||||||
|  |  | ||||||
|  | namespace MMCZip { | ||||||
| // ours | // ours | ||||||
| bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained, const FilterFunction filter) | bool mergeZipFiles(QuaZip* into, QFileInfo from, QSet<QString>& contained, const FilterFunction filter) | ||||||
| { | { | ||||||
|     QuaZip modZip(from.filePath()); |     QuaZip modZip(from.filePath()); | ||||||
|     modZip.open(QuaZip::mdUnzip); |     modZip.open(QuaZip::mdUnzip); | ||||||
|  |  | ||||||
|     QuaZipFile fileInsideMod(&modZip); |     QuaZipFile fileInsideMod(&modZip); | ||||||
|     QuaZipFile zipOutFile(into); |     QuaZipFile zipOutFile(into); | ||||||
|     for (bool more = modZip.goToFirstFile(); more; more = modZip.goToNextFile()) |     for (bool more = modZip.goToFirstFile(); more; more = modZip.goToNextFile()) { | ||||||
|     { |  | ||||||
|         QString filename = modZip.getCurrentFileName(); |         QString filename = modZip.getCurrentFileName(); | ||||||
|         if (filter && !filter(filename)) |         if (filter && !filter(filename)) { | ||||||
|         { |             qDebug() << "Skipping file " << filename << " from " << from.fileName() << " - filtered"; | ||||||
|             qDebug() << "Skipping file " << filename << " from " |  | ||||||
|                         << from.fileName() << " - filtered"; |  | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|         if (contained.contains(filename)) |         if (contained.contains(filename)) { | ||||||
|         { |             qDebug() << "Skipping already contained file " << filename << " from " << from.fileName(); | ||||||
|             qDebug() << "Skipping already contained file " << filename << " from " |  | ||||||
|                         << from.fileName(); |  | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|         contained.insert(filename); |         contained.insert(filename); | ||||||
|  |  | ||||||
|         if (!fileInsideMod.open(QIODevice::ReadOnly)) |         if (!fileInsideMod.open(QIODevice::ReadOnly)) { | ||||||
|         { |  | ||||||
|             qCritical() << "Failed to open " << filename << " from " << from.fileName(); |             qCritical() << "Failed to open " << filename << " from " << from.fileName(); | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         QuaZipNewInfo info_out(fileInsideMod.getActualFileName()); |         QuaZipNewInfo info_out(fileInsideMod.getActualFileName()); | ||||||
|  |  | ||||||
|         if (!zipOutFile.open(QIODevice::WriteOnly, info_out)) |         if (!zipOutFile.open(QIODevice::WriteOnly, info_out)) { | ||||||
|         { |  | ||||||
|             qCritical() << "Failed to open " << filename << " in the jar"; |             qCritical() << "Failed to open " << filename << " in the jar"; | ||||||
|             fileInsideMod.close(); |             fileInsideMod.close(); | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|         if (!JlCompress::copyData(fileInsideMod, zipOutFile)) |         if (!JlCompress::copyData(fileInsideMod, zipOutFile)) { | ||||||
|         { |  | ||||||
|             zipOutFile.close(); |             zipOutFile.close(); | ||||||
|             fileInsideMod.close(); |             fileInsideMod.close(); | ||||||
|             qCritical() << "Failed to copy data of " << filename << " into the jar"; |             qCritical() << "Failed to copy data of " << filename << " into the jar"; | ||||||
| @@ -94,10 +89,11 @@ bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &containe | |||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool MMCZip::compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files, bool followSymlinks) | bool compressDirFiles(QuaZip* zip, QString dir, QFileInfoList files, bool followSymlinks) | ||||||
| { | { | ||||||
|     QDir directory(dir); |     QDir directory(dir); | ||||||
|     if (!directory.exists()) return false; |     if (!directory.exists()) | ||||||
|  |         return false; | ||||||
|  |  | ||||||
|     for (auto e : files) { |     for (auto e : files) { | ||||||
|         auto filePath = directory.relativeFilePath(e.absoluteFilePath()); |         auto filePath = directory.relativeFilePath(e.absoluteFilePath()); | ||||||
| @@ -109,17 +105,18 @@ bool MMCZip::compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files, boo | |||||||
|                 srcPath = e.canonicalFilePath(); |                 srcPath = e.canonicalFilePath(); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         if( !JlCompress::compressFile(zip, srcPath, filePath)) return false; |         if (!JlCompress::compressFile(zip, srcPath, filePath)) | ||||||
|  |             return false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks) | bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks) | ||||||
| { | { | ||||||
|     QuaZip zip(fileCompressed); |     QuaZip zip(fileCompressed); | ||||||
|     QDir().mkpath(QFileInfo(fileCompressed).absolutePath()); |     QDir().mkpath(QFileInfo(fileCompressed).absolutePath()); | ||||||
|     if(!zip.open(QuaZip::mdCreate)) { |     if (!zip.open(QuaZip::mdCreate)) { | ||||||
|         QFile::remove(fileCompressed); |         QFile::remove(fileCompressed); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| @@ -127,7 +124,7 @@ bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList | |||||||
|     auto result = compressDirFiles(&zip, dir, files, followSymlinks); |     auto result = compressDirFiles(&zip, dir, files, followSymlinks); | ||||||
|  |  | ||||||
|     zip.close(); |     zip.close(); | ||||||
|     if(zip.getZipError()!=0) { |     if (zip.getZipError() != 0) { | ||||||
|         QFile::remove(fileCompressed); |         QFile::remove(fileCompressed); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| @@ -136,11 +133,10 @@ bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList | |||||||
| } | } | ||||||
|  |  | ||||||
| // ours | // ours | ||||||
| bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods) | bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods) | ||||||
| { | { | ||||||
|     QuaZip zipOut(targetJarPath); |     QuaZip zipOut(targetJarPath); | ||||||
|     if (!zipOut.open(QuaZip::mdCreate)) |     if (!zipOut.open(QuaZip::mdCreate)) { | ||||||
|     { |  | ||||||
|         QFile::remove(targetJarPath); |         QFile::remove(targetJarPath); | ||||||
|         qCritical() << "Failed to open the minecraft.jar for modding"; |         qCritical() << "Failed to open the minecraft.jar for modding"; | ||||||
|         return false; |         return false; | ||||||
| @@ -151,37 +147,29 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const | |||||||
|  |  | ||||||
|     // Modify the jar |     // Modify the jar | ||||||
|     // This needs to be done in reverse-order to ensure we respect the loading order of components |     // This needs to be done in reverse-order to ensure we respect the loading order of components | ||||||
|     for (auto i = mods.crbegin(); i != mods.crend(); i++) |     for (auto i = mods.crbegin(); i != mods.crend(); i++) { | ||||||
|     { |  | ||||||
|         const auto* mod = *i; |         const auto* mod = *i; | ||||||
|         // do not merge disabled mods. |         // do not merge disabled mods. | ||||||
|         if (!mod->enabled()) |         if (!mod->enabled()) | ||||||
|             continue; |             continue; | ||||||
|         if (mod->type() == ResourceType::ZIPFILE) |         if (mod->type() == ResourceType::ZIPFILE) { | ||||||
|         { |             if (!mergeZipFiles(&zipOut, mod->fileinfo(), addedFiles)) { | ||||||
|             if (!mergeZipFiles(&zipOut, mod->fileinfo(), addedFiles)) |  | ||||||
|             { |  | ||||||
|                 zipOut.close(); |                 zipOut.close(); | ||||||
|                 QFile::remove(targetJarPath); |                 QFile::remove(targetJarPath); | ||||||
|                 qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; |                 qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|         } |         } else if (mod->type() == ResourceType::SINGLEFILE) { | ||||||
|         else if (mod->type() == ResourceType::SINGLEFILE) |  | ||||||
|         { |  | ||||||
|             // FIXME: buggy - does not work with addedFiles |             // FIXME: buggy - does not work with addedFiles | ||||||
|             auto filename = mod->fileinfo(); |             auto filename = mod->fileinfo(); | ||||||
|             if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName())) |             if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName())) { | ||||||
|             { |  | ||||||
|                 zipOut.close(); |                 zipOut.close(); | ||||||
|                 QFile::remove(targetJarPath); |                 QFile::remove(targetJarPath); | ||||||
|                 qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; |                 qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|             addedFiles.insert(filename.fileName()); |             addedFiles.insert(filename.fileName()); | ||||||
|         } |         } else if (mod->type() == ResourceType::FOLDER) { | ||||||
|         else if (mod->type() == ResourceType::FOLDER) |  | ||||||
|         { |  | ||||||
|             // untested, but seems to be unused / not possible to reach |             // untested, but seems to be unused / not possible to reach | ||||||
|             // FIXME: buggy - does not work with addedFiles |             // FIXME: buggy - does not work with addedFiles | ||||||
|             auto filename = mod->fileinfo(); |             auto filename = mod->fileinfo(); | ||||||
| @@ -190,25 +178,21 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const | |||||||
|             dir.cdUp(); |             dir.cdUp(); | ||||||
|             QString parent_dir = dir.absolutePath(); |             QString parent_dir = dir.absolutePath(); | ||||||
|             auto files = QFileInfoList(); |             auto files = QFileInfoList(); | ||||||
|             MMCZip::collectFileListRecursively(what_to_zip, nullptr, &files, nullptr); |             collectFileListRecursively(what_to_zip, nullptr, &files, nullptr); | ||||||
|  |  | ||||||
|             for (auto e : files) { |             for (auto e : files) { | ||||||
|                 if (addedFiles.contains(e.filePath())) |                 if (addedFiles.contains(e.filePath())) | ||||||
|                     files.removeAll(e); |                     files.removeAll(e); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (!MMCZip::compressDirFiles(&zipOut, parent_dir, files)) |             if (!compressDirFiles(&zipOut, parent_dir, files)) { | ||||||
|             { |  | ||||||
|                 zipOut.close(); |                 zipOut.close(); | ||||||
|                 QFile::remove(targetJarPath); |                 QFile::remove(targetJarPath); | ||||||
|                 qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; |                 qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|             qDebug() << "Adding folder " << filename.fileName() << " from " |             qDebug() << "Adding folder " << filename.fileName() << " from " << filename.absoluteFilePath(); | ||||||
|                      << filename.absoluteFilePath(); |         } else { | ||||||
|         } |  | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             // Make sure we do not continue launching when something is missing or undefined... |             // Make sure we do not continue launching when something is missing or undefined... | ||||||
|             zipOut.close(); |             zipOut.close(); | ||||||
|             QFile::remove(targetJarPath); |             QFile::remove(targetJarPath); | ||||||
| @@ -217,8 +201,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!mergeZipFiles(&zipOut, QFileInfo(sourceJarPath), addedFiles, [](const QString key){return !key.contains("META-INF");})) |     if (!mergeZipFiles(&zipOut, QFileInfo(sourceJarPath), addedFiles, [](const QString key) { return !key.contains("META-INF"); })) { | ||||||
|     { |  | ||||||
|         zipOut.close(); |         zipOut.close(); | ||||||
|         QFile::remove(targetJarPath); |         QFile::remove(targetJarPath); | ||||||
|         qCritical() << "Failed to insert minecraft.jar contents."; |         qCritical() << "Failed to insert minecraft.jar contents."; | ||||||
| @@ -227,8 +210,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const | |||||||
|  |  | ||||||
|     // Recompress the jar |     // Recompress the jar | ||||||
|     zipOut.close(); |     zipOut.close(); | ||||||
|     if (zipOut.getZipError() != 0) |     if (zipOut.getZipError() != 0) { | ||||||
|     { |  | ||||||
|         QFile::remove(targetJarPath); |         QFile::remove(targetJarPath); | ||||||
|         qCritical() << "Failed to finalize minecraft.jar!"; |         qCritical() << "Failed to finalize minecraft.jar!"; | ||||||
|         return false; |         return false; | ||||||
| @@ -237,7 +219,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const | |||||||
| } | } | ||||||
|  |  | ||||||
| // ours | // ours | ||||||
| QString MMCZip::findFolderOfFileInZip(QuaZip* zip, const QString& what, const QStringList& ignore_paths, const QString& root) | QString findFolderOfFileInZip(QuaZip* zip, const QString& what, const QStringList& ignore_paths, const QString& root) | ||||||
| { | { | ||||||
|     QuaZipDir rootDir(zip, root); |     QuaZipDir rootDir(zip, root); | ||||||
|     for (auto&& fileName : rootDir.entryList(QDir::Files)) { |     for (auto&& fileName : rootDir.entryList(QDir::Files)) { | ||||||
| @@ -261,27 +243,23 @@ QString MMCZip::findFolderOfFileInZip(QuaZip* zip, const QString& what, const QS | |||||||
| } | } | ||||||
|  |  | ||||||
| // ours | // ours | ||||||
| bool MMCZip::findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root) | bool findFilesInZip(QuaZip* zip, const QString& what, QStringList& result, const QString& root) | ||||||
| { | { | ||||||
|     QuaZipDir rootDir(zip, root); |     QuaZipDir rootDir(zip, root); | ||||||
|     for(auto fileName: rootDir.entryList(QDir::Files)) |     for (auto fileName : rootDir.entryList(QDir::Files)) { | ||||||
|     { |         if (fileName == what) { | ||||||
|         if(fileName == what) |  | ||||||
|         { |  | ||||||
|             result.append(root); |             result.append(root); | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     for(auto fileName: rootDir.entryList(QDir::Dirs)) |     for (auto fileName : rootDir.entryList(QDir::Dirs)) { | ||||||
|     { |  | ||||||
|         findFilesInZip(zip, what, result, root + fileName); |         findFilesInZip(zip, what, result, root + fileName); | ||||||
|     } |     } | ||||||
|     return !result.isEmpty(); |     return !result.isEmpty(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| // ours | // ours | ||||||
| std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & subdir, const QString &target) | std::optional<QStringList> extractSubDir(QuaZip* zip, const QString& subdir, const QString& target) | ||||||
| { | { | ||||||
|     auto target_top_dir = QUrl::fromLocalFile(target); |     auto target_top_dir = QUrl::fromLocalFile(target); | ||||||
|  |  | ||||||
| @@ -289,16 +267,13 @@ std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & su | |||||||
|  |  | ||||||
|     qDebug() << "Extracting subdir" << subdir << "from" << zip->getZipName() << "to" << target; |     qDebug() << "Extracting subdir" << subdir << "from" << zip->getZipName() << "to" << target; | ||||||
|     auto numEntries = zip->getEntriesCount(); |     auto numEntries = zip->getEntriesCount(); | ||||||
|     if(numEntries < 0) { |     if (numEntries < 0) { | ||||||
|         qWarning() << "Failed to enumerate files in archive"; |         qWarning() << "Failed to enumerate files in archive"; | ||||||
|         return std::nullopt; |         return std::nullopt; | ||||||
|     } |     } else if (numEntries == 0) { | ||||||
|     else if(numEntries == 0) { |  | ||||||
|         qDebug() << "Extracting empty archives seems odd..."; |         qDebug() << "Extracting empty archives seems odd..."; | ||||||
|         return extracted; |         return extracted; | ||||||
|     } |     } else if (!zip->goToFirstFile()) { | ||||||
|     else if (!zip->goToFirstFile()) |  | ||||||
|     { |  | ||||||
|         qWarning() << "Failed to seek to first file in zip"; |         qWarning() << "Failed to seek to first file in zip"; | ||||||
|         return std::nullopt; |         return std::nullopt; | ||||||
|     } |     } | ||||||
| @@ -334,7 +309,8 @@ std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & su | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (!target_top_dir.isParentOf(QUrl::fromLocalFile(target_file_path))) { |         if (!target_top_dir.isParentOf(QUrl::fromLocalFile(target_file_path))) { | ||||||
|             qWarning() << "Extracting" << relative_file_name << "was cancelled, because it was effectively outside of the target path" << target; |             qWarning() << "Extracting" << relative_file_name << "was cancelled, because it was effectively outside of the target path" | ||||||
|  |                        << target; | ||||||
|             return std::nullopt; |             return std::nullopt; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -345,7 +321,8 @@ std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & su | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         extracted.append(target_file_path); |         extracted.append(target_file_path); | ||||||
|         QFile::setPermissions(target_file_path, QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser); |         QFile::setPermissions(target_file_path, | ||||||
|  |                               QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser); | ||||||
|  |  | ||||||
|         qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path; |         qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path; | ||||||
|     } while (zip->goToNextFile()); |     } while (zip->goToNextFile()); | ||||||
| @@ -354,66 +331,66 @@ std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & su | |||||||
| } | } | ||||||
|  |  | ||||||
| // ours | // ours | ||||||
| bool MMCZip::extractRelFile(QuaZip *zip, const QString &file, const QString &target) | bool extractRelFile(QuaZip* zip, const QString& file, const QString& target) | ||||||
| { | { | ||||||
|     return JlCompress::extractFile(zip, file, target); |     return JlCompress::extractFile(zip, file, target); | ||||||
| } | } | ||||||
|  |  | ||||||
| // ours | // ours | ||||||
| std::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString dir) | std::optional<QStringList> extractDir(QString fileCompressed, QString dir) | ||||||
| { | { | ||||||
|     QuaZip zip(fileCompressed); |     QuaZip zip(fileCompressed); | ||||||
|     if (!zip.open(QuaZip::mdUnzip)) |     if (!zip.open(QuaZip::mdUnzip)) { | ||||||
|     { |  | ||||||
|         // check if this is a minimum size empty zip file... |         // check if this is a minimum size empty zip file... | ||||||
|         QFileInfo fileInfo(fileCompressed); |         QFileInfo fileInfo(fileCompressed); | ||||||
|         if(fileInfo.size() == 22) { |         if (fileInfo.size() == 22) { | ||||||
|             return QStringList(); |             return QStringList(); | ||||||
|         } |         } | ||||||
|         qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();; |         qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError(); | ||||||
|  |         ; | ||||||
|         return std::nullopt; |         return std::nullopt; | ||||||
|     } |     } | ||||||
|     return MMCZip::extractSubDir(&zip, "", dir); |     return extractSubDir(&zip, "", dir); | ||||||
| } | } | ||||||
|  |  | ||||||
| // ours | // ours | ||||||
| std::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString subdir, QString dir) | std::optional<QStringList> extractDir(QString fileCompressed, QString subdir, QString dir) | ||||||
| { | { | ||||||
|     QuaZip zip(fileCompressed); |     QuaZip zip(fileCompressed); | ||||||
|     if (!zip.open(QuaZip::mdUnzip)) |     if (!zip.open(QuaZip::mdUnzip)) { | ||||||
|     { |  | ||||||
|         // check if this is a minimum size empty zip file... |         // check if this is a minimum size empty zip file... | ||||||
|         QFileInfo fileInfo(fileCompressed); |         QFileInfo fileInfo(fileCompressed); | ||||||
|         if(fileInfo.size() == 22) { |         if (fileInfo.size() == 22) { | ||||||
|             return QStringList(); |             return QStringList(); | ||||||
|         } |         } | ||||||
|         qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();; |         qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError(); | ||||||
|  |         ; | ||||||
|         return std::nullopt; |         return std::nullopt; | ||||||
|     } |     } | ||||||
|     return MMCZip::extractSubDir(&zip, subdir, dir); |     return extractSubDir(&zip, subdir, dir); | ||||||
| } | } | ||||||
|  |  | ||||||
| // ours | // ours | ||||||
| bool MMCZip::extractFile(QString fileCompressed, QString file, QString target) | bool extractFile(QString fileCompressed, QString file, QString target) | ||||||
| { | { | ||||||
|     QuaZip zip(fileCompressed); |     QuaZip zip(fileCompressed); | ||||||
|     if (!zip.open(QuaZip::mdUnzip)) |     if (!zip.open(QuaZip::mdUnzip)) { | ||||||
|     { |  | ||||||
|         // check if this is a minimum size empty zip file... |         // check if this is a minimum size empty zip file... | ||||||
|         QFileInfo fileInfo(fileCompressed); |         QFileInfo fileInfo(fileCompressed); | ||||||
|         if(fileInfo.size() == 22) { |         if (fileInfo.size() == 22) { | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|         qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError(); |         qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError(); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|     return MMCZip::extractRelFile(&zip, file, target); |     return extractRelFile(&zip, file, target); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool MMCZip::collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList *files, | bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFunction excludeFilter) | ||||||
|                                         MMCZip::FilterFunction excludeFilter) { | { | ||||||
|     QDir rootDirectory(rootDir); |     QDir rootDirectory(rootDir); | ||||||
|     if (!rootDirectory.exists()) return false; |     if (!rootDirectory.exists()) | ||||||
|  |         return false; | ||||||
|  |  | ||||||
|     QDir directory; |     QDir directory; | ||||||
|     if (subDir == nullptr) |     if (subDir == nullptr) | ||||||
| @@ -421,25 +398,107 @@ bool MMCZip::collectFileListRecursively(const QString& rootDir, const QString& s | |||||||
|     else |     else | ||||||
|         directory = QDir(subDir); |         directory = QDir(subDir); | ||||||
|  |  | ||||||
|     if (!directory.exists()) return false;  // shouldn't ever happen |     if (!directory.exists()) | ||||||
|  |         return false;  // shouldn't ever happen | ||||||
|  |  | ||||||
|     // recurse directories |     // recurse directories | ||||||
|     QFileInfoList entries = directory.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Hidden); |     QFileInfoList entries = directory.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Hidden); | ||||||
|     for (const auto& e: entries) { |     for (const auto& e : entries) { | ||||||
|         if (!collectFileListRecursively(rootDir, e.filePath(), files, excludeFilter)) |         if (!collectFileListRecursively(rootDir, e.filePath(), files, excludeFilter)) | ||||||
|             return false; |             return false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // collect files |     // collect files | ||||||
|     entries = directory.entryInfoList(QDir::Files); |     entries = directory.entryInfoList(QDir::Files); | ||||||
|     for (const auto& e: entries) { |     for (const auto& e : entries) { | ||||||
|         QString relativeFilePath = rootDirectory.relativeFilePath(e.absoluteFilePath()); |         QString relativeFilePath = rootDirectory.relativeFilePath(e.absoluteFilePath()); | ||||||
|         if (excludeFilter && excludeFilter(relativeFilePath)) { |         if (excludeFilter && excludeFilter(relativeFilePath)) { | ||||||
|             qDebug() << "Skipping file " << relativeFilePath; |             qDebug() << "Skipping file " << relativeFilePath; | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         files->append(e);  // we want the original paths for MMCZip::compressDirFiles |         files->append(e);  // we want the original paths for compressDirFiles | ||||||
|     } |     } | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void ExportToZipTask::executeTask() | ||||||
|  | { | ||||||
|  |     setStatus("Adding files..."); | ||||||
|  |     setProgress(0, m_files.length()); | ||||||
|  |     m_build_zip_future = QtConcurrent::run(QThreadPool::globalInstance(), [this]() { return exportZip(); }); | ||||||
|  |     connect(&m_build_zip_watcher, &QFutureWatcher<ZipResult>::finished, this, &ExportToZipTask::finish); | ||||||
|  |     m_build_zip_watcher.setFuture(m_build_zip_future); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | auto ExportToZipTask::exportZip() -> ZipResult | ||||||
|  | { | ||||||
|  |     if (!m_dir.exists()) { | ||||||
|  |         return ZipResult(tr("Folder doesn't exist")); | ||||||
|  |     } | ||||||
|  |     if (!m_output.isOpen() && !m_output.open(QuaZip::mdCreate)) { | ||||||
|  |         return ZipResult(tr("Could not create file")); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (auto fileName : m_extra_files.keys()) { | ||||||
|  |         if (m_build_zip_future.isCanceled()) | ||||||
|  |             return ZipResult(); | ||||||
|  |         QuaZipFile indexFile(&m_output); | ||||||
|  |         if (!indexFile.open(QIODevice::WriteOnly, QuaZipNewInfo(fileName))) { | ||||||
|  |             return ZipResult(tr("Could not create:") + fileName); | ||||||
|  |         } | ||||||
|  |         indexFile.write(m_extra_files[fileName]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (const QFileInfo& file : m_files) { | ||||||
|  |         if (m_build_zip_future.isCanceled()) | ||||||
|  |             return ZipResult(); | ||||||
|  |  | ||||||
|  |         auto absolute = file.absoluteFilePath(); | ||||||
|  |         auto relative = m_dir.relativeFilePath(absolute); | ||||||
|  |         setStatus("Compresing: " + relative); | ||||||
|  |         setProgress(m_progress + 1, m_progressTotal); | ||||||
|  |         if (m_follow_symlinks) { | ||||||
|  |             if (file.isSymLink()) | ||||||
|  |                 absolute = file.symLinkTarget(); | ||||||
|  |             else | ||||||
|  |                 absolute = file.canonicalFilePath(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!m_exclude_files.contains(relative) && !JlCompress::compressFile(&m_output, absolute, m_destination_prefix + relative)) { | ||||||
|  |             return ZipResult(tr("Could not read and compress %1").arg(relative)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     m_output.close(); | ||||||
|  |     if (m_output.getZipError() != 0) { | ||||||
|  |         return ZipResult(tr("A zip error occurred")); | ||||||
|  |     } | ||||||
|  |     return ZipResult(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ExportToZipTask::finish() | ||||||
|  | { | ||||||
|  |     if (m_build_zip_future.isCanceled()) { | ||||||
|  |         QFile::remove(m_output_path); | ||||||
|  |         emitAborted(); | ||||||
|  |     } else if (auto result = m_build_zip_future.result(); result.has_value()) { | ||||||
|  |         QFile::remove(m_output_path); | ||||||
|  |         emitFailed(result.value()); | ||||||
|  |     } else { | ||||||
|  |         emitSucceeded(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool ExportToZipTask::abort() | ||||||
|  | { | ||||||
|  |     if (m_build_zip_future.isRunning()) { | ||||||
|  |         m_build_zip_future.cancel(); | ||||||
|  |         // NOTE: Here we don't do `emitAborted()` because it will be done when `m_build_zip_future` actually cancels, which may not occur | ||||||
|  |         // immediately. | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace MMCZip | ||||||
| @@ -1,7 +1,8 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  PolyMC - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  |  *  Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  *  it under the terms of the GNU General Public License as published by |  *  it under the terms of the GNU General Public License as published by | ||||||
| @@ -35,110 +36,157 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <QString> | #include <quazip.h> | ||||||
| #include <QFileInfo> |  | ||||||
| #include <QSet> |  | ||||||
| #include "minecraft/mod/Mod.h" |  | ||||||
| #include <functional> |  | ||||||
|  |  | ||||||
| #include <quazip/JlCompress.h> | #include <quazip/JlCompress.h> | ||||||
|  | #include <QDir> | ||||||
|  | #include <QFileInfo> | ||||||
|  | #include <QFuture> | ||||||
|  | #include <QFutureWatcher> | ||||||
|  | #include <QHash> | ||||||
|  | #include <QSet> | ||||||
|  | #include <QString> | ||||||
|  | #include <functional> | ||||||
|  | #include <memory> | ||||||
| #include <optional> | #include <optional> | ||||||
|  | #include "minecraft/mod/Mod.h" | ||||||
|  | #include "tasks/Task.h" | ||||||
|  |  | ||||||
| namespace MMCZip | namespace MMCZip { | ||||||
| { | using FilterFunction = std::function<bool(const QString&)>; | ||||||
|     using FilterFunction = std::function<bool(const QString &)>; |  | ||||||
|  |  | ||||||
|     /** | /** | ||||||
|      * Merge two zip files, using a filter function |  * Merge two zip files, using a filter function | ||||||
|      */ |  */ | ||||||
|     bool mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained, | bool mergeZipFiles(QuaZip* into, QFileInfo from, QSet<QString>& contained, const FilterFunction filter = nullptr); | ||||||
|                                             const FilterFunction filter = nullptr); |  | ||||||
|  |  | ||||||
|     /** | /** | ||||||
|      * Compress directory, by providing a list of files to compress |  * Compress directory, by providing a list of files to compress | ||||||
|      * \param zip target archive |  * \param zip target archive | ||||||
|      * \param dir directory that will be compressed (to compress with relative paths) |  * \param dir directory that will be compressed (to compress with relative paths) | ||||||
|      * \param files list of files to compress |  * \param files list of files to compress | ||||||
|      * \param followSymlinks should follow symlinks when compressing file data |  * \param followSymlinks should follow symlinks when compressing file data | ||||||
|      * \return true for success or false for failure |  * \return true for success or false for failure | ||||||
|      */ |  */ | ||||||
|     bool compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files, bool followSymlinks = false); | bool compressDirFiles(QuaZip* zip, QString dir, QFileInfoList files, bool followSymlinks = false); | ||||||
|  |  | ||||||
|     /** | /** | ||||||
|      * Compress directory, by providing a list of files to compress |  * Compress directory, by providing a list of files to compress | ||||||
|      * \param fileCompressed target archive file |  * \param fileCompressed target archive file | ||||||
|      * \param dir directory that will be compressed (to compress with relative paths) |  * \param dir directory that will be compressed (to compress with relative paths) | ||||||
|      * \param files list of files to compress |  * \param files list of files to compress | ||||||
|      * \param followSymlinks should follow symlinks when compressing file data |  * \param followSymlinks should follow symlinks when compressing file data | ||||||
|      * \return true for success or false for failure |  * \return true for success or false for failure | ||||||
|      */ |  */ | ||||||
|     bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks = false); | bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks = false); | ||||||
|  |  | ||||||
|     /** | /** | ||||||
|      * take a source jar, add mods to it, resulting in target jar |  * take a source jar, add mods to it, resulting in target jar | ||||||
|      */ |  */ | ||||||
|     bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods); | bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods); | ||||||
|  |  | ||||||
|     /** | /** | ||||||
|      * Find a single file in archive by file name (not path) |  * Find a single file in archive by file name (not path) | ||||||
|      * |  * | ||||||
|      * \param ignore_paths paths to skip when recursing the search |  * \param ignore_paths paths to skip when recursing the search | ||||||
|      * |  * | ||||||
|      * \return the path prefix where the file is |  * \return the path prefix where the file is | ||||||
|      */ |  */ | ||||||
|     QString findFolderOfFileInZip(QuaZip * zip, const QString & what, const QStringList& ignore_paths = {}, const QString &root = QString("")); | QString findFolderOfFileInZip(QuaZip* zip, const QString& what, const QStringList& ignore_paths = {}, const QString& root = QString("")); | ||||||
|  |  | ||||||
|     /** | /** | ||||||
|      * Find a multiple files of the same name in archive by file name |  * Find a multiple files of the same name in archive by file name | ||||||
|      * If a file is found in a path, no deeper paths are searched |  * If a file is found in a path, no deeper paths are searched | ||||||
|      * |  * | ||||||
|      * \return true if anything was found |  * \return true if anything was found | ||||||
|      */ |  */ | ||||||
|     bool findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root = QString()); | bool findFilesInZip(QuaZip* zip, const QString& what, QStringList& result, const QString& root = QString()); | ||||||
|  |  | ||||||
|     /** | /** | ||||||
|      * Extract a subdirectory from an archive |  * Extract a subdirectory from an archive | ||||||
|      */ |  */ | ||||||
|     std::optional<QStringList> extractSubDir(QuaZip *zip, const QString & subdir, const QString &target); | std::optional<QStringList> extractSubDir(QuaZip* zip, const QString& subdir, const QString& target); | ||||||
|  |  | ||||||
|     bool extractRelFile(QuaZip *zip, const QString & file, const QString &target); | bool extractRelFile(QuaZip* zip, const QString& file, const QString& target); | ||||||
|  |  | ||||||
|     /** | /** | ||||||
|      * Extract a whole archive. |  * Extract a whole archive. | ||||||
|      * |  * | ||||||
|      * \param fileCompressed The name of the archive. |  * \param fileCompressed The name of the archive. | ||||||
|      * \param dir The directory to extract to, the current directory if left empty. |  * \param dir The directory to extract to, the current directory if left empty. | ||||||
|      * \return The list of the full paths of the files extracted, empty on failure. |  * \return The list of the full paths of the files extracted, empty on failure. | ||||||
|      */ |  */ | ||||||
|     std::optional<QStringList> extractDir(QString fileCompressed, QString dir); | std::optional<QStringList> extractDir(QString fileCompressed, QString dir); | ||||||
|  |  | ||||||
|     /** | /** | ||||||
|      * Extract a subdirectory from an archive |  * Extract a subdirectory from an archive | ||||||
|      * |  * | ||||||
|      * \param fileCompressed The name of the archive. |  * \param fileCompressed The name of the archive. | ||||||
|      * \param subdir The directory within the archive to extract |  * \param subdir The directory within the archive to extract | ||||||
|      * \param dir The directory to extract to, the current directory if left empty. |  * \param dir The directory to extract to, the current directory if left empty. | ||||||
|      * \return The list of the full paths of the files extracted, empty on failure. |  * \return The list of the full paths of the files extracted, empty on failure. | ||||||
|      */ |  */ | ||||||
|     std::optional<QStringList> extractDir(QString fileCompressed, QString subdir, QString dir); | std::optional<QStringList> extractDir(QString fileCompressed, QString subdir, QString dir); | ||||||
|  |  | ||||||
|     /** | /** | ||||||
|      * Extract a single file from an archive into a directory |  * Extract a single file from an archive into a directory | ||||||
|      * |  * | ||||||
|      * \param fileCompressed The name of the archive. |  * \param fileCompressed The name of the archive. | ||||||
|      * \param file The file within the archive to extract |  * \param file The file within the archive to extract | ||||||
|      * \param dir The directory to extract to, the current directory if left empty. |  * \param dir The directory to extract to, the current directory if left empty. | ||||||
|      * \return true for success or false for failure |  * \return true for success or false for failure | ||||||
|      */ |  */ | ||||||
|     bool extractFile(QString fileCompressed, QString file, QString dir); | bool extractFile(QString fileCompressed, QString file, QString dir); | ||||||
|  |  | ||||||
|     /** | /** | ||||||
|      * Populate a QFileInfoList with a directory tree recursively, while allowing to excludeFilter what shouldn't be included. |  * Populate a QFileInfoList with a directory tree recursively, while allowing to excludeFilter what shouldn't be included. | ||||||
|      * \param rootDir directory to start off |  * \param rootDir directory to start off | ||||||
|      * \param subDir subdirectory, should be nullptr for first invocation |  * \param subDir subdirectory, should be nullptr for first invocation | ||||||
|      * \param files resulting list of QFileInfo |  * \param files resulting list of QFileInfo | ||||||
|      * \param excludeFilter function to excludeFilter which files shouldn't be included (returning true means to excude) |  * \param excludeFilter function to excludeFilter which files shouldn't be included (returning true means to excude) | ||||||
|      * \return true for success or false for failure |  * \return true for success or false for failure | ||||||
|      */ |  */ | ||||||
|     bool collectFileListRecursively(const QString &rootDir, const QString &subDir, QFileInfoList *files, FilterFunction excludeFilter); | bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFunction excludeFilter); | ||||||
| } |  | ||||||
|  | class ExportToZipTask : public Task { | ||||||
|  |    public: | ||||||
|  |     ExportToZipTask(QString outputPath, QDir dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false) | ||||||
|  |         : m_output_path(outputPath) | ||||||
|  |         , m_output(outputPath) | ||||||
|  |         , m_dir(dir) | ||||||
|  |         , m_files(files) | ||||||
|  |         , m_destination_prefix(destinationPrefix) | ||||||
|  |         , m_follow_symlinks(followSymlinks) | ||||||
|  |     { | ||||||
|  |         setAbortable(true); | ||||||
|  |     }; | ||||||
|  |     ExportToZipTask(QString outputPath, QString dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false) | ||||||
|  |         : ExportToZipTask(outputPath, QDir(dir), files, destinationPrefix, followSymlinks){}; | ||||||
|  |  | ||||||
|  |     virtual ~ExportToZipTask() = default; | ||||||
|  |  | ||||||
|  |     void setExcludeFiles(QStringList excludeFiles) { m_exclude_files = excludeFiles; } | ||||||
|  |     void addExtraFile(QString fileName, QByteArray data) { m_extra_files.insert(fileName, data); } | ||||||
|  |  | ||||||
|  |     typedef std::optional<QString> ZipResult; | ||||||
|  |  | ||||||
|  |    protected: | ||||||
|  |     virtual void executeTask() override; | ||||||
|  |     bool abort() override; | ||||||
|  |  | ||||||
|  |     ZipResult exportZip(); | ||||||
|  |     void finish(); | ||||||
|  |  | ||||||
|  |    private: | ||||||
|  |     QString m_output_path; | ||||||
|  |     QuaZip m_output; | ||||||
|  |     QDir m_dir; | ||||||
|  |     QFileInfoList m_files; | ||||||
|  |     QString m_destination_prefix; | ||||||
|  |     bool m_follow_symlinks; | ||||||
|  |     QStringList m_exclude_files; | ||||||
|  |     QHash<QString, QByteArray> m_extra_files; | ||||||
|  |  | ||||||
|  |     QFuture<ZipResult> m_build_zip_future; | ||||||
|  |     QFutureWatcher<ZipResult> m_build_zip_watcher; | ||||||
|  | }; | ||||||
|  | }  // namespace MMCZip | ||||||
|   | |||||||
| @@ -1,10 +1,10 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <QCoreApplication> | #include <QCoreApplication> | ||||||
|  | #include <QDebug> | ||||||
| #include <QPixmapCache> | #include <QPixmapCache> | ||||||
| #include <QThread> | #include <QThread> | ||||||
| #include <QTime> | #include <QTime> | ||||||
| #include <QDebug> |  | ||||||
|  |  | ||||||
| #define GET_TYPE()                                                          \ | #define GET_TYPE()                                                          \ | ||||||
|     Qt::ConnectionType type;                                                \ |     Qt::ConnectionType type;                                                \ | ||||||
| @@ -94,8 +94,8 @@ class PixmapCache final : public QObject { | |||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /**  |     /** | ||||||
|      *  Mark that a cache miss occurred because of a eviction if too many of these occur too fast the cache size is increased  |      *  Mark that a cache miss occurred because of a eviction if too many of these occur too fast the cache size is increased | ||||||
|      * @return if the cache size was increased |      * @return if the cache size was increased | ||||||
|      */ |      */ | ||||||
|     bool _markCacheMissByEviciton() |     bool _markCacheMissByEviciton() | ||||||
|   | |||||||
| @@ -16,15 +16,15 @@ | |||||||
|  *  along with this program.  If not, see <https://www.gnu.org/licenses/>. |  *  along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include <QStringList> |  | ||||||
| #include <QDir> | #include <QDir> | ||||||
| #include <QString> | #include <QString> | ||||||
|  | #include <QStringList> | ||||||
| #include <QSysInfo> | #include <QSysInfo> | ||||||
| #include <QtGlobal> | #include <QtGlobal> | ||||||
|  |  | ||||||
| #include "MangoHud.h" |  | ||||||
| #include "FileSystem.h" | #include "FileSystem.h" | ||||||
| #include "Json.h" | #include "Json.h" | ||||||
|  | #include "MangoHud.h" | ||||||
|  |  | ||||||
| namespace MangoHud { | namespace MangoHud { | ||||||
|  |  | ||||||
|   | |||||||
| @@ -18,7 +18,7 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <QString> |  | ||||||
| #include <cmark.h> | #include <cmark.h> | ||||||
|  | #include <QString> | ||||||
|  |  | ||||||
| QString markdownToHTML(const QString& markdown); | QString markdownToHTML(const QString& markdown); | ||||||
| @@ -22,12 +22,11 @@ MessageLevel::Enum MessageLevel::getLevel(const QString& levelName) | |||||||
|         return MessageLevel::Unknown; |         return MessageLevel::Unknown; | ||||||
| } | } | ||||||
|  |  | ||||||
| MessageLevel::Enum MessageLevel::fromLine(QString &line) | MessageLevel::Enum MessageLevel::fromLine(QString& line) | ||||||
| { | { | ||||||
|     // Level prefix |     // Level prefix | ||||||
|     int endmark = line.indexOf("]!"); |     int endmark = line.indexOf("]!"); | ||||||
|     if (line.startsWith("!![") && endmark != -1) |     if (line.startsWith("!![") && endmark != -1) { | ||||||
|     { |  | ||||||
|         auto level = MessageLevel::getLevel(line.left(endmark).mid(3)); |         auto level = MessageLevel::getLevel(line.left(endmark).mid(3)); | ||||||
|         line = line.mid(endmark + 2); |         line = line.mid(endmark + 2); | ||||||
|         return level; |         return level; | ||||||
|   | |||||||
| @@ -6,23 +6,21 @@ | |||||||
|  * @brief the MessageLevel Enum |  * @brief the MessageLevel Enum | ||||||
|  * defines what level a log message is |  * defines what level a log message is | ||||||
|  */ |  */ | ||||||
| namespace MessageLevel | namespace MessageLevel { | ||||||
| { | enum Enum { | ||||||
| enum Enum |     Unknown,  /**< No idea what this is or where it came from */ | ||||||
| { |     StdOut,   /**< Undetermined stderr messages */ | ||||||
|     Unknown, /**< No idea what this is or where it came from */ |     StdErr,   /**< Undetermined stdout messages */ | ||||||
|     StdOut,  /**< Undetermined stderr messages */ |  | ||||||
|     StdErr,  /**< Undetermined stdout messages */ |  | ||||||
|     Launcher, /**< Launcher Messages */ |     Launcher, /**< Launcher Messages */ | ||||||
|     Debug,   /**< Debug Messages */ |     Debug,    /**< Debug Messages */ | ||||||
|     Info,    /**< Info Messages */ |     Info,     /**< Info Messages */ | ||||||
|     Message, /**< Standard Messages */ |     Message,  /**< Standard Messages */ | ||||||
|     Warning, /**< Warnings */ |     Warning,  /**< Warnings */ | ||||||
|     Error,   /**< Errors */ |     Error,    /**< Errors */ | ||||||
|     Fatal,   /**< Fatal Errors */ |     Fatal,    /**< Fatal Errors */ | ||||||
| }; | }; | ||||||
| MessageLevel::Enum getLevel(const QString &levelName); | MessageLevel::Enum getLevel(const QString& levelName); | ||||||
|  |  | ||||||
| /* Get message level from a line. Line is modified if it was successful. */ | /* Get message level from a line. Line is modified if it was successful. */ | ||||||
| MessageLevel::Enum fromLine(QString &line); | MessageLevel::Enum fromLine(QString& line); | ||||||
| } | }  // namespace MessageLevel | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  PolyMC - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
| @@ -37,88 +37,38 @@ | |||||||
| #include "BaseInstance.h" | #include "BaseInstance.h" | ||||||
| #include "launch/LaunchTask.h" | #include "launch/LaunchTask.h" | ||||||
|  |  | ||||||
| class NullInstance: public BaseInstance | class NullInstance : public BaseInstance { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| public: |    public: | ||||||
|     NullInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir) |     NullInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir) | ||||||
|     :BaseInstance(globalSettings, settings, rootDir) |         : BaseInstance(globalSettings, settings, rootDir) | ||||||
|     { |     { | ||||||
|         setVersionBroken(true); |         setVersionBroken(true); | ||||||
|     } |     } | ||||||
|     virtual ~NullInstance() {}; |     virtual ~NullInstance(){}; | ||||||
|     void saveNow() override |     void saveNow() override {} | ||||||
|     { |     void loadSpecificSettings() override { setSpecificSettingsLoaded(true); } | ||||||
|     } |     QString getStatusbarDescription() override { return tr("Unknown instance type"); }; | ||||||
|     void loadSpecificSettings() override |     QSet<QString> traits() const override { return {}; }; | ||||||
|     { |     QString instanceConfigFolder() const override { return instanceRoot(); }; | ||||||
|         setSpecificSettingsLoaded(true); |     shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr, MinecraftServerTargetPtr) override { return nullptr; } | ||||||
|     } |     shared_qobject_ptr<Task> createUpdateTask([[maybe_unused]] Net::Mode mode) override { return nullptr; } | ||||||
|     QString getStatusbarDescription() override |     QProcessEnvironment createEnvironment() override { return QProcessEnvironment(); } | ||||||
|     { |     QProcessEnvironment createLaunchEnvironment() override { return QProcessEnvironment(); } | ||||||
|         return tr("Unknown instance type"); |     QMap<QString, QString> getVariables() override { return QMap<QString, QString>(); } | ||||||
|     }; |     IPathMatcher::Ptr getLogFileMatcher() override { return nullptr; } | ||||||
|     QSet< QString > traits() const override |     QString getLogFileRoot() override { return instanceRoot(); } | ||||||
|     { |     QString typeName() const override { return "Null"; } | ||||||
|         return {}; |     bool canExport() const override { return false; } | ||||||
|     }; |     bool canEdit() const override { return false; } | ||||||
|     QString instanceConfigFolder() const override |     bool canLaunch() const override { return false; } | ||||||
|     { |  | ||||||
|         return instanceRoot(); |  | ||||||
|     }; |  | ||||||
|     shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr, MinecraftServerTargetPtr) override |  | ||||||
|     { |  | ||||||
|         return nullptr; |  | ||||||
|     } |  | ||||||
|     shared_qobject_ptr< Task > createUpdateTask(Net::Mode mode) override |  | ||||||
|     { |  | ||||||
|         return nullptr; |  | ||||||
|     } |  | ||||||
|     QProcessEnvironment createEnvironment() override |  | ||||||
|     { |  | ||||||
|         return QProcessEnvironment(); |  | ||||||
|     } |  | ||||||
|     QProcessEnvironment createLaunchEnvironment() override |  | ||||||
|     { |  | ||||||
|         return QProcessEnvironment(); |  | ||||||
|     } |  | ||||||
|     QMap<QString, QString> getVariables() override |  | ||||||
|     { |  | ||||||
|         return QMap<QString, QString>(); |  | ||||||
|     } |  | ||||||
|     IPathMatcher::Ptr getLogFileMatcher() override |  | ||||||
|     { |  | ||||||
|         return nullptr; |  | ||||||
|     } |  | ||||||
|     QString getLogFileRoot() override |  | ||||||
|     { |  | ||||||
|         return instanceRoot(); |  | ||||||
|     } |  | ||||||
|     QString typeName() const override |  | ||||||
|     { |  | ||||||
|         return "Null"; |  | ||||||
|     } |  | ||||||
|     bool canExport() const override |  | ||||||
|     { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|     bool canEdit() const override |  | ||||||
|     { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|     bool canLaunch() const override |  | ||||||
|     { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|     QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override |     QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override | ||||||
|     { |     { | ||||||
|         QStringList out; |         QStringList out; | ||||||
|         out << "Null instance - placeholder."; |         out << "Null instance - placeholder."; | ||||||
|         return out; |         return out; | ||||||
|     } |     } | ||||||
|     QString modsRoot() const override { |     QString modsRoot() const override { return QString(); } | ||||||
|         return QString(); |  | ||||||
|     } |  | ||||||
|     void updateRuntimeContext() |     void updateRuntimeContext() | ||||||
|     { |     { | ||||||
|         // NOOP |         // NOOP | ||||||
|   | |||||||
| @@ -1,47 +1,35 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| enum class ProblemSeverity | #include <QList> | ||||||
| { | #include <QString> | ||||||
|     None, |  | ||||||
|     Warning, |  | ||||||
|     Error |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct PatchProblem | enum class ProblemSeverity { None, Warning, Error }; | ||||||
| { |  | ||||||
|  | struct PatchProblem { | ||||||
|     ProblemSeverity m_severity; |     ProblemSeverity m_severity; | ||||||
|     QString m_description; |     QString m_description; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class ProblemProvider | class ProblemProvider { | ||||||
| { |    public: | ||||||
| public: |     virtual ~ProblemProvider() {} | ||||||
|     virtual ~ProblemProvider() {}; |  | ||||||
|     virtual const QList<PatchProblem> getProblems() const = 0; |     virtual const QList<PatchProblem> getProblems() const = 0; | ||||||
|     virtual ProblemSeverity getProblemSeverity() const = 0; |     virtual ProblemSeverity getProblemSeverity() const = 0; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class ProblemContainer : public ProblemProvider | class ProblemContainer : public ProblemProvider { | ||||||
| { |    public: | ||||||
| public: |     const QList<PatchProblem> getProblems() const override { return m_problems; } | ||||||
|     const QList<PatchProblem> getProblems() const override |     ProblemSeverity getProblemSeverity() const override { return m_problemSeverity; } | ||||||
|  |     virtual void addProblem(ProblemSeverity severity, const QString& description) | ||||||
|     { |     { | ||||||
|         return m_problems; |         if (severity > m_problemSeverity) { | ||||||
|     } |  | ||||||
|     ProblemSeverity getProblemSeverity() const override |  | ||||||
|     { |  | ||||||
|         return m_problemSeverity; |  | ||||||
|     } |  | ||||||
|     virtual void addProblem(ProblemSeverity severity, const QString &description) |  | ||||||
|     { |  | ||||||
|         if(severity > m_problemSeverity) |  | ||||||
|         { |  | ||||||
|             m_problemSeverity = severity; |             m_problemSeverity = severity; | ||||||
|         } |         } | ||||||
|         m_problems.append({severity, description}); |         m_problems.append({ severity, description }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| private: |    private: | ||||||
|     QList<PatchProblem> m_problems; |     QList<PatchProblem> m_problems; | ||||||
|     ProblemSeverity m_problemSeverity = ProblemSeverity::None; |     ProblemSeverity m_problemSeverity = ProblemSeverity::None; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -36,35 +36,34 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
|  |  | ||||||
| #include <QVariant> |  | ||||||
| #include <QList> | #include <QList> | ||||||
|  | #include <QVariant> | ||||||
|  |  | ||||||
| namespace QVariantUtils { | namespace QVariantUtils { | ||||||
|  |  | ||||||
| template <typename T>  | template <typename T> | ||||||
| inline QList<T> toList(QVariant src) { | inline QList<T> toList(QVariant src) | ||||||
|  | { | ||||||
|     QVariantList variantList = src.toList(); |     QVariantList variantList = src.toList(); | ||||||
|  |  | ||||||
|     QList<T> list_t; |     QList<T> list_t; | ||||||
|     list_t.reserve(variantList.size()); |     list_t.reserve(variantList.size()); | ||||||
|     for (const QVariant& v : variantList) |     for (const QVariant& v : variantList) { | ||||||
|     { |  | ||||||
|         list_t.append(v.value<T>()); |         list_t.append(v.value<T>()); | ||||||
|     } |     } | ||||||
|     return list_t;  |     return list_t; | ||||||
| } | } | ||||||
|  |  | ||||||
| template <typename T>  | template <typename T> | ||||||
| inline QVariant fromList(QList<T> val) { | inline QVariant fromList(QList<T> val) | ||||||
|  | { | ||||||
|     QVariantList variantList; |     QVariantList variantList; | ||||||
|     variantList.reserve(val.size()); |     variantList.reserve(val.size()); | ||||||
|     for (const T& v : val) |     for (const T& v : val) { | ||||||
|     { |  | ||||||
|         variantList.append(v); |         variantList.append(v); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return variantList; |     return variantList; | ||||||
| } | } | ||||||
|  |  | ||||||
| } | }  // namespace QVariantUtils | ||||||
| @@ -1,13 +1,12 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include <QWriteLocker> |  | ||||||
| #include <QReadLocker> |  | ||||||
| #include <QMap> | #include <QMap> | ||||||
|  | #include <QReadLocker> | ||||||
| #include <QSet> | #include <QSet> | ||||||
|  | #include <QWriteLocker> | ||||||
|  |  | ||||||
| template <typename K, typename V> | template <typename K, typename V> | ||||||
| class RWStorage | class RWStorage { | ||||||
| { |    public: | ||||||
| public: |  | ||||||
|     void add(K key, V value) |     void add(K key, V value) | ||||||
|     { |     { | ||||||
|         QWriteLocker l(&lock); |         QWriteLocker l(&lock); | ||||||
| @@ -17,21 +16,19 @@ public: | |||||||
|     V get(K key) |     V get(K key) | ||||||
|     { |     { | ||||||
|         QReadLocker l(&lock); |         QReadLocker l(&lock); | ||||||
|         if(cache.contains(key)) |         if (cache.contains(key)) { | ||||||
|         { |  | ||||||
|             return cache[key]; |             return cache[key]; | ||||||
|         } |         } else | ||||||
|         else return V(); |             return V(); | ||||||
|     } |     } | ||||||
|     bool get(K key, V& value) |     bool get(K key, V& value) | ||||||
|     { |     { | ||||||
|         QReadLocker l(&lock); |         QReadLocker l(&lock); | ||||||
|         if(cache.contains(key)) |         if (cache.contains(key)) { | ||||||
|         { |  | ||||||
|             value = cache[key]; |             value = cache[key]; | ||||||
|             return true; |             return true; | ||||||
|         } |         } else | ||||||
|         else return false; |             return false; | ||||||
|     } |     } | ||||||
|     bool has(K key) |     bool has(K key) | ||||||
|     { |     { | ||||||
| @@ -41,15 +38,14 @@ public: | |||||||
|     bool stale(K key) |     bool stale(K key) | ||||||
|     { |     { | ||||||
|         QReadLocker l(&lock); |         QReadLocker l(&lock); | ||||||
|         if(!cache.contains(key)) |         if (!cache.contains(key)) | ||||||
|             return true; |             return true; | ||||||
|         return stale_entries.contains(key); |         return stale_entries.contains(key); | ||||||
|     } |     } | ||||||
|     void setStale(K key) |     void setStale(K key) | ||||||
|     { |     { | ||||||
|         QWriteLocker l(&lock); |         QWriteLocker l(&lock); | ||||||
|         if(cache.contains(key)) |         if (cache.contains(key)) { | ||||||
|         { |  | ||||||
|             stale_entries.insert(key); |             stale_entries.insert(key); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -59,7 +55,8 @@ public: | |||||||
|         cache.clear(); |         cache.clear(); | ||||||
|         stale_entries.clear(); |         stale_entries.clear(); | ||||||
|     } |     } | ||||||
| private: |  | ||||||
|  |    private: | ||||||
|     QReadWriteLock lock; |     QReadWriteLock lock; | ||||||
|     QMap<K, V> cache; |     QMap<K, V> cache; | ||||||
|     QSet<K> stale_entries; |     QSet<K> stale_entries; | ||||||
|   | |||||||
| @@ -1,25 +1,21 @@ | |||||||
| #include "RecursiveFileSystemWatcher.h" | #include "RecursiveFileSystemWatcher.h" | ||||||
|  |  | ||||||
| #include <QRegularExpression> |  | ||||||
| #include <QDebug> | #include <QDebug> | ||||||
|  | #include <QRegularExpression> | ||||||
|  |  | ||||||
| RecursiveFileSystemWatcher::RecursiveFileSystemWatcher(QObject *parent) | RecursiveFileSystemWatcher::RecursiveFileSystemWatcher(QObject* parent) : QObject(parent), m_watcher(new QFileSystemWatcher(this)) | ||||||
|     : QObject(parent), m_watcher(new QFileSystemWatcher(this)) |  | ||||||
| { | { | ||||||
|     connect(m_watcher, &QFileSystemWatcher::fileChanged, this, |     connect(m_watcher, &QFileSystemWatcher::fileChanged, this, &RecursiveFileSystemWatcher::fileChange); | ||||||
|             &RecursiveFileSystemWatcher::fileChange); |     connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &RecursiveFileSystemWatcher::directoryChange); | ||||||
|     connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, |  | ||||||
|             &RecursiveFileSystemWatcher::directoryChange); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void RecursiveFileSystemWatcher::setRootDir(const QDir &root) | void RecursiveFileSystemWatcher::setRootDir(const QDir& root) | ||||||
| { | { | ||||||
|     bool wasEnabled = m_isEnabled; |     bool wasEnabled = m_isEnabled; | ||||||
|     disable(); |     disable(); | ||||||
|     m_root = root; |     m_root = root; | ||||||
|     setFiles(scanRecursive(m_root)); |     setFiles(scanRecursive(m_root)); | ||||||
|     if (wasEnabled) |     if (wasEnabled) { | ||||||
|     { |  | ||||||
|         enable(); |         enable(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -28,16 +24,14 @@ void RecursiveFileSystemWatcher::setWatchFiles(const bool watchFiles) | |||||||
|     bool wasEnabled = m_isEnabled; |     bool wasEnabled = m_isEnabled; | ||||||
|     disable(); |     disable(); | ||||||
|     m_watchFiles = watchFiles; |     m_watchFiles = watchFiles; | ||||||
|     if (wasEnabled) |     if (wasEnabled) { | ||||||
|     { |  | ||||||
|         enable(); |         enable(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void RecursiveFileSystemWatcher::enable() | void RecursiveFileSystemWatcher::enable() | ||||||
| { | { | ||||||
|     if (m_isEnabled) |     if (m_isEnabled) { | ||||||
|     { |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     Q_ASSERT(m_root != QDir::root()); |     Q_ASSERT(m_root != QDir::root()); | ||||||
| @@ -46,8 +40,7 @@ void RecursiveFileSystemWatcher::enable() | |||||||
| } | } | ||||||
| void RecursiveFileSystemWatcher::disable() | void RecursiveFileSystemWatcher::disable() | ||||||
| { | { | ||||||
|     if (!m_isEnabled) |     if (!m_isEnabled) { | ||||||
|     { |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     m_isEnabled = false; |     m_isEnabled = false; | ||||||
| @@ -55,57 +48,49 @@ void RecursiveFileSystemWatcher::disable() | |||||||
|     m_watcher->removePaths(m_watcher->directories()); |     m_watcher->removePaths(m_watcher->directories()); | ||||||
| } | } | ||||||
|  |  | ||||||
| void RecursiveFileSystemWatcher::setFiles(const QStringList &files) | void RecursiveFileSystemWatcher::setFiles(const QStringList& files) | ||||||
| { | { | ||||||
|     if (files != m_files) |     if (files != m_files) { | ||||||
|     { |  | ||||||
|         m_files = files; |         m_files = files; | ||||||
|         emit filesChanged(); |         emit filesChanged(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void RecursiveFileSystemWatcher::addFilesToWatcherRecursive(const QDir &dir) | void RecursiveFileSystemWatcher::addFilesToWatcherRecursive(const QDir& dir) | ||||||
| { | { | ||||||
|     m_watcher->addPath(dir.absolutePath()); |     m_watcher->addPath(dir.absolutePath()); | ||||||
|     for (const QString &directory : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) |     for (const QString& directory : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { | ||||||
|     { |  | ||||||
|         addFilesToWatcherRecursive(dir.absoluteFilePath(directory)); |         addFilesToWatcherRecursive(dir.absoluteFilePath(directory)); | ||||||
|     } |     } | ||||||
|     if (m_watchFiles) |     if (m_watchFiles) { | ||||||
|     { |         for (const QFileInfo& info : dir.entryInfoList(QDir::Files)) { | ||||||
|         for (const QFileInfo &info : dir.entryInfoList(QDir::Files)) |  | ||||||
|         { |  | ||||||
|             m_watcher->addPath(info.absoluteFilePath()); |             m_watcher->addPath(info.absoluteFilePath()); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| QStringList RecursiveFileSystemWatcher::scanRecursive(const QDir &directory) | QStringList RecursiveFileSystemWatcher::scanRecursive(const QDir& directory) | ||||||
| { | { | ||||||
|     QStringList ret; |     QStringList ret; | ||||||
|     if(!m_matcher) |     if (!m_matcher) { | ||||||
|     { |  | ||||||
|         return {}; |         return {}; | ||||||
|     } |     } | ||||||
|     for (const QString &dir : directory.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden)) |     for (const QString& dir : directory.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden)) { | ||||||
|     { |  | ||||||
|         ret.append(scanRecursive(directory.absoluteFilePath(dir))); |         ret.append(scanRecursive(directory.absoluteFilePath(dir))); | ||||||
|     } |     } | ||||||
|     for (const QString &file : directory.entryList(QDir::Files | QDir::Hidden)) |     for (const QString& file : directory.entryList(QDir::Files | QDir::Hidden)) { | ||||||
|     { |  | ||||||
|         auto relPath = m_root.relativeFilePath(directory.absoluteFilePath(file)); |         auto relPath = m_root.relativeFilePath(directory.absoluteFilePath(file)); | ||||||
|         if (m_matcher->matches(relPath)) |         if (m_matcher->matches(relPath)) { | ||||||
|         { |  | ||||||
|             ret.append(relPath); |             ret.append(relPath); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| void RecursiveFileSystemWatcher::fileChange(const QString &path) | void RecursiveFileSystemWatcher::fileChange(const QString& path) | ||||||
| { | { | ||||||
|     emit fileChanged(path); |     emit fileChanged(path); | ||||||
| } | } | ||||||
| void RecursiveFileSystemWatcher::directoryChange(const QString &path) | void RecursiveFileSystemWatcher::directoryChange([[maybe_unused]] const QString& path) | ||||||
| { | { | ||||||
|     setFiles(scanRecursive(m_root)); |     setFiles(scanRecursive(m_root)); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,61 +1,48 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <QFileSystemWatcher> |  | ||||||
| #include <QDir> | #include <QDir> | ||||||
|  | #include <QFileSystemWatcher> | ||||||
| #include "pathmatcher/IPathMatcher.h" | #include "pathmatcher/IPathMatcher.h" | ||||||
|  |  | ||||||
| class RecursiveFileSystemWatcher : public QObject | class RecursiveFileSystemWatcher : public QObject { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| public: |    public: | ||||||
|     RecursiveFileSystemWatcher(QObject *parent); |     RecursiveFileSystemWatcher(QObject* parent); | ||||||
|  |  | ||||||
|     void setRootDir(const QDir &root); |     void setRootDir(const QDir& root); | ||||||
|     QDir rootDir() const |     QDir rootDir() const { return m_root; } | ||||||
|     { |  | ||||||
|         return m_root; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // WARNING: setting this to true may be bad for performance |     // WARNING: setting this to true may be bad for performance | ||||||
|     void setWatchFiles(const bool watchFiles); |     void setWatchFiles(const bool watchFiles); | ||||||
|     bool watchFiles() const |     bool watchFiles() const { return m_watchFiles; } | ||||||
|     { |  | ||||||
|         return m_watchFiles; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void setMatcher(IPathMatcher::Ptr matcher) |     void setMatcher(IPathMatcher::Ptr matcher) { m_matcher = matcher; } | ||||||
|     { |  | ||||||
|         m_matcher = matcher; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     QStringList files() const |     QStringList files() const { return m_files; } | ||||||
|     { |  | ||||||
|         return m_files; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| signals: |    signals: | ||||||
|     void filesChanged(); |     void filesChanged(); | ||||||
|     void fileChanged(const QString &path); |     void fileChanged(const QString& path); | ||||||
|  |  | ||||||
| public slots: |    public slots: | ||||||
|     void enable(); |     void enable(); | ||||||
|     void disable(); |     void disable(); | ||||||
|  |  | ||||||
| private: |    private: | ||||||
|     QDir m_root; |     QDir m_root; | ||||||
|     bool m_watchFiles = false; |     bool m_watchFiles = false; | ||||||
|     bool m_isEnabled = false; |     bool m_isEnabled = false; | ||||||
|     IPathMatcher::Ptr m_matcher; |     IPathMatcher::Ptr m_matcher; | ||||||
|  |  | ||||||
|     QFileSystemWatcher *m_watcher; |     QFileSystemWatcher* m_watcher; | ||||||
|  |  | ||||||
|     QStringList m_files; |     QStringList m_files; | ||||||
|     void setFiles(const QStringList &files); |     void setFiles(const QStringList& files); | ||||||
|  |  | ||||||
|     void addFilesToWatcherRecursive(const QDir &dir); |     void addFilesToWatcherRecursive(const QDir& dir); | ||||||
|     QStringList scanRecursive(const QDir &dir); |     QStringList scanRecursive(const QDir& dir); | ||||||
|  |  | ||||||
| private slots: |    private slots: | ||||||
|     void fileChange(const QString &path); |     void fileChange(const QString& path); | ||||||
|     void directoryChange(const QString &path); |     void directoryChange(const QString& path); | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -24,6 +24,8 @@ | |||||||
| #include "minecraft/mod/ModFolderModel.h" | #include "minecraft/mod/ModFolderModel.h" | ||||||
| #include "minecraft/mod/ResourceFolderModel.h" | #include "minecraft/mod/ResourceFolderModel.h" | ||||||
|  |  | ||||||
|  | #include "net/ApiDownload.h" | ||||||
|  |  | ||||||
| ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack, | ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack, | ||||||
|                                            ModPlatform::IndexedVersion version, |                                            ModPlatform::IndexedVersion version, | ||||||
|                                            const std::shared_ptr<ResourceFolderModel> packs, |                                            const std::shared_ptr<ResourceFolderModel> packs, | ||||||
| @@ -51,10 +53,10 @@ ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack, | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     m_filesNetJob->addNetAction(Net::Download::makeFile(m_pack_version.downloadUrl, dir.absoluteFilePath(getFilename()))); |     m_filesNetJob->addNetAction(Net::ApiDownload::makeFile(m_pack_version.downloadUrl, dir.absoluteFilePath(getFilename()))); | ||||||
|     connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ResourceDownloadTask::downloadSucceeded); |     connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ResourceDownloadTask::downloadSucceeded); | ||||||
|     connect(m_filesNetJob.get(), &NetJob::progress, this, &ResourceDownloadTask::downloadProgressChanged); |     connect(m_filesNetJob.get(), &NetJob::progress, this, &ResourceDownloadTask::downloadProgressChanged); | ||||||
|     connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &ResourceDownloadTask::propogateStepProgress); |     connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &ResourceDownloadTask::propagateStepProgress); | ||||||
|     connect(m_filesNetJob.get(), &NetJob::failed, this, &ResourceDownloadTask::downloadFailed); |     connect(m_filesNetJob.get(), &NetJob::failed, this, &ResourceDownloadTask::downloadFailed); | ||||||
|  |  | ||||||
|     addTask(m_filesNetJob); |     addTask(m_filesNetJob); | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  PolyMC - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|   | |||||||
| @@ -1,44 +1,32 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include <QString> |  | ||||||
| #include <QMap> | #include <QMap> | ||||||
|  | #include <QString> | ||||||
| #include <QStringList> | #include <QStringList> | ||||||
|  |  | ||||||
| template <char Tseparator> | template <char Tseparator> | ||||||
| class SeparatorPrefixTree | class SeparatorPrefixTree { | ||||||
| { |    public: | ||||||
| public: |     SeparatorPrefixTree(QStringList paths) { insert(paths); } | ||||||
|     SeparatorPrefixTree(QStringList paths) |  | ||||||
|     { |  | ||||||
|         insert(paths); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     SeparatorPrefixTree(bool contained = false) |     SeparatorPrefixTree(bool contained = false) { m_contained = contained; } | ||||||
|     { |  | ||||||
|         m_contained = contained; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void insert(QStringList paths) |     void insert(QStringList paths) | ||||||
|     { |     { | ||||||
|         for(auto &path: paths) |         for (auto& path : paths) { | ||||||
|         { |  | ||||||
|             insert(path); |             insert(path); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// insert an exact path into the tree |     /// insert an exact path into the tree | ||||||
|     SeparatorPrefixTree & insert(QString path) |     SeparatorPrefixTree& insert(QString path) | ||||||
|     { |     { | ||||||
|         auto sepIndex = path.indexOf(Tseparator); |         auto sepIndex = path.indexOf(Tseparator); | ||||||
|         if(sepIndex == -1) |         if (sepIndex == -1) { | ||||||
|         { |  | ||||||
|             children[path] = SeparatorPrefixTree(true); |             children[path] = SeparatorPrefixTree(true); | ||||||
|             return children[path]; |             return children[path]; | ||||||
|         } |         } else { | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             auto prefix = path.left(sepIndex); |             auto prefix = path.left(sepIndex); | ||||||
|             if(!children.contains(prefix)) |             if (!children.contains(prefix)) { | ||||||
|             { |  | ||||||
|                 children[prefix] = SeparatorPrefixTree(false); |                 children[prefix] = SeparatorPrefixTree(false); | ||||||
|             } |             } | ||||||
|             return children[prefix].insert(path.mid(sepIndex + 1)); |             return children[prefix].insert(path.mid(sepIndex + 1)); | ||||||
| @@ -56,26 +44,20 @@ public: | |||||||
|     bool covers(QString path) const |     bool covers(QString path) const | ||||||
|     { |     { | ||||||
|         // if we found some valid node, it's good enough. the tree covers the path |         // if we found some valid node, it's good enough. the tree covers the path | ||||||
|         if(m_contained) |         if (m_contained) { | ||||||
|         { |  | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|         auto sepIndex = path.indexOf(Tseparator); |         auto sepIndex = path.indexOf(Tseparator); | ||||||
|         if(sepIndex == -1) |         if (sepIndex == -1) { | ||||||
|         { |  | ||||||
|             auto found = children.find(path); |             auto found = children.find(path); | ||||||
|             if(found == children.end()) |             if (found == children.end()) { | ||||||
|             { |  | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|             return (*found).covers(QString()); |             return (*found).covers(QString()); | ||||||
|         } |         } else { | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             auto prefix = path.left(sepIndex); |             auto prefix = path.left(sepIndex); | ||||||
|             auto found = children.find(prefix); |             auto found = children.find(prefix); | ||||||
|             if(found == children.end()) |             if (found == children.end()) { | ||||||
|             { |  | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|             return (*found).covers(path.mid(sepIndex + 1)); |             return (*found).covers(path.mid(sepIndex + 1)); | ||||||
| @@ -86,41 +68,33 @@ public: | |||||||
|     QString cover(QString path) const |     QString cover(QString path) const | ||||||
|     { |     { | ||||||
|         // if we found some valid node, it's good enough. the tree covers the path |         // if we found some valid node, it's good enough. the tree covers the path | ||||||
|         if(m_contained) |         if (m_contained) { | ||||||
|         { |  | ||||||
|             return QString(""); |             return QString(""); | ||||||
|         } |         } | ||||||
|         auto sepIndex = path.indexOf(Tseparator); |         auto sepIndex = path.indexOf(Tseparator); | ||||||
|         if(sepIndex == -1) |         if (sepIndex == -1) { | ||||||
|         { |  | ||||||
|             auto found = children.find(path); |             auto found = children.find(path); | ||||||
|             if(found == children.end()) |             if (found == children.end()) { | ||||||
|             { |  | ||||||
|                 return QString(); |                 return QString(); | ||||||
|             } |             } | ||||||
|             auto nested = (*found).cover(QString()); |             auto nested = (*found).cover(QString()); | ||||||
|             if(nested.isNull()) |             if (nested.isNull()) { | ||||||
|             { |  | ||||||
|                 return nested; |                 return nested; | ||||||
|             } |             } | ||||||
|             if(nested.isEmpty()) |             if (nested.isEmpty()) | ||||||
|                 return path; |                 return path; | ||||||
|             return path + Tseparator + nested; |             return path + Tseparator + nested; | ||||||
|         } |         } else { | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             auto prefix = path.left(sepIndex); |             auto prefix = path.left(sepIndex); | ||||||
|             auto found = children.find(prefix); |             auto found = children.find(prefix); | ||||||
|             if(found == children.end()) |             if (found == children.end()) { | ||||||
|             { |  | ||||||
|                 return QString(); |                 return QString(); | ||||||
|             } |             } | ||||||
|             auto nested = (*found).cover(path.mid(sepIndex + 1)); |             auto nested = (*found).cover(path.mid(sepIndex + 1)); | ||||||
|             if(nested.isNull()) |             if (nested.isNull()) { | ||||||
|             { |  | ||||||
|                 return nested; |                 return nested; | ||||||
|             } |             } | ||||||
|             if(nested.isEmpty()) |             if (nested.isEmpty()) | ||||||
|                 return prefix; |                 return prefix; | ||||||
|             return prefix + Tseparator + nested; |             return prefix + Tseparator + nested; | ||||||
|         } |         } | ||||||
| @@ -130,21 +104,16 @@ public: | |||||||
|     bool exists(QString path) const |     bool exists(QString path) const | ||||||
|     { |     { | ||||||
|         auto sepIndex = path.indexOf(Tseparator); |         auto sepIndex = path.indexOf(Tseparator); | ||||||
|         if(sepIndex == -1) |         if (sepIndex == -1) { | ||||||
|         { |  | ||||||
|             auto found = children.find(path); |             auto found = children.find(path); | ||||||
|             if(found == children.end()) |             if (found == children.end()) { | ||||||
|             { |  | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|             return true; |             return true; | ||||||
|         } |         } else { | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             auto prefix = path.left(sepIndex); |             auto prefix = path.left(sepIndex); | ||||||
|             auto found = children.find(prefix); |             auto found = children.find(prefix); | ||||||
|             if(found == children.end()) |             if (found == children.end()) { | ||||||
|             { |  | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|             return (*found).exists(path.mid(sepIndex + 1)); |             return (*found).exists(path.mid(sepIndex + 1)); | ||||||
| @@ -152,24 +121,19 @@ public: | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// find a node in the tree by name |     /// find a node in the tree by name | ||||||
|     const SeparatorPrefixTree * find(QString path) const |     const SeparatorPrefixTree* find(QString path) const | ||||||
|     { |     { | ||||||
|         auto sepIndex = path.indexOf(Tseparator); |         auto sepIndex = path.indexOf(Tseparator); | ||||||
|         if(sepIndex == -1) |         if (sepIndex == -1) { | ||||||
|         { |  | ||||||
|             auto found = children.find(path); |             auto found = children.find(path); | ||||||
|             if(found == children.end()) |             if (found == children.end()) { | ||||||
|             { |  | ||||||
|                 return nullptr; |                 return nullptr; | ||||||
|             } |             } | ||||||
|             return &(*found); |             return &(*found); | ||||||
|         } |         } else { | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             auto prefix = path.left(sepIndex); |             auto prefix = path.left(sepIndex); | ||||||
|             auto found = children.find(prefix); |             auto found = children.find(prefix); | ||||||
|             if(found == children.end()) |             if (found == children.end()) { | ||||||
|             { |  | ||||||
|                 return nullptr; |                 return nullptr; | ||||||
|             } |             } | ||||||
|             return (*found).find(path.mid(sepIndex + 1)); |             return (*found).find(path.mid(sepIndex + 1)); | ||||||
| @@ -177,70 +141,48 @@ public: | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// is this a leaf node? |     /// is this a leaf node? | ||||||
|     bool leaf() const |     bool leaf() const { return children.isEmpty(); } | ||||||
|     { |  | ||||||
|         return children.isEmpty(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// is this node actually contained in the tree, or is it purely structural? |     /// is this node actually contained in the tree, or is it purely structural? | ||||||
|     bool contained() const |     bool contained() const { return m_contained; } | ||||||
|     { |  | ||||||
|         return m_contained; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// Remove a path from the tree |     /// Remove a path from the tree | ||||||
|     bool remove(QString path) |     bool remove(QString path) { return removeInternal(path) != Failed; } | ||||||
|     { |  | ||||||
|         return removeInternal(path) != Failed; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// Clear all children of this node tree node |     /// Clear all children of this node tree node | ||||||
|     void clear() |     void clear() { children.clear(); } | ||||||
|     { |  | ||||||
|         children.clear(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     QStringList toStringList() const |     QStringList toStringList() const | ||||||
|     { |     { | ||||||
|         QStringList collected; |         QStringList collected; | ||||||
|         // collecting these is more expensive. |         // collecting these is more expensive. | ||||||
|         auto iter = children.begin(); |         auto iter = children.begin(); | ||||||
|         while(iter != children.end()) |         while (iter != children.end()) { | ||||||
|         { |  | ||||||
|             QStringList list = iter.value().toStringList(); |             QStringList list = iter.value().toStringList(); | ||||||
|             for(int i = 0; i < list.size(); i++) |             for (int i = 0; i < list.size(); i++) { | ||||||
|             { |  | ||||||
|                 list[i] = iter.key() + Tseparator + list[i]; |                 list[i] = iter.key() + Tseparator + list[i]; | ||||||
|             } |             } | ||||||
|             collected.append(list); |             collected.append(list); | ||||||
|             if((*iter).m_contained) |             if ((*iter).m_contained) { | ||||||
|             { |  | ||||||
|                 collected.append(iter.key()); |                 collected.append(iter.key()); | ||||||
|             } |             } | ||||||
|             iter++; |             iter++; | ||||||
|         } |         } | ||||||
|         return collected; |         return collected; | ||||||
|     } |     } | ||||||
| private: |  | ||||||
|     enum Removal |    private: | ||||||
|     { |     enum Removal { Failed, Succeeded, HasChildren }; | ||||||
|         Failed, |  | ||||||
|         Succeeded, |  | ||||||
|         HasChildren |  | ||||||
|     }; |  | ||||||
|     Removal removeInternal(QString path = QString()) |     Removal removeInternal(QString path = QString()) | ||||||
|     { |     { | ||||||
|         if(path.isEmpty()) |         if (path.isEmpty()) { | ||||||
|         { |             if (!m_contained) { | ||||||
|             if(!m_contained) |  | ||||||
|             { |  | ||||||
|                 // remove all children - we are removing a prefix |                 // remove all children - we are removing a prefix | ||||||
|                 clear(); |                 clear(); | ||||||
|                 return Succeeded; |                 return Succeeded; | ||||||
|             } |             } | ||||||
|             m_contained = false; |             m_contained = false; | ||||||
|             if(children.size()) |             if (children.size()) { | ||||||
|             { |  | ||||||
|                 return HasChildren; |                 return HasChildren; | ||||||
|             } |             } | ||||||
|             return Succeeded; |             return Succeeded; | ||||||
| @@ -248,42 +190,32 @@ private: | |||||||
|         Removal remStatus = Failed; |         Removal remStatus = Failed; | ||||||
|         QString childToRemove; |         QString childToRemove; | ||||||
|         auto sepIndex = path.indexOf(Tseparator); |         auto sepIndex = path.indexOf(Tseparator); | ||||||
|         if(sepIndex == -1) |         if (sepIndex == -1) { | ||||||
|         { |  | ||||||
|             childToRemove = path; |             childToRemove = path; | ||||||
|             auto found = children.find(childToRemove); |             auto found = children.find(childToRemove); | ||||||
|             if(found == children.end()) |             if (found == children.end()) { | ||||||
|             { |  | ||||||
|                 return Failed; |                 return Failed; | ||||||
|             } |             } | ||||||
|             remStatus = (*found).removeInternal(); |             remStatus = (*found).removeInternal(); | ||||||
|         } |         } else { | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             childToRemove = path.left(sepIndex); |             childToRemove = path.left(sepIndex); | ||||||
|             auto found = children.find(childToRemove); |             auto found = children.find(childToRemove); | ||||||
|             if(found == children.end()) |             if (found == children.end()) { | ||||||
|             { |  | ||||||
|                 return Failed; |                 return Failed; | ||||||
|             } |             } | ||||||
|             remStatus = (*found).removeInternal(path.mid(sepIndex + 1)); |             remStatus = (*found).removeInternal(path.mid(sepIndex + 1)); | ||||||
|         } |         } | ||||||
|         switch (remStatus) |         switch (remStatus) { | ||||||
|         { |  | ||||||
|             case Failed: |             case Failed: | ||||||
|             case HasChildren: |             case HasChildren: { | ||||||
|             { |  | ||||||
|                 return remStatus; |                 return remStatus; | ||||||
|             } |             } | ||||||
|             case Succeeded: |             case Succeeded: { | ||||||
|             { |  | ||||||
|                 children.remove(childToRemove); |                 children.remove(childToRemove); | ||||||
|                 if(m_contained) |                 if (m_contained) { | ||||||
|                 { |  | ||||||
|                     return HasChildren; |                     return HasChildren; | ||||||
|                 } |                 } | ||||||
|                 if(children.size()) |                 if (children.size()) { | ||||||
|                 { |  | ||||||
|                     return HasChildren; |                     return HasChildren; | ||||||
|                 } |                 } | ||||||
|                 return Succeeded; |                 return Succeeded; | ||||||
| @@ -292,7 +224,7 @@ private: | |||||||
|         return Failed; |         return Failed; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| private: |    private: | ||||||
|     QMap<QString,SeparatorPrefixTree<Tseparator>> children; |     QMap<QString, SeparatorPrefixTree<Tseparator>> children; | ||||||
|     bool m_contained = false; |     bool m_contained = false; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -14,17 +14,16 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "SkinUtils.h" | #include "SkinUtils.h" | ||||||
| #include "net/HttpMetaCache.h" |  | ||||||
| #include "Application.h" | #include "Application.h" | ||||||
|  | #include "net/HttpMetaCache.h" | ||||||
|  |  | ||||||
| #include <QFile> | #include <QFile> | ||||||
| #include <QPainter> | #include <QJsonArray> | ||||||
| #include <QJsonDocument> | #include <QJsonDocument> | ||||||
| #include <QJsonObject> | #include <QJsonObject> | ||||||
| #include <QJsonArray> | #include <QPainter> | ||||||
|  |  | ||||||
| namespace SkinUtils | namespace SkinUtils { | ||||||
| { |  | ||||||
| /* | /* | ||||||
|  * Given a username, return a pixmap of the cached skin (if it exists), QPixmap() otherwise |  * Given a username, return a pixmap of the cached skin (if it exists), QPixmap() otherwise | ||||||
|  */ |  */ | ||||||
| @@ -32,12 +31,15 @@ QPixmap getFaceFromCache(QString username, int height, int width) | |||||||
| { | { | ||||||
|     QFile fskin(APPLICATION->metacache()->resolveEntry("skins", username + ".png")->getFullPath()); |     QFile fskin(APPLICATION->metacache()->resolveEntry("skins", username + ".png")->getFullPath()); | ||||||
|  |  | ||||||
|     if (fskin.exists()) |     if (fskin.exists()) { | ||||||
|     { |  | ||||||
|         QPixmap skinTexture(fskin.fileName()); |         QPixmap skinTexture(fskin.fileName()); | ||||||
|         if(!skinTexture.isNull()) |         if (!skinTexture.isNull()) { | ||||||
|         { |  | ||||||
|             QPixmap skin = QPixmap(8, 8); |             QPixmap skin = QPixmap(8, 8); | ||||||
|  | #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) | ||||||
|  |             skin.fill(QColorConstants::Transparent); | ||||||
|  | #else | ||||||
|  |             skin.fill(QColor(0, 0, 0, 0)); | ||||||
|  | #endif | ||||||
|             QPainter painter(&skin); |             QPainter painter(&skin); | ||||||
|             painter.drawPixmap(0, 0, skinTexture.copy(8, 8, 8, 8)); |             painter.drawPixmap(0, 0, skinTexture.copy(8, 8, 8, 8)); | ||||||
|             painter.drawPixmap(0, 0, skinTexture.copy(40, 8, 8, 8)); |             painter.drawPixmap(0, 0, skinTexture.copy(40, 8, 8, 8)); | ||||||
| @@ -47,4 +49,4 @@ QPixmap getFaceFromCache(QString username, int height, int width) | |||||||
|  |  | ||||||
|     return QPixmap(); |     return QPixmap(); | ||||||
| } | } | ||||||
| } | }  // namespace SkinUtils | ||||||
|   | |||||||
| @@ -17,7 +17,6 @@ | |||||||
|  |  | ||||||
| #include <QPixmap> | #include <QPixmap> | ||||||
|  |  | ||||||
| namespace SkinUtils | namespace SkinUtils { | ||||||
| { |  | ||||||
| QPixmap getFaceFromCache(QString id, int height = 64, int width = 64); | QPixmap getFaceFromCache(QString id, int height = 64, int width = 64); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -68,15 +68,14 @@ inline QString fromStdString(string s) | |||||||
| int naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs); | int naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs); | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * @brief Truncate a url while keeping its readability py placing the `...` in the middle of the path   |  * @brief Truncate a url while keeping its readability py placing the `...` in the middle of the path | ||||||
|  * @param url Url to truncate |  * @param url Url to truncate | ||||||
|  * @param max_len max lenght of url in charaters |  * @param max_len max lenght of url in charaters | ||||||
|  * @param hard_limit if truncating the path can't get the url short enough, truncate it normaly. |  * @param hard_limit if truncating the path can't get the url short enough, truncate it normaly. | ||||||
|  */ |  */ | ||||||
| QString truncateUrlHumanFriendly(QUrl &url, int max_len, bool hard_limit = false); | QString truncateUrlHumanFriendly(QUrl& url, int max_len, bool hard_limit = false); | ||||||
|  |  | ||||||
| QString humanReadableFileSize(double bytes, bool use_si = false, int decimal_points = 1); | QString humanReadableFileSize(double bytes, bool use_si = false, int decimal_points = 1); | ||||||
|  |  | ||||||
|  |  | ||||||
| QString getRandomAlphaNumeric(); | QString getRandomAlphaNumeric(); | ||||||
| }  // namespace StringUtils | }  // namespace StringUtils | ||||||
|   | |||||||
| @@ -12,28 +12,20 @@ class Usable; | |||||||
|  * |  * | ||||||
|  * @see UseLock |  * @see UseLock | ||||||
|  */ |  */ | ||||||
| class Usable | class Usable { | ||||||
| { |  | ||||||
|     friend class UseLock; |     friend class UseLock; | ||||||
| public: |  | ||||||
|     std::size_t useCount() const |    public: | ||||||
|     { |     virtual ~Usable() {} | ||||||
|         return m_useCount; |  | ||||||
|     } |     std::size_t useCount() const { return m_useCount; } | ||||||
|     bool isInUse() const |     bool isInUse() const { return m_useCount > 0; } | ||||||
|     { |  | ||||||
|         return m_useCount > 0; |    protected: | ||||||
|     } |     virtual void decrementUses() { m_useCount--; } | ||||||
| protected: |     virtual void incrementUses() { m_useCount++; } | ||||||
|     virtual void decrementUses() |  | ||||||
|     { |    private: | ||||||
|         m_useCount--; |  | ||||||
|     } |  | ||||||
|     virtual void incrementUses() |  | ||||||
|     { |  | ||||||
|         m_useCount++; |  | ||||||
|     } |  | ||||||
| private: |  | ||||||
|     std::size_t m_useCount = 0; |     std::size_t m_useCount = 0; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -42,19 +34,15 @@ private: | |||||||
|  * |  * | ||||||
|  * @see Usable |  * @see Usable | ||||||
|  */ |  */ | ||||||
| class UseLock | class UseLock { | ||||||
| { |    public: | ||||||
| public: |     UseLock(shared_qobject_ptr<Usable> usable) : m_usable(usable) | ||||||
|     UseLock(shared_qobject_ptr<Usable> usable) |  | ||||||
|         : m_usable(usable) |  | ||||||
|     { |     { | ||||||
|         // this doesn't use shared pointer use count, because that wouldn't be correct. this count is separate. |         // this doesn't use shared pointer use count, because that wouldn't be correct. this count is separate. | ||||||
|         m_usable->incrementUses(); |         m_usable->incrementUses(); | ||||||
|     } |     } | ||||||
|     ~UseLock() |     ~UseLock() { m_usable->decrementUses(); } | ||||||
|     { |  | ||||||
|         m_usable->decrementUses(); |    private: | ||||||
|     } |  | ||||||
| private: |  | ||||||
|     shared_qobject_ptr<Usable> m_usable; |     shared_qobject_ptr<Usable> m_usable; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -117,12 +117,14 @@ QDebug operator<<(QDebug debug, const Version& v) | |||||||
|  |  | ||||||
|     bool first = true; |     bool first = true; | ||||||
|     for (auto s : v.m_sections) { |     for (auto s : v.m_sections) { | ||||||
|         if (!first) debug.nospace() << ", "; |         if (!first) | ||||||
|  |             debug.nospace() << ", "; | ||||||
|         debug.nospace() << s.m_fullString; |         debug.nospace() << s.m_fullString; | ||||||
|         first = false; |         first = false; | ||||||
|     } |     } | ||||||
|                      |  | ||||||
|     debug.nospace() << " ]" << " }"; |     debug.nospace() << " ]" | ||||||
|  |                     << " }"; | ||||||
|  |  | ||||||
|     return debug; |     return debug; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  PolyMC - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2023 flowln <flowlnlnln@gmail.com> |  *  Copyright (C) 2023 flowln <flowlnlnln@gmail.com> | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  * |  * | ||||||
| @@ -48,12 +48,12 @@ class Version { | |||||||
|     Version(QString str); |     Version(QString str); | ||||||
|     Version() = default; |     Version() = default; | ||||||
|  |  | ||||||
|     bool operator<(const Version &other) const; |     bool operator<(const Version& other) const; | ||||||
|     bool operator<=(const Version &other) const; |     bool operator<=(const Version& other) const; | ||||||
|     bool operator>(const Version &other) const; |     bool operator>(const Version& other) const; | ||||||
|     bool operator>=(const Version &other) const; |     bool operator>=(const Version& other) const; | ||||||
|     bool operator==(const Version &other) const; |     bool operator==(const Version& other) const; | ||||||
|     bool operator!=(const Version &other) const; |     bool operator!=(const Version& other) const; | ||||||
|  |  | ||||||
|     QString toString() const { return m_string; } |     QString toString() const { return m_string; } | ||||||
|  |  | ||||||
| @@ -63,7 +63,7 @@ class Version { | |||||||
|     struct Section { |     struct Section { | ||||||
|         explicit Section(QString fullString) : m_fullString(std::move(fullString)) |         explicit Section(QString fullString) : m_fullString(std::move(fullString)) | ||||||
|         { |         { | ||||||
|             int cutoff = m_fullString.size(); |             qsizetype cutoff = m_fullString.size(); | ||||||
|             for (int i = 0; i < m_fullString.size(); i++) { |             for (int i = 0; i < m_fullString.size(); i++) { | ||||||
|                 if (!m_fullString[i].isDigit()) { |                 if (!m_fullString[i].isDigit()) { | ||||||
|                     cutoff = i; |                     cutoff = i; | ||||||
| @@ -72,7 +72,7 @@ class Version { | |||||||
|             } |             } | ||||||
|  |  | ||||||
| #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) | #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) | ||||||
|             auto numPart = QStringView{m_fullString}.left(cutoff); |             auto numPart = QStringView{ m_fullString }.left(cutoff); | ||||||
| #else | #else | ||||||
|             auto numPart = m_fullString.leftRef(cutoff); |             auto numPart = m_fullString.leftRef(cutoff); | ||||||
| #endif | #endif | ||||||
| @@ -83,7 +83,7 @@ class Version { | |||||||
|             } |             } | ||||||
|  |  | ||||||
| #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) | #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) | ||||||
|             auto stringPart = QStringView{m_fullString}.mid(cutoff); |             auto stringPart = QStringView{ m_fullString }.mid(cutoff); | ||||||
| #else | #else | ||||||
|             auto stringPart = m_fullString.midRef(cutoff); |             auto stringPart = m_fullString.midRef(cutoff); | ||||||
| #endif | #endif | ||||||
| @@ -103,8 +103,14 @@ class Version { | |||||||
|  |  | ||||||
|         QString m_fullString; |         QString m_fullString; | ||||||
|  |  | ||||||
|         [[nodiscard]] inline bool isAppendix() const { return m_stringPart.startsWith('+'); } |         [[nodiscard]] inline bool isAppendix() const | ||||||
|         [[nodiscard]] inline bool isPreRelease() const { return m_stringPart.startsWith('-') && m_stringPart.length() > 1; } |         { | ||||||
|  |             return m_stringPart.startsWith('+'); | ||||||
|  |         } | ||||||
|  |         [[nodiscard]] inline bool isPreRelease() const | ||||||
|  |         { | ||||||
|  |             return m_stringPart.startsWith('-') && m_stringPart.length() > 1; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         inline bool operator==(const Section& other) const |         inline bool operator==(const Section& other) const | ||||||
|         { |         { | ||||||
| @@ -121,7 +127,7 @@ class Version { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         inline bool operator<(const Section& other) const |         inline bool operator<(const Section& other) const | ||||||
|         {    |         { | ||||||
|             static auto unequal_is_less = [](Section const& non_null) -> bool { |             static auto unequal_is_less = [](Section const& non_null) -> bool { | ||||||
|                 if (non_null.m_stringPart.isEmpty()) |                 if (non_null.m_stringPart.isEmpty()) | ||||||
|                     return non_null.m_numPart == 0; |                     return non_null.m_numPart == 0; | ||||||
| @@ -154,7 +160,7 @@ class Version { | |||||||
|         { |         { | ||||||
|             return !(*this == other); |             return !(*this == other); | ||||||
|         } |         } | ||||||
|         inline bool operator>(const Section &other) const |         inline bool operator>(const Section& other) const | ||||||
|         { |         { | ||||||
|             return !(*this < other || *this == other); |             return !(*this < other || *this == other); | ||||||
|         } |         } | ||||||
| @@ -166,5 +172,3 @@ class Version { | |||||||
|  |  | ||||||
|     void parse(); |     void parse(); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -35,53 +35,48 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "VersionProxyModel.h" | #include "VersionProxyModel.h" | ||||||
| #include "Application.h" |  | ||||||
| #include <QSortFilterProxyModel> |  | ||||||
| #include <QPixmapCache> |  | ||||||
| #include <Version.h> | #include <Version.h> | ||||||
| #include <meta/VersionList.h> | #include <meta/VersionList.h> | ||||||
|  | #include <QPixmapCache> | ||||||
|  | #include <QSortFilterProxyModel> | ||||||
|  | #include "Application.h" | ||||||
|  |  | ||||||
| class VersionFilterModel : public QSortFilterProxyModel | class VersionFilterModel : public QSortFilterProxyModel { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| public: |    public: | ||||||
|     VersionFilterModel(VersionProxyModel *parent) : QSortFilterProxyModel(parent) |     VersionFilterModel(VersionProxyModel* parent) : QSortFilterProxyModel(parent) | ||||||
|     { |     { | ||||||
|         m_parent = parent; |         m_parent = parent; | ||||||
|         setSortRole(BaseVersionList::SortRole); |         setSortRole(BaseVersionList::SortRole); | ||||||
|         sort(0, Qt::DescendingOrder); |         sort(0, Qt::DescendingOrder); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const |     bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const | ||||||
|     { |     { | ||||||
|         const auto &filters = m_parent->filters(); |         const auto& filters = m_parent->filters(); | ||||||
|         const QString &search = m_parent->search(); |         const QString& search = m_parent->search(); | ||||||
|         const QModelIndex idx = sourceModel()->index(source_row, 0, source_parent); |         const QModelIndex idx = sourceModel()->index(source_row, 0, source_parent); | ||||||
|  |  | ||||||
|         if (!search.isEmpty() && !sourceModel()->data(idx, BaseVersionList::VersionRole).toString().contains(search, Qt::CaseInsensitive)) |         if (!search.isEmpty() && !sourceModel()->data(idx, BaseVersionList::VersionRole).toString().contains(search, Qt::CaseInsensitive)) | ||||||
|             return false; |             return false; | ||||||
|  |  | ||||||
|         for (auto it = filters.begin(); it != filters.end(); ++it) |         for (auto it = filters.begin(); it != filters.end(); ++it) { | ||||||
|         { |  | ||||||
|             auto data = sourceModel()->data(idx, it.key()); |             auto data = sourceModel()->data(idx, it.key()); | ||||||
|             auto match = data.toString(); |             auto match = data.toString(); | ||||||
|             if(!it.value()->accepts(match)) |             if (!it.value()->accepts(match)) { | ||||||
|             { |  | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void filterChanged() |     void filterChanged() { invalidateFilter(); } | ||||||
|     { |  | ||||||
|         invalidateFilter(); |    private: | ||||||
|     } |     VersionProxyModel* m_parent; | ||||||
| private: |  | ||||||
|     VersionProxyModel *m_parent; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| VersionProxyModel::VersionProxyModel(QObject *parent) : QAbstractProxyModel(parent) | VersionProxyModel::VersionProxyModel(QObject* parent) : QAbstractProxyModel(parent) | ||||||
| { | { | ||||||
|     filterModel = new VersionFilterModel(this); |     filterModel = new VersionFilterModel(this); | ||||||
|     connect(filterModel, &QAbstractItemModel::dataChanged, this, &VersionProxyModel::sourceDataChanged); |     connect(filterModel, &QAbstractItemModel::dataChanged, this, &VersionProxyModel::sourceDataChanged); | ||||||
| @@ -104,19 +99,17 @@ VersionProxyModel::VersionProxyModel(QObject *parent) : QAbstractProxyModel(pare | |||||||
|  |  | ||||||
| QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation, int role) const | QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation, int role) const | ||||||
| { | { | ||||||
|     if(section < 0 || section >= m_columns.size()) |     if (section < 0 || section >= m_columns.size()) | ||||||
|         return QVariant(); |         return QVariant(); | ||||||
|     if(orientation != Qt::Horizontal) |     if (orientation != Qt::Horizontal) | ||||||
|         return QVariant(); |         return QVariant(); | ||||||
|     auto column = m_columns[section]; |     auto column = m_columns[section]; | ||||||
|     if(role == Qt::DisplayRole) |     if (role == Qt::DisplayRole) { | ||||||
|     { |         switch (column) { | ||||||
|         switch(column) |  | ||||||
|         { |  | ||||||
|             case Name: |             case Name: | ||||||
|                 return tr("Version"); |                 return tr("Version"); | ||||||
|             case ParentVersion: |             case ParentVersion: | ||||||
|                 return tr("Minecraft"); //FIXME: this should come from metadata |                 return tr("Minecraft");  // FIXME: this should come from metadata | ||||||
|             case Branch: |             case Branch: | ||||||
|                 return tr("Branch"); |                 return tr("Branch"); | ||||||
|             case Type: |             case Type: | ||||||
| @@ -128,15 +121,12 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation, | |||||||
|             case Time: |             case Time: | ||||||
|                 return tr("Released"); |                 return tr("Released"); | ||||||
|         } |         } | ||||||
|     } |     } else if (role == Qt::ToolTipRole) { | ||||||
|     else if(role == Qt::ToolTipRole) |         switch (column) { | ||||||
|     { |  | ||||||
|         switch(column) |  | ||||||
|         { |  | ||||||
|             case Name: |             case Name: | ||||||
|                 return tr("The name of the version."); |                 return tr("The name of the version."); | ||||||
|             case ParentVersion: |             case ParentVersion: | ||||||
|                 return tr("Minecraft version"); //FIXME: this should come from metadata |                 return tr("Minecraft version");  // FIXME: this should come from metadata | ||||||
|             case Branch: |             case Branch: | ||||||
|                 return tr("The version's branch"); |                 return tr("The version's branch"); | ||||||
|             case Type: |             case Type: | ||||||
| @@ -152,25 +142,19 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation, | |||||||
|     return QVariant(); |     return QVariant(); | ||||||
| } | } | ||||||
|  |  | ||||||
| QVariant VersionProxyModel::data(const QModelIndex &index, int role) const | QVariant VersionProxyModel::data(const QModelIndex& index, int role) const | ||||||
| { | { | ||||||
|     if(!index.isValid()) |     if (!index.isValid()) { | ||||||
|     { |  | ||||||
|         return QVariant(); |         return QVariant(); | ||||||
|     } |     } | ||||||
|     auto column = m_columns[index.column()]; |     auto column = m_columns[index.column()]; | ||||||
|     auto parentIndex = mapToSource(index); |     auto parentIndex = mapToSource(index); | ||||||
|     switch(role) |     switch (role) { | ||||||
|     { |         case Qt::DisplayRole: { | ||||||
|         case Qt::DisplayRole: |             switch (column) { | ||||||
|         { |                 case Name: { | ||||||
|             switch(column) |  | ||||||
|             { |  | ||||||
|                 case Name: |  | ||||||
|                 { |  | ||||||
|                     QString version = sourceModel()->data(parentIndex, BaseVersionList::VersionRole).toString(); |                     QString version = sourceModel()->data(parentIndex, BaseVersionList::VersionRole).toString(); | ||||||
|                     if(version == m_currentVersion) |                     if (version == m_currentVersion) { | ||||||
|                     { |  | ||||||
|                         return tr("%1 (installed)").arg(version); |                         return tr("%1 (installed)").arg(version); | ||||||
|                     } |                     } | ||||||
|                     return version; |                     return version; | ||||||
| @@ -191,18 +175,14 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const | |||||||
|                     return QVariant(); |                     return QVariant(); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         case Qt::ToolTipRole: |         case Qt::ToolTipRole: { | ||||||
|         { |             if (column == Name && hasRecommended) { | ||||||
|             if(column == Name && hasRecommended)  |  | ||||||
|             { |  | ||||||
|                 auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole); |                 auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole); | ||||||
|                 if(value.toBool()) |                 if (value.toBool()) { | ||||||
|                 { |  | ||||||
|                     return tr("Recommended"); |                     return tr("Recommended"); | ||||||
|                 } else if(hasLatest) { |                 } else if (hasLatest) { | ||||||
|                     auto value = sourceModel()->data(parentIndex, BaseVersionList::LatestRole); |                     auto value = sourceModel()->data(parentIndex, BaseVersionList::LatestRole); | ||||||
|                     if(value.toBool()) |                     if (value.toBool()) { | ||||||
|                     { |  | ||||||
|                         return tr("Latest"); |                         return tr("Latest"); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
| @@ -210,32 +190,23 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const | |||||||
|                 return sourceModel()->data(parentIndex, BaseVersionList::VersionIdRole); |                 return sourceModel()->data(parentIndex, BaseVersionList::VersionIdRole); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         case Qt::DecorationRole: |         case Qt::DecorationRole: { | ||||||
|         { |             switch (column) { | ||||||
|             switch(column) |                 case Name: { | ||||||
|             { |                     if (hasRecommended) { | ||||||
|                 case Name: |                         auto recommenced = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole); | ||||||
|                 { |                         if (recommenced.toBool()) { | ||||||
|                     if(hasRecommended) |  | ||||||
|                     { |  | ||||||
|                         auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole); |  | ||||||
|                         if(value.toBool()) |  | ||||||
|                         { |  | ||||||
|                             return APPLICATION->getThemedIcon("star"); |                             return APPLICATION->getThemedIcon("star"); | ||||||
|                         } |                         } else if (hasLatest) { | ||||||
|                         else if(hasLatest) |                             auto latest = sourceModel()->data(parentIndex, BaseVersionList::LatestRole); | ||||||
|                         { |                             if (latest.toBool()) { | ||||||
|                             auto value = sourceModel()->data(parentIndex, BaseVersionList::LatestRole); |  | ||||||
|                             if(value.toBool()) |  | ||||||
|                             { |  | ||||||
|                                 return APPLICATION->getThemedIcon("bug"); |                                 return APPLICATION->getThemedIcon("bug"); | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|                         QPixmap pixmap; |                         QPixmap pixmap; | ||||||
|                         QPixmapCache::find("placeholder", &pixmap); |                         QPixmapCache::find("placeholder", &pixmap); | ||||||
|                         if(!pixmap) |                         if (!pixmap) { | ||||||
|                         { |                             QPixmap px(16, 16); | ||||||
|                             QPixmap px(16,16); |  | ||||||
|                             px.fill(Qt::transparent); |                             px.fill(Qt::transparent); | ||||||
|                             QPixmapCache::insert("placeholder", px); |                             QPixmapCache::insert("placeholder", px); | ||||||
|                             return px; |                             return px; | ||||||
| @@ -243,16 +214,13 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const | |||||||
|                         return pixmap; |                         return pixmap; | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|                 default: |                 default: { | ||||||
|                 { |  | ||||||
|                     return QVariant(); |                     return QVariant(); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         default: |         default: { | ||||||
|         { |             if (roles.contains((BaseVersionList::ModelRoles)role)) { | ||||||
|             if(roles.contains((BaseVersionList::ModelRoles)role)) |  | ||||||
|             { |  | ||||||
|                 return sourceModel()->data(parentIndex, role); |                 return sourceModel()->data(parentIndex, role); | ||||||
|             } |             } | ||||||
|             return QVariant(); |             return QVariant(); | ||||||
| @@ -260,61 +228,56 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| QModelIndex VersionProxyModel::parent(const QModelIndex &child) const | QModelIndex VersionProxyModel::parent([[maybe_unused]] const QModelIndex& child) const | ||||||
| { | { | ||||||
|     return QModelIndex(); |     return QModelIndex(); | ||||||
| } | } | ||||||
|  |  | ||||||
| QModelIndex VersionProxyModel::mapFromSource(const QModelIndex &sourceIndex) const | QModelIndex VersionProxyModel::mapFromSource(const QModelIndex& sourceIndex) const | ||||||
| { | { | ||||||
|     if(sourceIndex.isValid()) |     if (sourceIndex.isValid()) { | ||||||
|     { |  | ||||||
|         return index(sourceIndex.row(), 0); |         return index(sourceIndex.row(), 0); | ||||||
|     } |     } | ||||||
|     return QModelIndex(); |     return QModelIndex(); | ||||||
| } | } | ||||||
|  |  | ||||||
| QModelIndex VersionProxyModel::mapToSource(const QModelIndex &proxyIndex) const | QModelIndex VersionProxyModel::mapToSource(const QModelIndex& proxyIndex) const | ||||||
| { | { | ||||||
|     if(proxyIndex.isValid()) |     if (proxyIndex.isValid()) { | ||||||
|     { |  | ||||||
|         return sourceModel()->index(proxyIndex.row(), 0); |         return sourceModel()->index(proxyIndex.row(), 0); | ||||||
|     } |     } | ||||||
|     return QModelIndex(); |     return QModelIndex(); | ||||||
| } | } | ||||||
|  |  | ||||||
| QModelIndex VersionProxyModel::index(int row, int column, const QModelIndex &parent) const | QModelIndex VersionProxyModel::index(int row, int column, const QModelIndex& parent) const | ||||||
| { | { | ||||||
|     // no trees here... shoo |     // no trees here... shoo | ||||||
|     if(parent.isValid()) |     if (parent.isValid()) { | ||||||
|     { |  | ||||||
|         return QModelIndex(); |         return QModelIndex(); | ||||||
|     } |     } | ||||||
|     if(row < 0 || row >= sourceModel()->rowCount()) |     if (row < 0 || row >= sourceModel()->rowCount()) | ||||||
|         return QModelIndex(); |         return QModelIndex(); | ||||||
|     if(column < 0 || column >= columnCount()) |     if (column < 0 || column >= columnCount()) | ||||||
|         return QModelIndex(); |         return QModelIndex(); | ||||||
|     return QAbstractItemModel::createIndex(row, column); |     return QAbstractItemModel::createIndex(row, column); | ||||||
| } | } | ||||||
|  |  | ||||||
| int VersionProxyModel::columnCount(const QModelIndex &parent) const | int VersionProxyModel::columnCount(const QModelIndex& parent) const | ||||||
| { | { | ||||||
|     return parent.isValid() ? 0 : m_columns.size(); |     return parent.isValid() ? 0 : m_columns.size(); | ||||||
| } | } | ||||||
|  |  | ||||||
| int VersionProxyModel::rowCount(const QModelIndex &parent) const | int VersionProxyModel::rowCount(const QModelIndex& parent) const | ||||||
| { | { | ||||||
|     if(sourceModel()) |     if (sourceModel()) { | ||||||
|     { |  | ||||||
|         return sourceModel()->rowCount(parent); |         return sourceModel()->rowCount(parent); | ||||||
|     } |     } | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| void VersionProxyModel::sourceDataChanged(const QModelIndex &source_top_left, | void VersionProxyModel::sourceDataChanged(const QModelIndex& source_top_left, const QModelIndex& source_bottom_right) | ||||||
|                                           const QModelIndex &source_bottom_right) |  | ||||||
| { | { | ||||||
|     if(source_top_left.parent() != source_bottom_right.parent()) |     if (source_top_left.parent() != source_bottom_right.parent()) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|     // whole row is getting changed |     // whole row is getting changed | ||||||
| @@ -323,22 +286,20 @@ void VersionProxyModel::sourceDataChanged(const QModelIndex &source_top_left, | |||||||
|     emit dataChanged(topLeft, bottomRight); |     emit dataChanged(topLeft, bottomRight); | ||||||
| } | } | ||||||
|  |  | ||||||
| void VersionProxyModel::setSourceModel(QAbstractItemModel *replacingRaw) | void VersionProxyModel::setSourceModel(QAbstractItemModel* replacingRaw) | ||||||
| { | { | ||||||
|     auto replacing = dynamic_cast<BaseVersionList *>(replacingRaw); |     auto replacing = dynamic_cast<BaseVersionList*>(replacingRaw); | ||||||
|     beginResetModel(); |     beginResetModel(); | ||||||
|  |  | ||||||
|     m_columns.clear(); |     m_columns.clear(); | ||||||
|     if(!replacing) |     if (!replacing) { | ||||||
|     { |  | ||||||
|         roles.clear(); |         roles.clear(); | ||||||
|         filterModel->setSourceModel(replacing); |         filterModel->setSourceModel(replacing); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     roles = replacing->providesRoles(); |     roles = replacing->providesRoles(); | ||||||
|     if(roles.contains(BaseVersionList::VersionRole)) |     if (roles.contains(BaseVersionList::VersionRole)) { | ||||||
|     { |  | ||||||
|         m_columns.push_back(Name); |         m_columns.push_back(Name); | ||||||
|     } |     } | ||||||
|     /* |     /* | ||||||
| @@ -347,32 +308,25 @@ void VersionProxyModel::setSourceModel(QAbstractItemModel *replacingRaw) | |||||||
|         m_columns.push_back(ParentVersion); |         m_columns.push_back(ParentVersion); | ||||||
|     } |     } | ||||||
|     */ |     */ | ||||||
|     if(roles.contains(BaseVersionList::ArchitectureRole)) |     if (roles.contains(BaseVersionList::ArchitectureRole)) { | ||||||
|     { |  | ||||||
|         m_columns.push_back(Architecture); |         m_columns.push_back(Architecture); | ||||||
|     } |     } | ||||||
|     if(roles.contains(BaseVersionList::PathRole)) |     if (roles.contains(BaseVersionList::PathRole)) { | ||||||
|     { |  | ||||||
|         m_columns.push_back(Path); |         m_columns.push_back(Path); | ||||||
|     } |     } | ||||||
|     if(roles.contains(Meta::VersionList::TimeRole)) |     if (roles.contains(Meta::VersionList::TimeRole)) { | ||||||
|     { |  | ||||||
|         m_columns.push_back(Time); |         m_columns.push_back(Time); | ||||||
|     } |     } | ||||||
|     if(roles.contains(BaseVersionList::BranchRole)) |     if (roles.contains(BaseVersionList::BranchRole)) { | ||||||
|     { |  | ||||||
|         m_columns.push_back(Branch); |         m_columns.push_back(Branch); | ||||||
|     } |     } | ||||||
|     if(roles.contains(BaseVersionList::TypeRole)) |     if (roles.contains(BaseVersionList::TypeRole)) { | ||||||
|     { |  | ||||||
|         m_columns.push_back(Type); |         m_columns.push_back(Type); | ||||||
|     } |     } | ||||||
|     if(roles.contains(BaseVersionList::RecommendedRole)) |     if (roles.contains(BaseVersionList::RecommendedRole)) { | ||||||
|     { |  | ||||||
|         hasRecommended = true; |         hasRecommended = true; | ||||||
|     } |     } | ||||||
|     if(roles.contains(BaseVersionList::LatestRole)) |     if (roles.contains(BaseVersionList::LatestRole)) { | ||||||
|     { |  | ||||||
|         hasLatest = true; |         hasLatest = true; | ||||||
|     } |     } | ||||||
|     filterModel->setSourceModel(replacing); |     filterModel->setSourceModel(replacing); | ||||||
| @@ -382,16 +336,13 @@ void VersionProxyModel::setSourceModel(QAbstractItemModel *replacingRaw) | |||||||
|  |  | ||||||
| QModelIndex VersionProxyModel::getRecommended() const | QModelIndex VersionProxyModel::getRecommended() const | ||||||
| { | { | ||||||
|     if(!roles.contains(BaseVersionList::RecommendedRole)) |     if (!roles.contains(BaseVersionList::RecommendedRole)) { | ||||||
|     { |  | ||||||
|         return index(0, 0); |         return index(0, 0); | ||||||
|     } |     } | ||||||
|     int recommended = 0; |     int recommended = 0; | ||||||
|     for (int i = 0; i < rowCount(); i++) |     for (int i = 0; i < rowCount(); i++) { | ||||||
|     { |  | ||||||
|         auto value = sourceModel()->data(mapToSource(index(i, 0)), BaseVersionList::RecommendedRole); |         auto value = sourceModel()->data(mapToSource(index(i, 0)), BaseVersionList::RecommendedRole); | ||||||
|         if (value.toBool()) |         if (value.toBool()) { | ||||||
|         { |  | ||||||
|             recommended = i; |             recommended = i; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -401,16 +352,13 @@ QModelIndex VersionProxyModel::getRecommended() const | |||||||
| QModelIndex VersionProxyModel::getVersion(const QString& version) const | QModelIndex VersionProxyModel::getVersion(const QString& version) const | ||||||
| { | { | ||||||
|     int found = -1; |     int found = -1; | ||||||
|     for (int i = 0; i < rowCount(); i++) |     for (int i = 0; i < rowCount(); i++) { | ||||||
|     { |  | ||||||
|         auto value = sourceModel()->data(mapToSource(index(i, 0)), BaseVersionList::VersionRole); |         auto value = sourceModel()->data(mapToSource(index(i, 0)), BaseVersionList::VersionRole); | ||||||
|         if (value.toString() == version) |         if (value.toString() == version) { | ||||||
|         { |  | ||||||
|             found = i; |             found = i; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     if(found == -1) |     if (found == -1) { | ||||||
|     { |  | ||||||
|         return QModelIndex(); |         return QModelIndex(); | ||||||
|     } |     } | ||||||
|     return index(found, 0); |     return index(found, 0); | ||||||
| @@ -423,23 +371,24 @@ void VersionProxyModel::clearFilters() | |||||||
|     filterModel->filterChanged(); |     filterModel->filterChanged(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void VersionProxyModel::setFilter(const BaseVersionList::ModelRoles column, Filter * f) | void VersionProxyModel::setFilter(const BaseVersionList::ModelRoles column, Filter* f) | ||||||
| { | { | ||||||
|     m_filters[column].reset(f); |     m_filters[column].reset(f); | ||||||
|     filterModel->filterChanged(); |     filterModel->filterChanged(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void VersionProxyModel::setSearch(const QString &search) { | void VersionProxyModel::setSearch(const QString& search) | ||||||
|  | { | ||||||
|     m_search = search; |     m_search = search; | ||||||
|     filterModel->filterChanged(); |     filterModel->filterChanged(); | ||||||
| } | } | ||||||
|  |  | ||||||
| const VersionProxyModel::FilterMap &VersionProxyModel::filters() const | const VersionProxyModel::FilterMap& VersionProxyModel::filters() const | ||||||
| { | { | ||||||
|     return m_filters; |     return m_filters; | ||||||
| } | } | ||||||
|  |  | ||||||
| const QString &VersionProxyModel::search() const | const QString& VersionProxyModel::search() const | ||||||
| { | { | ||||||
|     return m_search; |     return m_search; | ||||||
| } | } | ||||||
| @@ -459,7 +408,9 @@ void VersionProxyModel::sourceRowsAboutToBeInserted(const QModelIndex& parent, i | |||||||
|     beginInsertRows(parent, first, last); |     beginInsertRows(parent, first, last); | ||||||
| } | } | ||||||
|  |  | ||||||
| void VersionProxyModel::sourceRowsInserted(const QModelIndex& parent, int first, int last) | void VersionProxyModel::sourceRowsInserted([[maybe_unused]] const QModelIndex& parent, | ||||||
|  |                                            [[maybe_unused]] int first, | ||||||
|  |                                            [[maybe_unused]] int last) | ||||||
| { | { | ||||||
|     endInsertRows(); |     endInsertRows(); | ||||||
| } | } | ||||||
| @@ -469,12 +420,12 @@ void VersionProxyModel::sourceRowsAboutToBeRemoved(const QModelIndex& parent, in | |||||||
|     beginRemoveRows(parent, first, last); |     beginRemoveRows(parent, first, last); | ||||||
| } | } | ||||||
|  |  | ||||||
| void VersionProxyModel::sourceRowsRemoved(const QModelIndex& parent, int first, int last) | void VersionProxyModel::sourceRowsRemoved([[maybe_unused]] const QModelIndex& parent, [[maybe_unused]] int first, [[maybe_unused]] int last) | ||||||
| { | { | ||||||
|     endRemoveRows(); |     endRemoveRows(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void VersionProxyModel::setCurrentVersion(const QString &version) | void VersionProxyModel::setCurrentVersion(const QString& version) | ||||||
| { | { | ||||||
|     m_currentVersion = version; |     m_currentVersion = version; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -6,64 +6,53 @@ | |||||||
|  |  | ||||||
| class VersionFilterModel; | class VersionFilterModel; | ||||||
|  |  | ||||||
| class VersionProxyModel: public QAbstractProxyModel | class VersionProxyModel : public QAbstractProxyModel { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| public: |    public: | ||||||
|  |     enum Column { Name, ParentVersion, Branch, Type, Architecture, Path, Time }; | ||||||
|     enum Column |  | ||||||
|     { |  | ||||||
|         Name, |  | ||||||
|         ParentVersion, |  | ||||||
|         Branch, |  | ||||||
|         Type, |  | ||||||
|         Architecture, |  | ||||||
|         Path, |  | ||||||
|         Time |  | ||||||
|     }; |  | ||||||
|     typedef QHash<BaseVersionList::ModelRoles, std::shared_ptr<Filter>> FilterMap; |     typedef QHash<BaseVersionList::ModelRoles, std::shared_ptr<Filter>> FilterMap; | ||||||
|  |  | ||||||
| public: |    public: | ||||||
|     VersionProxyModel ( QObject* parent = 0 ); |     VersionProxyModel(QObject* parent = 0); | ||||||
|     virtual ~VersionProxyModel() {}; |     virtual ~VersionProxyModel(){}; | ||||||
|  |  | ||||||
|     virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override; |     virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override; | ||||||
|     virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; |     virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; | ||||||
|     virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override; |     virtual QModelIndex mapFromSource(const QModelIndex& sourceIndex) const override; | ||||||
|     virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const override; |     virtual QModelIndex mapToSource(const QModelIndex& proxyIndex) const override; | ||||||
|     virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; |     virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; | ||||||
|     virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; |     virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; | ||||||
|     virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; |     virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; | ||||||
|     virtual QModelIndex parent(const QModelIndex &child) const override; |     virtual QModelIndex parent(const QModelIndex& child) const override; | ||||||
|     virtual void setSourceModel(QAbstractItemModel *sourceModel) override; |     virtual void setSourceModel(QAbstractItemModel* sourceModel) override; | ||||||
|  |  | ||||||
|     const FilterMap &filters() const; |     const FilterMap& filters() const; | ||||||
|     const QString &search() const; |     const QString& search() const; | ||||||
|     void setFilter(const BaseVersionList::ModelRoles column, Filter * filter); |     void setFilter(const BaseVersionList::ModelRoles column, Filter* filter); | ||||||
|     void setSearch(const QString &search); |     void setSearch(const QString& search); | ||||||
|     void clearFilters(); |     void clearFilters(); | ||||||
|     QModelIndex getRecommended() const; |     QModelIndex getRecommended() const; | ||||||
|     QModelIndex getVersion(const QString & version) const; |     QModelIndex getVersion(const QString& version) const; | ||||||
|     void setCurrentVersion(const QString &version); |     void setCurrentVersion(const QString& version); | ||||||
| private slots: |    private slots: | ||||||
|  |  | ||||||
|     void sourceDataChanged(const QModelIndex &source_top_left,const QModelIndex &source_bottom_right); |     void sourceDataChanged(const QModelIndex& source_top_left, const QModelIndex& source_bottom_right); | ||||||
|  |  | ||||||
|     void sourceAboutToBeReset(); |     void sourceAboutToBeReset(); | ||||||
|     void sourceReset(); |     void sourceReset(); | ||||||
|  |  | ||||||
|     void sourceRowsAboutToBeInserted(const QModelIndex &parent, int first, int last); |     void sourceRowsAboutToBeInserted(const QModelIndex& parent, int first, int last); | ||||||
|     void sourceRowsInserted(const QModelIndex &parent, int first, int last); |     void sourceRowsInserted(const QModelIndex& parent, int first, int last); | ||||||
|  |  | ||||||
|     void sourceRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last); |     void sourceRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last); | ||||||
|     void sourceRowsRemoved(const QModelIndex &parent, int first, int last); |     void sourceRowsRemoved(const QModelIndex& parent, int first, int last); | ||||||
|  |  | ||||||
| private: |    private: | ||||||
|     QList<Column> m_columns; |     QList<Column> m_columns; | ||||||
|     FilterMap m_filters; |     FilterMap m_filters; | ||||||
|     QString m_search; |     QString m_search; | ||||||
|     BaseVersionList::RoleList roles; |     BaseVersionList::RoleList roles; | ||||||
|     VersionFilterModel * filterModel; |     VersionFilterModel* filterModel; | ||||||
|     bool hasRecommended = false; |     bool hasRecommended = false; | ||||||
|     bool hasLatest = false; |     bool hasLatest = false; | ||||||
|     QString m_currentVersion; |     QString m_currentVersion; | ||||||
|   | |||||||
| @@ -1,20 +1,15 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <QString> |  | ||||||
| #include <QFileSystemWatcher> | #include <QFileSystemWatcher> | ||||||
|  | #include <QString> | ||||||
|  |  | ||||||
| struct WatchLock | struct WatchLock { | ||||||
| { |     WatchLock(QFileSystemWatcher* watcher, const QString& directory) : m_watcher(watcher), m_directory(directory) | ||||||
|     WatchLock(QFileSystemWatcher * watcher, const QString& directory) |  | ||||||
|         : m_watcher(watcher), m_directory(directory) |  | ||||||
|     { |     { | ||||||
|         m_watcher->removePath(m_directory); |         m_watcher->removePath(m_directory); | ||||||
|     } |     } | ||||||
|     ~WatchLock() |     ~WatchLock() { m_watcher->addPath(m_directory); } | ||||||
|     { |     QFileSystemWatcher* m_watcher; | ||||||
|         m_watcher->addPath(m_directory); |  | ||||||
|     } |  | ||||||
|     QFileSystemWatcher * m_watcher; |  | ||||||
|     QString m_directory; |     QString m_directory; | ||||||
| }; | }; | ||||||
|   | |||||||
							
								
								
									
										128
									
								
								launcher/WindowsConsole.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								launcher/WindowsConsole.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,128 @@ | |||||||
|  | /* | ||||||
|  |  *  Prism Launcher - Minecraft Launcher | ||||||
|  |  *  Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> | ||||||
|  |  * | ||||||
|  |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation, version 3. | ||||||
|  |  * | ||||||
|  |  *  This program is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef WIN32_LEAN_AND_MEAN | ||||||
|  | #define WIN32_LEAN_AND_MEAN | ||||||
|  | #endif | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <io.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <windows.h> | ||||||
|  | #include <iostream> | ||||||
|  |  | ||||||
|  | void RedirectHandle(DWORD handle, FILE* stream, const char* mode) | ||||||
|  | { | ||||||
|  |     HANDLE stdHandle = GetStdHandle(handle); | ||||||
|  |     if (stdHandle != INVALID_HANDLE_VALUE) { | ||||||
|  |         int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); | ||||||
|  |         if (fileDescriptor != -1) { | ||||||
|  |             FILE* file = _fdopen(fileDescriptor, mode); | ||||||
|  |             if (file != NULL) { | ||||||
|  |                 int dup2Result = _dup2(_fileno(file), _fileno(stream)); | ||||||
|  |                 if (dup2Result == 0) { | ||||||
|  |                     setvbuf(stream, NULL, _IONBF, 0); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // taken from https://stackoverflow.com/a/25927081 | ||||||
|  | // getting a proper output to console with redirection support on windows is apparently hell | ||||||
|  | void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr) | ||||||
|  | { | ||||||
|  |     // Re-initialize the C runtime "FILE" handles with clean handles bound to "nul". We do this because it has been | ||||||
|  |     // observed that the file number of our standard handle file objects can be assigned internally to a value of -2 | ||||||
|  |     // when not bound to a valid target, which represents some kind of unknown internal invalid state. In this state our | ||||||
|  |     // call to "_dup2" fails, as it specifically tests to ensure that the target file number isn't equal to this value | ||||||
|  |     // before allowing the operation to continue. We can resolve this issue by first "re-opening" the target files to | ||||||
|  |     // use the "nul" device, which will place them into a valid state, after which we can redirect them to our target | ||||||
|  |     // using the "_dup2" function. | ||||||
|  |     if (bindStdIn) { | ||||||
|  |         FILE* dummyFile; | ||||||
|  |         freopen_s(&dummyFile, "nul", "r", stdin); | ||||||
|  |     } | ||||||
|  |     if (bindStdOut) { | ||||||
|  |         FILE* dummyFile; | ||||||
|  |         freopen_s(&dummyFile, "nul", "w", stdout); | ||||||
|  |     } | ||||||
|  |     if (bindStdErr) { | ||||||
|  |         FILE* dummyFile; | ||||||
|  |         freopen_s(&dummyFile, "nul", "w", stderr); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Redirect unbuffered stdin from the current standard input handle | ||||||
|  |     if (bindStdIn) { | ||||||
|  |         RedirectHandle(STD_INPUT_HANDLE, stdin, "r"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Redirect unbuffered stdout to the current standard output handle | ||||||
|  |     if (bindStdOut) { | ||||||
|  |         RedirectHandle(STD_OUTPUT_HANDLE, stdout, "w"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Redirect unbuffered stderr to the current standard error handle | ||||||
|  |     if (bindStdErr) { | ||||||
|  |         RedirectHandle(STD_ERROR_HANDLE, stderr, "w"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Clear the error state for each of the C++ standard stream objects. We need to do this, as attempts to access the | ||||||
|  |     // standard streams before they refer to a valid target will cause the iostream objects to enter an error state. In | ||||||
|  |     // versions of Visual Studio after 2005, this seems to always occur during startup regardless of whether anything | ||||||
|  |     // has been read from or written to the targets or not. | ||||||
|  |     if (bindStdIn) { | ||||||
|  |         std::wcin.clear(); | ||||||
|  |         std::cin.clear(); | ||||||
|  |     } | ||||||
|  |     if (bindStdOut) { | ||||||
|  |         std::wcout.clear(); | ||||||
|  |         std::cout.clear(); | ||||||
|  |     } | ||||||
|  |     if (bindStdErr) { | ||||||
|  |         std::wcerr.clear(); | ||||||
|  |         std::cerr.clear(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool AttachWindowsConsole() | ||||||
|  | { | ||||||
|  |     auto stdinType = GetFileType(GetStdHandle(STD_INPUT_HANDLE)); | ||||||
|  |     auto stdoutType = GetFileType(GetStdHandle(STD_OUTPUT_HANDLE)); | ||||||
|  |     auto stderrType = GetFileType(GetStdHandle(STD_ERROR_HANDLE)); | ||||||
|  |  | ||||||
|  |     bool bindStdIn = false; | ||||||
|  |     bool bindStdOut = false; | ||||||
|  |     bool bindStdErr = false; | ||||||
|  |  | ||||||
|  |     if (stdinType == FILE_TYPE_CHAR || stdinType == FILE_TYPE_UNKNOWN) { | ||||||
|  |         bindStdIn = true; | ||||||
|  |     } | ||||||
|  |     if (stdoutType == FILE_TYPE_CHAR || stdoutType == FILE_TYPE_UNKNOWN) { | ||||||
|  |         bindStdOut = true; | ||||||
|  |     } | ||||||
|  |     if (stderrType == FILE_TYPE_CHAR || stderrType == FILE_TYPE_UNKNOWN) { | ||||||
|  |         bindStdErr = true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (AttachConsole(ATTACH_PARENT_PROCESS)) { | ||||||
|  |         BindCrtHandlesToStdHandles(bindStdIn, bindStdOut, bindStdErr); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return false; | ||||||
|  | } | ||||||
							
								
								
									
										25
									
								
								launcher/WindowsConsole.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								launcher/WindowsConsole.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | // | ||||||
|  | // SPDX-License-Identifier: GPL-3.0-only | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  *  Prism Launcher - Minecraft Launcher | ||||||
|  |  *  Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> | ||||||
|  |  * | ||||||
|  |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation, version 3. | ||||||
|  |  * | ||||||
|  |  *  This program is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr); | ||||||
|  | bool AttachWindowsConsole(); | ||||||
| @@ -37,11 +37,7 @@ | |||||||
| #include <sys.h> | #include <sys.h> | ||||||
|  |  | ||||||
| #if defined Q_OS_WIN32 | #if defined Q_OS_WIN32 | ||||||
| #ifndef WIN32_LEAN_AND_MEAN | #include "WindowsConsole.h" | ||||||
| #define WIN32_LEAN_AND_MEAN |  | ||||||
| #endif |  | ||||||
| #include <stdio.h> |  | ||||||
| #include <windows.h> |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| // Snippet from https://github.com/gulrak/filesystem#using-it-as-single-file-header | // Snippet from https://github.com/gulrak/filesystem#using-it-as-single-file-header | ||||||
| @@ -67,21 +63,7 @@ FileLinkApp::FileLinkApp(int& argc, char** argv) : QCoreApplication(argc, argv), | |||||||
| { | { | ||||||
| #if defined Q_OS_WIN32 | #if defined Q_OS_WIN32 | ||||||
|     // attach the parent console |     // attach the parent console | ||||||
|     if (AttachConsole(ATTACH_PARENT_PROCESS)) { |     if (AttachWindowsConsole()) { | ||||||
|         // if attach succeeds, reopen and sync all the i/o |  | ||||||
|         if (freopen("CON", "w", stdout)) { |  | ||||||
|             std::cout.sync_with_stdio(); |  | ||||||
|         } |  | ||||||
|         if (freopen("CON", "w", stderr)) { |  | ||||||
|             std::cerr.sync_with_stdio(); |  | ||||||
|         } |  | ||||||
|         if (freopen("CON", "r", stdin)) { |  | ||||||
|             std::cin.sync_with_stdio(); |  | ||||||
|         } |  | ||||||
|         auto out = GetStdHandle(STD_OUTPUT_HANDLE); |  | ||||||
|         DWORD written; |  | ||||||
|         const char* endline = "\n"; |  | ||||||
|         WriteConsole(out, endline, strlen(endline), &written, NULL); |  | ||||||
|         consoleAttached = true; |         consoleAttached = true; | ||||||
|     } |     } | ||||||
| #endif | #endif | ||||||
| @@ -188,7 +170,7 @@ void FileLinkApp::runLink() | |||||||
|             FS::LinkResult result = { src_path, dst_path, QString::fromStdString(os_err.message()), os_err.value() }; |             FS::LinkResult result = { src_path, dst_path, QString::fromStdString(os_err.message()), os_err.value() }; | ||||||
|             m_path_results.append(result); |             m_path_results.append(result); | ||||||
|         } else { |         } else { | ||||||
|             FS::LinkResult result = { src_path, dst_path }; |             FS::LinkResult result = { src_path, dst_path, "", 0 }; | ||||||
|             m_path_results.append(result); |             m_path_results.append(result); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -248,7 +230,7 @@ void FileLinkApp::readPathPairs() | |||||||
|     in >> numLinks; |     in >> numLinks; | ||||||
|     qDebug() << "numLinks" << numLinks; |     qDebug() << "numLinks" << numLinks; | ||||||
|  |  | ||||||
|     for (int i = 0; i < numLinks; i++) { |     for (quint32 i = 0; i < numLinks; i++) { | ||||||
|         FS::LinkPair pair; |         FS::LinkPair pair; | ||||||
|         in >> pair.src; |         in >> pair.src; | ||||||
|         in >> pair.dst; |         in >> pair.dst; | ||||||
| @@ -271,7 +253,6 @@ FileLinkApp::~FileLinkApp() | |||||||
|         fclose(stdout); |         fclose(stdout); | ||||||
|         fclose(stdin); |         fclose(stdin); | ||||||
|         fclose(stderr); |         fclose(stderr); | ||||||
|         FreeConsole(); |  | ||||||
|     } |     } | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,7 +1,8 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  PolyMC - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  |  *  Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  *  it under the terms of the GNU General Public License as published by |  *  it under the terms of the GNU General Public License as published by | ||||||
| @@ -35,32 +36,30 @@ | |||||||
|  |  | ||||||
| #include "IconList.h" | #include "IconList.h" | ||||||
| #include <FileSystem.h> | #include <FileSystem.h> | ||||||
| #include <QMap> |  | ||||||
| #include <QEventLoop> |  | ||||||
| #include <QMimeData> |  | ||||||
| #include <QUrl> |  | ||||||
| #include <QFileSystemWatcher> |  | ||||||
| #include <QSet> |  | ||||||
| #include <QDebug> | #include <QDebug> | ||||||
|  | #include <QEventLoop> | ||||||
|  | #include <QFileSystemWatcher> | ||||||
|  | #include <QMap> | ||||||
|  | #include <QMimeData> | ||||||
|  | #include <QSet> | ||||||
|  | #include <QUrl> | ||||||
|  | #include "icons/IconUtils.h" | ||||||
|  |  | ||||||
| #define MAX_SIZE 1024 | #define MAX_SIZE 1024 | ||||||
|  |  | ||||||
| IconList::IconList(const QStringList &builtinPaths, QString path, QObject *parent) : QAbstractListModel(parent) | IconList::IconList(const QStringList& builtinPaths, QString path, QObject* parent) : QAbstractListModel(parent) | ||||||
| { | { | ||||||
|     QSet<QString> builtinNames; |     QSet<QString> builtinNames; | ||||||
|  |  | ||||||
|     // add builtin icons |     // add builtin icons | ||||||
|     for(auto & builtinPath: builtinPaths) |     for (auto& builtinPath : builtinPaths) { | ||||||
|     { |  | ||||||
|         QDir instance_icons(builtinPath); |         QDir instance_icons(builtinPath); | ||||||
|         auto file_info_list = instance_icons.entryInfoList(QDir::Files, QDir::Name); |         auto file_info_list = instance_icons.entryInfoList(QDir::Files, QDir::Name); | ||||||
|         for (auto file_info : file_info_list) |         for (auto file_info : file_info_list) { | ||||||
|         { |  | ||||||
|             builtinNames.insert(file_info.completeBaseName()); |             builtinNames.insert(file_info.completeBaseName()); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     for(auto & builtinName : builtinNames) |     for (auto& builtinName : builtinNames) { | ||||||
|     { |  | ||||||
|         addThemeIcon(builtinName); |         addThemeIcon(builtinName); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -78,31 +77,27 @@ IconList::IconList(const QStringList &builtinPaths, QString path, QObject *paren | |||||||
| void IconList::sortIconList() | void IconList::sortIconList() | ||||||
| { | { | ||||||
|     qDebug() << "Sorting icon list..."; |     qDebug() << "Sorting icon list..."; | ||||||
|     std::sort(icons.begin(), icons.end(), [](const MMCIcon& a, const MMCIcon& b) { |     std::sort(icons.begin(), icons.end(), [](const MMCIcon& a, const MMCIcon& b) { return a.m_key.localeAwareCompare(b.m_key) < 0; }); | ||||||
|         return a.m_key.localeAwareCompare(b.m_key) < 0; |  | ||||||
|     }); |  | ||||||
|     reindex(); |     reindex(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void IconList::directoryChanged(const QString &path) | void IconList::directoryChanged(const QString& path) | ||||||
| { | { | ||||||
|     QDir new_dir (path); |     QDir new_dir(path); | ||||||
|     if(m_dir.absolutePath() != new_dir.absolutePath()) |     if (m_dir.absolutePath() != new_dir.absolutePath()) { | ||||||
|     { |  | ||||||
|         m_dir.setPath(path); |         m_dir.setPath(path); | ||||||
|         m_dir.refresh(); |         m_dir.refresh(); | ||||||
|         if(is_watching) |         if (is_watching) | ||||||
|             stopWatching(); |             stopWatching(); | ||||||
|         startWatching(); |         startWatching(); | ||||||
|     } |     } | ||||||
|     if(!m_dir.exists()) |     if (!m_dir.exists()) | ||||||
|         if(!FS::ensureFolderPathExists(m_dir.absolutePath())) |         if (!FS::ensureFolderPathExists(m_dir.absolutePath())) | ||||||
|             return; |             return; | ||||||
|     m_dir.refresh(); |     m_dir.refresh(); | ||||||
|     auto new_list = m_dir.entryList(QDir::Files, QDir::Name); |     auto new_list = m_dir.entryList(QDir::Files, QDir::Name); | ||||||
|     for (auto it = new_list.begin(); it != new_list.end(); it++) |     for (auto it = new_list.begin(); it != new_list.end(); it++) { | ||||||
|     { |         QString& foo = (*it); | ||||||
|         QString &foo = (*it); |  | ||||||
|         foo = m_dir.filePath(foo); |         foo = m_dir.filePath(foo); | ||||||
|     } |     } | ||||||
| #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) | #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) | ||||||
| @@ -111,8 +106,7 @@ void IconList::directoryChanged(const QString &path) | |||||||
|     auto new_set = new_list.toSet(); |     auto new_set = new_list.toSet(); | ||||||
| #endif | #endif | ||||||
|     QList<QString> current_list; |     QList<QString> current_list; | ||||||
|     for (auto &it : icons) |     for (auto& it : icons) { | ||||||
|     { |  | ||||||
|         if (!it.has(IconType::FileBased)) |         if (!it.has(IconType::FileBased)) | ||||||
|             continue; |             continue; | ||||||
|         current_list.push_back(it.m_images[IconType::FileBased].filename); |         current_list.push_back(it.m_images[IconType::FileBased].filename); | ||||||
| @@ -129,38 +123,33 @@ void IconList::directoryChanged(const QString &path) | |||||||
|     QSet<QString> to_add = new_set; |     QSet<QString> to_add = new_set; | ||||||
|     to_add -= current_set; |     to_add -= current_set; | ||||||
|  |  | ||||||
|     for (auto remove : to_remove) |     for (auto remove : to_remove) { | ||||||
|     { |  | ||||||
|         qDebug() << "Removing " << remove; |         qDebug() << "Removing " << remove; | ||||||
|         QFileInfo rmfile(remove); |         QFileInfo rmfile(remove); | ||||||
|         QString key = rmfile.completeBaseName(); |         QString key = rmfile.completeBaseName(); | ||||||
|  |  | ||||||
|         QString suffix = rmfile.suffix(); |         QString suffix = rmfile.suffix(); | ||||||
|         // The icon doesnt have a suffix, but it can have other .s in the name, so we account for those as well |         // The icon doesnt have a suffix, but it can have other .s in the name, so we account for those as well | ||||||
|         if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico" && suffix != "svg" && suffix != "gif") |         if (!IconUtils::isIconSuffix(suffix)) | ||||||
|             key = rmfile.fileName(); |             key = rmfile.fileName(); | ||||||
|  |  | ||||||
|         int idx = getIconIndex(key); |         int idx = getIconIndex(key); | ||||||
|         if (idx == -1) |         if (idx == -1) | ||||||
|             continue; |             continue; | ||||||
|         icons[idx].remove(IconType::FileBased); |         icons[idx].remove(IconType::FileBased); | ||||||
|         if (icons[idx].type() == IconType::ToBeDeleted) |         if (icons[idx].type() == IconType::ToBeDeleted) { | ||||||
|         { |  | ||||||
|             beginRemoveRows(QModelIndex(), idx, idx); |             beginRemoveRows(QModelIndex(), idx, idx); | ||||||
|             icons.remove(idx); |             icons.remove(idx); | ||||||
|             reindex(); |             reindex(); | ||||||
|             endRemoveRows(); |             endRemoveRows(); | ||||||
|         } |         } else { | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             dataChanged(index(idx), index(idx)); |             dataChanged(index(idx), index(idx)); | ||||||
|         } |         } | ||||||
|         m_watcher->removePath(remove); |         m_watcher->removePath(remove); | ||||||
|         emit iconUpdated(key); |         emit iconUpdated(key); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     for (auto add : to_add) |     for (auto add : to_add) { | ||||||
|     { |  | ||||||
|         qDebug() << "Adding " << add; |         qDebug() << "Adding " << add; | ||||||
|  |  | ||||||
|         QFileInfo addfile(add); |         QFileInfo addfile(add); | ||||||
| @@ -168,11 +157,10 @@ void IconList::directoryChanged(const QString &path) | |||||||
|  |  | ||||||
|         QString suffix = addfile.suffix(); |         QString suffix = addfile.suffix(); | ||||||
|         // The icon doesnt have a suffix, but it can have other .s in the name, so we account for those as well |         // The icon doesnt have a suffix, but it can have other .s in the name, so we account for those as well | ||||||
|         if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico" && suffix != "svg" && suffix != "gif") |         if (!IconUtils::isIconSuffix(suffix)) | ||||||
|             key = addfile.fileName(); |             key = addfile.fileName(); | ||||||
|  |  | ||||||
|         if (addIcon(key, QString(), addfile.filePath(), IconType::FileBased)) |         if (addIcon(key, QString(), addfile.filePath(), IconType::FileBased)) { | ||||||
|         { |  | ||||||
|             m_watcher->addPath(add); |             m_watcher->addPath(add); | ||||||
|             emit iconUpdated(key); |             emit iconUpdated(key); | ||||||
|         } |         } | ||||||
| @@ -181,7 +169,7 @@ void IconList::directoryChanged(const QString &path) | |||||||
|     sortIconList(); |     sortIconList(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void IconList::fileChanged(const QString &path) | void IconList::fileChanged(const QString& path) | ||||||
| { | { | ||||||
|     qDebug() << "Checking " << path; |     qDebug() << "Checking " << path; | ||||||
|     QFileInfo checkfile(path); |     QFileInfo checkfile(path); | ||||||
| @@ -200,9 +188,9 @@ void IconList::fileChanged(const QString &path) | |||||||
|     emit iconUpdated(key); |     emit iconUpdated(key); | ||||||
| } | } | ||||||
|  |  | ||||||
| void IconList::SettingChanged(const Setting &setting, QVariant value) | void IconList::SettingChanged(const Setting& setting, QVariant value) | ||||||
| { | { | ||||||
|     if(setting.id() != "IconsDir") |     if (setting.id() != "IconsDir") | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|     directoryChanged(value.toString()); |     directoryChanged(value.toString()); | ||||||
| @@ -213,12 +201,9 @@ void IconList::startWatching() | |||||||
|     auto abs_path = m_dir.absolutePath(); |     auto abs_path = m_dir.absolutePath(); | ||||||
|     FS::ensureFolderPathExists(abs_path); |     FS::ensureFolderPathExists(abs_path); | ||||||
|     is_watching = m_watcher->addPath(abs_path); |     is_watching = m_watcher->addPath(abs_path); | ||||||
|     if (is_watching) |     if (is_watching) { | ||||||
|     { |  | ||||||
|         qDebug() << "Started watching " << abs_path; |         qDebug() << "Started watching " << abs_path; | ||||||
|     } |     } else { | ||||||
|     else |  | ||||||
|     { |  | ||||||
|         qDebug() << "Failed to start watching " << abs_path; |         qDebug() << "Failed to start watching " << abs_path; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -241,7 +226,11 @@ Qt::DropActions IconList::supportedDropActions() const | |||||||
|     return Qt::CopyAction; |     return Qt::CopyAction; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, [[maybe_unused]] int row, [[maybe_unused]] int column, [[maybe_unused]] const QModelIndex &parent) | bool IconList::dropMimeData(const QMimeData* data, | ||||||
|  |                             Qt::DropAction action, | ||||||
|  |                             [[maybe_unused]] int row, | ||||||
|  |                             [[maybe_unused]] int column, | ||||||
|  |                             [[maybe_unused]] const QModelIndex& parent) | ||||||
| { | { | ||||||
|     if (action == Qt::IgnoreAction) |     if (action == Qt::IgnoreAction) | ||||||
|         return true; |         return true; | ||||||
| @@ -250,12 +239,10 @@ bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, [[mayb | |||||||
|         return false; |         return false; | ||||||
|  |  | ||||||
|     // files dropped from outside? |     // files dropped from outside? | ||||||
|     if (data->hasUrls()) |     if (data->hasUrls()) { | ||||||
|     { |  | ||||||
|         auto urls = data->urls(); |         auto urls = data->urls(); | ||||||
|         QStringList iconFiles; |         QStringList iconFiles; | ||||||
|         for (auto url : urls) |         for (auto url : urls) { | ||||||
|         { |  | ||||||
|             // only local files may be dropped... |             // only local files may be dropped... | ||||||
|             if (!url.isLocalFile()) |             if (!url.isLocalFile()) | ||||||
|                 continue; |                 continue; | ||||||
| @@ -267,16 +254,13 @@ bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, [[mayb | |||||||
|     return false; |     return false; | ||||||
| } | } | ||||||
|  |  | ||||||
| Qt::ItemFlags IconList::flags(const QModelIndex &index) const | Qt::ItemFlags IconList::flags(const QModelIndex& index) const | ||||||
| { | { | ||||||
|     Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index); |     Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index); | ||||||
|     if (index.isValid()) |     return Qt::ItemIsDropEnabled | defaultFlags; | ||||||
|         return Qt::ItemIsDropEnabled | defaultFlags; |  | ||||||
|     else |  | ||||||
|         return Qt::ItemIsDropEnabled | defaultFlags; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| QVariant IconList::data(const QModelIndex &index, int role) const | QVariant IconList::data(const QModelIndex& index, int role) const | ||||||
| { | { | ||||||
|     if (!index.isValid()) |     if (!index.isValid()) | ||||||
|         return QVariant(); |         return QVariant(); | ||||||
| @@ -286,64 +270,49 @@ QVariant IconList::data(const QModelIndex &index, int role) const | |||||||
|     if (row < 0 || row >= icons.size()) |     if (row < 0 || row >= icons.size()) | ||||||
|         return QVariant(); |         return QVariant(); | ||||||
|  |  | ||||||
|     switch (role) |     switch (role) { | ||||||
|     { |         case Qt::DecorationRole: | ||||||
|     case Qt::DecorationRole: |             return icons[row].icon(); | ||||||
|         return icons[row].icon(); |         case Qt::DisplayRole: | ||||||
|     case Qt::DisplayRole: |             return icons[row].name(); | ||||||
|         return icons[row].name(); |         case Qt::UserRole: | ||||||
|     case Qt::UserRole: |             return icons[row].m_key; | ||||||
|         return icons[row].m_key; |         default: | ||||||
|     default: |             return QVariant(); | ||||||
|         return QVariant(); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| int IconList::rowCount(const QModelIndex &parent) const | int IconList::rowCount(const QModelIndex& parent) const | ||||||
| { | { | ||||||
|     return parent.isValid() ? 0 : icons.size(); |     return parent.isValid() ? 0 : icons.size(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void IconList::installIcons(const QStringList &iconFiles) | void IconList::installIcons(const QStringList& iconFiles) | ||||||
| { | { | ||||||
|     for (QString file : iconFiles) |     for (QString file : iconFiles) | ||||||
|     { |         installIcon(file, {}); | ||||||
|         QFileInfo fileinfo(file); |  | ||||||
|         if (!fileinfo.isReadable() || !fileinfo.isFile()) |  | ||||||
|             continue; |  | ||||||
|         QString target = FS::PathCombine(getDirectory(), fileinfo.fileName()); |  | ||||||
|  |  | ||||||
|         QString suffix = fileinfo.suffix(); |  | ||||||
|         if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico" && suffix != "svg" && suffix != "gif") |  | ||||||
|             continue; |  | ||||||
|  |  | ||||||
|         if (!QFile::copy(file, target)) |  | ||||||
|             continue; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void IconList::installIcon(const QString &file, const QString &name) | void IconList::installIcon(const QString& file, const QString& name) | ||||||
| { | { | ||||||
|     QFileInfo fileinfo(file); |     QFileInfo fileinfo(file); | ||||||
|     if(!fileinfo.isReadable() || !fileinfo.isFile()) |     if (!fileinfo.isReadable() || !fileinfo.isFile()) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|     QString target = FS::PathCombine(getDirectory(), name); |     if (!IconUtils::isIconSuffix(fileinfo.suffix())) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     QString target = FS::PathCombine(getDirectory(), name.isEmpty() ? fileinfo.fileName() : name); | ||||||
|     QFile::copy(file, target); |     QFile::copy(file, target); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool IconList::iconFileExists(const QString &key) const | bool IconList::iconFileExists(const QString& key) const | ||||||
| { | { | ||||||
|     auto iconEntry = icon(key); |     auto iconEntry = icon(key); | ||||||
|     if(!iconEntry) |     return iconEntry && iconEntry->has(IconType::FileBased); | ||||||
|     { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|     return iconEntry->has(IconType::FileBased); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| const MMCIcon *IconList::icon(const QString &key) const | const MMCIcon* IconList::icon(const QString& key) const | ||||||
| { | { | ||||||
|     int iconIdx = getIconIndex(key); |     int iconIdx = getIconIndex(key); | ||||||
|     if (iconIdx == -1) |     if (iconIdx == -1) | ||||||
| @@ -351,100 +320,84 @@ const MMCIcon *IconList::icon(const QString &key) const | |||||||
|     return &icons[iconIdx]; |     return &icons[iconIdx]; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool IconList::deleteIcon(const QString &key) | bool IconList::deleteIcon(const QString& key) | ||||||
| { | { | ||||||
|     if (!iconFileExists(key)) |     return iconFileExists(key) && QFile::remove(icon(key)->getFilePath()); | ||||||
|         return false; |  | ||||||
|  |  | ||||||
|     return QFile::remove(icon(key)->getFilePath()); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| bool IconList::trashIcon(const QString &key) | bool IconList::trashIcon(const QString& key) | ||||||
| { | { | ||||||
|     if (!iconFileExists(key)) |     return iconFileExists(key) && FS::trash(icon(key)->getFilePath(), nullptr); | ||||||
|         return false; |  | ||||||
|  |  | ||||||
|     return FS::trash(icon(key)->getFilePath(), nullptr); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| bool IconList::addThemeIcon(const QString& key) | bool IconList::addThemeIcon(const QString& key) | ||||||
| { | { | ||||||
|     auto iter = name_index.find(key); |     auto iter = name_index.find(key); | ||||||
|     if (iter != name_index.end()) |     if (iter != name_index.end()) { | ||||||
|     { |         auto& oldOne = icons[*iter]; | ||||||
|         auto &oldOne = icons[*iter]; |  | ||||||
|         oldOne.replace(Builtin, key); |         oldOne.replace(Builtin, key); | ||||||
|         dataChanged(index(*iter), index(*iter)); |         dataChanged(index(*iter), index(*iter)); | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|     else |     // add a new icon | ||||||
|  |     beginInsertRows(QModelIndex(), icons.size(), icons.size()); | ||||||
|     { |     { | ||||||
|         // add a new icon |         MMCIcon mmc_icon; | ||||||
|         beginInsertRows(QModelIndex(), icons.size(), icons.size()); |         mmc_icon.m_name = key; | ||||||
|         { |         mmc_icon.m_key = key; | ||||||
|             MMCIcon mmc_icon; |         mmc_icon.replace(Builtin, key); | ||||||
|             mmc_icon.m_name = key; |         icons.push_back(mmc_icon); | ||||||
|             mmc_icon.m_key = key; |         name_index[key] = icons.size() - 1; | ||||||
|             mmc_icon.replace(Builtin, key); |  | ||||||
|             icons.push_back(mmc_icon); |  | ||||||
|             name_index[key] = icons.size() - 1; |  | ||||||
|         } |  | ||||||
|         endInsertRows(); |  | ||||||
|         return true; |  | ||||||
|     } |     } | ||||||
|  |     endInsertRows(); | ||||||
|  |     return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool IconList::addIcon(const QString &key, const QString &name, const QString &path, const IconType type) | bool IconList::addIcon(const QString& key, const QString& name, const QString& path, const IconType type) | ||||||
| { | { | ||||||
|     // replace the icon even? is the input valid? |     // replace the icon even? is the input valid? | ||||||
|     QIcon icon(path); |     QIcon icon(path); | ||||||
|     if (icon.isNull()) |     if (icon.isNull()) | ||||||
|         return false; |         return false; | ||||||
|     auto iter = name_index.find(key); |     auto iter = name_index.find(key); | ||||||
|     if (iter != name_index.end()) |     if (iter != name_index.end()) { | ||||||
|     { |         auto& oldOne = icons[*iter]; | ||||||
|         auto &oldOne = icons[*iter]; |  | ||||||
|         oldOne.replace(type, icon, path); |         oldOne.replace(type, icon, path); | ||||||
|         dataChanged(index(*iter), index(*iter)); |         dataChanged(index(*iter), index(*iter)); | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|     else |     // add a new icon | ||||||
|  |     beginInsertRows(QModelIndex(), icons.size(), icons.size()); | ||||||
|     { |     { | ||||||
|         // add a new icon |         MMCIcon mmc_icon; | ||||||
|         beginInsertRows(QModelIndex(), icons.size(), icons.size()); |         mmc_icon.m_name = name; | ||||||
|         { |         mmc_icon.m_key = key; | ||||||
|             MMCIcon mmc_icon; |         mmc_icon.replace(type, icon, path); | ||||||
|             mmc_icon.m_name = name; |         icons.push_back(mmc_icon); | ||||||
|             mmc_icon.m_key = key; |         name_index[key] = icons.size() - 1; | ||||||
|             mmc_icon.replace(type, icon, path); |  | ||||||
|             icons.push_back(mmc_icon); |  | ||||||
|             name_index[key] = icons.size() - 1; |  | ||||||
|         } |  | ||||||
|         endInsertRows(); |  | ||||||
|         return true; |  | ||||||
|     } |     } | ||||||
|  |     endInsertRows(); | ||||||
|  |     return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| void IconList::saveIcon(const QString &key, const QString &path, const char * format) const | void IconList::saveIcon(const QString& key, const QString& path, const char* format) const | ||||||
| { | { | ||||||
|     auto icon = getIcon(key); |     auto icon = getIcon(key); | ||||||
|     auto pixmap = icon.pixmap(128, 128); |     auto pixmap = icon.pixmap(128, 128); | ||||||
|     pixmap.save(path, format); |     pixmap.save(path, format); | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| void IconList::reindex() | void IconList::reindex() | ||||||
| { | { | ||||||
|     name_index.clear(); |     name_index.clear(); | ||||||
|     int i = 0; |     int i = 0; | ||||||
|     for (auto &iter : icons) |     for (auto& iter : icons) { | ||||||
|     { |  | ||||||
|         name_index[iter.m_key] = i; |         name_index[iter.m_key] = i; | ||||||
|         i++; |         i++; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| QIcon IconList::getIcon(const QString &key) const | QIcon IconList::getIcon(const QString& key) const | ||||||
| { | { | ||||||
|     int icon_index = getIconIndex(key); |     int icon_index = getIconIndex(key); | ||||||
|  |  | ||||||
| @@ -459,7 +412,7 @@ QIcon IconList::getIcon(const QString &key) const | |||||||
|     return QIcon(); |     return QIcon(); | ||||||
| } | } | ||||||
|  |  | ||||||
| int IconList::getIconIndex(const QString &key) const | int IconList::getIconIndex(const QString& key) const | ||||||
| { | { | ||||||
|     auto iter = name_index.find(key == "default" ? "grass" : key); |     auto iter = name_index.find(key == "default" ? "grass" : key); | ||||||
|     if (iter != name_index.end()) |     if (iter != name_index.end()) | ||||||
| @@ -472,5 +425,3 @@ QString IconList::getDirectory() const | |||||||
| { | { | ||||||
|     return m_dir.absolutePath(); |     return m_dir.absolutePath(); | ||||||
| } | } | ||||||
|  |  | ||||||
| //#include "IconList.moc" |  | ||||||
|   | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user
	 Trial97
					Trial97