mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-22 02:32:36 +01:00
rpcs3qt: Add macOS support to the updater.
This commit is contained in:
parent
5fe36872c5
commit
3ef48cbdd5
2
3rdparty/7z/CMakeLists.txt
vendored
2
3rdparty/7z/CMakeLists.txt
vendored
@ -1,5 +1,5 @@
|
|||||||
# 7z sdk
|
# 7z sdk
|
||||||
if(WIN32)
|
if(WIN32 OR APPLE)
|
||||||
add_library(3rdparty_7z STATIC EXCLUDE_FROM_ALL
|
add_library(3rdparty_7z STATIC EXCLUDE_FROM_ALL
|
||||||
src/7zAlloc.c
|
src/7zAlloc.c
|
||||||
src/7zArcIn.c
|
src/7zArcIn.c
|
||||||
|
@ -357,6 +357,12 @@ namespace fs
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool device_base::create_symlink(const std::string&)
|
||||||
|
{
|
||||||
|
g_tls_error = error::readonly;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool device_base::rename(const std::string&, const std::string&)
|
bool device_base::rename(const std::string&, const std::string&)
|
||||||
{
|
{
|
||||||
g_tls_error = error::readonly;
|
g_tls_error = error::readonly;
|
||||||
@ -581,6 +587,7 @@ bool fs::get_stat(const std::string& path, stat_t& info)
|
|||||||
}
|
}
|
||||||
|
|
||||||
info.is_directory = (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
info.is_directory = (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||||
|
info.is_symlink = (attrs.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
|
||||||
info.is_writable = (attrs.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0;
|
info.is_writable = (attrs.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0;
|
||||||
info.size = attrs.nFileSizeLow | (u64{attrs.nFileSizeHigh} << 32);
|
info.size = attrs.nFileSizeLow | (u64{attrs.nFileSizeHigh} << 32);
|
||||||
info.atime = to_time(attrs.ftLastAccessTime);
|
info.atime = to_time(attrs.ftLastAccessTime);
|
||||||
@ -595,6 +602,7 @@ bool fs::get_stat(const std::string& path, stat_t& info)
|
|||||||
}
|
}
|
||||||
|
|
||||||
info.is_directory = S_ISDIR(file_info.st_mode);
|
info.is_directory = S_ISDIR(file_info.st_mode);
|
||||||
|
info.is_symlink = S_ISLNK(file_info.st_mode);
|
||||||
info.is_writable = file_info.st_mode & 0200; // HACK: approximation
|
info.is_writable = file_info.st_mode & 0200; // HACK: approximation
|
||||||
info.size = file_info.st_size;
|
info.size = file_info.st_size;
|
||||||
info.atime = file_info.st_atime;
|
info.atime = file_info.st_atime;
|
||||||
@ -648,6 +656,23 @@ bool fs::is_dir(const std::string& path)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool fs::is_symlink(const std::string& path)
|
||||||
|
{
|
||||||
|
fs::stat_t info{};
|
||||||
|
if (!fs::get_stat(path, info))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info.is_symlink)
|
||||||
|
{
|
||||||
|
g_tls_error = error::exist;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool fs::statfs(const std::string& path, fs::device_stat& info)
|
bool fs::statfs(const std::string& path, fs::device_stat& info)
|
||||||
{
|
{
|
||||||
if (auto device = get_virtual_device(path))
|
if (auto device = get_virtual_device(path))
|
||||||
@ -794,6 +819,33 @@ bool fs::remove_dir(const std::string& path)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool fs::create_symlink(const std::string& path, const std::string& target)
|
||||||
|
{
|
||||||
|
if (auto device = get_virtual_device(path))
|
||||||
|
{
|
||||||
|
return device->create_symlink(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
const DWORD flags = is_dir(target) ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;
|
||||||
|
if (!CreateSymbolicLinkW(to_wchar(path).get(), to_wchar(target).get(), flags))
|
||||||
|
{
|
||||||
|
g_tls_error = to_error(GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
if (::symlink(target.c_str(), path.c_str()) != 0)
|
||||||
|
{
|
||||||
|
g_tls_error = to_error(errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
bool fs::rename(const std::string& from, const std::string& to, bool overwrite)
|
bool fs::rename(const std::string& from, const std::string& to, bool overwrite)
|
||||||
{
|
{
|
||||||
if (from.empty() || to.empty())
|
if (from.empty() || to.empty())
|
||||||
@ -2105,8 +2157,21 @@ const std::string& fs::get_temp_dir()
|
|||||||
|
|
||||||
dir = wchar_to_utf8(buf);
|
dir = wchar_to_utf8(buf);
|
||||||
#else
|
#else
|
||||||
// TODO
|
const char* tmp_dir = getenv("TMPDIR");
|
||||||
dir = get_cache_dir();
|
if (tmp_dir == nullptr || tmp_dir[0] == '\0')
|
||||||
|
{
|
||||||
|
// Fall back to cache directory
|
||||||
|
dir = get_cache_dir();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dir = tmp_dir;
|
||||||
|
if (!dir.ends_with("/"))
|
||||||
|
{
|
||||||
|
// Ensure path ends with a separator
|
||||||
|
dir += "/";
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return dir;
|
return dir;
|
||||||
|
@ -66,6 +66,7 @@ namespace fs
|
|||||||
struct stat_t
|
struct stat_t
|
||||||
{
|
{
|
||||||
bool is_directory;
|
bool is_directory;
|
||||||
|
bool is_symlink;
|
||||||
bool is_writable;
|
bool is_writable;
|
||||||
u64 size;
|
u64 size;
|
||||||
s64 atime;
|
s64 atime;
|
||||||
@ -155,6 +156,7 @@ namespace fs
|
|||||||
virtual bool statfs(const std::string& path, device_stat& info) = 0;
|
virtual bool statfs(const std::string& path, device_stat& info) = 0;
|
||||||
virtual bool remove_dir(const std::string& path);
|
virtual bool remove_dir(const std::string& path);
|
||||||
virtual bool create_dir(const std::string& path);
|
virtual bool create_dir(const std::string& path);
|
||||||
|
virtual bool create_symlink(const std::string& path);
|
||||||
virtual bool rename(const std::string& from, const std::string& to);
|
virtual bool rename(const std::string& from, const std::string& to);
|
||||||
virtual bool remove(const std::string& path);
|
virtual bool remove(const std::string& path);
|
||||||
virtual bool trunc(const std::string& path, u64 length);
|
virtual bool trunc(const std::string& path, u64 length);
|
||||||
@ -197,6 +199,9 @@ namespace fs
|
|||||||
// Check whether the directory exists and is NOT a file
|
// Check whether the directory exists and is NOT a file
|
||||||
bool is_dir(const std::string& path);
|
bool is_dir(const std::string& path);
|
||||||
|
|
||||||
|
// Check whether the path points to an existing symlink
|
||||||
|
bool is_symlink(const std::string& path);
|
||||||
|
|
||||||
// Get filesystem information
|
// Get filesystem information
|
||||||
bool statfs(const std::string& path, device_stat& info);
|
bool statfs(const std::string& path, device_stat& info);
|
||||||
|
|
||||||
@ -209,6 +214,9 @@ namespace fs
|
|||||||
// Create directories
|
// Create directories
|
||||||
bool create_path(const std::string& path);
|
bool create_path(const std::string& path);
|
||||||
|
|
||||||
|
// Create symbolic link
|
||||||
|
bool create_symlink(const std::string& path, const std::string& target);
|
||||||
|
|
||||||
// Rename (move) file or directory
|
// Rename (move) file or directory
|
||||||
bool rename(const std::string& from, const std::string& to, bool overwrite);
|
bool rename(const std::string& from, const std::string& to, bool overwrite);
|
||||||
|
|
||||||
|
@ -45,7 +45,8 @@ if(WIN32)
|
|||||||
target_compile_definitions(rpcs3 PRIVATE UNICODE _UNICODE)
|
target_compile_definitions(rpcs3 PRIVATE UNICODE _UNICODE)
|
||||||
elseif(APPLE)
|
elseif(APPLE)
|
||||||
add_executable(rpcs3 MACOSX_BUNDLE)
|
add_executable(rpcs3 MACOSX_BUNDLE)
|
||||||
target_sources(rpcs3 PRIVATE rpcs3.icns)
|
target_sources(rpcs3 PRIVATE rpcs3.icns update_helper.sh)
|
||||||
|
set_source_files_properties(update_helper.sh PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||||
set_target_properties(rpcs3
|
set_target_properties(rpcs3
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/rpcs3.plist.in")
|
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/rpcs3.plist.in")
|
||||||
|
@ -241,7 +241,7 @@ bool main_window::Init([[maybe_unused]] bool with_cli_boot)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(__linux__)
|
#if defined(_WIN32) || defined(__linux__) || defined(__APPLE__)
|
||||||
if (const auto update_value = m_gui_settings->GetValue(gui::m_check_upd_start).toString(); update_value != gui::update_off)
|
if (const auto update_value = m_gui_settings->GetValue(gui::m_check_upd_start).toString(); update_value != gui::update_off)
|
||||||
{
|
{
|
||||||
const bool in_background = with_cli_boot || update_value == gui::update_bkg;
|
const bool in_background = with_cli_boot || update_value == gui::update_bkg;
|
||||||
@ -3028,7 +3028,7 @@ void main_window::CreateConnects()
|
|||||||
|
|
||||||
connect(ui->updateAct, &QAction::triggered, this, [this]()
|
connect(ui->updateAct, &QAction::triggered, this, [this]()
|
||||||
{
|
{
|
||||||
#if !defined(_WIN32) && !defined(__linux__)
|
#if !defined(_WIN32) && !defined(__linux__) && !defined(__APPLE__)
|
||||||
QMessageBox::warning(this, tr("Auto-updater"), tr("The auto-updater isn't available for your OS currently."));
|
QMessageBox::warning(this, tr("Auto-updater"), tr("The auto-updater isn't available for your OS currently."));
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
|
@ -20,16 +20,19 @@
|
|||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
|
#if defined(_WIN32) || defined(__APPLE__)
|
||||||
|
#include <7z.h>
|
||||||
|
#include <7zAlloc.h>
|
||||||
|
#include <7zCrc.h>
|
||||||
|
#include <7zFile.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
#ifndef NOMINMAX
|
#ifndef NOMINMAX
|
||||||
#define NOMINMAX
|
#define NOMINMAX
|
||||||
#endif
|
#endif
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <CpuArch.h>
|
#include <CpuArch.h>
|
||||||
#include <7z.h>
|
|
||||||
#include <7zAlloc.h>
|
|
||||||
#include <7zCrc.h>
|
|
||||||
#include <7zFile.h>
|
|
||||||
|
|
||||||
#ifndef PATH_MAX
|
#ifndef PATH_MAX
|
||||||
#define PATH_MAX MAX_PATH
|
#define PATH_MAX MAX_PATH
|
||||||
@ -143,6 +146,8 @@ bool update_manager::handle_json(bool automatic, bool check_only, bool auto_acce
|
|||||||
os = "windows";
|
os = "windows";
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
os = "linux";
|
os = "linux";
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
os = "mac";
|
||||||
#else
|
#else
|
||||||
update_log.error("Your OS isn't currently supported by the auto-updater");
|
update_log.error("Your OS isn't currently supported by the auto-updater");
|
||||||
return false;
|
return false;
|
||||||
@ -370,13 +375,6 @@ bool update_manager::handle_rpcs3(const QByteArray& data, bool auto_accept)
|
|||||||
{
|
{
|
||||||
m_downloader->update_progress_dialog(tr("Updating RPCS3"));
|
m_downloader->update_progress_dialog(tr("Updating RPCS3"));
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
Q_UNUSED(data);
|
|
||||||
Q_UNUSED(auto_accept);
|
|
||||||
update_log.error("Unsupported operating system.");
|
|
||||||
return false;
|
|
||||||
#else
|
|
||||||
|
|
||||||
if (m_expected_size != static_cast<u64>(data.size()))
|
if (m_expected_size != static_cast<u64>(data.size()))
|
||||||
{
|
{
|
||||||
update_log.error("Download size mismatch: %d expected: %d", data.size(), m_expected_size);
|
update_log.error("Download size mismatch: %d expected: %d", data.size(), m_expected_size);
|
||||||
@ -390,13 +388,17 @@ bool update_manager::handle_rpcs3(const QByteArray& data, bool auto_accept)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#if defined(_WIN32) || defined(__APPLE__)
|
||||||
|
|
||||||
// Get executable path
|
// Get executable path
|
||||||
const std::string exe_dir = fs::get_executable_dir();
|
const std::string exe_dir = fs::get_executable_dir();
|
||||||
const std::string orig_path = exe_dir + "rpcs3.exe";
|
const std::string orig_path = fs::get_executable_path();
|
||||||
|
#ifdef _WIN32
|
||||||
const std::wstring wchar_orig_path = utf8_to_wchar(orig_path);
|
const std::wstring wchar_orig_path = utf8_to_wchar(orig_path);
|
||||||
const std::string tmpfile_path = fs::get_temp_dir() + "\\rpcs3_update.7z";
|
const std::string tmpfile_path = fs::get_temp_dir() + "\\rpcs3_update.7z";
|
||||||
|
#else
|
||||||
|
const std::string tmpfile_path = fs::get_temp_dir() + "rpcs3_update.7z";
|
||||||
|
#endif
|
||||||
|
|
||||||
fs::file tmpfile(tmpfile_path, fs::read + fs::write + fs::create + fs::trunc);
|
fs::file tmpfile(tmpfile_path, fs::read + fs::write + fs::create + fs::trunc);
|
||||||
if (!tmpfile)
|
if (!tmpfile)
|
||||||
@ -482,16 +484,31 @@ bool update_manager::handle_rpcs3(const QByteArray& data, bool auto_accept)
|
|||||||
Byte* outBuffer = nullptr;
|
Byte* outBuffer = nullptr;
|
||||||
usz outBufferSize = 0;
|
usz outBufferSize = 0;
|
||||||
|
|
||||||
// Creates temp folder for moving active files
|
#ifdef _WIN32
|
||||||
|
// Create temp folder for moving active files
|
||||||
const std::string tmp_folder = exe_dir + "rpcs3_old/";
|
const std::string tmp_folder = exe_dir + "rpcs3_old/";
|
||||||
|
#else
|
||||||
|
// Create temp folder for extracting the new app
|
||||||
|
const std::string tmp_folder = fs::get_temp_dir() + "rpcs3_new/";
|
||||||
|
#endif
|
||||||
fs::create_dir(tmp_folder);
|
fs::create_dir(tmp_folder);
|
||||||
|
|
||||||
for (UInt32 i = 0; i < db.NumFiles; i++)
|
for (UInt32 i = 0; i < db.NumFiles; i++)
|
||||||
{
|
{
|
||||||
usz offset = 0;
|
usz offset = 0;
|
||||||
usz outSizeProcessed = 0;
|
usz outSizeProcessed = 0;
|
||||||
const bool isDir = SzArEx_IsDir(&db, i);
|
const bool isDir = SzArEx_IsDir(&db, i);
|
||||||
const usz len = SzArEx_GetFileNameUtf16(&db, i, nullptr);
|
const DWORD attribs = SzBitWithVals_Check(&db.Attribs, i) ? db.Attribs.Vals[i] : 0;
|
||||||
|
#ifdef _WIN32
|
||||||
|
// This is commented out for now as we shouldn't need it and symlinks
|
||||||
|
// aren't well supported on Windows. Left in case it is needed in the future.
|
||||||
|
// const bool is_symlink = (attribs & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
|
||||||
|
const bool is_symlink = false;
|
||||||
|
#else
|
||||||
|
const DWORD permissions = (attribs >> 16) & (S_IRWXU | S_IRWXG | S_IRWXO);
|
||||||
|
const bool is_symlink = (attribs & FILE_ATTRIBUTE_UNIX_EXTENSION) != 0 && S_ISLNK(attribs >> 16);
|
||||||
|
#endif
|
||||||
|
const usz len = SzArEx_GetFileNameUtf16(&db, i, nullptr);
|
||||||
|
|
||||||
if (len >= PATH_MAX)
|
if (len >= PATH_MAX)
|
||||||
{
|
{
|
||||||
@ -515,7 +532,12 @@ bool update_manager::handle_rpcs3(const QByteArray& data, bool auto_accept)
|
|||||||
temp_u8[index] = static_cast<u8>(temp_u16[index]);
|
temp_u8[index] = static_cast<u8>(temp_u16[index]);
|
||||||
}
|
}
|
||||||
temp_u8[len] = 0;
|
temp_u8[len] = 0;
|
||||||
const std::string name = exe_dir + std::string(reinterpret_cast<char*>(temp_u8));
|
const std::string archived_name = std::string(reinterpret_cast<char*>(temp_u8));
|
||||||
|
#ifdef __APPLE__
|
||||||
|
const std::string name = tmp_folder + archived_name;
|
||||||
|
#else
|
||||||
|
const std::string name = exe_dir + archived_name;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!isDir)
|
if (!isDir)
|
||||||
{
|
{
|
||||||
@ -537,6 +559,14 @@ bool update_manager::handle_rpcs3(const QByteArray& data, bool auto_accept)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_symlink)
|
||||||
|
{
|
||||||
|
const std::string link_target(reinterpret_cast<const char*>(outBuffer + offset), outSizeProcessed);
|
||||||
|
update_log.trace("Creating symbolic link: %s -> %s", name, link_target);
|
||||||
|
fs::create_symlink(name, link_target);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
fs::file outfile(name, fs::read + fs::write + fs::create + fs::trunc);
|
fs::file outfile(name, fs::read + fs::write + fs::create + fs::trunc);
|
||||||
if (!outfile)
|
if (!outfile)
|
||||||
{
|
{
|
||||||
@ -575,6 +605,11 @@ bool update_manager::handle_rpcs3(const QByteArray& data, bool auto_accept)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
outfile.close();
|
outfile.close();
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
// Apply correct file permissions.
|
||||||
|
chmod(name.c_str(), permissions);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
error_free7z();
|
error_free7z();
|
||||||
@ -644,6 +679,12 @@ bool update_manager::handle_rpcs3(const QByteArray& data, bool auto_accept)
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
const int ret = _wexecl(wchar_orig_path.data(), wchar_orig_path.data(), L"--updating", nullptr);
|
const int ret = _wexecl(wchar_orig_path.data(), wchar_orig_path.data(), L"--updating", nullptr);
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
// Execute helper script to replace the app and relaunch
|
||||||
|
const std::string helper_script = fmt::format("%s/Contents/Resources/update_helper.sh", orig_path);
|
||||||
|
const std::string extracted_app = fmt::format("%s/RPCS3.app", tmp_folder);
|
||||||
|
update_log.notice("Executing update helper script: '%s %s %s'", helper_script, extracted_app, orig_path);
|
||||||
|
const int ret = execl(helper_script.c_str(), helper_script.c_str(), extracted_app.c_str(), orig_path.c_str(), nullptr);
|
||||||
#else
|
#else
|
||||||
// execv is used for compatibility with checkrt
|
// execv is used for compatibility with checkrt
|
||||||
const char * const params[3] = { replace_path.c_str(), "--updating", nullptr };
|
const char * const params[3] = { replace_path.c_str(), "--updating", nullptr };
|
||||||
@ -656,5 +697,4 @@ bool update_manager::handle_rpcs3(const QByteArray& data, bool auto_accept)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
#endif //def __APPLE__
|
|
||||||
}
|
}
|
||||||
|
14
rpcs3/update_helper.sh
Executable file
14
rpcs3/update_helper.sh
Executable file
@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# This script copies the new app over the old app and launches it.
|
||||||
|
# This is required since invalidating the code signing of an app by
|
||||||
|
# replacing it while it is running can result in the app being killed.
|
||||||
|
|
||||||
|
if [ "$#" -ne 2 ]; then
|
||||||
|
echo "Usage: update_helper.sh <new_app> <old_app>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
new_app="$1/"
|
||||||
|
old_app="$2/"
|
||||||
|
|
||||||
|
cp -Rf -p "$new_app" "$old_app"
|
||||||
|
open -n -a "$2" --args --updating
|
Loading…
Reference in New Issue
Block a user