Add update installer program (Windows only for now)

This commit is contained in:
Connor McLaughlin 2020-08-06 19:33:33 +10:00
parent 045e6dff61
commit 12bef7caa2
10 changed files with 872 additions and 2 deletions

View File

@ -61,8 +61,8 @@ Binaries of DuckStation for Windows 64-bit, x86_64 Linux x86_64 (in AppImage for
**Windows 10 is the only version of Windows supported by the developer.** Windows 7/8 may work, but is not supported. I am aware some users are still using Windows 7, but it is no longer supported by Microsoft and too much effort to get running on modern hardware. Game bugs are unlikely to be affected by the operating system, however performance issues should be verified on Windows 10 before reporting.
To download:
- Go to https://github.com/stenzek/duckstation/releases/tag/latest, and download the Windows x64 build. This is a 7-Zip archive containing the prebuilt binary.
- Alternatively, direct download link: https://github.com/stenzek/duckstation/releases/download/latest/duckstation-windows-x64-release.7z
- Go to https://github.com/stenzek/duckstation/releases/tag/latest, and download the Windows x64 build. This is a zip archive containing the prebuilt binary.
- Alternatively, direct download link: https://github.com/stenzek/duckstation/releases/download/latest/duckstation-windows-x64-release.zip
- Extract the archive **to a subdirectory**. The archive has no root subdirectory, so extracting to the current directory will drop a bunch of files in your download directory if you do not extract to a subdirectory.
Once downloaded and extracted, you can launch the Qt frontend from `duckstation-qt-x64-ReleaseLTCG.exe`, or the SDL frontend from `duckstation-sdl-x64-ReleaseLTCG.exe`.

View File

@ -57,6 +57,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "glslang", "dep\glslang\glsl
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vulkan-loader", "dep\vulkan-loader\vulkan-loader.vcxproj", "{9C8DDEB0-2B8F-4F5F-BA86-127CDF27F035}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "updater", "src\updater\updater.vcxproj", "{32EEAF44-57F8-4C6C-A6F0-DE5667123DD5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@ -485,6 +487,22 @@ Global
{9C8DDEB0-2B8F-4F5F-BA86-127CDF27F035}.ReleaseLTCG|x64.Build.0 = ReleaseLTCG|x64
{9C8DDEB0-2B8F-4F5F-BA86-127CDF27F035}.ReleaseLTCG|x86.ActiveCfg = ReleaseLTCG|Win32
{9C8DDEB0-2B8F-4F5F-BA86-127CDF27F035}.ReleaseLTCG|x86.Build.0 = ReleaseLTCG|Win32
{32EEAF44-57F8-4C6C-A6F0-DE5667123DD5}.Debug|x64.ActiveCfg = Debug|x64
{32EEAF44-57F8-4C6C-A6F0-DE5667123DD5}.Debug|x64.Build.0 = Debug|x64
{32EEAF44-57F8-4C6C-A6F0-DE5667123DD5}.Debug|x86.ActiveCfg = Debug|Win32
{32EEAF44-57F8-4C6C-A6F0-DE5667123DD5}.Debug|x86.Build.0 = Debug|Win32
{32EEAF44-57F8-4C6C-A6F0-DE5667123DD5}.DebugFast|x64.ActiveCfg = DebugFast|x64
{32EEAF44-57F8-4C6C-A6F0-DE5667123DD5}.DebugFast|x64.Build.0 = DebugFast|x64
{32EEAF44-57F8-4C6C-A6F0-DE5667123DD5}.DebugFast|x86.ActiveCfg = DebugFast|Win32
{32EEAF44-57F8-4C6C-A6F0-DE5667123DD5}.DebugFast|x86.Build.0 = DebugFast|Win32
{32EEAF44-57F8-4C6C-A6F0-DE5667123DD5}.Release|x64.ActiveCfg = Release|x64
{32EEAF44-57F8-4C6C-A6F0-DE5667123DD5}.Release|x64.Build.0 = Release|x64
{32EEAF44-57F8-4C6C-A6F0-DE5667123DD5}.Release|x86.ActiveCfg = Release|Win32
{32EEAF44-57F8-4C6C-A6F0-DE5667123DD5}.Release|x86.Build.0 = Release|Win32
{32EEAF44-57F8-4C6C-A6F0-DE5667123DD5}.ReleaseLTCG|x64.ActiveCfg = ReleaseLTCG|x64
{32EEAF44-57F8-4C6C-A6F0-DE5667123DD5}.ReleaseLTCG|x64.Build.0 = ReleaseLTCG|x64
{32EEAF44-57F8-4C6C-A6F0-DE5667123DD5}.ReleaseLTCG|x86.ActiveCfg = ReleaseLTCG|Win32
{32EEAF44-57F8-4C6C-A6F0-DE5667123DD5}.ReleaseLTCG|x86.Build.0 = ReleaseLTCG|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -8,6 +8,9 @@ add_subdirectory(scmversion)
if(NOT BUILD_LIBRETRO_CORE)
add_subdirectory(common-tests)
if(WIN32)
add_subdirectory(updater)
endif()
endif()
if(ANDROID OR BUILD_SDL_FRONTEND OR BUILD_QT_FRONTEND OR BUILD_LIBRETRO_CORE)

View File

@ -0,0 +1,12 @@
add_executable(updater
updater.cpp
updater.h
)
target_link_libraries(updater PRIVATE common minizip zlib)
if(WIN32)
target_sources(updater PRIVATE
win32_main.cpp
)
endif()

289
src/updater/updater.cpp Normal file
View File

@ -0,0 +1,289 @@
#include "updater.h"
#include "common/file_system.h"
#include "common/log.h"
#include "common/minizip_helpers.h"
#include "common/string_util.h"
#include "common/win32_progress_callback.h"
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <memory>
#include <set>
#include <string>
#include <vector>
Updater::Updater(ProgressCallback* progress) : m_progress(progress)
{
progress->SetTitle("DuckStation Update Installer");
}
Updater::~Updater()
{
if (m_zf)
unzClose(m_zf);
}
bool Updater::Initialize(std::string destination_directory)
{
m_destination_directory = std::move(destination_directory);
m_staging_directory = StringUtil::StdStringFromFormat("%s%c%s", m_destination_directory.c_str(),
FS_OSPATH_SEPERATOR_CHARACTER, "UPDATE_STAGING");
m_progress->DisplayFormattedDebugMessage("Destination directory: '%s'", m_destination_directory.c_str());
m_progress->DisplayFormattedDebugMessage("Staging directory: '%s'", m_staging_directory.c_str());
// log everything to file as well
Log::SetFileOutputParams(true, StringUtil::StdStringFromFormat("%s%cupdater.log", m_destination_directory.c_str(),
FS_OSPATH_SEPERATOR_CHARACTER)
.c_str());
return true;
}
bool Updater::OpenUpdateZip(const char* path)
{
m_zf = MinizipHelpers::OpenUnzFile(path);
if (!m_zf)
return false;
return ParseZip();
}
bool Updater::ParseZip()
{
if (unzGoToFirstFile(m_zf) != UNZ_OK)
{
m_progress->ModalError("unzGoToFirstFile() failed");
return {};
}
for (;;)
{
char zip_filename_buffer[256];
unz_file_info64 file_info;
if (unzGetCurrentFileInfo64(m_zf, &file_info, zip_filename_buffer, sizeof(zip_filename_buffer), nullptr, 0, nullptr,
0) != UNZ_OK)
{
m_progress->ModalError("unzGetCurrentFileInfo64() failed");
return false;
}
FileToUpdate entry;
entry.original_zip_filename = zip_filename_buffer;
// replace forward slashes with backslashes
size_t len = std::strlen(zip_filename_buffer);
for (size_t i = 0; i < len; i++)
{
if (zip_filename_buffer[i] == '/' || zip_filename_buffer[i] == '\\')
zip_filename_buffer[i] = FS_OSPATH_SEPERATOR_CHARACTER;
}
// should never have a leading slash. just in case.
while (zip_filename_buffer[0] == FS_OSPATH_SEPERATOR_CHARACTER)
std::memmove(&zip_filename_buffer[1], &zip_filename_buffer[0], --len);
// skip directories (we sort them out later)
if (len > 0 && zip_filename_buffer[len - 1] != FS_OSPATH_SEPERATOR_CHARACTER)
{
// skip updater itself, since it was already pre-extracted.
if (StringUtil::Strcasecmp(zip_filename_buffer, "updater.exe") == 0)
continue;
entry.destination_filename = zip_filename_buffer;
m_progress->DisplayFormattedDebugMessage("Found file in zip: '%s'", entry.destination_filename.c_str());
m_update_paths.push_back(std::move(entry));
}
int res = unzGoToNextFile(m_zf);
if (res == UNZ_END_OF_LIST_OF_FILE)
break;
if (res != UNZ_OK)
{
m_progress->ModalError("unzGoToNextFile() failed");
return false;
}
}
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_SEPERATOR_CHARACTER)
{
std::string dir(ftu.destination_filename.begin(), ftu.destination_filename.begin() + i);
while (!dir.empty() && dir[dir.length() - 1] == FS_OSPATH_SEPERATOR_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");
FileSystem::DeleteDirectory(m_staging_directory.c_str(), true);
if (FileSystem::DirectoryExists(m_staging_directory.c_str()))
{
m_progress->ModalError("Failed to remove old staging directory");
return false;
}
}
if (!FileSystem::CreateDirectory(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%c%s", m_staging_directory.c_str(),
FS_OSPATH_SEPERATOR_CHARACTER, subdir.c_str());
if (!FileSystem::CreateDirectory(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);
for (const FileToUpdate& ftu : m_update_paths)
{
m_progress->SetFormattedStatusText("Extracting '%s'...", ftu.original_zip_filename.c_str());
if (unzLocateFile(m_zf, ftu.original_zip_filename.c_str(), 0) != UNZ_OK)
{
m_progress->DisplayFormattedModalError("Unable to locate file '%s' in zip", ftu.original_zip_filename.c_str());
return false;
}
else if (unzOpenCurrentFile(m_zf) != UNZ_OK)
{
m_progress->DisplayFormattedModalError("Failed to open file '%s' in zip", ftu.original_zip_filename.c_str());
return false;
}
m_progress->DisplayFormattedInformation("Extracting '%s'...", ftu.destination_filename.c_str());
const std::string destination_file = StringUtil::StdStringFromFormat(
"%s%c%s", m_staging_directory.c_str(), FS_OSPATH_SEPERATOR_CHARACTER, 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());
unzCloseCurrentFile(m_zf);
return false;
}
static constexpr u32 CHUNK_SIZE = 4096;
u8 buffer[CHUNK_SIZE];
for (;;)
{
int byte_count = unzReadCurrentFile(m_zf, buffer, CHUNK_SIZE);
if (byte_count < 0)
{
m_progress->DisplayFormattedModalError("Failed to read file '%s' from zip", ftu.original_zip_filename.c_str());
std::fclose(fp);
FileSystem::DeleteFile(destination_file.c_str());
unzCloseCurrentFile(m_zf);
return false;
}
else if (byte_count == 0)
{
// end of file
break;
}
if (std::fwrite(buffer, static_cast<size_t>(byte_count), 1, fp) != 1)
{
m_progress->DisplayFormattedModalError("Failed to write to file '%s'", destination_file.c_str());
std::fclose(fp);
FileSystem::DeleteFile(destination_file.c_str());
unzCloseCurrentFile(m_zf);
return false;
}
}
std::fclose(fp);
unzCloseCurrentFile(m_zf);
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%c%s", m_destination_directory.c_str(),
FS_OSPATH_SEPERATOR_CHARACTER, subdir.c_str());
if (!FileSystem::DirectoryExists(dest_subdir.c_str()) && !FileSystem::CreateDirectory(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%c%s", m_staging_directory.c_str(), FS_OSPATH_SEPERATOR_CHARACTER, ftu.destination_filename.c_str());
const std::string dest_file_name = StringUtil::StdStringFromFormat(
"%s%c%s", m_destination_directory.c_str(), FS_OSPATH_SEPERATOR_CHARACTER, ftu.destination_filename.c_str());
m_progress->DisplayFormattedDebugMessage("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 (!FileSystem::DeleteDirectory(m_staging_directory.c_str(), true))
m_progress->DisplayFormattedError("Failed to remove staging directory '%s'", m_staging_directory.c_str());
}

38
src/updater/updater.h Normal file
View File

@ -0,0 +1,38 @@
#pragma once
#include "common/progress_callback.h"
#include "unzip.h"
#include <string>
#include <vector>
class Updater
{
public:
Updater(ProgressCallback* progress);
~Updater();
bool Initialize(std::string destination_directory);
bool OpenUpdateZip(const char* path);
bool PrepareStagingDirectory();
bool StageUpdate();
bool CommitUpdate();
void CleanupStagingDirectory();
private:
struct FileToUpdate
{
std::string original_zip_filename;
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;
unzFile m_zf = nullptr;
};

View File

@ -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>DuckStation Updater</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
</assembly>

382
src/updater/updater.vcxproj Normal file
View File

@ -0,0 +1,382 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="DebugFast|Win32">
<Configuration>DebugFast</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="DebugFast|x64">
<Configuration>DebugFast</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="ReleaseLTCG|Win32">
<Configuration>ReleaseLTCG</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="ReleaseLTCG|x64">
<Configuration>ReleaseLTCG</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\dep\minizip\minizip.vcxproj">
<Project>{8bda439c-6358-45fb-9994-2ff083babe06}</Project>
</ProjectReference>
<ProjectReference Include="..\..\dep\zlib\zlib.vcxproj">
<Project>{7ff9fdb9-d504-47db-a16a-b08071999620}</Project>
</ProjectReference>
<ProjectReference Include="..\common\common.vcxproj">
<Project>{ee054e08-3799-4a59-a422-18259c105ffd}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ClCompile Include="win32_main.cpp" />
<ClCompile Include="updater.cpp" />
</ItemGroup>
<ItemGroup>
<Manifest Include="updater.manifest" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="updater.h" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{32EEAF44-57F8-4C6C-A6F0-DE5667123DD5}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>updater</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
<SpectreMitigation>false</SpectreMitigation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
<SpectreMitigation>false</SpectreMitigation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
<SpectreMitigation>false</SpectreMitigation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
<SpectreMitigation>false</SpectreMitigation>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)bin\$(Platform)\</OutDir>
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)bin\$(Platform)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)bin\$(Platform)\</OutDir>
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'">
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)bin\$(Platform)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>$(SolutionDir)bin\$(Platform)\</OutDir>
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)bin\$(Platform)\</OutDir>
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
<OutDir>$(SolutionDir)bin\$(Platform)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|x64'">
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)bin\$(Platform)\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<MinimalRebuild>false</MinimalRebuild>
<LanguageStandard>stdcpp17</LanguageStandard>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>d3d11.lib;dxgi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<MinimalRebuild>false</MinimalRebuild>
<LanguageStandard>stdcpp17</LanguageStandard>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>d3d11.lib;dxgi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<MinimalRebuild>false</MinimalRebuild>
<LanguageStandard>stdcpp17</LanguageStandard>
<SupportJustMyCode>false</SupportJustMyCode>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>d3d11.lib;dxgi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<MinimalRebuild>false</MinimalRebuild>
<LanguageStandard>stdcpp17</LanguageStandard>
<SupportJustMyCode>false</SupportJustMyCode>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>d3d11.lib;dxgi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<WholeProgramOptimization>false</WholeProgramOptimization>
<LanguageStandard>stdcpp17</LanguageStandard>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>d3d11.lib;dxgi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|Win32'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<WholeProgramOptimization>true</WholeProgramOptimization>
<LanguageStandard>stdcpp17</LanguageStandard>
<OmitFramePointers>true</OmitFramePointers>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>d3d11.lib;dxgi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<WholeProgramOptimization>false</WholeProgramOptimization>
<LanguageStandard>stdcpp17</LanguageStandard>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>d3d11.lib;dxgi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|x64'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<WholeProgramOptimization>true</WholeProgramOptimization>
<LanguageStandard>stdcpp17</LanguageStandard>
<OmitFramePointers>true</OmitFramePointers>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>d3d11.lib;dxgi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,13 @@
<?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="win32_main.cpp" />
</ItemGroup>
<ItemGroup>
<Manifest Include="updater.manifest" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="updater.h" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,93 @@
#include "common/file_system.h"
#include "common/log.h"
#include "common/string_util.h"
#include "common/win32_progress_callback.h"
#include "common/windows_headers.h"
#include "updater.h"
#include <shellapi.h>
static void WaitForProcessToExit(int process_id)
{
HANDLE hProcess = OpenProcess(SYNCHRONIZE, FALSE, process_id);
if (!hProcess)
return;
WaitForSingleObject(hProcess, INFINITE);
CloseHandle(hProcess);
}
int wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd)
{
Log::SetConsoleOutputParams(true);
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.");
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::string program_to_launch = StringUtil::WideStringToUTF8String(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;
}
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'...", program_to_launch.c_str());
ShellExecuteA(nullptr, "open", program_to_launch.c_str(), nullptr, nullptr, SW_SHOWNORMAL);
return 0;
}