mirror of https://github.com/987123879113/pcsx2
parent
a3f6efecb8
commit
a289723f66
14 changed files with 1358 additions and 0 deletions
@ -0,0 +1,12 @@ |
||||
add_executable(updater |
||||
Updater.cpp |
||||
Updater.h |
||||
) |
||||
|
||||
target_link_libraries(updater PRIVATE common fmt::fmt lzma) |
||||
|
||||
if(WIN32) |
||||
target_sources(updater PRIVATE |
||||
Win32Update.cpp |
||||
) |
||||
endif() |
@ -0,0 +1,43 @@ |
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team |
||||
* |
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms |
||||
* of the GNU Lesser General Public License as published by the Free Software Found- |
||||
* ation, either version 3 of the License, or (at your option) any later version. |
||||
* |
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; |
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
||||
* PURPOSE. See the GNU General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License along with PCSX2. |
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include "7zTypes.h" |
||||
|
||||
static inline const char* SZErrorToString(SRes res) |
||||
{ |
||||
// clang-format off
|
||||
switch (res) |
||||
{ |
||||
case SZ_OK: return "SZ_OK"; |
||||
case SZ_ERROR_DATA: return "SZ_ERROR_DATA"; |
||||
case SZ_ERROR_MEM: return "SZ_ERROR_MEM"; |
||||
case SZ_ERROR_CRC: return "SZ_ERROR_CRC"; |
||||
case SZ_ERROR_UNSUPPORTED: return "SZ_ERROR_UNSUPPORTED"; |
||||
case SZ_ERROR_PARAM: return "SZ_ERROR_PARAM"; |
||||
case SZ_ERROR_INPUT_EOF: return "SZ_ERROR_INPUT_EOF"; |
||||
case SZ_ERROR_OUTPUT_EOF: return "SZ_ERROR_OUTPUT_EOF"; |
||||
case SZ_ERROR_READ: return "SZ_ERROR_READ"; |
||||
case SZ_ERROR_WRITE: return "SZ_ERROR_WRITE"; |
||||
case SZ_ERROR_PROGRESS: return "SZ_ERROR_PROGRESS"; |
||||
case SZ_ERROR_FAIL: return "SZ_ERROR_FAIL"; |
||||
case SZ_ERROR_THREAD: return "SZ_ERROR_THREAD"; |
||||
case SZ_ERROR_ARCHIVE: return "SZ_ERROR_ARCHIVE"; |
||||
case SZ_ERROR_NO_ARCHIVE: return "SZ_ERROR_NO_ARCHIVE"; |
||||
default: return "SZ_UNKNOWN"; |
||||
} |
||||
// clang-format on
|
||||
} |
@ -0,0 +1,392 @@ |
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team |
||||
* |
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms |
||||
* of the GNU Lesser General Public License as published by the Free Software Found- |
||||
* ation, either version 3 of the License, or (at your option) any later version. |
||||
* |
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; |
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
||||
* PURPOSE. See the GNU General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License along with PCSX2. |
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
#include "Updater.h" |
||||
#include "SZErrors.h" |
||||
|
||||
#include "common/Console.h" |
||||
#include "common/FileSystem.h" |
||||
#include "common/Path.h" |
||||
#include "common/ScopedGuard.h" |
||||
#include "common/StringUtil.h" |
||||
|
||||
#include "7zAlloc.h" |
||||
#include "7zCrc.h" |
||||
|
||||
#include <algorithm> |
||||
#include <cstdio> |
||||
#include <cstring> |
||||
#include <memory> |
||||
#include <set> |
||||
#include <string> |
||||
#include <vector> |
||||
|
||||
#ifdef _WIN32 |
||||
#include <shellapi.h> |
||||
#endif |
||||
|
||||
static constexpr size_t kInputBufSize = ((size_t)1 << 18); |
||||
static constexpr ISzAlloc g_Alloc = {SzAlloc, SzFree}; |
||||
|
||||
static std::FILE* s_file_console_stream; |
||||
static constexpr IConsoleWriter s_file_console_writer = { |
||||
[](const wxString& fmt) { // WriteRaw
|
||||
auto buf = fmt.ToUTF8(); |
||||
std::fwrite(buf.data(), buf.length(), 1, s_file_console_stream); |
||||
std::fflush(s_file_console_stream); |
||||
}, |
||||
[](const wxString& fmt) { // DoWriteLn
|
||||
auto buf = fmt.ToUTF8(); |
||||
std::fwrite(buf.data(), buf.length(), 1, s_file_console_stream); |
||||
std::fputc('\n', s_file_console_stream); |
||||
std::fflush(s_file_console_stream); |
||||
}, |
||||
[](ConsoleColors) { // DoSetColor
|
||||
}, |
||||
[](const wxString& fmt) { // DoWriteFromStdout
|
||||
auto buf = fmt.ToUTF8(); |
||||
std::fwrite(buf.data(), buf.length(), 1, s_file_console_stream); |
||||
std::fflush(s_file_console_stream); |
||||
}, |
||||
[]() { // Newline
|
||||
std::fputc('\n', s_file_console_stream); |
||||
std::fflush(s_file_console_stream); |
||||
}, |
||||
[](const wxString&) { // SetTitle
|
||||
}}; |
||||
|
||||
static void CloseConsoleFile() |
||||
{ |
||||
if (s_file_console_stream) |
||||
std::fclose(s_file_console_stream); |
||||
} |
||||
|
||||
Updater::Updater(ProgressCallback* progress) |
||||
: m_progress(progress) |
||||
{ |
||||
progress->SetTitle("PCSX2 Update Installer"); |
||||
} |
||||
|
||||
Updater::~Updater() |
||||
{ |
||||
if (m_archive_opened) |
||||
SzArEx_Free(&m_archive, &g_Alloc); |
||||
|
||||
ISzAlloc_Free(&g_Alloc, m_look_stream.buf); |
||||
|
||||
if (m_file_opened) |
||||
File_Close(&m_archive_stream.file); |
||||
} |
||||
|
||||
void Updater::SetupLogging(ProgressCallback* progress, const std::string& destination_directory) |
||||
{ |
||||
const std::string log_path(Path::CombineStdString(destination_directory, "updater.log")); |
||||
s_file_console_stream = FileSystem::OpenCFile(log_path.c_str(), "w"); |
||||
if (!s_file_console_stream) |
||||
{ |
||||
progress->DisplayFormattedModalError("Failed to open log file '%s'", log_path.c_str()); |
||||
return; |
||||
} |
||||
|
||||
Console_SetActiveHandler(s_file_console_writer); |
||||
std::atexit(CloseConsoleFile); |
||||
} |
||||
|
||||
bool Updater::Initialize(std::string destination_directory) |
||||
{ |
||||
m_destination_directory = std::move(destination_directory); |
||||
m_staging_directory = StringUtil::StdStringFromFormat("%s" FS_OSPATH_SEPARATOR_STR "%s", |
||||
m_destination_directory.c_str(), "UPDATE_STAGING"); |
||||
m_progress->DisplayFormattedInformation("Destination directory: '%s'", m_destination_directory.c_str()); |
||||
m_progress->DisplayFormattedInformation("Staging directory: '%s'", m_staging_directory.c_str()); |
||||
return true; |
||||
} |
||||
|
||||
bool Updater::OpenUpdateZip(const char* path) |
||||
{ |
||||
FileInStream_CreateVTable(&m_archive_stream); |
||||
LookToRead2_CreateVTable(&m_look_stream, False); |
||||
CrcGenerateTable(); |
||||
|
||||
m_look_stream.buf = (Byte*)ISzAlloc_Alloc(&g_Alloc, kInputBufSize); |
||||
if (!m_look_stream.buf) |
||||
{ |
||||
m_progress->DisplayFormattedError("Failed to allocate input buffer?!"); |
||||
return false; |
||||
} |
||||
|
||||
m_look_stream.bufSize = kInputBufSize; |
||||
m_look_stream.realStream = &m_archive_stream.vt; |
||||
LookToRead2_Init(&m_look_stream); |
||||
|
||||
#ifdef _WIN32 |
||||
WRes wres = InFile_OpenW(&m_archive_stream.file, StringUtil::UTF8StringToWideString(path).c_str()); |
||||
#else |
||||
WRes wres = InFile_Open(&m_archive_stream.file, path); |
||||
#endif |
||||
if (wres != 0) |
||||
{ |
||||
m_progress->DisplayFormattedModalError("Failed to open '%s': %d", path, wres); |
||||
return false; |
||||
} |
||||
|
||||
m_file_opened = true; |
||||
SzArEx_Init(&m_archive); |
||||
|
||||
SRes res = SzArEx_Open(&m_archive, &m_look_stream.vt, &g_Alloc, &g_Alloc); |
||||
if (res != SZ_OK) |
||||
{ |
||||
m_progress->DisplayFormattedModalError("SzArEx_Open() failed: %s [%d]", SZErrorToString(res), res); |
||||
return false; |
||||
} |
||||
|
||||
m_archive_opened = true; |
||||
m_progress->SetStatusText("Parsing update zip..."); |
||||
return ParseZip(); |
||||
} |
||||
|
||||
bool Updater::RecursiveDeleteDirectory(const char* path) |
||||
{ |
||||
#ifdef _WIN32 |
||||
// making this safer on Win32...
|
||||
std::wstring wpath(StringUtil::UTF8StringToWideString(path)); |
||||
wpath += L'\0'; |
||||
|
||||
SHFILEOPSTRUCTW op = {}; |
||||
op.wFunc = FO_DELETE; |
||||
op.pFrom = wpath.c_str(); |
||||
op.fFlags = FOF_NOCONFIRMATION; |
||||
|
||||
return (SHFileOperationW(&op) == 0 && !op.fAnyOperationsAborted); |
||||
#else |
||||
return FileSystem::DeleteDirectory(path, true); |
||||
#endif |
||||
} |
||||
|
||||
bool Updater::ParseZip() |
||||
{ |
||||
std::vector<UInt16> filename_buffer; |
||||
|
||||
for (u32 file_index = 0; file_index < m_archive.NumFiles; file_index++) |
||||
{ |
||||
// skip directories, we handle them ourselves
|
||||
if (SzArEx_IsDir(&m_archive, file_index)) |
||||
continue; |
||||
|
||||
size_t filename_len = SzArEx_GetFileNameUtf16(&m_archive, file_index, nullptr); |
||||
if (filename_len <= 1) |
||||
continue; |
||||
|
||||
filename_buffer.resize(filename_len); |
||||
SzArEx_GetFileNameUtf16(&m_archive, file_index, filename_buffer.data()); |
||||
|
||||
// TODO: This won't work on Linux (4-byte wchar_t).
|
||||
FileToUpdate entry; |
||||
entry.file_index = file_index; |
||||
entry.destination_filename = StringUtil::WideStringToUTF8String(reinterpret_cast<wchar_t*>(filename_buffer.data())); |
||||
if (entry.destination_filename.empty()) |
||||
continue; |
||||
|
||||
// replace forward slashes with backslashes
|
||||
for (size_t i = 0; i < entry.destination_filename.length(); i++) |
||||
{ |
||||
if (entry.destination_filename[i] == '/' || entry.destination_filename[i] == '\\') |
||||
entry.destination_filename[i] = FS_OSPATH_SEPARATOR_CHARACTER; |
||||
} |
||||
|
||||
// should never have a leading slash. just in case.
|
||||
while (entry.destination_filename[0] == FS_OSPATH_SEPARATOR_CHARACTER) |
||||
entry.destination_filename.erase(0, 1); |
||||
|
||||
// skip directories (we sort them out later)
|
||||
if (!entry.destination_filename.empty() && entry.destination_filename.back() != FS_OSPATH_SEPARATOR_CHARACTER) |
||||
{ |
||||
// skip updater itself, since it was already pre-extracted.
|
||||
if (StringUtil::Strcasecmp(entry.destination_filename.c_str(), "updater.exe") != 0) |
||||
{ |
||||
m_progress->DisplayFormattedInformation("Found file in zip: '%s'", entry.destination_filename.c_str()); |
||||
m_update_paths.push_back(std::move(entry)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
if (m_update_paths.empty()) |
||||
{ |
||||
m_progress->ModalError("No files found in update zip."); |
||||
return false; |
||||
} |
||||
|
||||
for (const FileToUpdate& ftu : m_update_paths) |
||||
{ |
||||
const size_t len = ftu.destination_filename.length(); |
||||
for (size_t i = 0; i < len; i++) |
||||
{ |
||||
if (ftu.destination_filename[i] == FS_OSPATH_SEPARATOR_CHARACTER) |
||||
{ |
||||
std::string dir(ftu.destination_filename.begin(), ftu.destination_filename.begin() + i); |
||||
while (!dir.empty() && dir[dir.length() - 1] == FS_OSPATH_SEPARATOR_CHARACTER) |
||||
dir.erase(dir.length() - 1); |
||||
|
||||
if (std::find(m_update_directories.begin(), m_update_directories.end(), dir) == m_update_directories.end()) |
||||
m_update_directories.push_back(std::move(dir)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
std::sort(m_update_directories.begin(), m_update_directories.end()); |
||||
for (const std::string& dir : m_update_directories) |
||||
m_progress->DisplayFormattedDebugMessage("Directory: %s", dir.c_str()); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
bool Updater::PrepareStagingDirectory() |
||||
{ |
||||
if (FileSystem::DirectoryExists(m_staging_directory.c_str())) |
||||
{ |
||||
m_progress->DisplayFormattedWarning("Update staging directory already exists, removing"); |
||||
if (!RecursiveDeleteDirectory(m_staging_directory.c_str()) || |
||||
FileSystem::DirectoryExists(m_staging_directory.c_str())) |
||||
{ |
||||
m_progress->ModalError("Failed to remove old staging directory"); |
||||
return false; |
||||
} |
||||
} |
||||
if (!FileSystem::CreateDirectoryPath(m_staging_directory.c_str(), false)) |
||||
{ |
||||
m_progress->DisplayFormattedModalError("Failed to create staging directory %s", m_staging_directory.c_str()); |
||||
return false; |
||||
} |
||||
|
||||
// create subdirectories in staging directory
|
||||
for (const std::string& subdir : m_update_directories) |
||||
{ |
||||
m_progress->DisplayFormattedInformation("Creating subdirectory in staging: %s", subdir.c_str()); |
||||
|
||||
const std::string staging_subdir = |
||||
StringUtil::StdStringFromFormat("%s" FS_OSPATH_SEPARATOR_STR "%s", m_staging_directory.c_str(), subdir.c_str()); |
||||
if (!FileSystem::CreateDirectoryPath(staging_subdir.c_str(), false)) |
||||
{ |
||||
m_progress->DisplayFormattedModalError("Failed to create staging subdirectory %s", staging_subdir.c_str()); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
bool Updater::StageUpdate() |
||||
{ |
||||
m_progress->SetProgressRange(static_cast<u32>(m_update_paths.size())); |
||||
m_progress->SetProgressValue(0); |
||||
|
||||
UInt32 block_index = 0xFFFFFFFF; /* it can have any value before first call (if outBuffer = 0) */ |
||||
Byte* out_buffer = 0; /* it must be 0 before first call for each new archive. */ |
||||
size_t out_buffer_size = 0; /* it can have any value before first call (if outBuffer = 0) */ |
||||
ScopedGuard out_buffer_guard([&out_buffer]() { |
||||
if (out_buffer) |
||||
ISzAlloc_Free(&g_Alloc, out_buffer); |
||||
}); |
||||
|
||||
for (const FileToUpdate& ftu : m_update_paths) |
||||
{ |
||||
m_progress->SetFormattedStatusText("Extracting '%s'...", ftu.destination_filename.c_str()); |
||||
m_progress->DisplayFormattedInformation("Decompressing '%s'...", ftu.destination_filename.c_str()); |
||||
|
||||
size_t out_offset = 0; |
||||
size_t extracted_size = 0; |
||||
SRes res = SzArEx_Extract(&m_archive, &m_look_stream.vt, ftu.file_index, |
||||
&block_index, &out_buffer, &out_buffer_size, &out_offset, &extracted_size, &g_Alloc, &g_Alloc); |
||||
if (res != SZ_OK) |
||||
{ |
||||
m_progress->DisplayFormattedModalError("Failed to decompress file '%s' from 7z (file index=%u, error=%s)", |
||||
ftu.destination_filename.c_str(), ftu.file_index, SZErrorToString(res)); |
||||
return false; |
||||
} |
||||
|
||||
m_progress->DisplayFormattedInformation("Writing '%s' to staging (%zu bytes)...", ftu.destination_filename.c_str(), extracted_size); |
||||
|
||||
const std::string destination_file = StringUtil::StdStringFromFormat( |
||||
"%s" FS_OSPATH_SEPARATOR_STR "%s", m_staging_directory.c_str(), ftu.destination_filename.c_str()); |
||||
std::FILE* fp = FileSystem::OpenCFile(destination_file.c_str(), "wb"); |
||||
if (!fp) |
||||
{ |
||||
m_progress->DisplayFormattedModalError("Failed to open staging output file '%s'", destination_file.c_str()); |
||||
return false; |
||||
} |
||||
|
||||
const bool wrote_completely = std::fwrite(out_buffer + out_offset, extracted_size, 1, fp) == 1 && std::fflush(fp) == 0; |
||||
if (std::fclose(fp) != 0 || !wrote_completely) |
||||
{ |
||||
m_progress->DisplayFormattedModalError("Failed to write output file '%s'", destination_file.c_str()); |
||||
FileSystem::DeleteFilePath(destination_file.c_str()); |
||||
return false; |
||||
} |
||||
|
||||
m_progress->IncrementProgressValue(); |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
bool Updater::CommitUpdate() |
||||
{ |
||||
m_progress->SetStatusText("Committing update..."); |
||||
|
||||
// create directories in target
|
||||
for (const std::string& subdir : m_update_directories) |
||||
{ |
||||
const std::string dest_subdir = StringUtil::StdStringFromFormat("%s" FS_OSPATH_SEPARATOR_STR "%s", |
||||
m_destination_directory.c_str(), subdir.c_str()); |
||||
|
||||
if (!FileSystem::DirectoryExists(dest_subdir.c_str()) && !FileSystem::CreateDirectoryPath(dest_subdir.c_str(), false)) |
||||
{ |
||||
m_progress->DisplayFormattedModalError("Failed to create target directory '%s'", dest_subdir.c_str()); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
// move files to target
|
||||
for (const FileToUpdate& ftu : m_update_paths) |
||||
{ |
||||
const std::string staging_file_name = StringUtil::StdStringFromFormat( |
||||
"%s" FS_OSPATH_SEPARATOR_STR "%s", m_staging_directory.c_str(), ftu.destination_filename.c_str()); |
||||
const std::string dest_file_name = StringUtil::StdStringFromFormat( |
||||
"%s" FS_OSPATH_SEPARATOR_STR "%s", m_destination_directory.c_str(), ftu.destination_filename.c_str()); |
||||
m_progress->DisplayFormattedInformation("Moving '%s' to '%s'", staging_file_name.c_str(), dest_file_name.c_str()); |
||||
#ifdef _WIN32 |
||||
const bool result = |
||||
MoveFileExW(StringUtil::UTF8StringToWideString(staging_file_name).c_str(), |
||||
StringUtil::UTF8StringToWideString(dest_file_name).c_str(), MOVEFILE_REPLACE_EXISTING); |
||||
#else |
||||
const bool result = (rename(staging_file_name.c_str(), dest_file_name.c_str()) == 0); |
||||
#endif |
||||
if (!result) |
||||
{ |
||||
m_progress->DisplayFormattedModalError("Failed to rename '%s' to '%s'", staging_file_name.c_str(), |
||||
dest_file_name.c_str()); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
void Updater::CleanupStagingDirectory() |
||||
{ |
||||
// remove staging directory itself
|
||||
if (!RecursiveDeleteDirectory(m_staging_directory.c_str())) |
||||
m_progress->DisplayFormattedError("Failed to remove staging directory '%s'", m_staging_directory.c_str()); |
||||
} |
@ -0,0 +1,66 @@ |
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team |
||||
* |
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms |
||||
* of the GNU Lesser General Public License as published by the Free Software Found- |
||||
* ation, either version 3 of the License, or (at your option) any later version. |
||||
* |
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; |
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
||||
* PURPOSE. See the GNU General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License along with PCSX2. |
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include "common/ProgressCallback.h" |
||||
|
||||
#include "7z.h" |
||||
#include "7zFile.h" |
||||
|
||||
#include <string> |
||||
#include <vector> |
||||
|
||||
class Updater |
||||
{ |
||||
public: |
||||
Updater(ProgressCallback* progress); |
||||
~Updater(); |
||||
|
||||
static void SetupLogging(ProgressCallback* progress, const std::string& destination_directory); |
||||
|
||||
bool Initialize(std::string destination_directory); |
||||
|
||||
bool OpenUpdateZip(const char* path); |
||||
bool PrepareStagingDirectory(); |
||||
bool StageUpdate(); |
||||
bool CommitUpdate(); |
||||
void CleanupStagingDirectory(); |
||||
|
||||
private: |
||||
static bool RecursiveDeleteDirectory(const char* path); |
||||
|
||||
struct FileToUpdate |
||||
{ |
||||
u32 file_index; |
||||
std::string destination_filename; |
||||
}; |
||||
|
||||
bool ParseZip(); |
||||
|
||||
std::string m_destination_directory; |
||||
std::string m_staging_directory; |
||||
|
||||
std::vector<FileToUpdate> m_update_paths; |
||||
std::vector<std::string> m_update_directories; |
||||
|
||||
ProgressCallback* m_progress; |
||||
CFileInStream m_archive_stream = {}; |
||||
CLookToRead2 m_look_stream = {}; |
||||
CSzArEx m_archive = {}; |
||||
|
||||
bool m_file_opened = false; |
||||
bool m_archive_opened = false; |
||||
}; |
@ -0,0 +1,166 @@ |
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team |
||||
* |
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms |
||||
* of the GNU Lesser General Public License as published by the Free Software Found- |
||||
* ation, either version 3 of the License, or (at your option) any later version. |
||||
* |
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; |
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
||||
* PURPOSE. See the GNU General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License along with PCSX2. |
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include "SZErrors.h" |
||||
|
||||
#include "common/FileSystem.h" |
||||
#include "common/ScopedGuard.h" |
||||
#include "common/StringUtil.h" |
||||
|
||||
#include "fmt/core.h" |
||||
|
||||
#if defined(_WIN32) |
||||
#include "7z.h" |
||||
#include "7zAlloc.h" |
||||
#include "7zCrc.h" |
||||
#include "7zFile.h" |
||||
#endif |
||||
|
||||
#include <cstdio> |
||||
#include <string> |
||||
#include <vector> |
||||
|
||||
#ifdef _WIN32 |
||||
static constexpr char UPDATER_EXECUTABLE[] = "updater.exe"; |
||||
static constexpr char UPDATER_ARCHIVE_NAME[] = "update.7z"; |
||||
#endif |
||||
|
||||
static inline bool ExtractUpdater(const char* archive_path, const char* destination_path, std::string* error) |
||||
{ |
||||
#if defined(_WIN32) |
||||
static constexpr size_t kInputBufSize = ((size_t)1 << 18); |
||||
static constexpr ISzAlloc g_Alloc = {SzAlloc, SzFree}; |
||||
|
||||
CFileInStream instream = {}; |
||||
CLookToRead2 lookstream = {}; |
||||
CSzArEx archive = {}; |
||||
|
||||
FileInStream_CreateVTable(&instream); |
||||
LookToRead2_CreateVTable(&lookstream, False); |
||||
CrcGenerateTable(); |
||||
|
||||
lookstream.buf = (Byte*)ISzAlloc_Alloc(&g_Alloc, kInputBufSize); |
||||
if (!lookstream.buf) |
||||
{ |
||||
*error = "Failed to allocate input buffer?!"; |
||||
return false; |
||||
} |
||||
|
||||
lookstream.bufSize = kInputBufSize; |
||||
lookstream.realStream = &instream.vt; |
||||
LookToRead2_Init(&lookstream); |
||||
ScopedGuard buffer_guard([&lookstream]() { |
||||
ISzAlloc_Free(&g_Alloc, lookstream.buf); |
||||
}); |
||||
|
||||
#ifdef _WIN32 |
||||
WRes wres = InFile_OpenW(&instream.file, StringUtil::UTF8StringToWideString(archive_path).c_str()); |
||||
#else |
||||
WRes wres = InFile_Open(&instream.file, archive_path); |
||||
#endif |
||||
if (wres != 0) |
||||
{ |
||||
*error = fmt::format("Failed to open '{0}': {1}", archive_path, wres); |
||||
return false; |
||||
} |
||||
|
||||
ScopedGuard file_guard([&instream]() { |
||||
File_Close(&instream.file); |
||||
}); |
||||
|
||||
SzArEx_Init(&archive); |
||||
|
||||
SRes res = SzArEx_Open(&archive, &lookstream.vt, &g_Alloc, &g_Alloc); |
||||
if (res != SZ_OK) |
||||
{ |
||||
*error = fmt::format("SzArEx_Open() failed: {0} [{1}]", SZErrorToString(res), res); |
||||
return false; |
||||
} |
||||
ScopedGuard archive_guard([&archive]() { |
||||
SzArEx_Free(&archive, &g_Alloc); |
||||
}); |
||||
|
||||
std::vector<UInt16> filename_buffer; |
||||
u32 updater_file_index = archive.NumFiles; |
||||
for (u32 file_index = 0; file_index < archive.NumFiles; file_index++) |
||||
{ |
||||
if (SzArEx_IsDir(&archive, file_index)) |
||||
continue; |
||||
|
||||
size_t filename_len = SzArEx_GetFileNameUtf16(&archive, file_index, nullptr); |
||||
if (filename_len <= 1) |
||||
continue; |
||||
|
||||
filename_buffer.resize(filename_len); |
||||
filename_len = SzArEx_GetFileNameUtf16(&archive, file_index, filename_buffer.data()); |
||||
|
||||
// TODO: This won't work on Linux (4-byte wchar_t).
|
||||
const std::string filename(StringUtil::WideStringToUTF8String(reinterpret_cast<wchar_t*>(filename_buffer.data()))); |
||||
if (filename != UPDATER_EXECUTABLE) |
||||
continue; |
||||
|
||||
updater_file_index = file_index; |
||||
break; |
||||
} |
||||
|
||||
if (updater_file_index == archive.NumFiles) |
||||
{ |
||||
*error = fmt::format("Updater executable ({}) not found in archive.", UPDATER_EXECUTABLE); |
||||
return false; |
||||
} |
||||
|
||||
UInt32 block_index = 0xFFFFFFFF; /* it can have any value before first call (if outBuffer = 0) */ |
||||
Byte* out_buffer = 0; /* it must be 0 before first call for each new archive. */ |
||||
size_t out_buffer_size = 0; /* it can have any value before first call (if outBuffer = 0) */ |
||||
ScopedGuard out_buffer_guard([&out_buffer]() { |
||||
if (out_buffer) |
||||
ISzAlloc_Free(&g_Alloc, out_buffer); |
||||
}); |
||||
|
||||
size_t out_offset = 0; |
||||
size_t extracted_size = 0; |
||||
res = SzArEx_Extract(&archive, &lookstream.vt, updater_file_index, |
||||
&block_index, &out_buffer, &out_buffer_size, &out_offset, &extracted_size, &g_Alloc, &g_Alloc); |
||||
if (res != SZ_OK) |
||||
{ |
||||
*error = fmt::format("Failed to decompress {0} from 7z (file index=%u, error=%s)", |
||||
UPDATER_EXECUTABLE, updater_file_index, SZErrorToString(res)); |
||||
return false; |
||||
} |
||||
|
||||
std::FILE* fp = FileSystem::OpenCFile(destination_path, "wb"); |
||||
if (!fp) |
||||
{ |
||||
*error = fmt::format("Failed to open '{0}' for writing.", destination_path); |
||||
return false; |
||||
} |
||||
|
||||
const bool wrote_completely = std::fwrite(out_buffer + out_offset, extracted_size, 1, fp) == 1 && std::fflush(fp) == 0; |
||||
if (std::fclose(fp) != 0 || !wrote_completely) |
||||
{ |
||||
*error = fmt::format("Failed to write output file '{}'", destination_path); |
||||
FileSystem::DeleteFilePath(destination_path); |
||||
return false; |
||||
} |
||||
|
||||
error->clear(); |
||||
return true; |
||||
#else |
||||
*error = "Not supported on this platform"; |
||||
return false; |
||||
#endif |
||||
} |
@ -0,0 +1,395 @@ |
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team |
||||
* |
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms |
||||
* of the GNU Lesser General Public License as published by the Free Software Found- |
||||
* ation, either version 3 of the License, or (at your option) any later version. |
||||
* |
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; |
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
||||
* PURPOSE. See the GNU General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License along with PCSX2. |
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
#include "Updater.h" |
||||
#include "Windows/resource.h" |
||||
|
||||
#include "common/FileSystem.h" |
||||
#include "common/Console.h" |
||||
#include "common/StringUtil.h" |
||||
#include "common/ProgressCallback.h" |
||||
#include "common/RedtapeWindows.h" |
||||
|
||||
#include <CommCtrl.h> |
||||
#include <shellapi.h> |
||||
|
||||
class Win32ProgressCallback final : public BaseProgressCallback |
||||
{ |
||||
public: |
||||
Win32ProgressCallback(); |
||||
|
||||
void PushState() override; |
||||
void PopState() override; |
||||
|
||||
void SetCancellable(bool cancellable) override; |
||||
void SetTitle(const char* title) override; |
||||
void SetStatusText(const char* text) override; |
||||
void SetProgressRange(u32 range) override; |
||||
void SetProgressValue(u32 value) override; |
||||
|
||||
void DisplayError(const char* message) override; |
||||
void DisplayWarning(const char* message) override; |
||||
void DisplayInformation(const char* message) override; |
||||
void DisplayDebugMessage(const char* message) override; |
||||
|
||||
void ModalError(const char* message) override; |
||||
bool ModalConfirmation(const char* message) override; |
||||
void ModalInformation(const char* message) override; |
||||
|
||||
private: |
||||
enum : int |
||||
{ |
||||
WINDOW_WIDTH = 600, |
||||
WINDOW_HEIGHT = 300, |
||||
WINDOW_MARGIN = 10, |
||||
SUBWINDOW_WIDTH = WINDOW_WIDTH - 20 - WINDOW_MARGIN - WINDOW_MARGIN, |
||||
}; |
||||
|
||||
bool Create(); |
||||
void Destroy(); |
||||
void Redraw(bool force); |
||||
void PumpMessages(); |
||||
|
||||
static LRESULT CALLBACK WndProcThunk(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); |
||||
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); |
||||
|
||||
HWND m_window_hwnd{}; |
||||
HWND m_text_hwnd{}; |
||||
HWND m_progress_hwnd{}; |
||||
HWND m_list_box_hwnd{}; |
||||
|
||||
int m_last_progress_percent = -1; |
||||
}; |
||||
|
||||
Win32ProgressCallback::Win32ProgressCallback() |
||||
: BaseProgressCallback() |
||||
{ |
||||
Create(); |
||||
} |
||||
|
||||
void Win32ProgressCallback::PushState() |
||||
{ |
||||
BaseProgressCallback::PushState(); |
||||
} |
||||
|
||||
void Win32ProgressCallback::PopState() |
||||
{ |
||||
BaseProgressCallback::PopState(); |
||||
Redraw(true); |
||||
} |
||||
|
||||
void Win32ProgressCallback::SetCancellable(bool cancellable) |
||||
{ |
||||
BaseProgressCallback::SetCancellable(cancellable); |
||||
Redraw(true); |
||||
} |
||||
|
||||
void Win32ProgressCallback::SetTitle(const char* title) |
||||
{ |
||||
SetWindowTextW(m_window_hwnd, StringUtil::UTF8StringToWideString(title).c_str()); |
||||
} |
||||
|
||||
void Win32ProgressCallback::SetStatusText(const char* text) |
||||
{ |
||||
BaseProgressCallback::SetStatusText(text); |
||||
Redraw(true); |
||||
} |
||||
|
||||
void Win32ProgressCallback::SetProgressRange(u32 range) |
||||
{ |
||||
BaseProgressCallback::SetProgressRange(range); |
||||
Redraw(false); |
||||
} |
||||
|
||||
void Win32ProgressCallback::SetProgressValue(u32 value) |
||||
{ |
||||
BaseProgressCallback::SetProgressValue(value); |
||||
Redraw(false); |
||||
} |
||||
|
||||
bool Win32ProgressCallback::Create() |
||||
{ |
||||
static const wchar_t* CLASS_NAME = L"PCSX2Win32ProgressCallbackWindow"; |
||||
static bool class_registered = false; |
||||
|
||||
if (!class_registered) |
||||
{ |
||||
InitCommonControls(); |
||||
|
||||
WNDCLASSEX wc = {}; |
||||
wc.cbSize = sizeof(WNDCLASSEX); |
||||
wc.lpfnWndProc = WndProcThunk; |
||||
wc.hInstance = GetModuleHandle(nullptr); |
||||
wc.hIcon = LoadIcon(wc.hInstance, MAKEINTRESOURCE(IDI_ICON1)); |
||||
wc.hIconSm = LoadIcon(wc.hInstance, MAKEINTRESOURCE(IDI_ICON1)); |
||||
wc.hCursor = LoadCursor(NULL, IDC_WAIT); |
||||
wc.hbrBackground = (HBRUSH)COLOR_WINDOW; |
||||
wc.lpszClassName = CLASS_NAME; |
||||
if (!RegisterClassExW(&wc)) |
||||
{ |
||||
MessageBoxW(nullptr, L"Failed to register window class", L"Error", MB_OK); |
||||
return false; |
||||
} |
||||
|
||||
class_registered = true; |
||||
} |
||||
|
||||
m_window_hwnd = |
||||
CreateWindowExW(WS_EX_CLIENTEDGE, CLASS_NAME, L"Win32ProgressCallback", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, |
||||
CW_USEDEFAULT, WINDOW_WIDTH, WINDOW_HEIGHT, nullptr, nullptr, GetModuleHandle(nullptr), this); |
||||
if (!m_window_hwnd) |
||||
{ |
||||
MessageBoxW(nullptr, L"Failed to create window", L"Error", MB_OK); |
||||
return false; |
||||
} |
||||
|
||||
SetWindowLongPtr(m_window_hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this)); |
||||
ShowWindow(m_window_hwnd, SW_SHOW); |
||||
PumpMessages(); |
||||
return true; |
||||
} |
||||
|
||||
void Win32ProgressCallback::Destroy() |
||||
{ |
||||
if (!m_window_hwnd) |
||||
return; |
||||
|
||||
DestroyWindow(m_window_hwnd); |
||||
m_window_hwnd = {}; |
||||
m_text_hwnd = {}; |
||||
m_progress_hwnd = {}; |
||||
} |
||||
|
||||
void Win32ProgressCallback::PumpMessages() |
||||
{ |
||||
MSG msg; |
||||
while (PeekMessageW(&msg, m_window_hwnd, 0, 0, PM_REMOVE)) |
||||
{ |
||||
TranslateMessage(&msg); |
||||
DispatchMessageW(&msg); |
||||
} |
||||
} |
||||
|
||||
void Win32ProgressCallback::Redraw(bool force) |
||||
{ |
||||
const int percent = |
||||
static_cast<int>((static_cast<float>(m_progress_value) / static_cast<float>(m_progress_range)) * 100.0f); |
||||
if (percent == m_last_progress_percent && !force) |
||||
{ |
||||
PumpMessages(); |
||||
return; |
||||
} |
||||
|
||||
m_last_progress_percent = percent; |
||||
|
||||
SendMessageW(m_progress_hwnd, PBM_SETRANGE, 0, MAKELPARAM(0, m_progress_range)); |
||||
SendMessageW(m_progress_hwnd, PBM_SETPOS, static_cast<WPARAM>(m_progress_value), 0); |
||||
SetWindowTextW(m_text_hwnd, StringUtil::UTF8StringToWideString(m_status_text).c_str()); |
||||
RedrawWindow(m_text_hwnd, nullptr, nullptr, RDW_INVALIDATE); |
||||
PumpMessages(); |
||||
} |
||||
|
||||
LRESULT CALLBACK Win32ProgressCallback::WndProcThunk(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) |
||||
{ |
||||
Win32ProgressCallback* cb; |
||||
if (msg == WM_CREATE) |
||||
{ |
||||
const CREATESTRUCTW* cs = reinterpret_cast<CREATESTRUCTW*>(lparam); |
||||
cb = static_cast<Win32ProgressCallback*>(cs->lpCreateParams); |
||||
} |
||||
else |
||||
{ |
||||
cb = reinterpret_cast<Win32ProgressCallback*>(GetWindowLongPtrW(hwnd, GWLP_USERDATA)); |
||||
} |
||||
|
||||
return cb->WndProc(hwnd, msg, wparam, lparam); |
||||
} |
||||
|
||||
LRESULT CALLBACK Win32ProgressCallback::WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) |
||||
{ |
||||
switch (msg) |
||||
{ |
||||
case WM_CREATE: |
||||
{ |
||||
const CREATESTRUCTA* cs = reinterpret_cast<CREATESTRUCTA*>(lparam); |
||||
HFONT default_font = reinterpret_cast<HFONT>(GetStockObject(ANSI_VAR_FONT)); |
||||
SendMessageW(hwnd, WM_SETFONT, WPARAM(default_font), TRUE); |
||||
|
||||
int y = WINDOW_MARGIN; |
||||
|
||||
m_text_hwnd = CreateWindowExW(0, L"Static", nullptr, WS_VISIBLE | WS_CHILD, WINDOW_MARGIN, y, SUBWINDOW_WIDTH, 16, |
||||
hwnd, nullptr, cs->hInstance, nullptr); |
||||
SendMessageW(m_text_hwnd, WM_SETFONT, WPARAM(default_font), TRUE); |
||||
y += 16 + WINDOW_MARGIN; |
||||
|
||||
m_progress_hwnd = CreateWindowExW(0, PROGRESS_CLASSW, nullptr, WS_VISIBLE | WS_CHILD, WINDOW_MARGIN, y, |
||||
SUBWINDOW_WIDTH, 32, hwnd, nullptr, cs->hInstance, nullptr); |
||||
y += 32 + WINDOW_MARGIN; |
||||
|
||||
m_list_box_hwnd = |
||||
CreateWindowExW(0, L"LISTBOX", nullptr, WS_VISIBLE | WS_CHILD | WS_VSCROLL | WS_HSCROLL | WS_BORDER | LBS_NOSEL, |
||||
WINDOW_MARGIN, y, SUBWINDOW_WIDTH, 170, hwnd, nullptr, cs->hInstance, nullptr); |
||||
SendMessageW(m_list_box_hwnd, WM_SETFONT, WPARAM(default_font), TRUE); |
||||
y += 170; |
||||
} |
||||
break; |
||||
|
||||
default: |
||||
return DefWindowProcW(hwnd, msg, wparam, lparam); |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
void Win32ProgressCallback::DisplayError(const char* message) |
||||
{ |
||||
Console.Error(message); |
||||
SendMessageW(m_list_box_hwnd, LB_ADDSTRING, 0, reinterpret_cast<LPARAM>(StringUtil::UTF8StringToWideString(message).c_str())); |
||||
SendMessageW(m_list_box_hwnd, WM_VSCROLL, SB_BOTTOM, 0); |
||||
PumpMessages(); |
||||
} |
||||
|
||||
void Win32ProgressCallback::DisplayWarning(const char* message) |
||||
{ |
||||
Console.Warning(message); |
||||
SendMessageW(m_list_box_hwnd, LB_ADDSTRING, 0, reinterpret_cast<LPARAM>(StringUtil::UTF8StringToWideString(message).c_str())); |
||||
SendMessageW(m_list_box_hwnd, WM_VSCROLL, SB_BOTTOM, 0); |
||||
PumpMessages(); |
||||
} |
||||
|
||||
void Win32ProgressCallback::DisplayInformation(const char* message) |
||||
{ |
||||
Console.WriteLn(message); |
||||
SendMessageW(m_list_box_hwnd, LB_ADDSTRING, 0, reinterpret_cast<LPARAM>(StringUtil::UTF8StringToWideString(message).c_str())); |
||||
SendMessageW(m_list_box_hwnd, WM_VSCROLL, SB_BOTTOM, 0); |
||||
PumpMessages(); |
||||
} |
||||
|
||||
void Win32ProgressCallback::DisplayDebugMessage(const char* message) |
||||
{ |
||||
Console.WriteLn(message); |
||||
} |
||||
|
||||
void Win32ProgressCallback::ModalError(const char* message) |
||||
{ |
||||
PumpMessages(); |
||||
MessageBoxW(m_window_hwnd, StringUtil::UTF8StringToWideString(message).c_str(), L"Error", MB_ICONERROR | MB_OK); |
||||
PumpMessages(); |
||||
} |
||||
|
||||
bool Win32ProgressCallback::ModalConfirmation(const char* message) |
||||
{ |
||||
PumpMessages(); |
||||
bool result = MessageBoxW(m_window_hwnd, StringUtil::UTF8StringToWideString(message).c_str(), L"Confirmation", MB_ICONQUESTION | MB_YESNO) == IDYES; |
||||
PumpMessages(); |
||||
return result; |
||||
} |
||||
|
||||
void Win32ProgressCallback::ModalInformation(const char* message) |
||||
{ |
||||
MessageBoxW(m_window_hwnd, StringUtil::UTF8StringToWideString(message).c_str(), L"Information", MB_ICONINFORMATION | MB_OK); |
||||
} |
||||
|
||||
|
||||
static void WaitForProcessToExit(int process_id) |
||||
{ |
||||
HANDLE hProcess = OpenProcess(SYNCHRONIZE, FALSE, process_id); |
||||
if (!hProcess) |
||||
return; |
||||
|
||||
WaitForSingleObject(hProcess, INFINITE); |
||||
CloseHandle(hProcess); |
||||
} |
||||
|
||||
#include "UpdaterExtractor.h" |
||||
|
||||
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd) |
||||
{ |
||||
Win32ProgressCallback progress; |
||||
|
||||
int argc = 0; |
||||
LPWSTR* argv = CommandLineToArgvW(lpCmdLine, &argc); |
||||
if (!argv || argc <= 0) |
||||
{ |
||||
progress.ModalError("Failed to parse command line."); |
||||
return 1; |
||||
} |
||||
if (argc != 4) |
||||
{ |
||||
progress.ModalError("Expected 4 arguments: parent process id, output directory, update zip, program to " |
||||
"launch.\n\nThis program is not intended to be run manually, please use the Qt frontend and " |
||||
"click Help->Check for Updates."); |
||||
LocalFree(argv); |
||||
return 1; |
||||
} |
||||
|
||||
const int parent_process_id = StringUtil::FromChars<int>(StringUtil::WideStringToUTF8String(argv[0])).value_or(0); |
||||
const std::string destination_directory = StringUtil::WideStringToUTF8String(argv[1]); |
||||
const std::string zip_path = StringUtil::WideStringToUTF8String(argv[2]); |
||||
const std::wstring program_to_launch(argv[3]); |
||||
LocalFree(argv); |
||||
|
||||
if (parent_process_id <= 0 || destination_directory.empty() || zip_path.empty() || program_to_launch.empty()) |
||||
{ |
||||
progress.ModalError("One or more parameters is empty."); |
||||
return 1; |
||||
} |
||||
|
||||
Updater::SetupLogging(&progress, destination_directory); |
||||
|
||||
progress.SetFormattedStatusText("Waiting for parent process %d to exit...", parent_process_id); |
||||
WaitForProcessToExit(parent_process_id); |
||||
|
||||
Updater updater(&progress); |
||||
if (!updater.Initialize(destination_directory)) |
||||
{ |
||||
progress.ModalError("Failed to initialize updater."); |
||||
return 1; |
||||
} |
||||
|
||||
if (!updater.OpenUpdateZip(zip_path.c_str())) |
||||
{ |
||||
progress.DisplayFormattedModalError("Could not open update zip '%s'. Update not installed.", zip_path.c_str()); |
||||
return 1; |
||||
} |
||||
|
||||
if (!updater.PrepareStagingDirectory()) |
||||
{ |
||||
progress.ModalError("Failed to prepare staging directory. Update not installed."); |
||||
return 1; |
||||
} |
||||
|
||||
if (!updater.StageUpdate()) |
||||
{ |
||||
progress.ModalError("Failed to stage update. Update not installed."); |
||||
return 1; |
||||
} |
||||
|
||||
if (!updater.CommitUpdate()) |
||||
{ |
||||
progress.ModalError( |
||||
"Failed to commit update. Your installation may be corrupted, please re-download a fresh version from GitHub."); |
||||
return 1; |
||||
} |
||||
|
||||
updater.CleanupStagingDirectory(); |
||||
|
||||
progress.ModalInformation("Update complete."); |
||||
|
||||
progress.DisplayFormattedInformation("Launching '%s'...", |
||||
StringUtil::WideStringToUTF8String(program_to_launch).c_str()); |
||||
ShellExecuteW(nullptr, L"open", program_to_launch.c_str(), nullptr, nullptr, SW_SHOWNORMAL); |
||||
return 0; |
||||
} |
@ -0,0 +1,16 @@ |
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by duckstation-qt.rc
|
||||
//
|
||||
#define IDI_ICON1 102 |
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED |
||||
#ifndef APSTUDIO_READONLY_SYMBOLS |
||||
#define _APS_NEXT_RESOURCE_VALUE 103 |
||||
#define _APS_NEXT_COMMAND_VALUE 40001 |
||||
#define _APS_NEXT_CONTROL_VALUE 1001 |
||||
#define _APS_NEXT_SYMED_VALUE 101 |
||||
#endif |
||||
#endif |
After Width: | Height: | Size: 41 KiB |
@ -0,0 +1,22 @@ |
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> |
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> |
||||
<assemblyIdentity |
||||
version="1.0.0.0" |
||||
processorArchitecture="*" |
||||
name="com.github.stenzek.duckstation.updater" |
||||
type="win32" |
||||
/> |
||||
<description>PCSX2 Updater</description> |
||||
<dependency> |
||||
<dependentAssembly> |
||||
<assemblyIdentity |
||||
type="win32" |
||||
name="Microsoft.Windows.Common-Controls" |
||||
version="6.0.0.0" |
||||
processorArchitecture="*" |
||||
publicKeyToken="6595b64144ccf1df" |
||||
language="*" |
||||
/> |
||||
</dependentAssembly> |
||||
</dependency> |
||||
</assembly> |
@ -0,0 +1,110 @@ |
||||
// Microsoft Visual C++ generated resource script. |
||||
// |
||||
#include "resource.h" |
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS |
||||
///////////////////////////////////////////////////////////////////////////// |
||||
// |
||||
// Generated from the TEXTINCLUDE 2 resource. |
||||
// |
||||
#include "winres.h" |
||||
|
||||
///////////////////////////////////////////////////////////////////////////// |
||||
#undef APSTUDIO_READONLY_SYMBOLS |
||||
|
||||
///////////////////////////////////////////////////////////////////////////// |
||||
// English (Australia) resources |
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) |
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS |
||||
#pragma code_page(1252) |
||||
|
||||
#ifdef APSTUDIO_INVOKED |
||||
///////////////////////////////////////////////////////////////////////////// |
||||
// |
||||
// TEXTINCLUDE |
||||
// |
||||
|
||||
1 TEXTINCLUDE |
||||
BEGIN |
||||
"resource.h\0" |
||||
END |
||||
|
||||
2 TEXTINCLUDE |
||||
BEGIN |
||||
"#include ""winres.h""\r\n" |
||||
"\0" |
||||
END |
||||
|
||||
3 TEXTINCLUDE |
||||
BEGIN |
||||
"\r\n" |
||||
"\0" |
||||
END |
||||
|
||||
#endif // APSTUDIO_INVOKED |
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////// |
||||
// |
||||
// Version |
||||
// |
||||
|
||||
VS_VERSION_INFO VERSIONINFO |
||||
FILEVERSION 1,0,0,1 |
||||
PRODUCTVERSION 1,0,0,1 |
||||
FILEFLAGSMASK 0x3fL |
||||
#ifdef _DEBUG |
||||
FILEFLAGS 0x1L |
||||
#else |
||||
FILEFLAGS 0x0L |
||||
#endif |
||||
FILEOS 0x40004L |
||||
FILETYPE 0x1L |
||||
FILESUBTYPE 0x0L |
||||
BEGIN |
||||
BLOCK "StringFileInfo" |
||||
BEGIN |
||||
BLOCK "0c0904b0" |
||||
BEGIN |
||||
VALUE "CompanyName", "PCSX2" |
||||
VALUE "FileDescription", "PCSX2" |
||||
VALUE "FileVersion", "2.0" |
||||
VALUE "InternalName", "updater.exe" |
||||
VALUE "LegalCopyright", "Copyright (C) 2022 PCSX2 Dev Team" |
||||
VALUE "OriginalFilename", "updater.exe" |
||||
VALUE "ProductName", "PCSX2 Update Installer" |
||||
VALUE "ProductVersion", "2.0" |
||||
END |
||||
END |
||||
BLOCK "VarFileInfo" |
||||
BEGIN |
||||
VALUE "Translation", 0xc09, 1200 |
||||
END |
||||
END |
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////// |
||||
// |
||||
// Icon |
||||
// |
||||
|
||||
// Icon with lowest ID value placed first to ensure application icon |
||||
// remains consistent on all systems. |
||||
IDI_ICON1 ICON "updater.ico" |
||||
|
||||
#endif // English (Australia) resources |
||||
///////////////////////////////////////////////////////////////////////////// |
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED |
||||
///////////////////////////////////////////////////////////////////////////// |
||||
// |
||||
// Generated from the TEXTINCLUDE 3 resource. |
||||
// |
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////// |
||||
#endif // not APSTUDIO_INVOKED |
||||
|
@ -0,0 +1,84 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
<Import Project="$(SolutionDir)common\vsprops\BaseProjectConfig.props" /> |
||||
<Import Project="$(SolutionDir)common\vsprops\WinSDK.props" /> |
||||
<PropertyGroup Label="Globals"> |
||||
<ProjectGuid>{90BBDC04-CC44-4006-B893-06A4FEA8ED47}</ProjectGuid> |
||||
</PropertyGroup> |
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> |
||||
<PropertyGroup Label="Configuration"> |
||||
<ConfigurationType>Application</ConfigurationType> |
||||
<CharacterSet>Unicode</CharacterSet> |
||||
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset> |
||||
<WholeProgramOptimization Condition="$(Configuration.Contains(Release))">true</WholeProgramOptimization> |
||||
<UseDebugLibraries Condition="$(Configuration.Contains(Debug))">true</UseDebugLibraries> |
||||
<UseDebugLibraries Condition="!$(Configuration.Contains(Debug))">false</UseDebugLibraries> |
||||
</PropertyGroup> |
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> |
||||
<ImportGroup Label="ExtensionSettings" /> |
||||
<ImportGroup Label="PropertySheets"> |
||||
<Import Project="$(SolutionDir)common\vsprops\common.props" /> |
||||
<Import Project="$(SolutionDir)common\vsprops\BaseProperties.props" /> |
||||
<Import Project="$(SolutionDir)common\vsprops\3rdpartyDeps.props" /> |
||||
<Import Condition="$(Configuration.Contains(Debug))" Project="$(SolutionDir)common\vsprops\CodeGen_Debug.props" /> |
||||
<Import Condition="$(Configuration.Contains(Devel))" Project="$(SolutionDir)common\vsprops\CodeGen_Devel.props" /> |
||||
<Import Condition="$(Configuration.Contains(Release))" Project="$(SolutionDir)common\vsprops\CodeGen_Release.props" /> |
||||
<Import Condition="!$(Configuration.Contains(Release))" Project="$(SolutionDir)common\vsprops\IncrementalLinking.props" /> |
||||
</ImportGroup> |
||||
<PropertyGroup Label="UserMacros" /> |
||||
<PropertyGroup> |
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> |
||||
<TargetName>updater$(BuildString)</TargetName> |
||||
</PropertyGroup> |
||||
<ItemDefinitionGroup> |
||||
<ClCompile> |
||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\lzma\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> |
||||
<AdditionalIncludeDirectories>$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> |
||||
<ExceptionHandling>Async</ExceptionHandling> |
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader> |
||||
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet> |
||||
<PreprocessorDefinitions>WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions> |
||||
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet> |
||||
<MinimalRebuild>false</MinimalRebuild> |
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation> |
||||
<ConformanceMode>true</ConformanceMode> |
||||
<AdditionalOptions>/Zc:__cplusplus /Zo /utf-8%(AdditionalOptions)</AdditionalOptions> |
||||
</ClCompile> |
||||
<Link> |
||||
<SubSystem>Windows</SubSystem> |
||||
<LargeAddressAware>Yes</LargeAddressAware> |
||||
</Link> |
||||
</ItemDefinitionGroup> |
||||
<ItemGroup> |
||||
<ProjectReference Include="$(SolutionDir)3rdparty\fmt\fmt.vcxproj"> |
||||
<Project>{449ad25e-424a-4714-babc-68706cdcc33b}</Project> |
||||
</ProjectReference> |
||||
<ProjectReference Include="$(SolutionDir)3rdparty\lzma\lzma.vcxproj"> |
||||
<Project>{a4323327-3f2b-4271-83d9-7f9a3c66b6b2}</Project> |
||||
</ProjectReference> |
||||
<ProjectReference Include="$(SolutionDir)common\common.vcxproj"> |
||||
<Project>{4639972e-424e-4e13-8b07-ca403c481346}</Project> |
||||
</ProjectReference> |
||||
</ItemGroup> |
||||
<ItemGroup> |
||||
<ClCompile Include="Updater.cpp" /> |
||||
<ClCompile Include="Windows\WindowsUpdater.cpp" /> |
||||
</ItemGroup> |
||||
<ItemGroup> |
||||
<ClInclude Include="SZErrors.h" /> |
||||
<ClInclude Include="Updater.h" /> |
||||
<ClInclude Include="UpdaterExtractor.h" /> |
||||
<ClInclude Include="Windows\resource.h" /> |
||||
</ItemGroup> |
||||
<ItemGroup> |
||||
<Manifest Include="Windows\updater.manifest" /> |
||||
</ItemGroup> |
||||
<ItemGroup> |
||||
<ResourceCompile Include="Windows\updater.rc" /> |
||||
</ItemGroup> |
||||
<ItemGroup> |
||||
<Image Include="Windows\updater.ico" /> |
||||
</ItemGroup> |
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> |
||||
<ImportGroup Label="ExtensionTargets" /> |
||||
</Project> |
@ -0,0 +1,37 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
<ItemGroup> |
||||
<ClCompile Include="Updater.cpp" /> |
||||
<ClCompile Include="Windows\WindowsUpdater.cpp"> |
||||
<Filter>Windows</Filter> |
||||
</ClCompile> |
||||
</ItemGroup> |
||||
<ItemGroup> |
||||
<ClInclude Include="Updater.h" /> |
||||
<ClInclude Include="SZErrors.h" /> |
||||
<ClInclude Include="Windows\resource.h"> |
||||
<Filter>Windows</Filter> |
||||
</ClInclude> |
||||
<ClInclude Include="UpdaterExtractor.h" /> |
||||
</ItemGroup> |
||||
<ItemGroup> |
||||
<Filter Include="Windows"> |
||||
<UniqueIdentifier>{bdeccfd9-a573-4076-b112-e013516c30c8}</UniqueIdentifier> |
||||
</Filter> |
||||
</ItemGroup> |
||||
<ItemGroup> |
||||
<Image Include="Windows\updater.ico"> |
||||
<Filter>Windows</Filter> |
||||
</Image> |
||||
</ItemGroup> |
||||
<ItemGroup> |
||||
<Manifest Include="Windows\updater.manifest"> |
||||
<Filter>Windows</Filter> |
||||
</Manifest> |
||||
</ItemGroup> |
||||
<ItemGroup> |
||||
<ResourceCompile Include="Windows\updater.rc"> |
||||
<Filter>Windows</Filter> |
||||
</ResourceCompile> |
||||
</ItemGroup> |
||||
</Project> |
Loading…
Reference in new issue