Пример #1
0
        public static void Build(CsbProject project, string outputFileName)
        {
            CriCpkArchive cpkArchive = new CriCpkArchive();

            DirectoryInfo outputDirectory = new DirectoryInfo(Path.GetDirectoryName(outputFileName));

            List <SerializationCueSheetTable> cueSheetTables = new List <SerializationCueSheetTable>();

            SerializationVersionInfoTable versionInfoTable = new SerializationVersionInfoTable();

            cueSheetTables.Add(new SerializationCueSheetTable
            {
                TableData = CriTableSerializer.Serialize(new List <SerializationVersionInfoTable>()
                {
                    versionInfoTable
                }, CriTableWriterSettings.AdxSettings),
                Name      = "INFO",
                TableType = 7,
            });

            // Serialize cues.
            List <SerializationCueTable> cueTables = new List <SerializationCueTable>();

            foreach (BuilderCueNode cueNode in project.CueNodes)
            {
                cueTables.Add(new SerializationCueTable
                {
                    Name      = cueNode.Name,
                    Id        = cueNode.Identifier,
                    UserData  = cueNode.UserComment,
                    Flags     = cueNode.Flags,
                    SynthPath = cueNode.SynthReference,
                });
            }

            cueSheetTables.Add(new SerializationCueSheetTable
            {
                TableData = CriTableSerializer.Serialize(cueTables, CriTableWriterSettings.AdxSettings),
                Name      = "CUE",
                TableType = 1,
            });

            // Serialize synth tables.
            List <SerializationSynthTable> synthTables = new List <SerializationSynthTable>();

            foreach (BuilderSynthNode synthNode in project.SynthNodes)
            {
                SerializationSynthTable synthTable = new SerializationSynthTable
                {
                    SynthName           = synthNode.Name,
                    SynthType           = (byte)synthNode.Type,
                    ComplexType         = (byte)synthNode.PlaybackType,
                    Volume              = synthNode.Volume,
                    Pitch               = synthNode.Pitch,
                    DelayTime           = synthNode.DelayTime,
                    SControl            = synthNode.SControl,
                    EgDelay             = synthNode.EgDelay,
                    EgAttack            = synthNode.EgAttack,
                    EgHold              = synthNode.EgHold,
                    EgDecay             = synthNode.EgDecay,
                    EgRelease           = synthNode.EgRelease,
                    EgSustain           = synthNode.EgSustain,
                    FType               = synthNode.FilterType,
                    FCof1               = synthNode.FilterCutoff1,
                    FCof2               = synthNode.FilterCutoff2,
                    FReso               = synthNode.FilterReso,
                    FReleaseOffset      = synthNode.FilterReleaseOffset,
                    DryOName            = synthNode.DryOName,
                    Mtxrtr              = synthNode.Mtxrtr,
                    Dry0                = synthNode.Dry0,
                    Dry1                = synthNode.Dry1,
                    Dry2                = synthNode.Dry2,
                    Dry3                = synthNode.Dry3,
                    Dry4                = synthNode.Dry4,
                    Dry5                = synthNode.Dry5,
                    Dry6                = synthNode.Dry6,
                    Dry7                = synthNode.Dry7,
                    WetOName            = synthNode.WetOName,
                    Wet0                = synthNode.Wet0,
                    Wet1                = synthNode.Wet1,
                    Wet2                = synthNode.Wet2,
                    Wet3                = synthNode.Wet3,
                    Wet4                = synthNode.Wet4,
                    Wet5                = synthNode.Wet5,
                    Wet6                = synthNode.Wet6,
                    Wet7                = synthNode.Wet7,
                    Wcnct0              = synthNode.Wcnct0,
                    Wcnct1              = synthNode.Wcnct1,
                    Wcnct2              = synthNode.Wcnct2,
                    Wcnct3              = synthNode.Wcnct3,
                    Wcnct4              = synthNode.Wcnct4,
                    Wcnct5              = synthNode.Wcnct5,
                    Wcnct6              = synthNode.Wcnct6,
                    Wcnct7              = synthNode.Wcnct7,
                    VoiceLimitGroupName = synthNode.VoiceLimitGroupReference,
                    VoiceLimitType      = synthNode.VoiceLimitType,
                    VoiceLimitPriority  = synthNode.VoiceLimitPriority,
                    VoiceLimitPhTime    = synthNode.VoiceLimitProhibitionTime,
                    VoiceLimitPcdlt     = synthNode.VoiceLimitPcdlt,
                    Pan3dVolumeOffset   = synthNode.Pan3dVolumeOffset,
                    Pan3dVolumeGain     = synthNode.Pan3dVolumeGain,
                    Pan3dAngleOffset    = synthNode.Pan3dAngleOffset,
                    Pan3dAngleGain      = synthNode.Pan3dAngleGain,
                    Pan3dDistanceOffset = synthNode.Pan3dDistanceOffset,
                    Pan3dDistanceGain   = synthNode.Pan3dDistanceGain,
                    Dry0g               = synthNode.Dry0g,
                    Dry1g               = synthNode.Dry1g,
                    Dry2g               = synthNode.Dry2g,
                    Dry3g               = synthNode.Dry3g,
                    Dry4g               = synthNode.Dry4g,
                    Dry5g               = synthNode.Dry5g,
                    Dry6g               = synthNode.Dry6g,
                    Dry7g               = synthNode.Dry7g,
                    Wet0g               = synthNode.Wet0g,
                    Wet1g               = synthNode.Wet1g,
                    Wet2g               = synthNode.Wet2g,
                    Wet3g               = synthNode.Wet3g,
                    Wet4g               = synthNode.Wet4g,
                    Wet5g               = synthNode.Wet5g,
                    Wet6g               = synthNode.Wet6g,
                    Wet7g               = synthNode.Wet7g,
                    F1Type              = synthNode.Filter1Type,
                    F1CofOffset         = synthNode.Filter1CutoffOffset,
                    F1CofGain           = synthNode.Filter1CutoffGain,
                    F1ResoOffset        = synthNode.Filter1ResoOffset,
                    F1ResoGain          = synthNode.Filter1ResoGain,
                    F2Type              = synthNode.Filter2Type,
                    F2CofLowOffset      = synthNode.Filter2CutoffLowerOffset,
                    F2CofLowGain        = synthNode.Filter2CutoffLowerGain,
                    F2CofHighOffset     = synthNode.Filter2CutoffHigherOffset,
                    F2CofHighGain       = synthNode.Filter2CutoffHigherGain,
                    Probability         = synthNode.PlaybackProbability,
                    NumberLmtChildren   = synthNode.NLmtChildren,
                    Repeat              = synthNode.Repeat,
                    ComboTime           = synthNode.ComboTime,
                    ComboLoopBack       = synthNode.ComboLoopBack,
                };

                if (synthNode.Type == BuilderSynthType.Single)
                {
                    synthTable.LinkName = synthNode.SoundElementReference;
                }

                else if (synthNode.Type == BuilderSynthType.WithChildren)
                {
                    foreach (string trackReference in synthNode.Children)
                    {
                        synthTable.LinkName += trackReference + (char)0x0A;
                    }
                }

                if (!string.IsNullOrEmpty(synthNode.AisacReference))
                {
                    BuilderAisacNode aisacNode = project.AisacNodes.First(aisac => aisac.Name == synthNode.AisacReference);
                    synthTable.AisacSetName = aisacNode.AisacName + "::" + aisacNode.Name + (char)0x0A;
                }

                synthTables.Add(synthTable);
            }

            cueSheetTables.Add(new SerializationCueSheetTable
            {
                TableData = CriTableSerializer.Serialize(synthTables, CriTableWriterSettings.AdxSettings),
                Name      = "SYNTH",
                TableType = 2,
            });

            List <FileInfo> junks = new List <FileInfo>();

            // Serialize the sound elements.
            List <SerializationSoundElementTable> soundElementTables = new List <SerializationSoundElementTable>();

            foreach (BuilderSoundElementNode soundElementNode in project.SoundElementNodes)
            {
                CriAaxArchive aaxArchive = new CriAaxArchive();

                if (!string.IsNullOrEmpty(soundElementNode.Intro))
                {
                    aaxArchive.Add(new CriAaxEntry
                    {
                        Flag     = CriAaxEntryFlag.Intro,
                        FilePath = new FileInfo(Path.Combine(project.AudioDirectory.FullName, soundElementNode.Intro)),
                    });

                    aaxArchive.SetModeFromExtension(soundElementNode.Intro);
                }

                if (!string.IsNullOrEmpty(soundElementNode.Loop))
                {
                    aaxArchive.Add(new CriAaxEntry
                    {
                        Flag     = CriAaxEntryFlag.Loop,
                        FilePath = new FileInfo(Path.Combine(project.AudioDirectory.FullName, soundElementNode.Loop)),
                    });

                    aaxArchive.SetModeFromExtension(soundElementNode.Loop);
                }

                byte[] data = new byte[0];

                if (soundElementNode.Streaming)
                {
                    CriCpkEntry entry = new CriCpkEntry();
                    entry.Name           = Path.GetFileName(soundElementNode.Name);
                    entry.DirectoryName  = Path.GetDirectoryName(soundElementNode.Name);
                    entry.Id             = (uint)cpkArchive.Count;
                    entry.UpdateDateTime = DateTime.Now;
                    entry.FilePath       = new FileInfo(Path.GetTempFileName());
                    cpkArchive.Add(entry);

                    aaxArchive.Save(entry.FilePath.FullName);
                    junks.Add(entry.FilePath);
                }

                else
                {
                    data = aaxArchive.Save();
                }

                soundElementTables.Add(new SerializationSoundElementTable
                {
                    Name           = soundElementNode.Name,
                    Data           = data,
                    FormatType     = (byte)aaxArchive.Mode,
                    SoundFrequency = soundElementNode.SampleRate,
                    NumberChannels = soundElementNode.ChannelCount,
                    Streaming      = soundElementNode.Streaming,
                    NumberSamples  = soundElementNode.SampleCount,
                });
            }

            cueSheetTables.Add(new SerializationCueSheetTable
            {
                TableData = CriTableSerializer.Serialize(soundElementTables, CriTableWriterSettings.AdxSettings),
                Name      = "SOUND_ELEMENT",
                TableType = 4,
            });

            // Serialize the aisacs.
            List <SerializationAisacTable> aisacTables = new List <SerializationAisacTable>();

            foreach (BuilderAisacNode aisacNode in project.AisacNodes)
            {
                List <SerializationAisacGraphTable> graphTables = new List <SerializationAisacGraphTable>();
                foreach (BuilderAisacGraphNode graphNode in aisacNode.Graphs)
                {
                    List <SerializationAisacPointTable> pointTables = new List <SerializationAisacPointTable>();
                    foreach (BuilderAisacPointNode pointNode in graphNode.Points)
                    {
                        pointTables.Add(new SerializationAisacPointTable
                        {
                            In  = pointNode.X,
                            Out = pointNode.Y,
                        });
                    }

                    graphTables.Add(new SerializationAisacGraphTable
                    {
                        Points = CriTableSerializer.Serialize(pointTables, CriTableWriterSettings.AdxSettings),
                        Type   = graphNode.Type,
                        InMax  = graphNode.MaximumX,
                        InMin  = graphNode.MinimumX,
                        OutMax = graphNode.MaximumY,
                        OutMin = graphNode.MinimumY,
                    });
                }

                aisacTables.Add(new SerializationAisacTable
                {
                    Graph       = CriTableSerializer.Serialize(graphTables, CriTableWriterSettings.AdxSettings),
                    Name        = aisacNode.AisacName,
                    PathName    = aisacNode.Name,
                    Type        = aisacNode.Type,
                    RandomRange = aisacNode.RandomRange,
                });
            }

            cueSheetTables.Add(new SerializationCueSheetTable
            {
                TableData = CriTableSerializer.Serialize(aisacTables, CriTableWriterSettings.AdxSettings),
                Name      = "ISAAC",
                TableType = 5,
            });

            // Serialize the voice limit groups.
            List <SerializationVoiceLimitGroupTable> voiceLimitGroupTables = new List <SerializationVoiceLimitGroupTable>();

            foreach (BuilderVoiceLimitGroupNode voiceLimitGroupNode in project.VoiceLimitGroupNodes)
            {
                voiceLimitGroupTables.Add(new SerializationVoiceLimitGroupTable
                {
                    VoiceLimitGroupName = voiceLimitGroupNode.Name,
                    VoiceLimitGroupNum  = voiceLimitGroupNode.MaxAmountOfInstances,
                });
            }

            cueSheetTables.Add(new SerializationCueSheetTable
            {
                TableData = CriTableSerializer.Serialize(voiceLimitGroupTables, CriTableWriterSettings.AdxSettings),
                Name      = "VOICE_LIMIT_GROUP",
                TableType = 6,
            });

            // Finally, serialize the CSB file.
            CriTableSerializer.Serialize(outputFileName, cueSheetTables, CriTableWriterSettings.AdxSettings, MainForm.Settings.BufferSize);

            if (cpkArchive.Count > 0)
            {
                cpkArchive.Save(Path.ChangeExtension(outputFileName, "cpk"), MainForm.Settings.BufferSize);
            }

            foreach (FileInfo junk in junks)
            {
                junk.Delete();
            }
        }
