mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-09-29 15:31:14 +00:00
Qt: refactor curl stuff into a downloader
And add 'Background' updater
This commit is contained in:
parent
c495ef10b0
commit
14200c1a1f
@ -393,6 +393,11 @@
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Debug - LLVM\moc_downloader.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Debug - LLVM\moc_emu_settings.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
@ -678,6 +683,11 @@
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Debug\moc_downloader.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Debug\moc_emu_settings.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
@ -983,6 +993,11 @@
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Release - LLVM\moc_downloader.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Release - LLVM\moc_emu_settings.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
@ -1268,6 +1283,11 @@
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Release\moc_downloader.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Release\moc_emu_settings.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
@ -1507,6 +1527,7 @@
|
||||
<ClCompile Include="rpcs3qt\curl_handle.cpp" />
|
||||
<ClCompile Include="rpcs3qt\custom_dialog.cpp" />
|
||||
<ClCompile Include="rpcs3qt\debugger_list.cpp" />
|
||||
<ClCompile Include="rpcs3qt\downloader.cpp" />
|
||||
<ClCompile Include="rpcs3qt\fatal_error_dialog.cpp" />
|
||||
<ClCompile Include="rpcs3qt\gui_application.cpp" />
|
||||
<ClCompile Include="rpcs3qt\input_dialog.cpp" />
|
||||
@ -2269,6 +2290,24 @@
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl" "-I.\..\3rdparty\curl\include" "-I.\..\3rdparty\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="rpcs3qt\downloader.h">
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||
<Message Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">Moc%27ing %(Identity)...</Message>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl" "-I.\..\3rdparty\curl\include" "-I.\..\3rdparty\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl" "-I.\..\3rdparty\curl\include" "-I.\..\3rdparty\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing %(Identity)...</Message>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl" "-I.\..\3rdparty\curl\include" "-I.\..\3rdparty\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||
<Message Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">Moc%27ing %(Identity)...</Message>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl" "-I.\..\3rdparty\curl\include" "-I.\..\3rdparty\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
||||
</CustomBuild>
|
||||
<ClInclude Include="rpcs3qt\emu_settings_type.h" />
|
||||
<CustomBuild Include="rpcs3qt\render_creator.h">
|
||||
<Message Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">Moc%27ing %(Identity)...</Message>
|
||||
|
@ -131,6 +131,9 @@
|
||||
<Filter Include="Gui\patch manager">
|
||||
<UniqueIdentifier>{e72a0cbe-fbcd-4a0b-8c17-a2a3b7a42258}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Gui\network">
|
||||
<UniqueIdentifier>{bad5498c-a915-4a96-b0cc-f754c02d8e65}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp">
|
||||
@ -901,9 +904,6 @@
|
||||
<ClCompile Include="QTGeneratedFiles\Debug - LLVM\moc_update_manager.cpp">
|
||||
<Filter>Generated Files\Debug - LLVM</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rpcs3qt\curl_handle.cpp">
|
||||
<Filter>rpcs3</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rpcs3qt\screenshot_manager_dialog.cpp">
|
||||
<Filter>Gui\screenshot manager</Filter>
|
||||
</ClCompile>
|
||||
@ -1057,6 +1057,24 @@
|
||||
<ClCompile Include="QTGeneratedFiles\Debug - LLVM\moc_patch_manager_dialog.cpp">
|
||||
<Filter>Generated Files\Debug - LLVM</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rpcs3qt\curl_handle.cpp">
|
||||
<Filter>Gui\network</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rpcs3qt\downloader.cpp">
|
||||
<Filter>Gui\network</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Release - LLVM\moc_downloader.cpp">
|
||||
<Filter>Generated Files\Release - LLVM</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Debug\moc_downloader.cpp">
|
||||
<Filter>Generated Files\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Release\moc_downloader.cpp">
|
||||
<Filter>Generated Files\Release</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Debug - LLVM\moc_downloader.cpp">
|
||||
<Filter>Generated Files\Debug - LLVM</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Input\ds4_pad_handler.h">
|
||||
@ -1140,9 +1158,6 @@
|
||||
<ClInclude Include="rpcs3qt\category.h">
|
||||
<Filter>Gui\game list</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rpcs3qt\curl_handle.h">
|
||||
<Filter>rpcs3</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rpcs3qt\emu_settings_type.h">
|
||||
<Filter>Gui\settings</Filter>
|
||||
</ClInclude>
|
||||
@ -1158,6 +1173,9 @@
|
||||
<ClInclude Include="QTGeneratedFiles\ui_patch_manager_dialog.h">
|
||||
<Filter>Generated Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rpcs3qt\curl_handle.h">
|
||||
<Filter>Gui\network</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="debug\moc_predefs.h.cbt">
|
||||
@ -1376,6 +1394,9 @@
|
||||
<CustomBuild Include="rpcs3qt\patch_manager_dialog.ui">
|
||||
<Filter>Form Files</Filter>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="rpcs3qt\downloader.h">
|
||||
<Filter>Gui\network</Filter>
|
||||
</CustomBuild>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="rpcs3.ico" />
|
||||
|
@ -11,6 +11,7 @@
|
||||
custom_dialog.cpp
|
||||
debugger_frame.cpp
|
||||
debugger_list.cpp
|
||||
downloader.cpp
|
||||
_discord_utils.cpp
|
||||
emu_settings.cpp
|
||||
fatal_error_dialog.cpp
|
||||
|
164
rpcs3/rpcs3qt/downloader.cpp
Normal file
164
rpcs3/rpcs3qt/downloader.cpp
Normal file
@ -0,0 +1,164 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QThread>
|
||||
|
||||
#include "downloader.h"
|
||||
#include "curl_handle.h"
|
||||
#include "progress_dialog.h"
|
||||
|
||||
LOG_CHANNEL(network_log, "NETWORK");
|
||||
|
||||
size_t curl_write_cb_compat(char* ptr, size_t /*size*/, size_t nmemb, void* userdata)
|
||||
{
|
||||
downloader* download = reinterpret_cast<downloader*>(userdata);
|
||||
return download->update_buffer(ptr, nmemb);
|
||||
}
|
||||
|
||||
downloader::downloader(const std::string& thread_name, QWidget* parent)
|
||||
: QObject(parent)
|
||||
, m_parent(parent)
|
||||
, m_thread_name(thread_name)
|
||||
{
|
||||
m_curl = new curl_handle(this);
|
||||
}
|
||||
|
||||
void downloader::start(const std::string& url, bool follow_location, bool show_progress_dialog, const QString& progress_dialog_title, bool keep_progress_dialog_open, int exptected_size)
|
||||
{
|
||||
connect(this, &downloader::signal_buffer_update, this, &downloader::handle_buffer_update);
|
||||
|
||||
m_keep_progress_dialog_open = keep_progress_dialog_open;
|
||||
m_curl_buf.clear();
|
||||
m_curl_abort = false;
|
||||
|
||||
curl_easy_setopt(m_curl->get_curl(), CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(m_curl->get_curl(), CURLOPT_WRITEFUNCTION, curl_write_cb_compat);
|
||||
curl_easy_setopt(m_curl->get_curl(), CURLOPT_WRITEDATA, this);
|
||||
curl_easy_setopt(m_curl->get_curl(), CURLOPT_FOLLOWLOCATION, follow_location ? 1 : 0);
|
||||
|
||||
const auto thread = QThread::create([this]
|
||||
{
|
||||
const auto result = curl_easy_perform(m_curl->get_curl());
|
||||
m_curl_success = result == CURLE_OK;
|
||||
|
||||
if (!m_curl_success && !m_curl_abort)
|
||||
{
|
||||
const std::string error = "Curl error: " + std::string{ curl_easy_strerror(result) };
|
||||
network_log.error("%s", error);
|
||||
Q_EMIT signal_download_error(QString::fromStdString(error));
|
||||
}
|
||||
});
|
||||
|
||||
connect(thread, &QThread::finished, this, [this]()
|
||||
{
|
||||
if (m_curl_abort)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_progress_dialog && (!m_keep_progress_dialog_open || !m_curl_success))
|
||||
{
|
||||
m_progress_dialog->close();
|
||||
m_progress_dialog = nullptr;
|
||||
}
|
||||
|
||||
if (m_curl_success)
|
||||
{
|
||||
Q_EMIT signal_download_finished(m_curl_buf);
|
||||
}
|
||||
});
|
||||
|
||||
if (show_progress_dialog)
|
||||
{
|
||||
const int maximum = exptected_size > 0 ? exptected_size : 100;
|
||||
|
||||
if (m_progress_dialog)
|
||||
{
|
||||
m_progress_dialog->setWindowTitle(progress_dialog_title);
|
||||
m_progress_dialog->setAutoClose(!m_keep_progress_dialog_open);
|
||||
m_progress_dialog->setMaximum(maximum);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_progress_dialog = new progress_dialog(progress_dialog_title, tr("Please wait..."), tr("Abort"), 0, maximum, true, m_parent);
|
||||
m_progress_dialog->setAutoReset(false);
|
||||
m_progress_dialog->setAutoClose(!m_keep_progress_dialog_open);
|
||||
m_progress_dialog->show();
|
||||
|
||||
// Handle abort
|
||||
connect(m_progress_dialog, &QProgressDialog::canceled, this, [this]()
|
||||
{
|
||||
m_curl_abort = true;
|
||||
close_progress_dialog();
|
||||
});
|
||||
connect(m_progress_dialog, &QProgressDialog::finished, m_progress_dialog, &QProgressDialog::deleteLater);
|
||||
}
|
||||
}
|
||||
|
||||
thread->setObjectName("Compat Update");
|
||||
thread->start();
|
||||
}
|
||||
|
||||
void downloader::update_progress_dialog(const QString& title)
|
||||
{
|
||||
if (m_progress_dialog)
|
||||
{
|
||||
m_progress_dialog->setWindowTitle(title);
|
||||
}
|
||||
}
|
||||
|
||||
void downloader::close_progress_dialog()
|
||||
{
|
||||
if (m_progress_dialog)
|
||||
{
|
||||
m_progress_dialog->close();
|
||||
m_progress_dialog = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
progress_dialog* downloader::get_progress_dialog() const
|
||||
{
|
||||
return m_progress_dialog;
|
||||
}
|
||||
|
||||
size_t downloader::update_buffer(char* data, size_t size)
|
||||
{
|
||||
if (m_curl_abort)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto old_size = m_curl_buf.size();
|
||||
const auto new_size = old_size + size;
|
||||
m_curl_buf.resize(static_cast<int>(new_size));
|
||||
memcpy(m_curl_buf.data() + old_size, data, size);
|
||||
|
||||
int max = 0;
|
||||
|
||||
if (m_actual_download_size < 0)
|
||||
{
|
||||
if (curl_easy_getinfo(m_curl->get_curl(), CURLINFO_CONTENT_LENGTH_DOWNLOAD, &m_actual_download_size) == CURLE_OK && m_actual_download_size > 0)
|
||||
{
|
||||
max = static_cast<int>(m_actual_download_size);
|
||||
}
|
||||
}
|
||||
|
||||
Q_EMIT signal_buffer_update(static_cast<int>(new_size), max);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void downloader::handle_buffer_update(int size, int max)
|
||||
{
|
||||
if (m_curl_abort)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_progress_dialog)
|
||||
{
|
||||
m_progress_dialog->setMaximum(max > 0 ? max : m_progress_dialog->maximum());
|
||||
m_progress_dialog->setValue(size);
|
||||
QApplication::processEvents();
|
||||
}
|
||||
}
|
46
rpcs3/rpcs3qt/downloader.h
Normal file
46
rpcs3/rpcs3qt/downloader.h
Normal file
@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
class curl_handle;
|
||||
class progress_dialog;
|
||||
|
||||
class downloader : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
downloader(const std::string& thread_name, QWidget* parent = nullptr);
|
||||
|
||||
void start(const std::string& url, bool follow_location, bool show_progress_dialog, const QString& progress_dialog_title = "", bool keep_progress_dialog_open = false, int expected_size = -1);
|
||||
size_t update_buffer(char* data, size_t size);
|
||||
|
||||
void update_progress_dialog(const QString& title);
|
||||
void close_progress_dialog();
|
||||
|
||||
progress_dialog* get_progress_dialog() const;
|
||||
|
||||
private Q_SLOTS:
|
||||
void handle_buffer_update(int size, int max);
|
||||
|
||||
Q_SIGNALS:
|
||||
void signal_download_error(const QString& error);
|
||||
void signal_download_finished(const QByteArray& data);
|
||||
void signal_buffer_update(int size, int max);
|
||||
|
||||
private:
|
||||
QWidget* m_parent = nullptr;
|
||||
std::string m_thread_name;
|
||||
|
||||
curl_handle* m_curl = nullptr;
|
||||
QByteArray m_curl_buf;
|
||||
std::atomic<bool> m_curl_abort = false;
|
||||
std::atomic<bool> m_curl_success = false;
|
||||
double m_actual_download_size = -1.0;
|
||||
|
||||
progress_dialog* m_progress_dialog = nullptr;
|
||||
std::atomic<bool> m_keep_progress_dialog_open = false;
|
||||
QString m_progress_dialog_title;
|
||||
};
|
@ -1,77 +1,67 @@
|
||||
#include "game_compatibility.h"
|
||||
#include "gui_settings.h"
|
||||
#include "progress_dialog.h"
|
||||
#include "curl_handle.h"
|
||||
#include "downloader.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QMessageBox>
|
||||
#include <QJsonDocument>
|
||||
#include <QThread>
|
||||
|
||||
LOG_CHANNEL(compat_log, "Compat");
|
||||
|
||||
constexpr auto qstr = QString::fromStdString;
|
||||
inline std::string sstr(const QString& _in) { return _in.toStdString(); }
|
||||
|
||||
size_t curl_write_cb_compat(char* ptr, size_t /*size*/, size_t nmemb, void* userdata)
|
||||
game_compatibility::game_compatibility(std::shared_ptr<gui_settings> settings, QWidget* parent)
|
||||
: QObject(parent)
|
||||
, m_gui_settings(settings)
|
||||
{
|
||||
game_compatibility* gm_cmp = reinterpret_cast<game_compatibility*>(userdata);
|
||||
return gm_cmp->update_buffer(ptr, nmemb);
|
||||
}
|
||||
|
||||
game_compatibility::game_compatibility(std::shared_ptr<gui_settings> settings) : m_xgui_settings(settings)
|
||||
{
|
||||
m_filepath = m_xgui_settings->GetSettingsDir() + "/compat_database.dat";
|
||||
m_url = "https://rpcs3.net/compatibility?api=v1&export";
|
||||
|
||||
m_curl = new curl_handle(this);
|
||||
|
||||
m_filepath = m_gui_settings->GetSettingsDir() + "/compat_database.dat";
|
||||
m_downloader = new downloader("Compat Update", parent);
|
||||
RequestCompatibility();
|
||||
|
||||
// We need this signal in order to update the GUI from the main thread
|
||||
connect(this, &game_compatibility::signal_buffer_update, this, &game_compatibility::handle_buffer_update);
|
||||
connect(m_downloader, &downloader::signal_download_error, this, &game_compatibility::handle_download_error);
|
||||
connect(m_downloader, &downloader::signal_download_finished, this, &game_compatibility::handle_download_finished);
|
||||
}
|
||||
|
||||
void game_compatibility::handle_buffer_update(int size, int max)
|
||||
void game_compatibility::handle_download_error(const QString& error)
|
||||
{
|
||||
if (m_progress_dialog)
|
||||
{
|
||||
m_progress_dialog->setMaximum(max);
|
||||
m_progress_dialog->setValue(size);
|
||||
QApplication::processEvents();
|
||||
}
|
||||
Q_EMIT DownloadError(error);
|
||||
}
|
||||
|
||||
size_t game_compatibility::update_buffer(char* data, size_t size)
|
||||
void game_compatibility::handle_download_finished(const QByteArray& data)
|
||||
{
|
||||
if (m_curl_abort)
|
||||
compat_log.notice("Database download finished");
|
||||
|
||||
// Create new map from database and write database to file if database was valid
|
||||
if (ReadJSON(QJsonDocument::fromJson(data).object(), true))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
// We have a new database in map, therefore refresh gamelist to new state
|
||||
Q_EMIT DownloadFinished();
|
||||
|
||||
const auto old_size = m_curl_buf.size();
|
||||
const auto new_size = old_size + size;
|
||||
m_curl_buf.resize(static_cast<int>(new_size));
|
||||
memcpy(m_curl_buf.data() + old_size, data, size);
|
||||
// Write database to file
|
||||
QFile file(m_filepath);
|
||||
|
||||
int max = m_progress_dialog ? m_progress_dialog->maximum() : 0;
|
||||
|
||||
if (m_actual_dwnld_size < 0)
|
||||
{
|
||||
if (curl_easy_getinfo(m_curl->get_curl(), CURLINFO_CONTENT_LENGTH_DOWNLOAD, &m_actual_dwnld_size) == CURLE_OK && m_actual_dwnld_size > 0)
|
||||
if (file.exists())
|
||||
{
|
||||
max = static_cast<int>(m_actual_dwnld_size);
|
||||
compat_log.notice("Database file found: %s", sstr(m_filepath));
|
||||
}
|
||||
|
||||
if (!file.open(QIODevice::WriteOnly))
|
||||
{
|
||||
compat_log.error("Database Error - Could not write database to file: %s", sstr(m_filepath));
|
||||
return;
|
||||
}
|
||||
|
||||
file.write(data);
|
||||
file.close();
|
||||
|
||||
compat_log.success("Wrote database to file: %s", sstr(m_filepath));
|
||||
}
|
||||
|
||||
Q_EMIT signal_buffer_update(static_cast<int>(new_size), max);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
bool game_compatibility::ReadJSON(const QJsonObject& json_data, bool after_download)
|
||||
{
|
||||
int return_code = json_data["return_code"].toInt();
|
||||
const int return_code = json_data["return_code"].toInt();
|
||||
|
||||
if (return_code < 0)
|
||||
{
|
||||
@ -167,77 +157,10 @@ void game_compatibility::RequestCompatibility(bool online)
|
||||
return;
|
||||
}
|
||||
|
||||
compat_log.notice("Beginning compatibility database download from: %s", m_url);
|
||||
const std::string url = "https://rpcs3.net/compatibility?api=v1&export";
|
||||
compat_log.notice("Beginning compatibility database download from: %s", url);
|
||||
|
||||
// Show Progress
|
||||
m_progress_dialog = new progress_dialog(tr("Downloading Database"), tr("Please wait..."), tr("Abort"), 0, 100, true);
|
||||
m_progress_dialog->show();
|
||||
|
||||
curl_easy_setopt(m_curl->get_curl(), CURLOPT_URL, m_url.c_str());
|
||||
curl_easy_setopt(m_curl->get_curl(), CURLOPT_WRITEFUNCTION, curl_write_cb_compat);
|
||||
curl_easy_setopt(m_curl->get_curl(), CURLOPT_WRITEDATA, this);
|
||||
curl_easy_setopt(m_curl->get_curl(), CURLOPT_FOLLOWLOCATION, 1);
|
||||
|
||||
m_curl_buf.clear();
|
||||
m_curl_abort = false;
|
||||
|
||||
// Handle abort
|
||||
connect(m_progress_dialog, &QProgressDialog::canceled, [this] { m_curl_abort = true; });
|
||||
connect(m_progress_dialog, &QProgressDialog::finished, m_progress_dialog, &QProgressDialog::deleteLater);
|
||||
|
||||
auto thread = QThread::create([&]
|
||||
{
|
||||
const auto result = curl_easy_perform(m_curl->get_curl());
|
||||
m_curl_result = result == CURLE_OK;
|
||||
|
||||
if (!m_curl_result)
|
||||
{
|
||||
Q_EMIT DownloadError(qstr("Curl error: ") + qstr(curl_easy_strerror(result)));
|
||||
}
|
||||
});
|
||||
connect(thread, &QThread::finished, this, [this, online]()
|
||||
{
|
||||
if (m_progress_dialog)
|
||||
{
|
||||
m_progress_dialog->close();
|
||||
m_progress_dialog = nullptr;
|
||||
}
|
||||
|
||||
if (!m_curl_result)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
compat_log.notice("Database download finished");
|
||||
|
||||
// Create new map from database and write database to file if database was valid
|
||||
if (ReadJSON(QJsonDocument::fromJson(m_curl_buf).object(), online))
|
||||
{
|
||||
// We have a new database in map, therefore refresh gamelist to new state
|
||||
Q_EMIT DownloadFinished();
|
||||
|
||||
// Write database to file
|
||||
QFile file(m_filepath);
|
||||
|
||||
if (file.exists())
|
||||
{
|
||||
compat_log.notice("Database file found: %s", sstr(m_filepath));
|
||||
}
|
||||
|
||||
if (!file.open(QIODevice::WriteOnly))
|
||||
{
|
||||
compat_log.error("Database Error - Could not write database to file: %s", sstr(m_filepath));
|
||||
return;
|
||||
}
|
||||
|
||||
file.write(m_curl_buf);
|
||||
file.close();
|
||||
|
||||
compat_log.success("Wrote database to file: %s", sstr(m_filepath));
|
||||
}
|
||||
});
|
||||
thread->setObjectName("Compat Update");
|
||||
thread->start();
|
||||
m_downloader->start(url, true, true, tr("Downloading Database"));
|
||||
|
||||
// We want to retrieve a new database, therefore refresh gamelist and indicate that
|
||||
Q_EMIT DownloadStarted();
|
||||
|
@ -5,9 +5,8 @@
|
||||
#include <QPainter>
|
||||
#include <QJsonObject>
|
||||
|
||||
class curl_handle;
|
||||
class downloader;
|
||||
class gui_settings;
|
||||
class progress_dialog;
|
||||
|
||||
struct compat_status
|
||||
{
|
||||
@ -35,16 +34,9 @@ private:
|
||||
{ "NoData", { 6, "", "", tr("Database missing"), tr("Right click here and download the current database.\nMake sure you are connected to the internet.") } },
|
||||
{ "Download", { 7, "", "", tr("Retrieving..."), tr("Downloading the compatibility database. Please wait...") } }
|
||||
};
|
||||
int m_timer_count = 0;
|
||||
std::shared_ptr<gui_settings> m_gui_settings;
|
||||
QString m_filepath;
|
||||
std::string m_url;
|
||||
std::atomic<bool> m_curl_result = false;
|
||||
std::atomic<bool> m_curl_abort = false;
|
||||
double m_actual_dwnld_size = -1.0;
|
||||
curl_handle* m_curl = nullptr;
|
||||
QByteArray m_curl_buf;
|
||||
progress_dialog* m_progress_dialog = nullptr;
|
||||
std::shared_ptr<gui_settings> m_xgui_settings;
|
||||
downloader* m_downloader = nullptr;
|
||||
std::map<std::string, compat_status> m_compat_database;
|
||||
|
||||
/** Creates new map from the database */
|
||||
@ -52,7 +44,7 @@ private:
|
||||
|
||||
public:
|
||||
/** Handles reads, writes and downloads for the compatibility database */
|
||||
game_compatibility(std::shared_ptr<gui_settings> settings);
|
||||
game_compatibility(std::shared_ptr<gui_settings> settings, QWidget* parent);
|
||||
|
||||
/** Reads database. If online set to true: Downloads and writes the database to file */
|
||||
void RequestCompatibility(bool online = false);
|
||||
@ -63,16 +55,14 @@ public:
|
||||
/** Returns the data for the requested status */
|
||||
compat_status GetStatusData(const QString& status);
|
||||
|
||||
size_t update_buffer(char* data, size_t size);
|
||||
|
||||
Q_SIGNALS:
|
||||
void DownloadStarted();
|
||||
void DownloadFinished();
|
||||
void DownloadError(const QString& error);
|
||||
void signal_buffer_update(int size, int max);
|
||||
|
||||
private Q_SLOTS:
|
||||
void handle_buffer_update(int size, int max);
|
||||
void handle_download_error(const QString& error);
|
||||
void handle_download_finished(const QByteArray& data);
|
||||
};
|
||||
|
||||
class compat_pixmap : public QPixmap
|
||||
|
@ -95,7 +95,7 @@ game_list_frame::game_list_frame(std::shared_ptr<gui_settings> gui_settings, std
|
||||
m_game_list->installEventFilter(this);
|
||||
m_game_list->setColumnCount(gui::column_count);
|
||||
|
||||
m_game_compat = std::make_unique<game_compatibility>(m_gui_settings);
|
||||
m_game_compat = new game_compatibility(m_gui_settings, this);
|
||||
|
||||
m_central_widget = new QStackedWidget(this);
|
||||
m_central_widget->addWidget(m_game_list);
|
||||
@ -143,7 +143,7 @@ game_list_frame::game_list_frame(std::shared_ptr<gui_settings> gui_settings, std
|
||||
connect(m_game_grid, &QTableWidget::itemSelectionChanged, this, &game_list_frame::itemSelectionChangedSlot);
|
||||
connect(m_game_grid, &QTableWidget::itemDoubleClicked, this, &game_list_frame::doubleClickedSlot);
|
||||
|
||||
connect(m_game_compat.get(), &game_compatibility::DownloadStarted, [this]()
|
||||
connect(m_game_compat, &game_compatibility::DownloadStarted, [this]()
|
||||
{
|
||||
for (const auto& game : m_game_data)
|
||||
{
|
||||
@ -151,7 +151,7 @@ game_list_frame::game_list_frame(std::shared_ptr<gui_settings> gui_settings, std
|
||||
}
|
||||
Refresh();
|
||||
});
|
||||
connect(m_game_compat.get(), &game_compatibility::DownloadFinished, [this]()
|
||||
connect(m_game_compat, &game_compatibility::DownloadFinished, [this]()
|
||||
{
|
||||
for (const auto& game : m_game_data)
|
||||
{
|
||||
@ -159,14 +159,14 @@ game_list_frame::game_list_frame(std::shared_ptr<gui_settings> gui_settings, std
|
||||
}
|
||||
Refresh();
|
||||
});
|
||||
connect(m_game_compat.get(), &game_compatibility::DownloadError, [this](const QString& error)
|
||||
connect(m_game_compat, &game_compatibility::DownloadError, [this](const QString& error)
|
||||
{
|
||||
for (const auto& game : m_game_data)
|
||||
{
|
||||
game->compat = m_game_compat->GetCompatibility(game->info.serial);
|
||||
}
|
||||
Refresh();
|
||||
QMessageBox::warning(this, tr("Warning!"), tr("Failed to retrieve the online compatibility database!\nFalling back to local database.\n\n") + tr(qPrintable(error)));
|
||||
QMessageBox::warning(this, tr("Warning!"), tr("Failed to retrieve the online compatibility database!\nFalling back to local database.\n\n%0").arg(error));
|
||||
});
|
||||
|
||||
for (int col = 0; col < m_columnActs.count(); ++col)
|
||||
|
@ -128,15 +128,15 @@ private:
|
||||
game_info GetGameInfoFromItem(const QTableWidgetItem* item);
|
||||
|
||||
// Which widget we are displaying depends on if we are in grid or list mode.
|
||||
QMainWindow* m_game_dock;
|
||||
QStackedWidget* m_central_widget;
|
||||
QMainWindow* m_game_dock = nullptr;
|
||||
QStackedWidget* m_central_widget = nullptr;
|
||||
|
||||
// Game Grid
|
||||
game_list_grid* m_game_grid;
|
||||
game_list_grid* m_game_grid = nullptr;
|
||||
|
||||
// Game List
|
||||
game_list* m_game_list;
|
||||
std::unique_ptr<game_compatibility> m_game_compat;
|
||||
game_list* m_game_list = nullptr;
|
||||
game_compatibility* m_game_compat = nullptr;
|
||||
QList<QAction*> m_columnActs;
|
||||
Qt::SortOrder m_col_sort_order;
|
||||
int m_sort_column;
|
||||
|
@ -176,10 +176,35 @@ void main_window::Init()
|
||||
// Fix possible hidden game list columns. The game list has to be visible already. Use this after show()
|
||||
m_game_list_frame->FixNarrowColumns();
|
||||
|
||||
#if defined(_WIN32) || defined(__linux__)
|
||||
if (m_gui_settings->GetValue(gui::m_check_upd_start).toBool())
|
||||
// RPCS3 Updater
|
||||
|
||||
QMenuBar *corner_bar = new QMenuBar(ui->menuBar);
|
||||
|
||||
QMenu *download_menu = new QMenu(tr("Update Available!"), corner_bar);
|
||||
corner_bar->addMenu(download_menu);
|
||||
|
||||
QAction *download_action = new QAction(tr("Download Update"), download_menu);
|
||||
connect(download_action, &QAction::triggered, this, [this]
|
||||
{
|
||||
m_updater.check_for_updates(true, this);
|
||||
m_updater.update(false);
|
||||
});
|
||||
|
||||
download_menu->addAction(download_action);
|
||||
ui->menuBar->setCornerWidget(corner_bar);
|
||||
ui->menuBar->cornerWidget()->setVisible(false);
|
||||
|
||||
connect(&m_updater, &update_manager::signal_update_available, this, [this](bool update_available)
|
||||
{
|
||||
if (ui->menuBar->cornerWidget())
|
||||
{
|
||||
ui->menuBar->cornerWidget()->setVisible(update_available);
|
||||
}
|
||||
});
|
||||
|
||||
#if defined(_WIN32) || defined(__linux__)
|
||||
if (const auto update_value = m_gui_settings->GetValue(gui::m_check_upd_start).toString(); update_value != "false")
|
||||
{
|
||||
m_updater.check_for_updates(true, update_value != "true", this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -1538,7 +1563,7 @@ void main_window::CreateConnects()
|
||||
std::unordered_map<std::string, std::set<std::string>> games;
|
||||
if (m_game_list_frame)
|
||||
{
|
||||
for (const auto game : m_game_list_frame->GetGameInfo())
|
||||
for (const auto& game : m_game_list_frame->GetGameInfo())
|
||||
{
|
||||
if (game)
|
||||
{
|
||||
@ -1675,7 +1700,7 @@ void main_window::CreateConnects()
|
||||
QMessageBox::warning(this, tr("Auto-updater"), tr("Please stop the emulation before trying to update."));
|
||||
return;
|
||||
}
|
||||
m_updater.check_for_updates(false, this);
|
||||
m_updater.check_for_updates(false, false, this);
|
||||
});
|
||||
|
||||
connect(ui->aboutAct, &QAction::triggered, [this]
|
||||
|
@ -341,7 +341,7 @@ namespace gui
|
||||
{
|
||||
bool match = true;
|
||||
|
||||
for (const auto [role, data] : criteria)
|
||||
for (const auto& [role, data] : criteria)
|
||||
{
|
||||
if (item->data(0, role) != data)
|
||||
{
|
||||
|
@ -1537,7 +1537,18 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> gui_settings, std
|
||||
ui->cb_show_pkg_install->setChecked(m_gui_settings->GetValue(gui::ib_pkg_success).toBool());
|
||||
ui->cb_show_pup_install->setChecked(m_gui_settings->GetValue(gui::ib_pup_success).toBool());
|
||||
|
||||
ui->cb_check_update_start->setChecked(m_gui_settings->GetValue(gui::m_check_upd_start).toBool());
|
||||
const QString updates_yes = tr("Yes", "Updates");
|
||||
const QString updates_background = tr("Background", "Updates");
|
||||
const QString updates_no = tr("No", "Updates");
|
||||
|
||||
ui->combo_updates->addItem(updates_yes, "true");
|
||||
ui->combo_updates->addItem(updates_background, "background");
|
||||
ui->combo_updates->addItem(updates_no, "false");
|
||||
ui->combo_updates->setCurrentIndex(ui->combo_updates->findData(m_gui_settings->GetValue(gui::m_check_upd_start).toString()));
|
||||
connect(ui->combo_updates, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [this](int index)
|
||||
{
|
||||
m_gui_settings->SetValue(gui::m_check_upd_start, ui->combo_updates->itemData(index));
|
||||
});
|
||||
|
||||
const bool enable_ui_colors = m_gui_settings->GetValue(gui::m_enableUIColors).toBool();
|
||||
ui->cb_custom_colors->setChecked(enable_ui_colors);
|
||||
@ -1612,10 +1623,6 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> gui_settings, std
|
||||
{
|
||||
m_gui_settings->SetValue(gui::ib_pup_success, val);
|
||||
});
|
||||
connect(ui->cb_check_update_start, &QCheckBox::clicked, [this](bool val)
|
||||
{
|
||||
m_gui_settings->SetValue(gui::m_check_upd_start, val);
|
||||
});
|
||||
|
||||
connect(ui->cb_custom_colors, &QCheckBox::clicked, [this](bool val)
|
||||
{
|
||||
|
@ -3142,13 +3142,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cb_check_update_start">
|
||||
<property name="text">
|
||||
<string>Check for updates on startup</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="guiTabSpacerRight">
|
||||
<property name="orientation">
|
||||
@ -3168,6 +3161,18 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb_updates">
|
||||
<property name="title">
|
||||
<string>Check for updates on startup</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="layout_gb_updates">
|
||||
<item>
|
||||
<widget class="QComboBox" name="combo_updates"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb_discord">
|
||||
<property name="title">
|
||||
|
@ -163,7 +163,7 @@ public:
|
||||
const QString show_boot_game = tr("Shows a confirmation dialog when a game was booted while another game is running.");
|
||||
const QString show_pkg_install = tr("Shows a dialog when packages were installed successfully.");
|
||||
const QString show_pup_install = tr("Shows a dialog when firmware was installed successfully.");
|
||||
const QString check_update_start = tr("Check if an update is available on startup.");
|
||||
const QString check_update_start = tr("Checks if an update is available on startup and asks if you want to update.\nIf \"Background\" is selected, the check is done silently in the background and a new download option is shown in the top right corner of the menu if a new version was found.");
|
||||
const QString use_rich_presence = tr("Enables use of Discord Rich Presence to show what game you are playing on Discord.\nRequires a restart of RPCS3 to completely close the connection.");
|
||||
const QString discord_state = tr("Tell your friends what you are doing.");
|
||||
const QString custom_colors = tr("Prioritize custom user interface colors over properties set in stylesheet.");
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include "progress_dialog.h"
|
||||
#include "localized.h"
|
||||
#include "rpcs3_version.h"
|
||||
#include "curl_handle.h"
|
||||
#include "downloader.h"
|
||||
#include "Utilities/StrUtil.h"
|
||||
#include "Crypto/sha256.h"
|
||||
#include "Emu/System.h"
|
||||
@ -35,47 +35,11 @@
|
||||
|
||||
LOG_CHANNEL(update_log, "UPDATER");
|
||||
|
||||
size_t curl_write_cb(char* ptr, size_t /*size*/, size_t nmemb, void* userdata)
|
||||
{
|
||||
update_manager* upd_mgr = reinterpret_cast<update_manager*>(userdata);
|
||||
return upd_mgr->update_buffer(ptr, nmemb);
|
||||
}
|
||||
|
||||
update_manager::update_manager()
|
||||
{
|
||||
m_curl = new curl_handle(this);
|
||||
|
||||
// We need this signal in order to update the GUI from the main thread
|
||||
connect(this, &update_manager::signal_buffer_update, this, &update_manager::handle_buffer_update);
|
||||
}
|
||||
|
||||
void update_manager::handle_buffer_update(int size)
|
||||
{
|
||||
if (m_progress_dialog && m_update_dialog)
|
||||
{
|
||||
m_progress_dialog->setValue(size);
|
||||
QApplication::processEvents();
|
||||
}
|
||||
}
|
||||
|
||||
size_t update_manager::update_buffer(char* data, size_t size)
|
||||
{
|
||||
if (m_curl_abort)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto old_size = m_curl_buf.size();
|
||||
const auto new_size = old_size + size;
|
||||
m_curl_buf.resize(static_cast<int>(new_size));
|
||||
memcpy(m_curl_buf.data() + old_size, data, size);
|
||||
|
||||
Q_EMIT signal_buffer_update(static_cast<int>(new_size));
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void update_manager::check_for_updates(bool automatic, QWidget* parent)
|
||||
void update_manager::check_for_updates(bool automatic, bool check_only, QWidget* parent)
|
||||
{
|
||||
#ifdef __linux__
|
||||
if (automatic && !::getenv("APPIMAGE"))
|
||||
@ -85,59 +49,42 @@ void update_manager::check_for_updates(bool automatic, QWidget* parent)
|
||||
}
|
||||
#endif
|
||||
|
||||
m_parent = parent;
|
||||
m_curl_abort = false;
|
||||
m_update_dialog = false;
|
||||
m_curl_buf.clear();
|
||||
m_parent = parent;
|
||||
m_downloader = new downloader("RPCS3 Updater", parent);
|
||||
|
||||
m_progress_dialog = new progress_dialog(tr("Checking For Updates"), tr("Please wait..."), tr("Abort"), 0, 100, true, parent);
|
||||
m_progress_dialog->setAutoClose(false);
|
||||
m_progress_dialog->setAutoReset(false);
|
||||
m_progress_dialog->show();
|
||||
|
||||
connect(m_progress_dialog, &QProgressDialog::canceled, [this]() { m_curl_abort = true; });
|
||||
connect(m_progress_dialog, &QProgressDialog::finished, m_progress_dialog, &QProgressDialog::deleteLater);
|
||||
|
||||
const std::string request_url = "https://update.rpcs3.net/?api=v1&c=" + rpcs3::get_commit_and_hash().second;
|
||||
curl_easy_setopt(m_curl->get_curl(), CURLOPT_URL, request_url.c_str());
|
||||
curl_easy_setopt(m_curl->get_curl(), CURLOPT_WRITEFUNCTION, curl_write_cb);
|
||||
curl_easy_setopt(m_curl->get_curl(), CURLOPT_WRITEDATA, this);
|
||||
|
||||
auto thread = QThread::create([this]
|
||||
connect(m_downloader, &downloader::signal_download_error, this, [this, automatic](const QString& /*error*/)
|
||||
{
|
||||
const auto curl_result = curl_easy_perform(m_curl->get_curl());
|
||||
m_curl_result = curl_result == CURLE_OK;
|
||||
|
||||
if (!m_curl_result)
|
||||
if (!automatic)
|
||||
{
|
||||
update_log.error("Curl error(query): %s", curl_easy_strerror(curl_result));
|
||||
QMessageBox::warning(m_parent, tr("Auto-updater"), tr("An error occurred during the auto-updating process.\nCheck the log for more information."));
|
||||
}
|
||||
});
|
||||
connect(thread, &QThread::finished, this, [this, automatic]()
|
||||
{
|
||||
const bool result_json = m_curl_result && handle_json(automatic);
|
||||
|
||||
if (!result_json && !m_curl_abort)
|
||||
connect(m_downloader, &downloader::signal_download_finished, this, [this, automatic, check_only](const QByteArray& data)
|
||||
{
|
||||
const bool result_json = handle_json(automatic, check_only, data);
|
||||
|
||||
if (!result_json)
|
||||
{
|
||||
// The progress dialog is configured to stay open, so we need to close it manually if the download succeeds.
|
||||
m_downloader->close_progress_dialog();
|
||||
|
||||
if (!automatic)
|
||||
{
|
||||
QMessageBox::warning(m_parent, tr("Auto-updater"), tr("An error occurred during the auto-updating process.\nCheck the log for more information."));
|
||||
}
|
||||
|
||||
if (m_progress_dialog)
|
||||
{
|
||||
m_progress_dialog->close();
|
||||
m_progress_dialog = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Q_EMIT signal_update_available(result_json);
|
||||
});
|
||||
thread->setObjectName("RPCS3 Update Check");
|
||||
thread->start();
|
||||
|
||||
const std::string url = "https://update.rpcs3.net/?api=v1&c=" + rpcs3::get_commit_and_hash().second;
|
||||
m_downloader->start(url, true, !automatic, tr("Checking For Updates"), true);
|
||||
}
|
||||
|
||||
bool update_manager::handle_json(bool automatic)
|
||||
bool update_manager::handle_json(bool automatic, bool check_only, const QByteArray& data)
|
||||
{
|
||||
const QJsonObject json_data = QJsonDocument::fromJson(m_curl_buf).object();
|
||||
const QJsonObject json_data = QJsonDocument::fromJson(data).object();
|
||||
const int return_code = json_data["return_code"].toInt(-255);
|
||||
|
||||
bool hash_found = true;
|
||||
@ -202,7 +149,7 @@ bool update_manager::handle_json(bool automatic)
|
||||
if (hash_found && return_code == 0)
|
||||
{
|
||||
update_log.success("RPCS3 is up to date!");
|
||||
m_progress_dialog->close();
|
||||
m_downloader->close_progress_dialog();
|
||||
|
||||
if (!automatic)
|
||||
QMessageBox::information(m_parent, tr("Auto-updater"), tr("Your version is already up to date!"));
|
||||
@ -225,11 +172,9 @@ bool update_manager::handle_json(bool automatic)
|
||||
|
||||
Localized localized;
|
||||
|
||||
QString message;
|
||||
|
||||
if (hash_found)
|
||||
{
|
||||
message = tr("A new version of RPCS3 is available!\n\nCurrent version: %0 (%1)\nLatest version: %2 (%3)\nYour version is %4 old.\n\nDo you want to update?")
|
||||
m_update_message = tr("A new version of RPCS3 is available!\n\nCurrent version: %0 (%1)\nLatest version: %2 (%3)\nYour version is %4 old.\n\nDo you want to update?")
|
||||
.arg(current["version"].toString())
|
||||
.arg(cur_str)
|
||||
.arg(latest["version"].toString())
|
||||
@ -238,73 +183,72 @@ bool update_manager::handle_json(bool automatic)
|
||||
}
|
||||
else
|
||||
{
|
||||
message = tr("You're currently using a custom or PR build.\n\nLatest version: %0 (%1)\nThe latest version is %2 old.\n\nDo you want to update to the latest official RPCS3 version?")
|
||||
m_update_message = tr("You're currently using a custom or PR build.\n\nLatest version: %0 (%1)\nThe latest version is %2 old.\n\nDo you want to update to the latest official RPCS3 version?")
|
||||
.arg(latest["version"].toString())
|
||||
.arg(lts_str)
|
||||
.arg(localized.GetVerboseTimeByMs(std::abs(diff_msec), true));
|
||||
}
|
||||
|
||||
if (QMessageBox::question(m_progress_dialog, tr("Update Available"), message, QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
|
||||
{
|
||||
m_progress_dialog->close();
|
||||
return true;
|
||||
}
|
||||
|
||||
m_request_url = latest[os]["download"].toString().toStdString();
|
||||
m_expected_hash = latest[os]["checksum"].toString().toStdString();
|
||||
m_expected_size = latest[os]["size"].toInt();
|
||||
|
||||
m_progress_dialog->setWindowTitle(tr("Downloading Update"));
|
||||
|
||||
// Download RPCS3
|
||||
m_progress_dialog->setMaximum(m_expected_size);
|
||||
m_progress_dialog->setValue(0);
|
||||
m_update_dialog = true;
|
||||
|
||||
const std::string request_url = latest[os]["download"].toString().toStdString();
|
||||
curl_easy_setopt(m_curl->get_curl(), CURLOPT_URL, request_url.c_str());
|
||||
curl_easy_setopt(m_curl->get_curl(), CURLOPT_FOLLOWLOCATION, 1);
|
||||
|
||||
m_curl_buf.clear();
|
||||
|
||||
auto thread = QThread::create([this]
|
||||
if (check_only)
|
||||
{
|
||||
const auto curl_result = curl_easy_perform(m_curl->get_curl());
|
||||
m_curl_result = curl_result == CURLE_OK;
|
||||
m_downloader->close_progress_dialog();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!m_curl_result)
|
||||
update(automatic);
|
||||
return true;
|
||||
}
|
||||
|
||||
void update_manager::update(bool automatic)
|
||||
{
|
||||
if (QMessageBox::question(m_downloader->get_progress_dialog(), tr("Update Available"), m_update_message, QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
|
||||
{
|
||||
m_downloader->close_progress_dialog();
|
||||
return;
|
||||
}
|
||||
|
||||
m_downloader->disconnect();
|
||||
|
||||
connect(m_downloader, &downloader::signal_download_error, this, [this, automatic](const QString& /*error*/)
|
||||
{
|
||||
if (!automatic)
|
||||
{
|
||||
update_log.error("Curl error(download): %s", curl_easy_strerror(curl_result));
|
||||
QMessageBox::warning(m_parent, tr("Auto-updater"), tr("An error occurred during the auto-updating process.\nCheck the log for more information."));
|
||||
}
|
||||
});
|
||||
connect(thread, &QThread::finished, this, [this, automatic]()
|
||||
{
|
||||
const bool result_rpcs3 = m_curl_result && handle_rpcs3();
|
||||
|
||||
if (!result_rpcs3 && !m_curl_abort)
|
||||
connect(m_downloader, &downloader::signal_download_finished, this, [this, automatic](const QByteArray& data)
|
||||
{
|
||||
const bool result_json = handle_rpcs3(data);
|
||||
|
||||
if (!result_json)
|
||||
{
|
||||
// The progress dialog is configured to stay open, so we need to close it manually if the download succeeds.
|
||||
m_downloader->close_progress_dialog();
|
||||
|
||||
if (!automatic)
|
||||
{
|
||||
QMessageBox::warning(m_parent, tr("Auto-updater"), tr("An error occurred during the auto-updating process.\nCheck the log for more information."));
|
||||
}
|
||||
|
||||
if (m_progress_dialog)
|
||||
{
|
||||
m_progress_dialog->close();
|
||||
m_progress_dialog = nullptr;
|
||||
}
|
||||
}
|
||||
});
|
||||
thread->setObjectName("RPCS3 Updater");
|
||||
thread->start();
|
||||
|
||||
return true;
|
||||
Q_EMIT signal_update_available(false);
|
||||
});
|
||||
|
||||
m_downloader->start(m_request_url, true, true, tr("Downloading Update"), true, m_expected_size);
|
||||
}
|
||||
|
||||
bool update_manager::handle_rpcs3()
|
||||
bool update_manager::handle_rpcs3(const QByteArray& data)
|
||||
{
|
||||
if (m_expected_size != m_curl_buf.size() + 0u)
|
||||
m_downloader->update_progress_dialog(tr("Updating RPCS3"));
|
||||
|
||||
if (m_expected_size != data.size() + 0u)
|
||||
{
|
||||
update_log.error("Download size mismatch: %d expected: %d", m_curl_buf.size(), m_expected_size);
|
||||
update_log.error("Download size mismatch: %d expected: %d", data.size(), m_expected_size);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -312,7 +256,7 @@ bool update_manager::handle_rpcs3()
|
||||
mbedtls_sha256_context ctx;
|
||||
mbedtls_sha256_init(&ctx);
|
||||
mbedtls_sha256_starts_ret(&ctx, 0);
|
||||
mbedtls_sha256_update_ret(&ctx, reinterpret_cast<const unsigned char*>(m_curl_buf.data()), m_curl_buf.size());
|
||||
mbedtls_sha256_update_ret(&ctx, reinterpret_cast<const unsigned char*>(data.data()), data.size());
|
||||
mbedtls_sha256_finish_ret(&ctx, res_hash);
|
||||
|
||||
std::string res_hash_string("0000000000000000000000000000000000000000000000000000000000000000");
|
||||
@ -329,9 +273,8 @@ bool update_manager::handle_rpcs3()
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string replace_path;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
// Get executable path
|
||||
const std::string orig_path = Emulator::GetExeDir() + "rpcs3.exe";
|
||||
|
||||
@ -340,61 +283,6 @@ bool update_manager::handle_rpcs3()
|
||||
wchar_orig_path.resize(tmp_size);
|
||||
MultiByteToWideChar(CP_UTF8, 0, orig_path.c_str(), -1, wchar_orig_path.data(), tmp_size);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
const char* appimage_path = ::getenv("APPIMAGE");
|
||||
if (appimage_path != nullptr)
|
||||
{
|
||||
replace_path = appimage_path;
|
||||
update_log.notice("Found AppImage path: %s", appimage_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
update_log.warning("Failed to find AppImage path");
|
||||
char exe_path[PATH_MAX];
|
||||
ssize_t len = ::readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1);
|
||||
|
||||
if (len == -1)
|
||||
{
|
||||
update_log.error("Failed to find executable path");
|
||||
return false;
|
||||
}
|
||||
|
||||
exe_path[len] = '\0';
|
||||
update_log.trace("Found exec path: %s", exe_path);
|
||||
|
||||
replace_path = exe_path;
|
||||
}
|
||||
|
||||
m_progress_dialog->setWindowTitle(tr("Updating RPCS3"));
|
||||
|
||||
// Move the appimage/exe and replace with new appimage
|
||||
const std::string move_dest = replace_path + "_old";
|
||||
fs::rename(replace_path, move_dest, true);
|
||||
fs::file new_appimage(replace_path, fs::read + fs::write + fs::create + fs::trunc);
|
||||
if (!new_appimage)
|
||||
{
|
||||
update_log.error("Failed to create new AppImage file: %s", replace_path);
|
||||
return false;
|
||||
}
|
||||
if (new_appimage.write(m_curl_buf.data(), m_curl_buf.size()) != m_curl_buf.size() + 0u)
|
||||
{
|
||||
update_log.error("Failed to write new AppImage file: %s", replace_path);
|
||||
return false;
|
||||
}
|
||||
if (fchmod(new_appimage.get_handle(), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1)
|
||||
{
|
||||
update_log.error("Failed to chmod rwxrxrx %s", replace_path);
|
||||
return false;
|
||||
}
|
||||
new_appimage.close();
|
||||
|
||||
update_log.success("Successfully updated %s!", replace_path);
|
||||
|
||||
#elif defined(_WIN32)
|
||||
|
||||
char temp_path[PATH_MAX];
|
||||
|
||||
GetTempPathA(sizeof(temp_path) - 1, temp_path);
|
||||
@ -409,15 +297,13 @@ bool update_manager::handle_rpcs3()
|
||||
update_log.error("Failed to create temporary file: %s", tmpfile_path);
|
||||
return false;
|
||||
}
|
||||
if (tmpfile.write(m_curl_buf.data(), m_curl_buf.size()) != m_curl_buf.size())
|
||||
if (tmpfile.write(data.data(), data.size()) != data.size())
|
||||
{
|
||||
update_log.error("Failed to write temporary file: %s", tmpfile_path);
|
||||
return false;
|
||||
}
|
||||
tmpfile.close();
|
||||
|
||||
m_progress_dialog->setWindowTitle(tr("Updating RPCS3"));
|
||||
|
||||
// 7z stuff (most of this stuff is from 7z Util sample and has been reworked to be more stl friendly)
|
||||
|
||||
ISzAlloc allocImp;
|
||||
@ -596,8 +482,62 @@ bool update_manager::handle_rpcs3()
|
||||
error_free7z();
|
||||
if (res)
|
||||
return false;
|
||||
|
||||
#else
|
||||
|
||||
std::string replace_path;
|
||||
|
||||
const char* appimage_path = ::getenv("APPIMAGE");
|
||||
if (appimage_path != nullptr)
|
||||
{
|
||||
replace_path = appimage_path;
|
||||
update_log.notice("Found AppImage path: %s", appimage_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
update_log.warning("Failed to find AppImage path");
|
||||
char exe_path[PATH_MAX];
|
||||
ssize_t len = ::readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1);
|
||||
|
||||
if (len == -1)
|
||||
{
|
||||
update_log.error("Failed to find executable path");
|
||||
return false;
|
||||
}
|
||||
|
||||
exe_path[len] = '\0';
|
||||
update_log.trace("Found exec path: %s", exe_path);
|
||||
|
||||
replace_path = exe_path;
|
||||
}
|
||||
|
||||
// Move the appimage/exe and replace with new appimage
|
||||
const std::string move_dest = replace_path + "_old";
|
||||
fs::rename(replace_path, move_dest, true);
|
||||
fs::file new_appimage(replace_path, fs::read + fs::write + fs::create + fs::trunc);
|
||||
if (!new_appimage)
|
||||
{
|
||||
update_log.error("Failed to create new AppImage file: %s", replace_path);
|
||||
return false;
|
||||
}
|
||||
if (new_appimage.write(data.data(), data.size()) != data.size() + 0u)
|
||||
{
|
||||
update_log.error("Failed to write new AppImage file: %s", replace_path);
|
||||
return false;
|
||||
}
|
||||
if (fchmod(new_appimage.get_handle(), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1)
|
||||
{
|
||||
update_log.error("Failed to chmod rwxrxrx %s", replace_path);
|
||||
return false;
|
||||
}
|
||||
new_appimage.close();
|
||||
|
||||
update_log.success("Successfully updated %s!", replace_path);
|
||||
|
||||
#endif
|
||||
|
||||
m_downloader->close_progress_dialog();
|
||||
|
||||
QMessageBox::information(m_parent, tr("Auto-updater"), tr("Update successful!\nRPCS3 will now restart."));
|
||||
|
||||
#ifdef _WIN32
|
||||
|
@ -4,37 +4,30 @@
|
||||
#include <QObject>
|
||||
#include <QByteArray>
|
||||
|
||||
class curl_handle;
|
||||
class progress_dialog;
|
||||
class downloader;
|
||||
|
||||
class update_manager final : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
std::atomic<bool> m_update_dialog = false;
|
||||
progress_dialog* m_progress_dialog = nullptr;
|
||||
QWidget* m_parent = nullptr;
|
||||
downloader* m_downloader = nullptr;
|
||||
QWidget* m_parent = nullptr;
|
||||
|
||||
curl_handle* m_curl = nullptr;
|
||||
QByteArray m_curl_buf;
|
||||
std::atomic<bool> m_curl_abort = false;
|
||||
std::atomic<bool> m_curl_result = false;
|
||||
QString m_update_message;
|
||||
|
||||
std::string m_request_url;
|
||||
std::string m_expected_hash;
|
||||
u64 m_expected_size = 0;
|
||||
|
||||
bool handle_json(bool automatic);
|
||||
bool handle_rpcs3();
|
||||
bool handle_json(bool automatic, bool check_only, const QByteArray& data);
|
||||
bool handle_rpcs3(const QByteArray& data);
|
||||
|
||||
public:
|
||||
update_manager();
|
||||
void check_for_updates(bool automatic, QWidget* parent = nullptr);
|
||||
size_t update_buffer(char* data, size_t size);
|
||||
void check_for_updates(bool automatic, bool check_only, QWidget* parent = nullptr);
|
||||
void update(bool automatic);
|
||||
|
||||
Q_SIGNALS:
|
||||
void signal_buffer_update(int size);
|
||||
|
||||
private Q_SLOTS:
|
||||
void handle_buffer_update(int size);
|
||||
void signal_update_available(bool update_available);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user