package main import ( "fmt" "io" "os" "os/exec" "path/filepath" "regexp" "strconv" "strings" "time" ) func findAkaiutil() (string, error) { if env := os.Getenv("AKAIUTIL"); env != "" { if fi, err := os.Stat(env); err == nil && !fi.IsDir() { return env, nil } } candidates := []string{ filepath.Join(filepath.Dir(os.Args[0]), "akaiutil"), filepath.Join(filepath.Dir(os.Args[0]), "third_party", "akaiutil", "akaiutil"), filepath.Join("third_party", "akaiutil", "akaiutil"), "akaiutil", } for _, p := range candidates { if fi, err := os.Stat(p); err == nil && !fi.IsDir() { return p, nil } } return "", fmt.Errorf("akaiutil not found — set AKAIUTIL env var or run: make akaiutil") } var runAkaiutil = func(isoPath, commands string) (string, error) { bin, err := findAkaiutil() if err != nil { return "", err } cmd := exec.Command(bin, "-r", isoPath) cmd.Stderr = nil stdin, err := cmd.StdinPipe() if err != nil { return "", fmt.Errorf("stdin pipe: %w", err) } stdout, err := cmd.StdoutPipe() if err != nil { return "", fmt.Errorf("stdout pipe: %w", err) } if err := cmd.Start(); err != nil { return "", fmt.Errorf("start akaiutil: %w", err) } stdin.Write([]byte(commands)) stdin.Close() output, _ := io.ReadAll(stdout) cmd.Wait() return string(output), nil } type volInfo struct { Part string Name string } func listPartitions(isoPath string) ([]string, error) { out, err := runAkaiutil(isoPath, "df\nq\n") if err != nil { return nil, err } re := regexp.MustCompile(`^\s+([A-Z])\s+`) var parts []string for _, line := range strings.Split(out, "\n") { if m := re.FindStringSubmatch(line); m != nil { parts = append(parts, m[1]) } } if len(parts) == 0 { return []string{"A"}, nil } return parts, nil } func listVolumes(isoPath string, partitions []string) ([]volInfo, error) { var cmds strings.Builder for _, p := range partitions { fmt.Fprintf(&cmds, "cd /disk0/%s\n", p) fmt.Fprintf(&cmds, "dir\n") } cmds.WriteString("q\n") out, err := runAkaiutil(isoPath, cmds.String()) if err != nil { return nil, err } rePrompt := regexp.MustCompile(`/disk0/([A-Z])\s*>`) reEntry := regexp.MustCompile(`^\s+\d+\s+(.+?)\s+-\s+`) var vols []volInfo currentPart := "" for _, line := range strings.Split(out, "\n") { if m := rePrompt.FindStringSubmatch(line); m != nil { currentPart = m[1] continue } if m := reEntry.FindStringSubmatch(line); m != nil { name := strings.TrimRight(m[1], " ") if currentPart != "" && name != "" { vols = append(vols, volInfo{Part: currentPart, Name: name}) } } } return vols, nil } func runExtraction(isoPath, baseDir string, vols []volInfo) (int, error) { var cmds strings.Builder for _, v := range vols { safe := sanitiseISOName(v.Name) if safe == "" { safe = "volume" } volDir := filepath.Join(baseDir, safe) os.MkdirAll(volDir, 0755) nav := strings.ReplaceAll(v.Name, " ", "_") fmt.Fprintf(&cmds, "lcd %s\n", volDir) fmt.Fprintf(&cmds, "cd /disk0/%s/%s\n", v.Part, nav) fmt.Fprintf(&cmds, "sample2wavall\n") } fmt.Fprintf(&cmds, "lcd /\nq\n") out, err := runAkaiutil(isoPath, cmds.String()) if err != nil { return 0, err } re := regexp.MustCompile(`exported\s+(\d+)\s+file`) total := 0 for _, line := range strings.Split(out, "\n") { if m := re.FindStringSubmatch(line); m != nil { n, _ := strconv.Atoi(m[1]) total += n } } return total, nil } func extractWAVs(isoPath, outDir string) (int, error) { parts, err := listPartitions(isoPath) if err != nil { return 0, fmt.Errorf("enumerate partitions: %w", err) } vols, err := listVolumes(isoPath, parts) if err != nil { return 0, fmt.Errorf("enumerate volumes: %w", err) } if len(vols) == 0 { return 0, nil } isoBase := sanitiseISOName(filepath.Base(isoPath)) baseDir := filepath.Join(outDir, isoBase) os.MkdirAll(baseDir, 0755) return runExtraction(isoPath, baseDir, vols) } func extractISO(isoPath, outDir string) (string, error) { isoOutDir := filepath.Join(outDir, sanitiseISOName(filepath.Base(isoPath))) if fi, err := os.Stat(isoOutDir); err == nil && fi.IsDir() { var wavCount int filepath.WalkDir(isoOutDir, func(p string, d os.DirEntry, err error) error { if err == nil && !d.IsDir() && strings.HasSuffix(strings.ToLower(d.Name()), ".wav") { wavCount++ } return nil }) if wavCount > 0 { return fmt.Sprintf("skipped (%d WAVs exist)", wavCount), nil } } start := time.Now() total, err := extractWAVs(isoPath, outDir) elapsed := time.Since(start).Round(time.Second) if err != nil { return "", err } return fmt.Sprintf("Total WAV files: %d (%s)", total, elapsed), nil }