mirror of
https://github.com/Vita3K/Vita3K.git
synced 2026-07-11 01:34:23 +02:00
vita3k update: move from curl and powershell to https.
- move openssl code inside https directory. gui: reworks info msg dialog.
This commit is contained in:
@@ -126,6 +126,7 @@ add_subdirectory(ngs)
|
||||
add_subdirectory(np)
|
||||
add_subdirectory(emuenv)
|
||||
add_subdirectory(http)
|
||||
add_subdirectory(https)
|
||||
add_subdirectory(io)
|
||||
add_subdirectory(kernel)
|
||||
add_subdirectory(mem)
|
||||
|
||||
@@ -7,5 +7,5 @@ add_library(
|
||||
)
|
||||
|
||||
target_include_directories(compat PUBLIC include)
|
||||
target_link_libraries(compat PUBLIC gui emuenv)
|
||||
target_link_libraries(compat PRIVATE pugixml::pugixml ssl)
|
||||
target_link_libraries(compat PUBLIC gui https emuenv)
|
||||
target_link_libraries(compat PRIVATE pugixml::pugixml)
|
||||
|
||||
@@ -36,7 +36,7 @@ enum CompatibilityState {
|
||||
|
||||
struct Compatibility {
|
||||
uint32_t issue_id;
|
||||
CompatibilityState state = CompatibilityState::Unknown;
|
||||
CompatibilityState state = Unknown;
|
||||
time_t updated_at;
|
||||
};
|
||||
|
||||
|
||||
+15
-190
@@ -15,24 +15,14 @@
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include <compat/functions.h>
|
||||
#include <compat/state.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <io.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#include <emuenv/state.h>
|
||||
#include <gui/state.h>
|
||||
|
||||
#include <https/functions.h>
|
||||
|
||||
#include <pugixml.hpp>
|
||||
|
||||
enum LabelIdState {
|
||||
@@ -70,7 +60,7 @@ bool load_compat_app_db(GuiState &gui, EmuEnvState &emuenv) {
|
||||
const auto version = compatibility.attribute("version").as_uint();
|
||||
if (db_version != version) {
|
||||
LOG_WARN("Compatibility database version {} is outdated, download it again.", version);
|
||||
return false;
|
||||
return update_compat_app_db(gui, emuenv);
|
||||
}
|
||||
|
||||
// Clear old compat database
|
||||
@@ -115,183 +105,18 @@ bool load_compat_app_db(GuiState &gui, EmuEnvState &emuenv) {
|
||||
return !gui.compat.app_compat_db.empty();
|
||||
}
|
||||
|
||||
constexpr int READ_BUFFER_SIZE = 1048;
|
||||
static std::string get_https_response(const std::string &url) {
|
||||
#ifdef WIN32
|
||||
WORD versionWanted = MAKEWORD(2, 2);
|
||||
WSADATA wsaData;
|
||||
WSAStartup(versionWanted, &wsaData);
|
||||
#endif
|
||||
|
||||
// Initialize SSL
|
||||
const auto ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
if (!ctx) {
|
||||
LOG_ERROR("Error creating SSL context");
|
||||
return {};
|
||||
}
|
||||
|
||||
// Create socket
|
||||
const auto sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sockfd < 0) {
|
||||
LOG_ERROR("ERROR opening socket: {}", sockfd);
|
||||
return {};
|
||||
}
|
||||
|
||||
// Parse URL to get host and uri
|
||||
std::string host, uri;
|
||||
size_t start = url.find("://");
|
||||
if (start != std::string::npos) {
|
||||
start += 3; // skip "://"
|
||||
size_t end = url.find("/", start);
|
||||
if (end != std::string::npos) {
|
||||
host = url.substr(start, end - start);
|
||||
uri = url.substr(end);
|
||||
} else {
|
||||
host = url.substr(start);
|
||||
uri = "/";
|
||||
}
|
||||
}
|
||||
|
||||
// Set address info option
|
||||
const addrinfo hints = {
|
||||
AI_PASSIVE, /* For wildcard IP address */
|
||||
AF_UNSPEC, /* Allow IPv4 or IPv6 */
|
||||
SOCK_DGRAM, /* Datagram socket */
|
||||
0, /* Any protocol */
|
||||
};
|
||||
addrinfo *result = { 0 };
|
||||
|
||||
// Get address info with using host and port
|
||||
auto ret = getaddrinfo(host.c_str(), "https", &hints, &result);
|
||||
if (ret < 0) {
|
||||
LOG_ERROR("getaddrinfo = {}", ret);
|
||||
return {};
|
||||
}
|
||||
|
||||
// Connect to host
|
||||
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));
|
||||
return {};
|
||||
}
|
||||
|
||||
// Create and connect SSL
|
||||
SSL *ssl;
|
||||
ssl = SSL_new(ctx);
|
||||
SSL_set_fd(ssl, static_cast<uint32_t>(sockfd));
|
||||
if (SSL_connect(ssl) <= 0) {
|
||||
char err_buf[256];
|
||||
ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf));
|
||||
LOG_ERROR("Error establishing SSL connection: {}", err_buf);
|
||||
return {};
|
||||
}
|
||||
|
||||
// Send HTTP GET request to extracted URI
|
||||
std::string request = "GET " + 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";
|
||||
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));
|
||||
LOG_ERROR("Error sending request: {}", err_buf);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::array<char, READ_BUFFER_SIZE> read_buffer{};
|
||||
std::string response;
|
||||
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);
|
||||
}
|
||||
|
||||
SSL_shutdown(ssl);
|
||||
SSL_free(ssl);
|
||||
SSL_CTX_free(ctx);
|
||||
|
||||
#ifdef WIN32
|
||||
closesocket(sockfd);
|
||||
WSACleanup();
|
||||
#else
|
||||
close(sockfd);
|
||||
#endif // WIN32
|
||||
|
||||
boost::trim(response);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
static std::string get_updated_at() {
|
||||
const std::string latest_link = "https://api.github.com/repos/Vita3K/compatibility/releases/latest";
|
||||
std::string updated_at;
|
||||
|
||||
// Get the response of last release from the GitHub API
|
||||
const auto response = get_https_response(latest_link);
|
||||
|
||||
// Check if the response is not empty
|
||||
if (!response.empty()) {
|
||||
// Extract the content of the response
|
||||
const std::string content = response.substr(response.find("\r\n\r\n") + 4);
|
||||
|
||||
// Get the Updated at of content
|
||||
updated_at = content.substr(content.find("Updated at:") + 12, content.find_last_of("\r\n}") - content.find("Updated at:") - 13);
|
||||
}
|
||||
|
||||
return updated_at;
|
||||
}
|
||||
|
||||
static bool download_app_compat_db(const std::string &output_file_path) {
|
||||
const std::string app_compat_db_link = "https://github.com/Vita3K/compatibility/releases/download/compat_db/app_compat_db.xml";
|
||||
|
||||
// Get the response of the app compat db
|
||||
auto response = get_https_response(app_compat_db_link);
|
||||
|
||||
// Check if the response is not empty
|
||||
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 response of redirect url
|
||||
response = get_https_response(redirected_url);
|
||||
if (response.empty()) {
|
||||
LOG_ERROR("Failed to get response on redirected url: {}", redirected_url);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the XML content from the response
|
||||
std::string xml = response.substr(response.find("\r\n\r\n") + 4);
|
||||
|
||||
// Create the xml output file
|
||||
std::ofstream output_file(output_file_path, std::ios::binary);
|
||||
if (!output_file.is_open()) {
|
||||
LOG_ERROR("Failed to open output file: {}", output_file_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write the XML content to the output file
|
||||
output_file.write(xml.c_str(), xml.length());
|
||||
output_file.close();
|
||||
|
||||
return fs::exists(output_file_path);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
static const std::string latest_link = "https://api.github.com/repos/Vita3K/compatibility/releases/latest";
|
||||
static const std::string app_compat_db_link = "https://github.com/Vita3K/compatibility/releases/download/compat_db/app_compat_db.xml";
|
||||
|
||||
bool update_compat_app_db(GuiState &gui, EmuEnvState &emuenv) {
|
||||
const auto compat_db_path = fs::path(emuenv.base_path) / "cache/app_compat_db.xml";
|
||||
const auto app_compat_db_path = fs::path(emuenv.base_path) / "cache/app_compat_db.xml";
|
||||
gui.info_message.function = SPDLOG_FUNCTION;
|
||||
|
||||
// Get current date of last issue updated
|
||||
const auto updated_at = get_updated_at();
|
||||
// Get current date of last compat database updated at
|
||||
const auto updated_at = https::get_web_regex_result(latest_link, std::regex("Updated at: (\\d{2}-\\d{2}-\\d{4} \\d{2}:\\d{2}:\\d{2})"));
|
||||
if (updated_at.empty()) {
|
||||
gui.info_message.level = spdlog::level::err;
|
||||
gui.info_message.msg = "Failed to get current compatibility database version, check firewall/internet access, try again later.";
|
||||
gui.info_message.msg = "Failed to get current compatibility database updated at, check firewall/internet access, try again later.";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -301,11 +126,11 @@ bool update_compat_app_db(GuiState &gui, EmuEnvState &emuenv) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto compat_db_exist = fs::exists(compat_db_path);
|
||||
const auto compat_db_exist = fs::exists(app_compat_db_path);
|
||||
|
||||
LOG_INFO("Applications compatibility database is {}, attempting to download latest updated at: {}", compat_db_exist ? "outdated" : "missing", updated_at);
|
||||
|
||||
if (!download_app_compat_db(compat_db_path.string())) {
|
||||
if (!https::download_file(app_compat_db_link, app_compat_db_path.string())) {
|
||||
gui.info_message.level = spdlog::level::err;
|
||||
gui.info_message.msg = fmt::format("Failed to download Applications compatibility database updated at: {}", updated_at);
|
||||
return false;
|
||||
@@ -325,10 +150,10 @@ bool update_compat_app_db(GuiState &gui, EmuEnvState &emuenv) {
|
||||
|
||||
if (compat_db_exist) {
|
||||
const auto dif = static_cast<int32_t>(gui.compat.app_compat_db.size() - old_compat_count);
|
||||
if (dif > 0)
|
||||
gui.info_message.msg = fmt::format("The compatibility database was successfully updated from {} to {}.\n\n{} new application(s) are listed!", old_db_updated_at, db_updated_at, dif);
|
||||
if (!old_db_updated_at.empty() && dif > 0)
|
||||
gui.info_message.msg = fmt::format("The compatibility database was successfully updated from:\n{} to {}.\n\n{} new application(s) are listed!", old_db_updated_at, db_updated_at, dif);
|
||||
else
|
||||
gui.info_message.msg = fmt::format("The compatibility database was successfully updated from {} to {}.\n\n{} applications are listed!", old_db_updated_at, db_updated_at, gui.compat.app_compat_db.size());
|
||||
gui.info_message.msg = fmt::format("The compatibility database was successfully updated from:\n{} to {}.\n\n{} applications are listed!", old_db_updated_at, db_updated_at, gui.compat.app_compat_db.size());
|
||||
} else
|
||||
gui.info_message.msg = fmt::format("The compatibility database updated at {} has been successfully downloaded and loaded.\n\n{} applications are listed!", db_updated_at, gui.compat.app_compat_db.size());
|
||||
|
||||
|
||||
@@ -50,6 +50,6 @@ add_library(
|
||||
)
|
||||
|
||||
target_include_directories(gui PUBLIC include ${CMAKE_SOURCE_DIR}/vita3k)
|
||||
target_link_libraries(gui PUBLIC app compat config dialog emuenv ime imgui glutil lang np)
|
||||
target_link_libraries(gui PUBLIC app compat config dialog emuenv https ime imgui glutil lang np)
|
||||
target_link_libraries(gui PRIVATE audio ctrl kernel miniz psvpfsparser pugixml::pugixml stb renderer packages sdl2 vkutil host::dialog)
|
||||
target_link_libraries(gui PUBLIC tracy)
|
||||
|
||||
+15
-8
@@ -49,28 +49,35 @@ namespace gui {
|
||||
void draw_info_message(GuiState &gui, EmuEnvState &emuenv) {
|
||||
if (emuenv.cfg.display_info_message) {
|
||||
const ImVec2 display_size(emuenv.viewport_size.x, emuenv.viewport_size.y);
|
||||
const ImVec2 RES_SCALE(display_size.x / emuenv.res_width_dpi_scale, display_size.y / emuenv.res_height_dpi_scale);
|
||||
const ImVec2 SCALE(RES_SCALE.x * emuenv.dpi_scale, RES_SCALE.y * emuenv.dpi_scale);
|
||||
|
||||
const ImVec2 WINDOW_SIZE(680.0f * SCALE.x, 320.0f * SCALE.y);
|
||||
const ImVec2 BUTTON_SIZE(160.f * SCALE.x, 46.f * SCALE.y);
|
||||
|
||||
ImGui::SetNextWindowPos(ImVec2(emuenv.viewport_pos.x, emuenv.viewport_pos.x), ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(display_size, ImGuiCond_Always);
|
||||
ImGui::Begin("##information", nullptr, ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDecoration);
|
||||
ImGui::SetNextWindowPos(ImVec2(display_size.x / 2, display_size.y / 2), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.f * emuenv.dpi_scale);
|
||||
ImGui::BeginChild("##info", ImVec2(display_size.x / 1.4f, display_size.y / 2.6f), true, ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDecoration);
|
||||
ImGui::SetNextWindowPos(ImVec2(emuenv.viewport_pos.x + (display_size.x / 2) - (WINDOW_SIZE.x / 2.f), emuenv.viewport_pos.y + (display_size.y / 2.f) - (WINDOW_SIZE.y / 2.f)), ImGuiCond_Always);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.f * SCALE.x);
|
||||
ImGui::BeginChild("##info", WINDOW_SIZE, true, ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDecoration);
|
||||
const auto title = fmt::format("{}", spdlog::level::to_string_view(gui.info_message.level));
|
||||
ImGui::SetWindowFontScale(RES_SCALE.x);
|
||||
ImGui::SetCursorPosX((ImGui::GetWindowWidth() - ImGui::CalcTextSize(title.c_str()).x) / 2);
|
||||
ImGui::TextColored(GUI_COLOR_TEXT_TITLE, "%s", title.c_str());
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
const auto text_size = ImGui::CalcTextSize(gui.info_message.msg.c_str(), 0 , false, WINDOW_SIZE.x - (24.f * SCALE.x));
|
||||
const auto text_pos = ImVec2((WINDOW_SIZE.x / 2.f) - (text_size.x / 2.f), (WINDOW_SIZE.y / 2.f) - (text_size.y / 2.f) - (24 * SCALE.y));
|
||||
ImGui::SetCursorPos(text_pos);
|
||||
ImGui::TextWrapped("%s", gui.info_message.msg.c_str());
|
||||
ImGui::Spacing();
|
||||
ImGui::SetCursorPosY(WINDOW_SIZE.y - BUTTON_SIZE.y - (42.0f * SCALE.y));
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
const auto BUTTON_SIZE = ImVec2(160.f * emuenv.dpi_scale, 46.f * emuenv.dpi_scale);
|
||||
ImGui::SetCursorPosX((ImGui::GetWindowWidth() / 2.f) - (BUTTON_SIZE.x / 2.f));
|
||||
ImGui::SetCursorPos(ImVec2((ImGui::GetWindowWidth() / 2.f) - (BUTTON_SIZE.x / 2.f), WINDOW_SIZE.y - BUTTON_SIZE.y - (24.0f * SCALE.y)));
|
||||
if (ImGui::Button("Ok", BUTTON_SIZE))
|
||||
gui.info_message = {};
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::End();
|
||||
} else {
|
||||
|
||||
@@ -24,10 +24,9 @@
|
||||
#include <SDL.h>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <https/functions.h>
|
||||
|
||||
#include <regex>
|
||||
|
||||
namespace gui {
|
||||
|
||||
@@ -46,42 +45,14 @@ static VitaAreaState vita_area_state;
|
||||
static int git_version;
|
||||
static std::vector<std::pair<std::string, std::string>> git_commit_desc_list;
|
||||
bool init_vita3k_update(GuiState &gui) {
|
||||
#ifndef __APPLE__
|
||||
const std::string path = "vita3k-latest.zip";
|
||||
#else
|
||||
struct passwd *pwent = getpwuid(getuid());
|
||||
const std::string path = fmt::format("{}/vita3k-latest.dmg", pwent->pw_dir);
|
||||
#endif
|
||||
if (fs::exists(path))
|
||||
fs::remove(path);
|
||||
|
||||
state = NO_UPDATE;
|
||||
git_commit_desc_list.clear();
|
||||
git_version = 0;
|
||||
vita_area_state = {};
|
||||
const auto latest_link = "https://api.github.com/repos/Vita3K/Vita3K/releases/latest";
|
||||
|
||||
// Get Build number of latest release
|
||||
const auto latest_link = "https://api.github.com/repos/Vita3K/Vita3K/releases/latest";
|
||||
#ifdef WIN32
|
||||
std::string power_shell_version;
|
||||
std::getline(std::ifstream(_popen("powershell (Get-Host).Version.major", "r")), power_shell_version);
|
||||
if (power_shell_version.empty() || !std::isdigit(power_shell_version[0]) || (std::stoi(power_shell_version) < 3)) {
|
||||
LOG_WARN("You powershell version {} is outdated and incompatible with Vita3K Update, consider to update it", power_shell_version);
|
||||
return false;
|
||||
}
|
||||
const auto github_version_cmd = fmt::format(R"(powershell ((Invoke-RestMethod {} -timeout 2).body.split([Environment]::NewLine) ^| Select-String -Pattern \"Vita3K Build: \") -replace \"Vita3K Build: \")", latest_link);
|
||||
#else
|
||||
const auto github_version_cmd = fmt::format(R"(curl -m 2 -sL {} | grep "Corresponding commit:" | cut -d " " -f 8 | grep -o '[[:digit:]]*')", latest_link);
|
||||
#endif
|
||||
char tmpver[L_tmpnam + 1];
|
||||
std::tmpnam(tmpver);
|
||||
std::string ver_cmd = github_version_cmd + " >> " + tmpver;
|
||||
std::system(ver_cmd.c_str());
|
||||
std::ifstream ver(tmpver, std::ios::in | std::ios::binary);
|
||||
std::string version;
|
||||
std::getline(ver, version);
|
||||
ver.close();
|
||||
std::remove(tmpver);
|
||||
const auto version = https::get_web_regex_result(latest_link, std::regex("Vita3K Build: (\\d+)"));
|
||||
if (!version.empty() && std::isdigit(version[0]))
|
||||
git_version = std::stoi(version);
|
||||
else {
|
||||
@@ -102,66 +73,47 @@ bool init_vita3k_update(GuiState &gui) {
|
||||
page_count.push_back({ page, per_page });
|
||||
}
|
||||
|
||||
uint32_t commit_pos = 0;
|
||||
// Browse all page
|
||||
for (const auto &page : page_count) {
|
||||
const auto continuous_link = fmt::format(R"(https://api.github.com/repos/Vita3K/Vita3K/commits?sha=continuous&page={}&per_page={})", page.first, dif_from_current < 100 ? dif_from_current : 100);
|
||||
#ifdef WIN32
|
||||
const auto github_commit_sha_cmd = fmt::format(R"(powershell ((Invoke-RestMethod \"{}\" -Timeout 2).sha ^| Select-Object -first {}))", continuous_link, page.second);
|
||||
const auto github_commit_msg_cmd = fmt::format(R"(powershell ((Invoke-RestMethod \"{}\" -Timeout 2).commit.message ^| Select-Object -first {}) -replace(\"\r\n\", \"`n\"))", continuous_link, page.second);
|
||||
#else
|
||||
const auto sha_filter = R"(grep '"sha":' | sed 's/"/ /g' | awk -v n=3 'NR%n==1' | awk '{print $3}')";
|
||||
const auto github_commit_sha_cmd = fmt::format(R"(curl -m 2 -sL "{}" | {} | head -n {})", continuous_link, sha_filter, page.second);
|
||||
const auto github_commit_msg_cmd = fmt::format(R"(curl -m 2 -sL "{}" | grep '"message":' | cut -d '"' -f4 | sed 's/",/ /g' | head -n {})", continuous_link, page.second);
|
||||
#endif
|
||||
std::string line;
|
||||
|
||||
// Get Commits SHA
|
||||
char tmpsha[L_tmpnam + 1];
|
||||
std::tmpnam(tmpsha);
|
||||
std::string sha_cmd = github_commit_sha_cmd + " >> " + tmpsha;
|
||||
std::system(sha_cmd.c_str());
|
||||
std::ifstream sha_list(tmpsha, std::ios::in | std::ios::binary);
|
||||
while (std::getline(sha_list, line)) {
|
||||
git_commit_desc_list.push_back({ line, {} });
|
||||
}
|
||||
sha_list.close();
|
||||
std::remove(tmpsha);
|
||||
// Get response from github api
|
||||
auto response = https::get_web_response(continuous_link);
|
||||
|
||||
// Get Commits Message
|
||||
char tmpmsg[L_tmpnam + 1];
|
||||
std::tmpnam(tmpmsg);
|
||||
std::string msg_cmd = github_commit_msg_cmd + " >> " + tmpmsg;
|
||||
std::system(msg_cmd.c_str());
|
||||
std::ifstream msg_list(tmpmsg, std::ios::in | std::ios::binary);
|
||||
const auto push_commit = [&](const std::string commit) {
|
||||
if (git_commit_desc_list.size() < commit_pos) {
|
||||
LOG_WARN("Error of get commit description, abort");
|
||||
return false;
|
||||
// Check if response is not empty
|
||||
if (!response.empty()) {
|
||||
// Get commits from response with remove HTTP header
|
||||
std::string commits = response.substr(response.find("\r\n\r\n") + 4);
|
||||
std::string msg, sha;
|
||||
std::smatch match;
|
||||
|
||||
// Replace \" to " for help regex search message
|
||||
boost::replace_all(commits, "\\\"", """);
|
||||
|
||||
// Using regex to get sha from commits
|
||||
const std::regex commit_regex("\"sha\":\"([a-f0-9]{40})\".*?\"message\":\"([^\"]+)\"");
|
||||
while (std::regex_search(commits, match, commit_regex)) {
|
||||
// Get sha and message from regex match result
|
||||
sha = match[1];
|
||||
msg = match[2];
|
||||
|
||||
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");
|
||||
|
||||
// Add commit to list
|
||||
git_commit_desc_list.push_back({ sha, msg });
|
||||
}
|
||||
|
||||
// Remove current commit for next search
|
||||
const std::regex end_commit(R"(\s*"parents":\[\{"sha":"[a-f0-9]{40}","url":"[^"]+","html_url":"[^"]+"\}\]\})");
|
||||
if (std::regex_search(commits, match, end_commit))
|
||||
commits = match.suffix();
|
||||
}
|
||||
|
||||
git_commit_desc_list[commit_pos].second = commit;
|
||||
++commit_pos;
|
||||
return true;
|
||||
};
|
||||
std::string commit_msg;
|
||||
while (std::getline(msg_list, line)) {
|
||||
#ifdef WIN32
|
||||
if (line.find(static_cast<char>('\r\n')) != std::string::npos) {
|
||||
commit_msg += line;
|
||||
if (!push_commit(commit_msg))
|
||||
break;
|
||||
commit_msg.clear();
|
||||
} else
|
||||
commit_msg += line + "\n";
|
||||
#else
|
||||
// Contrary to windows, each line is a commit
|
||||
boost::replace_all(line, "\\n", "\n");
|
||||
if (!push_commit(line))
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
msg_list.close();
|
||||
remove(tmpmsg);
|
||||
}
|
||||
});
|
||||
get_commit_desc.detach();
|
||||
@@ -180,48 +132,46 @@ bool init_vita3k_update(GuiState &gui) {
|
||||
return has_update;
|
||||
}
|
||||
|
||||
static void download_update() {
|
||||
#ifndef __APPLE__
|
||||
const std::string path = "vita3k-latest.zip";
|
||||
#else
|
||||
struct passwd *pwent = getpwuid(getuid());
|
||||
const std::string path = fmt::format("{}/vita3k-latest.dmg", pwent->pw_dir);
|
||||
#endif
|
||||
if (!fs::exists(path)) {
|
||||
std::thread download([path]() {
|
||||
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";
|
||||
#ifdef WIN32
|
||||
const auto download_command = "powershell Invoke-WebRequest https://github.com/Vita3K/Vita3K/releases/download/continuous/windows-latest.zip -OutFile vita3k-latest.zip";
|
||||
download_continuous_link += "/windows-latest.zip";
|
||||
#elif defined(__APPLE__)
|
||||
const auto download_command = "curl -L https://github.com/Vita3K/Vita3K/releases/download/continuous/macos-latest.dmg -o ~/vita3k-latest.dmg";
|
||||
download_continuous_link += "/macos-latest.dmg";
|
||||
#else
|
||||
const auto download_command = "curl -L https://github.com/Vita3K/Vita3K/releases/download/continuous/ubuntu-latest.zip -o ./vita3k-latest.zip";
|
||||
#endif
|
||||
LOG_INFO("Attempting to download and extract the latest Vita3K version {} in progress...", git_version);
|
||||
system(download_command);
|
||||
if (fs::exists(path)) {
|
||||
SDL_Event event;
|
||||
event.type = SDL_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
|
||||
#ifdef WIN32
|
||||
const auto vita3K_batch = "update-vita3k.bat";
|
||||
#elif defined(__APPLE__)
|
||||
char *base_path = SDL_GetBasePath();
|
||||
std::string batch = fmt::format("sh {}/update-vita3k.sh {}/../../..", base_path, base_path);
|
||||
const auto vita3K_batch = batch.c_str();
|
||||
SDL_free(base_path);
|
||||
#else
|
||||
const auto vita3K_batch = "chmod +x ./update-vita3k.sh && ./update-vita3k.sh";
|
||||
download_continuous_link += "/ubuntu-latest.zip";
|
||||
#endif
|
||||
|
||||
std::system(vita3K_batch);
|
||||
} else {
|
||||
state = NOT_COMPLETE_UPDATE;
|
||||
LOG_WARN("Download failed, please try again later.");
|
||||
}
|
||||
});
|
||||
download.detach();
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
const std::string archive_ext = ".dmg";
|
||||
#else
|
||||
const std::string archive_ext = ".zip";
|
||||
#endif
|
||||
|
||||
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)) {
|
||||
SDL_Event event;
|
||||
event.type = SDL_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
|
||||
#ifdef WIN32
|
||||
const auto vita3K_batch = fmt::format("{}/update-vita3k.bat", base_path);
|
||||
#elif defined(__APPLE__)
|
||||
const auto vita3K_batch = fmt::format("sh {}/update-vita3k.sh", base_path);
|
||||
#else
|
||||
const auto vita3K_batch = fmt::format("chmod +x {}/update-vita3k.sh && {}/update-vita3k.sh", base_path, base_path);
|
||||
#endif
|
||||
|
||||
std::system(vita3K_batch.c_str());
|
||||
} else {
|
||||
state = NOT_COMPLETE_UPDATE;
|
||||
LOG_WARN("Download failed, please try again later.");
|
||||
}
|
||||
});
|
||||
download.detach();
|
||||
}
|
||||
|
||||
void draw_vita3k_update(GuiState &gui, EmuEnvState &emuenv) {
|
||||
@@ -346,7 +296,7 @@ void draw_vita3k_update(GuiState &gui, EmuEnvState &emuenv) {
|
||||
if (ImGui::Button(state < UPDATE_VITA3K ? lang["next"].c_str() : lang["update"].c_str(), BUTTON_SIZE) || ImGui::IsKeyPressed(emuenv.cfg.keyboard_button_circle)) {
|
||||
state = (Vita3kUpdate)(state + 1);
|
||||
if (state == DOWNLOAD)
|
||||
download_update();
|
||||
download_update(emuenv.base_path);
|
||||
}
|
||||
if (state == DOWNLOAD)
|
||||
ImGui::EndDisabled();
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
add_library(
|
||||
https
|
||||
STATIC
|
||||
include/https/functions.h
|
||||
src/https.cpp
|
||||
)
|
||||
|
||||
target_include_directories(https PUBLIC include)
|
||||
target_include_directories(https PRIVATE ${OPENSSL_INCLUDE_DIR})
|
||||
target_link_libraries(https PUBLIC util)
|
||||
@@ -0,0 +1,29 @@
|
||||
// Vita3K emulator project
|
||||
// Copyright (C) 2023 Vita3K team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
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);
|
||||
|
||||
} // namespace https
|
||||
@@ -0,0 +1,235 @@
|
||||
// Vita3K emulator project
|
||||
// Copyright (C) 2023 Vita3K team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include <https/functions.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <io.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
typedef SOCKET abs_socket;
|
||||
#else
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
typedef int abs_socket;
|
||||
#endif
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#include <util/log.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace https {
|
||||
|
||||
static void close_socket(const abs_socket sockfd) {
|
||||
#ifdef WIN32
|
||||
closesocket(sockfd);
|
||||
WSACleanup();
|
||||
#else
|
||||
close(sockfd);
|
||||
#endif // WIN32
|
||||
}
|
||||
|
||||
static void close_ssl(SSL *ssl) {
|
||||
SSL_shutdown(ssl);
|
||||
SSL_free(ssl);
|
||||
}
|
||||
|
||||
constexpr int READ_BUFFER_SIZE = 1048;
|
||||
std::string get_web_response(const std::string url) {
|
||||
#ifdef WIN32
|
||||
WORD versionWanted = MAKEWORD(2, 2);
|
||||
WSADATA wsaData;
|
||||
WSAStartup(versionWanted, &wsaData);
|
||||
#endif
|
||||
|
||||
// Initialize SSL
|
||||
const auto ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
if (!ctx) {
|
||||
LOG_ERROR("Error creating SSL context");
|
||||
return {};
|
||||
}
|
||||
|
||||
// Create socket
|
||||
const auto sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sockfd < 0) {
|
||||
LOG_ERROR("ERROR opening socket: {}", sockfd);
|
||||
SSL_CTX_free(ctx);
|
||||
return {};
|
||||
}
|
||||
|
||||
// Parse URL to get host and uri
|
||||
std::string host, uri;
|
||||
size_t start = url.find("://");
|
||||
if (start != std::string::npos) {
|
||||
start += 3; // skip "://"
|
||||
size_t end = url.find("/", start);
|
||||
if (end != std::string::npos) {
|
||||
host = url.substr(start, end - start);
|
||||
uri = url.substr(end);
|
||||
} else {
|
||||
host = url.substr(start);
|
||||
uri = "/";
|
||||
}
|
||||
}
|
||||
|
||||
// Set address info option
|
||||
const addrinfo hints = {
|
||||
AI_PASSIVE, /* For wildcard IP address */
|
||||
AF_UNSPEC, /* Allow IPv4 or IPv6 */
|
||||
SOCK_DGRAM, /* Datagram socket */
|
||||
0, /* Any protocol */
|
||||
};
|
||||
addrinfo *result = { 0 };
|
||||
|
||||
// Get address info with using host and port
|
||||
auto ret = getaddrinfo(host.c_str(), "https", &hints, &result);
|
||||
if (ret < 0) {
|
||||
LOG_ERROR("getaddrinfo = {}", ret);
|
||||
SSL_CTX_free(ctx);
|
||||
close_socket(sockfd);
|
||||
return {};
|
||||
}
|
||||
|
||||
// Connect to host
|
||||
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));
|
||||
SSL_CTX_free(ctx);
|
||||
close_socket(sockfd);
|
||||
return {};
|
||||
}
|
||||
|
||||
// Create and connect SSL
|
||||
SSL *ssl;
|
||||
ssl = SSL_new(ctx);
|
||||
SSL_set_fd(ssl, static_cast<uint32_t>(sockfd));
|
||||
if (SSL_connect(ssl) <= 0) {
|
||||
char err_buf[256];
|
||||
ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf));
|
||||
LOG_ERROR("Error establishing SSL connection: {}", err_buf);
|
||||
SSL_CTX_free(ctx);
|
||||
close_ssl(ssl);
|
||||
close_socket(sockfd);
|
||||
return {};
|
||||
}
|
||||
|
||||
// Send HTTP GET request to extracted URI
|
||||
std::string request = "GET " + 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";
|
||||
|
||||
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));
|
||||
SSL_CTX_free(ctx);
|
||||
close_ssl(ssl);
|
||||
close_socket(sockfd);
|
||||
LOG_ERROR("Error sending request: {}", err_buf);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::array<char, READ_BUFFER_SIZE> read_buffer{};
|
||||
std::string response;
|
||||
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);
|
||||
}
|
||||
|
||||
SSL_CTX_free(ctx);
|
||||
close_ssl(ssl);
|
||||
close_socket(sockfd);
|
||||
|
||||
boost::trim(response);
|
||||
|
||||
// Check if the response is resource not found
|
||||
if (response.find("HTTP/1.1 404 Not Found") != std::string::npos) {
|
||||
LOG_ERROR("404 Not Found");
|
||||
return {};
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
std::string get_web_regex_result(const std::string url, const std::regex regex) {
|
||||
std::string result;
|
||||
|
||||
// Get the response of the web
|
||||
const auto response = https::get_web_response(url);
|
||||
|
||||
// Check if the response is not empty
|
||||
if (!response.empty()) {
|
||||
// Get the content of the response (without headers)
|
||||
const std::string content = response.substr(response.find("\r\n\r\n") + 4);
|
||||
|
||||
std::smatch match;
|
||||
// Check if the content matches the regex
|
||||
if (std::regex_search(response, match, regex)) {
|
||||
result = match[1];
|
||||
} else
|
||||
LOG_ERROR("No success found regex: {}", content);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool download_file(const std::string url, const std::string output_file_path) {
|
||||
// Get the response of the app compat db
|
||||
auto response = get_web_response(url);
|
||||
|
||||
// Check if the response is not empty
|
||||
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 response of redirect url
|
||||
response = get_web_response(redirected_url);
|
||||
if (response.empty()) {
|
||||
LOG_ERROR("Failed to get response on redirected url: {}", redirected_url);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the content of the response
|
||||
std::string content = response.substr(response.find("\r\n\r\n") + 4);
|
||||
|
||||
// Create the output file
|
||||
std::ofstream output_file(output_file_path, std::ios::binary);
|
||||
if (!output_file.is_open()) {
|
||||
LOG_ERROR("Failed to open output file: {}", output_file_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write the content to the output file
|
||||
output_file.write(content.c_str(), content.length());
|
||||
output_file.close();
|
||||
|
||||
return fs::exists(output_file_path);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace https
|
||||
Reference in New Issue
Block a user