mirror of
https://github.com/CosmicScale/PSBBN-Definitive-English-Patch.git
synced 2025-09-12 20:09:33 +02:00

* Add Windows-based launcher script * Clone the test branch when running the windows-launcher * Add warning related to unix username rules * Add logic to check and install the WSL feature * Add version number of the script to the title * Use a label for the wsl distro to distinguish it from a potential existing install * fix installed distro not being detected properly under powershell 5.1 * force wsl to output utf8 and improve wsl mount/unmount output messages * move mounting the disk after apt update and clone to help with the disk not appearing in lsblk * fix wsl encoding for PS5.1 and ensure only one restart is needed upon installing all 3 features * improve disk size format * add parameter -log to copy the output of the console to ~\psbbn-windows-launcher.log * relaunch the script as administrator automatically if needed * implement a folderpicker for the main content folder and pass it to linux scripts * improve disk picker to detect OPL volume and allow to refresh * add comments and linebreak the longer code lines * checkout branch even if the repo is already cloned to another one * add fallback logic in case the remote branch was deleted * change target branch to main, and add some comments * fix variable scope of $diskList * change string.Split call to work with powershell 5.1 * move and rename the windows scripts * resize the console to 100x45 and save the selected path to path.cfg * offline/online disk before mounting on wsl to avoid locking issues * silence error when plugging a disk with no partition + add path.cfg to .gitignore * use utf8-bom on the powershell script to enable proper display of utf8 characters * update title acsii art * offer to keep using the previously used games' path * fix disk picking issue and add serial to the disk list * open the explorer even when the same path is reused * improve UI of the restart prompt
493 lines
17 KiB
PowerShell
493 lines
17 KiB
PowerShell
#Requires -Version 5.1
|
|
|
|
# User must open its execution policy by running
|
|
# `Set-ExecutionPolicy -ExecutionPolicy Unrestricted`
|
|
# and choose "Yes to All"
|
|
|
|
param(
|
|
[Alias("logs", "log")]
|
|
[switch]$LogsEnabled = $false
|
|
)
|
|
|
|
# the version of this script itself, useful to know if a user is running the latest version
|
|
$version = "1.0.8"
|
|
|
|
# the label of the WSL machine. Still based on Debian, but this label makes sure we get the
|
|
# machine created by this script and not some pre-existing Debian the user had.
|
|
$wslLabel = "PSBBN"
|
|
|
|
# the name of the exfat volume created by the psbbn installer with apajail
|
|
# this is used to attempt to detect which disks have an install of PSBBN
|
|
$oplVolumeName = "OPL"
|
|
|
|
# a list of subfolders to be created in the main folder if missing
|
|
$defaultFolders = @('DVD', 'CD', 'POPS', 'APPS', 'music', 'movie', 'photo')
|
|
|
|
# the specific git branch to be checked out
|
|
$gitBranch = "main"
|
|
|
|
# the console size that is set at the start of the script
|
|
$consoleWidth = 100
|
|
$consoleHeight = 45
|
|
|
|
# the filename of the file holding the last path used
|
|
$pathFilename = "path.cfg"
|
|
|
|
# --- DO NOT MODIFY THE VARIABLES BELOW ---
|
|
|
|
# in case $gitBranch no longer exists on the remote, use this as fallback
|
|
# DO NOT change this unless the repo has deleted the main branch or is using another branch as release
|
|
$fallbackGitBranch = "main"
|
|
|
|
# this make wsl commands output utf8 instead of utf16_LE
|
|
$env:WSL_UTF8 = 1
|
|
|
|
# flag potentially raised when enabling features, signaling a reboot is necessary to finish the install
|
|
$global:IsRestartRequired = $false
|
|
|
|
# stores Get-Disk's result to avoid multiple calls
|
|
$global:diskList = $null
|
|
|
|
# stores the disk number (obtained with Get-Disk) that was selected via diskPicker
|
|
$global:selectedDisk = -1
|
|
|
|
function main {
|
|
# set the console size so it matches the other scripts
|
|
$host.UI.RawUI.WindowSize = New-Object `
|
|
-TypeName System.Management.Automation.Host.Size `
|
|
-ArgumentList ($consoleWidth, $consoleHeight)
|
|
|
|
$PSDefaultParameterValues['*:Encoding'] = 'utf8'
|
|
|
|
clear
|
|
printTitle
|
|
Write-Host "Prepare Windows to run the PSBBN scripts.`n"
|
|
|
|
# check if mandatory windows features are enabled
|
|
Write-Host "Checking if the mandatory features are enabled..." -NoNewline
|
|
$wslFeature = Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
|
|
$hyperVFeature = Get-WindowsOptionalFeature -Online -FeatureName HypervisorPlatform
|
|
$virtualMachineFeature = Get-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform
|
|
printOK
|
|
|
|
# enable WSL as needed
|
|
enableFeature($wslFeature)
|
|
|
|
# enable Hyper V as needed
|
|
enableFeature($hyperVFeature)
|
|
|
|
# enable Virtual Maching Platform as needed
|
|
enableFeature($virtualMachineFeature)
|
|
|
|
# detect if virtualization is enabled in BIOS
|
|
detectVirtualization
|
|
|
|
if ($isRestartRequired) {
|
|
Write-Host "
|
|
You need to restart your computer in order for some newly installed features to work properly.
|
|
Once you have restarted, just run this script again.
|
|
" -ForegroundColor Yellow
|
|
do {
|
|
clearLines(1)
|
|
$keyPressed = Read-Host "⚠️ You are about to restart your PC. Save your work, type `"restart`" and press ENTER"
|
|
$keyPressed = $keyPressed.ToLower()
|
|
} while ($keyPressed -ne 'restart')
|
|
|
|
if ($keyPressed -eq 'restart') {
|
|
Restart-Computer
|
|
}
|
|
Exit
|
|
}
|
|
|
|
# check if wsl exists
|
|
if (-Not (Get-Command wsl -errorAction SilentlyContinue)) {
|
|
Write-Host "WSL is not available on this computer." -ForegroundColor Yellow
|
|
Exit
|
|
}
|
|
|
|
# fetch the latest wsl update
|
|
Write-Host "Check the latest WSL updates...`t`t`t" -NoNewline
|
|
$wslUpdate = wsl --update --web-download
|
|
printOK
|
|
|
|
# check if a wsl distro is installed already
|
|
$isWslInstalled = wsl --list | Select-String -SimpleMatch -Quiet $wslLabel
|
|
if (-Not ($isWslInstalled)) {
|
|
Write-Host "`nThe WSL distro will prompt you to set a username and a password." -ForegroundColor Yellow
|
|
Write-Host "⚠️ The username must start with a lowercase letter, and only contain letters and numbers." -ForegroundColor Red
|
|
Write-Host "⚠️ A password must be set." -ForegroundColor Red
|
|
Write-Host "After this is done, you can type 'exit' and press enter to return to this script.`n" -ForegroundColor Yellow
|
|
Write-Host "------- Linux magic starts ---------"
|
|
|
|
wsl --install --distribution Debian --name $wslLabel
|
|
} else {
|
|
Write-Host "The WSL distro is already present, skipping.`t" -NoNewline
|
|
printOK
|
|
}
|
|
|
|
# install git if it is missing
|
|
wsl -d $wslLabel -- type git `&`> /dev/null `|`| `(sudo apt update `&`& sudo apt -y install git`)
|
|
|
|
# check if the git branch exists on the remote, and if not, use the fallback
|
|
if (-Not (wsl -d $wslLabel --cd "~/PSBBN-Definitive-English-Patch" -- git branch -r --list origin/$gitBranch)) {
|
|
$gitBranch = $fallbackGitBranch
|
|
}
|
|
|
|
# clone the PSBBN repo into ~, or pull if it's already there
|
|
Write-Host
|
|
wsl -d $wslLabel --cd "~" -- [ -d PSBBN-Definitive-English-Patch/.git ] `
|
|
`&`& `( `
|
|
cd PSBBN-Definitive-English-Patch/ `
|
|
`&`& git fetch origin $gitBranch `
|
|
`&`& git checkout $gitBranch `
|
|
`&`& git pull --ff-only `
|
|
`) `
|
|
`|`| git clone -b $gitBranch https://github.com/CosmicScale/PSBBN-Definitive-English-Patch.git
|
|
|
|
if (-Not ($isWslInstalled)) {
|
|
Write-Host "------- Linux magic finishes ---------`n"
|
|
}
|
|
|
|
# prompt user to choose a disk
|
|
diskPicker
|
|
|
|
# mount the disk
|
|
$deviceName = "\\.\PHYSICALDRIVE$global:selectedDisk"
|
|
Write-Host "`nMounting $deviceName on wsl...`t`t" -NoNewline
|
|
Set-Disk $global:selectedDisk -isOffline $true
|
|
$mountOut = wsl -d $wslLabel --mount $deviceName --bare
|
|
handleMountOutput($mountOut)
|
|
|
|
# give the user the opportunity to put games/homebrew in the PSBBN folder
|
|
$path = getTargetFolder
|
|
clear
|
|
|
|
# run PSBBN regular steps
|
|
wsl -d $wslLabel --cd "~/PSBBN-Definitive-English-Patch" -- `
|
|
./PSBBN-Definitive-Patch.sh -wsl $global:diskList[$selectedDisk].SerialNumber $path
|
|
|
|
# clear the terminal to get rid of the wsl-run scripts
|
|
clear
|
|
printTitle
|
|
|
|
# unmount the disk before exiting
|
|
Write-Host "Unmounting $deviceName...`t`t" -NoNewline
|
|
$unmountOut = wsl -d $wslLabel --unmount $deviceName
|
|
Set-Disk $global:selectedDisk -isOffline $false
|
|
handleMountOutput($unmountOut)
|
|
|
|
# ensures wsl doesnt run out of resources if this script is ran repeatedly
|
|
wsl --shutdown
|
|
|
|
Write-Host "`nHave fun exploring PSBBN!`n" -ForegroundColor Green
|
|
}
|
|
|
|
# print colored `[ OK ]`
|
|
function printOK {
|
|
Write-Host " `t`t[ " -NoNewline
|
|
Write-Host "OK" -NoNewline -ForegroundColor Green
|
|
Write-Host " ]"
|
|
}
|
|
|
|
# print colored `[ NG ]`
|
|
function printNG {
|
|
Write-Host "`t`t[ " -NoNewline
|
|
Write-Host "NG" -NoNewline -ForegroundColor Red
|
|
Write-Host " ]"
|
|
}
|
|
|
|
# print colored `[ Restart required ]`
|
|
function printRestartRequired {
|
|
Write-Host " `t`t[ " -NoNewline
|
|
Write-Host "Restart required" -NoNewline -ForegroundColor Yellow
|
|
Write-Host " ]"
|
|
}
|
|
|
|
# takes a feature as parameter and enable it if needed
|
|
function enableFeature ($feature) {
|
|
if (($null -ne $feature.FeatureName) -and ($feature.State -ne "Enabled")) {
|
|
Write-Host " └ Enabling" $feature.DisplayName "..." -NoNewline;
|
|
$enabled = Enable-WindowsOptionalFeature -Online -FeatureName $feature.FeatureName -All -NoRestart -WarningAction:SilentlyContinue
|
|
if ($enabled.RestartNeeded) {
|
|
$global:IsRestartRequired = $true
|
|
printRestartRequired
|
|
} else {
|
|
printOK
|
|
}
|
|
} elseif ($feature.State -eq "Enabled"){
|
|
Write-Host " └" $feature.DisplayName 'already enabled.' -NoNewline
|
|
printOK
|
|
}
|
|
}
|
|
|
|
# prints the big psbbn logo and the version number
|
|
function printTitle {
|
|
Write-Host "
|
|
______ _________________ _ _ ______ __ _ _ _ _ ______ _ _
|
|
| ___ \/ ___| ___ \ ___ \ \ | | | _ \ / _(_) (_) | (_) | ___ \ | | | |
|
|
| |_/ /\ ``--.| |_/ / |_/ / \| | | | | |___| |_ _ _ __ _| |_ ___ _____ | |_/ /_ _| |_ ___| |__
|
|
| __/ ``--. \ ___ \ ___ \ . `` | | | | / _ \ _| | '_ \| | __| \ \ / / _ \ | __/ _`` | __/ __| '_ \
|
|
| | /\__/ / |_/ / |_/ / |\ | | |/ / __/ | | | | | | | |_| |\ V / __/ | | | (_| | || (__| | | |
|
|
\_| \____/\____/\____/\_| \_/ |___/ \___|_| |_|_| |_|_|\__|_| \_/ \___| \_| \__,_|\__\___|_| |_|
|
|
|
|
Created by CosmicScale
|
|
---
|
|
Launcher for Windows v$version
|
|
Written by Yornn
|
|
|
|
"
|
|
}
|
|
|
|
# display the available disks and prompt user for choice
|
|
function diskPicker {
|
|
# store the current cursor position to help clear the console upon refreshing the list of disks
|
|
$lineStart = $Host.UI.RawUI.CursorPosition.Y
|
|
|
|
# list available disks and pick the one to be mounted
|
|
Write-Host "`nList of available disks:"
|
|
$global:diskList = Get-Disk | Sort -Property Number
|
|
$disksWithOplVolume = detectOplVolume($global:diskList)
|
|
$global:diskList | Format-Table -Property `
|
|
Number, `
|
|
@{Label="Name";Expression={$_.FriendlyName}}, `
|
|
@{Label="Size";Expression={("{0:N2}" -f ($_.Size / 1GB)).ToString() + " GB"}}, `
|
|
SerialNumber, `
|
|
@{Label="";Expression={if ($disksWithOplVolume -contains $_.Number) { "<- PSBBN detected" } else { " " }}}
|
|
|
|
# generate a list of disk number to validate user input
|
|
$availableNumbers = $global:diskList | Foreach-Object {$_.Number}
|
|
|
|
$selectedDisk = handleDiskSelection($availableNumbers)
|
|
|
|
if ($selectedDisk -eq "r") {
|
|
clearLines($Host.UI.RawUI.CursorPosition.Y - $lineStart)
|
|
diskPicker
|
|
} else {
|
|
$global:selectedDisk = $selectedDisk
|
|
}
|
|
}
|
|
|
|
# handles the input logic of the diskpicker
|
|
# this function calls itself recursively as long an invalid input is provided
|
|
function handleDiskSelection ($availableNumbers) {
|
|
Write-Host "Select a disk to use by typing its number or press `"r`" to refresh the list: " -NoNewline
|
|
$keyPressed = $Host.UI.RawUI.ReadKey("IncludeKeyDown")
|
|
try {
|
|
$selectedDisk = [int][string]$keyPressed.Character
|
|
} catch {
|
|
$selectedDisk = -1
|
|
}
|
|
|
|
if ($keyPressed.Character -eq "r") {
|
|
Write-Host $(" " * 45) -NoNewline
|
|
return "r"
|
|
}
|
|
|
|
if ((-Not ($availableNumbers -contains $selectedDisk)) -or ($keyPressed.VirtualKeyCode -eq 13)) {
|
|
Write-Host " - Invalid input, try again.`r" -NoNewline -ForegroundColor Red
|
|
$selectedDisk = handleDiskSelection($maxDiskNumber)
|
|
} else {
|
|
# erase the "invalid output" message
|
|
Write-Host $(" " * 45) -NoNewline
|
|
}
|
|
return $selectedDisk
|
|
}
|
|
|
|
# detects if bios-level virtualization is enabled or not, and display messages
|
|
function detectVirtualization {
|
|
Write-Host "Checking if virtualization is enabled in BIOS..." -NoNewline
|
|
|
|
$propertyName = "HyperVRequirementVirtualizationFirmwareEnabled"
|
|
$property = Get-ComputerInfo -property $propertyName
|
|
|
|
# the property is null if the feature is enabled, false otherwise
|
|
if ($property.$propertyName -eq $false) {
|
|
printNG
|
|
Write-Host "`n
|
|
Virtualization is not enabled. It is mandatory to run PSBBN scripts.
|
|
If you have an AMD CPU, enable SVM in your BIOS.
|
|
If you have an Intel CPU, enable VT-x in your BIOS.
|
|
Check the manual of your motherboard if you have troubles finding this setting.
|
|
"
|
|
Exit
|
|
} else {
|
|
printOK
|
|
}
|
|
}
|
|
|
|
# handles the error code returned by `wsl --mount` or wsl --unmount` and display human friendly messages
|
|
function handleMountOutput ($mountOut) {
|
|
if ($mountOut -like "*Wsl/Service/AttachDisk/MountDisk/WSL_E_DISK_ALREADY_ATTACHED*") {
|
|
# in the case where the disk was already attached, we just carry on and treat it as a happy path
|
|
printOK
|
|
Write-Host "The disk was already mounted." -ForegroundColor Green
|
|
return
|
|
} elseif ($mountOut -like "*Wsl/Service/DetachDisk/ERROR_FILE_NOT_FOUND*") {
|
|
# in the case where the disk was already detached, we just carry on and treat it as a happy path
|
|
printOK
|
|
Write-Host "The disk was already unmounted." -ForegroundColor Green
|
|
return
|
|
} elseif ($mountOut -like "*Error code*") {
|
|
printNG
|
|
Write-Host $mountOut -ForegroundColor Red
|
|
Exit
|
|
}
|
|
printOK
|
|
}
|
|
|
|
# checks if the script is running as admin, and if not launches itself in powershell instance running as admin
|
|
function restartAsAdminIfNeeded {
|
|
if (([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
|
|
# already running as admin, so we just carry on
|
|
return
|
|
}
|
|
|
|
# if powershell 7 is installed, launch that one instead of 5.1
|
|
$shell = "powershell"
|
|
if (Get-Command pwsh -errorAction SilentlyContinue) {
|
|
$shell = "pwsh"
|
|
}
|
|
|
|
# relaunch the script as admin, this should trigger a UAC prompt
|
|
Start-Process $shell -Verb RunAs -ArgumentList "-NoLogo -NoExit -ExecutionPolicy Bypass -File `"$PSCommandPath`""
|
|
|
|
# close the initial non-admin shell to avoid confusion
|
|
exit
|
|
}
|
|
|
|
# handles the folder picker and return a wsl compatible path as a string
|
|
function getTargetFolder {
|
|
# if a previously used path exists, use that in the folder picker, otherwise use "Desktop"
|
|
$desktopDirectory = [Environment]::GetFolderPath('Desktop')
|
|
$initialDirectory = $desktopDirectory
|
|
$isPresetPath = $false
|
|
if (Test-Path ".\$pathFilename" -PathType Leaf) {
|
|
$initialDirectory = Get-Content -Path ".\$pathFilename"
|
|
$isPresetPath = $true
|
|
|
|
# check that the path in path.cfg still exists, if not default to "Desktop"
|
|
if (-Not (Test-Path $initialDirectory -PathType Container)) {
|
|
$initialDirectory = $desktopDirectory
|
|
$isPresetPath = $false
|
|
}
|
|
}
|
|
|
|
# if the path is already set, ask if the user wants to re-use it
|
|
if ($isPresetPath) {
|
|
Write-Host "`nThe following path was previously used: " -NoNewLine
|
|
Write-Host "$initialDirectory`n" -ForegroundColor Green
|
|
do {
|
|
clearLines(1)
|
|
$keyPressed = Read-Host "Would you like to keep using this path? (y/n)"
|
|
$keyPressed = $keyPressed.ToLower()
|
|
} while ($keyPressed -ne 'y' -and $keyPressed -ne 'n')
|
|
|
|
if ($keyPressed -eq 'y') {
|
|
explorer $initialDirectory
|
|
return convertPathToWsl($initialDirectory)
|
|
}
|
|
}
|
|
|
|
Write-Host "`nNext you will be asked to pick a folder where to put all your games, movies, photos, and music." -ForegroundColor Yellow
|
|
|
|
pause
|
|
|
|
# prepare and then open the folder picker
|
|
Add-Type -AssemblyName System.Windows.Forms
|
|
$folderselection = New-Object System.Windows.Forms.OpenFileDialog -Property @{
|
|
InitialDirectory = $initialDirectory
|
|
CheckFileExists = 0
|
|
ValidateNames = 0
|
|
FileName = "Choose Folder"
|
|
}
|
|
$result = $folderselection.ShowDialog()
|
|
|
|
if($result -ne "OK") {
|
|
Write-Host "No folder was picked, you will have to do it manually later." -ForegroundColor Yellow
|
|
return ""
|
|
}
|
|
|
|
$pickedPath = Split-Path -Parent $folderselection.FileName
|
|
|
|
# create default folders if missing
|
|
$defaultFolders.ForEach({
|
|
if (-Not (Test-Path -Path "$pickedPath\$PSItem")) {
|
|
New-Item -Path $pickedPath -Name $PSItem -ItemType Directory | Out-Null
|
|
}
|
|
})
|
|
|
|
Write-Host "`nThe following path was chosen: $pickedPath" -ForegroundColor Green
|
|
Write-Host "
|
|
Before you continue, you can fill this folder with your games and other media:
|
|
- put PS2 games in /DVD or /CD (.iso or .zso files)
|
|
- put PS1 games in /POPS (must be .vcd files)
|
|
- put homebrew in /APPS (.elf or SAS-compliant .psu files)
|
|
- put music in /music (.mp3, .m4a, .flac, or .ogg files)
|
|
|
|
You can refer to the PSBBN Readme to know more.
|
|
https://github.com/CosmicScale/PSBBN-Definitive-English-Patch
|
|
" -ForegroundColor Yellow
|
|
|
|
# store the selected path to re-use next time the script is ran
|
|
New-Item -Path "." -Name $pathFilename -ItemType "file" -Value $pickedPath -Force | Out-Null
|
|
|
|
explorer $pickedPath
|
|
|
|
pause
|
|
|
|
return convertPathToWsl($pickedPath)
|
|
}
|
|
|
|
# clears $count lines with spaces and move the cursor back
|
|
function clearLines ($count) {
|
|
$currentLine = $Host.UI.RawUI.CursorPosition.Y
|
|
$consoleWidth = $Host.UI.RawUI.BufferSize.Width
|
|
|
|
$i = 0
|
|
for ($i; $i -le $count; $i++) {
|
|
[Console]::SetCursorPosition(0,($currentLine - $i))
|
|
[Console]::Write("{0,-$consoleWidth}" -f " ")
|
|
}
|
|
[Console]::SetCursorPosition(0,($CurrentLine - $count))
|
|
}
|
|
|
|
# walks through each disk -> partition -> volume to find which disks have
|
|
# volumes named $oplVolumeName, and return an array of disk numbers
|
|
function detectOplVolume ($diskList) {
|
|
$disksWithOplVolume = @()
|
|
$diskList | ForEach-Object {
|
|
$diskNumber = $_.Number
|
|
Get-Partition -disknumber $diskNumber -errorAction SilentlyContinue | ForEach-Object {
|
|
Get-Volume -partition $_ | ForEach-Object {
|
|
$_.FileSystemLabel.GetType()
|
|
if ($_.FileSystemLabel -eq $oplVolumeName) {
|
|
$disksWithOplVolume += $diskNumber
|
|
}
|
|
}
|
|
} | Out-Null
|
|
}
|
|
return $disksWithOplVolume
|
|
}
|
|
|
|
function convertPathToWsl ($windowsPath) {
|
|
$driveLetter = $windowsPath.Split(":")[0].ToLower()
|
|
$path = $windowsPath.Split(":")[1].Replace("\", "/")
|
|
return "/mnt/$driveLetter$path"
|
|
}
|
|
|
|
restartAsAdminIfNeeded
|
|
|
|
# necessary to ensure the CWD is where the script is located, in case the script was restarted as admin
|
|
cd $PSScriptRoot
|
|
|
|
if ($LogsEnabled) {
|
|
try { $null = Start-Transcript -Path ".\psbbn-windows-launcher.log" } catch {}
|
|
try {
|
|
main
|
|
} finally {
|
|
try { $null = Stop-Transcript } catch {}
|
|
}
|
|
} else {
|
|
main
|
|
}
|