Add option to pass an arbitrary list of options to IReadOnlyFilesystem.Mount()

This commit is contained in:
2017-12-27 23:55:59 +00:00
parent e009d86fcc
commit a1f82e0e72
11 changed files with 454 additions and 417 deletions

View File

@@ -40,22 +40,27 @@ namespace DiscImageChef.Filesystems.AppleDOS
{ {
public partial class AppleDOS : IReadOnlyFilesystem public partial class AppleDOS : IReadOnlyFilesystem
{ {
bool debug; bool debug;
IMediaImage device; IMediaImage device;
bool mounted; bool mounted;
int sectorsPerTrack; int sectorsPerTrack;
ulong start; ulong start;
ulong totalFileEntries; ulong totalFileEntries;
bool track1UsedByFiles; bool track1UsedByFiles;
bool track2UsedByFiles; bool track2UsedByFiles;
int usedSectors; int usedSectors;
Vtoc vtoc; Vtoc vtoc;
public FileSystemType XmlFsType { get; private set; } public FileSystemType XmlFsType { get; private set; }
public Encoding Encoding { get; private set; } public Encoding Encoding { get; private set; }
public string Name => "Apple DOS File System"; public string Name => "Apple DOS File System";
public Guid Id => new Guid("8658A1E9-B2E7-4BCC-9638-157A31B0A700\n"); public Guid Id => new Guid("8658A1E9-B2E7-4BCC-9638-157A31B0A700\n");
static Dictionary<string, string> GetDefaultOptions()
{
return new Dictionary<string, string> {{"debug", false.ToString()}};
}
#region Caches #region Caches
/// <summary>Caches track/sector lists</summary> /// <summary>Caches track/sector lists</summary>
@@ -65,17 +70,17 @@ namespace DiscImageChef.Filesystems.AppleDOS
/// <summary>Caches catalog</summary> /// <summary>Caches catalog</summary>
Dictionary<string, ushort> catalogCache; Dictionary<string, ushort> catalogCache;
/// <summary>Caches file size</summary> /// <summary>Caches file size</summary>
Dictionary<string, int> fileSizeCache; Dictionary<string, int> fileSizeCache;
/// <summary>Caches VTOC</summary> /// <summary>Caches VTOC</summary>
byte[] vtocBlocks; byte[] vtocBlocks;
/// <summary>Caches catalog</summary> /// <summary>Caches catalog</summary>
byte[] catalogBlocks; byte[] catalogBlocks;
/// <summary>Caches boot code</summary> /// <summary>Caches boot code</summary>
byte[] bootBlocks; byte[] bootBlocks;
/// <summary>Caches file type</summary> /// <summary>Caches file type</summary>
Dictionary<string, byte> fileTypeCache; Dictionary<string, byte> fileTypeCache;
/// <summary>Caches locked files</summary> /// <summary>Caches locked files</summary>
List<string> lockedFiles; List<string> lockedFiles;
#endregion Caches #endregion Caches
} }
} }

View File

@@ -31,6 +31,7 @@
// ****************************************************************************/ // ****************************************************************************/
using System; using System;
using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Claunia.Encoding; using Claunia.Encoding;
using DiscImageChef.CommonTypes; using DiscImageChef.CommonTypes;
@@ -46,10 +47,11 @@ namespace DiscImageChef.Filesystems.AppleDOS
/// <summary> /// <summary>
/// Mounts an Apple DOS filesystem /// Mounts an Apple DOS filesystem
/// </summary> /// </summary>
public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, bool debug) public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding,
Dictionary<string, string> options)
{ {
device = imagePlugin; device = imagePlugin;
start = partition.Start; start = partition.Start;
Encoding = encoding ?? new Apple2(); Encoding = encoding ?? new Apple2();
if(device.Info.Sectors != 455 && device.Info.Sectors != 560) if(device.Info.Sectors != 455 && device.Info.Sectors != 560)
@@ -73,8 +75,8 @@ namespace DiscImageChef.Filesystems.AppleDOS
sectorsPerTrack = device.Info.Sectors == 455 ? 13 : 16; sectorsPerTrack = device.Info.Sectors == 455 ? 13 : 16;
// Read the VTOC // Read the VTOC
vtocBlocks = device.ReadSector((ulong)(17 * sectorsPerTrack)); vtocBlocks = device.ReadSector((ulong)(17 * sectorsPerTrack));
vtoc = new Vtoc(); vtoc = new Vtoc();
IntPtr vtocPtr = Marshal.AllocHGlobal(256); IntPtr vtocPtr = Marshal.AllocHGlobal(256);
Marshal.Copy(vtocBlocks, 0, vtocPtr, 256); Marshal.Copy(vtocBlocks, 0, vtocPtr, 256);
vtoc = (Vtoc)Marshal.PtrToStructure(vtocPtr, typeof(Vtoc)); vtoc = (Vtoc)Marshal.PtrToStructure(vtocPtr, typeof(Vtoc));
@@ -82,7 +84,7 @@ namespace DiscImageChef.Filesystems.AppleDOS
track1UsedByFiles = false; track1UsedByFiles = false;
track2UsedByFiles = false; track2UsedByFiles = false;
usedSectors = 1; usedSectors = 1;
Errno error = ReadCatalog(); Errno error = ReadCatalog();
if(error != Errno.NoError) if(error != Errno.NoError)
@@ -101,17 +103,18 @@ namespace DiscImageChef.Filesystems.AppleDOS
// Create XML metadata for mounted filesystem // Create XML metadata for mounted filesystem
XmlFsType = new FileSystemType XmlFsType = new FileSystemType
{ {
Bootable = true, Bootable = true,
Clusters = (long)device.Info.Sectors, Clusters = (long)device.Info.Sectors,
ClusterSize = vtoc.bytesPerSector, ClusterSize = vtoc.bytesPerSector,
Files = catalogCache.Count, Files = catalogCache.Count,
FilesSpecified = true, FilesSpecified = true,
FreeClustersSpecified = true, FreeClustersSpecified = true,
Type = "Apple DOS" Type = "Apple DOS"
}; };
XmlFsType.FreeClusters = XmlFsType.Clusters - usedSectors; XmlFsType.FreeClusters = XmlFsType.Clusters - usedSectors;
this.debug = debug; if(options == null) options = GetDefaultOptions();
if(options.TryGetValue("debug", out string debugString)) bool.TryParse(debugString, out debug);
mounted = true; mounted = true;
return Errno.NoError; return Errno.NoError;
} }
@@ -121,10 +124,10 @@ namespace DiscImageChef.Filesystems.AppleDOS
/// </summary> /// </summary>
public Errno Unmount() public Errno Unmount()
{ {
mounted = false; mounted = false;
extentCache = null; extentCache = null;
fileCache = null; fileCache = null;
catalogCache = null; catalogCache = null;
fileSizeCache = null; fileSizeCache = null;
return Errno.NoError; return Errno.NoError;
@@ -138,14 +141,14 @@ namespace DiscImageChef.Filesystems.AppleDOS
{ {
stat = new FileSystemInfo stat = new FileSystemInfo
{ {
Blocks = (long)device.Info.Sectors, Blocks = (long)device.Info.Sectors,
FilenameLength = 30, FilenameLength = 30,
Files = (ulong)catalogCache.Count, Files = (ulong)catalogCache.Count,
PluginId = Id, PluginId = Id,
Type = "Apple DOS" Type = "Apple DOS"
}; };
stat.FreeFiles = totalFileEntries - stat.Files; stat.FreeFiles = totalFileEntries - stat.Files;
stat.FreeBlocks = stat.Blocks - usedSectors; stat.FreeBlocks = stat.Blocks - usedSectors;
return Errno.NoError; return Errno.NoError;
} }

View File

@@ -33,7 +33,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using DiscImageChef.CommonTypes;
using DiscImageChef.DiscImages; using DiscImageChef.DiscImages;
using Schemas; using Schemas;
@@ -42,47 +41,35 @@ namespace DiscImageChef.Filesystems.AppleMFS
// Information from Inside Macintosh Volume II // Information from Inside Macintosh Volume II
public partial class AppleMFS : IReadOnlyFilesystem public partial class AppleMFS : IReadOnlyFilesystem
{ {
bool mounted; bool mounted;
bool debug; bool debug;
IMediaImage device; IMediaImage device;
ulong partitionStart; ulong partitionStart;
Dictionary<uint, string> idToFilename; Dictionary<uint, string> idToFilename;
Dictionary<uint, MFS_FileEntry> idToEntry; Dictionary<uint, MFS_FileEntry> idToEntry;
Dictionary<string, uint> filenameToId; Dictionary<string, uint> filenameToId;
MFS_MasterDirectoryBlock volMDB; MFS_MasterDirectoryBlock volMDB;
byte[] bootBlocks; byte[] bootBlocks;
byte[] mdbBlocks; byte[] mdbBlocks;
byte[] directoryBlocks; byte[] directoryBlocks;
byte[] blockMapBytes; byte[] blockMapBytes;
uint[] blockMap; uint[] blockMap;
int sectorsPerBlock; int sectorsPerBlock;
byte[] bootTags; byte[] bootTags;
byte[] mdbTags; byte[] mdbTags;
byte[] directoryTags; byte[] directoryTags;
byte[] bitmapTags; byte[] bitmapTags;
public FileSystemType XmlFsType { get; private set; } public FileSystemType XmlFsType { get; private set; }
public string Name => "Apple Macintosh File System"; public string Name => "Apple Macintosh File System";
public Guid Id => new Guid("36405F8D-0D26-4066-6538-5DBF5D065C3A"); public Guid Id => new Guid("36405F8D-0D26-4066-6538-5DBF5D065C3A");
public Encoding Encoding { get; private set; } public Encoding Encoding { get; private set; }
public AppleMFS() static Dictionary<string, string> GetDefaultOptions()
{ {
Encoding = Encoding.GetEncoding("macintosh"); return new Dictionary<string, string> {{"debug", false.ToString()}};
}
public AppleMFS(Encoding encoding)
{
Encoding = encoding ?? Encoding.GetEncoding("macintosh");
}
public AppleMFS(IMediaImage imagePlugin, Partition partition, Encoding encoding)
{
device = imagePlugin;
partitionStart = partition.Start;
Encoding = encoding ?? Encoding.GetEncoding("macintosh");
} }
} }
} }

View File

