2017-05-19 20:28:49 +01:00
// /***************************************************************************
2016-07-28 18:13:49 +01:00
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : AmigaDOS.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Amiga Fast File System plugin.
//
// --[ Description ] ----------------------------------------------------------
//
// Identifies the Amiga Fast File System and shows information.
//
// --[ 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/>.
//
// ----------------------------------------------------------------------------
2017-05-19 20:28:49 +01:00
// Copyright © 2011-2017 Natalia Portillo
2016-07-28 18:13:49 +01:00
// ****************************************************************************/
2015-04-20 06:26:33 +01:00
using System ;
using System.Text ;
using System.Collections.Generic ;
2015-10-18 22:04:03 +01:00
using DiscImageChef.Console ;
2017-07-19 05:21:25 +01:00
using System.Runtime.InteropServices ;
2015-04-20 06:26:33 +01:00
2016-07-21 16:15:39 +01:00
namespace DiscImageChef.Filesystems
2015-04-20 06:26:33 +01:00
{
2017-07-01 03:26:08 +01:00
public class AmigaDOSPlugin : Filesystem
2015-04-20 06:26:33 +01:00
{
2015-10-05 20:04:05 +01:00
public AmigaDOSPlugin ( )
2015-04-20 06:26:33 +01:00
{
Name = "Amiga DOS filesystem" ;
PluginUUID = new Guid ( "3c882400-208c-427d-a086-9119852a1bc7" ) ;
2017-06-06 21:23:20 +01:00
CurrentEncoding = Encoding . GetEncoding ( "iso-8859-1" ) ;
2016-07-27 13:32:45 +01:00
}
2017-06-06 21:23:20 +01:00
public AmigaDOSPlugin ( ImagePlugins . ImagePlugin imagePlugin , ulong partitionStart , ulong partitionEnd , Encoding encoding )
2016-07-27 13:32:45 +01:00
{
Name = "Amiga DOS filesystem" ;
PluginUUID = new Guid ( "3c882400-208c-427d-a086-9119852a1bc7" ) ;
2017-06-06 21:23:20 +01:00
if ( encoding = = null )
CurrentEncoding = Encoding . GetEncoding ( "iso-8859-1" ) ;
2015-04-20 06:26:33 +01:00
}
/// <summary>
/// Boot block, first 2 sectors
/// </summary>
2017-07-19 05:21:25 +01:00
[StructLayout(LayoutKind.Sequential, Pack = 1)]
2015-04-20 06:26:33 +01:00
struct BootBlock
{
/// <summary>
/// Offset 0x00, "DOSx" disk type
/// </summary>
2016-07-28 22:25:26 +01:00
public uint diskType ;
2015-04-20 06:26:33 +01:00
/// <summary>
/// Offset 0x04, Checksum
/// </summary>
2016-07-28 22:25:26 +01:00
public uint checksum ;
2015-04-20 06:26:33 +01:00
/// <summary>
/// Offset 0x08, Pointer to root block, mostly invalid
/// </summary>
2016-07-28 22:25:26 +01:00
public uint root_ptr ;
2015-04-20 06:26:33 +01:00
/// <summary>
2017-07-19 05:21:25 +01:00
/// Offset 0x0C, Boot code, til completion. Size is intentionally incorrect to allow marshaling to work.
2015-04-20 06:26:33 +01:00
/// </summary>
2017-07-19 05:21:25 +01:00
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
2015-04-20 06:26:33 +01:00
public byte [ ] bootCode ;
}
2017-07-19 05:21:25 +01:00
[StructLayout(LayoutKind.Sequential, Pack = 1)]
2015-04-20 06:26:33 +01:00
struct RootBlock
{
/// <summary>
/// Offset 0x00, block type, value = T_HEADER (2)
/// </summary>
2016-07-28 22:25:26 +01:00
public uint type ;
2015-04-20 06:26:33 +01:00
/// <summary>
/// Offset 0x04, unused
/// </summary>
2016-07-28 22:25:26 +01:00
public uint headerKey ;
2015-04-20 06:26:33 +01:00
/// <summary>
/// Offset 0x08, unused
/// </summary>
2016-07-28 22:25:26 +01:00
public uint highSeq ;
2015-04-20 06:26:33 +01:00
/// <summary>
/// Offset 0x0C, longs used by hash table
/// </summary>
2016-07-28 22:25:26 +01:00
public uint hashTableSize ;
2015-04-20 06:26:33 +01:00
/// <summary>
/// Offset 0x10, unused
/// </summary>
2016-07-28 22:25:26 +01:00
public uint firstData ;
2015-04-20 06:26:33 +01:00
/// <summary>
/// Offset 0x14, Rootblock checksum
/// </summary>
2016-07-28 22:25:26 +01:00
public uint checksum ;
2015-04-20 06:26:33 +01:00
/// <summary>
2017-07-19 05:21:25 +01:00
/// Offset 0x18, Hashtable, size = (block size / 4) - 56 or size = hashTableSize.
/// Size intentionally bad to allow marshalling to work.
2015-04-20 06:26:33 +01:00
/// </summary>
2017-07-19 05:21:25 +01:00
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
2016-07-28 22:25:26 +01:00
public uint [ ] hashTable ;
2015-04-20 06:26:33 +01:00
/// <summary>
/// Offset 0x18+hashTableSize*4+0, bitmap flag, 0xFFFFFFFF if valid
/// </summary>
2016-07-28 22:25:26 +01:00
public uint bitmapFlag ;
2015-04-20 06:26:33 +01:00
/// <summary>
/// Offset 0x18+hashTableSize*4+4, bitmap pages, 25 entries
/// </summary>
2017-07-19 05:21:25 +01:00
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 25)]
2016-07-28 22:25:26 +01:00
public uint [ ] bitmapPages ;
2015-04-20 06:26:33 +01:00
/// <summary>
/// Offset 0x18+hashTableSize*4+104, pointer to bitmap extension block
/// </summary>
2016-07-28 22:25:26 +01:00
public uint bitmapExtensionBlock ;
2015-04-20 06:26:33 +01:00
/// <summary>
/// Offset 0x18+hashTableSize*4+108, last root alteration days since 1978/01/01
/// </summary>
2016-07-28 22:25:26 +01:00
public uint rDays ;
2015-04-20 06:26:33 +01:00
/// <summary>
/// Offset 0x18+hashTableSize*4+112, last root alteration minutes past midnight
/// </summary>
2016-07-28 22:25:26 +01:00
public uint rMins ;
2015-04-20 06:26:33 +01:00
/// <summary>
/// Offset 0x18+hashTableSize*4+116, last root alteration ticks (1/50 secs)
/// </summary>
2016-07-28 22:25:26 +01:00
public uint rTicks ;
2015-04-20 06:26:33 +01:00
/// <summary>
2017-07-19 05:21:25 +01:00
/// Offset 0x18+hashTableSize*4+120, disk name, pascal string, 31 bytes
2015-04-20 06:26:33 +01:00
/// </summary>
2017-07-19 05:21:25 +01:00
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 31)]
public byte [ ] diskName ;
/// <summary>
/// Offset 0x18+hashTableSize*4+151, unused
/// </summary>
public byte padding ;
2015-04-20 06:26:33 +01:00
/// <summary>
/// Offset 0x18+hashTableSize*4+152, unused
/// </summary>
2016-07-28 22:25:26 +01:00
public uint reserved1 ;
2015-04-20 06:26:33 +01:00
/// <summary>
/// Offset 0x18+hashTableSize*4+156, unused
/// </summary>
2016-07-28 22:25:26 +01:00
public uint reserved2 ;
2015-04-20 06:26:33 +01:00
/// <summary>
/// Offset 0x18+hashTableSize*4+160, last disk alteration days since 1978/01/01
/// </summary>
2016-07-28 22:25:26 +01:00
public uint vDays ;
2015-04-20 06:26:33 +01:00
/// <summary>
/// Offset 0x18+hashTableSize*4+164, last disk alteration minutes past midnight
/// </summary>
2016-07-28 22:25:26 +01:00
public uint vMins ;
2015-04-20 06:26:33 +01:00
/// <summary>
/// Offset 0x18+hashTableSize*4+168, last disk alteration ticks (1/50 secs)
/// </summary>
2016-07-28 22:25:26 +01:00
public uint vTicks ;
2015-04-20 06:26:33 +01:00
/// <summary>
/// Offset 0x18+hashTableSize*4+172, filesystem creation days since 1978/01/01
/// </summary>
2016-07-28 22:25:26 +01:00
public uint cDays ;
2015-04-20 06:26:33 +01:00
/// <summary>
/// Offset 0x18+hashTableSize*4+176, filesystem creation minutes since 1978/01/01
/// </summary>
2016-07-28 22:25:26 +01:00
public uint cMins ;
2015-04-20 06:26:33 +01:00
/// <summary>
/// Offset 0x18+hashTableSize*4+180, filesystem creation ticks since 1978/01/01
/// </summary>
2016-07-28 22:25:26 +01:00
public uint cTicks ;
2015-04-20 06:26:33 +01:00
/// <summary>
/// Offset 0x18+hashTableSize*4+184, unused
/// </summary>
2016-07-28 22:25:26 +01:00
public uint nextHash ;
2015-04-20 06:26:33 +01:00
/// <summary>
/// Offset 0x18+hashTableSize*4+188, unused
/// </summary>
2016-07-28 22:25:26 +01:00
public uint parentDir ;
2015-04-20 06:26:33 +01:00
/// <summary>
/// Offset 0x18+hashTableSize*4+192, first directory cache block
/// </summary>
2016-07-28 22:25:26 +01:00
public uint extension ;
2015-04-20 06:26:33 +01:00
/// <summary>
/// Offset 0x18+hashTableSize*4+196, block secondary type = ST_ROOT (1)
/// </summary>
2016-07-28 22:25:26 +01:00
public uint sec_type ;
2015-04-20 06:26:33 +01:00
}
2017-07-19 05:21:25 +01:00
public const uint FFS_Mask = 0x444F5300 ;
public const uint MuFS_Mask = 0x6D754600 ;
public const uint TypeHeader = 2 ;
public const uint SubTypeRoot = 1 ;
2015-04-20 06:26:33 +01:00
public override bool Identify ( ImagePlugins . ImagePlugin imagePlugin , ulong partitionStart , ulong partitionEnd )
{
2016-08-08 18:44:08 +01:00
if ( partitionStart > = partitionEnd )
2015-04-20 06:26:33 +01:00
return false ;
BigEndianBitConverter . IsLittleEndian = BitConverter . IsLittleEndian ;
2017-07-19 05:21:25 +01:00
// Boot block is unless defined otherwise, 2 blocks
// Funny, you may need boot block to find root block if it's not in standard place just to know size of
// block size and then read the whole boot block.
// However while you can set a block size different from the sector size on formatting, the bootblock block
// size for floppies is the sector size, and for RDB is usually is the hard disk sector size,
// so this is not entirely wrong...
byte [ ] sector = imagePlugin . ReadSectors ( 0 + partitionStart , 2 ) ;
BootBlock bblk = BigEndianMarshal . ByteArrayToStructureBigEndian < BootBlock > ( sector ) ;
// Not FFS or MuFS?
if ( ( bblk . diskType & FFS_Mask ) ! = FFS_Mask & &
( bblk . diskType & MuFS_Mask ) ! = MuFS_Mask )
return false ;
2015-04-20 06:26:33 +01:00
2017-07-19 05:21:25 +01:00
// Clear checksum on sector
sector [ 4 ] = sector [ 5 ] = sector [ 6 ] = sector [ 7 ] = 0 ;
uint bsum = AmigaBootChecksum ( sector ) ;
2015-04-20 06:26:33 +01:00
2017-07-19 05:21:25 +01:00
DicConsole . DebugWriteLine ( "AmigaDOS plugin" , "bblk.checksum = 0x{0:X8}" , bblk . checksum ) ;
DicConsole . DebugWriteLine ( "AmigaDOS plugin" , "bsum = 0x{0:X8}" , bsum ) ;
2015-04-20 06:26:33 +01:00
2017-07-19 05:21:25 +01:00
ulong b_root_ptr = 0 ;
2015-04-20 06:26:33 +01:00
2017-07-19 05:21:25 +01:00
// If bootblock is correct, let's take its rootblock pointer
if ( bsum = = bblk . checksum )
2016-09-13 14:52:31 +01:00
{
2017-07-19 05:21:25 +01:00
b_root_ptr = bblk . root_ptr + partitionStart ;
DicConsole . DebugWriteLine ( "AmigaDOS plugin" , "Bootblock points to {0} as Rootblock" , b_root_ptr ) ;
2016-09-13 14:52:31 +01:00
}
2015-04-20 06:26:33 +01:00
2017-07-19 05:21:25 +01:00
ulong [ ] root_ptrs = { b_root_ptr + partitionStart , ( ( partitionEnd - partitionStart ) + 1 ) / 2 + partitionStart - 2 ,
( ( partitionEnd - partitionStart ) + 1 ) / 2 + partitionStart - 1 , ( ( partitionEnd - partitionStart ) + 1 ) / 2 + partitionStart } ;
RootBlock rblk = new RootBlock ( ) ;
// So to handle even number of sectors
foreach ( ulong root_ptr in root_ptrs )
{
if ( root_ptr > = partitionEnd | | root_ptr < partitionStart )
continue ;
DicConsole . DebugWriteLine ( "AmigaDOS plugin" , "Searching for Rootblock in sector {0}" , root_ptr ) ;
sector = imagePlugin . ReadSector ( root_ptr ) ;
rblk . type = BigEndianBitConverter . ToUInt32 ( sector , 0x00 ) ;
DicConsole . DebugWriteLine ( "AmigaDOS plugin" , "rblk.type = {0}" , rblk . type ) ;
if ( rblk . type ! = TypeHeader )
continue ;
rblk . hashTableSize = BigEndianBitConverter . ToUInt32 ( sector , 0x0C ) ;
DicConsole . DebugWriteLine ( "AmigaDOS plugin" , "rblk.hashTableSize = {0}" , rblk . hashTableSize ) ;
2015-04-20 06:26:33 +01:00
2017-07-19 05:21:25 +01:00
uint blockSize = ( rblk . hashTableSize + 56 ) * 4 ;
uint sectorsPerBlock = ( uint ) ( blockSize / sector . Length ) ;
2015-04-20 06:26:33 +01:00
2017-07-19 05:21:25 +01:00
DicConsole . DebugWriteLine ( "AmigaDOS plugin" , "blockSize = {0}" , blockSize ) ;
DicConsole . DebugWriteLine ( "AmigaDOS plugin" , "sectorsPerBlock = {0}" , sectorsPerBlock ) ;
2015-04-20 06:26:33 +01:00
2017-07-19 05:21:25 +01:00
if ( blockSize % sector . Length > 0 )
sectorsPerBlock + + ;
2016-09-13 14:52:31 +01:00
2017-07-19 05:21:25 +01:00
if ( root_ptr + sectorsPerBlock > = partitionEnd )
continue ;
2016-09-13 14:52:31 +01:00
2017-07-19 05:21:25 +01:00
sector = imagePlugin . ReadSectors ( root_ptr , sectorsPerBlock ) ;
2015-04-20 06:26:33 +01:00
2017-07-19 05:21:25 +01:00
// Clear checksum on sector
rblk . checksum = BigEndianBitConverter . ToUInt32 ( sector , 20 ) ;
sector [ 20 ] = sector [ 21 ] = sector [ 22 ] = sector [ 23 ] = 0 ;
uint rsum = AmigaChecksum ( sector ) ;
2015-04-20 06:26:33 +01:00
2017-07-19 05:21:25 +01:00
DicConsole . DebugWriteLine ( "AmigaDOS plugin" , "rblk.checksum = 0x{0:X8}" , rblk . checksum ) ;
DicConsole . DebugWriteLine ( "AmigaDOS plugin" , "rsum = 0x{0:X8}" , rsum ) ;
rblk . sec_type = BigEndianBitConverter . ToUInt32 ( sector , sector . Length - 4 ) ;
DicConsole . DebugWriteLine ( "AmigaDOS plugin" , "rblk.sec_type = {0}" , rblk . sec_type ) ;
if ( rblk . sec_type = = SubTypeRoot & & rblk . checksum = = rsum )
return true ;
}
return false ;
2015-04-20 06:26:33 +01:00
}
public override void GetInformation ( ImagePlugins . ImagePlugin imagePlugin , ulong partitionStart , ulong partitionEnd , out string information )
{
StringBuilder sbInformation = new StringBuilder ( ) ;
2017-07-19 05:21:25 +01:00
xmlFSType = new Schemas . FileSystemType ( ) ;
information = null ;
BigEndianBitConverter . IsLittleEndian = BitConverter . IsLittleEndian ;
2015-04-20 06:26:33 +01:00
byte [ ] BootBlockSectors = imagePlugin . ReadSectors ( 0 + partitionStart , 2 ) ;
2016-09-13 14:52:31 +01:00
2017-07-19 05:21:25 +01:00
BootBlock bootBlk = BigEndianMarshal . ByteArrayToStructureBigEndian < BootBlock > ( BootBlockSectors ) ;
bootBlk . bootCode = new byte [ BootBlockSectors . Length - 12 ] ;
Array . Copy ( BootBlockSectors , 12 , bootBlk . bootCode , 0 , bootBlk . bootCode . Length ) ;
BootBlockSectors [ 4 ] = BootBlockSectors [ 5 ] = BootBlockSectors [ 6 ] = BootBlockSectors [ 7 ] = 0 ;
uint bsum = AmigaBootChecksum ( BootBlockSectors ) ;
2016-09-13 14:52:31 +01:00
2017-07-19 05:21:25 +01:00
ulong b_root_ptr = 0 ;
2016-09-13 14:52:31 +01:00
2017-07-19 05:21:25 +01:00
// If bootblock is correct, let's take its rootblock pointer
if ( bsum = = bootBlk . checksum )
{
b_root_ptr = bootBlk . root_ptr + partitionStart ;
DicConsole . DebugWriteLine ( "AmigaDOS plugin" , "Bootblock points to {0} as Rootblock" , b_root_ptr ) ;
2016-09-13 14:52:31 +01:00
}
2017-07-19 05:21:25 +01:00
ulong [ ] root_ptrs = { b_root_ptr + partitionStart , ( ( partitionEnd - partitionStart ) + 1 ) / 2 + partitionStart - 2 ,
( ( partitionEnd - partitionStart ) + 1 ) / 2 + partitionStart - 1 , ( ( partitionEnd - partitionStart ) + 1 ) / 2 + partitionStart } ;
2016-09-13 14:52:31 +01:00
2017-07-19 05:21:25 +01:00
RootBlock rootBlk = new RootBlock ( ) ;
byte [ ] RootBlockSector = null ;
2016-09-13 14:52:31 +01:00
2017-07-19 05:21:25 +01:00
bool root_found = false ;
uint blockSize = 0 ;
2016-09-13 14:52:31 +01:00
2017-07-19 05:21:25 +01:00
// So to handle even number of sectors
foreach ( ulong root_ptr in root_ptrs )
{
if ( root_ptr > = partitionEnd | | root_ptr < partitionStart )
continue ;
2016-09-13 14:52:31 +01:00
2017-07-19 05:21:25 +01:00
DicConsole . DebugWriteLine ( "AmigaDOS plugin" , "Searching for Rootblock in sector {0}" , root_ptr ) ;
2015-04-20 06:26:33 +01:00
2017-07-19 05:21:25 +01:00
RootBlockSector = imagePlugin . ReadSector ( root_ptr ) ;
2015-04-20 06:26:33 +01:00
2017-07-19 05:21:25 +01:00
rootBlk . type = BigEndianBitConverter . ToUInt32 ( RootBlockSector , 0x00 ) ;
DicConsole . DebugWriteLine ( "AmigaDOS plugin" , "rootBlk.type = {0}" , rootBlk . type ) ;
if ( rootBlk . type ! = TypeHeader )
continue ;
rootBlk . hashTableSize = BigEndianBitConverter . ToUInt32 ( RootBlockSector , 0x0C ) ;
DicConsole . DebugWriteLine ( "AmigaDOS plugin" , "rootBlk.hashTableSize = {0}" , rootBlk . hashTableSize ) ;
2015-04-20 06:26:33 +01:00
2017-07-19 05:21:25 +01:00
blockSize = ( rootBlk . hashTableSize + 56 ) * 4 ;
uint sectorsPerBlock = ( uint ) ( blockSize / RootBlockSector . Length ) ;
DicConsole . DebugWriteLine ( "AmigaDOS plugin" , "blockSize = {0}" , blockSize ) ;
DicConsole . DebugWriteLine ( "AmigaDOS plugin" , "sectorsPerBlock = {0}" , sectorsPerBlock ) ;
if ( blockSize % RootBlockSector . Length > 0 )
sectorsPerBlock + + ;
if ( root_ptr + sectorsPerBlock > = partitionEnd )
continue ;
RootBlockSector = imagePlugin . ReadSectors ( root_ptr , sectorsPerBlock ) ;
// Clear checksum on sector
rootBlk . checksum = BigEndianBitConverter . ToUInt32 ( RootBlockSector , 20 ) ;
RootBlockSector [ 20 ] = RootBlockSector [ 21 ] = RootBlockSector [ 22 ] = RootBlockSector [ 23 ] = 0 ;
uint rsum = AmigaChecksum ( RootBlockSector ) ;
DicConsole . DebugWriteLine ( "AmigaDOS plugin" , "rootBlk.checksum = 0x{0:X8}" , rootBlk . checksum ) ;
DicConsole . DebugWriteLine ( "AmigaDOS plugin" , "rsum = 0x{0:X8}" , rsum ) ;
rootBlk . sec_type = BigEndianBitConverter . ToUInt32 ( RootBlockSector , RootBlockSector . Length - 4 ) ;
DicConsole . DebugWriteLine ( "AmigaDOS plugin" , "rootBlk.sec_type = {0}" , rootBlk . sec_type ) ;
if ( rootBlk . sec_type = = SubTypeRoot & & rootBlk . checksum = = rsum )
{
RootBlockSector = imagePlugin . ReadSectors ( root_ptr , sectorsPerBlock ) ;
root_found = true ;
break ;
}
}
if ( ! root_found )
return ;
rootBlk = MarshalRootBlock ( RootBlockSector ) ;
string diskName = StringHandlers . PascalToString ( rootBlk . diskName , CurrentEncoding ) ;
2015-04-20 06:26:33 +01:00
2016-04-19 02:11:47 +01:00
switch ( bootBlk . diskType & 0xFF )
2015-04-20 06:26:33 +01:00
{
case 0 :
sbInformation . Append ( "Amiga Original File System" ) ;
2015-12-05 17:10:27 +00:00
xmlFSType . Type = "Amiga OFS" ;
2015-04-20 06:26:33 +01:00
break ;
case 1 :
sbInformation . Append ( "Amiga Fast File System" ) ;
2015-12-05 17:10:27 +00:00
xmlFSType . Type = "Amiga FFS" ;
2015-04-20 06:26:33 +01:00
break ;
case 2 :
sbInformation . Append ( "Amiga Original File System with international characters" ) ;
2015-12-05 17:10:27 +00:00
xmlFSType . Type = "Amiga OFS" ;
2015-04-20 06:26:33 +01:00
break ;
case 3 :
sbInformation . Append ( "Amiga Fast File System with international characters" ) ;
2015-12-05 17:10:27 +00:00
xmlFSType . Type = "Amiga FFS" ;
2015-04-20 06:26:33 +01:00
break ;
case 4 :
sbInformation . Append ( "Amiga Original File System with directory cache" ) ;
2015-12-05 17:10:27 +00:00
xmlFSType . Type = "Amiga OFS" ;
2015-04-20 06:26:33 +01:00
break ;
case 5 :
sbInformation . Append ( "Amiga Fast File System with directory cache" ) ;
2015-12-05 17:10:27 +00:00
xmlFSType . Type = "Amiga FFS" ;
2015-04-20 06:26:33 +01:00
break ;
case 6 :
sbInformation . Append ( "Amiga Original File System with long filenames" ) ;
2017-07-19 05:21:25 +01:00
xmlFSType . Type = "Amiga OFS2" ;
2015-04-20 06:26:33 +01:00
break ;
case 7 :
sbInformation . Append ( "Amiga Fast File System with long filenames" ) ;
2017-07-19 05:21:25 +01:00
xmlFSType . Type = "Amiga FFS2" ;
2015-04-20 06:26:33 +01:00
break ;
}
2016-04-19 02:11:47 +01:00
if ( ( bootBlk . diskType & 0x6D754600 ) = = 0x6D754600 )
2015-04-20 06:26:33 +01:00
sbInformation . Append ( ", with multi-user patches" ) ;
sbInformation . AppendLine ( ) ;
2017-07-19 05:21:25 +01:00
sbInformation . AppendFormat ( "Volume name: {0}" , diskName ) . AppendLine ( ) ;
if ( bootBlk . checksum = = bsum )
2015-12-05 17:10:27 +00:00
{
2017-07-19 05:21:25 +01:00
Checksums . SHA1Context sha1Ctx = new Checksums . SHA1Context ( ) ;
sha1Ctx . Init ( ) ;
sha1Ctx . Update ( bootBlk . bootCode ) ;
sbInformation . AppendLine ( "Volume is bootable" ) ;
sbInformation . AppendFormat ( "Boot code SHA1 is {0}" , sha1Ctx . End ( ) ) . AppendLine ( ) ;
2015-12-05 17:10:27 +00:00
}
2015-04-20 06:26:33 +01:00
2016-04-19 02:11:47 +01:00
if ( rootBlk . bitmapFlag = = 0xFFFFFFFF )
2015-04-20 06:26:33 +01:00
sbInformation . AppendLine ( "Volume bitmap is valid" ) ;
2016-04-19 02:11:47 +01:00
if ( rootBlk . bitmapExtensionBlock ! = 0x00000000 & & rootBlk . bitmapExtensionBlock ! = 0xFFFFFFFF )
2015-04-20 06:26:33 +01:00
sbInformation . AppendFormat ( "Bitmap extension at block {0}" , rootBlk . bitmapExtensionBlock ) . AppendLine ( ) ;
2016-04-19 02:11:47 +01:00
if ( ( bootBlk . diskType & 0xFF ) = = 4 | | ( bootBlk . diskType & 0xFF ) = = 5 )
2015-04-20 06:26:33 +01:00
sbInformation . AppendFormat ( "Directory cache starts at block {0}" , rootBlk . extension ) . AppendLine ( ) ;
2017-07-19 05:21:25 +01:00
long blocks = ( long ) ( ( ( ( partitionEnd - partitionStart ) + 1 ) * imagePlugin . ImageInfo . sectorSize ) / blockSize ) ;
sbInformation . AppendFormat ( "Volume block size is {0} bytes" , blockSize ) . AppendLine ( ) ;
sbInformation . AppendFormat ( "Volume has {0} blocks" , blocks ) . AppendLine ( ) ;
2015-04-20 06:26:33 +01:00
sbInformation . AppendFormat ( "Volume created on {0}" , DateHandlers . AmigaToDateTime ( rootBlk . cDays , rootBlk . cMins , rootBlk . cTicks ) ) . AppendLine ( ) ;
sbInformation . AppendFormat ( "Volume last modified on {0}" , DateHandlers . AmigaToDateTime ( rootBlk . vDays , rootBlk . vMins , rootBlk . vTicks ) ) . AppendLine ( ) ;
sbInformation . AppendFormat ( "Volume root directory last modified on on {0}" , DateHandlers . AmigaToDateTime ( rootBlk . rDays , rootBlk . rMins , rootBlk . rTicks ) ) . AppendLine ( ) ;
2017-07-19 05:21:25 +01:00
sbInformation . AppendFormat ( "Root block checksum is 0x{0:X8}" , rootBlk . checksum ) . AppendLine ( ) ;
2015-04-20 06:26:33 +01:00
information = sbInformation . ToString ( ) ;
2015-12-04 18:52:23 +00:00
xmlFSType . CreationDate = DateHandlers . AmigaToDateTime ( rootBlk . cDays , rootBlk . cMins , rootBlk . cTicks ) ;
2015-12-06 05:09:31 +00:00
xmlFSType . CreationDateSpecified = true ;
2015-12-04 18:52:23 +00:00
xmlFSType . ModificationDate = DateHandlers . AmigaToDateTime ( rootBlk . vDays , rootBlk . vMins , rootBlk . vTicks ) ;
2015-12-06 05:09:31 +00:00
xmlFSType . ModificationDateSpecified = true ;
2015-12-05 17:10:27 +00:00
xmlFSType . Dirty = rootBlk . bitmapFlag ! = 0xFFFFFFFF ;
2017-07-19 05:21:25 +01:00
xmlFSType . Clusters = blocks ;
xmlFSType . ClusterSize = ( int ) blockSize ;
xmlFSType . VolumeName = diskName ;
xmlFSType . Bootable = bsum = = bootBlk . checksum ;
// Useful as a serial
xmlFSType . VolumeSerial = string . Format ( "{0:X8}" , rootBlk . checksum ) ;
2015-04-20 06:26:33 +01:00
}
2017-07-19 05:21:25 +01:00
static RootBlock MarshalRootBlock ( byte [ ] block )
2015-04-20 06:26:33 +01:00
{
2017-07-19 05:21:25 +01:00
byte [ ] tmp = new byte [ 228 ] ;
Array . Copy ( block , 0 , tmp , 0 , 24 ) ;
Array . Copy ( block , block . Length - 200 , tmp , 28 , 200 ) ;
RootBlock root = BigEndianMarshal . ByteArrayToStructureBigEndian < RootBlock > ( tmp ) ;
root . hashTable = new uint [ ( block . Length - 224 ) / 4 ] ;
2015-04-20 06:26:33 +01:00
BigEndianBitConverter . IsLittleEndian = BitConverter . IsLittleEndian ;
2017-07-19 05:21:25 +01:00
for ( int i = 0 ; i < root . hashTable . Length ; i + + )
root . hashTable [ i ] = BigEndianBitConverter . ToUInt32 ( block , 24 + i * 4 ) ;
return root ;
}
static uint AmigaChecksum ( byte [ ] data )
{
2016-07-28 22:25:26 +01:00
uint sum = 0 ;
2015-04-20 06:26:33 +01:00
2016-04-19 02:11:47 +01:00
for ( int i = 0 ; i < data . Length ; i + = 4 )
2017-07-19 05:21:25 +01:00
sum + = ( uint ) ( ( data [ i ] < < 24 ) + ( data [ i + 1 ] < < 16 ) + ( data [ i + 2 ] < < 8 ) + data [ i + 3 ] ) ;
return ( uint ) - sum ;
}
static uint AmigaBootChecksum ( byte [ ] data )
{
uint sum , psum ;
sum = 0 ;
for ( int i = 0 ; i < data . Length ; i + = 4 ) {
psum = sum ;
if ( ( sum + = ( uint ) ( ( data [ i ] < < 24 ) + ( data [ i + 1 ] < < 16 ) + ( data [ i + 2 ] < < 8 ) + data [ i + 3 ] ) ) < psum )
sum + + ;
}
2015-04-20 06:26:33 +01:00
2017-07-19 05:21:25 +01:00
return ~ sum ;
2015-04-20 06:26:33 +01:00
}
2016-07-21 17:16:08 +01:00
public override Errno Mount ( )
{
return Errno . NotImplemented ;
}
2016-07-22 00:43:22 +01:00
public override Errno Mount ( bool debug )
{
return Errno . NotImplemented ;
}
2016-07-21 17:16:08 +01:00
public override Errno Unmount ( )
{
return Errno . NotImplemented ;
}
public override Errno MapBlock ( string path , long fileBlock , ref long deviceBlock )
{
return Errno . NotImplemented ;
}
public override Errno GetAttributes ( string path , ref FileAttributes attributes )
{
return Errno . NotImplemented ;
}
public override Errno ListXAttr ( string path , ref List < string > xattrs )
{
return Errno . NotImplemented ;
}
public override Errno GetXattr ( string path , string xattr , ref byte [ ] buf )
{
return Errno . NotImplemented ;
}
public override Errno Read ( string path , long offset , long size , ref byte [ ] buf )
{
return Errno . NotImplemented ;
}
public override Errno ReadDir ( string path , ref List < string > contents )
{
return Errno . NotImplemented ;
}
public override Errno StatFs ( ref FileSystemInfo stat )
{
return Errno . NotImplemented ;
}
public override Errno Stat ( string path , ref FileEntryInfo stat )
{
return Errno . NotImplemented ;
}
public override Errno ReadLink ( string path , ref string dest )
{
return Errno . NotImplemented ;
}
2015-04-20 06:26:33 +01:00
}
}