Adds support for Apple DOS filesystem, closes #33.

This commit is contained in:
2016-10-07 00:41:59 +01:00
parent 93e86637c1
commit 7483d004b7
12 changed files with 1091 additions and 4 deletions

View File

@@ -0,0 +1,41 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : AppleNIB.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Component
//
// --[ Description ] ----------------------------------------------------------
//
// Description
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2016 Natalia Portillo
// ****************************************************************************/
using System;
namespace DiscImageChef.DiscImages
{
public class AppleNIB
{
public AppleNIB()
{
}
}
}

View File

@@ -0,0 +1,88 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : AppleDOS.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Apple DOS filesystem plugin.
//
// --[ Description ] ----------------------------------------------------------
//
// Constructors and common variables for the Apple DOS filesystem plugin.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2016 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using DiscImageChef.ImagePlugins;
namespace DiscImageChef.Filesystems.AppleDOS
{
partial class AppleDOS : Filesystem
{
bool mounted;
bool debug;
readonly ImagePlugin device;
#region Caches
/// <summary>Caches track/sector lists</summary>
Dictionary<string, byte[]> extentCache;
/// <summary>Caches files</summary>
Dictionary<string, byte[]> fileCache;
/// <summary>Caches catalog</summary>
Dictionary<string, ushort> catalogCache;
/// <summary>Caches file size</summary>
Dictionary<string, int> fileSizeCache;
/// <summary>Caches VTOC</summary>
byte[] vtocBlocks;
/// <summary>Caches catalog</summary>
byte[] catalogBlocks;
/// <summary>Caches boot code</summary>
byte[] bootBlocks;
/// <summary>Caches file type</summary>
Dictionary<string, byte> fileTypeCache;
/// <summary>Caches locked files</summary>
List<string> lockedFiles;
#endregion Caches
VTOC vtoc;
ulong start;
int sectorsPerTrack;
ulong totalFileEntries;
bool track1UsedByFiles;
bool track2UsedByFiles;
int usedSectors;
public AppleDOS()
{
Name = "Apple DOS File System";
PluginUUID = new Guid("8658A1E9-B2E7-4BCC-9638-157A31B0A700\n");
}
public AppleDOS(ImagePlugin imagePlugin, ulong partitionStart, ulong partitionEnd)
{
device = imagePlugin;
start = partitionStart;
Name = "Apple DOS File System";
PluginUUID = new Guid("8658A1E9-B2E7-4BCC-9638-157A31B0A700\n");
}
}
}

View File