@@ -31,6 +31,7 @@
// ****************************************************************************/ // ****************************************************************************/
using System; using System;
using System.Collections.Generic;
using System.Text; using System.Text;
using DiscImageChef.CommonTypes; using DiscImageChef.CommonTypes;
using DiscImageChef.DiscImages; using DiscImageChef.DiscImages;
@@ -41,15 +42,17 @@ namespace DiscImageChef.Filesystems.AppleMFS
// Information from Inside Macintosh Volume II // Information from Inside Macintosh Volume II
public partial class AppleMFS public partial class AppleMFS
{ {
public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, bool debug) public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding,
Dictionary<string, string> options)
{ {
device = imagePlugin; device = imagePlugin;
partitionStart = partition.Start; partitionStart = partition.Start;
Encoding = encoding ?? Encoding.GetEncoding("macintosh"); Encoding = encoding ?? Encoding.GetEncoding("macintosh");
this.debug = debug; if(options == null) options = GetDefaultOptions();
if(options.TryGetValue("debug", out string debugString)) bool.TryParse(debugString, out debug);
volMDB = new MFS_MasterDirectoryBlock(); volMDB = new MFS_MasterDirectoryBlock();
mdbBlocks = device.ReadSector(2 + partitionStart); mdbBlocks = device.ReadSector(2 + partitionStart);
bootBlocks = device.ReadSector(0 + partitionStart); bootBlocks = device.ReadSector(0 + partitionStart);
BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;
@@ -57,63 +60,64 @@ namespace DiscImageChef.Filesystems.AppleMFS
volMDB.drSigWord = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x000); volMDB.drSigWord = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x000);
if(volMDB.drSigWord != MFS_MAGIC) return Errno.InvalidArgument; if(volMDB.drSigWord != MFS_MAGIC) return Errno.InvalidArgument;
volMDB.drCrDate = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x002); volMDB.drCrDate = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x002);
volMDB.drLsBkUp = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x006); volMDB.drLsBkUp = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x006);
volMDB.drAtrb = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x00A); volMDB.drAtrb = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x00A);
volMDB.drNmFls = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x00C); volMDB.drNmFls = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x00C);
volMDB.drDirSt = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x00E); volMDB.drDirSt = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x00E);
volMDB.drBlLen = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x010); volMDB.drBlLen = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x010);
volMDB.drNmAlBlks = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x012); volMDB.drNmAlBlks = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x012);
volMDB.drAlBlkSiz = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x014); volMDB.drAlBlkSiz = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x014);
volMDB.drClpSiz = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x018); volMDB.drClpSiz = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x018);
volMDB.drAlBlSt = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x01C); volMDB.drAlBlSt = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x01C);
volMDB.drNxtFNum = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x01E); volMDB.drNxtFNum = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x01E);
volMDB.drFreeBks = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x022); volMDB.drFreeBks = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x022);
volMDB.drVNSiz = mdbBlocks[0x024]; volMDB.drVNSiz = mdbBlocks[0x024];
byte[] variableSize = new byte[volMDB.drVNSiz + 1]; byte[] variableSize = new byte[volMDB.drVNSiz + 1];
Array.Copy(mdbBlocks, 0x024, variableSize, 0, volMDB.drVNSiz + 1); Array.Copy(mdbBlocks, 0x024, variableSize, 0, volMDB.drVNSiz + 1);
volMDB.drVN = StringHandlers.PascalToString(variableSize, Encoding); volMDB.drVN = StringHandlers.PascalToString(variableSize, Encoding);
directoryBlocks = device.ReadSectors(volMDB.drDirSt + partitionStart, volMDB.drBlLen); directoryBlocks = device.ReadSectors(volMDB.drDirSt + partitionStart, volMDB.drBlLen);
int bytesInBlockMap = volMDB.drNmAlBlks * 12 / 8 + volMDB.drNmAlBlks * 12 % 8; int bytesInBlockMap = volMDB.drNmAlBlks * 12 / 8 + volMDB.drNmAlBlks * 12 % 8;
const int BYTES_BEFORE_BLOCK_MAP = 64; const int BYTES_BEFORE_BLOCK_MAP = 64;
int bytesInWholeMdb = bytesInBlockMap + BYTES_BEFORE_BLOCK_MAP; int bytesInWholeMdb = bytesInBlockMap + BYTES_BEFORE_BLOCK_MAP;
int sectorsInWholeMdb = bytesInWholeMdb / (int)device.Info.SectorSize + int sectorsInWholeMdb = bytesInWholeMdb / (int)device.Info.SectorSize +
bytesInWholeMdb % (int)device.Info.SectorSize; bytesInWholeMdb % (int)device.Info.SectorSize;
byte[] wholeMdb = device.ReadSectors(partitionStart + 2, (uint)sectorsInWholeMdb); byte[] wholeMdb = device.ReadSectors(partitionStart + 2, (uint)sectorsInWholeMdb);
blockMapBytes = new byte[bytesInBlockMap]; blockMapBytes = new byte[bytesInBlockMap];
Array.Copy(wholeMdb, BYTES_BEFORE_BLOCK_MAP, blockMapBytes, 0, blockMapBytes.Length); Array.Copy(wholeMdb, BYTES_BEFORE_BLOCK_MAP, blockMapBytes, 0, blockMapBytes.Length);
int offset = 0; int offset = 0;
blockMap = new uint[volMDB.drNmAlBlks + 2 + 1]; blockMap = new uint[volMDB.drNmAlBlks + 2 + 1];
for(int i = 2; i < volMDB.drNmAlBlks + 2; i += 8) for(int i = 2; i < volMDB.drNmAlBlks + 2; i += 8)
{ {
uint tmp1 = 0; uint tmp1 = 0;
uint tmp2 = 0; uint tmp2 = 0;
uint tmp3 = 0; uint tmp3 = 0;
if(offset + 4 <= blockMapBytes.Length) tmp1 = BigEndianBitConverter.ToUInt32(blockMapBytes, offset); if(offset + 4 <= blockMapBytes.Length)
if(offset + 4 + 4 <= blockMapBytes.Length) tmp1 = BigEndianBitConverter.ToUInt32(blockMapBytes, offset);
if(offset + 4 + 4 <= blockMapBytes.Length)
tmp2 = BigEndianBitConverter.ToUInt32(blockMapBytes, offset + 4); tmp2 = BigEndianBitConverter.ToUInt32(blockMapBytes, offset + 4);
if(offset + 8 + 4 <= blockMapBytes.Length) if(offset + 8 + 4 <= blockMapBytes.Length)
tmp3 = BigEndianBitConverter.ToUInt32(blockMapBytes, offset + 8); tmp3 = BigEndianBitConverter.ToUInt32(blockMapBytes, offset + 8);
if(i < blockMap.Length) blockMap[i] = (tmp1 & 0xFFF00000) >> 20; if(i < blockMap.Length) blockMap[i] = (tmp1 & 0xFFF00000) >> 20;
if(i + 2 < blockMap.Length) blockMap[i + 1] = (tmp1 & 0xFFF00) >> 8; if(i + 2 < blockMap.Length) blockMap[i + 1] = (tmp1 & 0xFFF00) >> 8;
if(i + 3 < blockMap.Length) blockMap[i + 2] = ((tmp1 & 0xFF) << 4) + ((tmp2 & 0xF0000000) >> 28); if(i + 3 < blockMap.Length) blockMap[i + 2] = ((tmp1 & 0xFF) << 4) + ((tmp2 & 0xF0000000) >> 28);
if(i + 4 < blockMap.Length) blockMap[i + 3] = (tmp2 & 0xFFF0000) >> 16; if(i + 4 < blockMap.Length) blockMap[i + 3] = (tmp2 & 0xFFF0000) >> 16;
if(i + 5 < blockMap.Length) blockMap[i + 4] = (tmp2 & 0xFFF0) >> 4; if(i + 5 < blockMap.Length) blockMap[i + 4] = (tmp2 & 0xFFF0) >> 4;
if(i + 6 < blockMap.Length) blockMap[i + 5] = ((tmp2 & 0xF) << 8) + ((tmp3 & 0xFF000000) >> 24); if(i + 6 < blockMap.Length) blockMap[i + 5] = ((tmp2 & 0xF) << 8) + ((tmp3 & 0xFF000000) >> 24);
if(i + 7 < blockMap.Length) blockMap[i + 6] = (tmp3 & 0xFFF000) >> 12; if(i + 7 < blockMap.Length) blockMap[i + 6] = (tmp3 & 0xFFF000) >> 12;
if(i + 8 < blockMap.Length) blockMap[i + 7] = tmp3 & 0xFFF; if(i + 8 < blockMap.Length) blockMap[i + 7] = tmp3 & 0xFFF;
offset += 12; offset += 12;
} }
if(device.Info.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag)) if(device.Info.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag))
{ {
mdbTags = device.ReadSectorTag(2 + partitionStart, SectorTagType.AppleSectorTag); mdbTags = device.ReadSectorTag(2 + partitionStart, SectorTagType.AppleSectorTag);
bootTags = device.ReadSectorTag(0 + partitionStart, SectorTagType.AppleSectorTag); bootTags = device.ReadSectorTag(0 + partitionStart, SectorTagType.AppleSectorTag);
directoryTags = device.ReadSectorsTag(volMDB.drDirSt + partitionStart, volMDB.drBlLen, directoryTags = device.ReadSectorsTag(volMDB.drDirSt + partitionStart, volMDB.drBlLen,
SectorTagType.AppleSectorTag); SectorTagType.AppleSectorTag);
bitmapTags = device.ReadSectorsTag(partitionStart + 2, (uint)sectorsInWholeMdb, bitmapTags = device.ReadSectorsTag(partitionStart + 2, (uint)sectorsInWholeMdb,
@@ -133,34 +137,36 @@ namespace DiscImageChef.Filesystems.AppleMFS
XmlFsType = new FileSystemType(); XmlFsType = new FileSystemType();
if(volMDB.drLsBkUp > 0) if(volMDB.drLsBkUp > 0)
{ {
XmlFsType.BackupDate = DateHandlers.MacToDateTime(volMDB.drLsBkUp); XmlFsType.BackupDate = DateHandlers.MacToDateTime(volMDB.drLsBkUp);
XmlFsType.BackupDateSpecified = true; XmlFsType.BackupDateSpecified = true;
} }
XmlFsType.Bootable = bbSig == MFSBB_MAGIC;
XmlFsType.Clusters = volMDB.drNmAlBlks; XmlFsType.Bootable = bbSig == MFSBB_MAGIC;
XmlFsType.Clusters = volMDB.drNmAlBlks;
XmlFsType.ClusterSize = (int)volMDB.drAlBlkSiz; XmlFsType.ClusterSize = (int)volMDB.drAlBlkSiz;
if(volMDB.drCrDate > 0) if(volMDB.drCrDate > 0)
{ {
XmlFsType.CreationDate = DateHandlers.MacToDateTime(volMDB.drCrDate); XmlFsType.CreationDate = DateHandlers.MacToDateTime(volMDB.drCrDate);
XmlFsType.CreationDateSpecified = true; XmlFsType.CreationDateSpecified = true;
} }
XmlFsType.Files = volMDB.drNmFls;
XmlFsType.FilesSpecified = true; XmlFsType.Files = volMDB.drNmFls;
XmlFsType.FreeClusters = volMDB.drFreeBks; XmlFsType.FilesSpecified = true;
XmlFsType.FreeClusters = volMDB.drFreeBks;
XmlFsType.FreeClustersSpecified = true; XmlFsType.FreeClustersSpecified = true;
XmlFsType.Type = "MFS"; XmlFsType.Type = "MFS";
XmlFsType.VolumeName = volMDB.drVN; XmlFsType.VolumeName = volMDB.drVN;
return Errno.NoError; return Errno.NoError;
} }
public Errno Unmount() public Errno Unmount()
{ {
mounted = false; mounted = false;
idToFilename = null; idToFilename = null;
idToEntry = null; idToEntry = null;
filenameToId = null; filenameToId = null;
bootBlocks = null; bootBlocks = null;
return Errno.NoError; return Errno.NoError;
} }
@@ -169,12 +175,12 @@ namespace DiscImageChef.Filesystems.AppleMFS
{ {
stat = new FileSystemInfo stat = new FileSystemInfo
{ {
Blocks = volMDB.drNmAlBlks, Blocks = volMDB.drNmAlBlks,
FilenameLength = 255, FilenameLength = 255,
Files = volMDB.drNmFls, Files = volMDB.drNmFls,
FreeBlocks = volMDB.drFreeBks, FreeBlocks = volMDB.drFreeBks,
PluginId = Id, PluginId = Id,
Type = "Apple MFS" Type = "Apple MFS"
}; };
stat.FreeFiles = uint.MaxValue - stat.Files; stat.FreeFiles = uint.MaxValue - stat.Files;

View File

@@ -60,7 +60,7 @@ namespace DiscImageChef.Filesystems.CPM
/// Stores all known CP/M disk definitions /// Stores all known CP/M disk definitions
/// </summary> /// </summary>
CpmDefinitions definitions; CpmDefinitions definitions;
IMediaImage device; IMediaImage device;
/// <summary> /// <summary>
/// Cached directory listing /// Cached directory listing
/// </summary> /// </summary>
@@ -84,8 +84,8 @@ namespace DiscImageChef.Filesystems.CPM
/// <summary> /// <summary>
/// Timestamp in volume label for update /// Timestamp in volume label for update
/// </summary> /// </summary>
byte[] labelUpdateDate; byte[] labelUpdateDate;
bool mounted; bool mounted;
Partition partition; Partition partition;
/// <summary> /// <summary>
/// Cached file passwords /// Cached file passwords
@@ -113,8 +113,13 @@ namespace DiscImageChef.Filesystems.CPM
CpmDefinition workingDefinition; CpmDefinition workingDefinition;
public FileSystemType XmlFsType { get; private set; } public FileSystemType XmlFsType { get; private set; }
public Encoding Encoding { get; private set; } public Encoding Encoding { get; private set; }
public string Name => "CP/M File System"; public string Name => "CP/M File System";
public Guid Id => new Guid("AA2B8585-41DF-4E3B-8A35-D1A935E2F8A1"); public Guid Id => new Guid("AA2B8585-41DF-4E3B-8A35-D1A935E2F8A1");
static Dictionary<string, string> GetDefaultOptions()
{
return new Dictionary<string, string> {{"debug", false.ToString()}};
}
} }
} }

