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

View File

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

View File

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

View File

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

View File

@@ -60,7 +60,7 @@ namespace DiscImageChef.Filesystems.CPM
/// Stores all known CP/M disk definitions
/// </summary>
CpmDefinitions definitions;
IMediaImage device;
IMediaImage device;
/// <summary>
/// Cached directory listing
/// </summary>
@@ -84,8 +84,8 @@ namespace DiscImageChef.Filesystems.CPM
/// <summary>
/// Timestamp in volume label for update
/// </summary>
byte[] labelUpdateDate;
bool mounted;
byte[] labelUpdateDate;
bool mounted;
Partition partition;
/// <summary>
/// Cached file passwords
@@ -113,8 +113,13 @@ namespace DiscImageChef.Filesystems.CPM
CpmDefinition workingDefinition;
public FileSystemType XmlFsType { get; private set; }
public Encoding Encoding { get; private set; }
public string Name => "CP/M File System";
public Guid Id => new Guid("AA2B8585-41DF-4E3B-8A35-D1A935E2F8A1");
public Encoding Encoding { get; private set; }
public string Name => "CP/M File System";
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
{
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;
partition = partition;
Encoding = encoding ?? Encoding.GetEncoding("IBM437");
device = imagePlugin;
this.partition = partition;
Encoding = encoding ?? Encoding.GetEncoding("IBM437");
// As the identification is so complex, just call Identify() and relay on its findings
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];
// Skip first track (first side)
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.side1.sectorIds.Length;
}
@@ -87,8 +88,8 @@ namespace DiscImageChef.Filesystems.CPM
sectorMask[m] = workingDefinition.side1.sectorIds[m] - workingDefinition.side1.sectorIds[0];
// Skip first track (first side) and first track (second side)
for(int m = 0; m < workingDefinition.side1.sectorIds.Length; m++)
sectorMask[m + workingDefinition.side1.sectorIds.Length] =
workingDefinition.side1.sectorIds[m] - workingDefinition.side1.sectorIds[0] +
sectorMask[m + workingDefinition.side1.sectorIds.Length] =
workingDefinition.side1.sectorIds[m] - workingDefinition.side1.sectorIds[0] +
workingDefinition.side1.sectorIds.Length + workingDefinition.side2.sectorIds.Length;
// TODO: Implement CYLINDERS ordering
@@ -96,9 +97,8 @@ namespace DiscImageChef.Filesystems.CPM
return Errno.NotImplemented;
}
// TODO: Implement COLUMBIA ordering
else if(
string.Compare(workingDefinition.order, "COLUMBIA", StringComparison.InvariantCultureIgnoreCase) ==
0)
else if(string.Compare(workingDefinition.order, "COLUMBIA",
StringComparison.InvariantCultureIgnoreCase) == 0)
{
DicConsole.DebugWriteLine("CP/M Plugin",
"Don't know how to handle COLUMBIA ordering, not proceeding with this definition.");
@@ -123,7 +123,7 @@ namespace DiscImageChef.Filesystems.CPM
// Deinterleave whole volume
Dictionary<ulong, byte[]> deinterleavedSectors = new Dictionary<ulong, byte[]>();
if(workingDefinition.sides == 1 ||
if(workingDefinition.sides == 1 ||
string.Compare(workingDefinition.order, "SIDES", StringComparison.InvariantCultureIgnoreCase) == 0)
{
DicConsole.DebugWriteLine("CP/M Plugin", "Deinterleaving whole volume.");
@@ -132,18 +132,19 @@ namespace DiscImageChef.Filesystems.CPM
{
byte[] readSector =
device.ReadSector((ulong)((int)partition.Start + p / sectorMask.Length * sectorMask.Length +
sectorMask[p % sectorMask.Length]));
sectorMask[p % sectorMask.Length]));
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);
}
}
int blockSize = 128 << dpb.bsh;
MemoryStream blockMs = new MemoryStream();
ulong blockNo = 0;
int sectorsPerBlock = 0;
int blockSize = 128 << dpb.bsh;
MemoryStream blockMs = new MemoryStream();
ulong blockNo = 0;
int sectorsPerBlock = 0;
Dictionary<ulong, byte[]> allocationBlocks = new Dictionary<ulong, byte[]>();
DicConsole.DebugWriteLine("CP/M Plugin", "Creating allocation blocks.");
@@ -154,7 +155,7 @@ namespace DiscImageChef.Filesystems.CPM
deinterleavedSectors.TryGetValue(a, out byte[] sector);
// 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++)
{
byte[] tmp = new byte[blockSize];
@@ -171,18 +172,19 @@ namespace DiscImageChef.Filesystems.CPM
allocationBlocks.Add(blockNo++, blockMs.ToArray());
sectorsPerBlock = 0;
blockMs = new MemoryStream();
blockMs = new MemoryStream();
}
// 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.");
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;
else dirOff = workingDefinition.ofs * workingDefinition.sectorsPerTrack;
else dirOff = workingDefinition.ofs * workingDefinition.sectorsPerTrack;
// Read the whole directory blocks
MemoryStream dirMs = new MemoryStream();
@@ -196,19 +198,19 @@ namespace DiscImageChef.Filesystems.CPM
if(directory == null) return Errno.InvalidArgument;
int dirCnt = 0;
string file1 = null;
string file2 = null;
string file3 = null;
int dirCnt = 0;
string file1 = null;
string file2 = null;
string file3 = null;
Dictionary<string, Dictionary<int, List<ushort>>> fileExtents =
new Dictionary<string, Dictionary<int, List<ushort>>>();
statCache = new Dictionary<string, FileEntryInfo>();
cpmStat = new FileSystemInfo();
bool atime = false;
dirList = new List<string>();
statCache = new Dictionary<string, FileEntryInfo>();
cpmStat = new FileSystemInfo();
bool atime = false;
dirList = new List<string>();
labelCreationDate = null;
labelUpdateDate = null;
passwordCache = new Dictionary<string, byte[]>();
labelUpdateDate = null;
passwordCache = new Dictionary<string, byte[]>();
DicConsole.DebugWriteLine("CP/M Plugin", "Traversing directory.");
IntPtr dirPtr;
@@ -216,7 +218,7 @@ namespace DiscImageChef.Filesystems.CPM
// For each directory entry
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
if((directory[dOff] & 0x7F) < 0x10)
if((directory[dOff] & 0x7F) < 0x10)
if(allocationBlocks.Count > 256)
{
dirPtr = Marshal.AllocHGlobal(32);
@@ -225,7 +227,7 @@ namespace DiscImageChef.Filesystems.CPM
(DirectoryEntry16)Marshal.PtrToStructure(dirPtr, typeof(DirectoryEntry16));
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 system = (entry.filename[1] & 0x80) == 0x80 || (entry.extension[2] & 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++)
{
entry.filename[i] &= 0x7F;
validEntry &= entry.filename[i] >= 0x20;
validEntry &= entry.filename[i] >= 0x20;
}
for(int i = 0; i < 3; i++)
{
entry.extension[i] &= 0x7F;
validEntry &= entry.extension[i] >= 0x20;
validEntry &= entry.extension[i] >= 0x20;
}
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();
// 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;
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(system) fInfo.Attributes |= FileAttributes.System;
// Supposedly there is a value in the directory entry telling how many blocks are designated in this entry
// However some implementations tend to do whatever they wish, but none will ever 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
// we must ignore it.
// Supposedly there is a value in the directory entry telling how many blocks are designated in
// this entry. However some implementations tend to do whatever they wish, but none will ever
// 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 we must ignore it.
foreach(ushort blk in entry.allocations.Where(blk => !blocks.Contains(blk) && blk != 0))
blocks.Add(blk);
@@ -313,7 +316,7 @@ namespace DiscImageChef.Filesystems.CPM
DirectoryEntry entry = (DirectoryEntry)Marshal.PtrToStructure(dirPtr, typeof(DirectoryEntry));
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 system = (entry.filename[1] & 0x80) == 0x80 || (entry.extension[2] & 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++)
{
entry.filename[i] &= 0x7F;
validEntry &= entry.filename[i] >= 0x20;
validEntry &= entry.filename[i] >= 0x20;
}
for(int i = 0; i < 3; i++)
{
entry.extension[i] &= 0x7F;
validEntry &= entry.extension[i] >= 0x20;
validEntry &= entry.extension[i] >= 0x20;
}
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();
// 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;
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(system) fInfo.Attributes |= FileAttributes.System;
// Supposedly there is a value in the directory entry telling how many blocks are designated in this entry
// However some implementations tend to do whatever they wish, but none will ever 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
// we must ignore it.
// Supposedly there is a value in the directory entry telling how many blocks are designated in
// this entry. However some implementations tend to do whatever they wish, but none will ever
// 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 we must ignore it.
foreach(ushort blk in entry.allocations.Cast<ushort>()
.Where(blk => !blocks.Contains(blk) && blk != 0)) blocks.Add(blk);
@@ -404,14 +408,14 @@ namespace DiscImageChef.Filesystems.CPM
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;
string filename = Encoding.ASCII.GetString(entry.filename).Trim();
string filename = Encoding.ASCII.GetString(entry.filename).Trim();
string extension = Encoding.ASCII.GetString(entry.extension).Trim();
// 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;
// Do not repeat passwords
@@ -449,14 +453,15 @@ namespace DiscImageChef.Filesystems.CPM
labelEntry = (LabelEntry)Marshal.PtrToStructure(dirPtr, typeof(LabelEntry));
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;
label = Encoding.ASCII.GetString(directory, dOff + 1, 11).Trim();
label = Encoding.ASCII.GetString(directory, dOff + 1, 11).Trim();
labelCreationDate = new byte[4];
labelUpdateDate = new byte[4];
labelUpdateDate = new byte[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
switch(dirCnt % 3)
@@ -492,7 +497,7 @@ namespace DiscImageChef.Filesystems.CPM
else fInfo = new FileEntryInfo();
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);
@@ -505,7 +510,7 @@ namespace DiscImageChef.Filesystems.CPM
else fInfo = new FileEntryInfo();
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);
@@ -518,16 +523,16 @@ namespace DiscImageChef.Filesystems.CPM
else fInfo = new FileEntryInfo();
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);
statCache.Add(file3, fInfo);
}
file1 = null;
file2 = null;
file3 = null;
file1 = null;
file2 = null;
file3 = null;
dirCnt = 0;
}
// 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();
byte[] ctime = new byte[4];
ctime[0] = trdPartyDateEntry.create1[0];
ctime[1] = trdPartyDateEntry.create1[1];
ctime[0] = trdPartyDateEntry.create1[0];
ctime[1] = trdPartyDateEntry.create1[1];
fInfo.AccessTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.access1);
fInfo.CreationTime = DateHandlers.CpmToDateTime(ctime);
fInfo.AccessTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.access1);
fInfo.CreationTime = DateHandlers.CpmToDateTime(ctime);
fInfo.LastWriteTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.modify1);
statCache.Add(file1, fInfo);
@@ -564,11 +569,11 @@ namespace DiscImageChef.Filesystems.CPM
else fInfo = new FileEntryInfo();
byte[] ctime = new byte[4];
ctime[0] = trdPartyDateEntry.create2[0];
ctime[1] = trdPartyDateEntry.create2[1];
ctime[0] = trdPartyDateEntry.create2[0];
ctime[1] = trdPartyDateEntry.create2[1];
fInfo.AccessTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.access2);
fInfo.CreationTime = DateHandlers.CpmToDateTime(ctime);
fInfo.AccessTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.access2);
fInfo.CreationTime = DateHandlers.CpmToDateTime(ctime);
fInfo.LastWriteTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.modify2);
statCache.Add(file2, fInfo);
@@ -580,21 +585,22 @@ namespace DiscImageChef.Filesystems.CPM
else fInfo = new FileEntryInfo();
byte[] ctime = new byte[4];
ctime[0] = trdPartyDateEntry.create3[0];
ctime[1] = trdPartyDateEntry.create3[1];
ctime[0] = trdPartyDateEntry.create3[0];
ctime[1] = trdPartyDateEntry.create3[1];
fInfo.AccessTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.access3);
fInfo.CreationTime = DateHandlers.CpmToDateTime(ctime);
fInfo.AccessTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.access3);
fInfo.CreationTime = DateHandlers.CpmToDateTime(ctime);
fInfo.LastWriteTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.modify3);
statCache.Add(file3, fInfo);
}
file1 = null;
file2 = null;
file3 = null;
file1 = null;
file2 = null;
file3 = null;
dirCnt = 0;
}
break;
}
@@ -602,7 +608,7 @@ namespace DiscImageChef.Filesystems.CPM
// this should not be a problem
DicConsole.DebugWriteLine("CP/M Plugin", "Reading files.");
long usedBlocks = 0;
fileCache = new Dictionary<string, byte[]>();
fileCache = new Dictionary<string, byte[]>();
foreach(string filename in dirList)
{
MemoryStream fileMs = new MemoryStream();
@@ -626,8 +632,8 @@ namespace DiscImageChef.Filesystems.CPM
// If you insist to call CP/M "extent based"
fInfo.Attributes |= FileAttributes.Extents;
fInfo.BlockSize = blockSize;
fInfo.Length = fileMs.Length;
fInfo.BlockSize = blockSize;
fInfo.Length = fileMs.Length;
cpmStat.Files++;
usedBlocks += fInfo.Blocks;
@@ -648,34 +654,36 @@ namespace DiscImageChef.Filesystems.CPM
}
// Generate statfs.
cpmStat.Blocks = dpb.dsm + 1;
cpmStat.Blocks = dpb.dsm + 1;
cpmStat.FilenameLength = 11;
cpmStat.Files = (ulong)fileCache.Count;
cpmStat.FreeBlocks = cpmStat.Blocks - usedBlocks;
cpmStat.PluginId = Id;
cpmStat.Type = "CP/M filesystem";
cpmStat.Files = (ulong)fileCache.Count;
cpmStat.FreeBlocks = cpmStat.Blocks - usedBlocks;
cpmStat.PluginId = Id;
cpmStat.Type = "CP/M filesystem";
// Generate XML info
XmlFsType = new FileSystemType
{
Clusters = cpmStat.Blocks,
ClusterSize = blockSize,
Files = fileCache.Count,
FilesSpecified = true,
FreeClusters = cpmStat.FreeBlocks,
Clusters = cpmStat.Blocks,
ClusterSize = blockSize,
Files = fileCache.Count,
FilesSpecified = true,
FreeClusters = cpmStat.FreeBlocks,
FreeClustersSpecified = true,
Type = "CP/M filesystem"
Type = "CP/M filesystem"
};
if(labelCreationDate != null)
{
XmlFsType.CreationDate = DateHandlers.CpmToDateTime(labelCreationDate);
XmlFsType.CreationDate = DateHandlers.CpmToDateTime(labelCreationDate);
XmlFsType.CreationDateSpecified = true;
}
if(labelUpdateDate != null)
{
XmlFsType.ModificationDate = DateHandlers.CpmToDateTime(labelUpdateDate);
XmlFsType.ModificationDate = DateHandlers.CpmToDateTime(labelUpdateDate);
XmlFsType.ModificationDateSpecified = true;
}
if(!string.IsNullOrEmpty(label)) XmlFsType.VolumeName = label;
mounted = true;
@@ -698,17 +706,17 @@ namespace DiscImageChef.Filesystems.CPM
public Errno Unmount()
{
mounted = false;
definitions = null;
cpmFound = false;
workingDefinition = null;
dpb = null;
sectorMask = null;
label = null;
mounted = false;
definitions = null;
cpmFound = false;
workingDefinition = null;
dpb = null;
sectorMask = null;
label = null;
thirdPartyTimestamps = false;
standardTimestamps = false;
labelCreationDate = null;
labelUpdateDate = null;
standardTimestamps = false;
labelCreationDate = null;
labelUpdateDate = null;
return Errno.NoError;
}
}

