aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build.yml39
-rw-r--r--.github/workflows/trigger_builds.yml2
-rw-r--r--.github/workflows/trigger_release.yml3
-rw-r--r--.github/workflows/winget.yml2
-rw-r--r--README.md4
-rw-r--r--launcher/Application.cpp1
-rw-r--r--launcher/InstanceImportTask.cpp22
-rw-r--r--launcher/InstanceImportTask.h1
-rwxr-xr-xlauncher/Launcher.in2
-rw-r--r--launcher/MMCZip.cpp60
-rw-r--r--launcher/minecraft/PackProfile.cpp39
-rw-r--r--launcher/minecraft/PackProfile.h10
-rw-r--r--launcher/ui/pages/instance/VersionPage.cpp22
-rw-r--r--launcher/ui/pages/instance/VersionPage.h7
-rw-r--r--launcher/ui/pages/instance/VersionPage.ui13
15 files changed, 173 insertions, 54 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 86e88fa1..41eb5eb7 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -15,6 +15,12 @@ on:
SPARKLE_ED25519_KEY:
description: Private key for signing Sparkle updates
required: false
+ WINDOWS_CODESIGN_CERT:
+ description: Certificate for signing Windows builds
+ required: false
+ WINDOWS_CODESIGN_PASSWORD:
+ description: Password for signing Windows builds
+ required: false
CACHIX_AUTH_TOKEN:
description: Private token for authenticating against Cachix cache
required: false
@@ -40,6 +46,7 @@ jobs:
- os: windows-2022
name: "Windows-MinGW-w64"
msystem: clang64
+ vcvars_arch: 'amd64_x86'
- os: windows-2022
name: "Windows-MSVC-Legacy"
@@ -151,7 +158,7 @@ jobs:
- name: Retrieve ccache cache (Windows MinGW-w64)
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
- uses: actions/cache@v3.2.4
+ uses: actions/cache@v3.2.6
with:
path: '${{ github.workspace }}\.ccache'
key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }}
@@ -225,7 +232,7 @@ jobs:
cache: ${{ inputs.is_qt_cached }}
- name: Install MSVC (Windows MSVC)
- if: runner.os == 'Windows' && matrix.msystem == ''
+ if: runner.os == 'Windows' # We want this for MinGW builds as well, as we need SignTool
uses: ilammy/msvc-dev-cmd@v1
with:
vsversion: 2022
@@ -377,6 +384,23 @@ jobs:
Copy-Item D:/a/PrismLauncher/Qt/Tools/OpenSSL/Win_x86/bin/libssl-1_1.dll -Destination libssl-1_1.dll
}
+ - name: Fetch codesign certificate (Windows)
+ if: runner.os == 'Windows'
+ shell: bash # yes, we are not using MSYS2 or PowerShell here
+ run: |
+ echo '${{ secrets.WINDOWS_CODESIGN_CERT }}' | base64 --decode > codesign.pfx
+
+ - name: Sign executable (Windows)
+ if: runner.os == 'Windows'
+ 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 '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com prismlauncher.exe
+ } else {
+ ":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
+ }
+
- name: Package (Windows MinGW-w64, portable)
if: runner.os == 'Windows' && matrix.msystem != ''
shell: msys2 {0}
@@ -396,6 +420,15 @@ jobs:
cd ${{ env.INSTALL_DIR }}
makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
+ - name: Sign installer (Windows)
+ if: runner.os == 'Windows'
+ run: |
+ if (Get-Content ./codesign.pfx){
+ SignTool sign /fd sha256 /td sha256 /f codesign.pfx /p '${{ secrets.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
+ }
+
- name: Package (Linux)
if: runner.os == 'Linux'
run: |
@@ -575,7 +608,7 @@ jobs:
submodules: 'true'
- name: Install nix
if: inputs.build_type == 'Debug'
- uses: cachix/install-nix-action@v18
+ uses: cachix/install-nix-action@v20
with:
install_url: https://nixos.org/nix/install
extra_nix_config: |
diff --git a/.github/workflows/trigger_builds.yml b/.github/workflows/trigger_builds.yml
index a08193a0..26ee4380 100644
--- a/.github/workflows/trigger_builds.yml
+++ b/.github/workflows/trigger_builds.yml
@@ -31,4 +31,6 @@ jobs:
is_qt_cached: true
secrets:
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
+ WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
+ WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml
index a2f89819..3c56a38e 100644
--- a/.github/workflows/trigger_release.yml
+++ b/.github/workflows/trigger_release.yml
@@ -15,6 +15,9 @@ jobs:
is_qt_cached: false
secrets:
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
+ WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
+ WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
+ CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
create_release:
needs: build_release
diff --git a/.github/workflows/winget.yml b/.github/workflows/winget.yml
index ef9561cf..eacf2309 100644
--- a/.github/workflows/winget.yml
+++ b/.github/workflows/winget.yml
@@ -7,7 +7,7 @@ jobs:
publish:
runs-on: windows-latest
steps:
- - uses: vedantmgoyal2009/winget-releaser@v1
+ - uses: vedantmgoyal2009/winget-releaser@v2
with:
identifier: PrismLauncher.PrismLauncher
version: ${{ github.event.release.tag_name }}
diff --git a/README.md b/README.md
index 8765da93..aaa1fd4c 100644
--- a/README.md
+++ b/README.md
@@ -28,7 +28,7 @@ Prebuilt Development builds are provided for **Linux**, **Windows** and **macOS*
For **Arch**, **Debian**, **Fedora**, **OpenSUSE (Tumbleweed)** and **Gentoo**, respectively, you can use these packages for the latest development versions:
-[![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-git) [![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--qt5--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-qt5-git) [![prismlauncher-git](https://img.shields.io/badge/mpr-prismlauncher--git-A80030?label=MPR&logo=debian&logoColor=white)](https://mpr.makedeb.org/packages/prismlauncher-git)<br />[![prismlauncher-nightly](https://img.shields.io/badge/copr-prismlauncher--nightly-51A2DA?label=CORP&logo=fedora&logoColor=white)](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [![prismlauncher-nightly](https://img.shields.io/badge/OBS-prismlauncher--nightly-3AB6A9?logo=opensuse&logoColor=white)](https://build.opensuse.org/project/show/home:getchoo) [![prismlauncher-9999](https://img.shields.io/badge/gentoo-prismlauncher--9999-4D4270?label=Gentoo&logo=gentoo&logoColor=white)](https://packages.gentoo.org/packages/games-action/prismlauncher)
+[![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-git) [![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--qt5--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-qt5-git) [![prismlauncher-git](https://img.shields.io/badge/mpr-prismlauncher--git-A80030?label=MPR&logo=debian&logoColor=white)](https://mpr.makedeb.org/packages/prismlauncher-git)<br />[![prismlauncher-nightly](https://img.shields.io/badge/copr-prismlauncher--nightly-51A2DA?label=COPR&logo=fedora&logoColor=white)](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [![prismlauncher-nightly](https://img.shields.io/badge/OBS-prismlauncher--nightly-3AB6A9?logo=opensuse&logoColor=white)](https://build.opensuse.org/project/show/home:getchoo) [![prismlauncher-9999](https://img.shields.io/badge/gentoo-prismlauncher--9999-4D4270?label=Gentoo&logo=gentoo&logoColor=white)](https://packages.gentoo.org/packages/games-action/prismlauncher)
These packages are also availiable to all the distributions based on the ones mentioned above.
@@ -96,7 +96,7 @@ Be aware that if you build this software without removing the provided API keys
If you do not agree with these terms and conditions, then remove the associated API keys from the [CMakeLists.txt](CMakeLists.txt) file by setting them to an empty string (`""`).
-## License ![https://github.com/PrismLauncher/PrismLauncher/blob/develop/LICENSE](https://img.shields.io/github/license/PrismLauncher/PrismLauncher?label=License&logo=gnu&color=C4282D)
+## License [![https://github.com/PrismLauncher/PrismLauncher/blob/develop/LICENSE](https://img.shields.io/github/license/PrismLauncher/PrismLauncher?label=License&logo=gnu&color=C4282D)](LICENSE)
All launcher code is available under the GPL-3.0-only license.
diff --git a/launcher/Application.cpp b/launcher/Application.cpp
index 2956d798..aed862dc 100644
--- a/launcher/Application.cpp
+++ b/launcher/Application.cpp
@@ -79,6 +79,7 @@
#include <iostream>
#include <mutex>
+#include <QFileOpenEvent>
#include <QAccessible>
#include <QCommandLineParser>
#include <QDir>
diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp
index 70bf5784..080828a8 100644
--- a/launcher/InstanceImportTask.cpp
+++ b/launcher/InstanceImportTask.cpp
@@ -66,7 +66,12 @@ bool InstanceImportTask::abort()
if (m_filesNetJob)
m_filesNetJob->abort();
- m_extractFuture.cancel();
+ if (m_extractFuture.isRunning()) {
+ // NOTE: The tasks created by QtConcurrent::run() can't actually get cancelled,
+ // but we can use this call to check the state when the extraction finishes.
+ m_extractFuture.cancel();
+ m_extractFuture.waitForFinished();
+ }
return Task::abort();
}
@@ -185,18 +190,20 @@ void InstanceImportTask::processZipPack()
// make sure we extract just the pack
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath());
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &InstanceImportTask::extractFinished);
- connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &InstanceImportTask::extractAborted);
m_extractFutureWatcher.setFuture(m_extractFuture);
}
void InstanceImportTask::extractFinished()
{
m_packZip.reset();
- if (!m_extractFuture.result())
- {
+
+ if (m_extractFuture.isCanceled())
+ return;
+ if (!m_extractFuture.result().has_value()) {
emitFailed(tr("Failed to extract modpack"));
return;
}
+
QDir extractDir(m_stagingPath);
qDebug() << "Fixing permissions for extracted pack files...";
@@ -250,11 +257,6 @@ void InstanceImportTask::extractFinished()
}
}
-void InstanceImportTask::extractAborted()
-{
- emitAborted();
-}
-
void InstanceImportTask::processFlame()
{
FlameCreationTask* inst_creation_task = nullptr;
@@ -361,7 +363,7 @@ void InstanceImportTask::processModrinth()
} else {
QString pack_id;
if (!m_sourceUrl.isEmpty()) {
- QRegularExpression regex(R"(data\/(.*)\/versions)");
+ QRegularExpression regex(R"(data\/([^\/]*)\/versions)");
pack_id = regex.match(m_sourceUrl.toString()).captured(1);
}
diff --git a/launcher/InstanceImportTask.h b/launcher/InstanceImportTask.h
index 6b8ac966..7fda439f 100644
--- a/launcher/InstanceImportTask.h
+++ b/launcher/InstanceImportTask.h
@@ -81,7 +81,6 @@ private slots:
void downloadProgressChanged(qint64 current, qint64 total);
void downloadAborted();
void extractFinished();
- void extractAborted();
private: /* data */
NetJob::Ptr m_filesNetJob;
diff --git a/launcher/Launcher.in b/launcher/Launcher.in
index 68fac26a..1a23f255 100755
--- a/launcher/Launcher.in
+++ b/launcher/Launcher.in
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
# Basic start script for running the launcher with the libs packaged with it.
function printerror {
diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp
index f6600343..c6d56543 100644
--- a/launcher/MMCZip.cpp
+++ b/launcher/MMCZip.cpp
@@ -275,7 +275,8 @@ bool MMCZip::findFilesInZip(QuaZip * zip, const QString & what, QStringList & re
// ours
std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & subdir, const QString &target)
{
- QDir directory(target);
+ auto target_top_dir = QUrl::fromLocalFile(target);
+
QStringList extracted;
qDebug() << "Extracting subdir" << subdir << "from" << zip->getZipName() << "to" << target;
@@ -294,48 +295,53 @@ std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & su
return std::nullopt;
}
- do
- {
- QString name = zip->getCurrentFileName();
- if(!name.startsWith(subdir))
- {
+ do {
+ QString file_name = zip->getCurrentFileName();
+ if (!file_name.startsWith(subdir))
continue;
- }
- name.remove(0, subdir.size());
- auto original_name = name;
+ auto relative_file_name = QDir::fromNativeSeparators(file_name.remove(0, subdir.size()));
+ auto original_name = relative_file_name;
+
+ // Fix subdirs/files ending with a / getting transformed into absolute paths
+ if (relative_file_name.startsWith('/'))
+ relative_file_name = relative_file_name.mid(1);
// Fix weird "folders with a single file get squashed" thing
- QString path;
- if(name.contains('/') && !name.endsWith('/')){
- path = name.section('/', 0, -2) + "/";
- FS::ensureFolderPathExists(FS::PathCombine(target, path));
+ QString sub_path;
+ if (relative_file_name.contains('/') && !relative_file_name.endsWith('/')) {
+ sub_path = relative_file_name.section('/', 0, -2) + '/';
+ FS::ensureFolderPathExists(FS::PathCombine(target, sub_path));
- name = name.split('/').last();
+ relative_file_name = relative_file_name.split('/').last();
}
- QString absFilePath;
- if(name.isEmpty())
- {
- absFilePath = directory.absoluteFilePath(name) + "/";
+ QString target_file_path;
+ if (relative_file_name.isEmpty()) {
+ target_file_path = target + '/';
+ } else {
+ target_file_path = FS::PathCombine(target_top_dir.path(), sub_path, relative_file_name);
+ if (relative_file_name.endsWith('/') && !target_file_path.endsWith('/'))
+ target_file_path += '/';
}
- else
- {
- absFilePath = directory.absoluteFilePath(path + name);
+
+ if (!target_top_dir.isParentOf(QUrl::fromLocalFile(target_file_path))) {
+ qWarning() << "Extracting" << relative_file_name << "was cancelled, because it was effectively outside of the target path" << target;
+ return std::nullopt;
}
- if (!JlCompress::extractFile(zip, "", absFilePath))
- {
- qWarning() << "Failed to extract file" << original_name << "to" << absFilePath;
+ if (!JlCompress::extractFile(zip, "", target_file_path)) {
+ qWarning() << "Failed to extract file" << original_name << "to" << target_file_path;
JlCompress::removeFile(extracted);
return std::nullopt;
}
- extracted.append(absFilePath);
- QFile::setPermissions(absFilePath, QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser);
+ extracted.append(target_file_path);
+ QFile::setPermissions(target_file_path, QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser);
- qDebug() << "Extracted file" << name << "to" << absFilePath;
+ qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path;
} while (zip->goToNextFile());
+
return extracted;
}
diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp
index da7c1d84..aff05dbc 100644
--- a/launcher/minecraft/PackProfile.cpp
+++ b/launcher/minecraft/PackProfile.cpp
@@ -1,7 +1,10 @@
-// SPDX-License-Identifier: GPL-3.0-only
+// SPDX-FileCopyrightText: 2022-2023 Sefa Eyeoglu <contact@scrumplex.net>
+//
+// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0
+
/*
* Prism Launcher - Minecraft Launcher
- * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
+ * Copyright (C) 2022-2023 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
*
* This program is free software: you can redistribute it and/or modify
@@ -49,6 +52,7 @@
#include "minecraft/OneSixVersionFormat.h"
#include "FileSystem.h"
#include "minecraft/MinecraftInstance.h"
+#include "minecraft/ProfileUtils.h"
#include "Json.h"
#include "PackProfile.h"
@@ -730,16 +734,47 @@ void PackProfile::invalidateLaunchProfile()
void PackProfile::installJarMods(QStringList selectedFiles)
{
+ // FIXME: get rid of _internal
installJarMods_internal(selectedFiles);
}
void PackProfile::installCustomJar(QString selectedFile)
{
+ // FIXME: get rid of _internal
installCustomJar_internal(selectedFile);
}
+bool PackProfile::installComponents(QStringList selectedFiles)
+{
+ const QString patchDir = FS::PathCombine(d->m_instance->instanceRoot(), "patches");
+ if (!FS::ensureFolderPathExists(patchDir))
+ return false;
+
+ bool result = true;
+ for (const QString& source : selectedFiles) {
+ const QFileInfo sourceInfo(source);
+
+ auto versionFile = ProfileUtils::parseJsonFile(sourceInfo, false);
+ const QString target = FS::PathCombine(patchDir, versionFile->uid + ".json");
+
+ if (!QFile::copy(source, target)) {
+ qWarning() << "Component" << source << "could not be copied to target" << target;
+ result = false;
+ continue;
+ }
+
+ appendComponent(makeShared<Component>(this, versionFile->uid, versionFile));
+ }
+
+ scheduleSave();
+ invalidateLaunchProfile();
+
+ return result;
+}
+
void PackProfile::installAgents(QStringList selectedFiles)
{
+ // FIXME: get rid of _internal
installAgents_internal(selectedFiles);
}
diff --git a/launcher/minecraft/PackProfile.h b/launcher/minecraft/PackProfile.h
index 731cd0ba..d144d875 100644
--- a/launcher/minecraft/PackProfile.h
+++ b/launcher/minecraft/PackProfile.h
@@ -1,7 +1,10 @@
-// SPDX-License-Identifier: GPL-3.0-only
+// SPDX-FileCopyrightText: 2022-2023 Sefa Eyeoglu <contact@scrumplex.net>
+//
+// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0
+
/*
* Prism Launcher - Minecraft Launcher
- * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
+ * Copyright (C) 2022-2023 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
*
* This program is free software: you can redistribute it and/or modify
@@ -86,6 +89,9 @@ public:
/// install a jar/zip as a replacement for the main jar
void installCustomJar(QString selectedFile);
+ /// install MMC/Prism component files
+ bool installComponents(QStringList selectedFiles);
+
/// install Java agent files
void installAgents(QStringList selectedFiles);
diff --git a/launcher/ui/pages/instance/VersionPage.cpp b/launcher/ui/pages/instance/VersionPage.cpp
index 94315395..7fff3b93 100644
--- a/launcher/ui/pages/instance/VersionPage.cpp
+++ b/launcher/ui/pages/instance/VersionPage.cpp
@@ -1,8 +1,11 @@
-// SPDX-License-Identifier: GPL-3.0-only
+// SPDX-FileCopyrightText: 2022-2023 Sefa Eyeoglu <contact@scrumplex.net>
+//
+// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0
+
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
- * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
+ * Copyright (C) 2022-2023 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
*
* This program is free software: you can redistribute it and/or modify
@@ -282,6 +285,7 @@ void VersionPage::updateButtons(int row)
ui->actionRevert->setEnabled(controlsEnabled && patch && patch->isRevertible());
ui->actionDownload_All->setEnabled(controlsEnabled);
ui->actionAdd_Empty->setEnabled(controlsEnabled);
+ ui->actionImport_Components->setEnabled(controlsEnabled);
ui->actionReload->setEnabled(controlsEnabled);
ui->actionInstall_mods->setEnabled(controlsEnabled);
ui->actionReplace_Minecraft_jar->setEnabled(controlsEnabled);
@@ -375,6 +379,20 @@ void VersionPage::on_actionReplace_Minecraft_jar_triggered()
updateButtons();
}
+void VersionPage::on_actionImport_Components_triggered()
+{
+ QStringList list = GuiUtil::BrowseForFiles("component", tr("Select components"), tr("Components (*.json)"),
+ APPLICATION->settings()->get("CentralModsDir").toString(), this->parentWidget());
+
+ if (!list.isEmpty()) {
+ if (!m_profile->installComponents(list)) {
+ QMessageBox::warning(this, tr("Failed to import components"),
+ tr("Some components could not be imported. Check logs for details"));
+ }
+ }
+
+ updateButtons();
+}
void VersionPage::on_actionAdd_Agents_triggered()
{
diff --git a/launcher/ui/pages/instance/VersionPage.h b/launcher/ui/pages/instance/VersionPage.h
index 183bad9a..d0087714 100644
--- a/launcher/ui/pages/instance/VersionPage.h
+++ b/launcher/ui/pages/instance/VersionPage.h
@@ -1,7 +1,11 @@
-// SPDX-License-Identifier: GPL-3.0-only
+// SPDX-FileCopyrightText: 2022-2023 Sefa Eyeoglu <contact@scrumplex.net>
+//
+// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0
+
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
+ * Copyright (C) 2022-2023 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
*
* This program is free software: you can redistribute it and/or modify
@@ -86,6 +90,7 @@ private slots:
void on_actionMove_down_triggered();
void on_actionAdd_to_Minecraft_jar_triggered();
void on_actionReplace_Minecraft_jar_triggered();
+ void on_actionImport_Components_triggered();
void on_actionAdd_Agents_triggered();
void on_actionRevert_triggered();
void on_actionEdit_triggered();
diff --git a/launcher/ui/pages/instance/VersionPage.ui b/launcher/ui/pages/instance/VersionPage.ui
index 4cd50885..4777eafe 100644
--- a/launcher/ui/pages/instance/VersionPage.ui
+++ b/launcher/ui/pages/instance/VersionPage.ui
@@ -108,6 +108,7 @@
<addaction name="actionReplace_Minecraft_jar"/>
<addaction name="actionAdd_Agents"/>
<addaction name="actionAdd_Empty"/>
+ <addaction name="actionImport_Components"/>
<addaction name="separator"/>
<addaction name="actionMinecraftFolder"/>
<addaction name="actionLibrariesFolder"/>
@@ -226,10 +227,10 @@
</action>
<action name="actionAdd_Agents">
<property name="text">
- <string>Add Agents</string>
+ <string>Add Agents</string>
</property>
<property name="toolTip">
- <string>Add Java agents.</string>
+ <string>Add Java agents.</string>
</property>
</action>
<action name="actionAdd_Empty">
@@ -272,6 +273,14 @@
<string>Open the instance's local libraries folder.</string>
</property>
</action>
+ <action name="actionImport_Components">
+ <property name="text">
+ <string>Import Components</string>
+ </property>
+ <property name="toolTip">
+ <string>Import existing component JSON files.</string>
+ </property>
+ </action>
</widget>
<customwidgets>
<customwidget>