param([switch]$SkipDownload) $ErrorActionPreference = 'Stop' $root = Split-Path -Parent $PSScriptRoot $version = '1.1.4' $asset = "FreeCAD_${version}-Windows-x86_64-py311.7z" $expected = '4828741fc91ee37fafcdb97a1abacb18b04ba451ac4372d9ff7a7349b36f4d6d' $archive = Join-Path $root "downloads\$asset" $runtime = Join-Path $root 'runtime' New-Item -ItemType Directory -Path (Join-Path $root 'downloads') -Force | Out-Null if (-not (Test-Path -LiteralPath $archive)) { if ($SkipDownload) { throw "Не найден дистрибутив CAD: $archive" } Invoke-WebRequest "https://github.com/FreeCAD/FreeCAD/releases/download/$version/$asset" -OutFile $archive } if ((Get-FileHash -LiteralPath $archive -Algorithm SHA256).Hash.ToLowerInvariant() -ne $expected) { throw 'Контрольная сумма архива FreeCAD не совпадает. Загруженный файл не использован.' } $sevenZip = 'C:\Program Files\7-Zip\7z.exe' if (-not (Test-Path -LiteralPath $sevenZip)) { throw 'Для подготовки CAD-библиотек установите 7-Zip.' } function Test-QIRuntimeFiles([string]$ArchivePath, [string]$RuntimePath) { $listing = & $sevenZip l -slt -ba $ArchivePath if ($LASTEXITCODE -ne 0) { throw 'Не удалось прочитать состав официального архива CAD.' } $runtimeAbsolute = [IO.Path]::GetFullPath($RuntimePath).TrimEnd([IO.Path]::DirectorySeparatorChar) $entryPath = ''; $entrySize = 0L; $isFolder = $false; $files = 0 foreach ($line in @($listing) + @('')) { if ($line.StartsWith('Path = ')) { $entryPath = $line.Substring(7) } elseif ($line.StartsWith('Size = ')) { $entrySize = [long]$line.Substring(7) } elseif ($line.StartsWith('Attributes = ')) { $isFolder = $line.Substring(13).Contains('D') } elseif ($line -eq '' -and $entryPath) { if (-not $isFolder -and -not $entryPath.EndsWith('.pyc', [StringComparison]::OrdinalIgnoreCase)) { $target = [IO.Path]::GetFullPath((Join-Path $runtimeAbsolute $entryPath)) if (-not $target.StartsWith($runtimeAbsolute + [IO.Path]::DirectorySeparatorChar, [StringComparison]::OrdinalIgnoreCase)) { throw 'Путь в архиве CAD выходит за пределы папки runtime.' } if (-not [IO.File]::Exists($target) -or ([IO.FileInfo]::new($target)).Length -ne $entrySize) { Write-Host "Восстановление CAD-библиотек: $entryPath" return $false } $files++ } $entryPath = ''; $entrySize = 0L; $isFolder = $false } } return $files -gt 0 } # An interrupted extraction can leave the launcher EXE while omitting DLLs or # workbenches. Compare every original non-bytecode file, not just the EXE. if (-not (Test-QIRuntimeFiles $archive $runtime)) { & $sevenZip x $archive "-o$runtime" -y -bsp0 -bso0 if ($LASTEXITCODE -ne 0) { throw 'Не удалось распаковать библиотеки CAD.' } if (-not (Test-QIRuntimeFiles $archive $runtime)) { throw 'Не удалось восстановить полный комплект CAD-библиотек.' } } & (Join-Path $PSScriptRoot 'sync.ps1') Write-Host "Библиотеки QInventor проверены и готовы к запуску: $root"