mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
[CBMFS] Implement full support for mounting, reading and extracting Commodore 1540/1541/1571/1581 filesystems.
This commit is contained in:
@@ -27,15 +27,27 @@
|
|||||||
// ****************************************************************************/
|
// ****************************************************************************/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Aaru.CommonTypes.AaruMetadata;
|
||||||
|
using Aaru.CommonTypes.Enums;
|
||||||
using Aaru.CommonTypes.Interfaces;
|
using Aaru.CommonTypes.Interfaces;
|
||||||
|
using Aaru.CommonTypes.Structs;
|
||||||
|
|
||||||
namespace Aaru.Filesystems;
|
namespace Aaru.Filesystems;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
/// <summary>Implements detection of the filesystem used in 8-bit Commodore microcomputers</summary>
|
/// <summary>Implements detection of the filesystem used in 8-bit Commodore microcomputers</summary>
|
||||||
public sealed partial class CBM : IFilesystem
|
public sealed partial class CBM : IReadOnlyFilesystem
|
||||||
{
|
{
|
||||||
#region IFilesystem Members
|
byte[] _bam;
|
||||||
|
Dictionary<string, CachedFile> _cache;
|
||||||
|
bool _debug;
|
||||||
|
byte[] _diskHeader;
|
||||||
|
bool _mounted;
|
||||||
|
byte[] _root;
|
||||||
|
FileSystemInfo _statfs;
|
||||||
|
|
||||||
|
#region IReadOnlyFilesystem Members
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => Localization.CBM_Name;
|
public string Name => Localization.CBM_Name;
|
||||||
@@ -46,5 +58,41 @@ public sealed partial class CBM : IFilesystem
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Author => Authors.NataliaPortillo;
|
public string Author => Authors.NataliaPortillo;
|
||||||
|
|
||||||
#endregion
|
/// <inheritdoc />
|
||||||
|
public ErrorNumber ListXAttr(string path, out List<string> xattrs)
|
||||||
|
{
|
||||||
|
xattrs = null;
|
||||||
|
|
||||||
|
return ErrorNumber.NotSupported;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ErrorNumber GetXattr(string path, string xattr, ref byte[] buf) => ErrorNumber.NotSupported;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ErrorNumber ReadLink(string path, out string dest)
|
||||||
|
{
|
||||||
|
dest = null;
|
||||||
|
|
||||||
|
return ErrorNumber.NotSupported;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public FileSystem Metadata { get; private set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IEnumerable<(string name, Type type, string description)> SupportedOptions =>
|
||||||
|
Array.Empty<(string name, Type type, string description)>();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Dictionary<string, string> Namespaces => null;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
static Dictionary<string, string> GetDefaultOptions() => new()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"debug", false.ToString()
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
100
Aaru.Filesystems/CBM/Dir.cs
Normal file
100
Aaru.Filesystems/CBM/Dir.cs
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
// /***************************************************************************
|
||||||
|
// Aaru Data Preservation Suite
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Filename : CBM.cs
|
||||||
|
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||||
|
//
|
||||||
|
// Component : Commodore file system plugin.
|
||||||
|
//
|
||||||
|
// --[ License ] --------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as
|
||||||
|
// published by the Free Software Foundation; either version 2.1 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful, but
|
||||||
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Copyright © 2011-2023 Natalia Portillo
|
||||||
|
// ****************************************************************************/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Aaru.CommonTypes.Enums;
|
||||||
|
using Aaru.CommonTypes.Interfaces;
|
||||||
|
|
||||||
|
namespace Aaru.Filesystems;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
/// <summary>Implements detection of the filesystem used in 8-bit Commodore microcomputers</summary>
|
||||||
|
public sealed partial class CBM
|
||||||
|
{
|
||||||
|
#region IReadOnlyFilesystem Members
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ErrorNumber OpenDir(string path, out IDirNode node)
|
||||||
|
{
|
||||||
|
node = null;
|
||||||
|
|
||||||
|
if(!_mounted)
|
||||||
|
return ErrorNumber.AccessDenied;
|
||||||
|
|
||||||
|
if(!string.IsNullOrEmpty(path) && string.Compare(path, "/", StringComparison.OrdinalIgnoreCase) != 0)
|
||||||
|
return ErrorNumber.NotSupported;
|
||||||
|
|
||||||
|
var contents = _cache.Keys.ToList();
|
||||||
|
|
||||||
|
contents.Sort();
|
||||||
|
|
||||||
|
node = new CbmDirNode
|
||||||
|
{
|
||||||
|
Path = path, Position = 0, Contents = contents.ToArray()
|
||||||
|
};
|
||||||
|
|
||||||
|
return ErrorNumber.NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ErrorNumber CloseDir(IDirNode node)
|
||||||
|
{
|
||||||
|
if(node is not CbmDirNode mynode)
|
||||||
|
return ErrorNumber.InvalidArgument;
|
||||||
|
|
||||||
|
mynode.Position = -1;
|
||||||
|
mynode.Contents = null;
|
||||||
|
|
||||||
|
return ErrorNumber.NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ErrorNumber ReadDir(IDirNode node, out string filename)
|
||||||
|
{
|
||||||
|
filename = null;
|
||||||
|
|
||||||
|
if(!_mounted)
|
||||||
|
return ErrorNumber.AccessDenied;
|
||||||
|
|
||||||
|
if(node is not CbmDirNode mynode)
|
||||||
|
return ErrorNumber.InvalidArgument;
|
||||||
|
|
||||||
|
if(mynode.Position < 0)
|
||||||
|
return ErrorNumber.InvalidArgument;
|
||||||
|
|
||||||
|
if(mynode.Position >= mynode.Contents.Length)
|
||||||
|
return ErrorNumber.NoError;
|
||||||
|
|
||||||
|
filename = mynode.Contents[mynode.Position++];
|
||||||
|
|
||||||
|
return ErrorNumber.NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
178
Aaru.Filesystems/CBM/File.cs
Normal file
178
Aaru.Filesystems/CBM/File.cs
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
// /***************************************************************************
|
||||||
|
// Aaru Data Preservation Suite
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Filename : CBM.cs
|
||||||
|
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||||
|
//
|
||||||
|
// Component : Commodore file system plugin.
|
||||||
|
//
|
||||||
|
// --[ License ] --------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as
|
||||||
|
// published by the Free Software Foundation; either version 2.1 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful, but
|
||||||
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Copyright © 2011-2023 Natalia Portillo
|
||||||
|
// ****************************************************************************/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Aaru.CommonTypes.Enums;
|
||||||
|
using Aaru.CommonTypes.Interfaces;
|
||||||
|
using Aaru.CommonTypes.Structs;
|
||||||
|
|
||||||
|
namespace Aaru.Filesystems;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
/// <summary>Implements detection of the filesystem used in 8-bit Commodore microcomputers</summary>
|
||||||
|
public sealed partial class CBM
|
||||||
|
{
|
||||||
|
#region IReadOnlyFilesystem Members
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ErrorNumber GetAttributes(string path, out FileAttributes attributes)
|
||||||
|
{
|
||||||
|
attributes = new FileAttributes();
|
||||||
|
|
||||||
|
if(!_mounted)
|
||||||
|
return ErrorNumber.AccessDenied;
|
||||||
|
|
||||||
|
string[] pathElements = path.Split(new[]
|
||||||
|
{
|
||||||
|
'/'
|
||||||
|
}, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
if(pathElements.Length != 1)
|
||||||
|
return ErrorNumber.NotSupported;
|
||||||
|
|
||||||
|
string filename = pathElements[0].ToUpperInvariant();
|
||||||
|
|
||||||
|
if(!_cache.TryGetValue(filename, out CachedFile file))
|
||||||
|
return ErrorNumber.NoSuchFile;
|
||||||
|
|
||||||
|
attributes = file.attributes;
|
||||||
|
|
||||||
|
return ErrorNumber.NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ErrorNumber Stat(string path, out FileEntryInfo stat)
|
||||||
|
{
|
||||||
|
stat = null;
|
||||||
|
|
||||||
|
if(!_mounted)
|
||||||
|
return ErrorNumber.AccessDenied;
|
||||||
|
|
||||||
|
string[] pathElements = path.Split(new[]
|
||||||
|
{
|
||||||
|
'/'
|
||||||
|
}, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
if(pathElements.Length != 1)
|
||||||
|
return ErrorNumber.NotSupported;
|
||||||
|
|
||||||
|
string filename = pathElements[0].ToUpperInvariant();
|
||||||
|
|
||||||
|
if(filename.Length > 14)
|
||||||
|
return ErrorNumber.NameTooLong;
|
||||||
|
|
||||||
|
if(!_cache.TryGetValue(filename, out CachedFile file))
|
||||||
|
return ErrorNumber.NoSuchFile;
|
||||||
|
|
||||||
|
stat = new FileEntryInfo
|
||||||
|
{
|
||||||
|
Attributes = file.attributes,
|
||||||
|
BlockSize = 256,
|
||||||
|
Length = (long)file.length,
|
||||||
|
Blocks = file.blocks,
|
||||||
|
Inode = file.id,
|
||||||
|
Links = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
return ErrorNumber.NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ErrorNumber OpenFile(string path, out IFileNode node)
|
||||||
|
{
|
||||||
|
node = null;
|
||||||
|
|
||||||
|
if(!_mounted)
|
||||||
|
return ErrorNumber.AccessDenied;
|
||||||
|
|
||||||
|
string[] pathElements = path.Split(new[]
|
||||||
|
{
|
||||||
|
'/'
|
||||||
|
}, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
if(pathElements.Length != 1)
|
||||||
|
return ErrorNumber.NotSupported;
|
||||||
|
|
||||||
|
string filename = pathElements[0].ToUpperInvariant();
|
||||||
|
|
||||||
|
if(filename.Length > 14)
|
||||||
|
return ErrorNumber.NameTooLong;
|
||||||
|
|
||||||
|
if(!_cache.TryGetValue(filename, out CachedFile file))
|
||||||
|
return ErrorNumber.NoSuchFile;
|
||||||
|
|
||||||
|
node = new CbmFileNode
|
||||||
|
{
|
||||||
|
Path = path, Length = (long)file.length, Offset = 0, Cache = file.data
|
||||||
|
};
|
||||||
|
|
||||||
|
return ErrorNumber.NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ErrorNumber CloseFile(IFileNode node)
|
||||||
|
{
|
||||||
|
if(!_mounted)
|
||||||
|
return ErrorNumber.AccessDenied;
|
||||||
|
|
||||||
|
if(node is not CbmFileNode mynode)
|
||||||
|
return ErrorNumber.InvalidArgument;
|
||||||
|
|
||||||
|
mynode.Cache = null;
|
||||||
|
|
||||||
|
return ErrorNumber.NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ErrorNumber ReadFile(IFileNode node, long length, byte[] buffer, out long read)
|
||||||
|
{
|
||||||
|
read = 0;
|
||||||
|
|
||||||
|
if(!_mounted)
|
||||||
|
return ErrorNumber.AccessDenied;
|
||||||
|
|
||||||
|
if(buffer is null || buffer.Length < length)
|
||||||
|
return ErrorNumber.InvalidArgument;
|
||||||
|
|
||||||
|
if(node is not CbmFileNode mynode)
|
||||||
|
return ErrorNumber.InvalidArgument;
|
||||||
|
|
||||||
|
read = length;
|
||||||
|
|
||||||
|
if(length + mynode.Offset >= mynode.Length)
|
||||||
|
read = mynode.Length - mynode.Offset;
|
||||||
|
|
||||||
|
Array.Copy(mynode.Cache, mynode.Offset, buffer, 0, read);
|
||||||
|
|
||||||
|
mynode.Offset += read;
|
||||||
|
|
||||||
|
return ErrorNumber.NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -27,6 +27,8 @@
|
|||||||
// ****************************************************************************/
|
// ****************************************************************************/
|
||||||
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using Aaru.CommonTypes.Interfaces;
|
||||||
|
using Aaru.CommonTypes.Structs;
|
||||||
|
|
||||||
namespace Aaru.Filesystems;
|
namespace Aaru.Filesystems;
|
||||||
|
|
||||||
@@ -81,6 +83,81 @@ public sealed partial class CBM
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Nested type: CachedFile
|
||||||
|
|
||||||
|
struct CachedFile
|
||||||
|
{
|
||||||
|
public byte[] data;
|
||||||
|
public ulong length;
|
||||||
|
public FileAttributes attributes;
|
||||||
|
public int blocks;
|
||||||
|
public ulong id;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Nested type: CbmDirNode
|
||||||
|
|
||||||
|
sealed class CbmDirNode : IDirNode
|
||||||
|
{
|
||||||
|
internal string[] Contents;
|
||||||
|
internal int Position;
|
||||||
|
|
||||||
|
#region IDirNode Members
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string Path { get; init; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Nested type: CbmFileNode
|
||||||
|
|
||||||
|
sealed class CbmFileNode : IFileNode
|
||||||
|
{
|
||||||
|
internal byte[] Cache;
|
||||||
|
|
||||||
|
#region IFileNode Members
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string Path { get; init; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public long Length { get; init; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public long Offset { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Nested type: DirectoryEntry
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
readonly struct DirectoryEntry
|
||||||
|
{
|
||||||
|
public readonly byte nextDirBlockTrack;
|
||||||
|
public readonly byte nextDirBlockSector;
|
||||||
|
public readonly byte fileType;
|
||||||
|
public readonly byte firstFileBlockTrack;
|
||||||
|
public readonly byte firstFileBlockSector;
|
||||||
|
/// <summary>Filename</summary>
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
|
||||||
|
public readonly byte[] name;
|
||||||
|
public readonly byte firstSideBlockTrack;
|
||||||
|
public readonly byte firstSideBlockSector;
|
||||||
|
public readonly ulong unused;
|
||||||
|
public readonly byte replacementTrack;
|
||||||
|
public readonly byte replacementSector;
|
||||||
|
public readonly short blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Nested type: Header
|
#region Nested type: Header
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
|||||||
328
Aaru.Filesystems/CBM/Super.cs
Normal file
328
Aaru.Filesystems/CBM/Super.cs
Normal file
@@ -0,0 +1,328 @@
|
|||||||
|
// /***************************************************************************
|
||||||
|
// Aaru Data Preservation Suite
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Filename : CBM.cs
|
||||||
|
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||||
|
//
|
||||||
|
// Component : Commodore file system plugin.
|
||||||
|
//
|
||||||
|
// --[ License ] --------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as
|
||||||
|
// published by the Free Software Foundation; either version 2.1 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful, but
|
||||||
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Copyright © 2011-2023 Natalia Portillo
|
||||||
|
// ****************************************************************************/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using Aaru.CommonTypes.AaruMetadata;
|
||||||
|
using Aaru.CommonTypes.Enums;
|
||||||
|
using Aaru.CommonTypes.Interfaces;
|
||||||
|
using Aaru.CommonTypes.Structs;
|
||||||
|
using Aaru.Console;
|
||||||
|
using Aaru.Helpers;
|
||||||
|
using Claunia.Encoding;
|
||||||
|
using Encoding = System.Text.Encoding;
|
||||||
|
using FileAttributes = Aaru.CommonTypes.Structs.FileAttributes;
|
||||||
|
using FileSystemInfo = Aaru.CommonTypes.Structs.FileSystemInfo;
|
||||||
|
using Partition = Aaru.CommonTypes.Partition;
|
||||||
|
|
||||||
|
namespace Aaru.Filesystems;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
/// <summary>Implements detection of the filesystem used in 8-bit Commodore microcomputers</summary>
|
||||||
|
public sealed partial class CBM
|
||||||
|
{
|
||||||
|
#region IReadOnlyFilesystem Members
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ErrorNumber Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding,
|
||||||
|
Dictionary<string, string> options, string @namespace)
|
||||||
|
{
|
||||||
|
if(partition.Start > 0)
|
||||||
|
return ErrorNumber.InvalidArgument;
|
||||||
|
|
||||||
|
if(imagePlugin.Info.SectorSize != 256)
|
||||||
|
return ErrorNumber.InvalidArgument;
|
||||||
|
|
||||||
|
if(imagePlugin.Info.Sectors != 683 &&
|
||||||
|
imagePlugin.Info.Sectors != 768 &&
|
||||||
|
imagePlugin.Info.Sectors != 1366 &&
|
||||||
|
imagePlugin.Info.Sectors != 3200)
|
||||||
|
return ErrorNumber.InvalidArgument;
|
||||||
|
|
||||||
|
options ??= GetDefaultOptions();
|
||||||
|
|
||||||
|
if(options.TryGetValue("debug", out string debugString))
|
||||||
|
bool.TryParse(debugString, out _debug);
|
||||||
|
|
||||||
|
encoding = new PETSCII();
|
||||||
|
byte[] sector;
|
||||||
|
ulong rootLba;
|
||||||
|
string volumeName = null;
|
||||||
|
Metadata = new FileSystem
|
||||||
|
{
|
||||||
|
Type = FS_TYPE, Clusters = imagePlugin.Info.Sectors, ClusterSize = 256
|
||||||
|
};
|
||||||
|
uint serial;
|
||||||
|
// Commodore 1581
|
||||||
|
if(imagePlugin.Info.Sectors == 3200)
|
||||||
|
{
|
||||||
|
ErrorNumber errno = imagePlugin.ReadSector(1560, out _diskHeader);
|
||||||
|
|
||||||
|
if(errno != ErrorNumber.NoError)
|
||||||
|
return errno;
|
||||||
|
|
||||||
|
Header cbmHdr = Marshal.ByteArrayToStructureLittleEndian<Header>(_diskHeader);
|
||||||
|
|
||||||
|
if(cbmHdr.diskDosVersion != 0x44 || cbmHdr is not { dosVersion: 0x33, diskVersion: 0x44 })
|
||||||
|
return ErrorNumber.InvalidArgument;
|
||||||
|
|
||||||
|
_bam = new byte[512];
|
||||||
|
// Got to first BAM sector
|
||||||
|
errno = imagePlugin.ReadSector(1561, out sector);
|
||||||
|
|
||||||
|
if(errno != ErrorNumber.NoError)
|
||||||
|
return errno;
|
||||||
|
|
||||||
|
Array.Copy(sector, 0, _bam, 0, 256);
|
||||||
|
|
||||||
|
if(_bam[0] > 0)
|
||||||
|
{
|
||||||
|
// Got to next (and last) BAM sector
|
||||||
|
errno = imagePlugin.ReadSector((ulong)((_bam[0] - 1) * 40), out sector);
|
||||||
|
|
||||||
|
if(errno != ErrorNumber.NoError)
|
||||||
|
return errno;
|
||||||
|
|
||||||
|
Array.Copy(sector, 0, _bam, 256, 256);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cbmHdr.directoryTrack == 0)
|
||||||
|
return ErrorNumber.InvalidArgument;
|
||||||
|
|
||||||
|
rootLba = (ulong)((cbmHdr.directoryTrack - 1) * 40 + cbmHdr.directorySector - 1);
|
||||||
|
serial = cbmHdr.diskId;
|
||||||
|
Metadata.VolumeName = StringHandlers.CToString(cbmHdr.name, encoding);
|
||||||
|
Metadata.VolumeSerial = $"{cbmHdr.diskId}";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ErrorNumber errno = imagePlugin.ReadSector(357, out _bam);
|
||||||
|
|
||||||
|
if(errno != ErrorNumber.NoError)
|
||||||
|
return errno;
|
||||||
|
|
||||||
|
BAM cbmBam = Marshal.ByteArrayToStructureLittleEndian<BAM>(_bam);
|
||||||
|
|
||||||
|
if(cbmBam is not ({ dosVersion: 0x41, doubleSided : 0x00 or 0x80 }
|
||||||
|
and { unused1 : 0x00, directoryTrack: 0x12 }))
|
||||||
|
return ErrorNumber.InvalidArgument;
|
||||||
|
|
||||||
|
rootLba = (ulong)((cbmBam.directoryTrack - 1) * 40 + cbmBam.directorySector - 1);
|
||||||
|
serial = cbmBam.diskId;
|
||||||
|
|
||||||
|
Metadata.VolumeName = StringHandlers.CToString(cbmBam.name, encoding);
|
||||||
|
Metadata.VolumeSerial = $"{cbmBam.diskId}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rootLba >= imagePlugin.Info.Sectors)
|
||||||
|
return ErrorNumber.IllegalSeek;
|
||||||
|
|
||||||
|
ulong nextLba = rootLba;
|
||||||
|
var rootMs = new MemoryStream();
|
||||||
|
var relativeFileWarningShown = false;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ErrorNumber errno = imagePlugin.ReadSector(nextLba, out sector);
|
||||||
|
|
||||||
|
if(errno != ErrorNumber.NoError)
|
||||||
|
return errno;
|
||||||
|
|
||||||
|
rootMs.Write(sector, 0, 256);
|
||||||
|
|
||||||
|
if(sector[0] == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
nextLba = (ulong)((sector[0] - 1) * 40 + sector[1] - 1);
|
||||||
|
} while(nextLba > 0);
|
||||||
|
|
||||||
|
_root = rootMs.ToArray();
|
||||||
|
|
||||||
|
_statfs = new FileSystemInfo
|
||||||
|
{
|
||||||
|
Blocks = imagePlugin.Info.Sectors,
|
||||||
|
FilenameLength = 14,
|
||||||
|
Files = 0,
|
||||||
|
FreeBlocks =
|
||||||
|
imagePlugin.Info.Sectors -
|
||||||
|
(ulong)(_diskHeader?.Length ?? 0 / 256 - _bam.Length / 256 - _root.Length / 256),
|
||||||
|
FreeFiles = (ulong)(_root.Length / 32),
|
||||||
|
Id = new FileSystemId
|
||||||
|
{
|
||||||
|
Serial32 = serial, IsInt = true
|
||||||
|
},
|
||||||
|
PluginId = Id,
|
||||||
|
Type = "CBMFS"
|
||||||
|
};
|
||||||
|
|
||||||
|
// As this filesystem comes in (by nowadays standards) very small sizes, we can cache all files
|
||||||
|
_cache = new Dictionary<string, CachedFile>();
|
||||||
|
var offset = 0;
|
||||||
|
ulong fileId = 0;
|
||||||
|
|
||||||
|
if(_debug)
|
||||||
|
{
|
||||||
|
// Root
|
||||||
|
_cache.Add("$", new CachedFile
|
||||||
|
{
|
||||||
|
attributes = FileAttributes.Directory | FileAttributes.Hidden | FileAttributes.System,
|
||||||
|
length = (ulong)_root.Length,
|
||||||
|
data = _root,
|
||||||
|
blocks = _root.Length / 256,
|
||||||
|
id = fileId++
|
||||||
|
});
|
||||||
|
|
||||||
|
// BAM
|
||||||
|
_cache.Add("$BAM", new CachedFile
|
||||||
|
{
|
||||||
|
attributes = FileAttributes.File | FileAttributes.Hidden | FileAttributes.System,
|
||||||
|
length = (ulong)_bam.Length,
|
||||||
|
data = _bam,
|
||||||
|
blocks = _bam.Length / 256,
|
||||||
|
id = fileId++
|
||||||
|
});
|
||||||
|
|
||||||
|
_statfs.Files += 2;
|
||||||
|
|
||||||
|
// 1581 disk header
|
||||||
|
if(_diskHeader != null)
|
||||||
|
{
|
||||||
|
_cache.Add("$DISK_HEADER", new CachedFile
|
||||||
|
{
|
||||||
|
attributes = FileAttributes.File | FileAttributes.Hidden | FileAttributes.System,
|
||||||
|
length = (ulong)_diskHeader.Length,
|
||||||
|
data = _diskHeader,
|
||||||
|
blocks = _diskHeader.Length / 256,
|
||||||
|
id = fileId++
|
||||||
|
});
|
||||||
|
_statfs.Files++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while(offset < _root.Length)
|
||||||
|
{
|
||||||
|
DirectoryEntry dirEntry = Marshal.ByteArrayToStructureLittleEndian<DirectoryEntry>(_root, offset, 32);
|
||||||
|
|
||||||
|
if(dirEntry.fileType == 0)
|
||||||
|
{
|
||||||
|
offset += 32;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_statfs.Files++;
|
||||||
|
_statfs.FreeFiles--;
|
||||||
|
|
||||||
|
for(var i = 0; i < dirEntry.name.Length; i++)
|
||||||
|
{
|
||||||
|
if(dirEntry.name[i] == 0xA0)
|
||||||
|
dirEntry.name[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
string name = StringHandlers.CToString(dirEntry.name, encoding);
|
||||||
|
|
||||||
|
if((dirEntry.fileType & 0x07) == 4 && !relativeFileWarningShown)
|
||||||
|
{
|
||||||
|
AaruConsole.WriteLine(Localization.CBM_Mount_REL_file_warning);
|
||||||
|
relativeFileWarningShown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = new MemoryStream();
|
||||||
|
|
||||||
|
nextLba = (ulong)((dirEntry.firstFileBlockTrack - 1) * 40 + dirEntry.firstFileBlockSector - 1);
|
||||||
|
|
||||||
|
_statfs.FreeBlocks -= (ulong)dirEntry.blocks;
|
||||||
|
|
||||||
|
while(dirEntry.blocks > 0)
|
||||||
|
{
|
||||||
|
if(dirEntry.firstFileBlockTrack == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ErrorNumber errno = imagePlugin.ReadSector(nextLba, out sector);
|
||||||
|
if(errno != ErrorNumber.NoError)
|
||||||
|
break;
|
||||||
|
|
||||||
|
data.Write(sector, 2, 254);
|
||||||
|
|
||||||
|
if(sector[0] == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
nextLba = (ulong)((sector[0] - 1) * 40 + sector[1] - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileAttributes attributes = FileAttributes.File;
|
||||||
|
|
||||||
|
if((dirEntry.fileType & 0x80) != 0x80)
|
||||||
|
attributes |= FileAttributes.Open;
|
||||||
|
if((dirEntry.fileType & 0x40) > 0)
|
||||||
|
attributes |= FileAttributes.ReadOnly;
|
||||||
|
if((dirEntry.fileType & 7) == 2)
|
||||||
|
attributes |= FileAttributes.Executable;
|
||||||
|
|
||||||
|
_cache.Add(name, new CachedFile
|
||||||
|
{
|
||||||
|
attributes = attributes,
|
||||||
|
length = (ulong)data.Length,
|
||||||
|
data = data.ToArray(),
|
||||||
|
blocks = dirEntry.blocks,
|
||||||
|
id = fileId++
|
||||||
|
});
|
||||||
|
|
||||||
|
offset += 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
_mounted = true;
|
||||||
|
return ErrorNumber.NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ErrorNumber Unmount()
|
||||||
|
{
|
||||||
|
if(!_mounted)
|
||||||
|
return ErrorNumber.AccessDenied;
|
||||||
|
|
||||||
|
_mounted = false;
|
||||||
|
|
||||||
|
return ErrorNumber.NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ErrorNumber StatFs(out FileSystemInfo stat)
|
||||||
|
{
|
||||||
|
stat = null;
|
||||||
|
|
||||||
|
if(!_mounted)
|
||||||
|
return ErrorNumber.AccessDenied;
|
||||||
|
|
||||||
|
stat = _statfs.ShallowCopy();
|
||||||
|
|
||||||
|
return ErrorNumber.NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -2517,6 +2517,15 @@ namespace Aaru.Filesystems {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to This disk contains a relative file. These have not been fully tested, please open a bug report and include this disk image..
|
||||||
|
/// </summary>
|
||||||
|
internal static string CBM_Mount_REL_file_warning {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("CBM_Mount_REL_file_warning", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Commodore file system.
|
/// Looks up a localized string similar to Commodore file system.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -3680,4 +3680,7 @@
|
|||||||
<data name="_512_bytes_per_block" xml:space="preserve">
|
<data name="_512_bytes_per_block" xml:space="preserve">
|
||||||
<value>512 bytes por bloque</value>
|
<value>512 bytes por bloque</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="CBM_Mount_REL_file_warning" xml:space="preserve">
|
||||||
|
<value>Este disco contiene un fichero relativo. Estos no han sido totalmente probados, por favor abra un reporte de errores y adjunte esta imagen de disco.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -3689,4 +3689,7 @@
|
|||||||
<data name="_0_equals_unknown_data_type_1" xml:space="preserve">
|
<data name="_0_equals_unknown_data_type_1" xml:space="preserve">
|
||||||
<value>{0} = Unknown data type {1}</value>
|
<value>{0} = Unknown data type {1}</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="CBM_Mount_REL_file_warning" xml:space="preserve">
|
||||||
|
<value>This disk contains a relative file. These have not been fully tested, please open a bug report and include this disk image.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -127,6 +127,7 @@ Supported disk image formats (read and write)
|
|||||||
|
|
||||||
Supported disk image formats (read-only)
|
Supported disk image formats (read-only)
|
||||||
========================================
|
========================================
|
||||||
|
|
||||||
* Symbian Installation File (.SIS)
|
* Symbian Installation File (.SIS)
|
||||||
|
|
||||||
Supported partitioning schemes
|
Supported partitioning schemes
|
||||||
@@ -165,6 +166,7 @@ Supported file systems for read-only operations
|
|||||||
* Apple Lisa file system
|
* Apple Lisa file system
|
||||||
* Apple Macintosh File System (MFS)
|
* Apple Macintosh File System (MFS)
|
||||||
* CD-i file system
|
* CD-i file system
|
||||||
|
* Commodore 1540/1541/1571/1581 filesystems
|
||||||
* CP/M file system
|
* CP/M file system
|
||||||
* High Sierra Format
|
* High Sierra Format
|
||||||
* ISO9660, including Apple, Amiga, Rock Ridge, Joliet and Romeo extensions
|
* ISO9660, including Apple, Amiga, Rock Ridge, Joliet and Romeo extensions
|
||||||
@@ -192,7 +194,6 @@ Supported file systems for identification and information only
|
|||||||
* BSD Unix File System 2 (UFS2)
|
* BSD Unix File System 2 (UFS2)
|
||||||
* B-tree file system (btrfs)
|
* B-tree file system (btrfs)
|
||||||
* Coherent UNIX file system
|
* Coherent UNIX file system
|
||||||
* Commodore 1540/1541/1571/1581 filesystems
|
|
||||||
* Cram file system
|
* Cram file system
|
||||||
* DEC Files-11 (only checked with On Disk Structure 2, ODS-2)
|
* DEC Files-11 (only checked with On Disk Structure 2, ODS-2)
|
||||||
* DEC RT-11 file system
|
* DEC RT-11 file system
|
||||||
|
|||||||
Reference in New Issue
Block a user