diff --git a/.idea/.idea.DiscImageChef/.idea/contentModel.xml b/.idea/.idea.DiscImageChef/.idea/contentModel.xml
index 89977601f..7fbf5a66e 100644
--- a/.idea/.idea.DiscImageChef/.idea/contentModel.xml
+++ b/.idea/.idea.DiscImageChef/.idea/contentModel.xml
@@ -1641,6 +1641,8 @@
+
+
diff --git a/DiscImageChef.Gui/Forms/frmMain.xeto.cs b/DiscImageChef.Gui/Forms/frmMain.xeto.cs
index b6b363243..0f962de37 100644
--- a/DiscImageChef.Gui/Forms/frmMain.xeto.cs
+++ b/DiscImageChef.Gui/Forms/frmMain.xeto.cs
@@ -37,6 +37,7 @@ using System.IO;
using System.Linq;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Interfaces;
+using DiscImageChef.CommonTypes.Structs;
using DiscImageChef.Console;
using DiscImageChef.Core;
using DiscImageChef.Core.Media.Info;
@@ -47,6 +48,7 @@ using DiscImageChef.Gui.Panels;
using Eto.Drawing;
using Eto.Forms;
using Eto.Serialization.Xaml;
+using FileAttributes = DiscImageChef.CommonTypes.Structs.FileAttributes;
using ImageFormat = DiscImageChef.Core.ImageFormat;
namespace DiscImageChef.Gui.Forms
@@ -252,6 +254,17 @@ namespace DiscImageChef.Gui.Forms
{
plugin.GetInformation(imageFormat, partition, out string information, null);
+ IReadOnlyFilesystem fsPlugin = plugin as IReadOnlyFilesystem;
+
+ if(fsPlugin != null)
+ {
+ Errno error =
+ fsPlugin.Mount(imageFormat, partition, null,
+ new Dictionary());
+
+ if(error != Errno.NoError) fsPlugin = null;
+ }
+
TreeGridItem filesystemGridItem = new TreeGridItem
{
Values = new object[]
@@ -260,10 +273,16 @@ namespace DiscImageChef.Gui.Forms
plugin.XmlFsType.VolumeName is null
? $"{plugin.XmlFsType.Type}"
: $"{plugin.XmlFsType.VolumeName} ({plugin.XmlFsType.Type})",
- null, new pnlFilesystem(plugin.XmlFsType, information)
+ fsPlugin, new pnlFilesystem(plugin.XmlFsType, information)
}
};
+ if(fsPlugin != null)
+ {
+ Core.Statistics.AddCommand("ls");
+ filesystemGridItem.Children.Add(placeholderItem);
+ }
+
Statistics.AddFilesystem(plugin.XmlFsType.Type);
partitionGridItem.Children.Add(filesystemGridItem);
}
@@ -296,6 +315,17 @@ namespace DiscImageChef.Gui.Forms
{
plugin.GetInformation(imageFormat, wholePart, out string information, null);
+ IReadOnlyFilesystem fsPlugin = plugin as IReadOnlyFilesystem;
+
+ if(fsPlugin != null)
+ {
+ Errno error =
+ fsPlugin.Mount(imageFormat, wholePart, null,
+ new Dictionary());
+
+ if(error != Errno.NoError) fsPlugin = null;
+ }
+
TreeGridItem filesystemGridItem = new TreeGridItem
{
Values = new object[]
@@ -304,10 +334,16 @@ namespace DiscImageChef.Gui.Forms
plugin.XmlFsType.VolumeName is null
? $"{plugin.XmlFsType.Type}"
: $"{plugin.XmlFsType.VolumeName} ({plugin.XmlFsType.Type})",
- null, new pnlFilesystem(plugin.XmlFsType, information)
+ fsPlugin, new pnlFilesystem(plugin.XmlFsType, information)
}
};
+ if(fsPlugin != null)
+ {
+ Core.Statistics.AddCommand("ls");
+ filesystemGridItem.Children.Add(placeholderItem);
+ }
+
Statistics.AddFilesystem(plugin.XmlFsType.Type);
imageGridItem.Children.Add(filesystemGridItem);
}
@@ -503,11 +539,11 @@ namespace DiscImageChef.Gui.Forms
return;
}
- if(selectedItem.Parent != devicesRoot) return;
+ if(selectedItem.Values.Length < 4) return;
switch(selectedItem.Values[3])
{
- case null:
+ case null when selectedItem.Parent == devicesRoot:
try
{
Device dev = new Device((string)selectedItem.Values[2]);
@@ -533,19 +569,26 @@ namespace DiscImageChef.Gui.Forms
}
break;
- case string devErrorMessage:
+ case string devErrorMessage when selectedItem.Parent == devicesRoot:
lblError.Text = devErrorMessage;
splMain.Panel2 = lblError;
break;
+ case Dictionary files:
+ splMain.Panel2 = new pnlListFiles(selectedItem.Values[2] as IReadOnlyFilesystem, files,
+ selectedItem.Values[1] as string == "/"
+ ? "/"
+ : selectedItem.Values[4] as string);
+ break;
}
}
protected void OnTreeImagesItemExpanding(object sender, TreeGridViewItemCancelEventArgs e)
{
// First expansion of a device
- if((e.Item as TreeGridItem)?.Children?.Count == 1 &&
- ((TreeGridItem)e.Item).Children[0] == placeholderItem &&
- ((TreeGridItem)e.Item).Parent == devicesRoot)
+ if((e.Item as TreeGridItem)?.Children?.Count != 1 ||
+ ((TreeGridItem)e.Item).Children[0] != placeholderItem) return;
+
+ if(((TreeGridItem)e.Item).Parent == devicesRoot)
{
TreeGridItem deviceItem = (TreeGridItem)e.Item;
@@ -613,6 +656,104 @@ namespace DiscImageChef.Gui.Forms
dev.Close();
}
+ else if(((TreeGridItem)e.Item).Values[2] is IReadOnlyFilesystem fsPlugin)
+ {
+ TreeGridItem fsItem = (TreeGridItem)e.Item;
+
+ fsItem.Children.Clear();
+
+ if(fsItem.Values.Length == 5 && fsItem.Values[4] is string dirPath)
+ {
+ Errno errno = fsPlugin.ReadDir(dirPath, out List dirents);
+
+ if(errno != Errno.NoError)
+ {
+ MessageBox.Show($"Error {errno} trying to read \"{dirPath}\" of chosen filesystem",
+ MessageBoxType.Error);
+ return;
+ }
+
+ Dictionary files = new Dictionary();
+ List directories = new List();
+
+ foreach(string dirent in dirents)
+ {
+ errno = fsPlugin.Stat(dirPath + "/" + dirent, out FileEntryInfo stat);
+
+ if(errno != Errno.NoError)
+ {
+ DicConsole
+ .ErrorWriteLine($"Error {errno} trying to get information about filesystem entry named {dirent}");
+ continue;
+ }
+
+ if(stat.Attributes.HasFlag(FileAttributes.Directory)) directories.Add(dirent);
+ else files.Add(dirent, stat);
+ }
+
+ foreach(string directory in directories)
+ {
+ TreeGridItem dirItem = new TreeGridItem
+ {
+ Values = new object[] {imagesIcon, directory, fsPlugin, null, dirPath + "/" + directory}
+ };
+
+ dirItem.Children.Add(placeholderItem);
+ fsItem.Children.Add(dirItem);
+ }
+ }
+ else
+ {
+ Errno errno = fsPlugin.ReadDir("/", out List dirents);
+
+ if(errno != Errno.NoError)
+ {
+ MessageBox.Show($"Error {errno} trying to read root directory of chosen filesystem",
+ MessageBoxType.Error);
+ return;
+ }
+
+ Dictionary files = new Dictionary();
+ List directories = new List();
+
+ foreach(string dirent in dirents)
+ {
+ errno = fsPlugin.Stat("/" + dirent, out FileEntryInfo stat);
+
+ if(errno != Errno.NoError)
+ {
+ DicConsole
+ .ErrorWriteLine($"Error {errno} trying to get information about filesystem entry named {dirent}");
+ continue;
+ }
+
+ if(stat.Attributes.HasFlag(FileAttributes.Directory)) directories.Add(dirent);
+ else files.Add(dirent, stat);
+ }
+
+ TreeGridItem rootDirectoryItem = new TreeGridItem
+ {
+ Values = new object[]
+ {
+ nullImage, // TODO: Get icon from volume
+ "/", fsPlugin, files
+ }
+ };
+
+ foreach(string directory in directories)
+ {
+ TreeGridItem dirItem = new TreeGridItem
+ {
+ Values = new object[] {imagesIcon, directory, fsPlugin, null, "/" + directory}
+ };
+
+ dirItem.Children.Add(placeholderItem);
+ rootDirectoryItem.Children.Add(dirItem);
+ }
+
+ fsItem.Children.Add(rootDirectoryItem);
+ }
+ }
}
#region XAML IDs
diff --git a/DiscImageChef.Gui/Panels/pnlListFiles.xeto b/DiscImageChef.Gui/Panels/pnlListFiles.xeto
new file mode 100644
index 000000000..cf56d312d
--- /dev/null
+++ b/DiscImageChef.Gui/Panels/pnlListFiles.xeto
@@ -0,0 +1,36 @@
+
+
+
+
\ No newline at end of file
diff --git a/DiscImageChef.Gui/Panels/pnlListFiles.xeto.cs b/DiscImageChef.Gui/Panels/pnlListFiles.xeto.cs
new file mode 100644
index 000000000..a90547ed3
--- /dev/null
+++ b/DiscImageChef.Gui/Panels/pnlListFiles.xeto.cs
@@ -0,0 +1,298 @@
+// /***************************************************************************
+// The Disc Image Chef
+// ----------------------------------------------------------------------------
+//
+// Filename : pnlListFiles.xeto.cs
+// Author(s) : Natalia Portillo
+//
+// Component : List files.
+//
+// --[ Description ] ----------------------------------------------------------
+//
+// Implements the list files panel.
+//
+// --[ License ] --------------------------------------------------------------
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General public License for more details.
+//
+// You should have received a copy of the GNU General public License
+// along with this program. If not, see .
+//
+// ----------------------------------------------------------------------------
+// Copyright © 2011-2018 Natalia Portillo
+// ****************************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using DiscImageChef.CommonTypes.Interfaces;
+using DiscImageChef.CommonTypes.Structs;
+using Eto.Forms;
+using Eto.Serialization.Xaml;
+
+namespace DiscImageChef.Gui.Panels
+{
+ // TODO: Resize columns
+ // TODO: File icons?
+ // TODO: Show xattrs
+ public class pnlListFiles : Panel
+ {
+ GridColumn accessColumn;
+ bool ascendingSort;
+ GridColumn attributesColumn;
+ GridColumn backupColumn;
+ GridColumn changedColumn;
+ GridColumn createdColumn;
+ ObservableCollection entries;
+ IReadOnlyFilesystem filesystem;
+ GridColumn gidColumn;
+
+ #region XAML controls
+ #pragma warning disable 169
+ #pragma warning disable 649
+ GridView grdFiles;
+ #pragma warning restore 169
+ #pragma warning restore 649
+ #endregion
+
+ GridColumn inodeColumn;
+ GridColumn linksColumn;
+ GridColumn modeColumn;
+ GridColumn nameColumn;
+ GridColumn sizeColumn;
+ GridColumn sortedColumn;
+ GridColumn uidColumn;
+ GridColumn writeColumn;
+
+ public pnlListFiles(IReadOnlyFilesystem filesystem, Dictionary files, string parentPath)
+ {
+ this.filesystem = filesystem;
+ XamlReader.Load(this);
+
+ entries = new ObservableCollection();
+
+ nameColumn = new GridColumn
+ {
+ DataCell = new TextBoxCell {Binding = Binding.Property(r => r.Name)},
+ HeaderText = "Name",
+ Sortable = true
+ };
+ sizeColumn = new GridColumn
+ {
+ DataCell = new TextBoxCell
+ {
+ Binding = Binding.Property(r => $"{r.Stat.Length}")
+ },
+ HeaderText = "Size",
+ Sortable = true
+ };
+ createdColumn = new GridColumn
+ {
+ DataCell = new TextBoxCell
+ {
+ Binding =
+ Binding.Property(r => r.Stat.CreationTime == default(DateTime)
+ ? ""
+ : $"{r.Stat.CreationTime:G}")
+ },
+ HeaderText = "Created",
+ Sortable = true
+ };
+ accessColumn = new GridColumn
+ {
+ DataCell = new TextBoxCell
+ {
+ Binding = Binding.Property(r => r.Stat.AccessTime == default(DateTime)
+ ? ""
+ : $"{r.Stat.AccessTime:G}")
+ },
+ HeaderText = "Last access",
+ Sortable = true
+ };
+ changedColumn = new GridColumn
+ {
+ DataCell = new TextBoxCell
+ {
+ Binding =
+ Binding.Property(r => r.Stat.StatusChangeTime == default(DateTime)
+ ? ""
+ : $"{r.Stat.StatusChangeTime:G}")
+ },
+ HeaderText = "Changed",
+ Sortable = true
+ };
+ backupColumn = new GridColumn
+ {
+ DataCell = new TextBoxCell
+ {
+ Binding = Binding.Property(r => r.Stat.BackupTime == default(DateTime)
+ ? ""
+ : $"{r.Stat.BackupTime:G}")
+ },
+ HeaderText = "Last backup",
+ Sortable = true
+ };
+ writeColumn = new GridColumn
+ {
+ DataCell = new TextBoxCell
+ {
+ Binding =
+ Binding.Property(r => r.Stat.LastWriteTime == default(DateTime)
+ ? ""
+ : $"{r.Stat.LastWriteTime:G}")
+ },
+ HeaderText = "Last write",
+ Sortable = true
+ };
+ attributesColumn = new GridColumn
+ {
+ DataCell = new TextBoxCell
+ {
+ Binding = Binding.Property(r => $"{r.Stat.Attributes}")
+ },
+ HeaderText = "Attributes",
+ Sortable = true
+ };
+ gidColumn = new GridColumn
+ {
+ DataCell = new TextBoxCell {Binding = Binding.Property(r => $"{r.Stat.GID}")},
+ HeaderText = "GID",
+ Sortable = true
+ };
+ uidColumn = new GridColumn
+ {
+ DataCell = new TextBoxCell {Binding = Binding.Property(r => $"{r.Stat.UID}")},
+ HeaderText = "UID",
+ Sortable = true
+ };
+ inodeColumn = new GridColumn
+ {
+ DataCell = new TextBoxCell
+ {
+ Binding = Binding.Property(r => $"{r.Stat.Inode}")
+ },
+ HeaderText = "Inode",
+ Sortable = true
+ };
+ linksColumn = new GridColumn
+ {
+ DataCell = new TextBoxCell
+ {
+ Binding = Binding.Property(r => $"{r.Stat.Links}")
+ },
+ HeaderText = "Links",
+ Sortable = true
+ };
+ modeColumn = new GridColumn
+ {
+ DataCell =
+ new TextBoxCell {Binding = Binding.Property(r => $"{r.Stat.Mode}")},
+ HeaderText = "Mode",
+ Sortable = true
+ };
+
+ grdFiles.Columns.Add(nameColumn);
+ grdFiles.Columns.Add(sizeColumn);
+ grdFiles.Columns.Add(createdColumn);
+ grdFiles.Columns.Add(accessColumn);
+ grdFiles.Columns.Add(changedColumn);
+ grdFiles.Columns.Add(backupColumn);
+ grdFiles.Columns.Add(writeColumn);
+ grdFiles.Columns.Add(attributesColumn);
+ grdFiles.Columns.Add(gidColumn);
+ grdFiles.Columns.Add(uidColumn);
+ grdFiles.Columns.Add(inodeColumn);
+ grdFiles.Columns.Add(linksColumn);
+ grdFiles.Columns.Add(modeColumn);
+
+ grdFiles.AllowColumnReordering = true;
+ grdFiles.AllowDrop = false;
+ grdFiles.AllowMultipleSelection = true;
+ grdFiles.ShowHeader = true;
+
+ foreach(KeyValuePair file in files)
+ entries.Add(new EntryForGrid {Name = file.Key, Stat = file.Value, ParentPath = parentPath});
+
+ grdFiles.DataStore = entries;
+ sortedColumn = null;
+ grdFiles.ColumnHeaderClick += OnGrdFilesOnColumnHeaderClick;
+ ascendingSort = true;
+ }
+
+ void OnGrdFilesOnColumnHeaderClick(object sender, GridColumnEventArgs gridColumnEventArgs)
+ {
+ if(sortedColumn == gridColumnEventArgs.Column) ascendingSort = !ascendingSort;
+ else ascendingSort = true;
+
+ sortedColumn = gridColumnEventArgs.Column;
+
+ if(sortedColumn == nameColumn)
+ grdFiles.DataStore =
+ ascendingSort ? entries.OrderBy(t => t.Name) : entries.OrderByDescending(t => t.Name);
+ else if(sortedColumn == sizeColumn)
+ grdFiles.DataStore = ascendingSort
+ ? entries.OrderBy(t => t.Stat.Length)
+ : entries.OrderByDescending(t => t.Stat.Length);
+ else if(sortedColumn == createdColumn)
+ grdFiles.DataStore = ascendingSort
+ ? entries.OrderBy(t => t.Stat.CreationTime)
+ : entries.OrderByDescending(t => t.Stat.CreationTime);
+ else if(sortedColumn == accessColumn)
+ grdFiles.DataStore = ascendingSort
+ ? entries.OrderBy(t => t.Stat.AccessTime)
+ : entries.OrderByDescending(t => t.Stat.AccessTime);
+ else if(sortedColumn == changedColumn)
+ grdFiles.DataStore = ascendingSort
+ ? entries.OrderBy(t => t.Stat.StatusChangeTime)
+ : entries.OrderByDescending(t => t.Stat.StatusChangeTime);
+ else if(sortedColumn == backupColumn)
+ grdFiles.DataStore = ascendingSort
+ ? entries.OrderBy(t => t.Stat.BackupTime)
+ : entries.OrderByDescending(t => t.Stat.BackupTime);
+ else if(sortedColumn == writeColumn)
+ grdFiles.DataStore = ascendingSort
+ ? entries.OrderBy(t => t.Stat.LastWriteTime)
+ : entries.OrderByDescending(t => t.Stat.LastWriteTime);
+ else if(sortedColumn == attributesColumn)
+ grdFiles.DataStore = ascendingSort
+ ? entries.OrderBy(t => t.Stat.Attributes)
+ : entries.OrderByDescending(t => t.Stat.Attributes);
+ else if(sortedColumn == gidColumn)
+ grdFiles.DataStore = ascendingSort
+ ? entries.OrderBy(t => t.Stat.GID)
+ : entries.OrderByDescending(t => t.Stat.GID);
+ else if(sortedColumn == uidColumn)
+ grdFiles.DataStore = ascendingSort
+ ? entries.OrderBy(t => t.Stat.UID)
+ : entries.OrderByDescending(t => t.Stat.UID);
+ else if(sortedColumn == inodeColumn)
+ grdFiles.DataStore = ascendingSort
+ ? entries.OrderBy(t => t.Stat.Inode)
+ : entries.OrderByDescending(t => t.Stat.Inode);
+ else if(sortedColumn == linksColumn)
+ grdFiles.DataStore = ascendingSort
+ ? entries.OrderBy(t => t.Stat.Links)
+ : entries.OrderByDescending(t => t.Stat.Links);
+ else if(sortedColumn == modeColumn)
+ grdFiles.DataStore = ascendingSort
+ ? entries.OrderBy(t => t.Stat.Mode)
+ : entries.OrderByDescending(t => t.Stat.Mode);
+ }
+
+ class EntryForGrid
+ {
+ public string ParentPath;
+ public FileEntryInfo Stat;
+ public string Name { get; set; }
+ }
+ }
+}
\ No newline at end of file