mirror of
https://github.com/cemu-project/Cemu.git
synced 2024-11-21 16:49:19 +01:00
Minor tweaks and code clean up (#357)
This commit is contained in:
parent
b07e9efba4
commit
2b9edced81
1
.gitignore
vendored
1
.gitignore
vendored
@ -34,6 +34,7 @@ bin/network_services.xml
|
||||
bin/title_list_cache.xml
|
||||
bin/debugger/*
|
||||
bin/sdcard/*
|
||||
bin/screenshots/*
|
||||
|
||||
!bin/shaderCache/info.txt
|
||||
bin/shaderCache/*
|
||||
|
@ -498,23 +498,27 @@ namespace iosu
|
||||
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, task_header_callback);
|
||||
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &(*it));
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 0x3C);
|
||||
if (GetNetworkConfig().disablesslver.GetValue() && ActiveSettings::GetNetworkService() == NetworkService::Custom || ActiveSettings::GetNetworkService() == NetworkService::Pretendo){ //Remove Pretendo Function once SSL is in the Service
|
||||
if (GetNetworkConfig().disablesslver.GetValue() && ActiveSettings::GetNetworkService() == NetworkService::Custom || ActiveSettings::GetNetworkService() == NetworkService::Pretendo) // remove Pretendo Function once SSL is in the Service
|
||||
{
|
||||
curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,0L);
|
||||
} else {
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, task_sslctx_function);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, &it->task_settings);
|
||||
curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_0);
|
||||
}
|
||||
else
|
||||
{
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, task_sslctx_function);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, &it->task_settings);
|
||||
curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_0);
|
||||
}
|
||||
|
||||
char url[512];
|
||||
std::string requestUrl;
|
||||
if(it->task_settings.taskType == kRawDlTaskSetting)
|
||||
{
|
||||
char serviceToken[TaskSetting::kServiceTokenLen];
|
||||
strncpy(serviceToken, (char*)&it->task_settings.settings[TaskSetting::kServiceToken], TaskSetting::kServiceTokenLen);
|
||||
list_headerParam = append_header_param(list_headerParam, "X-Nintendo-ServiceToken: {}", serviceToken);
|
||||
|
||||
char url[TaskSetting::kURLLen + 1]{};
|
||||
strncpy(url, (char*)&it->task_settings.settings[TaskSetting::kURL], TaskSetting::kURLLen);
|
||||
forceLogDebug_printf("\tserviceToken: %s", serviceToken);
|
||||
requestUrl.assign(url);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -566,26 +570,25 @@ namespace iosu
|
||||
|
||||
char boss_code[0x20];
|
||||
strncpy(boss_code, (char*)&it->task_settings.settings[TaskSetting::kBossCode], TaskSetting::kBossCodeLen);
|
||||
|
||||
switch (ActiveSettings::GetNetworkService())
|
||||
{
|
||||
case NetworkService::Nintendo:
|
||||
sprintf(url, NintendoURLs::BOSSURL.append("/%s/%s/%s?c=%s&l=%s").c_str(), "1", boss_code, it->task_id, countryCode, languageCode);
|
||||
break;
|
||||
case NetworkService::Pretendo:
|
||||
sprintf(url, PretendoURLs::BOSSURL.append("/%s/%s/%s?c=%s&l=%s").c_str(), "1", boss_code, it->task_id, countryCode, languageCode);
|
||||
requestUrl = PretendoURLs::BOSSURL;
|
||||
break;
|
||||
case NetworkService::Custom:
|
||||
sprintf(url, GetNetworkConfig().urls.BOSS.GetValue().append("/%s/%s/%s?c=%s&l=%s").c_str(), "1", boss_code, it->task_id, countryCode, languageCode);
|
||||
requestUrl = GetNetworkConfig().urls.BOSS.GetValue();
|
||||
break;
|
||||
case NetworkService::Nintendo:
|
||||
default:
|
||||
sprintf(url, NintendoURLs::BOSSURL.append("/%s/%s/%s?c=%s&l=%s").c_str(), "1", boss_code, it->task_id, countryCode, languageCode);
|
||||
requestUrl = NintendoURLs::BOSSURL;
|
||||
break;
|
||||
}
|
||||
requestUrl.append(fmt::format(fmt::runtime("/{}/{}/{}?c={}&l={}"), "1", boss_code, it->task_id, countryCode, languageCode));
|
||||
}
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list_headerParam);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
forceLogDebug_printf("task_run url %s", url);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, requestUrl.c_str());
|
||||
|
||||
int curl_result = curl_easy_perform(curl);
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &it->http_status_code);
|
||||
|
@ -57,21 +57,23 @@ namespace NAPI
|
||||
std::vector<uint8> IDBE_RequestRawEncrypted(uint64 titleId)
|
||||
{
|
||||
CurlRequestHelper req;
|
||||
switch (ActiveSettings::GetNetworkService())
|
||||
{
|
||||
case NetworkService::Nintendo:
|
||||
req.initate(fmt::format(fmt::runtime(NintendoURLs::IDBEURL + "/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE);
|
||||
break;
|
||||
case NetworkService::Pretendo:
|
||||
req.initate(fmt::format(fmt::runtime(PretendoURLs::IDBEURL + "/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE);
|
||||
break;
|
||||
case NetworkService::Custom:
|
||||
req.initate(fmt::format(fmt::runtime(GetNetworkConfig().urls.IDBE.GetValue() + "/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE);
|
||||
break;
|
||||
default:
|
||||
req.initate(fmt::format(fmt::runtime(NintendoURLs::IDBEURL + "/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE);
|
||||
break;
|
||||
}
|
||||
std::string requestUrl;
|
||||
switch (ActiveSettings::GetNetworkService())
|
||||
{
|
||||
case NetworkService::Pretendo:
|
||||
requestUrl = PretendoURLs::IDBEURL;
|
||||
break;
|
||||
case NetworkService::Custom:
|
||||
requestUrl = GetNetworkConfig().urls.IDBE.GetValue();
|
||||
break;
|
||||
case NetworkService::Nintendo:
|
||||
default:
|
||||
requestUrl = NintendoURLs::IDBEURL;
|
||||
break;
|
||||
}
|
||||
requestUrl.append(fmt::format(fmt::runtime("/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId));
|
||||
req.initate(requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE);
|
||||
|
||||
if (!req.submitRequest(false))
|
||||
{
|
||||
cemuLog_log(LogType::Force, fmt::format("Failed to request IDBE icon for title {0:016X}", titleId));
|
||||
@ -99,59 +101,30 @@ namespace NAPI
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
CurlRequestHelper req;
|
||||
switch (ActiveSettings::GetNetworkService())
|
||||
{
|
||||
case NetworkService::Nintendo:
|
||||
req.initate(fmt::format(fmt::runtime(NintendoURLs::IDBEURL + "/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE);
|
||||
break;
|
||||
case NetworkService::Pretendo:
|
||||
req.initate(fmt::format(fmt::runtime(PretendoURLs::IDBEURL + "/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE);
|
||||
break;
|
||||
case NetworkService::Custom:
|
||||
req.initate(fmt::format(fmt::runtime(GetNetworkConfig().urls.IDBE.GetValue() + "/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE);
|
||||
break;
|
||||
default:
|
||||
req.initate(fmt::format(fmt::runtime(NintendoURLs::IDBEURL + "/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!req.submitRequest(false))
|
||||
{
|
||||
cemuLog_log(LogType::Force, fmt::format("Failed to request IDBE icon for title {0:016X}", titleId));
|
||||
std::vector<uint8> idbeData = IDBE_RequestRawEncrypted(titleId);
|
||||
if (idbeData.size() < 0x22)
|
||||
return std::nullopt;
|
||||
}
|
||||
/*
|
||||
format:
|
||||
+0x00 uint8 version (0)
|
||||
+0x01 uint8 keyIndex
|
||||
+0x02 uint8[32] hashSHA256
|
||||
+0x22 uint8[] EncryptedIconData
|
||||
*/
|
||||
auto& receivedData = req.getReceivedData();
|
||||
if (receivedData.size() < 0x22)
|
||||
return std::nullopt;
|
||||
if (receivedData[0] != 0)
|
||||
if (idbeData[0] != 0)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "IDBE_Request: File has invalid version");
|
||||
return std::nullopt;
|
||||
}
|
||||
uint8 keyIndex = receivedData[1];
|
||||
uint8 keyIndex = idbeData[1];
|
||||
if (keyIndex >= 4)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "IDBE_Request: Key index out of range");
|
||||
return std::nullopt;
|
||||
}
|
||||
if (receivedData.size() < (0x22 + sizeof(IDBEIconDataV0)))
|
||||
if (idbeData.size() < (0x22 + sizeof(IDBEIconDataV0)))
|
||||
{
|
||||
cemuLog_log(LogType::Force, "IDBE_Request: File size does not match");
|
||||
return std::nullopt;
|
||||
}
|
||||
// extract hash and encrypted icon data
|
||||
uint8 hash[32];
|
||||
std::memcpy(hash, receivedData.data() + 0x2, 32);
|
||||
std::memcpy(hash, idbeData.data() + 0x2, 32);
|
||||
IDBEIconDataV0 iconDataV0;
|
||||
std::memcpy(&iconDataV0, receivedData.data() + 0x22, sizeof(IDBEIconDataV0));
|
||||
std::memcpy(&iconDataV0, idbeData.data() + 0x22, sizeof(IDBEIconDataV0));
|
||||
// decrypt icon data and hash
|
||||
_decryptIDBEAndHash(&iconDataV0, hash, keyIndex);
|
||||
// verify hash of decrypted data
|
||||
|
@ -16,21 +16,23 @@ namespace NAPI
|
||||
{
|
||||
NAPI_VersionListVersion_Result result;
|
||||
CurlRequestHelper req;
|
||||
|
||||
std::string requestUrl;
|
||||
switch (ActiveSettings::GetNetworkService())
|
||||
{
|
||||
case NetworkService::Nintendo:
|
||||
req.initate(fmt::format(fmt::runtime(NintendoURLs::TAGAYAURL + "/{}/{}/latest_version"), NCrypto::GetRegionAsString(authInfo.region), authInfo.country), CurlRequestHelper::SERVER_SSL_CONTEXT::TAGAYA);
|
||||
break;
|
||||
case NetworkService::Pretendo:
|
||||
req.initate(fmt::format(fmt::runtime(PretendoURLs::TAGAYAURL + "/{}/{}/latest_version"), NCrypto::GetRegionAsString(authInfo.region), authInfo.country), CurlRequestHelper::SERVER_SSL_CONTEXT::TAGAYA);
|
||||
requestUrl = PretendoURLs::TAGAYAURL;
|
||||
break;
|
||||
case NetworkService::Custom:
|
||||
req.initate(fmt::format(fmt::runtime(GetNetworkConfig().urls.TAGAYA.GetValue() + "/{}/{}/latest_version"), NCrypto::GetRegionAsString(authInfo.region), authInfo.country), CurlRequestHelper::SERVER_SSL_CONTEXT::TAGAYA);
|
||||
requestUrl = GetNetworkConfig().urls.TAGAYA.GetValue();
|
||||
break;
|
||||
case NetworkService::Nintendo:
|
||||
default:
|
||||
req.initate(fmt::format(fmt::runtime(NintendoURLs::TAGAYAURL + "/{}/{}/latest_version"), NCrypto::GetRegionAsString(authInfo.region), authInfo.country), CurlRequestHelper::SERVER_SSL_CONTEXT::TAGAYA);
|
||||
requestUrl = NintendoURLs::TAGAYAURL;
|
||||
break;
|
||||
}
|
||||
requestUrl.append(fmt::format(fmt::runtime("/{}/{}/latest_version"), NCrypto::GetRegionAsString(authInfo.region), authInfo.country));
|
||||
req.initate(requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::TAGAYA);
|
||||
|
||||
if (!req.submitRequest(false))
|
||||
{
|
||||
|
@ -196,8 +196,6 @@ bool LaunchSettings::HandleCommandline(const std::vector<std::wstring>& args)
|
||||
errorMsg.append("Error while trying to parse command line parameter:\n");
|
||||
errorMsg.append(ex.what());
|
||||
wxMessageBox(errorMsg, wxT("Parameter error"), wxICON_ERROR);
|
||||
//cemuLog_log(LogType::Force, ex.what());
|
||||
//std::cout << "Command line parameter error: " << ex.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -270,10 +268,6 @@ void LaunchSettings::ChangeNetworkServiceURL(int ID){
|
||||
NetworkService Network = static_cast<NetworkService>(ID);
|
||||
switch (Network)
|
||||
{
|
||||
case NetworkService::Nintendo:
|
||||
serviceURL_ACT = NintendoURLs::ACTURL;
|
||||
serviceURL_ECS = NintendoURLs::ECSURL;
|
||||
break;
|
||||
case NetworkService::Pretendo:
|
||||
serviceURL_ACT = PretendoURLs::ACTURL;
|
||||
serviceURL_ECS = PretendoURLs::ECSURL;
|
||||
@ -282,10 +276,10 @@ void LaunchSettings::ChangeNetworkServiceURL(int ID){
|
||||
serviceURL_ACT = GetNetworkConfig().urls.ACT.GetValue();
|
||||
serviceURL_ECS = GetNetworkConfig().urls.ECS.GetValue();
|
||||
break;
|
||||
case NetworkService::Nintendo:
|
||||
default:
|
||||
serviceURL_ACT = NintendoURLs::ACTURL;
|
||||
serviceURL_ECS = NintendoURLs::ECSURL;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -3,41 +3,48 @@
|
||||
#include "CemuConfig.h"
|
||||
#include <boost/dll/runtime_symbol_info.hpp>
|
||||
#include "Common/FileStream.h"
|
||||
|
||||
XMLNetworkConfig_t n_config(L"network_services.xml");
|
||||
|
||||
void NetworkConfig::LoadOnce() {
|
||||
s_full_path = boost::dll::program_location().generic_wstring();
|
||||
void NetworkConfig::LoadOnce()
|
||||
{
|
||||
s_full_path = boost::dll::program_location().generic_wstring();
|
||||
s_path = s_full_path.parent_path();
|
||||
n_config.SetFilename(GetPath("network_services.xml").generic_wstring());
|
||||
if(XMLExists()) {
|
||||
n_config.Load();
|
||||
}
|
||||
n_config.SetFilename(GetPath("network_services.xml").generic_wstring());
|
||||
if (XMLExists())
|
||||
n_config.Load();
|
||||
}
|
||||
|
||||
void NetworkConfig::Load(XMLConfigParser& parser){
|
||||
auto config = parser.get("content");
|
||||
networkname = config.get("networkname","[Nintendo]");
|
||||
disablesslver = config.get("disablesslverification",disablesslver);
|
||||
auto u = config.get("urls");
|
||||
urls.ACT = u.get("act",NintendoURLs::ACTURL);
|
||||
urls.ECS = u.get("ecs",NintendoURLs::ECSURL);
|
||||
urls.NUS = u.get("nus",NintendoURLs::NUSURL);
|
||||
urls.IAS = u.get("ias",NintendoURLs::IASURL);
|
||||
urls.CCSU = u.get("ccsu",NintendoURLs::CCSUURL);
|
||||
urls.CCS = u.get("ccs",NintendoURLs::CCSURL);
|
||||
urls.IDBE = u.get("idbe",NintendoURLs::IDBEURL);
|
||||
urls.BOSS = u.get("boss",NintendoURLs::BOSSURL);
|
||||
urls.TAGAYA = u.get("tagaya",NintendoURLs::TAGAYAURL);
|
||||
if (static_cast<NetworkService>(GetConfig().account.active_service.GetValue()) == NetworkService::Custom) {
|
||||
LaunchSettings::ChangeNetworkServiceURL(2);
|
||||
}
|
||||
void NetworkConfig::Load(XMLConfigParser& parser)
|
||||
{
|
||||
auto config = parser.get("content");
|
||||
networkname = config.get("networkname", "Custom");
|
||||
disablesslver = config.get("disablesslverification", disablesslver);
|
||||
auto u = config.get("urls");
|
||||
urls.ACT = u.get("act", NintendoURLs::ACTURL);
|
||||
urls.ECS = u.get("ecs", NintendoURLs::ECSURL);
|
||||
urls.NUS = u.get("nus", NintendoURLs::NUSURL);
|
||||
urls.IAS = u.get("ias", NintendoURLs::IASURL);
|
||||
urls.CCSU = u.get("ccsu", NintendoURLs::CCSUURL);
|
||||
urls.CCS = u.get("ccs", NintendoURLs::CCSURL);
|
||||
urls.IDBE = u.get("idbe", NintendoURLs::IDBEURL);
|
||||
urls.BOSS = u.get("boss", NintendoURLs::BOSSURL);
|
||||
urls.TAGAYA = u.get("tagaya", NintendoURLs::TAGAYAURL);
|
||||
if (static_cast<NetworkService>(GetConfig().account.active_service.GetValue()) == NetworkService::Custom)
|
||||
LaunchSettings::ChangeNetworkServiceURL(2);
|
||||
}
|
||||
bool NetworkConfig::XMLExists() {
|
||||
if (!fs::exists(GetPath("network_services.xml"))) {
|
||||
if (static_cast<NetworkService>(GetConfig().account.active_service.GetValue()) == NetworkService::Custom) {
|
||||
LaunchSettings::ChangeNetworkServiceURL(0);
|
||||
GetConfig().account.active_service = 0;
|
||||
}
|
||||
return false;
|
||||
} else {return true;}
|
||||
|
||||
bool NetworkConfig::XMLExists()
|
||||
{
|
||||
std::error_code ec;
|
||||
if (!fs::exists(GetPath("network_services.xml"), ec))
|
||||
{
|
||||
if (static_cast<NetworkService>(GetConfig().account.active_service.GetValue()) == NetworkService::Custom)
|
||||
{
|
||||
LaunchSettings::ChangeNetworkServiceURL(0);
|
||||
GetConfig().account.active_service = 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
@ -605,14 +605,12 @@ wxPanel* GeneralSettings2::AddAccountPage(wxNotebook* notebook)
|
||||
content->Add(m_delete_account, 0, wxEXPAND | wxALL | wxALIGN_RIGHT, 5);
|
||||
m_delete_account->Bind(wxEVT_BUTTON, &GeneralSettings2::OnAccountDelete, this);
|
||||
|
||||
if (NetworkConfig::XMLExists()) {
|
||||
wxString choices[] = { _("Nintendo"), _("Pretendo"), _("Custom") };
|
||||
m_active_service= new wxRadioBox(online_panel, wxID_ANY, _("Network Service"), wxDefaultPosition, wxDefaultSize, std::size(choices), choices, 3, wxRA_SPECIFY_COLS);
|
||||
}
|
||||
else {
|
||||
wxString choices[] = { _("Nintendo"), _("Pretendo") };
|
||||
m_active_service= new wxRadioBox(online_panel, wxID_ANY, _("Network Service"), wxDefaultPosition, wxDefaultSize, std::size(choices), choices, 2, wxRA_SPECIFY_COLS);
|
||||
}
|
||||
wxString choices[] = { _("Nintendo"), _("Pretendo"), _("Custom") };
|
||||
m_active_service = new wxRadioBox(online_panel, wxID_ANY, _("Network Service"), wxDefaultPosition, wxDefaultSize, std::size(choices), choices, 3, wxRA_SPECIFY_COLS);
|
||||
if (!NetworkConfig::XMLExists())
|
||||
m_active_service->Enable(2, false);
|
||||
|
||||
|
||||
m_active_service->SetToolTip(_("Connect to which Network Service"));
|
||||
m_active_service->Bind(wxEVT_RADIOBOX, &GeneralSettings2::OnAccountServiceChanged,this);
|
||||
content->Add(m_active_service, 0, wxEXPAND | wxALL, 5);
|
||||
|
@ -97,17 +97,14 @@ void gui_updateWindowTitles(bool isIdle, bool isLoading, double fps)
|
||||
const uint64 titleId = CafeSystem::GetForegroundTitleId();
|
||||
windowText.append(fmt::format(" - FPS: {:.2f} {} {} [TitleId: {:08x}-{:08x}]", (double)fps, renderer, graphicMode, (uint32)(titleId >> 32), (uint32)(titleId & 0xFFFFFFFF)));
|
||||
|
||||
if (ActiveSettings::IsOnlineEnabled()){
|
||||
windowText.append(" [Online]");
|
||||
if (ActiveSettings::GetNetworkService() == NetworkService::Nintendo) {
|
||||
windowText.append("[Nintendo]");
|
||||
}
|
||||
else if (ActiveSettings::GetNetworkService() == NetworkService::Pretendo) {
|
||||
windowText.append("[Pretendo]");
|
||||
}
|
||||
else if (ActiveSettings::GetNetworkService() == NetworkService::Custom) {
|
||||
windowText.append("[" + GetNetworkConfig().networkname.GetValue() + "]");
|
||||
}
|
||||
if (ActiveSettings::IsOnlineEnabled())
|
||||
{
|
||||
if (ActiveSettings::GetNetworkService() == NetworkService::Nintendo)
|
||||
windowText.append(" [Online]");
|
||||
else if (ActiveSettings::GetNetworkService() == NetworkService::Pretendo)
|
||||
windowText.append(" [Online-Pretendo]");
|
||||
else if (ActiveSettings::GetNetworkService() == NetworkService::Custom)
|
||||
windowText.append(" [Online-" + GetNetworkConfig().networkname.GetValue() + "]");
|
||||
}
|
||||
windowText.append(" ");
|
||||
windowText.append(CafeSystem::GetForegroundTitleName());
|
||||
|
Loading…
Reference in New Issue
Block a user