// --- load --- /// <summary>Loads a database from a byte array.</summary> /// <param name="bytes">The byte array.</param> /// <returns>The database.</returns> internal static Database Load(byte[] bytes) { /* * Parse the enclosing file format that holds the * compressed (gzip) data and then decompress the data. * */ int version; byte[] compressed; byte[] md5; using (MemoryStream stream = new MemoryStream(bytes)) { using (BinaryReader reader = new BinaryReader(stream, Encoding.UTF8)) { // 0x5453494C5F46535 = "TSF_LIST" if (reader.ReadUInt64() != 0x5453494C5F465354) { throw new InvalidDataException("The identifier in the header is invalid."); } version = reader.ReadInt32(); if (version != 2) { throw new InvalidDataException("The version number in the header is invalid."); } md5 = reader.ReadBytes(16); int length = reader.ReadInt32(); compressed = reader.ReadBytes(length); // 0x444E455F = "_END" if (reader.ReadUInt32() != 0x444E455F) { throw new InvalidDataException("The identifier in the footer is invalid."); } } } byte[] check = (new MD5CryptoServiceProvider()).ComputeHash(compressed); for (int i = 0; i < 16; i++) { if (md5[i] != check[i]) { throw new InvalidDataException("The MD5 does not match."); } } byte[] decompressed = Gzip.Decompress(compressed); /* * Parse the raw file format and extract the database. * */ Database database = new Database(); using (MemoryStream stream = new MemoryStream(decompressed)) { using (BinaryReader reader = new BinaryReader(stream)) { // 0x74727473 = "strt" if (reader.ReadUInt32() != 0x74727473) { throw new InvalidDataException("The uncompressed stream is invalid."); } database.Packages = new Package[reader.ReadInt32()]; for (int i = 0; i < database.Packages.Length; i++) { database.Packages[i] = new Package(); database.Packages[i].Name = reader.ReadString(); database.Packages[i].Versions = new Version[reader.ReadInt32()]; for (int j = 0; j < database.Packages[i].Versions.Length; j++) { database.Packages[i].Versions[j] = new Version(); database.Packages[i].Versions[j].Name = database.Packages[i].Name; database.Packages[i].Versions[j].Number = reader.ReadString(); database.Packages[i].Versions[j].Sources = new Source[reader.ReadInt32()]; for (int k = 0; k < database.Packages[i].Versions[j].Sources.Length; k++) { database.Packages[i].Versions[j].Sources[k].Size = reader.ReadInt32(); database.Packages[i].Versions[j].Sources[k].MD5 = reader.ReadBytes(16); database.Packages[i].Versions[j].Sources[k].Url = reader.ReadString(); } database.Packages[i].Versions[j].Dependencies = new Dependency[reader.ReadInt32()]; for (int k = 0; k < database.Packages[i].Versions[j].Dependencies.Length; k++) { database.Packages[i].Versions[j].Dependencies[k] = new Dependency(); database.Packages[i].Versions[j].Dependencies[k].Name = reader.ReadString(); database.Packages[i].Versions[j].Dependencies[k].Version = reader.ReadString(); } database.Packages[i].Versions[j].Suggestions = new Dependency[reader.ReadInt32()]; for (int k = 0; k < database.Packages[i].Versions[j].Suggestions.Length; k++) { database.Packages[i].Versions[j].Suggestions[k] = new Dependency(); database.Packages[i].Versions[j].Suggestions[k].Name = reader.ReadString(); database.Packages[i].Versions[j].Suggestions[k].Version = reader.ReadString(); } database.Packages[i].Versions[j].Metadata = new KeyValuePair[reader.ReadInt32()]; for (int k = 0; k < database.Packages[i].Versions[j].Metadata.Length; k++) { string key = reader.ReadString(); string value = reader.ReadString(); database.Packages[i].Versions[j].Metadata[k] = new KeyValuePair(key, value); } } } if (reader.ReadUInt32() != 0x646E655F) { throw new InvalidDataException("The uncompressed stream is invalid."); } } } return(database); }
/// <summary>Installs the specified package, but not its dependencies.</summary> /// <param name="version">The specific version of the package.</param> /// <param name="size">Accumulates the size of the downloaded data in an interlocked operation. If the operation fails, all accumulated size is subtracted again.</param> /// <returns>Whether the package was successfully installed.</returns> internal bool InstallPackage(Version version, ref int size) { try { /* * Check whether the package is protected. * */ if (IsInstalledPackageProtected(version.Name)) { return(false); } /* * Remove existing versions of this package. */ string packageDirectory = GetInstalledPackageDirectory(version.Name); if (packageDirectory != null) { RemoveDirectory(packageDirectory); } else { packageDirectory = Path.Combine(Program.FileSystem.ManagedContentFolders[0], version.Name); } /* * Install the package. * */ Source[] sources = version.Sources; Shuffle(sources); int attemps = sources.Length == 1 ? 3 : sources.Length == 2 ? 2 : 1; for (int a = 0; a < attemps; a++) { for (int i = 0; i < sources.Length; i++) { byte[] bytes; if (Internet.TryDownloadBytesFromUrl(sources[i].Url, out bytes, ref size)) { if (bytes.Length == sources[i].Size) { byte[] md5 = (new MD5CryptoServiceProvider()).ComputeHash(bytes); bool add = true; for (int k = 0; k < 16; k++) { if (md5[k] != sources[i].Md5[k]) { add = false; break; } } if (add) { if (bytes.Length >= 2 && bytes[0] == 0x1F && bytes[1] == 0x8B) { /* GZIP-compressed */ bytes = Gzip.Decompress(bytes); } if ((bytes.Length & 0x1FF) == 0) { /* TAR archive */ RemoveDirectory(packageDirectory); CreateDirectory(packageDirectory); Tar.Unpack(bytes, packageDirectory, version.Name); } else { add = false; } if (add) { /* * The installation was successful. Save some of * the package information for offline storage. * */ StringBuilder builder = new StringBuilder(); builder.AppendLine("name = " + version.Name); builder.AppendLine("version = " + version.Number); if (version.Sources.Length != 0) { builder.Append("sources = "); for (int j = 0; j < version.Sources.Length; j++) { if (j != 0) { builder.Append(',').Append(' '); } builder.Append(version.Sources[j].Url); } builder.AppendLine(); } if (version.Dependencies.Length != 0) { builder.Append("dependencies = "); for (int j = 0; j < version.Dependencies.Length; j++) { if (j != 0) { builder.Append(',').Append(' '); } builder.Append(version.Dependencies[j].Name); builder.Append(' ').Append('('); builder.Append(version.Dependencies[j].Version); builder.Append(')'); } builder.AppendLine(); } if (version.Suggestions.Length != 0) { builder.Append("suggestions = "); for (int j = 0; j < version.Suggestions.Length; j++) { if (j != 0) { builder.Append(',').Append(' '); } builder.Append(version.Suggestions[j].Name); builder.Append(' ').Append('('); builder.Append(version.Suggestions[j].Version); builder.Append(')'); } builder.AppendLine(); } builder.AppendLine(); foreach (KeyValuePair pair in version.Metadata) { if ( !string.Equals(pair.Key, "protected", StringComparison.OrdinalIgnoreCase) && !string.Equals(pair.Key, "wip", StringComparison.OrdinalIgnoreCase) ) { builder.AppendLine(pair.ToString()); } } string file = Path.Combine(packageDirectory, "package.cfg"); File.WriteAllText(file, builder.ToString(), new UTF8Encoding(true)); return(true); } } } Interlocked.Add(ref size, -bytes.Length); } } Thread.Sleep(1000); } return(false); } catch { return(false); } }