@@ -0,0 +1,153 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Dir.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Apple DOS filesystem plugin.
//
// --[ Description ] ----------------------------------------------------------
//
// Methods to handle Apple DOS filesystem catalog (aka directory).
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2016 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
namespace DiscImageChef.Filesystems.AppleDOS
{
partial class AppleDOS : Filesystem
{
/// <summary>
/// Solves a symbolic link.
/// </summary>
/// <param name="path">Link path.</param>
/// <param name="dest">Link destination.</param>
public override Errno ReadLink(string path, ref string dest)
{
if(!mounted)
return Errno.AccessDenied;
return Errno.NotSupported;
}
/// <summary>
/// Lists contents from a directory.
/// </summary>
/// <param name="path">Directory path.</param>
/// <param name="contents">Directory contents.</param>
public override Errno ReadDir(string path, ref List<string> contents)
{
if(!mounted)
return Errno.AccessDenied;
if(!string.IsNullOrEmpty(path) && string.Compare(path, "/", StringComparison.OrdinalIgnoreCase) != 0)
return Errno.NotSupported;
contents = new List<string>();
foreach(string ent in catalogCache.Keys)
contents.Add(ent);
if(debug)
{
contents.Add("$");
contents.Add("$Boot");
contents.Add("$Vtoc");
}
contents.Sort();
return Errno.NoError;
}
Errno ReadCatalog()
{
MemoryStream catalogMs = new MemoryStream();
ulong lba = (ulong)((vtoc.catalogTrack * sectorsPerTrack) + vtoc.catalogSector);
totalFileEntries = 0;
catalogCache = new Dictionary<string, ushort>();
fileTypeCache = new Dictionary<string, byte>();
fileSizeCache = new Dictionary<string, int>();
lockedFiles = new List<string>();
if(lba == 0 || lba > device.ImageInfo.sectors)
return Errno.InvalidArgument;
while(lba != 0)
{
usedSectors++;
byte[] catSector_b = device.ReadSector(lba);
totalFileEntries += 7;
if(debug)
catalogMs.Write(catSector_b, 0, catSector_b.Length);
// Read the catalog sector
CatalogSector catSector = new CatalogSector();
IntPtr catPtr = Marshal.AllocHGlobal(256);
Marshal.Copy(catSector_b, 0, catPtr, 256);
catSector = (CatalogSector)Marshal.PtrToStructure(catPtr, typeof(CatalogSector));
Marshal.FreeHGlobal(catPtr);
foreach(FileEntry entry in catSector.entries)
{
if(entry.extentTrack > 0)
{
track1UsedByFiles |= entry.extentTrack == 1;
track2UsedByFiles |= entry.extentTrack == 2;
byte[] filename_b = new byte[30];
ushort ts = (ushort)((entry.extentTrack << 8) | entry.extentSector);
// Apple DOS has high byte set over ASCII.
for(int i = 0; i < 30; i++)
filename_b[i] = (byte)(entry.filename[i] & 0x7F);
string filename = StringHandlers.SpacePaddedToString(filename_b);
if(!catalogCache.ContainsKey(filename))
catalogCache.Add(filename, ts);
if(!fileTypeCache.ContainsKey(filename))
fileTypeCache.Add(filename, (byte)(entry.typeAndFlags & 0x7F));
if(!fileSizeCache.ContainsKey(filename))
fileSizeCache.Add(filename, entry.length * vtoc.bytesPerSector);
if((entry.typeAndFlags & 0x80) == 0x80 && !lockedFiles.Contains(filename))
lockedFiles.Add(filename);
}
}
lba = (ulong)((catSector.trackOfNext * sectorsPerTrack) + catSector.sectorOfNext);
if(lba > device.ImageInfo.sectors)
break;
}
if(debug)
catalogBlocks = catalogMs.ToArray();
return Errno.NoError;
}
}
}

View File

