// // Versions.cs // // Author: // fjy // // Copyright (c) 2020 fjy // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System; using System.Collections.Generic; using System.IO; using UnityEngine; namespace libx { public enum VerifyBy { Size, Hash, } public static class Versions { public const string Dataname = "res"; public const string Filename = "ver"; public static readonly VerifyBy verifyBy = VerifyBy.Hash; private static readonly VDisk _disk = new VDisk (); private static readonly Dictionary _updateData = new Dictionary (); private static readonly Dictionary _baseData = new Dictionary (); public static AssetBundle LoadAssetBundleFromFile (string url) { if (!File.Exists (url)) { if (_disk != null) { string name = Path.GetFileName (url); VFile file = _disk.GetFile (name, string.Empty); if (file != null) { return AssetBundle.LoadFromFile (_disk.name, 0, (ulong)file.offset); } } } return AssetBundle.LoadFromFile (url); } public static AssetBundleCreateRequest LoadAssetBundleFromFileAsync (string url) { if (!File.Exists (url)) { if (_disk != null) { string name = Path.GetFileName (url); VFile file = _disk.GetFile (name, string.Empty); if (file != null) { return AssetBundle.LoadFromFileAsync (_disk.name, 0, (ulong)file.offset); } } } return AssetBundle.LoadFromFileAsync (url); } public static void BuildVersions (string outputPath, string[] bundles, int version) { string path = outputPath + "/" + Filename; if (File.Exists (path)) { File.Delete (path); } string dataPath = outputPath + "/" + Dataname; if (File.Exists (dataPath)) { File.Delete (dataPath); } VDisk disk = new VDisk (); foreach (string file in bundles) { using (FileStream fs = File.OpenRead (outputPath + "/" + file)) { disk.AddFile (file, fs.Length, Utilitys.GetCRC32Hash (fs)); } } disk.name = dataPath; disk.Save (); using (FileStream stream = File.OpenWrite (path)) { BinaryWriter writer = new BinaryWriter (stream); writer.Write (version); writer.Write (disk.files.Count + 1); using (FileStream fs = File.OpenRead (dataPath)) { VFile file = new VFile { name = Dataname, len = fs.Length, hash = Utilitys.GetCRC32Hash (fs) }; file.Serialize (writer); } foreach (VFile file in disk.files) { file.Serialize (writer); } } } public static int LoadVersion (string filename) { if (!File.Exists (filename)) return -1; try { using (FileStream stream = File.OpenRead (filename)) { BinaryReader reader = new BinaryReader (stream); if (reader.BaseStream.Position != reader.BaseStream.Length) { return reader.ReadInt32(); } return -1; } } catch (Exception e) { Debug.LogException(e); return -1; } } public static List LoadVersions (string filename, bool update = false) { string rootDir = Path.GetDirectoryName(filename); var data = update ? _updateData : _baseData; data.Clear (); using (FileStream stream = File.OpenRead (filename)) { BinaryReader reader = new BinaryReader (stream); var list = new List (); int ver = reader.ReadInt32 (); Debug.Log ($"LoadVersions:{ver} \n{filename}"); int count = reader.ReadInt32 (); for (int i = 0; i < count; i++) { VFile version = new VFile (); version.Deserialize (reader); list.Add (version); data [version.name] = version; string dir = string.Format("{0}/{1}", rootDir, Path.GetDirectoryName(version.name)); if (! Directory.Exists(dir)) { Directory.CreateDirectory(dir); } } return list; } } public static void UpdateDisk(string savePath, List newFiles) { var saveFiles = new List (); var files = _disk.files; foreach (VFile file in files) { if (_updateData.ContainsKey (file.name)) { saveFiles.Add (file); } } _disk.Update(savePath, newFiles, saveFiles); } public static bool LoadDisk (string filename) { return _disk.Load (filename); } public static bool IsNew (string path, long len, string hash) { VFile file; string key = Path.GetFileName (path); if (_baseData.TryGetValue (key, out file)) { if (key.Equals (Dataname) || file.len == len && file.hash.Equals (hash, StringComparison.OrdinalIgnoreCase)) { return false; } } if (_disk.Exists ()) { VFile vdf = _disk.GetFile (path, hash); if (vdf != null && vdf.len == len && vdf.hash.Equals (hash, StringComparison.OrdinalIgnoreCase)) { return false; } } if (!File.Exists (path)) { return true; } using (FileStream stream = File.OpenRead (path)) { if (stream.Length != len) { return true; } if (verifyBy != VerifyBy.Hash) return false; return !Utilitys.GetCRC32Hash (stream).Equals (hash, StringComparison.OrdinalIgnoreCase); } } } }