From 0cac93383fffe0be6972676e9500634fe1d2085a Mon Sep 17 00:00:00 2001 From: Nes370 Date: Tue, 23 Jul 2024 23:46:18 -0700 Subject: [PATCH] Update AudioExtractor.java --- src/goblincave/gitea/nes/AudioExtractor.java | 270 ++++++++++++------- 1 file changed, 180 insertions(+), 90 deletions(-) diff --git a/src/goblincave/gitea/nes/AudioExtractor.java b/src/goblincave/gitea/nes/AudioExtractor.java index 474e440..bf79e44 100644 --- a/src/goblincave/gitea/nes/AudioExtractor.java +++ b/src/goblincave/gitea/nes/AudioExtractor.java @@ -33,7 +33,7 @@ public class AudioExtractor { * Activate alternate behavior for command line terminal. * Specifically prints things in a narrower width, and overwrites previous lines to add color. */ - private final static boolean CMD_MODE = true; + private final static boolean CMD_MODE = false; /** * ANSI code to go up one line, followed by a carriage return. @@ -51,6 +51,11 @@ public class AudioExtractor { * 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. @@ -76,6 +81,12 @@ public class AudioExtractor { * 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 "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.
@@ -252,28 +263,14 @@ public class AudioExtractor { } - /** - * Helper method to convert a number of bytes to a more readable form. - * - * @param bytes - * @return legible form - */ - private static String convertBytes(long bytes) { + public static void identify(File unknownFile) { - final String[] units = new String[] {"B", "KB", "MB", "GB", "TB"}; - int i = (int) (Math.log10(bytes) / Math.log10(1024)); - double value = bytes / Math.pow(1024, i); - return String.format("%.2f %s", value, units[i]); - - } - - public static void identify(File file) { // TODO check for characteristics of BIN or WAV files - String name = file.getName(); + String name = unknownFile.getName(); System.out.println("Name:\t" + name); - long size = file.length(); - System.out.println("Size:\t" + size + " Bytes" + (size > 1024 ? " (" + convertBytes(size) + ")" : "")); + long size = unknownFile.length(); + System.out.println("Size:\t" + size + " Bytes" + (size > 1024 ? " (" + convertBytes(size) + ")" : "") + "\n"); // file extension int extIndex = name.lastIndexOf('.'); @@ -281,10 +278,88 @@ public class AudioExtractor { if(extIndex > 0 && extIndex < name.length() - 1) extension = name.substring(extIndex + 1); + try { + RandomAccessFile file = new RandomAccessFile(unknownFile, "r"); + switch(extension.toLowerCase()) { + case "wav": + case "xma": { + byte[] buffer = new byte[4]; + for(long i = 0; i < size - 4; i += 4) { + file.seek(i); + int value = file.readInt(); + switch(value) { + case ASCII_RIFF: { + System.out.println(formatAddress(i) + ":\tRIFF (Resource Interchange File Format) chunk"); + if(i + 8 < size) { + int riffSize = readChunkSize(file, (int) i); + System.out.println(formatAddress(i + 4) + ":\t" + riffSize + " Bytes" + (riffSize > 1024 ? " (" + convertBytes(riffSize) + ")" : "")); + } + if(i + 12 < size) { + file.seek(i + 8); + value = file.readInt(); + if(value == ASCII_WAVE) + System.out.println(formatAddress(i + 8) + ":\tWAVE (Waveform Audio File Format) type"); + i += 12; + } + System.out.println(); + break; + } + case ASCII_fmt: { + System.out.println(formatAddress(i) + ":\tfmt (Format) chunk"); + int fmtSize = 8; + if(i + 8 < size) { + fmtSize = readChunkSize(file, (int) i); + System.out.println(formatAddress(i + 4) + ":\t" + fmtSize + " Bytes" + (fmtSize > 1024 ? " (" + convertBytes(fmtSize) + ")" : "")); + } + if(i + fmtSize < size) { + file.seek(i + 8); + byte[] compression = new byte[2]; + file.read(compression); + + + i += fmtSize; + } + System.out.println(); + break; + } + } + } + break; + } + case "bin": { + + break; + } + case "flac": { + + break; + } + default: { + break; + } + } + } catch (IOException e) { + System.out.println("Could not read file."); + } + // check if RIFF header } + /** + * Formats a file address with leading zeroes. + * + * @param address + * @return padded address + */ + private static String formatAddress(long address) { + + if(address > 0xFFFF) + return String.format("%08x", address); + else return String.format("%04x", address); + + } + /** * Extracts audio files to the extract directory from package files located at the package directory. * Also supports FLAC conversion for BGM files. @@ -558,6 +633,72 @@ public class AudioExtractor { } + /** + * Helper function to prompt user to select a program task. + * + * @param reader + * @param input + * @return operation + */ + private static String retrieveOperation(BufferedReader reader, String input) { + + String operation = null; + String[] values = {"identify", "extract", "pack", "patch", "print", "exit"}; + + if(input != null) + for(int i = 0; i < values.length; i++) + if(values[i].equalsIgnoreCase(input)) + operation = values[i]; + + if(operation == null) { + + String prompt = "\n" + Ansi.colorize("Operations", Attribute.BRIGHT_YELLOW_TEXT(), Attribute.BOLD()) + ":\n" + + "- " + Ansi.colorize("Identify", Attribute.BRIGHT_RED_TEXT()) + " a package or audio file\n" + + "- " + Ansi.colorize("Extract", Attribute.TEXT_COLOR(0xFF, 0x68, 0x1F)) + " audio tracks from a package (BIN -> WAV/XMA)\n" + + "- " + Ansi.colorize("Convert", Attribute.TEXT_COLOR(0xFF, 0xEA, 0x00)) + " audio tracks to another format (XMA <-> WAV <-> FLAC)\n" + + "- " + Ansi.colorize("Patch", Attribute.BRIGHT_GREEN_TEXT()) + " an audio track to repeat (WAV)\n" + + "- " + Ansi.colorize("Pack", Attribute.BRIGHT_BLUE_TEXT()) + " audio tracks into a package (WAV/XMA -> BIN)\n" + + "- " + Ansi.colorize("Print", Attribute.TEXT_COLOR(0x6B, 0x4C, 0xE3)) + " a Ridge Racer 6 style ASCII logo\n" + + "- " + Ansi.colorize("Exit", Attribute.BRIGHT_MAGENTA_TEXT()) + " program\n" + + "\nPlease select an operation: " + ANSI_BEIGE; + + operation = retrieveInput(reader, prompt, values); + + if(CMD_MODE) { + + Attribute color = null; + switch(operation.toLowerCase()) { + case "identify": + color = Attribute.BRIGHT_RED_TEXT(); + break; + case "extract": + color = Attribute.TEXT_COLOR(0xFF, 0x68, 0x1F); + break; + case "convert": + color = Attribute.TEXT_COLOR(0xFF, 0xEA, 0x00); + case "patch": + color = Attribute.BRIGHT_GREEN_TEXT(); + break; + case "pack": + color = Attribute.BRIGHT_BLUE_TEXT(); + break; + case "print": + color = Attribute.TEXT_COLOR(0x6B, 0x4C, 0xE3); + break; + case "exit": + color = Attribute.BRIGHT_MAGENTA_TEXT(); + break; + } + System.out.println(ANSI_BACKLINE + Ansi.colorize("Please select an operation: ") + Ansi.colorize(operation, color)); + + } else System.out.print(Ansi.colorize("")); + + } + + return operation.toLowerCase(); + + } + /** * Helper function to prompt user to select a WAV file. * @@ -741,72 +882,6 @@ public class AudioExtractor { } - /** - * Helper function to prompt user to select a program task. - * - * @param reader - * @param input - * @return operation - */ - private static String retrieveOperation(BufferedReader reader, String input) { - - String operation = null; - String[] values = {"identify", "extract", "pack", "patch", "print", "exit"}; - - if(input != null) - for(int i = 0; i < values.length; i++) - if(values[i].equalsIgnoreCase(input)) - operation = values[i]; - - if(operation == null) { - - String prompt = "\n" + Ansi.colorize("Operations", Attribute.BRIGHT_YELLOW_TEXT(), Attribute.BOLD()) + ":\n" - + "- " + Ansi.colorize("Identify", Attribute.BRIGHT_RED_TEXT()) + " a package or audio file\n" - + "- " + Ansi.colorize("Extract", Attribute.TEXT_COLOR(0xFF, 0x68, 0x1F)) + " audio tracks from a package (BIN to WAV)\n" - + "- " + Ansi.colorize("Convert", Attribute.TEXT_COLOR(0xFF, 0xEA, 0x00)) + " audio tracks to another format (XMA to PCM / WAV to FLAC)\n" - + "- " + Ansi.colorize("Patch", Attribute.BRIGHT_GREEN_TEXT()) + " an audio track to repeat (PCM-encoded WAV)\n" - + "- " + Ansi.colorize("Pack", Attribute.BRIGHT_BLUE_TEXT()) + " audio tracks into a package (WAV to BIN)\n" - + "- " + Ansi.colorize("Print", Attribute.TEXT_COLOR(0x6B, 0x4C, 0xE3)) + " a Ridge Racer 6 style ASCII logo\n" - + "- " + Ansi.colorize("Exit", Attribute.BRIGHT_MAGENTA_TEXT()) + " program\n" - + "\nPlease select an operation: " + ANSI_BEIGE; - - operation = retrieveInput(reader, prompt, values); - - if(CMD_MODE) { - - Attribute color = null; - switch(operation.toLowerCase()) { - case "identify": - color = Attribute.BRIGHT_RED_TEXT(); - break; - case "extract": - color = Attribute.TEXT_COLOR(0xFF, 0x68, 0x1F); - break; - case "convert": - color = Attribute.TEXT_COLOR(0xFF, 0xEA, 0x00); - case "patch": - color = Attribute.BRIGHT_GREEN_TEXT(); - break; - case "pack": - color = Attribute.BRIGHT_BLUE_TEXT(); - break; - case "print": - color = Attribute.TEXT_COLOR(0x6B, 0x4C, 0xE3); - break; - case "exit": - color = Attribute.BRIGHT_MAGENTA_TEXT(); - break; - } - System.out.println(ANSI_BACKLINE + Ansi.colorize("Please select an operation: ") + Ansi.colorize(operation, color)); - - } else System.out.print(Ansi.colorize("")); - - } - - return operation.toLowerCase(); - - } - /** * Prompts the user for input without strict validation. * @@ -895,12 +970,27 @@ public class AudioExtractor { file.seek(chunkAddress + 4); file.read(buffer); - +// System.out.println("DEBUG: " + (buffer[0] & 0xFF) + " " + (buffer[1] & 0xFF) + " " + (buffer[2] & 0xFF) + " " + (buffer[3] & 0xFF) + ""); return 8 // size offset - + buffer[3] // 16^0 - + buffer[2] * 256 // 16^2 - + buffer[1] * 65_536 // 16^4 - + buffer[0] * 16_777_216; // 16^6 + + (buffer[0] & 0xFF) // 256^0 + + (buffer[1] & 0xFF) * 256 // 256^1 + + (buffer[2] & 0xFF) * 65_536 // 256^2 + + (buffer[3] & 0xFF) * 16_777_216; // 256^3 + + } + + /** + * Helper method to convert a number of bytes to a more readable form. + * + * @param bytes + * @return legible form + */ + private static String convertBytes(long bytes) { + + final String[] units = new String[] {"B", "KB", "MB", "GB", "TB"}; + int i = (int) (Math.log10(bytes) / Math.log10(1024)); + double value = bytes / Math.pow(1024, i); + return String.format("%.2f %s", value, units[i]); }