@@ -0,0 +1,281 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : File.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Apple DOS filesystem plugin.
//
// --[ Description ] ----------------------------------------------------------
//
// Methods to handle files.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2016 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
namespace DiscImageChef.Filesystems.AppleDOS
{
partial class AppleDOS : Filesystem
{
public override Errno GetAttributes(string path, ref FileAttributes attributes)
{
if(!mounted)
return Errno.AccessDenied;
string[] pathElements = path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
if(pathElements.Length != 1)
return Errno.NotSupported;
string filename = pathElements[0].ToUpperInvariant();
if(!fileCache.ContainsKey(filename))
return Errno.NoSuchFile;
attributes = new FileAttributes();
attributes = FileAttributes.Extents;
attributes |= FileAttributes.File;
if(lockedFiles.Contains(filename))
attributes |= FileAttributes.ReadOnly;
if(debug &&
(string.Compare(path, "$", StringComparison.InvariantCulture) == 0
|| string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0
|| string.Compare(path, "$Vtoc", StringComparison.InvariantCulture) == 0))
{
attributes |= FileAttributes.System;
}
return Errno.NoError;
}
public override Errno Read(string path, long offset, long size, ref byte[] buf)
{
if(!mounted)
return Errno.AccessDenied;
string[] pathElements = path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
if(pathElements.Length != 1)
return Errno.NotSupported;
byte[] file;
Errno error;
string filename = pathElements[0].ToUpperInvariant();
if(filename.Length > 30)
return Errno.NameTooLong;
if(debug &&
(string.Compare(path, "$", StringComparison.InvariantCulture) == 0
|| string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0
|| string.Compare(path, "$Vtoc", StringComparison.InvariantCulture) == 0))
{
if(string.Compare(path, "$", StringComparison.InvariantCulture) == 0)
file = catalogBlocks;
if(string.Compare(path, "$Vtoc", StringComparison.InvariantCulture) == 0)
file = vtocBlocks;
else
file = bootBlocks;
}
else
{
if(!fileCache.TryGetValue(filename, out file))
{
error = CacheFile(filename);
if(error != Errno.NoError)
return error;
if(!fileCache.TryGetValue(filename, out file))
return Errno.InvalidArgument;
}
}
if(offset >= file.Length)
return Errno.InvalidArgument;
if(size + offset >= file.Length)
size = file.Length - offset;
buf = new byte[size];
Array.Copy(file, offset, buf, 0, size);
return Errno.NoError;
}
public override Errno Stat(string path, ref FileEntryInfo stat)
{
if(!mounted)
return Errno.AccessDenied;
string[] pathElements = path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
if(pathElements.Length != 1)
return Errno.NotSupported;
string filename = pathElements[0].ToUpperInvariant();
if(filename.Length > 30)
return Errno.NameTooLong;
if(!fileCache.ContainsKey(filename))
return Errno.NoSuchFile;
int filesize;
FileAttributes attrs = new FileAttributes();
fileSizeCache.TryGetValue(filename, out filesize);
GetAttributes(path, ref attrs);
stat = new FileEntryInfo();
if(debug &&
(string.Compare(path, "$", StringComparison.InvariantCulture) == 0
|| string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0
|| string.Compare(path, "$Vtoc", StringComparison.InvariantCulture) == 0))
{
if(string.Compare(path, "$", StringComparison.InvariantCulture) == 0)
stat.Length = catalogBlocks.Length;
else if(string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0)
stat.Length = bootBlocks.Length;
else if(string.Compare(path, "$Vtoc", StringComparison.InvariantCulture) == 0)
stat.Length = vtocBlocks.Length;
stat.Blocks = stat.Length / vtoc.bytesPerSector;
}
else
{
stat.Length = filesize;
stat.Blocks = stat.Length / vtoc.bytesPerSector;
}
stat.Attributes = attrs;
stat.BlockSize = vtoc.bytesPerSector;
stat.Links = 1;
return Errno.NoError;
}
public override Errno MapBlock(string path, long fileBlock, ref long deviceBlock)
{
if(!mounted)
return Errno.AccessDenied;
// TODO: Not really important.
return Errno.NotImplemented;
}
Errno CacheFile(string path)
{
string[] pathElements = path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
if(pathElements.Length != 1)
return Errno.NotSupported;
string filename = pathElements[0].ToUpperInvariant();
if(filename.Length > 30)
return Errno.NameTooLong;
ushort ts;
if(!catalogCache.TryGetValue(filename, out ts))
return Errno.NoSuchFile;
ulong lba = (ulong)((((ts & 0xFF00) >> 8) * sectorsPerTrack) + (ts & 0xFF));
MemoryStream fileMs = new MemoryStream();
MemoryStream tsListMs = new MemoryStream();
ushort expectedBlock = 0;
while(lba != 0)
{
usedSectors++;
byte[] tsSector_b = device.ReadSector(lba);
if(debug)
tsListMs.Write(tsSector_b, 0, tsSector_b.Length);
// Read the track/sector list sector
TrackSectorList tsSector = new TrackSectorList();
IntPtr tsPtr = Marshal.AllocHGlobal(256);
Marshal.Copy(tsSector_b, 0, tsPtr, 256);
tsSector = (TrackSectorList)Marshal.PtrToStructure(tsPtr, typeof(TrackSectorList));
Marshal.FreeHGlobal(tsPtr);
if(tsSector.sectorOffset > expectedBlock)
{
byte[] hole = new byte[(tsSector.sectorOffset - expectedBlock) * vtoc.bytesPerSector];
fileMs.Write(hole, 0, hole.Length);
expectedBlock = tsSector.sectorOffset;
}
foreach(TrackSectorListEntry entry in tsSector.entries)
{
track1UsedByFiles |= entry.track == 1;
track2UsedByFiles |= entry.track == 2;
usedSectors++;
ulong blockLba = (ulong)((entry.track * sectorsPerTrack) + entry.sector);
if(blockLba == 0)
break;
byte[] fileBlock = device.ReadSector(blockLba);
fileMs.Write(fileBlock, 0, fileBlock.Length);
expectedBlock++;
}
lba = (ulong)((tsSector.nextListTrack * sectorsPerTrack) + tsSector.nextListSector);
}
if(fileCache.ContainsKey(filename))
fileCache.Remove(filename);
if(extentCache.ContainsKey(filename))
extentCache.Remove(filename);
fileCache.Add(filename, fileMs.ToArray());
extentCache.Add(filename, tsListMs.ToArray());
return Errno.NoError;
}
Errno CacheAllFiles()
{
fileCache = new Dictionary<string, byte[]>();
extentCache = new Dictionary<string, byte[]>();
Errno error;
foreach(string file in catalogCache.Keys)
{
error = CacheFile(file);
if(error != Errno.NoError)
return error;
}
uint tracksOnBoot = 1;
if(!track1UsedByFiles)
tracksOnBoot++;
if(!track2UsedByFiles)
tracksOnBoot++;
bootBlocks = device.ReadSectors(0, (uint)(tracksOnBoot * sectorsPerTrack));
usedSectors += bootBlocks.Length / vtoc.bytesPerSector;
return Errno.NoError;
}
}
}

