private IEnumerable <CatalogueEntry> BuildFileListInCatalogues(IEnumerable <BundleList> bundleCollections)
        {
            foreach (var collection in bundleCollections)
            {
                string appendPrefix = collection.Name;

                foreach (var item in collection.Bundles)
                {
                    // using a level of indirection
                    if (item.Indirection != null)
                    {
                        if (item.Properties.ContainsKey("id"))
                        {
                            appendPrefix = collection.Name + @"\" + ((string)item.Properties["id"].Value).Replace(@"/", @"\");
                        }
                        foreach (var entry in BuildFileListInCatalogues(item.Indirection.BundleCollections))
                        {
                            entry.ResolvedName = appendPrefix + @"\" + entry.ResolvedName;
                            yield return(entry);
                        }
                    }
                    // directly
                    else
                    {
                        if (item.Properties.ContainsKey("sha1"))
                        {
                            byte[] sha1 = (byte[])item.Properties["sha1"].Value;

                            CatalogueEntry catalogueEntry = FindCorrespondingCatalogueEntry(sha1);
                            if (catalogueEntry != null)
                            {
                                if (item.Properties.ContainsKey("name"))
                                {
                                    catalogueEntry.ResolvedName = ((string)item.Properties["name"].Value).Replace(@"/", @"\");
                                }
                                else if (item.Properties.ContainsKey("id"))
                                {
                                    catalogueEntry.ResolvedName = Helpers.ByteArrayToString((byte[])item.Properties["id"].Value);
                                }
                                catalogueEntry.ResolvedName = appendPrefix + @"\" + catalogueEntry.ResolvedName;

                                yield return(catalogueEntry);
                            }
                        }
                    }
                }
            }
        }
        private void BuildCatalogueEntry(Catalogue catalogue, byte version)
        {
            byte[]         hash  = ReadBytes(20);
            CatalogueEntry entry = new CatalogueEntry();

            entry.Offset = ReadInt32();
            entry.Size   = ReadInt32();

            if (version == 0x01)
            {
                entry.Extra = ReadInt32();
            }

            entry.Archive = ReadInt32();

            catalogue.Files[hash] = entry;
            entry.Parent          = catalogue;
        }
        public void ImportFiles(string tableOfContentsName)
        {
            Dictionary <string, string> filesToOverwriteMap = new Dictionary <string, string>();

            string directory = Path.GetDirectoryName(tableOfContentsName) + @"\" + Path.GetFileNameWithoutExtension(tableOfContentsName);
            Random generator = new Random();

            ResolveNewFiles(tableOfContentsName, directory);

            bool isIndirect = myTableOfContents.Payload.BundleCollections.Where(bundleList => bundleList.Bundles.Count() > 0).First().Bundles.First().Indirection != null;

            List <bool> isIndirectionConsistentAndMatching = myTableOfContents.Payload.BundleCollections.Select(bundleList => bundleList.Bundles.All(bundle => (bundle.Indirection != null) == isIndirect)).Distinct().ToList();
            bool        isIndirectOrMixed = false;

            if (isIndirectionConsistentAndMatching.Count() != 1 || !isIndirectionConsistentAndMatching.First())
            {
                isIndirectOrMixed = true;
            }

            string originalSuperBundlePath = Path.ChangeExtension(tableOfContentsName, "sb");
            string newSuperBundlePath      = Path.ChangeExtension(tableOfContentsName, "sb_" + generator.Next().ToString());

            // table of contents pointing directly to raw files in superbundles
            if (!myTableOfContents.Payload.Properties.ContainsKey("cas"))
            {
                filesToOverwriteMap.Add(originalSuperBundlePath, newSuperBundlePath);

                // write superbundle
                var allBundles = myTableOfContents.Payload.BundleCollections.SelectMany(bundleList => bundleList.Bundles).OrderBy(superBundle => (long)superBundle.Properties["offset"].Value);

                using (BinaryReader originalReader = new BinaryReader(File.Open(originalSuperBundlePath, FileMode.Open)))
                {
                    using (BundleBinaryWriter writer = new BundleBinaryWriter(File.Open(newSuperBundlePath, FileMode.Create)))
                    {
                        foreach (SuperBundle superBundle in allBundles)
                        {
                            Int64 initialFilePosition = writer.BaseStream.Position;

                            if (superBundle.Changed == null)
                            {
                                long offset = (long)superBundle.Properties["offset"].Value;
                                originalReader.BaseStream.Seek(offset, SeekOrigin.Begin);
                                writer.Write(originalReader.ReadBytes((int)superBundle.Properties["size"].Value));
                            }
                            else
                            {
                                superBundle.Properties["size"].Value = ChunkHandler.Chunk(writer, superBundle);
                            }
                            superBundle.Properties["offset"].Value = initialFilePosition;
                        }
                    }
                }

                // write table of contents
                string newTableOfContentsName = Path.ChangeExtension(tableOfContentsName, @".toc_" + generator.Next().ToString());
                filesToOverwriteMap.Add(tableOfContentsName, newTableOfContentsName);

                using (BundleBinaryWriter writer = new BundleBinaryWriter(File.Open(newTableOfContentsName, FileMode.Create)))
                {
                    writer.Write(myTableOfContents.Header);
                    writer.Write(myTableOfContents.Payload);
                }
            }
            // table of contents pointing to raw files in catalogues
            else
            {
                IEnumerable <SuperBundle> modifiedBundles;

                if (isIndirectOrMixed)
                {
                    modifiedBundles = myTableOfContents.Payload.BundleCollections
                                      .SelectMany(bundleList => bundleList.Bundles
                                                  .Where(superBundles => superBundles.Indirection != null)
                                                  .SelectMany(superBundles => superBundles.Indirection.BundleCollections
                                                              .SelectMany(indirectBundleList => indirectBundleList.Bundles)))
                                      .Where(bundle => bundle.Changed != null);
                }
                else
                {
                    modifiedBundles = myTableOfContents.Payload.BundleCollections.SelectMany(bundleList => bundleList.Bundles).Where(bundle => bundle.Changed != null);
                }

                // write cas
                foreach (SuperBundle superBundle in modifiedBundles)
                {
                    CatalogueEntry catEntry = FindCorrespondingCatalogueEntry((byte[])superBundle.Properties["sha1"].Value);
                    catEntry.Changed = true;

                    string pathToNewCascade = Path.GetDirectoryName(catEntry.Parent.Path) + @"\\" + "cas_99.cas";
                    catEntry.Archive = 99;

                    using (BundleBinaryWriter writer = new BundleBinaryWriter(File.Open(pathToNewCascade, FileMode.Append)))
                    {
                        if (writer.BaseStream.Position == 0 && myTableOfContents.Header.Version == 0x03)
                        {
                            writer.Write(Catalogue.CasHeader);
                        }

                        catEntry.Offset = (int)writer.BaseStream.Position;
                        int fileSize = ChunkHandler.Chunk(writer, superBundle);
                        catEntry.Size = fileSize;

                        if (superBundle.Properties.ContainsKey("size"))
                        {
                            superBundle.Properties["size"].Value = (Int64)fileSize;
                        }
                        if (superBundle.Properties.ContainsKey("originalSize"))
                        {
                            superBundle.Properties["originalSize"].Value = (Int64) new FileInfo(superBundle.Changed).Length;
                        }
                    }
                }

                // write catalogue
                foreach (Catalogue catalogue in myCatalogues.Where(_ => _.Files.Values.Any(entry => entry.Changed)))
                {
                    // fix for duplicates
                    catalogue.NumberOfFiles = catalogue.Files.Count;

                    string newCataloguePath = catalogue.Path + "_tmp" + generator.Next().ToString();
                    filesToOverwriteMap.Add(catalogue.Path, newCataloguePath);

                    using (BundleBinaryWriter writer = new BundleBinaryWriter(File.Open(newCataloguePath, FileMode.Create)))
                    {
                        writer.Write(myTableOfContents.Header.Version, catalogue);
                    }
                }

                // write superbundle
                if (isIndirectOrMixed)
                {
                    filesToOverwriteMap.Add(originalSuperBundlePath, newSuperBundlePath);

                    // write table of contents
                    using (BundleBinaryWriter writer = new BundleBinaryWriter(File.Open(newSuperBundlePath, FileMode.Create)))
                    {
                        using (BinaryReader originalReader = new BinaryReader(File.Open(originalSuperBundlePath, FileMode.Open)))
                        {
                            originalReader.BaseStream.Seek(0, SeekOrigin.Begin);
                            writer.Write(originalReader.ReadBytes(16));
                        }

                        foreach (SuperBundle indirectSuperBundle in myTableOfContents.Payload.BundleCollections.SelectMany(_ => _.Bundles.Select(x => x.Indirection)))
                        {
                            writer.Write(indirectSuperBundle);
                        }
                        writer.Write((Int16)0);
                    }
                }
            }

            CleanupTemporaryFiles(filesToOverwriteMap);
        }