Skip to content

Instantly share code, notes, and snippets.

@BenMcLean
Last active November 2, 2025 23:58
Show Gist options
  • Save BenMcLean/be7cf1c25cb71647d0547ef2bc2f5950 to your computer and use it in GitHub Desktop.
Save BenMcLean/be7cf1c25cb71647d0547ef2bc2f5950 to your computer and use it in GitHub Desktop.

Revisions

  1. BenMcLean renamed this gist Nov 2, 2025. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  2. BenMcLean revised this gist Oct 2, 2025. No changes.
  3. BenMcLean created this gist Oct 2, 2025.
    146 changes: 146 additions & 0 deletions convert_to_chd.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,146 @@
    #!/bin/bash

    # Check if directory argument provided
    if [ $# -eq 0 ]; then
    echo "Usage: $0 <directory>"
    echo "Example: $0 /path/to/psx"
    exit 1
    fi

    TARGET_DIR="$1"

    # Check if directory exists
    if [ ! -d "$TARGET_DIR" ]; then
    echo "Error: Directory does not exist: $TARGET_DIR"
    exit 1
    fi

    # Function to format seconds into human-readable time
    format_time() {
    local seconds=$1
    local hours=$((seconds / 3600))
    local minutes=$(((seconds % 3600) / 60))
    local secs=$((seconds % 60))

    if [ $hours -gt 0 ]; then
    printf "%dh %dm %ds" $hours $minutes $secs
    elif [ $minutes -gt 0 ]; then
    printf "%dm %ds" $minutes $secs
    else
    printf "%ds" $secs
    fi
    }

    echo "Converting CD images to CHD in: $TARGET_DIR"
    echo "================================================"
    echo ""

    # Count total CUE files
    echo "Scanning for CUE files..."
    total_files=$(find "$TARGET_DIR" -type f -iname "*.cue" | wc -l)

    if [ $total_files -eq 0 ]; then
    echo "No CUE files found in $TARGET_DIR"
    exit 0
    fi

    echo "Found $total_files CUE file(s) to convert"
    echo ""

    # Counter for statistics
    current=0
    total_converted=0
    total_failed=0

    # Start time
    start_time=$(date +%s)

    # Find all .cue files
    find "$TARGET_DIR" -type f -iname "*.cue" | sort | while read -r cuefile; do
    ((current++))

    dirname=$(dirname "$cuefile")
    basename=$(basename "$cuefile" .cue)
    chdfile="${dirname}/${basename}.chd"

    # Calculate progress percentage
    progress=$((current * 100 / total_files))

    # Calculate time estimates
    current_time=$(date +%s)
    elapsed=$((current_time - start_time))

    if [ $current -gt 1 ]; then
    avg_time_per_file=$((elapsed / (current - 1)))
    remaining_files=$((total_files - current + 1))
    eta=$((avg_time_per_file * remaining_files))
    eta_formatted=$(format_time $eta)
    elapsed_formatted=$(format_time $elapsed)
    else
    eta_formatted="calculating..."
    elapsed_formatted=$(format_time $elapsed)
    fi

    # Progress bar with standard ASCII characters
    bar_width=30
    filled=$((progress * bar_width / 100))
    empty=$((bar_width - filled))
    bar=$(printf "%${filled}s" | tr ' ' '#')
    bar+=$(printf "%${empty}s" | tr ' ' '-')

    echo "[$bar] $progress% ($current/$total_files)"
    echo "Elapsed: $elapsed_formatted | ETA: $eta_formatted"
    echo "Converting: $basename"
    echo "---"

    # Convert to CHD (show all output)
    if chdman createcd -i "$cuefile" -o "$chdfile"; then
    echo "---"
    echo "✓ Successfully converted: $basename"

    # Parse cue file for referenced files and collect them
    cue_dir=$(dirname "$cuefile")
    files_to_delete=()

    # Add the cue file itself
    files_to_delete+=("$cuefile")

    # Parse cue file for referenced files (BIN, WAV, ISO, etc.)
    while IFS= read -r line; do
    # Match FILE lines with quoted or unquoted filenames
    if [[ $line =~ FILE[[:space:]]+\"([^\"]+)\" ]] || [[ $line =~ FILE[[:space:]]+([^[:space:]]+)[[:space:]] ]]; then
    referenced_file="${BASH_REMATCH[1]}"
    # Handle relative paths
    full_path="${cue_dir}/${referenced_file}"
    if [[ -f "$full_path" ]]; then
    files_to_delete+=("$full_path")
    fi
    fi
    done < "$cuefile"

    # Delete all referenced files
    for file in "${files_to_delete[@]}"; do
    echo " Removing: $(basename "$file")"
    rm -f "$file"
    done

    ((total_converted++))
    echo ""
    else
    echo "---"
    echo "✗ Failed to convert: $basename"
    echo " Skipping deletion of source files"
    ((total_failed++))
    echo ""
    fi
    done

    # Final statistics
    end_time=$(date +%s)
    total_time=$((end_time - start_time))
    total_time_formatted=$(format_time $total_time)

    echo "================================================"
    echo "Conversion complete!"
    echo "Total time: $total_time_formatted"
    echo ""