View File

@@ -47,11 +47,12 @@ namespace DiscImageChef.Filesystems.CPM
{ {
partial class CPM partial class CPM
{ {
public Errno Mount(IMediaImage imagePlugin, Partition partition1, Encoding encoding, bool debug) public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding,
Dictionary<string, string> options)
{ {
device = imagePlugin; device = imagePlugin;
partition = partition; this.partition = partition;
Encoding = encoding ?? Encoding.GetEncoding("IBM437"); Encoding = encoding ?? Encoding.GetEncoding("IBM437");
// As the identification is so complex, just call Identify() and relay on its findings // As the identification is so complex, just call Identify() and relay on its findings
if(!Identify(device, partition) || !cpmFound || workingDefinition == null || dpb == null) if(!Identify(device, partition) || !cpmFound || workingDefinition == null || dpb == null)
@@ -75,7 +76,7 @@ namespace DiscImageChef.Filesystems.CPM
sectorMask[m] = workingDefinition.side1.sectorIds[m] - workingDefinition.side1.sectorIds[0]; sectorMask[m] = workingDefinition.side1.sectorIds[m] - workingDefinition.side1.sectorIds[0];
// Skip first track (first side) // Skip first track (first side)
for(int m = 0; m < workingDefinition.side2.sectorIds.Length; m++) for(int m = 0; m < workingDefinition.side2.sectorIds.Length; m++)
sectorMask[m + workingDefinition.side1.sectorIds.Length] = sectorMask[m + workingDefinition.side1.sectorIds.Length] =
workingDefinition.side2.sectorIds[m] - workingDefinition.side2.sectorIds[0] + workingDefinition.side2.sectorIds[m] - workingDefinition.side2.sectorIds[0] +
workingDefinition.side1.sectorIds.Length; workingDefinition.side1.sectorIds.Length;
} }
@@ -87,8 +88,8 @@ namespace DiscImageChef.Filesystems.CPM
sectorMask[m] = workingDefinition.side1.sectorIds[m] - workingDefinition.side1.sectorIds[0]; sectorMask[m] = workingDefinition.side1.sectorIds[m] - workingDefinition.side1.sectorIds[0];
// Skip first track (first side) and first track (second side) // Skip first track (first side) and first track (second side)
for(int m = 0; m < workingDefinition.side1.sectorIds.Length; m++) for(int m = 0; m < workingDefinition.side1.sectorIds.Length; m++)
sectorMask[m + workingDefinition.side1.sectorIds.Length] = sectorMask[m + workingDefinition.side1.sectorIds.Length] =
workingDefinition.side1.sectorIds[m] - workingDefinition.side1.sectorIds[0] + workingDefinition.side1.sectorIds[m] - workingDefinition.side1.sectorIds[0] +
workingDefinition.side1.sectorIds.Length + workingDefinition.side2.sectorIds.Length; workingDefinition.side1.sectorIds.Length + workingDefinition.side2.sectorIds.Length;
// TODO: Implement CYLINDERS ordering // TODO: Implement CYLINDERS ordering
@@ -96,9 +97,8 @@ namespace DiscImageChef.Filesystems.CPM
return Errno.NotImplemented; return Errno.NotImplemented;
} }
// TODO: Implement COLUMBIA ordering // TODO: Implement COLUMBIA ordering
else if( else if(string.Compare(workingDefinition.order, "COLUMBIA",
string.Compare(workingDefinition.order, "COLUMBIA", StringComparison.InvariantCultureIgnoreCase) == StringComparison.InvariantCultureIgnoreCase) == 0)
0)
{ {
DicConsole.DebugWriteLine("CP/M Plugin", DicConsole.DebugWriteLine("CP/M Plugin",
"Don't know how to handle COLUMBIA ordering, not proceeding with this definition."); "Don't know how to handle COLUMBIA ordering, not proceeding with this definition.");
@@ -123,7 +123,7 @@ namespace DiscImageChef.Filesystems.CPM
// Deinterleave whole volume // Deinterleave whole volume
Dictionary<ulong, byte[]> deinterleavedSectors = new Dictionary<ulong, byte[]>(); Dictionary<ulong, byte[]> deinterleavedSectors = new Dictionary<ulong, byte[]>();
if(workingDefinition.sides == 1 || if(workingDefinition.sides == 1 ||
string.Compare(workingDefinition.order, "SIDES", StringComparison.InvariantCultureIgnoreCase) == 0) string.Compare(workingDefinition.order, "SIDES", StringComparison.InvariantCultureIgnoreCase) == 0)
{ {
DicConsole.DebugWriteLine("CP/M Plugin", "Deinterleaving whole volume."); DicConsole.DebugWriteLine("CP/M Plugin", "Deinterleaving whole volume.");
@@ -132,18 +132,19 @@ namespace DiscImageChef.Filesystems.CPM
{ {
byte[] readSector = byte[] readSector =
device.ReadSector((ulong)((int)partition.Start + p / sectorMask.Length * sectorMask.Length + device.ReadSector((ulong)((int)partition.Start + p / sectorMask.Length * sectorMask.Length +
sectorMask[p % sectorMask.Length])); sectorMask[p % sectorMask.Length]));
if(workingDefinition.complement) if(workingDefinition.complement)
for(int b = 0; b < readSector.Length; b++) readSector[b] = (byte)(~readSector[b] & 0xFF); for(int b = 0; b < readSector.Length; b++)
readSector[b] = (byte)(~readSector[b] & 0xFF);
deinterleavedSectors.Add((ulong)p, readSector); deinterleavedSectors.Add((ulong)p, readSector);
} }
} }
int blockSize = 128 << dpb.bsh; int blockSize = 128 << dpb.bsh;
MemoryStream blockMs = new MemoryStream(); MemoryStream blockMs = new MemoryStream();
ulong blockNo = 0; ulong blockNo = 0;
int sectorsPerBlock = 0; int sectorsPerBlock = 0;
Dictionary<ulong, byte[]> allocationBlocks = new Dictionary<ulong, byte[]>(); Dictionary<ulong, byte[]> allocationBlocks = new Dictionary<ulong, byte[]>();
DicConsole.DebugWriteLine("CP/M Plugin", "Creating allocation blocks."); DicConsole.DebugWriteLine("CP/M Plugin", "Creating allocation blocks.");
@@ -154,7 +155,7 @@ namespace DiscImageChef.Filesystems.CPM
deinterleavedSectors.TryGetValue(a, out byte[] sector); deinterleavedSectors.TryGetValue(a, out byte[] sector);
// May it happen? Just in case, CP/M blocks are smaller than physical sectors // May it happen? Just in case, CP/M blocks are smaller than physical sectors
if(sector.Length > blockSize) if(sector.Length > blockSize)
for(int i = 0; i < sector.Length / blockSize; i++) for(int i = 0; i < sector.Length / blockSize; i++)
{ {
byte[] tmp = new byte[blockSize]; byte[] tmp = new byte[blockSize];
@@ -171,18 +172,19 @@ namespace DiscImageChef.Filesystems.CPM
allocationBlocks.Add(blockNo++, blockMs.ToArray()); allocationBlocks.Add(blockNo++, blockMs.ToArray());
sectorsPerBlock = 0; sectorsPerBlock = 0;
blockMs = new MemoryStream(); blockMs = new MemoryStream();
} }
// CP/M blocks are same size than physical sectors // CP/M blocks are same size than physical sectors
else allocationBlocks.Add(blockNo++, sector); else
allocationBlocks.Add(blockNo++, sector);
} }
DicConsole.DebugWriteLine("CP/M Plugin", "Reading directory."); DicConsole.DebugWriteLine("CP/M Plugin", "Reading directory.");
int dirOff; int dirOff;
int dirSectors = (dpb.drm + 1) * 32 / workingDefinition.bytesPerSector; int dirSectors = (dpb.drm + 1) * 32 / workingDefinition.bytesPerSector;
if(workingDefinition.sofs > 0) dirOff = workingDefinition.sofs; if(workingDefinition.sofs > 0) dirOff = workingDefinition.sofs;
else dirOff = workingDefinition.ofs * workingDefinition.sectorsPerTrack; else dirOff = workingDefinition.ofs * workingDefinition.sectorsPerTrack;
// Read the whole directory blocks // Read the whole directory blocks
MemoryStream dirMs = new MemoryStream(); MemoryStream dirMs = new MemoryStream();
@@ -196,19 +198,19 @@ namespace DiscImageChef.Filesystems.CPM
if(directory == null) return Errno.InvalidArgument; if(directory == null) return Errno.InvalidArgument;
int dirCnt = 0; int dirCnt = 0;
string file1 = null; string file1 = null;
string file2 = null; string file2 = null;
string file3 = null; string file3 = null;
Dictionary<string, Dictionary<int, List<ushort>>> fileExtents = Dictionary<string, Dictionary<int, List<ushort>>> fileExtents =
new Dictionary<string, Dictionary<int, List<ushort>>>(); new Dictionary<string, Dictionary<int, List<ushort>>>();
statCache = new Dictionary<string, FileEntryInfo>(); statCache = new Dictionary<string, FileEntryInfo>();
cpmStat = new FileSystemInfo(); cpmStat = new FileSystemInfo();
bool atime = false; bool atime = false;
dirList = new List<string>(); dirList = new List<string>();
labelCreationDate = null; labelCreationDate = null;
labelUpdateDate = null; labelUpdateDate = null;
passwordCache = new Dictionary<string, byte[]>(); passwordCache = new Dictionary<string, byte[]>();
DicConsole.DebugWriteLine("CP/M Plugin", "Traversing directory."); DicConsole.DebugWriteLine("CP/M Plugin", "Traversing directory.");
IntPtr dirPtr; IntPtr dirPtr;
@@ -216,7 +218,7 @@ namespace DiscImageChef.Filesystems.CPM
// For each directory entry // For each directory entry
for(int dOff = 0; dOff < directory.Length; dOff += 32) for(int dOff = 0; dOff < directory.Length; dOff += 32)
// Describes a file (does not support PDOS entries with user >= 16, because they're identical to password entries // Describes a file (does not support PDOS entries with user >= 16, because they're identical to password entries
if((directory[dOff] & 0x7F) < 0x10) if((directory[dOff] & 0x7F) < 0x10)
if(allocationBlocks.Count > 256) if(allocationBlocks.Count > 256)
{ {
dirPtr = Marshal.AllocHGlobal(32); dirPtr = Marshal.AllocHGlobal(32);
@@ -225,7 +227,7 @@ namespace DiscImageChef.Filesystems.CPM
(DirectoryEntry16)Marshal.PtrToStructure(dirPtr, typeof(DirectoryEntry16)); (DirectoryEntry16)Marshal.PtrToStructure(dirPtr, typeof(DirectoryEntry16));
Marshal.FreeHGlobal(dirPtr); Marshal.FreeHGlobal(dirPtr);
bool hidden = (entry.statusUser & 0x80) == 0x80; bool hidden = (entry.statusUser & 0x80) == 0x80;
bool rdOnly = (entry.filename[0] & 0x80) == 0x80 || (entry.extension[0] & 0x80) == 0x80; bool rdOnly = (entry.filename[0] & 0x80) == 0x80 || (entry.extension[0] & 0x80) == 0x80;
bool system = (entry.filename[1] & 0x80) == 0x80 || (entry.extension[2] & 0x80) == 0x80; bool system = (entry.filename[1] & 0x80) == 0x80 || (entry.extension[2] & 0x80) == 0x80;
//bool backed = (entry.filename[3] & 0x80) == 0x80 || (entry.extension[3] & 0x80) == 0x80; //bool backed = (entry.filename[3] & 0x80) == 0x80 || (entry.extension[3] & 0x80) == 0x80;
@@ -236,21 +238,22 @@ namespace DiscImageChef.Filesystems.CPM
for(int i = 0; i < 8; i++) for(int i = 0; i < 8; i++)
{ {
entry.filename[i] &= 0x7F; entry.filename[i] &= 0x7F;
validEntry &= entry.filename[i] >= 0x20; validEntry &= entry.filename[i] >= 0x20;
} }
for(int i = 0; i < 3; i++) for(int i = 0; i < 3; i++)
{ {
entry.extension[i] &= 0x7F; entry.extension[i] &= 0x7F;
validEntry &= entry.extension[i] >= 0x20; validEntry &= entry.extension[i] >= 0x20;
} }
if(!validEntry) continue; if(!validEntry) continue;
string filename = Encoding.ASCII.GetString(entry.filename).Trim(); string filename = Encoding.ASCII.GetString(entry.filename).Trim();
string extension = Encoding.ASCII.GetString(entry.extension).Trim(); string extension = Encoding.ASCII.GetString(entry.extension).Trim();
// If user is != 0, append user to name to have identical filenames // If user is != 0, append user to name to have identical filenames
if(user > 0) filename = $"{user:X1}:{filename}"; if(user > 0) filename = $"{user:X1}:{filename}";
if(!string.IsNullOrEmpty(extension)) filename = filename + "." + extension; if(!string.IsNullOrEmpty(extension)) filename = filename + "." + extension;
int entryNo = (32 * entry.extentCounter + entry.extentCounterHigh) / (dpb.exm + 1); int entryNo = (32 * entry.extentCounter + entry.extentCounterHigh) / (dpb.exm + 1);
@@ -273,11 +276,11 @@ namespace DiscImageChef.Filesystems.CPM
if(rdOnly) fInfo.Attributes |= FileAttributes.ReadOnly; if(rdOnly) fInfo.Attributes |= FileAttributes.ReadOnly;
if(system) fInfo.Attributes |= FileAttributes.System; if(system) fInfo.Attributes |= FileAttributes.System;
// Supposedly there is a value in the directory entry telling how many blocks are designated in this entry // Supposedly there is a value in the directory entry telling how many blocks are designated in
// However some implementations tend to do whatever they wish, but none will ever allocate block 0 for a file // this entry. However some implementations tend to do whatever they wish, but none will ever
// because that's where the directory resides. // allocate block 0 for a file because that's where the directory resides.
// There is also a field telling how many bytes are used in the last block, but its meaning is non-standard so // There is also a field telling how many bytes are used in the last block, but its meaning is
// we must ignore it. // non-standard so we must ignore it.
foreach(ushort blk in entry.allocations.Where(blk => !blocks.Contains(blk) && blk != 0)) foreach(ushort blk in entry.allocations.Where(blk => !blocks.Contains(blk) && blk != 0))
blocks.Add(blk); blocks.Add(blk);
@@ -313,7 +316,7 @@ namespace DiscImageChef.Filesystems.CPM
DirectoryEntry entry = (DirectoryEntry)Marshal.PtrToStructure(dirPtr, typeof(DirectoryEntry)); DirectoryEntry entry = (DirectoryEntry)Marshal.PtrToStructure(dirPtr, typeof(DirectoryEntry));
Marshal.FreeHGlobal(dirPtr); Marshal.FreeHGlobal(dirPtr);
bool hidden = (entry.statusUser & 0x80) == 0x80; bool hidden = (entry.statusUser & 0x80) == 0x80;
bool rdOnly = (entry.filename[0] & 0x80) == 0x80 || (entry.extension[0] & 0x80) == 0x80; bool rdOnly = (entry.filename[0] & 0x80) == 0x80 || (entry.extension[0] & 0x80) == 0x80;
bool system = (entry.filename[1] & 0x80) == 0x80 || (entry.extension[2] & 0x80) == 0x80; bool system = (entry.filename[1] & 0x80) == 0x80 || (entry.extension[2] & 0x80) == 0x80;
//bool backed = (entry.filename[3] & 0x80) == 0x80 || (entry.extension[3] & 0x80) == 0x80; //bool backed = (entry.filename[3] & 0x80) == 0x80 || (entry.extension[3] & 0x80) == 0x80;
@@ -324,21 +327,22 @@ namespace DiscImageChef.Filesystems.CPM
for(int i = 0; i < 8; i++) for(int i = 0; i < 8; i++)
{ {
entry.filename[i] &= 0x7F; entry.filename[i] &= 0x7F;
validEntry &= entry.filename[i] >= 0x20; validEntry &= entry.filename[i] >= 0x20;
} }
for(int i = 0; i < 3; i++) for(int i = 0; i < 3; i++)
{ {
entry.extension[i] &= 0x7F; entry.extension[i] &= 0x7F;
validEntry &= entry.extension[i] >= 0x20; validEntry &= entry.extension[i] >= 0x20;
} }
if(!validEntry) continue; if(!validEntry) continue;
string filename = Encoding.ASCII.GetString(entry.filename).Trim(); string filename = Encoding.ASCII.GetString(entry.filename).Trim();
string extension = Encoding.ASCII.GetString(entry.extension).Trim(); string extension = Encoding.ASCII.GetString(entry.extension).Trim();
// If user is != 0, append user to name to have identical filenames // If user is != 0, append user to name to have identical filenames
if(user > 0) filename = $"{user:X1}:{filename}"; if(user > 0) filename = $"{user:X1}:{filename}";
if(!string.IsNullOrEmpty(extension)) filename = filename + "." + extension; if(!string.IsNullOrEmpty(extension)) filename = filename + "." + extension;
int entryNo = (32 * entry.extentCounterHigh + entry.extentCounter) / (dpb.exm + 1); int entryNo = (32 * entry.extentCounterHigh + entry.extentCounter) / (dpb.exm + 1);
@@ -361,11 +365,11 @@ namespace DiscImageChef.Filesystems.CPM
if(rdOnly) fInfo.Attributes |= FileAttributes.ReadOnly; if(rdOnly) fInfo.Attributes |= FileAttributes.ReadOnly;
if(system) fInfo.Attributes |= FileAttributes.System; if(system) fInfo.Attributes |= FileAttributes.System;
// Supposedly there is a value in the directory entry telling how many blocks are designated in this entry // Supposedly there is a value in the directory entry telling how many blocks are designated in
// However some implementations tend to do whatever they wish, but none will ever allocate block 0 for a file // this entry. However some implementations tend to do whatever they wish, but none will ever
// because that's where the directory resides. // allocate block 0 for a file because that's where the directory resides.
// There is also a field telling how many bytes are used in the last block, but its meaning is non-standard so // There is also a field telling how many bytes are used in the last block, but its meaning is
// we must ignore it. // non-standard so we must ignore it.
foreach(ushort blk in entry.allocations.Cast<ushort>() foreach(ushort blk in entry.allocations.Cast<ushort>()
.Where(blk => !blocks.Contains(blk) && blk != 0)) blocks.Add(blk); .Where(blk => !blocks.Contains(blk) && blk != 0)) blocks.Add(blk);
@@ -404,14 +408,14 @@ namespace DiscImageChef.Filesystems.CPM
int user = entry.userNumber & 0x0F; int user = entry.userNumber & 0x0F;
for(int i = 0; i < 8; i++) entry.filename[i] &= 0x7F; for(int i = 0; i < 8; i++) entry.filename[i] &= 0x7F;
for(int i = 0; i < 3; i++) entry.extension[i] &= 0x7F; for(int i = 0; i < 3; i++) entry.extension[i] &= 0x7F;
string filename = Encoding.ASCII.GetString(entry.filename).Trim(); string filename = Encoding.ASCII.GetString(entry.filename).Trim();
string extension = Encoding.ASCII.GetString(entry.extension).Trim(); string extension = Encoding.ASCII.GetString(entry.extension).Trim();
// If user is != 0, append user to name to have identical filenames // If user is != 0, append user to name to have identical filenames
if(user > 0) filename = $"{user:X1}:{filename}"; if(user > 0) filename = $"{user:X1}:{filename}";
if(!string.IsNullOrEmpty(extension)) filename = filename + "." + extension; if(!string.IsNullOrEmpty(extension)) filename = filename + "." + extension;
// Do not repeat passwords // Do not repeat passwords
@@ -449,14 +453,15 @@ namespace DiscImageChef.Filesystems.CPM
labelEntry = (LabelEntry)Marshal.PtrToStructure(dirPtr, typeof(LabelEntry)); labelEntry = (LabelEntry)Marshal.PtrToStructure(dirPtr, typeof(LabelEntry));
Marshal.FreeHGlobal(dirPtr); Marshal.FreeHGlobal(dirPtr);
// The volume label defines if one of the fields in CP/M 3 timestamp is a creation or an access time // The volume label defines if one of the fields in CP/M 3 timestamp is a creation or an
// access time
atime |= (labelEntry.flags & 0x40) == 0x40; atime |= (labelEntry.flags & 0x40) == 0x40;
label = Encoding.ASCII.GetString(directory, dOff + 1, 11).Trim(); label = Encoding.ASCII.GetString(directory, dOff + 1, 11).Trim();
labelCreationDate = new byte[4]; labelCreationDate = new byte[4];
labelUpdateDate = new byte[4]; labelUpdateDate = new byte[4];
Array.Copy(directory, dOff + 24, labelCreationDate, 0, 4); Array.Copy(directory, dOff + 24, labelCreationDate, 0, 4);
Array.Copy(directory, dOff + 28, labelUpdateDate, 0, 4); Array.Copy(directory, dOff + 28, labelUpdateDate, 0, 4);
// Count entries 3 by 3 for timestamps // Count entries 3 by 3 for timestamps
switch(dirCnt % 3) switch(dirCnt % 3)
@@ -492,7 +497,7 @@ namespace DiscImageChef.Filesystems.CPM
else fInfo = new FileEntryInfo(); else fInfo = new FileEntryInfo();
if(atime) fInfo.AccessTime = DateHandlers.CpmToDateTime(dateEntry.date1); if(atime) fInfo.AccessTime = DateHandlers.CpmToDateTime(dateEntry.date1);
else fInfo.CreationTime = DateHandlers.CpmToDateTime(dateEntry.date1); else fInfo.CreationTime = DateHandlers.CpmToDateTime(dateEntry.date1);
fInfo.LastWriteTime = DateHandlers.CpmToDateTime(dateEntry.date2); fInfo.LastWriteTime = DateHandlers.CpmToDateTime(dateEntry.date2);
@@ -505,7 +510,7 @@ namespace DiscImageChef.Filesystems.CPM
else fInfo = new FileEntryInfo(); else fInfo = new FileEntryInfo();
if(atime) fInfo.AccessTime = DateHandlers.CpmToDateTime(dateEntry.date3); if(atime) fInfo.AccessTime = DateHandlers.CpmToDateTime(dateEntry.date3);
else fInfo.CreationTime = DateHandlers.CpmToDateTime(dateEntry.date3); else fInfo.CreationTime = DateHandlers.CpmToDateTime(dateEntry.date3);
fInfo.LastWriteTime = DateHandlers.CpmToDateTime(dateEntry.date4); fInfo.LastWriteTime = DateHandlers.CpmToDateTime(dateEntry.date4);
@@ -518,16 +523,16 @@ namespace DiscImageChef.Filesystems.CPM
else fInfo = new FileEntryInfo(); else fInfo = new FileEntryInfo();
if(atime) fInfo.AccessTime = DateHandlers.CpmToDateTime(dateEntry.date5); if(atime) fInfo.AccessTime = DateHandlers.CpmToDateTime(dateEntry.date5);
else fInfo.CreationTime = DateHandlers.CpmToDateTime(dateEntry.date5); else fInfo.CreationTime = DateHandlers.CpmToDateTime(dateEntry.date5);
fInfo.LastWriteTime = DateHandlers.CpmToDateTime(dateEntry.date6); fInfo.LastWriteTime = DateHandlers.CpmToDateTime(dateEntry.date6);
statCache.Add(file3, fInfo); statCache.Add(file3, fInfo);
} }
file1 = null; file1 = null;
file2 = null; file2 = null;
file3 = null; file3 = null;
dirCnt = 0; dirCnt = 0;
} }
// However, if this byte is 0, timestamp is in Z80DOS or DOS+ format // However, if this byte is 0, timestamp is in Z80DOS or DOS+ format
@@ -548,11 +553,11 @@ namespace DiscImageChef.Filesystems.CPM
else fInfo = new FileEntryInfo(); else fInfo = new FileEntryInfo();
byte[] ctime = new byte[4]; byte[] ctime = new byte[4];
ctime[0] = trdPartyDateEntry.create1[0]; ctime[0] = trdPartyDateEntry.create1[0];
ctime[1] = trdPartyDateEntry.create1[1]; ctime[1] = trdPartyDateEntry.create1[1];
fInfo.AccessTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.access1); fInfo.AccessTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.access1);
fInfo.CreationTime = DateHandlers.CpmToDateTime(ctime); fInfo.CreationTime = DateHandlers.CpmToDateTime(ctime);
fInfo.LastWriteTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.modify1); fInfo.LastWriteTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.modify1);
statCache.Add(file1, fInfo); statCache.Add(file1, fInfo);
@@ -564,11 +569,11 @@ namespace DiscImageChef.Filesystems.CPM
else fInfo = new FileEntryInfo(); else fInfo = new FileEntryInfo();
byte[] ctime = new byte[4]; byte[] ctime = new byte[4];
ctime[0] = trdPartyDateEntry.create2[0]; ctime[0] = trdPartyDateEntry.create2[0];
ctime[1] = trdPartyDateEntry.create2[1]; ctime[1] = trdPartyDateEntry.create2[1];
fInfo.AccessTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.access2); fInfo.AccessTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.access2);
fInfo.CreationTime = DateHandlers.CpmToDateTime(ctime); fInfo.CreationTime = DateHandlers.CpmToDateTime(ctime);
fInfo.LastWriteTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.modify2); fInfo.LastWriteTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.modify2);
statCache.Add(file2, fInfo); statCache.Add(file2, fInfo);
@@ -580,21 +585,22 @@ namespace DiscImageChef.Filesystems.CPM
else fInfo = new FileEntryInfo(); else fInfo = new FileEntryInfo();
byte[] ctime = new byte[4]; byte[] ctime = new byte[4];
ctime[0] = trdPartyDateEntry.create3[0]; ctime[0] = trdPartyDateEntry.create3[0];
ctime[1] = trdPartyDateEntry.create3[1]; ctime[1] = trdPartyDateEntry.create3[1];
fInfo.AccessTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.access3); fInfo.AccessTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.access3);
fInfo.CreationTime = DateHandlers.CpmToDateTime(ctime); fInfo.CreationTime = DateHandlers.CpmToDateTime(ctime);
fInfo.LastWriteTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.modify3); fInfo.LastWriteTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.modify3);
statCache.Add(file3, fInfo); statCache.Add(file3, fInfo);
} }
file1 = null; file1 = null;
file2 = null; file2 = null;
file3 = null; file3 = null;
dirCnt = 0; dirCnt = 0;
} }
break; break;
} }
@@ -602,7 +608,7 @@ namespace DiscImageChef.Filesystems.CPM
// this should not be a problem // this should not be a problem
DicConsole.DebugWriteLine("CP/M Plugin", "Reading files."); DicConsole.DebugWriteLine("CP/M Plugin", "Reading files.");
long usedBlocks = 0; long usedBlocks = 0;
fileCache = new Dictionary<string, byte[]>(); fileCache = new Dictionary<string, byte[]>();
foreach(string filename in dirList) foreach(string filename in dirList)
{ {
MemoryStream fileMs = new MemoryStream(); MemoryStream fileMs = new MemoryStream();
@@ -626,8 +632,8 @@ namespace DiscImageChef.Filesystems.CPM
// If you insist to call CP/M "extent based" // If you insist to call CP/M "extent based"
fInfo.Attributes |= FileAttributes.Extents; fInfo.Attributes |= FileAttributes.Extents;
fInfo.BlockSize = blockSize; fInfo.BlockSize = blockSize;
fInfo.Length = fileMs.Length; fInfo.Length = fileMs.Length;
cpmStat.Files++; cpmStat.Files++;
usedBlocks += fInfo.Blocks; usedBlocks += fInfo.Blocks;
@@ -648,34 +654,36 @@ namespace DiscImageChef.Filesystems.CPM
} }
// Generate statfs. // Generate statfs.
cpmStat.Blocks = dpb.dsm + 1; cpmStat.Blocks = dpb.dsm + 1;
cpmStat.FilenameLength = 11; cpmStat.FilenameLength = 11;
cpmStat.Files = (ulong)fileCache.Count; cpmStat.Files = (ulong)fileCache.Count;
cpmStat.FreeBlocks = cpmStat.Blocks - usedBlocks; cpmStat.FreeBlocks = cpmStat.Blocks - usedBlocks;
cpmStat.PluginId = Id; cpmStat.PluginId = Id;
cpmStat.Type = "CP/M filesystem"; cpmStat.Type = "CP/M filesystem";
// Generate XML info // Generate XML info
XmlFsType = new FileSystemType XmlFsType = new FileSystemType
{ {
Clusters = cpmStat.Blocks, Clusters = cpmStat.Blocks,
ClusterSize = blockSize, ClusterSize = blockSize,
Files = fileCache.Count, Files = fileCache.Count,
FilesSpecified = true, FilesSpecified = true,
FreeClusters = cpmStat.FreeBlocks, FreeClusters = cpmStat.FreeBlocks,
FreeClustersSpecified = true, FreeClustersSpecified = true,
Type = "CP/M filesystem" Type = "CP/M filesystem"
}; };
if(labelCreationDate != null) if(labelCreationDate != null)
{ {
XmlFsType.CreationDate = DateHandlers.CpmToDateTime(labelCreationDate); XmlFsType.CreationDate = DateHandlers.CpmToDateTime(labelCreationDate);
XmlFsType.CreationDateSpecified = true; XmlFsType.CreationDateSpecified = true;
} }
if(labelUpdateDate != null) if(labelUpdateDate != null)
{ {
XmlFsType.ModificationDate = DateHandlers.CpmToDateTime(labelUpdateDate); XmlFsType.ModificationDate = DateHandlers.CpmToDateTime(labelUpdateDate);
XmlFsType.ModificationDateSpecified = true; XmlFsType.ModificationDateSpecified = true;
} }
if(!string.IsNullOrEmpty(label)) XmlFsType.VolumeName = label; if(!string.IsNullOrEmpty(label)) XmlFsType.VolumeName = label;
mounted = true; mounted = true;
@@ -698,17 +706,17 @@ namespace DiscImageChef.Filesystems.CPM
public Errno Unmount() public Errno Unmount()
{ {
mounted = false; mounted = false;
definitions = null; definitions = null;
cpmFound = false; cpmFound = false;
workingDefinition = null; workingDefinition = null;
dpb = null; dpb = null;
sectorMask = null; sectorMask = null;
label = null; label = null;
thirdPartyTimestamps = false; thirdPartyTimestamps = false;
standardTimestamps = false; standardTimestamps = false;
labelCreationDate = null; labelCreationDate = null;
labelUpdateDate = null; labelUpdateDate = null;
return Errno.NoError; return Errno.NoError;
} }
} }

