public static void Encode(
            this AssetLibraryManager assetLibraryManager,
            BitWriter writer,
            Platform platform,
            AssetGroup group,
            Items.PackedAssetReference value)
        {
            if (writer == null)
            {
                throw new ArgumentNullException(nameof(writer));
            }

            var config = assetLibraryManager.Configurations[group];

            uint index;

            if (value == Items.PackedAssetReference.None)
            {
                index = config.NoneIndex;
            }
            else
            {
                index  = 0;
                index |= (((uint)value.AssetIndex) & config.AssetMask) << 0;
                index |= (((uint)value.SublibraryIndex) & config.SublibraryMask) << config.AssetBits;

                if (value.UseSetId == true)
                {
                    index |= 1u << config.AssetBits + config.SublibraryBits - 1;
                }
            }

            writer.WriteUInt32(index, config.SublibraryBits + config.AssetBits);
        }
        private static bool GetIndex(this AssetLibraryManager alm,
                                     int setId,
                                     AssetGroup group,
                                     string package,
                                     string asset,
                                     out uint index)
        {
            var config = alm.Configurations[group];

            var set     = alm.GetSet(setId);
            var library = set.Libraries[group];

            var sublibrary =
                library.Sublibraries.FirstOrDefault(sl => sl.Package == package && sl.Assets.Contains(asset));

            if (sublibrary == null)
            {
                index = 0;
                return(false);
            }

            var sublibraryIndex = library.Sublibraries.IndexOf(sublibrary);
            var assetIndex      = sublibrary.Assets.IndexOf(asset);

            index  = 0;
            index |= (((uint)assetIndex) & config.AssetMask) << 0;
            index |= (((uint)sublibraryIndex) & config.SublibraryMask) << config.AssetBits;

            if (setId != 0)
            {
                index |= 1u << config.AssetBits + config.SublibraryBits - 1;
            }

            return(true);
        }
        public static string Decode(this AssetLibraryManager alm, BitReader reader, int setId, AssetGroup group)
        {
            var config = alm.Configurations[group];

            var index = reader.ReadUInt32(config.SublibraryBits + config.AssetBits);

            if (index == config.NoneIndex)
            {
                return("None");
            }

            var assetIndex      = (int)((index >> 0) & config.AssetMask);
            var sublibraryIndex = (int)((index >> config.AssetBits) & config.SublibraryMask);
            var useSetId        = ((index >> config.AssetBits) & config.UseSetIdMask) != 0;

            var set     = alm.GetSet(useSetId == false ? 0 : setId);
            var library = set.Libraries[group];

            if (sublibraryIndex < 0 || sublibraryIndex >= library.Sublibraries.Count)
            {
                throw new ArgumentOutOfRangeException();
            }

            return(library.Sublibraries[sublibraryIndex].GetAsset(assetIndex));
        }
        private static bool GetIndex(this AssetLibraryManager assetLibraryManager,
                                     Platform platform,
                                     int setId,
                                     AssetGroup group,
                                     string package,
                                     string asset,
                                     out Items.PackedAssetReference packed)
        {
            var set = assetLibraryManager.GetSet(setId);

            if (set == null)
            {
                packed = Items.PackedAssetReference.None;
                return(false);
            }

            var library = set.Libraries[group];

            var sublibrary =
                library.Sublibraries.FirstOrDefault(sl => sl.Package == package && sl.Assets.Contains(asset) == true);

            if (sublibrary == null)
            {
                packed = Items.PackedAssetReference.None;
                return(false);
            }
            var sublibraryIndex = library.Sublibraries.IndexOf(sublibrary);
            var assetIndex      = sublibrary.Assets.IndexOf(asset);

            var platformConfig = InfoManager.PlatformConfigurations.GetOrDefault(platform);

            if (platformConfig != null)
            {
                var platformSet = platformConfig.GetSet(setId);
                if (platformSet != null &&
                    platformSet.Libraries.ContainsKey(group) == true)
                {
                    var platformLibrary = platformSet.Libraries[group];
                    if (platformLibrary.SublibraryRemappingAtoB != null &&
                        platformLibrary.SublibraryRemappingAtoB.Count > 0)
                    {
                        if (platformLibrary.SublibraryRemappingAtoB.ContainsKey(sublibraryIndex) == false)
                        {
                            throw new InvalidOperationException(
                                      string.Format("don't know how to remap sublibrary {0} for set {1}!",
                                                    sublibraryIndex,
                                                    setId));
                        }
                        sublibraryIndex = platformLibrary.SublibraryRemappingAtoB[sublibraryIndex];
                    }
                }
            }

            packed = new Items.PackedAssetReference(assetIndex, sublibraryIndex, setId != 0);
            return(true);
        }
        public static string Lookup(
            this AssetLibraryManager assetLibraryManager,
            Items.PackedAssetReference packed,
            Platform platform,
            int setId,
            AssetGroup group)
        {
            if (packed == Items.PackedAssetReference.None)
            {
                return("None");
            }

            var assetIndex      = packed.AssetIndex;
            var sublibraryIndex = packed.SublibraryIndex;
            var useSetId        = packed.UseSetId;
            var actualSetId     = useSetId == false ? 0 : setId;

            var platformConfig = InfoManager.PlatformConfigurations.GetOrDefault(platform);

            if (platformConfig != null)
            {
                var platformSet = platformConfig.GetSet(actualSetId);
                if (platformSet != null &&
                    platformSet.Libraries.ContainsKey(group) == true)
                {
                    var platformLibrary = platformSet.Libraries[group];
                    if (platformLibrary.SublibraryRemappingBtoA != null &&
                        platformLibrary.SublibraryRemappingBtoA.Count > 0)
                    {
                        if (platformLibrary.SublibraryRemappingBtoA.ContainsKey(sublibraryIndex) == false)
                        {
                            throw new InvalidOperationException($"don't know how to remap sublibrary {sublibraryIndex} for set {actualSetId}!");
                        }
                        sublibraryIndex = platformLibrary.SublibraryRemappingBtoA[sublibraryIndex];
                    }
                }
            }

            var set = assetLibraryManager.GetSet(actualSetId);

            if (set == null)
            {
                throw new FormatException($"unknown asset library set {actualSetId} in packed data (this generally means new DLC that is not supported yet)");
            }

            var library = set.Libraries[group];

            if (sublibraryIndex < 0 || sublibraryIndex >= library.Sublibraries.Count)
            {
                throw new ArgumentOutOfRangeException($"invalid sublibrary index {set.Id} in set {sublibraryIndex}");
            }

            return(library.Sublibraries[sublibraryIndex].GetAsset(assetIndex));
        }
        public static void Encode(this AssetLibraryManager alm,
                                  BitWriter writer,
                                  int setId,
                                  AssetGroup group,
                                  string value)
        {
            if (writer == null)
            {
                throw new ArgumentNullException("writer");
            }

            if (string.IsNullOrEmpty(value) == true)
            {
                throw new ArgumentNullException("value");
            }

            var config = alm.Configurations[group];

            uint index;

            if (value == "None")
            {
                index = config.NoneIndex;
            }
            else
            {
                var parts = value.Split(new[]
                {
                    '.'
                },
                                        2);
                if (parts.Length != 2)
                {
                    throw new ArgumentException();
                }

                var package = parts[0];
                var asset   = parts[1];

                if (alm.GetIndex(setId, group, package, asset, out index) == false)
                {
                    if (alm.GetIndex(0, group, package, asset, out index) == false)
                    {
                        throw new ArgumentException("unsupported asset");
                    }
                }
            }

            writer.WriteUInt32(index, config.SublibraryBits + config.AssetBits);
        }
        public static Items.PackedAssetReference Decode(
            this AssetLibraryManager assetLibraryManager,
            BitReader reader,
            Platform platform,
            AssetGroup group)
        {
            var config = assetLibraryManager.Configurations[group];

            var index = reader.ReadUInt32(config.SublibraryBits + config.AssetBits);

            if (index == config.NoneIndex)
            {
                return(Items.PackedAssetReference.None);
            }

            var assetIndex      = (int)((index >> 0) & config.AssetMask);
            var sublibraryIndex = (int)((index >> config.AssetBits) & config.SublibraryMask);
            var useSetId        = ((index >> config.AssetBits) & config.UseSetIdMask) != 0;

            return(new Items.PackedAssetReference(assetIndex, sublibraryIndex, useSetId));
        }
        public static Items.PackedAssetReference Lookup(
            this AssetLibraryManager assetLibraryManager,
            string value,
            Platform platform,
            int setId,
            AssetGroup group)
        {
            if (string.IsNullOrEmpty(value) == true)
            {
                throw new ArgumentNullException("value");
            }

            if (value == "None")
            {
                return(Items.PackedAssetReference.None);
            }

            var parts = value.Split(new[] { '.' }, 2);

            if (parts.Length != 2)
            {
                throw new ArgumentException();
            }

            var package = parts[0];
            var asset   = parts[1];

            Items.PackedAssetReference packed;
            if (assetLibraryManager.GetIndex(platform, setId, @group, package, asset, out packed) == false)
            {
                if (assetLibraryManager.GetIndex(platform, 0, @group, package, asset, out packed) == false)
                {
                    throw new ArgumentException("unsupported asset");
                }
            }
            return(packed);
        }