diff --git a/.github/actions/package/linux/action.yml b/.github/actions/package/linux/action.yml
index 4aebcf39d..74e1c2a14 100644
--- a/.github/actions/package/linux/action.yml
+++ b/.github/actions/package/linux/action.yml
@@ -13,10 +13,6 @@ inputs:
description: Name of the uploaded artifact
required: true
default: Linux
- cmake-preset:
- description: Base CMake preset previously used for the build
- required: true
- default: linux
qt-version:
description: Version of Qt to use
required: true
@@ -62,7 +58,7 @@ runs:
GPG_PRIVATE_KEY: ${{ inputs.gpg-private-key }}
run: |
- cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr
+ cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --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
export "NO_APPSTREAM=1" # we have to skip appstream checking because appstream on ubuntu 20.04 is outdated
@@ -107,12 +103,10 @@ runs:
env:
BUILD_DIR: build
- CMAKE_PRESET: ${{ inputs.cmake-preset }}
-
INSTALL_PORTABLE_DIR: install-portable
run: |
- cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }}
- cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
+ cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_PORTABLE_DIR }}
+ cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt
cd ${{ env.INSTALL_PORTABLE_DIR }}
diff --git a/.github/actions/package/macos/action.yml b/.github/actions/package/macos/action.yml
index cecd9d797..e85ffa07c 100644
--- a/.github/actions/package/macos/action.yml
+++ b/.github/actions/package/macos/action.yml
@@ -59,7 +59,7 @@ runs:
BUILD_DIR: build
INSTALL_DIR: install
run: |
- cmake --install ${{ env.BUILD_DIR }}
+ cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }}
cd ${{ env.INSTALL_DIR }}
chmod +x "PrismLauncher.app/Contents/MacOS/prismlauncher"
diff --git a/.github/actions/package/windows/action.yml b/.github/actions/package/windows/action.yml
index 39bb0e018..09d39f121 100644
--- a/.github/actions/package/windows/action.yml
+++ b/.github/actions/package/windows/action.yml
@@ -15,12 +15,15 @@ inputs:
msystem:
description: MSYS2 subsystem to use
required: false
- windows-codesign-cert:
- description: Certificate for signing Windows builds
- required: false
- windows-codesign-password:
- description: Password for signing Windows builds
- required: false
+ azure-client-id:
+ description: Client ID for the Azure Signer Application
+ required: true
+ azure-tenant-id:
+ description: Tenant ID for the Azure Signer Application
+ required: true
+ azure-subscription-id:
+ description: Subscription ID for the Azure Signer Application
+ required: true
runs:
using: composite
@@ -33,7 +36,7 @@ runs:
BUILD_DIR: build
INSTALL_DIR: install
run: |
- cmake --install ${{ env.BUILD_DIR }}
+ cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }}
touch ${{ env.INSTALL_DIR }}/manifest.txt
for l in $(find ${{ env.INSTALL_DIR }} -type f); do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}/}; l=${l#./}; echo $l; done >> ${{ env.INSTALL_DIR }}/manifest.txt
@@ -50,23 +53,45 @@ runs:
Get-ChildItem ${{ env.INSTALL_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
- - name: Fetch codesign certificate
- shell: bash # yes, we are not using MSYS2 or PowerShell here
- run: |
- echo '${{ inputs.windows-codesign-cert }}' | base64 --decode > codesign.pfx
-
- - name: Sign executable
+ - name: Emit warning for unsigned builds
+ if: ${{ github.ref_name != 'develop' || inputs.azure-client-id == '' }}
shell: pwsh
- env:
- INSTALL_DIR: install
run: |
- if (Get-Content ./codesign.pfx){
- cd ${{ env.INSTALL_DIR }}
- # We ship the exact same executable for portable and non-portable editions, so signing just once is fine
- SignTool sign /fd sha256 /td sha256 /f ../codesign.pfx /p '${{ inputs.windows-codesign-password }}' /tr http://timestamp.digicert.com prismlauncher.exe prismlauncher_updater.exe prismlauncher_filelink.exe
- } else {
- ":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
- }
+ ":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
+
+ - name: Login to Azure
+ if: ${{ github.ref_name == 'develop' && inputs.azure-client-id != '' }}
+ uses: azure/login@v2
+ with:
+ client-id: ${{ inputs.azure-client-id }}
+ tenant-id: ${{ inputs.azure-tenant-id }}
+ subscription-id: ${{ inputs.azure-subscription-id }}
+
+ - name: Sign executables
+ if: ${{ github.ref_name == 'develop' && inputs.azure-client-id != '' }}
+ uses: azure/trusted-signing-action@v0
+ with:
+ endpoint: https://eus.codesigning.azure.net/
+ trusted-signing-account-name: PrismLauncher
+ certificate-profile-name: PrismLauncher
+
+ files: |
+ ${{ github.workspace }}\install\prismlauncher.exe
+ ${{ github.workspace }}\install\prismlauncher_filelink.exe
+ ${{ github.workspace }}\install\prismlauncher_updater.exe
+
+ # TODO(@getchoo): Is this all really needed???
+ # https://github.com/Azure/trusted-signing-action/blob/fc390cf8ed0f14e248a542af1d838388a47c7a7c/docs/OIDC.md
+ exclude-environment-credential: true
+ exclude-workload-identity-credential: true
+ exclude-managed-identity-credential: true
+ exclude-shared-token-cache-credential: true
+ exclude-visual-studio-credential: true
+ exclude-visual-studio-code-credential: true
+ exclude-azure-cli-credential: false
+ exclude-azure-powershell-credential: true
+ exclude-azure-developer-cli-credential: true
+ exclude-interactive-browser-credential: true
- name: Package (MinGW, portable)
if: ${{ inputs.msystem != '' }}
@@ -77,7 +102,7 @@ runs:
INSTALL_PORTABLE_DIR: install-portable
run: |
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 }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt
- name: Package (MSVC, portable)
@@ -89,7 +114,7 @@ runs:
INSTALL_PORTABLE_DIR: install-portable
run: |
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 }} --config ${{ inputs.build-type }} --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
@@ -115,13 +140,28 @@ runs:
makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
- name: Sign installer
- shell: pwsh
- run: |
- if (Get-Content ./codesign.pfx){
- SignTool sign /fd sha256 /td sha256 /f codesign.pfx /p '${{ inputs.windows-codesign-password }}' /tr http://timestamp.digicert.com PrismLauncher-Setup.exe
- } else {
- ":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
- }
+ if: ${{ github.ref_name == 'develop' && inputs.azure-client-id != '' }}
+ uses: azure/trusted-signing-action@v0
+ with:
+ endpoint: https://eus.codesigning.azure.net/
+ trusted-signing-account-name: PrismLauncher
+ certificate-profile-name: PrismLauncher
+
+ files: |
+ ${{ github.workspace }}\PrismLauncher-Setup.exe
+
+ # TODO(@getchoo): Is this all really needed???
+ # https://github.com/Azure/trusted-signing-action/blob/fc390cf8ed0f14e248a542af1d838388a47c7a7c/docs/OIDC.md
+ exclude-environment-credential: true
+ exclude-workload-identity-credential: true
+ exclude-managed-identity-credential: true
+ exclude-shared-token-cache-credential: true
+ exclude-visual-studio-credential: true
+ exclude-visual-studio-code-credential: true
+ exclude-azure-cli-credential: false
+ exclude-azure-powershell-credential: true
+ exclude-azure-developer-cli-credential: true
+ exclude-interactive-browser-credential: true
- name: Upload binary zip
uses: actions/upload-artifact@v5
diff --git a/.github/actions/setup-dependencies/linux/action.yml b/.github/actions/setup-dependencies/linux/action.yml
index 94c04abe5..21ba30dc7 100644
--- a/.github/actions/setup-dependencies/linux/action.yml
+++ b/.github/actions/setup-dependencies/linux/action.yml
@@ -11,6 +11,7 @@ runs:
sudo apt-get -y install \
dpkg-dev \
ninja-build extra-cmake-modules scdoc \
+ libqrencode-dev \
appstream libxcb-cursor-dev
- name: Setup AppImage tooling
diff --git a/.github/actions/setup-dependencies/windows/action.yml b/.github/actions/setup-dependencies/windows/action.yml
index 1f10ff9fb..c6f23a9ca 100644
--- a/.github/actions/setup-dependencies/windows/action.yml
+++ b/.github/actions/setup-dependencies/windows/action.yml
@@ -79,6 +79,7 @@ runs:
qt6-5compat:p
qt6-networkauth:p
cmark:p
+ qrencode:p
tomlplusplus:p
quazip-qt6:p
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index a216e0a5e..696311033 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -70,6 +70,8 @@ jobs:
name: Build (${{ matrix.artifact-name }})
permissions:
+ # Required for Azure Trusted Signing
+ id-token: write
# Required for vcpkg binary cache
packages: write
@@ -79,7 +81,7 @@ jobs:
include:
- os: ubuntu-22.04
artifact-name: Linux
- base-cmake-preset: linux
+ cmake-preset: linux
# NOTE(@getchoo): Yes, we're intentionally using 24.04 here!!!
#
@@ -87,34 +89,34 @@ jobs:
# *for the same version* are compiled against 24.04 on ARM, and *not* 22.04 like x64
- os: ubuntu-24.04-arm
artifact-name: Linux-aarch64
- base-cmake-preset: linux
+ cmake-preset: linux
- os: windows-2022
artifact-name: Windows-MinGW-w64
- base-cmake-preset: windows_mingw
+ cmake-preset: windows_mingw
msystem: CLANG64
vcvars-arch: amd64_x86
- os: windows-11-arm
artifact-name: Windows-MinGW-arm64
- base-cmake-preset: windows_mingw
+ cmake-preset: windows_mingw
msystem: CLANGARM64
vcvars-arch: arm64
- os: windows-2022
artifact-name: Windows-MSVC
- base-cmake-preset: windows_msvc
+ cmake-preset: windows_msvc
# TODO(@getchoo): This is the default in setup-dependencies/windows. Why isn't it working?!?!
vcvars-arch: amd64
- os: windows-11-arm
artifact-name: Windows-MSVC-arm64
- base-cmake-preset: windows_msvc
+ cmake-preset: windows_msvc
vcvars-arch: arm64
- os: macos-14
artifact-name: macOS
- base-cmake-preset: ${{ (inputs.build-type || 'Debug') == 'Debug' && 'macos_universal' || 'macos' }}
+ cmake-preset: macos_universal
macosx-deployment-target: 12.0
runs-on: ${{ matrix.os }}
@@ -124,6 +126,11 @@ jobs:
shell: ${{ matrix.msystem != '' && 'msys2 {0}' || 'bash' }}
env:
+ ARTIFACT_NAME: ${{ matrix.artifact-name }}-Qt6
+ BUILD_PLATFORM: official
+ BUILD_TYPE: ${{ inputs.build-type || 'Debug' }}
+ CMAKE_PRESET: ${{ matrix.cmake-preset }}
+
MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macosx-deployment-target }}
steps:
@@ -140,7 +147,7 @@ jobs:
id: setup-dependencies
uses: ./.github/actions/setup-dependencies
with:
- build-type: ${{ inputs.build-type || 'Debug' }}
+ build-type: ${{ env.BUILD_TYPE }}
artifact-name: ${{ matrix.artifact-name }}
msystem: ${{ matrix.msystem }}
vcvars-arch: ${{ matrix.vcvars-arch }}
@@ -150,22 +157,17 @@ jobs:
# BUILD
##
- - name: Get CMake preset
- id: cmake-preset
- env:
- BASE_CMAKE_PRESET: ${{ matrix.base-cmake-preset }}
- PRESET_TYPE: ${{ (inputs.build-type || 'Debug') == 'Debug' && 'debug' || 'ci' }}
+ - name: Configure project
run: |
- echo preset="$BASE_CMAKE_PRESET"_"$PRESET_TYPE" >> "$GITHUB_OUTPUT"
+ cmake --preset "$CMAKE_PRESET"
- - name: Run CMake workflow
- env:
- CMAKE_PRESET: ${{ steps.cmake-preset.outputs.preset }}
-
- ARTIFACT_NAME: ${{ matrix.artifact-name }}-Qt6
- BUILD_PLATFORM: official
+ - name: Run build
run: |
- cmake --workflow --preset "$CMAKE_PRESET"
+ cmake --build --preset "$CMAKE_PRESET" --config "$BUILD_TYPE"
+
+ - name: Run tests
+ run: |
+ ctest --preset "$CMAKE_PRESET" --build-config "$BUILD_TYPE"
##
# PACKAGE
@@ -184,7 +186,6 @@ jobs:
version: ${{ steps.short-version.outputs.version }}
build-type: ${{ steps.setup-dependencies.outputs.build-type }}
artifact-name: ${{ matrix.artifact-name }}
- cmake-preset: ${{ steps.cmake-preset.outputs.preset }}
qt-version: ${{ steps.setup-dependencies.outputs.qt-version }}
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
@@ -198,13 +199,13 @@ jobs:
build-type: ${{ steps.setup-dependencies.outputs.build-type }}
artifact-name: ${{ matrix.artifact-name }}
- apple-codesign-cert: ${{ secrets.APPLE-CODESIGN-CERT }}
- apple-codesign-password: ${{ secrets.APPLE-CODESIGN_PASSWORD }}
- apple-codesign-id: ${{ secrets.APPLE-CODESIGN_ID }}
+ apple-codesign-cert: ${{ secrets.APPLE_CODESIGN_CERT }}
+ apple-codesign-password: ${{ secrets.APPLE_CODESIGN_PASSWORD }}
+ apple-codesign-id: ${{ secrets.APPLE_CODESIGN_ID }}
apple-notarize-apple-id: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }}
apple-notarize-team-id: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }}
- apple-notarize-password: ${{ secrets.APPLE-NOTARIZE_PASSWORD }}
- sparkle-ed25519-key: ${{ secrets.SPARKLE-ED25519_KEY }}
+ apple-notarize-password: ${{ secrets.APPLE_NOTARIZE_PASSWORD }}
+ sparkle-ed25519-key: ${{ secrets.SPARKLE_ED25519_KEY }}
- name: Package (Windows)
if: ${{ runner.os == 'Windows' }}
@@ -215,5 +216,6 @@ jobs:
artifact-name: ${{ matrix.artifact-name }}
msystem: ${{ matrix.msystem }}
- windows-codesign-cert: ${{ secrets.WINDOWS_CODESIGN_CERT }}
- windows-codesign-password: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
+ azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}
+ azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
+ azure-subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 47fa17f3d..3fdcc68ba 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -78,8 +78,8 @@ jobs:
- name: Configure and Build
run: |
- cmake --preset linux_debug
- cmake --build --preset linux_debug
+ cmake --preset linux
+ cmake --build --preset linux --config Debug
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
diff --git a/.github/workflows/update-flake.yml b/.github/workflows/update-flake.yml
index 66d4d8b4a..64d299ef7 100644
--- a/.github/workflows/update-flake.yml
+++ b/.github/workflows/update-flake.yml
@@ -17,7 +17,7 @@ jobs:
steps:
- uses: actions/checkout@v5
- - uses: cachix/install-nix-action@fd24c48048070c1be9acd18c9d369a83f0fe94d7 # v31
+ - uses: cachix/install-nix-action@0b0e072294b088b73964f1d72dfdac0951439dbd # v31
- uses: DeterminateSystems/update-flake-lock@v27
with:
diff --git a/.gitmodules b/.gitmodules
index 0a0a50bee..7ad40becb 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -19,6 +19,3 @@
[submodule "flatpak/shared-modules"]
path = flatpak/shared-modules
url = https://github.com/flathub/shared-modules.git
-[submodule "libraries/qrcodegenerator"]
- path = libraries/qrcodegenerator
- url = https://github.com/nayuki/QR-Code-generator
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 02d7300d2..6caa45bc7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -112,7 +112,7 @@ set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}"
# Export compile commands for debug builds if we can (useful in LSPs like clangd)
# https://cmake.org/cmake/help/v3.31/variable/CMAKE_EXPORT_COMPILE_COMMANDS.html
-if(CMAKE_GENERATOR STREQUAL "Unix Makefiles" OR CMAKE_GENERATOR STREQUAL "Ninja" AND CMAKE_BUILD_TYPE STREQUAL "Debug")
+if(CMAKE_GENERATOR STREQUAL "Unix Makefiles" OR CMAKE_GENERATOR MATCHES "^Ninja")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
endif()
@@ -345,12 +345,24 @@ if(Launcher_QT_VERSION_MAJOR EQUAL 6)
set(QT_LIBEXECS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_LIBEXECS})
endif()
+# Find libqrencode
+## NOTE(@getchoo): Never use pkg-config with MSVC since the vcpkg port makes our install bundle fail to find the dll
+if(MSVC)
+ find_path(LIBQRENCODE_INCLUDE_DIR qrencode.h REQUIRED)
+ find_library(LIBQRENCODE_LIBRARY_RELEASE qrencode REQUIRED)
+ find_library(LIBQRENCODE_LIBRARY_DEBUG qrencoded)
+ set(LIBQRENCODE_LIBRARIES optimized ${LIBQRENCODE_LIBRARY_RELEASE} debug ${LIBQRENCODE_LIBRARY_DEBUG})
+else()
+ find_package(PkgConfig REQUIRED)
+ pkg_check_modules(libqrencode REQUIRED IMPORTED_TARGET libqrencode)
+endif()
+
if(NOT Launcher_FORCE_BUNDLED_LIBS)
# Find toml++
find_package(tomlplusplus 3.2.0 QUIET)
# Fallback to pkg-config (if available) if CMake files aren't found
if(NOT tomlplusplus_FOUND)
- find_package(PkgConfig)
+ find_package(PkgConfig QUIET)
if(PkgConfig_FOUND)
pkg_check_modules(tomlplusplus IMPORTED_TARGET tomlplusplus>=3.2.0)
endif()
@@ -359,9 +371,6 @@ if(NOT Launcher_FORCE_BUNDLED_LIBS)
# Find cmark
find_package(cmark QUIET)
-
- # Find qrcodegencpp-cmake
- find_package(qrcodegencpp QUIET)
endif()
include(ECMQtDeclareLoggingCategory)
@@ -405,8 +414,8 @@ if(UNIX AND APPLE)
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" CACHE STRING "URL for Sparkle update feed")
- set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.6.4/Sparkle-2.6.4.tar.xz" CACHE STRING "URL to Sparkle release archive")
- set(MACOSX_SPARKLE_SHA256 "50612a06038abc931f16011d7903b8326a362c1074dabccb718404ce8e585f0b" CACHE STRING "SHA256 checksum for Sparkle release archive")
+ set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.8.0/Sparkle-2.8.0.tar.xz" CACHE STRING "URL to Sparkle release archive")
+ set(MACOSX_SPARKLE_SHA256 "fd5681ee92bf238aaac2d08214ceaf0cc8976e452d7f882d80bac1e61581f3b1" CACHE STRING "SHA256 checksum for Sparkle release archive")
set(MACOSX_SPARKLE_DIR "${CMAKE_BINARY_DIR}/frameworks/Sparkle")
if(NOT MACOSX_SPARKLE_UPDATE_PUBLIC_KEY STREQUAL "" AND NOT MACOSX_SPARKLE_UPDATE_FEED_URL STREQUAL "")
@@ -522,18 +531,6 @@ if(NOT cmark_FOUND)
else()
message(STATUS "Using system cmark")
endif()
-if(NOT qrcodegencpp_FOUND)
- set(QRCODE_SOURCES
- libraries/qrcodegenerator/cpp/qrcodegen.cpp
- libraries/qrcodegenerator/cpp/qrcodegen.hpp
- )
- add_library(qrcodegenerator STATIC ${QRCODE_SOURCES})
- target_include_directories(qrcodegenerator PUBLIC "libraries/qrcodegenerator/cpp/" )
- generate_export_header(qrcodegenerator)
-else()
- add_library(qrcodegenerator ALIAS qrcodegencpp::qrcodegencpp)
- message(STATUS "Using system qrcodegencpp-cmake")
-endif()
add_subdirectory(libraries/gamemode)
add_subdirectory(libraries/murmur2) # Hash for usage with the CurseForge API
add_subdirectory(libraries/qdcss) # css parser
diff --git a/CMakePresets.json b/CMakePresets.json
index f8e688b89..613fada07 100644
--- a/CMakePresets.json
+++ b/CMakePresets.json
@@ -5,10 +5,210 @@
"major": 3,
"minor": 28
},
- "include": [
- "cmake/linuxPreset.json",
- "cmake/macosPreset.json",
- "cmake/windowsMinGWPreset.json",
- "cmake/windowsMSVCPreset.json"
+ "configurePresets": [
+ {
+ "name": "base",
+ "hidden": true,
+ "binaryDir": "build",
+ "installDir": "install",
+ "generator": "Ninja Multi-Config",
+ "cacheVariables": {
+ "Launcher_BUILD_ARTIFACT": "$penv{ARTIFACT_NAME}",
+ "Launcher_BUILD_PLATFORM": "$penv{BUILD_PLATFORM}",
+ "Launcher_ENABLE_JAVA_DOWNLOADER": "ON",
+ "ENABLE_LTO": "ON"
+ }
+ },
+ {
+ "name": "linux",
+ "displayName": "Linux",
+ "inherits": [
+ "base"
+ ],
+ "condition": {
+ "type": "equals",
+ "lhs": "${hostSystemName}",
+ "rhs": "Linux"
+ }
+ },
+ {
+ "name": "macos",
+ "displayName": "macOS",
+ "inherits": [
+ "base"
+ ],
+ "condition": {
+ "type": "equals",
+ "lhs": "${hostSystemName}",
+ "rhs": "Darwin"
+ }
+ },
+ {
+ "name": "macos_universal",
+ "displayName": "macOS (Universal Binary)",
+ "inherits": [
+ "macos"
+ ],
+ "cacheVariables": {
+ "CMAKE_OSX_ARCHITECTURES": "x86_64;arm64",
+ "VCPKG_TARGET_TRIPLET": "universal-osx"
+ }
+ },
+ {
+ "name": "windows_mingw",
+ "displayName": "Windows (MinGW)",
+ "inherits": [
+ "base"
+ ],
+ "condition": {
+ "type": "equals",
+ "lhs": "${hostSystemName}",
+ "rhs": "Windows"
+ }
+ },
+ {
+ "name": "windows_msvc",
+ "displayName": "Windows (MSVC)",
+ "inherits": [
+ "base"
+ ],
+ "condition": {
+ "type": "equals",
+ "lhs": "${hostSystemName}",
+ "rhs": "Windows"
+ }
+ }
+ ],
+ "buildPresets": [
+ {
+ "name": "linux",
+ "displayName": "Linux",
+ "condition": {
+ "type": "equals",
+ "lhs": "${hostSystemName}",
+ "rhs": "Linux"
+ },
+ "configurePreset": "linux"
+ },
+ {
+ "name": "macos",
+ "displayName": "macOS",
+ "condition": {
+ "type": "equals",
+ "lhs": "${hostSystemName}",
+ "rhs": "Darwin"
+ },
+ "configurePreset": "macos"
+ },
+ {
+ "name": "macos_universal",
+ "displayName": "macOS (Universal Binary)",
+ "inherits": [
+ "macos"
+ ],
+ "configurePreset": "macos_universal"
+ },
+ {
+ "name": "windows_mingw",
+ "displayName": "Windows (MinGW)",
+ "condition": {
+ "type": "equals",
+ "lhs": "${hostSystemName}",
+ "rhs": "Windows"
+ },
+ "configurePreset": "windows_mingw"
+ },
+ {
+ "name": "windows_msvc",
+ "displayName": "Windows (MSVC)",
+ "condition": {
+ "type": "equals",
+ "lhs": "${hostSystemName}",
+ "rhs": "Windows"
+ },
+ "configurePreset": "windows_msvc"
+ }
+ ],
+ "testPresets": [
+ {
+ "name": "base",
+ "hidden": true,
+ "output": {
+ "outputOnFailure": true
+ },
+ "execution": {
+ "noTestsAction": "error"
+ },
+ "filter": {
+ "exclude": {
+ "name": "^example64|example$"
+ }
+ }
+ },
+ {
+ "name": "linux",
+ "displayName": "Linux",
+ "inherits": [
+ "base"
+ ],
+ "condition": {
+ "type": "equals",
+ "lhs": "${hostSystemName}",
+ "rhs": "Linux"
+ },
+ "configurePreset": "linux"
+ },
+ {
+ "name": "macos",
+ "displayName": "macOS",
+ "inherits": [
+ "base"
+ ],
+ "condition": {
+ "type": "equals",
+ "lhs": "${hostSystemName}",
+ "rhs": "Darwin"
+ },
+ "configurePreset": "macos"
+ },
+ {
+ "name": "macos_universal",
+ "displayName": "macOS (Universal Binary)",
+ "inherits": [
+ "base"
+ ],
+ "condition": {
+ "type": "equals",
+ "lhs": "${hostSystemName}",
+ "rhs": "Darwin"
+ },
+ "configurePreset": "macos_universal"
+ },
+ {
+ "name": "windows_mingw",
+ "displayName": "Windows (MinGW)",
+ "inherits": [
+ "base"
+ ],
+ "condition": {
+ "type": "equals",
+ "lhs": "${hostSystemName}",
+ "rhs": "Windows"
+ },
+ "configurePreset": "windows_mingw"
+ },
+ {
+ "name": "windows_msvc",
+ "displayName": "Windows (MSVC)",
+ "inherits": [
+ "base"
+ ],
+ "condition": {
+ "type": "equals",
+ "lhs": "${hostSystemName}",
+ "rhs": "Windows"
+ },
+ "configurePreset": "windows_msvc"
+ }
]
}
diff --git a/COPYING.md b/COPYING.md
index e64bb8760..8588c8951 100644
--- a/COPYING.md
+++ b/COPYING.md
@@ -404,14 +404,23 @@
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see .
-## QR-Code-generator (`libraries/qrcodegenerator`)
+## libqrencode (`fukuchi/libqrencode`)
- Copyright © 2024 Project Nayuki. (MIT License)
- https://www.nayuki.io/page/qr-code-generator-library
+ Copyright (C) 2020 libqrencode Authors
- 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.
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
## vcpkg (`cmake/vcpkg-ports`)
diff --git a/cmake/commonPresets.json b/cmake/commonPresets.json
deleted file mode 100644
index 9be0fb447..000000000
--- a/cmake/commonPresets.json
+++ /dev/null
@@ -1,81 +0,0 @@
-{
- "$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json",
- "version": 8,
- "configurePresets": [
- {
- "name": "base",
- "hidden": true,
- "binaryDir": "build",
- "installDir": "install",
- "cacheVariables": {
- "Launcher_BUILD_ARTIFACT": "$penv{ARTIFACT_NAME}",
- "Launcher_BUILD_PLATFORM": "$penv{BUILD_PLATFORM}"
- }
- },
- {
- "name": "base_debug",
- "hidden": true,
- "inherits": [
- "base"
- ],
- "cacheVariables": {
- "CMAKE_BUILD_TYPE": "Debug"
- }
- },
- {
- "name": "base_release",
- "hidden": true,
- "inherits": [
- "base"
- ],
- "cacheVariables": {
- "CMAKE_BUILD_TYPE": "Release",
- "ENABLE_LTO": "ON"
- }
- },
- {
- "name": "base_ci",
- "hidden": true,
- "inherits": [
- "base_release"
- ],
- "cacheVariables": {
- "Launcher_FORCE_BUNDLED_LIBS": "ON"
- }
- }
- ],
- "testPresets": [
- {
- "name": "base",
- "hidden": true,
- "output": {
- "outputOnFailure": true
- },
- "execution": {
- "noTestsAction": "error"
- },
- "filter": {
- "exclude": {
- "name": "^example64|example$"
- }
- }
- },
- {
- "name": "base_debug",
- "hidden": true,
- "inherits": [
- "base"
- ],
- "output": {
- "debug": true
- }
- },
- {
- "name": "base_release",
- "hidden": true,
- "inherits": [
- "base"
- ]
- }
- ]
-}
diff --git a/cmake/linuxPreset.json b/cmake/linuxPreset.json
deleted file mode 100644
index 984defa5d..000000000
--- a/cmake/linuxPreset.json
+++ /dev/null
@@ -1,176 +0,0 @@
-{
- "$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json",
- "version": 8,
- "include": [
- "commonPresets.json"
- ],
- "configurePresets": [
- {
- "name": "linux_base",
- "hidden": true,
- "condition": {
- "type": "equals",
- "lhs": "${hostSystemName}",
- "rhs": "Linux"
- },
- "generator": "Ninja",
- "cacheVariables": {
- "Launcher_ENABLE_JAVA_DOWNLOADER": "ON"
- }
- },
- {
- "name": "linux_debug",
- "inherits": [
- "base_debug",
- "linux_base"
- ],
- "displayName": "Linux (Debug)"
- },
- {
- "name": "linux_release",
- "inherits": [
- "base_release",
- "linux_base"
- ],
- "displayName": "Linux (Release)"
- },
- {
- "name": "linux_ci",
- "inherits": [
- "base_ci",
- "linux_base"
- ],
- "displayName": "Linux (CI)",
- "installDir": "/usr"
- }
- ],
- "buildPresets": [
- {
- "name": "linux_base",
- "hidden": true,
- "condition": {
- "type": "equals",
- "lhs": "${hostSystemName}",
- "rhs": "Linux"
- }
- },
- {
- "name": "linux_debug",
- "inherits": [
- "linux_base"
- ],
- "displayName": "Linux (Debug)",
- "configurePreset": "linux_debug"
- },
- {
- "name": "linux_release",
- "inherits": [
- "linux_base"
- ],
- "displayName": "Linux (Release)",
- "configurePreset": "linux_release"
- },
- {
- "name": "linux_ci",
- "inherits": [
- "linux_base"
- ],
- "displayName": "Linux (CI)",
- "configurePreset": "linux_ci"
- }
- ],
- "testPresets": [
- {
- "name": "linux_base",
- "hidden": true,
- "condition": {
- "type": "equals",
- "lhs": "${hostSystemName}",
- "rhs": "Linux"
- }
- },
- {
- "name": "linux_debug",
- "inherits": [
- "base_debug",
- "linux_base"
- ],
- "displayName": "Linux (Debug)",
- "configurePreset": "linux_debug"
- },
- {
- "name": "linux_release",
- "inherits": [
- "base_release",
- "linux_base"
- ],
- "displayName": "Linux (Release)",
- "configurePreset": "linux_release"
- },
- {
- "name": "linux_ci",
- "inherits": [
- "base_release",
- "linux_base"
- ],
- "displayName": "Linux (CI)",
- "configurePreset": "linux_ci"
- }
- ],
- "workflowPresets": [
- {
- "name": "linux_debug",
- "displayName": "Linux (Debug)",
- "steps": [
- {
- "type": "configure",
- "name": "linux_debug"
- },
- {
- "type": "build",
- "name": "linux_debug"
- },
- {
- "type": "test",
- "name": "linux_debug"
- }
- ]
- },
- {
- "name": "linux",
- "displayName": "Linux (Release)",
- "steps": [
- {
- "type": "configure",
- "name": "linux_release"
- },
- {
- "type": "build",
- "name": "linux_release"
- },
- {
- "type": "test",
- "name": "linux_release"
- }
- ]
- },
- {
- "name": "linux_ci",
- "displayName": "Linux (CI)",
- "steps": [
- {
- "type": "configure",
- "name": "linux_ci"
- },
- {
- "type": "build",
- "name": "linux_ci"
- },
- {
- "type": "test",
- "name": "linux_ci"
- }
- ]
- }
- ]
-}
diff --git a/cmake/macosPreset.json b/cmake/macosPreset.json
deleted file mode 100644
index 9098f9a9a..000000000
--- a/cmake/macosPreset.json
+++ /dev/null
@@ -1,269 +0,0 @@
-{
- "$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json",
- "version": 8,
- "include": [
- "commonPresets.json"
- ],
- "configurePresets": [
- {
- "name": "macos_base",
- "hidden": true,
- "condition": {
- "type": "equals",
- "lhs": "${hostSystemName}",
- "rhs": "Darwin"
- },
- "generator": "Ninja"
- },
- {
- "name": "macos_universal_base",
- "hidden": true,
- "inherits": [
- "macos_base"
- ],
- "cacheVariables": {
- "CMAKE_OSX_ARCHITECTURES": "x86_64;arm64",
- "VCPKG_TARGET_TRIPLET": "universal-osx"
- }
- },
- {
- "name": "macos_debug",
- "inherits": [
- "base_debug",
- "macos_base"
- ],
- "displayName": "macOS (Debug)"
- },
- {
- "name": "macos_release",
- "inherits": [
- "base_release",
- "macos_base"
- ],
- "displayName": "macOS (Release)"
- },
- {
- "name": "macos_universal_debug",
- "inherits": [
- "base_debug",
- "macos_universal_base"
- ],
- "displayName": "macOS (Universal Binary, Debug)"
- },
- {
- "name": "macos_universal_release",
- "inherits": [
- "base_release",
- "macos_universal_base"
- ],
- "displayName": "macOS (Universal Binary, Release)"
- },
- {
- "name": "macos_ci",
- "inherits": [
- "base_ci",
- "macos_universal_base"
- ],
- "displayName": "macOS (CI)"
- }
- ],
- "buildPresets": [
- {
- "name": "macos_base",
- "hidden": true,
- "condition": {
- "type": "equals",
- "lhs": "${hostSystemName}",
- "rhs": "Darwin"
- }
- },
- {
- "name": "macos_debug",
- "inherits": [
- "macos_base"
- ],
- "displayName": "macOS (Debug)",
- "configurePreset": "macos_debug"
- },
- {
- "name": "macos_release",
- "inherits": [
- "macos_base"
- ],
- "displayName": "macOS (Release)",
- "configurePreset": "macos_release"
- },
- {
- "name": "macos_universal_debug",
- "inherits": [
- "macos_base"
- ],
- "displayName": "macOS (Universal Binary, Debug)",
- "configurePreset": "macos_universal_debug"
- },
- {
- "name": "macos_universal_release",
- "inherits": [
- "macos_base"
- ],
- "displayName": "macOS (Universal Binary, Release)",
- "configurePreset": "macos_universal_release"
- },
- {
- "name": "macos_ci",
- "inherits": [
- "macos_base"
- ],
- "displayName": "macOS (CI)",
- "configurePreset": "macos_ci"
- }
- ],
- "testPresets": [
- {
- "name": "macos_base",
- "hidden": true,
- "condition": {
- "type": "equals",
- "lhs": "${hostSystemName}",
- "rhs": "Darwin"
- }
- },
- {
- "name": "macos_debug",
- "inherits": [
- "base_debug",
- "macos_base"
- ],
- "displayName": "MacOS (Debug)",
- "configurePreset": "macos_debug"
- },
- {
- "name": "macos_release",
- "inherits": [
- "base_release",
- "macos_base"
- ],
- "displayName": "macOS (Release)",
- "configurePreset": "macos_release"
- },
- {
- "name": "macos_universal_debug",
- "inherits": [
- "base_debug",
- "macos_base"
- ],
- "displayName": "MacOS (Universal Binary, Debug)",
- "configurePreset": "macos_universal_debug"
- },
- {
- "name": "macos_universal_release",
- "inherits": [
- "base_release",
- "macos_base"
- ],
- "displayName": "macOS (Universal Binary, Release)",
- "configurePreset": "macos_universal_release"
- },
- {
- "name": "macos_ci",
- "inherits": [
- "base_release",
- "macos_base"
- ],
- "displayName": "macOS (CI)",
- "configurePreset": "macos_ci"
- }
- ],
- "workflowPresets": [
- {
- "name": "macos_debug",
- "displayName": "macOS (Debug)",
- "steps": [
- {
- "type": "configure",
- "name": "macos_debug"
- },
- {
- "type": "build",
- "name": "macos_debug"
- },
- {
- "type": "test",
- "name": "macos_debug"
- }
- ]
- },
- {
- "name": "macos",
- "displayName": "macOS (Release)",
- "steps": [
- {
- "type": "configure",
- "name": "macos_release"
- },
- {
- "type": "build",
- "name": "macos_release"
- },
- {
- "type": "test",
- "name": "macos_release"
- }
- ]
- },
- {
- "name": "macos_universal_debug",
- "displayName": "macOS (Universal Binary, Debug)",
- "steps": [
- {
- "type": "configure",
- "name": "macos_universal_debug"
- },
- {
- "type": "build",
- "name": "macos_universal_debug"
- },
- {
- "type": "test",
- "name": "macos_universal_debug"
- }
- ]
- },
- {
- "name": "macos_universal",
- "displayName": "macOS (Universal Binary, Release)",
- "steps": [
- {
- "type": "configure",
- "name": "macos_universal_release"
- },
- {
- "type": "build",
- "name": "macos_universal_release"
- },
- {
- "type": "test",
- "name": "macos_universal_release"
- }
- ]
- },
- {
- "name": "macos_ci",
- "displayName": "macOS (CI)",
- "steps": [
- {
- "type": "configure",
- "name": "macos_ci"
- },
- {
- "type": "build",
- "name": "macos_ci"
- },
- {
- "type": "test",
- "name": "macos_ci"
- }
- ]
- }
- ]
-}
diff --git a/cmake/windowsMSVCPreset.json b/cmake/windowsMSVCPreset.json
deleted file mode 100644
index 2cb996b81..000000000
--- a/cmake/windowsMSVCPreset.json
+++ /dev/null
@@ -1,281 +0,0 @@
-{
- "$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json",
- "version": 8,
- "include": [
- "commonPresets.json"
- ],
- "configurePresets": [
- {
- "name": "windows_msvc_base",
- "hidden": true,
- "condition": {
- "type": "equals",
- "lhs": "${hostSystemName}",
- "rhs": "Windows"
- },
- "generator": "Ninja"
- },
- {
- "name": "windows_msvc_arm64_cross_base",
- "hidden": true,
- "inherits": [
- "windows_msvc_base"
- ],
- "cacheVariables": {
- "CMAKE_SYSTEM_NAME": "${hostSystemName}"
- }
- },
- {
- "name": "windows_msvc_debug",
- "inherits": [
- "base_debug",
- "windows_msvc_base"
- ],
- "displayName": "Windows MSVC (Debug)"
- },
- {
- "name": "windows_msvc_release",
- "inherits": [
- "base_release",
- "windows_msvc_base"
- ],
- "displayName": "Windows MSVC (Release)"
- },
- {
- "name": "windows_msvc_arm64_cross_debug",
- "inherits": [
- "base_debug",
- "windows_msvc_arm64_cross_base"
- ],
- "displayName": "Windows MSVC (ARM64 cross, Debug)"
- },
- {
- "name": "windows_msvc_arm64_cross_release",
- "inherits": [
- "base_release",
- "windows_msvc_arm64_cross_base"
- ],
- "displayName": "Windows MSVC (ARM64 cross, Release)"
- },
- {
- "name": "windows_msvc_ci",
- "inherits": [
- "base_ci",
- "windows_msvc_base"
- ],
- "displayName": "Windows MSVC (CI)"
- },
- {
- "name": "windows_msvc_arm64_cross_ci",
- "inherits": [
- "base_ci",
- "windows_msvc_arm64_cross_base"
- ],
- "displayName": "Windows MSVC (ARM64 cross, CI)"
- }
- ],
- "buildPresets": [
- {
- "name": "windows_msvc_base",
- "hidden": true,
- "condition": {
- "type": "equals",
- "lhs": "${hostSystemName}",
- "rhs": "Windows"
- }
- },
- {
- "name": "windows_msvc_debug",
- "inherits": [
- "windows_msvc_base"
- ],
- "displayName": "Windows MSVC (Debug)",
- "configurePreset": "windows_msvc_debug",
- "configuration": "Debug"
- },
- {
- "name": "windows_msvc_release",
- "inherits": [
- "windows_msvc_base"
- ],
- "displayName": "Windows MSVC (Release)",
- "configurePreset": "windows_msvc_release",
- "configuration": "Release"
- },
- {
- "name": "windows_msvc_arm64_cross_debug",
- "inherits": [
- "windows_msvc_base"
- ],
- "displayName": "Windows MSVC (ARM64 cross, Debug)",
- "configurePreset": "windows_msvc_arm64_cross_debug",
- "configuration": "Debug"
- },
- {
- "name": "windows_msvc_arm64_cross_release",
- "inherits": [
- "windows_msvc_base"
- ],
- "displayName": "Windows MSVC (ARM64 cross, Release)",
- "configurePreset": "windows_msvc_arm64_cross_release",
- "configuration": "Release"
- },
- {
- "name": "windows_msvc_ci",
- "inherits": [
- "windows_msvc_base"
- ],
- "displayName": "Windows MSVC (CI)",
- "configurePreset": "windows_msvc_ci",
- "configuration": "Release"
- },
- {
- "name": "windows_msvc_arm64_cross_ci",
- "inherits": [
- "windows_msvc_base"
- ],
- "displayName": "Windows MSVC (ARM64 cross, CI)",
- "configurePreset": "windows_msvc_arm64_cross_ci",
- "configuration": "Release"
- }
- ],
- "testPresets": [
- {
- "name": "windows_msvc_base",
- "hidden": true,
- "condition": {
- "type": "equals",
- "lhs": "${hostSystemName}",
- "rhs": "Windows"
- }
- },
- {
- "name": "windows_msvc_debug",
- "inherits": [
- "base_debug",
- "windows_msvc_base"
- ],
- "displayName": "Windows MSVC (Debug)",
- "configurePreset": "windows_msvc_debug",
- "configuration": "Debug"
- },
- {
- "name": "windows_msvc_release",
- "inherits": [
- "base_release",
- "windows_msvc_base"
- ],
- "displayName": "Windows MSVC (Release)",
- "configurePreset": "windows_msvc_release",
- "configuration": "Release"
- },
- {
- "name": "windows_msvc_ci",
- "inherits": [
- "base_release",
- "windows_msvc_base"
- ],
- "displayName": "Windows MSVC (CI)",
- "configurePreset": "windows_msvc_ci",
- "configuration": "Release"
- }
- ],
- "workflowPresets": [
- {
- "name": "windows_msvc_debug",
- "displayName": "Windows MSVC (Debug)",
- "steps": [
- {
- "type": "configure",
- "name": "windows_msvc_debug"
- },
- {
- "type": "build",
- "name": "windows_msvc_debug"
- },
- {
- "type": "test",
- "name": "windows_msvc_debug"
- }
- ]
- },
- {
- "name": "windows_msvc",
- "displayName": "Windows MSVC (Release)",
- "steps": [
- {
- "type": "configure",
- "name": "windows_msvc_release"
- },
- {
- "type": "build",
- "name": "windows_msvc_release"
- },
- {
- "type": "test",
- "name": "windows_msvc_release"
- }
- ]
- },
- {
- "name": "windows_msvc_arm64_cross_debug",
- "displayName": "Windows MSVC (ARM64 cross, Debug)",
- "steps": [
- {
- "type": "configure",
- "name": "windows_msvc_arm64_cross_debug"
- },
- {
- "type": "build",
- "name": "windows_msvc_arm64_cross_debug"
- }
- ]
- },
- {
- "name": "windows_msvc_arm64_cross",
- "displayName": "Windows MSVC (ARM64 cross, Release)",
- "steps": [
- {
- "type": "configure",
- "name": "windows_msvc_arm64_cross_release"
- },
- {
- "type": "build",
- "name": "windows_msvc_arm64_cross_release"
- }
- ]
- },
- {
- "name": "windows_msvc_ci",
- "displayName": "Windows MSVC (CI)",
- "steps": [
- {
- "type": "configure",
- "name": "windows_msvc_ci"
- },
- {
- "type": "build",
- "name": "windows_msvc_ci"
- },
- {
- "type": "test",
- "name": "windows_msvc_ci"
- }
- ]
- },
- {
- "name": "windows_msvc_arm64_cross_ci",
- "displayName": "Windows MSVC (ARM64 cross, CI)",
- "steps": [
- {
- "type": "configure",
- "name": "windows_msvc_arm64_cross_ci"
- },
- {
- "type": "build",
- "name": "windows_msvc_arm64_cross_ci"
- }
- ]
- }
- ]
-}
diff --git a/cmake/windowsMinGWPreset.json b/cmake/windowsMinGWPreset.json
deleted file mode 100644
index 7c4adbcf2..000000000
--- a/cmake/windowsMinGWPreset.json
+++ /dev/null
@@ -1,177 +0,0 @@
-{
- "$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json",
- "version": 8,
- "include": [
- "commonPresets.json"
- ],
- "configurePresets": [
- {
- "name": "windows_mingw_base",
- "hidden": true,
- "condition": {
- "type": "equals",
- "lhs": "${hostSystemName}",
- "rhs": "Windows"
- },
- "generator": "Ninja"
- },
- {
- "name": "windows_mingw_debug",
- "inherits": [
- "base_debug",
- "windows_mingw_base"
- ],
- "displayName": "Windows MinGW (Debug)"
- },
- {
- "name": "windows_mingw_release",
- "inherits": [
- "base_release",
- "windows_mingw_base"
- ],
- "displayName": "Windows MinGW (Release)"
- },
- {
- "name": "windows_mingw_ci",
- "inherits": [
- "base_ci",
- "windows_mingw_base"
- ],
- "displayName": "Windows MinGW (CI)"
- }
- ],
- "buildPresets": [
- {
- "name": "windows_mingw_base",
- "hidden": true,
- "condition": {
- "type": "equals",
- "lhs": "${hostSystemName}",
- "rhs": "Windows"
- }
- },
- {
- "name": "windows_mingw_debug",
- "inherits": [
- "windows_mingw_base"
- ],
- "displayName": "Windows MinGW (Debug)",
- "configurePreset": "windows_mingw_debug"
- },
- {
- "name": "windows_mingw_release",
- "inherits": [
- "windows_mingw_base"
- ],
- "displayName": "Windows MinGW (Release)",
- "configurePreset": "windows_mingw_release"
- },
- {
- "name": "windows_mingw_ci",
- "inherits": [
- "windows_mingw_base"
- ],
- "displayName": "Windows MinGW (CI)",
- "configurePreset": "windows_mingw_ci"
- }
- ],
- "testPresets": [
- {
- "name": "windows_mingw_base",
- "hidden": true,
- "condition": {
- "type": "equals",
- "lhs": "${hostSystemName}",
- "rhs": "Windows"
- },
- "filter": {
- "exclude": {
- "name": "^example64|example$"
- }
- }
- },
- {
- "name": "windows_mingw_debug",
- "inherits": [
- "base_debug",
- "windows_mingw_base"
- ],
- "displayName": "Windows MinGW (Debug)",
- "configurePreset": "windows_mingw_debug"
- },
- {
- "name": "windows_mingw_release",
- "inherits": [
- "base_release",
- "windows_mingw_base"
- ],
- "displayName": "Windows MinGW (Release)",
- "configurePreset": "windows_mingw_release"
- },
- {
- "name": "windows_mingw_ci",
- "inherits": [
- "base_release",
- "windows_mingw_base"
- ],
- "displayName": "Windows MinGW (CI)",
- "configurePreset": "windows_mingw_ci"
- }
- ],
- "workflowPresets": [
- {
- "name": "windows_mingw_debug",
- "displayName": "Windows MinGW (Debug)",
- "steps": [
- {
- "type": "configure",
- "name": "windows_mingw_debug"
- },
- {
- "type": "build",
- "name": "windows_mingw_debug"
- },
- {
- "type": "test",
- "name": "windows_mingw_debug"
- }
- ]
- },
- {
- "name": "windows_mingw",
- "displayName": "Windows MinGW (Release)",
- "steps": [
- {
- "type": "configure",
- "name": "windows_mingw_release"
- },
- {
- "type": "build",
- "name": "windows_mingw_release"
- },
- {
- "type": "test",
- "name": "windows_mingw_release"
- }
- ]
- },
- {
- "name": "windows_mingw_ci",
- "displayName": "Windows MinGW (CI)",
- "steps": [
- {
- "type": "configure",
- "name": "windows_mingw_ci"
- },
- {
- "type": "build",
- "name": "windows_mingw_ci"
- },
- {
- "type": "test",
- "name": "windows_mingw_ci"
- }
- ]
- }
- ]
-}
diff --git a/flake.lock b/flake.lock
index b04969b11..6cdc11a85 100644
--- a/flake.lock
+++ b/flake.lock
@@ -18,11 +18,11 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1761907660,
- "narHash": "sha256-kJ8lIZsiPOmbkJypG+B5sReDXSD1KGu2VEPNqhRa/ew=",
+ "lastModified": 1762977756,
+ "narHash": "sha256-4PqRErxfe+2toFJFgcRKZ0UI9NSIOJa+7RXVtBhy4KE=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "2fb006b87f04c4d3bdf08cfdbc7fab9c13d94a15",
+ "rev": "c5ae371f1a6a7fd27823bc500d9390b38c05fa55",
"type": "github"
},
"original": {
@@ -32,27 +32,10 @@
"type": "github"
}
},
- "qrcodegenerator": {
- "flake": false,
- "locked": {
- "lastModified": 1737616857,
- "narHash": "sha256-6SugPt0lp1Gz7nV23FLmsmpfzgFItkSw7jpGftsDPWc=",
- "owner": "nayuki",
- "repo": "QR-Code-generator",
- "rev": "2c9044de6b049ca25cb3cd1649ed7e27aa055138",
- "type": "github"
- },
- "original": {
- "owner": "nayuki",
- "repo": "QR-Code-generator",
- "type": "github"
- }
- },
"root": {
"inputs": {
"libnbtplusplus": "libnbtplusplus",
- "nixpkgs": "nixpkgs",
- "qrcodegenerator": "qrcodegenerator"
+ "nixpkgs": "nixpkgs"
}
}
},
diff --git a/flake.nix b/flake.nix
index 751ef2eeb..594a82d91 100644
--- a/flake.nix
+++ b/flake.nix
@@ -15,11 +15,6 @@
url = "github:PrismLauncher/libnbtplusplus";
flake = false;
};
-
- qrcodegenerator = {
- url = "github:nayuki/QR-Code-generator";
- flake = false;
- };
};
outputs =
@@ -27,7 +22,6 @@
self,
nixpkgs,
libnbtplusplus,
- qrcodegenerator,
}:
let
@@ -175,7 +169,6 @@
prismlauncher-unwrapped = prev.callPackage ./nix/unwrapped.nix {
inherit
libnbtplusplus
- qrcodegenerator
self
;
};
diff --git a/launcher/Application.cpp b/launcher/Application.cpp
index a011e1465..682cf5b1a 100644
--- a/launcher/Application.cpp
+++ b/launcher/Application.cpp
@@ -108,8 +108,6 @@
#include "icons/IconList.h"
#include "net/HttpMetaCache.h"
-#include "java/JavaInstallList.h"
-
#include "updater/ExternalUpdater.h"
#include "tools/JProfiler.h"
@@ -127,7 +125,6 @@
#include
#include
-#include
#include "SysInfo.h"
#ifdef Q_OS_LINUX
@@ -708,6 +705,16 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_settings->registerSetting("SkinsDir", "skins");
m_settings->registerSetting("JavaDir", "java");
+#ifdef Q_OS_MACOS
+ // Folder security-scoped bookmarks
+ m_settings->registerSetting("InstanceDirBookmark", "");
+ m_settings->registerSetting("CentralModsDirBookmark", "");
+ m_settings->registerSetting("IconsDirBookmark", "");
+ m_settings->registerSetting("DownloadsDirBookmark", "");
+ m_settings->registerSetting("SkinsDirBookmark", "");
+ m_settings->registerSetting("JavaDirBookmark", "");
+#endif
+
// Editors
m_settings->registerSetting("JsonEditor", QString());
@@ -958,12 +965,27 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
// Themes
m_themeManager = std::make_unique();
+#ifdef Q_OS_MACOS
+ // for macOS: getting directory settings will generate URL security-scoped bookmarks if needed and not present
+ // this facilitates a smooth transition from a non-sandboxed version of the launcher, that likely can access the directory,
+ // and a sandboxed version that can't access the directory without a bookmark
+ // this section can likely be removed once the sandboxed version has been released for a while and migrations aren't done anymore
+ {
+ m_settings->get("InstanceDir");
+ m_settings->get("CentralModsDir");
+ m_settings->get("IconsDir");
+ m_settings->get("DownloadsDir");
+ m_settings->get("SkinsDir");
+ m_settings->get("JavaDir");
+ }
+#endif
+
// initialize and load all instances
{
auto InstDirSetting = m_settings->getSetting("InstanceDir");
// instance path: check for problems with '!' in instance path and warn the user in the log
// and remember that we have to show him a dialog when the gui starts (if it does so)
- QString instDir = InstDirSetting->get().toString();
+ QString instDir = m_settings->get("InstanceDir").toString();
qInfo() << "Instance path : " << instDir;
if (FS::checkProblemticPathJava(QDir(instDir))) {
qWarning() << "Your instance path contains \'!\' and this is known to cause java problems!";
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index f5133b299..c59c1a0bc 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -1053,8 +1053,6 @@ SET(LAUNCHER_SOURCES
ui/dialogs/ImportResourceDialog.h
ui/dialogs/MSALoginDialog.cpp
ui/dialogs/MSALoginDialog.h
- ui/dialogs/OfflineLoginDialog.cpp
- ui/dialogs/OfflineLoginDialog.h
ui/dialogs/NewComponentDialog.cpp
ui/dialogs/NewComponentDialog.h
ui/dialogs/NewInstanceDialog.cpp
@@ -1081,6 +1079,8 @@ SET(LAUNCHER_SOURCES
ui/dialogs/ResourceUpdateDialog.h
ui/dialogs/InstallLoaderDialog.cpp
ui/dialogs/InstallLoaderDialog.h
+ ui/dialogs/ChooseOfflineNameDialog.cpp
+ ui/dialogs/ChooseOfflineNameDialog.h
ui/dialogs/skins/SkinManageDialog.cpp
ui/dialogs/skins/SkinManageDialog.h
@@ -1173,6 +1173,15 @@ if (NOT Apple)
)
endif()
+if (APPLE)
+ set(LAUNCHER_SOURCES
+ ${LAUNCHER_SOURCES}
+
+ macsandbox/SecurityBookmarkFileAccess.h
+ macsandbox/SecurityBookmarkFileAccess.mm
+ )
+endif()
+
if(WIN32)
set(LAUNCHER_SOURCES
console/WindowsConsole.h
@@ -1234,13 +1243,13 @@ qt_wrap_ui(LAUNCHER_UI
ui/dialogs/IconPickerDialog.ui
ui/dialogs/ImportResourceDialog.ui
ui/dialogs/MSALoginDialog.ui
- ui/dialogs/OfflineLoginDialog.ui
ui/dialogs/AboutDialog.ui
ui/dialogs/ReviewMessageBox.ui
ui/dialogs/ScrollMessageBox.ui
ui/dialogs/BlockedModsDialog.ui
ui/dialogs/ChooseProviderDialog.ui
ui/dialogs/skins/SkinManageDialog.ui
+ ui/dialogs/ChooseOfflineNameDialog.ui
)
qt_wrap_ui(PRISM_UPDATE_UI
@@ -1296,8 +1305,15 @@ target_link_libraries(Launcher_logic
qdcss
BuildConfig
Qt${QT_VERSION_MAJOR}::Widgets
- qrcodegenerator
)
+
+if(TARGET PkgConfig::libqrencode)
+ target_link_libraries(Launcher_logic PkgConfig::libqrencode)
+else()
+ target_include_directories(Launcher_logic PRIVATE ${LIBQRENCODE_INCLUDE_DIR})
+ target_link_libraries(Launcher_logic ${LIBQRENCODE_LIBRARIES})
+endif()
+
if(TARGET PkgConfig::tomlplusplus)
target_link_libraries(Launcher_logic PkgConfig::tomlplusplus)
else()
diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp
index 1ace331ce..5b44dbd48 100644
--- a/launcher/FileSystem.cpp
+++ b/launcher/FileSystem.cpp
@@ -59,10 +59,8 @@
#if defined Q_OS_WIN32
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
-#include
#include
#include
-#include
#include
#include
#include
diff --git a/launcher/Json.cpp b/launcher/Json.cpp
index 8eedb9b05..dd7287e00 100644
--- a/launcher/Json.cpp
+++ b/launcher/Json.cpp
@@ -153,7 +153,7 @@ QJsonValue toJson(const QVariant& variant)
template <>
QByteArray requireIsType(const QJsonValue& value, const QString& what)
{
- const QString string = ensureIsType(value, what);
+ const QString string = value.toString(what);
// ensure that the string can be safely cast to Latin1
if (string != QString::fromLatin1(string.toLatin1())) {
throw JsonException(what + " is not encodable as Latin1");
@@ -221,7 +221,7 @@ QDateTime requireIsType(const QJsonValue& value, const QString& what)
template <>
QUrl requireIsType(const QJsonValue& value, const QString& what)
{
- const QString string = ensureIsType(value, what);
+ const QString string = value.toString(what);
if (string.isEmpty()) {
return QUrl();
}
@@ -287,7 +287,7 @@ QStringList toStringList(const QString& jsonString)
if (parseError.error != QJsonParseError::NoError || !doc.isArray())
return {};
try {
- return ensureIsArrayOf(doc.array(), "");
+ return requireIsArrayOf(doc);
} catch (Json::JsonException& e) {
return {};
}
diff --git a/launcher/Json.h b/launcher/Json.h
index e51c737c2..8a994d7bc 100644
--- a/launcher/Json.h
+++ b/launcher/Json.h
@@ -153,18 +153,6 @@ QUrl requireIsType(const QJsonValue& value, const QString& what);
// the following functions are higher level functions, that make use of the above functions for
// type conversion
-template
-T ensureIsType(const QJsonValue& value, const T default_ = T(), const QString& what = "Value")
-{
- if (value.isUndefined() || value.isNull()) {
- return default_;
- }
- try {
- return requireIsType(value, what);
- } catch (const JsonException&) {
- return default_;
- }
-}
/// @throw JsonException
template
@@ -177,16 +165,6 @@ T requireIsType(const QJsonObject& parent, const QString& key, const QString& wh
return requireIsType(parent.value(key), localWhat);
}
-template
-T ensureIsType(const QJsonObject& parent, const QString& key, const T default_ = T(), const QString& what = "__placeholder__")
-{
- const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
- if (!parent.contains(key)) {
- return default_;
- }
- return ensureIsType(parent.value(key), default_, localWhat);
-}
-
template
QList requireIsArrayOf(const QJsonDocument& doc)
{
@@ -198,26 +176,6 @@ QList requireIsArrayOf(const QJsonDocument& doc)
return out;
}
-template
-QList ensureIsArrayOf(const QJsonValue& value, const QString& what = "Value")
-{
- const QJsonArray array = ensureIsType(value, QJsonArray(), what);
- QList out;
- for (const QJsonValue val : array) {
- out.append(requireIsType(val, what));
- }
- return out;
-}
-
-template
-QList ensureIsArrayOf(const QJsonValue& value, const QList default_, const QString& what = "Value")
-{
- if (value.isUndefined()) {
- return default_;
- }
- return ensureIsArrayOf(value, what);
-}
-
/// @throw JsonException
template
QList requireIsArrayOf(const QJsonObject& parent, const QString& key, const QString& what = "__placeholder__")
@@ -226,20 +184,13 @@ QList requireIsArrayOf(const QJsonObject& parent, const QString& key, const Q
if (!parent.contains(key)) {
throw JsonException(localWhat + "s parent does not contain " + localWhat);
}
- return ensureIsArrayOf(parent.value(key), localWhat);
-}
-template
-QList ensureIsArrayOf(const QJsonObject& parent,
- const QString& key,
- const QList& default_ = QList(),
- const QString& what = "__placeholder__")
-{
- const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
- if (!parent.contains(key)) {
- return default_;
+ const QJsonArray array = parent[key].toArray();
+ QList out;
+ for (const QJsonValue val : array) {
+ out.append(requireIsType(val, "Document"));
}
- return ensureIsArrayOf(parent.value(key), default_, localWhat);
+ return out;
}
// this macro part could be replaced by variadic functions that just pass on their arguments, but that wouldn't work well with IDE helpers
@@ -248,18 +199,9 @@ QList ensureIsArrayOf(const QJsonObject& parent,
{ \
return requireIsType(value, what); \
} \
- inline TYPE ensure##NAME(const QJsonValue& value, const TYPE default_ = TYPE(), const QString& what = "Value") \
- { \
- return ensureIsType(value, default_, what); \
- } \
inline TYPE require##NAME(const QJsonObject& parent, const QString& key, const QString& what = "__placeholder__") \
{ \
return requireIsType(parent, key, what); \
- } \
- inline TYPE ensure##NAME(const QJsonObject& parent, const QString& key, const TYPE default_ = TYPE(), \
- const QString& what = "__placeholder") \
- { \
- return ensureIsType(parent, key, default_, what); \
}
JSON_HELPERFUNCTIONS(Array, QJsonArray)
diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp
index 26f539e15..2273318ee 100644
--- a/launcher/LaunchController.cpp
+++ b/launcher/LaunchController.cpp
@@ -61,6 +61,7 @@
#include "JavaCommon.h"
#include "launch/steps/TextPrint.h"
#include "tasks/Task.h"
+#include "ui/dialogs/ChooseOfflineNameDialog.h"
LaunchController::LaunchController() : Task() {}
@@ -157,10 +158,15 @@ QString LaunchController::askOfflineName(QString playerName, bool demo, bool& ok
QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString();
QString usedname = lastOfflinePlayerName.isEmpty() ? playerName : lastOfflinePlayerName;
- QString name = QInputDialog::getText(m_parentWidget, tr("Player name"), message, QLineEdit::Normal, usedname, &ok);
- if (!ok)
+
+ ChooseOfflineNameDialog dialog(message, m_parentWidget);
+ dialog.setWindowTitle(tr("Player name"));
+ dialog.setUsername(usedname);
+ if (dialog.exec() != QDialog::Accepted) {
return {};
- if (name.length()) {
+ }
+
+ if (const QString name = dialog.getUsername(); !name.isEmpty()) {
usedname = name;
APPLICATION->settings()->set("LastOfflinePlayerName", usedname);
}
diff --git a/launcher/MangoHud.cpp b/launcher/MangoHud.cpp
index 29a7c63d9..d85100207 100644
--- a/launcher/MangoHud.cpp
+++ b/launcher/MangoHud.cpp
@@ -111,8 +111,8 @@ QString getLibraryString()
try {
auto conf = Json::requireDocument(filePath, vkLayer);
auto confObject = Json::requireObject(conf, vkLayer);
- auto layer = Json::ensureObject(confObject, "layer");
- QString libraryName = Json::ensureString(layer, "library_path");
+ auto layer = confObject["layer"].toObject();
+ QString libraryName = layer["library_path"].toString();
if (libraryName.isEmpty()) {
continue;
diff --git a/launcher/java/JavaMetadata.cpp b/launcher/java/JavaMetadata.cpp
index d4da95457..115baa9e5 100644
--- a/launcher/java/JavaMetadata.cpp
+++ b/launcher/java/JavaMetadata.cpp
@@ -52,27 +52,27 @@ MetadataPtr parseJavaMeta(const QJsonObject& in)
{
auto meta = std::make_shared();
- meta->m_name = Json::ensureString(in, "name", "");
- meta->vendor = Json::ensureString(in, "vendor", "");
- meta->url = Json::ensureString(in, "url", "");
- meta->releaseTime = timeFromS3Time(Json::ensureString(in, "releaseTime", ""));
- meta->downloadType = parseDownloadType(Json::ensureString(in, "downloadType", ""));
- meta->packageType = Json::ensureString(in, "packageType", "");
- meta->runtimeOS = Json::ensureString(in, "runtimeOS", "unknown");
+ meta->m_name = in["name"].toString("");
+ meta->vendor = in["vendor"].toString("");
+ meta->url = in["url"].toString("");
+ meta->releaseTime = timeFromS3Time(in["releaseTime"].toString(""));
+ meta->downloadType = parseDownloadType(in["downloadType"].toString(""));
+ meta->packageType = in["packageType"].toString("");
+ meta->runtimeOS = in["runtimeOS"].toString("unknown");
if (in.contains("checksum")) {
auto obj = Json::requireObject(in, "checksum");
- meta->checksumHash = Json::ensureString(obj, "hash", "");
- meta->checksumType = Json::ensureString(obj, "type", "");
+ meta->checksumHash = obj["hash"].toString("");
+ meta->checksumType = obj["type"].toString("");
}
if (in.contains("version")) {
auto obj = Json::requireObject(in, "version");
- auto name = Json::ensureString(obj, "name", "");
- auto major = Json::ensureInteger(obj, "major", 0);
- auto minor = Json::ensureInteger(obj, "minor", 0);
- auto security = Json::ensureInteger(obj, "security", 0);
- auto build = Json::ensureInteger(obj, "build", 0);
+ auto name = obj["name"].toString("");
+ auto major = obj["major"].toInteger();
+ auto minor = obj["minor"].toInteger();
+ auto security = obj["security"].toInteger();
+ auto build = obj["build"].toInteger();
meta->version = JavaVersion(major, minor, security, build, name);
}
return meta;
diff --git a/launcher/java/download/ManifestDownloadTask.cpp b/launcher/java/download/ManifestDownloadTask.cpp
index 20b39e751..04d28a5cc 100644
--- a/launcher/java/download/ManifestDownloadTask.cpp
+++ b/launcher/java/download/ManifestDownloadTask.cpp
@@ -77,27 +77,27 @@ void ManifestDownloadTask::downloadJava(const QJsonDocument& doc)
// valid json doc, begin making jre spot
FS::ensureFolderPathExists(m_final_path);
std::vector toDownload;
- auto list = Json::ensureObject(Json::ensureObject(doc.object()), "files");
+ auto list = doc.object()["files"].toObject();
for (const auto& paths : list.keys()) {
auto file = FS::PathCombine(m_final_path, paths);
- const QJsonObject& meta = Json::ensureObject(list, paths);
- auto type = Json::ensureString(meta, "type");
+ const QJsonObject& meta = list[paths].toObject();
+ auto type = meta["type"].toString();
if (type == "directory") {
FS::ensureFolderPathExists(file);
} else if (type == "link") {
// this is *nix only !
- auto path = Json::ensureString(meta, "target");
+ auto path = meta["target"].toString();
if (!path.isEmpty()) {
QFile::link(path, file);
}
} else if (type == "file") {
// TODO download compressed version if it exists ?
- auto raw = Json::ensureObject(Json::ensureObject(meta, "downloads"), "raw");
- auto isExec = Json::ensureBoolean(meta, "executable", false);
- auto url = Json::ensureString(raw, "url");
+ auto raw = meta["downloads"].toObject()["raw"].toObject();
+ auto isExec = meta["executable"].toBool();
+ auto url = raw["url"].toString();
if (!url.isEmpty() && QUrl(url).isValid()) {
- auto f = File{ file, url, QByteArray::fromHex(Json::ensureString(raw, "sha1").toLatin1()), isExec };
+ auto f = File{ file, url, QByteArray::fromHex(raw["sha1"].toString().toLatin1()), isExec };
toDownload.push_back(f);
}
}
@@ -134,4 +134,4 @@ bool ManifestDownloadTask::abort()
emitAborted();
return aborted;
};
-} // namespace Java
\ No newline at end of file
+} // namespace Java
diff --git a/launcher/launch/LaunchTask.cpp b/launcher/launch/LaunchTask.cpp
index c524fa097..4468a2a77 100644
--- a/launcher/launch/LaunchTask.cpp
+++ b/launcher/launch/LaunchTask.cpp
@@ -234,29 +234,40 @@ bool LaunchTask::parseXmlLogs(QString const& line, MessageLevel::Enum level)
auto& model = *getLogModel();
model.append(MessageLevel::Error, tr("[Log4j Parse Error] Failed to parse log4j log event: %1").arg(err.value().errMessage));
return false;
- } else {
- if (!items.isEmpty()) {
- auto& model = *getLogModel();
- for (auto const& item : items) {
- if (std::holds_alternative(item)) {
- auto entry = std::get(item);
- auto msg = QString("[%1] [%2/%3] [%4]: %5")
- .arg(entry.timestamp.toString("HH:mm:ss"))
- .arg(entry.thread)
- .arg(entry.levelText)
- .arg(entry.logger)
- .arg(entry.message);
- msg = censorPrivateInfo(msg);
- model.append(entry.level, msg);
- } else if (std::holds_alternative(item)) {
- auto msg = std::get(item).message;
- level = LogParser::guessLevel(msg, model.previousLevel());
- msg = censorPrivateInfo(msg);
- model.append(level, msg);
- }
- }
+ }
+
+ if (items.isEmpty())
+ return true;
+
+ auto model = getLogModel();
+ for (auto const& item : items) {
+ if (std::holds_alternative(item)) {
+ auto entry = std::get(item);
+ auto msg = QString("[%1] [%2/%3] [%4]: %5")
+ .arg(entry.timestamp.toString("HH:mm:ss"))
+ .arg(entry.thread)
+ .arg(entry.levelText)
+ .arg(entry.logger)
+ .arg(entry.message);
+ msg = censorPrivateInfo(msg);
+ model->append(entry.level, msg);
+ } else if (std::holds_alternative(item)) {
+ auto msg = std::get(item).message;
+
+ MessageLevel::Enum newLevel = MessageLevel::fromLine(msg);
+
+ if (newLevel == MessageLevel::Unknown)
+ newLevel = LogParser::guessLevel(line);
+
+ if (newLevel == MessageLevel::Unknown)
+ newLevel = model->previousLevel();
+
+ msg = censorPrivateInfo(msg);
+
+ model->append(newLevel, msg);
}
}
+
return true;
}
@@ -273,23 +284,10 @@ void LaunchTask::onLogLine(QString line, MessageLevel::Enum level)
return;
}
- // if the launcher part set a log level, use it
- auto innerLevel = MessageLevel::fromLine(line);
- if (innerLevel != MessageLevel::Unknown) {
- level = innerLevel;
- }
-
- auto& model = *getLogModel();
-
- // If the level is still undetermined, guess level
- if (level == MessageLevel::Unknown) {
- level = LogParser::guessLevel(line, model.previousLevel());
- }
-
// censor private user info
line = censorPrivateInfo(line);
- model.append(level, line);
+ getLogModel()->append(level, line);
}
void LaunchTask::emitSucceeded()
diff --git a/launcher/launch/LogModel.cpp b/launcher/launch/LogModel.cpp
index 90af9787d..f13e2c27f 100644
--- a/launcher/launch/LogModel.cpp
+++ b/launcher/launch/LogModel.cpp
@@ -169,8 +169,8 @@ bool LogModel::isOverFlow()
MessageLevel::Enum LogModel::previousLevel()
{
- if (!m_content.isEmpty()) {
- return m_content.last().level;
+ if (m_numLines > 0) {
+ return m_content[m_numLines - 1].level;
}
return MessageLevel::Unknown;
}
diff --git a/launcher/logs/LogParser.cpp b/launcher/logs/LogParser.cpp
index 0790dec4d..962e5184f 100644
--- a/launcher/logs/LogParser.cpp
+++ b/launcher/logs/LogParser.cpp
@@ -320,7 +320,7 @@ std::optional LogParser::parseLog4J()
throw std::runtime_error("unreachable: already verified this was a complete log4j:Event");
}
-MessageLevel::Enum LogParser::guessLevel(const QString& line, MessageLevel::Enum level)
+MessageLevel::Enum LogParser::guessLevel(const QString& line)
{
static const QRegularExpression LINE_WITH_LEVEL("^\\[(?[0-9:]+)\\] \\[[^/]+/(?[^\\]]+)\\]");
auto match = LINE_WITH_LEVEL.match(line);
@@ -328,24 +328,26 @@ MessageLevel::Enum LogParser::guessLevel(const QString& line, MessageLevel::Enum
// New style logs from log4j
QString timestamp = match.captured("timestamp");
QString levelStr = match.captured("level");
- level = MessageLevel::getLevel(levelStr);
+
+ return MessageLevel::getLevel(levelStr);
} else {
// Old style forge logs
if (line.contains("[INFO]") || line.contains("[CONFIG]") || line.contains("[FINE]") || line.contains("[FINER]") ||
line.contains("[FINEST]"))
- level = MessageLevel::Info;
+ return MessageLevel::Info;
if (line.contains("[SEVERE]") || line.contains("[STDERR]"))
- level = MessageLevel::Error;
+ return MessageLevel::Error;
if (line.contains("[WARNING]"))
- level = MessageLevel::Warning;
+ return MessageLevel::Warning;
if (line.contains("[DEBUG]"))
- level = MessageLevel::Debug;
+ return MessageLevel::Debug;
}
- if (level != MessageLevel::Unknown)
- return level;
if (line.contains("overwriting existing"))
return MessageLevel::Fatal;
- return MessageLevel::Info;
+ if (line == "---- Minecraft Crash Report ----")
+ return MessageLevel::Error;
+
+ return MessageLevel::Unknown;
}
diff --git a/launcher/logs/LogParser.h b/launcher/logs/LogParser.h
index aaf21e397..609913d3b 100644
--- a/launcher/logs/LogParser.h
+++ b/launcher/logs/LogParser.h
@@ -59,7 +59,7 @@ class LogParser {
std::optional getError();
/// guess log level from a line of game log
- static MessageLevel::Enum guessLevel(const QString& line, MessageLevel::Enum level);
+ static MessageLevel::Enum guessLevel(const QString& line);
protected:
std::optional parseAttributes();
diff --git a/launcher/macsandbox/SecurityBookmarkFileAccess.h b/launcher/macsandbox/SecurityBookmarkFileAccess.h
new file mode 100644
index 000000000..5bddf0e31
--- /dev/null
+++ b/launcher/macsandbox/SecurityBookmarkFileAccess.h
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (C) 2024 Kenneth Chew <79120643+kthchew@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 .
+ */
+
+#ifndef FILEACCESS_H
+#define FILEACCESS_H
+
+#include
+#include
+Q_FORWARD_DECLARE_OBJC_CLASS(NSData);
+Q_FORWARD_DECLARE_OBJC_CLASS(NSURL);
+Q_FORWARD_DECLARE_OBJC_CLASS(NSString);
+Q_FORWARD_DECLARE_OBJC_CLASS(NSAutoreleasePool);
+Q_FORWARD_DECLARE_OBJC_CLASS(NSMutableDictionary);
+Q_FORWARD_DECLARE_OBJC_CLASS(NSMutableSet);
+class QString;
+class QByteArray;
+class QUrl;
+
+class SecurityBookmarkFileAccess {
+ /// The keys are bookmarks and the values are URLs.
+ NSMutableDictionary* m_bookmarks;
+ /// The keys are paths and the values are bookmarks.
+ NSMutableDictionary* m_paths;
+ /// Contains URLs that are currently being accessed.
+ NSMutableSet* m_activeURLs;
+
+ bool m_readOnly;
+
+ NSURL* securityScopedBookmarkToNSURL(QByteArray& bookmark, bool& isStale);
+public:
+ /// \param readOnly A boolean indicating whether the bookmark should be read-only.
+ SecurityBookmarkFileAccess(bool readOnly = false);
+ ~SecurityBookmarkFileAccess();
+
+ /// Get a security scoped bookmark from a URL.
+ ///
+ /// The URL must be accessible before calling this function. That is, call `startAccessingSecurityScopedResource()` before calling
+ /// this function. Note that this is called implicitly if the user selects the directory from a file picker.
+ /// \param url The URL to get the security scoped bookmark from.
+ /// \return A QByteArray containing the security scoped bookmark.
+ QByteArray urlToSecurityScopedBookmark(const QUrl& url);
+ /// Get a security scoped bookmark from a path.
+ ///
+ /// The path must be accessible before calling this function. That is, call `startAccessingSecurityScopedResource()` before calling
+ /// this function. Note that this is called implicitly if the user selects the directory from a file picker.
+ /// \param path The path to get the security scoped bookmark from.
+ /// \return A QByteArray containing the security scoped bookmark.
+ QByteArray pathToSecurityScopedBookmark(const QString& path);
+ /// Get a QUrl from a security scoped bookmark. If the bookmark is stale, isStale will be set to true and the bookmark will be updated.
+ ///
+ /// You must check whether the URL is valid before using it.
+ /// \param bookmark The security scoped bookmark to get the URL from.
+ /// \param isStale A boolean that will be set to true if the bookmark is stale.
+ /// \return The URL from the security scoped bookmark.
+ QUrl securityScopedBookmarkToURL(QByteArray& bookmark, bool& isStale);
+
+ /// Makes the file or directory at the path pointed to by the bookmark accessible. Unlike `startAccessingSecurityScopedResource()`, this
+ /// class ensures that only one "access" is active at a time. Calling this function again after the security-scoped resource has
+ /// already been used will do nothing, and a single call to `stopUsingSecurityScopedBookmark()` will release the resource provided that
+ /// this is the only `SecurityBookmarkFileAccess` accessing the resource.
+ ///
+ /// If the bookmark is stale, `isStale` will be set to true and the bookmark will be updated. Stored copies of the bookmark need to be
+ /// updated.
+ /// \param bookmark The security scoped bookmark to start accessing.
+ /// \param isStale A boolean that will be set to true if the bookmark is stale.
+ /// \return A boolean indicating whether the bookmark was successfully accessed.
+ bool startUsingSecurityScopedBookmark(QByteArray& bookmark, bool& isStale);
+ void stopUsingSecurityScopedBookmark(QByteArray& bookmark);
+
+ /// Returns true if access to the `path` is currently being maintained by this object.
+ bool isAccessingPath(const QString& path);
+};
+
+#endif //FILEACCESS_H
diff --git a/launcher/macsandbox/SecurityBookmarkFileAccess.mm b/launcher/macsandbox/SecurityBookmarkFileAccess.mm
new file mode 100644
index 000000000..bee854abe
--- /dev/null
+++ b/launcher/macsandbox/SecurityBookmarkFileAccess.mm
@@ -0,0 +1,172 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (C) 2024 Kenneth Chew <79120643+kthchew@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 .
+ */
+
+#include "SecurityBookmarkFileAccess.h"
+
+#include
+#include
+#include
+
+QByteArray SecurityBookmarkFileAccess::urlToSecurityScopedBookmark(const QUrl& url)
+{
+ if (!url.isLocalFile())
+ return {};
+
+ NSError* error = nil;
+ NSURL* nsurl = [url.toNSURL() absoluteURL];
+ NSData* bookmark;
+ if ([m_paths objectForKey:[nsurl path]]) {
+ bookmark = m_paths[[nsurl path]];
+ } else {
+ bookmark = [nsurl bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope |
+ (m_readOnly ? NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess : 0)
+ includingResourceValuesForKeys:nil
+ relativeToURL:nil
+ error:&error];
+ }
+ if (error) {
+ return {};
+ }
+
+ // remove/reapply access to ensure that write access is immediately cut off for read-only bookmarks
+ // sometimes you need to call this twice to actually stop access (extra calls aren't harmful)
+ [nsurl stopAccessingSecurityScopedResource];
+ [nsurl stopAccessingSecurityScopedResource];
+ nsurl = [NSURL URLByResolvingBookmarkData:bookmark
+ options:NSURLBookmarkResolutionWithSecurityScope |
+ (m_readOnly ? NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess : 0)
+ relativeToURL:nil
+ bookmarkDataIsStale:nil
+ error:&error];
+ m_paths[[nsurl path]] = bookmark;
+ m_bookmarks[bookmark] = nsurl;
+
+ QByteArray qBookmark = QByteArray::fromNSData(bookmark);
+ bool isStale = false;
+ startUsingSecurityScopedBookmark(qBookmark, isStale);
+
+ return qBookmark;
+}
+
+SecurityBookmarkFileAccess::SecurityBookmarkFileAccess(bool readOnly) : m_readOnly(readOnly)
+{
+ m_bookmarks = [NSMutableDictionary new];
+ m_paths = [NSMutableDictionary new];
+ m_activeURLs = [NSMutableSet new];
+}
+
+SecurityBookmarkFileAccess::~SecurityBookmarkFileAccess()
+{
+ for (NSURL* url : m_activeURLs) {
+ [url stopAccessingSecurityScopedResource];
+ }
+}
+
+QByteArray SecurityBookmarkFileAccess::pathToSecurityScopedBookmark(const QString& path)
+{
+ return urlToSecurityScopedBookmark(QUrl::fromLocalFile(path));
+}
+
+NSURL* SecurityBookmarkFileAccess::securityScopedBookmarkToNSURL(QByteArray& bookmark, bool& isStale)
+{
+ NSError* error = nil;
+ BOOL localStale = NO;
+ NSURL* nsurl = [NSURL URLByResolvingBookmarkData:bookmark.toNSData()
+ options:NSURLBookmarkResolutionWithSecurityScope |
+ (m_readOnly ? NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess : 0)
+ relativeToURL:nil
+ bookmarkDataIsStale:&localStale
+ error:&error];
+ if (error) {
+ return nil;
+ }
+ isStale = localStale;
+ if (isStale) {
+ NSData* nsBookmark = [nsurl bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope |
+ (m_readOnly ? NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess : 0)
+ includingResourceValuesForKeys:nil
+ relativeToURL:nil
+ error:&error];
+ if (error) {
+ return nil;
+ }
+ bookmark = QByteArray::fromNSData(nsBookmark);
+ }
+
+ NSData* nsBookmark = bookmark.toNSData();
+ m_paths[[nsurl path]] = nsBookmark;
+ m_bookmarks[nsBookmark] = nsurl;
+
+ return nsurl;
+}
+
+QUrl SecurityBookmarkFileAccess::securityScopedBookmarkToURL(QByteArray& bookmark, bool& isStale)
+{
+ if (bookmark.isEmpty())
+ return {};
+
+ NSURL* url = securityScopedBookmarkToNSURL(bookmark, isStale);
+ if (!url)
+ return {};
+
+ return QUrl::fromNSURL(url);
+}
+
+bool SecurityBookmarkFileAccess::startUsingSecurityScopedBookmark(QByteArray& bookmark, bool& isStale)
+{
+ NSURL* url = [m_bookmarks objectForKey:bookmark.toNSData()] ? m_bookmarks[bookmark.toNSData()]
+ : securityScopedBookmarkToNSURL(bookmark, isStale);
+ if ([m_activeURLs containsObject:url])
+ return false;
+
+ [url stopAccessingSecurityScopedResource];
+ if ([url startAccessingSecurityScopedResource]) {
+ [m_activeURLs addObject:url];
+ return true;
+ }
+ return false;
+}
+
+void SecurityBookmarkFileAccess::stopUsingSecurityScopedBookmark(QByteArray& bookmark)
+{
+ if (![m_bookmarks objectForKey:bookmark.toNSData()])
+ return;
+ NSURL* url = m_bookmarks[bookmark.toNSData()];
+
+ if ([m_activeURLs containsObject:url]) {
+ [url stopAccessingSecurityScopedResource];
+ [url stopAccessingSecurityScopedResource];
+
+ [m_activeURLs removeObject:url];
+ [m_paths removeObjectForKey:[url path]];
+ [m_bookmarks removeObjectForKey:bookmark.toNSData()];
+ }
+}
+
+bool SecurityBookmarkFileAccess::isAccessingPath(const QString& path)
+{
+ NSData* bookmark = [m_paths objectForKey:path.toNSString()];
+ if (!bookmark && path.endsWith('/')) {
+ bookmark = [m_paths objectForKey:path.left(path.length() - 1).toNSString()];
+ }
+ if (!bookmark) {
+ return false;
+ }
+ NSURL* url = [m_bookmarks objectForKey:bookmark];
+ return [m_activeURLs containsObject:url];
+}
diff --git a/launcher/main.cpp b/launcher/main.cpp
index c41c510dd..2bce655d2 100644
--- a/launcher/main.cpp
+++ b/launcher/main.cpp
@@ -35,34 +35,8 @@
#include "Application.h"
-// #define BREAK_INFINITE_LOOP
-// #define BREAK_EXCEPTION
-// #define BREAK_RETURN
-
-#ifdef BREAK_INFINITE_LOOP
-#include
-#include
-#endif
-
int main(int argc, char* argv[])
{
-#ifdef BREAK_INFINITE_LOOP
- while (true) {
- std::this_thread::sleep_for(std::chrono::milliseconds(250));
- }
-#endif
-#ifdef BREAK_EXCEPTION
- throw 42;
-#endif
-#ifdef BREAK_RETURN
- return 42;
-#endif
-
-#if QT_VERSION <= QT_VERSION_CHECK(6, 0, 0)
- QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
- QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
-#endif
-
// initialize Qt
Application app(argc, argv);
diff --git a/launcher/meta/JsonFormat.cpp b/launcher/meta/JsonFormat.cpp
index 8d8466c87..db1947655 100644
--- a/launcher/meta/JsonFormat.cpp
+++ b/launcher/meta/JsonFormat.cpp
@@ -40,8 +40,8 @@ static std::shared_ptr parseIndexInternal(const QJsonObject& obj)
lists.reserve(objects.size());
std::transform(objects.begin(), objects.end(), std::back_inserter(lists), [](const QJsonObject& obj) {
VersionList::Ptr list = std::make_shared(requireString(obj, "uid"));
- list->setName(ensureString(obj, "name", QString()));
- list->setSha256(ensureString(obj, "sha256", QString()));
+ list->setName(obj["name"].toString());
+ list->setSha256(obj["sha256"].toString());
return list;
});
return std::make_shared(lists);
@@ -52,14 +52,14 @@ static Version::Ptr parseCommonVersion(const QString& uid, const QJsonObject& ob
{
Version::Ptr version = std::make_shared(uid, requireString(obj, "version"));
version->setTime(QDateTime::fromString(requireString(obj, "releaseTime"), Qt::ISODate).toMSecsSinceEpoch() / 1000);
- version->setType(ensureString(obj, "type", QString()));
- version->setRecommended(ensureBoolean(obj, QString("recommended"), false));
- version->setVolatile(ensureBoolean(obj, QString("volatile"), false));
+ version->setType(obj["type"].toString());
+ version->setRecommended(obj["recommended"].toBool());
+ version->setVolatile(obj["volatile"].toBool());
RequireSet reqs, conflicts;
parseRequires(obj, &reqs, "requires");
parseRequires(obj, &conflicts, "conflicts");
version->setRequires(reqs, conflicts);
- if (auto sha256 = ensureString(obj, "sha256", QString()); !sha256.isEmpty()) {
+ if (auto sha256 = obj["sha256"].toString(); !sha256.isEmpty()) {
version->setSha256(sha256);
}
return version;
@@ -89,7 +89,7 @@ static VersionList::Ptr parseVersionListInternal(const QJsonObject& obj)
});
VersionList::Ptr list = std::make_shared(uid);
- list->setName(ensureString(obj, "name", QString()));
+ list->setName(obj["name"].toString());
list->setVersions(versions);
return list;
}
@@ -171,8 +171,8 @@ void parseRequires(const QJsonObject& obj, RequireSet* ptr, const char* keyName)
while (iter != reqArray.end()) {
auto reqObject = requireObject(*iter);
auto uid = requireString(reqObject, "uid");
- auto equals = ensureString(reqObject, "equals", QString());
- auto suggests = ensureString(reqObject, "suggests", QString());
+ auto equals = reqObject["equals"].toString();
+ auto suggests = reqObject["suggests"].toString();
ptr->insert({ uid, equals, suggests });
iter++;
}
diff --git a/launcher/minecraft/OneSixVersionFormat.cpp b/launcher/minecraft/OneSixVersionFormat.cpp
index 32dd1875c..48ea3b894 100644
--- a/launcher/minecraft/OneSixVersionFormat.cpp
+++ b/launcher/minecraft/OneSixVersionFormat.cpp
@@ -259,8 +259,8 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument& doc
if (root.contains("runtimes")) {
out->runtimes = {};
- for (auto runtime : ensureArray(root, "runtimes")) {
- out->runtimes.append(Java::parseJavaMeta(ensureObject(runtime)));
+ for (auto runtime : root["runtimes"].toArray()) {
+ out->runtimes.append(Java::parseJavaMeta(runtime.toObject()));
}
}
diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp
index 8475a1f32..74c8d5ef8 100644
--- a/launcher/minecraft/PackProfile.cpp
+++ b/launcher/minecraft/PackProfile.cpp
@@ -130,18 +130,18 @@ static ComponentPtr componentFromJsonV1(PackProfile* parent, const QString& comp
auto uid = Json::requireString(obj.value("uid"));
auto filePath = componentJsonPattern.arg(uid);
auto component = makeShared(parent, uid);
- component->m_version = Json::ensureString(obj.value("version"));
- component->m_dependencyOnly = Json::ensureBoolean(obj.value("dependencyOnly"), false);
- component->m_important = Json::ensureBoolean(obj.value("important"), false);
+ component->m_version = obj.value("version").toString();
+ component->m_dependencyOnly = obj.value("dependencyOnly").toBool();
+ component->m_important = obj.value("important").toBool();
// cached
// TODO @RESILIENCE: ignore invalid values/structure here?
- component->m_cachedVersion = Json::ensureString(obj.value("cachedVersion"));
- component->m_cachedName = Json::ensureString(obj.value("cachedName"));
+ component->m_cachedVersion = obj.value("cachedVersion").toString();
+ component->m_cachedName = obj.value("cachedName").toString();
Meta::parseRequires(obj, &component->m_cachedRequires, "cachedRequires");
Meta::parseRequires(obj, &component->m_cachedConflicts, "cachedConflicts");
- component->m_cachedVolatile = Json::ensureBoolean(obj.value("volatile"), false);
- bool disabled = Json::ensureBoolean(obj.value("disabled"), false);
+ component->m_cachedVolatile = obj.value("volatile").toBool();
+ bool disabled = obj.value("disabled").toBool();
component->setEnabled(!disabled);
return component;
}
diff --git a/launcher/minecraft/auth/AccountData.h b/launcher/minecraft/auth/AccountData.h
index df7d569da..3b76012cb 100644
--- a/launcher/minecraft/auth/AccountData.h
+++ b/launcher/minecraft/auth/AccountData.h
@@ -41,7 +41,6 @@
#include
#include
-#include
#include
enum class Validity { None, Assumed, Certain };
diff --git a/launcher/minecraft/auth/steps/MSADeviceCodeStep.cpp b/launcher/minecraft/auth/steps/MSADeviceCodeStep.cpp
index 38ff90a47..7a4722f21 100644
--- a/launcher/minecraft/auth/steps/MSADeviceCodeStep.cpp
+++ b/launcher/minecraft/auth/steps/MSADeviceCodeStep.cpp
@@ -105,9 +105,8 @@ DeviceAuthorizationResponse parseDeviceAuthorizationResponse(const QByteArray& d
}
auto obj = doc.object();
return {
- Json::ensureString(obj, "device_code"), Json::ensureString(obj, "user_code"), Json::ensureString(obj, "verification_uri"),
- Json::ensureInteger(obj, "expires_in"), Json::ensureInteger(obj, "interval"), Json::ensureString(obj, "error"),
- Json::ensureString(obj, "error_description"),
+ obj["device_code"].toString(), obj["user_code"].toString(), obj["verification_uri"].toString(), obj["expires_in"].toInt(),
+ obj["interval"].toInt(), obj["error"].toString(), obj["error_description"].toString(),
};
}
@@ -217,12 +216,12 @@ AuthenticationResponse parseAuthenticationResponse(const QByteArray& data)
return {};
}
auto obj = doc.object();
- return { Json::ensureString(obj, "access_token"),
- Json::ensureString(obj, "token_type"),
- Json::ensureString(obj, "refresh_token"),
- Json::ensureInteger(obj, "expires_in"),
- Json::ensureString(obj, "error"),
- Json::ensureString(obj, "error_description"),
+ return { obj["access_token"].toString(),
+ obj["token_type"].toString(),
+ obj["refresh_token"].toString(),
+ obj["expires_in"].toInt(),
+ obj["error"].toString(),
+ obj["error_description"].toString(),
obj.toVariantMap() };
}
diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h
index 0dea3c7f1..391c5c0c0 100644
--- a/launcher/minecraft/mod/ResourceFolderModel.h
+++ b/launcher/minecraft/mod/ResourceFolderModel.h
@@ -21,11 +21,11 @@ class QSortFilterProxyModel;
/* A macro to define useful functions to handle Resource* -> T* more easily on derived classes */
#define RESOURCE_HELPERS(T) \
- T& at(int index) \
+ T& at(int index) \
{ \
return *static_cast(m_resources[index].get()); \
} \
- const T& at(int index) const \
+ const T& at(int index) const \
{ \
return *static_cast(m_resources.at(index).get()); \
} \
diff --git a/launcher/minecraft/mod/tasks/LocalDataPackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalDataPackParseTask.cpp
index c63e0c65f..73e676dbd 100644
--- a/launcher/minecraft/mod/tasks/LocalDataPackParseTask.cpp
+++ b/launcher/minecraft/mod/tasks/LocalDataPackParseTask.cpp
@@ -180,7 +180,7 @@ bool processMCMeta(DataPack* pack, QByteArray&& raw_data)
auto json_doc = QJsonDocument::fromJson(raw_data);
auto pack_obj = Json::requireObject(json_doc.object(), "pack", {});
- pack->setPackFormat(Json::ensureInteger(pack_obj, "pack_format", 0));
+ pack->setPackFormat(pack_obj["pack_format"].toInt());
pack->setDescription(DataPackUtils::processComponent(pack_obj.value("description")));
} catch (Json::JsonException& e) {
qWarning() << "JsonException: " << e.what() << e.cause();
@@ -192,19 +192,19 @@ bool processMCMeta(DataPack* pack, QByteArray&& raw_data)
QString buildStyle(const QJsonObject& obj)
{
QStringList styles;
- if (auto color = Json::ensureString(obj, "color"); !color.isEmpty()) {
+ if (auto color = obj["color"].toString(); !color.isEmpty()) {
styles << QString("color: %1;").arg(color);
}
if (obj.contains("bold")) {
QString weight = "normal";
- if (Json::ensureBoolean(obj, "bold", false)) {
+ if (obj["bold"].toBool()) {
weight = "bold";
}
styles << QString("font-weight: %1;").arg(weight);
}
if (obj.contains("italic")) {
QString style = "normal";
- if (Json::ensureBoolean(obj, "italic", false)) {
+ if (obj["italic"].toBool()) {
style = "italic";
}
styles << QString("font-style: %1;").arg(style);
@@ -223,10 +223,10 @@ QString processComponent(const QJsonArray& value, bool strikethrough, bool under
QString processComponent(const QJsonObject& obj, bool strikethrough, bool underline)
{
- underline = Json::ensureBoolean(obj, "underlined", underline);
- strikethrough = Json::ensureBoolean(obj, "strikethrough", strikethrough);
+ underline = obj["underlined"].toBool(underline);
+ strikethrough = obj["strikethrough"].toBool(strikethrough);
- QString result = Json::ensureString(obj, "text");
+ QString result = obj["text"].toString();
if (underline) {
result = QString("%1").arg(result);
}
@@ -234,14 +234,14 @@ QString processComponent(const QJsonObject& obj, bool strikethrough, bool underl
result = QString("%1").arg(result);
}
// the extra needs to be a array
- result += processComponent(Json::ensureArray(obj, "extra"), strikethrough, underline);
+ result += processComponent(obj["extra"].toArray(), strikethrough, underline);
if (auto style = buildStyle(obj); !style.isEmpty()) {
result = QString("%2").arg(style, result);
}
if (obj.contains("clickEvent")) {
- auto click_event = Json::ensureObject(obj, "clickEvent");
- auto action = Json::ensureString(click_event, "action");
- auto value = Json::ensureString(click_event, "value");
+ auto click_event = obj["clickEvent"].toObject();
+ auto action = click_event["action"].toString();
+ auto value = click_event["value"].toString();
if (action == "open_url" && !value.isEmpty()) {
result = QString("%2").arg(value, result);
}
@@ -366,4 +366,4 @@ void LocalDataPackParseTask::executeTask()
}
emitSucceeded();
-}
\ No newline at end of file
+}
diff --git a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp
index 952115bed..38280f5af 100644
--- a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp
+++ b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp
@@ -75,11 +75,11 @@ ModDetails ReadMCModInfo(QByteArray contents)
val = jsonDoc.object().value("modListVersion");
}
- int version = Json::ensureInteger(val, -1);
+ int version = val.toInt(-1);
// Some mods set the number with "", so it's a String instead
if (version < 0)
- version = Json::ensureString(val, "").toInt();
+ version = val.toString("").toInt();
if (version != 2) {
qWarning() << QString(R"(The value of 'modListVersion' is "%1" (expected "2")! The file may be corrupted.)").arg(version);
@@ -298,7 +298,7 @@ ModDetails ReadQuiltModInfo(QByteArray contents)
QJsonParseError jsonError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
auto object = Json::requireObject(jsonDoc, "quilt.mod.json");
- auto schemaVersion = Json::ensureInteger(object.value("schema_version"), 0, "Quilt schema_version");
+ auto schemaVersion = object.value("schema_version").toInt();
// https://github.com/QuiltMC/rfcs/blob/be6ba280d785395fefa90a43db48e5bfc1d15eb4/specification/0002-quilt.mod.json.md
if (schemaVersion == 1) {
@@ -307,17 +307,17 @@ ModDetails ReadQuiltModInfo(QByteArray contents)
details.mod_id = Json::requireString(modInfo.value("id"), "Mod ID");
details.version = Json::requireString(modInfo.value("version"), "Mod version");
- auto modMetadata = Json::ensureObject(modInfo.value("metadata"));
+ auto modMetadata = modInfo.value("metadata").toObject();
- details.name = Json::ensureString(modMetadata.value("name"), details.mod_id);
- details.description = Json::ensureString(modMetadata.value("description"));
+ details.name = modMetadata.value("name").toString(details.mod_id);
+ details.description = modMetadata.value("description").toString();
- auto modContributors = Json::ensureObject(modMetadata.value("contributors"));
+ auto modContributors = modMetadata.value("contributors").toObject();
// We don't really care about the role of a contributor here
details.authors += modContributors.keys();
- auto modContact = Json::ensureObject(modMetadata.value("contact"));
+ auto modContact = modMetadata.value("contact").toObject();
if (modContact.contains("homepage")) {
details.homeurl = Json::requireString(modContact.value("homepage"));
diff --git a/launcher/minecraft/skins/SkinList.cpp b/launcher/minecraft/skins/SkinList.cpp
index be3bc776b..54c77bf25 100644
--- a/launcher/minecraft/skins/SkinList.cpp
+++ b/launcher/minecraft/skins/SkinList.cpp
@@ -75,9 +75,9 @@ bool SkinList::update()
try {
auto doc = Json::requireDocument(manifestInfo.absoluteFilePath(), "SkinList JSON file");
const auto root = doc.object();
- auto skins = Json::ensureArray(root, "skins");
+ auto skins = root["skins"].toArray();
for (auto jSkin : skins) {
- SkinModel s(m_dir, Json::ensureObject(jSkin));
+ SkinModel s(m_dir, jSkin.toObject());
if (s.isValid()) {
newSkins << s;
}
diff --git a/launcher/minecraft/skins/SkinModel.cpp b/launcher/minecraft/skins/SkinModel.cpp
index 209207215..789899388 100644
--- a/launcher/minecraft/skins/SkinModel.cpp
+++ b/launcher/minecraft/skins/SkinModel.cpp
@@ -21,12 +21,17 @@
#include
#include "FileSystem.h"
-#include "Json.h"
-static QImage improveSkin(const QImage& skin)
+static QImage improveSkin(QImage skin)
{
+ // It seems some older skins may use this format, which can't be drawn onto
+ // https://github.com/PrismLauncher/PrismLauncher/issues/4032
+ // https://doc.qt.io/qt-6/qpainter.html#begin
+ if (skin.format() == QImage::Format_Indexed8) {
+ skin = skin.convertToFormat(QImage::Format_RGB32);
+ }
if (skin.size() == QSize(64, 32)) { // old format
- QImage newSkin = QImage(QSize(64, 64), skin.format());
+ auto newSkin = QImage(QSize(64, 64), skin.format());
newSkin.fill(Qt::transparent);
QPainter p(&newSkin);
p.drawImage(QPoint(0, 0), skin.copy(QRect(0, 0, 64, 32))); // copy head
@@ -102,15 +107,15 @@ SkinModel::SkinModel(QString path) : m_path(path), m_texture(getSkin(path)), m_m
}
SkinModel::SkinModel(QDir skinDir, QJsonObject obj)
- : m_capeId(Json::ensureString(obj, "capeId")), m_model(Model::CLASSIC), m_url(Json::ensureString(obj, "url"))
+ : m_capeId(obj["capeId"].toString()), m_model(Model::CLASSIC), m_url(obj["url"].toString())
{
- auto name = Json::ensureString(obj, "name");
+ auto name = obj["name"].toString();
- if (auto model = Json::ensureString(obj, "model"); model == "SLIM") {
+ if (auto model = obj["model"].toString(); model == "SLIM") {
m_model = Model::SLIM;
}
m_path = skinDir.absoluteFilePath(name) + ".png";
- m_texture = QImage(getSkin(m_path));
+ m_texture = getSkin(m_path);
m_preview = generatePreviews(m_texture, m_model == Model::SLIM);
}
diff --git a/launcher/modplatform/EnsureMetadataTask.cpp b/launcher/modplatform/EnsureMetadataTask.cpp
index e170fbcd0..ecf83c7c2 100644
--- a/launcher/modplatform/EnsureMetadataTask.cpp
+++ b/launcher/modplatform/EnsureMetadataTask.cpp
@@ -374,8 +374,8 @@ Task::Ptr EnsureMetadataTask::flameVersionsTask()
}
for (auto match : data_arr) {
- auto match_obj = Json::ensureObject(match, {});
- auto file_obj = Json::ensureObject(match_obj, "file", {});
+ auto match_obj = match.toObject();
+ auto file_obj = match_obj["file"].toObject();
if (match_obj.isEmpty() || file_obj.isEmpty()) {
qWarning() << "Fingerprint match is empty!";
@@ -383,7 +383,7 @@ Task::Ptr EnsureMetadataTask::flameVersionsTask()
return;
}
- auto fingerprint = QString::number(Json::ensureVariant(file_obj, "fileFingerprint").toUInt());
+ auto fingerprint = QString::number(file_obj["fileFingerprint"].toInteger());
auto resource = m_resources.find(fingerprint);
if (resource == m_resources.end()) {
qWarning() << "Invalid fingerprint from the API response.";
diff --git a/launcher/modplatform/ModIndex.cpp b/launcher/modplatform/ModIndex.cpp
index b13087158..5e8759f97 100644
--- a/launcher/modplatform/ModIndex.cpp
+++ b/launcher/modplatform/ModIndex.cpp
@@ -31,7 +31,8 @@ static const QMap s_indexed_version_ty
{ "alpha", IndexedVersionType::VersionType::Alpha }
};
-static const QList loaderList = { NeoForge, Forge, Cauldron, LiteLoader, Quilt, Fabric, Babric, BTA, LegacyFabric, Ornithe, Rift };
+static const QList loaderList = { NeoForge, Forge, Cauldron, LiteLoader, Quilt, Fabric,
+ Babric, BTA, LegacyFabric, Ornithe, Rift };
QList modLoaderTypesToList(ModLoaderTypes flags)
{
diff --git a/launcher/modplatform/ResourceAPI.cpp b/launcher/modplatform/ResourceAPI.cpp
index 448efbc24..5aff9d217 100644
--- a/launcher/modplatform/ResourceAPI.cpp
+++ b/launcher/modplatform/ResourceAPI.cpp
@@ -67,7 +67,10 @@ Task::Ptr ResourceAPI::searchProjects(SearchArgs&& args, Callback(QString("%1::Versions").arg(args.pack.name), APPLICATION->network());
+ auto netJob = makeShared(QString("%1::Versions").arg(args.pack->name), APPLICATION->network());
auto response = std::make_shared();
netJob->addNetAction(Net::ApiDownload::makeByteArray(versions_url, response));
@@ -97,14 +100,14 @@ Task::Ptr ResourceAPI::getProjectVersions(VersionSearchArgs&& args, Callback unsortedVersions;
try {
- auto arr = doc.isObject() ? Json::ensureArray(doc.object(), "data") : doc.array();
+ auto arr = doc.isObject() ? doc.object()["data"].toArray() : doc.array();
for (auto versionIter : arr) {
auto obj = versionIter.toObject();
auto file = loadIndexedPackVersion(obj, args.resourceType);
if (!file.addonId.isValid())
- file.addonId = args.pack.addonId;
+ file.addonId = args.pack->addonId;
if (file.fileId.isValid() && !file.downloadUrl.isEmpty()) // Heuristic to check if the returned value is valid
unsortedVersions.append(file);
@@ -135,15 +138,18 @@ Task::Ptr ResourceAPI::getProjectVersions(VersionSearchArgs&& args, Callback&& callbacks) const
+Task::Ptr ResourceAPI::getProjectInfo(ProjectInfoArgs&& args, Callback&& callbacks) const
{
auto response = std::make_shared();
- auto job = getProject(args.pack.addonId.toString(), response);
+ auto job = getProject(args.pack->addonId.toString(), response);
QObject::connect(job.get(), &NetJob::succeeded, [this, response, callbacks, args] {
auto pack = args.pack;
@@ -159,8 +165,8 @@ Task::Ptr ResourceAPI::getProjectInfo(ProjectInfoArgs&& args, Callback versions;
diff --git a/launcher/modplatform/ResourceAPI.h b/launcher/modplatform/ResourceAPI.h
index 211a6e477..ca77dc8a7 100644
--- a/launcher/modplatform/ResourceAPI.h
+++ b/launcher/modplatform/ResourceAPI.h
@@ -88,7 +88,7 @@ class ResourceAPI {
};
struct VersionSearchArgs {
- ModPlatform::IndexedPack pack;
+ ModPlatform::IndexedPack::Ptr pack;
std::optional> mcVersions;
std::optional loaders;
@@ -96,7 +96,7 @@ class ResourceAPI {
};
struct ProjectInfoArgs {
- ModPlatform::IndexedPack pack;
+ ModPlatform::IndexedPack::Ptr pack;
};
struct DependencySearchArgs {
@@ -115,7 +115,7 @@ class ResourceAPI {
virtual Task::Ptr getProject(QString addonId, std::shared_ptr response) const;
virtual Task::Ptr getProjects(QStringList addonIds, std::shared_ptr response) const = 0;
- virtual Task::Ptr getProjectInfo(ProjectInfoArgs&&, Callback&&) const;
+ virtual Task::Ptr getProjectInfo(ProjectInfoArgs&&, Callback&&) const;
Task::Ptr getProjectVersions(VersionSearchArgs&& args, Callback>&& callbacks) const;
virtual Task::Ptr getDependencyVersion(DependencySearchArgs&&, Callback&&) const;
diff --git a/launcher/modplatform/ResourceType.h b/launcher/modplatform/ResourceType.h
index b9073aa17..7f3e3042c 100644
--- a/launcher/modplatform/ResourceType.h
+++ b/launcher/modplatform/ResourceType.h
@@ -33,7 +33,7 @@ enum class ResourceType { Mod, ResourcePack, ShaderPack, Modpack, DataPack, Worl
namespace ResourceTypeUtils {
static const std::set VALID_RESOURCES = { ResourceType::DataPack, ResourceType::ResourcePack, ResourceType::TexturePack,
- ResourceType::ShaderPack, ResourceType::World, ResourceType::Mod };
+ ResourceType::ShaderPack, ResourceType::World, ResourceType::Mod };
QString getName(ResourceType type);
} // namespace ResourceTypeUtils
} // namespace ModPlatform
\ No newline at end of file
diff --git a/launcher/modplatform/atlauncher/ATLPackIndex.cpp b/launcher/modplatform/atlauncher/ATLPackIndex.cpp
index c35569d45..d41e446cf 100644
--- a/launcher/modplatform/atlauncher/ATLPackIndex.cpp
+++ b/launcher/modplatform/atlauncher/ATLPackIndex.cpp
@@ -40,8 +40,8 @@ void ATLauncher::loadIndexedPack(ATLauncher::IndexedPack& m, QJsonObject& obj)
loadIndexedVersion(version, versionObj);
m.versions.append(version);
}
- m.system = Json::ensureBoolean(obj, QString("system"), false);
- m.description = Json::ensureString(obj, "description", "");
+ m.system = obj["system"].toBool();
+ m.description = obj["description"].toString("");
static const QRegularExpression s_regex("[^A-Za-z0-9]");
m.safeName = Json::requireString(obj, "name").replace(s_regex, "").toLower() + ".png";
diff --git a/launcher/modplatform/atlauncher/ATLPackManifest.cpp b/launcher/modplatform/atlauncher/ATLPackManifest.cpp
index 9ff2f339e..22f63ad0d 100644
--- a/launcher/modplatform/atlauncher/ATLPackManifest.cpp
+++ b/launcher/modplatform/atlauncher/ATLPackManifest.cpp
@@ -100,20 +100,20 @@ static ATLauncher::ModType parseModType(QString rawType)
static void loadVersionLoader(ATLauncher::VersionLoader& p, QJsonObject& obj)
{
p.type = Json::requireString(obj, "type");
- p.choose = Json::ensureBoolean(obj, QString("choose"), false);
+ p.choose = obj["choose"].toBool();
auto metadata = Json::requireObject(obj, "metadata");
- p.latest = Json::ensureBoolean(metadata, QString("latest"), false);
- p.recommended = Json::ensureBoolean(metadata, QString("recommended"), false);
+ p.latest = metadata["latest"].toBool();
+ p.recommended = metadata["recommended"].toBool();
// Minecraft Forge
- if (p.type == "forge") {
- p.version = Json::ensureString(metadata, "version", "");
+ if (p.type == "forge" || p.type == "neoforge") {
+ p.version = metadata["version"].toString("");
}
// Fabric Loader
if (p.type == "fabric") {
- p.version = Json::ensureString(metadata, "loader", "");
+ p.version = metadata["loader"].toString("");
}
}
@@ -126,7 +126,7 @@ static void loadVersionLibrary(ATLauncher::VersionLibrary& p, QJsonObject& obj)
p.download_raw = Json::requireString(obj, "download");
p.download = parseDownloadType(p.download_raw);
- p.server = Json::ensureString(obj, "server", "");
+ p.server = obj["server"].toString("");
}
static void loadVersionConfigs(ATLauncher::VersionConfigs& p, QJsonObject& obj)
@@ -141,7 +141,7 @@ static void loadVersionMod(ATLauncher::VersionMod& p, QJsonObject& obj)
p.version = Json::requireString(obj, "version");
p.url = Json::requireString(obj, "url");
p.file = Json::requireString(obj, "file");
- p.md5 = Json::ensureString(obj, "md5", "");
+ p.md5 = obj["md5"].toString("");
p.download_raw = Json::requireString(obj, "download");
p.download = parseDownloadType(p.download_raw);
@@ -161,7 +161,7 @@ static void loadVersionMod(ATLauncher::VersionMod& p, QJsonObject& obj)
if (obj.contains("extractTo")) {
p.extractTo_raw = Json::requireString(obj, "extractTo");
p.extractTo = parseModType(p.extractTo_raw);
- p.extractFolder = Json::ensureString(obj, "extractFolder", "").replace("%s%", "/");
+ p.extractFolder = obj["extractFolder"].toString("").replace("%s%", "/");
}
if (obj.contains("decompType")) {
@@ -170,23 +170,23 @@ static void loadVersionMod(ATLauncher::VersionMod& p, QJsonObject& obj)
p.decompFile = Json::requireString(obj, "decompFile");
}
- p.description = Json::ensureString(obj, QString("description"), "");
- p.optional = Json::ensureBoolean(obj, QString("optional"), false);
- p.recommended = Json::ensureBoolean(obj, QString("recommended"), false);
- p.selected = Json::ensureBoolean(obj, QString("selected"), false);
- p.hidden = Json::ensureBoolean(obj, QString("hidden"), false);
- p.library = Json::ensureBoolean(obj, QString("library"), false);
- p.group = Json::ensureString(obj, QString("group"), "");
+ p.description = obj["description"].toString("");
+ p.optional = obj["optional"].toBool();
+ p.recommended = obj["recommended"].toBool();
+ p.selected = obj["selected"].toBool();
+ p.hidden = obj["hidden"].toBool();
+ p.library = obj["library"].toBool();
+ p.group = obj["group"].toString("");
if (obj.contains("depends")) {
auto dependsArr = Json::requireArray(obj, "depends");
for (const auto depends : dependsArr) {
p.depends.append(Json::requireString(depends));
}
}
- p.colour = Json::ensureString(obj, QString("colour"), "");
- p.warning = Json::ensureString(obj, QString("warning"), "");
+ p.colour = obj["colour"].toString("");
+ p.warning = obj["warning"].toString("");
- p.client = Json::ensureBoolean(obj, QString("client"), false);
+ p.client = obj["client"].toBool();
// computed
p.effectively_hidden = p.hidden || p.library;
@@ -194,20 +194,20 @@ static void loadVersionMod(ATLauncher::VersionMod& p, QJsonObject& obj)
static void loadVersionMessages(ATLauncher::VersionMessages& m, QJsonObject& obj)
{
- m.install = Json::ensureString(obj, "install", "");
- m.update = Json::ensureString(obj, "update", "");
+ m.install = obj["install"].toString("");
+ m.update = obj["update"].toString("");
}
static void loadVersionMainClass(ATLauncher::PackVersionMainClass& m, QJsonObject& obj)
{
- m.mainClass = Json::ensureString(obj, "mainClass", "");
- m.depends = Json::ensureString(obj, "depends", "");
+ m.mainClass = obj["mainClass"].toString("");
+ m.depends = obj["depends"].toString("");
}
static void loadVersionExtraArguments(ATLauncher::PackVersionExtraArguments& a, QJsonObject& obj)
{
- a.arguments = Json::ensureString(obj, "arguments", "");
- a.depends = Json::ensureString(obj, "depends", "");
+ a.arguments = obj["arguments"].toString("");
+ a.depends = obj["depends"].toString("");
}
static void loadVersionKeep(ATLauncher::VersionKeep& k, QJsonObject& obj)
@@ -272,7 +272,7 @@ void ATLauncher::loadVersion(PackVersion& v, QJsonObject& obj)
{
v.version = Json::requireString(obj, "version");
v.minecraft = Json::requireString(obj, "minecraft");
- v.noConfigs = Json::ensureBoolean(obj, QString("noConfigs"), false);
+ v.noConfigs = obj["noConfigs"].toBool();
if (obj.contains("mainClass")) {
auto main = Json::requireObject(obj, "mainClass");
@@ -314,22 +314,22 @@ void ATLauncher::loadVersion(PackVersion& v, QJsonObject& obj)
loadVersionConfigs(v.configs, configsObj);
}
- auto colourObj = Json::ensureObject(obj, "colours");
+ auto colourObj = obj["colours"].toObject();
for (const auto& key : colourObj.keys()) {
v.colours[key] = Json::requireString(colourObj.value(key), "colour");
}
- auto warningsObj = Json::ensureObject(obj, "warnings");
+ auto warningsObj = obj["warnings"].toObject();
for (const auto& key : warningsObj.keys()) {
v.warnings[key] = Json::requireString(warningsObj.value(key), "warning");
}
- auto messages = Json::ensureObject(obj, "messages");
+ auto messages = obj["messages"].toObject();
loadVersionMessages(v.messages, messages);
- auto keeps = Json::ensureObject(obj, "keeps");
+ auto keeps = obj["keeps"].toObject();
loadVersionKeeps(v.keeps, keeps);
- auto deletes = Json::ensureObject(obj, "deletes");
+ auto deletes = obj["deletes"].toObject();
loadVersionDeletes(v.deletes, deletes);
}
diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp
index b0d9af804..2b634efef 100644
--- a/launcher/modplatform/flame/FlameAPI.cpp
+++ b/launcher/modplatform/flame/FlameAPI.cpp
@@ -59,7 +59,7 @@ QString FlameAPI::getModFileChangelog(int modId, int fileId)
return;
}
- changelog = Json::ensureString(doc.object(), "data");
+ changelog = doc.object()["data"].toString();
});
QObject::connect(netJob.get(), &NetJob::finished, [&lock] { lock.quit(); });
@@ -92,7 +92,7 @@ QString FlameAPI::getModDescription(int modId)
return;
}
- description = Json::ensureString(doc.object(), "data");
+ description = doc.object()["data"].toString();
});
QObject::connect(netJob.get(), &NetJob::finished, [&lock] { lock.quit(); });
diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h
index 799e142ce..8bcb3ff46 100644
--- a/launcher/modplatform/flame/FlameAPI.h
+++ b/launcher/modplatform/flame/FlameAPI.h
@@ -126,7 +126,7 @@ class FlameAPI : public ResourceAPI {
std::optional getVersionsURL(VersionSearchArgs const& args) const override
{
- auto addonId = args.pack.addonId.toString();
+ auto addonId = args.pack->addonId.toString();
QString url = QString(BuildConfig.FLAME_BASE_URL + "/mods/%1/files?pageSize=10000").arg(addonId);
if (args.mcVersions.has_value())
@@ -140,7 +140,7 @@ class FlameAPI : public ResourceAPI {
return url;
}
- QJsonArray documentToArray(QJsonDocument& obj) const override { return Json::ensureArray(obj.object(), "data"); }
+ QJsonArray documentToArray(QJsonDocument& obj) const override { return obj.object()["data"].toArray(); }
void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) const override { FlameMod::loadIndexedPack(m, obj); }
ModPlatform::IndexedVersion loadIndexedPackVersion(QJsonObject& obj, ModPlatform::ResourceType resourceType) const override
{
@@ -160,10 +160,7 @@ class FlameAPI : public ResourceAPI {
void loadExtraPackInfo(ModPlatform::IndexedPack& m, [[maybe_unused]] QJsonObject&) const override { FlameMod::loadBody(m); }
private:
- std::optional getInfoURL(QString const& id) const override
- {
- return QString(BuildConfig.FLAME_BASE_URL + "/mods/%1").arg(id);
- }
+ std::optional getInfoURL(QString const& id) const override { return QString(BuildConfig.FLAME_BASE_URL + "/mods/%1").arg(id); }
std::optional getDependencyURL(DependencySearchArgs const& args) const override
{
auto addonId = args.dependency.addonId.toString();
diff --git a/launcher/modplatform/flame/FlameCheckUpdate.cpp b/launcher/modplatform/flame/FlameCheckUpdate.cpp
index 17d13deda..937f3dc7a 100644
--- a/launcher/modplatform/flame/FlameCheckUpdate.cpp
+++ b/launcher/modplatform/flame/FlameCheckUpdate.cpp
@@ -46,7 +46,9 @@ void FlameCheckUpdate::executeTask()
connect(netJob, &Task::stepProgress, this, &FlameCheckUpdate::propagateStepProgress);
connect(netJob, &Task::details, this, &FlameCheckUpdate::setDetails);
for (auto* resource : m_resources) {
- auto versionsUrlOptional = api.getVersionsURL({ { resource->metadata()->project_id.toString() }, m_gameVersions });
+ auto project = std::make_shared();
+ project->addonId = resource->metadata()->project_id.toString();
+ auto versionsUrlOptional = api.getVersionsURL({ project, m_gameVersions });
if (!versionsUrlOptional.has_value())
continue;
diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp
index d92ee729c..a01de847b 100644
--- a/launcher/modplatform/flame/FlameModIndex.cpp
+++ b/launcher/modplatform/flame/FlameModIndex.cpp
@@ -15,23 +15,26 @@ void FlameMod::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
pack.provider = ModPlatform::ResourceProvider::FLAME;
pack.name = Json::requireString(obj, "name");
pack.slug = Json::requireString(obj, "slug");
- pack.websiteUrl = Json::ensureString(Json::ensureObject(obj, "links"), "websiteUrl", "");
- pack.description = Json::ensureString(obj, "summary", "");
+ pack.websiteUrl = obj["links"].toObject()["websiteUrl"].toString("");
+ pack.description = obj["summary"].toString("");
- QJsonObject logo = Json::ensureObject(obj, "logo");
- pack.logoName = Json::ensureString(logo, "title");
- pack.logoUrl = Json::ensureString(logo, "thumbnailUrl");
+ QJsonObject logo = obj["logo"].toObject();
+ pack.logoName = logo["title"].toString();
+ pack.logoUrl = logo["thumbnailUrl"].toString();
if (pack.logoUrl.isEmpty()) {
- pack.logoUrl = Json::ensureString(logo, "url");
+ pack.logoUrl = logo["url"].toString();
}
- auto authors = Json::ensureArray(obj, "authors");
- for (auto authorIter : authors) {
- auto author = Json::requireObject(authorIter);
- ModPlatform::ModpackAuthor packAuthor;
- packAuthor.name = Json::requireString(author, "name");
- packAuthor.url = Json::requireString(author, "url");
- pack.authors.append(packAuthor);
+ auto authors = obj["authors"].toArray();
+ if (!authors.isEmpty()) {
+ pack.authors.clear();
+ for (auto authorIter : authors) {
+ auto author = Json::requireObject(authorIter);
+ ModPlatform::ModpackAuthor packAuthor;
+ packAuthor.name = Json::requireString(author, "name");
+ packAuthor.url = Json::requireString(author, "url");
+ pack.authors.append(packAuthor);
+ }
}
pack.extraDataLoaded = false;
@@ -40,17 +43,17 @@ void FlameMod::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
void FlameMod::loadURLs(ModPlatform::IndexedPack& pack, QJsonObject& obj)
{
- auto links_obj = Json::ensureObject(obj, "links");
+ auto links_obj = obj["links"].toObject();
- pack.extraData.issuesUrl = Json::ensureString(links_obj, "issuesUrl");
+ pack.extraData.issuesUrl = links_obj["issuesUrl"].toString();
if (pack.extraData.issuesUrl.endsWith('/'))
pack.extraData.issuesUrl.chop(1);
- pack.extraData.sourceUrl = Json::ensureString(links_obj, "sourceUrl");
+ pack.extraData.sourceUrl = links_obj["sourceUrl"].toString();
if (pack.extraData.sourceUrl.endsWith('/'))
pack.extraData.sourceUrl.chop(1);
- pack.extraData.wikiUrl = Json::ensureString(links_obj, "wikiUrl");
+ pack.extraData.wikiUrl = links_obj["wikiUrl"].toString();
if (pack.extraData.wikiUrl.endsWith('/'))
pack.extraData.wikiUrl.chop(1);
@@ -136,7 +139,7 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->
file.fileId = Json::requireInteger(obj, "id");
file.date = Json::requireString(obj, "fileDate");
file.version = Json::requireString(obj, "displayName");
- file.downloadUrl = Json::ensureString(obj, "downloadUrl");
+ file.downloadUrl = obj["downloadUrl"].toString();
file.fileName = Json::requireString(obj, "fileName");
file.fileName = FS::RemoveInvalidPathChars(file.fileName);
@@ -156,11 +159,11 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->
}
file.version_type = ModPlatform::IndexedVersionType(ver_type);
- auto hash_list = Json::ensureArray(obj, "hashes");
+ auto hash_list = obj["hashes"].toArray();
for (auto h : hash_list) {
- auto hash_entry = Json::ensureObject(h);
+ auto hash_entry = h.toObject();
auto hash_types = ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::FLAME);
- auto hash_algo = enumToString(Json::ensureInteger(hash_entry, "algo", 1, "algorithm"));
+ auto hash_algo = enumToString(hash_entry["algo"].toInt(1));
if (hash_types.contains(hash_algo)) {
file.hash = Json::requireString(hash_entry, "value");
file.hash_type = hash_algo;
@@ -168,9 +171,9 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->
}
}
- auto dependencies = Json::ensureArray(obj, "dependencies");
+ auto dependencies = obj["dependencies"].toArray();
for (auto d : dependencies) {
- auto dep = Json::ensureObject(d);
+ auto dep = d.toObject();
ModPlatform::Dependency dependency;
dependency.addonId = Json::requireInteger(dep, "modId");
switch (Json::requireInteger(dep, "relationType")) {
diff --git a/launcher/modplatform/flame/FlamePackExportTask.cpp b/launcher/modplatform/flame/FlamePackExportTask.cpp
index 900fd1a87..98a61d0d1 100644
--- a/launcher/modplatform/flame/FlamePackExportTask.cpp
+++ b/launcher/modplatform/flame/FlamePackExportTask.cpp
@@ -199,8 +199,8 @@ void FlamePackExportTask::makeApiRequest()
return;
}
for (auto match : dataArr) {
- auto matchObj = Json::ensureObject(match, {});
- auto fileObj = Json::ensureObject(matchObj, "file", {});
+ auto matchObj = match.toObject();
+ auto fileObj = matchObj["file"].toObject();
if (matchObj.isEmpty() || fileObj.isEmpty()) {
qWarning() << "Fingerprint match is empty!";
@@ -208,7 +208,7 @@ void FlamePackExportTask::makeApiRequest()
return;
}
- auto fingerprint = QString::number(Json::ensureVariant(fileObj, "fileFingerprint").toUInt());
+ auto fingerprint = QString::number(fileObj["fileFingerprint"].toInteger());
auto mod = pendingHashes.find(fingerprint);
if (mod == pendingHashes.end()) {
qWarning() << "Invalid fingerprint from the API response.";
@@ -216,7 +216,7 @@ void FlamePackExportTask::makeApiRequest()
}
setStatus(tr("Parsing API response from CurseForge for '%1'...").arg(mod->name));
- if (Json::ensureBoolean(fileObj, "isAvailable", false, "isAvailable"))
+ if (fileObj["isAvailable"].toBool())
resolvedFiles.insert(mod->path, { Json::requireInteger(fileObj, "modId"), Json::requireInteger(fileObj, "id"),
mod->enabled, mod->isMod });
}
@@ -429,4 +429,4 @@ QByteArray FlamePackExportTask::generateHTML()
}
content = "";
return content.toUtf8();
-}
\ No newline at end of file
+}
diff --git a/launcher/modplatform/flame/PackManifest.cpp b/launcher/modplatform/flame/PackManifest.cpp
index 641fb5d9a..dc176d770 100644
--- a/launcher/modplatform/flame/PackManifest.cpp
+++ b/launcher/modplatform/flame/PackManifest.cpp
@@ -5,13 +5,13 @@ static void loadFileV1(Flame::File& f, QJsonObject& file)
{
f.projectId = Json::requireInteger(file, "projectID");
f.fileId = Json::requireInteger(file, "fileID");
- f.required = Json::ensureBoolean(file, QString("required"), true);
+ f.required = file["required"].toBool(true);
}
static void loadModloaderV1(Flame::Modloader& m, QJsonObject& modLoader)
{
m.id = Json::requireString(modLoader, "id");
- m.primary = Json::ensureBoolean(modLoader, QString("primary"), false);
+ m.primary = modLoader["primary"].toBool();
}
static void loadMinecraftV1(Flame::Minecraft& m, QJsonObject& minecraft)
@@ -19,15 +19,15 @@ static void loadMinecraftV1(Flame::Minecraft& m, QJsonObject& minecraft)
m.version = Json::requireString(minecraft, "version");
// extra libraries... apparently only used for a custom Minecraft launcher in the 1.2.5 FTB retro pack
// intended use is likely hardcoded in the 'Flame' client, the manifest says nothing
- m.libraries = Json::ensureString(minecraft, QString("libraries"), QString());
- auto arr = Json::ensureArray(minecraft, "modLoaders", QJsonArray());
+ m.libraries = minecraft["libraries"].toString();
+ auto arr = minecraft["modLoaders"].toArray();
for (QJsonValueRef item : arr) {
auto obj = Json::requireObject(item);
Flame::Modloader loader;
loadModloaderV1(loader, obj);
m.modLoaders.append(loader);
}
- m.recommendedRAM = Json::ensureInteger(minecraft, "recommendedRam", 0);
+ m.recommendedRAM = minecraft["recommendedRam"].toInt();
}
static void loadManifestV1(Flame::Manifest& pack, QJsonObject& manifest)
@@ -36,11 +36,11 @@ static void loadManifestV1(Flame::Manifest& pack, QJsonObject& manifest)
loadMinecraftV1(pack.minecraft, mc);
- pack.name = Json::ensureString(manifest, QString("name"), "Unnamed");
- pack.version = Json::ensureString(manifest, QString("version"), QString());
- pack.author = Json::ensureString(manifest, QString("author"), "Anonymous");
+ pack.name = manifest["name"].toString("Unnamed");
+ pack.version = manifest["version"].toString();
+ pack.author = manifest["author"].toString("Anonymous");
- auto arr = Json::ensureArray(manifest, "files", QJsonArray());
+ auto arr = manifest["files"].toArray();
for (auto item : arr) {
auto obj = Json::requireObject(item);
@@ -50,7 +50,7 @@ static void loadManifestV1(Flame::Manifest& pack, QJsonObject& manifest)
pack.files.insert(file.fileId, file);
}
- pack.overrides = Json::ensureString(manifest, "overrides", "overrides");
+ pack.overrides = manifest["overrides"].toString("overrides");
pack.is_loaded = true;
}
diff --git a/launcher/modplatform/import_ftb/PackHelpers.cpp b/launcher/modplatform/import_ftb/PackHelpers.cpp
index 22d1242e9..78f7e4ba6 100644
--- a/launcher/modplatform/import_ftb/PackHelpers.cpp
+++ b/launcher/modplatform/import_ftb/PackHelpers.cpp
@@ -72,7 +72,7 @@ Modpack parseDirectory(QString path)
modpack.name = Json::requireString(root, "name", "name");
modpack.version = Json::requireString(root, "version", "version");
modpack.mcVersion = Json::requireString(root, "mcVersion", "mcVersion");
- modpack.jvmArgs = Json::ensureVariant(root, "jvmArgs", {}, "jvmArgs");
+ modpack.jvmArgs = root["jvmArgs"].toVariant();
modpack.totalPlayTime = Json::requireInteger(root, "totalPlayTime", "totalPlayTime");
} catch (const Exception& e) {
qDebug() << "Couldn't load ftb instance json: " << e.cause();
diff --git a/launcher/modplatform/legacy_ftb/PackInstallTask.h b/launcher/modplatform/legacy_ftb/PackInstallTask.h
index 42808a1a2..3459ee902 100644
--- a/launcher/modplatform/legacy_ftb/PackInstallTask.h
+++ b/launcher/modplatform/legacy_ftb/PackInstallTask.h
@@ -8,8 +8,6 @@
#include "meta/VersionList.h"
#include "net/NetJob.h"
-#include "net/NetJob.h"
-
#include
namespace LegacyFTB {
diff --git a/launcher/modplatform/modrinth/ModrinthAPI.cpp b/launcher/modplatform/modrinth/ModrinthAPI.cpp
index bdef1a0e5..f82d75a8b 100644
--- a/launcher/modplatform/modrinth/ModrinthAPI.cpp
+++ b/launcher/modplatform/modrinth/ModrinthAPI.cpp
@@ -147,7 +147,7 @@ QList ModrinthAPI::loadCategories(std::shared_ptraddonId.toString(), get_arguments.isEmpty() ? "" : "?", get_arguments.join('&'));
};
QString getGameVersionsArray(std::list mcVersions) const
@@ -204,7 +205,8 @@ class ModrinthAPI : public ResourceAPI {
static inline auto validateModLoaders(ModPlatform::ModLoaderTypes loaders) -> bool
{
return loaders & (ModPlatform::NeoForge | ModPlatform::Forge | ModPlatform::Fabric | ModPlatform::Quilt | ModPlatform::LiteLoader |
- ModPlatform::DataPack | ModPlatform::Babric | ModPlatform::BTA | ModPlatform::LegacyFabric | ModPlatform::Ornithe | ModPlatform::Rift);
+ ModPlatform::DataPack | ModPlatform::Babric | ModPlatform::BTA | ModPlatform::LegacyFabric |
+ ModPlatform::Ornithe | ModPlatform::Rift);
}
std::optional getDependencyURL(DependencySearchArgs const& args) const override
diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp
index 18b435106..767bb003f 100644
--- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp
+++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp
@@ -249,7 +249,7 @@ bool ModrinthCreationTask::createInstance()
auto root_modpack_url = QUrl::fromLocalFile(root_modpack_path);
// TODO make this work with other sorts of resource
QHash resources;
- for (auto file : m_files) {
+ for (auto& file : m_files) {
auto fileName = file.path;
fileName = FS::RemoveInvalidPathChars(fileName);
auto file_path = FS::PathCombine(root_modpack_path, fileName);
@@ -371,8 +371,8 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path,
if (set_internal_data) {
if (m_managed_version_id.isEmpty())
- m_managed_version_id = Json::ensureString(obj, "versionId", {}, "Managed ID");
- m_managed_name = Json::ensureString(obj, "name", {}, "Managed Name");
+ m_managed_version_id = obj["versionId"].toString();
+ m_managed_name = obj["name"].toString();
}
auto jsonFiles = Json::requireIsArrayOf(obj, "files", "modrinth.index.json");
@@ -381,10 +381,10 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path,
File file;
file.path = Json::requireString(modInfo, "path").replace("\\", "/");
- auto env = Json::ensureObject(modInfo, "env");
+ auto env = modInfo["env"].toObject();
// 'env' field is optional
if (!env.isEmpty()) {
- QString support = Json::ensureString(env, "client", "unsupported");
+ QString support = env["client"].toString("unsupported");
if (support == "unsupported") {
continue;
} else if (support == "optional") {
@@ -399,7 +399,7 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path,
// Do not use requireUrl, which uses StrictMode, instead use QUrl's default TolerantMode
// (as Modrinth seems to incorrectly handle spaces)
- auto download_arr = Json::ensureArray(modInfo, "downloads");
+ auto download_arr = modInfo["downloads"].toArray();
for (auto download : download_arr) {
qWarning() << download.toString();
bool is_last = download.toString() == download_arr.last().toString();
diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp
index 702a8f309..2c8ce76b9 100644
--- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp
+++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp
@@ -33,34 +33,36 @@ bool shouldDownloadOnSide(QString side)
return side == "required" || side == "optional";
}
-// https://docs.modrinth.com/api-spec/#tag/projects/operation/getProject
+// https://docs.modrinth.com/api/operations/getproject/
void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
{
- pack.addonId = Json::ensureString(obj, "project_id");
+ pack.addonId = obj["project_id"].toString();
if (pack.addonId.toString().isEmpty())
pack.addonId = Json::requireString(obj, "id");
pack.provider = ModPlatform::ResourceProvider::MODRINTH;
pack.name = Json::requireString(obj, "title");
- pack.slug = Json::ensureString(obj, "slug", "");
+ pack.slug = obj["slug"].toString("");
if (!pack.slug.isEmpty())
pack.websiteUrl = "https://modrinth.com/mod/" + pack.slug;
else
pack.websiteUrl = "";
- pack.description = Json::ensureString(obj, "description", "");
+ pack.description = obj["description"].toString("");
- pack.logoUrl = Json::ensureString(obj, "icon_url", "");
- pack.logoName = QString("%1.%2").arg(Json::ensureString(obj, "slug"), QFileInfo(QUrl(pack.logoUrl).fileName()).suffix());
+ pack.logoUrl = obj["icon_url"].toString("");
+ pack.logoName = QString("%1.%2").arg(obj["slug"].toString(), QFileInfo(QUrl(pack.logoUrl).fileName()).suffix());
- ModPlatform::ModpackAuthor modAuthor;
- modAuthor.name = Json::ensureString(obj, "author", QObject::tr("No author(s)"));
- modAuthor.url = api.getAuthorURL(modAuthor.name);
- pack.authors.append(modAuthor);
+ if (obj.contains("author")) {
+ ModPlatform::ModpackAuthor modAuthor;
+ modAuthor.name = obj["author"].toString();
+ modAuthor.url = api.getAuthorURL(modAuthor.name);
+ pack.authors = { modAuthor };
+ }
- auto client = shouldDownloadOnSide(Json::ensureString(obj, "client_side"));
- auto server = shouldDownloadOnSide(Json::ensureString(obj, "server_side"));
+ auto client = shouldDownloadOnSide(obj["client_side"].toString());
+ auto server = shouldDownloadOnSide(obj["server_side"].toString());
if (server && client) {
pack.side = ModPlatform::Side::UniversalSide;
@@ -76,38 +78,38 @@ void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
void Modrinth::loadExtraPackData(ModPlatform::IndexedPack& pack, QJsonObject& obj)
{
- pack.extraData.issuesUrl = Json::ensureString(obj, "issues_url");
+ pack.extraData.issuesUrl = obj["issues_url"].toString();
if (pack.extraData.issuesUrl.endsWith('/'))
pack.extraData.issuesUrl.chop(1);
- pack.extraData.sourceUrl = Json::ensureString(obj, "source_url");
+ pack.extraData.sourceUrl = obj["source_url"].toString();
if (pack.extraData.sourceUrl.endsWith('/'))
pack.extraData.sourceUrl.chop(1);
- pack.extraData.wikiUrl = Json::ensureString(obj, "wiki_url");
+ pack.extraData.wikiUrl = obj["wiki_url"].toString();
if (pack.extraData.wikiUrl.endsWith('/'))
pack.extraData.wikiUrl.chop(1);
- pack.extraData.discordUrl = Json::ensureString(obj, "discord_url");
+ pack.extraData.discordUrl = obj["discord_url"].toString();
if (pack.extraData.discordUrl.endsWith('/'))
pack.extraData.discordUrl.chop(1);
- auto donate_arr = Json::ensureArray(obj, "donation_urls");
+ auto donate_arr = obj["donation_urls"].toArray();
for (auto d : donate_arr) {
auto d_obj = Json::requireObject(d);
ModPlatform::DonationData donate;
- donate.id = Json::ensureString(d_obj, "id");
- donate.platform = Json::ensureString(d_obj, "platform");
- donate.url = Json::ensureString(d_obj, "url");
+ donate.id = d_obj["id"].toString();
+ donate.platform = d_obj["platform"].toString();
+ donate.url = d_obj["url"].toString();
pack.extraData.donate.append(donate);
}
- pack.extraData.status = Json::ensureString(obj, "status");
+ pack.extraData.status = obj["status"].toString();
- pack.extraData.body = Json::ensureString(obj, "body").remove("
");
+ pack.extraData.body = obj["body"].toString().remove("
");
pack.extraDataLoaded = true;
}
@@ -147,12 +149,12 @@ ModPlatform::IndexedVersion Modrinth::loadIndexedPackVersion(QJsonObject& obj, Q
file.changelog = Json::requireString(obj, "changelog");
- auto dependencies = Json::ensureArray(obj, "dependencies");
+ auto dependencies = obj["dependencies"].toArray();
for (auto d : dependencies) {
- auto dep = Json::ensureObject(d);
+ auto dep = d.toObject();
ModPlatform::Dependency dependency;
- dependency.addonId = Json::ensureString(dep, "project_id");
- dependency.version = Json::ensureString(dep, "version_id");
+ dependency.addonId = dep["project_id"].toString();
+ dependency.version = dep["version_id"].toString();
auto depType = Json::requireString(dep, "dependency_type");
if (depType == "required")
diff --git a/launcher/modplatform/technic/SolderPackManifest.cpp b/launcher/modplatform/technic/SolderPackManifest.cpp
index 38b668f6b..4b9701e7d 100644
--- a/launcher/modplatform/technic/SolderPackManifest.cpp
+++ b/launcher/modplatform/technic/SolderPackManifest.cpp
@@ -37,7 +37,7 @@ void loadPack(Pack& v, QJsonObject& obj)
static void loadPackBuildMod(PackBuildMod& b, QJsonObject& obj)
{
b.name = Json::requireString(obj, "name");
- b.version = Json::ensureString(obj, "version", "");
+ b.version = obj["version"].toString("");
b.md5 = Json::requireString(obj, "md5");
b.url = Json::requireString(obj, "url");
}
diff --git a/launcher/modplatform/technic/TechnicPackProcessor.cpp b/launcher/modplatform/technic/TechnicPackProcessor.cpp
index 9050e14d8..b762e8882 100644
--- a/launcher/modplatform/technic/TechnicPackProcessor.cpp
+++ b/launcher/modplatform/technic/TechnicPackProcessor.cpp
@@ -142,7 +142,7 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings,
try {
QJsonDocument doc = Json::requireDocument(data);
QJsonObject root = Json::requireObject(doc, "version.json");
- QString packMinecraftVersion = Json::ensureString(root, "inheritsFrom", QString(), "");
+ QString packMinecraftVersion = root["inheritsFrom"].toString();
if (packMinecraftVersion.isEmpty()) {
if (fmlMinecraftVersion.isEmpty()) {
emit failed(tr("Could not understand \"version.json\":\ninheritsFrom is missing"));
@@ -151,21 +151,21 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings,
packMinecraftVersion = fmlMinecraftVersion;
}
components->setComponentVersion("net.minecraft", packMinecraftVersion, true);
- for (auto library : Json::ensureArray(root, "libraries", {})) {
+ for (auto library : root["libraries"].toArray()) {
if (!library.isObject()) {
continue;
}
- auto libraryObject = Json::ensureObject(library, {}, "");
- auto libraryName = Json::ensureString(libraryObject, "name", "", "");
+ auto libraryObject = library.toObject();
+ auto libraryName = libraryObject["name"].toString();
if (libraryName.startsWith("net.neoforged.fancymodloader:")) { // it is neoforge
// no easy way to get the version from the libs so use the arguments
- auto arguments = Json::ensureObject(root, "arguments", {});
+ auto arguments = root["arguments"].toObject();
bool isVersionArg = false;
QString neoforgeVersion;
- for (auto arg : Json::ensureArray(arguments, "game", {})) {
- auto argument = Json::ensureString(arg, "");
+ for (auto arg : arguments["game"].toArray()) {
+ auto argument = arg.toString("");
if (isVersionArg) {
neoforgeVersion = argument;
break;
diff --git a/launcher/net/HttpMetaCache.cpp b/launcher/net/HttpMetaCache.cpp
index b0b3b6533..624f45ccf 100644
--- a/launcher/net/HttpMetaCache.cpp
+++ b/launcher/net/HttpMetaCache.cpp
@@ -248,15 +248,15 @@ void HttpMetaCache::Load()
auto root = json.object();
// check file version first
- auto version_val = Json::ensureString(root, "version");
+ auto version_val = root["version"].toString();
if (version_val != "1")
return;
// read the entry array
- auto array = Json::ensureArray(root, "entries");
+ auto array = root["entries"].toArray();
for (auto element : array) {
- auto element_obj = Json::ensureObject(element);
- auto base = Json::ensureString(element_obj, "base");
+ auto element_obj = element.toObject();
+ auto base = element_obj["base"].toString();
if (!m_entries.contains(base))
continue;
@@ -264,16 +264,16 @@ void HttpMetaCache::Load()
auto foo = new MetaEntry();
foo->m_baseId = base;
- foo->m_relativePath = Json::ensureString(element_obj, "path");
- foo->m_md5sum = Json::ensureString(element_obj, "md5sum");
- foo->m_etag = Json::ensureString(element_obj, "etag");
- foo->m_local_changed_timestamp = Json::ensureDouble(element_obj, "last_changed_timestamp");
- foo->m_remote_changed_timestamp = Json::ensureString(element_obj, "remote_changed_timestamp");
+ foo->m_relativePath = element_obj["path"].toString();
+ foo->m_md5sum = element_obj["md5sum"].toString();
+ foo->m_etag = element_obj["etag"].toString();
+ foo->m_local_changed_timestamp = element_obj["last_changed_timestamp"].toDouble();
+ foo->m_remote_changed_timestamp = element_obj["remote_changed_timestamp"].toString();
- foo->makeEternal(Json::ensureBoolean(element_obj, (const QString)QStringLiteral("eternal"), false));
+ foo->makeEternal(element_obj[QStringLiteral("eternal")].toBool());
if (!foo->isEternal()) {
- foo->m_current_age = Json::ensureDouble(element_obj, "current_age");
- foo->m_max_age = Json::ensureDouble(element_obj, "max_age");
+ foo->m_current_age = element_obj["current_age"].toDouble();
+ foo->m_max_age = element_obj["max_age"].toDouble();
}
// presumed innocent until closer examination
diff --git a/launcher/resources/multimc/multimc.qrc b/launcher/resources/multimc/multimc.qrc
index 116dd73cd..2937cc34b 100644
--- a/launcher/resources/multimc/multimc.qrc
+++ b/launcher/resources/multimc/multimc.qrc
@@ -252,7 +252,6 @@
scalable/discord.svg
- scalable/instances/flame.svg
scalable/instances/chicken.svg
scalable/instances/creeper.svg
scalable/instances/enderpearl.svg
diff --git a/launcher/settings/SettingsObject.cpp b/launcher/settings/SettingsObject.cpp
index 7501d6748..8bc691f4c 100644
--- a/launcher/settings/SettingsObject.cpp
+++ b/launcher/settings/SettingsObject.cpp
@@ -20,6 +20,12 @@
#include "settings/Setting.h"
#include
+#include
+#include
+
+#ifdef Q_OS_MACOS
+#include "macsandbox/SecurityBookmarkFileAccess.h"
+#endif
SettingsObject::SettingsObject(QObject* parent) : QObject(parent) {}
@@ -78,9 +84,17 @@ std::shared_ptr SettingsObject::getSetting(const QString& id) const
return m_settings[id];
}
-QVariant SettingsObject::get(const QString& id) const
+QVariant SettingsObject::get(const QString& id)
{
auto setting = getSetting(id);
+
+#ifdef Q_OS_MACOS
+ // for macOS, use a security scoped bookmark for the paths
+ if (id.endsWith("Dir")) {
+ return { getPathFromBookmark(id) };
+ }
+#endif
+
return (setting ? setting->get() : QVariant());
}
@@ -90,11 +104,105 @@ bool SettingsObject::set(const QString& id, QVariant value)
if (!setting) {
qCritical() << QString("Error changing setting %1. Setting doesn't exist.").arg(id);
return false;
- } else {
- setting->set(value);
+ }
+
+#ifdef Q_OS_MACOS
+ // for macOS, keep a security scoped bookmark for the paths
+ if (value.userType() == QMetaType::QString && id.endsWith("Dir")) {
+ setPathWithBookmark(id, value.toString());
+ }
+#endif
+
+ setting->set(std::move(value));
+ return true;
+}
+
+#ifdef Q_OS_MACOS
+QString SettingsObject::getPathFromBookmark(const QString& id)
+{
+ auto setting = getSetting(id);
+ if (!setting) {
+ qCritical() << QString("Error changing setting %1. Setting doesn't exist.").arg(id);
+ return "";
+ }
+
+ // there is no need to use bookmarks if the default value is used or the directory is within the data directory (already can access)
+ if (setting->get() == setting->defValue() || QDir(setting->get().toString()).absolutePath().startsWith(QDir::current().absolutePath())) {
+ return setting->get().toString();
+ }
+
+ auto bookmarkId = id + "Bookmark";
+ auto bookmarkSetting = getSetting(bookmarkId);
+ if (!bookmarkSetting) {
+ qCritical() << QString("Error changing setting %1. Bookmark setting doesn't exist.").arg(id);
+ return "";
+ }
+
+ QByteArray bookmark = bookmarkSetting->get().toByteArray();
+ if (bookmark.isEmpty()) {
+ qDebug() << "Creating bookmark for" << id << "at" << setting->get().toString();
+ setPathWithBookmark(id, setting->get().toString());
+ return setting->get().toString();
+ }
+ bool stale;
+ QUrl url = m_sandboxedFileAccess.securityScopedBookmarkToURL(bookmark, stale);
+ if (url.isValid()) {
+ if (stale) {
+ setting->set(url.path());
+ bookmarkSetting->set(bookmark);
+ }
+
+ m_sandboxedFileAccess.startUsingSecurityScopedBookmark(bookmark, stale);
+ // already did a stale check, no need to do it again
+
+ // convert to relative path to current directory if `url` is a descendant of the current directory
+ QDir currentDir = QDir::current().absolutePath();
+ return url.path().startsWith(currentDir.absolutePath()) ? currentDir.relativeFilePath(url.path()) : url.path();
+ }
+
+ return setting->get().toString();
+}
+
+bool SettingsObject::setPathWithBookmark(const QString& id, const QString& path)
+{
+ auto setting = getSetting(id);
+ if (!setting) {
+ qCritical() << QString("Error changing setting %1. Setting doesn't exist.").arg(id);
+ return false;
+ }
+
+ QDir dir(path);
+ if (!dir.exists()) {
+ qCritical() << QString("Error changing setting %1. Path doesn't exist.").arg(id);
+ return false;
+ }
+ QString absolutePath = dir.absolutePath();
+ QString bookmarkId = id + "Bookmark";
+ std::shared_ptr bookmarkSetting = getSetting(bookmarkId);
+ // there is no need to use bookmarks if the default value is used or the directory is within the data directory (already can access)
+ if (path == setting->defValue().toString() || absolutePath.startsWith(QDir::current().absolutePath())) {
+ bookmarkSetting->reset();
return true;
}
+ QByteArray bytes = m_sandboxedFileAccess.pathToSecurityScopedBookmark(absolutePath);
+ if (bytes.isEmpty()) {
+ qCritical() << QString("Failed to create bookmark for %1 - no access?").arg(id);
+ // TODO: show an alert to the user asking them to reselect the directory
+ return false;
+ }
+ auto oldBookmark = bookmarkSetting->get().toByteArray();
+ m_sandboxedFileAccess.stopUsingSecurityScopedBookmark(oldBookmark);
+ if (!bytes.isEmpty() && bookmarkSetting) {
+ bookmarkSetting->set(bytes);
+ bool stale;
+ m_sandboxedFileAccess.startUsingSecurityScopedBookmark(bytes, stale);
+ // just created the bookmark, it shouldn't be stale
+ }
+
+ setting->set(path);
+ return true;
}
+#endif
void SettingsObject::reset(const QString& id) const
{
diff --git a/launcher/settings/SettingsObject.h b/launcher/settings/SettingsObject.h
index bd3f71b36..abd6c29c5 100644
--- a/launcher/settings/SettingsObject.h
+++ b/launcher/settings/SettingsObject.h
@@ -23,6 +23,10 @@
#include
#include
+#ifdef Q_OS_MACOS
+#include "macsandbox/SecurityBookmarkFileAccess.h"
+#endif
+
class Setting;
class SettingsObject;
@@ -119,7 +123,27 @@ class SettingsObject : public QObject {
* \return The setting's value as a QVariant.
* If no setting with the given ID exists, returns an invalid QVariant.
*/
- QVariant get(const QString& id) const;
+ QVariant get(const QString& id);
+
+#ifdef Q_OS_MACOS
+ /*!
+ * \brief Get the path to the file or directory represented by the bookmark stored in the associated setting.
+ * \param id The setting ID of the relevant directory - this should not include "Bookmark" at the end.
+ * \return A path to the file or directory represented by the bookmark.
+ * If a bookmark is not valid or stored, use default logic (directly return the stored path).
+ * This can attempt to create a bookmark if the path is accessible and the bookmark is not valid.
+ */
+ QString getPathFromBookmark(const QString& id);
+ /*!
+ * \brief Set a security-scoped bookmark to the provided path for the associated setting.
+ * \param id The setting ID of the relevant directory - this should not include "Bookmark" at the end.
+ * \param path The new desired path.
+ * \return A boolean indicating whether a bookmark was successfully set.
+ * The path needs to be accessible to the launcher before calling this function. For example,
+ * it could come from a user selection in an open panel.
+ */
+ bool setPathWithBookmark(const QString& id, const QString& path);
+#endif
/*!
* \brief Sets the value of the setting with the given ID.
@@ -207,6 +231,9 @@ class SettingsObject : public QObject {
private:
QMap> m_settings;
+#ifdef Q_OS_MACOS
+ SecurityBookmarkFileAccess m_sandboxedFileAccess;
+#endif
protected:
bool m_suspendSave = false;
diff --git a/launcher/translations/TranslationsModel.cpp b/launcher/translations/TranslationsModel.cpp
index 860e57e54..6d5609077 100644
--- a/launcher/translations/TranslationsModel.cpp
+++ b/launcher/translations/TranslationsModel.cpp
@@ -251,8 +251,8 @@ void readIndex(const QString& path, QMap& languages)
Language lang(iter.key());
auto langObj = Json::requireObject(iter.value());
- lang.setTranslationStats(Json::ensureInteger(langObj, "translated", 0), Json::ensureInteger(langObj, "untranslated", 0),
- Json::ensureInteger(langObj, "fuzzy", 0));
+ lang.setTranslationStats(langObj["translated"].toInt(), langObj["untranslated"].toInt(),
+ langObj["fuzzy"].toInt());
lang.file_name = Json::requireString(langObj, "file");
lang.file_sha1 = Json::requireString(langObj, "sha1");
lang.file_size = Json::requireInteger(langObj, "size");
diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp
index 3cec0ae53..77a480e70 100644
--- a/launcher/ui/MainWindow.cpp
+++ b/launcher/ui/MainWindow.cpp
@@ -967,7 +967,7 @@ void MainWindow::processURLs(QList urls)
connect(job.get(), &Task::succeeded, this, [this, array, addonId, fileId, &dl_url, &version] {
qDebug() << "Returned CFURL Json:\n" << array->toStdString().c_str();
auto doc = Json::requireDocument(*array);
- auto data = Json::ensureObject(Json::ensureObject(doc.object()), "data");
+ auto data = doc.object()["data"].toObject();
// No way to find out if it's a mod or a modpack before here
// And also we need to check if it ends with .zip, instead of any better way
version = FlameMod::loadIndexedPackVersion(data);
diff --git a/launcher/ui/dialogs/ChooseOfflineNameDialog.cpp b/launcher/ui/dialogs/ChooseOfflineNameDialog.cpp
new file mode 100644
index 000000000..79a567404
--- /dev/null
+++ b/launcher/ui/dialogs/ChooseOfflineNameDialog.cpp
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (C) 2025 Octol1ttle
+ *
+ * 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 .
+ */
+
+#include "ChooseOfflineNameDialog.h"
+
+#include
+#include
+
+#include "ui_ChooseOfflineNameDialog.h"
+
+ChooseOfflineNameDialog::ChooseOfflineNameDialog(const QString& message, QWidget* parent) : QDialog(parent), ui(new Ui::ChooseOfflineNameDialog)
+{
+ ui->setupUi(this);
+ ui->label->setText(message);
+
+ ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
+ ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
+ ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK"));
+
+ const QRegularExpression usernameRegExp("^[A-Za-z0-9_]{3,16}$");
+ m_usernameValidator = new QRegularExpressionValidator(usernameRegExp, this);
+ ui->usernameTextBox->setValidator(m_usernameValidator);
+
+ connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
+ connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
+}
+
+ChooseOfflineNameDialog::~ChooseOfflineNameDialog()
+{
+ delete ui;
+}
+
+QString ChooseOfflineNameDialog::getUsername() const
+{
+ return ui->usernameTextBox->text();
+}
+
+void ChooseOfflineNameDialog::setUsername(const QString& username) const
+{
+ ui->usernameTextBox->setText(username);
+ updateAcceptAllowed(username);
+}
+
+void ChooseOfflineNameDialog::updateAcceptAllowed(const QString& username) const
+{
+ const bool allowed = ui->allowInvalidUsernames->isChecked() ? !username.isEmpty() : ui->usernameTextBox->hasAcceptableInput();
+ ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(allowed);
+}
+
+void ChooseOfflineNameDialog::on_usernameTextBox_textEdited(const QString& newText) const
+{
+ updateAcceptAllowed(newText);
+}
+
+void ChooseOfflineNameDialog::on_allowInvalidUsernames_checkStateChanged(const Qt::CheckState checkState) const
+{
+ ui->usernameTextBox->setValidator(checkState == Qt::Checked ? nullptr : m_usernameValidator);
+ updateAcceptAllowed(getUsername());
+}
diff --git a/launcher/ui/dialogs/ChooseOfflineNameDialog.h b/launcher/ui/dialogs/ChooseOfflineNameDialog.h
new file mode 100644
index 000000000..fb3bb4257
--- /dev/null
+++ b/launcher/ui/dialogs/ChooseOfflineNameDialog.h
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (C) 2025 Octol1ttle
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include
+#include
+
+QT_BEGIN_NAMESPACE
+namespace Ui {
+class ChooseOfflineNameDialog;
+}
+QT_END_NAMESPACE
+
+class ChooseOfflineNameDialog final : public QDialog {
+ Q_OBJECT
+
+ public:
+ explicit ChooseOfflineNameDialog(const QString& message, QWidget* parent = nullptr);
+ ~ChooseOfflineNameDialog() override;
+
+ QString getUsername() const;
+ void setUsername(const QString& username) const;
+
+ private:
+ void updateAcceptAllowed(const QString& username) const;
+
+ protected slots:
+ void on_usernameTextBox_textEdited(const QString& newText) const;
+ void on_allowInvalidUsernames_checkStateChanged(Qt::CheckState checkState) const;
+
+ private:
+ Ui::ChooseOfflineNameDialog* ui;
+ QRegularExpressionValidator* m_usernameValidator;
+};
\ No newline at end of file
diff --git a/launcher/ui/dialogs/ChooseOfflineNameDialog.ui b/launcher/ui/dialogs/ChooseOfflineNameDialog.ui
new file mode 100644
index 000000000..51a10e5b8
--- /dev/null
+++ b/launcher/ui/dialogs/ChooseOfflineNameDialog.ui
@@ -0,0 +1,58 @@
+
+
+ ChooseOfflineNameDialog
+
+
+
+ 0
+ 0
+ 400
+ 158
+
+
+
+ Choose Offline Name
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Message label placeholder.
+
+
+
+ -
+
+
+ Username
+
+
+
+ -
+
+
+ A username is valid only if it is from 3 to 16 characters in length, uses English letters, numbers, and underscores. An invalid username may prevent joining servers and singleplayer worlds.
+
+
+ Allow invalid usernames
+
+
+
+ -
+
+
+ QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok
+
+
+
+
+
+
+
+
diff --git a/launcher/ui/dialogs/MSALoginDialog.cpp b/launcher/ui/dialogs/MSALoginDialog.cpp
index 9e2c3df02..79131a74c 100644
--- a/launcher/ui/dialogs/MSALoginDialog.cpp
+++ b/launcher/ui/dialogs/MSALoginDialog.cpp
@@ -50,7 +50,7 @@
#include
#include
-#include "qrcodegen.hpp"
+#include "qrencode.h"
MSALoginDialog::MSALoginDialog(QWidget* parent) : QDialog(parent), ui(new Ui::MSALoginDialog)
{
@@ -146,27 +146,32 @@ void MSALoginDialog::authorizeWithBrowser(const QUrl& url)
m_url = url;
}
-// https://stackoverflow.com/questions/21400254/how-to-draw-a-qr-code-with-qt-in-native-c-c
-void paintQR(QPainter& painter, const QSize sz, const QString& data, QColor fg)
+void paintQR(QPainter& painter, const QSize canvasSize, const QString& data, QColor fg)
{
- // NOTE: At this point you will use the API to get the encoding and format you want, instead of my hardcoded stuff:
- qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(data.toUtf8().constData(), qrcodegen::QrCode::Ecc::LOW);
- const int s = qr.getSize() > 0 ? qr.getSize() : 1;
- const double w = sz.width();
- const double h = sz.height();
- const double aspect = w / h;
- const double size = ((aspect > 1.0) ? h : w);
- const double scale = size / (s + 2);
- // NOTE: For performance reasons my implementation only draws the foreground parts in supplied color.
- // It expects background to be prepared already (in white or whatever is preferred).
+ const auto* qr = QRcode_encodeString(data.toUtf8().constData(), 0, QRecLevel::QR_ECLEVEL_M, QRencodeMode::QR_MODE_8, 1);
+ if (!qr) {
+ qWarning() << "Unable to encode" << data << "as QR code";
+ return;
+ }
+
painter.setPen(Qt::NoPen);
painter.setBrush(fg);
- for (int y = 0; y < s; y++) {
- for (int x = 0; x < s; x++) {
- const int color = qr.getModule(x, y); // 0 for white, 1 for black
- if (0 != color) {
- const double rx1 = (x + 1) * scale, ry1 = (y + 1) * scale;
- QRectF r(rx1, ry1, scale, scale);
+
+ // Make sure the QR code fits in the canvas with some padding
+ const auto qrSize = qr->width;
+ const auto canvasWidth = canvasSize.width();
+ const auto canvasHeight = canvasSize.height();
+ const auto scale = 0.8 * std::min(canvasWidth / qrSize, canvasHeight / qrSize);
+
+ // Find an offset to center it in the canvas
+ const auto offsetX = (canvasWidth - qrSize * scale) / 2;
+ const auto offsetY = (canvasHeight - qrSize * scale) / 2;
+
+ for (int y = 0; y < qrSize; y++) {
+ for (int x = 0; x < qrSize; x++) {
+ auto shouldFillIn = qr->data[y * qrSize + x] & 1;
+ if (shouldFillIn) {
+ QRectF r(offsetX + x * scale, offsetY + y * scale, scale, scale);
painter.drawRects(&r, 1);
}
}
diff --git a/launcher/ui/dialogs/OfflineLoginDialog.cpp b/launcher/ui/dialogs/OfflineLoginDialog.cpp
deleted file mode 100644
index d8fbc04fd..000000000
--- a/launcher/ui/dialogs/OfflineLoginDialog.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-#include "OfflineLoginDialog.h"
-#include "ui_OfflineLoginDialog.h"
-
-#include
-
-OfflineLoginDialog::OfflineLoginDialog(QWidget* parent) : QDialog(parent), ui(new Ui::OfflineLoginDialog)
-{
- ui->setupUi(this);
- ui->progressBar->setVisible(false);
- ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
-
- ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
- ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK"));
-
- connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
- connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
-}
-
-OfflineLoginDialog::~OfflineLoginDialog()
-{
- delete ui;
-}
-
-// Stage 1: User interaction
-void OfflineLoginDialog::accept()
-{
- setUserInputsEnabled(false);
- ui->progressBar->setVisible(true);
-
- // Setup the login task and start it
- m_account = MinecraftAccount::createOffline(ui->userTextBox->text());
- m_loginTask = m_account->login();
- connect(m_loginTask.get(), &Task::failed, this, &OfflineLoginDialog::onTaskFailed);
- connect(m_loginTask.get(), &Task::succeeded, this, &OfflineLoginDialog::onTaskSucceeded);
- connect(m_loginTask.get(), &Task::status, this, &OfflineLoginDialog::onTaskStatus);
- connect(m_loginTask.get(), &Task::progress, this, &OfflineLoginDialog::onTaskProgress);
- m_loginTask->start();
-}
-
-void OfflineLoginDialog::setUserInputsEnabled(bool enable)
-{
- ui->userTextBox->setEnabled(enable);
- ui->buttonBox->setEnabled(enable);
-}
-
-void OfflineLoginDialog::on_allowLongUsernames_stateChanged(int value)
-{
- if (value == Qt::Checked) {
- ui->userTextBox->setMaxLength(INT_MAX);
- } else {
- ui->userTextBox->setMaxLength(16);
- }
-}
-
-// Enable the OK button only when the textbox contains something.
-void OfflineLoginDialog::on_userTextBox_textEdited(const QString& newText)
-{
- ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!newText.isEmpty());
-}
-
-void OfflineLoginDialog::onTaskFailed(const QString& reason)
-{
- // Set message
- auto lines = reason.split('\n');
- QString processed;
- for (auto line : lines) {
- if (line.size()) {
- processed += "" + line + "
";
- } else {
- processed += "
";
- }
- }
- ui->label->setText(processed);
-
- // Re-enable user-interaction
- setUserInputsEnabled(true);
- ui->progressBar->setVisible(false);
-}
-
-void OfflineLoginDialog::onTaskSucceeded()
-{
- QDialog::accept();
-}
-
-void OfflineLoginDialog::onTaskStatus(const QString& status)
-{
- ui->label->setText(status);
-}
-
-void OfflineLoginDialog::onTaskProgress(qint64 current, qint64 total)
-{
- ui->progressBar->setMaximum(total);
- ui->progressBar->setValue(current);
-}
-
-// Public interface
-MinecraftAccountPtr OfflineLoginDialog::newAccount(QWidget* parent, QString msg)
-{
- OfflineLoginDialog dlg(parent);
- dlg.ui->label->setText(msg);
- if (dlg.exec() == QDialog::Accepted) {
- return dlg.m_account;
- }
- return nullptr;
-}
diff --git a/launcher/ui/dialogs/OfflineLoginDialog.h b/launcher/ui/dialogs/OfflineLoginDialog.h
deleted file mode 100644
index 6660a18ec..000000000
--- a/launcher/ui/dialogs/OfflineLoginDialog.h
+++ /dev/null
@@ -1,40 +0,0 @@
-#pragma once
-
-#include
-
-#include "minecraft/auth/MinecraftAccount.h"
-#include "tasks/Task.h"
-
-namespace Ui {
-class OfflineLoginDialog;
-}
-
-class OfflineLoginDialog : public QDialog {
- Q_OBJECT
-
- public:
- ~OfflineLoginDialog();
-
- static MinecraftAccountPtr newAccount(QWidget* parent, QString message);
-
- private:
- explicit OfflineLoginDialog(QWidget* parent = 0);
-
- void setUserInputsEnabled(bool enable);
-
- protected slots:
- void accept();
-
- void onTaskFailed(const QString& reason);
- void onTaskSucceeded();
- void onTaskStatus(const QString& status);
- void onTaskProgress(qint64 current, qint64 total);
-
- void on_userTextBox_textEdited(const QString& newText);
- void on_allowLongUsernames_stateChanged(int value);
-
- private:
- Ui::OfflineLoginDialog* ui;
- MinecraftAccountPtr m_account;
- Task::Ptr m_loginTask;
-};
diff --git a/launcher/ui/dialogs/OfflineLoginDialog.ui b/launcher/ui/dialogs/OfflineLoginDialog.ui
deleted file mode 100644
index 4633cbe3a..000000000
--- a/launcher/ui/dialogs/OfflineLoginDialog.ui
+++ /dev/null
@@ -1,80 +0,0 @@
-
-
- OfflineLoginDialog
-
-
-
- 0
- 0
- 400
- 150
-
-
-
-
- 0
- 0
-
-
-
- Add Account
-
-
- -
-
-
- Message label placeholder.
-
-
- Qt::RichText
-
-
- Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse
-
-
-
- -
-
-
- 16
-
-
- Username
-
-
-
- -
-
-
- Usernames longer than 16 characters cannot be used for LAN games or offline-mode servers.
-
-
- Allow long usernames
-
-
-
- -
-
-
- 69
-
-
- false
-
-
-
- -
-
-
- Qt::Horizontal
-
-
- QDialogButtonBox::Cancel|QDialogButtonBox::Ok
-
-
-
-
-
-
-
-
diff --git a/launcher/ui/dialogs/ResourceUpdateDialog.cpp b/launcher/ui/dialogs/ResourceUpdateDialog.cpp
index eeaf6b3d0..a460e9d35 100644
--- a/launcher/ui/dialogs/ResourceUpdateDialog.cpp
+++ b/launcher/ui/dialogs/ResourceUpdateDialog.cpp
@@ -461,13 +461,12 @@ void ResourceUpdateDialog::appendResource(CheckUpdateTask::Update const& info, Q
auto requiredByItem = new QTreeWidgetItem(item_top);
if (requiredBy.length() == 1) {
requiredByItem->setText(0, tr("Required by: %1").arg(requiredBy.back()));
+ requiredByItem->setData(0, Qt::UserRole, requiredBy.back());
} else {
requiredByItem->setText(0, tr("Required by:"));
- auto i = 0;
for (auto req : requiredBy) {
auto reqItem = new QTreeWidgetItem(requiredByItem);
reqItem->setText(0, req);
- reqItem->insertChildren(i++, { reqItem });
}
}
diff --git a/launcher/ui/dialogs/ReviewMessageBox.cpp b/launcher/ui/dialogs/ReviewMessageBox.cpp
index 904686664..c2d884756 100644
--- a/launcher/ui/dialogs/ReviewMessageBox.cpp
+++ b/launcher/ui/dialogs/ReviewMessageBox.cpp
@@ -68,18 +68,14 @@ void ReviewMessageBox::appendResource(ResourceInformation&& info)
filenameItem->setText(0, tr("Filename: %1").arg(info.filename));
filenameItem->setData(0, Qt::UserRole, info.filename);
- auto childIndx = 0;
- itemTop->insertChildren(childIndx++, { filenameItem });
-
if (!info.custom_file_path.isEmpty()) {
auto customPathItem = new QTreeWidgetItem(itemTop);
customPathItem->setText(0, tr("This download will be placed in: %1").arg(info.custom_file_path));
-
- itemTop->insertChildren(1, { customPathItem });
+ customPathItem->setData(0, Qt::UserRole, info.custom_file_path);
itemTop->setIcon(1, QIcon(QIcon::fromTheme("status-yellow")));
itemTop->setToolTip(
- childIndx++,
+ 1,
tr("This file will be downloaded to a folder location different from the default, possibly due to its loader requiring it."));
}
@@ -87,23 +83,19 @@ void ReviewMessageBox::appendResource(ResourceInformation&& info)
providerItem->setText(0, tr("Provider: %1").arg(info.provider));
providerItem->setData(0, Qt::UserRole, info.provider);
- itemTop->insertChildren(childIndx++, { providerItem });
-
if (!info.required_by.isEmpty()) {
auto requiredByItem = new QTreeWidgetItem(itemTop);
if (info.required_by.length() == 1) {
requiredByItem->setText(0, tr("Required by: %1").arg(info.required_by.back()));
+ requiredByItem->setData(0, Qt::UserRole, info.required_by.back());
} else {
requiredByItem->setText(0, tr("Required by:"));
- auto i = 0;
for (auto req : info.required_by) {
auto reqItem = new QTreeWidgetItem(requiredByItem);
reqItem->setText(0, req);
- reqItem->insertChildren(i++, { reqItem });
}
}
- itemTop->insertChildren(childIndx++, { requiredByItem });
ui->toggleDepsButton->show();
m_deps << itemTop;
}
@@ -112,8 +104,6 @@ void ReviewMessageBox::appendResource(ResourceInformation&& info)
versionTypeItem->setText(0, tr("Version Type: %1").arg(info.version_type));
versionTypeItem->setData(0, Qt::UserRole, info.version_type);
- itemTop->insertChildren(childIndx++, { versionTypeItem });
-
ui->modTreeWidget->addTopLevelItem(itemTop);
}
diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.cpp b/launcher/ui/dialogs/skins/SkinManageDialog.cpp
index 5967281c4..cecde236a 100644
--- a/launcher/ui/dialogs/skins/SkinManageDialog.cpp
+++ b/launcher/ui/dialogs/skins/SkinManageDialog.cpp
@@ -481,7 +481,7 @@ void SkinManageDialog::on_userBtn_clicked()
return;
}
const auto root = doc.object();
- auto id = Json::ensureString(root, "id");
+ auto id = root["id"].toString();
if (!id.isEmpty()) {
getProfile->setUrl("https://sessionserver.mojang.com/session/minecraft/profile/" + id);
} else {
diff --git a/launcher/ui/dialogs/skins/draw/SkinOpenGLWindow.cpp b/launcher/ui/dialogs/skins/draw/SkinOpenGLWindow.cpp
index b5aa2dc54..962717a30 100644
--- a/launcher/ui/dialogs/skins/draw/SkinOpenGLWindow.cpp
+++ b/launcher/ui/dialogs/skins/draw/SkinOpenGLWindow.cpp
@@ -202,6 +202,13 @@ void SkinOpenGLWindow::resizeGL(int w, int h)
void SkinOpenGLWindow::paintGL()
{
+ // Adjust the viewport to account for fractional scaling
+ qreal dpr = devicePixelRatio();
+ if (dpr != 1.f) {
+ QSize scaledSize = size() * dpr;
+ glViewport(0, 0, scaledSize.width(), scaledSize.height());
+ }
+
// Clear color and depth buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
@@ -235,6 +242,13 @@ void SkinOpenGLWindow::paintGL()
m_scene->draw(m_modelProgram);
m_modelProgram->release();
+
+ // Redraw the first frame; this is necessary because the pixel ratio for Wayland fractional scaling is not negotiated properly on the
+ // first frame
+ if (m_isFirstFrame) {
+ m_isFirstFrame = false;
+ update();
+ }
}
void SkinOpenGLWindow::updateScene(SkinModel* skin)
diff --git a/launcher/ui/dialogs/skins/draw/SkinOpenGLWindow.h b/launcher/ui/dialogs/skins/draw/SkinOpenGLWindow.h
index 17476d3c8..7b88fca9c 100644
--- a/launcher/ui/dialogs/skins/draw/SkinOpenGLWindow.h
+++ b/launcher/ui/dialogs/skins/draw/SkinOpenGLWindow.h
@@ -74,6 +74,8 @@ class SkinOpenGLWindow : public QOpenGLWindow, protected QOpenGLFunctions {
float m_yaw = 90; // Horizontal rotation angle
float m_pitch = 0; // Vertical rotation angle
+ bool m_isFirstFrame = true;
+
opengl::BoxGeometry* m_background = nullptr;
QOpenGLTexture* m_backgroundTexture = nullptr;
QColor m_baseColor;
diff --git a/launcher/ui/pages/global/AccountListPage.cpp b/launcher/ui/pages/global/AccountListPage.cpp
index 041b8faff..ff250888a 100644
--- a/launcher/ui/pages/global/AccountListPage.cpp
+++ b/launcher/ui/pages/global/AccountListPage.cpp
@@ -45,8 +45,8 @@
#include
#include "ui/dialogs/CustomMessageBox.h"
+#include "ui/dialogs/ChooseOfflineNameDialog.h"
#include "ui/dialogs/MSALoginDialog.h"
-#include "ui/dialogs/OfflineLoginDialog.h"
#include "Application.h"
@@ -149,10 +149,13 @@ void AccountListPage::on_actionAddOffline_triggered()
return;
}
- MinecraftAccountPtr account =
- OfflineLoginDialog::newAccount(this, tr("Please enter your desired username to add your offline account."));
+ ChooseOfflineNameDialog dialog(tr("Please enter your desired username to add your offline account."), this);
+ if (dialog.exec() != QDialog::Accepted) {
+ return;
+ }
- if (account) {
+ if (const MinecraftAccountPtr account = MinecraftAccount::createOffline(dialog.getUsername())) {
+ account->login()->start(); // The task will complete here.
m_accounts->addAccount(account);
if (m_accounts->count() == 1) {
m_accounts->setDefaultAccount(account);
diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp
index e97935650..230cf1bdf 100644
--- a/launcher/ui/pages/instance/ManagedPackPage.cpp
+++ b/launcher/ui/pages/instance/ManagedPackPage.cpp
@@ -6,12 +6,14 @@
#include
#include
#include
+#include "modplatform/ModIndex.h"
#include "ui_ManagedPackPage.h"
#include
#include
#include
#include
+#include
#include "Application.h"
#include "BuildConfig.h"
@@ -284,7 +286,8 @@ void ModrinthManagedPackPage::parseManagedPack()
};
callbacks.on_fail = [this](QString reason, int) { setFailState(); };
callbacks.on_abort = [this]() { setFailState(); };
- m_fetch_job = m_api.getProjectVersions({ m_pack, {}, {}, ModPlatform::ResourceType::Modpack }, std::move(callbacks));
+ m_fetch_job = m_api.getProjectVersions(
+ { std::make_shared(m_pack), {}, {}, ModPlatform::ResourceType::Modpack }, std::move(callbacks));
ui->changelogTextBrowser->setText(tr("Fetching changelogs..."));
@@ -455,7 +458,8 @@ void FlameManagedPackPage::parseManagedPack()
};
callbacks.on_fail = [this](QString reason, int) { setFailState(); };
callbacks.on_abort = [this]() { setFailState(); };
- m_fetch_job = m_api.getProjectVersions({ m_pack, {}, {}, ModPlatform::ResourceType::Modpack }, std::move(callbacks));
+ m_fetch_job = m_api.getProjectVersions(
+ { std::make_shared(m_pack), {}, {}, ModPlatform::ResourceType::Modpack }, std::move(callbacks));
m_fetch_job->start();
}
diff --git a/launcher/ui/pages/instance/OtherLogsPage.cpp b/launcher/ui/pages/instance/OtherLogsPage.cpp
index 69e152475..2449ecb2b 100644
--- a/launcher/ui/pages/instance/OtherLogsPage.cpp
+++ b/launcher/ui/pages/instance/OtherLogsPage.cpp
@@ -287,16 +287,10 @@ void OtherLogsPage::reload()
if (!m_instance) {
level = MessageLevel::fromLauncherLine(lineTemp);
} else {
- // if the launcher part set a log level, use it
- auto innerLevel = MessageLevel::fromLine(lineTemp);
- if (innerLevel != MessageLevel::Unknown) {
- level = innerLevel;
- }
+ level = LogParser::guessLevel(line);
- // If the level is still undetermined, guess level
- if (level == MessageLevel::StdErr || level == MessageLevel::StdOut || level == MessageLevel::Unknown) {
- level = LogParser::guessLevel(line, last);
- }
+ if (level == MessageLevel::Unknown)
+ level = last;
}
last = level;
diff --git a/launcher/ui/pages/modplatform/DataPackModel.cpp b/launcher/ui/pages/modplatform/DataPackModel.cpp
index 846ef5aa4..fb535ae03 100644
--- a/launcher/ui/pages/modplatform/DataPackModel.cpp
+++ b/launcher/ui/pages/modplatform/DataPackModel.cpp
@@ -23,14 +23,14 @@ ResourceAPI::SearchArgs DataPackResourceModel::createSearchArguments()
ResourceAPI::VersionSearchArgs DataPackResourceModel::createVersionsArguments(const QModelIndex& entry)
{
- auto& pack = m_packs[entry.row()];
- return { *pack, {}, ModPlatform::ModLoaderType::DataPack };
+ auto pack = m_packs[entry.row()];
+ return { pack, {}, ModPlatform::ModLoaderType::DataPack };
}
ResourceAPI::ProjectInfoArgs DataPackResourceModel::createInfoArguments(const QModelIndex& entry)
{
- auto& pack = m_packs[entry.row()];
- return { *pack };
+ auto pack = m_packs[entry.row()];
+ return { pack };
}
void DataPackResourceModel::searchWithTerm(const QString& term, unsigned int sort)
diff --git a/launcher/ui/pages/modplatform/ImportPage.cpp b/launcher/ui/pages/modplatform/ImportPage.cpp
index 1efc6199e..c43d3e1fa 100644
--- a/launcher/ui/pages/modplatform/ImportPage.cpp
+++ b/launcher/ui/pages/modplatform/ImportPage.cpp
@@ -141,13 +141,13 @@ void ImportPage::updateState()
connect(job.get(), &NetJob::succeeded, this, [this, array, addonId, fileId] {
qDebug() << "Returned CFURL Json:\n" << array->toStdString().c_str();
auto doc = Json::requireDocument(*array);
- auto data = Json::ensureObject(Json::ensureObject(doc.object()), "data");
+ auto data = doc.object()["data"].toObject();
// No way to find out if it's a mod or a modpack before here
// And also we need to check if it ends with .zip, instead of any better way
- auto fileName = Json::ensureString(data, "fileName");
+ auto fileName = data["fileName"].toString();
if (fileName.endsWith(".zip")) {
// Have to use ensureString then use QUrl to get proper url encoding
- auto dl_url = QUrl(Json::ensureString(data, "downloadUrl", "", "downloadUrl"));
+ auto dl_url = QUrl(data["downloadUrl"].toString(""));
if (!dl_url.isValid()) {
CustomMessageBox::selectable(
this, tr("Error"),
@@ -158,7 +158,7 @@ void ImportPage::updateState()
}
QFileInfo dl_file(dl_url.fileName());
- QString pack_name = Json::ensureString(data, "displayName", dl_file.completeBaseName(), "displayName");
+ QString pack_name = data["displayName"].toString(dl_file.completeBaseName());
QMap extra_info;
extra_info.insert("pack_id", addonId);
diff --git a/launcher/ui/pages/modplatform/ModModel.cpp b/launcher/ui/pages/modplatform/ModModel.cpp
index c5a03e1fd..430424cfa 100644
--- a/launcher/ui/pages/modplatform/ModModel.cpp
+++ b/launcher/ui/pages/modplatform/ModModel.cpp
@@ -49,7 +49,7 @@ ResourceAPI::SearchArgs ModModel::createSearchArguments()
ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(const QModelIndex& entry)
{
- auto& pack = *m_packs[entry.row()];
+ auto pack = m_packs[entry.row()];
auto profile = static_cast(m_base_instance).getPackProfile();
Q_ASSERT(profile);
@@ -67,7 +67,7 @@ ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(const QModelInd
ResourceAPI::ProjectInfoArgs ModModel::createInfoArguments(const QModelIndex& entry)
{
- auto& pack = *m_packs[entry.row()];
+ auto pack = m_packs[entry.row()];
return { pack };
}
diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp
index eea7af25e..2d5add0c0 100644
--- a/launcher/ui/pages/modplatform/ResourceModel.cpp
+++ b/launcher/ui/pages/modplatform/ResourceModel.cpp
@@ -141,7 +141,7 @@ void ResourceModel::search()
if (m_search_term.startsWith("#")) {
auto projectId = m_search_term.mid(1);
if (!projectId.isEmpty()) {
- ResourceAPI::Callback callbacks;
+ ResourceAPI::Callback callbacks;
callbacks.on_fail = [this](QString reason, int) {
if (!s_running_models.constFind(this).value())
@@ -159,7 +159,9 @@ void ResourceModel::search()
return;
searchRequestForOneSucceeded(pack);
};
- if (auto job = m_api->getProjectInfo({ projectId }, std::move(callbacks)); job)
+ auto project = std::make_shared();
+ project->addonId = projectId;
+ if (auto job = m_api->getProjectInfo({ project }, std::move(callbacks)); job)
runSearchJob(job);
return;
}
@@ -219,7 +221,7 @@ void ResourceModel::loadEntry(const QModelIndex& entry)
if (!pack->extraDataLoaded) {
auto args{ createInfoArguments(entry) };
- ResourceAPI::Callback callbacks{};
+ ResourceAPI::Callback callbacks{};
callbacks.on_succeed = [this, entry](auto& newpack) {
if (!s_running_models.constFind(this).value())
@@ -388,12 +390,12 @@ void ResourceModel::searchRequestSucceeded(QList&
endInsertRows();
}
-void ResourceModel::searchRequestForOneSucceeded(ModPlatform::IndexedPack& pack)
+void ResourceModel::searchRequestForOneSucceeded(ModPlatform::IndexedPack::Ptr pack)
{
m_search_state = SearchState::Finished;
beginInsertRows(QModelIndex(), m_packs.size(), m_packs.size() + 1);
- m_packs.append(std::make_shared(pack));
+ m_packs.append(pack);
endInsertRows();
}
@@ -448,18 +450,17 @@ void ResourceModel::versionRequestSucceeded(QVector
emit versionListUpdated(index);
}
-void ResourceModel::infoRequestSucceeded(ModPlatform::IndexedPack& pack, const QModelIndex& index)
+void ResourceModel::infoRequestSucceeded(ModPlatform::IndexedPack::Ptr pack, const QModelIndex& index)
{
auto current_pack = data(index, Qt::UserRole).value();
// Check if the index is still valid for this resource or not
- if (pack.addonId != current_pack->addonId)
+ if (pack->addonId != current_pack->addonId)
return;
- *current_pack = pack;
// Cache info :^)
QVariant new_pack;
- new_pack.setValue(current_pack);
+ new_pack.setValue(pack);
if (!setData(index, new_pack, Qt::UserRole)) {
qWarning() << "Failed to cache resource info!";
return;
diff --git a/launcher/ui/pages/modplatform/ResourceModel.h b/launcher/ui/pages/modplatform/ResourceModel.h
index 0b56b2b6a..a261c6e43 100644
--- a/launcher/ui/pages/modplatform/ResourceModel.h
+++ b/launcher/ui/pages/modplatform/ResourceModel.h
@@ -138,13 +138,13 @@ class ResourceModel : public QAbstractListModel {
private:
/* Default search request callbacks */
void searchRequestSucceeded(QList&);
- void searchRequestForOneSucceeded(ModPlatform::IndexedPack&);
+ void searchRequestForOneSucceeded(ModPlatform::IndexedPack::Ptr);
void searchRequestFailed(QString reason, int network_error_code);
void searchRequestAborted();
void versionRequestSucceeded(QVector&, QVariant, const QModelIndex&);
- void infoRequestSucceeded(ModPlatform::IndexedPack&, const QModelIndex&);
+ void infoRequestSucceeded(ModPlatform::IndexedPack::Ptr, const QModelIndex&);
signals:
void versionListUpdated(const QModelIndex& index);
diff --git a/launcher/ui/pages/modplatform/ResourcePackModel.cpp b/launcher/ui/pages/modplatform/ResourcePackModel.cpp
index e774c6f64..53a40faa2 100644
--- a/launcher/ui/pages/modplatform/ResourcePackModel.cpp
+++ b/launcher/ui/pages/modplatform/ResourcePackModel.cpp
@@ -25,14 +25,14 @@ ResourceAPI::SearchArgs ResourcePackResourceModel::createSearchArguments()
ResourceAPI::VersionSearchArgs ResourcePackResourceModel::createVersionsArguments(const QModelIndex& entry)
{
- auto& pack = m_packs[entry.row()];
- return { *pack, {}, {}, ModPlatform::ResourceType::ResourcePack };
+ auto pack = m_packs[entry.row()];
+ return { pack, {}, {}, ModPlatform::ResourceType::ResourcePack };
}
ResourceAPI::ProjectInfoArgs ResourcePackResourceModel::createInfoArguments(const QModelIndex& entry)
{
- auto& pack = m_packs[entry.row()];
- return { *pack };
+ auto pack = m_packs[entry.row()];
+ return { pack };
}
void ResourcePackResourceModel::searchWithTerm(const QString& term, unsigned int sort)
diff --git a/launcher/ui/pages/modplatform/ShaderPackModel.cpp b/launcher/ui/pages/modplatform/ShaderPackModel.cpp
index f54a868db..5349b69ab 100644
--- a/launcher/ui/pages/modplatform/ShaderPackModel.cpp
+++ b/launcher/ui/pages/modplatform/ShaderPackModel.cpp
@@ -22,14 +22,14 @@ ResourceAPI::SearchArgs ShaderPackResourceModel::createSearchArguments()
ResourceAPI::VersionSearchArgs ShaderPackResourceModel::createVersionsArguments(const QModelIndex& entry)
{
- auto& pack = m_packs[entry.row()];
- return { *pack, {}, {}, ModPlatform::ResourceType::ShaderPack };
+ auto pack = m_packs[entry.row()];
+ return { pack, {}, {}, ModPlatform::ResourceType::ShaderPack };
}
ResourceAPI::ProjectInfoArgs ShaderPackResourceModel::createInfoArguments(const QModelIndex& entry)
{
- auto& pack = m_packs[entry.row()];
- return { *pack };
+ auto pack = m_packs[entry.row()];
+ return { pack };
}
void ShaderPackResourceModel::searchWithTerm(const QString& term, unsigned int sort)
diff --git a/launcher/ui/pages/modplatform/flame/FlameModel.cpp b/launcher/ui/pages/modplatform/flame/FlameModel.cpp
index e5d880dca..3ae700fb4 100644
--- a/launcher/ui/pages/modplatform/flame/FlameModel.cpp
+++ b/launcher/ui/pages/modplatform/flame/FlameModel.cpp
@@ -11,6 +11,7 @@
#include
#include
+#include
namespace Flame {
@@ -167,7 +168,7 @@ void ListModel::performPaginatedSearch()
if (m_currentSearchTerm.startsWith("#")) {
auto projectId = m_currentSearchTerm.mid(1);
if (!projectId.isEmpty()) {
- ResourceAPI::Callback callbacks;
+ ResourceAPI::Callback callbacks;
callbacks.on_fail = [this](QString reason, int) { searchRequestFailed(reason); };
callbacks.on_succeed = [this](auto& pack) { searchRequestForOneSucceeded(pack); };
@@ -175,7 +176,9 @@ void ListModel::performPaginatedSearch()
qCritical() << "Search task aborted by an unknown reason!";
searchRequestFailed("Aborted");
};
- if (auto job = api.getProjectInfo({ { projectId } }, std::move(callbacks)); job) {
+ auto project = std::make_shared();
+ project->addonId = projectId;
+ if (auto job = api.getProjectInfo({ project }, std::move(callbacks)); job) {
m_jobPtr = job;
m_jobPtr->start();
}
@@ -189,6 +192,10 @@ void ListModel::performPaginatedSearch()
callbacks.on_succeed = [this](auto& doc) { searchRequestFinished(doc); };
callbacks.on_fail = [this](QString reason, int) { searchRequestFailed(reason); };
+ callbacks.on_abort = [this] {
+ qCritical() << "Search task aborted by an unknown reason!";
+ searchRequestFailed("Aborted");
+ };
auto netJob = api.searchProjects({ ModPlatform::ResourceType::Modpack, m_nextSearchOffset, m_currentSearchTerm, sort, m_filter->loaders,
m_filter->versions, ModPlatform::Side::NoSide, m_filter->categoryIds, m_filter->openSource },
@@ -241,12 +248,12 @@ void Flame::ListModel::searchRequestFinished(QList(pack));
+ m_modpacks.append(pack);
endInsertRows();
}
diff --git a/launcher/ui/pages/modplatform/flame/FlameModel.h b/launcher/ui/pages/modplatform/flame/FlameModel.h
index f98e2be96..92ff098d4 100644
--- a/launcher/ui/pages/modplatform/flame/FlameModel.h
+++ b/launcher/ui/pages/modplatform/flame/FlameModel.h
@@ -50,7 +50,7 @@ class ListModel : public QAbstractListModel {
void searchRequestFinished(QList&);
void searchRequestFailed(QString reason);
- void searchRequestForOneSucceeded(ModPlatform::IndexedPack&);
+ void searchRequestForOneSucceeded(ModPlatform::IndexedPack::Ptr);
private:
void requestLogo(QString file, QString url);
diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.cpp b/launcher/ui/pages/modplatform/flame/FlamePage.cpp
index 059d65438..79faccbc1 100644
--- a/launcher/ui/pages/modplatform/flame/FlamePage.cpp
+++ b/launcher/ui/pages/modplatform/flame/FlamePage.cpp
@@ -203,7 +203,7 @@ void FlamePage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelInde
CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec();
};
- auto netJob = api.getProjectVersions({ *m_current, {}, {}, ModPlatform::ResourceType::Modpack }, std::move(callbacks));
+ auto netJob = api.getProjectVersions({ m_current, {}, {}, ModPlatform::ResourceType::Modpack }, std::move(callbacks));
m_job = netJob;
netJob->start();
diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp
index acd321d4b..33aeb494e 100644
--- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp
+++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp
@@ -47,6 +47,7 @@
#include "net/ApiDownload.h"
#include
+#include
namespace Modrinth {
@@ -137,7 +138,7 @@ void ModpackListModel::performPaginatedSearch()
if (m_currentSearchTerm.startsWith("#")) {
auto projectId = m_currentSearchTerm.mid(1);
if (!projectId.isEmpty()) {
- ResourceAPI::Callback callbacks;
+ ResourceAPI::Callback callbacks;
callbacks.on_fail = [this](QString reason, int) { searchRequestFailed(reason); };
callbacks.on_succeed = [this](auto& pack) { searchRequestForOneSucceeded(pack); };
@@ -145,7 +146,9 @@ void ModpackListModel::performPaginatedSearch()
qCritical() << "Search task aborted by an unknown reason!";
searchRequestFailed("Aborted");
};
- if (auto job = api.getProjectInfo({ projectId }, std::move(callbacks)); job) {
+ auto project = std::make_shared();
+ project->addonId = projectId;
+ if (auto job = api.getProjectInfo({ project }, std::move(callbacks)); job) {
m_jobPtr = job;
m_jobPtr->start();
}
@@ -159,6 +162,10 @@ void ModpackListModel::performPaginatedSearch()
callbacks.on_succeed = [this](auto& doc) { searchRequestFinished(doc); };
callbacks.on_fail = [this](QString reason, int) { searchRequestFailed(reason); };
+ callbacks.on_abort = [this] {
+ qCritical() << "Search task aborted by an unknown reason!";
+ searchRequestFailed("Aborted");
+ };
auto netJob = api.searchProjects({ ModPlatform::ResourceType::Modpack, m_nextSearchOffset, m_currentSearchTerm, sort, m_filter->loaders,
m_filter->versions, ModPlatform::Side::NoSide, m_filter->categoryIds, m_filter->openSource },
@@ -300,12 +307,12 @@ void ModpackListModel::searchRequestFinished(QList(pack));
+ m_modpacks.append(pack);
endInsertRows();
}
diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h
index 7037f4745..114c07ca6 100644
--- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h
+++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h
@@ -86,7 +86,7 @@ class ModpackListModel : public QAbstractListModel {
public slots:
void searchRequestFinished(QList& doc_all);
void searchRequestFailed(QString reason);
- void searchRequestForOneSucceeded(ModPlatform::IndexedPack&);
+ void searchRequestForOneSucceeded(ModPlatform::IndexedPack::Ptr);
protected slots:
diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp
index 8de263078..a29b1ae36 100644
--- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp
+++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp
@@ -146,7 +146,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI
if (!m_current->extraDataLoaded) {
qDebug() << "Loading modrinth modpack information";
- ResourceAPI::Callback callbacks;
+ ResourceAPI::Callback callbacks;
auto id = m_current->addonId;
callbacks.on_fail = [this](QString reason, int) {
@@ -157,10 +157,8 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI
return; // wrong request?
}
- *m_current = pack;
-
QVariant current_updated;
- current_updated.setValue(m_current);
+ current_updated.setValue(pack);
if (!m_model->setData(curr, current_updated, Qt::UserRole))
qWarning() << "Failed to cache extra info for the current pack!";
@@ -168,7 +166,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI
suggestCurrent();
updateUI();
};
- if (auto netJob = m_api.getProjectInfo({ { m_current->addonId } }, std::move(callbacks)); netJob) {
+ if (auto netJob = m_api.getProjectInfo({ m_current }, std::move(callbacks)); netJob) {
m_job = netJob;
m_job->start();
}
@@ -220,7 +218,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI
CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec();
};
- auto netJob = m_api.getProjectVersions({ *m_current, {}, {}, ModPlatform::ResourceType::Modpack }, std::move(callbacks));
+ auto netJob = m_api.getProjectVersions({ m_current, {}, {}, ModPlatform::ResourceType::Modpack }, std::move(callbacks));
m_job2 = netJob;
m_job2->start();
diff --git a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp
index 62bad833b..4fc50ab8d 100644
--- a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp
+++ b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp
@@ -191,7 +191,7 @@ void Technic::ListModel::searchRequestFinished()
if (pack.slug == "vanilla")
continue;
- auto rawURL = Json::ensureString(technicPackObject, "iconUrl", "null");
+ auto rawURL = technicPackObject["iconUrl"].toString("null");
if (rawURL == "null") {
pack.logoUrl = "null";
pack.logoName = "null";
diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp
index 7d999b31e..fef665149 100644
--- a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp
+++ b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp
@@ -202,11 +202,11 @@ void TechnicPage::suggestCurrent()
}
}
- current.minecraftVersion = Json::ensureString(obj, "minecraft", QString(), "__placeholder__");
- current.websiteUrl = Json::ensureString(obj, "platformUrl", QString(), "__placeholder__");
- current.author = Json::ensureString(obj, "user", QString(), "__placeholder__");
- current.description = Json::ensureString(obj, "description", QString(), "__placeholder__");
- current.currentVersion = Json::ensureString(obj, "version", QString(), "__placeholder__");
+ current.minecraftVersion = obj["minecraft"].toString();
+ current.websiteUrl = obj["platformUrl"].toString();
+ current.author = obj["user"].toString();
+ current.description = obj["description"].toString();
+ current.currentVersion = obj["version"].toString();
current.metadataLoaded = true;
metadataLoaded();
diff --git a/launcher/ui/setupwizard/ThemeWizardPage.h b/launcher/ui/setupwizard/ThemeWizardPage.h
index 8ea438398..ef4613e41 100644
--- a/launcher/ui/setupwizard/ThemeWizardPage.h
+++ b/launcher/ui/setupwizard/ThemeWizardPage.h
@@ -42,5 +42,5 @@ class ThemeWizardPage : public BaseWizardPage {
void retranslate() override { widget.retranslateUi(); }
private:
- AppearanceWidget widget{true};
+ AppearanceWidget widget{ true };
};
diff --git a/launcher/ui/themes/CatPack.cpp b/launcher/ui/themes/CatPack.cpp
index 416b86139..7d39c8c05 100644
--- a/launcher/ui/themes/CatPack.cpp
+++ b/launcher/ui/themes/CatPack.cpp
@@ -63,12 +63,12 @@ QString BasicCatPack::path() const
JsonCatPack::PartialDate partialDate(QJsonObject date)
{
- auto month = Json::ensureInteger(date, "month", 1);
+ auto month = date["month"].toInt(1);
if (month > 12)
month = 12;
else if (month <= 0)
month = 1;
- auto day = Json::ensureInteger(date, "day", 1);
+ auto day = date["day"].toInt(1);
if (day > 31)
day = 31;
else if (day <= 0)
@@ -83,9 +83,9 @@ JsonCatPack::JsonCatPack(QFileInfo& manifestInfo) : BasicCatPack(manifestInfo.di
const auto root = doc.object();
m_name = Json::requireString(root, "name", "Catpack name");
m_default_path = FS::PathCombine(path, Json::requireString(root, "default", "Default Cat"));
- auto variants = Json::ensureArray(root, "variants", QJsonArray(), "Catpack Variants");
+ auto variants = root["variants"].toArray();
for (auto v : variants) {
- auto variant = Json::ensureObject(v, QJsonObject(), "Cat variant");
+ auto variant = v.toObject();
m_variants << Variant{ FS::PathCombine(path, Json::requireString(variant, "path", "Variant path")),
partialDate(Json::requireObject(variant, "startTime", "Variant startTime")),
partialDate(Json::requireObject(variant, "endTime", "Variant endTime")) };
diff --git a/launcher/ui/themes/CustomTheme.cpp b/launcher/ui/themes/CustomTheme.cpp
index b8c5738b7..560d0ece8 100644
--- a/launcher/ui/themes/CustomTheme.cpp
+++ b/launcher/ui/themes/CustomTheme.cpp
@@ -179,10 +179,10 @@ bool CustomTheme::read(const QString& path, bool& hasCustomLogColors)
const QJsonObject root = doc.object();
m_name = Json::requireString(root, "name", "Theme name");
m_widgets = Json::requireString(root, "widgets", "Qt widget theme");
- m_qssFilePath = Json::ensureString(root, "qssFilePath", "themeStyle.css");
+ m_qssFilePath = root["qssFilePath"].toString("themeStyle.css");
auto readColor = [](const QJsonObject& colors, const QString& colorName) -> QColor {
- auto colorValue = Json::ensureString(colors, colorName, QString());
+ auto colorValue = colors[colorName].toString();
if (!colorValue.isEmpty()) {
QColor color(colorValue);
if (!color.isValid()) {
@@ -222,7 +222,7 @@ bool CustomTheme::read(const QString& path, bool& hasCustomLogColors)
// fade
m_fadeColor = readColor(colorsRoot, "fadeColor");
- m_fadeAmount = Json::ensureDouble(colorsRoot, "fadeAmount", 0.5, "fade amount");
+ m_fadeAmount = colorsRoot["fadeAmount"].toDouble(0.5);
}
if (root.contains("logColors")) {
diff --git a/launcher/ui/themes/ThemeManager.cpp b/launcher/ui/themes/ThemeManager.cpp
index a7076da5a..6e22c5b76 100644
--- a/launcher/ui/themes/ThemeManager.cpp
+++ b/launcher/ui/themes/ThemeManager.cpp
@@ -180,12 +180,9 @@ void ThemeManager::initializeWidgets()
}
#ifndef Q_OS_MACOS
-void ThemeManager::setTitlebarColorOnMac(WId windowId, QColor color)
-{}
-void ThemeManager::setTitlebarColorOfAllWindowsOnMac(QColor color)
-{}
-void ThemeManager::stopSettingNewWindowColorsOnMac()
-{}
+void ThemeManager::setTitlebarColorOnMac(WId windowId, QColor color) {}
+void ThemeManager::setTitlebarColorOfAllWindowsOnMac(QColor color) {}
+void ThemeManager::stopSettingNewWindowColorsOnMac() {}
#endif
QList ThemeManager::getValidIconThemes()
diff --git a/launcher/ui/themes/ThemeManager.h b/launcher/ui/themes/ThemeManager.h
index 8baa88627..fe0fea926 100644
--- a/launcher/ui/themes/ThemeManager.h
+++ b/launcher/ui/themes/ThemeManager.h
@@ -27,12 +27,10 @@
#include "ui/themes/CatPack.h"
#include "ui/themes/ITheme.h"
-inline auto themeDebugLog()
-{
+inline auto themeDebugLog() {
return qDebug() << "[Theme]";
}
-inline auto themeWarningLog()
-{
+inline auto themeWarningLog() {
return qWarning() << "[Theme]";
}
@@ -65,9 +63,9 @@ class ThemeManager {
private:
std::map> m_themes;
std::map m_icons;
- QDir m_iconThemeFolder{ "iconthemes" };
- QDir m_applicationThemeFolder{ "themes" };
- QDir m_catPacksFolder{ "catpacks" };
+ QDir m_iconThemeFolder{"iconthemes"};
+ QDir m_applicationThemeFolder{"themes"};
+ QDir m_catPacksFolder{"catpacks"};
std::map> m_catPacks;
QPalette m_defaultPalette;
QString m_defaultStyle;
@@ -93,6 +91,6 @@ class ThemeManager {
NSObject* m_windowTitlebarObserver = nullptr;
#endif
- const QStringList builtinIcons{ "pe_colored", "pe_light", "pe_dark", "pe_blue", "breeze_light", "breeze_dark",
- "OSX", "iOS", "flat", "flat_white", "multimc" };
+ const QStringList builtinIcons{"pe_colored", "pe_light", "pe_dark", "pe_blue", "breeze_light", "breeze_dark",
+ "OSX", "iOS", "flat", "flat_white", "multimc"};
};
diff --git a/launcher/ui/widgets/InfoFrame.cpp b/launcher/ui/widgets/InfoFrame.cpp
index 2363b6592..1e641c4f9 100644
--- a/launcher/ui/widgets/InfoFrame.cpp
+++ b/launcher/ui/widgets/InfoFrame.cpp
@@ -227,7 +227,8 @@ void InfoFrame::updateWithResourcePack(ResourcePack& resource_pack)
setImage(resource_pack.image({ 64, 64 }));
}
-void InfoFrame::updateWithDataPack(DataPack& data_pack) {
+void InfoFrame::updateWithDataPack(DataPack& data_pack)
+{
setName(renderColorCodes(data_pack.name()));
setDescription(renderColorCodes(data_pack.description()));
setImage(data_pack.image({ 64, 64 }));
diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp
index c3fb4eb36..cc6a46e43 100644
--- a/launcher/ui/widgets/JavaSettingsWidget.cpp
+++ b/launcher/ui/widgets/JavaSettingsWidget.cpp
@@ -292,7 +292,8 @@ void JavaSettingsWidget::updateThresholds()
const QString warningColour(QStringLiteral("%1"));
if (maxMem >= sysMiB) {
- m_ui->labelMaxMemNotice->setText(QString("%1").arg(tr("Your maximum memory allocation exceeds your system memory capacity.")));
+ m_ui->labelMaxMemNotice->setText(
+ QString("%1").arg(tr("Your maximum memory allocation exceeds your system memory capacity.")));
m_ui->labelMaxMemNotice->show();
} else if (maxMem > (sysMiB * 0.9)) {
m_ui->labelMaxMemNotice->setText(warningColour.arg(tr("Your maximum memory allocation is close to your system memory capacity.")));
diff --git a/launcher/ui/widgets/MinecraftSettingsWidget.cpp b/launcher/ui/widgets/MinecraftSettingsWidget.cpp
index b738cab74..e57233b90 100644
--- a/launcher/ui/widgets/MinecraftSettingsWidget.cpp
+++ b/launcher/ui/widgets/MinecraftSettingsWidget.cpp
@@ -488,7 +488,7 @@ void MinecraftSettingsWidget::openGlobalSettings()
APPLICATION->ShowGlobalSettings(this, "minecraft-settings");
}
-void MinecraftSettingsWidget::updateAccountsMenu(const SettingsObject& settings)
+void MinecraftSettingsWidget::updateAccountsMenu(SettingsObject& settings)
{
m_ui->instanceAccountSelector->clear();
auto accounts = APPLICATION->accounts();
diff --git a/launcher/ui/widgets/MinecraftSettingsWidget.h b/launcher/ui/widgets/MinecraftSettingsWidget.h
index 9e3e7afb2..0dd8e6ba7 100644
--- a/launcher/ui/widgets/MinecraftSettingsWidget.h
+++ b/launcher/ui/widgets/MinecraftSettingsWidget.h
@@ -54,7 +54,7 @@ class MinecraftSettingsWidget : public QWidget {
private:
void openGlobalSettings();
- void updateAccountsMenu(const SettingsObject& settings);
+ void updateAccountsMenu(SettingsObject& settings);
bool isQuickPlaySupported();
private slots:
void saveSelectedLoaders();
diff --git a/launcher/ui/widgets/PageContainer.cpp b/launcher/ui/widgets/PageContainer.cpp
index cffda086c..2e4533a6b 100644
--- a/launcher/ui/widgets/PageContainer.cpp
+++ b/launcher/ui/widgets/PageContainer.cpp
@@ -160,7 +160,6 @@ void PageContainer::createUI()
m_pageStack = new QStackedLayout;
m_pageList = new PageView;
m_header = new QLabel();
- m_iconHeader = new IconLabel(this, QIcon(), QSize(24, 24));
QFont headerLabelFont = m_header->font();
headerLabelFont.setBold(true);
@@ -173,10 +172,6 @@ void PageContainer::createUI()
const int leftMargin = APPLICATION->style()->pixelMetric(QStyle::PM_LayoutLeftMargin);
headerHLayout->addSpacerItem(new QSpacerItem(leftMargin, 0, QSizePolicy::Fixed, QSizePolicy::Ignored));
headerHLayout->addWidget(m_header);
- headerHLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Ignored));
- headerHLayout->addWidget(m_iconHeader);
- const int rightMargin = APPLICATION->style()->pixelMetric(QStyle::PM_LayoutRightMargin);
- headerHLayout->addSpacerItem(new QSpacerItem(rightMargin, 0, QSizePolicy::Fixed, QSizePolicy::Ignored));
headerHLayout->setContentsMargins(0, 6, 0, 0);
m_pageStack->setContentsMargins(0, 0, 0, 0);
@@ -223,12 +218,10 @@ void PageContainer::showPage(int row)
if (m_currentPage) {
m_pageStack->setCurrentIndex(m_currentPage->stackIndex);
m_header->setText(m_currentPage->displayName());
- m_iconHeader->setIcon(m_currentPage->icon());
m_currentPage->opened();
} else {
m_pageStack->setCurrentIndex(0);
m_header->setText(QString());
- m_iconHeader->setIcon(QIcon::fromTheme("bug"));
}
}
diff --git a/launcher/ui/widgets/PageContainer.h b/launcher/ui/widgets/PageContainer.h
index ab4444c99..34970d0cd 100644
--- a/launcher/ui/widgets/PageContainer.h
+++ b/launcher/ui/widgets/PageContainer.h
@@ -112,6 +112,5 @@ class PageContainer : public QWidget, public BasePageContainer {
QStackedLayout* m_pageStack;
QListView* m_pageList;
QLabel* m_header;
- IconLabel* m_iconHeader;
QGridLayout* m_layout;
};
diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp
index 5275b3179..391c15eba 100644
--- a/launcher/updater/prismupdater/PrismUpdater.cpp
+++ b/launcher/updater/prismupdater/PrismUpdater.cpp
@@ -1237,13 +1237,13 @@ int PrismUpdaterApp::parseReleasePage(const QByteArray* response)
GitHubRelease release = {};
release.id = Json::requireInteger(release_obj, "id");
- release.name = Json::ensureString(release_obj, "name");
+ release.name = release_obj["name"].toString();
release.tag_name = Json::requireString(release_obj, "tag_name");
release.created_at = QDateTime::fromString(Json::requireString(release_obj, "created_at"), Qt::ISODate);
- release.published_at = QDateTime::fromString(Json::ensureString(release_obj, "published_at"), Qt::ISODate);
+ release.published_at = QDateTime::fromString(release_obj["published_at"].toString(), Qt::ISODate);
release.draft = Json::requireBoolean(release_obj, "draft");
release.prerelease = Json::requireBoolean(release_obj, "prerelease");
- release.body = Json::ensureString(release_obj, "body");
+ release.body = release_obj["body"].toString();
release.version = Version(release.tag_name);
auto release_assets_obj = Json::requireArray(release_obj, "assets");
@@ -1252,7 +1252,7 @@ int PrismUpdaterApp::parseReleasePage(const QByteArray* response)
GitHubReleaseAsset asset = {};
asset.id = Json::requireInteger(asset_obj, "id");
asset.name = Json::requireString(asset_obj, "name");
- asset.label = Json::ensureString(asset_obj, "label");
+ asset.label = asset_obj["label"].toString();
asset.content_type = Json::requireString(asset_obj, "content_type");
asset.size = Json::requireInteger(asset_obj, "size");
asset.created_at = QDateTime::fromString(Json::requireString(asset_obj, "created_at"), Qt::ISODate);
diff --git a/libraries/qrcodegenerator b/libraries/qrcodegenerator
deleted file mode 160000
index 2c9044de6..000000000
--- a/libraries/qrcodegenerator
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 2c9044de6b049ca25cb3cd1649ed7e27aa055138
diff --git a/nix/unwrapped.nix b/nix/unwrapped.nix
index 93ac21de0..2a00b5620 100644
--- a/nix/unwrapped.nix
+++ b/nix/unwrapped.nix
@@ -9,8 +9,8 @@
jdk17,
kdePackages,
libnbtplusplus,
- qrcodegenerator,
ninja,
+ qrencode,
self,
stripJavaArchivesHook,
tomlplusplus,
@@ -63,9 +63,6 @@ stdenv.mkDerivation {
postUnpack = ''
rm -rf source/libraries/libnbtplusplus
ln -s ${libnbtplusplus} source/libraries/libnbtplusplus
-
- rm -rf source/libraries/qrcodegenerator
- ln -s ${qrcodegenerator} source/libraries/qrcodegenerator
'';
nativeBuildInputs = [
@@ -81,14 +78,13 @@ stdenv.mkDerivation {
kdePackages.qtbase
kdePackages.qtnetworkauth
kdePackages.quazip
+ qrencode
tomlplusplus
zlib
]
++ lib.optionals stdenv.hostPlatform.isDarwin [ apple-sdk_11 ]
++ lib.optional gamemodeSupport gamemode;
- hardeningEnable = lib.optionals stdenv.hostPlatform.isLinux [ "pie" ];
-
cmakeFlags = [
# downstream branding
(lib.cmakeFeature "Launcher_BUILD_PLATFORM" "nixpkgs")
diff --git a/tests/XmlLogs_test.cpp b/tests/XmlLogs_test.cpp
index e01238570..9e7aed974 100644
--- a/tests/XmlLogs_test.cpp
+++ b/tests/XmlLogs_test.cpp
@@ -126,7 +126,11 @@ class XmlLogParseTest : public QObject {
last = entry.level;
} else if (std::holds_alternative(item)) {
auto msg = std::get(item).message;
- auto level = LogParser::guessLevel(msg, last);
+ auto level = LogParser::guessLevel(msg);
+
+ if (level == MessageLevel::Unknown)
+ level = last;
+
out.append(std::make_pair(level, msg));
last = level;
}
diff --git a/tests/testdata/TestLogs/TerraFirmaGreg-Modern-levels.txt b/tests/testdata/TestLogs/TerraFirmaGreg-Modern-levels.txt
index 1b3002117..1d0c18f9d 100644
Binary files a/tests/testdata/TestLogs/TerraFirmaGreg-Modern-levels.txt and b/tests/testdata/TestLogs/TerraFirmaGreg-Modern-levels.txt differ
diff --git a/tests/testdata/TestLogs/TerraFirmaGreg-Modern-xml-levels.txt b/tests/testdata/TestLogs/TerraFirmaGreg-Modern-xml-levels.txt
index 941e5e3fe..42f1e3882 100644
Binary files a/tests/testdata/TestLogs/TerraFirmaGreg-Modern-xml-levels.txt and b/tests/testdata/TestLogs/TerraFirmaGreg-Modern-xml-levels.txt differ
diff --git a/vcpkg-configuration.json b/vcpkg-configuration.json
index 71db4bcca..b1b0996e2 100644
--- a/vcpkg-configuration.json
+++ b/vcpkg-configuration.json
@@ -1,7 +1,7 @@
{
"default-registry": {
"kind": "git",
- "baseline": "0c4cf19224a049cf82f4521e29e39f7bd680440c",
+ "baseline": "1fddddc280dfed63956e15ef74f4321bc6a219c9",
"repository": "https://github.com/microsoft/vcpkg"
},
"registries": [
diff --git a/vcpkg.json b/vcpkg.json
index 4abf8d1b7..0399cdf27 100644
--- a/vcpkg.json
+++ b/vcpkg.json
@@ -2,8 +2,18 @@
"dependencies": [
"bzip2",
"cmark",
- { "name": "ecm", "host": true },
- { "name": "pkgconf", "host": true },
+ {
+ "name": "ecm",
+ "host": true
+ },
+ {
+ "name": "libqrencode",
+ "default-features": false
+ },
+ {
+ "name": "pkgconf",
+ "host": true
+ },
"tomlplusplus",
"zlib"
]