View File

@@ -31,12 +31,10 @@
// Copyright © 2011-2018 Natalia Portillo // Copyright © 2011-2018 Natalia Portillo
// ****************************************************************************/ // ****************************************************************************/
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using DiscImageChef.CommonTypes; using DiscImageChef.CommonTypes;
using DiscImageChef.DiscImages; using DiscImageChef.DiscImages;
using Schemas;
namespace DiscImageChef.Filesystems namespace DiscImageChef.Filesystems
{ {
@@ -52,13 +50,13 @@ namespace DiscImageChef.Filesystems
/// <param name="imagePlugin"></param> /// <param name="imagePlugin"></param>
/// <param name="partition"></param> /// <param name="partition"></param>
/// <param name="encoding">Which encoding to use for this filesystem.</param> /// <param name="encoding">Which encoding to use for this filesystem.</param>
/// <param name="debug">If <c>true</c> enable debug.</param> /// <param name="options">Dictionary of key=value pairs containing options to pass to the filesystem</param>
Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, bool debug); Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding,
Dictionary<string, string> options);
/// <summary> /// <summary>
/// Frees all internal structures created by /// Frees all internal structures created by
/// <see /// <see cref="Mount" />
/// cref="M:DiscImageChef.Filesystems.Filesystem.Mount(DiscImageChef.DiscImages.IMediaImage,DiscImageChef.CommonTypes.Partition,System.Text.Encoding,System.Boolean)" />
/// </summary> /// </summary>
Errno Unmount(); Errno Unmount();