View File

@@ -0,0 +1,106 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Info.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Apple DOS filesystem plugin.
//
// --[ Description ] ----------------------------------------------------------
//
// Identifies the Apple DOS filesystem and shows information.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2016 Natalia Portillo
// ****************************************************************************/
using System;
using System.Runtime.InteropServices;
using System.Text;
using DiscImageChef.ImagePlugins;
namespace DiscImageChef.Filesystems.AppleDOS
{
partial class AppleDOS : Filesystem
{
public override bool Identify(ImagePlugin imagePlugin, ulong partitionStart, ulong partitionEnd)
{
if(imagePlugin.ImageInfo.sectors != 455 && imagePlugin.ImageInfo.sectors != 560)
return false;
if(partitionStart > 0 || imagePlugin.ImageInfo.sectorSize != 256)
return false;
int spt = 0;
if(imagePlugin.ImageInfo.sectors == 455)
spt = 13;
else
spt = 16;
byte[] vtoc_b = imagePlugin.ReadSector((ulong)(17 * spt));
vtoc = new VTOC();
IntPtr vtocPtr = Marshal.AllocHGlobal(256);
Marshal.Copy(vtoc_b, 0, vtocPtr, 256);
vtoc = (VTOC)Marshal.PtrToStructure(vtocPtr, typeof(VTOC));
Marshal.FreeHGlobal(vtocPtr);
return vtoc.catalogSector < spt && vtoc.maxTrackSectorPairsPerSector <= 122 && vtoc.sectorsPerTrack == spt && vtoc.bytesPerSector == 256;
}
public override void GetInformation(ImagePlugin imagePlugin, ulong partitionStart, ulong partitionEnd, out string information)
{
information = "";
StringBuilder sb = new StringBuilder();
int spt = 0;
if(imagePlugin.ImageInfo.sectors == 455)
spt = 13;
else
spt = 16;
byte[] vtoc_b = imagePlugin.ReadSector((ulong)(17 * spt));
vtoc = new VTOC();
IntPtr vtocPtr = Marshal.AllocHGlobal(256);
Marshal.Copy(vtoc_b, 0, vtocPtr, 256);
vtoc = (VTOC)Marshal.PtrToStructure(vtocPtr, typeof(VTOC));
Marshal.FreeHGlobal(vtocPtr);
sb.AppendLine("Apple DOS File System");
sb.AppendLine();
sb.AppendFormat("Catalog starts at sector {0} of track {1}", vtoc.catalogSector, vtoc.catalogTrack).AppendLine();
sb.AppendFormat("File system initialized by DOS release {0}", vtoc.dosRelease).AppendLine();
sb.AppendFormat("Disk volume number {0}", vtoc.volumeNumber).AppendLine();
sb.AppendFormat("Sectors allocated at most in track {0}", vtoc.lastAllocatedSector).AppendLine();
sb.AppendFormat("{0} tracks in volume", vtoc.tracks).AppendLine();
sb.AppendFormat("{0} sectors per track", vtoc.sectorsPerTrack).AppendLine();
sb.AppendFormat("{0} bytes per sector", vtoc.bytesPerSector).AppendLine();
sb.AppendFormat("Track allocation is {0}", vtoc.allocationDirection > 0 ? "forward" : "reverse").AppendLine();
information = sb.ToString();
xmlFSType = new Schemas.FileSystemType();
xmlFSType.Bootable = true;
xmlFSType.Clusters = (long)imagePlugin.ImageInfo.sectors;
xmlFSType.ClusterSize = (int)imagePlugin.ImageInfo.sectorSize;
xmlFSType.Type = "Apple DOS";
return;
}
}
}

