예제 #1
0
        public static string BuildCueSheet(TonieAudio file)
        {
            ulong[]       positions = file.ParsePositions();
            string[]      titles    = null;
            string        title     = null;
            StringBuilder cue       = new StringBuilder();

            var found = TonieInfos.Where(t => t.AudioIds.Contains(file.Header.AudioId));

            if (found.Count() > 0)
            {
                var info = found.First();

                title  = info.Title;
                titles = info.Tracks;
            }

            if (title != null)
            {
                cue.Append("TITLE \"").Append(title).AppendLine("\"");
            }

            cue.Append("FILE ").Append(file.FilenameShort).Append(".ogg").AppendLine(" MP3");

            for (int chapter = 0; chapter < file.Header.AudioChapters.Length; chapter++)
            {
                /* seems the chapter is the page, but off by one */
                uint  offset  = file.Header.AudioChapters[chapter];
                ulong granule = file.GetGranuleByPage((offset > 0) ? (offset - 1) : 0);

                cue.Append("  TRACK ").Append(chapter + 1).AppendLine(" AUDIO");
                if (titles != null)
                {
                    cue.Append("    TITLE \"").Append(titles[chapter]).AppendLine("\"");
                }
                string time = FormatGranuleCue(granule);
                cue.Append("    INDEX 1 ").AppendLine(time);
            }

            return(cue.ToString());
        }