View File

@@ -42,35 +42,40 @@ namespace DiscImageChef.Filesystems.LisaFS
// Variable names from Lisa API // Variable names from Lisa API
public partial class LisaFS : IReadOnlyFilesystem public partial class LisaFS : IReadOnlyFilesystem
{ {
bool debug; bool debug;
IMediaImage device; IMediaImage device;
int devTagSize; int devTagSize;
MDDF mddf; MDDF mddf;
bool mounted; bool mounted;
SRecord[] srecords; SRecord[] srecords;
ulong volumePrefix; ulong volumePrefix;
public string Name => "Apple Lisa File System"; public string Name => "Apple Lisa File System";
public Guid Id => new Guid("7E6034D1-D823-4248-A54D-239742B28391"); public Guid Id => new Guid("7E6034D1-D823-4248-A54D-239742B28391");
public Encoding Encoding { get; private set; } public Encoding Encoding { get; private set; }
public FileSystemType XmlFsType { get; private set; } public FileSystemType XmlFsType { get; private set; }
static Dictionary<string, string> GetDefaultOptions()
{
return new Dictionary<string, string> {{"debug", false.ToString()}};
}
#region Caches #region Caches
/// <summary>Caches Extents Files</summary> /// <summary>Caches Extents Files</summary>
Dictionary<short, ExtentFile> extentCache; Dictionary<short, ExtentFile> extentCache;
/// <summary>Caches system files</summary> /// <summary>Caches system files</summary>
Dictionary<short, byte[]> systemFileCache; Dictionary<short, byte[]> systemFileCache;
/// <summary>Caches user files files</summary> /// <summary>Caches user files files</summary>
Dictionary<short, byte[]> fileCache; Dictionary<short, byte[]> fileCache;
/// <summary>Caches catalogs</summary> /// <summary>Caches catalogs</summary>
List<CatalogEntry> catalogCache; List<CatalogEntry> catalogCache;
/// <summary>Caches file size</summary> /// <summary>Caches file size</summary>
Dictionary<short, int> fileSizeCache; Dictionary<short, int> fileSizeCache;
/// <summary>Lists Extents Files already printed in debug mode to not repeat them</summary> /// <summary>Lists Extents Files already printed in debug mode to not repeat them</summary>
List<short> printedExtents; List<short> printedExtents;
/// <summary>Caches the creation times for subdirectories as to not have to traverse the Catalog File on each stat</summary> /// <summary>Caches the creation times for subdirectories as to not have to traverse the Catalog File on each stat</summary>
Dictionary<short, DateTime> directoryDtcCache; Dictionary<short, DateTime> directoryDtcCache;
#endregion Caches #endregion Caches
} }
} }

