mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
Add option to pass an arbitrary list of options to IReadOnlyFilesystem.Mount()
This commit is contained in:
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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()}};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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()}};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user