View File

@@ -0,0 +1,110 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Structs.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Apple DOS filesystem plugin.
//
// --[ Description ] ----------------------------------------------------------
//
// Apple DOS filesystem structures.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2016 Natalia Portillo
// ****************************************************************************/
using System.Runtime.InteropServices;
namespace DiscImageChef.Filesystems.AppleDOS
{
partial class AppleDOS : Filesystem
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct VTOC
{
public byte unused1;
public byte catalogTrack;
public byte catalogSector;
public byte dosRelease;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public byte[] unused2;
public byte volumeNumber;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] unused3;
public byte maxTrackSectorPairsPerSector;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public byte[] unused4;
public byte lastAllocatedSector;
public sbyte allocationDirection;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public byte[] unused5;
public byte tracks;
public byte sectorsPerTrack;
public ushort bytesPerSector;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 200)]
public byte[] bitmap;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct CatalogSector
{
public byte unused1;
public byte trackOfNext;
public byte sectorOfNext;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public byte[] unused2;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)]
public FileEntry[] entries;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct FileEntry
{
public byte extentTrack;
public byte extentSector;
public byte typeAndFlags;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)]
public byte[] filename;
public ushort length;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct TrackSectorList
{
public byte unused1;
public byte nextListTrack;
public byte nextListSector;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public byte[] unused2;
public ushort sectorOffset;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
public byte[] unused3;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 122)]
public TrackSectorListEntry[] entries;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct TrackSectorListEntry
{
public byte track;
public byte sector;
}
}
}

View File

@@ -0,0 +1,153 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Super.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Apple DOS filesystem plugin.
//
// --[ Description ] ----------------------------------------------------------
//
// Handles mounting and umounting the Apple DOS filesystem.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2016 Natalia Portillo
// ****************************************************************************/
using System;
using System.Runtime.InteropServices;
using DiscImageChef.Console;
namespace DiscImageChef.Filesystems.AppleDOS
{
partial class AppleDOS : Filesystem
{
/// <summary>
/// Mounts an Apple Lisa filesystem
/// </summary>
public override Errno Mount()
{
return Mount(false);
}
/// <summary>
/// Mounts an Apple Lisa filesystem
/// </summary>
public override Errno Mount(bool debug)
{
if(device.ImageInfo.sectors != 455 && device.ImageInfo.sectors != 560)
{
DicConsole.DebugWriteLine("Apple DOS plugin", "Incorrect device size.");
return Errno.InOutError;
}
if(start > 0)
{
DicConsole.DebugWriteLine("Apple DOS plugin", "Partitions are not supported.");
return Errno.InOutError;
}
if(device.ImageInfo.sectorSize != 256)
{
DicConsole.DebugWriteLine("Apple DOS plugin", "Incorrect sector size.");
return Errno.InOutError;
}
if(device.ImageInfo.sectors == 455)
sectorsPerTrack = 13;
else
sectorsPerTrack = 16;
// Read the VTOC
byte[] vtoc_b = device.ReadSector((ulong)(17 * sectorsPerTrack));
vtoc = new VTOC();
IntPtr vtocPtr = Marshal.AllocHGlobal(256);
Marshal.Copy(vtoc_b, 0, vtocPtr, 256);
vtoc = (VTOC)Marshal.PtrToStructure(vtocPtr, typeof(VTOC));
Marshal.FreeHGlobal(vtocPtr);
track1UsedByFiles = false;
track2UsedByFiles = false;
usedSectors = 1;
Errno error;
error = ReadCatalog();
if(error != Errno.NoError)
{
DicConsole.DebugWriteLine("Apple DOS plugin", "Unable to read catalog.");
return error;
}
error = CacheAllFiles();
if(error != Errno.NoError)
{
DicConsole.DebugWriteLine("Apple DOS plugin", "Unable cache all files.");
return error;
}
// Create XML metadata for mounted filesystem
xmlFSType = new Schemas.FileSystemType();
xmlFSType.Bootable = true;
xmlFSType.Clusters = (long)device.ImageInfo.sectors;
xmlFSType.ClusterSize = vtoc.bytesPerSector;
xmlFSType.Files = catalogCache.Count;
xmlFSType.FilesSpecified = true;
xmlFSType.FreeClusters = xmlFSType.Clusters - usedSectors;
xmlFSType.FreeClustersSpecified = true;
xmlFSType.Type = "Apple DOS";
this.debug = debug;
mounted = true;
return Errno.NoError;
}
/// <summary>
/// Umounts this Lisa filesystem
/// </summary>
public override Errno Unmount()
{
mounted = false;
extentCache = null;
fileCache = null;
catalogCache = null;
fileSizeCache = null;
return Errno.NoError;
}
/// <summary>
/// Gets information about the mounted volume.
/// </summary>
/// <param name="stat">Information about the mounted volume.</param>
public override Errno StatFs(ref FileSystemInfo stat)
{
stat = new FileSystemInfo();
stat.Blocks = (long)device.ImageInfo.sectors;
stat.FilenameLength = 30;
stat.Files = (ulong)catalogCache.Count;
stat.FreeBlocks = stat.Blocks - usedSectors;
stat.FreeFiles = totalFileEntries - stat.Files;
stat.PluginId = PluginUUID;
stat.Type = "Apple DOS";
return Errno.NoError;
}
}
}

