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
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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()}};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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()}};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()}};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user