209 lines
6.3 KiB
C#
209 lines
6.3 KiB
C#
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
|
|
namespace ISOParser
|
|
{
|
|
/// <summary>
|
|
/// This class is meant to parse disk images as specified by:
|
|
///
|
|
/// ISO9660
|
|
/// -------
|
|
/// It should work for most disk images that are created
|
|
/// by the standard disk imaging software. This class is by no means
|
|
/// robust to all variations of ISO9660.
|
|
/// Also, this class does not currently support the UDF file system.
|
|
///
|
|
/// The information for building class came from three primary sources:
|
|
/// 1. The ISO9660 wikipedia article:
|
|
/// http://en.wikipedia.org/wiki/ISO_9660
|
|
/// 2. ISO9660 Simplified for DOS/Windows
|
|
/// http://alumnus.caltech.edu/~pje/iso9660.html
|
|
/// 3. The ISO 9660 File System
|
|
/// http://users.telenet.be/it3.consultants.bvba/handouts/ISO9960.html
|
|
///
|
|
///
|
|
/// CD-I
|
|
/// ----
|
|
/// (asni - 20171013) - Class modified to be able to detect and consume Green
|
|
/// Book disc images.
|
|
///
|
|
/// The implementation of CD-I in this class adds some (but not all) additional
|
|
/// properties to the class structures that CD-I brings. This means that
|
|
/// the same ISO class structures can be returned for both standards.
|
|
/// These small additions are readily found in ISOVolumeDescriptor.cs
|
|
///
|
|
/// ISOFile.cs also now contains a public 'ISOFormat' enum that is set
|
|
/// during disc parsing.
|
|
///
|
|
/// The main reference source for this implementation:
|
|
/// 1. The CD-I Full Functional Specification (aka Green Book)
|
|
/// https://www.lscdweb.com/data/downloadables/2/8/cdi_may94_r2.pdf
|
|
///
|
|
///
|
|
/// TODO: Add functions to enumerate a directory or visit a file...
|
|
///
|
|
/// </summary>
|
|
public class ISOFile
|
|
{
|
|
/// <summary>
|
|
/// We are hard coding the SECTOR_SIZE
|
|
/// </summary>
|
|
public const int SECTOR_SIZE = 2048;
|
|
|
|
#pragma warning disable CA2211
|
|
/// <summary>
|
|
/// Making this a static for now. Every other way I tried was fairly ineligant (asni)
|
|
/// </summary>
|
|
public static ISOFormat Format;
|
|
|
|
public static List<CDIPathNode> CDIPathTable;
|
|
#pragma warning restore CA2211
|
|
|
|
/// <summary>
|
|
/// This is a list of all the volume descriptors in the disk image.
|
|
/// NOTE: The first entry should be the primary volume.
|
|
/// </summary>
|
|
public List<ISOVolumeDescriptor> VolumeDescriptors;
|
|
|
|
/// <summary>
|
|
/// The Directory that is the root of this file system
|
|
/// </summary>
|
|
public ISODirectoryNode Root;
|
|
|
|
/// <summary>
|
|
/// The type of CDFS format detected
|
|
/// </summary>
|
|
public ISOFormat CDFSType;
|
|
|
|
/// <summary>
|
|
/// Construct the ISO file data structures, but leave everything
|
|
/// blank.
|
|
/// </summary>
|
|
public ISOFile()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse the given stream to populate the iso information
|
|
/// </summary>
|
|
/// <param name="s">The stream which we are using to parse the image.
|
|
/// Should already be located at the start of the image.</param>
|
|
public bool Parse(Stream s, int startSector = 16)
|
|
{
|
|
this.VolumeDescriptors = new List<ISOVolumeDescriptor>();
|
|
Root = null;
|
|
|
|
long startPosition = s.Position;
|
|
byte[] buffer = new byte[ISOFile.SECTOR_SIZE];
|
|
|
|
// Seek through the first volume descriptor
|
|
s.Seek(startPosition + (SECTOR_SIZE * startSector), SeekOrigin.Begin);
|
|
|
|
// Read one of more volume descriptors
|
|
do
|
|
{
|
|
//zero 24-jun-2013 - improved validity checks
|
|
|
|
ISOVolumeDescriptor desc = new ISOVolumeDescriptor();
|
|
bool isValid = desc.Parse(s);
|
|
if (!isValid) return false;
|
|
|
|
this.CDFSType = Format;
|
|
|
|
if (desc.IsTerminator())
|
|
break;
|
|
else if (desc.Type < 4)
|
|
this.VolumeDescriptors.Add(desc);
|
|
else
|
|
//found a volume descriptor of incorrect type.. maybe this isnt a cdfs
|
|
//supposedly these exist.. wait for one to show up
|
|
return false;
|
|
|
|
} while (true);
|
|
|
|
//zero 24-jun-2013 - well, my very first test iso had 2 volume descriptors.
|
|
// Check to make sure we only read one volume descriptor
|
|
// Finding more could be an error with the disk.
|
|
//if (this.VolumeDescriptors.Count != 1) {
|
|
// Console.WriteLine("Strange ISO format...");
|
|
// return;
|
|
//}
|
|
|
|
|
|
//zero 24-jun-2013 - if theres no volume descriptors, we're gonna call this not a cdfs
|
|
if (VolumeDescriptors.Count == 0) return false;
|
|
|
|
// Visit all the directories and get the offset of each directory/file
|
|
|
|
// We need to keep track of the directories and files we have visited in case there are loops.
|
|
Dictionary<long, ISONode> visitedNodes = new Dictionary<long, ISONode>();
|
|
|
|
// Create (and visit) the root node
|
|
this.Root = new ISODirectoryNode(this.VolumeDescriptors[0].RootDirectoryRecord);
|
|
|
|
visitedNodes.Add(this.Root.Offset, this.Root);
|
|
this.Root.Parse(s, visitedNodes);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
private List<KeyValuePair<string, ISOFileNode>> fileNodes;
|
|
private List<ISODirectoryNode> dirsParsed;
|
|
|
|
/// <summary>
|
|
/// Returns a flat list of all recursed files
|
|
/// </summary>
|
|
public List<KeyValuePair<string, ISOFileNode>> EnumerateAllFilesRecursively()
|
|
{
|
|
fileNodes = new List<KeyValuePair<string, ISOFileNode>>();
|
|
if (Root.Children == null) return fileNodes;
|
|
|
|
dirsParsed = new List<ISODirectoryNode>();
|
|
foreach (var idn in Root.Children.Values.OfType<ISODirectoryNode>()) // iterate through each folder
|
|
{
|
|
// process all files in this directory (and recursively process files in subfolders)
|
|
if (dirsParsed.Contains(idn)) continue;
|
|
dirsParsed.Add(idn);
|
|
ProcessDirectoryFiles(idn.Children);
|
|
}
|
|
return fileNodes.Distinct().ToList();
|
|
}
|
|
|
|
private void ProcessDirectoryFiles(Dictionary<string, ISONode> idn)
|
|
{
|
|
foreach (var n in idn)
|
|
{
|
|
if (n.Value is ISODirectoryNode subdirNode)
|
|
{
|
|
if (dirsParsed.Contains(subdirNode)) continue;
|
|
dirsParsed.Add(subdirNode);
|
|
ProcessDirectoryFiles(subdirNode.Children);
|
|
}
|
|
else
|
|
{
|
|
KeyValuePair<string, ISOFileNode> f = new KeyValuePair<string, ISOFileNode>(n.Key, n.Value as ISOFileNode);
|
|
fileNodes.Add(f);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Print the directory tree for the image.
|
|
/// </summary>
|
|
public void Print()
|
|
{
|
|
// DEBUGGING: Now print out the directory structure
|
|
this.Root.Print(0);
|
|
}
|
|
|
|
public enum ISOFormat
|
|
{
|
|
Unknown,
|
|
ISO9660,
|
|
CDInteractive
|
|
}
|
|
}
|
|
}
|