From d8da8a54a6c08296a884b892d714cabcb7e4bdb5 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Wed, 31 Aug 2022 12:04:09 +0200 Subject: [PATCH] Infrastructure for shipping and auto-updating experimental releases (#131) --- .github/workflows/build.yml | 22 +- .github/workflows/deploy_experimental.yml | 66 ++++++ .gitignore | 2 + CMakeLists.txt | 5 + src/Cemu/DiscordPresence/DiscordPresence.cpp | 5 +- .../ExceptionHandler/ExceptionHandler.cpp | 2 +- src/Common/version.h | 29 ++- src/config/LaunchSettings.cpp | 7 +- src/gui/CemuUpdateWindow.cpp | 207 ++++++++++-------- src/gui/CemuUpdateWindow.h | 18 +- src/gui/ChecksumTool.cpp | 4 +- src/gui/DownloadGraphicPacksWindow.cpp | 4 +- src/gui/MainWindow.cpp | 8 +- src/gui/guiWrapper.cpp | 2 +- src/main.cpp | 2 +- src/resource/cemu.rc | Bin 10076 -> 9690 bytes 16 files changed, 261 insertions(+), 122 deletions(-) create mode 100644 .github/workflows/deploy_experimental.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8a8dddf6..926ed4a9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,6 +18,9 @@ on: deploymode: required: false type: string + experimentalversion: + required: false + type: string env: VCPKG_ROOT: "${{github.workspace}}/dependencies/vcpkg" @@ -38,7 +41,6 @@ jobs: echo "BUILD_MODE=release" >> $GITHUB_ENV echo "BUILD_FLAGS=-DPUBLIC_RELEASE=ON" >> $GITHUB_ENV echo "Build mode is release" - - name: Setup debug mode parameters (for continous build) if: ${{ inputs.deploymode != 'release' }} run: | @@ -46,11 +48,16 @@ jobs: echo "BUILD_FLAGS=" >> $GITHUB_ENV echo "Build mode is debug" + - name: Setup version for experimental + if: ${{ inputs.experimentalversion != '' }} + run: | + echo "[INFO] Experimental version ${{ inputs.experimentalversion }}" + echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEXPERIMENTAL_VERSION=${{ inputs.experimentalversion }}" >> $GITHUB_ENV + - name: "Install system dependencies" run: | sudo apt update -qq sudo apt install -y ninja-build cmake libgtk-3-dev libsecret-1-dev libgcrypt20-dev libsystemd-dev freeglut3-dev clang-12 nasm - - name: "Bootstrap vcpkg" run: | bash ./dependencies/vcpkg/bootstrap-vcpkg.sh @@ -109,7 +116,11 @@ jobs: echo "BUILD_MODE=debug" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append echo "BUILD_FLAGS=" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append echo "Build mode is debug" - + - name: Setup version for experimental + if: ${{ inputs.experimentalversion != '' }} + run: | + echo "[INFO] Experimental version ${{ inputs.experimentalversion }}" + echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEXPERIMENTAL_VERSION=${{ inputs.experimentalversion }}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append - name: Workaround run: | @@ -140,7 +151,6 @@ jobs: # should be run twice $process = Start-Process -FilePath cmd.exe -ArgumentList $Arguments -Wait -PassThru -WindowStyle Hidden $process = Start-Process -FilePath cmd.exe -ArgumentList $Arguments -Wait -PassThru -WindowStyle Hidden - - name: Configure MSVC uses: ilammy/msvc-dev-cmd@v1 with: @@ -170,6 +180,8 @@ jobs: run: | mkdir -p build cd build + echo "[INFO] BUILD_FLAGS: ${{ env.BUILD_FLAGS }}" + echo "[INFO] BUILD_MODE: ${{ env.BUILD_MODE }}" cmake .. ${{ env.BUILD_FLAGS }} -DCMAKE_BUILD_TYPE=${{ env.BUILD_MODE }} - name: "Build Cemu" @@ -182,4 +194,4 @@ jobs: if: ${{ inputs.deploymode == 'release' }} with: name: cemu-bin-windows-x64 - path: ./bin/Cemu.exe + path: ./bin/Cemu.exe \ No newline at end of file diff --git a/.github/workflows/deploy_experimental.yml b/.github/workflows/deploy_experimental.yml new file mode 100644 index 00000000..32000986 --- /dev/null +++ b/.github/workflows/deploy_experimental.yml @@ -0,0 +1,66 @@ +name: Deploy experimental release +on: + workflow_dispatch: + +jobs: + call-release-build: + uses: ./.github/workflows/build.yml + with: + deploymode: release + experimentalversion: ${{ github.run_number }} + deploy: + name: Deploy experimental release + runs-on: ubuntu-20.04 + needs: call-release-build + steps: + - uses: actions/checkout@v2 + + - uses: actions/download-artifact@v3 + with: + name: cemu-bin-linux-x64 + path: cemu-bin-linux-x64 + + - uses: actions/download-artifact@v3 + with: + name: cemu-bin-windows-x64 + path: cemu-bin-windows-x64 + + - name: Initialize + run: | + mkdir upload + sudo apt install zip + + - name: Get version + run: | + echo "Experimental version: ${{ github.run_number }}" + ls + gcc -o getversion .github/getversion.cpp + ./getversion + echo "Cemu CI version: $(./getversion)" + echo "CEMU_FOLDER_NAME=Cemu_$(./getversion)-${{ github.run_number }}" >> $GITHUB_ENV + echo "CEMU_VERSION=$(./getversion)-${{ github.run_number }}" >> $GITHUB_ENV + + - name: Create release from windows-bin + run: | + ls ./ + ls ./bin/ + cp -R ./bin ./${{ env.CEMU_FOLDER_NAME }} + mv cemu-bin-windows-x64/Cemu.exe ./${{ env.CEMU_FOLDER_NAME }}/Cemu.exe + zip -9 -r upload/cemu-${{ env.CEMU_VERSION }}-windows-x64.zip ${{ env.CEMU_FOLDER_NAME }} + rm -r ./${{ env.CEMU_FOLDER_NAME }} + + - name: Create release from linux-bin + run: | + ls ./ + ls ./bin/ + cp -R ./bin ./${{ env.CEMU_FOLDER_NAME }} + mv cemu-bin-linux-x64/Cemu ./${{ env.CEMU_FOLDER_NAME }}/Cemu + zip -9 -r upload/cemu-${{ env.CEMU_VERSION }}-ubuntu-20.04-x64.zip ${{ env.CEMU_FOLDER_NAME }} + rm -r ./${{ env.CEMU_FOLDER_NAME }} + + - name: Create release + run: | + wget -O ghr.tar.gz https://github.com/tcnksm/ghr/releases/download/v0.15.0/ghr_v0.15.0_linux_amd64.tar.gz + tar xvzf ghr.tar.gz; rm ghr.tar.gz + echo "[INFO] Release tag: v${{ env.CEMU_VERSION }}" + ghr_v0.15.0_linux_amd64/ghr -prerelease -t ${{ secrets.GITHUB_TOKEN }} -n "Cemu ${{ env.CEMU_VERSION }} (Experimental)" -b "" "v${{ env.CEMU_VERSION }}" ./upload diff --git a/.gitignore b/.gitignore index 85a59243..cdf32796 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,8 @@ bin/Cemu bin/otp.bin bin/seeprom.bin bin/Cemu.pdb +bin/Cemu.ilk +bin/Cemu.exe.backup bin/mlc01/* bin/settings.xml bin/title_list_cache.xml diff --git a/CMakeLists.txt b/CMakeLists.txt index 12daf566..2f7ee45a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,12 +2,17 @@ cmake_minimum_required(VERSION 3.21.1) option(PUBLIC_RELEASE "Compile with debug asserts disabled and no console" OFF) option(ENABLE_VCPKG "Enable the vcpkg package manager" ON) +set(EXPERIMENTAL_VERSION "" CACHE STRING "") # used by CI script to set experimental version if (PUBLIC_RELEASE) add_definitions(-DPUBLIC_RELEASE) set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) # enable LTO endif() +if (EXPERIMENTAL_VERSION) + add_definitions(-DEMULATOR_VERSION_MINOR=${EXPERIMENTAL_VERSION}) +endif() + if (ENABLE_VCPKG) set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports") set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/vcpkg/scripts/buildsystems/vcpkg.cmake" diff --git a/src/Cemu/DiscordPresence/DiscordPresence.cpp b/src/Cemu/DiscordPresence/DiscordPresence.cpp index 61734614..a9feeba9 100644 --- a/src/Cemu/DiscordPresence/DiscordPresence.cpp +++ b/src/Cemu/DiscordPresence/DiscordPresence.cpp @@ -37,13 +37,10 @@ void DiscordPresence::UpdatePresence(State state, const std::string& text) const break; } - char image_text[128]; - sprintf(image_text, EMULATOR_NAME" %d.%d%s", EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_SUFFIX); - discord_presence.details = details_string.c_str(); discord_presence.state = state_string.c_str(); discord_presence.startTimestamp = time(nullptr); - discord_presence.largeImageText = image_text; + discord_presence.largeImageText = BUILD_VERSION_WITH_NAME_STRING; discord_presence.largeImageKey = "logo_icon_big_png"; Discord_UpdatePresence(&discord_presence); } diff --git a/src/Common/ExceptionHandler/ExceptionHandler.cpp b/src/Common/ExceptionHandler/ExceptionHandler.cpp index 20074fbf..48a99033 100644 --- a/src/Common/ExceptionHandler/ExceptionHandler.cpp +++ b/src/Common/ExceptionHandler/ExceptionHandler.cpp @@ -195,7 +195,7 @@ void createCrashlog(EXCEPTION_POINTERS* e, PCONTEXT context) char dumpLine[1024 * 4]; // info about Cemu version - sprintf(dumpLine, "\nCrashlog for Cemu %d.%d%s\n", EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_SUFFIX); + sprintf(dumpLine, "\nCrashlog for %s\n", BUILD_VERSION_WITH_NAME_STRING); cemuLog_writePlainToLog(dumpLine); SYSTEMTIME sysTime; diff --git a/src/Common/version.h b/src/Common/version.h index 2352878b..0eff0197 100644 --- a/src/Common/version.h +++ b/src/Common/version.h @@ -1,8 +1,33 @@ +#ifndef EMULATOR_NAME + #define EMULATOR_NAME "Cemu" #define EMULATOR_VERSION_LEAD 2 #define EMULATOR_VERSION_MAJOR 0 -#define EMULATOR_VERSION_MINOR 0 // kept internally for backwards compatibility purposes, but isn't displayed anymore +// the minor version is used for experimental builds to indicate the build index. Set by command line option from CI build script +// if zero, the version text will be constructed as LEAD.MAJOR, otherwise as LEAD.MAJOR-MINOR + +#if !defined(PUBLIC_RELEASE) +#define EMULATOR_VERSION_SUFFIX " (dev)" +#elif defined(EMULATOR_VERSION_MINOR) && EMULATOR_VERSION_MINOR == 0 +#define EMULATOR_VERSION_SUFFIX "" // stable +#else #define EMULATOR_VERSION_SUFFIX " (experimental)" +#endif -#define EMULATOR_SERVER_VERSION 4000 // used for auto-updater, increment this with every release +#ifndef EMULATOR_VERSION_MINOR +#define EMULATOR_VERSION_MINOR 0 +#endif + +#define _XSTRINGFY(s) _STRINGFY(s) +#define _STRINGFY(s) #s + +#if EMULATOR_VERSION_MINOR != 0 +#define BUILD_VERSION_WITH_NAME_STRING (EMULATOR_NAME " " _XSTRINGFY(EMULATOR_VERSION_LEAD) "." _XSTRINGFY(EMULATOR_VERSION_MAJOR) "-" _XSTRINGFY(EMULATOR_VERSION_MINOR) EMULATOR_VERSION_SUFFIX) +#define BUILD_VERSION_STRING (_XSTRINGFY(EMULATOR_VERSION_LEAD) "." _XSTRINGFY(EMULATOR_VERSION_MAJOR) "-" _XSTRINGFY(EMULATOR_VERSION_MINOR) EMULATOR_VERSION_SUFFIX) +#else +#define BUILD_VERSION_STRING (_XSTRINGFY(EMULATOR_VERSION_LEAD) "." _XSTRINGFY(EMULATOR_VERSION_MAJOR) EMULATOR_VERSION_SUFFIX) +#define BUILD_VERSION_WITH_NAME_STRING (EMULATOR_NAME " " _XSTRINGFY(EMULATOR_VERSION_LEAD) "." _XSTRINGFY(EMULATOR_VERSION_MAJOR) EMULATOR_VERSION_SUFFIX) +#endif + +#endif diff --git a/src/config/LaunchSettings.cpp b/src/config/LaunchSettings.cpp index 322999fd..7d7c0c98 100644 --- a/src/config/LaunchSettings.cpp +++ b/src/config/LaunchSettings.cpp @@ -112,7 +112,12 @@ bool LaunchSettings::HandleCommandline(const std::vector& args) if (vm.count("version")) { requireConsole(); - std::string versionStr = fmt::format("{}.{}{}", EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_SUFFIX); + std::string versionStr; +#if EMULATOR_VERSION_MINOR == 0 + versionStr = fmt::format("{}.{}{}", EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_SUFFIX); +#else + versionStr = fmt::format("{}.{}-{}{}", EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR, EMULATOR_VERSION_SUFFIX); +#endif std::cout << versionStr << std::endl; return false; // exit in main } diff --git a/src/gui/CemuUpdateWindow.cpp b/src/gui/CemuUpdateWindow.cpp index 6d79d13c..ef3aaab1 100644 --- a/src/gui/CemuUpdateWindow.cpp +++ b/src/gui/CemuUpdateWindow.cpp @@ -24,7 +24,7 @@ wxDEFINE_EVENT(wxEVT_PROGRESS, wxCommandEvent); CemuUpdateWindow::CemuUpdateWindow(wxWindow* parent) : wxDialog(parent, wxID_ANY, "Cemu update", wxDefaultPosition, wxDefaultSize, - wxCAPTION | wxMINIMIZE_BOX | wxSYSTEM_MENU | wxTAB_TRAVERSAL | wxCLOSE_BOX) + wxCAPTION | wxMINIMIZE_BOX | wxSYSTEM_MENU | wxTAB_TRAVERSAL | wxCLOSE_BOX) { auto* sizer = new wxBoxSizer(wxVERTICAL); m_gauge = new wxGauge(this, wxID_ANY, 100, wxDefaultPosition, wxSize(500, 20), wxGA_HORIZONTAL); @@ -40,13 +40,13 @@ CemuUpdateWindow::CemuUpdateWindow(wxWindow* parent) { auto* right_side = new wxBoxSizer(wxHORIZONTAL); - m_update_button = new wxButton(this, wxID_ANY, _("Update")); - m_update_button->Bind(wxEVT_BUTTON, &CemuUpdateWindow::OnUpdateButton, this); - right_side->Add(m_update_button, 0, wxALL, 5); + m_updateButton = new wxButton(this, wxID_ANY, _("Update")); + m_updateButton->Bind(wxEVT_BUTTON, &CemuUpdateWindow::OnUpdateButton, this); + right_side->Add(m_updateButton, 0, wxALL, 5); - m_cancel_button = new wxButton(this, wxID_ANY, _("Cancel")); - m_cancel_button->Bind(wxEVT_BUTTON, &CemuUpdateWindow::OnCancelButton, this); - right_side->Add(m_cancel_button, 0, wxALL, 5); + m_cancelButton = new wxButton(this, wxID_ANY, _("Cancel")); + m_cancelButton->Bind(wxEVT_BUTTON, &CemuUpdateWindow::OnCancelButton, this); + right_side->Add(m_cancelButton, 0, wxALL, 5); rows->Add(right_side, 1, wxALIGN_RIGHT, 5); } @@ -64,7 +64,7 @@ CemuUpdateWindow::CemuUpdateWindow(wxWindow* parent) Bind(wxEVT_PROGRESS, &CemuUpdateWindow::OnGaugeUpdate, this); m_thread = std::thread(&CemuUpdateWindow::WorkerThread, this); - m_update_button->Hide(); + m_updateButton->Hide(); m_changelog->Hide(); } @@ -89,14 +89,33 @@ std::string _curlUrlEscape(CURL* curl, const std::string& input) return r; } -bool CemuUpdateWindow::GetServerVersion(uint64& version, std::string& filename, std::string& changelog_filename) +std::string _curlUrlUnescape(CURL* curl, std::string_view input) +{ + int decodedLen = 0; + const char* decoded = curl_easy_unescape(curl, input.data(), input.size(), &decodedLen); + return std::string(decoded, decodedLen); +} + +// returns true if update is available and sets output parameters +bool CemuUpdateWindow::QueryUpdateInfo(std::string& downloadUrlOut, std::string& changelogUrlOut) { std::string buffer; - std::string urlStr("https://cemu.info/api/cemu_version3.php?version2="); + std::string urlStr("https://cemu.info/api2/version.php?v="); auto* curl = curl_easy_init(); - urlStr.append(_curlUrlEscape(curl, fmt::format("{}.{}.{}{}", EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR, EMULATOR_VERSION_SUFFIX))); + urlStr.append(_curlUrlEscape(curl, BUILD_VERSION_STRING)); +#if BOOST_OS_LINUX + urlStr.append("&platform=linux"); +#elif BOOST_OS_WINDOWS + urlStr.append("&platform=windows"); +#elif BOOST_OS_MACOS + urlStr.append("&platform=macos_x86"); +#elif + +#error Name for current platform is missing +#endif curl_easy_setopt(curl, CURLOPT_URL, urlStr.c_str()); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteStringCallback); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); @@ -114,26 +133,22 @@ bool CemuUpdateWindow::GetServerVersion(uint64& version, std::string& filename, cemu_assert_debug(false); return false; } - + std::vector tokens; const boost::char_separator sep{ "|" }; for (const auto& token : boost::tokenizer(buffer, sep)) - { tokens.emplace_back(token); - } - if (tokens.size() >= 2) + if (tokens.size() >= 3 && tokens[0] == "UPDATE") { - const auto latest_version = ConvertString(tokens[0]); - result = latest_version > 0 && !tokens[1].empty(); - if (result) - { - version = latest_version; - filename = tokens[1]; - - if(tokens.size() >= 3) - changelog_filename = tokens[2]; - } + // first token: "UPDATE" + // second token: Download URL + // third token: Changelog URL + // we allow more tokens in case we ever want to add extra information for future releases + downloadUrlOut = _curlUrlUnescape(curl, tokens[1]); + changelogUrlOut = _curlUrlUnescape(curl, tokens[2]); + if (!downloadUrlOut.empty() && !changelogUrlOut.empty()) + result = true; } } else @@ -146,30 +161,20 @@ bool CemuUpdateWindow::GetServerVersion(uint64& version, std::string& filename, return result; } -std::future CemuUpdateWindow::IsUpdateAvailable() +std::future CemuUpdateWindow::IsUpdateAvailableAsync() { return std::async(std::launch::async, CheckVersion); } bool CemuUpdateWindow::CheckVersion() { - uint64 latest_version; - std::string filename, changelog; - if (!GetServerVersion(latest_version, filename, changelog)) - return false; - - return IsUpdateAvailable(latest_version); + std::string downloadUrl, changelogUrl; + return QueryUpdateInfo(downloadUrl, changelogUrl); } -bool CemuUpdateWindow::IsUpdateAvailable(uint64 latest_version) -{ - uint64 version = EMULATOR_SERVER_VERSION; - - return latest_version > version; -} int CemuUpdateWindow::ProgressCallback(void* clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, - curl_off_t ulnow) + curl_off_t ulnow) { auto* thisptr = (CemuUpdateWindow*)clientp; auto* event = new wxCommandEvent(wxEVT_PROGRESS); @@ -188,7 +193,11 @@ bool CemuUpdateWindow::DownloadCemuZip(const std::string& url, const fs::path& f auto* curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_NOBODY, 1); - if (curl_easy_perform(curl) == CURLE_OK) + curl_easy_setopt(curl, CURLOPT_USERAGENT, BUILD_VERSION_WITH_NAME_STRING); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + auto r = curl_easy_perform(curl); + if (r == CURLE_OK) { long http_code = 0; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); @@ -198,10 +207,10 @@ bool CemuUpdateWindow::DownloadCemuZip(const std::string& url, const fs::path& f curl_easy_cleanup(curl); return false; } - + curl_off_t update_size; if (curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &update_size) == CURLE_OK) - m_gauge_max_value = (int)update_size; + m_gaugeMaxValue = (int)update_size; auto _curlWriteData = +[](void* ptr, size_t size, size_t nmemb, void* ctx) -> size_t @@ -220,11 +229,11 @@ bool CemuUpdateWindow::DownloadCemuZip(const std::string& url, const fs::path& f curl_easy_setopt(curl, CURLOPT_XFERINFODATA, this); auto curl_result = std::async(std::launch::async, [](CURL* curl, long* http_code) - { - const auto r = curl_easy_perform(curl); - curl_easy_cleanup(curl); - return r; - }, curl, &http_code); + { + const auto r = curl_easy_perform(curl); + curl_easy_cleanup(curl); + return r; + }, curl, &http_code); while (!curl_result.valid()) { if (m_order == WorkerOrder::Exit) @@ -232,13 +241,16 @@ bool CemuUpdateWindow::DownloadCemuZip(const std::string& url, const fs::path& f std::this_thread::sleep_for(std::chrono::milliseconds(1)); } - + result = curl_result.get() == CURLE_OK; delete fsUpdateFile; } else + { + cemuLog_log(LogType::Force, "Cemu zip download failed with error {}", r); curl_easy_cleanup(curl); + } if (!result && fs::exists(filename)) { @@ -254,8 +266,9 @@ bool CemuUpdateWindow::DownloadCemuZip(const std::string& url, const fs::path& f return result; } -bool CemuUpdateWindow::ExtractUpdate(const fs::path& zipname, const fs::path& targetpath) +bool CemuUpdateWindow::ExtractUpdate(const fs::path& zipname, const fs::path& targetpath, std::string& cemuFolderName) { + cemuFolderName.clear(); // open downloaded zip int err; auto* za = zip_open(zipname.string().c_str(), ZIP_RDONLY, &err); @@ -266,7 +279,7 @@ bool CemuUpdateWindow::ExtractUpdate(const fs::path& zipname, const fs::path& ta } const auto count = zip_get_num_entries(za, 0); - m_gauge_max_value = count; + m_gaugeMaxValue = count; for (auto i = 0; i < count; i++) { if (m_order == WorkerOrder::Exit) @@ -297,6 +310,13 @@ bool CemuUpdateWindow::ExtractUpdate(const fs::path& zipname, const fs::path& ta SystemException sys(ex); forceLog_printf("can't create folder \"%s\" for update: %s", sb.name, sys.what()); } + // the root should have only one Cemu_... directory, we track it here + if ((std::count(sb.name, sb.name + len, '/') + std::count(sb.name, sb.name + len, '\\')) == 1) + { + if (!cemuFolderName.empty()) + forceLog_printf("update zip has multiple folders in root"); + cemuFolderName.assign(sb.name, len - 1); + } continue; } @@ -342,7 +362,7 @@ bool CemuUpdateWindow::ExtractUpdate(const fs::path& zipname, const fs::path& ta } auto* event = new wxCommandEvent(wxEVT_PROGRESS); - event->SetInt(m_gauge_max_value); + event->SetInt(m_gaugeMaxValue); wxQueueEvent(this, event); zip_close(za); @@ -372,7 +392,7 @@ void CemuUpdateWindow::WorkerThread() if (m_order == WorkerOrder::CheckVersion) { auto* event = new wxCommandEvent(wxEVT_RESULT); - if (GetServerVersion(m_version, m_filename, m_changelog_filename) && IsUpdateAvailable(m_version)) + if (QueryUpdateInfo(m_downloadUrl, m_changelogUrl)) event->SetInt((int)Result::UpdateAvailable); else event->SetInt((int)Result::NoUpdateAvailable); @@ -382,7 +402,7 @@ void CemuUpdateWindow::WorkerThread() else if (m_order == WorkerOrder::UpdateVersion) { // download update - const std::string url = fmt::format("http://cemu.info/releases/{}", m_filename); + const std::string url = m_downloadUrl; if (!exists(tmppath)) create_directory(tmppath); @@ -405,8 +425,20 @@ void CemuUpdateWindow::WorkerThread() break; // extract - const auto expected_path = (tmppath / m_filename).replace_extension(""); - if (ExtractUpdate(update_file, tmppath) && exists(expected_path)) + std::string cemuFolderName; + if (!ExtractUpdate(update_file, tmppath, cemuFolderName)) + { + cemuLog_log(LogType::Force, "Extracting Cemu zip failed"); + break; + } + if (cemuFolderName.empty()) + { + cemuLog_log(LogType::Force, "Cemu folder not found in zip"); + break; + } + + const auto expected_path = tmppath / cemuFolderName; + if (exists(expected_path)) { auto* event = new wxCommandEvent(wxEVT_RESULT); event->SetInt((int)Result::ExtractSuccess); @@ -446,7 +478,7 @@ void CemuUpdateWindow::WorkerThread() const auto exec = ActiveSettings::GetFullPath(); const auto target_exe = fs::path(exec).replace_extension("exe.backup"); fs::rename(exec, target_exe); - m_restart_file = exec; + m_restartFile = exec; const auto index = expected_path.wstring().size(); int counter = 0; @@ -463,7 +495,7 @@ void CemuUpdateWindow::WorkerThread() } else { - if(it.path().filename() == L"Cemu.exe") + if (it.path().filename() == L"Cemu.exe") fs::rename(it.path(), fs::path(target_file).replace_filename(exec.filename())); else fs::rename(it.path(), target_file); @@ -484,7 +516,7 @@ void CemuUpdateWindow::WorkerThread() } auto* event = new wxCommandEvent(wxEVT_PROGRESS); - event->SetInt(m_gauge_max_value); + event->SetInt(m_gaugeMaxValue); wxQueueEvent(this, event); auto* result_event = new wxCommandEvent(wxEVT_RESULT); @@ -513,9 +545,9 @@ void CemuUpdateWindow::WorkerThread() void CemuUpdateWindow::OnClose(wxCloseEvent& event) { event.Skip(); - + #if BOOST_OS_WINDOWS - if (m_restart_required && !m_restart_file.empty() && fs::exists(m_restart_file)) + if (m_restartRequired && !m_restartFile.empty() && fs::exists(m_restartFile)) { PROCESS_INFORMATION pi{}; STARTUPINFO si{}; @@ -524,11 +556,11 @@ void CemuUpdateWindow::OnClose(wxCloseEvent& event) std::wstring cmdline = GetCommandLineW(); const auto index = cmdline.find('"', 1); cemu_assert_debug(index != std::wstring::npos); - cmdline = L"\"" + m_restart_file.wstring() + L"\"" + cmdline.substr(index + 1); + cmdline = L"\"" + m_restartFile.wstring() + L"\"" + cmdline.substr(index + 1); HANDLE lock = CreateMutex(nullptr, TRUE, L"Global\\cemu_update_lock"); CreateProcess(nullptr, (wchar_t*)cmdline.c_str(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi); - + exit(0); } #else @@ -542,60 +574,59 @@ void CemuUpdateWindow::OnResult(wxCommandEvent& event) switch ((Result)event.GetInt()) { case Result::NoUpdateAvailable: - m_cancel_button->SetLabel(_("Exit")); + m_cancelButton->SetLabel(_("Exit")); m_text->SetLabel(_("No update available!")); m_gauge->SetValue(100); break; case Result::UpdateAvailable: + { + if (!m_changelogUrl.empty()) { - if (!m_changelog_filename.empty()) - { - m_changelog->SetURL(fmt::format("https://cemu.info/changelog/{}", m_changelog_filename)); - m_changelog->Show(); - } - else - m_changelog->Hide(); - - m_update_button->Show(); - - - m_text->SetLabel(_("Update available!")); - m_cancel_button->SetLabel(_("Exit")); - break; + m_changelog->SetURL(m_changelogUrl); + m_changelog->Show(); } + else + m_changelog->Hide(); + + m_updateButton->Show(); + + m_text->SetLabel(_("Update available!")); + m_cancelButton->SetLabel(_("Exit")); + break; + } case Result::UpdateDownloaded: m_text->SetLabel(_("Extracting update...")); m_gauge->SetValue(0); break; case Result::UpdateDownloadError: - m_update_button->Enable(); + m_updateButton->Enable(); m_text->SetLabel(_("Couldn't download the update!")); break; case Result::ExtractSuccess: m_text->SetLabel(_("Applying update...")); m_gauge->SetValue(0); - m_cancel_button->Disable(); + m_cancelButton->Disable(); break; case Result::ExtractError: - m_update_button->Enable(); - m_cancel_button->Enable(); + m_updateButton->Enable(); + m_cancelButton->Enable(); m_text->SetLabel(_("Extracting failed!")); break; case Result::Success: - m_cancel_button->Enable(); - m_update_button->Hide(); + m_cancelButton->Enable(); + m_updateButton->Hide(); m_text->SetLabel(_("Success")); - m_cancel_button->SetLabel(_("Restart")); - m_restart_required = true; + m_cancelButton->SetLabel(_("Restart")); + m_restartRequired = true; break; - default: ; + default:; } } void CemuUpdateWindow::OnGaugeUpdate(wxCommandEvent& event) { - const int total_size = m_gauge_max_value > 0 ? m_gauge_max_value : 10000000; + const int total_size = m_gaugeMaxValue > 0 ? m_gaugeMaxValue : 10000000; m_gauge->SetValue((event.GetInt() * 100) / total_size); } @@ -606,7 +637,7 @@ void CemuUpdateWindow::OnUpdateButton(const wxCommandEvent& event) m_condition.notify_all(); - m_update_button->Disable(); + m_updateButton->Disable(); m_text->SetLabel(_("Downloading update...")); } diff --git a/src/gui/CemuUpdateWindow.h b/src/gui/CemuUpdateWindow.h index 5df8aecd..e3836da3 100644 --- a/src/gui/CemuUpdateWindow.h +++ b/src/gui/CemuUpdateWindow.h @@ -15,12 +15,12 @@ public: CemuUpdateWindow(wxWindow* parent); ~CemuUpdateWindow(); - static std::future IsUpdateAvailable(); + static std::future IsUpdateAvailableAsync(); private: wxStaticText* m_text; wxGauge* m_gauge; - wxButton* m_cancel_button, *m_update_button; + wxButton* m_cancelButton, *m_updateButton; wxHyperlinkCtrl* m_changelog; void OnUpdateButton(const wxCommandEvent& event); @@ -30,13 +30,12 @@ private: void OnGaugeUpdate(wxCommandEvent& event); static size_t WriteStringCallback(char* ptr, size_t size, size_t nmemb, void* userdata); - static bool GetServerVersion(uint64& version, std::string& filename, std::string& changelog_filename); + static bool QueryUpdateInfo(std::string& downloadUrlOut, std::string& changelogUrlOut); static bool CheckVersion(); - static bool IsUpdateAvailable(uint64 latest_version); static int ProgressCallback(void* clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow); bool DownloadCemuZip(const std::string& url, const fs::path& filename); - bool ExtractUpdate(const fs::path& zipname, const fs::path& targetpath); + bool ExtractUpdate(const fs::path& zipname, const fs::path& targetpath, std::string& cemuFolderName); enum class WorkerOrder { @@ -61,11 +60,10 @@ private: WorkerOrder m_order = WorkerOrder::CheckVersion; void WorkerThread(); - uint64 m_version = 0; - std::string m_filename, m_changelog_filename; - int m_gauge_max_value = 0; + std::string m_downloadUrl, m_changelogUrl; + int m_gaugeMaxValue = 0; std::thread m_thread; - fs::path m_restart_file; - bool m_restart_required = false; + fs::path m_restartFile; + bool m_restartRequired = false; }; diff --git a/src/gui/ChecksumTool.cpp b/src/gui/ChecksumTool.cpp index 1c38e2fd..25e6c7f2 100644 --- a/src/gui/ChecksumTool.cpp +++ b/src/gui/ChecksumTool.cpp @@ -212,7 +212,7 @@ void ChecksumTool::LoadOnlineData() const curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); - curl_easy_setopt(curl, CURLOPT_USERAGENT, fmt::format("Cemu_{}.{}{}", EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_SUFFIX).c_str()); + curl_easy_setopt(curl, CURLOPT_USERAGENT, BUILD_VERSION_WITH_NAME_STRING); curl_easy_perform(curl); @@ -248,7 +248,7 @@ void ChecksumTool::LoadOnlineData() const curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(curl, CURLOPT_USERAGENT, fmt::format("Cemu_{}.{}{}", EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_SUFFIX).c_str()); + curl_easy_setopt(curl, CURLOPT_USERAGENT, BUILD_VERSION_WITH_NAME_STRING); curl_easy_perform(curl); diff --git a/src/gui/DownloadGraphicPacksWindow.cpp b/src/gui/DownloadGraphicPacksWindow.cpp index 8835b9fc..651f7399 100644 --- a/src/gui/DownloadGraphicPacksWindow.cpp +++ b/src/gui/DownloadGraphicPacksWindow.cpp @@ -54,9 +54,7 @@ bool DownloadGraphicPacksWindow::curlDownloadFile(const char *url, curlDownloadF curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); - char temp[128]; - sprintf(temp, "Cemu_%d.%d%s", EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_SUFFIX); - curl_easy_setopt(curl, CURLOPT_USERAGENT, temp); + curl_easy_setopt(curl, CURLOPT_USERAGENT, BUILD_VERSION_WITH_NAME_STRING); downloadState->fileData.resize(0); const CURLcode res = curl_easy_perform(curl); curl_easy_cleanup(curl); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 89895502..d936434f 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -368,7 +368,7 @@ MainWindow::~MainWindow() wxString MainWindow::GetInitialWindowTitle() { - return wxStringFormat2(EMULATOR_NAME" {}.{}{}", EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_SUFFIX); + return BUILD_VERSION_WITH_NAME_STRING; } void MainWindow::ShowGettingStartedDialog() @@ -798,7 +798,7 @@ void MainWindow::OpenSettings() #endif if(config.check_update && !m_game_launched) - m_update_available = CemuUpdateWindow::IsUpdateAvailable(); + m_update_available = CemuUpdateWindow::IsUpdateAvailableAsync(); if (mlc_modified) RecreateMenu(); @@ -1217,7 +1217,7 @@ void MainWindow::LoadSettings() const auto& config = GetConfig(); if(config.check_update) - m_update_available = CemuUpdateWindow::IsUpdateAvailable(); + m_update_available = CemuUpdateWindow::IsUpdateAvailableAsync(); if (config.window_position != Vector2i{ -1,-1 }) this->SetPosition({ config.window_position.x, config.window_position.y }); @@ -1802,7 +1802,7 @@ public: void AddHeaderInfo(wxWindow* parent, wxSizer* sizer) { char versionString[512]; - sprintf(versionString, "Cemu\nVersion %d.%d\nCompiled " BUILD_DATE "\nOriginal authors: Exzap, Petergov", EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR); + sprintf(versionString, "Cemu\nVersion %s\nCompiled " BUILD_DATE "\nOriginal authors: Exzap, Petergov", BUILD_VERSION_STRING); sizer->Add(new wxStaticText(parent, wxID_ANY, versionString), wxSizerFlags().Border(wxALL, 3).Border(wxTOP, 10)); sizer->Add(new wxHyperlinkCtrl(parent, -1, "https://cemu.info", "https://cemu.info"), wxSizerFlags().Expand().Border(wxTOP | wxBOTTOM, 3)); diff --git a/src/gui/guiWrapper.cpp b/src/gui/guiWrapper.cpp index 17eb3bc6..6c06f4f9 100644 --- a/src/gui/guiWrapper.cpp +++ b/src/gui/guiWrapper.cpp @@ -47,7 +47,7 @@ WindowInfo& gui_getWindowInfo() void gui_updateWindowTitles(bool isIdle, bool isLoading, double fps) { std::string windowText; - windowText = fmt::format("" EMULATOR_NAME " {}.{}{}", EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_SUFFIX); + windowText = BUILD_VERSION_WITH_NAME_STRING; if (isIdle) { diff --git a/src/main.cpp b/src/main.cpp index e6529391..494d1f02 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -111,7 +111,7 @@ void checkForWine() void infoLog_cemuStartup() { - cemuLog_force("------- Init {} {}.{}{} -------", EMULATOR_NAME, EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_SUFFIX); + cemuLog_force("------- Init {} -------", BUILD_VERSION_WITH_NAME_STRING); cemuLog_force("Init Wii U memory space (base: 0x{:016x})", (size_t)memory_base); cemuLog_force(u8"mlc01 path: {}", ActiveSettings::GetMlcPath().generic_u8string()); // check for wine version diff --git a/src/resource/cemu.rc b/src/resource/cemu.rc index 11f70d04f899fe6d27d6b6a28823508c196b56cd..4781111df8a49cd45832f9e3529faf50f585f3e2 100644 GIT binary patch delta 29 lcmccPcguUj5#h;v!UCKB2`ex%$1oU74ipmKJVES?006Bs3Q+(6 delta 148 zcmccReaCOZ5n*d(hD?Suh7^WWAgRC*%;3S`&k)WK&k(@i$PmI1#NZ0#`vLi3Kvob? z-V@B{W#D4qoZKlayZMta6QiaAP