diff --git a/README.md b/README.md index 272aa1a..9b13197 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@

A multi-purpose adblocker and skip-bypass for the Spotify for Windows (64 bit)

Please support Spotify by purchasing premium

- Last updated: 14 February 2024
- Last tested version: Spotify for Windows (64 bit) 1.2.31.1205.g4d59ad7c + Last updated: 18 March 2024
+ Last tested version: Spotify for Windows (64 bit) 1.2.33.1039.g8ddb5918

@@ -61,6 +61,17 @@ rm -fo $env:APPDATA\spotify\dpapi.dll rm -fo $env:APPDATA\spotify\config.ini ``` +### Disabling Automatic Updates + +The automatic update feature is enabled by default. To disable it: + +1. Navigate to the directory where Spotify is installed: `%APPDATA%\Spotify`. +2. Open the `config.ini` file. +3. Set `Enable_Auto_Update` to `0` under the `[Config]` section. +4. Save your changes and close the file. + +Automatic updates will now be disabled. If you wish to update, you'll need to do so manually. + ### Additional Notes: * Installation script automatically detects if your Spotify client version is supported, or not. If the version is not supported, you will be prompted to update your Spotify client. To enforce client update, supply an optional parameter `UpdateSpotify` to the installation script. diff --git a/blockthespot_settings.json b/blockthespot_settings.json new file mode 100644 index 0000000..4a98b5f --- /dev/null +++ b/blockthespot_settings.json @@ -0,0 +1,96 @@ +{ + "Cef Offsets": { + "x64": { + "cef_request_t_get_url": 48, + "cef_zip_reader_t_get_file_name": 72, + "cef_zip_reader_t_read_file": 112 + }, + "x32": { + "cef_request_t_get_url": 24, + "cef_zip_reader_t_get_file_name": 36, + "cef_zip_reader_t_read_file": 56 + } + }, + "Developer": { + "x64": { + "Signature": "80 E3 01 48 8B 95 ?? ?? ?? ?? 48 83 FA 10", + "Value": "B3 01 90", + "Offset": 0, + "Address": 0 + }, + "x32": { + "Signature": "25 01 FF FF FF 89 ?? ?? ?? FF FF", + "Value": "B8 03 00", + "Offset": 0, + "Address": 0 + } + }, + "Zip Reader": { + "home-hpto.css": { + "hptocss": { + "Signature": ".WiPggcPDzbwGxoxwLWFf{display:-webkit-box;display:-ms-flexbox;display:flex;", + "Value": ".WiPggcPDzbwGxoxwLWFf{display:-webkit-box;display:-ms-flexbox;display:none;", + "Offset": 0, + "Fill": 0, + "Address": 0 + } + }, + "xpui.js": { + "adsEnabled": { + "Signature": "adsEnabled:!0", + "Value": "1", + "Offset": 12, + "Fill": 0, + "Address": 0 + }, + "ishptohidden": { + "Signature": "isHptoHidden:!0", + "Value": "1", + "Offset": 14, + "Fill": 0, + "Address": 0 + }, + "sponsorship": { + "Signature": ".set(\"allSponsorships\",t.sponsorships)}}(e,t);", + "Value": "\"", + "Offset": 5, + "Fill": 15, + "Address": 0 + }, + "skipsentry": { + "Signature": "sentry.io", + "Value": "localhost", + "Offset": 0, + "Fill": 0, + "Address": 0 + }, + "hptoEnabled": { + "Signature": "hptoEnabled:!0", + "Value": "1", + "Offset": 13, + "Fill": 0, + "Address": 0 + }, + "sp_localhost": { + "Signature": "sp://ads/v1/ads/", + "Value": "sp://localhost//", + "Offset": 0, + "Fill": 0, + "Address": 0 + }, + "premium_free": { + "Signature": "e.session?.productState?.catalogue?.toLowerCase()", + "Value": "\"\"", + "Offset": -1, + "Fill": 48, + "Address": 0 + } + } + }, + "Block List": [ + "/ads/", + "/ad-logic/", + "/gabo-receiver-service/" + ], + "Latest Release Date": "" +} \ No newline at end of file diff --git a/config.ini b/config.ini index 51381a4..97d2803 100644 --- a/config.ini +++ b/config.ini @@ -2,5 +2,6 @@ Block_Ads=1 Block_Banner=1 Enable_Developer=1 +Enable_Auto_Update=1 ;Log system Enable_Log=0 diff --git a/install.ps1 b/install.ps1 index d60ead2..ac13ebe 100644 --- a/install.ps1 +++ b/install.ps1 @@ -265,6 +265,31 @@ $patchFiles = (Join-Path -Path $PWD -ChildPath 'dpapi.dll'), (Join-Path -Path $P Copy-Item -LiteralPath $patchFiles -Destination "$spotifyDirectory" +function Install-VcRedist { + # https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170 + $vcRedistX86Url = "https://aka.ms/vs/17/release/vc_redist.x86.exe" + $vcRedistX64Url = "https://aka.ms/vs/17/release/vc_redist.x64.exe" + + if ([Environment]::Is64BitOperatingSystem) { + if (!(Test-Path 'HKLM:\Software\Microsoft\VisualStudio\14.0\VC\Runtimes\x64')) { + $vcRedistX64File = Join-Path -Path $PWD -ChildPath 'vc_redist.x64.exe' + Write-Host "Downloading and installing vc_redist.x64.exe..." + Get-File -Uri $vcRedistX64Url -TargetFile $vcRedistX64File + Start-Process -FilePath $vcRedistX64File -ArgumentList "/install /quiet /norestart" -Wait + } + } + else { + if (!(Test-Path 'HKLM:\Software\Microsoft\VisualStudio\14.0\VC\Runtimes\x86')) { + $vcRedistX86File = Join-Path -Path $PWD -ChildPath 'vc_redist.x86.exe' + Write-Host "Downloading and installing vc_redist.x86.exe..." + Get-File -Uri $vcRedistX86Url -TargetFile $vcRedistX86File + Start-Process -FilePath $vcRedistX86File -ArgumentList "/install /quiet /norestart" -Wait + } + } +} + +Install-VcRedist + $tempDirectory = $PWD Pop-Location diff --git a/src/BasicUtils/Json.cpp b/src/BasicUtils/Json.cpp new file mode 100644 index 0000000..bfa77da --- /dev/null +++ b/src/BasicUtils/Json.cpp @@ -0,0 +1,551 @@ +#include "Json.h" +#include "Utils.h" +#include "Logger.h" +#include +#include +#include + +Json& Json::operator[](const std::wstring& key) +{ + if (auto* object_ptr = std::get_if(&m_value)) { + auto it = object_ptr->find(key); + if (it != object_ptr->end()) { + return it->second; + } + return (*object_ptr)[key]; + } + + m_value = Object(); + return std::get(m_value)[key]; +} + +Json& Json::operator[](size_t index) +{ + return at(index); +} + +Json& Json::operator=(std::initializer_list> list) +{ + m_value = Object(list.begin(), list.end()); + return *this; +} + +Json& Json::operator=(std::initializer_list list) +{ + m_value = Array(list.begin(), list.end()); + return *this; +} + +std::wostream& operator<<(std::wostream& os, const Json& json) +{ + std::wostringstream oss; + json.dump_impl(oss, 0, 0); + os << oss.str(); + return os; +} + +std::wistream& operator>>(std::wistream& is, Json& json) +{ + json = Json::parse(is); + return is; +} + +bool operator==(const Json& lhs, const Json& rhs) +{ + return lhs.m_value == rhs.m_value; +} + +bool operator!=(const Json& lhs, const Json& rhs) +{ + return !(lhs == rhs); +} + +Json::Object::iterator Json::begin() +{ + if (is_object()) { + return std::get(m_value).begin(); + } + throw std::runtime_error("Json value is not an object"); +} + +Json::Object::const_iterator Json::begin() const +{ + return const_cast(this)->begin(); +} + +Json::Object::iterator Json::end() +{ + if (is_object()) { + return std::get(m_value).end(); + } + throw std::runtime_error("Json value is not an object"); +} + +Json::Object::const_iterator Json::end() const +{ + return const_cast(this)->end(); +} + +Json::Object::iterator Json::find(const std::wstring& key) +{ + if (is_object()) { + return std::get(m_value).find(key); + } + throw std::runtime_error("Json value is not an object"); +} + +Json::Object::const_iterator Json::find(const std::wstring& key) const +{ + return const_cast(this)->find(key); +} + +//Json::Array::iterator Json::begin() +//{ +// if (is_array()) { +// return std::get(m_value).begin(); +// } +// throw std::runtime_error("Json value is not an array"); +//} +// +//Json::Array::iterator Json::end() +//{ +// if (is_array()) { +// return std::get(m_value).end(); +// } +// throw std::runtime_error("Json value is not an array"); +//} + +bool Json::is_null() const +{ + return std::holds_alternative(m_value); +} + +bool Json::is_integer() const +{ + return std::holds_alternative(m_value); +} + +bool Json::is_float() const +{ + return std::holds_alternative(m_value); +} + +bool Json::is_double() const +{ + return std::holds_alternative(m_value); +} + +bool Json::is_boolean() const +{ + return std::holds_alternative(m_value); +} + +bool Json::is_string() const +{ + return std::holds_alternative(m_value); +} + +bool Json::is_object() const +{ + return std::holds_alternative(m_value); +} + +bool Json::is_array() const +{ + return std::holds_alternative(m_value); +} + +int Json::get_integer() const +{ + if (std::holds_alternative(m_value)) { + return std::get(m_value); + } + Log(L"JSON value is not an integer", LogLevel::Error); + return 0; +} + +float Json::get_float() const +{ + if (std::holds_alternative(m_value)) { + return std::get(m_value); + } + Log(L"JSON value is not an float", LogLevel::Error); + return 0.0f; +} + +double Json::get_double() const +{ + if (std::holds_alternative(m_value)) { + return std::get(m_value); + } + Log(L"JSON value is not a double", LogLevel::Error); + return 0.0; +} + +bool Json::get_boolean() const +{ + if (std::holds_alternative(m_value)) { + return std::get(m_value); + } + Log(L"JSON value is not a boolean", LogLevel::Error); + return false; +} + +std::wstring Json::get_string() const +{ + if (std::holds_alternative(m_value)) { + return std::get(m_value); + } + Log(L"JSON value is not a string", LogLevel::Error); + return L""; +} + +Json::Object Json::get_object() const +{ + if (std::holds_alternative(m_value)) { + return std::get(m_value); + } + Log(L"JSON value is not an object", LogLevel::Error); + return Object(); +} + +Json::Array Json::get_array() const +{ + if (std::holds_alternative(m_value)) { + return std::get(m_value); + } + Log(L"JSON value is not an array", LogLevel::Error); + return Array(); +} + +Json& Json::at(const std::wstring& key) +{ + if (auto* object_ptr = std::get_if(&m_value)) { + auto it = object_ptr->find(key); + if (it != object_ptr->end()) { + return it->second; + } + throw std::out_of_range("Key not found in JSON object"); + } + throw std::runtime_error("Trying to access key in non-object JSON value"); +} + +const Json& Json::at(const std::wstring& key) const +{ + return const_cast(*this).at(key); +} + +Json& Json::at(std::size_t index) +{ + if (auto* array_ptr = std::get_if(&m_value)) { + if (index < array_ptr->size()) { + return (*array_ptr)[index]; + } + throw std::out_of_range("Index out of range in JSON array"); + } + throw std::out_of_range("Trying to access index in non-array JSON value"); +} + +const Json& Json::at(std::size_t index) const +{ + return const_cast(*this).at(index); +} + +void Json::clear() +{ + if (std::holds_alternative(m_value)) { + std::get(m_value).clear(); + } + else if (std::holds_alternative(m_value)) { + std::get(m_value).clear(); + } +} + +bool Json::empty() const +{ + if (std::holds_alternative(m_value)) { + return std::get(m_value).empty(); + } + else if (std::holds_alternative(m_value)) { + return std::get(m_value).empty(); + } + else if (std::holds_alternative(m_value)) { + return true; + } + return false; +} + +std::size_t Json::size() const +{ + if (std::holds_alternative(m_value)) { + return std::get(m_value).size(); + } + else if (std::holds_alternative(m_value)) { + return std::get(m_value).size(); + } + return 0; +} + +bool Json::contains(const std::wstring& key) const +{ + if (is_object()) { + const auto& object = std::get(m_value); + return object.find(key) != object.end(); + } + return false; +} + +std::wstring Json::dump(int indent) const +{ + std::wostringstream os; + dump_impl(os, indent, indent); + return os.str(); +} + +void Json::dump_impl(std::wostream& os, int indent, int step) const +{ + std::visit([&os, indent, step](const auto& value) { + if constexpr (std::is_same_v, Object>) { + os << (indent == 0 ? L"{" : L"{\n"); + bool first = true; + for (const auto& [key, val] : value) { + if (!first) { + os << (indent == 0 ? L", " : L",\n"); + } + first = false; + os << std::wstring(indent, L' ') << L"\"" << key << L"\": "; + val.dump_impl(os, indent + step, step); + } + os << (indent == 0 ? L"}" : L"\n" + std::wstring(indent - step, L' ') + L"}"); + } + else if constexpr (std::is_same_v, Array>) { + os << (indent == 0 ? L"[" : L"[\n"); + bool first = true; + for (const auto& val : value) { + if (!first) { + os << (indent == 0 ? L", " : L",\n"); + } + first = false; + if (indent != 0) + os << std::wstring(indent, L' '); + val.dump_impl(os, indent + step, step); + } + os << (indent == 0 ? L"]" : L"\n" + std::wstring(indent - step, L' ') + L"]"); + } + else { + if constexpr (std::is_same_v, std::wstring>) + os << std::quoted(value); + else if constexpr (std::is_same_v, bool>) + os << (value ? L"true" : L"false"); + else if constexpr (std::is_same_v, std::nullptr_t>) + os << L"null"; + else + os << value; + } + }, m_value); +} + +Json Json::parse(const std::wstring& json_text) +{ + std::wistringstream iss(json_text); + return parse(iss); +} + +Json Json::parse(std::wistream& is) +{ + try { + wchar_t ch; + is >> ch; + is.unget(); + + if (ch == L'{') { + return parse_object(is); + } + else if (ch == L'[') { + return parse_array(is); + } + else if (ch == L'"') { + return parse_string(is); + } + else if (ch == L'-' || std::iswdigit(ch)) { + return parse_number(is); + } + else if (ch == L't' || ch == L'f') { + return parse_boolean(is); + } + else if (ch == L'n') { + return parse_null(is); + } + else { + throw std::runtime_error("Invalid JSON"); + } + } + catch (const std::exception& e) { + Log(Utils::FormatString(L"{}", e.what()), LogLevel::Error); + return Json(); + } +} + +Json::Object Json::parse_object(std::wistream& is) +{ + Object result; + wchar_t ch; + is >> ch; + while (is >> ch) { + if (ch == L'}') { + return result; + } + is.unget(); + std::wstring key = parse_string(is); + is >> ch; + if (ch != L':') { + throw std::runtime_error("Expected ':' in JSON object"); + } + Json value = parse(is); + result[key] = value; + is >> ch; + if (ch == L'}') { + return result; + } + else if (ch != L',') { + throw std::runtime_error("Expected ',' or '}' in JSON object"); + } + } + throw std::runtime_error("Unexpected end of JSON object"); +} + +Json::Array Json::parse_array(std::wistream& is) +{ + Array result; + wchar_t ch; + is >> ch; + while (is >> ch) { + if (ch == L']') { + return result; + } + is.unget(); + Json value = parse(is); + result.push_back(value); + is >> ch; + if (ch == L']') { + return result; + } + else if (ch != L',') { + throw std::runtime_error("Expected ',' or ']' in JSON array"); + } + } + throw std::runtime_error("Unexpected end of JSON array"); +} + +std::wstring Json::parse_string(std::wistream& is) +{ + std::wstring result; + wchar_t ch; + is >> std::ws; + if (!(is >> ch) || ch != L'"') { + throw std::runtime_error("Expected opening quote in JSON string"); + } + + while (is.get(ch)) { + if (ch == L'\\') { + if (!(is.get(ch))) { + throw std::runtime_error("Unexpected end of JSON string"); + } + switch (ch) { + case L'"': result += L'"'; break; + case L'\\': result += L'\\'; break; + case L'/': result += L'/'; break; + case L'b': result += L'\b'; break; + case L'f': result += L'\f'; break; + case L'n': result += L'\n'; break; + case L'r': result += L'\r'; break; + case L't': result += L'\t'; break; + case L'u': { + std::wstring hex_code; + for (int i = 0; i < 4; ++i) { + if (!(is.get(ch))) { + throw std::runtime_error("Incomplete Unicode escape sequence in JSON string"); + } + hex_code += ch; + } + wchar_t unicode_char = static_cast(std::stoi(hex_code, nullptr, 16)); + result += unicode_char; + break; + } + default: throw std::runtime_error("Invalid JSON string escape sequence"); + } + } + else if (ch == L'"') { + break; + } + else { + result += ch; + } + } + + if (ch != L'"') { + throw std::runtime_error("Unexpected end of JSON string"); + } + + return result; +} + +Json Json::parse_number(std::wistream& is) +{ + wchar_t ch; + std::wstring number_str; + while (is >> ch && (std::iswdigit(ch) || ch == L'.' || ch == L'e' || ch == L'E' || ch == L'+' || ch == L'-')) { + number_str += ch; + } + is.unget(); + std::wistringstream number_iss(number_str); + double number; + number_iss >> number; + if (number_iss.fail() || !number_iss.eof()) { + throw std::runtime_error("Invalid JSON number"); + } + + if (number_str.find(L'.') != std::wstring::npos || number_str.find(L'e') != std::wstring::npos || number_str.find(L'E') != std::wstring::npos) { + return number; + } + else { + return static_cast(number); + } +} + +Json Json::parse_boolean(std::wistream& is) +{ + std::wstring boolean_str; + wchar_t ch; + for (int i = 0; i < 5; ++i) { + if (!(is.get(ch))) { + throw std::runtime_error("Unexpected end of JSON boolean"); + } + boolean_str += ch; + if (boolean_str == L"true") { + return true; + } + else if (boolean_str == L"false") { + return false; + } + } + throw std::runtime_error("Invalid JSON boolean"); +} + +Json Json::parse_null(std::wistream& is) +{ + std::wstring null_str; + wchar_t ch; + for (int i = 0; i < 4; ++i) { + if (!(is.get(ch))) { + throw std::runtime_error("Unexpected end of JSON null"); + } + null_str += ch; + } + if (null_str == L"null") { + return Json(); + } + else { + throw std::runtime_error("Invalid JSON null"); + } +} diff --git a/src/BasicUtils/Json.h b/src/BasicUtils/Json.h new file mode 100644 index 0000000..9a06d4e --- /dev/null +++ b/src/BasicUtils/Json.h @@ -0,0 +1,150 @@ +#ifndef JSON_H +#define JSON_H + +#include +#include +#include +#include +#include + +template class Template> +struct is_specialization : std::false_type {}; + +template