private static void LoadTableInfo(FileTableContainer tableContainer, CFStorage rootStorage, CFStorage gameStorage)
        {
            // first, although we can loop through entries, get them from the game storage, so we
            // know their order, which is important when writing back (because you know, hashing).
            gameStorage.TryGetStream("CustomInfoTags", out var citStream);
            if (citStream != null)
            {
                using (var stream = new MemoryStream(citStream.GetData()))
                    using (var reader = new BinaryReader(stream)) {
                        tableContainer.CustomInfoTags.Load(reader);
                    }
            }

            // now actually read them in
            rootStorage.TryGetStorage("TableInfo", out var tableInfoStorage);
            if (tableInfoStorage == null)
            {
                Logger.Info("TableInfo storage not found, skipping.");
                return;
            }
            tableInfoStorage.VisitEntries(item => {
                if (item.IsStream)
                {
                    var itemStream = item as CFStream;
                    if (itemStream != null)
                    {
                        tableContainer.TableInfo[item.Name] = BiffUtil.ParseWideString(itemStream.GetData());
                    }
                }
            }, false);
        }
        private static void LoadTableMeta(FileTableContainer tableContainer, CFStorage gameStorage)
        {
            // version
            gameStorage.TryGetStream("Version", out var versionBytes);
            if (versionBytes != null)
            {
                tableContainer.FileVersion = BitConverter.ToInt32(versionBytes.GetData(), 0);
            }
            else
            {
                Logger.Info("No Version under GameStg found, skipping.");
            }


            // hash
            gameStorage.TryGetStream("Version", out var hashBytes);
            if (hashBytes != null)
            {
                tableContainer.FileHash = hashBytes.GetData();
            }
            else
            {
                Logger.Info("No MAC under GameStg found, skipping.");
            }
        }
        public static FileTableContainer Load(string filename, bool loadGameItems = true)
        {
            var cf = new CompoundFile(filename);

            try {
                var gameStorage = cf.RootStorage.GetStorage("GameStg");
                var gameData    = gameStorage.GetStream("GameData");

                var fileVersion = BitConverter.ToInt32(gameStorage.GetStream("Version").GetData(), 0);
                using (var stream = new MemoryStream(gameData.GetData()))
                    using (var reader = new BinaryReader(stream)) {
                        var tableContainer = new FileTableContainer(reader);

                        LoadTableInfo(tableContainer, cf.RootStorage, gameStorage);
                        if (loadGameItems)
                        {
                            LoadGameItems(tableContainer, gameStorage, tableContainer.NumGameItems, "GameItem");
                            LoadGameItems(tableContainer, gameStorage, tableContainer.NumVpeGameItems, "VpeGameItem");
                        }
                        LoadTextures(tableContainer, gameStorage);
                        LoadSounds(tableContainer, gameStorage, fileVersion);
                        LoadCollections(tableContainer, gameStorage);
                        LoadTableMeta(tableContainer, gameStorage);

                        return(tableContainer);
                    }
            } finally {
                cf.Close();
            }
        }
        private static void LoadTextures(FileTableContainer tableContainer, CFStorage storage)
        {
            for (var i = 0; i < tableContainer.NumTextures; i++)
            {
                var textureName = $"Image{i}";
                storage.TryGetStream(textureName, out var textureStream);
                if (textureStream == null)
                {
                    Logger.Warn("Could not find stream {0}, skipping.", textureName);
                    continue;
                }
                var textureData = textureStream.GetData();
                if (textureData.Length < 4)
                {
                    Logger.Warn("Skipping {itemName} because it has size of {itemDataLength}.", textureName, textureData.Length);
                    continue;
                }

                using (var stream = new MemoryStream(textureData))
                    using (var reader = new BinaryReader(stream)) {
                        var texture = new Texture(reader, textureName);
                        tableContainer.AddTexture(texture);
                    }
            }
        }
 private static void LoadCollections(FileTableContainer tableContainer, CFStorage storage)
 {
     for (var i = 0; i < tableContainer.NumCollections; i++)
     {
         var collectionName = $"Collection{i}";
         storage.TryGetStream(collectionName, out var collectionStream);
         if (collectionStream == null)
         {
             Logger.Warn("Could not find stream {0}, skipping.", collectionName);
             continue;
         }
         using (var stream = new MemoryStream(collectionStream.GetData()))
             using (var reader = new BinaryReader(stream)) {
                 tableContainer.Collections.Add(new CollectionData(reader, collectionName));
             }
     }
 }
 private static void LoadSounds(FileTableContainer tableContainer, CFStorage storage, int fileVersion)
 {
     for (var i = 0; i < tableContainer.NumSounds; i++)
     {
         var soundName = $"Sound{i}";
         storage.TryGetStream(soundName, out var soundStream);
         if (soundStream == null)
         {
             Logger.Warn("Could not find stream {0}, skipping.", soundName);
             continue;
         }
         var soundData = soundStream.GetData();
         using (var stream = new MemoryStream(soundData))
             using (var reader = new BinaryReader(stream)) {
                 var sound = new Sound.Sound(reader, soundName, fileVersion);
                 tableContainer.AddSound(sound);
             }
     }
 }
        private static void LoadGameItems(FileTableContainer tableContainer, CFStorage storage, int count, string storagePrefix)
        {
            for (var i = 0; i < count; i++)
            {
                var itemName = $"{storagePrefix}{i}";
                storage.TryGetStream(itemName, out var itemStream);
                if (itemStream == null)
                {
                    Logger.Warn("Could not find stream {0}, skipping.", itemName);
                    continue;
                }
                var itemData = itemStream.GetData();
                if (itemData.Length < 4)
                {
                    Logger.Warn("Skipping {itemName} because it has size of {itemDataLength}.", itemName, itemData.Length);
                    continue;
                }

                var reader = new BinaryReader(new MemoryStream(itemData));

                // parse to enum
                var iItemType = reader.ReadInt32();
                if (!Enum.IsDefined(typeof(ItemType), iItemType))
                {
                    Logger.Info("Invalid item type " + iItemType);
                    return;
                }

                var itemType = (ItemType)iItemType;
                switch (itemType)
                {
                case ItemType.Bumper: {
                    var item = new VisualPinball.Engine.VPT.Bumper.Bumper(reader, itemName);
                    tableContainer.Add(item);
                    break;
                }

                case ItemType.Decal: {
                    tableContainer.Add(new VisualPinball.Engine.VPT.Decal.Decal(reader, itemName));
                    break;
                }

                case ItemType.DispReel: {
                    var item = new VisualPinball.Engine.VPT.DispReel.DispReel(reader, itemName);
                    tableContainer.Add(item);
                    break;
                }

                case ItemType.Flasher: {
                    var item = new VisualPinball.Engine.VPT.Flasher.Flasher(reader, itemName);
                    tableContainer.Add(item);
                    break;
                }

                case ItemType.Flipper: {
                    var item = new VisualPinball.Engine.VPT.Flipper.Flipper(reader, itemName);
                    tableContainer.Add(item);
                    break;
                }

                case ItemType.Gate: {
                    var item = new VisualPinball.Engine.VPT.Gate.Gate(reader, itemName);
                    tableContainer.Add(item);
                    break;
                }

                case ItemType.HitTarget: {
                    var item = new VisualPinball.Engine.VPT.HitTarget.HitTarget(reader, itemName);
                    tableContainer.Add(item);
                    break;
                }

                case ItemType.Kicker: {
                    var item = new VisualPinball.Engine.VPT.Kicker.Kicker(reader, itemName);
                    tableContainer.Add(item);
                    break;
                }

                case ItemType.Light: {
                    var item = new VisualPinball.Engine.VPT.Light.Light(reader, itemName);
                    tableContainer.Add(item);
                    break;
                }

                case ItemType.LightSeq: {
                    var item = new VisualPinball.Engine.VPT.LightSeq.LightSeq(reader, itemName);
                    tableContainer.Add(item);
                    break;
                }

                case ItemType.Plunger: {
                    var item = new VisualPinball.Engine.VPT.Plunger.Plunger(reader, itemName);
                    tableContainer.Add(item);
                    break;
                }

                case ItemType.Primitive: {
                    var item = new Primitive.Primitive(reader, itemName);
                    tableContainer.Add(item);
                    break;
                }

                case ItemType.Ramp: {
                    var item = new Ramp.Ramp(reader, itemName);
                    tableContainer.Add(item);
                    break;
                }

                case ItemType.Rubber: {
                    var item = new Rubber.Rubber(reader, itemName);
                    tableContainer.Add(item);
                    break;
                }

                case ItemType.Spinner: {
                    var item = new Spinner.Spinner(reader, itemName);
                    tableContainer.Add(item);
                    break;
                }

                case ItemType.Surface: {
                    var item = new Surface.Surface(reader, itemName);
                    tableContainer.Add(item);
                    break;
                }

                case ItemType.TextBox: {
                    var item = new TextBox.TextBox(reader, itemName);
                    tableContainer.Add(item);
                    break;
                }

                case ItemType.Timer: {
                    var item = new Timer.Timer(reader, itemName);
                    tableContainer.Add(item);
                    break;
                }

                case ItemType.Trigger: {
                    var item = new Trigger.Trigger(reader, itemName);
                    tableContainer.Add(item);
                    break;
                }

                case ItemType.Trough: {
                    var item = new Trough.Trough(reader, itemName);
                    tableContainer.Add(item);
                    break;
                }

                case ItemType.MetalWireGuide:
                {
                    var item = new MetalWireGuide.MetalWireGuide(reader, itemName);
                    tableContainer.Add(item);
                    break;
                }
                }
            }
        }