Skip to content

Instantly share code, notes, and snippets.

@revoice1
Last active October 1, 2023 17:33
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save revoice1/73e2972f00d1fe3ee3f24216437013ac to your computer and use it in GitHub Desktop.
Save revoice1/73e2972f00d1fe3ee3f24216437013ac to your computer and use it in GitHub Desktop.
Convert_z7_cue_to_CHD.ps1
#Requires -Version 5.1
[cmdletbinding()]
param(
[string]$SourcePath = "", # Path to 7z zipped disc images to be converted
[string]$DestinationPath = "", # Path where you would like the CHD's to go. One folder per game.
[string]$WorkingPath = "", # Path where files will be extracting temporarily for CHD creation, use fast disk if possible. Will be created in the running path if undefined
[string]$CHDMANPath = "", # Path to the CHDMAN executable, will be downloaded if this isn't defined or isn't correct
[switch]$CleanWorkingPath = $false, # Switch to automatically clean (delete contents) the working path dir if it is non-empty
[switch]$DisableMultithreading = $false, # Option to disable multithreading logic, for low thread systems or if you don't want CHD conversion to take most of your CPU %
[switch]$WritePlaylistFiles = $false # Write m3u files in the destination dir, for all CHDs present (not just new ones)
)
<#
This code can be run in a script editor by manually editing the parameters above or as a script, defining the parameters as switches
Example 1 - Covert a folder of 7z compressed bin/cue or iso/cue cd images to CHD and copy to the destination folder
PS> .\Convert_z7_cue_to_CHD.ps1 -SourcePath "C:\Path\..\Sega Mega CD & Sega CD" -DestinationPath "C:\Other Path\..\Sega CD"
Example 2 - Include verbose output and generate m3u files for multi-disc games
PS> .\Convert_z7_cue_to_CHD.ps1 -SourcePath "..\Sega Mega CD & Sega CD" -DestinationPath "..\Sega CD" -Verbose -WritePlaylistFiles
Example 3 - Don't convert anything, just create playlist files in a dir with CHD's already present
PS> .\Convert_z7_cue_to_CHD.ps1 -SourcePath "" -DestinationPath "..\Sega CD" -WritePlaylistFiles
#>
$Stopwatch = [system.diagnostics.stopwatch]::StartNew() # Start a stopwatch for runtime calc
# If CHDMAN path isn't defined or the path doesn't exist, download CHDMAN
if ((!$CHDMANPath) -or !(Test-Path $CHDMANPath)) {
if (Test-Path .\CHDMAN\chdman.exe) {
$CHDMANPath = ".\CHDMAN\chdman.exe"
}
else {
Write-Verbose "chdman.exe not found, downloading from archive.org to $((Get-Location).Path)\CHDMAN\chdman.exe"
Invoke-WebRequest https://archive.org/download/chdman/CHDMAN.zip -OutFile .\CHDMAN.zip
Expand-Archive .\CHDMAN.zip .\
Remove-Item .\CHDMAN.zip
$CHDMANPath = ".\CHDMAN\chdman.exe"
}
}
# create working path directory if it doesn't exist or is undefined
if ((!$WorkingPath) -or !(Test-Path $WorkingPath)) {
if (Test-Path .\_temp_extract_dir) {
$WorkingPath = ".\_temp_extract_dir"
Write-Verbose "Working dir set: $WorkingPath"
}
else {
Write-Warning "Working path not defined or non-existent, creating directory .\_temp_extract_dir "
$WorkingDirCreate = New-Item -ItemType Directory -Path .\_temp_extract_dir
$WorkingPath = ".\_temp_extract_dir"
Write-Verbose "Working dir created: $($WorkingDirCreate.FullName)"
}
}
# If the 7Zip4Powershell module isn't installed, attempt to install it
if (!(Get-Module 7Zip4Powershell -ListAvailable)) {
Write-Verbose "Attempting to install 7zip powershell module"
try {
Install-Module 7Zip4Powershell -Confirm:$false -Scope CurrentUser -Force
}
catch {
Write-Error "Failed to detect or install the 7Zip4Powershell module. Please manually install the module or try running the script with administrator rights. $_"
}
}
# If the working path doesn't exist attempt to create it
if (!(Test-Path $WorkingPath)) { New-Item -ItemType Directory $WorkingPath | Out-Null }
# If the working path is not empty, throw a warning
# Or delete the contents if the CleanWorkingPath switch is used
elseif (Test-Path -Path $WorkingPath\*) {
if ($CleanWorkingPath) {
Remove-Item -Recurse $WorkingPath\*
}
else {
return Write-Warning "Working path not empty, this can cause unintended issues. Please use an empty folder.`n`t Or use the `"-CleanWorkingPath`" switch to have the contents of `"$($WorkingPath)`" automatically deleted"
}
}
if ($DisableMultithreading) {
$NumProcessors = 0 # If multithreading is disabled, set to 0 to use the default CHDMAN logic
}
else {
# Get the num of logical processors to use for CHDMAN
# Subtract one from total just to allow for some breathing room
$NumProcessors = (Get-CimInstance -ClassName 'Win32_Processor' -Verbose:$false).NumberOfLogicalProcessors - 1
Write-Verbose "$NumProcessors processors will be used for CHDMAN"
}
# Locate all the 7z files in the source path
$7zDiscs = Get-ChildItem $SourcePath -Recurse -Include *.7z
# Counts for reporting
$CHDCreateCount = 0
$CHDSkipCount = 0
# For each 7z file found in the source path...
foreach ($7zDisc in $7zDiscs) {
$FolderName = $7zDisc.name -replace "\.7z$", "" -replace "(\s+)?.?disc.\d.?", ""
$DestinationSubfolder = "$DestinationPath\$FolderName"
if (!(Test-Path $DestinationSubfolder)) {
New-Item -ItemType Directory -Path $DestinationSubfolder | Out-Null
}
# Generate a list of cue files in the 7z file
$ZippedCues = Get-7Zip $7zDisc.FullName | Where-Object { $_.FileName -match "\.cue$" }
$Skip7z = $true # Default to skipping unless we find an unconverted cue"
# For each cue in the 7z, check if a CHD already exists in the destination path
# If there are any cue files that don't already had CHD files, proceed to extraction
# If all cue files in the 7z already have CHD files present, skip the 7z
foreach ($ZippedCue in $ZippedCues) {
if ($Skip7z) {
$ZippedCueName = Split-Path $ZippedCue.FileName -Leaf
$DestinationCHD = "$($DestinationSubfolder)\$($ZippedCueName -replace "\.cue$",".chd")"
if (!(Test-Path -LiteralPath $DestinationCHD)) {
$Skip7z = $false # Don't skip the 7z as we need to create some CHD's
}
else {
Write-Verbose "Destination CHD already exists for $($ZippedCue.FileName)"
}
}
}
if ($Skip7z) {
$CHDSkipCount += $ZippedCues.count
continue
}
# Extract the 7z file to the working path if an existing CHD isn't found
Expand-7Zip -ArchiveFileName $7zDisc.FullName -TargetPath $WorkingPath
# Locate cue file(s) that were extracted from the 7z file
$CueFiles = Get-ChildItem $WorkingPath -Recurse -Include "*.cue"
# For each cue file found...
foreach ($CueFile in $CueFiles) {
# Check if a CHD file exists in the destination path with the same name as the cue file
# This will likely catch multi-disc games that have already been converted
$DestinationCHD = "$($DestinationSubfolder)\$($CueFile.name -replace "\.cue$",".chd")"
if (Test-Path -LiteralPath $DestinationCHD) {
Write-Verbose "Destination CHD already exists for $($CueFile.name)"
$CHDSkipCount++
continue
}
# Converto the cue file to a chd file
&$CHDMANPath createcd -i $CueFile.fullname -o $DestinationCHD -np $NumProcessors
$CHDCreateCount++
}
# Clean up the extracted files in the working path
Remove-Item -Recurse $WorkingPath\*
}
# Generate report object
$Report = [PSCustomObject]@{
"CHD Files Created" = $CHDCreateCount
"CHD Files Skipped" = $CHDSkipCount
}
# If CreatePlayListFiles was set
if ($WritePlaylistFiles) {
$PlaylistWriteCount = 0 # Count for reporting
# Get a list of multi-disc games
$Files = Get-ChildItem -Recurse $DestinationPath -Include "*.chd" | Where-Object { $_.name -match "disc.\d" }
# Group them and filter out false positives
$Groups = $Files | Group-Object -Property { $_.name -replace "(\s+)?.?disc.\d.?", "" } | Where-Object { $_.count -gt 1 }
Write-Verbose "$($Groups.count) multi-disc games found"
# For each multi-disc game found...
foreach ($Group in $Groups) {
$Extension = $Group.group[0].Extension # Isolate the extension to replace
$m3uName = $group.name -replace "$Extension$", ".m3u" # Replace the extension with m3u for the target filename
$m3uDir = Split-Path $Group.group[0].FullName -Parent
$m3uPath = "$m3uDir\$m3uName" # The absolute path of the m3u file
Write-Verbose "Writing m3u for $($Group.count) disc game: $m3uName"
$Group.group.name | Out-File -LiteralPath $m3uPath # Write the m3u file
$PlaylistWriteCount++
}
# Add count to report
$Report | Add-Member "Playlist Files Written" $PlaylistWriteCount
}
# Add runtime to report
$Report | Add-Member "Elapsed Runtime" ([string]$Stopwatch.Elapsed)
# Write the report output
Write-Output ""
return "--- Script Result ---$($Report | Format-List | Out-String)"
@dazeb
Copy link

dazeb commented Aug 28, 2023

This is actually running now on 250 PSX roms and seems to be doing a perfect job, thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment