using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; namespace AssetStudio { public static class ResourceIndex { public static Dictionary> BundleDependencyMap; public static Dictionary BlockInfoMap; public static Dictionary BlockMap; public static Dictionary AssetMap; public static List> AssetLocationMap; public static List BlockSortList; static ResourceIndex() { BlockSortList = new List(); AssetMap = new Dictionary(); AssetLocationMap = new List>(0x100); for (int i = 0; i < AssetLocationMap.Capacity; i++) { AssetLocationMap.Add(new Dictionary(0x1FF)); } BundleDependencyMap = new Dictionary>(); BlockInfoMap = new Dictionary(); BlockMap = new Dictionary(); } public static void FromFile(string path) { if (!string.IsNullOrEmpty(path)) { Logger.Info(string.Format("Parsing....")); try { Clear(); using (var stream = File.OpenRead(path)) { var bytes = new byte[stream.Length]; var count = stream.Read(bytes, 0, bytes.Length); if (count != bytes.Length) throw new Exception("Error While Reading AssetIndex"); var json = Encoding.UTF8.GetString(bytes); var obj = JsonConvert.DeserializeObject(json); if (obj != null) { MapToResourceIndex(obj); } } } catch (Exception e) { Logger.Error("AssetIndex was not loaded"); Console.WriteLine(e.ToString()); return; } Logger.Info("Loaded !!"); } } public static void Clear() { BundleDependencyMap.Clear(); BlockInfoMap.Clear(); BlockMap.Clear(); AssetMap.Clear(); AssetLocationMap.ForEach(x => x.Clear()); BlockSortList.Clear(); } public static void MapToResourceIndex(AssetIndex assetIndex) { BundleDependencyMap = assetIndex.Dependencies; BlockSortList = assetIndex.SortList.ConvertAll(x => (int)x); foreach (var asset in assetIndex.SubAssets) { foreach (var subAsset in asset.Value) { var bundleInfo = new BundleInfo() { Bundle = asset.Key, Path = subAsset.Name }; AssetLocationMap[subAsset.PathHashPre].Add(subAsset.PathHashLast, bundleInfo); AssetMap[subAsset.PathHashLast] = ((ulong)subAsset.PathHashLast) << 8 | subAsset.PathHashPre; } } foreach (var asset in assetIndex.Assets) { var block = new Block() { Id = (int)asset.Value.Id, Offset = (int)asset.Value.Offset }; BlockInfoMap.Add(asset.Key, block); if (!BlockMap.ContainsKey((int)asset.Value.Id)) BlockMap.Add((int)asset.Value.Id, asset.Value.Language); } } public static List GetAllAssets() => AssetLocationMap.SelectMany(x => x.Values).ToList(); public static List GetAssets(int bundle) => AssetLocationMap.SelectMany(x => x.Values).Where(x => x.Bundle == bundle).ToList(); public static ulong GetAssetIndex(ulong blkHash) => AssetMap.TryGetValue(blkHash, out var value) ? value : 0; public static Block GetBlockInfo(int bundle) => BlockInfoMap.TryGetValue(bundle, out var blk) ? blk : null; public static BlockFile GetBlockFile(int id) => BlockMap.TryGetValue(id, out var languageCode) ? new BlockFile() { LanguageCode = languageCode, Id = id } : null; public static int GetBlockID(int bundle) => BlockInfoMap.TryGetValue(bundle, out var block) ? block.Id : 0; public static List GetBundleDep(int bundle) => BundleDependencyMap.TryGetValue(bundle, out var dep) ? dep : new List(); public static BundleInfo GetBundleInfo(ulong hash) { var asset = new Asset() { Hash = hash }; if (AssetLocationMap.ElementAtOrDefault(asset.Pre) != null) if (AssetLocationMap[asset.Pre].TryGetValue(asset.Last, out var bundleInfo)) return bundleInfo; return null; } public static string GetBundlePath(uint last) { foreach (var location in AssetLocationMap) if (location.TryGetValue(last, out var bundleInfo)) return bundleInfo.Path; return null; } public static List GetAllAssetIndices(int bundle) { var hashes = new List(); foreach (var location in AssetLocationMap) foreach (var pair in location) if (pair.Value.Bundle == bundle) hashes.Add(pair.Key); return hashes; } public static List GetBundles(int id) { var bundles = new List(); foreach (var block in BlockInfoMap) if (block.Value.Id == id) bundles.Add(block.Key); return bundles; } public static void GetDepBundles(ref List bundles) { for (int i = 0; i < bundles.Count; i++) { var bundle = bundles[i]; bundles.AddRange(GetBundleDep(bundle)); } bundles = bundles.Distinct().ToList(); } public static bool CheckIsLegitAssetPath(ulong hash) { var asset = new Asset() { Hash = hash }; return AssetLocationMap.ElementAtOrDefault(asset.Pre).ContainsKey(asset.Last); } public static string GetContainerFromBinName(string binName) { var lastHex = Convert.ToUInt32(binName, 16); var index = GetAssetIndex(lastHex); var bundleInfo = GetBundleInfo(index); return bundleInfo != null ? bundleInfo.Path : ""; } } public class BundleInfo { public int Bundle; public string Path; } public class Asset { public ulong Hash; public uint Last => (uint)(Hash >> 8); public byte Pre => (byte)(Hash & 0xFF); } public class Block { public int Id; public int Offset; } public class BlockFile { public int LanguageCode; public int Id; } public class AssetIndex { public Dictionary Types { get; set; } public class SubAssetInfo { public string Name { get; set; } public byte PathHashPre { get; set; } public uint PathHashLast { get; set; } } public Dictionary> SubAssets { get; set; } public Dictionary> Dependencies { get; set; } public List PreloadBlocks { get; set; } public List PreloadShaderBlocks { get; set; } public class BlockInfo { public byte Language { get; set; } public uint Id { get; set; } public uint Offset { get; set; } } public Dictionary Assets { get; set; } public List SortList { get; set; } } }