View File

@@ -0,0 +1,135 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Xattr.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Apple DOS filesystem plugin.
//
// --[ Description ] ----------------------------------------------------------
//
// Methods to handle Apple DOS extended attributes (file type).
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2016 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
namespace DiscImageChef.Filesystems.AppleDOS
{
partial class AppleDOS : Filesystem
{
/// <summary>
/// Lists all extended attributes, alternate data streams and forks of the given file.
/// </summary>
/// <returns>Error number.</returns>
/// <param name="path">Path.</param>
/// <param name="xattrs">List of extended attributes, alternate data streams and forks.</param>
public override Errno ListXAttr(string path, ref List<string> xattrs)
{
if(!mounted)
return Errno.AccessDenied;
string[] pathElements = path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
if(pathElements.Length != 1)
return Errno.NotSupported;
string filename = pathElements[0].ToUpperInvariant();
if(filename.Length > 30)
return Errno.NameTooLong;
xattrs = new List<string>();
if(debug &&
(string.Compare(path, "$", StringComparison.InvariantCulture) == 0
|| string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0
|| string.Compare(path, "$Vtoc", StringComparison.InvariantCulture) == 0))
{
}
else
{
if(!catalogCache.ContainsKey(filename))
return Errno.NoSuchFile;
xattrs.Add("com.apple.dos.type");
if(debug)
xattrs.Add("com.apple.dos.tracksectorlist");
}
return Errno.NoError;
}
/// <summary>
/// Reads an extended attribute, alternate data stream or fork from the given file.
/// </summary>
/// <returns>Error number.</returns>
/// <param name="path">File path.</param>
/// <param name="xattr">Extended attribute, alternate data stream or fork name.</param>
/// <param name="buf">Buffer.</param>
public override Errno GetXattr(string path, string xattr, ref byte[] buf)
{
if(!mounted)
return Errno.AccessDenied;
string[] pathElements = path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
if(pathElements.Length != 1)
return Errno.NotSupported;
string filename = pathElements[0].ToUpperInvariant();
if(filename.Length > 30)
return Errno.NameTooLong;
if(debug &&
(string.Compare(path, "$", StringComparison.InvariantCulture) == 0
|| string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0
|| string.Compare(path, "$Vtoc", StringComparison.InvariantCulture) == 0))
{
return Errno.NoSuchExtendedAttribute;
}
if(!catalogCache.ContainsKey(filename))
return Errno.NoSuchFile;
if(string.Compare(xattr, "com.apple.dos.type", StringComparison.InvariantCulture) == 0)
{
byte type;
if(!fileTypeCache.TryGetValue(filename, out type))
return Errno.InvalidArgument;
buf = new byte[1];
buf[0] = type;
return Errno.NoError;
}
if(string.Compare(xattr, "com.apple.dos.tracksectorlist", StringComparison.InvariantCulture) == 0 && debug)
{
byte[] ts;
if(!extentCache.TryGetValue(filename, out ts))
return Errno.InvalidArgument;
buf = new byte[ts.Length];
Array.Copy(ts, 0, buf, 0, buf.Length);
return Errno.NoError;
}
return Errno.NoSuchExtendedAttribute;
}
}
}

