mirror of
https://github.com/aaru-dps/Aaru.Server.git
synced 2025-12-16 19:24:27 +00:00
REFACTOR: Reformat code.
This commit is contained in:
@@ -121,10 +121,8 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
{
|
||||
Name = "CP/M File System";
|
||||
PluginUUID = new Guid("AA2B8585-41DF-4E3B-8A35-D1A935E2F8A1");
|
||||
if(encoding == null)
|
||||
CurrentEncoding = Encoding.GetEncoding("IBM437");
|
||||
else
|
||||
CurrentEncoding = encoding;
|
||||
if(encoding == null) CurrentEncoding = Encoding.GetEncoding("IBM437");
|
||||
else CurrentEncoding = encoding;
|
||||
}
|
||||
|
||||
public CPM(ImagePlugin imagePlugin, Partition partition, Encoding encoding)
|
||||
@@ -133,11 +131,8 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
this.partition = partition;
|
||||
Name = "CP/M File System";
|
||||
PluginUUID = new Guid("AA2B8585-41DF-4E3B-8A35-D1A935E2F8A1");
|
||||
if(encoding == null)
|
||||
CurrentEncoding = Encoding.GetEncoding("IBM437");
|
||||
else
|
||||
CurrentEncoding = encoding;
|
||||
if(encoding == null) CurrentEncoding = Encoding.GetEncoding("IBM437");
|
||||
else CurrentEncoding = encoding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -81,5 +81,4 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
k720Alt = 0x94
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -48,7 +48,9 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
{
|
||||
try
|
||||
{
|
||||
XmlReader defsReader = XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream("DiscImageChef.Filesystems.CPM.cpmdefs.xml"));
|
||||
XmlReader defsReader =
|
||||
XmlReader.Create(Assembly.GetExecutingAssembly()
|
||||
.GetManifestResourceStream("DiscImageChef.Filesystems.CPM.cpmdefs.xml"));
|
||||
XmlSerializer defsSerializer = new XmlSerializer(typeof(CpmDefinitions));
|
||||
definitions = (CpmDefinitions)defsSerializer.Deserialize(defsReader);
|
||||
|
||||
@@ -60,8 +62,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
def.side1 = new Side();
|
||||
def.side1.sideId = 0;
|
||||
def.side1.sectorIds = new int[def.sectorsPerTrack];
|
||||
for(int i = 0; i < def.sectorsPerTrack; i++)
|
||||
def.side1.sectorIds[i] = i + 1;
|
||||
for(int i = 0; i < def.sectorsPerTrack; i++) def.side1.sectorIds[i] = i + 1;
|
||||
}
|
||||
|
||||
if(def.sides == 2 && def.side2 == null)
|
||||
@@ -69,17 +70,13 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
def.side2 = new Side();
|
||||
def.side2.sideId = 1;
|
||||
def.side2.sectorIds = new int[def.sectorsPerTrack];
|
||||
for(int i = 0; i < def.sectorsPerTrack; i++)
|
||||
def.side2.sectorIds[i] = i + 1;
|
||||
for(int i = 0; i < def.sectorsPerTrack; i++) def.side2.sectorIds[i] = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
catch { return false; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,5 +208,4 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
/// </summary>
|
||||
public int[] sectorIds;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -41,8 +41,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
{
|
||||
public override Errno ReadDir(string path, ref List<string> contents)
|
||||
{
|
||||
if(!mounted)
|
||||
return Errno.AccessDenied;
|
||||
if(!mounted) return Errno.AccessDenied;
|
||||
|
||||
if(!string.IsNullOrEmpty(path) && string.Compare(path, "/", StringComparison.OrdinalIgnoreCase) != 0)
|
||||
return Errno.NotSupported;
|
||||
@@ -64,8 +63,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
{
|
||||
try
|
||||
{
|
||||
if(directory == null)
|
||||
return false;
|
||||
if(directory == null) return false;
|
||||
|
||||
int fileCount = 0;
|
||||
|
||||
@@ -81,31 +79,26 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
{
|
||||
for(int f = 0; f < 8; f++)
|
||||
{
|
||||
if(entry.filename[f] < 0x20 && entry.filename[f] != 0x00)
|
||||
return false;
|
||||
if(entry.filename[f] < 0x20 && entry.filename[f] != 0x00) return false;
|
||||
}
|
||||
|
||||
for(int e = 0; e < 3; e++)
|
||||
{
|
||||
if(entry.extension[e] < 0x20 && entry.extension[e] != 0x00)
|
||||
return false;
|
||||
if(entry.extension[e] < 0x20 && entry.extension[e] != 0x00) return false;
|
||||
}
|
||||
|
||||
if(!ArrayHelpers.ArrayIsNullOrWhiteSpace(entry.filename))
|
||||
fileCount++;
|
||||
if(!ArrayHelpers.ArrayIsNullOrWhiteSpace(entry.filename)) fileCount++;
|
||||
}
|
||||
else if(entry.statusUser == 0x20)
|
||||
{
|
||||
for(int f = 0; f < 8; f++)
|
||||
{
|
||||
if(entry.filename[f] < 0x20 && entry.filename[f] != 0x00)
|
||||
return false;
|
||||
if(entry.filename[f] < 0x20 && entry.filename[f] != 0x00) return false;
|
||||
}
|
||||
|
||||
for(int e = 0; e < 3; e++)
|
||||
{
|
||||
if(entry.extension[e] < 0x20 && entry.extension[e] != 0x00)
|
||||
return false;
|
||||
if(entry.extension[e] < 0x20 && entry.extension[e] != 0x00) return false;
|
||||
}
|
||||
|
||||
label = Encoding.ASCII.GetString(directory, off + 1, 11).Trim();
|
||||
@@ -116,21 +109,14 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
}
|
||||
else if(entry.statusUser == 0x21)
|
||||
{
|
||||
if(directory[off + 1] == 0x00)
|
||||
{
|
||||
thirdPartyTimestamps = true;
|
||||
}
|
||||
if(directory[off + 1] == 0x00) { thirdPartyTimestamps = true; }
|
||||
else standardTimestamps |= (directory[off + 21] == 0x00 && directory[off + 31] == 0x00);
|
||||
}
|
||||
}
|
||||
|
||||
return fileCount > 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
catch { return false; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -38,16 +38,15 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
{
|
||||
public override Errno GetAttributes(string path, ref FileAttributes attributes)
|
||||
{
|
||||
if(!mounted)
|
||||
return Errno.AccessDenied;
|
||||
if(!mounted) return Errno.AccessDenied;
|
||||
|
||||
string[] pathElements = path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if(pathElements.Length != 1)
|
||||
return Errno.NotSupported;
|
||||
string[] pathElements = path.Split(new char[] {'/'}, StringSplitOptions.RemoveEmptyEntries);
|
||||
if(pathElements.Length != 1) return Errno.NotSupported;
|
||||
|
||||
FileEntryInfo fInfo;
|
||||
|
||||
if(string.IsNullOrEmpty(pathElements[0]) || string.Compare(pathElements[0], "/", StringComparison.OrdinalIgnoreCase) == 0)
|
||||
if(string.IsNullOrEmpty(pathElements[0]) ||
|
||||
string.Compare(pathElements[0], "/", StringComparison.OrdinalIgnoreCase) == 0)
|
||||
{
|
||||
attributes = new FileAttributes();
|
||||
attributes = FileAttributes.Directory;
|
||||
@@ -65,8 +64,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
|
||||
public override Errno MapBlock(string path, long fileBlock, ref long deviceBlock)
|
||||
{
|
||||
if(!mounted)
|
||||
return Errno.AccessDenied;
|
||||
if(!mounted) return Errno.AccessDenied;
|
||||
|
||||
// TODO: Implementing this would require storing the interleaving
|
||||
return Errno.NotImplemented;
|
||||
@@ -74,8 +72,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
|
||||
public override Errno Read(string path, long offset, long size, ref byte[] buf)
|
||||
{
|
||||
if(!mounted)
|
||||
return Errno.AccessDenied;
|
||||
if(!mounted) return Errno.AccessDenied;
|
||||
|
||||
if(size == 0)
|
||||
{
|
||||
@@ -83,23 +80,18 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
return Errno.NoError;
|
||||
}
|
||||
|
||||
if(offset < 0)
|
||||
return Errno.InvalidArgument;
|
||||
if(offset < 0) return Errno.InvalidArgument;
|
||||
|
||||
string[] pathElements = path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if(pathElements.Length != 1)
|
||||
return Errno.NotSupported;
|
||||
string[] pathElements = path.Split(new char[] {'/'}, StringSplitOptions.RemoveEmptyEntries);
|
||||
if(pathElements.Length != 1) return Errno.NotSupported;
|
||||
|
||||
byte[] file;
|
||||
|
||||
if(!fileCache.TryGetValue(pathElements[0].ToUpperInvariant(), out file))
|
||||
return Errno.NoSuchFile;
|
||||
if(!fileCache.TryGetValue(pathElements[0].ToUpperInvariant(), out file)) return Errno.NoSuchFile;
|
||||
|
||||
if(offset >= file.Length)
|
||||
return Errno.EINVAL;
|
||||
if(offset >= file.Length) return Errno.EINVAL;
|
||||
|
||||
if(size + offset >= file.Length)
|
||||
size = file.Length - offset;
|
||||
if(size + offset >= file.Length) size = file.Length - offset;
|
||||
|
||||
buf = new byte[size];
|
||||
Array.Copy(file, offset, buf, 0, size);
|
||||
@@ -108,38 +100,30 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
|
||||
public override Errno ReadLink(string path, ref string dest)
|
||||
{
|
||||
if(!mounted)
|
||||
return Errno.AccessDenied;
|
||||
if(!mounted) return Errno.AccessDenied;
|
||||
|
||||
return Errno.NotSupported;
|
||||
}
|
||||
|
||||
public override Errno Stat(string path, ref FileEntryInfo stat)
|
||||
{
|
||||
if(!mounted)
|
||||
return Errno.AccessDenied;
|
||||
if(!mounted) return Errno.AccessDenied;
|
||||
|
||||
string[] pathElements = path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if(pathElements.Length != 1)
|
||||
return Errno.NotSupported;
|
||||
string[] pathElements = path.Split(new char[] {'/'}, StringSplitOptions.RemoveEmptyEntries);
|
||||
if(pathElements.Length != 1) return Errno.NotSupported;
|
||||
|
||||
if(string.IsNullOrEmpty(path) || string.Compare(path, "/", StringComparison.OrdinalIgnoreCase) == 0)
|
||||
{
|
||||
if(labelCreationDate != null)
|
||||
stat.CreationTime = DateHandlers.CPMToDateTime(labelCreationDate);
|
||||
if(labelUpdateDate != null)
|
||||
stat.StatusChangeTime = DateHandlers.CPMToDateTime(labelUpdateDate);
|
||||
if(labelCreationDate != null) stat.CreationTime = DateHandlers.CPMToDateTime(labelCreationDate);
|
||||
if(labelUpdateDate != null) stat.StatusChangeTime = DateHandlers.CPMToDateTime(labelUpdateDate);
|
||||
stat.Attributes = FileAttributes.Directory;
|
||||
stat.BlockSize = xmlFSType.ClusterSize;
|
||||
return Errno.NoError;
|
||||
}
|
||||
|
||||
if(statCache.TryGetValue(pathElements[0].ToUpperInvariant(), out stat))
|
||||
return Errno.NoError;
|
||||
if(statCache.TryGetValue(pathElements[0].ToUpperInvariant(), out stat)) return Errno.NoError;
|
||||
|
||||
return Errno.NoSuchFile;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -153,12 +153,10 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
case MediaType.RP06:
|
||||
case MediaType.RP06_18:
|
||||
case MediaType.GENERIC_HDD:
|
||||
case MediaType.FlashDrive:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
case MediaType.FlashDrive: break;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
|
||||
// This will try to identify a CP/M filesystem
|
||||
// However as it contains no identification marks whatsoever it's more something of trial-and-error
|
||||
// As anything can happen, better try{}catch{} than sorry ;)
|
||||
@@ -186,8 +184,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
sig3 = BitConverter.ToUInt32(sector, 0x7C);
|
||||
|
||||
// PCW16 extended boot record
|
||||
if(sig1 == 0x4D2F5043 && sig2 == 0x004B5344 && sig3 == sig1)
|
||||
amsSbOffset = 0x80;
|
||||
if(sig1 == 0x4D2F5043 && sig2 == 0x004B5344 && sig3 == sig1) amsSbOffset = 0x80;
|
||||
|
||||
// Read the superblock
|
||||
AmstradSuperBlock amsSb = new AmstradSuperBlock();
|
||||
@@ -207,8 +204,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
sectorSize = (ulong)(128 << amsSb.psh);
|
||||
|
||||
// Compare device limits from superblock to real limits
|
||||
if(sectorSize == imagePlugin.GetSectorSize() &&
|
||||
sectorCount == imagePlugin.GetSectors())
|
||||
if(sectorSize == imagePlugin.GetSectorSize() && sectorCount == imagePlugin.GetSectors())
|
||||
{
|
||||
cpmFound = true;
|
||||
firstDirectorySector = (ulong)((amsSb.off * amsSb.spt));
|
||||
@@ -218,8 +214,8 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
dpb.al0 = sectorCount == 1440 ? (byte)0xF0 : (byte)0xC0;
|
||||
dpb.spt = amsSb.spt;
|
||||
dpb.bsh = amsSb.bsh;
|
||||
for(int i = 0; i < dpb.bsh; i++)
|
||||
dpb.blm += (byte)Math.Pow(2, i);
|
||||
for(int i = 0; i < dpb.bsh; i++) dpb.blm += (byte)Math.Pow(2, i);
|
||||
|
||||
if(sectorCount >= 1440)
|
||||
{
|
||||
dpb.cks = 0x40;
|
||||
@@ -234,11 +230,12 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
dpb.exm = sectorCount == 2880 ? (byte)1 : (byte)0;
|
||||
dpb.off = amsSb.off;
|
||||
dpb.psh = amsSb.psh;
|
||||
for(int i = 0; i < dpb.psh; i++)
|
||||
dpb.phm += (byte)Math.Pow(2, i);
|
||||
for(int i = 0; i < dpb.psh; i++) dpb.phm += (byte)Math.Pow(2, i);
|
||||
|
||||
dpb.spt = (ushort)(amsSb.spt * (sectorSize / 128));
|
||||
uint directoryLength = (uint)((((ulong)dpb.drm + 1) * 32) / sectorSize);
|
||||
directory = imagePlugin.ReadSectors(firstDirectorySector + partition.Start, directoryLength);
|
||||
directory = imagePlugin.ReadSectors(firstDirectorySector + partition.Start,
|
||||
directoryLength);
|
||||
|
||||
// Build a CP/M disk definition
|
||||
workingDefinition = new CpmDefinition();
|
||||
@@ -262,26 +259,20 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
workingDefinition.side1 = new Side();
|
||||
workingDefinition.side1.sideId = 0;
|
||||
workingDefinition.side1.sectorIds = new int[amsSb.spt];
|
||||
for(int si = 0; si < amsSb.spt; si++)
|
||||
workingDefinition.side1.sectorIds[si] = si + 1;
|
||||
for(int si = 0; si < amsSb.spt; si++) workingDefinition.side1.sectorIds[si] = si + 1;
|
||||
|
||||
if(amsSb.format == 2)
|
||||
{
|
||||
if((amsSb.sidedness & 0x02) == 1)
|
||||
workingDefinition.order = "SIDES";
|
||||
else if((amsSb.sidedness & 0x02) == 2)
|
||||
workingDefinition.order = "CYLINDERS";
|
||||
else
|
||||
workingDefinition.order = null;
|
||||
if((amsSb.sidedness & 0x02) == 1) workingDefinition.order = "SIDES";
|
||||
else if((amsSb.sidedness & 0x02) == 2) workingDefinition.order = "CYLINDERS";
|
||||
else workingDefinition.order = null;
|
||||
|
||||
workingDefinition.side2 = new Side();
|
||||
workingDefinition.side2.sideId = 1;
|
||||
workingDefinition.side2.sectorIds = new int[amsSb.spt];
|
||||
for(int si = 0; si < amsSb.spt; si++)
|
||||
workingDefinition.side2.sectorIds[si] = si + 1;
|
||||
for(int si = 0; si < amsSb.spt; si++) workingDefinition.side2.sectorIds[si] = si + 1;
|
||||
}
|
||||
else
|
||||
workingDefinition.order = null;
|
||||
else workingDefinition.order = null;
|
||||
|
||||
workingDefinition.skew = 2;
|
||||
workingDefinition.sofs = 0;
|
||||
@@ -299,8 +290,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
ushort sum = 0;
|
||||
|
||||
// Sum of all 16-bit words that make this sector must be 0
|
||||
for(int i = 0; i < sector.Length; i += 2)
|
||||
sum += BitConverter.ToUInt16(sector, i);
|
||||
for(int i = 0; i < sector.Length; i += 2) sum += BitConverter.ToUInt16(sector, i);
|
||||
|
||||
// It may happen that there is a corrupted superblock
|
||||
// Better to ignore corrupted than to false positive the rest
|
||||
@@ -316,11 +306,11 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
// Calculate volume size
|
||||
sectorSize = (ulong)(hddSb.recordsPerSector * 128);
|
||||
ulong sectorsInPartition = (ulong)(hddSb.cylinders * hddSb.heads * hddSb.sectorsPerTrack);
|
||||
ulong startingSector = (ulong)((hddSb.firstCylinder * hddSb.heads + hddSb.heads) * hddSb.sectorsPerTrack);
|
||||
ulong startingSector =
|
||||
(ulong)((hddSb.firstCylinder * hddSb.heads + hddSb.heads) * hddSb.sectorsPerTrack);
|
||||
|
||||
// If volume size corresponds with working partition (this variant will be inside MBR partitioning)
|
||||
if(sectorSize == imagePlugin.GetSectorSize() &&
|
||||
startingSector == partition.Start &&
|
||||
if(sectorSize == imagePlugin.GetSectorSize() && startingSector == partition.Start &&
|
||||
sectorsInPartition + partition.Start <= partition.End)
|
||||
{
|
||||
cpmFound = true;
|
||||
@@ -341,7 +331,8 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
dpb.psh = 0; // Needed?
|
||||
dpb.spt = hddSb.spt;
|
||||
uint directoryLength = (uint)((((ulong)dpb.drm + 1) * 32) / sectorSize);
|
||||
directory = imagePlugin.ReadSectors(firstDirectorySector + partition.Start, directoryLength);
|
||||
directory = imagePlugin.ReadSectors(firstDirectorySector + partition.Start,
|
||||
directoryLength);
|
||||
DicConsole.DebugWriteLine("CP/M Plugin", "Found CP/M-86 hard disk superblock.");
|
||||
|
||||
// Build a CP/M disk definition
|
||||
@@ -367,12 +358,13 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
workingDefinition.side1.sectorIds = new int[hddSb.sectorsPerTrack];
|
||||
for(int si = 0; si < hddSb.sectorsPerTrack; si++)
|
||||
workingDefinition.side1.sectorIds[si] = si + 1;
|
||||
|
||||
workingDefinition.order = "SIDES";
|
||||
workingDefinition.side2 = new Side();
|
||||
workingDefinition.side2.sideId = 1;
|
||||
workingDefinition.side2.sectorIds = new int[hddSb.sectorsPerTrack];
|
||||
for(int si = 0; si < hddSb.spt; si++)
|
||||
workingDefinition.side2.sectorIds[si] = si + 1;
|
||||
for(int si = 0; si < hddSb.spt; si++) workingDefinition.side2.sectorIds[si] = si + 1;
|
||||
|
||||
workingDefinition.skew = 0;
|
||||
workingDefinition.sofs = 0;
|
||||
}
|
||||
@@ -389,13 +381,10 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
// Check for alternate location of format ID
|
||||
if(sector.Last() == 0x00 || sector.Last() == 0xFF)
|
||||
{
|
||||
if(sector[0x40] == 0x94 || sector[0x40] == 0x26)
|
||||
formatByte = sector[0x40];
|
||||
else
|
||||
formatByte = sector.Last();
|
||||
if(sector[0x40] == 0x94 || sector[0x40] == 0x26) formatByte = sector[0x40];
|
||||
else formatByte = sector.Last();
|
||||
}
|
||||
else
|
||||
formatByte = sector.Last();
|
||||
else formatByte = sector.Last();
|
||||
|
||||
uint firstDirectorySector86 = 0;
|
||||
|
||||
@@ -445,11 +434,12 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
workingDefinition.side1 = new Side();
|
||||
workingDefinition.side1.sideId = 0;
|
||||
workingDefinition.side1.sectorIds = new int[8];
|
||||
for(int si = 0; si < 8; si++)
|
||||
workingDefinition.side1.sectorIds[si] = si + 1;
|
||||
for(int si = 0; si < 8; si++) workingDefinition.side1.sectorIds[si] = si + 1;
|
||||
|
||||
workingDefinition.skew = 0;
|
||||
workingDefinition.sofs = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
case FormatByte.k320:
|
||||
if(imagePlugin.GetSectorSize() == 512 && imagePlugin.GetSectors() == 640)
|
||||
@@ -490,17 +480,18 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
workingDefinition.side1 = new Side();
|
||||
workingDefinition.side1.sideId = 0;
|
||||
workingDefinition.side1.sectorIds = new int[8];
|
||||
for(int si = 0; si < 8; si++)
|
||||
workingDefinition.side1.sectorIds[si] = si + 1;
|
||||
for(int si = 0; si < 8; si++) workingDefinition.side1.sectorIds[si] = si + 1;
|
||||
|
||||
workingDefinition.order = "SIDES";
|
||||
workingDefinition.side2 = new Side();
|
||||
workingDefinition.side2.sideId = 1;
|
||||
workingDefinition.side2.sectorIds = new int[8];
|
||||
for(int si = 0; si < 8; si++)
|
||||
workingDefinition.side2.sectorIds[si] = si + 1;
|
||||
for(int si = 0; si < 8; si++) workingDefinition.side2.sectorIds[si] = si + 1;
|
||||
|
||||
workingDefinition.skew = 0;
|
||||
workingDefinition.sofs = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
case FormatByte.k360:
|
||||
case FormatByte.k360Alt:
|
||||
@@ -543,17 +534,18 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
workingDefinition.side1 = new Side();
|
||||
workingDefinition.side1.sideId = 0;
|
||||
workingDefinition.side1.sectorIds = new int[9];
|
||||
for(int si = 0; si < 9; si++)
|
||||
workingDefinition.side1.sectorIds[si] = si + 1;
|
||||
for(int si = 0; si < 9; si++) workingDefinition.side1.sectorIds[si] = si + 1;
|
||||
|
||||
workingDefinition.order = "SIDES";
|
||||
workingDefinition.side2 = new Side();
|
||||
workingDefinition.side2.sideId = 1;
|
||||
workingDefinition.side2.sectorIds = new int[9];
|
||||
for(int si = 0; si < 9; si++)
|
||||
workingDefinition.side2.sectorIds[si] = si + 1;
|
||||
for(int si = 0; si < 9; si++) workingDefinition.side2.sectorIds[si] = si + 1;
|
||||
|
||||
workingDefinition.skew = 0;
|
||||
workingDefinition.sofs = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
case FormatByte.k720:
|
||||
case FormatByte.k720Alt:
|
||||
@@ -595,17 +587,18 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
workingDefinition.side1 = new Side();
|
||||
workingDefinition.side1.sideId = 0;
|
||||
workingDefinition.side1.sectorIds = new int[9];
|
||||
for(int si = 0; si < 9; si++)
|
||||
workingDefinition.side1.sectorIds[si] = si + 1;
|
||||
for(int si = 0; si < 9; si++) workingDefinition.side1.sectorIds[si] = si + 1;
|
||||
|
||||
workingDefinition.order = "SIDES";
|
||||
workingDefinition.side2 = new Side();
|
||||
workingDefinition.side2.sideId = 1;
|
||||
workingDefinition.side2.sectorIds = new int[9];
|
||||
for(int si = 0; si < 9; si++)
|
||||
workingDefinition.side2.sectorIds[si] = si + 1;
|
||||
for(int si = 0; si < 9; si++) workingDefinition.side2.sectorIds[si] = si + 1;
|
||||
|
||||
workingDefinition.skew = 0;
|
||||
workingDefinition.sofs = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
case FormatByte.f720:
|
||||
if(imagePlugin.GetSectorSize() == 512 && imagePlugin.GetSectors() == 1440)
|
||||
@@ -646,17 +639,18 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
workingDefinition.side1 = new Side();
|
||||
workingDefinition.side1.sideId = 0;
|
||||
workingDefinition.side1.sectorIds = new int[9];
|
||||
for(int si = 0; si < 9; si++)
|
||||
workingDefinition.side1.sectorIds[si] = si + 1;
|
||||
for(int si = 0; si < 9; si++) workingDefinition.side1.sectorIds[si] = si + 1;
|
||||
|
||||
workingDefinition.order = "CYLINDERS";
|
||||
workingDefinition.side2 = new Side();
|
||||
workingDefinition.side2.sideId = 1;
|
||||
workingDefinition.side2.sectorIds = new int[9];
|
||||
for(int si = 0; si < 9; si++)
|
||||
workingDefinition.side2.sectorIds[si] = si + 1;
|
||||
for(int si = 0; si < 9; si++) workingDefinition.side2.sectorIds[si] = si + 1;
|
||||
|
||||
workingDefinition.skew = 0;
|
||||
workingDefinition.sofs = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
case FormatByte.f1200:
|
||||
if(imagePlugin.GetSectorSize() == 512 && imagePlugin.GetSectors() == 2400)
|
||||
@@ -697,17 +691,18 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
workingDefinition.side1 = new Side();
|
||||
workingDefinition.side1.sideId = 0;
|
||||
workingDefinition.side1.sectorIds = new int[15];
|
||||
for(int si = 0; si < 15; si++)
|
||||
workingDefinition.side1.sectorIds[si] = si + 1;
|
||||
for(int si = 0; si < 15; si++) workingDefinition.side1.sectorIds[si] = si + 1;
|
||||
|
||||
workingDefinition.order = "CYLINDERS";
|
||||
workingDefinition.side2 = new Side();
|
||||
workingDefinition.side2.sideId = 1;
|
||||
workingDefinition.side2.sectorIds = new int[15];
|
||||
for(int si = 0; si < 15; si++)
|
||||
workingDefinition.side2.sectorIds[si] = si + 1;
|
||||
for(int si = 0; si < 15; si++) workingDefinition.side2.sectorIds[si] = si + 1;
|
||||
|
||||
workingDefinition.skew = 0;
|
||||
workingDefinition.sofs = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
case FormatByte.f1440:
|
||||
if(imagePlugin.GetSectorSize() == 512 && imagePlugin.GetSectors() == 2880)
|
||||
@@ -748,17 +743,18 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
workingDefinition.side1 = new Side();
|
||||
workingDefinition.side1.sideId = 0;
|
||||
workingDefinition.side1.sectorIds = new int[18];
|
||||
for(int si = 0; si < 18; si++)
|
||||
workingDefinition.side1.sectorIds[si] = si + 1;
|
||||
for(int si = 0; si < 18; si++) workingDefinition.side1.sectorIds[si] = si + 1;
|
||||
|
||||
workingDefinition.order = "CYLINDERS";
|
||||
workingDefinition.side2 = new Side();
|
||||
workingDefinition.side2.sideId = 1;
|
||||
workingDefinition.side2.sectorIds = new int[18];
|
||||
for(int si = 0; si < 18; si++)
|
||||
workingDefinition.side2.sectorIds[si] = si + 1;
|
||||
for(int si = 0; si < 18; si++) workingDefinition.side2.sectorIds[si] = si + 1;
|
||||
|
||||
workingDefinition.skew = 0;
|
||||
workingDefinition.sofs = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -787,7 +783,8 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
{
|
||||
// Load all definitions
|
||||
DicConsole.DebugWriteLine("CP/M Plugin", "Trying to load definitions.");
|
||||
if(LoadDefinitions() && definitions != null && definitions.definitions != null && definitions.definitions.Count > 0)
|
||||
if(LoadDefinitions() && definitions != null && definitions.definitions != null &&
|
||||
definitions.definitions.Count > 0)
|
||||
{
|
||||
DicConsole.DebugWriteLine("CP/M Plugin", "Trying all known definitions.");
|
||||
foreach(CpmDefinition def in definitions.definitions)
|
||||
@@ -799,10 +796,8 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
{
|
||||
DicConsole.DebugWriteLine("CP/M Plugin", "Trying definition \"{0}\"", def.comment);
|
||||
ulong offset;
|
||||
if(def.sofs != 0)
|
||||
offset = (ulong)def.sofs;
|
||||
else
|
||||
offset = (ulong)(def.ofs * def.sectorsPerTrack);
|
||||
if(def.sofs != 0) offset = (ulong)def.sofs;
|
||||
else offset = (ulong)(def.ofs * def.sectorsPerTrack);
|
||||
|
||||
int dirLen = ((def.drm + 1) * 32) / def.bytesPerSector;
|
||||
|
||||
@@ -815,39 +810,51 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
else
|
||||
{
|
||||
// Head changes after every track
|
||||
if(string.Compare(def.order, "SIDES", StringComparison.InvariantCultureIgnoreCase) == 0)
|
||||
if(string.Compare(def.order, "SIDES",
|
||||
StringComparison.InvariantCultureIgnoreCase) == 0)
|
||||
{
|
||||
sectorMask = new int[def.side1.sectorIds.Length + def.side2.sectorIds.Length];
|
||||
for(int m = 0; m < def.side1.sectorIds.Length; m++)
|
||||
sectorMask[m] = def.side1.sectorIds[m] - def.side1.sectorIds[0];
|
||||
// Skip first track (first side)
|
||||
for(int m = 0; m < def.side2.sectorIds.Length; m++)
|
||||
sectorMask[m + def.side1.sectorIds.Length] = (def.side2.sectorIds[m] - def.side2.sectorIds[0]) + def.side1.sectorIds.Length;
|
||||
sectorMask[m + def.side1.sectorIds.Length] =
|
||||
(def.side2.sectorIds[m] - def.side2.sectorIds[0]) +
|
||||
def.side1.sectorIds.Length;
|
||||
}
|
||||
// Head changes after whole side
|
||||
else if(string.Compare(def.order, "CYLINDERS", StringComparison.InvariantCultureIgnoreCase) == 0)
|
||||
else if(string.Compare(def.order, "CYLINDERS",
|
||||
StringComparison.InvariantCultureIgnoreCase) == 0)
|
||||
{
|
||||
for(int m = 0; m < def.side1.sectorIds.Length; m++)
|
||||
sectorMask[m] = def.side1.sectorIds[m] - def.side1.sectorIds[0];
|
||||
// Skip first track (first side) and first track (second side)
|
||||
for(int m = 0; m < def.side1.sectorIds.Length; m++)
|
||||
sectorMask[m + def.side1.sectorIds.Length] = (def.side1.sectorIds[m] - def.side1.sectorIds[0]) + def.side1.sectorIds.Length + def.side2.sectorIds.Length;
|
||||
sectorMask[m + def.side1.sectorIds.Length] =
|
||||
(def.side1.sectorIds[m] - def.side1.sectorIds[0]) +
|
||||
def.side1.sectorIds.Length + def.side2.sectorIds.Length;
|
||||
}
|
||||
// TODO: Implement COLUMBIA ordering
|
||||
else if(string.Compare(def.order, "COLUMBIA", StringComparison.InvariantCultureIgnoreCase) == 0)
|
||||
else if(string.Compare(def.order, "COLUMBIA",
|
||||
StringComparison.InvariantCultureIgnoreCase) == 0)
|
||||
{
|
||||
DicConsole.DebugWriteLine("CP/M Plugin", "Don't know how to handle COLUMBIA ordering, not proceeding with this definition.");
|
||||
DicConsole.DebugWriteLine("CP/M Plugin",
|
||||
"Don't know how to handle COLUMBIA ordering, not proceeding with this definition.");
|
||||
continue;
|
||||
}
|
||||
// TODO: Implement EAGLE ordering
|
||||
else if(string.Compare(def.order, "EAGLE", StringComparison.InvariantCultureIgnoreCase) == 0)
|
||||
else if(string.Compare(def.order, "EAGLE",
|
||||
StringComparison.InvariantCultureIgnoreCase) == 0)
|
||||
{
|
||||
DicConsole.DebugWriteLine("CP/M Plugin", "Don't know how to handle EAGLE ordering, not proceeding with this definition.");
|
||||
DicConsole.DebugWriteLine("CP/M Plugin",
|
||||
"Don't know how to handle EAGLE ordering, not proceeding with this definition.");
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
DicConsole.DebugWriteLine("CP/M Plugin", "Unknown order type \"{0}\", not proceeding with this definition.", def.order);
|
||||
DicConsole.DebugWriteLine("CP/M Plugin",
|
||||
"Unknown order type \"{0}\", not proceeding with this definition.",
|
||||
def.order);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -856,13 +863,18 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
MemoryStream ms = new MemoryStream();
|
||||
for(int p = 0; p < dirLen; p++)
|
||||
{
|
||||
byte[] dirSector = imagePlugin.ReadSector((ulong)((int)offset + (int)partition.Start + (p / sectorMask.Length) * sectorMask.Length + sectorMask[p % sectorMask.Length]));
|
||||
byte[] dirSector =
|
||||
imagePlugin.ReadSector((ulong)((int)offset + (int)partition.Start +
|
||||
(p / sectorMask.Length) * sectorMask.Length +
|
||||
sectorMask[p % sectorMask.Length]));
|
||||
ms.Write(dirSector, 0, dirSector.Length);
|
||||
}
|
||||
|
||||
directory = ms.ToArray();
|
||||
|
||||
if(def.evenOdd)
|
||||
DicConsole.DebugWriteLine("CP/M Plugin", "Definition contains EVEN-ODD field, with unknown meaning, detection may be wrong.");
|
||||
DicConsole.DebugWriteLine("CP/M Plugin",
|
||||
"Definition contains EVEN-ODD field, with unknown meaning, detection may be wrong.");
|
||||
|
||||
// Complement of the directory bytes if needed
|
||||
if(def.complement)
|
||||
@@ -874,7 +886,9 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
// Check the directory
|
||||
if(CheckDir(directory))
|
||||
{
|
||||
DicConsole.DebugWriteLine("CP/M Plugin", "Definition \"{0}\" has a correct directory", def.comment);
|
||||
DicConsole.DebugWriteLine("CP/M Plugin",
|
||||
"Definition \"{0}\" has a correct directory",
|
||||
def.comment);
|
||||
|
||||
// Build a Disc Parameter Block
|
||||
workingDefinition = def;
|
||||
@@ -927,6 +941,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
dpb.phm = 255;
|
||||
break;
|
||||
}
|
||||
|
||||
dpb.spt = (ushort)((def.sectorsPerTrack * def.bytesPerSector) / 128);
|
||||
cpmFound = true;
|
||||
workingDefinition = def;
|
||||
@@ -962,8 +977,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
{
|
||||
information = "";
|
||||
// As the identification is so complex, just call Identify() and relay on its findings
|
||||
if(!Identify(imagePlugin, partition) || !cpmFound || workingDefinition == null || dpb == null)
|
||||
return;
|
||||
if(!Identify(imagePlugin, partition) || !cpmFound || workingDefinition == null || dpb == null) return;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine("CP/M filesystem");
|
||||
@@ -971,12 +985,15 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
sb.AppendFormat("Identified as {0}", workingDefinition.comment).AppendLine();
|
||||
sb.AppendFormat("Volume block is {0} bytes", 128 << dpb.bsh).AppendLine();
|
||||
if(dpb.dsm > 0)
|
||||
sb.AppendFormat("Volume contains {0} blocks ({1} bytes)", dpb.dsm, (dpb.dsm) * (128 << dpb.bsh)).AppendLine();
|
||||
sb.AppendFormat("Volume contains {0} blocks ({1} bytes)", dpb.dsm, (dpb.dsm) * (128 << dpb.bsh))
|
||||
.AppendLine();
|
||||
sb.AppendFormat("Volume contains {0} directory entries", dpb.drm + 1).AppendLine();
|
||||
if(workingDefinition.sofs > 0)
|
||||
sb.AppendFormat("Volume reserves {0} sectors for system", workingDefinition.sofs).AppendLine();
|
||||
else
|
||||
sb.AppendFormat("Volume reserves {1} tracks ({0} sectors) for system", workingDefinition.ofs * workingDefinition.sectorsPerTrack, workingDefinition.ofs).AppendLine();
|
||||
sb.AppendFormat("Volume reserves {1} tracks ({0} sectors) for system",
|
||||
workingDefinition.ofs * workingDefinition.sectorsPerTrack, workingDefinition.ofs)
|
||||
.AppendLine();
|
||||
|
||||
int interleaveSide1;
|
||||
int interleaveSide2 = 1;
|
||||
@@ -987,7 +1004,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
if(interleaveSide1 > 1)
|
||||
sb.AppendFormat("Side 0 uses {0}:1 software interleaving", interleaveSide1).AppendLine();
|
||||
}
|
||||
|
||||
|
||||
if(workingDefinition.sides == 2)
|
||||
{
|
||||
if(workingDefinition.side2.sectorIds.Length >= 2)
|
||||
@@ -1014,18 +1031,19 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
sb.AppendFormat("Device uses {0}:1 hardware interleaving", workingDefinition.skew).AppendLine();
|
||||
|
||||
if(workingDefinition.sofs > 0)
|
||||
sb.AppendFormat("BSH {0} BLM {1} EXM {2} DSM {3} DRM {4} AL0 {5:X2}H AL1 {6:X2}H SOFS {7}", dpb.bsh, dpb.blm, dpb.exm, dpb.dsm, dpb.drm, dpb.al0, dpb.al1, workingDefinition.sofs).AppendLine();
|
||||
sb.AppendFormat("BSH {0} BLM {1} EXM {2} DSM {3} DRM {4} AL0 {5:X2}H AL1 {6:X2}H SOFS {7}", dpb.bsh,
|
||||
dpb.blm, dpb.exm, dpb.dsm, dpb.drm, dpb.al0, dpb.al1, workingDefinition.sofs)
|
||||
.AppendLine();
|
||||
else
|
||||
sb.AppendFormat("BSH {0} BLM {1} EXM {2} DSM {3} DRM {4} AL0 {5:X2}H AL1 {6:X2}H OFS {7}", dpb.bsh, dpb.blm, dpb.exm, dpb.dsm, dpb.drm, dpb.al0, dpb.al1, workingDefinition.ofs).AppendLine();
|
||||
sb.AppendFormat("BSH {0} BLM {1} EXM {2} DSM {3} DRM {4} AL0 {5:X2}H AL1 {6:X2}H OFS {7}", dpb.bsh,
|
||||
dpb.blm, dpb.exm, dpb.dsm, dpb.drm, dpb.al0, dpb.al1, workingDefinition.ofs)
|
||||
.AppendLine();
|
||||
|
||||
if(label != null)
|
||||
sb.AppendFormat("Volume label {0}", label).AppendLine();
|
||||
if(label != null) sb.AppendFormat("Volume label {0}", label).AppendLine();
|
||||
|
||||
if(standardTimestamps)
|
||||
sb.AppendLine("Volume uses standard CP/M timestamps");
|
||||
if(standardTimestamps) sb.AppendLine("Volume uses standard CP/M timestamps");
|
||||
|
||||
if(thirdPartyTimestamps)
|
||||
sb.AppendLine("Volume uses third party timestamps");
|
||||
if(thirdPartyTimestamps) sb.AppendLine("Volume uses third party timestamps");
|
||||
|
||||
if(labelCreationDate != null)
|
||||
sb.AppendFormat("Volume created on {0}", DateHandlers.CPMToDateTime(labelCreationDate)).AppendLine();
|
||||
@@ -1035,10 +1053,8 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
xmlFSType = new Schemas.FileSystemType();
|
||||
xmlFSType.Bootable |= (workingDefinition.sofs > 0 || workingDefinition.ofs > 0);
|
||||
xmlFSType.ClusterSize = 128 << dpb.bsh;
|
||||
if(dpb.dsm > 0)
|
||||
xmlFSType.Clusters = dpb.dsm;
|
||||
else
|
||||
xmlFSType.Clusters = (long)(partition.End - partition.Start);
|
||||
if(dpb.dsm > 0) xmlFSType.Clusters = dpb.dsm;
|
||||
else xmlFSType.Clusters = (long)(partition.End - partition.Start);
|
||||
if(labelCreationDate != null)
|
||||
{
|
||||
xmlFSType.CreationDate = DateHandlers.CPMToDateTime(labelCreationDate);
|
||||
@@ -1055,5 +1071,4 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
information = sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -176,8 +176,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
/// <summary>
|
||||
/// Copyright string
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x1F)]
|
||||
public byte[] copyright;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x1F)] public byte[] copyright;
|
||||
/// <summary>
|
||||
/// First cylinder of disk where this volume resides
|
||||
/// </summary>
|
||||
@@ -289,8 +288,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
/// <summary>
|
||||
/// Label in ASCII
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)]
|
||||
public byte[] label;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] public byte[] label;
|
||||
/// <summary>
|
||||
/// Label flags. Bit 0 = label exists, bit 4 = creation timestamp, bit 5 = modification timestamp, bit 6 = access timestamp, bit 7 = password enabled
|
||||
/// </summary>
|
||||
@@ -306,18 +304,15 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
/// <summary>
|
||||
/// Password XOR'ed with <see cref="passwordDecoder"/>
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
|
||||
public byte[] password;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] password;
|
||||
/// <summary>
|
||||
/// Label creation time
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
public byte[] ctime;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public byte[] ctime;
|
||||
/// <summary>
|
||||
/// Label modification time
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
public byte[] mtime;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public byte[] mtime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -333,8 +328,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
/// <summary>
|
||||
/// File 1 create/access timestamp
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
public byte[] date1;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public byte[] date1;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
/// <summary>
|
||||
/// File 1 modification timestamp
|
||||
@@ -348,13 +342,11 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
/// <summary>
|
||||
/// File 2 create/access timestamp
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
public byte[] date3;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public byte[] date3;
|
||||
/// <summary>
|
||||
/// File 2 modification timestamp
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
public byte[] date4;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public byte[] date4;
|
||||
/// <summary>
|
||||
/// File 2 password mode
|
||||
/// </summary>
|
||||
@@ -363,13 +355,11 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
/// <summary>
|
||||
/// File 3 create/access timestamp
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
public byte[] date5;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public byte[] date5;
|
||||
/// <summary>
|
||||
/// File 3 modification timestamp
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
public byte[] date6;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public byte[] date6;
|
||||
/// <summary>
|
||||
/// File 3 password mode
|
||||
/// </summary>
|
||||
@@ -390,13 +380,11 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
/// <summary>
|
||||
/// Filename
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
|
||||
public byte[] filename;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] filename;
|
||||
/// <summary>
|
||||
/// Extension
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
|
||||
public byte[] extension;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public byte[] extension;
|
||||
/// <summary>
|
||||
/// Password mode. Bit 7 = required for read, bit 6 = required for write, bit 5 = required for delete
|
||||
/// </summary>
|
||||
@@ -405,15 +393,12 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
/// Password decoder byte
|
||||
/// </summary>
|
||||
public byte passwordDecoder;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
|
||||
public byte[] reserved;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public byte[] reserved;
|
||||
/// <summary>
|
||||
/// Password XOR'ed with <see cref="passwordDecoder"/>
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
|
||||
public byte[] password;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
|
||||
public byte[] reserved2;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] password;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] reserved2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -430,48 +415,39 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
/// <summary>
|
||||
/// Creation year for file 1
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
|
||||
public byte[] create1;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public byte[] create1;
|
||||
/// <summary>
|
||||
/// Modification time for file 1
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
public byte[] modify1;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public byte[] modify1;
|
||||
/// <summary>
|
||||
/// Access time for file 1
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
public byte[] access1;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public byte[] access1;
|
||||
/// <summary>
|
||||
/// Creation year for file 2
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
|
||||
public byte[] create2;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public byte[] create2;
|
||||
/// <summary>
|
||||
/// Modification time for file 2
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
public byte[] modify2;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public byte[] modify2;
|
||||
/// <summary>
|
||||
/// Access time for file 2
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
public byte[] access2;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public byte[] access2;
|
||||
/// <summary>
|
||||
/// Creation year for file 3
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
|
||||
public byte[] create3;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public byte[] create3;
|
||||
/// <summary>
|
||||
/// Modification time for file 3
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
public byte[] modify3;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public byte[] modify3;
|
||||
/// <summary>
|
||||
/// Access time for file 3
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
public byte[] access3;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public byte[] access3;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -487,13 +463,11 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
/// <summary>
|
||||
/// Filename and bit 7 as flags
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
|
||||
public byte[] filename;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] filename;
|
||||
/// <summary>
|
||||
/// Extension and bit 7 as flags
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
|
||||
public byte[] extension;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public byte[] extension;
|
||||
/// <summary>
|
||||
/// Low byte of extent number
|
||||
/// </summary>
|
||||
@@ -514,8 +488,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
/// <summary>
|
||||
/// Allocation blocks
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
|
||||
public byte[] allocations;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] allocations;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -531,13 +504,11 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
/// <summary>
|
||||
/// Filename and bit 7 as flags
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
|
||||
public byte[] filename;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] filename;
|
||||
/// <summary>
|
||||
/// Extension and bit 7 as flags
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
|
||||
public byte[] extension;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public byte[] extension;
|
||||
/// <summary>
|
||||
/// Low byte of extent number
|
||||
/// </summary>
|
||||
@@ -558,9 +529,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
/// <summary>
|
||||
/// Allocation blocks
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
|
||||
public ushort[] allocations;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public ushort[] allocations;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -66,59 +66,75 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
// Head changes after every track
|
||||
if(string.Compare(workingDefinition.order, "SIDES", StringComparison.InvariantCultureIgnoreCase) == 0)
|
||||
{
|
||||
sectorMask = new int[workingDefinition.side1.sectorIds.Length + workingDefinition.side2.sectorIds.Length];
|
||||
sectorMask = new int[workingDefinition.side1.sectorIds.Length +
|
||||
workingDefinition.side2.sectorIds.Length];
|
||||
for(int m = 0; m < workingDefinition.side1.sectorIds.Length; m++)
|
||||
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] = (workingDefinition.side2.sectorIds[m] - workingDefinition.side2.sectorIds[0]) + workingDefinition.side1.sectorIds.Length;
|
||||
sectorMask[m + workingDefinition.side1.sectorIds.Length] =
|
||||
(workingDefinition.side2.sectorIds[m] - workingDefinition.side2.sectorIds[0]) +
|
||||
workingDefinition.side1.sectorIds.Length;
|
||||
}
|
||||
// Head changes after whole side
|
||||
else if(string.Compare(workingDefinition.order, "CYLINDERS", StringComparison.InvariantCultureIgnoreCase) == 0)
|
||||
else if(string.Compare(workingDefinition.order, "CYLINDERS",
|
||||
StringComparison.InvariantCultureIgnoreCase) == 0)
|
||||
{
|
||||
for(int m = 0; m < workingDefinition.side1.sectorIds.Length; m++)
|
||||
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]) + workingDefinition.side1.sectorIds.Length + workingDefinition.side2.sectorIds.Length;
|
||||
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
|
||||
DicConsole.DebugWriteLine("CP/M Plugin", "CYLINDERS ordering not yet implemented.");
|
||||
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.");
|
||||
DicConsole.DebugWriteLine("CP/M Plugin",
|
||||
"Don't know how to handle COLUMBIA ordering, not proceeding with this definition.");
|
||||
return Errno.NotImplemented;
|
||||
}
|
||||
// TODO: Implement EAGLE ordering
|
||||
else if(string.Compare(workingDefinition.order, "EAGLE", StringComparison.InvariantCultureIgnoreCase) == 0)
|
||||
else if(string.Compare(workingDefinition.order, "EAGLE", StringComparison.InvariantCultureIgnoreCase) ==
|
||||
0)
|
||||
{
|
||||
DicConsole.DebugWriteLine("CP/M Plugin", "Don't know how to handle EAGLE ordering, not proceeding with this definition.");
|
||||
DicConsole.DebugWriteLine("CP/M Plugin",
|
||||
"Don't know how to handle EAGLE ordering, not proceeding with this definition.");
|
||||
return Errno.NotImplemented;
|
||||
}
|
||||
else
|
||||
{
|
||||
DicConsole.DebugWriteLine("CP/M Plugin", "Unknown order type \"{0}\", not proceeding with this definition.", workingDefinition.order);
|
||||
DicConsole.DebugWriteLine("CP/M Plugin",
|
||||
"Unknown order type \"{0}\", not proceeding with this definition.",
|
||||
workingDefinition.order);
|
||||
return Errno.NotSupported;
|
||||
}
|
||||
}
|
||||
|
||||
// Deinterleave whole volume
|
||||
Dictionary<ulong, byte[]> deinterleavedSectors = new Dictionary<ulong, byte[]>();
|
||||
if(workingDefinition.sides == 1 || string.Compare(workingDefinition.order, "SIDES", StringComparison.InvariantCultureIgnoreCase) == 0)
|
||||
if(workingDefinition.sides == 1 ||
|
||||
string.Compare(workingDefinition.order, "SIDES", StringComparison.InvariantCultureIgnoreCase) == 0)
|
||||
{
|
||||
DicConsole.DebugWriteLine("CP/M Plugin", "Deinterleaving whole volume.");
|
||||
|
||||
for(int p = 0; p <= (int)(partition.End - partition.Start); p++)
|
||||
{
|
||||
byte[] readSector = device.ReadSector((ulong)((int)partition.Start + (p / sectorMask.Length) * sectorMask.Length + sectorMask[p % sectorMask.Length]));
|
||||
byte[] readSector =
|
||||
device.ReadSector((ulong)((int)partition.Start + (p / sectorMask.Length) * 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);
|
||||
}
|
||||
}
|
||||
@@ -161,18 +177,15 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
}
|
||||
}
|
||||
// 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;
|
||||
if(workingDefinition.sofs > 0)
|
||||
dirOff = workingDefinition.sofs;
|
||||
else
|
||||
dirOff = workingDefinition.ofs * workingDefinition.sectorsPerTrack;
|
||||
if(workingDefinition.sofs > 0) dirOff = workingDefinition.sofs;
|
||||
else dirOff = workingDefinition.ofs * workingDefinition.sectorsPerTrack;
|
||||
|
||||
// Read the whole directory blocks
|
||||
MemoryStream dirMs = new MemoryStream();
|
||||
@@ -182,16 +195,17 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
deinterleavedSectors.TryGetValue((ulong)(d + dirOff), out sector);
|
||||
dirMs.Write(sector, 0, sector.Length);
|
||||
}
|
||||
|
||||
byte[] directory = dirMs.ToArray();
|
||||
|
||||
if(directory == null)
|
||||
return Errno.InvalidArgument;
|
||||
if(directory == null) return Errno.InvalidArgument;
|
||||
|
||||
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>>>();
|
||||
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;
|
||||
@@ -236,17 +250,14 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
validEntry &= entry.extension[i] >= 0x20;
|
||||
}
|
||||
|
||||
if(!validEntry)
|
||||
continue;
|
||||
if(!validEntry) continue;
|
||||
|
||||
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 = string.Format("{0:X1}:{1}", user, filename);
|
||||
if(!string.IsNullOrEmpty(extension))
|
||||
filename = filename + "." + extension;
|
||||
if(user > 0) filename = string.Format("{0:X1}:{1}", user, filename);
|
||||
if(!string.IsNullOrEmpty(extension)) filename = filename + "." + extension;
|
||||
|
||||
int entryNo = ((32 * entry.extentCounter) + entry.extentCounterHigh) / (dpb.exm + 1);
|
||||
List<ushort> blocks;
|
||||
@@ -254,8 +265,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
FileEntryInfo fInfo;
|
||||
|
||||
// Do we have a stat for the file already?
|
||||
if(statCache.TryGetValue(filename, out fInfo))
|
||||
statCache.Remove(filename);
|
||||
if(statCache.TryGetValue(filename, out fInfo)) statCache.Remove(filename);
|
||||
else
|
||||
{
|
||||
fInfo = new FileEntryInfo();
|
||||
@@ -263,24 +273,17 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
}
|
||||
|
||||
// And any extent?
|
||||
if(fileExtents.TryGetValue(filename, out extentBlocks))
|
||||
fileExtents.Remove(filename);
|
||||
else
|
||||
extentBlocks = new Dictionary<int, List<ushort>>();
|
||||
if(fileExtents.TryGetValue(filename, out extentBlocks)) fileExtents.Remove(filename);
|
||||
else extentBlocks = new Dictionary<int, List<ushort>>();
|
||||
|
||||
// Do we already have this extent? Should never happen
|
||||
if(extentBlocks.TryGetValue(entryNo, out blocks))
|
||||
extentBlocks.Remove(entryNo);
|
||||
else
|
||||
blocks = new List<ushort>();
|
||||
if(extentBlocks.TryGetValue(entryNo, out blocks)) extentBlocks.Remove(entryNo);
|
||||
else blocks = new List<ushort>();
|
||||
|
||||
// Attributes
|
||||
if(hidden)
|
||||
fInfo.Attributes |= FileAttributes.Hidden;
|
||||
if(rdOnly)
|
||||
fInfo.Attributes |= FileAttributes.ReadOnly;
|
||||
if(system)
|
||||
fInfo.Attributes |= FileAttributes.System;
|
||||
if(hidden) fInfo.Attributes |= FileAttributes.Hidden;
|
||||
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
|
||||
@@ -289,8 +292,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
// we must ignore it.
|
||||
foreach(ushort blk in entry.allocations)
|
||||
{
|
||||
if(!blocks.Contains(blk) && blk != 0)
|
||||
blocks.Add(blk);
|
||||
if(!blocks.Contains(blk) && blk != 0) blocks.Add(blk);
|
||||
}
|
||||
|
||||
// Save the file
|
||||
@@ -300,8 +302,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
statCache.Add(filename, fInfo);
|
||||
|
||||
// Add the file to the directory listing
|
||||
if(!dirList.Contains(filename))
|
||||
dirList.Add(filename);
|
||||
if(!dirList.Contains(filename)) dirList.Add(filename);
|
||||
|
||||
// Count entries 3 by 3 for timestamps
|
||||
switch(dirCnt % 3)
|
||||
@@ -316,6 +317,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
file3 = filename;
|
||||
break;
|
||||
}
|
||||
|
||||
dirCnt++;
|
||||
}
|
||||
else
|
||||
@@ -345,17 +347,14 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
validEntry &= entry.extension[i] >= 0x20;
|
||||
}
|
||||
|
||||
if(!validEntry)
|
||||
continue;
|
||||
if(!validEntry) continue;
|
||||
|
||||
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 = string.Format("{0:X1}:{1}", user, filename);
|
||||
if(!string.IsNullOrEmpty(extension))
|
||||
filename = filename + "." + extension;
|
||||
if(user > 0) filename = string.Format("{0:X1}:{1}", user, filename);
|
||||
if(!string.IsNullOrEmpty(extension)) filename = filename + "." + extension;
|
||||
|
||||
int entryNo = ((32 * entry.extentCounterHigh) + entry.extentCounter) / (dpb.exm + 1);
|
||||
List<ushort> blocks;
|
||||
@@ -363,8 +362,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
FileEntryInfo fInfo;
|
||||
|
||||
// Do we have a stat for the file already?
|
||||
if(statCache.TryGetValue(filename, out fInfo))
|
||||
statCache.Remove(filename);
|
||||
if(statCache.TryGetValue(filename, out fInfo)) statCache.Remove(filename);
|
||||
else
|
||||
{
|
||||
fInfo = new FileEntryInfo();
|
||||
@@ -372,24 +370,17 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
}
|
||||
|
||||
// And any extent?
|
||||
if(fileExtents.TryGetValue(filename, out extentBlocks))
|
||||
fileExtents.Remove(filename);
|
||||
else
|
||||
extentBlocks = new Dictionary<int, List<ushort>>();
|
||||
if(fileExtents.TryGetValue(filename, out extentBlocks)) fileExtents.Remove(filename);
|
||||
else extentBlocks = new Dictionary<int, List<ushort>>();
|
||||
|
||||
// Do we already have this extent? Should never happen
|
||||
if(extentBlocks.TryGetValue(entryNo, out blocks))
|
||||
extentBlocks.Remove(entryNo);
|
||||
else
|
||||
blocks = new List<ushort>();
|
||||
if(extentBlocks.TryGetValue(entryNo, out blocks)) extentBlocks.Remove(entryNo);
|
||||
else blocks = new List<ushort>();
|
||||
|
||||
// Attributes
|
||||
if(hidden)
|
||||
fInfo.Attributes |= FileAttributes.Hidden;
|
||||
if(rdOnly)
|
||||
fInfo.Attributes |= FileAttributes.ReadOnly;
|
||||
if(system)
|
||||
fInfo.Attributes |= FileAttributes.System;
|
||||
if(hidden) fInfo.Attributes |= FileAttributes.Hidden;
|
||||
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
|
||||
@@ -398,8 +389,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
// we must ignore it.
|
||||
foreach(ushort blk in entry.allocations)
|
||||
{
|
||||
if(!blocks.Contains(blk) && blk != 0)
|
||||
blocks.Add(blk);
|
||||
if(!blocks.Contains(blk) && blk != 0) blocks.Add(blk);
|
||||
}
|
||||
|
||||
// Save the file
|
||||
@@ -409,8 +399,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
statCache.Add(filename, fInfo);
|
||||
|
||||
// Add the file to the directory listing
|
||||
if(!dirList.Contains(filename))
|
||||
dirList.Add(filename);
|
||||
if(!dirList.Contains(filename)) dirList.Add(filename);
|
||||
|
||||
// Count entries 3 by 3 for timestamps
|
||||
switch(dirCnt % 3)
|
||||
@@ -425,6 +414,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
file3 = filename;
|
||||
break;
|
||||
}
|
||||
|
||||
dirCnt++;
|
||||
}
|
||||
}
|
||||
@@ -439,23 +429,18 @@ 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 < 3; i++)
|
||||
entry.extension[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 extension = Encoding.ASCII.GetString(entry.extension).Trim();
|
||||
|
||||
// If user is != 0, append user to name to have identical filenames
|
||||
if(user > 0)
|
||||
filename = string.Format("{0:X1}:{1}", user, filename);
|
||||
if(!string.IsNullOrEmpty(extension))
|
||||
filename = filename + "." + extension;
|
||||
if(user > 0) filename = string.Format("{0:X1}:{1}", user, filename);
|
||||
if(!string.IsNullOrEmpty(extension)) filename = filename + "." + extension;
|
||||
|
||||
// Do not repeat passwords
|
||||
if(passwordCache.ContainsKey(filename))
|
||||
passwordCache.Remove(filename);
|
||||
if(passwordCache.ContainsKey(filename)) passwordCache.Remove(filename);
|
||||
|
||||
// Copy whole password entry
|
||||
byte[] tmp = new byte[32];
|
||||
@@ -475,6 +460,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
file3 = filename;
|
||||
break;
|
||||
}
|
||||
|
||||
dirCnt++;
|
||||
}
|
||||
// Volume label and password entry. Volume password is ignored.
|
||||
@@ -508,15 +494,14 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
file3 = null;
|
||||
break;
|
||||
}
|
||||
|
||||
dirCnt++;
|
||||
}
|
||||
// Timestamp entry
|
||||
else if((directory[dOff] & 0x7F) == 0x21)
|
||||
{
|
||||
// These places must be zero on CP/M 3 timestamp
|
||||
if(directory[dOff + 10] == 0x00 &&
|
||||
directory[dOff + 20] == 0x00 &&
|
||||
directory[dOff + 30] == 0x00 &&
|
||||
if(directory[dOff + 10] == 0x00 && directory[dOff + 20] == 0x00 && directory[dOff + 30] == 0x00 &&
|
||||
directory[dOff + 31] == 0x00)
|
||||
{
|
||||
DateEntry entry = new DateEntry();
|
||||
@@ -530,15 +515,11 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
// Entry contains timestamps for last 3 entries, whatever the kind they are.
|
||||
if(!string.IsNullOrEmpty(file1))
|
||||
{
|
||||
if(statCache.TryGetValue(file1, out fInfo))
|
||||
statCache.Remove(file1);
|
||||
else
|
||||
fInfo = new FileEntryInfo();
|
||||
if(statCache.TryGetValue(file1, out fInfo)) statCache.Remove(file1);
|
||||
else fInfo = new FileEntryInfo();
|
||||
|
||||
if(atime)
|
||||
fInfo.AccessTime = DateHandlers.CPMToDateTime(entry.date1);
|
||||
else
|
||||
fInfo.CreationTime = DateHandlers.CPMToDateTime(entry.date1);
|
||||
if(atime) fInfo.AccessTime = DateHandlers.CPMToDateTime(entry.date1);
|
||||
else fInfo.CreationTime = DateHandlers.CPMToDateTime(entry.date1);
|
||||
|
||||
fInfo.LastWriteTime = DateHandlers.CPMToDateTime(entry.date2);
|
||||
|
||||
@@ -547,15 +528,11 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
|
||||
if(!string.IsNullOrEmpty(file2))
|
||||
{
|
||||
if(statCache.TryGetValue(file2, out fInfo))
|
||||
statCache.Remove(file2);
|
||||
else
|
||||
fInfo = new FileEntryInfo();
|
||||
if(statCache.TryGetValue(file2, out fInfo)) statCache.Remove(file2);
|
||||
else fInfo = new FileEntryInfo();
|
||||
|
||||
if(atime)
|
||||
fInfo.AccessTime = DateHandlers.CPMToDateTime(entry.date3);
|
||||
else
|
||||
fInfo.CreationTime = DateHandlers.CPMToDateTime(entry.date3);
|
||||
if(atime) fInfo.AccessTime = DateHandlers.CPMToDateTime(entry.date3);
|
||||
else fInfo.CreationTime = DateHandlers.CPMToDateTime(entry.date3);
|
||||
|
||||
fInfo.LastWriteTime = DateHandlers.CPMToDateTime(entry.date4);
|
||||
|
||||
@@ -564,15 +541,11 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
|
||||
if(!string.IsNullOrEmpty(file3))
|
||||
{
|
||||
if(statCache.TryGetValue(file3, out fInfo))
|
||||
statCache.Remove(file3);
|
||||
else
|
||||
fInfo = new FileEntryInfo();
|
||||
if(statCache.TryGetValue(file3, out fInfo)) statCache.Remove(file3);
|
||||
else fInfo = new FileEntryInfo();
|
||||
|
||||
if(atime)
|
||||
fInfo.AccessTime = DateHandlers.CPMToDateTime(entry.date5);
|
||||
else
|
||||
fInfo.CreationTime = DateHandlers.CPMToDateTime(entry.date5);
|
||||
if(atime) fInfo.AccessTime = DateHandlers.CPMToDateTime(entry.date5);
|
||||
else fInfo.CreationTime = DateHandlers.CPMToDateTime(entry.date5);
|
||||
|
||||
fInfo.LastWriteTime = DateHandlers.CPMToDateTime(entry.date6);
|
||||
|
||||
@@ -598,10 +571,8 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
// Entry contains timestamps for last 3 entries, whatever the kind they are.
|
||||
if(!string.IsNullOrEmpty(file1))
|
||||
{
|
||||
if(statCache.TryGetValue(file1, out fInfo))
|
||||
statCache.Remove(file1);
|
||||
else
|
||||
fInfo = new FileEntryInfo();
|
||||
if(statCache.TryGetValue(file1, out fInfo)) statCache.Remove(file1);
|
||||
else fInfo = new FileEntryInfo();
|
||||
|
||||
byte[] ctime = new byte[4];
|
||||
ctime[0] = entry.create1[0];
|
||||
@@ -616,10 +587,8 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
|
||||
if(!string.IsNullOrEmpty(file2))
|
||||
{
|
||||
if(statCache.TryGetValue(file2, out fInfo))
|
||||
statCache.Remove(file2);
|
||||
else
|
||||
fInfo = new FileEntryInfo();
|
||||
if(statCache.TryGetValue(file2, out fInfo)) statCache.Remove(file2);
|
||||
else fInfo = new FileEntryInfo();
|
||||
|
||||
byte[] ctime = new byte[4];
|
||||
ctime[0] = entry.create2[0];
|
||||
@@ -634,10 +603,8 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
|
||||
if(!string.IsNullOrEmpty(file3))
|
||||
{
|
||||
if(statCache.TryGetValue(file1, out fInfo))
|
||||
statCache.Remove(file3);
|
||||
else
|
||||
fInfo = new FileEntryInfo();
|
||||
if(statCache.TryGetValue(file1, out fInfo)) statCache.Remove(file3);
|
||||
else fInfo = new FileEntryInfo();
|
||||
|
||||
byte[] ctime = new byte[4];
|
||||
ctime[0] = entry.create3[0];
|
||||
@@ -668,8 +635,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
MemoryStream fileMs = new MemoryStream();
|
||||
FileEntryInfo fInfo = new FileEntryInfo();
|
||||
|
||||
if(statCache.TryGetValue(filename, out fInfo))
|
||||
statCache.Remove(filename);
|
||||
if(statCache.TryGetValue(filename, out fInfo)) statCache.Remove(filename);
|
||||
|
||||
fInfo.Blocks = 0;
|
||||
|
||||
@@ -712,8 +678,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
{
|
||||
byte[] tmp = new byte[8];
|
||||
Array.Copy(kvp.Value, 16, tmp, 0, 8);
|
||||
for(int t = 0; t < 8; t++)
|
||||
tmp[t] ^= kvp.Value[13];
|
||||
for(int t = 0; t < 8; t++) tmp[t] ^= kvp.Value[13];
|
||||
|
||||
decodedPasswordCache.Add(kvp.Key, tmp);
|
||||
}
|
||||
@@ -746,8 +711,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
xmlFSType.FreeClusters = cpmStat.FreeBlocks;
|
||||
xmlFSType.FreeClustersSpecified = true;
|
||||
xmlFSType.Type = "CP/M filesystem";
|
||||
if(!string.IsNullOrEmpty(label))
|
||||
xmlFSType.VolumeName = label;
|
||||
if(!string.IsNullOrEmpty(label)) xmlFSType.VolumeName = label;
|
||||
|
||||
mounted = true;
|
||||
return Errno.NoError;
|
||||
@@ -759,8 +723,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
/// <param name="stat">Information about the mounted volume.</param>
|
||||
public override Errno StatFs(ref FileSystemInfo stat)
|
||||
{
|
||||
if(!mounted)
|
||||
return Errno.AccessDenied;
|
||||
if(!mounted) return Errno.AccessDenied;
|
||||
|
||||
stat = cpmStat;
|
||||
|
||||
@@ -773,7 +736,7 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
definitions = null;
|
||||
cpmFound = false;
|
||||
workingDefinition = null;
|
||||
dpb = null;
|
||||
dpb = null;
|
||||
sectorMask = null;
|
||||
label = null;
|
||||
thirdPartyTimestamps = false;
|
||||
@@ -783,5 +746,4 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
return Errno.NoError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -46,26 +46,21 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
/// <param name="buf">Buffer.</param>
|
||||
public override Errno GetXattr(string path, string xattr, ref byte[] buf)
|
||||
{
|
||||
if(!mounted)
|
||||
return Errno.AccessDenied;
|
||||
if(!mounted) return Errno.AccessDenied;
|
||||
|
||||
string[] pathElements = path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if(pathElements.Length != 1)
|
||||
return Errno.NotSupported;
|
||||
string[] pathElements = path.Split(new char[] {'/'}, StringSplitOptions.RemoveEmptyEntries);
|
||||
if(pathElements.Length != 1) return Errno.NotSupported;
|
||||
|
||||
if(!fileCache.ContainsKey(pathElements[0].ToUpperInvariant()))
|
||||
return Errno.NoSuchFile;
|
||||
if(!fileCache.ContainsKey(pathElements[0].ToUpperInvariant())) return Errno.NoSuchFile;
|
||||
|
||||
if(string.Compare(xattr, "com.caldera.cpm.password", StringComparison.InvariantCulture) == 0)
|
||||
{
|
||||
if(!passwordCache.TryGetValue(pathElements[0].ToUpperInvariant(), out buf))
|
||||
return Errno.NoError;
|
||||
if(!passwordCache.TryGetValue(pathElements[0].ToUpperInvariant(), out buf)) return Errno.NoError;
|
||||
}
|
||||
|
||||
if(string.Compare(xattr, "com.caldera.cpm.password.text", StringComparison.InvariantCulture) == 0)
|
||||
{
|
||||
if(!passwordCache.TryGetValue(pathElements[0].ToUpperInvariant(), out buf))
|
||||
return Errno.NoError;
|
||||
if(!passwordCache.TryGetValue(pathElements[0].ToUpperInvariant(), out buf)) return Errno.NoError;
|
||||
}
|
||||
|
||||
return Errno.NoSuchExtendedAttribute;
|
||||
@@ -79,19 +74,15 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
/// <param name="xattrs">List of extended attributes, alternate data streams and forks.</param>
|
||||
public override Errno ListXAttr(string path, ref List<string> xattrs)
|
||||
{
|
||||
if(!mounted)
|
||||
return Errno.AccessDenied;
|
||||
if(!mounted) return Errno.AccessDenied;
|
||||
|
||||
string[] pathElements = path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if(pathElements.Length != 1)
|
||||
return Errno.NotSupported;
|
||||
string[] pathElements = path.Split(new char[] {'/'}, StringSplitOptions.RemoveEmptyEntries);
|
||||
if(pathElements.Length != 1) return Errno.NotSupported;
|
||||
|
||||
if(!fileCache.ContainsKey(pathElements[0].ToUpperInvariant()))
|
||||
return Errno.NoSuchFile;
|
||||
if(!fileCache.ContainsKey(pathElements[0].ToUpperInvariant())) return Errno.NoSuchFile;
|
||||
|
||||
xattrs = new List<string>();
|
||||
if(passwordCache.ContainsKey(pathElements[0].ToUpperInvariant()))
|
||||
xattrs.Add("com.caldera.cpm.password");
|
||||
if(passwordCache.ContainsKey(pathElements[0].ToUpperInvariant())) xattrs.Add("com.caldera.cpm.password");
|
||||
|
||||
if(decodedPasswordCache.ContainsKey(pathElements[0].ToUpperInvariant()))
|
||||
xattrs.Add("com.caldera.cpm.password.text");
|
||||
@@ -99,5 +90,4 @@ namespace DiscImageChef.Filesystems.CPM
|
||||
return Errno.NoError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user