예제 #2
0
        static void Main(string[] args)
        {
            bool        showLicense    = false;
            bool        showHelp       = false;
            eDumpFormat dumpFormat     = eDumpFormat.FormatText;
            bool        useVbr         = false;
            string      mode           = "";
            string      outputLocation = "";
            string      prefixLocation = null;
            string      audioId        = "";
            string      jsonFile       = "tonies.json";

            int bitRate = 96;

            var p = new OptionSet {
                { "m|mode=", "Operating mode: info, decode, encode", (string n) => mode = n },
                { "o|output=", "Location where to write the file(s) to", (string r) => outputLocation = r },
                { "p|prefix=", "Location where to find prefix files", (string r) => prefixLocation = r },
                { "i|id=", "Set AudioID for encoding (default: current time)", (string r) => audioId = r },
                { "b|bitrate=", "Set opus bit rate (default: " + bitRate + " kbps)", (int r) => bitRate = r },
                { "vbr", "Use VBR encoding", r => useVbr = true },
                { "j|json=", "Set JSON file/URL with details about tonies", (string r) => jsonFile = r },
                { "f|format=", "Output details as: csv, json or text", v => { switch (v)
                                                                              {
                                                                              case "csv":  dumpFormat = eDumpFormat.FormatCSV; break;

                                                                              case "json":  dumpFormat = eDumpFormat.FormatJSON; break;

                                                                              case "text":  dumpFormat = eDumpFormat.FormatText; break;
                                                                              }
                  } },
                { "v", "increase debug message verbosity", v => { if (v != null)
                                                                  {
                                                                      ++Verbosity;
                                                                  }
                  } },
                { "h|help", "show this message and exit", h => showHelp = true },
                { "license", "show licenses and disclaimer", h => showLicense = true },
            };

            List <string> extra;

            try
            {
                extra = p.Parse(args);
            }
            catch (OptionException e)
            {
                Console.Write("Teddy.exe: ");
                Console.WriteLine(e.Message);
                Console.WriteLine("Try `Teddy.exe --help' for more information.");
                return;
            }

            if (showLicense)
            {
                ShowLicense(p);
                return;
            }

            if (showHelp)
            {
                ShowHelp(p);
                return;
            }

            LoadJson(jsonFile);

            switch (mode)
            {
            default:
                ShowHelp(p);
                return;

            case "info":
            {
                if (extra.Count < 1 || (!Directory.Exists(extra[0]) && !File.Exists(extra[0])))
                {
                    Console.WriteLine("Error: You must specify a file");
                    return;
                }

                switch (dumpFormat)
                {
                case eDumpFormat.FormatCSV:
                    if (extra.Count > 1 || Directory.Exists(extra[0]))
                    {
                        Console.WriteLine("UID;AudioID;AudioDate;HeaderLength;HeaderOK;Padding;AudioLength;AudioLengthCheck;AudioHash;AudioHashCheck;Chapters;Segments;MinSegmentsPerPage;MaxSegmentsPerPage;SegLengthSum;HighestGranule;Time;MinGranules;MaxGranules;MinTime;MaxTime;");
                    }
                    break;

                case eDumpFormat.FormatJSON:
                    Console.WriteLine("[");
                    break;

                case eDumpFormat.FormatText:
                    Console.WriteLine("[Mode: dump information]");
                    break;
                }

                List <string> files = new List <string>();

                foreach (string file in extra)
                {
                    if (Directory.Exists(file))
                    {
                        FindTonieFiles(files, file);
                    }
                    else
                    {
                        if (!File.Exists(file))
                        {
                            Console.WriteLine("Error: file '" + file + "' does not exist");
                            return;
                        }
                        files.Add(file);
                    }
                }

                bool first = true;
                foreach (string file in files.ToArray())
                {
                    try
                    {
                        TonieAudio dumpFile = TonieAudio.FromFile(file);

                        dumpFile.CalculateStatistics(out long segCount, out long segLength, out int minSegs, out int maxSegs, out ulong minGranule, out ulong maxGranule, out ulong highestGranule);
                        string        uidrev = new FileInfo(file).Directory.Name + new FileInfo(file).Name;
                        List <string> groups = (from Match m in Regex.Matches(uidrev, @"[A-F0-9]{2}") select m.Value).ToList();
                        groups.Reverse();
                        string uid  = string.Join("", groups.ToArray());
                        var    date = DateTimeOffset.FromUnixTimeSeconds(dumpFile.Header.AudioId);

                        switch (dumpFormat)
                        {
                        case eDumpFormat.FormatCSV:
                        {
                            Console.Write(uid + ";");
                            Console.Write(dumpFile.Header.AudioId.ToString("X8") + ";");
                            Console.Write(date.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss") + ";");
                            Console.Write(dumpFile.HeaderLength + ";");
                            Console.Write(((dumpFile.HeaderLength != 0xFFC) ? "[WARNING: EXTRA DATA]" : "[OK]") + ";");
                            Console.Write(dumpFile.Header.Padding.Length + ";");
                            Console.Write(dumpFile.Header.AudioLength + ";");
                            Console.Write((dumpFile.Header.AudioLength == dumpFile.Audio.Length ? "[OK]" : "[INCORRECT]") + ";");
                            Console.Write(BitConverter.ToString(dumpFile.Header.Hash).Replace("-", "") + ";");
                            Console.Write((dumpFile.HashCorrect ? "[OK]" : "[INCORRECT]") + ";");
                            foreach (var offset in dumpFile.Header.AudioChapters)
                            {
                                Console.Write(offset + " ");
                            }
                            Console.Write(";");

                            Console.Write(segCount + ";");
                            Console.Write(minSegs + ";");
                            Console.Write(maxSegs + ";");
                            Console.Write(segLength + ";");
                            Console.Write(highestGranule + ";");
                            Console.Write(TonieAudio.FormatGranule(highestGranule) + ";");
                            Console.Write(minGranule + ";");
                            Console.Write(maxGranule + ";");
                            Console.Write((1000 * minGranule / 48000.0f) + ";");
                            Console.Write((1000 * maxGranule / 48000.0f) + ";");
                            Console.WriteLine();
                            break;
                        }

                        case eDumpFormat.FormatText:
                        {
                            Console.WriteLine("Dump of " + dumpFile.Filename + " (UID " + uid + "):");

                            Console.WriteLine("  Header: AudioID     0x" + dumpFile.Header.AudioId.ToString("X8") + " (" + date.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss") + ")");

                            string[] titles = null;
                            var      found  = TonieInfos.Where(t => t.AudioIds.Contains(dumpFile.Header.AudioId));
                            if (found.Count() > 0)
                            {
                                var info = found.First();
                                titles = info.Tracks;

                                Console.WriteLine("  Header: JSON Name   '" + info.Title + "'");
                            }
                            Console.WriteLine("  Header: Length      0x" + dumpFile.HeaderLength.ToString("X8") + " " + ((dumpFile.HeaderLength != 0xFFC) ? " [WARNING: EXTRA DATA]" : "[OK]"));
                            Console.WriteLine("  Header: Padding     0x" + dumpFile.Header.Padding.Length.ToString("X8"));
                            Console.WriteLine("  Header: AudioLen    0x" + dumpFile.Header.AudioLength.ToString("X8") + " " + (dumpFile.Header.AudioLength == dumpFile.Audio.Length ? "[OK]" : "[INCORRECT]"));
                            Console.WriteLine("  Header: Checksum    " + BitConverter.ToString(dumpFile.Header.Hash).Replace("-", "") + " " + (dumpFile.HashCorrect ? "[OK]" : "[INCORRECT]"));
                            Console.WriteLine("  Header: Chapters    ");

                            TimeSpan prevTime = new TimeSpan();
                            for (int track = 1; track <= dumpFile.Header.AudioChapters.Length; track++)
                            {
                                uint off = dumpFile.GetHighestPage();

                                if (track < dumpFile.Header.AudioChapters.Length)
                                {
                                    off = dumpFile.Header.AudioChapters[track];
                                }
                                ulong  granule      = dumpFile.GetGranuleByPage(off);
                                string lengthString = "@" + off;

                                if (granule != ulong.MaxValue)
                                {
                                    TimeSpan trackOffset = TimeSpan.FromSeconds(granule / 48000.0f);
                                    lengthString = (trackOffset - prevTime).ToString(@"mm\:ss\.ff");
                                    prevTime     = trackOffset;
                                }

                                string title = "";

                                if (titles != null && track - 1 < titles.Length)
                                {
                                    title = titles[track - 1];
                                }
                                Console.WriteLine("    Track #" + track.ToString("00") + "  " + lengthString + "  " + title);
                            }

                            Console.WriteLine("  Ogg: Segments       " + segCount + " (min: " + minSegs + " max: " + maxSegs + " per OggPage)");
                            Console.WriteLine("  Ogg: net payload    " + segLength + " byte");
                            Console.WriteLine("  Ogg: granules       total: " + highestGranule + " (" + TonieAudio.FormatGranule(highestGranule) + " hh:mm:ss.ff)");
                            Console.WriteLine("  Ogg: granules/page  min: " + minGranule + " max: " + maxGranule + " (" + (1000 * minGranule / 48000.0f) + "ms - " + (1000 * maxGranule / 48000.0f) + "ms)");
                            Console.WriteLine();
                            break;
                        }

                        case eDumpFormat.FormatJSON:
                        {
                            if (!first)
                            {
                                Console.WriteLine(",");
                            }
                            Console.WriteLine("  {");
                            Console.WriteLine("    \"uid\": \"" + uid + "\",");
                            Console.WriteLine("    \"audio_id\": \"" + dumpFile.Header.AudioId.ToString("X8") + "\",");
                            Console.WriteLine("    \"audio_date\": \"" + date.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss") + "\",");
                            Console.WriteLine("    \"header_length\": " + dumpFile.HeaderLength + ",");
                            Console.WriteLine("    \"header_ok\": \"" + ((dumpFile.HeaderLength != 0xFFC) ? "FALSE" : "TRUE") + "\",");
                            Console.WriteLine("    \"padding\": " + dumpFile.Header.Padding.Length + ",");
                            Console.WriteLine("    \"audio_length\": " + dumpFile.Header.AudioLength + ",");
                            Console.WriteLine("    \"audio_length_check\": \"" + (dumpFile.Header.AudioLength == dumpFile.Audio.Length ? "TRUE" : "FALSE") + "\",");
                            Console.WriteLine("    \"audio_hash\": \"" + BitConverter.ToString(dumpFile.Header.Hash).Replace("-", "") + "\",");
                            Console.WriteLine("    \"audio_hash_check\": \"" + (dumpFile.HashCorrect ? "TRUE" : "FALSE") + "\",");
                            Console.Write("    \"chapters\": [ 0");
                            foreach (var offset in dumpFile.Header.AudioChapters)
                            {
                                Console.Write(", " + offset);
                            }
                            Console.WriteLine(" ],");
                            Console.WriteLine("    \"segments\": " + segCount + ",");
                            Console.WriteLine("    \"min_segments_per_page\": " + minSegs + ",");
                            Console.WriteLine("    \"max_segments_per_page\": " + maxSegs + ",");
                            Console.WriteLine("    \"segment_length_sum\": " + segLength + ",");
                            Console.WriteLine("    \"highest_granule\": " + highestGranule + ",");
                            Console.WriteLine("    \"time\": \"" + TonieAudio.FormatGranule(highestGranule) + "\",");
                            Console.WriteLine("    \"min_granules\": " + minGranule + ",");
                            Console.WriteLine("    \"max_granules\": " + maxGranule + ",");
                            Console.WriteLine("    \"min_time\": " + (1000 * minGranule / 48000.0f) + ",");
                            Console.WriteLine("    \"max_time\": " + (1000 * maxGranule / 48000.0f) + "");
                            Console.WriteLine("  }");

                            break;
                        }
                        }
                    }
                    catch (FileNotFoundException ex)
                    {
                        Console.WriteLine("File not found: " + file);
                    }
                    catch (InvalidDataException ex)
                    {
                        Console.WriteLine("File corrupt: " + file);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine();
                        Console.WriteLine("[ERROR] Failed to process '" + file + "'");
                        Console.WriteLine("   Exception:  " + e.GetType());
                        Console.WriteLine("   Message:    " + e.Message);
                        Console.WriteLine("   Stacktrace: " + e.StackTrace);
                    }

                    if (first)
                    {
                        first = false;
                    }
                }

                switch (dumpFormat)
                {
                case eDumpFormat.FormatCSV:
                    break;

                case eDumpFormat.FormatJSON:
                    Console.WriteLine("]");
                    break;

                case eDumpFormat.FormatText:
                    break;
                }
                break;
            }

            case "decode":
            {
                if (extra.Count < 1 || (!Directory.Exists(extra[0]) && !File.Exists(extra[0])))
                {
                    Console.WriteLine("Error: You must specify a file");
                    return;
                }

                List <string> files = new List <string>();

                foreach (string file in extra)
                {
                    if (Directory.Exists(file))
                    {
                        FindTonieFiles(files, file);
                    }
                    else
                    {
                        if (!File.Exists(file))
                        {
                            Console.WriteLine("Error: file '" + file + "' does not exist");
                            return;
                        }
                        files.Add(file);
                    }
                }

                Console.WriteLine("[Mode: decode file]");

                foreach (string file in files.ToArray())
                {
                    try
                    {
                        Console.WriteLine("Dumping '" + file + "'");
                        TonieAudio dump2        = TonieAudio.FromFile(file);
                        string     inFile       = new FileInfo(file).Name;
                        string     inDir        = new FileInfo(file).DirectoryName;
                        string     outDirectory = !string.IsNullOrEmpty(outputLocation) ? outputLocation : inDir;

                        if (!Directory.Exists(outDirectory))
                        {
                            Console.WriteLine("Error: Output directory '" + outDirectory + "' does not exist");
                            return;
                        }
                        string outFile = outDirectory + Path.DirectorySeparatorChar + inFile;

                        try
                        {
                            File.WriteAllBytes(outFile + ".ogg", dump2.Audio);
                            File.WriteAllText(outFile + ".cue", BuildCueSheet(dump2), Encoding.UTF8);
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine("[ERROR] Failed to write file '" + outFile + ".occ/.cue'");
                            Console.WriteLine("   Message:    " + ex.Message);
                        }
                        Console.WriteLine("Written content to " + outFile + ".ogg/.cue");
                    }
                    catch (FileNotFoundException ex)
                    {
                        Console.WriteLine("File not found: " + file);
                    }
                    catch (InvalidDataException ex)
                    {
                        Console.WriteLine("File corrupt: " + file);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine();
                        Console.WriteLine("[ERROR] Failed to process '" + file + "'");
                        Console.WriteLine("   Exception:  " + e.GetType());
                        Console.WriteLine("   Message:    " + e.Message);
                        Console.WriteLine("   Stacktrace: " + e.StackTrace);
                    }
                }
                break;
            }

            case "encode":
                Console.WriteLine("[Mode: encode, " + bitRate + " kbps, " + (useVbr ? "VBR" : "CBR") + "]");

                if (extra.Count < 1)
                {
                    Console.WriteLine("Error: You must specify a directory or files to encode");
                    return;
                }

                /*
                 * if ((bitRate % 24) != 0)
                 * {
                 *  Console.WriteLine("Error: You must specify a multiple of 24 kbps, else block alignment in output file would produce incompatible files");
                 *  return;
                 * }*/

                uint id = (uint)DateTimeOffset.Now.ToUnixTimeSeconds();
                if (audioId != "")
                {
                    if (audioId.Trim().StartsWith("0x"))
                    {
                        if (!uint.TryParse(audioId.Replace("0x", ""), System.Globalization.NumberStyles.HexNumber, null, out id))
                        {
                            Console.WriteLine("Error: You must specify the AudioID as hex value like 0x5E034216 or as decimal number like 1577271830");
                            return;
                        }
                    }
                    else
                    {
                        if (!uint.TryParse(audioId, System.Globalization.NumberStyles.Integer, null, out id))
                        {
                            Console.WriteLine("Error: You must specify the AudioID as hex value like 0x5E034216 or as decimal number like 1577271830");
                            return;
                        }
                    }
                }

                try
                {
                    string outLocationEncode = ((!string.IsNullOrEmpty(outputLocation)) ? outputLocation : ".");
                    string outFile;

                    if (!Directory.Exists(outLocationEncode))
                    {
                        string baseDirOutFile = new FileInfo(outLocationEncode).DirectoryName;
                        if (!Directory.Exists(baseDirOutFile))
                        {
                            Console.WriteLine("Error: Specified output directory '" + outLocationEncode + "' does not exist and file '" + baseDirOutFile + "' not reachable.");
                            return;
                        }
                        outFile = outLocationEncode;
                    }
                    else
                    {
                        outFile = outLocationEncode + Path.DirectorySeparatorChar + "500304E0";
                    }

                    TonieAudio generated = new TonieAudio(extra.ToArray(), id, bitRate * 1000, useVbr, prefixLocation);
                    try
                    {
                        File.WriteAllBytes(outFile, generated.FileContent);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("[ERROR] Failed to write file '" + outFile + "'");
                        Console.WriteLine("   Message:    " + ex.Message);
                    }

                    Console.WriteLine("");
                    Console.WriteLine("Written content to " + outFile);
                }
                catch (FileNotFoundException ex)
                {
                    Console.WriteLine("[ERROR] Failed to process due to a missing file");
                    Console.WriteLine("   Message:    " + ex.Message);
                }
                catch (InvalidDataException ex)
                {
                    Console.WriteLine("[ERROR] Failed to process due to an invalid file");
                    Console.WriteLine("   Message:    " + ex.Message);
                }
                catch (TonieAudio.EncodingException ex)
                {
                    Console.WriteLine("[ERROR] Failed to encode audio");
                    Console.WriteLine("   Message:    " + ex.Message);
                }
                catch (Exception e)
                {
                    Console.WriteLine();
                    Console.WriteLine("[ERROR] Failed to process");
                    Console.WriteLine("   Exception:  " + e.GetType());
                    Console.WriteLine("   Message:    " + e.Message);
                    Console.WriteLine("   Stacktrace: " + e.StackTrace);
                }
                break;
            }
        }
예제 #3
0
파일: Program.cs 프로젝트: l-y-n-x/teddy
        static void Main(string[] args)
        {
            bool showLicense = false;
            bool showHelp    = false;

            TonieTools.eDumpFormat dumpFormat = TonieTools.eDumpFormat.FormatText;
            bool   useVbr           = false;
            bool   reallyRename     = false;
            bool   deleteDuplicates = false;
            bool   singleOgg        = false;
            string mode             = "";
            string outputLocation   = "";
            string prefixLocation   = null;
            string audioId          = "";
            string writeJson        = null;
            string jsonFile         = "http://gt-blog.de/JSON/tonies.json?source=Teddy&version=" + ThisAssembly.Git.BaseTag;

            int bitRate = 96;

            var p = new OptionSet {
                { "m|mode=", "Operating mode: info, decode, encode, rename", (string n) => mode = n },
                { "o|output=", "Location where to write the file(s) to", (string r) => outputLocation = r },
                { "p|prefix=", "encode: Location where to find prefix files", (string r) => prefixLocation = r },
                { "i|id=", "encode: Set AudioID for encoding (default: current time)", (string r) => audioId = r },
                { "b|bitrate=", "encode: Set opus bit rate (default: " + bitRate + " kbps)", (int r) => bitRate = r },
                { "vbr", "encode: Use VBR encoding", r => useVbr = true },
                { "s", "decode: Export as single .ogg file", r => singleOgg = true },
                { "y", "rename: really rename files, else its a dry run", v => { reallyRename = true; } },
                { "d", "rename: delete duplicates", v => { deleteDuplicates = true; } },
                { "w|write=", "info: write updated json to local file", (string v) => { writeJson = v; } },
                { "j|json=", "Set JSON file/URL with details about tonies", (string r) => jsonFile = r },
                { "f|format=", "Output details as: csv, json or text", v => { switch (v)
                                                                              {
                                                                              case "csv":  dumpFormat = TonieTools.eDumpFormat.FormatCSV; break;

                                                                              case "json":  dumpFormat = TonieTools.eDumpFormat.FormatJSON; break;

                                                                              case "text":  dumpFormat = TonieTools.eDumpFormat.FormatText; break;
                                                                              }
                  } },
                { "v", "increase debug message verbosity", v => { if (v != null)
                                                                  {
                                                                      ++Verbosity;
                                                                  }
                  } },
                { "h|help", "show this message and exit", h => showHelp = true },
                { "license", "show licenses and disclaimer", h => showLicense = true },
            };

            List <string> extra;

            try
            {
                extra = p.Parse(args);
            }
            catch (OptionException e)
            {
                Console.Write("Teddy.exe: ");
                Console.WriteLine(e.Message);
                Console.WriteLine("Try `Teddy.exe --help' for more information.");
                return;
            }

            if (showLicense)
            {
                ShowLicense(p);
                return;
            }

            if (showHelp)
            {
                ShowHelp(p);
                return;
            }

            LoadJson(jsonFile);

            switch (mode)
            {
            default:
                ShowHelp(p);
                return;

            case "rename":
            {
                Dictionary <string, string> renameList = new Dictionary <string, string>();
                List <string> files = new List <string>();

                if (extra.Count < 2 || (!Directory.Exists(extra[0]) && !Directory.Exists(extra[1])))
                {
                    Console.WriteLine("Error: You must specify a source and a destination directory");
                    return;
                }

                FindTonieFiles(files, extra[0]);

                Console.WriteLine(" Scan files...");
                foreach (string file in files.ToArray())
                {
                    try
                    {
                        TonieAudio dumpFile = TonieAudio.FromFile(file);

                        /* skip creative tonies for now */
                        if (dumpFile.Header.AudioId == 1)
                        {
                            Console.WriteLine("  '" + file + "' -> '(creative)'");
                            continue;
                        }

                        var    found        = TonieInfos.Where(t => t.AudioIds.Contains(dumpFile.Header.AudioId));
                        string destFileName = "(unknown)";

                        if (found.Count() > 0)
                        {
                            var info = found.First();
                            destFileName = info.Title;
                            if (!string.IsNullOrEmpty(info.Model))
                            {
                                string destName = Path.Combine(extra[1], info.Model + " - " + dumpFile.Header.AudioId.ToString("X8") + " - " + RemoveInvalidChars(destFileName).Trim());
                                if (file != destName)
                                {
                                    renameList.Add(file, destName);
                                }
                            }
                        }

                        Console.WriteLine("  '" + file + "' -> '" + destFileName + "'");
                    }
                    catch (FileNotFoundException ex)
                    {
                        Console.WriteLine("File not found: " + file);
                    }
                    catch (InvalidDataException ex)
                    {
                        Console.WriteLine("  '" + file + "' -> (corrupt)");
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine();
                        Console.WriteLine("[ERROR] Failed to process '" + file + "'");
                        Console.WriteLine("   Exception:  " + e.GetType());
                        Console.WriteLine("   Message:    " + e.Message);
                        Console.WriteLine("   Stacktrace: " + e.StackTrace);
                    }
                }

                Console.WriteLine("");
                Console.WriteLine(" Rename files...");
                int renamed = 0;
                foreach (var key in renameList.Keys)
                {
                    string dest = renameList[key];

                    Console.WriteLine("  Rename '" + key + "' -> '" + dest + "'");
                    if (File.Exists(dest))
                    {
                        if (FileChecksum(dest) == FileChecksum(key))
                        {
                            if (deleteDuplicates)
                            {
                                if (reallyRename)
                                {
                                    var di = new FileInfo(key).Directory;
                                    File.Delete(key);
                                    renamed++;
                                    if (di.GetFiles().Length == 0)
                                    {
                                        di.Delete();
                                    }
                                }
                            }
                            else
                            {
                                Console.WriteLine("    Skipped, destination file content matches");
                            }
                        }
                        else
                        {
                            Console.WriteLine("    Skipped, destination file already exists but has different content");
                        }
                        continue;
                    }

                    if (reallyRename)
                    {
                        var di = new FileInfo(key).Directory;

                        try
                        {
                            File.Move(key, dest);
                            renamed++;
                            if (di.GetFiles().Length == 0)
                            {
                                di.Delete();
                            }
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine("[ERROR] Failed to rename '" + key + "'");
                        }
                    }
                }
                Console.WriteLine("");
                Console.WriteLine(" Renamed " + renamed + " files");

                break;
            }


            case "info":
            {
                if (extra.Count < 1 || (!Directory.Exists(extra[0]) && !File.Exists(extra[0])))
                {
                    Console.WriteLine("Error: You must specify a file");
                    return;
                }

                switch (dumpFormat)
                {
                case TonieTools.eDumpFormat.FormatCSV:
                    if (extra.Count > 1 || Directory.Exists(extra[0]))
                    {
                        Console.WriteLine("UID;AudioID;AudioDate;HeaderLength;HeaderOK;Padding;AudioLength;AudioLengthCheck;AudioHash;AudioHashCheck;Chapters;Segments;MinSegmentsPerPage;MaxSegmentsPerPage;SegLengthSum;HighestGranule;Time;MinGranules;MaxGranules;MinTime;MaxTime;");
                    }
                    break;

                case TonieTools.eDumpFormat.FormatJSON:
                    Console.WriteLine("[");
                    break;

                case TonieTools.eDumpFormat.FormatText:
                    Console.WriteLine("[Mode: dump information]");
                    break;
                }

                List <string> files = new List <string>();

                foreach (string file in extra)
                {
                    if (Directory.Exists(file))
                    {
                        FindTonieFiles(files, file);
                    }
                    else
                    {
                        if (!File.Exists(file))
                        {
                            Console.WriteLine("Error: file '" + file + "' does not exist");
                            return;
                        }
                        files.Add(file);
                    }
                }

                bool first = true;
                foreach (string file in files.ToArray())
                {
                    try
                    {
                        StringBuilder message = new StringBuilder();

                        TonieTools.DumpInfo(message, dumpFormat, file, TonieInfos);

                        Console.Write(message.ToString());
                    }
                    catch (FileNotFoundException ex)
                    {
                        Console.WriteLine("File not found: " + file);
                    }
                    catch (InvalidDataException ex)
                    {
                        Console.WriteLine("File corrupt: " + file);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine();
                        Console.WriteLine("[ERROR] Failed to process '" + file + "'");
                        Console.WriteLine("   Exception:  " + e.GetType());
                        Console.WriteLine("   Message:    " + e.Message);
                        Console.WriteLine("   Stacktrace: " + e.StackTrace);
                    }

                    if (first)
                    {
                        first = false;
                    }
                }

                switch (dumpFormat)
                {
                case TonieTools.eDumpFormat.FormatCSV:
                    break;

                case TonieTools.eDumpFormat.FormatJSON:
                    Console.WriteLine("]");
                    break;

                case TonieTools.eDumpFormat.FormatText:
                    break;
                }

                if (writeJson != null)
                {
                    SaveJson(writeJson);
                }
                break;
            }

            case "decode":
            {
                if (extra.Count < 1 || (!Directory.Exists(extra[0]) && !File.Exists(extra[0])))
                {
                    Console.WriteLine("Error: You must specify a file");
                    return;
                }

                List <string> files = new List <string>();

                foreach (string file in extra)
                {
                    if (Directory.Exists(file))
                    {
                        FindTonieFiles(files, file);
                    }
                    else
                    {
                        if (!File.Exists(file))
                        {
                            Console.WriteLine("Error: file '" + file + "' does not exist");
                            return;
                        }
                        files.Add(file);
                    }
                }

                Console.WriteLine("[Mode: decode file]");

                foreach (string file in files.ToArray())
                {
                    try
                    {
                        TonieAudio dump2 = TonieAudio.FromFile(file);

                        string[]      titles = null;
                        List <string> tags   = new List <string>();
                        tags.Add("TeddyVersion=" + GetVersion());
                        tags.Add("TeddyFile=" + file);

                        string hashString         = BitConverter.ToString(dump2.Header.Hash).Replace("-", "");
                        var    found              = TonieInfos.Where(t => t.Hash.Contains(hashString));
                        TonieTools.TonieData info = null;

                        if (found.Count() > 0)
                        {
                            info   = found.First();
                            titles = info.Tracks;
                            tags.Add("ALBUM=" + info.Title);
                            tags.Add("ARTIST=" + info.Series);
                            tags.Add("LANGUAGE=" + info.Language);
                        }
                        tags.Add("HASH=" + hashString);

                        string inFile       = new FileInfo(file).Name;
                        string inDir        = new FileInfo(file).DirectoryName;
                        string outDirectory = !string.IsNullOrEmpty(outputLocation) ? outputLocation : inDir;

                        if (!Directory.Exists(outDirectory))
                        {
                            Console.WriteLine("Error: Output directory '" + outDirectory + "' does not exist");
                            return;
                        }

                        try
                        {
                            dump2.DumpAudioFiles(outDirectory, inFile + "-" + dump2.Header.AudioId.ToString("X8"), singleOgg, tags.ToArray(), titles);
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine("[ERROR] Failed to write .ogg/.cue'");
                            Console.WriteLine("   Message:    " + ex.Message);
                        }
                    }
                    catch (FileNotFoundException ex)
                    {
                        Console.WriteLine("File not found: " + file);
                    }
                    catch (InvalidDataException ex)
                    {
                        Console.WriteLine("File corrupt: " + file);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine();
                        Console.WriteLine("[ERROR] Failed to process '" + file + "'");
                        Console.WriteLine("   Exception:  " + e.GetType());
                        Console.WriteLine("   Message:    " + e.Message);
                        Console.WriteLine("   Stacktrace: " + e.StackTrace);
                    }
                }
                break;
            }

            case "encode":
                Console.WriteLine("[Mode: encode, " + bitRate + " kbps, " + (useVbr ? "VBR" : "CBR") + "]");

                if (extra.Count < 1)
                {
                    Console.WriteLine("Error: You must specify a directory or files to encode");
                    return;
                }

                /*
                 * if ((bitRate % 24) != 0)
                 * {
                 *  Console.WriteLine("Error: You must specify a multiple of 24 kbps, else block alignment in output file would produce incompatible files");
                 *  return;
                 * }*/

                uint id = (uint)DateTimeOffset.Now.ToUnixTimeSeconds() - 0x50000000;
                if (audioId != "")
                {
                    if (audioId.Trim().StartsWith("0x"))
                    {
                        if (!uint.TryParse(audioId.Replace("0x", ""), System.Globalization.NumberStyles.HexNumber, null, out id))
                        {
                            Console.WriteLine("Error: You must specify the AudioID as hex value like 0x5E034216 or as decimal number like 1577271830");
                            return;
                        }
                    }
                    else
                    {
                        if (!uint.TryParse(audioId, System.Globalization.NumberStyles.Integer, null, out id))
                        {
                            Console.WriteLine("Error: You must specify the AudioID as hex value like 0x5E034216 or as decimal number like 1577271830");
                            return;
                        }
                    }
                }

                try
                {
                    string outLocationEncode = ((!string.IsNullOrEmpty(outputLocation)) ? outputLocation : ".");
                    string outFile;

                    if (!Directory.Exists(outLocationEncode))
                    {
                        string baseDirOutFile = new FileInfo(outLocationEncode).DirectoryName;
                        if (!Directory.Exists(baseDirOutFile))
                        {
                            Console.WriteLine("Error: Specified output directory '" + outLocationEncode + "' does not exist and file '" + baseDirOutFile + "' not reachable.");
                            return;
                        }
                        outFile = outLocationEncode;
                    }
                    else
                    {
                        outFile = Path.Combine(outLocationEncode, "500304E0");
                    }

                    TonieAudio generated = new TonieAudio(extra.ToArray(), id, bitRate * 1000, useVbr, prefixLocation);
                    try
                    {
                        File.WriteAllBytes(outFile, generated.FileContent);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("[ERROR] Failed to write file '" + outFile + "'");
                        Console.WriteLine("   Message:    " + ex.Message);
                    }

                    Console.WriteLine("");
                    Console.WriteLine("Written content to " + outFile);
                }
                catch (FileNotFoundException ex)
                {
                    Console.WriteLine("[ERROR] Failed to process due to a missing file");
                    Console.WriteLine("   Message:    " + ex.Message);
                }
                catch (InvalidDataException ex)
                {
                    Console.WriteLine("[ERROR] Failed to process due to an invalid file");
                    Console.WriteLine("   Message:    " + ex.Message);
                }
                catch (TonieAudio.EncodingException ex)
                {
                    Console.WriteLine("[ERROR] Failed to encode audio");
                    Console.WriteLine("   Message:    " + ex.Message);
                }
                catch (Exception e)
                {
                    Console.WriteLine();
                    Console.WriteLine("[ERROR] Failed to process");
                    Console.WriteLine("   Exception:  " + e.GetType());
                    Console.WriteLine("   Message:    " + e.Message);
                    Console.WriteLine("   Stacktrace: " + e.StackTrace);
                }
                break;
            }
        }