// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. namespace WixToolset.BuildTasks { using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Xml; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using WixToolset.Dtf.WindowsInstaller; using Microsoft.Win32; /// /// This task assigns Culture metadata to files based on the value of the Culture attribute on the /// WixLocalization element inside the file. /// public class GetLooseFileList : Task { private ITaskItem database; private ITaskItem[] looseFileList; internal const int MsidbFileAttributesNoncompressed = 8192; internal const int MsidbFileAttributesCompressed = 16384; /// /// The list of database files to find Loose Files in /// [Required] public ITaskItem Database { get { return this.database; } set { this.database = value; } } /// /// The total list of Loose Files in this database /// [Output] public ITaskItem[] LooseFileList { get { return this.looseFileList; } } /// /// Takes the "defaultDir" column /// /// Returns the corresponding sourceDir. public string SourceDirFromDefaultDir(string defaultDir) { string sourceDir; string[] splitted = defaultDir.Split(':'); if (1 == splitted.Length) { sourceDir = splitted[0]; } else { sourceDir = splitted[1]; } splitted = sourceDir.Split('|'); if (1 == splitted.Length) { sourceDir = splitted[0]; } else { sourceDir = splitted[1]; } return sourceDir; } /// /// Takes the "FileName" column /// /// Returns the corresponding source file name. public string SourceFileFromFileName(string fileName) { string sourceFile; string[] splitted = fileName.Split('|'); if (1 == splitted.Length) { sourceFile = splitted[0]; } else { sourceFile = splitted[1]; } return sourceFile; } /// /// Gets a complete list of external Loose Files referenced by the given installer database file. /// /// True upon completion of the task execution. public override bool Execute() { string databaseFile = this.database.ItemSpec; Object []emptyArgs = { }; System.Collections.Generic.List looseFileNames = new System.Collections.Generic.List(); Dictionary ComponentFullDirectory = new Dictionary(); Dictionary DirectoryIdDefaultDir = new Dictionary(); Dictionary DirectoryIdParent = new Dictionary(); Dictionary DirectoryIdFullSource = new Dictionary(); int i; string databaseDir = Path.GetDirectoryName(databaseFile); // If the file doesn't exist, no Loose Files to return, so exit now if (!File.Exists(databaseFile)) { return true; } using (Database database = new Database(databaseFile)) { bool compressed = false; if (2 == (database.SummaryInfo.WordCount & 2)) { compressed = true; } // If the media table doesn't exist, no Loose Files to return, so exit now if (null == database.Tables["File"]) { return true; } // Only setup all these helpful indexes if the database is marked as uncompressed. If it's marked as compressed, files are stored at the root, // so none of these indexes will be used if (!compressed) { if (null == database.Tables["Directory"] || null == database.Tables["Component"]) { return true; } System.Collections.IList directoryRecords = database.ExecuteQuery("SELECT `Directory`,`Directory_Parent`,`DefaultDir` FROM `Directory`", emptyArgs); // First setup a simple index from DirectoryId to DefaultDir for (i = 0; i < directoryRecords.Count; i += 3) { string directoryId = (string)(directoryRecords[i]); string directoryParent = (string)(directoryRecords[i + 1]); string defaultDir = (string)(directoryRecords[i + 2]); string sourceDir = SourceDirFromDefaultDir(defaultDir); DirectoryIdDefaultDir[directoryId] = sourceDir; DirectoryIdParent[directoryId] = directoryParent; } // Setup an index from directory Id to the full source path for (i = 0; i < directoryRecords.Count; i += 3) { string directoryId = (string)(directoryRecords[i]); string directoryParent = (string)(directoryRecords[i + 1]); string defaultDir = (string)(directoryRecords[i + 2]); string sourceDir = DirectoryIdDefaultDir[directoryId]; // The TARGETDIR case if (String.IsNullOrEmpty(directoryParent)) { DirectoryIdFullSource[directoryId] = databaseDir; } else { string tempDirectoryParent = directoryParent; while (!String.IsNullOrEmpty(tempDirectoryParent) && !String.IsNullOrEmpty(DirectoryIdParent[tempDirectoryParent])) { sourceDir = Path.Combine(DirectoryIdDefaultDir[tempDirectoryParent], sourceDir); tempDirectoryParent = DirectoryIdParent[tempDirectoryParent]; } DirectoryIdFullSource[directoryId] = Path.Combine(databaseDir, sourceDir); } } // Setup an index from component Id to full directory path System.Collections.IList componentRecords = database.ExecuteQuery("SELECT `Component`,`Directory_` FROM `Component`", emptyArgs); for (i = 0; i < componentRecords.Count; i += 2) { string componentId = (string)(componentRecords[i]); string componentDir = (string)(componentRecords[i + 1]); ComponentFullDirectory[componentId] = DirectoryIdFullSource[componentDir]; } } System.Collections.IList fileRecords = database.ExecuteQuery("SELECT `Component_`,`FileName`,`Attributes` FROM `File`", emptyArgs); for (i = 0; i < fileRecords.Count; i += 3) { string componentId = (string)(fileRecords[i]); string fileName = SourceFileFromFileName((string)(fileRecords[i + 1])); int attributes = (int)(fileRecords[i + 2]); // If the whole database is marked uncompressed, use the directory layout made above if ((!compressed && MsidbFileAttributesCompressed != (attributes & MsidbFileAttributesCompressed))) { looseFileNames.Add(new TaskItem(Path.GetFullPath(Path.Combine(ComponentFullDirectory[componentId], fileName)))); } // If the database is marked as compressed, put files at the root else if (compressed && (MsidbFileAttributesNoncompressed == (attributes & MsidbFileAttributesNoncompressed))) { looseFileNames.Add(new TaskItem(Path.GetFullPath(Path.Combine(databaseDir, fileName)))); } } } this.looseFileList = looseFileNames.ToArray(); return true; } } }