Example #1
0
        private void CompactAFCBackgroundThread(object sender, DoWorkEventArgs e)
        {
            var    arguments      = (Tuple <string, string>)e.Argument;
            string path           = arguments.Item1;
            string NewAFCBaseName = arguments.Item2;

            string[] pccFiles = System.IO.Directory.GetFiles(path, "*.pcc");
            string[] afcFiles = System.IO.Directory.GetFiles(path, "*.afc").Select(x => System.IO.Path.GetFileNameWithoutExtension(x).ToLower()).ToArray();

            var ReferencedAFCAudio = new List <Tuple <string, int, int> >();

            int i = 1;

            foreach (string pccPath in pccFiles)
            {
                BusyText = "Finding all referenced audio (" + i + "/" + pccFiles.Count() + ")";
                using (IMEPackage pack = MEPackageHandler.OpenMEPackage(pccPath))
                {
                    List <IExportEntry> wwiseStreamExports = pack.Exports.Where(x => x.ClassName == "WwiseStream").ToList();
                    foreach (IExportEntry exp in wwiseStreamExports)
                    {
                        var afcNameProp = exp.GetProperty <NameProperty>("Filename");
                        if (afcNameProp != null && afcFiles.Contains(afcNameProp.ToString().ToLower()))
                        {
                            string afcName     = afcNameProp.ToString().ToLower();
                            int    readPos     = exp.Data.Length - 8;
                            int    audioSize   = BitConverter.ToInt32(exp.Data, exp.Data.Length - 8);
                            int    audioOffset = BitConverter.ToInt32(exp.Data, exp.Data.Length - 4);
                            ReferencedAFCAudio.Add(new Tuple <string, int, int>(afcName, audioSize, audioOffset));
                        }
                    }
                }
                i++;
            }
            ReferencedAFCAudio = ReferencedAFCAudio.Distinct().ToList();

            //extract referenced audio
            BusyText = "Extracting referenced audio";
            var extractedAudioMap = new Dictionary <Tuple <string, int, int>, byte[]>();

            i = 1;
            foreach (Tuple <string, int, int> reference in ReferencedAFCAudio)
            {
                BusyText = "Extracting referenced audio (" + i + " / " + ReferencedAFCAudio.Count() + ")";
                string     afcPath = System.IO.Path.Combine(path, reference.Item1 + ".afc");
                FileStream stream  = new FileStream(afcPath, FileMode.Open, FileAccess.Read);
                stream.Seek(reference.Item3, SeekOrigin.Begin);
                byte[] extractedAudio = new byte[reference.Item2];
                stream.Read(extractedAudio, 0, reference.Item2);
                stream.Close();
                extractedAudioMap[reference] = extractedAudio;
                i++;
            }

            var newAFCEntryPointMap = new Dictionary <Tuple <string, int, int>, long>();

            i = 1;
            string newAfcPath = System.IO.Path.Combine(path, NewAFCBaseName + ".afc");

            if (File.Exists(newAfcPath))
            {
                File.Delete(newAfcPath);
            }
            FileStream newAFCStream = new FileStream(newAfcPath, FileMode.CreateNew, FileAccess.Write);

            foreach (Tuple <string, int, int> reference in ReferencedAFCAudio)
            {
                BusyText = "Building new AFC file (" + i + " / " + ReferencedAFCAudio.Count() + ")";
                newAFCEntryPointMap[reference] = newAFCStream.Position; //save entry point in map
                newAFCStream.Write(extractedAudioMap[reference], 0, extractedAudioMap[reference].Length);
                i++;
            }
            newAFCStream.Close();
            extractedAudioMap = null; //clean out ram on next GC

            i = 1;
            foreach (string pccPath in pccFiles)
            {
                BusyText = "Updating audio references (" + i + "/" + pccFiles.Count() + ")";
                using (IMEPackage pack = MEPackageHandler.OpenMEPackage(pccPath))
                {
                    bool shouldSave = false;
                    List <IExportEntry> wwiseStreamExports = pack.Exports.Where(x => x.ClassName == "WwiseStream").ToList();
                    foreach (IExportEntry exp in wwiseStreamExports)
                    {
                        var afcNameProp = exp.GetProperty <NameProperty>("Filename");
                        if (afcNameProp != null && afcFiles.Contains(afcNameProp.ToString().ToLower()))
                        {
                            string afcName     = afcNameProp.ToString().ToLower();
                            int    readPos     = exp.Data.Length - 8;
                            int    audioSize   = BitConverter.ToInt32(exp.Data, exp.Data.Length - 8);
                            int    audioOffset = BitConverter.ToInt32(exp.Data, exp.Data.Length - 4);
                            var    key         = new Tuple <string, int, int>(afcName, audioSize, audioOffset);
                            long   newOffset;
                            if (newAFCEntryPointMap.TryGetValue(key, out newOffset))
                            {
                                //its a match
                                afcNameProp.Value = NewAFCBaseName;
                                Application.Current.Dispatcher.Invoke(() =>
                                {
                                    exp.WriteProperty(afcNameProp);
                                    byte[] newData = exp.Data;
                                    Buffer.BlockCopy(BitConverter.GetBytes((int)newOffset), 0, newData, newData.Length - 4, 4); //update AFC audio offset
                                    exp.Data = newData;
                                    if (exp.DataChanged)
                                    {
                                        //don't mark for saving if the data didn't acutally change (e.g. trying to compact a compacted AFC).
                                        shouldSave = true;
                                    }
                                });
                            }
                        }
                    }
                    if (shouldSave)
                    {
                        Application.Current.Dispatcher.Invoke(
                            () =>
                        {
                            // Must run on the UI thread or the tool interop will throw an exception
                            // because we are on a background thread.
                            pack.save();
                        });
                    }
                }
                i++;
            }
            BusyText = "Rebuild complete";
            System.Threading.Thread.Sleep(2000);
        }