Пример #2
0
        static void Main(string[] args)
        {
            Settings.Default.Save();

            if (args.Length < 1)
            {
                Console.WriteLine(Resources.Description);
                Console.ReadLine();
                return;
            }

#if !DEBUG
            try
            {
#endif
            if (args[0].EndsWith(".csb", StringComparison.OrdinalIgnoreCase))
            {
                var extractor = new DataExtractor();
                extractor.ProgressChanged += OnProgressChanged;

                extractor.BufferSize      = Settings.Default.BufferSize;
                extractor.EnableThreading = Settings.Default.EnableThreading;
                extractor.MaxThreads      = Settings.Default.MaxThreads;

                string baseDirectory       = Path.GetDirectoryName(args[0]);
                string outputDirectoryName = Path.Combine(baseDirectory, Path.GetFileNameWithoutExtension(args[0]));

                CriCpkArchive cpkArchive = null;
                string        cpkPath    = outputDirectoryName + ".cpk";
                bool          found      = File.Exists(cpkPath);

                //This should fix "File not found" error in case-sensitive file systems.
                //Add new extensions when necessary.
                foreach (string extension in new string[] { "cpk", "CPK" })
                {
                    if (found)
                    {
                        break;
                    }

                    cpkPath = outputDirectoryName + "." + extension;
                    found   = File.Exists(cpkPath);
                }

                using (CriTableReader reader = CriTableReader.Create(args[0]))
                {
                    while (reader.Read())
                    {
                        if (reader.GetString("name") == "SOUND_ELEMENT")
                        {
                            long tablePosition = reader.GetPosition("utf");
                            using (CriTableReader sdlReader = CriTableReader.Create(reader.GetSubStream("utf")))
                            {
                                while (sdlReader.Read())
                                {
                                    if (sdlReader.GetByte("fmt") != 0)
                                    {
                                        throw new Exception("The given CSB file contains an audio file which is not an ADX. Only CSB files with ADXs are supported.");
                                    }

                                    bool streaming = sdlReader.GetBoolean("stmflg");
                                    if (streaming && !found)
                                    {
                                        throw new Exception("Cannot find the external .CPK file for this .CSB file. Please ensure that the external .CPK file is stored in the directory where the .CPK file is.");
                                    }

                                    else if (streaming && found && cpkArchive == null)
                                    {
                                        cpkArchive = new CriCpkArchive();
                                        cpkArchive.Load(cpkPath, Settings.Default.BufferSize);
                                    }

                                    string        sdlName         = sdlReader.GetString("name");
                                    DirectoryInfo destinationPath = new DirectoryInfo(Path.Combine(outputDirectoryName, sdlName));
                                    destinationPath.Create();

                                    CriAaxArchive aaxArchive = new CriAaxArchive();

                                    if (streaming)
                                    {
                                        CriCpkEntry cpkEntry = cpkArchive.GetByPath(sdlName);

                                        if (cpkEntry != null)
                                        {
                                            using (Stream cpkSource = File.OpenRead(cpkPath))
                                                using (Stream aaxSource = cpkEntry.Open(cpkSource))
                                                {
                                                    aaxArchive.Read(aaxSource);

                                                    foreach (CriAaxEntry entry in aaxArchive)
                                                    {
                                                        extractor.Add(cpkPath,
                                                                      Path.Combine(destinationPath.FullName,
                                                                                   entry.Flag == CriAaxEntryFlag.Intro ? "Intro.adx" : "Loop.adx"),
                                                                      cpkEntry.Position + entry.Position, entry.Length);
                                                    }
                                                }
                                        }
                                    }

                                    else
                                    {
                                        long aaxPosition = sdlReader.GetPosition("data");
                                        using (Stream aaxSource = sdlReader.GetSubStream("data"))
                                        {
                                            aaxArchive.Read(aaxSource);

                                            foreach (CriAaxEntry entry in aaxArchive)
                                            {
                                                extractor.Add(args[0],
                                                              Path.Combine(destinationPath.FullName,
                                                                           entry.Flag == CriAaxEntryFlag.Intro ? "Intro.adx" : "Loop.adx"),
                                                              tablePosition + aaxPosition + entry.Position, entry.Length);
                                            }
                                        }
                                    }
                                }
                            }

                            break;
                        }
                    }
                }

                extractor.Run();
            }

            else if (File.GetAttributes(args[0]).HasFlag(FileAttributes.Directory))
            {
                string baseDirectory = Path.GetDirectoryName(args[0]);
                string csbPath       = args[0] + ".csb";

                foreach (string extension in new string[] { "csb", "CSB" })
                {
                    if (File.Exists(csbPath))
                    {
                        break;
                    }
                    csbPath = args[0] + "." + extension;
                }

                if (!File.Exists(csbPath))
                {
                    throw new Exception("Cannot find the .CSB file for this directory. Please ensure that the .CSB file is stored in the directory where this directory is.");
                }

                CriCpkArchive cpkArchive = new CriCpkArchive();
                cpkArchive.ProgressChanged += OnProgressChanged;

                CriTable csbFile = new CriTable();
                csbFile.Load(csbPath, Settings.Default.BufferSize);

                CriRow soundElementRow = csbFile.Rows.First(row => (string)row["name"] == "SOUND_ELEMENT");

                CriTable soundElementTable = new CriTable();
                soundElementTable.Load((byte[])soundElementRow["utf"]);

                List <FileInfo> junks = new List <FileInfo>();

                foreach (CriRow sdlRow in soundElementTable.Rows)
                {
                    string sdlName = (string)sdlRow["name"];

                    DirectoryInfo sdlDirectory = new DirectoryInfo(Path.Combine(args[0], sdlName));

                    if (!sdlDirectory.Exists)
                    {
                        throw new Exception($"Cannot find sound element directory for replacement.\nPath attempt: {sdlDirectory.FullName}");
                    }

                    bool streaming      = (byte)sdlRow["stmflg"] != 0;
                    uint sampleRate     = (uint)sdlRow["sfreq"];
                    byte numberChannels = (byte)sdlRow["nch"];

                    CriAaxArchive aaxArchive = new CriAaxArchive();
                    foreach (FileInfo file in sdlDirectory.GetFiles("*.adx"))
                    {
                        CriAaxEntry entry = new CriAaxEntry();
                        if (file.Name.ToLower(CultureInfo.GetCultureInfo("en-US")) == "intro.adx")
                        {
                            entry.Flag     = CriAaxEntryFlag.Intro;
                            entry.FilePath = file;
                            aaxArchive.Add(entry);

                            ReadAdx(file, out sampleRate, out numberChannels);
                        }

                        else if (file.Name.ToLower(CultureInfo.GetCultureInfo("en-US")) == "loop.adx")
                        {
                            entry.Flag     = CriAaxEntryFlag.Loop;
                            entry.FilePath = file;
                            aaxArchive.Add(entry);

                            ReadAdx(file, out sampleRate, out numberChannels);
                        }
                    }

                    if (streaming)
                    {
                        CriCpkEntry entry = new CriCpkEntry();
                        entry.Name          = Path.GetFileName(sdlName);
                        entry.DirectoryName = Path.GetDirectoryName(sdlName);
                        entry.Id            = (uint)cpkArchive.Count;
                        entry.FilePath      = new FileInfo(Path.GetTempFileName());
                        junks.Add(entry.FilePath);

                        cpkArchive.Add(entry);
                        aaxArchive.Save(entry.FilePath.FullName, Settings.Default.BufferSize);
                    }

                    else
                    {
                        sdlRow["data"] = aaxArchive.Save();
                    }

                    sdlRow["sfreq"] = sampleRate;
                    sdlRow["nch"]   = numberChannels;
                }

                soundElementTable.WriterSettings = CriTableWriterSettings.AdxSettings;
                soundElementRow["utf"]           = soundElementTable.Save();

                csbFile.WriterSettings = CriTableWriterSettings.AdxSettings;
                csbFile.Save(csbPath, Settings.Default.BufferSize);

                if (cpkArchive.Count > 0)
                {
                    string cpkPath = args[0] + ".cpk";
                    foreach (string extension in new string[] { "cpk", "CPK" })
                    {
                        if (File.Exists(args[0] + "." + extension))
                        {
                            cpkPath = args[0] + "." + extension;
                            break;
                        }
                    }

                    cpkArchive.Save(cpkPath, Settings.Default.BufferSize);
                }

                foreach (FileInfo junk in junks)
                {
                    junk.Delete();
                }
            }
#if !DEBUG
        }

        catch (Exception exception)
        {
            MessageBox.Show($"{exception.Message}", "CSB Editor", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
#endif
        }
        // All taken from the code for CsbEditor: https://github.com/blueskythlikesclouds/SonicAudioTools/blob/master/Source/CsbEditor/Program.cs
        public static async Task CSBUnpack(string csbFile)
        {
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

            DataExtractor?extractor = new()
            {
                EnableThreading = false
            };

            string baseDirectory       = Path.GetDirectoryName(csbFile);
            string outputDirectoryName = Path.Combine(baseDirectory, Path.GetFileNameWithoutExtension(csbFile));

            CriCpkArchive cpkArchive = null;
            string        cpkPath    = outputDirectoryName + ".cpk";
            bool          found      = File.Exists(cpkPath);

            //This should fix "File not found" error in case-sensitive file systems.
            //Add new extensions when necessary.
            foreach (string extension in new string[] { "cpk", "CPK" })
            {
                if (found)
                {
                    break;
                }

                cpkPath = outputDirectoryName + "." + extension;
                found   = File.Exists(cpkPath);
            }

            using (CriTableReader reader = CriTableReader.Create(csbFile))
            {
                while (reader.Read())
                {
                    if (reader.GetString("name") == "SOUND_ELEMENT")
                    {
                        long tablePosition = reader.GetPosition("utf");
                        using (CriTableReader sdlReader = CriTableReader.Create(reader.GetSubStream("utf")))
                        {
                            while (sdlReader.Read())
                            {
                                if (sdlReader.GetByte("fmt") != 0)
                                {
                                    throw new Exception("The given CSB file contains an audio file which is not an ADX. Only CSB files with ADXs are supported.");
                                }

                                bool streaming = sdlReader.GetBoolean("stmflg");
                                if (streaming && !found)
                                {
                                    throw new Exception("Cannot find the external .CPK file for this .CSB file. Please ensure that the external .CPK file is stored in the directory where the .CPK file is.");
                                }

                                else if (streaming && found && cpkArchive == null)
                                {
                                    cpkArchive = new CriCpkArchive();
                                    cpkArchive.Load(cpkPath, 4096);
                                }

                                string        sdlName         = sdlReader.GetString("name");
                                DirectoryInfo destinationPath = new DirectoryInfo(Path.Combine(outputDirectoryName, sdlName));
                                destinationPath.Create();

                                CriAaxArchive aaxArchive = new CriAaxArchive();

                                if (streaming)
                                {
                                    CriCpkEntry cpkEntry = cpkArchive.GetByPath(sdlName);

                                    if (cpkEntry != null)
                                    {
                                        using (Stream cpkSource = File.OpenRead(cpkPath))
                                            using (Stream aaxSource = cpkEntry.Open(cpkSource))
                                            {
                                                aaxArchive.Read(aaxSource);

                                                foreach (CriAaxEntry entry in aaxArchive)
                                                {
                                                    extractor.Add(cpkPath,
                                                                  Path.Combine(destinationPath.FullName,
                                                                               entry.Flag == CriAaxEntryFlag.Intro ? "Intro.adx" : "Loop.adx"),
                                                                  cpkEntry.Position + entry.Position, entry.Length);
                                                }
                                            }
                                    }
                                }

                                else
                                {
                                    long aaxPosition = sdlReader.GetPosition("data");
                                    using (Stream aaxSource = sdlReader.GetSubStream("data"))
                                    {
                                        aaxArchive.Read(aaxSource);

                                        foreach (CriAaxEntry entry in aaxArchive)
                                        {
                                            extractor.Add(csbFile,
                                                          Path.Combine(destinationPath.FullName,
                                                                       entry.Flag == CriAaxEntryFlag.Intro ? "Intro.adx" : "Loop.adx"),
                                                          tablePosition + aaxPosition + entry.Position, entry.Length);
                                        }
                                    }
                                }
                            }
                        }

                        break;
                    }
                }
            }

            extractor.Run();
        }
        public static async Task CSBRepack(string csbDirectory)
        {
            string baseDirectory = Path.GetDirectoryName(csbDirectory);
            string csbPath       = csbDirectory + ".csb";

            foreach (string extension in new string[] { "csb", "CSB" })
            {
                if (File.Exists(csbPath))
                {
                    break;
                }
                csbPath = csbDirectory + "." + extension;
            }

            if (!File.Exists(csbPath))
            {
                throw new Exception("Cannot find the .CSB file for this directory. Please ensure that the .CSB file is stored in the directory where this directory is.");
            }

            CriCpkArchive cpkArchive = new CriCpkArchive();

            CriTable csbFile = new CriTable();

            csbFile.Load(csbPath, 4096);

            CriRow soundElementRow = csbFile.Rows.First(row => (string)row["name"] == "SOUND_ELEMENT");

            CriTable soundElementTable = new CriTable();

            soundElementTable.Load((byte[])soundElementRow["utf"]);

            List <FileInfo> junks = new List <FileInfo>();

            foreach (CriRow sdlRow in soundElementTable.Rows)
            {
                string sdlName = (string)sdlRow["name"];

                DirectoryInfo sdlDirectory = new DirectoryInfo(Path.Combine(csbDirectory, sdlName));

                if (!sdlDirectory.Exists)
                {
                    throw new Exception($"Cannot find sound element directory for replacement.\nPath attempt: {sdlDirectory.FullName}");
                }

                bool streaming      = (byte)sdlRow["stmflg"] != 0;
                uint sampleRate     = (uint)sdlRow["sfreq"];
                byte numberChannels = (byte)sdlRow["nch"];

                CriAaxArchive aaxArchive = new CriAaxArchive();
                foreach (FileInfo file in sdlDirectory.GetFiles("*.adx"))
                {
                    CriAaxEntry entry = new CriAaxEntry();
                    if (file.Name.ToLower(CultureInfo.GetCultureInfo("en-US")) == "intro.adx")
                    {
                        entry.Flag     = CriAaxEntryFlag.Intro;
                        entry.FilePath = file;
                        aaxArchive.Add(entry);

                        ReadAdx(file, out sampleRate, out numberChannels);
                    }

                    else if (file.Name.ToLower(CultureInfo.GetCultureInfo("en-US")) == "loop.adx")
                    {
                        entry.Flag     = CriAaxEntryFlag.Loop;
                        entry.FilePath = file;
                        aaxArchive.Add(entry);

                        ReadAdx(file, out sampleRate, out numberChannels);
                    }
                }

                if (streaming)
                {
                    CriCpkEntry entry = new CriCpkEntry();
                    entry.Name          = Path.GetFileName(sdlName);
                    entry.DirectoryName = Path.GetDirectoryName(sdlName);
                    entry.Id            = (uint)cpkArchive.Count;
                    entry.FilePath      = new FileInfo(Path.GetTempFileName());
                    junks.Add(entry.FilePath);

                    cpkArchive.Add(entry);
                    aaxArchive.Save(entry.FilePath.FullName, 4096);
                }

                else
                {
                    sdlRow["data"] = aaxArchive.Save();
                }

                sdlRow["sfreq"] = sampleRate;
                sdlRow["nch"]   = numberChannels;
            }

            soundElementTable.WriterSettings = CriTableWriterSettings.AdxSettings;
            soundElementRow["utf"]           = soundElementTable.Save();

            csbFile.WriterSettings = CriTableWriterSettings.AdxSettings;
            csbFile.Save(csbPath, 4096);

            if (cpkArchive.Count > 0)
            {
                string cpkPath = csbDirectory + ".cpk";
                foreach (string extension in new string[] { "cpk", "CPK" })
                {
                    if (File.Exists(csbDirectory + "." + extension))
                    {
                        cpkPath = csbDirectory + "." + extension;
                        break;
                    }
                }

                cpkArchive.Save(cpkPath, 4096);
            }

            foreach (FileInfo junk in junks)
            {
                junk.Delete();
            }
        }
Пример #5
0
        static void Main(string[] args)
        {
            if (!File.Exists(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile))
            {
                Settings.Default.Reset();
                Settings.Default.Save();
            }

            if (args.Length < 1)
            {
                Console.WriteLine(Resources.Description);
                Console.ReadLine();
                return;
            }
#if !DEBUG
            try
            {
#endif
            if (args[0].EndsWith(".acb", StringComparison.OrdinalIgnoreCase))
            {
                var extractor = new DataExtractor();
                extractor.ProgressChanged += OnProgressChanged;

                extractor.BufferSize      = Settings.Default.BufferSize;
                extractor.EnableThreading = Settings.Default.EnableThreading;
                extractor.MaxThreads      = Settings.Default.MaxThreads;

                string baseDirectory       = Path.GetDirectoryName(args[0]);
                string outputDirectoryPath = Path.ChangeExtension(args[0], null);
                string extAfs2ArchivePath  = string.Empty;

                Directory.CreateDirectory(outputDirectoryPath);

                using (CriTableReader acbReader = CriTableReader.Create(args[0]))
                {
                    acbReader.Read();

                    CriAfs2Archive afs2Archive    = new CriAfs2Archive();
                    CriAfs2Archive extAfs2Archive = new CriAfs2Archive();

                    CriCpkArchive cpkArchive    = new CriCpkArchive();
                    CriCpkArchive extCpkArchive = null;

                    extAfs2ArchivePath = outputDirectoryPath + ".awb";
                    bool found = File.Exists(extAfs2ArchivePath);

                    if (!found)
                    {
                        extAfs2ArchivePath = outputDirectoryPath + "_streamfiles.awb";
                        found = File.Exists(extAfs2ArchivePath);
                    }

                    if (!found)
                    {
                        extAfs2ArchivePath = outputDirectoryPath + "_STR.awb";
                        found = File.Exists(extAfs2ArchivePath);
                    }

                    bool cpkMode = true;

                    long awbPosition = acbReader.GetPosition("AwbFile");
                    if (acbReader.GetLength("AwbFile") > 0)
                    {
                        using (SubStream afs2Stream = acbReader.GetSubStream("AwbFile"))
                        {
                            cpkMode = !CheckIfAfs2(afs2Stream);

                            if (cpkMode)
                            {
                                cpkArchive.Read(afs2Stream);
                            }

                            else
                            {
                                afs2Archive.Read(afs2Stream);
                            }
                        }
                    }

                    if (acbReader.GetLength("StreamAwbAfs2Header") > 0)
                    {
                        cpkMode = false;

                        using (SubStream extAfs2Stream = acbReader.GetSubStream("StreamAwbAfs2Header"))
                        {
                            bool utfMode = DataStream.ReadCString(extAfs2Stream, 4) == "@UTF";
                            extAfs2Stream.Seek(0, SeekOrigin.Begin);

                            if (utfMode)
                            {
                                using (CriTableReader utfAfs2HeaderReader = CriTableReader.Create(extAfs2Stream))
                                {
                                    utfAfs2HeaderReader.Read();

                                    using (SubStream extAfs2HeaderStream = utfAfs2HeaderReader.GetSubStream("Header"))
                                    {
                                        extAfs2Archive.Read(extAfs2HeaderStream);
                                    }
                                }
                            }

                            else
                            {
                                extAfs2Archive.Read(extAfs2Stream);
                            }
                        }

                        if (!found)
                        {
                            throw new FileNotFoundException("Cannot find the external .AWB file for this .ACB file. Please ensure that the external .AWB file is stored in the directory where the .ACB file is.");
                        }
                    }

                    using (SubStream waveformTableStream = acbReader.GetSubStream("WaveformTable"))
                        using (CriTableReader waveformReader = CriTableReader.Create(waveformTableStream))
                        {
                            while (waveformReader.Read())
                            {
                                byte encodeType = waveformReader.GetByte("EncodeType");
                                bool streaming  = waveformReader.GetBoolean("Streaming");

                                ushort id =
                                    waveformReader.ContainsField("MemoryAwbId") ?
                                    streaming ? waveformReader.GetUInt16("StreamAwbId") : waveformReader.GetUInt16("MemoryAwbId") :
                                    waveformReader.GetUInt16("Id");

                                string outputName = id.ToString("D5");
                                if (streaming)
                                {
                                    outputName += "_streaming";
                                }

                                outputName += GetExtension(encodeType);
                                outputName  = Path.Combine(outputDirectoryPath, outputName);

                                if (streaming)
                                {
                                    if (!found)
                                    {
                                        throw new Exception("Cannot find the external .AWB file for this .ACB file. Please ensure that the external .AWB file is stored in the directory where the .ACB file is.");
                                    }

                                    else if (extCpkArchive == null && cpkMode)
                                    {
                                        extCpkArchive = new CriCpkArchive();
                                        extCpkArchive.Load(extAfs2ArchivePath, Settings.Default.BufferSize);
                                    }

                                    EntryBase afs2Entry = null;

                                    if (cpkMode)
                                    {
                                        afs2Entry = extCpkArchive.GetById(id);
                                    }

                                    else
                                    {
                                        afs2Entry = extAfs2Archive.GetById(id);
                                    }

                                    extractor.Add(extAfs2ArchivePath, outputName, afs2Entry.Position, afs2Entry.Length);
                                }

                                else
                                {
                                    EntryBase afs2Entry = null;

                                    if (cpkMode)
                                    {
                                        afs2Entry = cpkArchive.GetById(id);
                                    }

                                    else
                                    {
                                        afs2Entry = afs2Archive.GetById(id);
                                    }

                                    extractor.Add(args[0], outputName, awbPosition + afs2Entry.Position, afs2Entry.Length);
                                }
                            }
                        }
                }

                extractor.Run();
            }

            else if (File.GetAttributes(args[0]).HasFlag(FileAttributes.Directory))
            {
                string baseDirectory = Path.GetDirectoryName(args[0]);
                string acbPath       = args[0] + ".acb";

                string awbPath = args[0] + "_streamfiles.awb";
                bool   found   = File.Exists(awbPath);

                if (!found)
                {
                    awbPath = args[0] + "_STR.awb";
                    found   = File.Exists(awbPath);
                }

                if (!found)
                {
                    awbPath = args[0] + ".awb";
                }

                if (!File.Exists(acbPath))
                {
                    throw new FileNotFoundException("Cannot find the .ACB file for this directory. Please ensure that the .ACB file is stored in the directory where this directory is.");
                }

                CriTable acbFile = new CriTable();
                acbFile.Load(acbPath, Settings.Default.BufferSize);

                CriAfs2Archive afs2Archive    = new CriAfs2Archive();
                CriAfs2Archive extAfs2Archive = new CriAfs2Archive();

                CriCpkArchive cpkArchive    = new CriCpkArchive();
                CriCpkArchive extCpkArchive = new CriCpkArchive();
                cpkArchive.Mode = extCpkArchive.Mode = CriCpkMode.Id;

                afs2Archive.ProgressChanged    += OnProgressChanged;
                extAfs2Archive.ProgressChanged += OnProgressChanged;
                cpkArchive.ProgressChanged     += OnProgressChanged;
                extCpkArchive.ProgressChanged  += OnProgressChanged;

                bool cpkMode = true;

                byte[] awbFile             = (byte[])acbFile.Rows[0]["AwbFile"];
                byte[] streamAwbAfs2Header = (byte[])acbFile.Rows[0]["StreamAwbAfs2Header"];

                cpkMode = !(awbFile != null && awbFile.Length >= 4 && Encoding.ASCII.GetString(awbFile, 0, 4) == "AFS2") && (streamAwbAfs2Header == null || streamAwbAfs2Header.Length == 0);

                using (CriTableReader reader = CriTableReader.Create((byte[])acbFile.Rows[0]["WaveformTable"]))
                {
                    while (reader.Read())
                    {
                        byte encodeType = reader.GetByte("EncodeType");
                        bool streaming  = reader.GetBoolean("Streaming");

                        ushort id =
                            reader.ContainsField("MemoryAwbId") ?
                            streaming ? reader.GetUInt16("StreamAwbId") : reader.GetUInt16("MemoryAwbId") :
                            reader.GetUInt16("Id");

                        string inputName = id.ToString("D5");
                        if (streaming)
                        {
                            inputName += "_streaming";
                        }

                        inputName += GetExtension(encodeType);
                        inputName  = Path.Combine(args[0], inputName);

                        if (!File.Exists(inputName))
                        {
                            throw new FileNotFoundException($"Cannot find audio file with id {id} for replacement.\nPath attempt: {inputName}");
                        }

                        if (cpkMode)
                        {
                            CriCpkEntry entry = new CriCpkEntry();
                            entry.FilePath = new FileInfo(inputName);
                            entry.Id       = id;

                            if (streaming)
                            {
                                extCpkArchive.Add(entry);
                            }

                            else
                            {
                                cpkArchive.Add(entry);
                            }
                        }

                        else
                        {
                            CriAfs2Entry entry = new CriAfs2Entry();
                            entry.FilePath = new FileInfo(inputName);
                            entry.Id       = id;

                            if (streaming)
                            {
                                extAfs2Archive.Add(entry);
                            }

                            else
                            {
                                afs2Archive.Add(entry);
                            }
                        }
                    }
                }

                acbFile.Rows[0]["AwbFile"]             = null;
                acbFile.Rows[0]["StreamAwbAfs2Header"] = null;

                if (afs2Archive.Count > 0 || cpkArchive.Count > 0)
                {
                    Console.WriteLine("Saving internal AWB...");
                    acbFile.Rows[0]["AwbFile"] = cpkMode ? cpkArchive.Save() : afs2Archive.Save();
                    Console.WriteLine();
                }

                if (extAfs2Archive.Count > 0 || extCpkArchive.Count > 0)
                {
                    Console.WriteLine("Saving external AWB...");
                    if (cpkMode)
                    {
                        extCpkArchive.Save(awbPath, Settings.Default.BufferSize);
                    }

                    else
                    {
                        extAfs2Archive.Save(awbPath, Settings.Default.BufferSize);

                        if (Encoding.UTF8.GetString(streamAwbAfs2Header, 0, 4) == "@UTF")
                        {
                            CriTable headerTable = new CriTable();
                            headerTable.Load(streamAwbAfs2Header);

                            headerTable.Rows[0]["Header"]          = extAfs2Archive.Header;
                            headerTable.WriterSettings             = CriTableWriterSettings.Adx2Settings;
                            acbFile.Rows[0]["StreamAwbAfs2Header"] = headerTable.Save();
                        }

                        else
                        {
                            acbFile.Rows[0]["StreamAwbAfs2Header"] = extAfs2Archive.Header;
                        }
                    }
                }

                acbFile.WriterSettings = CriTableWriterSettings.Adx2Settings;
                acbFile.Save(acbPath, Settings.Default.BufferSize);
            }
#if !DEBUG
        }

        catch (Exception exception)
        {
            MessageBox.Show($"{exception.Message}", "ACB Editor", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
#endif
        }
Пример #6
0
        public static void Import(string path, CsbProject project)
        {
            var extractor = new DataExtractor();

            extractor.BufferSize      = MainForm.Settings.BufferSize;
            extractor.EnableThreading = MainForm.Settings.EnableThreading;
            extractor.MaxThreads      = MainForm.Settings.MaxThreads;

            // Find the CPK first
            string cpkPath = Path.ChangeExtension(path, "cpk");
            bool   exists  = File.Exists(cpkPath);

            CriCpkArchive cpkArchive = new CriCpkArchive();

            // First, deserialize the main tables
            List <SerializationCueSheetTable> cueSheets = CriTableSerializer.Deserialize <SerializationCueSheetTable>(path, MainForm.Settings.BufferSize);

            /* Deserialize all the tables we need to import.
             * None = 0,
             * Cue = 1,
             * Synth = 2,
             * SoundElement = 4,
             * Aisac = 5,
             * VoiceLimitGroup = 6,
             * VersionInfo = 7,
             */

            List <SerializationCueTable>          cueTables          = CriTableSerializer.Deserialize <SerializationCueTable>(cueSheets.FirstOrDefault(table => table.TableType == 1).TableData);
            List <SerializationSynthTable>        synthTables        = CriTableSerializer.Deserialize <SerializationSynthTable>(cueSheets.FirstOrDefault(table => table.TableType == 2).TableData);
            List <SerializationSoundElementTable> soundElementTables = CriTableSerializer.Deserialize <SerializationSoundElementTable>(cueSheets.FirstOrDefault(table => table.TableType == 4).TableData);
            List <SerializationAisacTable>        aisacTables        = CriTableSerializer.Deserialize <SerializationAisacTable>(cueSheets.FirstOrDefault(table => table.TableType == 5).TableData);

            // voice limit groups appeared in the later versions, so check if it exists.
            List <SerializationVoiceLimitGroupTable> voiceLimitGroupTables = new List <SerializationVoiceLimitGroupTable>();

            if (cueSheets.Exists(table => table.TableType == 6))
            {
                voiceLimitGroupTables = CriTableSerializer.Deserialize <SerializationVoiceLimitGroupTable>(cueSheets.FirstOrDefault(table => table.TableType == 6).TableData);
            }

            // Deserialize Sound Element tables

            // BUT BEFORE THAT, see if there's any sound element with Streamed on
            if (soundElementTables.Exists(soundElementTable => soundElementTable.Streaming))
            {
                if (!exists)
                {
                    throw new Exception("Cannot find CPK file for this CSB file. Please ensure that the CPK file is in the directory where the CSB file is, and has the same name as the CSB file, but with .CPK extension.");
                }

                cpkArchive.Load(cpkPath);
            }

            foreach (SerializationSoundElementTable soundElementTable in soundElementTables)
            {
                BuilderSoundElementNode soundElementNode = new BuilderSoundElementNode();
                soundElementNode.Name         = soundElementTable.Name;
                soundElementNode.ChannelCount = soundElementTable.NumberChannels;
                soundElementNode.SampleRate   = soundElementTable.SoundFrequency;
                soundElementNode.Streaming    = soundElementTable.Streaming;
                soundElementNode.SampleCount  = soundElementTable.NumberSamples;

                CriAaxArchive aaxArchive = new CriAaxArchive();

                CriCpkEntry cpkEntry = cpkArchive.GetByPath(soundElementTable.Name);
                if (soundElementNode.Streaming && cpkEntry != null)
                {
                    using (Stream source = File.OpenRead(cpkPath))
                        using (Stream entrySource = cpkEntry.Open(source))
                        {
                            aaxArchive.Read(entrySource);
                        }
                }

                else if (soundElementNode.Streaming && cpkEntry == null)
                {
                    soundElementNode.Intro      = soundElementNode.Loop = string.Empty;
                    soundElementNode.SampleRate = soundElementNode.SampleCount = soundElementNode.ChannelCount = 0;
                }

                else
                {
                    aaxArchive.Load(soundElementTable.Data);
                }

                foreach (CriAaxEntry entry in aaxArchive)
                {
                    string outputFileName = Path.Combine(project.AudioDirectory.FullName, soundElementTable.Name.Replace('/', '_'));
                    if (entry.Flag == CriAaxEntryFlag.Intro)
                    {
                        outputFileName        += $"_Intro{aaxArchive.GetModeExtension()}";
                        soundElementNode.Intro = Path.GetFileName(outputFileName);
                    }

                    else if (entry.Flag == CriAaxEntryFlag.Loop)
                    {
                        outputFileName       += $"_Loop{aaxArchive.GetModeExtension()}";
                        soundElementNode.Loop = Path.GetFileName(outputFileName);
                    }

                    if (soundElementNode.Streaming)
                    {
                        extractor.Add(cpkPath, outputFileName, cpkEntry.Position + entry.Position, entry.Length);
                    }

                    else
                    {
                        extractor.Add(soundElementTable.Data, outputFileName, entry.Position, entry.Length);
                    }
                }

                project.SoundElementNodes.Add(soundElementNode);
            }

            // Deserialize Voice Limit Group tables
            foreach (SerializationVoiceLimitGroupTable voiceLimitGroupTable in voiceLimitGroupTables)
            {
                project.VoiceLimitGroupNodes.Add(new BuilderVoiceLimitGroupNode
                {
                    Name = voiceLimitGroupTable.VoiceLimitGroupName,
                    MaxAmountOfInstances = voiceLimitGroupTable.VoiceLimitGroupNum,
                });
            }

            // Deserialize Aisac tables
            foreach (SerializationAisacTable aisacTable in aisacTables)
            {
                BuilderAisacNode aisacNode = new BuilderAisacNode();
                aisacNode.Name        = aisacTable.PathName;
                aisacNode.AisacName   = aisacTable.Name;
                aisacNode.Type        = aisacTable.Type;
                aisacNode.RandomRange = aisacTable.RandomRange;

                // Deserialize the graphs
                List <SerializationAisacGraphTable> graphTables = CriTableSerializer.Deserialize <SerializationAisacGraphTable>(aisacTable.Graph);
                foreach (SerializationAisacGraphTable graphTable in graphTables)
                {
                    BuilderAisacGraphNode graphNode = new BuilderAisacGraphNode();
                    graphNode.Name     = $"Graph{aisacNode.Graphs.Count}";
                    graphNode.Type     = graphTable.Type;
                    graphNode.MaximumX = graphTable.InMax;
                    graphNode.MinimumX = graphTable.InMin;
                    graphNode.MaximumY = graphTable.OutMax;
                    graphNode.MinimumY = graphTable.OutMin;

                    // Deserialize the points
                    List <SerializationAisacPointTable> pointTables = CriTableSerializer.Deserialize <SerializationAisacPointTable>(graphTable.Points);
                    foreach (SerializationAisacPointTable pointTable in pointTables)
                    {
                        BuilderAisacPointNode pointNode = new BuilderAisacPointNode();
                        pointNode.Name = $"Point{graphNode.Points.Count}";
                        pointNode.X    = pointTable.In;
                        pointNode.Y    = pointTable.Out;
                        graphNode.Points.Add(pointNode);
                    }

                    aisacNode.Graphs.Add(graphNode);
                }

                project.AisacNodes.Add(aisacNode);
            }

            // Deserialize Synth tables
            foreach (SerializationSynthTable synthTable in synthTables)
            {
                BuilderSynthNode synthNode = new BuilderSynthNode();
                synthNode.Name                      = synthTable.SynthName;
                synthNode.Type                      = (BuilderSynthType)synthTable.SynthType;
                synthNode.PlaybackType              = (BuilderSynthPlaybackType)synthTable.ComplexType;
                synthNode.Volume                    = synthTable.Volume;
                synthNode.Pitch                     = synthTable.Pitch;
                synthNode.DelayTime                 = synthTable.DelayTime;
                synthNode.SControl                  = synthTable.SControl;
                synthNode.EgDelay                   = synthTable.EgDelay;
                synthNode.EgAttack                  = synthTable.EgAttack;
                synthNode.EgHold                    = synthTable.EgHold;
                synthNode.EgDecay                   = synthTable.EgDecay;
                synthNode.EgRelease                 = synthTable.EgRelease;
                synthNode.EgSustain                 = synthTable.EgSustain;
                synthNode.FilterType                = synthTable.FType;
                synthNode.FilterCutoff1             = synthTable.FCof1;
                synthNode.FilterCutoff2             = synthTable.FCof2;
                synthNode.FilterReso                = synthTable.FReso;
                synthNode.FilterReleaseOffset       = synthTable.FReleaseOffset;
                synthNode.DryOName                  = synthTable.DryOName;
                synthNode.Mtxrtr                    = synthTable.Mtxrtr;
                synthNode.Dry0                      = synthTable.Dry0;
                synthNode.Dry1                      = synthTable.Dry1;
                synthNode.Dry2                      = synthTable.Dry2;
                synthNode.Dry3                      = synthTable.Dry3;
                synthNode.Dry4                      = synthTable.Dry4;
                synthNode.Dry5                      = synthTable.Dry5;
                synthNode.Dry6                      = synthTable.Dry6;
                synthNode.Dry7                      = synthTable.Dry7;
                synthNode.WetOName                  = synthTable.WetOName;
                synthNode.Wet0                      = synthTable.Wet0;
                synthNode.Wet1                      = synthTable.Wet1;
                synthNode.Wet2                      = synthTable.Wet2;
                synthNode.Wet3                      = synthTable.Wet3;
                synthNode.Wet4                      = synthTable.Wet4;
                synthNode.Wet5                      = synthTable.Wet5;
                synthNode.Wet6                      = synthTable.Wet6;
                synthNode.Wet7                      = synthTable.Wet7;
                synthNode.Wcnct0                    = synthTable.Wcnct0;
                synthNode.Wcnct1                    = synthTable.Wcnct1;
                synthNode.Wcnct2                    = synthTable.Wcnct2;
                synthNode.Wcnct3                    = synthTable.Wcnct3;
                synthNode.Wcnct4                    = synthTable.Wcnct4;
                synthNode.Wcnct5                    = synthTable.Wcnct5;
                synthNode.Wcnct6                    = synthTable.Wcnct6;
                synthNode.Wcnct7                    = synthTable.Wcnct7;
                synthNode.VoiceLimitType            = synthTable.VoiceLimitType;
                synthNode.VoiceLimitPriority        = synthTable.VoiceLimitPriority;
                synthNode.VoiceLimitProhibitionTime = synthTable.VoiceLimitPhTime;
                synthNode.VoiceLimitPcdlt           = synthTable.VoiceLimitPcdlt;
                synthNode.Pan3dVolumeOffset         = synthTable.Pan3dVolumeOffset;
                synthNode.Pan3dVolumeGain           = synthTable.Pan3dVolumeGain;
                synthNode.Pan3dAngleOffset          = synthTable.Pan3dAngleOffset;
                synthNode.Pan3dAngleGain            = synthTable.Pan3dAngleGain;
                synthNode.Pan3dDistanceOffset       = synthTable.Pan3dDistanceOffset;
                synthNode.Pan3dDistanceGain         = synthTable.Pan3dDistanceGain;
                synthNode.Dry0g                     = synthTable.Dry0g;
                synthNode.Dry1g                     = synthTable.Dry1g;
                synthNode.Dry2g                     = synthTable.Dry2g;
                synthNode.Dry3g                     = synthTable.Dry3g;
                synthNode.Dry4g                     = synthTable.Dry4g;
                synthNode.Dry5g                     = synthTable.Dry5g;
                synthNode.Dry6g                     = synthTable.Dry6g;
                synthNode.Dry7g                     = synthTable.Dry7g;
                synthNode.Wet0g                     = synthTable.Wet0g;
                synthNode.Wet1g                     = synthTable.Wet1g;
                synthNode.Wet2g                     = synthTable.Wet2g;
                synthNode.Wet3g                     = synthTable.Wet3g;
                synthNode.Wet4g                     = synthTable.Wet4g;
                synthNode.Wet5g                     = synthTable.Wet5g;
                synthNode.Wet6g                     = synthTable.Wet6g;
                synthNode.Wet7g                     = synthTable.Wet7g;
                synthNode.Filter1Type               = synthTable.F1Type;
                synthNode.Filter1CutoffOffset       = synthTable.F1CofOffset;
                synthNode.Filter1CutoffGain         = synthTable.F1CofGain;
                synthNode.Filter1ResoOffset         = synthTable.F1ResoOffset;
                synthNode.Filter1ResoGain           = synthTable.F1ResoGain;
                synthNode.Filter2Type               = synthTable.F2Type;
                synthNode.Filter2CutoffLowerOffset  = synthTable.F2CofLowOffset;
                synthNode.Filter2CutoffLowerGain    = synthTable.F2CofLowGain;
                synthNode.Filter2CutoffHigherOffset = synthTable.F2CofHighOffset;
                synthNode.Filter2CutoffHigherGain   = synthTable.F2CofHighGain;
                synthNode.PlaybackProbability       = synthTable.Probability;
                synthNode.NLmtChildren              = synthTable.NumberLmtChildren;
                synthNode.Repeat                    = synthTable.Repeat;
                synthNode.ComboTime                 = synthTable.ComboTime;
                synthNode.ComboLoopBack             = synthTable.ComboLoopBack;

                project.SynthNodes.Add(synthNode);
            }

            // Convert the cue tables
            foreach (SerializationCueTable cueTable in cueTables)
            {
                BuilderCueNode cueNode = new BuilderCueNode();
                cueNode.Name           = cueTable.Name;
                cueNode.Id             = cueTable.Id;
                cueNode.UserComment    = cueTable.UserData;
                cueNode.Flags          = cueTable.Flags;
                cueNode.SynthReference = cueTable.SynthPath;
                project.CueNodes.Add(cueNode);
            }

            // Fix links
            for (int i = 0; i < synthTables.Count; i++)
            {
                SerializationSynthTable synthTable = synthTables[i];
                BuilderSynthNode        synthNode  = project.SynthNodes[i];

                if (synthNode.Type == BuilderSynthType.Single)
                {
                    synthNode.SoundElementReference = synthTable.LinkName;
                }

                // Polyphonic
                else if (synthNode.Type == BuilderSynthType.WithChildren)
                {
                    synthNode.Children = synthTable.LinkName.Split(new char[] { (char)0x0A }, StringSplitOptions.RemoveEmptyEntries).ToList();
                }

                if (!string.IsNullOrEmpty(synthTable.AisacSetName))
                {
                    string[] aisacs = synthTable.AisacSetName.Split(new char[] { (char)0x0A }, StringSplitOptions.RemoveEmptyEntries);
                    string[] name   = aisacs[0].Split(new string[] { "::" }, StringSplitOptions.None);
                    synthNode.AisacReference = name[1]; // will add support for multiple aisacs (I'm actually not even sure if csbs support multiple aisacs...)
                }

                if (!string.IsNullOrEmpty(synthTable.VoiceLimitGroupName))
                {
                    synthNode.VoiceLimitGroupReference = synthTable.VoiceLimitGroupName;
                }
            }

            // Extract everything
            extractor.Run();
        }