[Main] - Add a check for new builds

Now that we're using GitHub Actions to automate copying the builds to releases, we can pretty easily query the releases API and get the last build date and link to the downoad.
This commit is contained in:
Cancerous 2020-02-01 00:49:17 -05:00 committed by illusion
parent abce6dcdaa
commit adca56b122
3 changed files with 238 additions and 0 deletions

View File

@ -0,0 +1,202 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/app/check_update.h"
#include "xenia/base/logging.h"
#include "xenia/base/platform_win.h"
#include <build/version.h>
#include <algorithm>
#include <string>
#include <cstdio>
#include <iomanip>
#include <sstream>
#include <WinInet.h>
#include <shellapi.h>
#pragma comment (lib, "Wininet.lib")
using std::string;
using std::wstring;
namespace xe {
namespace update {
// convert to wstring
wstring CharPToWstring(const char* _charP) {
return wstring(_charP, _charP + strlen(_charP));
}
// send https request
wstring SendHTTPSRequest_GET(const wstring& _server,
const wstring& _page,
const wstring& _params = L"") {
char szData[4096];
// initialize WinInet
HINTERNET hInternet =
::InternetOpen(
TEXT("Checking for updates"),INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (hInternet != NULL) {
// open HTTP session
HINTERNET hConnect =
::InternetConnect(
hInternet, _server.c_str(), INTERNET_DEFAULT_HTTPS_PORT,
NULL,NULL, INTERNET_SERVICE_HTTP, 0, 1);
if (hConnect != NULL) {
wstring request =
_page + (_params.empty() ? L"" : (L"?" + _params));
// open request
HINTERNET hRequest =
::HttpOpenRequest(hConnect, L"GET", (LPCWSTR)request.c_str(),
NULL, NULL, 0, (INTERNET_FLAG_SECURE), 1);
if (hRequest != NULL) {
// send request
BOOL isSend = ::HttpSendRequest(hRequest, NULL, 0, NULL, 0);
if (isSend) {
for(;;) {
// reading data
DWORD dwByteRead;
BOOL isRead =
::InternetReadFile(hRequest, szData, sizeof(szData) - 1, &dwByteRead);
// break cycle if error or end
if (isRead == FALSE || dwByteRead == 0) break;
// saving result
szData[dwByteRead] = 0;
}
}
// close request
::InternetCloseHandle(hRequest);
}
// close session
::InternetCloseHandle(hConnect);
}
// close WinInet
::InternetCloseHandle(hInternet);
}
wstring answer = CharPToWstring(szData);
// should we clean this up?
//delete [] szData;
return answer;
}
void parse_response(wstring response, string *date, string *url ) {
char buf[4096];
char * pch;
sprintf(buf, xe::to_string(response).c_str());
pch = strtok(buf,",");
while (pch != NULL) {
if (strstr(pch, "created_at") && !((date)->length())) {
*date = pch;
}
if (strstr(pch, "browser_download_url") && !((url)->length())) {
*url = pch;
// should we clean these up?
//delete [] buf;
//delete(pch);
break;
}
pch = strtok (NULL, ",");
}
}
int OfferUpdateWindow(wstring link) {
int msgboxID = MessageBox(
NULL,
(LPCWSTR)L"There is a newer version available. Would you like to download the latest update?",
// (LPCWSTR)link.c_str(),
(LPCWSTR)L"Get the latest Xenia-Canary from GitHub",
MB_ICONINFORMATION | MB_YESNO | MB_SETFOREGROUND | MB_TASKMODAL
);
switch (msgboxID) {
case IDYES:
return 1;
case IDNO:
return 0;
}
return 0;
}
void Update::CheckUpdate() {
char buffer [80];
string b_date, b_datetime, date, url = "";
b_date = __DATE__;
b_datetime = b_date + " " + __TIME__;
// current build time
std::tm t_b = {};
std::istringstream ss(b_datetime.c_str());
// ss.imbue(std::locale("en_US.utf-8"));
ss >> std::get_time(&t_b, "%b %d %Y %H:%M:%S");
if (ss.fail()) {
XELOGI("Parse failed");
} else {
strftime(buffer, 80, "### current built date : %m-%d-%Y %I:%M%p", &t_b);
XELOGI("%s", buffer);
}
// time at runtime
std::time_t t_n;
time(&t_n);
std::tm* now = std::localtime(&t_n);
strftime(buffer, 80, "### time now : %m-%d-%Y %I:%M%p", now);
XELOGI("%s", buffer);
// get difference of build/run time in hours
double diff = (difftime(mktime(now), mktime(&t_b)) / 3600.0);
//XELOGI("%f",diff);
if (diff > 1.0) {
//request api response for latest release
wstring answer = SendHTTPSRequest_GET(L"api.github.com",
L"/repos/xenia-canary/xenia-canary/releases/latest", L"");
//parse response for just the sections we're after
parse_response(answer, &date, &url );
// do some sanitation on the substrings we got back
std::replace(date.begin(), date.end(), '"', ' ');
url.erase(0, url.find(':') + 2); //24
url.erase(url.find('"'));
// XELOGI("### Link to latest build : %s", url.c_str());
// time of latest build on github
std::tm t_l = {};
std::istringstream ss2(date.c_str());
// ss.imbue(std::locale("en_US.utf-8"));
ss2 >> std::get_time(&t_l, " created_at : %Y-%m-%dT%H:%M:%S");
if (ss2.fail()) {
XELOGI("Parse failed");
} else {
strftime(buffer, 80, "### latest build: %m-%d-%Y %I:%M%p", &t_l);
XELOGI("%s", buffer);
}
//diff latest/current build time in hours
diff = (difftime(mktime(&t_l),mktime(&t_b)) / 3600.0);
//XELOGI("%f",diff);
if (diff > 0) {
if (OfferUpdateWindow(xe::to_wstring(url))) {
wstring wurl = xe::to_wstring(url);
ShellExecute(0, 0, wurl.c_str(), 0, 0 , SW_SHOW );
}
}
}
}
} // namespace update
} // namespace xe

View File

@ -0,0 +1,24 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_UPDATE_H_
#define XENIA_UPDATE_H_
namespace xe {
namespace update {
class Update {
public:
static void CheckUpdate();
private:
// static int check_update();
};
} // namespace update
} // namespace xe
#endif

View File

@ -44,6 +44,10 @@
#include "xenia/hid/xinput/xinput_hid.h"
#endif // XE_PLATFORM_WIN32
#if XE_PLATFORM_WIN32
#include "xenia/app/check_update.h"
#endif
#include "third_party/xbyak/xbyak/xbyak_util.h"
DEFINE_string(apu, "any", "Audio system. Use: [any, nop, sdl, xaudio2]", "APU");
@ -66,6 +70,7 @@ DEFINE_transient_string(target, "",
DECLARE_bool(debug);
DEFINE_bool(discord, true, "Enable Discord rich presence", "General");
DEFINE_bool(check_update, true, "Check GitHub for new builds", "General");
namespace xe {
namespace app {
@ -378,6 +383,13 @@ int xenia_main(const std::vector<std::wstring>& args) {
}
}
#if XE_PLATFORM_WIN32
// check for updates
if (cvars::check_update){
update::Update::CheckUpdate();
}
#endif
// Now, we're going to use the main thread to drive events related to
// emulation.
while (!exiting) {