diff --git a/src/goblincave/gitea/nes/AudioExtractor.java b/src/goblincave/gitea/nes/AudioExtractor.java
index a3d236f..162caed 100644
--- a/src/goblincave/gitea/nes/AudioExtractor.java
+++ b/src/goblincave/gitea/nes/AudioExtractor.java
@@ -38,63 +38,6 @@ public class AudioExtractor {
*/
private final static boolean CMD_MODE = true;
- /**
- * ANSI code to go up one line, followed by a carriage return.
- * Useful for overwriting user input.
- */
- private final static String ANSI_BACKLINE = "\033[F\r";
- /**
- * ANSI code to switch to beige colored text.
- * Useful for highlighting user input.
- */
- private final static String ANSI_BEIGE = "\u001B[93m";
-
- /**
- * Hex value for the ASCII sequence "RIFF".
- * Indicates the start of a RIFF header in WAV files.
- */
- private final static int ASCII_RIFF = 0x5249_4646;
- /**
- * Hex value for the ASCII sequence "RIFF".
- * Indicates the start of a RIFF header in WAV files.
- */
- private final static int ASCII_WAVE = 0x5741_5645;
- /**
- * Hex value for the ASCII sequence "fmt ".
- * Indicates the start of a format chunk in WAV files.
- */
- private final static int ASCII_fmt = 0x666D_7420;
- /**
- * Hex value for the ASCII sequence "smpl".
- * Indicates the start of a sample chunk in WAV files.
- */
- private final static int ASCII_smpl = 0x736D_706C;
- /**
- * Hex value for the ASCII sequence "data".
- * Indicates the start of a data chunk in WAV files.
- */
- private final static int ASCII_data = 0x6461_7461;
- /**
- * Hex value for the ASCII sequence "ALIG".
- * Indicates the start of an alignment chunk in WAV files.
- */
- private final static int ASCII_ALIG = 0x414C_4947;
- /**
- * Hex value for the ASCII sequence "x2st".
- * Indicates the start of an XMA stream chunk in WAV files.
- */
- private final static int ASCII_x2st = 0x7832_7374;
- /**
- * Hex value for the ASCII sequence "seek".
- * Indicates the start of an XMA stream chunk in WAV files.
- */
- private final static int ASCII_seek = 0x7365_656B;
- /**
- * Hex value for the ASCII sequence "fLaC".
- * Indicates the start of an FLAC file.
- */
- private final static int ASCII_fLaC = 0x664C_6143;
-
/**
* BIN files must first be extracted from game disk to use this program.
* By default uses program directory for I/O and saves audio as WAV.
@@ -186,6 +129,9 @@ public class AudioExtractor {
}
case "convert": {
+// File audioFile = retrieveAudio(reader);
+// if(audioFile.isFile())
+// convert(audioFile, encoding);
}
@@ -214,87 +160,83 @@ public class AudioExtractor {
}
-/*
- System.exit(0);
-
- if(args.length > 0) {
- System.out.println("Arguments\t:");
- for(int i = 0; i < args.length; i++)
- System.out.print(Ansi.colorize(args[i] + ' ', Attribute.BRIGHT_GREEN_TEXT()));
- }
-
- if(args.length > 0) {
- switch(args[0]) {
-
- case "help":
- if(args.length > 1)
- switch(args[1]) {
- case "extract":
-
- break;
- case "pack":
- break;
- case "print":
- break;
- }
- System.out.println("java -jar RR6AudioExtractor.jar [operation] [package] [extract] [option]");
- break;
-
- case "extract":
- // TODO
- break;
-
- case "pack":
- // TODO
- break;
-
- }
-
- }
-
- // Confirm input/output mode
- boolean packMode = false;
- if(args.length > 0)
- packMode = args[0].equalsIgnoreCase("pack");
-
- // Confirm package directory, else abort
- File packDirectory;
- if(args.length > 1)
- packDirectory = Path.of(args[1]).toFile();
- else packDirectory = new File(new File(AudioExtractor.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getParent());
- if(packMode && !packDirectory.exists() && !packDirectory.mkdir()) {
- System.out.println("Package output directory " + packDirectory + " does not exist, and could not be created. "
- + "Program execution aborted.");
- System.exit(1);
- } else if(!packMode && !packDirectory.exists()) {
- System.out.println("Package input directory " + packDirectory + " does not exist. Program execution aborted.");
- System.exit(1);
- }
-
- // Confirm extract directory, else abort
- File extractDirectory;
- if(args.length > 2)
- extractDirectory = Path.of(args[2]).toFile();
- else extractDirectory = packDirectory;
- if(!packMode && !extractDirectory.exists() && !extractDirectory.mkdir()) {
- System.out.println("Extract output directory " + extractDirectory + " does not exist, and could not be created. "
- + "Program execution aborted.");
- System.exit(2);
- }
-
- // Confirm BGM output format
- boolean compressBGM = false;
- if(args.length > 3)
- compressBGM = args[3].equalsIgnoreCase("FLAC");
-
- // Begin operation
- if(packMode)
- pack(packDirectory, extractDirectory);
- else extract(packDirectory, extractDirectory, compressBGM);
-*/
-
}
+ /**
+ * Examines a given file and reads some of its properties.
+ *
+ * @param givenFile
+ */
+ public static void identify(File givenFile) {
+
+ // TODO check for characteristics of BIN or WAV files
+ String name = givenFile.getName();
+ System.out.println("Name:\t" + name);
+
+ long size = givenFile.length();
+ System.out.println("Size:\t" + size + " Bytes" + (size > 1024 ? " (" + convertBytes(size) + ")" : "") + "\n");
+
+ // file extension
+ int extIndex = name.lastIndexOf('.');
+ String extension = null;
+ if(extIndex > 0 && extIndex < name.length() - 1)
+ extension = name.substring(extIndex + 1);
+
+ try {
+ RandomAccessFile file = new RandomAccessFile(givenFile, "r");
+ switch(extension.toLowerCase()) {
+ case "bin": {
+ LinkedHashMap tracklist = findAudioTracks(file);
+ System.out.println(tracklist.size() + " Tracks (" + convertBytes(size / tracklist.size()) + " avg size)\n");
+ System.out.println("Format Info");
+ readFormatChunk(file, 0x0C);
+ break;
+ }
+ case "wav":
+ case "xma":
+ default: {
+ for(long i = 0; i < size - 4; i += 4) {
+ file.seek(i);
+ int value = file.readInt();
+ switch(value) {
+ case ASCII_RIFF: {
+ readRIFFChunk(file, (int) i);
+ break;
+ }
+ case ASCII_fmt: {
+ i = readFormatChunk(file, (int) i) - 4;
+ break;
+ }
+ case ASCII_smpl: {
+ i = readSampleChunk(file, (int) i) - 4;
+ break;
+ }
+ case ASCII_data: {
+ i = readDataChunk(file, (int) i) - 4;
+ break;
+ }
+ case ASCII_ALIG: {
+ i = readAlignmentChunk(file, (int) i) - 4;
+ break;
+ }
+ case ASCII_seek: {
+ i = readx2stChunk(file, (int) i) - 4;
+ break;
+ }
+ case ASCII_x2st: {
+ i = readx2stChunk(file, (int) i) - 4;
+ break;
+ }
+ }
+ }
+ break;
+ }
+ }
+ } catch (IOException e) {
+ System.out.println("Could not read file.");
+ }
+
+ }
/**
* Extracts audio tracks from the given package file.
@@ -363,18 +305,6 @@ public class AudioExtractor {
System.out.println("Finished.\n");
}
-
- public static void printProgressBar(int current, int total) {
- int done = 50 * current / total;
- int todo = 50 - done;
- String doneStr = ""; // String.format("%" + done + "s", '█');
- for(int i = 0; i < done; i++)
- doneStr += '█';
- String todoStr = ""; // String.format("%" + todo + "s", '_');
- for(int i = 0; i < todo; i++)
- doneStr += '_';
- System.out.println(ANSI_BACKLINE + "Progress: |" + doneStr + todoStr + "| (" + current + "/" + total + ")");
- }
/**
* Extracts audio files to the extract directory from package files located at the package directory.
@@ -383,6 +313,7 @@ public class AudioExtractor {
* @param packDirectory
* @param extractDirectory
* @param compressBGM
+ * @deprecated delete after adapting FLAC conversion
*/
public static void extract(File packDirectory, File extractDirectory, boolean compressBGM) {
@@ -514,6 +445,168 @@ public class AudioExtractor {
}
}
+
+ /**
+ * Packs WAV files located at the extract directory into BIN files at the package directory.
+ *
+ * @param packDirectory
+ * @param extractDirectory
+ */
+ private static void pack(File packDirectory, File extractDirectory) {
+
+ // Identify extracted folders
+ File[] packages = new File(extractDirectory.toString()).listFiles(new FilenameFilter() {
+ public boolean accept(File dir, String name) {
+ return name.toLowerCase().startsWith("pack_") && !name.toLowerCase().endsWith(".bin");
+ }
+ });
+
+ for(int i = 0; i < packages.length; i++)
+ System.out.println(packages[i].getName());
+
+ for(File packDir : packages) {
+
+ // 1. Compile list of WAV files in extracted directories
+ File[] audioTracks = new File(packDir.toString()).listFiles(new FilenameFilter() {
+ public boolean accept(File dir, String name) {
+ return name.toLowerCase().endsWith(".wav");
+ }
+ });
+
+ // 2. Check if pack files already exist in package directory
+ File pack = new File(packDir.getPath() + ".bin");
+ if(pack.exists());
+
+ // 3. If pack file already exists, insert WAV files into existing file.
+ // 4. If pack file doesn't exist, create new file then insert WAV files.
+
+ }
+
+ // Hypothesis 1: Does Ridge Racer 6 use a predetermined address list for reading tracks from package?
+ // Test 1: Try changing the address of a track and see if the game can handle it successfully.
+
+ // Hypothesis 2: Does Ridge Racer 6 loop tracks in their entirety, or do they use loop points defined in the file header?
+ // Test 1: Try editing loop information and see if it affects in-game playback.
+ // Test 2: Try replacing a track with a WAV file without loop data and see if it loops in in-game playback.
+
+ }
+
+ /**
+ * Patches a WAV file by seeking out its data and smpl chunks,
+ * replacing any existing smpl chunk with a new smpl chunk,
+ * setting loop points at the start and end of the data chunk.
+ *
+ * @param waveFile
+ */
+ private static void patch(File waveFile) {
+
+ try {
+ RandomAccessFile file = new RandomAccessFile(waveFile, "r");
+
+ // Also Find data location
+ int dataAddress = -1;
+ // Check if sample chunk already exists
+ int smplAddress = -1;
+
+ for(int i = 0; i < file.length(); i += 4) {
+
+ file.seek(i);
+ int scan = file.readInt();
+
+ // match "data" chunk header
+ if(scan == ASCII_data)
+ dataAddress = i;
+ // match "smpl" chunk header
+ else if(scan == ASCII_smpl)
+ smplAddress = i;
+
+ // if both chunks already located, stop seeking
+ if(smplAddress > -1 && dataAddress > -1)
+ break;
+
+ }
+
+ // Determine size of data chunk
+ int dataSize;
+ if(dataAddress > -1)
+ dataSize = readChunkSize(file, dataAddress);
+ else throw new IOException("Data chunk not in file.");
+
+ // Determine size of smpl chunk
+ int smplSize;
+ if(smplAddress > -1)
+ smplSize = readChunkSize(file, smplAddress);
+ // smpl chunk may not already exist
+
+ // Create new sample chunk
+ // Insert 68-byte sample chunk with loop before data chunk
+
+ byte[] smpl = new byte[68];
+ // smpl (0)
+ smpl[0] = 0x73;
+ smpl[1] = 0x6D;
+ smpl[2] = 0x70;
+ smpl[3] = 0x6C;
+ // size (4)
+ smpl[4] = 0x3C;
+ // manufacturer (8)
+ // product (12)
+ // sample period (16)
+ // MIDI unity note (20)
+ smpl[20] = 0x3C;
+ // MIDI pitch fraction (24)
+ // SMPTE Format (28)
+ // SMPTE Offset (32)
+ // Number of Sample Loops (36)
+ smpl[36] = 0x01;
+ // Sample Loops size (40)
+ // loop ID (44)
+ // loop type (48)
+ // data start
+ // loop start (52)
+ // data end
+ // loop end (56)
+ // tuning fraction (60)
+ // play count (64)
+
+
+ // Set loop points to start and end of data chunk
+ // Update the new file size in RIFF header
+
+ } catch(IOException e) {
+ System.out.println("Failed to patch file.");
+ }
+
+ }
+
+ /**
+ * Helper function to show current progress.
+ * Should only be used in CMD Mode.
+ *
+ * @param current
+ * @param total
+ */
+ public static void printProgressBar(int current, int total) {
+
+ int width = 50; // progress bar total width
+ double progress = (double) current / total; // 0 = none, 1 = complete
+
+ int progressWidth = (int) Math.floor(progress * width);
+ double progressRemainder = (progress * width) % 1.0;
+ int x = (int) Math.floor(progressRemainder * 8);
+ char[] c = { ' ', '▏', '▎', '▍', '▌', '▋', '▊', '▉' };
+
+ String bar = "";
+ for(int i = 0; i < width; i++) {
+ if(i < progressWidth)
+ bar += '█';
+ else if(i == progressWidth)
+ bar += c[x];
+ else bar += ' ';
+ }
+
+ System.out.println(ANSI_BACKLINE + "Progress: |" + bar + "| (" + current + "/" + total + ")");
+ }
/**
* Helper method to prompt user to select a package file, or folder containing one or more packages.
@@ -637,84 +730,6 @@ public class AudioExtractor {
}
- /**
- * Examines a given file and reads some of its properties.
- *
- * @param givenFile
- */
- public static void identify(File givenFile) {
-
- // TODO check for characteristics of BIN or WAV files
- String name = givenFile.getName();
- System.out.println("Name:\t" + name);
-
- long size = givenFile.length();
- System.out.println("Size:\t" + size + " Bytes" + (size > 1024 ? " (" + convertBytes(size) + ")" : "") + "\n");
-
- // file extension
- int extIndex = name.lastIndexOf('.');
- String extension = null;
- if(extIndex > 0 && extIndex < name.length() - 1)
- extension = name.substring(extIndex + 1);
-
- try {
- RandomAccessFile file = new RandomAccessFile(givenFile, "r");
- switch(extension.toLowerCase()) {
- case "bin": {
- LinkedHashMap tracklist = findAudioTracks(file);
- System.out.println(tracklist.size() + " Tracks (" + convertBytes(size / tracklist.size()) + " avg size)\n");
- System.out.println("Format Info");
- readFormatChunk(file, 0x0C);
- break;
- }
- case "wav":
- case "xma":
- default: {
- for(long i = 0; i < size - 4; i += 4) {
- file.seek(i);
- int value = file.readInt();
- switch(value) {
- case ASCII_RIFF: {
- readRIFFChunk(file, (int) i);
- break;
- }
- case ASCII_fmt: {
- i = readFormatChunk(file, (int) i) - 4;
- break;
- }
- case ASCII_smpl: {
- i = readSampleChunk(file, (int) i) - 4;
- break;
- }
- case ASCII_data: {
- i = readDataChunk(file, (int) i) - 4;
- break;
- }
- case ASCII_ALIG: {
- i = readAlignmentChunk(file, (int) i) - 4;
- break;
- }
- case ASCII_seek: {
- i = readx2stChunk(file, (int) i) - 4;
- break;
- }
- case ASCII_x2st: {
- i = readx2stChunk(file, (int) i) - 4;
- break;
- }
- }
- }
- break;
- }
- }
- } catch (IOException e) {
- System.out.println("Could not read file.");
- }
-
- // check if RIFF header
-
- }
-
/**
* Helper function to read RIFF header information.
*
@@ -1046,138 +1061,7 @@ public class AudioExtractor {
}
- /**
- * Packs WAV files located at the extract directory into BIN files at the package directory.
- *
- * @param packDirectory
- * @param extractDirectory
- */
- private static void pack(File packDirectory, File extractDirectory) {
-
- // Identify extracted folders
- File[] packages = new File(extractDirectory.toString()).listFiles(new FilenameFilter() {
- public boolean accept(File dir, String name) {
- return name.toLowerCase().startsWith("pack_") && !name.toLowerCase().endsWith(".bin");
- }
- });
-
- for(int i = 0; i < packages.length; i++)
- System.out.println(packages[i].getName());
-
- for(File packDir : packages) {
-
- // 1. Compile list of WAV files in extracted directories
- File[] audioTracks = new File(packDir.toString()).listFiles(new FilenameFilter() {
- public boolean accept(File dir, String name) {
- return name.toLowerCase().endsWith(".wav");
- }
- });
-
- // 2. Check if pack files already exist in package directory
- File pack = new File(packDir.getPath() + ".bin");
- if(pack.exists());
-
- // 3. If pack file already exists, insert WAV files into existing file.
- // 4. If pack file doesn't exist, create new file then insert WAV files.
-
- }
-
- // Hypothesis 1: Does Ridge Racer 6 use a predetermined address list for reading tracks from package?
- // Test 1: Try changing the address of a track and see if the game can handle it successfully.
-
- // Hypothesis 2: Does Ridge Racer 6 loop tracks in their entirety, or do they use loop points defined in the file header?
- // Test 1: Try editing loop information and see if it affects in-game playback.
- // Test 2: Try replacing a track with a WAV file without loop data and see if it loops in in-game playback.
-
- }
- /**
- * Patches a WAV file by seeking out its data and smpl chunks,
- * replacing any existing smpl chunk with a new smpl chunk,
- * setting loop points at the start and end of the data chunk.
- *
- * @param waveFile
- */
- private static void patch(File waveFile) {
-
- try {
- RandomAccessFile file = new RandomAccessFile(waveFile, "r");
-
- // Also Find data location
- int dataAddress = -1;
- // Check if sample chunk already exists
- int smplAddress = -1;
-
- for(int i = 0; i < file.length(); i += 4) {
-
- file.seek(i);
- int scan = file.readInt();
-
- // match "data" chunk header
- if(scan == ASCII_data)
- dataAddress = i;
- // match "smpl" chunk header
- else if(scan == ASCII_smpl)
- smplAddress = i;
-
- // if both chunks already located, stop seeking
- if(smplAddress > -1 && dataAddress > -1)
- break;
-
- }
-
- // Determine size of data chunk
- int dataSize;
- if(dataAddress > -1)
- dataSize = readChunkSize(file, dataAddress);
- else throw new IOException("Data chunk not in file.");
-
- // Determine size of smpl chunk
- int smplSize;
- if(smplAddress > -1)
- smplSize = readChunkSize(file, smplAddress);
- // smpl chunk may not already exist
-
- // Create new sample chunk
- // Insert 68-byte sample chunk with loop before data chunk
-
- byte[] smpl = new byte[68];
- // smpl (0)
- smpl[0] = 0x73;
- smpl[1] = 0x6D;
- smpl[2] = 0x70;
- smpl[3] = 0x6C;
- // size (4)
- smpl[4] = 0x3C;
- // manufacturer (8)
- // product (12)
- // sample period (16)
- // MIDI unity note (20)
- smpl[20] = 0x3C;
- // MIDI pitch fraction (24)
- // SMPTE Format (28)
- // SMPTE Offset (32)
- // Number of Sample Loops (36)
- smpl[36] = 0x01;
- // Sample Loops size (40)
- // loop ID (44)
- // loop type (48)
- // data start
- // loop start (52)
- // data end
- // loop end (56)
- // tuning fraction (60)
- // play count (64)
-
-
- // Set loop points to start and end of data chunk
- // Update the new file size in RIFF header
-
- } catch(IOException e) {
- System.out.println("Failed to patch file.");
- }
-
- }
/**
* Helper function to prompt user to select a WAV file.
@@ -2443,4 +2327,63 @@ public class AudioExtractor {
return value;
}
+
+ /**
+ * ANSI code to go up one line, followed by a carriage return.
+ * Useful for overwriting user input.
+ */
+ private final static String ANSI_BACKLINE = "\033[F\r";
+ /**
+ * ANSI code to switch to beige colored text.
+ * Useful for highlighting user input.
+ */
+ private final static String ANSI_BEIGE = "\u001B[93m";
+
+ /**
+ * Hex value for the ASCII sequence "RIFF".
+ * Indicates the start of a RIFF header in WAV files.
+ */
+ private final static int ASCII_RIFF = 0x5249_4646;
+ /**
+ * Hex value for the ASCII sequence "RIFF".
+ * Indicates the start of a RIFF header in WAV files.
+ */
+ private final static int ASCII_WAVE = 0x5741_5645;
+ /**
+ * Hex value for the ASCII sequence "fmt ".
+ * Indicates the start of a format chunk in WAV files.
+ */
+ private final static int ASCII_fmt = 0x666D_7420;
+ /**
+ * Hex value for the ASCII sequence "smpl".
+ * Indicates the start of a sample chunk in WAV files.
+ */
+ private final static int ASCII_smpl = 0x736D_706C;
+ /**
+ * Hex value for the ASCII sequence "data".
+ * Indicates the start of a data chunk in WAV files.
+ */
+ private final static int ASCII_data = 0x6461_7461;
+ /**
+ * Hex value for the ASCII sequence "ALIG".
+ * Indicates the start of an alignment chunk in WAV files.
+ */
+ private final static int ASCII_ALIG = 0x414C_4947;
+ /**
+ * Hex value for the ASCII sequence "x2st".
+ * Indicates the start of an XMA stream chunk in WAV files.
+ */
+ private final static int ASCII_x2st = 0x7832_7374;
+ /**
+ * Hex value for the ASCII sequence "seek".
+ * Indicates the start of an XMA stream chunk in WAV files.
+ */
+ private final static int ASCII_seek = 0x7365_656B;
+ /**
+ * Hex value for the ASCII sequence "fLaC".
+ * Indicates the start of an FLAC file.
+ */
+ private final static int ASCII_fLaC = 0x664C_6143;
+
+
}