mirror of
https://github.com/SpotX-Official/SpotX.git
synced 2026-08-10 01:51:09 +10:00
Compare commits
5
Commits
e22aba4ed4
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a179d3cf0 | ||
|
|
c2cc808ef6 | ||
|
|
50bc5a6526 | ||
|
|
3d1ddd6850 | ||
|
|
afb4c3fcd1 |
@@ -1,38 +1,53 @@
|
|||||||
name: Check Spotx for the latest Spotify
|
name: Check Spotx for the latest Spotify
|
||||||
|
run-name: Check SpotX for Spotify ${{ inputs.version || 'latest' }}
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: Spotify version like 1.2.95 or 1.2.95.453, empty means latest
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
type: string
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Run Spotx
|
- name: Run Spotx
|
||||||
shell: powershell
|
shell: powershell
|
||||||
|
env:
|
||||||
|
SPOTIFY_VERSION: ${{ inputs.version }}
|
||||||
run: |
|
run: |
|
||||||
$jsonUrl = "https://raw.githubusercontent.com/LoaderSpot/table/refs/heads/main/table/versions.json"
|
$spotifyVersion = ([string]$env:SPOTIFY_VERSION).Trim()
|
||||||
$resp = Invoke-RestMethod -Uri $jsonUrl
|
|
||||||
|
|
||||||
# Use fullversion because installer names include architecture suffixes
|
if ([string]::IsNullOrWhiteSpace($spotifyVersion)) {
|
||||||
$latestEntry = $resp.PSObject.Properties | Select-Object -First 1
|
$jsonUrl = "https://raw.githubusercontent.com/LoaderSpot/table/refs/heads/main/table/versions.json"
|
||||||
if (-not $latestEntry) {
|
$resp = Invoke-RestMethod -Uri $jsonUrl
|
||||||
throw "No Spotify versions found in $jsonUrl"
|
|
||||||
|
# Use fullversion for the installer name
|
||||||
|
$latestEntry = $resp.PSObject.Properties | Select-Object -First 1
|
||||||
|
if (-not $latestEntry) {
|
||||||
|
throw "No Spotify versions found in $jsonUrl"
|
||||||
|
}
|
||||||
|
|
||||||
|
$spotifyVersion = [string]$latestEntry.Value.fullversion
|
||||||
|
if ([string]::IsNullOrWhiteSpace($spotifyVersion) -or $spotifyVersion -notmatch '^\d+\.\d+\.\d+\.\d+\.g[0-9a-f]{8}$') {
|
||||||
|
throw "Invalid Spotify fullversion for $($latestEntry.Name)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($spotifyVersion -notmatch '^\d+\.\d+\.\d+(?:\.\d+)?$') {
|
||||||
|
throw "Invalid Spotify version: $spotifyVersion"
|
||||||
}
|
}
|
||||||
|
|
||||||
$spotifyFullVersion = $latestEntry.Value.fullversion
|
Write-Host "Selected Spotify version: $spotifyVersion"
|
||||||
if ([string]::IsNullOrWhiteSpace($spotifyFullVersion) -or $spotifyFullVersion -notmatch '^\d+\.\d+\.\d+\.\d+\.g[0-9a-f]{8}$') {
|
|
||||||
throw "Invalid Spotify fullversion for $($latestEntry.Name)"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Incoming parameters
|
# Incoming parameters
|
||||||
$parameters = @{
|
$parameters = @{
|
||||||
Verbose = $true
|
Verbose = $true
|
||||||
new_theme = $true
|
new_theme = $true
|
||||||
version = $spotifyFullVersion
|
version = $spotifyVersion
|
||||||
confirm_spoti_recomended_over = $true
|
confirm_spoti_recomended_over = $true
|
||||||
cache_limit = 1000
|
cache_limit = 1000
|
||||||
block_update_on = $true
|
block_update_on = $true
|
||||||
|
|||||||
+115
-11
@@ -111,7 +111,7 @@
|
|||||||
"native_description": "Enable music leavebehinds on eligible playlists for desktop",
|
"native_description": "Enable music leavebehinds on eligible playlists for desktop",
|
||||||
"version": {
|
"version": {
|
||||||
"fr": "1.2.10",
|
"fr": "1.2.10",
|
||||||
"to": ""
|
"to": "1.2.93"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"HptoLocationRefactor": {
|
"HptoLocationRefactor": {
|
||||||
@@ -417,7 +417,7 @@
|
|||||||
"native_description": "Allow switching between the new Home Ads format and legacy HPTO format (as opposed to only showing the legacy HPTO format)",
|
"native_description": "Allow switching between the new Home Ads format and legacy HPTO format (as opposed to only showing the legacy HPTO format)",
|
||||||
"version": {
|
"version": {
|
||||||
"fr": "1.2.34",
|
"fr": "1.2.34",
|
||||||
"to": ""
|
"to": "1.2.94"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"LimitedAdsLabelsOnPlaylistCards": {
|
"LimitedAdsLabelsOnPlaylistCards": {
|
||||||
@@ -512,7 +512,7 @@
|
|||||||
"native_description": "Enables the Employee Flag to send to Sentry",
|
"native_description": "Enables the Employee Flag to send to Sentry",
|
||||||
"version": {
|
"version": {
|
||||||
"fr": "1.2.44",
|
"fr": "1.2.44",
|
||||||
"to": ""
|
"to": "1.2.93"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"HomeImpressions": {
|
"HomeImpressions": {
|
||||||
@@ -656,7 +656,7 @@
|
|||||||
"native_description": "Enable Hpto announcements on Home",
|
"native_description": "Enable Hpto announcements on Home",
|
||||||
"version": {
|
"version": {
|
||||||
"fr": "1.2.65",
|
"fr": "1.2.65",
|
||||||
"to": ""
|
"to": "1.2.94"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"SponsoredPlaylistV2": {
|
"SponsoredPlaylistV2": {
|
||||||
@@ -778,7 +778,7 @@
|
|||||||
"name": "bypassApplyUpdateCheck",
|
"name": "bypassApplyUpdateCheck",
|
||||||
"version": {
|
"version": {
|
||||||
"fr": "1.2.84",
|
"fr": "1.2.84",
|
||||||
"to": ""
|
"to": "1.2.93"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ad_feedback_in_stream": {
|
"ad_feedback_in_stream": {
|
||||||
@@ -836,6 +836,62 @@
|
|||||||
"fr": "1.2.92",
|
"fr": "1.2.92",
|
||||||
"to": ""
|
"to": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"EmbeddedAdHtmlDisplay": {
|
||||||
|
"name": "enableEmbeddedAdHtmlDisplay",
|
||||||
|
"description": "Enable HTML display ads in the embedded NPV",
|
||||||
|
"version": {
|
||||||
|
"fr": "1.2.94",
|
||||||
|
"to": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ImageOptimizationSentrySpanMeasurement": {
|
||||||
|
"name": "enableImageOptimizationSentrySpanMeasurement",
|
||||||
|
"description": "Enable Sentry image resource span attributes for image optimization rollout measurement",
|
||||||
|
"version": {
|
||||||
|
"fr": "1.2.94",
|
||||||
|
"to": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SentryReactRouterV6Routing": {
|
||||||
|
"name": "enableSentryReactRouterV6Routing",
|
||||||
|
"description": "Enable Sentry React Router v6 route instrumentation for Web Player SPA transactions",
|
||||||
|
"version": {
|
||||||
|
"fr": "1.2.94",
|
||||||
|
"to": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SponsoredPlaylistHorizontalVideo": {
|
||||||
|
"name": "enableSponsoredPlaylistHorizontalVideo",
|
||||||
|
"description": "Enables horizontal video layout for sponsored playlist headers on desktop",
|
||||||
|
"version": {
|
||||||
|
"fr": "1.2.95",
|
||||||
|
"to": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AdsSurfaceStateForAdOrchestration": {
|
||||||
|
"name": "useAdsSurfaceStateForAdOrchestration",
|
||||||
|
"description": "Use ads-owned NPV and cinema surface state for ad orchestration",
|
||||||
|
"version": {
|
||||||
|
"fr": "1.2.96",
|
||||||
|
"to": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"HptoHarmonyVideoPlayer": {
|
||||||
|
"name": "enableHptoHarmonyVideoPlayer",
|
||||||
|
"description": "Enable the Harmony-based video player for HPTO home video ads, replacing the Betamax player",
|
||||||
|
"version": {
|
||||||
|
"fr": "1.2.96",
|
||||||
|
"to": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SidekickFeedbackBanner": {
|
||||||
|
"name": "enableSidekickFeedbackBanner",
|
||||||
|
"description": "Show the feedback banner in the Sidekick chat",
|
||||||
|
"version": {
|
||||||
|
"fr": "1.2.96",
|
||||||
|
"to": ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"EnableExp": {
|
"EnableExp": {
|
||||||
@@ -962,7 +1018,7 @@
|
|||||||
"native_description": "Enable silence trimming in podcasts",
|
"native_description": "Enable silence trimming in podcasts",
|
||||||
"version": {
|
"version": {
|
||||||
"fr": "1.1.99",
|
"fr": "1.1.99",
|
||||||
"to": ""
|
"to": "1.2.93"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"PlaybackSpeedIncrements": {
|
"PlaybackSpeedIncrements": {
|
||||||
@@ -1448,7 +1504,7 @@
|
|||||||
"native_description": "Disable the Premium Only Modal",
|
"native_description": "Disable the Premium Only Modal",
|
||||||
"version": {
|
"version": {
|
||||||
"fr": "1.2.39",
|
"fr": "1.2.39",
|
||||||
"to": ""
|
"to": "1.2.95"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"CenteredLayout": {
|
"CenteredLayout": {
|
||||||
@@ -1664,7 +1720,7 @@
|
|||||||
"native_description": "Enable reverse sort direction in Your Library",
|
"native_description": "Enable reverse sort direction in Your Library",
|
||||||
"version": {
|
"version": {
|
||||||
"fr": "1.2.60",
|
"fr": "1.2.60",
|
||||||
"to": ""
|
"to": "1.2.94"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"BackendSearchHistory": {
|
"BackendSearchHistory": {
|
||||||
@@ -1910,7 +1966,7 @@
|
|||||||
"native_description": "Enable BPM and key columns in playlist recommendations",
|
"native_description": "Enable BPM and key columns in playlist recommendations",
|
||||||
"version": {
|
"version": {
|
||||||
"fr": "1.2.82",
|
"fr": "1.2.82",
|
||||||
"to": ""
|
"to": "1.2.94"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ShareActionBarButton": {
|
"ShareActionBarButton": {
|
||||||
@@ -1934,7 +1990,7 @@
|
|||||||
"native_description": "Show video preference settings for users to control video playback types",
|
"native_description": "Show video preference settings for users to control video playback types",
|
||||||
"version": {
|
"version": {
|
||||||
"fr": "1.2.86",
|
"fr": "1.2.86",
|
||||||
"to": ""
|
"to": "1.2.95"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"VideoQualityEnabled": {
|
"VideoQualityEnabled": {
|
||||||
@@ -1984,6 +2040,54 @@
|
|||||||
"fr": "1.2.89",
|
"fr": "1.2.89",
|
||||||
"to": ""
|
"to": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"LeaderboardEmptySlotHandling": {
|
||||||
|
"name": "enableLeaderboardEmptySlotHandling",
|
||||||
|
"native_description": "Config for clearing the current leaderboard ad and hiding the leaderboard container when the ad slot returns an empty response",
|
||||||
|
"version": {
|
||||||
|
"fr": "1.2.94",
|
||||||
|
"to": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"EntityHeaderNew": {
|
||||||
|
"name": "enableEntityHeaderNew",
|
||||||
|
"native_description": "Enable the new entity header design",
|
||||||
|
"version": {
|
||||||
|
"fr": "1.2.95",
|
||||||
|
"to": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ContextMenuLayoutV2": {
|
||||||
|
"name": "enableContextMenuLayoutV2",
|
||||||
|
"native_description": "Enables the Context Menu 2.0 layout",
|
||||||
|
"version": {
|
||||||
|
"fr": "1.2.96",
|
||||||
|
"to": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"CanvasContextMenuToggle": {
|
||||||
|
"name": "enableCanvasContextMenuToggle",
|
||||||
|
"native_description": "Enables the Canvas context menu toggle",
|
||||||
|
"version": {
|
||||||
|
"fr": "1.2.96",
|
||||||
|
"to": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"MusicVideos": {
|
||||||
|
"name": "enableMusicVideos",
|
||||||
|
"native_description": "Whether music videos are available for the current user. Override to true for supported products and markets",
|
||||||
|
"version": {
|
||||||
|
"fr": "1.2.96",
|
||||||
|
"to": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"VideoShelfPreviews": {
|
||||||
|
"name": "enableVideoShelfPreviews",
|
||||||
|
"native_description": "Enable video previews on hover in video shelf cards",
|
||||||
|
"version": {
|
||||||
|
"fr": "1.2.96",
|
||||||
|
"to": ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"CustomExp": {
|
"CustomExp": {
|
||||||
@@ -2084,7 +2188,7 @@
|
|||||||
"value": "CONTROL",
|
"value": "CONTROL",
|
||||||
"version": {
|
"version": {
|
||||||
"fr": "1.2.31",
|
"fr": "1.2.31",
|
||||||
"to": ""
|
"to": "1.2.93"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"CanvasNpv": {
|
"CanvasNpv": {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
param
|
param
|
||||||
(
|
(
|
||||||
[Parameter(HelpMessage = 'Latest recommended Spotify version for Windows 10+.')]
|
[Parameter(HelpMessage = 'Latest recommended Spotify version for Windows 10+.')]
|
||||||
[string]$latest_full = "1.2.94",
|
[string]$latest_full = "1.2.96",
|
||||||
|
|
||||||
[Parameter(HelpMessage = 'Latest supported Spotify version for Windows 7-8.1')]
|
[Parameter(HelpMessage = 'Latest supported Spotify version for Windows 7-8.1')]
|
||||||
[string]$last_win7_full = "1.2.5.1006.g22820f93",
|
[string]$last_win7_full = "1.2.5.1006.g22820f93",
|
||||||
@@ -30,6 +30,9 @@ param
|
|||||||
[Parameter(HelpMessage = 'Skip pause before exit')]
|
[Parameter(HelpMessage = 'Skip pause before exit')]
|
||||||
[switch]$no_pause,
|
[switch]$no_pause,
|
||||||
|
|
||||||
|
[Parameter(HelpMessage = 'Skip Microsoft Defender exclusions')]
|
||||||
|
[switch]$defender_exclusions_off,
|
||||||
|
|
||||||
[Parameter(HelpMessage = "Use github.io mirror instead of raw.githubusercontent.")]
|
[Parameter(HelpMessage = "Use github.io mirror instead of raw.githubusercontent.")]
|
||||||
[Alias("m")]
|
[Alias("m")]
|
||||||
[switch]$mirror,
|
[switch]$mirror,
|
||||||
@@ -325,7 +328,8 @@ function Format-LanguageCode {
|
|||||||
return $returnCode
|
return $returnCode
|
||||||
}
|
}
|
||||||
|
|
||||||
$spotifyDirectory = Join-Path $env:APPDATA 'Spotify'
|
$spotifyRoamingDirectory = Join-Path $env:APPDATA 'Spotify'
|
||||||
|
$spotifyDirectory = $spotifyRoamingDirectory
|
||||||
$spotifyDirectory2 = Join-Path $env:LOCALAPPDATA 'Spotify'
|
$spotifyDirectory2 = Join-Path $env:LOCALAPPDATA 'Spotify'
|
||||||
|
|
||||||
# Использовать кастомный путь если указан параметр -SpotifyPath
|
# Использовать кастомный путь если указан параметр -SpotifyPath
|
||||||
@@ -341,6 +345,7 @@ $dll_bak = Join-Path $spotifyDirectory 'Spotify.dll.bak'
|
|||||||
$chrome_elf_bak = Join-Path $spotifyDirectory 'chrome_elf.dll.bak'
|
$chrome_elf_bak = Join-Path $spotifyDirectory 'chrome_elf.dll.bak'
|
||||||
$spotifyUninstall = Join-Path ([System.IO.Path]::GetTempPath()) 'SpotifyUninstall.exe'
|
$spotifyUninstall = Join-Path ([System.IO.Path]::GetTempPath()) 'SpotifyUninstall.exe'
|
||||||
$start_menu = Join-Path $env:APPDATA 'Microsoft\Windows\Start Menu\Programs\Spotify.lnk'
|
$start_menu = Join-Path $env:APPDATA 'Microsoft\Windows\Start Menu\Programs\Spotify.lnk'
|
||||||
|
$xpui_spa_patch = Join-Path (Join-Path $spotifyDirectory 'Apps') 'xpui.spa'
|
||||||
|
|
||||||
$upgrade_client = $false
|
$upgrade_client = $false
|
||||||
$downgrading = $false
|
$downgrading = $false
|
||||||
@@ -1514,6 +1519,226 @@ function DesktopFolder {
|
|||||||
return $desktop_folder
|
return $desktop_folder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Test-IsAdministrator {
|
||||||
|
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||||
|
|
||||||
|
try {
|
||||||
|
$principal = New-Object -TypeName Security.Principal.WindowsPrincipal -ArgumentList $identity
|
||||||
|
return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
$identity.Dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ConvertTo-DefenderExclusionPath {
|
||||||
|
param (
|
||||||
|
[string[]]$Path
|
||||||
|
)
|
||||||
|
|
||||||
|
$normalizedPaths = foreach ($item in $Path) {
|
||||||
|
if ([string]::IsNullOrWhiteSpace($item)) { continue }
|
||||||
|
|
||||||
|
try {
|
||||||
|
[IO.Path]::GetFullPath([Environment]::ExpandEnvironmentVariables($item))
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Verbose "Invalid Microsoft Defender exclusion path: $item"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return @($normalizedPaths | Sort-Object -Unique)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ConvertTo-PowerShellStringLiteral {
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[AllowEmptyString()]
|
||||||
|
[string]$Value
|
||||||
|
)
|
||||||
|
|
||||||
|
if ($Value.IndexOf([char]34) -ge 0) {
|
||||||
|
throw 'Double quotes are not valid in Windows paths'
|
||||||
|
}
|
||||||
|
|
||||||
|
$escapedValue = [Management.Automation.Language.CodeGeneration]::EscapeSingleQuotedStringContent($Value)
|
||||||
|
return "'$escapedValue'"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Add-SpotifyDefenderExclusions {
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string[]]$Path,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string[]]$ProcessPath
|
||||||
|
)
|
||||||
|
|
||||||
|
[string[]]$paths = @(ConvertTo-DefenderExclusionPath -Path $Path)
|
||||||
|
[string[]]$processPaths = @(ConvertTo-DefenderExclusionPath -Path $ProcessPath)
|
||||||
|
if ($paths.Count -eq 0 -and $processPaths.Count -eq 0) { return }
|
||||||
|
|
||||||
|
$defenderPrompt = $lang.DefenderPrompt
|
||||||
|
$defenderAdded = $lang.DefenderAdded
|
||||||
|
|
||||||
|
try {
|
||||||
|
$isAdministrator = Test-IsAdministrator
|
||||||
|
|
||||||
|
if (-not $isAdministrator) {
|
||||||
|
do {
|
||||||
|
$defenderChoice = Read-Host -Prompt $defenderPrompt
|
||||||
|
Write-Host
|
||||||
|
if ($defenderChoice -notmatch '^y$|^n$') { incorrectValue }
|
||||||
|
}
|
||||||
|
while ($defenderChoice -notmatch '^y$|^n$')
|
||||||
|
|
||||||
|
if ($defenderChoice -eq 'n') { return }
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($isAdministrator) {
|
||||||
|
$modulePath = [IO.Path]::Combine(
|
||||||
|
[Environment]::SystemDirectory,
|
||||||
|
'WindowsPowerShell\v1.0\Modules\Defender\Defender.psd1'
|
||||||
|
)
|
||||||
|
$null = Import-Module -Name $modulePath -Force -ErrorAction Stop
|
||||||
|
$preferences = Defender\Get-MpPreference -ErrorAction Stop
|
||||||
|
$currentPaths = @($preferences.ExclusionPath)
|
||||||
|
$currentProcessPaths = @($preferences.ExclusionProcess)
|
||||||
|
$missingPaths = @($paths | Where-Object { $_ -notin $currentPaths })
|
||||||
|
$missingProcessPaths = @($processPaths | Where-Object { $_ -notin $currentProcessPaths })
|
||||||
|
|
||||||
|
if ($missingPaths.Count -gt 0) {
|
||||||
|
$null = Defender\Add-MpPreference -ExclusionPath $missingPaths -Force -ErrorAction Stop
|
||||||
|
}
|
||||||
|
if ($missingProcessPaths.Count -gt 0) {
|
||||||
|
$null = Defender\Add-MpPreference -ExclusionProcess $missingProcessPaths -Force -ErrorAction Stop
|
||||||
|
}
|
||||||
|
|
||||||
|
$verified = $false
|
||||||
|
for ($attempt = 0; $attempt -lt 5; $attempt++) {
|
||||||
|
$updatedPreferences = Defender\Get-MpPreference -ErrorAction Stop
|
||||||
|
$updatedPaths = @($updatedPreferences.ExclusionPath)
|
||||||
|
$updatedProcessPaths = @($updatedPreferences.ExclusionProcess)
|
||||||
|
$remainingPaths = @($paths | Where-Object { $_ -notin $updatedPaths })
|
||||||
|
$remainingProcessPaths = @($processPaths | Where-Object { $_ -notin $updatedProcessPaths })
|
||||||
|
if ($remainingPaths.Count -eq 0 -and $remainingProcessPaths.Count -eq 0) {
|
||||||
|
$verified = $true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if ($attempt -lt 4) { Start-Sleep -Milliseconds 250 }
|
||||||
|
}
|
||||||
|
if (-not $verified) {
|
||||||
|
throw 'Microsoft Defender exclusion verification failed'
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host $defenderAdded
|
||||||
|
Write-Host
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$pathLiterals = @($paths | ForEach-Object {
|
||||||
|
ConvertTo-PowerShellStringLiteral -Value $_
|
||||||
|
}) -join ', '
|
||||||
|
$processPathLiterals = @($processPaths | ForEach-Object {
|
||||||
|
ConvertTo-PowerShellStringLiteral -Value $_
|
||||||
|
}) -join ', '
|
||||||
|
$command = @(
|
||||||
|
"`$ErrorActionPreference = 'Stop';"
|
||||||
|
'try {'
|
||||||
|
"[string[]]`$paths = @($pathLiterals);"
|
||||||
|
"[string[]]`$processPaths = @($processPathLiterals);"
|
||||||
|
"`$modulePath = [IO.Path]::Combine([Environment]::SystemDirectory, 'WindowsPowerShell\v1.0\Modules\Defender\Defender.psd1');"
|
||||||
|
'$null = Import-Module -Name $modulePath -Force -ErrorAction Stop;'
|
||||||
|
'$preferences = Defender\Get-MpPreference -ErrorAction Stop;'
|
||||||
|
'$currentPaths = @($preferences.ExclusionPath);'
|
||||||
|
'$currentProcessPaths = @($preferences.ExclusionProcess);'
|
||||||
|
'$missingPaths = @($paths | Where-Object { $_ -notin $currentPaths });'
|
||||||
|
'$missingProcessPaths = @($processPaths | Where-Object { $_ -notin $currentProcessPaths });'
|
||||||
|
'if ($missingPaths.Count -gt 0) { $null = Defender\Add-MpPreference -ExclusionPath $missingPaths -Force -ErrorAction Stop };'
|
||||||
|
'if ($missingProcessPaths.Count -gt 0) { $null = Defender\Add-MpPreference -ExclusionProcess $missingProcessPaths -Force -ErrorAction Stop };'
|
||||||
|
'$verified = $false;'
|
||||||
|
'for ($attempt = 0; $attempt -lt 5; $attempt++) {'
|
||||||
|
'$updatedPreferences = Defender\Get-MpPreference -ErrorAction Stop;'
|
||||||
|
'$updatedPaths = @($updatedPreferences.ExclusionPath);'
|
||||||
|
'$updatedProcessPaths = @($updatedPreferences.ExclusionProcess);'
|
||||||
|
'$remainingPaths = @($paths | Where-Object { $_ -notin $updatedPaths });'
|
||||||
|
'$remainingProcessPaths = @($processPaths | Where-Object { $_ -notin $updatedProcessPaths });'
|
||||||
|
'if ($remainingPaths.Count -eq 0 -and $remainingProcessPaths.Count -eq 0) { $verified = $true; break };'
|
||||||
|
'if ($attempt -lt 4) { Start-Sleep -Milliseconds 250 };'
|
||||||
|
'}'
|
||||||
|
"if (-not `$verified) { throw 'Microsoft Defender exclusion verification failed' };"
|
||||||
|
'exit 0'
|
||||||
|
'}'
|
||||||
|
'catch {'
|
||||||
|
'exit 1'
|
||||||
|
'}'
|
||||||
|
) -join ' '
|
||||||
|
|
||||||
|
$systemDirectoryName = if (
|
||||||
|
[Environment]::Is64BitOperatingSystem -and
|
||||||
|
-not [Environment]::Is64BitProcess
|
||||||
|
) {
|
||||||
|
'Sysnative'
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
'System32'
|
||||||
|
}
|
||||||
|
$powerShellPath = Join-Path $env:SystemRoot "$systemDirectoryName\WindowsPowerShell\v1.0\powershell.exe"
|
||||||
|
$startProcessParams = @{
|
||||||
|
FilePath = $powerShellPath
|
||||||
|
ArgumentList = "-NoLogo -NoProfile -Command `"$command`""
|
||||||
|
Verb = 'RunAs'
|
||||||
|
WindowStyle = 'Normal'
|
||||||
|
Wait = $true
|
||||||
|
PassThru = $true
|
||||||
|
ErrorAction = 'Stop'
|
||||||
|
}
|
||||||
|
|
||||||
|
$process = Start-Process @startProcessParams
|
||||||
|
if ($process.ExitCode -ne 0) {
|
||||||
|
throw "Elevated Microsoft Defender command failed with exit code $($process.ExitCode)"
|
||||||
|
}
|
||||||
|
Write-Host $defenderAdded
|
||||||
|
Write-Host
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Warning $lang.DefenderFailed
|
||||||
|
Write-Verbose $_.Exception.Message
|
||||||
|
Write-Host
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $defender_exclusions_off) {
|
||||||
|
try {
|
||||||
|
$desktopShortcut = Join-Path (DesktopFolder) 'Spotify.lnk'
|
||||||
|
$currentProcess = [Diagnostics.Process]::GetCurrentProcess()
|
||||||
|
try {
|
||||||
|
$defenderPowerShellProcess = $currentProcess.MainModule.FileName
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
$currentProcess.Dispose()
|
||||||
|
}
|
||||||
|
$defenderExclusionPaths = @(
|
||||||
|
$spotifyRoamingDirectory
|
||||||
|
$spotifyDirectory2
|
||||||
|
$spotifyExecutable
|
||||||
|
$spotifyDll
|
||||||
|
$chrome_elf
|
||||||
|
$xpui_spa_patch
|
||||||
|
$desktopShortcut
|
||||||
|
$start_menu
|
||||||
|
)
|
||||||
|
$null = Add-SpotifyDefenderExclusions `
|
||||||
|
-Path $defenderExclusionPaths `
|
||||||
|
-ProcessPath $defenderPowerShellProcess
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Warning $lang.DefenderFailed
|
||||||
|
Write-Verbose $_.Exception.Message
|
||||||
|
Write-Host
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function Kill-Spotify {
|
function Kill-Spotify {
|
||||||
param (
|
param (
|
||||||
[int]$maxAttempts = 5
|
[int]$maxAttempts = 5
|
||||||
@@ -2841,7 +3066,17 @@ public static class BinaryScanner {
|
|||||||
}
|
}
|
||||||
"@
|
"@
|
||||||
|
|
||||||
Add-Type -TypeDefinition $csharpCode
|
try {
|
||||||
|
Add-Type -TypeDefinition $csharpCode -ErrorAction Stop
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
$compilerError = $_.Exception.Message -split '\r?\n' | Select-Object -First 1
|
||||||
|
throw "BinaryScanner initialization failed: $compilerError"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not ([System.Management.Automation.PSTypeName]'BinaryScanner').Type) {
|
||||||
|
throw "BinaryScanner initialization failed: Type was not loaded"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function Convert-HexStringToBytes {
|
function Convert-HexStringToBytes {
|
||||||
@@ -2974,7 +3209,13 @@ function Reset-Dll-Sign {
|
|||||||
$TargetStringText = "Check failed: sep_pos != std::wstring::npos."
|
$TargetStringText = "Check failed: sep_pos != std::wstring::npos."
|
||||||
$Patch_x64 = Convert-HexStringToBytes "B8 01 00 00 00 C3"
|
$Patch_x64 = Convert-HexStringToBytes "B8 01 00 00 00 C3"
|
||||||
$Patch_ARM64 = Convert-HexStringToBytes "20 00 80 52 C0 03 5F D6"
|
$Patch_ARM64 = Convert-HexStringToBytes "20 00 80 52 C0 03 5F D6"
|
||||||
Initialize-BinaryScanner
|
try {
|
||||||
|
Initialize-BinaryScanner
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Warning $_.Exception.Message
|
||||||
|
Stop-Script
|
||||||
|
}
|
||||||
|
|
||||||
Write-Verbose "Loading file: $FilePath"
|
Write-Verbose "Loading file: $FilePath"
|
||||||
if (-not (Test-Path $FilePath)) {
|
if (-not (Test-Path $FilePath)) {
|
||||||
@@ -3006,8 +3247,21 @@ function Reset-Dll-Sign {
|
|||||||
|
|
||||||
Write-Verbose "Searching for function..."
|
Write-Verbose "Searching for function..."
|
||||||
$StringBytes = [System.Text.Encoding]::ASCII.GetBytes($TargetStringText)
|
$StringBytes = [System.Text.Encoding]::ASCII.GetBytes($TargetStringText)
|
||||||
$StringOffset = [BinaryScanner]::FindBytes($bytes, $StringBytes, 0)
|
try {
|
||||||
if ($StringOffset -eq -1) {
|
$StringOffset = & {
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
[BinaryScanner]::FindBytes($bytes, $StringBytes, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Warning ("BinaryScanner scan failed for Spotify.dll: {0}" -f $_.Exception.Message)
|
||||||
|
Stop-Script
|
||||||
|
}
|
||||||
|
if ($null -eq $StringOffset) {
|
||||||
|
Write-Warning "BinaryScanner returned no result for Spotify.dll"
|
||||||
|
Stop-Script
|
||||||
|
}
|
||||||
|
if ($StringOffset -lt 0) {
|
||||||
Write-Warning "String not found in Spotify.dll"
|
Write-Warning "String not found in Spotify.dll"
|
||||||
Stop-Script
|
Stop-Script
|
||||||
}
|
}
|
||||||
@@ -3155,6 +3409,250 @@ function Read-X64BlockSlotsField {
|
|||||||
throw "Unexpected block_slots field instruction"
|
throw "Unexpected block_slots field instruction"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Get-X64BlockSlotsMapperSequenceEnum {
|
||||||
|
param(
|
||||||
|
[byte[]]$Bytes,
|
||||||
|
[int]$SequenceOffset,
|
||||||
|
[int]$BranchOffset,
|
||||||
|
[bool]$MatchTaken = $true
|
||||||
|
)
|
||||||
|
|
||||||
|
$movEcxR8d = Convert-HexStringToBytes '41 8B C8'
|
||||||
|
$subEcx8 = Convert-HexStringToBytes '83 E9'
|
||||||
|
$subEcx32 = Convert-HexStringToBytes '81 E9'
|
||||||
|
$cmpEcx8 = Convert-HexStringToBytes '83 F9'
|
||||||
|
$cmpEcx32 = Convert-HexStringToBytes '81 F9'
|
||||||
|
if (-not [BinaryScanner]::MatchBytes($Bytes, $SequenceOffset, $movEcxR8d)) {
|
||||||
|
return @()
|
||||||
|
}
|
||||||
|
|
||||||
|
$matches = @()
|
||||||
|
for ($enumValue = 1; $enumValue -le 0x3FF; $enumValue++) {
|
||||||
|
$ecx = [int64]$enumValue
|
||||||
|
$zeroFlag = $false
|
||||||
|
$hasFlags = $false
|
||||||
|
$cursor = $SequenceOffset + $movEcxR8d.Length
|
||||||
|
$valid = $true
|
||||||
|
$matched = $false
|
||||||
|
|
||||||
|
while ($cursor -le $BranchOffset) {
|
||||||
|
if ([BinaryScanner]::MatchBytes($Bytes, $cursor, $subEcx8)) {
|
||||||
|
$ecx -= [int64](Read-X64SignedByte -Bytes $Bytes -Offset ($cursor + 2))
|
||||||
|
$zeroFlag = ($ecx -eq 0)
|
||||||
|
$hasFlags = $true
|
||||||
|
$cursor += 3
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ([BinaryScanner]::MatchBytes($Bytes, $cursor, $subEcx32)) {
|
||||||
|
$ecx -= [int64][BitConverter]::ToInt32($Bytes, $cursor + 2)
|
||||||
|
$zeroFlag = ($ecx -eq 0)
|
||||||
|
$hasFlags = $true
|
||||||
|
$cursor += 6
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ([BinaryScanner]::MatchBytes($Bytes, $cursor, $cmpEcx8)) {
|
||||||
|
$compareValue = [int64](Read-X64SignedByte -Bytes $Bytes -Offset ($cursor + 2))
|
||||||
|
$zeroFlag = ($ecx -eq $compareValue)
|
||||||
|
$hasFlags = $true
|
||||||
|
$cursor += 3
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ([BinaryScanner]::MatchBytes($Bytes, $cursor, $cmpEcx32)) {
|
||||||
|
$compareValue = [int64][BitConverter]::ToInt32($Bytes, $cursor + 2)
|
||||||
|
$zeroFlag = ($ecx -eq $compareValue)
|
||||||
|
$hasFlags = $true
|
||||||
|
$cursor += 6
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
$branchLength = 0
|
||||||
|
$branchOnEqual = $false
|
||||||
|
if (($cursor + 2) -le $Bytes.Length -and ($Bytes[$cursor] -eq 0x74 -or $Bytes[$cursor] -eq 0x75)) {
|
||||||
|
$branchLength = 2
|
||||||
|
$branchOnEqual = ($Bytes[$cursor] -eq 0x74)
|
||||||
|
}
|
||||||
|
elseif (($cursor + 6) -le $Bytes.Length -and $Bytes[$cursor] -eq 0x0F -and
|
||||||
|
($Bytes[$cursor + 1] -eq 0x84 -or $Bytes[$cursor + 1] -eq 0x85)) {
|
||||||
|
$branchLength = 6
|
||||||
|
$branchOnEqual = ($Bytes[$cursor + 1] -eq 0x84)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$valid = $false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $hasFlags) {
|
||||||
|
$valid = $false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
$branchTaken = if ($branchOnEqual) { $zeroFlag } else { -not $zeroFlag }
|
||||||
|
if ($cursor -eq $BranchOffset) {
|
||||||
|
$matched = if ($MatchTaken) { $branchTaken } else { -not $branchTaken }
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if ($branchTaken) {
|
||||||
|
$valid = $false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
$cursor += $branchLength
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($valid -and $matched) {
|
||||||
|
$matches += [uint32]$enumValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $matches
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-BlockSlotsMapperEnumValue {
|
||||||
|
param(
|
||||||
|
[byte[]]$Bytes,
|
||||||
|
[object]$PeInfo,
|
||||||
|
[int64]$MapperStartRva,
|
||||||
|
[int64]$MapperEndRva,
|
||||||
|
[int[]]$AnchorRefs
|
||||||
|
)
|
||||||
|
|
||||||
|
$mapperOffset = Get-PEOffsetFromRva -Sections $PeInfo.Sections -Rva $MapperStartRva
|
||||||
|
if ($null -eq $mapperOffset) {
|
||||||
|
throw 'slot_is_disabled mapper offset was not found'
|
||||||
|
}
|
||||||
|
|
||||||
|
$mapperOffset = [int]$mapperOffset
|
||||||
|
$mapperEndOffset = [int64]$mapperOffset + ($MapperEndRva - $MapperStartRva)
|
||||||
|
if ($mapperEndOffset -le $mapperOffset -or $mapperEndOffset -gt $Bytes.Length) {
|
||||||
|
throw 'slot_is_disabled mapper range is invalid'
|
||||||
|
}
|
||||||
|
|
||||||
|
$stringCases = @(
|
||||||
|
[PSCustomObject]@{
|
||||||
|
PrefixLength = 20
|
||||||
|
Pattern = Convert-HexStringToBytes '0F 57 C0 0F 11 02 48 89 7A 10 48 89 7A 18 41 B8 10 00 00 00 48 8D 15 00 00 00 00'
|
||||||
|
Mask = Convert-HexStringToBytes 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 00 00 00'
|
||||||
|
DispatchMode = 'Target'
|
||||||
|
},
|
||||||
|
[PSCustomObject]@{
|
||||||
|
PrefixLength = 18
|
||||||
|
Pattern = Convert-HexStringToBytes '0F 57 C0 0F 11 02 48 89 7A 10 48 89 7A 18 44 8D 41 0F 48 8D 15 00 00 00 00'
|
||||||
|
Mask = Convert-HexStringToBytes 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 00 00 00'
|
||||||
|
DispatchMode = 'Fallthrough'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
$movEcxR8d = Convert-HexStringToBytes '41 8B C8'
|
||||||
|
$cmpR8d8 = Convert-HexStringToBytes '41 83 F8'
|
||||||
|
$cmpR8d32 = Convert-HexStringToBytes '41 81 F8'
|
||||||
|
$enumValues = @{}
|
||||||
|
|
||||||
|
foreach ($anchorRef in $AnchorRefs) {
|
||||||
|
if (([int64]$anchorRef + 7) -gt $mapperEndOffset) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($stringCase in $stringCases) {
|
||||||
|
$caseOffset = [int]$anchorRef - $stringCase.PrefixLength
|
||||||
|
if ($caseOffset -lt $mapperOffset -or
|
||||||
|
-not [BinaryScanner]::MatchMaskedBytes($Bytes, $caseOffset, $stringCase.Pattern, $stringCase.Mask)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
$dispatches = @()
|
||||||
|
if ($stringCase.DispatchMode -eq 'Target') {
|
||||||
|
for ($branchOffset = $mapperOffset; $branchOffset -lt $caseOffset; $branchOffset++) {
|
||||||
|
if (($Bytes[$branchOffset] -eq 0x74 -or $Bytes[$branchOffset] -eq 0x75) -and
|
||||||
|
($branchOffset + 2) -le $mapperEndOffset) {
|
||||||
|
$branchOnEqual = ($Bytes[$branchOffset] -eq 0x74)
|
||||||
|
$branchTarget = [int64]$branchOffset + 2 +
|
||||||
|
(Read-X64SignedByte -Bytes $Bytes -Offset ($branchOffset + 1))
|
||||||
|
}
|
||||||
|
elseif ($Bytes[$branchOffset] -eq 0x0F -and ($branchOffset + 6) -le $mapperEndOffset -and
|
||||||
|
($Bytes[$branchOffset + 1] -eq 0x84 -or $Bytes[$branchOffset + 1] -eq 0x85)) {
|
||||||
|
$branchOnEqual = ($Bytes[$branchOffset + 1] -eq 0x84)
|
||||||
|
$branchTarget = [int64]$branchOffset + 6 + [BitConverter]::ToInt32($Bytes, $branchOffset + 2)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($branchTarget -eq $caseOffset) {
|
||||||
|
$dispatches += [PSCustomObject]@{
|
||||||
|
Offset = $branchOffset
|
||||||
|
BranchOnEqual = $branchOnEqual
|
||||||
|
MatchTaken = $true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ($caseOffset -ge ($mapperOffset + 6) -and $Bytes[$caseOffset - 6] -eq 0x0F -and
|
||||||
|
($Bytes[$caseOffset - 5] -eq 0x84 -or $Bytes[$caseOffset - 5] -eq 0x85)) {
|
||||||
|
$branchOffset = $caseOffset - 6
|
||||||
|
$branchOnEqual = ($Bytes[$caseOffset - 5] -eq 0x84)
|
||||||
|
$branchTarget = [int64]$caseOffset + [BitConverter]::ToInt32($Bytes, $caseOffset - 4)
|
||||||
|
}
|
||||||
|
elseif ($caseOffset -ge ($mapperOffset + 2) -and
|
||||||
|
($Bytes[$caseOffset - 2] -eq 0x74 -or $Bytes[$caseOffset - 2] -eq 0x75)) {
|
||||||
|
$branchOffset = $caseOffset - 2
|
||||||
|
$branchOnEqual = ($Bytes[$caseOffset - 2] -eq 0x74)
|
||||||
|
$branchTarget = [int64]$caseOffset +
|
||||||
|
(Read-X64SignedByte -Bytes $Bytes -Offset ($caseOffset - 1))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($branchTarget -lt $mapperOffset -or $branchTarget -ge $mapperEndOffset -or $branchTarget -eq $caseOffset) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
$dispatches += [PSCustomObject]@{
|
||||||
|
Offset = $branchOffset
|
||||||
|
BranchOnEqual = $branchOnEqual
|
||||||
|
MatchTaken = $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($dispatch in $dispatches) {
|
||||||
|
$branchOffset = [int]$dispatch.Offset
|
||||||
|
$selectsEquality = ($dispatch.BranchOnEqual -eq $dispatch.MatchTaken)
|
||||||
|
if ($selectsEquality -and $branchOffset -ge ($mapperOffset + 4) -and
|
||||||
|
[BinaryScanner]::MatchBytes($Bytes, $branchOffset - 4, $cmpR8d8)) {
|
||||||
|
$enumValue = [int64](Read-X64SignedByte -Bytes $Bytes -Offset ($branchOffset - 1))
|
||||||
|
if ($enumValue -gt 0 -and $enumValue -le 0x3FF) {
|
||||||
|
$enumValues['{0:X}' -f $enumValue] = [uint32]$enumValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($selectsEquality -and $branchOffset -ge ($mapperOffset + 7) -and
|
||||||
|
[BinaryScanner]::MatchBytes($Bytes, $branchOffset - 7, $cmpR8d32)) {
|
||||||
|
$enumValue = [BitConverter]::ToUInt32($Bytes, $branchOffset - 4)
|
||||||
|
if ($enumValue -gt 0 -and $enumValue -le 0x3FF) {
|
||||||
|
$enumValues['{0:X}' -f $enumValue] = $enumValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$sequenceStart = [Math]::Max($mapperOffset, $branchOffset - 0x100)
|
||||||
|
for ($candidateOffset = $sequenceStart; $candidateOffset -lt $branchOffset; $candidateOffset++) {
|
||||||
|
if (-not [BinaryScanner]::MatchBytes($Bytes, $candidateOffset, $movEcxR8d)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
$sequenceEnums = @(Get-X64BlockSlotsMapperSequenceEnum `
|
||||||
|
-Bytes $Bytes `
|
||||||
|
-SequenceOffset $candidateOffset `
|
||||||
|
-BranchOffset $branchOffset `
|
||||||
|
-MatchTaken $dispatch.MatchTaken)
|
||||||
|
foreach ($enumValue in $sequenceEnums) {
|
||||||
|
$enumValues['{0:X}' -f [uint32]$enumValue] = [uint32]$enumValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($enumValues.Count -ne 1) {
|
||||||
|
throw "Expected one slot_is_disabled enum value, found $($enumValues.Count)"
|
||||||
|
}
|
||||||
|
return [uint32](@($enumValues.Values)[0])
|
||||||
|
}
|
||||||
|
|
||||||
function Get-BlockSlotsPredicateInfo {
|
function Get-BlockSlotsPredicateInfo {
|
||||||
param(
|
param(
|
||||||
[byte[]]$Bytes,
|
[byte[]]$Bytes,
|
||||||
@@ -3311,12 +3809,6 @@ function Get-BlockSlotsPredicateInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($flagField.Value -ne ($valueField.Value + 1) -or
|
|
||||||
$valueField.Value -ne ($gateField.Value + 0x18) -or
|
|
||||||
$fallbackField.Value -ne ($gateField.Value - 8)) {
|
|
||||||
return $null
|
|
||||||
}
|
|
||||||
|
|
||||||
$jumpDisplacement = [int64]$returnFalseOffset - ([int64]$patchOffset + 2)
|
$jumpDisplacement = [int64]$returnFalseOffset - ([int64]$patchOffset + 2)
|
||||||
if ($jumpDisplacement -lt 0 -or $jumpDisplacement -gt 0x7F) {
|
if ($jumpDisplacement -lt 0 -or $jumpDisplacement -gt 0x7F) {
|
||||||
return $null
|
return $null
|
||||||
@@ -3401,12 +3893,23 @@ function Find-BlockSlotsBinaryPatchLocation {
|
|||||||
if ($anchorFunctionSize -lt 0x100 -or $anchorFunctionSize -gt 0x4000) {
|
if ($anchorFunctionSize -lt 0x100 -or $anchorFunctionSize -gt 0x4000) {
|
||||||
throw 'Unexpected slot_is_disabled mapper function size'
|
throw 'Unexpected slot_is_disabled mapper function size'
|
||||||
}
|
}
|
||||||
$anchorFunctions['{0:X}' -f [int64]$anchorFunction[0]] = $true
|
$anchorFunctions['{0:X}' -f [int64]$anchorFunction[0]] = [PSCustomObject]@{
|
||||||
|
StartRva = [int64]$anchorFunction[0]
|
||||||
|
EndRva = [int64]$anchorFunction[1]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($anchorFunctions.Count -ne 1) {
|
if ($anchorFunctions.Count -ne 1) {
|
||||||
throw "Expected one slot_is_disabled mapper function, found $($anchorFunctions.Count)"
|
throw "Expected one slot_is_disabled mapper function, found $($anchorFunctions.Count)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$mapperFunction = @($anchorFunctions.Values)[0]
|
||||||
|
$slotDisabledEnum = Get-BlockSlotsMapperEnumValue `
|
||||||
|
-Bytes $Bytes `
|
||||||
|
-PeInfo $PeInfo `
|
||||||
|
-MapperStartRva $mapperFunction.StartRva `
|
||||||
|
-MapperEndRva $mapperFunction.EndRva `
|
||||||
|
-AnchorRefs $anchorRefs
|
||||||
|
|
||||||
$callPatterns = @(
|
$callPatterns = @(
|
||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
Pattern = Convert-HexStringToBytes 'E8 00 00 00 00 84 C0 75 00 BA 00 00 00 00'
|
Pattern = Convert-HexStringToBytes 'E8 00 00 00 00 84 C0 75 00 BA 00 00 00 00'
|
||||||
@@ -3425,7 +3928,6 @@ function Find-BlockSlotsBinaryPatchLocation {
|
|||||||
$textStart = [int]$text.RawPtr
|
$textStart = [int]$text.RawPtr
|
||||||
$textEnd = [int64]$text.RawPtr + [int64]$text.RawSize
|
$textEnd = [int64]$text.RawPtr + [int64]$text.RawSize
|
||||||
$validatedTargets = @{}
|
$validatedTargets = @{}
|
||||||
$ambiguousEnums = @{}
|
|
||||||
|
|
||||||
foreach ($callPattern in $callPatterns) {
|
foreach ($callPattern in $callPatterns) {
|
||||||
$searchOffset = $textStart
|
$searchOffset = $textStart
|
||||||
@@ -3444,7 +3946,7 @@ function Find-BlockSlotsBinaryPatchLocation {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
$enumValue = [BitConverter]::ToUInt32($Bytes, $callOffset + $callPattern.EnumOffset)
|
$enumValue = [BitConverter]::ToUInt32($Bytes, $callOffset + $callPattern.EnumOffset)
|
||||||
if ($enumValue -eq 0 -or $enumValue -gt 0x3FF) {
|
if ($enumValue -ne $slotDisabledEnum) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3496,12 +3998,7 @@ function Find-BlockSlotsBinaryPatchLocation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if ([uint32]$validatedTargets[$key].EnumValue -ne [uint32]$enumValue) {
|
$validatedTargets[$key].CallerCount++
|
||||||
$ambiguousEnums[$key] = $true
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$validatedTargets[$key].CallerCount++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
@@ -3513,9 +4010,6 @@ function Find-BlockSlotsBinaryPatchLocation {
|
|||||||
if ($validatedTargets.Count -eq 0) {
|
if ($validatedTargets.Count -eq 0) {
|
||||||
throw 'block_slots semantic function was not found'
|
throw 'block_slots semantic function was not found'
|
||||||
}
|
}
|
||||||
if ($ambiguousEnums.Count -gt 0) {
|
|
||||||
throw 'block_slots semantic callers use different enum values'
|
|
||||||
}
|
|
||||||
if ($validatedTargets.Count -ne 1) {
|
if ($validatedTargets.Count -ne 1) {
|
||||||
throw "Expected one block_slots semantic function, found $($validatedTargets.Count)"
|
throw "Expected one block_slots semantic function, found $($validatedTargets.Count)"
|
||||||
}
|
}
|
||||||
@@ -3814,7 +4308,6 @@ Write-Host ($lang).ModSpoti`n
|
|||||||
|
|
||||||
Remove-TempDirectory -Directory $tempDirectory
|
Remove-TempDirectory -Directory $tempDirectory
|
||||||
|
|
||||||
$xpui_spa_patch = Join-Path (Join-Path $spotifyDirectory 'Apps') 'xpui.spa'
|
|
||||||
$xpui_js_patch = Join-Path (Join-Path (Join-Path $spotifyDirectory 'Apps') 'xpui') 'xpui.js'
|
$xpui_js_patch = Join-Path (Join-Path (Join-Path $spotifyDirectory 'Apps') 'xpui') 'xpui.js'
|
||||||
$test_spa = Test-Path -Path $xpui_spa_patch
|
$test_spa = Test-Path -Path $xpui_spa_patch
|
||||||
$test_js = Test-Path -Path $xpui_js_patch
|
$test_js = Test-Path -Path $xpui_js_patch
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔════════════════════════════════════════╗
|
╔════════════════════════════════════════╗
|
||||||
║ Сардэчна запрашаем у SpotX для Windows ║
|
║ Сардэчна запрашаем у SpotX для Windows ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔═══════════════════════════════════╗
|
╔═══════════════════════════════════╗
|
||||||
║ উইন্ডোজ স্পটএক্স এ আপনাকে স্বাগতম ║
|
║ উইন্ডোজ স্পটএক্স এ আপনাকে স্বাগতম ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔═════════════════════════════╗
|
╔═════════════════════════════╗
|
||||||
║ Vítejte v SpotX pro Windows ║
|
║ Vítejte v SpotX pro Windows ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔══════════════════════════════════╗
|
╔══════════════════════════════════╗
|
||||||
║ Willkommen bei SpotX für Windows ║
|
║ Willkommen bei SpotX für Windows ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔════════════════════════════════════╗
|
╔════════════════════════════════════╗
|
||||||
║ Καλώς ήρθατε στο SpotX για Windows ║
|
║ Καλώς ήρθατε στο SpotX για Windows ║
|
||||||
|
|||||||
@@ -36,6 +36,9 @@
|
|||||||
Error = "Error"
|
Error = "Error"
|
||||||
FileLocBroken = "Location of Spotify files is broken, uninstall Spotify client and run the script again"
|
FileLocBroken = "Location of Spotify files is broken, uninstall Spotify client and run the script again"
|
||||||
Spicetify = "Spicetify detected, it must be installed after SpotX, open recommended actions in FAQ ? [Y/N]"
|
Spicetify = "Spicetify detected, it must be installed after SpotX, open recommended actions in FAQ ? [Y/N]"
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
NoRestore = "SpotX has already been installed, xpui.bak not found. `nPlease uninstall Spotify client and run Install.bat again"
|
NoRestore = "SpotX has already been installed, xpui.bak not found. `nPlease uninstall Spotify client and run Install.bat again"
|
||||||
InstallComplete = "installation completed"
|
InstallComplete = "installation completed"
|
||||||
HostInfo = "Unwanted URLs found in hosts file"
|
HostInfo = "Unwanted URLs found in hosts file"
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔═════════════════════════════════════════════╗
|
╔═════════════════════════════════════════════╗
|
||||||
║ Le damos la bienvenida a SpotX para Windows ║
|
║ Le damos la bienvenida a SpotX para Windows ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔══════════════════════════════╗
|
╔══════════════════════════════╗
|
||||||
║ Welcome to SpotX for Windows ║
|
║ Welcome to SpotX for Windows ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔═══════════════════════════════╗
|
╔═══════════════════════════════╗
|
||||||
║ Tervetuloa SpotX:n Windowsiin ║
|
║ Tervetuloa SpotX:n Windowsiin ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔═══════════════════════════════════════════════╗
|
╔═══════════════════════════════════════════════╗
|
||||||
║ Maligayang pagdating sa SpotX para sa Windows ║
|
║ Maligayang pagdating sa SpotX para sa Windows ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔══════════════════════════════════╗
|
╔══════════════════════════════════╗
|
||||||
║ Bienvenue sur SpotX pour Windows ║
|
║ Bienvenue sur SpotX pour Windows ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔════════════════════════════════════╗
|
╔════════════════════════════════════╗
|
||||||
║ SpotX for Windows में आपका स्वागत है ║
|
║ SpotX for Windows में आपका स्वागत है ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔════════════════════════════════════════════╗
|
╔════════════════════════════════════════════╗
|
||||||
║ Üdvözöllek a SpotX Windowsos telepítőjében ║
|
║ Üdvözöllek a SpotX Windowsos telepítőjében ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔═══════════════════════════════════════╗
|
╔═══════════════════════════════════════╗
|
||||||
║ Selamat datang di SpotX untuk Windows ║
|
║ Selamat datang di SpotX untuk Windows ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔═══════════════════════════════╗
|
╔═══════════════════════════════╗
|
||||||
║ Benvenuti a SpotX per Windows ║
|
║ Benvenuti a SpotX per Windows ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔═══════════════════════════╗
|
╔═══════════════════════════╗
|
||||||
║ SpotX Windows用にようこそ ║
|
║ SpotX Windows用にようこそ ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔══════════════════════════════╗
|
╔══════════════════════════════╗
|
||||||
║ Welcome to SpotX for Windows ║
|
║ Welcome to SpotX for Windows ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔══════════════════════════════╗
|
╔══════════════════════════════╗
|
||||||
║ Welcome to SpotX for Windows ║
|
║ Welcome to SpotX for Windows ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔══════════════════════════════╗
|
╔══════════════════════════════╗
|
||||||
║ Welcome to SpotX for Windows ║
|
║ Welcome to SpotX for Windows ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔════════════════════════════╗
|
╔════════════════════════════╗
|
||||||
║ Witamy w SpotX dla Windows ║
|
║ Witamy w SpotX dla Windows ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔═════════════════════════════════╗
|
╔═════════════════════════════════╗
|
||||||
║ Bem-vindo ao SpotX para Windows ║
|
║ Bem-vindo ao SpotX para Windows ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔════════════════════════════════════════╗
|
╔════════════════════════════════════════╗
|
||||||
║ Bine ați venit la SpotX pentru Windows ║
|
║ Bine ați venit la SpotX pentru Windows ║
|
||||||
|
|||||||
@@ -36,6 +36,9 @@
|
|||||||
Error = "Ошибка"
|
Error = "Ошибка"
|
||||||
FileLocBroken = "Расположение файлов Spotify нарушено, удалите клиент Spotify и снова запустите скрипт"
|
FileLocBroken = "Расположение файлов Spotify нарушено, удалите клиент Spotify и снова запустите скрипт"
|
||||||
Spicetify = "Обнаружен Spicetify, он должен быть установлен после SpotX, открыть рекомендуемые действия в FAQ ? [Y/N]"
|
Spicetify = "Обнаружен Spicetify, он должен быть установлен после SpotX, открыть рекомендуемые действия в FAQ ? [Y/N]"
|
||||||
|
DefenderPrompt = "Добавить исключения в Microsoft Defender для предотвращения ложных срабатываний? [Y/N]"
|
||||||
|
DefenderAdded = "Исключения добавлены в Microsoft Defender"
|
||||||
|
DefenderFailed = "Исключения не добавлены в Microsoft Defender"
|
||||||
NoRestore = "SpotX уже был установлен, но файл для восстановления xpui.bak не найден. `nУдалите клиент Spotify и снова запустите Install.bat"
|
NoRestore = "SpotX уже был установлен, но файл для восстановления xpui.bak не найден. `nУдалите клиент Spotify и снова запустите Install.bat"
|
||||||
InstallComplete = "Установка завершена"
|
InstallComplete = "Установка завершена"
|
||||||
HostInfo = "В файле hosts найдены нежелательные Url-адреса"
|
HostInfo = "В файле hosts найдены нежелательные Url-адреса"
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔══════════════════════════════╗
|
╔══════════════════════════════╗
|
||||||
║ Welcome to SpotX for Windows ║
|
║ Welcome to SpotX for Windows ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔══════════════════════════════╗
|
╔══════════════════════════════╗
|
||||||
║ Welcome to SpotX for Windows ║
|
║ Welcome to SpotX for Windows ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔══════════════════════════════╗
|
╔══════════════════════════════╗
|
||||||
║ Welcome to SpotX for Windows ║
|
║ Welcome to SpotX for Windows ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔══════════════════════════════════╗
|
╔══════════════════════════════════╗
|
||||||
║ Välkommen till SpotX för Windows ║
|
║ Välkommen till SpotX för Windows ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔════════════════════════════════╗
|
╔════════════════════════════════╗
|
||||||
║ Windows SpotXக்கு வரவேற்கிறோம் ║
|
║ Windows SpotXக்கு வரவேற்கிறோம் ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔══════════════════════════════════╗
|
╔══════════════════════════════════╗
|
||||||
║ Windows için SpotX'e Hoşgeldiniz ║
|
║ Windows için SpotX'e Hoşgeldiniz ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔══════════════════════════════════════╗
|
╔══════════════════════════════════════╗
|
||||||
║ Ласкаво просимо до SpotX для Windows ║
|
║ Ласкаво просимо до SpotX для Windows ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔═════════════════════════════════════╗
|
╔═════════════════════════════════════╗
|
||||||
║ Chào mừng đến với SpotX cho Windows ║
|
║ Chào mừng đến với SpotX cho Windows ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔═════════════════════════════╗
|
╔═════════════════════════════╗
|
||||||
║ 歡迎使用 SpotX Windows 版 ║
|
║ 歡迎使用 SpotX Windows 版 ║
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
[PSCustomObject]@{
|
[PSCustomObject]@{
|
||||||
|
DefenderPrompt = "Add exclusions to Microsoft Defender to prevent false positives? [Y/N]"
|
||||||
|
DefenderAdded = "Exclusions added to Microsoft Defender"
|
||||||
|
DefenderFailed = "Microsoft Defender exclusions were not added"
|
||||||
Welcome = "
|
Welcome = "
|
||||||
╔════════════════════════════════╗
|
╔════════════════════════════════╗
|
||||||
║ 欢迎使用 SpotX 的 Windows 版本 ║
|
║ 欢迎使用 SpotX 的 Windows 版本 ║
|
||||||
|
|||||||
Reference in New Issue
Block a user