// 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;
}
}
}