PSFAudioExtractor/ATXtoAT3.java

116 lines
3.2 KiB
Java

package goblincave.gitea.nes;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import org.apache.commons.io.FilenameUtils;
import io.github.jacksonbrienen.jwfd.FileExtension;
import io.github.jacksonbrienen.jwfd.JWindowsFileDialog;
public class ATXtoAT3 {
public static void main(String[] args) throws IOException {
FileExtension atx = new FileExtension("ATX File", "atx");
String[] selection = JWindowsFileDialog.showMultiDialog(null, "Select ATX files to process", atx, FileExtension.ALL);
String output = JWindowsFileDialog.showDirectoryDialog(null, "Choose where to export AT3 files");
if(selection == null)
System.out.println("No files selected.");
else {
for(int i = 0; i < selection.length; i++) {
String path = selection[i];
File file1 = new File(path);
System.out.println("Opening " + path);
int start = 0x20; // findChunk("RIFF", file, 0x10);
int end = findChunk("EOFC", file1, 0x10);
File AT3 = trim(file1, start, end, output);
System.out.println("Saving " + AT3.toPath());
}
}
System.out.println(selection.length + " files exported.");
}
/**
* Trims input file up to start address and after end address, saving trimmed data to output path.
*
* @param input
* @param start
* @param end
* @param output
* @return trimmed file
*/
private static File trim(File input, int start, int end, String output) {
// input validation
// start and end must be between 0 and file length
// start must be less than end
if(start < 0 || start > input.length() || end < 0 || end > input.length() || start >= end)
return null;
File trimmed = new File(output + "/" + FilenameUtils.removeExtension(input.getName()) + ".at3");
try (
FileInputStream in = new FileInputStream(input);
FileOutputStream out = new FileOutputStream(trimmed);
) {
in.skipNBytes(start);
byte[] bytes = new byte[end - start];
int readBytes = in.read(bytes);
out.write(bytes, 0, readBytes);
} catch (IOException e) {
e.printStackTrace();
}
return trimmed;
}
/**
* Returns the first instance of the provided key in the file.
*
* @param key
* @param file
* @param width - search address increment value. Smaller values are more thorough, but take longer to process. Adjust according to the conventions of a given file. If unsure, use a value of 1.
* @return first match
*/
public static int findChunk(String key, File file, int width) throws IOException {
int keyValue = asciiToInt(key);
// find address of sequence in file
try (RandomAccessFile RAF = new RandomAccessFile(file, "r")) {
for(int a = 0; a + width < file.length(); a += width) {
RAF.seek(a);
if(RAF.readInt() == keyValue) {
return a;
}
}
}
// returns -1 if not found
return -1;
}
/**
* Converts the given ASCII sequence to an equivalent integer value.
*/
private static int asciiToInt(String key) {
int value = 0;
for(int i = 0; i < key.length(); i++)
value += key.charAt(key.length() - 1 - i) * Math.pow(16, 2 * i);
return value;
}
}