#region Copyright & License Information /* * Copyright 2007-2015 The OpenRA Developers (see AUTHORS) * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation. For more information, * see COPYING. */ #endregion using System; using System.Collections.Generic; using System.IO; using System.Linq; using ICSharpCode.SharpZipLib; using ICSharpCode.SharpZipLib.Zip; using OpenRA.FileSystem; namespace OpenRA.Mods.Common { public class ContentInstaller : IGlobalModData { public readonly string MenuWidget = null; public readonly string MusicMenuWidget = null; public readonly string BackgroundWidget = null; public readonly string[] TestFiles = { }; public readonly string[] DiskTestFiles = { }; public readonly string PackageToExtractFromCD = null; public readonly bool OverwriteFiles = true; public readonly Dictionary CopyFilesFromCD = new Dictionary(); public readonly Dictionary ExtractFilesFromCD = new Dictionary(); public readonly string PackageMirrorList = null; public readonly string MusicPackageMirrorList = null; public readonly int ShippedSoundtracks = 0; /// InstallShield .cab File Ids, used to extract Mod specific files. public readonly int[] InstallShieldCABFileIds = { }; /// InstallShield .cab File Ids, used to extract Mod specific archives and extract contents of ExtractFilesFromCD. public readonly string[] InstallShieldCABFilePackageIds = { }; } public static class InstallUtils { static IEnumerable GetEntries(this ZipInputStream z) { for (;;) { var e = z.GetNextEntry(); if (e != null) yield return e; else break; } } public static string GetMountedDisk(Func isValidDisk) { var volumes = DriveInfo.GetDrives() .Where(v => v.DriveType == DriveType.CDRom && v.IsReady) .Select(v => v.RootDirectory.FullName); return volumes.FirstOrDefault(isValidDisk); } // TODO: The package should be mounted into its own context to avoid name collisions with installed files public static bool ExtractFromPackage(string srcPath, string package, string annotation, Dictionary filesByDirectory, string destPath, bool overwrite, Action onProgress, Action onError) { if (!Directory.Exists(destPath)) Directory.CreateDirectory(destPath); Log.Write("debug", "Mounting {0}".F(srcPath)); GlobalFileSystem.Mount(srcPath); Log.Write("debug", "Mounting {0}".F(package)); GlobalFileSystem.Mount(package, annotation); foreach (var directory in filesByDirectory) { var targetDir = directory.Key; foreach (var file in directory.Value) { var dest = Path.Combine(destPath, targetDir, file.ToLowerInvariant()); if (File.Exists(dest)) { if (overwrite) File.Delete(dest); else { Log.Write("debug", "Skipping {0}".F(dest)); continue; } } using (var sourceStream = GlobalFileSystem.Open(file)) using (var destStream = File.Create(dest)) { Log.Write("debug", "Extracting {0} to {1}".F(file, dest)); onProgress("Extracting " + file); destStream.Write(sourceStream.ReadAllBytes()); } } } return true; } public static bool CopyFiles(string srcPath, Dictionary files, string destPath, bool overwrite, Action onProgress, Action onError) { foreach (var folder in files) { var targetDir = folder.Key; foreach (var file in folder.Value) { var sourcePath = Path.Combine(srcPath, file); if (!File.Exists(sourcePath)) { onError("Cannot find " + file); return false; } var destFile = Path.GetFileName(file); var dest = Path.Combine(destPath, targetDir, destFile.ToLowerInvariant()); if (File.Exists(dest) && !overwrite) { Log.Write("debug", "Skipping {0}".F(dest)); continue; } onProgress("Copying " + destFile); Log.Write("debug", "Copy {0} to {1}".F(sourcePath, dest)); File.Copy(sourcePath, dest, true); } } return true; } public static bool ExtractZip(string zipFile, string dest, Action onProgress, Action onError) { if (!File.Exists(zipFile)) { onError("Invalid path: " + zipFile); return false; } var extracted = new List(); try { using (var stream = File.OpenRead(zipFile)) using (var z = new ZipInputStream(stream)) z.ExtractZip(dest, extracted, s => onProgress("Extracting " + s)); } catch (SharpZipBaseException) { foreach (var f in extracted) File.Delete(f); onError("Invalid archive"); return false; } return true; } // TODO: this belongs in FileSystem/ZipFile static void ExtractZip(this ZipInputStream z, string destPath, List extracted, Action onProgress) { foreach (var entry in z.GetEntries()) { if (!entry.IsFile) continue; onProgress(entry.Name); Directory.CreateDirectory(Path.Combine(destPath, Path.GetDirectoryName(entry.Name))); var path = Path.Combine(destPath, entry.Name); extracted.Add(path); using (var f = File.Create(path)) z.CopyTo(f); } z.Close(); } } }