View File

@@ -47,11 +47,12 @@ namespace DiscImageChef.Filesystems.LisaFS
/// <summary> /// <summary>
/// Mounts an Apple Lisa filesystem /// Mounts an Apple Lisa filesystem
/// </summary> /// </summary>
public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, bool debug) public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding,
Dictionary<string, string> options)
{ {
try try
{ {
device = imagePlugin; device = imagePlugin;
Encoding = new LisaRoman(); Encoding = new LisaRoman();
// Lisa OS is unable to work on disks without tags. // Lisa OS is unable to work on disks without tags.
@@ -91,96 +92,101 @@ namespace DiscImageChef.Filesystems.LisaFS
devTagSize = device.ReadSectorTag(i, SectorTagType.AppleSectorTag).Length; devTagSize = device.ReadSectorTag(i, SectorTagType.AppleSectorTag).Length;
byte[] sector = device.ReadSector(i); byte[] sector = device.ReadSector(i);
mddf = new MDDF(); mddf = new MDDF();
byte[] pString = new byte[33]; byte[] pString = new byte[33];
uint lisaTime; uint lisaTime;
mddf.fsversion = BigEndianBitConverter.ToUInt16(sector, 0x00); mddf.fsversion = BigEndianBitConverter.ToUInt16(sector, 0x00);
mddf.volid = BigEndianBitConverter.ToUInt64(sector, 0x02); mddf.volid = BigEndianBitConverter.ToUInt64(sector, 0x02);
mddf.volnum = BigEndianBitConverter.ToUInt16(sector, 0x0A); mddf.volnum = BigEndianBitConverter.ToUInt16(sector, 0x0A);
Array.Copy(sector, 0x0C, pString, 0, 33); Array.Copy(sector, 0x0C, pString, 0, 33);
mddf.volname = StringHandlers.PascalToString(pString, Encoding); mddf.volname = StringHandlers.PascalToString(pString, Encoding);
mddf.unknown1 = sector[0x2D]; mddf.unknown1 = sector[0x2D];
Array.Copy(sector, 0x2E, pString, 0, 33); Array.Copy(sector, 0x2E, pString, 0, 33);
// Prevent garbage // Prevent garbage
mddf.password = pString[0] <= 32 ? StringHandlers.PascalToString(pString, Encoding) : ""; mddf.password =
mddf.unknown2 = sector[0x4F]; pString[0] <= 32 ? StringHandlers.PascalToString(pString, Encoding) : "";
mddf.machine_id = BigEndianBitConverter.ToUInt32(sector, 0x50); mddf.unknown2 = sector[0x4F];
mddf.master_copy_id = BigEndianBitConverter.ToUInt32(sector, 0x54); mddf.machine_id = BigEndianBitConverter.ToUInt32(sector, 0x50);
lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x58); mddf.master_copy_id = BigEndianBitConverter.ToUInt32(sector, 0x54);
mddf.dtvc = DateHandlers.LisaToDateTime(lisaTime); lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x58);
lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x5C); mddf.dtvc = DateHandlers.LisaToDateTime(lisaTime);
mddf.dtcc = DateHandlers.LisaToDateTime(lisaTime); lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x5C);
lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x60); mddf.dtcc = DateHandlers.LisaToDateTime(lisaTime);
mddf.dtvb = DateHandlers.LisaToDateTime(lisaTime); lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x60);
lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x64); mddf.dtvb = DateHandlers.LisaToDateTime(lisaTime);
mddf.dtvs = DateHandlers.LisaToDateTime(lisaTime); lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x64);
mddf.unknown3 = BigEndianBitConverter.ToUInt32(sector, 0x68); mddf.dtvs = DateHandlers.LisaToDateTime(lisaTime);
mddf.mddf_block = BigEndianBitConverter.ToUInt32(sector, 0x6C); mddf.unknown3 = BigEndianBitConverter.ToUInt32(sector, 0x68);
mddf.volsize_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x70); mddf.mddf_block = BigEndianBitConverter.ToUInt32(sector, 0x6C);
mddf.volsize_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x70);
mddf.volsize_minus_mddf_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x74); mddf.volsize_minus_mddf_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x74);
mddf.vol_size = BigEndianBitConverter.ToUInt32(sector, 0x78); mddf.vol_size = BigEndianBitConverter.ToUInt32(sector, 0x78);
mddf.blocksize = BigEndianBitConverter.ToUInt16(sector, 0x7C); mddf.blocksize = BigEndianBitConverter.ToUInt16(sector, 0x7C);
mddf.datasize = BigEndianBitConverter.ToUInt16(sector, 0x7E); mddf.datasize = BigEndianBitConverter.ToUInt16(sector, 0x7E);
mddf.unknown4 = BigEndianBitConverter.ToUInt16(sector, 0x80); mddf.unknown4 = BigEndianBitConverter.ToUInt16(sector, 0x80);
mddf.unknown5 = BigEndianBitConverter.ToUInt32(sector, 0x82); mddf.unknown5 = BigEndianBitConverter.ToUInt32(sector, 0x82);
mddf.unknown6 = BigEndianBitConverter.ToUInt32(sector, 0x86); mddf.unknown6 = BigEndianBitConverter.ToUInt32(sector, 0x86);
mddf.clustersize = BigEndianBitConverter.ToUInt16(sector, 0x8A); mddf.clustersize = BigEndianBitConverter.ToUInt16(sector, 0x8A);
mddf.fs_size = BigEndianBitConverter.ToUInt32(sector, 0x8C); mddf.fs_size = BigEndianBitConverter.ToUInt32(sector, 0x8C);
mddf.unknown7 = BigEndianBitConverter.ToUInt32(sector, 0x90); mddf.unknown7 = BigEndianBitConverter.ToUInt32(sector, 0x90);
mddf.srec_ptr = BigEndianBitConverter.ToUInt32(sector, 0x94); mddf.srec_ptr = BigEndianBitConverter.ToUInt32(sector, 0x94);
mddf.unknown9 = BigEndianBitConverter.ToUInt16(sector, 0x98); mddf.unknown9 = BigEndianBitConverter.ToUInt16(sector, 0x98);
mddf.srec_len = BigEndianBitConverter.ToUInt16(sector, 0x9A); mddf.srec_len = BigEndianBitConverter.ToUInt16(sector, 0x9A);
mddf.unknown10 = BigEndianBitConverter.ToUInt32(sector, 0x9C); mddf.unknown10 = BigEndianBitConverter.ToUInt32(sector, 0x9C);
mddf.unknown11 = BigEndianBitConverter.ToUInt32(sector, 0xA0); mddf.unknown11 = BigEndianBitConverter.ToUInt32(sector, 0xA0);
mddf.unknown12 = BigEndianBitConverter.ToUInt32(sector, 0xA4); mddf.unknown12 = BigEndianBitConverter.ToUInt32(sector, 0xA4);
mddf.unknown13 = BigEndianBitConverter.ToUInt32(sector, 0xA8); mddf.unknown13 = BigEndianBitConverter.ToUInt32(sector, 0xA8);
mddf.unknown14 = BigEndianBitConverter.ToUInt32(sector, 0xAC); mddf.unknown14 = BigEndianBitConverter.ToUInt32(sector, 0xAC);
mddf.filecount = BigEndianBitConverter.ToUInt16(sector, 0xB0); mddf.filecount = BigEndianBitConverter.ToUInt16(sector, 0xB0);
mddf.unknown15 = BigEndianBitConverter.ToUInt32(sector, 0xB2); mddf.unknown15 = BigEndianBitConverter.ToUInt32(sector, 0xB2);
mddf.unknown16 = BigEndianBitConverter.ToUInt32(sector, 0xB6); mddf.unknown16 = BigEndianBitConverter.ToUInt32(sector, 0xB6);
mddf.freecount = BigEndianBitConverter.ToUInt32(sector, 0xBA); mddf.freecount = BigEndianBitConverter.ToUInt32(sector, 0xBA);
mddf.unknown17 = BigEndianBitConverter.ToUInt16(sector, 0xBE); mddf.unknown17 = BigEndianBitConverter.ToUInt16(sector, 0xBE);
mddf.unknown18 = BigEndianBitConverter.ToUInt32(sector, 0xC0); mddf.unknown18 = BigEndianBitConverter.ToUInt32(sector, 0xC0);
mddf.overmount_stamp = BigEndianBitConverter.ToUInt64(sector, 0xC4); mddf.overmount_stamp = BigEndianBitConverter.ToUInt64(sector, 0xC4);
mddf.serialization = BigEndianBitConverter.ToUInt32(sector, 0xCC); mddf.serialization = BigEndianBitConverter.ToUInt32(sector, 0xCC);
mddf.unknown19 = BigEndianBitConverter.ToUInt32(sector, 0xD0); mddf.unknown19 = BigEndianBitConverter.ToUInt32(sector, 0xD0);
mddf.unknown_timestamp = BigEndianBitConverter.ToUInt32(sector, 0xD4); mddf.unknown_timestamp = BigEndianBitConverter.ToUInt32(sector, 0xD4);
mddf.unknown20 = BigEndianBitConverter.ToUInt32(sector, 0xD8); mddf.unknown20 = BigEndianBitConverter.ToUInt32(sector, 0xD8);
mddf.unknown21 = BigEndianBitConverter.ToUInt32(sector, 0xDC); mddf.unknown21 = BigEndianBitConverter.ToUInt32(sector, 0xDC);
mddf.unknown22 = BigEndianBitConverter.ToUInt32(sector, 0xE0); mddf.unknown22 = BigEndianBitConverter.ToUInt32(sector, 0xE0);
mddf.unknown23 = BigEndianBitConverter.ToUInt32(sector, 0xE4); mddf.unknown23 = BigEndianBitConverter.ToUInt32(sector, 0xE4);
mddf.unknown24 = BigEndianBitConverter.ToUInt32(sector, 0xE8); mddf.unknown24 = BigEndianBitConverter.ToUInt32(sector, 0xE8);
mddf.unknown25 = BigEndianBitConverter.ToUInt32(sector, 0xEC); mddf.unknown25 = BigEndianBitConverter.ToUInt32(sector, 0xEC);
mddf.unknown26 = BigEndianBitConverter.ToUInt32(sector, 0xF0); mddf.unknown26 = BigEndianBitConverter.ToUInt32(sector, 0xF0);
mddf.unknown27 = BigEndianBitConverter.ToUInt32(sector, 0xF4); mddf.unknown27 = BigEndianBitConverter.ToUInt32(sector, 0xF4);
mddf.unknown28 = BigEndianBitConverter.ToUInt32(sector, 0xF8); mddf.unknown28 = BigEndianBitConverter.ToUInt32(sector, 0xF8);
mddf.unknown29 = BigEndianBitConverter.ToUInt32(sector, 0xFC); mddf.unknown29 = BigEndianBitConverter.ToUInt32(sector, 0xFC);
mddf.unknown30 = BigEndianBitConverter.ToUInt32(sector, 0x100); mddf.unknown30 = BigEndianBitConverter.ToUInt32(sector, 0x100);
mddf.unknown31 = BigEndianBitConverter.ToUInt32(sector, 0x104); mddf.unknown31 = BigEndianBitConverter.ToUInt32(sector, 0x104);
mddf.unknown32 = BigEndianBitConverter.ToUInt32(sector, 0x108); mddf.unknown32 = BigEndianBitConverter.ToUInt32(sector, 0x108);
mddf.unknown33 = BigEndianBitConverter.ToUInt32(sector, 0x10C); mddf.unknown33 = BigEndianBitConverter.ToUInt32(sector, 0x10C);
mddf.unknown34 = BigEndianBitConverter.ToUInt32(sector, 0x110); mddf.unknown34 = BigEndianBitConverter.ToUInt32(sector, 0x110);
mddf.unknown35 = BigEndianBitConverter.ToUInt32(sector, 0x114); mddf.unknown35 = BigEndianBitConverter.ToUInt32(sector, 0x114);
mddf.backup_volid = BigEndianBitConverter.ToUInt64(sector, 0x118); mddf.backup_volid = BigEndianBitConverter.ToUInt64(sector, 0x118);
mddf.label_size = BigEndianBitConverter.ToUInt16(sector, 0x120); mddf.label_size = BigEndianBitConverter.ToUInt16(sector, 0x120);
mddf.fs_overhead = BigEndianBitConverter.ToUInt16(sector, 0x122); mddf.fs_overhead = BigEndianBitConverter.ToUInt16(sector, 0x122);
mddf.result_scavenge = BigEndianBitConverter.ToUInt16(sector, 0x124); mddf.result_scavenge = BigEndianBitConverter.ToUInt16(sector, 0x124);
mddf.boot_code = BigEndianBitConverter.ToUInt16(sector, 0x126); mddf.boot_code = BigEndianBitConverter.ToUInt16(sector, 0x126);
mddf.boot_environ = BigEndianBitConverter.ToUInt16(sector, 0x6C); mddf.boot_environ = BigEndianBitConverter.ToUInt16(sector, 0x6C);
mddf.unknown36 = BigEndianBitConverter.ToUInt32(sector, 0x12A); mddf.unknown36 = BigEndianBitConverter.ToUInt32(sector, 0x12A);
mddf.unknown37 = BigEndianBitConverter.ToUInt32(sector, 0x12E); mddf.unknown37 = BigEndianBitConverter.ToUInt32(sector, 0x12E);
mddf.unknown38 = BigEndianBitConverter.ToUInt32(sector, 0x132); mddf.unknown38 = BigEndianBitConverter.ToUInt32(sector, 0x132);
mddf.vol_sequence = BigEndianBitConverter.ToUInt16(sector, 0x136); mddf.vol_sequence = BigEndianBitConverter.ToUInt16(sector, 0x136);
mddf.vol_left_mounted = sector[0x138]; mddf.vol_left_mounted = sector[0x138];
// Check that the MDDF is correct // Check that the MDDF is correct
if(mddf.mddf_block != i - volumePrefix || mddf.vol_size > device.Info.Sectors || if(mddf.mddf_block != i - volumePrefix ||
mddf.vol_size - 1 != mddf.volsize_minus_one || mddf.vol_size > device.Info.Sectors ||
mddf.vol_size - i - 1 != mddf.volsize_minus_mddf_minus_one - volumePrefix || mddf.vol_size - 1 !=
mddf.datasize > mddf.blocksize || mddf.blocksize < device.Info.SectorSize || mddf.volsize_minus_one ||
mddf.datasize != device.Info.SectorSize) mddf.vol_size - i - 1 !=
mddf.volsize_minus_mddf_minus_one - volumePrefix ||
mddf.datasize >
mddf.blocksize || mddf.blocksize < device.Info.SectorSize ||
mddf.datasize != device.Info.SectorSize)
{ {
DicConsole.DebugWriteLine("LisaFS plugin", "Incorrect MDDF found"); DicConsole.DebugWriteLine("LisaFS plugin", "Incorrect MDDF found");
return Errno.InvalidArgument; return Errno.InvalidArgument;
@@ -204,14 +210,15 @@ namespace DiscImageChef.Filesystems.LisaFS
} }
// Initialize caches // Initialize caches
extentCache = new Dictionary<short, ExtentFile>(); extentCache = new Dictionary<short, ExtentFile>();
systemFileCache = new Dictionary<short, byte[]>(); systemFileCache = new Dictionary<short, byte[]>();
fileCache = new Dictionary<short, byte[]>(); fileCache = new Dictionary<short, byte[]>();
//catalogCache = new Dictionary<short, List<CatalogEntry>>(); //catalogCache = new Dictionary<short, List<CatalogEntry>>();
fileSizeCache = new Dictionary<short, int>(); fileSizeCache = new Dictionary<short, int>();
mounted = true; mounted = true;
this.debug = debug; if(options == null) options = GetDefaultOptions();
if(options.TryGetValue("debug", out string debugString)) bool.TryParse(debugString, out debug);
if(debug) printedExtents = new List<short>(); if(debug) printedExtents = new List<short>();
@@ -284,24 +291,26 @@ namespace DiscImageChef.Filesystems.LisaFS
XmlFsType = new FileSystemType(); XmlFsType = new FileSystemType();
if(DateTime.Compare(mddf.dtvb, DateHandlers.LisaToDateTime(0)) > 0) if(DateTime.Compare(mddf.dtvb, DateHandlers.LisaToDateTime(0)) > 0)
{ {
XmlFsType.BackupDate = mddf.dtvb; XmlFsType.BackupDate = mddf.dtvb;
XmlFsType.BackupDateSpecified = true; XmlFsType.BackupDateSpecified = true;
} }
XmlFsType.Clusters = mddf.vol_size;
XmlFsType.Clusters = mddf.vol_size;
XmlFsType.ClusterSize = mddf.clustersize * mddf.datasize; XmlFsType.ClusterSize = mddf.clustersize * mddf.datasize;
if(DateTime.Compare(mddf.dtvc, DateHandlers.LisaToDateTime(0)) > 0) if(DateTime.Compare(mddf.dtvc, DateHandlers.LisaToDateTime(0)) > 0)
{ {
XmlFsType.CreationDate = mddf.dtvc; XmlFsType.CreationDate = mddf.dtvc;
XmlFsType.CreationDateSpecified = true; XmlFsType.CreationDateSpecified = true;
} }
XmlFsType.Dirty = mddf.vol_left_mounted != 0;
XmlFsType.Files = mddf.filecount; XmlFsType.Dirty = mddf.vol_left_mounted != 0;
XmlFsType.FilesSpecified = true; XmlFsType.Files = mddf.filecount;
XmlFsType.FreeClusters = mddf.freecount; XmlFsType.FilesSpecified = true;
XmlFsType.FreeClusters = mddf.freecount;
XmlFsType.FreeClustersSpecified = true; XmlFsType.FreeClustersSpecified = true;
XmlFsType.Type = "LisaFS"; XmlFsType.Type = "LisaFS";
XmlFsType.VolumeName = mddf.volname; XmlFsType.VolumeName = mddf.volname;
XmlFsType.VolumeSerial = $"{mddf.volid:X16}"; XmlFsType.VolumeSerial = $"{mddf.volid:X16}";
return Errno.NoError; return Errno.NoError;
} }
@@ -321,17 +330,17 @@ namespace DiscImageChef.Filesystems.LisaFS
/// </summary> /// </summary>
public Errno Unmount() public Errno Unmount()
{ {
mounted = false; mounted = false;
extentCache = null; extentCache = null;
systemFileCache = null; systemFileCache = null;
fileCache = null; fileCache = null;
catalogCache = null; catalogCache = null;
fileSizeCache = null; fileSizeCache = null;
printedExtents = null; printedExtents = null;
mddf = new MDDF(); mddf = new MDDF();
volumePrefix = 0; volumePrefix = 0;
devTagSize = 0; devTagSize = 0;
srecords = null; srecords = null;
return Errno.NoError; return Errno.NoError;
} }
@@ -347,12 +356,12 @@ namespace DiscImageChef.Filesystems.LisaFS
stat = new FileSystemInfo stat = new FileSystemInfo
{ {
Blocks = mddf.vol_size, Blocks = mddf.vol_size,
FilenameLength = (ushort)E_NAME, FilenameLength = (ushort)E_NAME,
Files = mddf.filecount, Files = mddf.filecount,
FreeBlocks = mddf.freecount, FreeBlocks = mddf.freecount,
Id = {Serial64 = mddf.volid, IsLong = true}, Id = {Serial64 = mddf.volid, IsLong = true},
PluginId = Id PluginId = Id
}; };
stat.FreeFiles = FILEID_MAX - stat.Files; stat.FreeFiles = FILEID_MAX - stat.Files;
switch(mddf.fsversion) switch(mddf.fsversion)

