mirror of
https://github.com/alterware/aw-installer.git
synced 2025-01-14 01:09:02 +00:00
init
This commit is contained in:
commit
095e3e8b42
7
.github/dependabot.yml
vendored
Normal file
7
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: gitsubmodule
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 10
|
162
.github/workflows/build.yml
vendored
Normal file
162
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,162 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "*"
|
||||
pull_request:
|
||||
branches:
|
||||
- "*"
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
env:
|
||||
PREMAKE_VERSION: "5.0.0-beta2"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-win:
|
||||
name: Build Windows
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
configuration:
|
||||
- debug
|
||||
- release
|
||||
arch:
|
||||
- x64
|
||||
include:
|
||||
- arch: x64
|
||||
platform: x64
|
||||
steps:
|
||||
- name: Check out files
|
||||
uses: actions/checkout@main
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
# NOTE - If LFS ever starts getting used during builds, switch this to true!
|
||||
lfs: false
|
||||
|
||||
- name: Add msbuild to PATH
|
||||
uses: microsoft/setup-msbuild@main
|
||||
|
||||
- name: Install Premake5
|
||||
uses: abel0b/setup-premake@v2.3
|
||||
with:
|
||||
version: ${{ env.PREMAKE_VERSION }}
|
||||
|
||||
- name: Generate project files
|
||||
run: premake5 vs2022
|
||||
|
||||
- name: Set up problem matching
|
||||
uses: ammaraskar/msvc-problem-matcher@master
|
||||
|
||||
- name: Build ${{matrix.arch}} ${{matrix.configuration}} binaries
|
||||
run: msbuild /m /v:minimal /p:Configuration=${{matrix.configuration}} /p:Platform=${{matrix.platform}} build/aw-installer.sln
|
||||
|
||||
- name: Upload ${{matrix.arch}} ${{matrix.configuration}} binaries
|
||||
uses: actions/upload-artifact@main
|
||||
with:
|
||||
name: windows-${{matrix.arch}}-${{matrix.configuration}}
|
||||
path: |
|
||||
build/bin/${{matrix.arch}}/${{matrix.configuration}}/aw-installer.exe
|
||||
build/bin/${{matrix.arch}}/${{matrix.configuration}}/aw-installer.pdb
|
||||
|
||||
build-linux:
|
||||
name: Build Linux
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
configuration:
|
||||
- debug
|
||||
- release
|
||||
arch:
|
||||
- x64
|
||||
steps:
|
||||
- name: Check out files
|
||||
uses: actions/checkout@main
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
# NOTE - If LFS ever starts getting used during builds, switch this to true!
|
||||
lfs: false
|
||||
|
||||
- name: Install dependencies (x64)
|
||||
if: matrix.arch == 'x64'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install libcurl4-gnutls-dev -y
|
||||
|
||||
- name: Install Premake5
|
||||
uses: abel0b/setup-premake@v2.3
|
||||
with:
|
||||
version: ${{ env.PREMAKE_VERSION }}
|
||||
|
||||
- name: Generate project files
|
||||
run: premake5 --cc=clang gmake2
|
||||
|
||||
- name: Set up problem matching
|
||||
uses: ammaraskar/gcc-problem-matcher@master
|
||||
|
||||
- name: Build ${{matrix.arch}} ${{matrix.configuration}} binaries
|
||||
run: |
|
||||
pushd build
|
||||
make config=${{matrix.configuration}}_${{matrix.arch}} -j$(nproc)
|
||||
env:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
|
||||
- name: Upload ${{matrix.arch}} ${{matrix.configuration}} binaries
|
||||
uses: actions/upload-artifact@main
|
||||
with:
|
||||
name: linux-${{matrix.arch}}-${{matrix.configuration}}
|
||||
path: |
|
||||
build/bin/${{matrix.arch}}/${{matrix.configuration}}/aw-installer
|
||||
|
||||
build-macos:
|
||||
name: Build macOS
|
||||
runs-on: macos-13
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
configuration:
|
||||
- debug
|
||||
- release
|
||||
arch:
|
||||
- x64
|
||||
- arm64
|
||||
steps:
|
||||
- name: Check out files
|
||||
uses: actions/checkout@main
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
# NOTE - If LFS ever starts getting used during builds, switch this to true!
|
||||
lfs: false
|
||||
|
||||
- name: Install Premake5
|
||||
uses: abel0b/setup-premake@v2.3
|
||||
with:
|
||||
version: ${{ env.PREMAKE_VERSION }}
|
||||
|
||||
- name: Generate project files
|
||||
run: premake5 gmake2
|
||||
|
||||
- name: Set up problem matching
|
||||
uses: ammaraskar/gcc-problem-matcher@master
|
||||
|
||||
- name: Build ${{matrix.arch}} ${{matrix.configuration}} binaries
|
||||
run: |
|
||||
pushd build
|
||||
make config=${{matrix.configuration}}_${{matrix.arch}} -j$(sysctl -n hw.logicalcpu)
|
||||
|
||||
- name: Upload ${{matrix.arch}} ${{matrix.configuration}} binaries
|
||||
uses: actions/upload-artifact@v3.1.3
|
||||
with:
|
||||
name: macos-${{matrix.arch}}-${{matrix.configuration}}
|
||||
path: |
|
||||
build/bin/${{matrix.arch}}/${{matrix.configuration}}/aw-installer
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Build results
|
||||
build
|
13
.gitmodules
vendored
Normal file
13
.gitmodules
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
[submodule "deps/GSL"]
|
||||
path = deps/GSL
|
||||
url = https://github.com/microsoft/GSL.git
|
||||
[submodule "deps/curl"]
|
||||
path = deps/curl
|
||||
url = https://github.com/curl/curl.git
|
||||
branch = curl-8_5_0
|
||||
[submodule "deps/rapidjson"]
|
||||
path = deps/rapidjson
|
||||
url = https://github.com/Tencent/rapidjson.git
|
||||
[submodule "deps/zlib"]
|
||||
path = deps/zlib
|
||||
url = https://github.com/madler/zlib.git
|
29
LICENSE
Normal file
29
LICENSE
Normal file
@ -0,0 +1,29 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2024, AlterWare
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
24
README.md
Normal file
24
README.md
Normal file
@ -0,0 +1,24 @@
|
||||
[![build](https://github.com/alterware/aw-installer/workflows/Build/badge.svg)](https://github.com/alterware/aw-installer/actions)
|
||||
|
||||
|
||||
# AlterWare: Installer
|
||||
This is the tool we use to pull changes made from the release page of some of our clients and install it where we need to.
|
||||
|
||||
## Build
|
||||
- Install [Premake5](premake5-link) and add it to your system PATH
|
||||
- Clone this repository using [Git][git-link]
|
||||
- Update the submodules using ``git submodule update --init --recursive``
|
||||
- Run Premake with either of these two options ``premake5 vs2022`` (Windows) or ``premake5 gmake2`` (Linux/macOS)
|
||||
|
||||
**IMPORTANT**
|
||||
Requirements for Unix systems:
|
||||
- Compilation: Please use Clang as the preferred compiler
|
||||
- Dependencies: Ensure the LLVM C++ Standard library is installed
|
||||
- Alternative compilers: If you opt for a different compiler such as GCC, use the [Mold][mold-link] linker
|
||||
- Customization: Modifications to the Premake5.lua script may be required
|
||||
- Platform support: Details regarding supported platforms are available in [build.yml][build-link]
|
||||
|
||||
[premake5-link]: https://premake.github.io
|
||||
[git-link]: https://git-scm.com
|
||||
[mold-link]: https://github.com/rui314/mold
|
||||
[build-link]: https://github.com/alterware/master-server/blob/master/.github/workflows/build.yml
|
1
deps/GSL
vendored
Submodule
1
deps/GSL
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit e64c97fc2cfc11992098bb38eda932de275e3f4d
|
1
deps/curl
vendored
Submodule
1
deps/curl
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 7161cb17c01dcff1dc5bf89a18437d9d729f1ecd
|
75
deps/premake/curl.lua
vendored
Normal file
75
deps/premake/curl.lua
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
curl = {
|
||||
source = path.join(dependencies.basePath, "curl"),
|
||||
}
|
||||
|
||||
function curl.import()
|
||||
links { "curl" }
|
||||
|
||||
filter "toolset:msc*"
|
||||
links { "Crypt32.lib" }
|
||||
filter {}
|
||||
|
||||
curl.includes()
|
||||
end
|
||||
|
||||
function curl.includes()
|
||||
filter "toolset:msc*"
|
||||
includedirs {
|
||||
path.join(curl.source, "include"),
|
||||
}
|
||||
|
||||
defines {
|
||||
"CURL_STRICTER",
|
||||
"CURL_STATICLIB",
|
||||
"CURL_DISABLE_LDAP",
|
||||
}
|
||||
filter {}
|
||||
end
|
||||
|
||||
function curl.project()
|
||||
if not os.istarget("windows") then
|
||||
return
|
||||
end
|
||||
|
||||
project "curl"
|
||||
language "C"
|
||||
|
||||
curl.includes()
|
||||
|
||||
includedirs {
|
||||
path.join(curl.source, "lib"),
|
||||
}
|
||||
|
||||
files {
|
||||
path.join(curl.source, "lib/**.c"),
|
||||
path.join(curl.source, "lib/**.h"),
|
||||
}
|
||||
|
||||
defines {
|
||||
"BUILDING_LIBCURL",
|
||||
}
|
||||
|
||||
filter "toolset:msc*"
|
||||
|
||||
defines {
|
||||
"USE_SCHANNEL",
|
||||
"USE_WINDOWS_SSPI",
|
||||
"USE_THREADS_WIN32",
|
||||
}
|
||||
|
||||
filter {}
|
||||
|
||||
filter "toolset:not msc*"
|
||||
|
||||
defines {
|
||||
"USE_GNUTLS",
|
||||
"USE_THREADS_POSIX",
|
||||
}
|
||||
|
||||
filter {}
|
||||
|
||||
warnings "Off"
|
||||
kind "StaticLib"
|
||||
end
|
||||
|
||||
table.insert(dependencies, curl)
|
19
deps/premake/gsl.lua
vendored
Normal file
19
deps/premake/gsl.lua
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
gsl = {
|
||||
source = path.join(dependencies.basePath, "GSL"),
|
||||
}
|
||||
|
||||
function gsl.import()
|
||||
gsl.includes()
|
||||
end
|
||||
|
||||
function gsl.includes()
|
||||
includedirs {
|
||||
path.join(gsl.source, "include")
|
||||
}
|
||||
end
|
||||
|
||||
function gsl.project()
|
||||
|
||||
end
|
||||
|
||||
table.insert(dependencies, gsl)
|
50
deps/premake/minizip.lua
vendored
Normal file
50
deps/premake/minizip.lua
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
minizip = {
|
||||
source = path.join(dependencies.basePath, "zlib/contrib/minizip"),
|
||||
}
|
||||
|
||||
function minizip.import()
|
||||
links { "minizip" }
|
||||
zlib.import()
|
||||
minizip.includes()
|
||||
end
|
||||
|
||||
function minizip.includes()
|
||||
includedirs {
|
||||
minizip.source
|
||||
}
|
||||
|
||||
zlib.includes()
|
||||
end
|
||||
|
||||
function minizip.project()
|
||||
project "minizip"
|
||||
language "C"
|
||||
cdialect "C89"
|
||||
|
||||
minizip.includes()
|
||||
|
||||
files {
|
||||
path.join(minizip.source, "*.h"),
|
||||
path.join(minizip.source, "*.c"),
|
||||
}
|
||||
|
||||
filter "system:not windows"
|
||||
removefiles {
|
||||
path.join(minizip.source, "iowin32.c"),
|
||||
}
|
||||
filter {}
|
||||
|
||||
removefiles {
|
||||
path.join(minizip.source, "miniunz.c"),
|
||||
path.join(minizip.source, "minizip.c"),
|
||||
}
|
||||
|
||||
filter { "system:windows" }
|
||||
defines "_CRT_SECURE_NO_DEPRECATE"
|
||||
filter {}
|
||||
|
||||
warnings "Off"
|
||||
kind "StaticLib"
|
||||
end
|
||||
|
||||
table.insert(dependencies, minizip)
|
20
deps/premake/rapidjson.lua
vendored
Normal file
20
deps/premake/rapidjson.lua
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
rapidjson = {
|
||||
source = path.join(dependencies.basePath, "rapidjson"),
|
||||
}
|
||||
|
||||
function rapidjson.import()
|
||||
defines{"RAPIDJSON_HAS_STDSTRING"}
|
||||
rapidjson.includes()
|
||||
end
|
||||
|
||||
function rapidjson.includes()
|
||||
includedirs {
|
||||
path.join(rapidjson.source, "include"),
|
||||
}
|
||||
end
|
||||
|
||||
function rapidjson.project()
|
||||
|
||||
end
|
||||
|
||||
table.insert(dependencies, rapidjson)
|
40
deps/premake/zlib.lua
vendored
Normal file
40
deps/premake/zlib.lua
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
zlib = {
|
||||
source = path.join(dependencies.basePath, "zlib"),
|
||||
}
|
||||
|
||||
function zlib.import()
|
||||
links { "zlib" }
|
||||
zlib.includes()
|
||||
end
|
||||
|
||||
function zlib.includes()
|
||||
includedirs {
|
||||
zlib.source
|
||||
}
|
||||
|
||||
defines {
|
||||
"ZLIB_CONST",
|
||||
}
|
||||
end
|
||||
|
||||
function zlib.project()
|
||||
project "zlib"
|
||||
language "C"
|
||||
cdialect "C89"
|
||||
|
||||
zlib.includes()
|
||||
|
||||
files {
|
||||
path.join(zlib.source, "*.h"),
|
||||
path.join(zlib.source, "*.c"),
|
||||
}
|
||||
|
||||
filter { "system:windows" }
|
||||
defines "_CRT_SECURE_NO_DEPRECATE"
|
||||
filter {}
|
||||
|
||||
warnings "Off"
|
||||
kind "StaticLib"
|
||||
end
|
||||
|
||||
table.insert(dependencies, zlib)
|
1
deps/rapidjson
vendored
Submodule
1
deps/rapidjson
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 6089180ecb704cb2b136777798fa1be303618975
|
1
deps/zlib
vendored
Submodule
1
deps/zlib
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 643e17b7498d12ab8d15565662880579692f769d
|
142
premake5.lua
Normal file
142
premake5.lua
Normal file
@ -0,0 +1,142 @@
|
||||
dependencies = {
|
||||
basePath = "./deps"
|
||||
}
|
||||
|
||||
function dependencies.load()
|
||||
dir = path.join(dependencies.basePath, "premake/*.lua")
|
||||
deps = os.matchfiles(dir)
|
||||
|
||||
for i, dep in pairs(deps) do
|
||||
dep = dep:gsub(".lua", "")
|
||||
require(dep)
|
||||
end
|
||||
end
|
||||
|
||||
function dependencies.imports()
|
||||
for i, proj in pairs(dependencies) do
|
||||
if type(i) == 'number' then
|
||||
proj.import()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function dependencies.projects()
|
||||
for i, proj in pairs(dependencies) do
|
||||
if type(i) == 'number' then
|
||||
proj.project()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
dependencies.load()
|
||||
|
||||
workspace "aw-installer"
|
||||
startproject "aw-installer"
|
||||
location "./build"
|
||||
objdir "%{wks.location}/obj"
|
||||
targetdir "%{wks.location}/bin/%{cfg.platform}/%{cfg.buildcfg}"
|
||||
|
||||
configurations {"debug", "release"}
|
||||
|
||||
language "C++"
|
||||
cppdialect "C++20"
|
||||
|
||||
if os.istarget("darwin") then
|
||||
platforms {"x64", "arm64"}
|
||||
else
|
||||
platforms {"x86", "x64"}
|
||||
end
|
||||
|
||||
filter "platforms:x86"
|
||||
architecture "x86"
|
||||
filter {}
|
||||
|
||||
filter "platforms:x64"
|
||||
architecture "x86_64"
|
||||
filter {}
|
||||
|
||||
filter "platforms:arm64"
|
||||
architecture "ARM64"
|
||||
filter {}
|
||||
|
||||
symbols "On"
|
||||
staticruntime "On"
|
||||
editandcontinue "Off"
|
||||
warnings "Extra"
|
||||
characterset "ASCII"
|
||||
|
||||
filter { "system:linux", "system:macosx" }
|
||||
buildoptions "-pthread"
|
||||
linkoptions "-pthread"
|
||||
filter {}
|
||||
|
||||
if os.istarget("linux") then
|
||||
filter { "toolset:clang*" }
|
||||
buildoptions "-stdlib=libc++"
|
||||
linkoptions "-stdlib=libc++"
|
||||
|
||||
-- always try to use lld. LD or Gold will not work
|
||||
linkoptions "-fuse-ld=lld"
|
||||
filter {}
|
||||
end
|
||||
|
||||
filter { "system:macosx", "platforms:arm64" }
|
||||
buildoptions "-arch arm64"
|
||||
linkoptions "-arch arm64"
|
||||
filter {}
|
||||
|
||||
if _OPTIONS["dev-build"] then
|
||||
defines {"DEV_BUILD"}
|
||||
end
|
||||
|
||||
if os.getenv("CI") then
|
||||
defines "CI"
|
||||
end
|
||||
|
||||
flags {"NoIncrementalLink", "NoMinimalRebuild", "MultiProcessorCompile", "No64BitChecks"}
|
||||
|
||||
filter "configurations:Release"
|
||||
optimize "Size"
|
||||
defines "NDEBUG"
|
||||
flags "FatalCompileWarnings"
|
||||
filter {}
|
||||
|
||||
filter "configurations:Debug"
|
||||
optimize "Debug"
|
||||
defines {"DEBUG", "_DEBUG"}
|
||||
filter {}
|
||||
|
||||
project "aw-installer"
|
||||
kind "ConsoleApp"
|
||||
language "C++"
|
||||
|
||||
pchheader "std_include.hpp"
|
||||
pchsource "src/std_include.cpp"
|
||||
|
||||
files {"./src/**.rc", "./src/**.hpp", "./src/**.cpp"}
|
||||
|
||||
includedirs {"./src", "%{prj.location}/src"}
|
||||
|
||||
filter "system:windows"
|
||||
files {
|
||||
"./src/**.rc",
|
||||
}
|
||||
filter {}
|
||||
|
||||
filter { "system:windows", "toolset:not msc*" }
|
||||
resincludedirs {
|
||||
"%{_MAIN_SCRIPT_DIR}/src"
|
||||
}
|
||||
filter {}
|
||||
|
||||
filter { "system:windows", "toolset:msc*" }
|
||||
resincludedirs {
|
||||
"$(ProjectDir)src"
|
||||
}
|
||||
filter {}
|
||||
|
||||
dependencies.imports()
|
||||
|
||||
|
||||
group "Dependencies"
|
||||
dependencies.projects()
|
248
src/console.cpp
Normal file
248
src/console.cpp
Normal file
@ -0,0 +1,248 @@
|
||||
#include "std_include.hpp"
|
||||
#include "console.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define COLOR_LOG_INFO 11//15
|
||||
#define COLOR_LOG_WARN 14
|
||||
#define COLOR_LOG_ERROR 12
|
||||
#define COLOR_LOG_DEBUG 15//7
|
||||
#else
|
||||
#define COLOR_LOG_INFO "\033[0;36m"
|
||||
#define COLOR_LOG_WARN "\033[0;33m"
|
||||
#define COLOR_LOG_ERROR "\033[0;31m"
|
||||
#define COLOR_LOG_DEBUG "\033[0m"
|
||||
#endif
|
||||
|
||||
namespace console
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::mutex signal_mutex;
|
||||
std::function<void()> signal_callback;
|
||||
|
||||
#ifdef _WIN32
|
||||
#define COLOR(win, posix) win
|
||||
using color_type = WORD;
|
||||
#else
|
||||
#define COLOR(win, posix) posix
|
||||
using color_type = const char*;
|
||||
#endif
|
||||
|
||||
const color_type color_array[] =
|
||||
{
|
||||
COLOR(0x8, "\033[0;90m"), // 0 - black
|
||||
COLOR(0xC, "\033[0;91m"), // 1 - red
|
||||
COLOR(0xA, "\033[0;92m"), // 2 - green
|
||||
COLOR(0xE, "\033[0;93m"), // 3 - yellow
|
||||
COLOR(0x9, "\033[0;94m"), // 4 - blue
|
||||
COLOR(0xB, "\033[0;96m"), // 5 - cyan
|
||||
COLOR(0xD, "\033[0;95m"), // 6 - pink
|
||||
COLOR(0xF, "\033[0;97m"), // 7 - white
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
BOOL WINAPI handler(const DWORD signal)
|
||||
{
|
||||
if (signal == CTRL_C_EVENT && signal_callback)
|
||||
{
|
||||
signal_callback();
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#else
|
||||
void handler(int signal)
|
||||
{
|
||||
if (signal == SIGINT && signal_callback)
|
||||
{
|
||||
signal_callback();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string format(va_list* ap, const char* message)
|
||||
{
|
||||
static thread_local char buffer[0x1000];
|
||||
|
||||
#ifdef _WIN32
|
||||
const int count = vsnprintf_s(buffer, _TRUNCATE, message, *ap);
|
||||
#else
|
||||
const int count = vsnprintf(buffer, sizeof(buffer), message, *ap);
|
||||
#endif
|
||||
|
||||
if (count < 0) return {};
|
||||
return {buffer, static_cast<size_t>(count)};
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
HANDLE get_console_handle()
|
||||
{
|
||||
return GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
}
|
||||
#endif
|
||||
|
||||
void set_color(const color_type color)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SetConsoleTextAttribute(get_console_handle(), color);
|
||||
#else
|
||||
printf("%s", color);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool apply_color(const std::string& data, const size_t index, const color_type base_color)
|
||||
{
|
||||
if (data[index] != '^' || (index + 1) >= data.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto code = data[index + 1] - '0';
|
||||
if (code < 0 || code > 11)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
code = std::min(code, 7); // Everything above white is white
|
||||
if (code == 7)
|
||||
{
|
||||
set_color(base_color);
|
||||
}
|
||||
else
|
||||
{
|
||||
set_color(color_array[code]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void print_colored(const std::string& line, const color_type base_color)
|
||||
{
|
||||
lock _{};
|
||||
set_color(base_color);
|
||||
|
||||
for (size_t i = 0; i < line.size(); ++i)
|
||||
{
|
||||
if (apply_color(line, i, base_color))
|
||||
{
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
putchar(line[i]);
|
||||
}
|
||||
|
||||
reset_color();
|
||||
}
|
||||
}
|
||||
|
||||
lock::lock()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
_lock_file(stdout);
|
||||
#else
|
||||
flockfile(stdout);
|
||||
#endif
|
||||
}
|
||||
|
||||
lock::~lock()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
_unlock_file(stdout);
|
||||
#else
|
||||
funlockfile(stdout);
|
||||
#endif
|
||||
}
|
||||
|
||||
void reset_color()
|
||||
{
|
||||
lock _{};
|
||||
#ifdef _WIN32
|
||||
SetConsoleTextAttribute(get_console_handle(), 7);
|
||||
#else
|
||||
printf("\033[0m");
|
||||
#endif
|
||||
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void info(const char* message, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, message);
|
||||
|
||||
const auto data = format(&ap, message);
|
||||
print_colored("[+] " + data + "\n", COLOR_LOG_INFO);
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void warn(const char* message, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, message);
|
||||
|
||||
const auto data = format(&ap, message);
|
||||
print_colored("[!] " + data + "\n", COLOR_LOG_WARN);
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void error(const char* message, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, message);
|
||||
|
||||
const auto data = format(&ap, message);
|
||||
print_colored("[-] " + data + "\n", COLOR_LOG_ERROR);
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void log(const char* message, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, message);
|
||||
|
||||
const auto data = format(&ap, message);
|
||||
print_colored("[*] " + data + "\n", COLOR_LOG_DEBUG);
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void set_title(const std::string& title)
|
||||
{
|
||||
lock _{};
|
||||
|
||||
#ifdef _WIN32
|
||||
SetConsoleTitleA(title.c_str());
|
||||
#else
|
||||
printf("\033]0;%s\007", title.c_str());
|
||||
fflush(stdout);
|
||||
#endif
|
||||
}
|
||||
|
||||
signal_handler::signal_handler(std::function<void()> callback)
|
||||
: std::lock_guard<std::mutex>(signal_mutex)
|
||||
{
|
||||
signal_callback = std::move(callback);
|
||||
|
||||
#ifdef _WIN32
|
||||
SetConsoleCtrlHandler(handler, TRUE);
|
||||
#else
|
||||
signal(SIGINT, handler);
|
||||
#endif
|
||||
}
|
||||
|
||||
signal_handler::~signal_handler()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SetConsoleCtrlHandler(handler, FALSE);
|
||||
#else
|
||||
signal(SIGINT, SIG_DFL);
|
||||
#endif
|
||||
|
||||
signal_callback = {};
|
||||
}
|
||||
}
|
32
src/console.hpp
Normal file
32
src/console.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
namespace console
|
||||
{
|
||||
class lock
|
||||
{
|
||||
public:
|
||||
lock();
|
||||
~lock();
|
||||
|
||||
lock(lock&&) = delete;
|
||||
lock(const lock&) = delete;
|
||||
lock& operator=(lock&&) = delete;
|
||||
lock& operator=(const lock&) = delete;
|
||||
};
|
||||
|
||||
void reset_color();
|
||||
|
||||
void info(const char* message, ...);
|
||||
void warn(const char* message, ...);
|
||||
void error(const char* message, ...);
|
||||
void log(const char* message, ...);
|
||||
|
||||
void set_title(const std::string& title);
|
||||
|
||||
class signal_handler : std::lock_guard<std::mutex>
|
||||
{
|
||||
public:
|
||||
signal_handler(std::function<void()> callback);
|
||||
~signal_handler();
|
||||
};
|
||||
}
|
53
src/main.cpp
Normal file
53
src/main.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
#include <std_include.hpp>
|
||||
|
||||
#include "console.hpp"
|
||||
|
||||
#include "updater/updater.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
int unsafe_main(std::string&& prog, std::vector<std::string>&& args)
|
||||
{
|
||||
// Parse command-line flags (only increment i for matching flags)
|
||||
for (auto i = args.begin(); i != args.end();)
|
||||
{
|
||||
if (*i == "-update-iw4x")
|
||||
{
|
||||
return updater::update_iw4x();
|
||||
}
|
||||
else
|
||||
{
|
||||
console::info("AlterWare Installer\n"
|
||||
"Usage: %s OPTIONS\n"
|
||||
" -update-iw4x\n",
|
||||
prog.data()
|
||||
);
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
int main(const int argc, char* argv[])
|
||||
{
|
||||
console::set_title("AlterWare Installer");
|
||||
console::log("AlterWare Installer");
|
||||
|
||||
try
|
||||
{
|
||||
std::string prog(argv[0]);
|
||||
std::vector<std::string> args;
|
||||
|
||||
args.reserve(argc - 1);
|
||||
args.assign(argv + 1, argv + argc);
|
||||
return unsafe_main(std::move(prog), std::move(args));
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
console::error("Fatal error: %s", ex.what());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
1
src/std_include.cpp
Normal file
1
src/std_include.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include <std_include.hpp>
|
74
src/std_include.hpp
Normal file
74
src/std_include.hpp
Normal file
@ -0,0 +1,74 @@
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#include <Windows.h>
|
||||
#include <WinSock2.h>
|
||||
#include <WS2tcpip.h>
|
||||
|
||||
#else
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define ZeroMemory(x, y) std::memset(x, 0, y)
|
||||
|
||||
#endif
|
||||
|
||||
// min and max is required by gdi, therefore NOMINMAX won't work
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif
|
||||
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <csignal>
|
||||
#include <cstdarg>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <queue>
|
||||
#include <ranges>
|
||||
#include <regex>
|
||||
#include <span>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <gsl/gsl>
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/prettywriter.h>
|
||||
#include <rapidjson/stringbuffer.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#pragma comment(lib, "ws2_32.lib")
|
||||
|
||||
#endif
|
||||
|
||||
using namespace std::literals;
|
258
src/updater/file_updater.cpp
Normal file
258
src/updater/file_updater.cpp
Normal file
@ -0,0 +1,258 @@
|
||||
#include <std_include.hpp>
|
||||
|
||||
#include <console.hpp>
|
||||
|
||||
#include "file_updater.hpp"
|
||||
|
||||
#include <utils/compression.hpp>
|
||||
#include <utils/http.hpp>
|
||||
#include <utils/io.hpp>
|
||||
|
||||
namespace updater
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::optional<std::string> get_release_tag(const std::string& release_url)
|
||||
{
|
||||
const auto release_info = utils::http::get_data(release_url);
|
||||
if (!release_info.has_value())
|
||||
{
|
||||
console::warn("Could not reach remote URL \"%s\"", release_url.c_str());
|
||||
return {};
|
||||
}
|
||||
|
||||
rapidjson::Document release_json{};
|
||||
|
||||
const rapidjson::ParseResult result = release_json.Parse(release_info.value());
|
||||
if (!result || !release_json.IsObject())
|
||||
{
|
||||
console::error("Could not parse remote JSON response from \"%s\"", release_url.c_str());
|
||||
return {};
|
||||
}
|
||||
|
||||
if (release_json.HasMember("tag_name") && release_json["tag_name"].IsString())
|
||||
{
|
||||
const auto* tag_name = release_json["tag_name"].GetString();
|
||||
return tag_name;
|
||||
}
|
||||
|
||||
console::error("Remote JSON response from \"%s\" does not contain the data we expected", release_url.c_str());
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
file_updater::file_updater(std::string name, std::filesystem::path base, std::filesystem::path out_name,
|
||||
std::filesystem::path version_file,
|
||||
std::string remote_tag, std::string remote_download)
|
||||
: name_(std::move(name))
|
||||
, base_(std::move(base))
|
||||
, out_name_(std::move(out_name))
|
||||
, version_file_(std::move(version_file))
|
||||
, remote_tag_(std::move(remote_tag))
|
||||
, remote_download_(std::move(remote_download))
|
||||
{
|
||||
}
|
||||
|
||||
bool file_updater::update_if_necessary() const
|
||||
{
|
||||
update_state update_state;
|
||||
|
||||
const auto local_version = this->read_local_revision_file();
|
||||
if (!this->does_require_update(update_state, local_version))
|
||||
{
|
||||
console::log("%s does not require an update", this->name_.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
console::info("Updating %s", this->name_.c_str());
|
||||
if (!this->update_file(this->remote_download_))
|
||||
{
|
||||
console::error("Update failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
this->cleanup_directories();
|
||||
|
||||
if (!this->deploy_files())
|
||||
{
|
||||
console::error("Unable to deploy files");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Do this last to make sure we don't ever create a version file when something failed
|
||||
this->create_version_file(update_state.latest_tag);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void file_updater::add_dir_to_clean(const std::string& dir)
|
||||
{
|
||||
this->cleanup_directories_.emplace_back(this->base_ / dir);
|
||||
}
|
||||
|
||||
void file_updater::add_file_to_skip(const std::string& file)
|
||||
{
|
||||
this->skip_files_.emplace_back(file);
|
||||
}
|
||||
|
||||
std::string file_updater::read_local_revision_file() const
|
||||
{
|
||||
const std::filesystem::path revision_file_path = this->version_file_;
|
||||
|
||||
std::string data;
|
||||
if (!utils::io::read_file(revision_file_path.string(), &data) || data.empty())
|
||||
{
|
||||
console::warn("Could not load \"%s\"", revision_file_path.string().c_str());
|
||||
return {};
|
||||
}
|
||||
|
||||
rapidjson::Document doc{};
|
||||
const rapidjson::ParseResult result = doc.Parse(data);
|
||||
if (!result || !doc.IsObject())
|
||||
{
|
||||
console::error("Could not parse \"%s\"", revision_file_path.string().c_str());
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!doc.HasMember("version") || !doc["version"].IsString())
|
||||
{
|
||||
console::error("\"%s\" contains invalid data", revision_file_path.string().c_str());
|
||||
return {};
|
||||
}
|
||||
|
||||
return doc["version"].GetString();
|
||||
}
|
||||
|
||||
bool file_updater::does_require_update(update_state& update_state, const std::string& local_version) const
|
||||
{
|
||||
console::info("Fetching tags from GitHub");
|
||||
|
||||
const auto raw_files_tag = get_release_tag(this->remote_tag_);
|
||||
if (!raw_files_tag.has_value())
|
||||
{
|
||||
console::warn("Failed to reach GitHub. Aborting the update");
|
||||
|
||||
update_state.requires_update = false;
|
||||
return update_state.requires_update;
|
||||
}
|
||||
|
||||
update_state.requires_update = local_version != raw_files_tag.value();
|
||||
update_state.latest_tag = raw_files_tag.value();
|
||||
|
||||
console::info("Got release tag \"%s\". Requires updating: %s", raw_files_tag.value().c_str(), update_state.requires_update ? "Yes" : "No");
|
||||
return update_state.requires_update;
|
||||
}
|
||||
|
||||
void file_updater::create_version_file(const std::string& revision_version) const
|
||||
{
|
||||
console::info("Creating version file \"%s\". Revision is \"%s\"", this->version_file_.c_str(), revision_version.c_str());
|
||||
|
||||
rapidjson::Document doc{};
|
||||
doc.SetObject();
|
||||
|
||||
doc.AddMember("version", revision_version, doc.GetAllocator());
|
||||
|
||||
rapidjson::StringBuffer buffer{};
|
||||
rapidjson::Writer<rapidjson::StringBuffer, rapidjson::Document::EncodingType, rapidjson::ASCII<>>
|
||||
writer(buffer);
|
||||
doc.Accept(writer);
|
||||
|
||||
const std::string json(buffer.GetString(), buffer.GetLength());
|
||||
if (utils::io::write_file(this->version_file_.string(), json))
|
||||
{
|
||||
console::info("File \"%s\" was created successfully", this->version_file_.string().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
console::error("Error while writing file \"%s\"", this->version_file_.string().c_str());
|
||||
}
|
||||
|
||||
bool file_updater::update_file(const std::string& url) const
|
||||
{
|
||||
console::info("Downloading %s", url.c_str());
|
||||
const auto data = utils::http::get_data(url, {});
|
||||
if (!data)
|
||||
{
|
||||
console::error("Failed to download %s", url.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data.value().empty())
|
||||
{
|
||||
console::error("The data buffer returned by Curl is empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Download the files in the working directory, move them later.
|
||||
const auto out_file = std::filesystem::current_path() / this->out_name_;
|
||||
|
||||
console::info("Writing file to \"%s\"", out_file.string().c_str());
|
||||
|
||||
if (!utils::io::write_file(out_file.string(), data.value(), false))
|
||||
{
|
||||
console::error("Error while writing file \"%s\"", out_file.string().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
console::info("Done updating file \"%s\"", out_file.string().c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Not a fan of using exceptions here. Once C++23 is more widespread I'd like to use <expected>
|
||||
bool file_updater::deploy_files() const
|
||||
{
|
||||
const auto out_dir = std::filesystem::current_path() / ".out";
|
||||
|
||||
assert(utils::io::file_exists(this->out_name_.string()));
|
||||
|
||||
// Always try to cleanup
|
||||
const auto _ = gsl::finally([this, &out_dir]() -> void
|
||||
{
|
||||
utils::io::remove_file(this->out_name_.string());
|
||||
|
||||
std::error_code ec;
|
||||
std::filesystem::remove_all(out_dir, ec);
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
utils::io::create_directory(out_dir);
|
||||
utils::compression::zip::archive::decompress(this->out_name_.string(), out_dir);
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
console::error("Get error \"%s\" while decompressing \"%s\"", ex.what(), this->out_name_.string().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
console::info("\"%s\" was decompressed. Removing files that must be skipped", this->out_name_.string().c_str());
|
||||
this->skip_files(out_dir);
|
||||
|
||||
console::info("Deploying files to \"%s\"", this->base_.string().c_str());
|
||||
|
||||
utils::io::copy_folder(out_dir, this->base_);
|
||||
return true;
|
||||
}
|
||||
|
||||
void file_updater::cleanup_directories() const
|
||||
{
|
||||
console::log("Cleaning up directories");
|
||||
std::for_each(this->cleanup_directories_.begin(), this->cleanup_directories_.end(), [](const auto& dir)
|
||||
{
|
||||
std::error_code ec;
|
||||
std::filesystem::remove_all(dir, ec);
|
||||
console::log("Removed directory \"%s\"", dir.string().c_str());
|
||||
});
|
||||
}
|
||||
|
||||
void file_updater::skip_files(const std::filesystem::path& target_dir) const
|
||||
{
|
||||
console::log("Skipping files");
|
||||
std::for_each(this->skip_files_.begin(), this->skip_files_.end(), [&target_dir](const auto& file)
|
||||
{
|
||||
const auto target_file = target_dir / file;
|
||||
utils::io::remove_file(target_file.string());
|
||||
console::log("Removed file \"%s\"", target_file.string().c_str());
|
||||
});
|
||||
}
|
||||
}
|
46
src/updater/file_updater.hpp
Normal file
46
src/updater/file_updater.hpp
Normal file
@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
namespace updater
|
||||
{
|
||||
class file_updater
|
||||
{
|
||||
public:
|
||||
file_updater(std::string name, std::filesystem::path base, std::filesystem::path out_name, std::filesystem::path version_file, std::string remote_tag, std::string remote_download);
|
||||
|
||||
[[nodiscard]] bool update_if_necessary() const;
|
||||
|
||||
void add_dir_to_clean(const std::string& dir);
|
||||
void add_file_to_skip(const std::string& file);
|
||||
|
||||
private:
|
||||
struct update_state
|
||||
{
|
||||
bool requires_update = false;
|
||||
std::string latest_tag;
|
||||
};
|
||||
|
||||
std::string name_;
|
||||
|
||||
std::filesystem::path base_;
|
||||
std::filesystem::path out_name_;
|
||||
std::filesystem::path version_file_;
|
||||
|
||||
std::string remote_tag_;
|
||||
std::string remote_download_;
|
||||
|
||||
// Directories to cleanup
|
||||
std::vector<std::filesystem::path> cleanup_directories_;
|
||||
|
||||
// Files to skip
|
||||
std::vector<std::string> skip_files_;
|
||||
|
||||
[[nodiscard]] std::string read_local_revision_file() const;
|
||||
[[nodiscard]] bool does_require_update(update_state& update_state, const std::string& local_version) const;
|
||||
void create_version_file(const std::string& revision_version) const;
|
||||
[[nodiscard]] bool update_file(const std::string& url) const;
|
||||
[[nodiscard]] bool deploy_files() const;
|
||||
|
||||
void cleanup_directories() const;
|
||||
void skip_files(const std::filesystem::path& target_dir) const;
|
||||
};
|
||||
}
|
37
src/updater/updater.cpp
Normal file
37
src/updater/updater.cpp
Normal file
@ -0,0 +1,37 @@
|
||||
#include <std_include.hpp>
|
||||
|
||||
#include <console.hpp>
|
||||
|
||||
#include "file_updater.hpp"
|
||||
#include "updater.hpp"
|
||||
|
||||
#include <utils/properties.hpp>
|
||||
|
||||
#define IW4X_VERSION_FILE "iw4x-version.json"
|
||||
#define IW4X_RAW_FILES_UPDATE_FILE "release.zip"
|
||||
#define IW4X_RAW_FILES_UPDATE_URL "https://github.com/iw4x/iw4x-rawfiles/releases/latest/download/" IW4X_RAW_FILES_UPDATE_FILE
|
||||
#define IW4X_RAW_FILES_TAGS "https://api.github.com/repos/iw4x/iw4x-rawfiles/releases/latest"
|
||||
|
||||
namespace updater
|
||||
{
|
||||
int update_iw4x()
|
||||
{
|
||||
const auto iw4_install = utils::properties::load("iw4-install");
|
||||
if (!iw4_install)
|
||||
{
|
||||
console::error("Failed to load the properties file");
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& base = iw4_install.value();
|
||||
|
||||
file_updater file_updater{ "IW4x", base, IW4X_RAW_FILES_UPDATE_FILE, IW4X_VERSION_FILE, IW4X_RAW_FILES_TAGS, IW4X_RAW_FILES_UPDATE_URL };
|
||||
|
||||
file_updater.add_dir_to_clean("iw4x");
|
||||
file_updater.add_dir_to_clean("zone");
|
||||
|
||||
file_updater.add_file_to_skip("iw4sp.exe");
|
||||
|
||||
return file_updater.update_if_necessary();
|
||||
}
|
||||
}
|
6
src/updater/updater.hpp
Normal file
6
src/updater/updater.hpp
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace updater
|
||||
{
|
||||
int update_iw4x();
|
||||
}
|
280
src/utils/compression.cpp
Normal file
280
src/utils/compression.cpp
Normal file
@ -0,0 +1,280 @@
|
||||
#include <std_include.hpp>
|
||||
|
||||
#include "compression.hpp"
|
||||
|
||||
#include <unzip.h>
|
||||
|
||||
#include <zlib.h>
|
||||
#include <zip.h>
|
||||
|
||||
#include <gsl/gsl>
|
||||
|
||||
#include "io.hpp"
|
||||
#include "string.hpp"
|
||||
|
||||
#ifndef MAX_PATH
|
||||
#define MAX_PATH 256
|
||||
#endif
|
||||
|
||||
namespace utils::compression
|
||||
{
|
||||
namespace zlib
|
||||
{
|
||||
namespace
|
||||
{
|
||||
class zlib_stream
|
||||
{
|
||||
public:
|
||||
zlib_stream()
|
||||
{
|
||||
memset(&stream_, 0, sizeof(stream_));
|
||||
valid_ = inflateInit(&stream_) == Z_OK;
|
||||
}
|
||||
|
||||
zlib_stream(zlib_stream&&) = delete;
|
||||
zlib_stream(const zlib_stream&) = delete;
|
||||
zlib_stream& operator=(zlib_stream&&) = delete;
|
||||
zlib_stream& operator=(const zlib_stream&) = delete;
|
||||
|
||||
~zlib_stream()
|
||||
{
|
||||
if (valid_)
|
||||
{
|
||||
inflateEnd(&stream_);
|
||||
}
|
||||
}
|
||||
|
||||
z_stream& get()
|
||||
{
|
||||
return stream_; //
|
||||
}
|
||||
|
||||
bool is_valid() const
|
||||
{
|
||||
return valid_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool valid_{false};
|
||||
z_stream stream_{};
|
||||
};
|
||||
}
|
||||
|
||||
std::string decompress(const std::string& data)
|
||||
{
|
||||
std::string buffer{};
|
||||
zlib_stream stream_container{};
|
||||
if (!stream_container.is_valid())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
int ret{};
|
||||
size_t offset = 0;
|
||||
static thread_local uint8_t dest[CHUNK] = {0};
|
||||
auto& stream = stream_container.get();
|
||||
|
||||
do
|
||||
{
|
||||
const auto input_size = std::min(sizeof(dest), data.size() - offset);
|
||||
stream.avail_in = static_cast<uInt>(input_size);
|
||||
stream.next_in = reinterpret_cast<const Bytef*>(data.data()) + offset;
|
||||
offset += stream.avail_in;
|
||||
|
||||
do
|
||||
{
|
||||
stream.avail_out = sizeof(dest);
|
||||
stream.next_out = dest;
|
||||
|
||||
ret = inflate(&stream, Z_NO_FLUSH);
|
||||
if (ret != Z_OK && ret != Z_STREAM_END)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
buffer.insert(buffer.end(), dest, dest + sizeof(dest) - stream.avail_out);
|
||||
}
|
||||
while (stream.avail_out == 0);
|
||||
}
|
||||
while (ret != Z_STREAM_END);
|
||||
|
||||
return buffer;
|
||||
}
|
||||