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) } if _, err := stdin.Write([]byte(commands)); err != nil { return "", fmt.Errorf("write stdin: %w", err) } if err := stdin.Close(); err != nil { return "", fmt.Errorf("close stdin: %w", err) } output, _ := io.ReadAll(stdout) if err := cmd.Wait(); err != nil { return "", fmt.Errorf("wait akaiutil: %w", err) } return string(output), nil } type volInfo struct { Part string `json:"part"` Name string `json:"name"` } 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) if err := os.MkdirAll(volDir, 0755); err != nil { return 0, fmt.Errorf("mkdir %s: %w", volDir, err) } 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) if err := os.MkdirAll(baseDir, 0755); err != nil { return 0, fmt.Errorf("mkdir %s: %w", baseDir, err) } return runExtraction(isoPath, baseDir, vols) } var extractISO = func(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 } type TagInfo struct { Index int `json:"index"` Name string `json:"name"` } func listTags(isoPath, part, volName string) ([]TagInfo, error) { var cmds strings.Builder fmt.Fprintf(&cmds, "df\n") fmt.Fprintf(&cmds, "cd /disk0/%s\n", part) nav := strings.ReplaceAll(volName, " ", "_") fmt.Fprintf(&cmds, "cd /disk0/%s/%s\n", part, nav) fmt.Fprintf(&cmds, "lstags\n") cmds.WriteString("q\n") out, err := runAkaiutil(isoPath, cmds.String()) if err != nil { return nil, fmt.Errorf("list tags: %w", err) } return parseTags(out), nil } func parseTags(output string) []TagInfo { reTag := regexp.MustCompile(`^\s*(\d+)\s+(\S.*)`) tags := []TagInfo{} inTags := false for _, line := range strings.Split(output, "\n") { trimmed := strings.TrimRight(line, " \r") if strings.Contains(trimmed, "tag name") { inTags = true continue } if !inTags { continue } if strings.HasPrefix(trimmed, "---") && len(tags) > 0 { break } if m := reTag.FindStringSubmatch(trimmed); m != nil { idx, _ := strconv.Atoi(m[1]) name := strings.TrimSpace(m[2]) if name != "" { tags = append(tags, TagInfo{Index: idx, Name: name}) } } } return tags }