import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
public class ZeldaBFSTM2WAV {
private static boolean checkFFmpeg() {
try {
ProcessBuilder pb = new ProcessBuilder("ffmpeg", "-version");
pb.redirectErrorStream(true);
Process process = pb.start();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
String line = reader.readLine();
if (line != null && line.contains("ffmpeg version")) {
System.out.println("✓ 检测到ffmpeg: " + line);
return true;
}
}
int exitCode = process.waitFor();
return exitCode == 0;
} catch (Exception e) {
return false;
}
}
public static void convertWithFFmpeg(String inputFile, String outputFile) throws IOException {
convertWithFFmpeg(inputFile, outputFile, true);
}
public static void convertWithFFmpeg(String inputFile, String outputFile, boolean verbose) throws IOException {
File input = new File(inputFile);
if (!input.exists()) {
throw new IOException("输入文件不存在: " + inputFile);
}
if (!checkFFmpeg()) {
throw new IOException("未检测到ffmpeg!\n" +
"请安装ffmpeg并添加到PATH环境变量中。\n" +
"下载地址:https://ffmpeg.org/download.html");
}
List<String> command = new ArrayList<>();
command.add("ffmpeg");
command.add("-i");
command.add(input.getAbsolutePath());
command.add("-acodec");
command.add("pcm_s16le");
command.add("-y");
if (!verbose) {
command.add("-loglevel");
command.add("error");
}
command.add(outputFile);
if (verbose) {
System.out.println("执行ffmpeg命令:");
System.out.println(String.join(" ", command));
System.out.println();
}
ProcessBuilder pb = new ProcessBuilder(command);
pb.redirectErrorStream(true);
try {
Process process = pb.start();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
if (verbose) {
System.out.println(line);
}
}
}
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new IOException("ffmpeg转换失败,退出码: " + exitCode);
}
File output = new File(outputFile);
if (!output.exists()) {
throw new IOException("输出文件未生成: " + outputFile);
}
if (verbose) {
System.out.println("\n✓ 转换成功!");
System.out.println("输出文件: " + output.getAbsolutePath());
System.out.println("文件大小: " + output.length() + " 字节");
} else {
System.out.println("✓ 转换成功: " + new File(outputFile).getName() +
" (" + (output.length() / 1024) + " KB)");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException("转换过程被中断", e);
}
}
private static List<File> scanBFSTMFiles(String directory) {
List<File> bfstmFiles = new ArrayList<>();
File dir = new File(directory);
if (!dir.exists() || !dir.isDirectory()) {
System.err.println("目录不存在或不是有效目录: " + directory);
return bfstmFiles;
}
scanBFSTMFilesRecursive(dir, bfstmFiles);
return bfstmFiles;
}
private static void scanBFSTMFilesRecursive(File directory, List<File> bfstmFiles) {
File[] files = directory.listFiles();
if (files == null) {
return;
}
for (File file : files) {
if (file.isFile()) {
String fileName = file.getName().toLowerCase();
if (fileName.endsWith(".bfstm") || fileName.endsWith(".fstm")) {
bfstmFiles.add(file);
}
} else if (file.isDirectory()) {
scanBFSTMFilesRecursive(file, bfstmFiles);
}
}
}
public static void convertDirectory(String inputDir, String outputDir) {
convertDirectory(inputDir, outputDir, 0);
}
public static void convertDirectory(String inputDir, String outputDir, int maxFileSizeKB) {
try {
if (!checkFFmpeg()) {
throw new IOException("未检测到ffmpeg!\n" +
"请安装ffmpeg并添加到PATH环境变量中。\n" +
"下载地址:https://ffmpeg.org/download.html");
}
File inputDirectory = new File(inputDir);
if (!inputDirectory.exists() || !inputDirectory.isDirectory()) {
throw new IOException("输入目录不存在或不是有效目录: " + inputDir);
}
File outputDirectory;
if (outputDir != null && !outputDir.isEmpty()) {
outputDirectory = new File(outputDir);
} else {
outputDirectory = inputDirectory;
}
if (!outputDirectory.exists()) {
outputDirectory.mkdirs();
}
List<File> bfstmFiles = scanBFSTMFiles(inputDir);
if (bfstmFiles.isEmpty()) {
System.out.println("未找到BFSTM文件在目录: " + inputDir);
return;
}
if (maxFileSizeKB > 0) {
long maxSizeBytes = maxFileSizeKB * 1024L;
int originalCount = bfstmFiles.size();
List<File> filteredFiles = new ArrayList<>();
for (File file : bfstmFiles) {
if (file.length() >= maxSizeBytes) {
filteredFiles.add(file);
}
}
bfstmFiles.removeAll(filteredFiles);
int filteredCount = filteredFiles.size();
if (filteredCount > 0) {
System.out.println("过滤掉 " + filteredCount + " 个大于 " + maxFileSizeKB + " KB 的文件:");
for (File file : filteredFiles) {
System.out.println(" - " + file.getName() + " (" + (file.length() / 1024) + " KB)");
}
System.out.println();
}
}
if (bfstmFiles.isEmpty()) {
System.out.println("过滤后没有符合条件的BFSTM文件");
return;
}
System.out.println("找到 " + bfstmFiles.size() + " 个BFSTM文件" +
(maxFileSizeKB > 0 ? "(小于 " + maxFileSizeKB + " KB)" : ""));
System.out.println("输入目录: " + inputDirectory.getAbsolutePath());
System.out.println("输出目录: " + outputDirectory.getAbsolutePath());
System.out.println();
int successCount = 0;
int failCount = 0;
for (int i = 0; i < bfstmFiles.size(); i++) {
File bfstmFile = bfstmFiles.get(i);
String fileName = bfstmFile.getName();
String baseName = fileName.substring(0, fileName.lastIndexOf('.'));
String sequenceNumber = String.format("%03d_", i + 1);
String outputFileName = sequenceNumber + baseName + ".wav";
String outputFile = new File(outputDirectory, outputFileName).getAbsolutePath();
System.out.println("========================================");
System.out.println("[" + (i + 1) + "/" + bfstmFiles.size() + "] 转换: " + fileName);
System.out.println("输出: " + outputFile);
System.out.println();
try {
convertWithFFmpeg(bfstmFile.getAbsolutePath(), outputFile, false);
successCount++;
} catch (Exception e) {
System.err.println("转换失败: " + fileName);
System.err.println("错误: " + e.getMessage());
failCount++;
}
System.out.println();
}
System.out.println("========================================");
System.out.println("批量转换完成!");
System.out.println("成功: " + successCount + " 个");
System.out.println("失败: " + failCount + " 个");
System.out.println("总计: " + bfstmFiles.size() + " 个");
} catch (Exception e) {
System.err.println("批量转换失败: " + e.getMessage());
e.printStackTrace();
}
}
public static void main(String[] args) {
String inputDir = "D:\game\wiiu塞尔达旷野之息1.6.0本体+MLC+1.271新手包\1.271\cemu1.27.1\Games\The Legend of Zelda-Breath of the Wild\content\Voice";
String outputDir = "D:/222/";
int maxFileSizeKB = 500;
System.out.println("开始批量转换目录下的所有BFSTM文件...");
convertDirectory(inputDir, outputDir, maxFileSizeKB);
}
}