vita3k update: Add progress bar on download new build.

https: Add check for null result from getaddrinfo when resolving host address.
- should fix crash when internet is no available.
This commit is contained in:
Zangetsu38
2023-02-27 06:34:52 +01:00
committed by Zangetsu
parent 6da03dbbbb
commit d5162f6ce6
4 changed files with 103 additions and 22 deletions
+16 -1
View File
@@ -100,6 +100,7 @@ bool init_vita3k_update(GuiState &gui) {
if (!sha.empty() && !msg.empty()) {
// Replace " to \" for get back original message
boost::replace_all(msg, """, "\"");
// Replace \r and \n to new line
boost::replace_all(msg, "\\r\\n", "\n");
boost::replace_all(msg, "\\n", "\n");
@@ -132,6 +133,11 @@ bool init_vita3k_update(GuiState &gui) {
return has_update;
}
static std::atomic<float> progress(0);
static const auto progress_callback = [](float updated_progress) {
progress = updated_progress;
};
static void download_update(const std::string base_path) {
std::thread download([base_path]() {
std::string download_continuous_link = "https://github.com/Vita3K/Vita3K/releases/download/continuous";
@@ -152,7 +158,7 @@ static void download_update(const std::string base_path) {
const auto vita3k_latest_path = base_path + "vita3k-latest" + archive_ext;
LOG_INFO("Attempting to download and extract the latest Vita3K version {} in progress...", git_version);
if (https::download_file(download_continuous_link, vita3k_latest_path)) {
if (https::download_file(download_continuous_link, vita3k_latest_path, progress_callback)) {
SDL_Event event;
event.type = SDL_QUIT;
SDL_PushEvent(&event);
@@ -265,6 +271,15 @@ void draw_vita3k_update(GuiState &gui, EmuEnvState &emuenv) {
ImGui::SetCursorPos(ImVec2(92.f * SCALE.x, (display_size.y / 2.f) - (calc_str.y / 2.f)));
ImGui::PushTextWrapPos(868.f * SCALE.x);
ImGui::Text("%s", lang["downloading"].c_str());
const float PROGRESS_BAR_WIDTH = 820.f * SCALE.x;
ImGui::SetCursorPos(ImVec2((ImGui::GetWindowWidth() / 2) - (PROGRESS_BAR_WIDTH / 2.f), ImGui::GetCursorPosY() + 30.f * emuenv.dpi_scale));
ImGui::PushStyleColor(ImGuiCol_PlotHistogram, GUI_PROGRESS_BAR);
ImGui::ProgressBar(progress / 100.f, ImVec2(PROGRESS_BAR_WIDTH, 15.f * emuenv.dpi_scale), "");
const auto progress_str = std::to_string(uint32_t(progress)).append("%");
ImGui::SetCursorPos(ImVec2((ImGui::GetWindowWidth() / 2.f) - (ImGui::CalcTextSize(progress_str.c_str()).x / 2.f), ImGui::GetCursorPosY() + 16.f * emuenv.dpi_scale));
ImGui::TextColored(GUI_COLOR_TEXT, "%s", progress_str.c_str());
ImGui::PopStyleColor();
ImGui::PopTextWrapPos();
break;
+3 -3
View File
@@ -22,8 +22,8 @@
namespace https {
std::string get_web_response(const std::string url);
std::string get_web_regex_result(const std::string url, const std::regex regex);
bool download_file(const std::string url, std::string output_file_path);
std::string get_web_response(const std::string url, const std::string method = "GET", const std::function<void(float)> &progress_callback = nullptr);
std::string get_web_regex_result(const std::string url, const std::regex regex, const std::string method = "GET");
bool download_file(const std::string url, const std::string output_file_path, const std::function<void(float)> &progress_callback = nullptr);
} // namespace https
+82 -16
View File
@@ -52,8 +52,9 @@ static void close_ssl(SSL *ssl) {
SSL_free(ssl);
}
static uint64_t file_size = 0, header_size = 0;
constexpr int READ_BUFFER_SIZE = 1048;
std::string get_web_response(const std::string url) {
std::string get_web_response(const std::string url, const std::string method, const std::function<void(float)> &progress_callback) {
#ifdef WIN32
WORD versionWanted = MAKEWORD(2, 2);
WSADATA wsaData;
@@ -101,8 +102,15 @@ std::string get_web_response(const std::string url) {
// Get address info with using host and port
auto ret = getaddrinfo(host.c_str(), "https", &hints, &result);
if (ret < 0) {
LOG_ERROR("getaddrinfo = {}", ret);
// Check if getaddrinfo failed or unable to resolve address for host
if ((ret < 0) || !result) {
if (!result)
LOG_ERROR("Unable to resolve address for host: {}", host);
else {
LOG_ERROR("getaddrinfo error: {}", gai_strerror(ret));
freeaddrinfo(result);
}
SSL_CTX_free(ctx);
close_socket(sockfd);
return {};
@@ -112,6 +120,26 @@ std::string get_web_response(const std::string url) {
ret = connect(sockfd, result->ai_addr, static_cast<uint32_t>(result->ai_addrlen));
if (ret < 0) {
LOG_ERROR("connect({},...) = {}, errno={}({})", sockfd, ret, errno, strerror(errno));
freeaddrinfo(result);
SSL_CTX_free(ctx);
close_socket(sockfd);
return {};
}
// Check if socket is connected
int error = 0;
socklen_t errlen = sizeof(error);
ret = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, reinterpret_cast<char *>(&error), &errlen);
if (ret < 0) {
LOG_ERROR("getsockopt({}, SOL_SOCKET, SO_ERROR, ...) failed: {}", sockfd, strerror(errno));
freeaddrinfo(result);
SSL_CTX_free(ctx);
close_socket(sockfd);
return {};
}
if (error != 0) {
LOG_ERROR("connect({}, ...) failed: {}", sockfd, error);
freeaddrinfo(result);
SSL_CTX_free(ctx);
close_socket(sockfd);
return {};
@@ -125,6 +153,7 @@ std::string get_web_response(const std::string url) {
char err_buf[256];
ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf));
LOG_ERROR("Error establishing SSL connection: {}", err_buf);
freeaddrinfo(result);
SSL_CTX_free(ctx);
close_ssl(ssl);
close_socket(sockfd);
@@ -132,7 +161,7 @@ std::string get_web_response(const std::string url) {
}
// Send HTTP GET request to extracted URI
std::string request = "GET " + uri + " HTTP/1.1\r\n";
std::string request = method + " " + uri + " HTTP/1.1\r\n";
request += "Host: " + host + "\r\n";
request += "User-Agent: OpenSSL/1.1.1\r\n";
request += "Connection: close\r\n\r\n";
@@ -140,6 +169,7 @@ std::string get_web_response(const std::string url) {
if (SSL_write(ssl, request.c_str(), static_cast<uint32_t>(request.length())) <= 0) {
char err_buf[256];
ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf));
freeaddrinfo(result);
SSL_CTX_free(ctx);
close_ssl(ssl);
close_socket(sockfd);
@@ -149,10 +179,20 @@ std::string get_web_response(const std::string url) {
std::array<char, READ_BUFFER_SIZE> read_buffer{};
std::string response;
uint64_t current_size = 0;
while (auto bytes_read = SSL_read(ssl, read_buffer.data(), static_cast<uint32_t>(read_buffer.size()))) {
response += std::string(read_buffer.data(), bytes_read);
current_size += bytes_read;
if (progress_callback && (file_size > 0)) {
float progress_percent = static_cast<float>(current_size - header_size) / static_cast<float>(file_size) * 100.0f;
progress_callback(progress_percent);
}
}
if (method == "HEAD")
header_size = current_size;
freeaddrinfo(result);
SSL_CTX_free(ctx);
close_ssl(ssl);
close_socket(sockfd);
@@ -168,11 +208,11 @@ std::string get_web_response(const std::string url) {
return response;
}
std::string get_web_regex_result(const std::string url, const std::regex regex) {
std::string get_web_regex_result(const std::string url, const std::regex regex, const std::string method) {
std::string result;
// Get the response of the web
const auto response = https::get_web_response(url);
const auto response = https::get_web_response(url, method);
// Check if the response is not empty
if (!response.empty()) {
@@ -190,7 +230,22 @@ std::string get_web_regex_result(const std::string url, const std::regex regex)
return result;
}
bool download_file(const std::string url, const std::string output_file_path) {
static uint64_t get_file_size(const std::string url) {
uint64_t content_length = 0;
// Get the file size from the header
const auto content_length_str = get_web_regex_result(url, std::regex("Content-Length: (\\d+)"), "HEAD");
// Check if the content length is not empty
if (!content_length_str.empty())
content_length = std::stoll(content_length_str);
return content_length;
}
bool download_file(const std::string url, const std::string output_file_path, const std::function<void(float)> &progress_callback) {
file_size = 0;
// Get the response of the app compat db
auto response = get_web_response(url);
@@ -198,20 +253,31 @@ bool download_file(const std::string url, const std::string output_file_path) {
if (!response.empty()) {
// Check if the response is a redirection
if (response.find("HTTP/1.1 302 Found") != std::string::npos) {
// Extract the redirection URL from the response
std::string location_header = "Location: ";
size_t location_index = response.find(location_header);
size_t end_of_location_index = response.find("\r\n", location_index + location_header.length());
const auto redirected_url = response.substr(location_index + location_header.length(), end_of_location_index - location_index - location_header.length());
// Get the redirection URL from the response header (Location)
std::smatch match;
if (std::regex_search(response, match, std::regex("Location: (https?://[^\\s]+)"))) {
const std::string redirected_url(match[1]);
// Get response of redirect url
response = get_web_response(redirected_url);
if (response.empty()) {
LOG_ERROR("Failed to get response on redirected url: {}", redirected_url);
// Get file size from the redirection URL when progress callback is not null
if (progress_callback) {
file_size = get_file_size(redirected_url);
if (file_size == 0) {
LOG_ERROR("Failed to get file size");
return false;
}
}
// Download the file from the redirection URL with using progress callback
response = get_web_response(redirected_url, "GET", progress_callback);
// Check if the response is empty
if (response.empty()) {
LOG_ERROR("Failed to download file");
return false;
}
}
}
// Get the content of the response
std::string content = response.substr(response.find("\r\n\r\n") + 4);