View File

@@ -43,11 +43,13 @@ namespace DiscImageChef.Filesystems.UCSDPascal
// Information from Call-A.P.P.L.E. Pascal Disk Directory Structure // Information from Call-A.P.P.L.E. Pascal Disk Directory Structure
public partial class PascalPlugin public partial class PascalPlugin
{ {
public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, bool debug) public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding,
Dictionary<string, string> options)
{ {
device = imagePlugin; device = imagePlugin;
Encoding = encoding ?? new Apple2(); Encoding = encoding ?? new Apple2();
this.debug = debug; if(options == null) options = GetDefaultOptions();
if(options.TryGetValue("debug", out string debugString)) bool.TryParse(debugString, out debug);
if(device.Info.Sectors < 3) return Errno.InvalidArgument; if(device.Info.Sectors < 3) return Errno.InvalidArgument;
// Blocks 0 and 1 are boot code // Blocks 0 and 1 are boot code
@@ -55,38 +57,41 @@ namespace DiscImageChef.Filesystems.UCSDPascal
BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;
mountedVolEntry.firstBlock = BigEndianBitConverter.ToInt16(catalogBlocks, 0x00); mountedVolEntry.firstBlock = BigEndianBitConverter.ToInt16(catalogBlocks, 0x00);
mountedVolEntry.lastBlock = BigEndianBitConverter.ToInt16(catalogBlocks, 0x02); mountedVolEntry.lastBlock = BigEndianBitConverter.ToInt16(catalogBlocks, 0x02);
mountedVolEntry.entryType = (PascalFileKind)BigEndianBitConverter.ToInt16(catalogBlocks, 0x04); mountedVolEntry.entryType = (PascalFileKind)BigEndianBitConverter.ToInt16(catalogBlocks, 0x04);
mountedVolEntry.volumeName = new byte[8]; mountedVolEntry.volumeName = new byte[8];
Array.Copy(catalogBlocks, 0x06, mountedVolEntry.volumeName, 0, 8); Array.Copy(catalogBlocks, 0x06, mountedVolEntry.volumeName, 0, 8);
mountedVolEntry.blocks = BigEndianBitConverter.ToInt16(catalogBlocks, 0x0E); mountedVolEntry.blocks = BigEndianBitConverter.ToInt16(catalogBlocks, 0x0E);
mountedVolEntry.files = BigEndianBitConverter.ToInt16(catalogBlocks, 0x10); mountedVolEntry.files = BigEndianBitConverter.ToInt16(catalogBlocks, 0x10);
mountedVolEntry.dummy = BigEndianBitConverter.ToInt16(catalogBlocks, 0x12); mountedVolEntry.dummy = BigEndianBitConverter.ToInt16(catalogBlocks, 0x12);
mountedVolEntry.lastBoot = BigEndianBitConverter.ToInt16(catalogBlocks, 0x14); mountedVolEntry.lastBoot = BigEndianBitConverter.ToInt16(catalogBlocks, 0x14);
mountedVolEntry.tail = BigEndianBitConverter.ToInt32(catalogBlocks, 0x16); mountedVolEntry.tail = BigEndianBitConverter.ToInt32(catalogBlocks, 0x16);
if(mountedVolEntry.firstBlock != 0 || mountedVolEntry.lastBlock <= mountedVolEntry.firstBlock || if(mountedVolEntry.firstBlock != 0 ||
(ulong)mountedVolEntry.lastBlock > device.Info.Sectors - 2 || mountedVolEntry.lastBlock <= mountedVolEntry.firstBlock ||
mountedVolEntry.entryType != PascalFileKind.Volume && (ulong)mountedVolEntry.lastBlock > device.Info.Sectors - 2 ||
mountedVolEntry.entryType != PascalFileKind.Secure || mountedVolEntry.volumeName[0] > 7 || mountedVolEntry.entryType != PascalFileKind.Volume &&
mountedVolEntry.blocks < 0 || (ulong)mountedVolEntry.blocks != device.Info.Sectors || mountedVolEntry.entryType != PascalFileKind.Secure ||
mountedVolEntry.files < 0) return Errno.InvalidArgument; mountedVolEntry.volumeName[0] > 7 ||
mountedVolEntry.blocks < 0 ||
(ulong)mountedVolEntry.blocks != device.Info.Sectors ||
mountedVolEntry.files < 0) return Errno.InvalidArgument;
catalogBlocks = device.ReadSectors(2, (uint)(mountedVolEntry.lastBlock - mountedVolEntry.firstBlock - 2)); catalogBlocks = device.ReadSectors(2, (uint)(mountedVolEntry.lastBlock - mountedVolEntry.firstBlock - 2));
int offset = 26; int offset = 26;
fileEntries = new List<PascalFileEntry>(); fileEntries = new List<PascalFileEntry>();
while(offset + 26 < catalogBlocks.Length) while(offset + 26 < catalogBlocks.Length)
{ {
PascalFileEntry entry = new PascalFileEntry PascalFileEntry entry = new PascalFileEntry
{ {
filename = new byte[16], filename = new byte[16],
firstBlock = BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x00), firstBlock = BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x00),
lastBlock = BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x02), lastBlock = BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x02),
entryType = (PascalFileKind)BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x04), entryType = (PascalFileKind)BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x04),
lastBytes = BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x16), lastBytes = BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x16),
mtime = BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x18) mtime = BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x18)
}; };
Array.Copy(catalogBlocks, offset + 0x06, entry.filename, 0, 16); Array.Copy(catalogBlocks, offset + 0x06, entry.filename, 0, 16);
@@ -99,13 +104,13 @@ namespace DiscImageChef.Filesystems.UCSDPascal
XmlFsType = new FileSystemType XmlFsType = new FileSystemType
{ {
Bootable = !ArrayHelpers.ArrayIsNullOrEmpty(bootBlocks), Bootable = !ArrayHelpers.ArrayIsNullOrEmpty(bootBlocks),
Clusters = mountedVolEntry.blocks, Clusters = mountedVolEntry.blocks,
ClusterSize = (int)device.Info.SectorSize, ClusterSize = (int)device.Info.SectorSize,
Files = mountedVolEntry.files, Files = mountedVolEntry.files,
FilesSpecified = true, FilesSpecified = true,
Type = "UCSD Pascal", Type = "UCSD Pascal",
VolumeName = StringHandlers.PascalToString(mountedVolEntry.volumeName, Encoding) VolumeName = StringHandlers.PascalToString(mountedVolEntry.volumeName, Encoding)
}; };
mounted = true; mounted = true;
@@ -115,7 +120,7 @@ namespace DiscImageChef.Filesystems.UCSDPascal
public Errno Unmount() public Errno Unmount()
{ {
mounted = false; mounted = false;
fileEntries = null; fileEntries = null;
return Errno.NoError; return Errno.NoError;
} }
@@ -124,15 +129,16 @@ namespace DiscImageChef.Filesystems.UCSDPascal
{ {
stat = new FileSystemInfo stat = new FileSystemInfo
{ {
Blocks = mountedVolEntry.blocks, Blocks = mountedVolEntry.blocks,
FilenameLength = 16, FilenameLength = 16,
Files = (ulong)mountedVolEntry.files, Files = (ulong)mountedVolEntry.files,
FreeBlocks = 0, FreeBlocks = 0,
PluginId = Id, PluginId = Id,
Type = "UCSD Pascal" Type = "UCSD Pascal"
}; };
stat.FreeBlocks = mountedVolEntry.blocks - (mountedVolEntry.lastBlock - mountedVolEntry.firstBlock); stat.FreeBlocks = mountedVolEntry.blocks - (mountedVolEntry.lastBlock - mountedVolEntry.firstBlock);
foreach(PascalFileEntry entry in fileEntries) stat.FreeBlocks -= entry.lastBlock - entry.firstBlock; foreach(PascalFileEntry entry in fileEntries) stat.FreeBlocks -= entry.lastBlock - entry.firstBlock;
return Errno.NotImplemented; return Errno.NotImplemented;

View File

@@ -44,17 +44,17 @@ namespace DiscImageChef.Filesystems.UCSDPascal
byte[] bootBlocks; byte[] bootBlocks;
byte[] catalogBlocks; byte[] catalogBlocks;
bool debug; bool debug;
IMediaImage device; IMediaImage device;
List<PascalFileEntry> fileEntries; List<PascalFileEntry> fileEntries;
bool mounted; bool mounted;
PascalVolumeEntry mountedVolEntry; PascalVolumeEntry mountedVolEntry;
public FileSystemType XmlFsType { get; private set; } public FileSystemType XmlFsType { get; private set; }
public string Name => "U.C.S.D. Pascal filesystem"; public string Name => "U.C.S.D. Pascal filesystem";
public Guid Id => new Guid("B0AC2CB5-72AA-473A-9200-270B5A2C2D53"); public Guid Id => new Guid("B0AC2CB5-72AA-473A-9200-270B5A2C2D53");
public Encoding Encoding { get; private set; } public Encoding Encoding { get; private set; }
public Errno ListXAttr(string path, out List<string> xattrs) public Errno ListXAttr(string path, out List<string> xattrs)
{ {
@@ -72,5 +72,10 @@ namespace DiscImageChef.Filesystems.UCSDPascal
dest = null; dest = null;
return Errno.NotSupported; return Errno.NotSupported;
} }
static Dictionary<string, string> GetDefaultOptions()
{
return new Dictionary<string, string> {{"debug", false.ToString()}};
}
} }
} }