diff --git a/DiscImageChef.DiscImages/AppleNIB.cs b/DiscImageChef.DiscImages/AppleNIB.cs new file mode 100644 index 00000000..703304a4 --- /dev/null +++ b/DiscImageChef.DiscImages/AppleNIB.cs @@ -0,0 +1,41 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : AppleNIB.cs +// Author(s) : Natalia Portillo +// +// 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 . +// +// ---------------------------------------------------------------------------- +// Copyright © 2011-2016 Natalia Portillo +// ****************************************************************************/ +using System; +namespace DiscImageChef.DiscImages +{ + public class AppleNIB + { + public AppleNIB() + { + } + } +} diff --git a/DiscImageChef.Filesystems/AppleDOS/AppleDOS.cs b/DiscImageChef.Filesystems/AppleDOS/AppleDOS.cs new file mode 100644 index 00000000..9d02764e --- /dev/null +++ b/DiscImageChef.Filesystems/AppleDOS/AppleDOS.cs @@ -0,0 +1,88 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : AppleDOS.cs +// Author(s) : Natalia Portillo +// +// 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 . +// +// ---------------------------------------------------------------------------- +// 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 + /// Caches track/sector lists + Dictionary extentCache; + /// Caches files + Dictionary fileCache; + /// Caches catalog + Dictionary catalogCache; + /// Caches file size + Dictionary fileSizeCache; + /// Caches VTOC + byte[] vtocBlocks; + /// Caches catalog + byte[] catalogBlocks; + /// Caches boot code + byte[] bootBlocks; + /// Caches file type + Dictionary fileTypeCache; + /// Caches locked files + List 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"); + } + } +} diff --git a/DiscImageChef.Filesystems/AppleDOS/Dir.cs b/DiscImageChef.Filesystems/AppleDOS/Dir.cs new file mode 100644 index 00000000..465f4414 --- /dev/null +++ b/DiscImageChef.Filesystems/AppleDOS/Dir.cs @@ -0,0 +1,153 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : Dir.cs +// Author(s) : Natalia Portillo +// +// 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 . +// +// ---------------------------------------------------------------------------- +// 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 + { + /// + /// Solves a symbolic link. + /// + /// Link path. + /// Link destination. + public override Errno ReadLink(string path, ref string dest) + { + if(!mounted) + return Errno.AccessDenied; + + return Errno.NotSupported; + } + + /// + /// Lists contents from a directory. + /// + /// Directory path. + /// Directory contents. + public override Errno ReadDir(string path, ref List contents) + { + if(!mounted) + return Errno.AccessDenied; + + if(!string.IsNullOrEmpty(path) && string.Compare(path, "/", StringComparison.OrdinalIgnoreCase) != 0) + return Errno.NotSupported; + + contents = new List(); + 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(); + fileTypeCache = new Dictionary(); + fileSizeCache = new Dictionary(); + lockedFiles = new List(); + + 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; + } + } +} diff --git a/DiscImageChef.Filesystems/AppleDOS/File.cs b/DiscImageChef.Filesystems/AppleDOS/File.cs new file mode 100644 index 00000000..07ab3dcd --- /dev/null +++ b/DiscImageChef.Filesystems/AppleDOS/File.cs @@ -0,0 +1,281 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : File.cs +// Author(s) : Natalia Portillo +// +// 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 . +// +// ---------------------------------------------------------------------------- +// 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(); + extentCache = new Dictionary(); + 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; + } + } +} diff --git a/DiscImageChef.Filesystems/AppleDOS/Info.cs b/DiscImageChef.Filesystems/AppleDOS/Info.cs new file mode 100644 index 00000000..87e8e1d0 --- /dev/null +++ b/DiscImageChef.Filesystems/AppleDOS/Info.cs @@ -0,0 +1,106 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : Info.cs +// Author(s) : Natalia Portillo +// +// 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 . +// +// ---------------------------------------------------------------------------- +// 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; + } + } +} diff --git a/DiscImageChef.Filesystems/AppleDOS/Structs.cs b/DiscImageChef.Filesystems/AppleDOS/Structs.cs new file mode 100644 index 00000000..3a504033 --- /dev/null +++ b/DiscImageChef.Filesystems/AppleDOS/Structs.cs @@ -0,0 +1,110 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : Structs.cs +// Author(s) : Natalia Portillo +// +// 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 . +// +// ---------------------------------------------------------------------------- +// 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; + } + } +} diff --git a/DiscImageChef.Filesystems/AppleDOS/Super.cs b/DiscImageChef.Filesystems/AppleDOS/Super.cs new file mode 100644 index 00000000..ae809747 --- /dev/null +++ b/DiscImageChef.Filesystems/AppleDOS/Super.cs @@ -0,0 +1,153 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : Super.cs +// Author(s) : Natalia Portillo +// +// 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 . +// +// ---------------------------------------------------------------------------- +// Copyright © 2011-2016 Natalia Portillo +// ****************************************************************************/ + +using System; +using System.Runtime.InteropServices; +using DiscImageChef.Console; + +namespace DiscImageChef.Filesystems.AppleDOS +{ + partial class AppleDOS : Filesystem + { + /// + /// Mounts an Apple Lisa filesystem + /// + public override Errno Mount() + { + return Mount(false); + } + + /// + /// Mounts an Apple Lisa filesystem + /// + 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; + } + + /// + /// Umounts this Lisa filesystem + /// + public override Errno Unmount() + { + mounted = false; + extentCache = null; + fileCache = null; + catalogCache = null; + fileSizeCache = null; + + return Errno.NoError; + } + + /// + /// Gets information about the mounted volume. + /// + /// Information about the mounted volume. + 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; + } + } +} diff --git a/DiscImageChef.Filesystems/AppleDOS/Xattr.cs b/DiscImageChef.Filesystems/AppleDOS/Xattr.cs new file mode 100644 index 00000000..a72cc3a6 --- /dev/null +++ b/DiscImageChef.Filesystems/AppleDOS/Xattr.cs @@ -0,0 +1,135 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : Xattr.cs +// Author(s) : Natalia Portillo +// +// 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 . +// +// ---------------------------------------------------------------------------- +// Copyright © 2011-2016 Natalia Portillo +// ****************************************************************************/ + +using System; +using System.Collections.Generic; + +namespace DiscImageChef.Filesystems.AppleDOS +{ + partial class AppleDOS : Filesystem + { + /// + /// Lists all extended attributes, alternate data streams and forks of the given file. + /// + /// Error number. + /// Path. + /// List of extended attributes, alternate data streams and forks. + public override Errno ListXAttr(string path, ref List 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(); + + 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; + } + + /// + /// Reads an extended attribute, alternate data stream or fork from the given file. + /// + /// Error number. + /// File path. + /// Extended attribute, alternate data stream or fork name. + /// Buffer. + 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; + } + } +} diff --git a/DiscImageChef.Filesystems/ChangeLog b/DiscImageChef.Filesystems/ChangeLog index 46aaaec8..d56dc426 100644 --- a/DiscImageChef.Filesystems/ChangeLog +++ b/DiscImageChef.Filesystems/ChangeLog @@ -1,3 +1,15 @@ +2016-10-07 Natalia Portillo + + * 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 * Info.cs: Do not throw identification exceptions. diff --git a/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj b/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj index 66600fd7..0a63916b 100644 --- a/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj +++ b/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj @@ -116,6 +116,13 @@ + + + + + + + @@ -157,6 +164,7 @@ + @@ -168,8 +176,8 @@ - - + + diff --git a/README.md b/README.md index 408a489b..08876fcf 100644 --- a/README.md +++ b/README.md @@ -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 ============================================================== diff --git a/TODO b/TODO index 342be38d..82e4846a 100644 --- a/TODO +++ b/TODO @@ -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