View File

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

View File

@@ -42,35 +42,40 @@ namespace DiscImageChef.Filesystems.LisaFS
// Variable names from Lisa API
public partial class LisaFS : IReadOnlyFilesystem
{
bool debug;
bool debug;
IMediaImage device;
int devTagSize;
int devTagSize;
MDDF mddf;
bool mounted;
MDDF mddf;
bool mounted;
SRecord[] srecords;
ulong volumePrefix;
ulong volumePrefix;
public string Name => "Apple Lisa File System";
public Guid Id => new Guid("7E6034D1-D823-4248-A54D-239742B28391");
public Encoding Encoding { get; private set; }
public string Name => "Apple Lisa File System";
public Guid Id => new Guid("7E6034D1-D823-4248-A54D-239742B28391");
public Encoding Encoding { get; private set; }
public FileSystemType XmlFsType { get; private set; }
static Dictionary<string, string> GetDefaultOptions()
{
return new Dictionary<string, string> {{"debug", false.ToString()}};
}
#region Caches
/// <summary>Caches Extents Files</summary>
Dictionary<short, ExtentFile> extentCache;
/// <summary>Caches system files</summary>
Dictionary<short, byte[]> systemFileCache;
Dictionary<short, byte[]> systemFileCache;
/// <summary>Caches user files files</summary>
Dictionary<short, byte[]> fileCache;
Dictionary<short, byte[]> fileCache;
/// <summary>Caches catalogs</summary>
List<CatalogEntry> catalogCache;
List<CatalogEntry> catalogCache;
/// <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>
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>
Dictionary<short, DateTime> directoryDtcCache;
Dictionary<short, DateTime> directoryDtcCache;
#endregion Caches
}
}

