1389 строки
46 KiB
C++
1389 строки
46 KiB
C++
// Copyright (c) Microsoft Corporation
|
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
|
|
#include "pch.h"
|
|
#include "xsapi_utils.h"
|
|
#include "xbox_live_app_config_internal.h"
|
|
#include <iomanip>
|
|
#include <chrono>
|
|
#include <time.h>
|
|
#include <string>
|
|
#if !HC_PLATFORM_IS_MICROSOFT
|
|
#include "xbl_guid.h"
|
|
#elif defined(_WIN32)
|
|
#include <objbase.h>
|
|
#endif
|
|
#if HC_PLATFORM == HC_PLATFORM_ANDROID
|
|
#include "a/utils_a.h"
|
|
#include "a/java_interop.h"
|
|
#endif
|
|
#include "presence_internal.h"
|
|
#include "httpClient/httpClient.h"
|
|
#include "Logger/log_hc_output.h"
|
|
#include "global_state.h"
|
|
|
|
#if !_LINK_WITH_CPPRESTSDK && HC_PLATFORM != HC_PLATFORM_GDK
|
|
#include "cpprestsdk_impl.h"
|
|
#endif
|
|
|
|
#ifndef _XTIME_TICKS_PER_TIME_T
|
|
#define _XTIME_TICKS_PER_TIME_T 10000000LL
|
|
#endif
|
|
|
|
NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN
|
|
|
|
#define MAKE_HTTP_HRESULT(code) MAKE_HRESULT(1, 0x019, code)
|
|
|
|
static char const * _sdaPrefix = "AAAAAAAA";
|
|
|
|
static const uint64_t _msTicks = static_cast<uint64_t>(10000);
|
|
static const uint64_t _secondTicks = 1000*_msTicks;
|
|
|
|
xsapi_internal_string utils::encode_uri(
|
|
_In_ const xsapi_internal_string& data,
|
|
_In_ xbox::services::uri::components::component component
|
|
)
|
|
{
|
|
return xbox::services::uri::encode_uri(data, component);
|
|
}
|
|
|
|
xsapi_internal_string utils::headers_to_string(
|
|
_In_ const xsapi_internal_http_headers& headers
|
|
)
|
|
{
|
|
xsapi_internal_stringstream ss;
|
|
|
|
for (const auto& header : headers)
|
|
{
|
|
ss << header.first << ": " << header.second << "\r\n";
|
|
}
|
|
|
|
return ss.str();
|
|
}
|
|
|
|
xsapi_internal_string
|
|
utils::get_query_from_params(
|
|
_In_ const xsapi_internal_vector<xsapi_internal_string>& params
|
|
)
|
|
{
|
|
xsapi_internal_stringstream strQueryString;
|
|
|
|
size_t cItems = params.size();
|
|
if (cItems > 0)
|
|
{
|
|
xsapi_internal_string strDelimiter = "&";
|
|
|
|
strQueryString << "?";
|
|
strQueryString << params[0];
|
|
|
|
size_t i = 0;
|
|
while (++i < cItems)
|
|
{
|
|
strQueryString << strDelimiter;
|
|
strQueryString << params[i];
|
|
}
|
|
}
|
|
|
|
return strQueryString.str();
|
|
}
|
|
|
|
void utils::append_paging_info(
|
|
_In_ xbox::services::uri_builder& uriBuilder,
|
|
_In_ unsigned int skipItems,
|
|
_In_ unsigned int maxItems,
|
|
_In_opt_ xsapi_internal_string continuationToken
|
|
)
|
|
{
|
|
// add maxItem parameter
|
|
if (maxItems > 0)
|
|
{
|
|
uriBuilder.append_query("maxItems", maxItems);
|
|
}
|
|
|
|
if (continuationToken.empty())
|
|
{
|
|
// use skip items value if continuation token is empty
|
|
if (skipItems > 0)
|
|
{
|
|
uriBuilder.append_query("skipItems", skipItems);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uriBuilder.append_query("continuationToken", continuationToken);
|
|
}
|
|
}
|
|
|
|
#if defined(_WIN32)
|
|
uint32_t utils::convert_timespan_to_days(
|
|
_In_ uint64_t timespan
|
|
)
|
|
{
|
|
int64_t days = (timespan / _XTIME_TICKS_PER_TIME_T) / SECONDS_PER_DAY;
|
|
THROW_CPP_INVALIDARGUMENT_IF(days < 0 || days > UINT32_MAX);
|
|
|
|
return static_cast<uint32_t>(days);
|
|
}
|
|
|
|
void utils::convert_unix_time_to_filetime(
|
|
_In_ std::time_t t,
|
|
_In_ FILETIME* ft
|
|
)
|
|
{
|
|
if (!ft)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LONGLONG ll;
|
|
|
|
#ifdef _USE_32BIT_TIME_T
|
|
ll = Int32x32To64(t * 10000000) + 116444736000000000;
|
|
#else
|
|
ll = (t * 10000000) + 116444736000000000;
|
|
#endif
|
|
|
|
ft->dwLowDateTime = (DWORD)ll;
|
|
ft->dwHighDateTime = ll >> 32;
|
|
}
|
|
void utils::convert_timepoint_to_filetime(
|
|
_In_ const chrono_clock_t::time_point& time_point,
|
|
_Inout_ uint64_t& largeInt
|
|
)
|
|
{
|
|
// time_point to system time
|
|
std::time_t t = convert_timepoint_to_time(time_point);
|
|
// system time to FILETIME
|
|
FILETIME ft = { 0 };
|
|
convert_unix_time_to_filetime(t, &ft);
|
|
|
|
if (largeInt)
|
|
{
|
|
ULARGE_INTEGER large;
|
|
large.LowPart = ft.dwLowDateTime;
|
|
large.HighPart = ft.dwHighDateTime;
|
|
largeInt = large.QuadPart;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
std::time_t utils::convert_timepoint_to_time(
|
|
_In_ const chrono_clock_t::time_point& time_point
|
|
)
|
|
{
|
|
#if _MSC_VER <= 1800
|
|
return chrono_clock_t::to_time_t(time_point);
|
|
#else
|
|
uint64_t timeDiff = std::chrono::duration_cast<std::chrono::seconds>(time_point.time_since_epoch() - std::chrono::steady_clock::now().time_since_epoch()).count();
|
|
uint64_t timeNow = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
|
return timeNow + timeDiff;
|
|
#endif
|
|
}
|
|
|
|
xsapi_internal_string utils::convert_timepoint_to_string(
|
|
_In_ const chrono_clock_t::time_point& time_point
|
|
)
|
|
{
|
|
xsapi_internal_string result;
|
|
xsapi_internal_string::value_type buff[FILENAME_MAX];
|
|
std::chrono::milliseconds ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_point.time_since_epoch());
|
|
time_t t = utils::convert_timepoint_to_time(time_point);
|
|
std::tm time;
|
|
#if defined(_WIN32)
|
|
errno_t errorCode = localtime_s(&time, &t);
|
|
if (errorCode != 0)
|
|
{
|
|
return result;
|
|
}
|
|
sprintf_s(buff, "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ",
|
|
time.tm_year + 1900, time.tm_mon + 1, time.tm_mday,
|
|
time.tm_hour, time.tm_min, time.tm_sec, static_cast<int>(ms.count() % 1000));
|
|
#else
|
|
#if defined(PAVO)
|
|
std::tm* error = localtime_s(&t, &time);
|
|
#else
|
|
std::tm* error = localtime_r(&t, &time);
|
|
#endif // PAVO
|
|
if (error == nullptr)
|
|
{
|
|
return result;
|
|
}
|
|
snprintf(buff, sizeof(buff), _T("%04d-%02d-%02dT%02d:%02d:%02d.%03dZ"),
|
|
time.tm_year + 1900, time.tm_mon + 1, time.tm_mday,
|
|
time.tm_hour, time.tm_min, time.tm_sec, static_cast<int>(ms.count() % 1000));
|
|
|
|
#endif
|
|
result = buff;
|
|
|
|
return result;
|
|
}
|
|
|
|
xsapi_internal_string utils::escape_special_characters(const xsapi_internal_string& str)
|
|
{
|
|
xsapi_internal_string result = str;
|
|
for (auto iter = result.begin(); iter != result.end(); ++iter)
|
|
{
|
|
if (*iter == '\r' || *iter == '\n')
|
|
{
|
|
iter = result.insert(iter, ' ');
|
|
iter = result.erase(iter + 1);
|
|
--iter;
|
|
}
|
|
else if (*iter == '\"')
|
|
{
|
|
iter = result.insert(iter, '\"');
|
|
++iter;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
uint32_t
|
|
utils::char_t_copy(
|
|
_In_reads_bytes_(sizeInWords) char_t* destinationCharArr,
|
|
_In_ size_t sizeInWords,
|
|
_In_ const char_t* sourceCharArr
|
|
)
|
|
{
|
|
#if HC_PLATFORM_IS_MICROSOFT
|
|
return wcscpy_s(destinationCharArr, sizeInWords, sourceCharArr);
|
|
#else
|
|
return (uint32_t)strlcpy(destinationCharArr, sourceCharArr, (uint32_t)sizeInWords);
|
|
#endif
|
|
}
|
|
|
|
size_t
|
|
utils::strcpy(
|
|
_In_ char* destinationCharArr,
|
|
_In_ size_t sizeInWords,
|
|
_In_ const char* sourceCharArr
|
|
)
|
|
|
|
{
|
|
#if HC_PLATFORM_IS_MICROSOFT
|
|
return strcpy_s(destinationCharArr, sizeInWords, sourceCharArr);
|
|
#else
|
|
return strlcpy(destinationCharArr, sourceCharArr, sizeInWords);
|
|
#endif
|
|
}
|
|
|
|
HRESULT
|
|
utils::convert_exception_to_hresult()
|
|
{
|
|
// Default value, if there is no exception appears, return S_OK
|
|
HRESULT hr = S_OK;
|
|
|
|
try
|
|
{
|
|
throw;
|
|
}
|
|
// std exceptions
|
|
catch (const std::bad_alloc&) // is an exception
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
catch (const std::bad_cast&) // is an exception
|
|
{
|
|
hr = E_NOINTERFACE;
|
|
}
|
|
catch (const std::invalid_argument&) // is a logic_error
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
catch (const std::out_of_range&) // is a logic_error
|
|
{
|
|
hr = E_BOUNDS;
|
|
}
|
|
catch (const std::length_error&) // is a logic_error
|
|
{
|
|
hr = __HRESULT_FROM_WIN32(ERROR_BAD_LENGTH);
|
|
}
|
|
catch (const std::overflow_error&) // is a runtime_error
|
|
{
|
|
hr = __HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
|
|
}
|
|
catch (const std::underflow_error&) // is a runtime_error
|
|
{
|
|
hr = __HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
|
|
}
|
|
catch (const std::range_error&) // is a runtime_error
|
|
{
|
|
hr = E_BOUNDS;
|
|
}
|
|
catch (const std::system_error& ex) // is a runtime_error
|
|
{
|
|
if (ex.code().category() == std::system_category())
|
|
{
|
|
hr = __HRESULT_FROM_WIN32(ex.code().value());
|
|
}
|
|
else
|
|
{
|
|
hr = ex.code().value();
|
|
}
|
|
}
|
|
catch (const std::logic_error&) // is an exception
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
catch (const std::runtime_error&) // is an exception
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
#if !XSAPI_NO_PPL
|
|
catch (const web::http::http_exception&) // is an exception
|
|
{
|
|
hr = HTTP_E_STATUS_UNEXPECTED;
|
|
}
|
|
#endif // !XSAPI_NO_PPL
|
|
catch (const xbox::services::uri_exception&) // is an exception
|
|
{
|
|
hr = WEB_E_UNEXPECTED_CONTENT;
|
|
}
|
|
catch (const std::exception&) // base class for standard C++ exceptions
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
catch (HRESULT exceptionHR)
|
|
{
|
|
hr = exceptionHR;
|
|
}
|
|
catch (...) // everything else
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
utils::convert_xbox_live_error_code_to_hresult(
|
|
_In_ const std::error_code& errCode
|
|
)
|
|
{
|
|
int err = static_cast<int>(errCode.value());
|
|
xbl_error_code xblErr = static_cast<xbl_error_code>(err);
|
|
|
|
if (err == 204)
|
|
{
|
|
return __HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND);
|
|
}
|
|
else if (err >= 300 && err <= 505)
|
|
{
|
|
return (HRESULT)convert_http_status_to_hresult(err);
|
|
}
|
|
else if (err >= 1000 && err <= 9999)
|
|
{
|
|
switch (xblErr)
|
|
{
|
|
case xbl_error_code::bad_alloc: return E_OUTOFMEMORY;
|
|
case xbl_error_code::invalid_argument: return E_INVALIDARG;
|
|
case xbl_error_code::runtime_error: return E_XBL_RUNTIME_ERROR;
|
|
case xbl_error_code::length_error: return __HRESULT_FROM_WIN32(ERROR_BAD_LENGTH);
|
|
case xbl_error_code::out_of_range: return E_BOUNDS;
|
|
case xbl_error_code::range_error: return E_BOUNDS;
|
|
case xbl_error_code::bad_cast: return E_NOINTERFACE;
|
|
case xbl_error_code::logic_error: return E_UNEXPECTED;
|
|
case xbl_error_code::json_error: return WEB_E_INVALID_JSON_STRING;
|
|
case xbl_error_code::uri_error: return WEB_E_UNEXPECTED_CONTENT;
|
|
case xbl_error_code::websocket_error: return WEB_E_UNEXPECTED_CONTENT;
|
|
case xbl_error_code::auth_user_interaction_required: return ONL_E_ACTION_REQUIRED;
|
|
case xbl_error_code::rta_generic_error: return E_XBL_RTA_GENERIC_ERROR;
|
|
case xbl_error_code::rta_subscription_limit_reached: return E_XBL_RTA_SUBSCRIPTION_LIMIT_REACHED;
|
|
case xbl_error_code::rta_access_denied: return E_XBL_RTA_ACCESS_DENIED;
|
|
case xbl_error_code::auth_unknown_error: return E_XBL_AUTH_UNKNOWN_ERROR;
|
|
case xbl_error_code::auth_runtime_error: return E_XBL_AUTH_RUNTIME_ERROR;
|
|
case xbl_error_code::auth_no_token_error: return E_XBL_AUTH_NO_TOKEN;
|
|
case xbl_error_code::auth_user_not_signed_in: return __HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER);
|
|
case xbl_error_code::auth_user_cancel: return __HRESULT_FROM_WIN32(ERROR_CANCELLED);
|
|
case xbl_error_code::auth_user_switched: return __HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER);
|
|
case xbl_error_code::invalid_config: return __HRESULT_FROM_WIN32(ERROR_BAD_CONFIGURATION);
|
|
case xbl_error_code::unsupported: return E_NOTIMPL;
|
|
|
|
default: return E_FAIL;
|
|
}
|
|
}
|
|
else if ((err & 0x87DD0000) == 0x87D8000)
|
|
{
|
|
return HTTP_E_STATUS_UNEXPECTED_SERVER_ERROR;
|
|
}
|
|
else return err; //return the original error code if can't be translated.
|
|
}
|
|
|
|
xbl_error_code utils::convert_http_status_to_xbox_live_error_code(
|
|
_In_ uint32_t statusCode
|
|
)
|
|
{
|
|
if (statusCode < 300 || statusCode >= 600)
|
|
{
|
|
// Treat as success so
|
|
// if (!result.err())
|
|
// works properly which requires all non-errors to be 0.
|
|
return xbl_error_code::no_error;
|
|
}
|
|
else
|
|
{
|
|
return static_cast<xbl_error_code>(statusCode);
|
|
}
|
|
}
|
|
|
|
HRESULT utils::convert_http_status_to_hresult(_In_ uint32_t httpStatusCode)
|
|
{
|
|
xbl_error_code errCode = static_cast<xbl_error_code>(httpStatusCode);
|
|
HRESULT hr = HTTP_E_STATUS_UNEXPECTED;
|
|
|
|
// 2xx are http success codes
|
|
if ((httpStatusCode >= 200) && (httpStatusCode < 300))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
|
|
// MSXML XHR bug: get_status() returns HTTP/1223 for HTTP/204:
|
|
// http://blogs.msdn.com/b/ieinternals/archive/2009/07/23/the-ie8-native-xmlhttprequest-object.aspx
|
|
// treat it as success code as well
|
|
else if (httpStatusCode == 1223)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
switch (errCode)
|
|
{
|
|
case xbl_error_code::http_status_300_multiple_choices: hr = HTTP_E_STATUS_AMBIGUOUS; break;
|
|
case xbl_error_code::http_status_301_moved_permanently: hr = HTTP_E_STATUS_MOVED; break;
|
|
case xbl_error_code::http_status_302_found: hr = HTTP_E_STATUS_REDIRECT; break;
|
|
case xbl_error_code::http_status_303_see_other: hr = HTTP_E_STATUS_REDIRECT_METHOD; break;
|
|
case xbl_error_code::http_status_304_not_modified: hr = HTTP_E_STATUS_NOT_MODIFIED; break;
|
|
case xbl_error_code::http_status_305_use_proxy: hr = HTTP_E_STATUS_USE_PROXY; break;
|
|
case xbl_error_code::http_status_307_temporary_redirect: hr = HTTP_E_STATUS_REDIRECT_KEEP_VERB; break;
|
|
|
|
case xbl_error_code::http_status_400_bad_request: hr = HTTP_E_STATUS_BAD_REQUEST; break;
|
|
case xbl_error_code::http_status_401_unauthorized: hr = HTTP_E_STATUS_DENIED; break;
|
|
case xbl_error_code::http_status_402_payment_required: hr = HTTP_E_STATUS_PAYMENT_REQ; break;
|
|
case xbl_error_code::http_status_403_forbidden: hr = HTTP_E_STATUS_FORBIDDEN; break;
|
|
case xbl_error_code::http_status_404_not_found: hr = HTTP_E_STATUS_NOT_FOUND; break;
|
|
case xbl_error_code::http_status_405_method_not_allowed: hr = HTTP_E_STATUS_BAD_METHOD; break;
|
|
case xbl_error_code::http_status_406_not_acceptable: hr = HTTP_E_STATUS_NONE_ACCEPTABLE; break;
|
|
case xbl_error_code::http_status_407_proxy_authentication_required: hr = HTTP_E_STATUS_PROXY_AUTH_REQ; break;
|
|
case xbl_error_code::http_status_408_request_timeout: hr = HTTP_E_STATUS_REQUEST_TIMEOUT; break;
|
|
case xbl_error_code::http_status_409_conflict: hr = HTTP_E_STATUS_CONFLICT; break;
|
|
case xbl_error_code::http_status_410_gone: hr = HTTP_E_STATUS_GONE; break;
|
|
case xbl_error_code::http_status_411_length_required: hr = HTTP_E_STATUS_LENGTH_REQUIRED; break;
|
|
case xbl_error_code::http_status_412_precondition_failed: hr = HTTP_E_STATUS_PRECOND_FAILED; break;
|
|
case xbl_error_code::http_status_413_request_entity_too_large: hr = HTTP_E_STATUS_REQUEST_TOO_LARGE; break;
|
|
case xbl_error_code::http_status_414_request_uri_too_long: hr = HTTP_E_STATUS_URI_TOO_LONG; break;
|
|
case xbl_error_code::http_status_415_unsupported_media_type: hr = HTTP_E_STATUS_UNSUPPORTED_MEDIA; break;
|
|
case xbl_error_code::http_status_416_requested_range_not_satisfiable: hr = HTTP_E_STATUS_RANGE_NOT_SATISFIABLE; break;
|
|
case xbl_error_code::http_status_417_expectation_failed: hr = HTTP_E_STATUS_EXPECTATION_FAILED; break;
|
|
case xbl_error_code::http_status_421_misdirected_request: hr = MAKE_HTTP_HRESULT(421); break;
|
|
case xbl_error_code::http_status_422_unprocessable_entity: hr = MAKE_HTTP_HRESULT(422); break;
|
|
case xbl_error_code::http_status_423_locked: hr = MAKE_HTTP_HRESULT(423); break;
|
|
case xbl_error_code::http_status_424_failed_dependency: hr = MAKE_HTTP_HRESULT(424); break;
|
|
case xbl_error_code::http_status_426_upgrade_required: hr = MAKE_HTTP_HRESULT(426); break;
|
|
case xbl_error_code::http_status_428_precondition_required: hr = MAKE_HTTP_HRESULT(428); break;
|
|
case xbl_error_code::http_status_429_too_many_requests: hr = MAKE_HTTP_HRESULT(429); break;
|
|
case xbl_error_code::http_status_431_request_header_fields_too_large: hr = MAKE_HTTP_HRESULT(431); break;
|
|
case xbl_error_code::http_status_449_retry_with:hr = MAKE_HTTP_HRESULT(449); break;
|
|
case xbl_error_code::http_status_451_unavailable_for_legal_reasons: hr = MAKE_HTTP_HRESULT(451); break;
|
|
|
|
case xbl_error_code::http_status_500_internal_server_error: hr = HTTP_E_STATUS_SERVER_ERROR; break;
|
|
case xbl_error_code::http_status_501_not_implemented: hr = HTTP_E_STATUS_NOT_SUPPORTED; break;
|
|
case xbl_error_code::http_status_502_bad_gateway: hr = HTTP_E_STATUS_BAD_GATEWAY; break;
|
|
case xbl_error_code::http_status_503_service_unavailable: hr = HTTP_E_STATUS_SERVICE_UNAVAIL; break;
|
|
case xbl_error_code::http_status_504_gateway_timeout: hr = HTTP_E_STATUS_GATEWAY_TIMEOUT; break;
|
|
case xbl_error_code::http_status_505_http_version_not_supported: hr = HTTP_E_STATUS_VERSION_NOT_SUP; break;
|
|
case xbl_error_code::http_status_506_variant_also_negotiates: hr = MAKE_HTTP_HRESULT(506); break;
|
|
case xbl_error_code::http_status_507_insufficient_storage: hr = MAKE_HTTP_HRESULT(507); break;
|
|
case xbl_error_code::http_status_508_loop_detected: hr = MAKE_HTTP_HRESULT(508); break;
|
|
case xbl_error_code::http_status_510_not_extended: hr = MAKE_HTTP_HRESULT(510); break;
|
|
case xbl_error_code::http_status_511_network_authentication_required: hr = MAKE_HTTP_HRESULT(511); break;
|
|
|
|
default:
|
|
hr = HTTP_E_STATUS_UNEXPECTED;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#if HC_PLATFORM_IS_MICROSOFT
|
|
// TODO: remove
|
|
xsapi_internal_string utils::convert_hresult_to_error_name(_In_ long hr)
|
|
{
|
|
switch (hr)
|
|
{
|
|
// Generic errors
|
|
case S_OK: return "S_OK";
|
|
case S_FALSE: return "S_FALSE";
|
|
case E_OUTOFMEMORY: return "E_OUTOFMEMORY";
|
|
case E_ACCESSDENIED: return "E_ACCESSDENIED";
|
|
case E_INVALIDARG: return "E_INVALIDARG";
|
|
case E_UNEXPECTED: return "E_UNEXPECTED";
|
|
case E_ABORT: return "E_ABORT";
|
|
case E_FAIL: return "E_FAIL";
|
|
case E_NOTIMPL: return "E_NOTIMPL";
|
|
case E_ILLEGAL_METHOD_CALL: return "E_ILLEGAL_METHOD_CALL";
|
|
|
|
// Authentication specific errors
|
|
case 0x87DD0003: return "AM_E_XASD_UNEXPECTED";
|
|
case 0x87DD0004: return "AM_E_XASU_UNEXPECTED";
|
|
case 0x87DD0005: return "AM_E_XAST_UNEXPECTED";
|
|
case 0x87DD0006: return "AM_E_XSTS_UNEXPECTED";
|
|
case 0x87DD0007: return "AM_E_XDEVICE_UNEXPECTED";
|
|
case 0x87DD0008: return "AM_E_DEVMODE_NOT_AUTHORIZED";
|
|
case 0x87DD0009: return "AM_E_NOT_AUTHORIZED";
|
|
case 0x87DD000A: return "AM_E_FORBIDDEN";
|
|
case 0x87DD000B: return "AM_E_UNKNOWN_TARGET";
|
|
case 0x87DD000C: return "AM_E_INVALID_NSAL_DATA";
|
|
case 0x87DD000D: return "AM_E_TITLE_NOT_AUTHENTICATED";
|
|
case 0x87DD000E: return "AM_E_TITLE_NOT_AUTHORIZED";
|
|
case 0x87DD000F: return "AM_E_DEVICE_NOT_AUTHENTICATED";
|
|
case 0x87DD0010: return "AM_E_INVALID_USER_INDEX";
|
|
|
|
case 0x8015DC00: return "XO_E_DEVMODE_NOT_AUTHORIZED";
|
|
case 0x8015DC01: return "XO_E_SYSTEM_UPDATE_REQUIRED";
|
|
case 0x8015DC02: return "XO_E_CONTENT_UPDATE_REQUIRED";
|
|
case 0x8015DC03: return "XO_E_ENFORCEMENT_BAN";
|
|
case 0x8015DC04: return "XO_E_THIRD_PARTY_BAN";
|
|
case 0x8015DC05: return "XO_E_ACCOUNT_PARENTALLY_RESTRICTED";
|
|
case 0x8015DC06: return "XO_E_DEVICE_SUBSCRIPTION_NOT_ACTIVATED";
|
|
case 0x8015DC08: return "XO_E_ACCOUNT_BILLING_MAINTENANCE_REQUIRED";
|
|
case 0x8015DC09: return "XO_E_ACCOUNT_CREATION_REQUIRED";
|
|
case 0x8015DC0A: return "XO_E_ACCOUNT_TERMS_OF_USE_NOT_ACCEPTED";
|
|
case 0x8015DC0B: return "XO_E_ACCOUNT_COUNTRY_NOT_AUTHORIZED";
|
|
case 0x8015DC0C: return "XO_E_ACCOUNT_AGE_VERIFICATION_REQUIRED";
|
|
case 0x8015DC0D: return "XO_E_ACCOUNT_CURFEW";
|
|
case 0x8015DC0E: return "XO_E_ACCOUNT_CHILD_NOT_IN_FAMILY";
|
|
case 0x8015DC0F: return "XO_E_ACCOUNT_CSV_TRANSITION_REQUIRED";
|
|
case 0x8015DC10: return "XO_E_ACCOUNT_MAINTENANCE_REQUIRED";
|
|
case 0x8015DC11: return "XO_E_ACCOUNT_TYPE_NOT_ALLOWED"; // dev account on retail box
|
|
case 0x8015DC12: return "XO_E_CONTENT_ISOLATION (Verify SCID / Sandbox)";
|
|
case 0x8015DC13: return "XO_E_ACCOUNT_NAME_CHANGE_REQUIRED";
|
|
case 0x8015DC14: return "XO_E_DEVICE_CHALLENGE_REQUIRED";
|
|
// case 0x8015DC15: synthetic device type not allowed - does not apply to consoles
|
|
case 0x8015DC16: return "XO_E_SIGNIN_COUNT_BY_DEVICE_TYPE_EXCEEDED";
|
|
case 0x8015DC17: return "XO_E_PIN_CHALLENGE_REQUIRED";
|
|
case 0x8015DC18: return "XO_E_RETAIL_ACCOUNT_NOT_ALLOWED"; // RETAIL account on devkit
|
|
case 0x8015DC19: return "XO_E_SANDBOX_NOT_ALLOWED";
|
|
case 0x8015DC1A: return "XO_E_ACCOUNT_SERVICE_UNAVAILABLE_UNKNOWN_USER";
|
|
case 0x8015DC1B: return "XO_E_GREEN_SIGNED_CONTENT_NOT_AUTHORIZED";
|
|
case 0x8015DC1C: return "XO_E_CONTENT_NOT_AUTHORIZED";
|
|
|
|
case 0x8015DC20: return "XO_E_EXPIRED_DEVICE_TOKEN";
|
|
case 0x8015DC21: return "XO_E_EXPIRED_TITLE_TOKEN";
|
|
case 0x8015DC22: return "XO_E_EXPIRED_USER_TOKEN";
|
|
case 0x8015DC23: return "XO_E_INVALID_DEVICE_TOKEN";
|
|
case 0x8015DC24: return "XO_E_INVALID_TITLE_TOKEN";
|
|
case 0x8015DC25: return "XO_E_INVALID_USER_TOKEN";
|
|
|
|
// HTTP specific errors
|
|
case WEB_E_UNSUPPORTED_FORMAT: return "WEB_E_UNSUPPORTED_FORMAT";
|
|
case WEB_E_INVALID_XML: return "WEB_E_INVALID_XML";
|
|
case WEB_E_MISSING_REQUIRED_ELEMENT: return "WEB_E_MISSING_REQUIRED_ELEMENT";
|
|
case WEB_E_MISSING_REQUIRED_ATTRIBUTE: return "WEB_E_MISSING_REQUIRED_ATTRIBUTE";
|
|
case WEB_E_UNEXPECTED_CONTENT: return "WEB_E_UNEXPECTED_CONTENT";
|
|
case WEB_E_RESOURCE_TOO_LARGE: return "WEB_E_RESOURCE_TOO_LARGE";
|
|
case WEB_E_INVALID_JSON_STRING: return "WEB_E_INVALID_JSON_STRING";
|
|
case WEB_E_INVALID_JSON_NUMBER: return "WEB_E_INVALID_JSON_NUMBER";
|
|
case WEB_E_JSON_VALUE_NOT_FOUND: return "WEB_E_JSON_VALUE_NOT_FOUND";
|
|
case ERROR_RESOURCE_DATA_NOT_FOUND: return "ERROR_RESOURCE_DATA_NOT_FOUND";
|
|
|
|
case HTTP_E_STATUS_UNEXPECTED: return "HTTP_E_STATUS_UNEXPECTED";
|
|
case HTTP_E_STATUS_UNEXPECTED_REDIRECTION: return "HTTP_E_STATUS_UNEXPECTED_REDIRECTION";
|
|
case HTTP_E_STATUS_UNEXPECTED_CLIENT_ERROR: return "HTTP_E_STATUS_UNEXPECTED_CLIENT_ERROR";
|
|
case HTTP_E_STATUS_UNEXPECTED_SERVER_ERROR: return "HTTP_E_STATUS_UNEXPECTED_SERVER_ERROR";
|
|
case HTTP_E_STATUS_AMBIGUOUS: return "HTTP_E_STATUS_AMBIGUOUS";
|
|
case HTTP_E_STATUS_MOVED: return "HTTP_E_STATUS_MOVED";
|
|
case HTTP_E_STATUS_REDIRECT: return "HTTP_E_STATUS_REDIRECT";
|
|
case HTTP_E_STATUS_REDIRECT_METHOD: return "HTTP_E_STATUS_REDIRECT_METHOD";
|
|
case HTTP_E_STATUS_NOT_MODIFIED: return "HTTP_E_STATUS_NOT_MODIFIED";
|
|
case HTTP_E_STATUS_USE_PROXY: return "HTTP_E_STATUS_USE_PROXY";
|
|
case HTTP_E_STATUS_REDIRECT_KEEP_VERB: return "HTTP_E_STATUS_REDIRECT_KEEP_VERB";
|
|
case HTTP_E_STATUS_BAD_REQUEST: return "HTTP_E_STATUS_BAD_REQUEST";
|
|
case HTTP_E_STATUS_DENIED: return "HTTP_E_STATUS_DENIED";
|
|
case HTTP_E_STATUS_PAYMENT_REQ: return "HTTP_E_STATUS_PAYMENT_REQ";
|
|
case HTTP_E_STATUS_FORBIDDEN: return "HTTP_E_STATUS_FORBIDDEN";
|
|
case HTTP_E_STATUS_NOT_FOUND: return "HTTP_E_STATUS_NOT_FOUND";
|
|
case HTTP_E_STATUS_BAD_METHOD: return "HTTP_E_STATUS_BAD_METHOD";
|
|
case HTTP_E_STATUS_NONE_ACCEPTABLE: return "HTTP_E_STATUS_NONE_ACCEPTABLE";
|
|
case HTTP_E_STATUS_PROXY_AUTH_REQ: return "HTTP_E_STATUS_PROXY_AUTH_REQ";
|
|
case HTTP_E_STATUS_REQUEST_TIMEOUT: return "HTTP_E_STATUS_REQUEST_TIMEOUT";
|
|
case HTTP_E_STATUS_CONFLICT: return "HTTP_E_STATUS_CONFLICT";
|
|
case HTTP_E_STATUS_GONE: return "HTTP_E_STATUS_GONE";
|
|
case HTTP_E_STATUS_LENGTH_REQUIRED: return "HTTP_E_STATUS_LENGTH_REQUIRED";
|
|
case HTTP_E_STATUS_PRECOND_FAILED: return "HTTP_E_STATUS_PRECOND_FAILED";
|
|
case HTTP_E_STATUS_REQUEST_TOO_LARGE: return "HTTP_E_STATUS_REQUEST_TOO_LARGE";
|
|
case HTTP_E_STATUS_URI_TOO_LONG: return "HTTP_E_STATUS_URI_TOO_LONG";
|
|
case HTTP_E_STATUS_UNSUPPORTED_MEDIA: return "HTTP_E_STATUS_UNSUPPORTED_MEDIA";
|
|
case HTTP_E_STATUS_RANGE_NOT_SATISFIABLE: return "HTTP_E_STATUS_RANGE_NOT_SATISFIABLE";
|
|
case HTTP_E_STATUS_EXPECTATION_FAILED: return "HTTP_E_STATUS_EXPECTATION_FAILED";
|
|
|
|
case MAKE_HTTP_HRESULT(421): return "HTTP_E_STATUS_421_MISDIRECTED_REQUEST";
|
|
case MAKE_HTTP_HRESULT(422): return "HTTP_E_STATUS_422_UNPROCESSABLE_ENTITY";
|
|
case MAKE_HTTP_HRESULT(423): return "HTTP_E_STATUS_423_LOCKED";
|
|
case MAKE_HTTP_HRESULT(424): return "HTTP_E_STATUS_424_FAILED_DEPENDENCY";
|
|
case MAKE_HTTP_HRESULT(426): return "HTTP_E_STATUS_426_UPGRADE_REQUIRED";
|
|
case MAKE_HTTP_HRESULT(428): return "HTTP_E_STATUS_428_PRECONDITION_REQUIRED";
|
|
case MAKE_HTTP_HRESULT(429): return "HTTP_E_STATUS_429_TOO_MANY_REQUESTS";
|
|
case MAKE_HTTP_HRESULT(431): return "HTTP_E_STATUS_431_REQUEST_HEADER_FIELDS_TOO_LARGE";
|
|
case MAKE_HTTP_HRESULT(449): return "HTTP_E_STATUS_449_RETRY_WITH";
|
|
case MAKE_HTTP_HRESULT(451): return "HTTP_E_STATUS_451_UNAVAILABLE_FOR_LEGAL_REASONS";
|
|
|
|
case HTTP_E_STATUS_SERVER_ERROR: return "HTTP_E_STATUS_SERVER_ERROR";
|
|
case HTTP_E_STATUS_NOT_SUPPORTED: return "HTTP_E_STATUS_NOT_SUPPORTED";
|
|
case HTTP_E_STATUS_BAD_GATEWAY: return "HTTP_E_STATUS_BAD_GATEWAY";
|
|
case HTTP_E_STATUS_SERVICE_UNAVAIL: return "HTTP_E_STATUS_SERVICE_UNAVAIL";
|
|
case HTTP_E_STATUS_GATEWAY_TIMEOUT: return "HTTP_E_STATUS_GATEWAY_TIMEOUT";
|
|
case HTTP_E_STATUS_VERSION_NOT_SUP: return "HTTP_E_STATUS_VERSION_NOT_SUP";
|
|
|
|
case MAKE_HTTP_HRESULT(506): return "HTTP_E_STATUS_506_VARIANT_ALSO_NEGOTIATES";
|
|
case MAKE_HTTP_HRESULT(507): return "HTTP_E_STATUS_507_INSUFFICIENT_STORAGE";
|
|
case MAKE_HTTP_HRESULT(508): return "HTTP_E_STATUS_508_LOOP_DETECTED";
|
|
case MAKE_HTTP_HRESULT(510): return "HTTP_E_STATUS_510_NOT_EXTENDED";
|
|
case MAKE_HTTP_HRESULT(511): return "HTTP_E_STATUS_511_NETWORK_AUTHENTICATION_REQUIRED";
|
|
|
|
// WinINet specific errors
|
|
case INET_E_INVALID_URL: return "INET_E_INVALID_URL";
|
|
case INET_E_NO_SESSION: return "INET_E_NO_SESSION";
|
|
case INET_E_CANNOT_CONNECT: return "INET_E_CANNOT_CONNECT";
|
|
case INET_E_RESOURCE_NOT_FOUND: return "INET_E_RESOURCE_NOT_FOUND";
|
|
case INET_E_OBJECT_NOT_FOUND: return "INET_E_OBJECT_NOT_FOUND";
|
|
case INET_E_DATA_NOT_AVAILABLE: return "INET_E_DATA_NOT_AVAILABLE";
|
|
case INET_E_DOWNLOAD_FAILURE: return "INET_E_DOWNLOAD_FAILURE";
|
|
case INET_E_AUTHENTICATION_REQUIRED: return "INET_E_AUTHENTICATION_REQUIRED";
|
|
case INET_E_NO_VALID_MEDIA: return "INET_E_NO_VALID_MEDIA";
|
|
case INET_E_CONNECTION_TIMEOUT: return "INET_E_CONNECTION_TIMEOUT";
|
|
case INET_E_INVALID_REQUEST: return "INET_E_INVALID_REQUEST";
|
|
case INET_E_UNKNOWN_PROTOCOL: return "INET_E_UNKNOWN_PROTOCOL";
|
|
case INET_E_SECURITY_PROBLEM: return "INET_E_SECURITY_PROBLEM";
|
|
case INET_E_CANNOT_LOAD_DATA: return "INET_E_CANNOT_LOAD_DATA";
|
|
case INET_E_CANNOT_INSTANTIATE_OBJECT: return "INET_E_CANNOT_INSTANTIATE_OBJECT";
|
|
case INET_E_INVALID_CERTIFICATE: return "INET_E_INVALID_CERTIFICATE";
|
|
case INET_E_REDIRECT_FAILED: return "INET_E_REDIRECT_FAILED";
|
|
case INET_E_REDIRECT_TO_DIR: return "INET_E_REDIRECT_TO_DIR";
|
|
}
|
|
|
|
return "Unknown error";
|
|
}
|
|
#endif
|
|
|
|
xbox::services::xbl_error_code
|
|
utils::convert_exception_to_xbox_live_error_code()
|
|
{
|
|
// Default value, if there is no exception appears, return no_error
|
|
xbox::services::xbl_error_code errCode = xbl_error_code::no_error;
|
|
|
|
try
|
|
{
|
|
throw;
|
|
}
|
|
// std exceptions
|
|
catch (const std::bad_alloc&) // is an exception
|
|
{
|
|
errCode = xbl_error_code::bad_alloc;
|
|
}
|
|
catch (const std::bad_cast&) // is an exception
|
|
{
|
|
errCode = xbl_error_code::bad_cast;
|
|
}
|
|
catch (const std::invalid_argument&) // is a logic_error
|
|
{
|
|
errCode = xbl_error_code::invalid_argument;
|
|
}
|
|
catch (const std::out_of_range&) // is a logic_error
|
|
{
|
|
errCode = xbl_error_code::out_of_range;
|
|
}
|
|
catch (const std::length_error&) // is a logic_error
|
|
{
|
|
errCode = xbl_error_code::length_error;
|
|
}
|
|
catch (const std::range_error&) // is a runtime_error
|
|
{
|
|
errCode = xbl_error_code::range_error;
|
|
}
|
|
catch (const std::system_error& ex) // is a runtime_error
|
|
{
|
|
errCode = static_cast<xbl_error_code>(ex.code().value());
|
|
}
|
|
catch (const std::logic_error&) // is an exception
|
|
{
|
|
errCode = xbl_error_code::logic_error;
|
|
}
|
|
catch (const std::runtime_error&) // is an exception
|
|
{
|
|
errCode = xbl_error_code::runtime_error;
|
|
}
|
|
#if !XSAPI_NO_PPL
|
|
catch (const web::http::http_exception& ex) // is an exception
|
|
{
|
|
errCode = static_cast<xbl_error_code>(ex.error_code().value());
|
|
}
|
|
#endif // !XSAPI_NO_PPL
|
|
catch (const xbox::services::uri_exception&) // is an exception
|
|
{
|
|
errCode = xbl_error_code::uri_error;
|
|
}
|
|
catch (const std::exception&) // base class for standard C++ exceptions
|
|
{
|
|
errCode = xbl_error_code::generic_error;
|
|
}
|
|
#if HC_PLATFORM_IS_MICROSOFT
|
|
catch (HRESULT exceptionHR)
|
|
{
|
|
errCode = static_cast<xbl_error_code>(exceptionHR);
|
|
}
|
|
#endif
|
|
catch (...) // everything else
|
|
{
|
|
errCode = xbl_error_code::generic_error;
|
|
}
|
|
|
|
return errCode;
|
|
}
|
|
|
|
#if HC_PLATFORM_IS_MICROSOFT
|
|
std::error_code utils::guid_from_string(
|
|
_In_ const string_t& str,
|
|
_In_ GUID* guid,
|
|
_In_ bool withBraces
|
|
)
|
|
{
|
|
uint32_t data[3] = {0};
|
|
uint32_t charData[8] = {0};
|
|
|
|
auto n = swscanf_s(
|
|
str.c_str(),
|
|
withBraces ? L"{%x-%x-%x-%2x%2x-%2x%2x%2x%2x%2x%2x}" : L"%x-%x-%x-%2x%2x-%2x%2x%2x%2x%2x%2x",
|
|
&data[0],
|
|
&data[1],
|
|
&data[2],
|
|
&charData[0],
|
|
&charData[1],
|
|
&charData[2],
|
|
&charData[3],
|
|
&charData[4],
|
|
&charData[5],
|
|
&charData[6],
|
|
&charData[7]
|
|
);
|
|
|
|
guid->Data1 = data[0];
|
|
guid->Data2 = static_cast<uint8_t>(data[1]);
|
|
guid->Data3 = static_cast<uint8_t>(data[2]);
|
|
for(uint32_t i=0; i<8; i++ )
|
|
{
|
|
guid->Data4[i] = static_cast<uint8_t>(charData[i]);
|
|
}
|
|
|
|
return ( n == 11 ) ? xbl_error_code::no_error : xbl_error_code::logic_error;
|
|
}
|
|
#endif
|
|
|
|
xsapi_internal_string utils::create_guid(_In_ bool removeBraces)
|
|
{
|
|
#if HC_PLATFORM_IS_MICROSOFT
|
|
GUID guid = {0};
|
|
THROW_CPP_RUNTIME_IF(FAILED(CoCreateGuid(&guid)), "");
|
|
|
|
WCHAR wszGuid[50];
|
|
THROW_CPP_RUNTIME_IF(FAILED(::StringFromGUID2(
|
|
guid,
|
|
wszGuid,
|
|
ARRAYSIZE(wszGuid)
|
|
)), "");
|
|
|
|
xsapi_internal_string strGuid = utils::internal_string_from_utf16(wszGuid);
|
|
#elif !HC_PLATFORM_IS_MICROSOFT
|
|
xsapi_internal_string strGuid = generate_guid();
|
|
#else
|
|
uuid_t uuid;
|
|
uuid_generate_random(uuid);
|
|
char s[37] = { 0 };
|
|
uuid_unparse(uuid, s);
|
|
string_t strGuid = s;
|
|
#endif
|
|
|
|
if (removeBraces)
|
|
{
|
|
if (strGuid.length() > 3 && strGuid[0] == L'{')
|
|
{
|
|
// Remove the { }
|
|
strGuid.erase(0, 1);
|
|
strGuid.erase(strGuid.end() - 1, strGuid.end());
|
|
}
|
|
}
|
|
|
|
return strGuid;
|
|
}
|
|
String
|
|
utils::format_secure_device_address(String deviceAddress)
|
|
{
|
|
if (deviceAddress.empty())
|
|
{
|
|
return "";
|
|
}
|
|
|
|
// A secure device address(SDA) is a legacy concept for UWP/Xbox One.
|
|
// SDAs encapsulate the deviceToken which is used by MPSD to identify
|
|
// a session host and the connection address which is used by the title
|
|
// to connect to the title.
|
|
|
|
String formattedDeviceAddress = deviceAddress;
|
|
#if !(HC_PLATFORM == HC_PLATFORM_XDK || HC_PLATFORM == HC_PLATFORM_UWP)
|
|
// SDAs that start with a 1 byte (\001) will be parsed differently in MPSD.
|
|
// Since this platform does not have a valid SDA, we are adding a prefix
|
|
// to ensure that the SDA is handled as a non-valid SDA.
|
|
//
|
|
// MPSD will base64 decode a non-valid SDA and the hashed value will be used
|
|
// as the device token. The SDA can then be parsed by the title while
|
|
// deserializing the MPSD session to retrieve the connectionAddress.
|
|
formattedDeviceAddress = _sdaPrefix + deviceAddress;
|
|
#endif
|
|
|
|
Vector<unsigned char> input(formattedDeviceAddress.c_str(), formattedDeviceAddress.c_str() + formattedDeviceAddress.size());
|
|
String sda = xbox::services::convert::to_base64(input);
|
|
|
|
return sda;
|
|
}
|
|
|
|
String
|
|
utils::parse_secure_device_address(String secureDeviceAddress)
|
|
{
|
|
if (secureDeviceAddress.empty())
|
|
{
|
|
return "";
|
|
}
|
|
|
|
// A secure device address(SDA) is a legacy concept for UWP/Xbox One.
|
|
// SDAs encapsulate the deviceToken which is used by MPSD to identify
|
|
// a session host and the connection address which is used by the title
|
|
// to connect to the title.
|
|
|
|
std::vector<unsigned char> base64ConnectionAddress(xbox::services::convert::from_base64(secureDeviceAddress.c_str()));
|
|
auto formattedDeviceAddress = String(base64ConnectionAddress.begin(), base64ConnectionAddress.end());
|
|
|
|
String deviceAddress = formattedDeviceAddress;
|
|
#if !(HC_PLATFORM == HC_PLATFORM_XDK || HC_PLATFORM == HC_PLATFORM_UWP)
|
|
if (deviceAddress.find(_sdaPrefix) == 0)
|
|
{
|
|
deviceAddress = deviceAddress.substr(strlen(_sdaPrefix));
|
|
}
|
|
#endif
|
|
|
|
return deviceAddress;
|
|
}
|
|
|
|
std::vector<string_t>
|
|
utils::string_split(
|
|
_In_ const string_t& string,
|
|
_In_ string_t::value_type seperator
|
|
)
|
|
{
|
|
std::vector<string_t> vSubStrings;
|
|
|
|
if (!string.empty())
|
|
{
|
|
size_t posStart = 0, posFound = 0;
|
|
while (posFound != string_t::npos && posStart < string.length())
|
|
{
|
|
posFound = string.find(seperator, posStart);
|
|
if (posFound != string_t::npos)
|
|
{
|
|
if (posFound != posStart)
|
|
{
|
|
// this substring is not empty
|
|
vSubStrings.push_back(string.substr(posStart, posFound - posStart));
|
|
}
|
|
posStart = posFound + 1;
|
|
}
|
|
else
|
|
{
|
|
vSubStrings.push_back(string.substr(posStart));
|
|
}
|
|
}
|
|
}
|
|
|
|
return vSubStrings;
|
|
}
|
|
|
|
xsapi_internal_vector<xsapi_internal_string> utils::string_split_internal(
|
|
_In_ const xsapi_internal_string& string,
|
|
_In_ xsapi_internal_string::value_type seperator
|
|
)
|
|
{
|
|
xsapi_internal_vector<xsapi_internal_string> vSubStrings;
|
|
|
|
if (!string.empty())
|
|
{
|
|
size_t posStart = 0, posFound = 0;
|
|
while (posFound != xsapi_internal_string::npos && posStart < string.length())
|
|
{
|
|
posFound = string.find(seperator, posStart);
|
|
if (posFound != string_t::npos)
|
|
{
|
|
if (posFound != posStart)
|
|
{
|
|
// this substring is not empty
|
|
vSubStrings.push_back(string.substr(posStart, posFound - posStart));
|
|
}
|
|
posStart = posFound + 1;
|
|
}
|
|
else
|
|
{
|
|
vSubStrings.push_back(string.substr(posStart));
|
|
}
|
|
}
|
|
}
|
|
|
|
return vSubStrings;
|
|
}
|
|
|
|
string_t utils::vector_join(
|
|
_In_ const std::vector<string_t>& vector,
|
|
_In_ string_t::value_type seperator
|
|
)
|
|
{
|
|
stringstream_t ss;
|
|
|
|
if (!vector.empty())
|
|
{
|
|
string_t::value_type delimiter[2] = { seperator, 0 };
|
|
std::copy(vector.begin(), vector.end() - 1, std::ostream_iterator<string_t, string_t::value_type>(ss, delimiter));
|
|
ss << vector.back();
|
|
}
|
|
|
|
return ss.str();
|
|
}
|
|
|
|
xsapi_internal_string utils::vector_join_internal(
|
|
_In_ const std::vector<xsapi_internal_string>& vector,
|
|
_In_ xsapi_internal_string::value_type seperator
|
|
)
|
|
{
|
|
xsapi_internal_stringstream ss;
|
|
|
|
if (!vector.empty())
|
|
{
|
|
xsapi_internal_string::value_type delimiter[2] = { seperator, 0 };
|
|
std::copy(vector.begin(), vector.end() - 1, std::ostream_iterator<xsapi_internal_string, xsapi_internal_string::value_type>(ss, delimiter));
|
|
ss << vector.back();
|
|
}
|
|
|
|
return ss.str();
|
|
}
|
|
|
|
string_t
|
|
utils::replace_sub_string(
|
|
_In_ const string_t& source,
|
|
_In_ const string_t& pattern,
|
|
_In_ const string_t& replacement
|
|
)
|
|
{
|
|
string_t result = source;
|
|
// Search the string backward for the given pattern first
|
|
size_t nPos = source.rfind(pattern);
|
|
|
|
while (nPos != source.npos)
|
|
{
|
|
result.replace(nPos, pattern.length(), replacement);
|
|
|
|
if (nPos == 0)
|
|
{
|
|
// There is nothing left to look at, break
|
|
break;
|
|
}
|
|
|
|
// Find the next match starting from the last replaced position
|
|
nPos = source.rfind(pattern, nPos - 1);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
xsapi_internal_string_t utils::read_file_to_string(
|
|
_In_ const xsapi_internal_string_t& filePath
|
|
)
|
|
{
|
|
std::ifstream in(filePath.c_str(), std::ios::in | std::ios::binary);
|
|
if (in)
|
|
{
|
|
std::vector<char> fileData;
|
|
in.seekg(0, std::ios::end);
|
|
uint32_t fileSizeInBytes = static_cast<uint32_t>(in.tellg());
|
|
if (fileSizeInBytes > 3)
|
|
{
|
|
fileData.resize(fileSizeInBytes);
|
|
in.seekg(0, std::ios::beg);
|
|
if (fileData.size() > 0)
|
|
{
|
|
in.read(&fileData[0], fileData.size());
|
|
}
|
|
in.close();
|
|
|
|
bool isUtf16LE =
|
|
(static_cast<unsigned char>(fileData[0]) == 0xFF &&
|
|
static_cast<unsigned char>(fileData[1]) == 0xFE); // check for UTF-16 LE BOM
|
|
|
|
bool isUtf8 =
|
|
(static_cast<unsigned char>(fileData[0]) == 0xEF &&
|
|
static_cast<unsigned char>(fileData[1]) == 0xBB &&
|
|
static_cast<unsigned char>(fileData[2]) == 0xBF); // check for UTF-8 BOM
|
|
|
|
xsapi_internal_string_t fileDataString;
|
|
#ifdef WIN32
|
|
// Convert file data to UTF16 string
|
|
if (isUtf16LE)
|
|
{
|
|
uint32_t byteOrderMarkSizeInBytes = 2;
|
|
uint32_t strLength = (fileSizeInBytes - byteOrderMarkSizeInBytes) / sizeof(WCHAR);
|
|
fileDataString = xsapi_internal_string_t(reinterpret_cast<WCHAR*>(fileData.data() + byteOrderMarkSizeInBytes), strLength);
|
|
}
|
|
else
|
|
{
|
|
int byteOrderMarkSizeInBytes = (isUtf8) ? 3 : 0;
|
|
uint32_t strLength = fileSizeInBytes - byteOrderMarkSizeInBytes;
|
|
xsapi_internal_string utf8FileData = xsapi_internal_string(fileData.data() + byteOrderMarkSizeInBytes, strLength);
|
|
fileDataString = xbox::services::convert::utf8_to_utf16(utf8FileData);
|
|
}
|
|
#else
|
|
// Convert file data to UTF8 string
|
|
if (isUtf16LE)
|
|
{
|
|
int byteOrderMarkSizeInBytes = 2;
|
|
uint32_t strLength = (fileSizeInBytes - byteOrderMarkSizeInBytes) / sizeof(wchar_t);
|
|
xsapi_internal_string_t utf16FileData = xsapi_internal_string_t(fileData.data(), strLength);
|
|
fileDataString = utf16FileData;
|
|
}
|
|
else
|
|
{
|
|
int byteOrderMarkSizeInBytes = (isUtf8) ? 3 : 0;
|
|
uint32_t strLength = fileSizeInBytes - byteOrderMarkSizeInBytes;
|
|
fileDataString = xsapi_internal_string(fileData.data(), strLength);
|
|
}
|
|
#endif
|
|
return fileDataString;
|
|
}
|
|
}
|
|
|
|
return xsapi_internal_string_t();
|
|
}
|
|
|
|
int utils::interlocked_increment(volatile long& incrementNum)
|
|
{
|
|
#if HC_PLATFORM_IS_MICROSOFT
|
|
return InterlockedIncrement(&incrementNum);
|
|
#else
|
|
return static_cast<uint32_t>(__sync_fetch_and_add(&incrementNum, 1));
|
|
#endif
|
|
}
|
|
|
|
int utils::interlocked_decrement(volatile long& decrementNum)
|
|
{
|
|
#if HC_PLATFORM_IS_MICROSOFT
|
|
return InterlockedDecrement(&decrementNum);
|
|
#else
|
|
return static_cast<uint32_t>(__sync_fetch_and_sub(&decrementNum, 1));
|
|
#endif
|
|
}
|
|
|
|
std::vector<string_t> utils::string_array_to_string_vector(
|
|
const char* *stringArray,
|
|
size_t stringArrayCount
|
|
)
|
|
{
|
|
std::vector<xbox::services::string_t> stringVector;
|
|
for (size_t i = 0; i < stringArrayCount; ++i)
|
|
{
|
|
stringVector.push_back(string_t_from_utf8(stringArray[i]));
|
|
}
|
|
return stringVector;
|
|
}
|
|
|
|
xsapi_internal_vector<xsapi_internal_string> utils::string_array_to_internal_string_vector(
|
|
const char* *stringArray,
|
|
size_t stringArrayCount
|
|
)
|
|
{
|
|
xsapi_internal_vector<xsapi_internal_string> stringVector;
|
|
stringVector.reserve(stringArrayCount);
|
|
for (size_t i = 0; i < stringArrayCount; ++i)
|
|
{
|
|
stringVector.push_back(stringArray[i]);
|
|
}
|
|
return stringVector;
|
|
}
|
|
|
|
xsapi_internal_vector<xsapi_internal_string> utils::xuid_array_to_internal_string_vector(
|
|
uint64_t* xuidArray,
|
|
size_t xuidArrayCount
|
|
)
|
|
{
|
|
xsapi_internal_vector<xsapi_internal_string> stringVector;
|
|
stringVector.reserve(xuidArrayCount);
|
|
for (size_t i = 0; i < xuidArrayCount; ++i)
|
|
{
|
|
stringVector.push_back(utils::uint64_to_internal_string(xuidArray[i]));
|
|
}
|
|
return stringVector;
|
|
}
|
|
|
|
xsapi_internal_vector<uint32_t> utils::uint32_array_to_internal_vector(
|
|
uint32_t* intArray,
|
|
size_t intArrayCount
|
|
)
|
|
{
|
|
xsapi_internal_vector<uint32_t> vector;
|
|
vector.reserve(intArrayCount);
|
|
for (size_t i = 0; i < intArrayCount; ++i)
|
|
{
|
|
vector.push_back(intArray[i]);
|
|
}
|
|
return vector;
|
|
}
|
|
|
|
bool utils::EnsureLessThanMaxLength(const char* str, size_t maxLength)
|
|
{
|
|
size_t i = 0;
|
|
while (true)
|
|
{
|
|
if (i >= maxLength)
|
|
{
|
|
return false;
|
|
}
|
|
if (str[i] == '\0')
|
|
{
|
|
return true;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
String utils::ToLower(String str) noexcept
|
|
{
|
|
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c)
|
|
{
|
|
return static_cast<char>(tolower(c));
|
|
});
|
|
|
|
return str;
|
|
}
|
|
|
|
XAsyncBlock* utils::MakeAsyncBlock(XTaskQueueHandle queue, void* context, XAsyncCompletionRoutine* callback)
|
|
{
|
|
auto async = Make<XAsyncBlock>();
|
|
async->queue = queue;
|
|
async->context = context;
|
|
async->callback = callback;
|
|
return async;
|
|
}
|
|
|
|
static void CALLBACK s_defaultAsyncBlockCallback(XAsyncBlock* async)
|
|
{
|
|
Delete(async);
|
|
}
|
|
|
|
XAsyncBlock* utils::MakeDefaultAsyncBlock(XTaskQueueHandle queue)
|
|
{
|
|
return MakeAsyncBlock(queue, nullptr, s_defaultAsyncBlockCallback);
|
|
}
|
|
|
|
time_t utils::time_t_from_datetime(const xbox::services::datetime& datetime)
|
|
{
|
|
uint64_t seconds = datetime.to_interval() / _secondTicks;
|
|
if (seconds >= 11644473600LL)
|
|
{
|
|
return (time_t)(seconds - 11644473600LL);
|
|
}
|
|
else
|
|
{
|
|
// If time is before epoch, 0 is returned.
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#if HC_PLATFORM_IS_MICROSOFT
|
|
xsapi_internal_string utils::internal_string_from_utf16(_In_z_ const wchar_t* utf16)
|
|
{
|
|
return internal_string_from_char_t(utf16);
|
|
}
|
|
#endif
|
|
|
|
#if XSAPI_WRL_EVENTS_SERVICE
|
|
Microsoft::WRL::Wrappers::HString utils::HStringFromUtf8(_In_z_ const char* utf8)
|
|
{
|
|
auto cchOutString = char_t_from_utf8(utf8, nullptr, 0);
|
|
xsapi_internal_wstring wstring(cchOutString - 1, '\0');
|
|
char_t_from_utf8(utf8, &wstring[0], cchOutString);
|
|
|
|
Microsoft::WRL::Wrappers::HString hstring;
|
|
hstring.Set(wstring.data());
|
|
return hstring;
|
|
}
|
|
#endif
|
|
|
|
#if __cplusplus_winrt
|
|
Platform::String^ utils::PlatformStringFromUtf8(_In_z_ const char* utf8)
|
|
{
|
|
auto cchOutString = char_t_from_utf8(utf8, nullptr, 0);
|
|
xsapi_internal_wstring wstr(cchOutString - 1, '\0');
|
|
char_t_from_utf8(utf8, &wstr[0], cchOutString);
|
|
return ref new Platform::String(wstr.data());
|
|
}
|
|
#endif
|
|
|
|
std::string utils::std_string_from_string_t(_In_ const string_t& stringt)
|
|
{
|
|
#if HC_PLATFORM_IS_MICROSOFT
|
|
auto cchOutString = utf8_from_char_t(stringt.data(), nullptr, 0);
|
|
std::string out(static_cast<size_t>(cchOutString) - 1, '\0');
|
|
utf8_from_char_t(stringt.data(), &out[0], cchOutString);
|
|
return out;
|
|
#else
|
|
return std::string(stringt.data());
|
|
#endif
|
|
}
|
|
|
|
xsapi_internal_string utils::internal_string_from_char_t(_In_ const char_t* char_t)
|
|
{
|
|
#if HC_PLATFORM_IS_MICROSOFT
|
|
auto cchOutString = utf8_from_char_t(char_t, nullptr, 0);
|
|
xsapi_internal_string out(static_cast<size_t>(cchOutString) - 1, '\0');
|
|
utf8_from_char_t(char_t, &out[0], cchOutString);
|
|
return out;
|
|
#else
|
|
return xsapi_internal_string(char_t);
|
|
#endif
|
|
}
|
|
|
|
string_t utils::string_t_from_internal_string(_In_ const xsapi_internal_string& internalString)
|
|
{
|
|
#if HC_PLATFORM_IS_MICROSOFT
|
|
return string_t_from_utf8(internalString.data());
|
|
#else
|
|
return string_t(internalString.c_str());
|
|
#endif
|
|
}
|
|
|
|
string_t utils::string_t_from_utf8(_In_z_ const char* utf8)
|
|
{
|
|
#if HC_PLATFORM_IS_MICROSOFT
|
|
auto cchOutString = char_t_from_utf8(utf8, nullptr, 0);
|
|
string_t out(static_cast<size_t>(cchOutString) - 1, '\0');
|
|
char_t_from_utf8(utf8, &out[0], cchOutString);
|
|
return out;
|
|
#else
|
|
return string_t(utf8);
|
|
#endif
|
|
}
|
|
|
|
xsapi_internal_string utils::internal_string_from_string_t(_In_ const string_t& externalString)
|
|
{
|
|
#if HC_PLATFORM_IS_MICROSOFT
|
|
return internal_string_from_utf16(externalString.c_str());
|
|
#else
|
|
return xsapi_internal_string(externalString.c_str());
|
|
#endif
|
|
}
|
|
|
|
int utils::utf8_from_char_t(
|
|
_In_z_ const char_t* inArray,
|
|
_Out_writes_z_(cchOutArray) char* outArray,
|
|
_In_ int cchOutArray
|
|
)
|
|
{
|
|
#if HC_PLATFORM_IS_MICROSOFT
|
|
// query for the buffer size
|
|
auto queryResult = WideCharToMultiByte(
|
|
CP_UTF8, WC_ERR_INVALID_CHARS,
|
|
inArray, -1,
|
|
nullptr, 0,
|
|
nullptr, nullptr
|
|
);
|
|
|
|
if (queryResult > cchOutArray && cchOutArray == 0)
|
|
{
|
|
return queryResult;
|
|
}
|
|
else if (queryResult == 0 || queryResult > cchOutArray)
|
|
{
|
|
throw std::exception("utf8_from_char_t failed");
|
|
}
|
|
|
|
auto conversionResult = WideCharToMultiByte(
|
|
CP_UTF8, WC_ERR_INVALID_CHARS,
|
|
inArray, -1,
|
|
outArray, cchOutArray,
|
|
nullptr, nullptr
|
|
);
|
|
if (conversionResult == 0)
|
|
{
|
|
throw std::exception("utf8_from_char_t failed");
|
|
}
|
|
|
|
return conversionResult;
|
|
#else
|
|
int len = (int)strlen(inArray);
|
|
if (len < cchOutArray && outArray != nullptr)
|
|
{
|
|
strcpy(outArray, len + 1, inArray);
|
|
}
|
|
else if (cchOutArray > 0)
|
|
{
|
|
return 0;
|
|
}
|
|
return len + 1;
|
|
#endif
|
|
}
|
|
|
|
int utils::char_t_from_utf8(
|
|
_In_z_ const char* inArray,
|
|
_Out_writes_z_(cchOutArray) char_t* outArray,
|
|
_In_ int cchOutArray
|
|
)
|
|
{
|
|
#if HC_PLATFORM_IS_MICROSOFT
|
|
// query for the buffer size
|
|
auto queryResult = MultiByteToWideChar(
|
|
CP_UTF8, MB_ERR_INVALID_CHARS,
|
|
inArray, -1,
|
|
nullptr, 0
|
|
);
|
|
|
|
if (queryResult > cchOutArray && cchOutArray == 0)
|
|
{
|
|
return queryResult;
|
|
}
|
|
else if (queryResult == 0 || queryResult > cchOutArray)
|
|
{
|
|
throw std::exception("char_t_from_utf8 failed");
|
|
}
|
|
|
|
auto conversionResult = MultiByteToWideChar(
|
|
CP_UTF8, MB_ERR_INVALID_CHARS,
|
|
inArray, -1,
|
|
outArray, cchOutArray
|
|
);
|
|
if (conversionResult == 0)
|
|
{
|
|
throw std::exception("char_t_from_utf8 failed");
|
|
}
|
|
|
|
return conversionResult;
|
|
#else
|
|
int len = (int)strlen(inArray);
|
|
if (len < cchOutArray && outArray != nullptr)
|
|
{
|
|
strcpy(outArray, len + 1, inArray);
|
|
}
|
|
else if (cchOutArray > 0)
|
|
{
|
|
return 0;
|
|
}
|
|
return len + 1;
|
|
#endif
|
|
}
|
|
|
|
NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END
|