View File

@@ -1,3 +1,15 @@
2016-10-07 Natalia Portillo <claunia@claunia.com>
* Dir.cs:
* Info.cs:
* File.cs:
* Xattr.cs:
* Super.cs:
* Structs.cs:
* AppleDOS.cs:
* DiscImageChef.Filesystems.csproj: Adds support for Apple DOS
filesystem, closes #33.
2016-09-05 Natalia Portillo <claunia@claunia.com>
* Info.cs: Do not throw identification exceptions.

View File

@@ -116,6 +116,13 @@
<Compile Include="ECMA67.cs" />
<Compile Include="FATX.cs" />
<Compile Include="ZFS.cs" />
<Compile Include="AppleDOS\Dir.cs" />
<Compile Include="AppleDOS\File.cs" />
<Compile Include="AppleDOS\Info.cs" />
<Compile Include="AppleDOS\AppleDOS.cs" />
<Compile Include="AppleDOS\Structs.cs" />
<Compile Include="AppleDOS\Super.cs" />
<Compile Include="AppleDOS\Xattr.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
@@ -157,6 +164,7 @@
<Folder Include="UCSDPascal\" />
<Folder Include="AppleMFS\" />
<Folder Include="CPM\" />
<Folder Include="AppleDOS\" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="..\LICENSE.LGPL">
@@ -168,8 +176,8 @@
<MonoDevelop>
<Properties>
<Policies>
<TextStylePolicy FileWidth="120" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="text/x-csharp" />
<CSharpFormattingPolicy IndentSwitchSection="True" NewLinesForBracesInProperties="True" NewLinesForBracesInAccessors="True" NewLinesForBracesInAnonymousMethods="True" NewLinesForBracesInControlBlocks="True" NewLinesForBracesInAnonymousTypes="True" NewLinesForBracesInObjectCollectionArrayInitializers="True" NewLinesForBracesInLambdaExpressionBody="True" NewLineForElse="True" NewLineForCatch="True" NewLineForFinally="True" NewLineForMembersInObjectInit="True" NewLineForMembersInAnonymousTypes="True" NewLineForClausesInQuery="True" SpacingAfterMethodDeclarationName="False" SpaceAfterMethodCallName="False" SpaceAfterControlFlowStatementKeyword="False" SpaceBeforeOpenSquareBracket="False" inheritsSet="Mono" inheritsScope="text/x-csharp" scope="text/x-csharp" />
<TextStylePolicy TabWidth="4" TabsToSpaces="True" IndentWidth="4" RemoveTrailingWhitespace="True" NoTabsAfterNonTabs="False" EolMarker="Native" FileWidth="120" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="text/x-csharp" />
<CSharpFormattingPolicy IndentBlock="True" IndentBraces="False" IndentSwitchCaseSection="True" LabelPositioning="OneLess" NewLinesForBracesInTypes="True" NewLinesForBracesInMethods="True" SpaceWithinMethodDeclarationParenthesis="False" SpaceBetweenEmptyMethodDeclarationParentheses="False" SpaceWithinMethodCallParentheses="False" SpaceBetweenEmptyMethodCallParentheses="False" SpaceWithinExpressionParentheses="False" SpaceWithinCastParentheses="False" SpaceWithinOtherParentheses="False" SpaceAfterCast="False" SpacesIgnoreAroundVariableDeclaration="False" SpaceBetweenEmptySquareBrackets="False" SpaceWithinSquareBrackets="False" SpaceAfterColonInBaseTypeDeclaration="True" SpaceAfterComma="True" SpaceAfterDot="False" SpaceAfterSemicolonsInForStatement="True" SpaceBeforeColonInBaseTypeDeclaration="True" SpaceBeforeComma="False" SpaceBeforeDot="False" SpaceBeforeSemicolonsInForStatement="False" SpacingAroundBinaryOperator="Single" WrappingPreserveSingleLine="True" WrappingKeepStatementsOnSingleLine="True" PlaceSystemDirectiveFirst="True" IndentSwitchSection="True" NewLinesForBracesInProperties="True" NewLinesForBracesInAccessors="True" NewLinesForBracesInAnonymousMethods="True" NewLinesForBracesInControlBlocks="True" NewLinesForBracesInAnonymousTypes="True" NewLinesForBracesInObjectCollectionArrayInitializers="True" NewLinesForBracesInLambdaExpressionBody="True" NewLineForElse="True" NewLineForCatch="True" NewLineForFinally="True" NewLineForMembersInObjectInit="True" NewLineForMembersInAnonymousTypes="True" NewLineForClausesInQuery="True" SpacingAfterMethodDeclarationName="False" SpaceAfterMethodCallName="False" SpaceAfterControlFlowStatementKeyword="False" SpaceBeforeOpenSquareBracket="False" inheritsSet="Mono" inheritsScope="text/x-csharp" scope="text/x-csharp" />
<DotNetNamingPolicy DirectoryNamespaceAssociation="PrefixedHierarchical" ResourceNamePolicy="MSBuild" />
<StandardHeader Text="/***************************************************************************&#xA;The Disc Image Chef&#xA;----------------------------------------------------------------------------&#xA; &#xA;Filename : ${FileName}&#xA;Author(s) : ${AuthorName} &lt;${AuthorEmail}&gt;&#xA;&#xA;Component : Component&#xA; &#xA;--[ Description ] ----------------------------------------------------------&#xA; &#xA; Description&#xA; &#xA;--[ License ] --------------------------------------------------------------&#xA; &#xA; This library is free software; you can redistribute it and/or modify&#xA; it under the terms of the GNU Lesser General Public License as&#xA; published by the Free Software Foundation; either version 2.1 of the&#xA; License, or (at your option) any later version.&#xA;&#xA; This library is distributed in the hope that it will be useful, but&#xA; WITHOUT ANY WARRANTY; without even the implied warranty of&#xA; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU&#xA; Lesser General Public License for more details.&#xA;&#xA; You should have received a copy of the GNU Lesser General Public&#xA; License along with this library; if not, see &lt;http://www.gnu.org/licenses/&gt;.&#xA;&#xA;----------------------------------------------------------------------------&#xA;Copyright © 2011-${Year} ${CopyrightHolder}&#xA;****************************************************************************/" IncludeInNewFiles="True" />
</Policies>

View File

@@ -18,7 +18,6 @@ Works under any operating system where there is Mono or .NET Framework. Tested w
Features
========
* Can read several disk image formats.
* Can read standard sector by sector copies for optical and magnetic discs with constant bytes per sector.
* Can read several known sector by sector formats with variable bytes per sector.
@@ -59,6 +58,7 @@ Supported disk image formats
* Apple New Disk Image Format (NDIF, requires Resource Fork)
* Apple Disk Archival/Retrieval Tool (DART)
* MAME Compressed Hunks of Data (CHD)
* Apple II nibble images (NIB)
Supported partitioning schemes
==============================
@@ -90,6 +90,7 @@ Supported file systems for read-only operations
* Apple Macintosh File System (MFS)
* U.C.S.D Pascal file system
* CP/M file system
* Apple DOS file system
Supported file systems for identification and information only
==============================================================

1
TODO
View File

@@ -8,7 +8,6 @@
--- Add support for XPACK images
Filesystem plugins:
--- Add support for Apple DOS filesystems
--- Add support for NwFS
--- Add support for ReFS