From 5cbb727eb042ea037af3b4c786d701321388e2b0 Mon Sep 17 00:00:00 2001 From: Nes370 Date: Sat, 20 Jul 2024 06:09:19 -0700 Subject: [PATCH] Making progress on patch function --- .gitignore | 2 + src/goblincave/gitea/nes/AudioExtractor.java | 465 ++++++++++++++----- 2 files changed, 352 insertions(+), 115 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fd0ec54 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ + +*.class diff --git a/src/goblincave/gitea/nes/AudioExtractor.java b/src/goblincave/gitea/nes/AudioExtractor.java index a64aada..cc53401 100644 --- a/src/goblincave/gitea/nes/AudioExtractor.java +++ b/src/goblincave/gitea/nes/AudioExtractor.java @@ -33,9 +33,16 @@ public class AudioExtractor { * ANSI code to go up one line, followed by a carriage return. * Useful for overwriting user input */ - private final static String RETURN_UP = "\033[F\r"; + private final static String BACKLINE = "\033[F\r"; private final static String PALE_YELLOW = "\u001B[93m"; + private final static int RIFF = 0x52494646; + /** + * 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 = false; + /** * 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.
@@ -49,67 +56,46 @@ public class AudioExtractor { *
  • extract directory for unpacked audio files
  • *
  • extract BGM in "WAV" or "FLAC" format
  • * @throws URISyntaxException + * @throws InterruptedException */ - public static void main(String[] args) throws URISyntaxException { + public static void main(String[] args) throws URISyntaxException, InterruptedException { + + if(CMD) { + // NARROW LOGO + String adjustment = "█████████ ████████████████████████████████████████████████████ ████████████████████████"; + System.out.println(Ansi.colorize(logoFormat("Ridge", true, 8)/*.substring(0, 484) + adjustment*/, Attribute.BRIGHT_RED_TEXT())); + Thread.sleep(500); + System.out.println(BACKLINE + BACKLINE + Ansi.colorize(adjustment, Attribute.BRIGHT_RED_TEXT())); + System.out.println(Ansi.colorize(logoFormat("Racer 6", true, 9), Attribute.BRIGHT_RED_TEXT())); + Thread.sleep(500); + System.out.println(Ansi.colorize(logoFormat("Audio", true, 8), Attribute.YELLOW_TEXT())); + Thread.sleep(500); + System.out.println(Ansi.colorize(logoFormat("Xtractr", true, 8), Attribute.YELLOW_TEXT())); + Thread.sleep(500); + } else { + // WIDE LOGO + System.out.println(Ansi.colorize(logoFormat("Ridge Racer 6"), Attribute.BRIGHT_RED_TEXT())); + Thread.sleep(500); + System.out.println(Ansi.colorize(logoFormat("Audio Extractor", true, 8), Attribute.YELLOW_TEXT())); + Thread.sleep(500); + } - System.out.println(Ansi.colorize(logoFormat("Ridge Racer 6"), Attribute.BRIGHT_RED_TEXT())); - System.out.println(Ansi.colorize(logoFormat("Audio Extractor", true, 8), Attribute.YELLOW_TEXT())); System.out.println("Program:\tRidge Racer 6 Audio Extractor " + Ansi.colorize("v1.0", Attribute.BRIGHT_GREEN_TEXT()) + " by " + Ansi.colorize("Nes", Attribute.TEXT_COLOR(252, 42, 124))); System.out.println("Repository:\t" + Ansi.colorize("https://gitea.goblincave.synology.me/Nes/RR6AudioExtractor", Attribute.CYAN_TEXT())); - 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())); - } + // Used for reading user input + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + + // determine which operation to execute + String operationInput = null; + if(args.length > 0) + operationInput = args[0]; + String operation = retrieveOperation(reader, operationInput); if(args.length == 0) { // no arguments provided - - // determine which operation to execute - - BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); - String prompt = "\n" + Ansi.colorize("Operations", Attribute.BRIGHT_YELLOW_TEXT(), Attribute.BOLD()) + ":\n" - + "- " + Ansi.colorize("Extract", Attribute.BRIGHT_RED_TEXT()) + " audio tracks from package\n" - + "- " + Ansi.colorize("Package", Attribute.YELLOW_TEXT()) + " audio tracks into package\n" - + "- " + Ansi.colorize("Patch", Attribute.BRIGHT_GREEN_TEXT()) + " audio track to loop indefinitely\n" - + "- " + Ansi.colorize("Print", Attribute.BRIGHT_BLUE_TEXT()) + " Ridge Racer 6 logo style ASCII text\n" - + "- " + Ansi.colorize("Exit", Attribute.BRIGHT_MAGENTA_TEXT()) + " program\n" - + "\nPlease select an operation:" + PALE_YELLOW + " "; - String input; - { - String[] values = {"extract", "package", "patch", "print", "exit"}; - input = retrieveInput(reader, prompt, values); - } - - int operation = -1; - switch(input.toLowerCase()) { - case "extract": - operation = 0; - System.out.println(RETURN_UP + Ansi.colorize("Please select an operation: ") + Ansi.colorize(input, Attribute.BRIGHT_RED_TEXT())); - break; - case "pack": - case "package": - operation = 1; - System.out.println(RETURN_UP + Ansi.colorize("Please select an operation: ") + Ansi.colorize(input, Attribute.YELLOW_TEXT())); - break; - case "patch": - operation = 2; - System.out.println(RETURN_UP + Ansi.colorize("Please select an operation: ") + Ansi.colorize(input, Attribute.BRIGHT_GREEN_TEXT())); - break; - case "logo": - case "print": - operation = 3; - System.out.println(RETURN_UP + Ansi.colorize("Please select an operation: ") + Ansi.colorize(input, Attribute.BRIGHT_BLUE_TEXT())); - break; - case "quit": - case "exit": - operation = 4; - System.out.println(RETURN_UP + Ansi.colorize("Please select an operation: ") + Ansi.colorize(input, Attribute.BRIGHT_MAGENTA_TEXT())); - break; - } - + switch(operation) { - case 0: // extract + case "extract": // extract // Determine package directory File currentDir = new File(new File(AudioExtractor.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getParent()); boolean packDirFound = false; @@ -119,50 +105,18 @@ public class AudioExtractor { } while(!packDirFound); break; - case 1: // pack + case "package": // pack break; - case 2: // patch + case "patch": // patch + File waveFile = retrieveWave(reader); + patch(waveFile); break; - case 3: // print - prompt = "\nCommand parameters: " + Ansi.colorize("Print", Attribute.BRIGHT_BLUE_TEXT()) + Ansi.colorize(" [color]", Attribute.BRIGHT_YELLOW_TEXT()) + Ansi.colorize(" [message]", Attribute.BRIGHT_RED_TEXT()) - + "\n" - + "\n" + Ansi.colorize("Colors", Attribute.BRIGHT_YELLOW_TEXT(), Attribute.BOLD()) + ":" - + "\n- " + Ansi.colorize("Default", Attribute.CLEAR()) - + "\n- " + Ansi.colorize("Black", Attribute.BLACK_TEXT()) + ", " + Ansi.colorize("Gray", Attribute.BRIGHT_BLACK_TEXT()) + ", " + Ansi.colorize("White", Attribute.BRIGHT_WHITE_TEXT()) - + "\n- " + Ansi.colorize("Indigo", Attribute.TEXT_COLOR(0x7D, 0x1A, 0xFF)) + ", " + Ansi.colorize("Purple", Attribute.BRIGHT_MAGENTA_TEXT()) + ", " + Ansi.colorize("Violet", Attribute.TEXT_COLOR(0xEE, 0x82, 0xEE)) - + "\n- " + Ansi.colorize("Red", Attribute.RED_TEXT()) + ", " + Ansi.colorize("Cinnabar", Attribute.BRIGHT_RED_TEXT()) + ", " + Ansi.colorize("Pink", Attribute.TEXT_COLOR(0xFF, 0xC0, 0xCB)) - + "\n- " + Ansi.colorize("Orange", Attribute.TEXT_COLOR(0xFF, 0x68, 0x1F)) + ", " + Ansi.colorize("Gold", Attribute.YELLOW_TEXT()) + ", " + Ansi.colorize("Yellow", Attribute.TEXT_COLOR(0xFF, 0xEA, 0x00)) - + "\n- " + Ansi.colorize("Green", Attribute.BRIGHT_GREEN_TEXT()) + ", " + Ansi.colorize("Chartreuse", Attribute.TEXT_COLOR(0xD1, 0xEB, 0x27)) + ", " + Ansi.colorize("Beige", Attribute.BRIGHT_YELLOW_TEXT()) - + "\n- " + Ansi.colorize("Blue", Attribute.BRIGHT_BLUE_TEXT()) + ", " + Ansi.colorize("Teal", Attribute.TEXT_COLOR(0x00, 0x93, 0xCF)) + ", " + Ansi.colorize("Cyan", Attribute.BRIGHT_CYAN_TEXT()) - + "\n" - + "\n" + Ansi.colorize("Please select a color:") + PALE_YELLOW + " "; - String[] values = {"Default", "Black", "Gray", "White", - "Indigo", "Purple", "Violet", "Red", "Cinnabar", "Pink", - "Orange", "Gold", "Yellow", "Green", "Chartreuse", "Beige", - "Blue", "Teal", "Cyan"}; - input = retrieveInput(reader, prompt, values); - Attribute color = null; - switch(input.toLowerCase()) { - case "default": color = Attribute.CLEAR(); break; - case "black": color = Attribute.BLACK_TEXT(); break; - case "gray": color = Attribute.BRIGHT_BLACK_TEXT(); break; - case "white": color = Attribute.BRIGHT_WHITE_TEXT(); break; - case "indigo": color = Attribute.TEXT_COLOR(0x7D, 0x1A, 0xFF); break; - case "purple": color = Attribute.BRIGHT_MAGENTA_TEXT(); break; - case "violet": color = Attribute.TEXT_COLOR(0xEE, 0x82, 0xEE); break; - case "red": color = Attribute.RED_TEXT(); break; - case "cinnabar": color = Attribute.BRIGHT_RED_TEXT(); break; - case "pink": color = Attribute.TEXT_COLOR(0xFF, 0xC0, 0xCB); break; - case "green": color = Attribute.BRIGHT_GREEN_TEXT(); break; - case "chartreuse": color = Attribute.TEXT_COLOR(0xD1, 0xEB, 0x27); break; - case "beige": color = Attribute.BRIGHT_YELLOW_TEXT(); break; - case "blue": color = Attribute.BRIGHT_BLUE_TEXT(); break; - case "teal": color = Attribute.TEXT_COLOR(0x00, 0x93, 0xCF); break; - case "cyan": color = Attribute.BRIGHT_CYAN_TEXT(); break; - } - System.out.println(RETURN_UP + Ansi.colorize("Please select a color: ") + Ansi.colorize(input, color)); + case "print": // print + Attribute color = retrieveColor(reader); + String message = retrieveInput(reader, "Please enter your logo message: " + PALE_YELLOW); + System.out.println(Ansi.colorize(logoFormat(message), color)); break; - case 4: // exit + case "exit": // exit System.out.println("Program closed."); System.exit(0); break; @@ -171,6 +125,12 @@ 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]) { @@ -239,6 +199,201 @@ public class AudioExtractor { else extract(packDirectory, extractDirectory, compressBGM); } + + private static File retrieveWave(BufferedReader reader) { + + String prompt = "Please provide a WAV file: " + PALE_YELLOW; + + File waveFile = null; + boolean valid = false; + do { + + // Retrieve an audio file + waveFile = retrieveFile(reader, prompt, ".wav"); + + try { + // validate that chosen file is a proper Wave file + RandomAccessFile file = new RandomAccessFile(waveFile, "r"); + // first 4 bytes start with RIFF + if(file.readInt() == RIFF) { + // next 4 bytes contain the size of the file in little-endian representation + file.seek(4); + byte[] buffer = new byte[4]; + file.read(buffer); // write size bytes to buffer + + // calculate expected file size + int headerSize = 8 + buffer[3] + + buffer[2] * (int) Math.pow(16, 2) + + buffer[1] * (int) Math.pow(16, 4) + + buffer[0] * (int) Math.pow(16, 6); + + // file is valid if actual file length matches expected size + valid = headerSize == file.length(); + if(!valid) + System.out.println("File length mismatch. Expected " + headerSize + " bytes, but found " + file.length() + " bytes."); + + } else System.out.println("Invalid RIFF header."); + file.close(); + } catch (IOException e) { + System.out.println("Invalid file."); + } + + } while(!valid); + + return waveFile; + + } + + private static File retrieveFile(BufferedReader reader, String prompt, String extension) { + + File file = null; + + String input = null; + boolean valid = false; + do { + System.out.print(prompt); + try { + input = reader.readLine(); + file = new File(input); + if(file.exists()) { + if(file.getName().toLowerCase().endsWith(extension)) { + valid = true; + } else System.out.println("File does not have " + extension.toUpperCase() + " file extension"); + } else System.out.println("File does not exist."); + } catch (IOException e) { + System.out.println(Ansi.colorize("Invalid file.")); + } + } while(!valid); + + return file; + + } + + private static Attribute retrieveColor(BufferedReader reader) { + + String prompt = "\nCommand parameters: " + Ansi.colorize("Print", Attribute.BRIGHT_BLUE_TEXT()) + Ansi.colorize(" [color]", Attribute.BRIGHT_YELLOW_TEXT()) + Ansi.colorize(" [message]", Attribute.BRIGHT_RED_TEXT()) + + "\n" + + "\n" + Ansi.colorize("Colors", Attribute.BRIGHT_YELLOW_TEXT(), Attribute.BOLD()) + ":" + + "\n- " + Ansi.colorize("Default", Attribute.CLEAR()) + + "\n- " + Ansi.colorize("Black", Attribute.BLACK_TEXT()) + ", " + Ansi.colorize("Gray", Attribute.BRIGHT_BLACK_TEXT()) + ", " + Ansi.colorize("White", Attribute.BRIGHT_WHITE_TEXT()) + + "\n- " + Ansi.colorize("Indigo", Attribute.TEXT_COLOR(0x7D, 0x1A, 0xFF)) + ", " + Ansi.colorize("Purple", Attribute.BRIGHT_MAGENTA_TEXT()) + ", " + Ansi.colorize("Violet", Attribute.TEXT_COLOR(0xEE, 0x82, 0xEE)) + + "\n- " + Ansi.colorize("Red", Attribute.RED_TEXT()) + ", " + Ansi.colorize("Cinnabar", Attribute.BRIGHT_RED_TEXT()) + ", " + Ansi.colorize("Pink", Attribute.TEXT_COLOR(0xFF, 0xC0, 0xCB)) + + "\n- " + Ansi.colorize("Orange", Attribute.TEXT_COLOR(0xFF, 0x68, 0x1F)) + ", " + Ansi.colorize("Gold", Attribute.YELLOW_TEXT()) + ", " + Ansi.colorize("Yellow", Attribute.TEXT_COLOR(0xFF, 0xEA, 0x00)) + + "\n- " + Ansi.colorize("Green", Attribute.BRIGHT_GREEN_TEXT()) + ", " + Ansi.colorize("Chartreuse", Attribute.TEXT_COLOR(0xD1, 0xEB, 0x27)) + ", " + Ansi.colorize("Beige", Attribute.BRIGHT_YELLOW_TEXT()) + + "\n- " + Ansi.colorize("Blue", Attribute.BRIGHT_BLUE_TEXT()) + ", " + Ansi.colorize("Teal", Attribute.TEXT_COLOR(0x00, 0x93, 0xCF)) + ", " + Ansi.colorize("Cyan", Attribute.BRIGHT_CYAN_TEXT()) + + "\n" + + "\n" + Ansi.colorize("Please select a color:") + PALE_YELLOW + " "; + String[] values = {"Default", "Black", "Gray", "White", "Indigo", "Purple", "Violet", "Red", "Cinnabar", "Pink", "Orange", "Gold", "Yellow", "Green", "Chartreuse", "Beige","Blue", "Teal", "Cyan"}; + + String input = retrieveInput(reader, prompt, values); + + Attribute color = null; + switch(input.toLowerCase()) { + case "default": color = Attribute.CLEAR(); break; + case "black": color = Attribute.BLACK_TEXT(); break; + case "gray": color = Attribute.BRIGHT_BLACK_TEXT(); break; + case "white": color = Attribute.BRIGHT_WHITE_TEXT(); break; + case "indigo": color = Attribute.TEXT_COLOR(0x7D, 0x1A, 0xFF); break; + case "purple": color = Attribute.BRIGHT_MAGENTA_TEXT(); break; + case "violet": color = Attribute.TEXT_COLOR(0xEE, 0x82, 0xEE); break; + case "red": color = Attribute.RED_TEXT(); break; + case "cinnabar": color = Attribute.BRIGHT_RED_TEXT(); break; + case "pink": color = Attribute.TEXT_COLOR(0xFF, 0xC0, 0xCB); break; + case "orange": color = Attribute.TEXT_COLOR(0xFF, 0x68, 0x1F); break; + case "gold": color = Attribute.YELLOW_TEXT(); break; + case "yellow": color = Attribute.TEXT_COLOR(0xFF, 0xEA, 0x00); break; + case "green": color = Attribute.BRIGHT_GREEN_TEXT(); break; + case "chartreuse": color = Attribute.TEXT_COLOR(0xD1, 0xEB, 0x27); break; + case "beige": color = Attribute.BRIGHT_YELLOW_TEXT(); break; + case "blue": color = Attribute.BRIGHT_BLUE_TEXT(); break; + case "teal": color = Attribute.TEXT_COLOR(0x00, 0x93, 0xCF); break; + case "cyan": color = Attribute.BRIGHT_CYAN_TEXT(); break; + } + + if(CMD) + System.out.println(BACKLINE + Ansi.colorize("Please select a color: ") + Ansi.colorize(input, color) + "\n"); + else System.out.println(Ansi.colorize("")); + + return color; + + } + + private static String retrieveOperation(BufferedReader reader, String input) { + + String operation = null; + String[] values = {"extract", "package", "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("Extract", Attribute.BRIGHT_RED_TEXT()) + " audio tracks from package\n" + + "- " + Ansi.colorize("Package", Attribute.YELLOW_TEXT()) + " audio tracks into package\n" + + "- " + Ansi.colorize("Patch", Attribute.BRIGHT_GREEN_TEXT()) + " audio track to loop indefinitely\n" + + "- " + Ansi.colorize("Print", Attribute.BRIGHT_BLUE_TEXT()) + " Ridge Racer 6 style ASCII logo\n" + + "- " + Ansi.colorize("Exit", Attribute.BRIGHT_MAGENTA_TEXT()) + " program\n" + + "\nPlease select an operation: " + PALE_YELLOW; + + operation = retrieveInput(reader, prompt, values); + + if(CMD) { + + Attribute color = null; + switch(operation.toLowerCase()) { + case "extract": + color = Attribute.BRIGHT_RED_TEXT(); + break; + case "package": + color = Attribute.YELLOW_TEXT(); + break; + case "patch": + color = Attribute.BRIGHT_GREEN_TEXT(); + break; + case "print": + color = Attribute.BRIGHT_BLUE_TEXT(); + break; + case "exit": + color = Attribute.BRIGHT_MAGENTA_TEXT(); + break; + } + System.out.println(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. + * @param reader + * @param prompt + * @return input + */ + private static String retrieveInput(BufferedReader reader, String prompt) { + + String input = null; + + boolean valid = false; + do { + System.out.print(prompt); + try { + input = reader.readLine(); + valid = true; + } catch (IOException e) { + System.out.println(Ansi.colorize("Invalid input.")); + } + } while(!valid); + + return input; + + } /** * Prompts the user for input and validates that the input matches one of the provided values. @@ -251,6 +406,7 @@ public class AudioExtractor { private static String retrieveInput(BufferedReader reader, String prompt, String[] values) { String input = null; + boolean valid = false; do { System.out.print(prompt); @@ -271,6 +427,78 @@ public class AudioExtractor { return input; } + + /** + * Patches the provided WAV file by seeking out its data and smpl chunks, + * deletes any existing smpl chunk, + * then inserts a new smpl chunk with loop points set 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 "smpl" chunk header + if(scan == 0x736D706C) + smplAddress = i; + // match "data" chunk header + if(scan == 0x64617461) + dataAddress = i; + if(smplAddress > -1 && dataAddress > -1) + break; + } + + // Determine size of data chunk + if(dataAddress > -1) { + + file.seek(dataAddress + 4); + byte[] buffer = new byte[4]; + file.read(buffer); + + int dataSize = 8 + buffer[3] + + buffer[2] * (int) Math.pow(16, 2) + + buffer[1] * (int) Math.pow(16, 4) + + buffer[0] * (int) Math.pow(16, 6); + + } + + // Determine size of smpl chunk + if(smplAddress > -1) { + + file.seek(smplAddress + 4); + byte[] buffer = new byte[4]; + file.read(buffer); // write size bytes to buffer + + // calculate expected chunk size + int smplSize = 8 + buffer[3] + + buffer[2] * (int) Math.pow(16, 2) + + buffer[1] * (int) Math.pow(16, 4) + + buffer[0] * (int) Math.pow(16, 6); + + } + + + + // Create new sample chunk + // Insert 68-byte sample chunk with loop before data chunk + + + + // 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."); + } + + } private static void pack(File packDirectory, File extractDirectory) { @@ -477,6 +705,11 @@ public class AudioExtractor { return tracklist; } + + + private static String logoFormat(String string, boolean italic) { + return logoFormat(string, italic, 10); + } /** * Creates an ASCII logo in RR6 style using the provided text. @@ -825,16 +1058,17 @@ public class AudioExtractor { logo[9] += "██████████████"; break; case 'N': - logo[0] += " "; - logo[1] += " "; - logo[2] += "████ ████"; - logo[3] += "██████ ████"; - logo[4] += "███████ ████"; - logo[5] += "████ ███████"; - logo[6] += "████ ██████"; - logo[7] += "████ ████"; - logo[8] += " "; - logo[9] += "████████████"; + logo[0] += " "; + logo[1] += " "; + logo[2] += "████ ████ "; + logo[3] += "██████ ████ "; + logo[4] += "███████ ████ "; + logo[5] += "████ ███████ "; + logo[6] += "████ ██████ "; + logo[7] += "████ ████ "; + logo[8] += " ████ "; + logo[9] += "█████████ ███"; + spaced = true; break; case 'O': if(logo[0].length() > 0) @@ -950,16 +1184,17 @@ public class AudioExtractor { logo[9] += "██████████████"; break; case 'X': - logo[0] += " "; - logo[1] += " "; - logo[2] += "████ ████"; - logo[3] += "████ ████"; - logo[4] += " ████ ████ "; - logo[5] += " ████ "; - logo[6] += " ████ ████ "; - logo[7] += "████ ████"; - logo[8] += " "; - logo[9] += "████████████"; + logo[0] += " "; + logo[1] += " "; + logo[2] += "████ ████ "; + logo[3] += "████ ████ "; + logo[4] += " ████ ████ "; + logo[5] += " ████ "; + logo[6] += " ████ ████ "; + logo[7] += "████ ████ "; + logo[8] += " █████"; + logo[9] += "█████████ ███"; + spaced = true; break; case 'Y': logo[0] += " ";