View File

@@ -47,11 +47,12 @@ namespace DiscImageChef.Filesystems.LisaFS
/// <summary>
/// Mounts an Apple Lisa filesystem
/// </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
{
device = imagePlugin;
device = imagePlugin;
Encoding = new LisaRoman();
// 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;
byte[] sector = device.ReadSector(i);
mddf = new MDDF();
byte[] sector = device.ReadSector(i);
mddf = new MDDF();
byte[] pString = new byte[33];
uint lisaTime;
uint lisaTime;
mddf.fsversion = BigEndianBitConverter.ToUInt16(sector, 0x00);
mddf.volid = BigEndianBitConverter.ToUInt64(sector, 0x02);
mddf.volnum = BigEndianBitConverter.ToUInt16(sector, 0x0A);
mddf.volid = BigEndianBitConverter.ToUInt64(sector, 0x02);
mddf.volnum = BigEndianBitConverter.ToUInt16(sector, 0x0A);
Array.Copy(sector, 0x0C, pString, 0, 33);
mddf.volname = StringHandlers.PascalToString(pString, Encoding);
mddf.volname = StringHandlers.PascalToString(pString, Encoding);
mddf.unknown1 = sector[0x2D];
Array.Copy(sector, 0x2E, pString, 0, 33);
// Prevent garbage
mddf.password = pString[0] <= 32 ? StringHandlers.PascalToString(pString, Encoding) : "";
mddf.unknown2 = sector[0x4F];
mddf.machine_id = BigEndianBitConverter.ToUInt32(sector, 0x50);
mddf.master_copy_id = BigEndianBitConverter.ToUInt32(sector, 0x54);
lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x58);
mddf.dtvc = DateHandlers.LisaToDateTime(lisaTime);
lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x5C);
mddf.dtcc = DateHandlers.LisaToDateTime(lisaTime);
lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x60);
mddf.dtvb = DateHandlers.LisaToDateTime(lisaTime);
lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x64);
mddf.dtvs = DateHandlers.LisaToDateTime(lisaTime);
mddf.unknown3 = BigEndianBitConverter.ToUInt32(sector, 0x68);
mddf.mddf_block = BigEndianBitConverter.ToUInt32(sector, 0x6C);
mddf.volsize_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x70);
mddf.password =
pString[0] <= 32 ? StringHandlers.PascalToString(pString, Encoding) : "";
mddf.unknown2 = sector[0x4F];
mddf.machine_id = BigEndianBitConverter.ToUInt32(sector, 0x50);
mddf.master_copy_id = BigEndianBitConverter.ToUInt32(sector, 0x54);
lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x58);
mddf.dtvc = DateHandlers.LisaToDateTime(lisaTime);
lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x5C);
mddf.dtcc = DateHandlers.LisaToDateTime(lisaTime);
lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x60);
mddf.dtvb = DateHandlers.LisaToDateTime(lisaTime);
lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x64);
mddf.dtvs = DateHandlers.LisaToDateTime(lisaTime);
mddf.unknown3 = BigEndianBitConverter.ToUInt32(sector, 0x68);
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.vol_size = BigEndianBitConverter.ToUInt32(sector, 0x78);
mddf.blocksize = BigEndianBitConverter.ToUInt16(sector, 0x7C);
mddf.datasize = BigEndianBitConverter.ToUInt16(sector, 0x7E);
mddf.unknown4 = BigEndianBitConverter.ToUInt16(sector, 0x80);
mddf.unknown5 = BigEndianBitConverter.ToUInt32(sector, 0x82);
mddf.unknown6 = BigEndianBitConverter.ToUInt32(sector, 0x86);
mddf.clustersize = BigEndianBitConverter.ToUInt16(sector, 0x8A);
mddf.fs_size = BigEndianBitConverter.ToUInt32(sector, 0x8C);
mddf.unknown7 = BigEndianBitConverter.ToUInt32(sector, 0x90);
mddf.srec_ptr = BigEndianBitConverter.ToUInt32(sector, 0x94);
mddf.unknown9 = BigEndianBitConverter.ToUInt16(sector, 0x98);
mddf.srec_len = BigEndianBitConverter.ToUInt16(sector, 0x9A);
mddf.unknown10 = BigEndianBitConverter.ToUInt32(sector, 0x9C);
mddf.unknown11 = BigEndianBitConverter.ToUInt32(sector, 0xA0);
mddf.unknown12 = BigEndianBitConverter.ToUInt32(sector, 0xA4);
mddf.unknown13 = BigEndianBitConverter.ToUInt32(sector, 0xA8);
mddf.unknown14 = BigEndianBitConverter.ToUInt32(sector, 0xAC);
mddf.filecount = BigEndianBitConverter.ToUInt16(sector, 0xB0);
mddf.unknown15 = BigEndianBitConverter.ToUInt32(sector, 0xB2);
mddf.unknown16 = BigEndianBitConverter.ToUInt32(sector, 0xB6);
mddf.freecount = BigEndianBitConverter.ToUInt32(sector, 0xBA);
mddf.unknown17 = BigEndianBitConverter.ToUInt16(sector, 0xBE);
mddf.unknown18 = BigEndianBitConverter.ToUInt32(sector, 0xC0);
mddf.overmount_stamp = BigEndianBitConverter.ToUInt64(sector, 0xC4);
mddf.serialization = BigEndianBitConverter.ToUInt32(sector, 0xCC);
mddf.unknown19 = BigEndianBitConverter.ToUInt32(sector, 0xD0);
mddf.unknown_timestamp = BigEndianBitConverter.ToUInt32(sector, 0xD4);
mddf.unknown20 = BigEndianBitConverter.ToUInt32(sector, 0xD8);
mddf.unknown21 = BigEndianBitConverter.ToUInt32(sector, 0xDC);
mddf.unknown22 = BigEndianBitConverter.ToUInt32(sector, 0xE0);
mddf.unknown23 = BigEndianBitConverter.ToUInt32(sector, 0xE4);
mddf.unknown24 = BigEndianBitConverter.ToUInt32(sector, 0xE8);
mddf.unknown25 = BigEndianBitConverter.ToUInt32(sector, 0xEC);
mddf.unknown26 = BigEndianBitConverter.ToUInt32(sector, 0xF0);
mddf.unknown27 = BigEndianBitConverter.ToUInt32(sector, 0xF4);
mddf.unknown28 = BigEndianBitConverter.ToUInt32(sector, 0xF8);
mddf.unknown29 = BigEndianBitConverter.ToUInt32(sector, 0xFC);
mddf.unknown30 = BigEndianBitConverter.ToUInt32(sector, 0x100);
mddf.unknown31 = BigEndianBitConverter.ToUInt32(sector, 0x104);
mddf.unknown32 = BigEndianBitConverter.ToUInt32(sector, 0x108);
mddf.unknown33 = BigEndianBitConverter.ToUInt32(sector, 0x10C);
mddf.unknown34 = BigEndianBitConverter.ToUInt32(sector, 0x110);
mddf.unknown35 = BigEndianBitConverter.ToUInt32(sector, 0x114);
mddf.backup_volid = BigEndianBitConverter.ToUInt64(sector, 0x118);
mddf.label_size = BigEndianBitConverter.ToUInt16(sector, 0x120);
mddf.fs_overhead = BigEndianBitConverter.ToUInt16(sector, 0x122);
mddf.result_scavenge = BigEndianBitConverter.ToUInt16(sector, 0x124);
mddf.boot_code = BigEndianBitConverter.ToUInt16(sector, 0x126);
mddf.boot_environ = BigEndianBitConverter.ToUInt16(sector, 0x6C);
mddf.unknown36 = BigEndianBitConverter.ToUInt32(sector, 0x12A);
mddf.unknown37 = BigEndianBitConverter.ToUInt32(sector, 0x12E);
mddf.unknown38 = BigEndianBitConverter.ToUInt32(sector, 0x132);
mddf.vol_sequence = BigEndianBitConverter.ToUInt16(sector, 0x136);
mddf.vol_left_mounted = sector[0x138];
mddf.vol_size = BigEndianBitConverter.ToUInt32(sector, 0x78);
mddf.blocksize = BigEndianBitConverter.ToUInt16(sector, 0x7C);
mddf.datasize = BigEndianBitConverter.ToUInt16(sector, 0x7E);
mddf.unknown4 = BigEndianBitConverter.ToUInt16(sector, 0x80);
mddf.unknown5 = BigEndianBitConverter.ToUInt32(sector, 0x82);
mddf.unknown6 = BigEndianBitConverter.ToUInt32(sector, 0x86);
mddf.clustersize = BigEndianBitConverter.ToUInt16(sector, 0x8A);
mddf.fs_size = BigEndianBitConverter.ToUInt32(sector, 0x8C);
mddf.unknown7 = BigEndianBitConverter.ToUInt32(sector, 0x90);
mddf.srec_ptr = BigEndianBitConverter.ToUInt32(sector, 0x94);
mddf.unknown9 = BigEndianBitConverter.ToUInt16(sector, 0x98);
mddf.srec_len = BigEndianBitConverter.ToUInt16(sector, 0x9A);
mddf.unknown10 = BigEndianBitConverter.ToUInt32(sector, 0x9C);
mddf.unknown11 = BigEndianBitConverter.ToUInt32(sector, 0xA0);
mddf.unknown12 = BigEndianBitConverter.ToUInt32(sector, 0xA4);
mddf.unknown13 = BigEndianBitConverter.ToUInt32(sector, 0xA8);
mddf.unknown14 = BigEndianBitConverter.ToUInt32(sector, 0xAC);
mddf.filecount = BigEndianBitConverter.ToUInt16(sector, 0xB0);
mddf.unknown15 = BigEndianBitConverter.ToUInt32(sector, 0xB2);
mddf.unknown16 = BigEndianBitConverter.ToUInt32(sector, 0xB6);
mddf.freecount = BigEndianBitConverter.ToUInt32(sector, 0xBA);
mddf.unknown17 = BigEndianBitConverter.ToUInt16(sector, 0xBE);
mddf.unknown18 = BigEndianBitConverter.ToUInt32(sector, 0xC0);
mddf.overmount_stamp = BigEndianBitConverter.ToUInt64(sector, 0xC4);
mddf.serialization = BigEndianBitConverter.ToUInt32(sector, 0xCC);
mddf.unknown19 = BigEndianBitConverter.ToUInt32(sector, 0xD0);
mddf.unknown_timestamp = BigEndianBitConverter.ToUInt32(sector, 0xD4);
mddf.unknown20 = BigEndianBitConverter.ToUInt32(sector, 0xD8);
mddf.unknown21 = BigEndianBitConverter.ToUInt32(sector, 0xDC);
mddf.unknown22 = BigEndianBitConverter.ToUInt32(sector, 0xE0);
mddf.unknown23 = BigEndianBitConverter.ToUInt32(sector, 0xE4);
mddf.unknown24 = BigEndianBitConverter.ToUInt32(sector, 0xE8);
mddf.unknown25 = BigEndianBitConverter.ToUInt32(sector, 0xEC);
mddf.unknown26 = BigEndianBitConverter.ToUInt32(sector, 0xF0);
mddf.unknown27 = BigEndianBitConverter.ToUInt32(sector, 0xF4);
mddf.unknown28 = BigEndianBitConverter.ToUInt32(sector, 0xF8);
mddf.unknown29 = BigEndianBitConverter.ToUInt32(sector, 0xFC);
mddf.unknown30 = BigEndianBitConverter.ToUInt32(sector, 0x100);
mddf.unknown31 = BigEndianBitConverter.ToUInt32(sector, 0x104);
mddf.unknown32 = BigEndianBitConverter.ToUInt32(sector, 0x108);
mddf.unknown33 = BigEndianBitConverter.ToUInt32(sector, 0x10C);
mddf.unknown34 = BigEndianBitConverter.ToUInt32(sector, 0x110);
mddf.unknown35 = BigEndianBitConverter.ToUInt32(sector, 0x114);
mddf.backup_volid = BigEndianBitConverter.ToUInt64(sector, 0x118);
mddf.label_size = BigEndianBitConverter.ToUInt16(sector, 0x120);
mddf.fs_overhead = BigEndianBitConverter.ToUInt16(sector, 0x122);
mddf.result_scavenge = BigEndianBitConverter.ToUInt16(sector, 0x124);
mddf.boot_code = BigEndianBitConverter.ToUInt16(sector, 0x126);
mddf.boot_environ = BigEndianBitConverter.ToUInt16(sector, 0x6C);
mddf.unknown36 = BigEndianBitConverter.ToUInt32(sector, 0x12A);
mddf.unknown37 = BigEndianBitConverter.ToUInt32(sector, 0x12E);
mddf.unknown38 = BigEndianBitConverter.ToUInt32(sector, 0x132);
mddf.vol_sequence = BigEndianBitConverter.ToUInt16(sector, 0x136);
mddf.vol_left_mounted = sector[0x138];
// Check that the MDDF is correct
if(mddf.mddf_block != i - volumePrefix || mddf.vol_size > device.Info.Sectors ||
mddf.vol_size - 1 != mddf.volsize_minus_one ||
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)
if(mddf.mddf_block != i - volumePrefix ||
mddf.vol_size > device.Info.Sectors ||
mddf.vol_size - 1 !=
mddf.volsize_minus_one ||
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");
return Errno.InvalidArgument;
@@ -204,14 +210,15 @@ namespace DiscImageChef.Filesystems.LisaFS
}
// Initialize caches
extentCache = new Dictionary<short, ExtentFile>();
extentCache = new Dictionary<short, ExtentFile>();
systemFileCache = new Dictionary<short, byte[]>();
fileCache = new Dictionary<short, byte[]>();
fileCache = new Dictionary<short, byte[]>();
//catalogCache = new Dictionary<short, List<CatalogEntry>>();
fileSizeCache = new Dictionary<short, int>();
mounted = true;
this.debug = debug;
mounted = true;
if(options == null) options = GetDefaultOptions();
if(options.TryGetValue("debug", out string debugString)) bool.TryParse(debugString, out debug);
if(debug) printedExtents = new List<short>();
@@ -284,24 +291,26 @@ namespace DiscImageChef.Filesystems.LisaFS
XmlFsType = new FileSystemType();
if(DateTime.Compare(mddf.dtvb, DateHandlers.LisaToDateTime(0)) > 0)
{
XmlFsType.BackupDate = mddf.dtvb;
XmlFsType.BackupDate = mddf.dtvb;
XmlFsType.BackupDateSpecified = true;
}
XmlFsType.Clusters = mddf.vol_size;
XmlFsType.Clusters = mddf.vol_size;
XmlFsType.ClusterSize = mddf.clustersize * mddf.datasize;
if(DateTime.Compare(mddf.dtvc, DateHandlers.LisaToDateTime(0)) > 0)
{
XmlFsType.CreationDate = mddf.dtvc;
XmlFsType.CreationDate = mddf.dtvc;
XmlFsType.CreationDateSpecified = true;
}
XmlFsType.Dirty = mddf.vol_left_mounted != 0;
XmlFsType.Files = mddf.filecount;
XmlFsType.FilesSpecified = true;
XmlFsType.FreeClusters = mddf.freecount;
XmlFsType.Dirty = mddf.vol_left_mounted != 0;
XmlFsType.Files = mddf.filecount;
XmlFsType.FilesSpecified = true;
XmlFsType.FreeClusters = mddf.freecount;
XmlFsType.FreeClustersSpecified = true;
XmlFsType.Type = "LisaFS";
XmlFsType.VolumeName = mddf.volname;
XmlFsType.VolumeSerial = $"{mddf.volid:X16}";
XmlFsType.Type = "LisaFS";
XmlFsType.VolumeName = mddf.volname;
XmlFsType.VolumeSerial = $"{mddf.volid:X16}";
return Errno.NoError;
}
@@ -321,17 +330,17 @@ namespace DiscImageChef.Filesystems.LisaFS
/// </summary>
public Errno Unmount()
{
mounted = false;
extentCache = null;
mounted = false;
extentCache = null;
systemFileCache = null;
fileCache = null;
catalogCache = null;
fileSizeCache = null;
printedExtents = null;
mddf = new MDDF();
volumePrefix = 0;
devTagSize = 0;
srecords = null;
fileCache = null;
catalogCache = null;
fileSizeCache = null;
printedExtents = null;
mddf = new MDDF();
volumePrefix = 0;
devTagSize = 0;
srecords = null;
return Errno.NoError;
}
@@ -347,12 +356,12 @@ namespace DiscImageChef.Filesystems.LisaFS
stat = new FileSystemInfo
{
Blocks = mddf.vol_size,
Blocks = mddf.vol_size,
FilenameLength = (ushort)E_NAME,
Files = mddf.filecount,
FreeBlocks = mddf.freecount,
Id = {Serial64 = mddf.volid, IsLong = true},
PluginId = Id
Files = mddf.filecount,
FreeBlocks = mddf.freecount,
Id = {Serial64 = mddf.volid, IsLong = true},
PluginId = Id
};
stat.FreeFiles = FILEID_MAX - stat.Files;
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
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;
Encoding = encoding ?? new Apple2();
this.debug = debug;
device = imagePlugin;
Encoding = encoding ?? new Apple2();
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;
// Blocks 0 and 1 are boot code
@@ -55,38 +57,41 @@ namespace DiscImageChef.Filesystems.UCSDPascal
BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;
mountedVolEntry.firstBlock = BigEndianBitConverter.ToInt16(catalogBlocks, 0x00);
mountedVolEntry.lastBlock = BigEndianBitConverter.ToInt16(catalogBlocks, 0x02);
mountedVolEntry.entryType = (PascalFileKind)BigEndianBitConverter.ToInt16(catalogBlocks, 0x04);
mountedVolEntry.firstBlock = BigEndianBitConverter.ToInt16(catalogBlocks, 0x00);
mountedVolEntry.lastBlock = BigEndianBitConverter.ToInt16(catalogBlocks, 0x02);
mountedVolEntry.entryType = (PascalFileKind)BigEndianBitConverter.ToInt16(catalogBlocks, 0x04);
mountedVolEntry.volumeName = new byte[8];
Array.Copy(catalogBlocks, 0x06, mountedVolEntry.volumeName, 0, 8);
mountedVolEntry.blocks = BigEndianBitConverter.ToInt16(catalogBlocks, 0x0E);
mountedVolEntry.files = BigEndianBitConverter.ToInt16(catalogBlocks, 0x10);
mountedVolEntry.dummy = BigEndianBitConverter.ToInt16(catalogBlocks, 0x12);
mountedVolEntry.blocks = BigEndianBitConverter.ToInt16(catalogBlocks, 0x0E);
mountedVolEntry.files = BigEndianBitConverter.ToInt16(catalogBlocks, 0x10);
mountedVolEntry.dummy = BigEndianBitConverter.ToInt16(catalogBlocks, 0x12);
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 ||
(ulong)mountedVolEntry.lastBlock > device.Info.Sectors - 2 ||
mountedVolEntry.entryType != PascalFileKind.Volume &&
mountedVolEntry.entryType != PascalFileKind.Secure || mountedVolEntry.volumeName[0] > 7 ||
mountedVolEntry.blocks < 0 || (ulong)mountedVolEntry.blocks != device.Info.Sectors ||
mountedVolEntry.files < 0) return Errno.InvalidArgument;
if(mountedVolEntry.firstBlock != 0 ||
mountedVolEntry.lastBlock <= mountedVolEntry.firstBlock ||
(ulong)mountedVolEntry.lastBlock > device.Info.Sectors - 2 ||
mountedVolEntry.entryType != PascalFileKind.Volume &&
mountedVolEntry.entryType != PascalFileKind.Secure ||
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));
int offset = 26;
int offset = 26;
fileEntries = new List<PascalFileEntry>();
while(offset + 26 < catalogBlocks.Length)
{
PascalFileEntry entry = new PascalFileEntry
{
filename = new byte[16],
firstBlock = BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x00),
lastBlock = BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x02),
entryType = (PascalFileKind)BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x04),
lastBytes = BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x16),
mtime = BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x18)
filename = new byte[16],
firstBlock = BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x00),
lastBlock = BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x02),
entryType = (PascalFileKind)BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x04),
lastBytes = BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x16),
mtime = BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x18)
};
Array.Copy(catalogBlocks, offset + 0x06, entry.filename, 0, 16);
@@ -99,13 +104,13 @@ namespace DiscImageChef.Filesystems.UCSDPascal
XmlFsType = new FileSystemType
{
Bootable = !ArrayHelpers.ArrayIsNullOrEmpty(bootBlocks),
Clusters = mountedVolEntry.blocks,
ClusterSize = (int)device.Info.SectorSize,
Files = mountedVolEntry.files,
Bootable = !ArrayHelpers.ArrayIsNullOrEmpty(bootBlocks),
Clusters = mountedVolEntry.blocks,
ClusterSize = (int)device.Info.SectorSize,
Files = mountedVolEntry.files,
FilesSpecified = true,
Type = "UCSD Pascal",
VolumeName = StringHandlers.PascalToString(mountedVolEntry.volumeName, Encoding)
Type = "UCSD Pascal",
VolumeName = StringHandlers.PascalToString(mountedVolEntry.volumeName, Encoding)
};
mounted = true;
@@ -115,7 +120,7 @@ namespace DiscImageChef.Filesystems.UCSDPascal
public Errno Unmount()
{
mounted = false;
mounted = false;
fileEntries = null;
return Errno.NoError;
}
@@ -124,15 +129,16 @@ namespace DiscImageChef.Filesystems.UCSDPascal
{
stat = new FileSystemInfo
{
Blocks = mountedVolEntry.blocks,
Blocks = mountedVolEntry.blocks,
FilenameLength = 16,
Files = (ulong)mountedVolEntry.files,
FreeBlocks = 0,
PluginId = Id,
Type = "UCSD Pascal"
Files = (ulong)mountedVolEntry.files,
FreeBlocks = 0,
PluginId = Id,
Type = "UCSD Pascal"
};
stat.FreeBlocks = mountedVolEntry.blocks - (mountedVolEntry.lastBlock - mountedVolEntry.firstBlock);
foreach(PascalFileEntry entry in fileEntries) stat.FreeBlocks -= entry.lastBlock - entry.firstBlock;
return Errno.NotImplemented;

View File

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