Refactor image classes and split them to smaller files.

This commit is contained in:
2018-07-23 23:25:43 +01:00
parent 55ca2d23b6
commit ed88989642
445 changed files with 50086 additions and 34342 deletions

View File

@@ -485,60 +485,531 @@
</e>
</e>
<e p="DiscImageChef.DiscImages" t="IncludeRecursive">
<e p="Alcohol120.cs" t="Include" />
<e p="Anex86.cs" t="Include" />
<e p="Apple2MG.cs" t="Include" />
<e p="AppleDOS.cs" t="Include" />
<e p="AppleNIB.cs" t="Include" />
<e p="Apridisk.cs" t="Include" />
<e p="BLU.cs" t="Include" />
<e p="BlindWrite4.cs" t="Include" />
<e p="BlindWrite5.cs" t="Include" />
<e p="CDRDAO.cs" t="Include" />
<e p="CDRWin.cs" t="Include" />
<e p="CHD.cs" t="Include" />
<e p="CPCDSK.cs" t="Include" />
<e p="CisCopy.cs" t="Include" />
<e p="CloneCD.cs" t="Include" />
<e p="CopyQM.cs" t="Include" />
<e p="D88.cs" t="Include" />
<e p="DART.cs" t="Include" />
<e p="DIM.cs" t="Include" />
<e p="DiscFerret.cs" t="Include" />
<e p="Alcohol120" t="Include">
<e p="Alcohol120.cs" t="Include" />
<e p="Constants.cs" t="Include" />
<e p="Enums.cs" t="Include" />
<e p="Helpers.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="Anex86" t="Include">
<e p="Anex86.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="Apple2MG" t="Include">
<e p="Apple2MG.cs" t="Include" />
<e p="Constants.cs" t="Include" />
<e p="Enums.cs" t="Include" />
<e p="Helpers.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="AppleDOS" t="Include">
<e p="AppleDOS.cs" t="Include" />
<e p="Constants.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="AppleNIB" t="Include">
<e p="AppleNIB.cs" t="Include" />
<e p="Constants.cs" t="Include" />
<e p="Helpers.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
</e>
<e p="Apridisk" t="Include">
<e p="Apridisk.cs" t="Include" />
<e p="Compression.cs" t="Include" />
<e p="Constants.cs" t="Include" />
<e p="Enums.cs" t="Include" />
<e p="Helpers.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="BLU" t="Include">
<e p="BLU.cs" t="Include" />
<e p="Constants.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="BlindWrite4" t="Include">
<e p="BlindWrite4.cs" t="Include" />
<e p="Constants.cs" t="Include" />
<e p="Enums.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
</e>
<e p="BlindWrite5" t="Include">
<e p="BlindWrite5.cs" t="Include" />
<e p="Constants.cs" t="Include" />
<e p="Enums.cs" t="Include" />
<e p="Helpers.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
</e>
<e p="CDRDAO" t="Include">
<e p="CDRDAO.cs" t="Include" />
<e p="Constants.cs" t="Include" />
<e p="Helpers.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="CDRWin" t="Include">
<e p="CDRWin.cs" t="Include" />
<e p="Constants.cs" t="Include" />
<e p="Helpers.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="CHD" t="Include">
<e p="CHD.cs" t="Include" />
<e p="Constants.cs" t="Include" />
<e p="Enums.cs" t="Include" />
<e p="Helpers.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
</e>
<e p="CPCDSK" t="Include">
<e p="CPCDSK.cs" t="Include" />
<e p="Constants.cs" t="Include" />
<e p="Helpers.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
</e>
<e p="CisCopy" t="Include">
<e p="CisCopy.cs" t="Include" />
<e p="Enums.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="CloneCD" t="Include">
<e p="CloneCD.cs" t="Include" />
<e p="Constants.cs" t="Include" />
<e p="Helpers.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="CopyQM" t="Include">
<e p="Constants.cs" t="Include" />
<e p="CopyQM.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
</e>
<e p="D88" t="Include">
<e p="Constants.cs" t="Include" />
<e p="D88.cs" t="Include" />
<e p="Enums.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
</e>
<e p="DART" t="Include">
<e p="Constants.cs" t="Include" />
<e p="DART.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
</e>
<e p="DIM" t="Include">
<e p="Constants.cs" t="Include" />
<e p="DIM.cs" t="Include" />
<e p="Enums.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
</e>
<e p="DiscFerret" t="Include">
<e p="Constants.cs" t="Include" />
<e p="DiscFerret.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
</e>
<e p="DiscImageChef" t="Include">
<e p="ClauniaSubchannelTransform.cs" t="Include" />
<e p="Constants.cs" t="Include" />
<e p="DiscImageChef.cs" t="Include" />
<e p="Enums.cs" t="Include" />
<e p="Helpers.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Verify.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="DiscImageChef.DiscImages.csproj" t="IncludeRecursive" />
<e p="DiscImageChef.cs" t="Include" />
<e p="DiscJuggler.cs" t="Include" />
<e p="DiskCopy42.cs" t="Include" />
<e p="DriDiskCopy.cs" t="Include" />
<e p="GDI.cs" t="Include" />
<e p="HDCopy.cs" t="Include" />
<e p="IMD.cs" t="Include" />
<e p="KryoFlux.cs" t="Include" />
<e p="MaxiDisk.cs" t="Include" />
<e p="NDIF.cs" t="Include" />
<e p="NHDr0.cs" t="Include" />
<e p="Nero.cs" t="Include" />
<e p="Parallels.cs" t="Include" />
<e p="PartClone.cs" t="Include" />
<e p="Partimage.cs" t="Include" />
<e p="QCOW.cs" t="Include" />
<e p="QCOW2.cs" t="Include" />
<e p="QED.cs" t="Include" />
<e p="RayDIM.cs" t="Include" />
<e p="DiscJuggler" t="Include">
<e p="DiscJuggler.cs" t="Include" />
<e p="Helpers.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
</e>
<e p="DiskCopy42" t="Include">
<e p="Constants.cs" t="Include" />
<e p="DiskCopy42.cs" t="Include" />
<e p="Helpers.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="DriDiskCopy" t="Include">
<e p="Constants.cs" t="Include" />
<e p="DriDiskCopy.cs" t="Include" />
<e p="Enums.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="GDI" t="Include">
<e p="Constants.cs" t="Include" />
<e p="GDI.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
</e>
<e p="HDCopy" t="Include">
<e p="HDCopy.cs" t="Include" />
<e p="Helpers.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
</e>
<e p="IMD" t="Include">
<e p="Constants.cs" t="Include" />
<e p="Enums.cs" t="Include" />
<e p="IMD.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
</e>
<e p="KryoFlux" t="Include">
<e p="Constants.cs" t="Include" />
<e p="Enums.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="KryoFlux.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
</e>
<e p="MaxiDisk" t="Include">
<e p="Enums.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="MaxiDisk.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="NDIF" t="Include">
<e p="Constants.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="NDIF.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
</e>
<e p="NHDr0" t="Include">
<e p="Constants.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="NHDr0.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="Nero" t="Include">
<e p="Constants.cs" t="Include" />
<e p="Enums.cs" t="Include" />
<e p="Helpers.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Nero.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
</e>
<e p="Parallels" t="Include">
<e p="Constants.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Parallels.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="PartClone" t="Include">
<e p="Constants.cs" t="Include" />
<e p="Helpers.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="PartClone.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
</e>
<e p="Partimage" t="Include">
<e p="Constants.cs" t="Include" />
<e p="Enums.cs" t="Include" />
<e p="Helpers.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Partimage.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
</e>
<e p="QCOW" t="Include">
<e p="Constants.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="QCOW.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="QCOW2" t="Include">
<e p="Constants.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="QCOW2.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="QED" t="Include">
<e p="Constants.cs" t="Include" />
<e p="Enums.cs" t="Include" />
<e p="Helpers.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="QED.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="RayDIM" t="Include">
<e p="Constants.cs" t="Include" />
<e p="Enums.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="RayDIM.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="Register.cs" t="Include" />
<e p="RsIde.cs" t="Include" />
<e p="SaveDskF.cs" t="Include" />
<e p="SuperCardPro.cs" t="Include" />
<e p="T98.cs" t="Include" />
<e p="TeleDisk.cs" t="Include" />
<e p="UDIF.cs" t="Include" />
<e p="UkvFdi.cs" t="Include" />
<e p="VDI.cs" t="Include" />
<e p="VHD.cs" t="Include" />
<e p="VHDX.cs" t="Include" />
<e p="VMware.cs" t="Include" />
<e p="Virtual98.cs" t="Include" />
<e p="ZZZRawImage.cs" t="Include" />
<e p="RsIde" t="Include">
<e p="Constants.cs" t="Include" />
<e p="Enums.cs" t="Include" />
<e p="Helpers.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="RsIde.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="SaveDskF" t="Include">
<e p="Constants.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="SaveDskF.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="SuperCardPro" t="Include">
<e p="Constants.cs" t="Include" />
<e p="Enums.cs" t="Include" />
<e p="Helpers.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="SuperCardPro.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
</e>
<e p="T98" t="Include">
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="T98.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="TeleDisk" t="Include">
<e p="Constants.cs" t="Include" />
<e p="Helpers.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="TeleDisk.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
</e>
<e p="UDIF" t="Include">
<e p="Constants.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="UDIF.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="UkvFdi" t="Include">
<e p="Constants.cs" t="Include" />
<e p="Enums.cs" t="Include" />
<e p="Helpers.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="UkvFdi.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
</e>
<e p="VDI" t="Include">
<e p="Constants.cs" t="Include" />
<e p="Enums.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="VDI.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="VHD" t="Include">
<e p="Constants.cs" t="Include" />
<e p="Helpers.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="VHD.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="VHDX" t="Include">
<e p="Constants.cs" t="Include" />
<e p="Helpers.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="VHDX.cs" t="Include" />
</e>
<e p="VMware" t="Include">
<e p="Constants.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="VMware.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="Virtual98" t="Include">
<e p="Constants.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Structs.cs" t="Include" />
<e p="Unsupported.cs" t="Include" />
<e p="Virtual98.cs" t="Include" />
<e p="Write.cs" t="Include" />
</e>
<e p="ZZZRawImage" t="Include">
<e p="Constants.cs" t="Include" />
<e p="Helpers.cs" t="Include" />
<e p="Identify.cs" t="Include" />
<e p="Properties.cs" t="Include" />
<e p="Read.cs" t="Include" />
<e p="Write.cs" t="Include" />
<e p="ZZZRawImage.cs" t="Include" />
</e>
<e p="bin" t="ExcludeRecursive" />
<e p="obj" t="ExcludeRecursive">
<e p="Debug" t="Include">

View File

@@ -0,0 +1,88 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Alcohol120.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disc image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Manages Alcohol 120% disc images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Collections.Generic;
using System.IO;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
namespace DiscImageChef.DiscImages
{
public partial class Alcohol120 : IWritableImage
{
AlcoholFooter alcFooter;
IFilter alcImage;
Dictionary<int, AlcoholSession> alcSessions;
Dictionary<int, Dictionary<int, AlcoholTrack>> alcToc;
Dictionary<int, AlcoholTrackExtra> alcTrackExtras;
Dictionary<int, AlcoholTrack> alcTracks;
byte[] bca;
FileStream descriptorStream;
byte[] dmi;
byte[] fullToc;
ImageInfo imageInfo;
Stream imageStream;
bool isDvd;
Dictionary<uint, ulong> offsetmap;
byte[] pfi;
Dictionary<byte, byte> trackFlags;
List<Track> writingTracks;
public Alcohol120()
{
imageInfo = new ImageInfo
{
ReadableSectorTags = new List<SectorTagType>(),
ReadableMediaTags = new List<MediaTagType>(),
HasPartitions = true,
HasSessions = true,
Version = null,
Application = null,
ApplicationVersion = null,
Creator = null,
Comments = null,
MediaManufacturer = null,
MediaModel = null,
MediaSerialNumber = null,
MediaBarcode = null,
MediaPartNumber = null,
MediaSequence = 0,
LastMediaSequence = 0,
DriveManufacturer = null,
DriveModel = null,
DriveSerialNumber = null,
DriveFirmwareRevision = null
};
}
}
}

View File

@@ -0,0 +1,40 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Identify.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disc image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Identifies Alcohol 120% disc images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
namespace DiscImageChef.DiscImages
{
public partial class Alcohol120
{
readonly byte[] alcoholSignature =
{0x4d, 0x45, 0x44, 0x49, 0x41, 0x20, 0x44, 0x45, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x4f, 0x52};
}
}

View File

@@ -0,0 +1,69 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Enums.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disc image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains enumerations for Alcohol 120% disc images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Diagnostics.CodeAnalysis;
namespace DiscImageChef.DiscImages
{
public partial class Alcohol120
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
enum AlcoholMediumType : ushort
{
CD = 0x00,
CDR = 0x01,
CDRW = 0x02,
DVD = 0x10,
DVDR = 0x12
}
[SuppressMessage("ReSharper", "InconsistentNaming")]
enum AlcoholTrackMode : byte
{
NoData = 0x00,
DVD = 0x02,
Audio = 0xA9,
Mode1 = 0xAA,
Mode2 = 0xAB,
Mode2F1 = 0xEC,
Mode2F2 = 0xED,
Mode2F1Alt = 0xAC,
Mode2F2Alt = 0xAD
}
enum AlcoholSubchannelMode : byte
{
None = 0x00,
Interleaved = 0x08
}
}
}

View File

@@ -0,0 +1,157 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Helpers.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disc image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains helpers for Alcohol 120% disc images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
namespace DiscImageChef.DiscImages
{
public partial class Alcohol120
{
static ushort AlcoholTrackModeToBytesPerSector(AlcoholTrackMode trackMode)
{
switch(trackMode)
{
case AlcoholTrackMode.Audio:
case AlcoholTrackMode.Mode1:
case AlcoholTrackMode.Mode2:
case AlcoholTrackMode.Mode2F1:
case AlcoholTrackMode.Mode2F2:
case AlcoholTrackMode.Mode2F2Alt:
case AlcoholTrackMode.Mode2F1Alt: return 2352;
case AlcoholTrackMode.DVD: return 2048;
default: return 0;
}
}
static ushort AlcoholTrackModeToCookedBytesPerSector(AlcoholTrackMode trackMode)
{
switch(trackMode)
{
case AlcoholTrackMode.Mode1:
case AlcoholTrackMode.Mode2F1:
case AlcoholTrackMode.Mode2F1Alt: return 2048;
case AlcoholTrackMode.Mode2F2:
case AlcoholTrackMode.Mode2F2Alt: return 2324;
case AlcoholTrackMode.Mode2: return 2336;
case AlcoholTrackMode.Audio: return 2352;
case AlcoholTrackMode.DVD: return 2048;
default: return 0;
}
}
static TrackType AlcoholTrackTypeToTrackType(AlcoholTrackMode trackType)
{
switch(trackType)
{
case AlcoholTrackMode.Mode1: return TrackType.CdMode1;
case AlcoholTrackMode.Mode2F1:
case AlcoholTrackMode.Mode2F1Alt: return TrackType.CdMode2Form1;
case AlcoholTrackMode.Mode2F2:
case AlcoholTrackMode.Mode2F2Alt: return TrackType.CdMode2Form2;
case AlcoholTrackMode.Mode2: return TrackType.CdMode2Formless;
case AlcoholTrackMode.Audio: return TrackType.Audio;
default: return TrackType.Data;
}
}
static MediaType AlcoholMediumTypeToMediaType(AlcoholMediumType discType)
{
switch(discType)
{
case AlcoholMediumType.CD: return MediaType.CD;
case AlcoholMediumType.CDR: return MediaType.CDR;
case AlcoholMediumType.CDRW: return MediaType.CDRW;
case AlcoholMediumType.DVD: return MediaType.DVDROM;
case AlcoholMediumType.DVDR: return MediaType.DVDR;
default: return MediaType.Unknown;
}
}
static AlcoholMediumType MediaTypeToAlcohol(MediaType type)
{
switch(type)
{
case MediaType.CD:
case MediaType.CDDA:
case MediaType.CDEG:
case MediaType.CDG:
case MediaType.CDI:
case MediaType.CDMIDI:
case MediaType.CDPLUS:
case MediaType.CDROM:
case MediaType.CDROMXA:
case MediaType.CDV:
case MediaType.DTSCD:
case MediaType.JaguarCD:
case MediaType.MEGACD:
case MediaType.PS1CD:
case MediaType.PS2CD:
case MediaType.SuperCDROM2:
case MediaType.SVCD:
case MediaType.SATURNCD:
case MediaType.ThreeDO:
case MediaType.VCD:
case MediaType.VCDHD: return AlcoholMediumType.CD;
case MediaType.CDR: return AlcoholMediumType.CDR;
case MediaType.CDRW:
case MediaType.CDMRW: return AlcoholMediumType.CDRW;
case MediaType.DVDR:
case MediaType.DVDRW:
case MediaType.DVDPR:
case MediaType.DVDRDL:
case MediaType.DVDRWDL:
case MediaType.DVDPRDL:
case MediaType.DVDPRWDL: return AlcoholMediumType.DVDR;
default: return AlcoholMediumType.DVD;
}
}
static AlcoholTrackMode TrackTypeToAlcohol(TrackType type)
{
switch(type)
{
case TrackType.Audio: return AlcoholTrackMode.Audio;
case TrackType.CdMode1: return AlcoholTrackMode.Mode1;
case TrackType.CdMode2Formless: return AlcoholTrackMode.Mode2;
case TrackType.CdMode2Form1: return AlcoholTrackMode.Mode2F1;
case TrackType.CdMode2Form2: return AlcoholTrackMode.Mode2F2;
default: return AlcoholTrackMode.DVD;
}
}
static (byte minute, byte second, byte frame) LbaToMsf(ulong sector)
{
return ((byte)((sector + 150) / 75 / 60), (byte)((sector + 150) / 75 % 60), (byte)((sector + 150) % 75));
}
}
}

View File

@@ -0,0 +1,59 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Identify.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disc image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Identifies Alcohol 120% disc images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using DiscImageChef.CommonTypes.Interfaces;
namespace DiscImageChef.DiscImages
{
public partial class Alcohol120
{
public bool Identify(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin);
if(stream.Length < 88) return false;
byte[] hdr = new byte[88];
stream.Read(hdr, 0, 88);
IntPtr hdrPtr = Marshal.AllocHGlobal(88);
Marshal.Copy(hdr, 0, hdrPtr, 88);
AlcoholHeader header = (AlcoholHeader)Marshal.PtrToStructure(hdrPtr, typeof(AlcoholHeader));
Marshal.FreeHGlobal(hdrPtr);
return header.signature.SequenceEqual(alcoholSignature);
}
}
}

View File

@@ -0,0 +1,141 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Properties.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disc image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains properties Alcohol 120% disc images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Structs;
using Schemas;
namespace DiscImageChef.DiscImages
{
public partial class Alcohol120
{
public ImageInfo Info => imageInfo;
public string Name => "Alcohol 120% Media Descriptor Structure";
public Guid Id => new Guid("A78FBEBA-0307-4915-BDE3-B8A3B57F843F");
public string Format => "Alcohol 120% Media Descriptor Structure";
public List<Partition> Partitions { get; private set; }
public List<Track> Tracks
{
get
{
List<Track> tracks = new List<Track>();
foreach(AlcoholTrack alcTrack in alcTracks.Values)
{
ushort sessionNo =
(from session in Sessions
where alcTrack.point >= session.StartTrack || alcTrack.point <= session.EndTrack
select session.SessionSequence).FirstOrDefault();
if(!alcTrackExtras.TryGetValue(alcTrack.point, out AlcoholTrackExtra alcExtra)) continue;
Track dicTrack = new Track
{
Indexes = new Dictionary<int, ulong> {{1, alcTrack.startLba}},
TrackStartSector = alcTrack.startLba,
TrackEndSector = alcTrack.startLba + alcExtra.sectors - 1,
TrackPregap = alcExtra.pregap,
TrackSession = sessionNo,
TrackSequence = alcTrack.point,
TrackType = AlcoholTrackTypeToTrackType(alcTrack.mode),
TrackFilter = alcImage,
TrackFile = alcImage.GetFilename(),
TrackFileOffset = alcTrack.startOffset,
TrackFileType = "BINARY",
TrackRawBytesPerSector = alcTrack.sectorSize,
TrackBytesPerSector = AlcoholTrackModeToCookedBytesPerSector(alcTrack.mode)
};
switch(alcTrack.subMode)
{
case AlcoholSubchannelMode.Interleaved:
dicTrack.TrackSubchannelFilter = alcImage;
dicTrack.TrackSubchannelFile = alcImage.GetFilename();
dicTrack.TrackSubchannelOffset = alcTrack.startOffset;
dicTrack.TrackSubchannelType = TrackSubchannelType.RawInterleaved;
break;
case AlcoholSubchannelMode.None:
dicTrack.TrackSubchannelType = TrackSubchannelType.None;
break;
}
tracks.Add(dicTrack);
}
return tracks;
}
}
public List<Session> Sessions { get; private set; }
public List<DumpHardwareType> DumpHardware => null;
public CICMMetadataType CicmMetadata => null;
public IEnumerable<MediaTagType> SupportedMediaTags =>
new[] {MediaTagType.CD_FullTOC, MediaTagType.DVD_BCA, MediaTagType.DVD_DMI, MediaTagType.DVD_PFI};
public IEnumerable<SectorTagType> SupportedSectorTags =>
new[]
{
SectorTagType.CdSectorEcc, SectorTagType.CdSectorEccP, SectorTagType.CdSectorEccQ,
SectorTagType.CdSectorEdc, SectorTagType.CdSectorHeader, SectorTagType.CdSectorSubHeader,
SectorTagType.CdSectorSync, SectorTagType.CdTrackFlags, SectorTagType.CdSectorSubchannel
};
public IEnumerable<MediaType> SupportedMediaTypes =>
new[]
{
MediaType.BDR, MediaType.BDRE, MediaType.BDREXL, MediaType.BDROM, MediaType.BDRXL, MediaType.CBHD,
MediaType.CD, MediaType.CDDA, MediaType.CDEG, MediaType.CDG, MediaType.CDI, MediaType.CDMIDI,
MediaType.CDMRW, MediaType.CDPLUS, MediaType.CDR, MediaType.CDROM, MediaType.CDROMXA, MediaType.CDRW,
MediaType.CDV, MediaType.DVDDownload, MediaType.DVDPR, MediaType.DVDPRDL, MediaType.DVDPRW,
MediaType.DVDPRWDL, MediaType.DVDR, MediaType.DVDRAM, MediaType.DVDRDL, MediaType.DVDROM,
MediaType.DVDRW, MediaType.DVDRWDL, MediaType.EVD, MediaType.FDDVD, MediaType.DTSCD, MediaType.FVD,
MediaType.HDDVDR, MediaType.HDDVDRAM, MediaType.HDDVDRDL, MediaType.HDDVDROM, MediaType.HDDVDRW,
MediaType.HDDVDRWDL, MediaType.HDVMD, MediaType.HVD, MediaType.JaguarCD, MediaType.MEGACD,
MediaType.PD650, MediaType.PD650_WORM, MediaType.PS1CD, MediaType.PS2CD, MediaType.PS2DVD,
MediaType.PS3BD, MediaType.PS3DVD, MediaType.PS4BD, MediaType.SuperCDROM2, MediaType.SVCD,
MediaType.SVOD, MediaType.SATURNCD, MediaType.ThreeDO, MediaType.UDO, MediaType.UDO2,
MediaType.UDO2_WORM, MediaType.UMD, MediaType.VCD, MediaType.VCDHD, MediaType.NeoGeoCD, MediaType.PCFX
};
public IEnumerable<(string name, Type type, string description)> SupportedOptions =>
new (string name, Type type, string description)[] { };
public IEnumerable<string> KnownExtensions => new[] {".mds"};
public bool IsWriting { get; private set; }
public string ErrorMessage { get; private set; }
}
}

View File

@@ -0,0 +1,120 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Structs.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disc image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains structures for Alcohol 120% disc images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Runtime.InteropServices;
namespace DiscImageChef.DiscImages
{
public partial class Alcohol120
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct AlcoholHeader
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] signature;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public byte[] version;
public AlcoholMediumType type;
public ushort sessions;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public ushort[] unknown1;
public ushort bcaLength;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public uint[] unknown2;
public uint bcaOffset;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public uint[] unknown3;
public uint structuresOffset;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public uint[] unknown4;
public uint sessionOffset;
public uint dpmOffset;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct AlcoholSession
{
public int sessionStart;
public int sessionEnd;
public ushort sessionSequence;
public byte allBlocks;
public byte nonTrackBlocks;
public ushort firstTrack;
public ushort lastTrack;
public uint unknown;
public uint trackOffset;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct AlcoholTrack
{
public AlcoholTrackMode mode;
public AlcoholSubchannelMode subMode;
public byte adrCtl;
public byte tno;
public byte point;
public byte min;
public byte sec;
public byte frame;
public byte zero;
public byte pmin;
public byte psec;
public byte pframe;
public uint extraOffset;
public ushort sectorSize;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 18)]
public byte[] unknown;
public uint startLba;
public ulong startOffset;
public uint files;
public uint footerOffset;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)]
public byte[] unknown2;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct AlcoholTrackExtra
{
public uint pregap;
public uint sectors;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct AlcoholFooter
{
public uint filenameOffset;
public uint widechar;
public uint unknown1;
public uint unknown2;
}
}
}

View File

@@ -0,0 +1,985 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Write.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disc image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Writes Alcohol 120% disc images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Structs;
using DiscImageChef.Decoders.CD;
using Schemas;
using TrackType = DiscImageChef.CommonTypes.Enums.TrackType;
namespace DiscImageChef.DiscImages
{
public partial class Alcohol120
{
public bool Create(string path, MediaType mediaType, Dictionary<string, string> options, ulong sectors,
uint sectorSize)
{
if(!SupportedMediaTypes.Contains(mediaType))
{
ErrorMessage = $"Unsupport media format {mediaType}";
return false;
}
imageInfo = new ImageInfo {MediaType = mediaType, SectorSize = sectorSize, Sectors = sectors};
try
{
descriptorStream = new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None);
imageStream =
new
FileStream(Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path)) + ".mdf",
FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
}
catch(IOException e)
{
ErrorMessage = $"Could not create new image file, exception {e.Message}";
return false;
}
imageInfo.MediaType = mediaType;
switch(mediaType)
{
case MediaType.CD:
case MediaType.CDDA:
case MediaType.CDEG:
case MediaType.CDG:
case MediaType.CDI:
case MediaType.CDMIDI:
case MediaType.CDMRW:
case MediaType.CDPLUS:
case MediaType.CDR:
case MediaType.CDROM:
case MediaType.CDROMXA:
case MediaType.CDRW:
case MediaType.CDV:
case MediaType.DTSCD:
case MediaType.JaguarCD:
case MediaType.MEGACD:
case MediaType.PS1CD:
case MediaType.PS2CD:
case MediaType.SuperCDROM2:
case MediaType.SVCD:
case MediaType.SATURNCD:
case MediaType.ThreeDO:
case MediaType.VCD:
case MediaType.VCDHD:
isDvd = false;
break;
default:
isDvd = true;
break;
}
trackFlags = new Dictionary<byte, byte>();
IsWriting = true;
ErrorMessage = null;
return true;
}
public bool WriteMediaTag(byte[] data, MediaTagType tag)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
switch(tag)
{
case MediaTagType.CD_FullTOC:
if(isDvd)
{
ErrorMessage = $"Unsupported media tag {tag} for medium type {imageInfo.MediaType}";
return false;
}
byte[] fullTocSize = BigEndianBitConverter.GetBytes((short)data.Length);
fullToc = new byte[data.Length + 2];
Array.Copy(data, 0, fullToc, 2, data.Length);
fullToc[0] = fullTocSize[0];
fullToc[1] = fullTocSize[1];
return true;
case MediaTagType.DVD_PFI:
if(!isDvd)
{
ErrorMessage = $"Unsupported media tag {tag} for medium type {imageInfo.MediaType}";
return false;
}
pfi = data;
return true;
case MediaTagType.DVD_DMI:
if(!isDvd)
{
ErrorMessage = $"Unsupported media tag {tag} for medium type {imageInfo.MediaType}";
return false;
}
dmi = data;
return true;
case MediaTagType.DVD_BCA:
if(!isDvd)
{
ErrorMessage = $"Unsupported media tag {tag} for medium type {imageInfo.MediaType}";
return false;
}
bca = data;
return true;
default:
ErrorMessage = $"Unsupported media tag {tag}";
return false;
}
}
public bool WriteSector(byte[] data, ulong sectorAddress)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
Track track =
writingTracks.FirstOrDefault(trk => sectorAddress >= trk.TrackStartSector &&
sectorAddress <= trk.TrackEndSector);
if(track.TrackSequence == 0)
{
ErrorMessage = $"Can't found track containing {sectorAddress}";
return false;
}
if(track.TrackBytesPerSector != track.TrackRawBytesPerSector)
{
ErrorMessage = "Invalid write mode for this sector";
return false;
}
if(data.Length != track.TrackRawBytesPerSector)
{
ErrorMessage = "Incorrect data size";
return false;
}
imageStream.Seek((long)(track.TrackFileOffset + (sectorAddress - track.TrackStartSector) * (ulong)track.TrackRawBytesPerSector),
SeekOrigin.Begin);
imageStream.Write(data, 0, data.Length);
return true;
}
public bool WriteSectors(byte[] data, ulong sectorAddress, uint length)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
Track track =
writingTracks.FirstOrDefault(trk => sectorAddress >= trk.TrackStartSector &&
sectorAddress <= trk.TrackEndSector);
if(track.TrackSequence == 0)
{
ErrorMessage = $"Can't found track containing {sectorAddress}";
return false;
}
if(track.TrackBytesPerSector != track.TrackRawBytesPerSector)
{
ErrorMessage = "Invalid write mode for this sector";
return false;
}
if(sectorAddress + length > track.TrackEndSector + 1)
{
ErrorMessage = "Can't cross tracks";
return false;
}
if(data.Length % track.TrackRawBytesPerSector != 0)
{
ErrorMessage = "Incorrect data size";
return false;
}
switch(track.TrackSubchannelType)
{
case TrackSubchannelType.None:
imageStream
.Seek((long)(track.TrackFileOffset + (sectorAddress - track.TrackStartSector) * (ulong)track.TrackRawBytesPerSector),
SeekOrigin.Begin);
imageStream.Write(data, 0, data.Length);
ErrorMessage = "";
return true;
case TrackSubchannelType.Raw:
case TrackSubchannelType.RawInterleaved:
imageStream
.Seek((long)(track.TrackFileOffset + (sectorAddress - track.TrackStartSector) * (ulong)(track.TrackRawBytesPerSector + 96)),
SeekOrigin.Begin);
for(uint i = 0; i < length; i++)
{
imageStream.Write(data, (int)(i * track.TrackRawBytesPerSector), track.TrackRawBytesPerSector);
imageStream.Position += 96;
}
ErrorMessage = "";
return true;
default:
ErrorMessage = "Invalid subchannel mode for this sector";
return false;
}
}
public bool WriteSectorLong(byte[] data, ulong sectorAddress)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
Track track =
writingTracks.FirstOrDefault(trk => sectorAddress >= trk.TrackStartSector &&
sectorAddress <= trk.TrackEndSector);
if(track.TrackSequence == 0)
{
ErrorMessage = $"Can't found track containing {sectorAddress}";
return false;
}
if(data.Length != track.TrackRawBytesPerSector)
{
ErrorMessage = "Incorrect data size";
return false;
}
uint subchannelSize = (uint)(track.TrackSubchannelType != TrackSubchannelType.None ? 96 : 0);
imageStream.Seek((long)(track.TrackFileOffset + (sectorAddress - track.TrackStartSector) * (ulong)(track.TrackRawBytesPerSector + subchannelSize)),
SeekOrigin.Begin);
imageStream.Write(data, 0, data.Length);
return true;
}
public bool WriteSectorsLong(byte[] data, ulong sectorAddress, uint length)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
Track track =
writingTracks.FirstOrDefault(trk => sectorAddress >= trk.TrackStartSector &&
sectorAddress <= trk.TrackEndSector);
if(track.TrackSequence == 0)
{
ErrorMessage = $"Can't found track containing {sectorAddress}";
return false;
}
if(sectorAddress + length > track.TrackEndSector + 1)
{
ErrorMessage = "Can't cross tracks";
return false;
}
if(data.Length % track.TrackRawBytesPerSector != 0)
{
ErrorMessage = "Incorrect data size";
return false;
}
uint subchannelSize = (uint)(track.TrackSubchannelType != TrackSubchannelType.None ? 96 : 0);
for(uint i = 0; i < length; i++)
{
imageStream.Seek((long)(track.TrackFileOffset + (i + sectorAddress - track.TrackStartSector) * (ulong)(track.TrackRawBytesPerSector + subchannelSize)),
SeekOrigin.Begin);
imageStream.Write(data, (int)(i * track.TrackRawBytesPerSector), track.TrackRawBytesPerSector);
}
return true;
}
public bool SetTracks(List<Track> tracks)
{
ulong currentDataOffset = 0;
writingTracks = new List<Track>();
foreach(Track track in tracks.OrderBy(t => t.TrackSequence))
{
Track newTrack = track;
uint subchannelSize;
switch(track.TrackSubchannelType)
{
case TrackSubchannelType.None:
subchannelSize = 0;
break;
case TrackSubchannelType.Raw:
case TrackSubchannelType.RawInterleaved:
subchannelSize = 96;
break;
default:
ErrorMessage = $"Unsupported subchannel type {track.TrackSubchannelType}";
return false;
}
newTrack.TrackFileOffset = currentDataOffset;
currentDataOffset += (ulong)(newTrack.TrackRawBytesPerSector + subchannelSize) *
(newTrack.TrackEndSector - newTrack.TrackStartSector + 1);
writingTracks.Add(newTrack);
}
return true;
}
public bool Close()
{
if(!IsWriting)
{
ErrorMessage = "Image is not opened for writing";
return false;
}
byte sessions = byte.MinValue;
foreach(Track t in writingTracks)
if(t.TrackSession > byte.MinValue)
sessions = (byte)t.TrackSession;
AlcoholHeader header = new AlcoholHeader
{
signature = alcoholSignature,
version = new byte[] {1, 5},
type = MediaTypeToAlcohol(imageInfo.MediaType),
sessions = sessions,
structuresOffset = (uint)(pfi == null ? 0 : 96),
sessionOffset = (uint)(pfi == null ? 96 : 4196),
unknown1 = new ushort[2],
unknown2 = new uint[2],
unknown3 = new uint[6],
unknown4 = new uint[3]
};
// Alcohol sets this always, Daemon Tool expects this
header.unknown1[0] = 2;
alcSessions = new Dictionary<int, AlcoholSession>();
alcTracks = new Dictionary<int, AlcoholTrack>();
alcToc = new Dictionary<int, Dictionary<int, AlcoholTrack>>();
writingTracks = writingTracks.OrderBy(t => t.TrackSession).ThenBy(t => t.TrackSequence).ToList();
alcTrackExtras = new Dictionary<int, AlcoholTrackExtra>();
long currentTrackOffset = header.sessionOffset + Marshal.SizeOf(typeof(AlcoholSession)) * sessions;
FullTOC.CDFullTOC? decodedToc = FullTOC.Decode(fullToc);
long currentExtraOffset = currentTrackOffset;
for(int i = 1; i <= sessions; i++)
if(decodedToc.HasValue)
currentExtraOffset += Marshal.SizeOf(typeof(AlcoholTrack)) *
decodedToc.Value.TrackDescriptors.Count(t => t.SessionNumber == i);
else
{
currentExtraOffset += Marshal.SizeOf(typeof(AlcoholTrack)) * 3;
currentExtraOffset += Marshal.SizeOf(typeof(AlcoholTrack)) *
writingTracks.Count(t => t.TrackSession == i);
if(i < sessions) currentExtraOffset += Marshal.SizeOf(typeof(AlcoholTrack)) * 2;
}
long footerOffset = currentExtraOffset + Marshal.SizeOf(typeof(AlcoholTrackExtra)) * writingTracks.Count;
if(bca != null)
{
header.bcaOffset = (uint)footerOffset;
footerOffset += bca.Length;
}
if(isDvd)
{
alcSessions.Add(1,
new AlcoholSession
{
sessionEnd =
(int)(writingTracks[0].TrackEndSector - writingTracks[0].TrackStartSector + 1),
sessionSequence = 1,
allBlocks = 1,
nonTrackBlocks = 3,
firstTrack = 1,
lastTrack = 1,
trackOffset = 4220
});
footerOffset = 4300;
if(bca != null) footerOffset += bca.Length;
alcTracks.Add(1,
new AlcoholTrack
{
mode = AlcoholTrackMode.DVD,
adrCtl = 20,
point = 1,
extraOffset =
(uint)(writingTracks[0].TrackEndSector - writingTracks[0].TrackStartSector + 1),
sectorSize = 2048,
files = 1,
footerOffset = (uint)footerOffset,
unknown = new byte[18],
unknown2 = new byte[24]
});
alcToc.Add(1, alcTracks);
}
else
for(int i = 1; i <= sessions; i++)
{
Track firstTrack = writingTracks.First(t => t.TrackSession == i);
Track lastTrack = writingTracks.Last(t => t.TrackSession == i);
alcSessions.Add(i,
new AlcoholSession
{
sessionStart = (int)firstTrack.TrackStartSector - 150,
sessionEnd = (int)lastTrack.TrackEndSector + 1,
sessionSequence = (ushort)i,
allBlocks =
(byte)(decodedToc?.TrackDescriptors.Count(t => t.SessionNumber == i) ??
writingTracks.Count(t => t.TrackSession == i) + 3),
nonTrackBlocks =
(byte)(decodedToc?.TrackDescriptors.Count(t => t.SessionNumber == i &&
t.POINT >= 0xA0 &&
t.POINT <= 0xAF) ??
3),
firstTrack = (ushort)firstTrack.TrackSequence,
lastTrack = (ushort)lastTrack.TrackSequence,
trackOffset = (uint)currentTrackOffset
});
Dictionary<int, AlcoholTrack> thisSessionTracks = new Dictionary<int, AlcoholTrack>();
trackFlags.TryGetValue((byte)firstTrack.TrackSequence, out byte firstTrackControl);
trackFlags.TryGetValue((byte)lastTrack.TrackSequence, out byte lastTrackControl);
if(firstTrackControl == 0 && firstTrack.TrackType != TrackType.Audio)
firstTrackControl = (byte)CdFlags.DataTrack;
if(lastTrackControl == 0 && lastTrack.TrackType != TrackType.Audio)
lastTrackControl = (byte)CdFlags.DataTrack;
(byte minute, byte second, byte frame) leadinPmsf = LbaToMsf(lastTrack.TrackEndSector + 1);
if(decodedToc.HasValue &&
decodedToc.Value.TrackDescriptors.Any(t => t.SessionNumber == i && t.POINT >= 0xA0 &&
t.POINT <= 0xAF))
foreach(FullTOC.TrackDataDescriptor tocTrk in
decodedToc.Value.TrackDescriptors.Where(t => t.SessionNumber == i && t.POINT >= 0xA0 &&
t.POINT <= 0xAF))
{
thisSessionTracks.Add(tocTrk.POINT,
new AlcoholTrack
{
adrCtl = (byte)((tocTrk.ADR << 4) + tocTrk.CONTROL),
tno = tocTrk.TNO,
point = tocTrk.POINT,
min = tocTrk.Min,
sec = tocTrk.Sec,
frame = tocTrk.Frame,
zero = tocTrk.Zero,
pmin = tocTrk.PMIN,
psec = tocTrk.PSEC,
pframe = tocTrk.PFRAME,
mode = AlcoholTrackMode.NoData,
unknown = new byte[18],
unknown2 = new byte[24]
});
currentTrackOffset += Marshal.SizeOf(typeof(AlcoholTrack));
}
else
{
thisSessionTracks.Add(0xA0, new AlcoholTrack
{
adrCtl = (byte)((1 << 4) + firstTrackControl),
pmin = (byte)firstTrack.TrackSequence,
mode = AlcoholTrackMode.NoData,
point = 0xA0,
unknown = new byte[18],
unknown2 = new byte[24],
psec = (byte)(imageInfo.MediaType == MediaType.CDI
? 0x10
: writingTracks.Any(t => t.TrackType == TrackType.CdMode2Form1 ||
t.TrackType == TrackType.CdMode2Form2 ||
t.TrackType == TrackType.CdMode2Formless)
? 0x20
: 0)
});
thisSessionTracks.Add(0xA1,
new AlcoholTrack
{
adrCtl = (byte)((1 << 4) + lastTrackControl),
pmin = (byte)lastTrack.TrackSequence,
mode = AlcoholTrackMode.NoData,
point = 0xA1,
unknown = new byte[18],
unknown2 = new byte[24]
});
thisSessionTracks.Add(0xA2,
new AlcoholTrack
{
adrCtl = (byte)((1 << 4) + firstTrackControl),
zero = 0,
pmin = leadinPmsf.minute,
psec = leadinPmsf.second,
pframe = leadinPmsf.frame,
mode = AlcoholTrackMode.NoData,
point = 0xA2,
unknown = new byte[18],
unknown2 = new byte[24]
});
currentTrackOffset += Marshal.SizeOf(typeof(AlcoholTrack)) * 3;
}
foreach(Track track in writingTracks.Where(t => t.TrackSession == i).OrderBy(t => t.TrackSequence))
{
AlcoholTrack alcTrk = new AlcoholTrack();
if(decodedToc.HasValue &&
decodedToc.Value.TrackDescriptors.Any(t => t.SessionNumber == i &&
t.POINT == track.TrackSequence))
{
FullTOC.TrackDataDescriptor tocTrk =
decodedToc.Value.TrackDescriptors.First(t => t.SessionNumber == i &&
t.POINT == track.TrackSequence);
alcTrk.adrCtl = (byte)((tocTrk.ADR << 4) + tocTrk.CONTROL);
alcTrk.tno = tocTrk.TNO;
alcTrk.point = tocTrk.POINT;
alcTrk.min = tocTrk.Min;
alcTrk.sec = tocTrk.Sec;
alcTrk.frame = tocTrk.Frame;
alcTrk.zero = tocTrk.Zero;
alcTrk.pmin = tocTrk.PMIN;
alcTrk.psec = tocTrk.PSEC;
alcTrk.pframe = tocTrk.PFRAME;
}
else
{
(byte minute, byte second, byte frame) msf = LbaToMsf(track.TrackStartSector);
trackFlags.TryGetValue((byte)track.TrackSequence, out byte trackControl);
if(trackControl == 0 && track.TrackType != TrackType.Audio)
trackControl = (byte)CdFlags.DataTrack;
alcTrk.adrCtl = (byte)((1 << 4) + trackControl);
alcTrk.point = (byte)track.TrackSequence;
alcTrk.zero = 0;
alcTrk.pmin = msf.minute;
alcTrk.psec = msf.second;
alcTrk.pframe = msf.frame;
}
alcTrk.mode = TrackTypeToAlcohol(track.TrackType);
alcTrk.subMode = track.TrackSubchannelType != TrackSubchannelType.None
? AlcoholSubchannelMode.Interleaved
: AlcoholSubchannelMode.None;
alcTrk.sectorSize = (ushort)(track.TrackRawBytesPerSector +
(track.TrackSubchannelType != TrackSubchannelType.None ? 96 : 0));
alcTrk.startLba = (uint)track.TrackStartSector;
alcTrk.startOffset = track.TrackFileOffset;
alcTrk.files = 1;
alcTrk.extraOffset = (uint)currentExtraOffset;
alcTrk.footerOffset = (uint)footerOffset;
// Alcohol seems to set that for all CD tracks
// Daemon Tools expect it to be like this
alcTrk.unknown = new byte[]
{
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00
};
alcTrk.unknown2 = new byte[24];
thisSessionTracks.Add((int)track.TrackSequence, alcTrk);
currentTrackOffset += Marshal.SizeOf(typeof(AlcoholTrack));
currentExtraOffset += Marshal.SizeOf(typeof(AlcoholTrackExtra));
AlcoholTrackExtra trkExtra = new AlcoholTrackExtra
{
sectors = (uint)(track.TrackEndSector - track.TrackStartSector + 1)
};
// When track mode changes there's a mandatory gap, Alcohol needs it
if(track.TrackSequence == firstTrack.TrackSequence) trkExtra.pregap = 150;
else if(thisSessionTracks.TryGetValue((int)(track.TrackSequence - 1),
out AlcoholTrack previousTrack) &&
alcTrackExtras.TryGetValue((int)(track.TrackSequence - 1),
out AlcoholTrackExtra previousExtra) &&
previousTrack.mode != alcTrk.mode)
{
previousExtra.sectors -= 150;
trkExtra.pregap = 150;
alcTrackExtras.Remove((int)(track.TrackSequence - 1));
alcTrackExtras.Add((int)(track.TrackSequence - 1), previousExtra);
}
else trkExtra.pregap = 0;
alcTrackExtras.Add((int)track.TrackSequence, trkExtra);
}
if(decodedToc.HasValue &&
decodedToc.Value.TrackDescriptors.Any(t => t.SessionNumber == i && t.POINT >= 0xB0))
foreach(FullTOC.TrackDataDescriptor tocTrk in
decodedToc.Value.TrackDescriptors.Where(t => t.SessionNumber == i && t.POINT >= 0xB0))
{
thisSessionTracks.Add(tocTrk.POINT,
new AlcoholTrack
{
adrCtl = (byte)((tocTrk.ADR << 4) + tocTrk.CONTROL),
tno = tocTrk.TNO,
point = tocTrk.POINT,
min = tocTrk.Min,
sec = tocTrk.Sec,
frame = tocTrk.Frame,
zero = tocTrk.Zero,
pmin = tocTrk.PMIN,
psec = tocTrk.PSEC,
pframe = tocTrk.PFRAME,
mode = AlcoholTrackMode.NoData,
unknown = new byte[18],
unknown2 = new byte[24]
});
currentTrackOffset += Marshal.SizeOf(typeof(AlcoholTrack));
}
else if(i < sessions)
{
(byte minute, byte second, byte frame) leadoutAmsf =
LbaToMsf(writingTracks.First(t => t.TrackSession == i + 1).TrackStartSector - 150);
(byte minute, byte second, byte frame) leadoutPmsf =
LbaToMsf(writingTracks.OrderBy(t => t.TrackSession).ThenBy(t => t.TrackSequence).Last()
.TrackStartSector);
thisSessionTracks.Add(0xB0,
new AlcoholTrack
{
point = 0xB0,
adrCtl = 0x50,
zero = 0,
min = leadoutAmsf.minute,
sec = leadoutAmsf.second,
frame = leadoutAmsf.frame,
pmin = leadoutPmsf.minute,
psec = leadoutPmsf.second,
pframe = leadoutPmsf.frame,
unknown = new byte[18],
unknown2 = new byte[24]
});
thisSessionTracks.Add(0xC0,
new AlcoholTrack
{
point = 0xC0,
adrCtl = 0x50,
min = 128,
pmin = 97,
psec = 25,
unknown = new byte[18],
unknown2 = new byte[24]
});
currentTrackOffset += Marshal.SizeOf(typeof(AlcoholTrack)) * 2;
}
alcToc.Add(i, thisSessionTracks);
}
alcFooter = new AlcoholFooter
{
filenameOffset = (uint)(footerOffset + Marshal.SizeOf(typeof(AlcoholFooter))),
widechar = 1
};
byte[] filename = Encoding.Unicode.GetBytes("*.mdf"); // Yup, Alcohol stores no filename but a wildcard.
IntPtr blockPtr;
// Write header
descriptorStream.Seek(0, SeekOrigin.Begin);
byte[] block = new byte[Marshal.SizeOf(header)];
blockPtr = Marshal.AllocHGlobal(Marshal.SizeOf(header));
Marshal.StructureToPtr(header, blockPtr, true);
Marshal.Copy(blockPtr, block, 0, block.Length);
Marshal.FreeHGlobal(blockPtr);
descriptorStream.Write(block, 0, block.Length);
// Write DVD structures if pressent
if(header.structuresOffset != 0)
{
if(dmi != null)
{
descriptorStream.Seek(header.structuresOffset, SeekOrigin.Begin);
if(dmi.Length == 2052) descriptorStream.Write(dmi, 0, 2052);
else if(dmi.Length == 2048)
{
descriptorStream.Write(new byte[] {0x08, 0x02, 0x00, 0x00}, 0, 4);
descriptorStream.Write(dmi, 0, 2048);
}
}
// TODO: Create fake PFI if none present
if(pfi != null)
{
descriptorStream.Seek(header.structuresOffset + 2052, SeekOrigin.Begin);
descriptorStream.Write(pfi, pfi.Length - 2048, 2048);
}
}
// Write sessions
descriptorStream.Seek(header.sessionOffset, SeekOrigin.Begin);
foreach(AlcoholSession session in alcSessions.Values)
{
block = new byte[Marshal.SizeOf(session)];
blockPtr = Marshal.AllocHGlobal(Marshal.SizeOf(session));
Marshal.StructureToPtr(session, blockPtr, true);
Marshal.Copy(blockPtr, block, 0, block.Length);
Marshal.FreeHGlobal(blockPtr);
descriptorStream.Write(block, 0, block.Length);
}
// Write tracks
foreach(KeyValuePair<int, Dictionary<int, AlcoholTrack>> kvp in alcToc)
{
descriptorStream.Seek(alcSessions.First(t => t.Key == kvp.Key).Value.trackOffset, SeekOrigin.Begin);
foreach(AlcoholTrack track in kvp.Value.Values)
{
block = new byte[Marshal.SizeOf(track)];
blockPtr = Marshal.AllocHGlobal(Marshal.SizeOf(track));
Marshal.StructureToPtr(track, blockPtr, true);
Marshal.Copy(blockPtr, block, 0, block.Length);
Marshal.FreeHGlobal(blockPtr);
descriptorStream.Write(block, 0, block.Length);
if(isDvd) continue;
// Write extra
long position = descriptorStream.Position;
descriptorStream.Seek(track.extraOffset, SeekOrigin.Begin);
if(alcTrackExtras.TryGetValue(track.point, out AlcoholTrackExtra extra))
{
block = new byte[Marshal.SizeOf(extra)];
blockPtr = Marshal.AllocHGlobal(Marshal.SizeOf(extra));
Marshal.StructureToPtr(extra, blockPtr, true);
Marshal.Copy(blockPtr, block, 0, block.Length);
Marshal.FreeHGlobal(blockPtr);
descriptorStream.Write(block, 0, block.Length);
}
descriptorStream.Seek(position, SeekOrigin.Begin);
}
}
// Write BCA
if(bca != null)
{
descriptorStream.Seek(header.bcaOffset, SeekOrigin.Begin);
descriptorStream.Write(bca, 0, bca.Length);
}
// Write footer
descriptorStream.Seek(footerOffset, SeekOrigin.Begin);
block = new byte[Marshal.SizeOf(alcFooter)];
blockPtr = Marshal.AllocHGlobal(Marshal.SizeOf(alcFooter));
Marshal.StructureToPtr(alcFooter, blockPtr, true);
Marshal.Copy(blockPtr, block, 0, block.Length);
Marshal.FreeHGlobal(blockPtr);
descriptorStream.Write(block, 0, block.Length);
// Write filename
descriptorStream.Write(filename, 0, filename.Length);
// Write filename null termination
descriptorStream.Write(new byte[] {0, 0}, 0, 2);
descriptorStream.Flush();
descriptorStream.Close();
imageStream.Flush();
imageStream.Close();
IsWriting = false;
ErrorMessage = "";
return true;
}
public bool SetMetadata(ImageInfo metadata)
{
return true;
}
public bool SetGeometry(uint cylinders, uint heads, uint sectorsPerTrack)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool WriteSectorTag(byte[] data, ulong sectorAddress, SectorTagType tag)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
Track track =
writingTracks.FirstOrDefault(trk => sectorAddress >= trk.TrackStartSector &&
sectorAddress <= trk.TrackEndSector);
if(track.TrackSequence == 0)
{
ErrorMessage = $"Can't found track containing {sectorAddress}";
return false;
}
switch(tag)
{
case SectorTagType.CdTrackFlags:
{
if(data.Length != 1)
{
ErrorMessage = "Incorrect data size for track flags";
return false;
}
trackFlags.Add((byte)track.TrackSequence, data[0]);
return true;
}
case SectorTagType.CdSectorSubchannel:
{
if(track.TrackSubchannelType == 0)
{
ErrorMessage =
$"Trying to write subchannel to track {track.TrackSequence}, that does not have subchannel";
return false;
}
if(data.Length != 96)
{
ErrorMessage = "Incorrect data size for subchannel";
return false;
}
imageStream
.Seek((long)(track.TrackFileOffset + (sectorAddress - track.TrackStartSector) * (ulong)(track.TrackRawBytesPerSector + 96)) + track.TrackRawBytesPerSector,
SeekOrigin.Begin);
imageStream.Write(data, 0, data.Length);
return true;
}
default:
ErrorMessage = $"Unsupported tag type {tag}";
return false;
}
}
public bool WriteSectorsTag(byte[] data, ulong sectorAddress, uint length, SectorTagType tag)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
Track track =
writingTracks.FirstOrDefault(trk => sectorAddress >= trk.TrackStartSector &&
sectorAddress <= trk.TrackEndSector);
if(track.TrackSequence == 0)
{
ErrorMessage = $"Can't found track containing {sectorAddress}";
return false;
}
switch(tag)
{
case SectorTagType.CdTrackFlags: return WriteSectorTag(data, sectorAddress, tag);
case SectorTagType.CdSectorSubchannel:
{
if(track.TrackSubchannelType == 0)
{
ErrorMessage =
$"Trying to write subchannel to track {track.TrackSequence}, that does not have subchannel";
return false;
}
if(data.Length % 96 != 0)
{
ErrorMessage = "Incorrect data size for subchannel";
return false;
}
for(uint i = 0; i < length; i++)
{
imageStream
.Seek((long)(track.TrackFileOffset + (i + sectorAddress - track.TrackStartSector) * (ulong)(track.TrackRawBytesPerSector + 96)) + track.TrackRawBytesPerSector,
SeekOrigin.Begin);
imageStream.Write(data, (int)(i * 96), 96);
}
return true;
}
default:
ErrorMessage = $"Unsupported tag type {tag}";
return false;
}
}
public bool SetDumpHardware(List<DumpHardwareType> dumpHardware)
{
// Not supported
return false;
}
public bool SetCicmMetadata(CICMMetadataType metadata)
{
// Not supported
return false;
}
}
}

View File

@@ -1,551 +0,0 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Anex86.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Manages Anex86 disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Exceptions;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
using DiscImageChef.Console;
using Schemas;
namespace DiscImageChef.DiscImages
{
public class Anex86 : IWritableImage
{
IFilter anexImageFilter;
Anex86Header fdihdr;
ImageInfo imageInfo;
FileStream writingStream;
public Anex86()
{
imageInfo = new ImageInfo
{
ReadableSectorTags = new List<SectorTagType>(),
ReadableMediaTags = new List<MediaTagType>(),
HasPartitions = false,
HasSessions = false,
Version = null,
Application = null,
ApplicationVersion = null,
Creator = null,
Comments = null,
MediaManufacturer = null,
MediaModel = null,
MediaSerialNumber = null,
MediaBarcode = null,
MediaPartNumber = null,
MediaSequence = 0,
LastMediaSequence = 0,
DriveManufacturer = null,
DriveModel = null,
DriveSerialNumber = null,
DriveFirmwareRevision = null
};
}
public ImageInfo Info => imageInfo;
public string Name => "Anex86 Disk Image";
public Guid Id => new Guid("0410003E-6E7B-40E6-9328-BA5651ADF6B7");
public string Format => "Anex86 disk image";
public List<Partition> Partitions =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<Track> Tracks =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<Session> Sessions =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public bool Identify(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin);
fdihdr = new Anex86Header();
if(stream.Length < Marshal.SizeOf(fdihdr)) return false;
byte[] hdrB = new byte[Marshal.SizeOf(fdihdr)];
stream.Read(hdrB, 0, hdrB.Length);
GCHandle handle = GCHandle.Alloc(hdrB, GCHandleType.Pinned);
fdihdr = (Anex86Header)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(Anex86Header));
handle.Free();
DicConsole.DebugWriteLine("Anex86 plugin", "fdihdr.unknown = {0}", fdihdr.unknown);
DicConsole.DebugWriteLine("Anex86 plugin", "fdihdr.hddtype = {0}", fdihdr.hddtype);
DicConsole.DebugWriteLine("Anex86 plugin", "fdihdr.hdrSize = {0}", fdihdr.hdrSize);
DicConsole.DebugWriteLine("Anex86 plugin", "fdihdr.dskSize = {0}", fdihdr.dskSize);
DicConsole.DebugWriteLine("Anex86 plugin", "fdihdr.bps = {0}", fdihdr.bps);
DicConsole.DebugWriteLine("Anex86 plugin", "fdihdr.spt = {0}", fdihdr.spt);
DicConsole.DebugWriteLine("Anex86 plugin", "fdihdr.heads = {0}", fdihdr.heads);
DicConsole.DebugWriteLine("Anex86 plugin", "fdihdr.cylinders = {0}", fdihdr.cylinders);
return stream.Length == fdihdr.hdrSize + fdihdr.dskSize &&
fdihdr.dskSize == fdihdr.bps * fdihdr.spt * fdihdr.heads * fdihdr.cylinders;
}
public bool Open(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin);
fdihdr = new Anex86Header();
if(stream.Length < Marshal.SizeOf(fdihdr)) return false;
byte[] hdrB = new byte[Marshal.SizeOf(fdihdr)];
stream.Read(hdrB, 0, hdrB.Length);
GCHandle handle = GCHandle.Alloc(hdrB, GCHandleType.Pinned);
fdihdr = (Anex86Header)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(Anex86Header));
handle.Free();
imageInfo.MediaType = Geometry.GetMediaType(((ushort)fdihdr.cylinders, (byte)fdihdr.heads,
(ushort)fdihdr.spt, (uint)fdihdr.bps, MediaEncoding.MFM,
false));
if(imageInfo.MediaType == MediaType.Unknown) imageInfo.MediaType = MediaType.GENERIC_HDD;
DicConsole.DebugWriteLine("Anex86 plugin", "MediaType: {0}", imageInfo.MediaType);
imageInfo.ImageSize = (ulong)fdihdr.dskSize;
imageInfo.CreationTime = imageFilter.GetCreationTime();
imageInfo.LastModificationTime = imageFilter.GetLastWriteTime();
imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename());
imageInfo.Sectors = (ulong)(fdihdr.cylinders * fdihdr.heads * fdihdr.spt);
imageInfo.XmlMediaType = XmlMediaType.BlockMedia;
imageInfo.SectorSize = (uint)fdihdr.bps;
imageInfo.Cylinders = (uint)fdihdr.cylinders;
imageInfo.Heads = (uint)fdihdr.heads;
imageInfo.SectorsPerTrack = (uint)fdihdr.spt;
anexImageFilter = imageFilter;
return true;
}
public byte[] ReadSector(ulong sectorAddress)
{
return ReadSectors(sectorAddress, 1);
}
public byte[] ReadSectors(ulong sectorAddress, uint length)
{
if(sectorAddress > imageInfo.Sectors - 1)
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
if(sectorAddress + length > imageInfo.Sectors)
throw new ArgumentOutOfRangeException(nameof(length), "Requested more sectors than available");
byte[] buffer = new byte[length * imageInfo.SectorSize];
Stream stream = anexImageFilter.GetDataForkStream();
stream.Seek((long)((ulong)fdihdr.hdrSize + sectorAddress * imageInfo.SectorSize), SeekOrigin.Begin);
stream.Read(buffer, 0, (int)(length * imageInfo.SectorSize));
return buffer;
}
public byte[] ReadDiskTag(MediaTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorTag(ulong sectorAddress, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSector(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorTag(ulong sectorAddress, uint track, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectors(ulong sectorAddress, uint length, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, uint track, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorLong(ulong sectorAddress)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorLong(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsLong(ulong sectorAddress, uint length)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsLong(ulong sectorAddress, uint length, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public List<Track> GetSessionTracks(Session session)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public List<Track> GetSessionTracks(ushort session)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public bool? VerifySector(ulong sectorAddress)
{
return null;
}
public bool? VerifySector(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public bool? VerifySectors(ulong sectorAddress, uint length, out List<ulong> failingLbas,
out List<ulong> unknownLbas)
{
failingLbas = new List<ulong>();
unknownLbas = new List<ulong>();
for(ulong i = 0; i < imageInfo.Sectors; i++) unknownLbas.Add(i);
return null;
}
public bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List<ulong> failingLbas,
out List<ulong> unknownLbas)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public bool? VerifyMediaImage()
{
return null;
}
public List<DumpHardwareType> DumpHardware => null;
public CICMMetadataType CicmMetadata => null;
public IEnumerable<MediaTagType> SupportedMediaTags => new MediaTagType[] { };
public IEnumerable<SectorTagType> SupportedSectorTags => new SectorTagType[] { };
// TODO: Test with real hardware to see real supported media
public IEnumerable<MediaType> SupportedMediaTypes =>
new[]
{
MediaType.IBM23FD, MediaType.ECMA_66, MediaType.DOS_525_SS_DD_8, MediaType.DOS_525_SS_DD_9,
MediaType.ACORN_525_SS_SD_40, MediaType.ACORN_525_SS_DD_40, MediaType.ATARI_525_SD,
MediaType.ATARI_525_DD, MediaType.ATARI_525_ED, MediaType.DOS_525_DS_DD_8, MediaType.DOS_525_DS_DD_9,
MediaType.ECMA_70, MediaType.Apricot_35, MediaType.RX01, MediaType.RX02, MediaType.NEC_525_HD,
MediaType.ECMA_99_15, MediaType.NEC_8_SD, MediaType.RX03, MediaType.DOS_35_SS_DD_8,
MediaType.DOS_35_SS_DD_9, MediaType.ACORN_525_SS_SD_80, MediaType.RX50, MediaType.ATARI_35_SS_DD_11,
MediaType.ACORN_525_SS_DD_80, MediaType.ACORN_35_DS_DD, MediaType.DOS_35_DS_DD_8,
MediaType.DOS_35_DS_DD_9, MediaType.ACORN_35_DS_HD, MediaType.DOS_525_HD, MediaType.ACORN_525_DS_DD,
MediaType.DOS_35_HD, MediaType.XDF_525, MediaType.DMF, MediaType.XDF_35, MediaType.DOS_35_ED,
MediaType.FDFORMAT_35_DD, MediaType.FDFORMAT_525_HD, MediaType.FDFORMAT_35_HD, MediaType.NEC_35_TD,
MediaType.Unknown, MediaType.GENERIC_HDD, MediaType.FlashDrive, MediaType.CompactFlash,
MediaType.CompactFlashType2, MediaType.PCCardTypeI, MediaType.PCCardTypeII, MediaType.PCCardTypeIII,
MediaType.PCCardTypeIV
};
public IEnumerable<(string name, Type type, string description)> SupportedOptions =>
new (string name, Type type, string description)[] { };
public IEnumerable<string> KnownExtensions => new[] {".fdi", ".hdi"};
public bool IsWriting { get; private set; }
public string ErrorMessage { get; private set; }
public bool Create(string path, MediaType mediaType, Dictionary<string, string> options, ulong sectors,
uint sectorSize)
{
if(sectorSize == 0)
{
ErrorMessage = "Unsupported sector size";
return false;
}
if(sectors * sectorSize > int.MaxValue || sectors > (long)int.MaxValue * 8 * 33)
{
ErrorMessage = "Too many sectors";
return false;
}
if(!SupportedMediaTypes.Contains(mediaType))
{
ErrorMessage = $"Unsupport media format {mediaType}";
return false;
}
imageInfo = new ImageInfo {MediaType = mediaType, SectorSize = sectorSize, Sectors = sectors};
try { writingStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); }
catch(IOException e)
{
ErrorMessage = $"Could not create new image file, exception {e.Message}";
return false;
}
fdihdr = new Anex86Header {hdrSize = 4096, dskSize = (int)(sectors * sectorSize), bps = (int)sectorSize};
IsWriting = true;
ErrorMessage = null;
return true;
}
public bool WriteMediaTag(byte[] data, MediaTagType tag)
{
ErrorMessage = "Writing media tags is not supported.";
return false;
}
public bool WriteSector(byte[] data, ulong sectorAddress)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
if(data.Length != imageInfo.SectorSize)
{
ErrorMessage = "Incorrect data size";
return false;
}
if(sectorAddress >= imageInfo.Sectors)
{
ErrorMessage = "Tried to write past image size";
return false;
}
writingStream.Seek((long)(4096 + sectorAddress * imageInfo.SectorSize), SeekOrigin.Begin);
writingStream.Write(data, 0, data.Length);
ErrorMessage = "";
return true;
}
public bool WriteSectors(byte[] data, ulong sectorAddress, uint length)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
if(data.Length % imageInfo.SectorSize != 0)
{
ErrorMessage = "Incorrect data size";
return false;
}
if(sectorAddress + length > imageInfo.Sectors)
{
ErrorMessage = "Tried to write past image size";
return false;
}
writingStream.Seek((long)(4096 + sectorAddress * imageInfo.SectorSize), SeekOrigin.Begin);
writingStream.Write(data, 0, data.Length);
ErrorMessage = "";
return true;
}
public bool WriteSectorLong(byte[] data, ulong sectorAddress)
{
ErrorMessage = "Writing sectors with tags is not supported.";
return false;
}
public bool WriteSectorsLong(byte[] data, ulong sectorAddress, uint length)
{
ErrorMessage = "Writing sectors with tags is not supported.";
return false;
}
public bool SetTracks(List<Track> tracks)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool Close()
{
if(!IsWriting)
{
ErrorMessage = "Image is not opened for writing";
return false;
}
if((imageInfo.MediaType == MediaType.Unknown || imageInfo.MediaType == MediaType.GENERIC_HDD ||
imageInfo.MediaType == MediaType.FlashDrive || imageInfo.MediaType == MediaType.CompactFlash ||
imageInfo.MediaType == MediaType.CompactFlashType2 || imageInfo.MediaType == MediaType.PCCardTypeI ||
imageInfo.MediaType == MediaType.PCCardTypeII || imageInfo.MediaType == MediaType.PCCardTypeIII ||
imageInfo.MediaType == MediaType.PCCardTypeIV) && fdihdr.cylinders == 0)
{
fdihdr.cylinders = (int)(imageInfo.Sectors / 8 / 33);
fdihdr.heads = 8;
fdihdr.spt = 33;
while(fdihdr.cylinders == 0)
{
fdihdr.heads--;
if(fdihdr.heads == 0)
{
fdihdr.spt--;
fdihdr.heads = 8;
}
fdihdr.cylinders = (int)imageInfo.Sectors / fdihdr.heads / fdihdr.spt;
if(fdihdr.cylinders == 0 && fdihdr.heads == 0 && fdihdr.spt == 0) break;
}
}
byte[] hdr = new byte[Marshal.SizeOf(fdihdr)];
IntPtr hdrPtr = Marshal.AllocHGlobal(Marshal.SizeOf(fdihdr));
Marshal.StructureToPtr(fdihdr, hdrPtr, true);
Marshal.Copy(hdrPtr, hdr, 0, hdr.Length);
Marshal.FreeHGlobal(hdrPtr);
writingStream.Seek(0, SeekOrigin.Begin);
writingStream.Write(hdr, 0, hdr.Length);
writingStream.Flush();
writingStream.Close();
IsWriting = false;
ErrorMessage = "";
return true;
}
public bool SetMetadata(ImageInfo metadata)
{
return true;
}
public bool SetGeometry(uint cylinders, uint heads, uint sectorsPerTrack)
{
if(cylinders > int.MaxValue)
{
ErrorMessage = "Too many cylinders.";
return false;
}
if(heads > int.MaxValue)
{
ErrorMessage = "Too many heads.";
return false;
}
if(sectorsPerTrack > int.MaxValue)
{
ErrorMessage = "Too many sectors per track.";
return false;
}
fdihdr.spt = (int)sectorsPerTrack;
fdihdr.heads = (int)heads;
fdihdr.cylinders = (int)cylinders;
return true;
}
public bool WriteSectorTag(byte[] data, ulong sectorAddress, SectorTagType tag)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool WriteSectorsTag(byte[] data, ulong sectorAddress, uint length, SectorTagType tag)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool SetDumpHardware(List<DumpHardwareType> dumpHardware)
{
// Not supported
return false;
}
public bool SetCicmMetadata(CICMMetadataType metadata)
{
// Not supported
return false;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Anex86Header
{
public int unknown;
public int hddtype;
public int hdrSize;
public int dskSize;
public int bps;
public int spt;
public int heads;
public int cylinders;
}
}
}

View File

@@ -0,0 +1,75 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Anex86.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Manages Anex86 disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Collections.Generic;
using System.IO;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
namespace DiscImageChef.DiscImages
{
public partial class Anex86 : IWritableImage
{
IFilter anexImageFilter;
Anex86Header fdihdr;
ImageInfo imageInfo;
FileStream writingStream;
public Anex86()
{
imageInfo = new ImageInfo
{
ReadableSectorTags = new List<SectorTagType>(),
ReadableMediaTags = new List<MediaTagType>(),
HasPartitions = false,
HasSessions = false,
Version = null,
Application = null,
ApplicationVersion = null,
Creator = null,
Comments = null,
MediaManufacturer = null,
MediaModel = null,
MediaSerialNumber = null,
MediaBarcode = null,
MediaPartNumber = null,
MediaSequence = 0,
LastMediaSequence = 0,
DriveManufacturer = null,
DriveModel = null,
DriveSerialNumber = null,
DriveFirmwareRevision = null
};
}
}
}

View File

@@ -0,0 +1,71 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Identify.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Identifies Anex86 disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.IO;
using System.Runtime.InteropServices;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.Console;
namespace DiscImageChef.DiscImages
{
public partial class Anex86
{
public bool Identify(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin);
fdihdr = new Anex86Header();
if(stream.Length < Marshal.SizeOf(fdihdr)) return false;
byte[] hdrB = new byte[Marshal.SizeOf(fdihdr)];
stream.Read(hdrB, 0, hdrB.Length);
GCHandle handle = GCHandle.Alloc(hdrB, GCHandleType.Pinned);
fdihdr = (Anex86Header)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(Anex86Header));
handle.Free();
DicConsole.DebugWriteLine("Anex86 plugin", "fdihdr.unknown = {0}", fdihdr.unknown);
DicConsole.DebugWriteLine("Anex86 plugin", "fdihdr.hddtype = {0}", fdihdr.hddtype);
DicConsole.DebugWriteLine("Anex86 plugin", "fdihdr.hdrSize = {0}", fdihdr.hdrSize);
DicConsole.DebugWriteLine("Anex86 plugin", "fdihdr.dskSize = {0}", fdihdr.dskSize);
DicConsole.DebugWriteLine("Anex86 plugin", "fdihdr.bps = {0}", fdihdr.bps);
DicConsole.DebugWriteLine("Anex86 plugin", "fdihdr.spt = {0}", fdihdr.spt);
DicConsole.DebugWriteLine("Anex86 plugin", "fdihdr.heads = {0}", fdihdr.heads);
DicConsole.DebugWriteLine("Anex86 plugin", "fdihdr.cylinders = {0}", fdihdr.cylinders);
return stream.Length == fdihdr.hdrSize + fdihdr.dskSize &&
fdihdr.dskSize == fdihdr.bps * fdihdr.spt * fdihdr.heads * fdihdr.cylinders;
}
}
}

View File

@@ -0,0 +1,90 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Properties.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains properties for Anex86 disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Exceptions;
using DiscImageChef.CommonTypes.Structs;
using Schemas;
namespace DiscImageChef.DiscImages
{
public partial class Anex86
{
public ImageInfo Info => imageInfo;
public string Name => "Anex86 Disk Image";
public Guid Id => new Guid("0410003E-6E7B-40E6-9328-BA5651ADF6B7");
public string Format => "Anex86 disk image";
public List<Partition> Partitions =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<Track> Tracks =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<Session> Sessions =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<DumpHardwareType> DumpHardware => null;
public CICMMetadataType CicmMetadata => null;
public IEnumerable<MediaTagType> SupportedMediaTags => new MediaTagType[] { };
public IEnumerable<SectorTagType> SupportedSectorTags => new SectorTagType[] { };
// TODO: Test with real hardware to see real supported media
public IEnumerable<MediaType> SupportedMediaTypes =>
new[]
{
MediaType.IBM23FD, MediaType.ECMA_66, MediaType.DOS_525_SS_DD_8, MediaType.DOS_525_SS_DD_9,
MediaType.ACORN_525_SS_SD_40, MediaType.ACORN_525_SS_DD_40, MediaType.ATARI_525_SD,
MediaType.ATARI_525_DD, MediaType.ATARI_525_ED, MediaType.DOS_525_DS_DD_8, MediaType.DOS_525_DS_DD_9,
MediaType.ECMA_70, MediaType.Apricot_35, MediaType.RX01, MediaType.RX02, MediaType.NEC_525_HD,
MediaType.ECMA_99_15, MediaType.NEC_8_SD, MediaType.RX03, MediaType.DOS_35_SS_DD_8,
MediaType.DOS_35_SS_DD_9, MediaType.ACORN_525_SS_SD_80, MediaType.RX50, MediaType.ATARI_35_SS_DD_11,
MediaType.ACORN_525_SS_DD_80, MediaType.ACORN_35_DS_DD, MediaType.DOS_35_DS_DD_8,
MediaType.DOS_35_DS_DD_9, MediaType.ACORN_35_DS_HD, MediaType.DOS_525_HD, MediaType.ACORN_525_DS_DD,
MediaType.DOS_35_HD, MediaType.XDF_525, MediaType.DMF, MediaType.XDF_35, MediaType.DOS_35_ED,
MediaType.FDFORMAT_35_DD, MediaType.FDFORMAT_525_HD, MediaType.FDFORMAT_35_HD, MediaType.NEC_35_TD,
MediaType.Unknown, MediaType.GENERIC_HDD, MediaType.FlashDrive, MediaType.CompactFlash,
MediaType.CompactFlashType2, MediaType.PCCardTypeI, MediaType.PCCardTypeII, MediaType.PCCardTypeIII,
MediaType.PCCardTypeIV
};
public IEnumerable<(string name, Type type, string description)> SupportedOptions =>
new (string name, Type type, string description)[] { };
public IEnumerable<string> KnownExtensions => new[] {".fdi", ".hdi"};
public bool IsWriting { get; private set; }
public string ErrorMessage { get; private set; }
}
}

View File

@@ -0,0 +1,129 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Read.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Reads Anex86 disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.Console;
namespace DiscImageChef.DiscImages
{
public partial class Anex86
{
public bool Open(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin);
fdihdr = new Anex86Header();
if(stream.Length < Marshal.SizeOf(fdihdr)) return false;
byte[] hdrB = new byte[Marshal.SizeOf(fdihdr)];
stream.Read(hdrB, 0, hdrB.Length);
GCHandle handle = GCHandle.Alloc(hdrB, GCHandleType.Pinned);
fdihdr = (Anex86Header)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(Anex86Header));
handle.Free();
imageInfo.MediaType = Geometry.GetMediaType(((ushort)fdihdr.cylinders, (byte)fdihdr.heads,
(ushort)fdihdr.spt, (uint)fdihdr.bps, MediaEncoding.MFM,
false));
if(imageInfo.MediaType == MediaType.Unknown) imageInfo.MediaType = MediaType.GENERIC_HDD;
DicConsole.DebugWriteLine("Anex86 plugin", "MediaType: {0}", imageInfo.MediaType);
imageInfo.ImageSize = (ulong)fdihdr.dskSize;
imageInfo.CreationTime = imageFilter.GetCreationTime();
imageInfo.LastModificationTime = imageFilter.GetLastWriteTime();
imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename());
imageInfo.Sectors = (ulong)(fdihdr.cylinders * fdihdr.heads * fdihdr.spt);
imageInfo.XmlMediaType = XmlMediaType.BlockMedia;
imageInfo.SectorSize = (uint)fdihdr.bps;
imageInfo.Cylinders = (uint)fdihdr.cylinders;
imageInfo.Heads = (uint)fdihdr.heads;
imageInfo.SectorsPerTrack = (uint)fdihdr.spt;
anexImageFilter = imageFilter;
return true;
}
public byte[] ReadSector(ulong sectorAddress)
{
return ReadSectors(sectorAddress, 1);
}
public byte[] ReadSectors(ulong sectorAddress, uint length)
{
if(sectorAddress > imageInfo.Sectors - 1)
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
if(sectorAddress + length > imageInfo.Sectors)
throw new ArgumentOutOfRangeException(nameof(length), "Requested more sectors than available");
byte[] buffer = new byte[length * imageInfo.SectorSize];
Stream stream = anexImageFilter.GetDataForkStream();
stream.Seek((long)((ulong)fdihdr.hdrSize + sectorAddress * imageInfo.SectorSize), SeekOrigin.Begin);
stream.Read(buffer, 0, (int)(length * imageInfo.SectorSize));
return buffer;
}
public bool? VerifySector(ulong sectorAddress)
{
return null;
}
public bool? VerifySectors(ulong sectorAddress, uint length, out List<ulong> failingLbas,
out List<ulong> unknownLbas)
{
failingLbas = new List<ulong>();
unknownLbas = new List<ulong>();
for(ulong i = 0; i < imageInfo.Sectors; i++) unknownLbas.Add(i);
return null;
}
public bool? VerifyMediaImage()
{
return null;
}
}
}

View File

@@ -0,0 +1,52 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Structs.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains structures for Anex86 disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Runtime.InteropServices;
namespace DiscImageChef.DiscImages
{
public partial class Anex86
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Anex86Header
{
public int unknown;
public int hddtype;
public int hdrSize;
public int dskSize;
public int bps;
public int spt;
public int heads;
public int cylinders;
}
}
}

View File

@@ -0,0 +1,119 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Unsupported.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains features unsupported by Anex86 disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Collections.Generic;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Exceptions;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
namespace DiscImageChef.DiscImages
{
public partial class Anex86 : IWritableImage
{
public byte[] ReadDiskTag(MediaTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorTag(ulong sectorAddress, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSector(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorTag(ulong sectorAddress, uint track, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectors(ulong sectorAddress, uint length, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, uint track, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorLong(ulong sectorAddress)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorLong(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsLong(ulong sectorAddress, uint length)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsLong(ulong sectorAddress, uint length, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public List<Track> GetSessionTracks(Session session)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public List<Track> GetSessionTracks(ushort session)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public bool? VerifySector(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List<ulong> failingLbas,
out List<ulong> unknownLbas)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
}
}

View File

@@ -0,0 +1,269 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Write.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Writes Anex86 disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Structs;
using Schemas;
namespace DiscImageChef.DiscImages
{
public partial class Anex86
{
public bool Create(string path, MediaType mediaType, Dictionary<string, string> options, ulong sectors,
uint sectorSize)
{
if(sectorSize == 0)
{
ErrorMessage = "Unsupported sector size";
return false;
}
if(sectors * sectorSize > int.MaxValue || sectors > (long)int.MaxValue * 8 * 33)
{
ErrorMessage = "Too many sectors";
return false;
}
if(!SupportedMediaTypes.Contains(mediaType))
{
ErrorMessage = $"Unsupport media format {mediaType}";
return false;
}
imageInfo = new ImageInfo {MediaType = mediaType, SectorSize = sectorSize, Sectors = sectors};
try { writingStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); }
catch(IOException e)
{
ErrorMessage = $"Could not create new image file, exception {e.Message}";
return false;
}
fdihdr = new Anex86Header {hdrSize = 4096, dskSize = (int)(sectors * sectorSize), bps = (int)sectorSize};
IsWriting = true;
ErrorMessage = null;
return true;
}
public bool WriteMediaTag(byte[] data, MediaTagType tag)
{
ErrorMessage = "Writing media tags is not supported.";
return false;
}
public bool WriteSector(byte[] data, ulong sectorAddress)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
if(data.Length != imageInfo.SectorSize)
{
ErrorMessage = "Incorrect data size";
return false;
}
if(sectorAddress >= imageInfo.Sectors)
{
ErrorMessage = "Tried to write past image size";
return false;
}
writingStream.Seek((long)(4096 + sectorAddress * imageInfo.SectorSize), SeekOrigin.Begin);
writingStream.Write(data, 0, data.Length);
ErrorMessage = "";
return true;
}
public bool WriteSectors(byte[] data, ulong sectorAddress, uint length)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
if(data.Length % imageInfo.SectorSize != 0)
{
ErrorMessage = "Incorrect data size";
return false;
}
if(sectorAddress + length > imageInfo.Sectors)
{
ErrorMessage = "Tried to write past image size";
return false;
}
writingStream.Seek((long)(4096 + sectorAddress * imageInfo.SectorSize), SeekOrigin.Begin);
writingStream.Write(data, 0, data.Length);
ErrorMessage = "";
return true;
}
public bool WriteSectorLong(byte[] data, ulong sectorAddress)
{
ErrorMessage = "Writing sectors with tags is not supported.";
return false;
}
public bool WriteSectorsLong(byte[] data, ulong sectorAddress, uint length)
{
ErrorMessage = "Writing sectors with tags is not supported.";
return false;
}
public bool SetTracks(List<Track> tracks)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool Close()
{
if(!IsWriting)
{
ErrorMessage = "Image is not opened for writing";
return false;
}
if((imageInfo.MediaType == MediaType.Unknown || imageInfo.MediaType == MediaType.GENERIC_HDD ||
imageInfo.MediaType == MediaType.FlashDrive || imageInfo.MediaType == MediaType.CompactFlash ||
imageInfo.MediaType == MediaType.CompactFlashType2 || imageInfo.MediaType == MediaType.PCCardTypeI ||
imageInfo.MediaType == MediaType.PCCardTypeII || imageInfo.MediaType == MediaType.PCCardTypeIII ||
imageInfo.MediaType == MediaType.PCCardTypeIV) && fdihdr.cylinders == 0)
{
fdihdr.cylinders = (int)(imageInfo.Sectors / 8 / 33);
fdihdr.heads = 8;
fdihdr.spt = 33;
while(fdihdr.cylinders == 0)
{
fdihdr.heads--;
if(fdihdr.heads == 0)
{
fdihdr.spt--;
fdihdr.heads = 8;
}
fdihdr.cylinders = (int)imageInfo.Sectors / fdihdr.heads / fdihdr.spt;
if(fdihdr.cylinders == 0 && fdihdr.heads == 0 && fdihdr.spt == 0) break;
}
}
byte[] hdr = new byte[Marshal.SizeOf(fdihdr)];
IntPtr hdrPtr = Marshal.AllocHGlobal(Marshal.SizeOf(fdihdr));
Marshal.StructureToPtr(fdihdr, hdrPtr, true);
Marshal.Copy(hdrPtr, hdr, 0, hdr.Length);
Marshal.FreeHGlobal(hdrPtr);
writingStream.Seek(0, SeekOrigin.Begin);
writingStream.Write(hdr, 0, hdr.Length);
writingStream.Flush();
writingStream.Close();
IsWriting = false;
ErrorMessage = "";
return true;
}
public bool SetMetadata(ImageInfo metadata)
{
return true;
}
public bool SetGeometry(uint cylinders, uint heads, uint sectorsPerTrack)
{
if(cylinders > int.MaxValue)
{
ErrorMessage = "Too many cylinders.";
return false;
}
if(heads > int.MaxValue)
{
ErrorMessage = "Too many heads.";
return false;
}
if(sectorsPerTrack > int.MaxValue)
{
ErrorMessage = "Too many sectors per track.";
return false;
}
fdihdr.spt = (int)sectorsPerTrack;
fdihdr.heads = (int)heads;
fdihdr.cylinders = (int)cylinders;
return true;
}
public bool WriteSectorTag(byte[] data, ulong sectorAddress, SectorTagType tag)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool WriteSectorsTag(byte[] data, ulong sectorAddress, uint length, SectorTagType tag)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool SetDumpHardware(List<DumpHardwareType> dumpHardware)
{
// Not supported
return false;
}
public bool SetCicmMetadata(CICMMetadataType metadata)
{
// Not supported
return false;
}
}
}

View File

@@ -1,852 +0,0 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Apple2MG.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Manages XGS emulator disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Exceptions;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
using DiscImageChef.Console;
using DiscImageChef.Filters;
using Schemas;
namespace DiscImageChef.DiscImages
{
public class Apple2Mg : IWritableImage
{
/// <summary>
/// Magic number, "2IMG"
/// </summary>
const uint MAGIC = 0x474D4932;
/// <summary>
/// Disk image created by ASIMOV2, "!nfc"
/// </summary>
const uint CREATOR_ASIMOV = 0x63666E21;
/// <summary>
/// Disk image created by Bernie ][ the Rescue, "B2TR"
/// </summary>
const uint CREATOR_BERNIE = 0x52543242;
/// <summary>
/// Disk image created by Catakig, "CTKG"
/// </summary>
const uint CREATOR_CATAKIG = 0x474B5443;
/// <summary>
/// Disk image created by Sheppy's ImageMaker, "ShIm"
/// </summary>
const uint CREATOR_SHEPPY = 0x6D496853;
/// <summary>
/// Disk image created by Sweet16, "WOOF"
/// </summary>
const uint CREATOR_SWEET = 0x464F4F57;
/// <summary>
/// Disk image created by XGS, "XGS!"
/// </summary>
const uint CREATOR_XGS = 0x21534758;
/// <summary>
/// Disk image created by CiderPress, "CdrP"
/// </summary>
const uint CREATOR_CIDER = 0x50726443;
/// <summary>
/// Disk image created by DiscImageChef, "dic "
/// </summary>
const uint CREATOR_DIC = 0x20636964;
const uint LOCKED_DISK = 0x80000000;
const uint VALID_VOLUME_NUMBER = 0x00000100;
const uint VOLUME_NUMBER_MASK = 0x000000FF;
readonly int[] deinterleave = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
readonly int[] interleave = {0, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 15};
IFilter a2MgImageFilter;
byte[] decodedImage;
A2ImgHeader imageHeader;
ImageInfo imageInfo;
FileStream writingStream;
public Apple2Mg()
{
imageInfo = new ImageInfo
{
ReadableSectorTags = new List<SectorTagType>(),
ReadableMediaTags = new List<MediaTagType>(),
HasPartitions = false,
HasSessions = false,
Version = null,
Application = null,
ApplicationVersion = null,
Creator = null,
Comments = null,
MediaManufacturer = null,
MediaModel = null,
MediaSerialNumber = null,
MediaBarcode = null,
MediaPartNumber = null,
MediaSequence = 0,
LastMediaSequence = 0,
DriveManufacturer = null,
DriveModel = null,
DriveSerialNumber = null,
DriveFirmwareRevision = null
};
}
public ImageInfo Info => imageInfo;
public string Name => "Apple 2IMG";
public Guid Id => new Guid("CBAF8824-BA5F-415F-953A-19A03519B2D1");
public string Format => "Apple 2IMG";
public List<Partition> Partitions =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<Track> Tracks =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<Session> Sessions =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public bool Identify(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin);
if(stream.Length < 65) return false;
byte[] header = new byte[64];
stream.Read(header, 0, 64);
uint magic = BitConverter.ToUInt32(header, 0x00);
if(magic != MAGIC) return false;
uint dataoff = BitConverter.ToUInt32(header, 0x18);
if(dataoff > stream.Length) return false;
uint datasize = BitConverter.ToUInt32(header, 0x1C);
// There seems to be incorrect endian in some images on the wild
if(datasize == 0x00800C00) datasize = 0x000C8000;
if(dataoff + datasize > stream.Length) return false;
uint commentoff = BitConverter.ToUInt32(header, 0x20);
if(commentoff > stream.Length) return false;
uint commentsize = BitConverter.ToUInt32(header, 0x24);
if(commentoff + commentsize > stream.Length) return false;
uint creatoroff = BitConverter.ToUInt32(header, 0x28);
if(creatoroff > stream.Length) return false;
uint creatorsize = BitConverter.ToUInt32(header, 0x2C);
return creatoroff + creatorsize <= stream.Length;
}
public bool Open(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin);
imageHeader = new A2ImgHeader();
byte[] header = new byte[64];
stream.Read(header, 0, 64);
byte[] magic = new byte[4];
byte[] creator = new byte[4];
Array.Copy(header, 0, magic, 0, 4);
Array.Copy(header, 4, creator, 0, 4);
imageHeader.Magic = BitConverter.ToUInt32(header, 0x00);
imageHeader.Creator = BitConverter.ToUInt32(header, 0x04);
imageHeader.HeaderSize = BitConverter.ToUInt16(header, 0x08);
imageHeader.Version = BitConverter.ToUInt16(header, 0x0A);
imageHeader.ImageFormat = (SectorOrder)BitConverter.ToUInt32(header, 0x0C);
imageHeader.Flags = BitConverter.ToUInt32(header, 0x10);
imageHeader.Blocks = BitConverter.ToUInt32(header, 0x14);
imageHeader.DataOffset = BitConverter.ToUInt32(header, 0x18);
imageHeader.DataSize = BitConverter.ToUInt32(header, 0x1C);
imageHeader.CommentOffset = BitConverter.ToUInt32(header, 0x20);
imageHeader.CommentSize = BitConverter.ToUInt32(header, 0x24);
imageHeader.CreatorSpecificOffset = BitConverter.ToUInt32(header, 0x28);
imageHeader.CreatorSpecificSize = BitConverter.ToUInt32(header, 0x2C);
imageHeader.Reserved1 = BitConverter.ToUInt32(header, 0x30);
imageHeader.Reserved2 = BitConverter.ToUInt32(header, 0x34);
imageHeader.Reserved3 = BitConverter.ToUInt32(header, 0x38);
imageHeader.Reserved4 = BitConverter.ToUInt32(header, 0x3C);
if(imageHeader.DataSize == 0x00800C00)
{
imageHeader.DataSize = 0x000C8000;
DicConsole.DebugWriteLine("2MG plugin", "Detected incorrect endian on data size field, correcting.");
}
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.magic = \"{0}\"",
Encoding.ASCII.GetString(magic));
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.creator = \"{0}\"",
Encoding.ASCII.GetString(creator));
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.headerSize = {0}", imageHeader.HeaderSize);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.version = {0}", imageHeader.Version);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.imageFormat = {0}", imageHeader.ImageFormat);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.flags = 0x{0:X8}", imageHeader.Flags);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.blocks = {0}", imageHeader.Blocks);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.dataOffset = 0x{0:X8}", imageHeader.DataOffset);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.dataSize = {0}", imageHeader.DataSize);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.commentOffset = 0x{0:X8}", imageHeader.CommentOffset);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.commentSize = {0}", imageHeader.CommentSize);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.creatorSpecificOffset = 0x{0:X8}",
imageHeader.CreatorSpecificOffset);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.creatorSpecificSize = {0}",
imageHeader.CreatorSpecificSize);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.reserved1 = 0x{0:X8}", imageHeader.Reserved1);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.reserved2 = 0x{0:X8}", imageHeader.Reserved2);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.reserved3 = 0x{0:X8}", imageHeader.Reserved3);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.reserved4 = 0x{0:X8}", imageHeader.Reserved4);
if(imageHeader.DataSize == 0 && imageHeader.Blocks == 0 &&
imageHeader.ImageFormat != SectorOrder.ProDos) return false;
byte[] tmp;
int[] offsets;
switch(imageHeader.ImageFormat)
{
case SectorOrder.Nibbles:
tmp = new byte[imageHeader.DataSize];
stream.Seek(imageHeader.DataOffset, SeekOrigin.Begin);
stream.Read(tmp, 0, tmp.Length);
AppleNib nibPlugin = new AppleNib();
ZZZNoFilter noFilter = new ZZZNoFilter();
noFilter.Open(tmp);
nibPlugin.Open(noFilter);
decodedImage = nibPlugin.ReadSectors(0, (uint)nibPlugin.Info.Sectors);
imageInfo.Sectors = nibPlugin.Info.Sectors;
imageInfo.SectorSize = nibPlugin.Info.SectorSize;
break;
case SectorOrder.Dos when imageHeader.DataSize == 143360:
case SectorOrder.ProDos when imageHeader.DataSize == 143360:
stream.Seek(imageHeader.DataOffset, SeekOrigin.Begin);
tmp = new byte[imageHeader.DataSize];
stream.Read(tmp, 0, tmp.Length);
bool isDos = tmp[0x11001] == 17 && tmp[0x11002] < 16 && tmp[0x11027] <= 122 && tmp[0x11034] == 35 &&
tmp[0x11035] == 16 && tmp[0x11036] == 0 && tmp[0x11037] == 1;
decodedImage = new byte[imageHeader.DataSize];
offsets = imageHeader.ImageFormat == SectorOrder.Dos
? (isDos ? deinterleave : interleave)
: (isDos ? interleave : deinterleave);
for(int t = 0; t < 35; t++)
{
for(int s = 0; s < 16; s++)
Array.Copy(tmp, t * 16 * 256 + s * 256, decodedImage, t * 16 * 256 + offsets[s] * 256, 256);
}
imageInfo.Sectors = 560;
imageInfo.SectorSize = 256;
break;
case SectorOrder.Dos when imageHeader.DataSize == 819200:
stream.Seek(imageHeader.DataOffset, SeekOrigin.Begin);
tmp = new byte[imageHeader.DataSize];
stream.Read(tmp, 0, tmp.Length);
decodedImage = new byte[imageHeader.DataSize];
offsets = interleave;
for(int t = 0; t < 200; t++)
{
for(int s = 0; s < 16; s++)
Array.Copy(tmp, t * 16 * 256 + s * 256, decodedImage, t * 16 * 256 + offsets[s] * 256, 256);
}
imageInfo.Sectors = 1600;
imageInfo.SectorSize = 512;
break;
default:
decodedImage = null;
imageInfo.SectorSize = 512;
imageInfo.Sectors = imageHeader.DataSize / 512;
break;
}
imageInfo.ImageSize = imageHeader.DataSize;
switch(imageHeader.Creator)
{
case CREATOR_ASIMOV:
imageInfo.Application = "ASIMOV2";
break;
case CREATOR_BERNIE:
imageInfo.Application = "Bernie ][ the Rescue";
break;
case CREATOR_CATAKIG:
imageInfo.Application = "Catakig";
break;
case CREATOR_SHEPPY:
imageInfo.Application = "Sheppy's ImageMaker";
break;
case CREATOR_SWEET:
imageInfo.Application = "Sweet16";
break;
case CREATOR_XGS:
imageInfo.Application = "XGS";
break;
case CREATOR_CIDER:
imageInfo.Application = "CiderPress";
break;
case CREATOR_DIC:
imageInfo.Application = "DiscImageChef";
break;
default:
imageInfo.Application = $"Unknown creator code \"{Encoding.ASCII.GetString(creator)}\"";
break;
}
imageInfo.Version = imageHeader.Version.ToString();
if(imageHeader.CommentOffset != 0 && imageHeader.CommentSize != 0)
{
stream.Seek(imageHeader.CommentOffset, SeekOrigin.Begin);
byte[] comments = new byte[imageHeader.CommentSize];
stream.Read(comments, 0, (int)imageHeader.CommentSize);
imageInfo.Comments = Encoding.ASCII.GetString(comments);
}
imageInfo.CreationTime = imageFilter.GetCreationTime();
imageInfo.LastModificationTime = imageFilter.GetLastWriteTime();
imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename());
imageInfo.MediaType = GetMediaType();
a2MgImageFilter = imageFilter;
imageInfo.XmlMediaType = XmlMediaType.BlockMedia;
DicConsole.VerboseWriteLine("2MG image contains a disk of type {0}", imageInfo.MediaType);
if(!string.IsNullOrEmpty(imageInfo.Comments))
DicConsole.VerboseWriteLine("2MG comments: {0}", imageInfo.Comments);
switch(imageInfo.MediaType)
{
case MediaType.Apple32SS:
imageInfo.Cylinders = 35;
imageInfo.Heads = 1;
imageInfo.SectorsPerTrack = 13;
break;
case MediaType.Apple32DS:
imageInfo.Cylinders = 35;
imageInfo.Heads = 2;
imageInfo.SectorsPerTrack = 13;
break;
case MediaType.Apple33SS:
imageInfo.Cylinders = 35;
imageInfo.Heads = 1;
imageInfo.SectorsPerTrack = 16;
break;
case MediaType.Apple33DS:
imageInfo.Cylinders = 35;
imageInfo.Heads = 2;
imageInfo.SectorsPerTrack = 16;
break;
case MediaType.AppleSonySS:
imageInfo.Cylinders = 80;
imageInfo.Heads = 1;
// Variable sectors per track, this suffices
imageInfo.SectorsPerTrack = 10;
break;
case MediaType.AppleSonyDS:
imageInfo.Cylinders = 80;
imageInfo.Heads = 2;
// Variable sectors per track, this suffices
imageInfo.SectorsPerTrack = 10;
break;
case MediaType.DOS_35_HD:
imageInfo.Cylinders = 80;
imageInfo.Heads = 2;
imageInfo.SectorsPerTrack = 18;
break;
}
return true;
}
public byte[] ReadSector(ulong sectorAddress)
{
return ReadSectors(sectorAddress, 1);
}
public byte[] ReadSectors(ulong sectorAddress, uint length)
{
if(sectorAddress > imageInfo.Sectors - 1)
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
if(sectorAddress + length > imageInfo.Sectors)
throw new ArgumentOutOfRangeException(nameof(length), "Requested more sectors than available");
byte[] buffer = new byte[length * imageInfo.SectorSize];
if(decodedImage != null)
Array.Copy(decodedImage, (long)(sectorAddress * imageInfo.SectorSize), buffer, 0,
length * imageInfo.SectorSize);
else
{
Stream stream = a2MgImageFilter.GetDataForkStream();
stream.Seek((long)(imageHeader.DataOffset + sectorAddress * imageInfo.SectorSize), SeekOrigin.Begin);
stream.Read(buffer, 0, (int)(length * imageInfo.SectorSize));
}
return buffer;
}
public byte[] ReadDiskTag(MediaTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorTag(ulong sectorAddress, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSector(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorTag(ulong sectorAddress, uint track, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectors(ulong sectorAddress, uint length, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, uint track, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorLong(ulong sectorAddress)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorLong(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsLong(ulong sectorAddress, uint length)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsLong(ulong sectorAddress, uint length, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public List<Track> GetSessionTracks(Session session)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public List<Track> GetSessionTracks(ushort session)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public bool? VerifySector(ulong sectorAddress)
{
return null;
}
public bool? VerifySector(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public bool? VerifySectors(ulong sectorAddress, uint length, out List<ulong> failingLbas,
out List<ulong> unknownLbas)
{
failingLbas = new List<ulong>();
unknownLbas = new List<ulong>();
for(ulong i = 0; i < imageInfo.Sectors; i++) unknownLbas.Add(i);
return null;
}
public bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List<ulong> failingLbas,
out List<ulong> unknownLbas)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public bool? VerifyMediaImage()
{
return null;
}
public List<DumpHardwareType> DumpHardware => null;
public CICMMetadataType CicmMetadata => null;
public IEnumerable<MediaTagType> SupportedMediaTags => new MediaTagType[] { };
public IEnumerable<SectorTagType> SupportedSectorTags => new SectorTagType[] { };
public IEnumerable<MediaType> SupportedMediaTypes =>
new[]
{
MediaType.Apple32SS, MediaType.Apple33SS, MediaType.AppleSonySS, MediaType.AppleSonyDS,
MediaType.DOS_35_HD, MediaType.Unknown, MediaType.GENERIC_HDD, MediaType.FlashDrive,
MediaType.CompactFlash, MediaType.CompactFlashType2, MediaType.PCCardTypeI, MediaType.PCCardTypeII,
MediaType.PCCardTypeIII, MediaType.PCCardTypeIV
};
public IEnumerable<(string name, Type type, string description)> SupportedOptions =>
new (string name, Type type, string description)[] { };
public IEnumerable<string> KnownExtensions => new[] {".2mg"};
public bool IsWriting { get; private set; }
public string ErrorMessage { get; private set; }
public bool Create(string path, MediaType mediaType, Dictionary<string, string> options, ulong sectors,
uint sectorSize)
{
if(sectorSize != 512)
if(sectorSize != 256 || mediaType != MediaType.Apple32SS && mediaType != MediaType.Apple33SS)
{
ErrorMessage = "Unsupported sector size";
return false;
}
if(!SupportedMediaTypes.Contains(mediaType))
{
ErrorMessage = $"Unsupport media format {mediaType}";
return false;
}
if(sectors > uint.MaxValue)
{
ErrorMessage = "Too many sectors";
return false;
}
imageInfo = new ImageInfo {MediaType = mediaType, SectorSize = sectorSize, Sectors = sectors};
try { writingStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); }
catch(IOException e)
{
ErrorMessage = $"Could not create new image file, exception {e.Message}";
return false;
}
IsWriting = true;
ErrorMessage = null;
return true;
}
public bool WriteMediaTag(byte[] data, MediaTagType tag)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool WriteSector(byte[] data, ulong sectorAddress)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
if(data.Length != imageInfo.SectorSize)
{
ErrorMessage = "Incorrect data size";
return false;
}
if(sectorAddress >= imageInfo.Sectors)
{
ErrorMessage = "Tried to write past image size";
return false;
}
writingStream.Seek((long)(0x40 + sectorAddress * imageInfo.SectorSize), SeekOrigin.Begin);
writingStream.Write(data, 0, data.Length);
ErrorMessage = "";
return true;
}
public bool WriteSectors(byte[] data, ulong sectorAddress, uint length)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
if(data.Length % imageInfo.SectorSize != 0)
{
ErrorMessage = "Incorrect data size";
return false;
}
if(sectorAddress + length > imageInfo.Sectors)
{
ErrorMessage = "Tried to write past image size";
return false;
}
writingStream.Seek((long)(0x40 + sectorAddress * imageInfo.SectorSize), SeekOrigin.Begin);
writingStream.Write(data, 0, data.Length);
ErrorMessage = "";
return true;
}
public bool WriteSectorLong(byte[] data, ulong sectorAddress)
{
ErrorMessage = "Writing sectors with tags is not supported.";
return false;
}
public bool WriteSectorsLong(byte[] data, ulong sectorAddress, uint length)
{
ErrorMessage = "Writing sectors with tags is not supported.";
return false;
}
public bool SetTracks(List<Track> tracks)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool Close()
{
if(!IsWriting)
{
ErrorMessage = "Image is not opened for writing";
return false;
}
writingStream.Seek(0x40 + 17 * 16 * 256, SeekOrigin.Begin);
byte[] tmp = new byte[256];
writingStream.Read(tmp, 0, tmp.Length);
bool isDos = tmp[0x01] == 17 && tmp[0x02] < 16 && tmp[0x27] <= 122 && tmp[0x34] == 35 && tmp[0x35] == 16 &&
tmp[0x36] == 0 && tmp[0x37] == 1;
imageHeader = new A2ImgHeader
{
Blocks = (uint)(imageInfo.Sectors * imageInfo.SectorSize) / 512,
Creator = CREATOR_DIC,
DataOffset = 0x40,
DataSize = (uint)(imageInfo.Sectors * imageInfo.SectorSize),
Flags =
(uint)(imageInfo.LastMediaSequence != 0
? VALID_VOLUME_NUMBER + (imageInfo.MediaSequence & 0xFF)
: 0),
HeaderSize = 0x40,
ImageFormat = isDos ? SectorOrder.Dos : SectorOrder.ProDos,
Magic = MAGIC,
Version = 1
};
if(!string.IsNullOrEmpty(imageInfo.Comments))
{
writingStream.Seek(0, SeekOrigin.End);
tmp = Encoding.UTF8.GetBytes(imageInfo.Comments);
imageHeader.CommentOffset = (uint)writingStream.Position;
imageHeader.CommentSize = (uint)(tmp.Length + 1);
writingStream.Write(tmp, 0, tmp.Length);
writingStream.WriteByte(0);
}
byte[] hdr = new byte[Marshal.SizeOf(typeof(A2ImgHeader))];
IntPtr hdrPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(A2ImgHeader)));
Marshal.StructureToPtr(imageHeader, hdrPtr, true);
Marshal.Copy(hdrPtr, hdr, 0, hdr.Length);
Marshal.FreeHGlobal(hdrPtr);
writingStream.Seek(0, SeekOrigin.Begin);
writingStream.Write(hdr, 0, hdr.Length);
writingStream.Flush();
writingStream.Close();
IsWriting = false;
ErrorMessage = "";
return true;
}
public bool SetMetadata(ImageInfo metadata)
{
imageInfo.Comments = metadata.Comments;
imageInfo.LastMediaSequence = metadata.LastMediaSequence;
imageInfo.MediaSequence = metadata.MediaSequence;
return true;
}
public bool SetGeometry(uint cylinders, uint heads, uint sectorsPerTrack)
{
// Geometry is not stored in image
return true;
}
public bool WriteSectorTag(byte[] data, ulong sectorAddress, SectorTagType tag)
{
ErrorMessage = "Writing sectors with tags is not supported.";
return false;
}
public bool WriteSectorsTag(byte[] data, ulong sectorAddress, uint length, SectorTagType tag)
{
ErrorMessage = "Writing sectors with tags is not supported.";
return false;
}
public bool SetDumpHardware(List<DumpHardwareType> dumpHardware)
{
// Not supported
return false;
}
public bool SetCicmMetadata(CICMMetadataType metadata)
{
// Not supported
return false;
}
MediaType GetMediaType()
{
switch(imageInfo.Sectors)
{
case 455: return MediaType.Apple32SS;
case 910: return MediaType.Apple32DS;
case 560: return MediaType.Apple33SS;
case 1120: return MediaType.Apple33DS;
case 800: return MediaType.AppleSonySS;
case 1600: return MediaType.AppleSonyDS;
case 2880: return MediaType.DOS_35_HD;
default: return MediaType.Unknown;
}
}
enum SectorOrder : uint
{
Dos = 0,
ProDos = 1,
Nibbles = 2
}
[SuppressMessage("ReSharper", "NotAccessedField.Local")]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct A2ImgHeader
{
/// <summary>
/// Offset 0x00, magic
/// </summary>
public uint Magic;
/// <summary>
/// Offset 0x04, disk image creator ID
/// </summary>
public uint Creator;
/// <summary>
/// Offset 0x08, header size, constant 0x0040
/// </summary>
public ushort HeaderSize;
/// <summary>
/// Offset 0x0A, disk image version
/// </summary>
public ushort Version;
/// <summary>
/// Offset 0x0C, disk image format
/// </summary>
public SectorOrder ImageFormat;
/// <summary>
/// Offset 0x10, flags and volume number
/// </summary>
public uint Flags;
/// <summary>
/// Offset 0x14, blocks for ProDOS, 0 otherwise
/// </summary>
public uint Blocks;
/// <summary>
/// Offset 0x18, offset to data
/// </summary>
public uint DataOffset;
/// <summary>
/// Offset 0x1C, data size in bytes
/// </summary>
public uint DataSize;
/// <summary>
/// Offset 0x20, offset to optional comment
/// </summary>
public uint CommentOffset;
/// <summary>
/// Offset 0x24, length of optional comment
/// </summary>
public uint CommentSize;
/// <summary>
/// Offset 0x28, offset to creator specific chunk
/// </summary>
public uint CreatorSpecificOffset;
/// <summary>
/// Offset 0x2C, creator specific chunk size
/// </summary>
public uint CreatorSpecificSize;
/// <summary>
/// Offset 0x30, reserved, should be zero
/// </summary>
public uint Reserved1;
/// <summary>
/// Offset 0x34, reserved, should be zero
/// </summary>
public uint Reserved2;
/// <summary>
/// Offset 0x38, reserved, should be zero
/// </summary>
public uint Reserved3;
/// <summary>
/// Offset 0x3C, reserved, should be zero
/// </summary>
public uint Reserved4;
}
}
}

View File

@@ -0,0 +1,76 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Apple2MG.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Manages XGS emulator disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Collections.Generic;
using System.IO;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
namespace DiscImageChef.DiscImages
{
public partial class Apple2Mg : IWritableImage
{
IFilter a2MgImageFilter;
byte[] decodedImage;
A2ImgHeader imageHeader;
ImageInfo imageInfo;
FileStream writingStream;
public Apple2Mg()
{
imageInfo = new ImageInfo
{
ReadableSectorTags = new List<SectorTagType>(),
ReadableMediaTags = new List<MediaTagType>(),
HasPartitions = false,
HasSessions = false,
Version = null,
Application = null,
ApplicationVersion = null,
Creator = null,
Comments = null,
MediaManufacturer = null,
MediaModel = null,
MediaSerialNumber = null,
MediaBarcode = null,
MediaPartNumber = null,
MediaSequence = 0,
LastMediaSequence = 0,
DriveManufacturer = null,
DriveModel = null,
DriveSerialNumber = null,
DriveFirmwareRevision = null
};
}
}
}

View File

@@ -0,0 +1,80 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Constants.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains constants for XGS emulator disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
namespace DiscImageChef.DiscImages
{
public partial class Apple2Mg
{
/// <summary>
/// Magic number, "2IMG"
/// </summary>
const uint MAGIC = 0x474D4932;
/// <summary>
/// Disk image created by ASIMOV2, "!nfc"
/// </summary>
const uint CREATOR_ASIMOV = 0x63666E21;
/// <summary>
/// Disk image created by Bernie ][ the Rescue, "B2TR"
/// </summary>
const uint CREATOR_BERNIE = 0x52543242;
/// <summary>
/// Disk image created by Catakig, "CTKG"
/// </summary>
const uint CREATOR_CATAKIG = 0x474B5443;
/// <summary>
/// Disk image created by Sheppy's ImageMaker, "ShIm"
/// </summary>
const uint CREATOR_SHEPPY = 0x6D496853;
/// <summary>
/// Disk image created by Sweet16, "WOOF"
/// </summary>
const uint CREATOR_SWEET = 0x464F4F57;
/// <summary>
/// Disk image created by XGS, "XGS!"
/// </summary>
const uint CREATOR_XGS = 0x21534758;
/// <summary>
/// Disk image created by CiderPress, "CdrP"
/// </summary>
const uint CREATOR_CIDER = 0x50726443;
/// <summary>
/// Disk image created by DiscImageChef, "dic "
/// </summary>
const uint CREATOR_DIC = 0x20636964;
const uint LOCKED_DISK = 0x80000000;
const uint VALID_VOLUME_NUMBER = 0x00000100;
const uint VOLUME_NUMBER_MASK = 0x000000FF;
readonly int[] deinterleave = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
readonly int[] interleave = {0, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 15};
}
}

View File

@@ -0,0 +1,44 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Enums.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains enumerations for XGS emulator disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
namespace DiscImageChef.DiscImages
{
public partial class Apple2Mg
{
enum SectorOrder : uint
{
Dos = 0,
ProDos = 1,
Nibbles = 2
}
}
}

View File

@@ -0,0 +1,54 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Helpers.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains helpers for XGS emulator disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using DiscImageChef.CommonTypes;
namespace DiscImageChef.DiscImages
{
public partial class Apple2Mg
{
MediaType GetMediaType()
{
switch(imageInfo.Sectors)
{
case 455: return MediaType.Apple32SS;
case 910: return MediaType.Apple32DS;
case 560: return MediaType.Apple33SS;
case 1120: return MediaType.Apple33DS;
case 800: return MediaType.AppleSonySS;
case 1600: return MediaType.AppleSonyDS;
case 2880: return MediaType.DOS_35_HD;
default: return MediaType.Unknown;
}
}
}
}

View File

@@ -0,0 +1,75 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Identify.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Identifies XGS emulator disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.IO;
using DiscImageChef.CommonTypes.Interfaces;
namespace DiscImageChef.DiscImages
{
public partial class Apple2Mg
{
public bool Identify(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin);
if(stream.Length < 65) return false;
byte[] header = new byte[64];
stream.Read(header, 0, 64);
uint magic = BitConverter.ToUInt32(header, 0x00);
if(magic != MAGIC) return false;
uint dataoff = BitConverter.ToUInt32(header, 0x18);
if(dataoff > stream.Length) return false;
uint datasize = BitConverter.ToUInt32(header, 0x1C);
// There seems to be incorrect endian in some images on the wild
if(datasize == 0x00800C00) datasize = 0x000C8000;
if(dataoff + datasize > stream.Length) return false;
uint commentoff = BitConverter.ToUInt32(header, 0x20);
if(commentoff > stream.Length) return false;
uint commentsize = BitConverter.ToUInt32(header, 0x24);
if(commentoff + commentsize > stream.Length) return false;
uint creatoroff = BitConverter.ToUInt32(header, 0x28);
if(creatoroff > stream.Length) return false;
uint creatorsize = BitConverter.ToUInt32(header, 0x2C);
return creatoroff + creatorsize <= stream.Length;
}
}
}

View File

@@ -0,0 +1,80 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Properties.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains properties for XGS emulator disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Exceptions;
using DiscImageChef.CommonTypes.Structs;
using Schemas;
namespace DiscImageChef.DiscImages
{
public partial class Apple2Mg
{
public ImageInfo Info => imageInfo;
public string Name => "Apple 2IMG";
public Guid Id => new Guid("CBAF8824-BA5F-415F-953A-19A03519B2D1");
public string Format => "Apple 2IMG";
public List<Partition> Partitions =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<Track> Tracks =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<Session> Sessions =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<DumpHardwareType> DumpHardware => null;
public CICMMetadataType CicmMetadata => null;
public IEnumerable<MediaTagType> SupportedMediaTags => new MediaTagType[] { };
public IEnumerable<SectorTagType> SupportedSectorTags => new SectorTagType[] { };
public IEnumerable<MediaType> SupportedMediaTypes =>
new[]
{
MediaType.Apple32SS, MediaType.Apple33SS, MediaType.AppleSonySS, MediaType.AppleSonyDS,
MediaType.DOS_35_HD, MediaType.Unknown, MediaType.GENERIC_HDD, MediaType.FlashDrive,
MediaType.CompactFlash, MediaType.CompactFlashType2, MediaType.PCCardTypeI, MediaType.PCCardTypeII,
MediaType.PCCardTypeIII, MediaType.PCCardTypeIV
};
public IEnumerable<(string name, Type type, string description)> SupportedOptions =>
new (string name, Type type, string description)[] { };
public IEnumerable<string> KnownExtensions => new[] {".2mg"};
public bool IsWriting { get; private set; }
public string ErrorMessage { get; private set; }
}
}

View File

@@ -0,0 +1,298 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Read.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Reads XGS emulator disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.IO;
using System.Text;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.Console;
using DiscImageChef.Filters;
namespace DiscImageChef.DiscImages
{
public partial class Apple2Mg
{
public bool Open(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin);
imageHeader = new A2ImgHeader();
byte[] header = new byte[64];
stream.Read(header, 0, 64);
byte[] magic = new byte[4];
byte[] creator = new byte[4];
Array.Copy(header, 0, magic, 0, 4);
Array.Copy(header, 4, creator, 0, 4);
imageHeader.Magic = BitConverter.ToUInt32(header, 0x00);
imageHeader.Creator = BitConverter.ToUInt32(header, 0x04);
imageHeader.HeaderSize = BitConverter.ToUInt16(header, 0x08);
imageHeader.Version = BitConverter.ToUInt16(header, 0x0A);
imageHeader.ImageFormat = (SectorOrder)BitConverter.ToUInt32(header, 0x0C);
imageHeader.Flags = BitConverter.ToUInt32(header, 0x10);
imageHeader.Blocks = BitConverter.ToUInt32(header, 0x14);
imageHeader.DataOffset = BitConverter.ToUInt32(header, 0x18);
imageHeader.DataSize = BitConverter.ToUInt32(header, 0x1C);
imageHeader.CommentOffset = BitConverter.ToUInt32(header, 0x20);
imageHeader.CommentSize = BitConverter.ToUInt32(header, 0x24);
imageHeader.CreatorSpecificOffset = BitConverter.ToUInt32(header, 0x28);
imageHeader.CreatorSpecificSize = BitConverter.ToUInt32(header, 0x2C);
imageHeader.Reserved1 = BitConverter.ToUInt32(header, 0x30);
imageHeader.Reserved2 = BitConverter.ToUInt32(header, 0x34);
imageHeader.Reserved3 = BitConverter.ToUInt32(header, 0x38);
imageHeader.Reserved4 = BitConverter.ToUInt32(header, 0x3C);
if(imageHeader.DataSize == 0x00800C00)
{
imageHeader.DataSize = 0x000C8000;
DicConsole.DebugWriteLine("2MG plugin", "Detected incorrect endian on data size field, correcting.");
}
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.magic = \"{0}\"",
Encoding.ASCII.GetString(magic));
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.creator = \"{0}\"",
Encoding.ASCII.GetString(creator));
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.headerSize = {0}", imageHeader.HeaderSize);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.version = {0}", imageHeader.Version);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.imageFormat = {0}", imageHeader.ImageFormat);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.flags = 0x{0:X8}", imageHeader.Flags);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.blocks = {0}", imageHeader.Blocks);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.dataOffset = 0x{0:X8}", imageHeader.DataOffset);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.dataSize = {0}", imageHeader.DataSize);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.commentOffset = 0x{0:X8}", imageHeader.CommentOffset);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.commentSize = {0}", imageHeader.CommentSize);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.creatorSpecificOffset = 0x{0:X8}",
imageHeader.CreatorSpecificOffset);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.creatorSpecificSize = {0}",
imageHeader.CreatorSpecificSize);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.reserved1 = 0x{0:X8}", imageHeader.Reserved1);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.reserved2 = 0x{0:X8}", imageHeader.Reserved2);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.reserved3 = 0x{0:X8}", imageHeader.Reserved3);
DicConsole.DebugWriteLine("2MG plugin", "ImageHeader.reserved4 = 0x{0:X8}", imageHeader.Reserved4);
if(imageHeader.DataSize == 0 && imageHeader.Blocks == 0 &&
imageHeader.ImageFormat != SectorOrder.ProDos) return false;
byte[] tmp;
int[] offsets;
switch(imageHeader.ImageFormat)
{
case SectorOrder.Nibbles:
tmp = new byte[imageHeader.DataSize];
stream.Seek(imageHeader.DataOffset, SeekOrigin.Begin);
stream.Read(tmp, 0, tmp.Length);
AppleNib nibPlugin = new AppleNib();
ZZZNoFilter noFilter = new ZZZNoFilter();
noFilter.Open(tmp);
nibPlugin.Open(noFilter);
decodedImage = nibPlugin.ReadSectors(0, (uint)nibPlugin.Info.Sectors);
imageInfo.Sectors = nibPlugin.Info.Sectors;
imageInfo.SectorSize = nibPlugin.Info.SectorSize;
break;
case SectorOrder.Dos when imageHeader.DataSize == 143360:
case SectorOrder.ProDos when imageHeader.DataSize == 143360:
stream.Seek(imageHeader.DataOffset, SeekOrigin.Begin);
tmp = new byte[imageHeader.DataSize];
stream.Read(tmp, 0, tmp.Length);
bool isDos = tmp[0x11001] == 17 && tmp[0x11002] < 16 && tmp[0x11027] <= 122 && tmp[0x11034] == 35 &&
tmp[0x11035] == 16 && tmp[0x11036] == 0 && tmp[0x11037] == 1;
decodedImage = new byte[imageHeader.DataSize];
offsets = imageHeader.ImageFormat == SectorOrder.Dos
? (isDos ? deinterleave : interleave)
: (isDos ? interleave : deinterleave);
for(int t = 0; t < 35; t++)
{
for(int s = 0; s < 16; s++)
Array.Copy(tmp, t * 16 * 256 + s * 256, decodedImage, t * 16 * 256 + offsets[s] * 256, 256);
}
imageInfo.Sectors = 560;
imageInfo.SectorSize = 256;
break;
case SectorOrder.Dos when imageHeader.DataSize == 819200:
stream.Seek(imageHeader.DataOffset, SeekOrigin.Begin);
tmp = new byte[imageHeader.DataSize];
stream.Read(tmp, 0, tmp.Length);
decodedImage = new byte[imageHeader.DataSize];
offsets = interleave;
for(int t = 0; t < 200; t++)
{
for(int s = 0; s < 16; s++)
Array.Copy(tmp, t * 16 * 256 + s * 256, decodedImage, t * 16 * 256 + offsets[s] * 256, 256);
}
imageInfo.Sectors = 1600;
imageInfo.SectorSize = 512;
break;
default:
decodedImage = null;
imageInfo.SectorSize = 512;
imageInfo.Sectors = imageHeader.DataSize / 512;
break;
}
imageInfo.ImageSize = imageHeader.DataSize;
switch(imageHeader.Creator)
{
case CREATOR_ASIMOV:
imageInfo.Application = "ASIMOV2";
break;
case CREATOR_BERNIE:
imageInfo.Application = "Bernie ][ the Rescue";
break;
case CREATOR_CATAKIG:
imageInfo.Application = "Catakig";
break;
case CREATOR_SHEPPY:
imageInfo.Application = "Sheppy's ImageMaker";
break;
case CREATOR_SWEET:
imageInfo.Application = "Sweet16";
break;
case CREATOR_XGS:
imageInfo.Application = "XGS";
break;
case CREATOR_CIDER:
imageInfo.Application = "CiderPress";
break;
case CREATOR_DIC:
imageInfo.Application = "DiscImageChef";
break;
default:
imageInfo.Application = $"Unknown creator code \"{Encoding.ASCII.GetString(creator)}\"";
break;
}
imageInfo.Version = imageHeader.Version.ToString();
if(imageHeader.CommentOffset != 0 && imageHeader.CommentSize != 0)
{
stream.Seek(imageHeader.CommentOffset, SeekOrigin.Begin);
byte[] comments = new byte[imageHeader.CommentSize];
stream.Read(comments, 0, (int)imageHeader.CommentSize);
imageInfo.Comments = Encoding.ASCII.GetString(comments);
}
imageInfo.CreationTime = imageFilter.GetCreationTime();
imageInfo.LastModificationTime = imageFilter.GetLastWriteTime();
imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename());
imageInfo.MediaType = GetMediaType();
a2MgImageFilter = imageFilter;
imageInfo.XmlMediaType = XmlMediaType.BlockMedia;
DicConsole.VerboseWriteLine("2MG image contains a disk of type {0}", imageInfo.MediaType);
if(!string.IsNullOrEmpty(imageInfo.Comments))
DicConsole.VerboseWriteLine("2MG comments: {0}", imageInfo.Comments);
switch(imageInfo.MediaType)
{
case MediaType.Apple32SS:
imageInfo.Cylinders = 35;
imageInfo.Heads = 1;
imageInfo.SectorsPerTrack = 13;
break;
case MediaType.Apple32DS:
imageInfo.Cylinders = 35;
imageInfo.Heads = 2;
imageInfo.SectorsPerTrack = 13;
break;
case MediaType.Apple33SS:
imageInfo.Cylinders = 35;
imageInfo.Heads = 1;
imageInfo.SectorsPerTrack = 16;
break;
case MediaType.Apple33DS:
imageInfo.Cylinders = 35;
imageInfo.Heads = 2;
imageInfo.SectorsPerTrack = 16;
break;
case MediaType.AppleSonySS:
imageInfo.Cylinders = 80;
imageInfo.Heads = 1;
// Variable sectors per track, this suffices
imageInfo.SectorsPerTrack = 10;
break;
case MediaType.AppleSonyDS:
imageInfo.Cylinders = 80;
imageInfo.Heads = 2;
// Variable sectors per track, this suffices
imageInfo.SectorsPerTrack = 10;
break;
case MediaType.DOS_35_HD:
imageInfo.Cylinders = 80;
imageInfo.Heads = 2;
imageInfo.SectorsPerTrack = 18;
break;
}
return true;
}
public byte[] ReadSector(ulong sectorAddress)
{
return ReadSectors(sectorAddress, 1);
}
public byte[] ReadSectors(ulong sectorAddress, uint length)
{
if(sectorAddress > imageInfo.Sectors - 1)
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
if(sectorAddress + length > imageInfo.Sectors)
throw new ArgumentOutOfRangeException(nameof(length), "Requested more sectors than available");
byte[] buffer = new byte[length * imageInfo.SectorSize];
if(decodedImage != null)
Array.Copy(decodedImage, (long)(sectorAddress * imageInfo.SectorSize), buffer, 0,
length * imageInfo.SectorSize);
else
{
Stream stream = a2MgImageFilter.GetDataForkStream();
stream.Seek((long)(imageHeader.DataOffset + sectorAddress * imageInfo.SectorSize), SeekOrigin.Begin);
stream.Read(buffer, 0, (int)(length * imageInfo.SectorSize));
}
return buffer;
}
}
}

View File

@@ -0,0 +1,114 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Structs.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains structures for XGS emulator disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
namespace DiscImageChef.DiscImages
{
public partial class Apple2Mg
{
[SuppressMessage("ReSharper", "NotAccessedField.Local")]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct A2ImgHeader
{
/// <summary>
/// Offset 0x00, magic
/// </summary>
public uint Magic;
/// <summary>
/// Offset 0x04, disk image creator ID
/// </summary>
public uint Creator;
/// <summary>
/// Offset 0x08, header size, constant 0x0040
/// </summary>
public ushort HeaderSize;
/// <summary>
/// Offset 0x0A, disk image version
/// </summary>
public ushort Version;
/// <summary>
/// Offset 0x0C, disk image format
/// </summary>
public SectorOrder ImageFormat;
/// <summary>
/// Offset 0x10, flags and volume number
/// </summary>
public uint Flags;
/// <summary>
/// Offset 0x14, blocks for ProDOS, 0 otherwise
/// </summary>
public uint Blocks;
/// <summary>
/// Offset 0x18, offset to data
/// </summary>
public uint DataOffset;
/// <summary>
/// Offset 0x1C, data size in bytes
/// </summary>
public uint DataSize;
/// <summary>
/// Offset 0x20, offset to optional comment
/// </summary>
public uint CommentOffset;
/// <summary>
/// Offset 0x24, length of optional comment
/// </summary>
public uint CommentSize;
/// <summary>
/// Offset 0x28, offset to creator specific chunk
/// </summary>
public uint CreatorSpecificOffset;
/// <summary>
/// Offset 0x2C, creator specific chunk size
/// </summary>
public uint CreatorSpecificSize;
/// <summary>
/// Offset 0x30, reserved, should be zero
/// </summary>
public uint Reserved1;
/// <summary>
/// Offset 0x34, reserved, should be zero
/// </summary>
public uint Reserved2;
/// <summary>
/// Offset 0x38, reserved, should be zero
/// </summary>
public uint Reserved3;
/// <summary>
/// Offset 0x3C, reserved, should be zero
/// </summary>
public uint Reserved4;
}
}
}

View File

@@ -0,0 +1,138 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Unsupported.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains features unsupported by XGS emulator disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Collections.Generic;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Exceptions;
using DiscImageChef.CommonTypes.Structs;
namespace DiscImageChef.DiscImages
{
public partial class Apple2Mg
{
public byte[] ReadDiskTag(MediaTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorTag(ulong sectorAddress, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSector(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorTag(ulong sectorAddress, uint track, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectors(ulong sectorAddress, uint length, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, uint track, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorLong(ulong sectorAddress)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorLong(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsLong(ulong sectorAddress, uint length)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsLong(ulong sectorAddress, uint length, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public List<Track> GetSessionTracks(Session session)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public List<Track> GetSessionTracks(ushort session)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public bool? VerifySector(ulong sectorAddress)
{
return null;
}
public bool? VerifySector(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public bool? VerifySectors(ulong sectorAddress, uint length, out List<ulong> failingLbas,
out List<ulong> unknownLbas)
{
failingLbas = new List<ulong>();
unknownLbas = new List<ulong>();
for(ulong i = 0; i < imageInfo.Sectors; i++) unknownLbas.Add(i);
return null;
}
public bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List<ulong> failingLbas,
out List<ulong> unknownLbas)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public bool? VerifyMediaImage()
{
return null;
}
}
}

View File

@@ -0,0 +1,257 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Write.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Writes XGS emulator disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Structs;
using Schemas;
namespace DiscImageChef.DiscImages
{
public partial class Apple2Mg
{
public bool Create(string path, MediaType mediaType, Dictionary<string, string> options, ulong sectors,
uint sectorSize)
{
if(sectorSize != 512)
if(sectorSize != 256 || mediaType != MediaType.Apple32SS && mediaType != MediaType.Apple33SS)
{
ErrorMessage = "Unsupported sector size";
return false;
}
if(!SupportedMediaTypes.Contains(mediaType))
{
ErrorMessage = $"Unsupport media format {mediaType}";
return false;
}
if(sectors > uint.MaxValue)
{
ErrorMessage = "Too many sectors";
return false;
}
imageInfo = new ImageInfo {MediaType = mediaType, SectorSize = sectorSize, Sectors = sectors};
try { writingStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); }
catch(IOException e)
{
ErrorMessage = $"Could not create new image file, exception {e.Message}";
return false;
}
IsWriting = true;
ErrorMessage = null;
return true;
}
public bool WriteMediaTag(byte[] data, MediaTagType tag)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool WriteSector(byte[] data, ulong sectorAddress)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
if(data.Length != imageInfo.SectorSize)
{
ErrorMessage = "Incorrect data size";
return false;
}
if(sectorAddress >= imageInfo.Sectors)
{
ErrorMessage = "Tried to write past image size";
return false;
}
writingStream.Seek((long)(0x40 + sectorAddress * imageInfo.SectorSize), SeekOrigin.Begin);
writingStream.Write(data, 0, data.Length);
ErrorMessage = "";
return true;
}
public bool WriteSectors(byte[] data, ulong sectorAddress, uint length)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
if(data.Length % imageInfo.SectorSize != 0)
{
ErrorMessage = "Incorrect data size";
return false;
}
if(sectorAddress + length > imageInfo.Sectors)
{
ErrorMessage = "Tried to write past image size";
return false;
}
writingStream.Seek((long)(0x40 + sectorAddress * imageInfo.SectorSize), SeekOrigin.Begin);
writingStream.Write(data, 0, data.Length);
ErrorMessage = "";
return true;
}
public bool WriteSectorLong(byte[] data, ulong sectorAddress)
{
ErrorMessage = "Writing sectors with tags is not supported.";
return false;
}
public bool WriteSectorsLong(byte[] data, ulong sectorAddress, uint length)
{
ErrorMessage = "Writing sectors with tags is not supported.";
return false;
}
public bool SetTracks(List<Track> tracks)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool Close()
{
if(!IsWriting)
{
ErrorMessage = "Image is not opened for writing";
return false;
}
writingStream.Seek(0x40 + 17 * 16 * 256, SeekOrigin.Begin);
byte[] tmp = new byte[256];
writingStream.Read(tmp, 0, tmp.Length);
bool isDos = tmp[0x01] == 17 && tmp[0x02] < 16 && tmp[0x27] <= 122 && tmp[0x34] == 35 && tmp[0x35] == 16 &&
tmp[0x36] == 0 && tmp[0x37] == 1;
imageHeader = new A2ImgHeader
{
Blocks = (uint)(imageInfo.Sectors * imageInfo.SectorSize) / 512,
Creator = CREATOR_DIC,
DataOffset = 0x40,
DataSize = (uint)(imageInfo.Sectors * imageInfo.SectorSize),
Flags =
(uint)(imageInfo.LastMediaSequence != 0
? VALID_VOLUME_NUMBER + (imageInfo.MediaSequence & 0xFF)
: 0),
HeaderSize = 0x40,
ImageFormat = isDos ? SectorOrder.Dos : SectorOrder.ProDos,
Magic = MAGIC,
Version = 1
};
if(!string.IsNullOrEmpty(imageInfo.Comments))
{
writingStream.Seek(0, SeekOrigin.End);
tmp = Encoding.UTF8.GetBytes(imageInfo.Comments);
imageHeader.CommentOffset = (uint)writingStream.Position;
imageHeader.CommentSize = (uint)(tmp.Length + 1);
writingStream.Write(tmp, 0, tmp.Length);
writingStream.WriteByte(0);
}
byte[] hdr = new byte[Marshal.SizeOf(typeof(A2ImgHeader))];
IntPtr hdrPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(A2ImgHeader)));
Marshal.StructureToPtr(imageHeader, hdrPtr, true);
Marshal.Copy(hdrPtr, hdr, 0, hdr.Length);
Marshal.FreeHGlobal(hdrPtr);
writingStream.Seek(0, SeekOrigin.Begin);
writingStream.Write(hdr, 0, hdr.Length);
writingStream.Flush();
writingStream.Close();
IsWriting = false;
ErrorMessage = "";
return true;
}
public bool SetMetadata(ImageInfo metadata)
{
imageInfo.Comments = metadata.Comments;
imageInfo.LastMediaSequence = metadata.LastMediaSequence;
imageInfo.MediaSequence = metadata.MediaSequence;
return true;
}
public bool SetGeometry(uint cylinders, uint heads, uint sectorsPerTrack)
{
// Geometry is not stored in image
return true;
}
public bool WriteSectorTag(byte[] data, ulong sectorAddress, SectorTagType tag)
{
ErrorMessage = "Writing sectors with tags is not supported.";
return false;
}
public bool WriteSectorsTag(byte[] data, ulong sectorAddress, uint length, SectorTagType tag)
{
ErrorMessage = "Writing sectors with tags is not supported.";
return false;
}
public bool SetDumpHardware(List<DumpHardwareType> dumpHardware)
{
// Not supported
return false;
}
public bool SetCicmMetadata(CICMMetadataType metadata)
{
// Not supported
return false;
}
}
}

View File

@@ -1,447 +0,0 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : AppleDOS.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Manages interleaved Apple ][ disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Exceptions;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
using Schemas;
namespace DiscImageChef.DiscImages
{
public class AppleDos : IWritableImage
{
readonly int[] deinterleave = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
readonly int[] interleave = {0, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 15};
byte[] deinterleaved;
string extension;
ImageInfo imageInfo;
FileStream writingStream;
public AppleDos()
{
imageInfo = new ImageInfo
{
ReadableSectorTags = new List<SectorTagType>(),
ReadableMediaTags = new List<MediaTagType>(),
HasPartitions = false,
HasSessions = false,
Version = null,
Application = null,
ApplicationVersion = null,
Creator = null,
Comments = null,
MediaManufacturer = null,
MediaModel = null,
MediaSerialNumber = null,
MediaBarcode = null,
MediaPartNumber = null,
MediaSequence = 0,
LastMediaSequence = 0,
DriveManufacturer = null,
DriveModel = null,
DriveSerialNumber = null,
DriveFirmwareRevision = null
};
}
public ImageInfo Info => imageInfo;
public string Name => "Apple ][ Interleaved Disk Image";
public Guid Id => new Guid("A5828AC0-62C9-4304-81D4-EFD4AAE47360");
public string Format =>
extension == ".po"
? "Apple ][ Interleaved Disk Image (ProDOS order)"
: "Apple ][ Interleaved Disk Image (DOS order)";
public List<Track> Tracks =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<Session> Sessions =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public bool Identify(IFilter imageFilter)
{
extension = Path.GetExtension(imageFilter.GetFilename())?.ToLower();
return imageFilter.GetDataForkLength() == 143360 && (extension == ".po" || extension == ".do");
}
public bool Open(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin);
byte[] tmp = new byte[imageFilter.GetDataForkLength()];
stream.Read(tmp, 0, tmp.Length);
bool isDos = tmp[0x11001] == 17 && tmp[0x11002] < 16 && tmp[0x11027] <= 122 && tmp[0x11034] == 35 &&
tmp[0x11035] == 16 && tmp[0x11036] == 0 && tmp[0x11037] == 1;
deinterleaved = new byte[tmp.Length];
extension = Path.GetExtension(imageFilter.GetFilename())?.ToLower();
int[] offsets = extension == ".do"
? (isDos ? deinterleave : interleave)
: (isDos ? interleave : deinterleave);
for(int t = 0; t < 35; t++)
{
for(int s = 0; s < 16; s++)
Array.Copy(tmp, t * 16 * 256 + s * 256, deinterleaved, t * 16 * 256 + offsets[s] * 256, 256);
}
imageInfo.SectorSize = 256;
imageInfo.ImageSize = (ulong)imageFilter.GetDataForkLength();
imageInfo.CreationTime = imageFilter.GetCreationTime();
imageInfo.LastModificationTime = imageFilter.GetLastWriteTime();
imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename());
imageInfo.Sectors = 560;
imageInfo.MediaType = MediaType.Apple33SS;
imageInfo.XmlMediaType = XmlMediaType.BlockMedia;
imageInfo.Cylinders = 35;
imageInfo.Heads = 2;
imageInfo.SectorsPerTrack = 16;
return true;
}
public byte[] ReadSector(ulong sectorAddress)
{
return ReadSectors(sectorAddress, 1);
}
public byte[] ReadSectors(ulong sectorAddress, uint length)
{
if(sectorAddress > imageInfo.Sectors - 1)
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
if(sectorAddress + length > imageInfo.Sectors)
throw new ArgumentOutOfRangeException(nameof(length), "Requested more sectors than available");
byte[] buffer = new byte[length * imageInfo.SectorSize];
Array.Copy(deinterleaved, (int)(sectorAddress * imageInfo.SectorSize), buffer, 0, buffer.Length);
return buffer;
}
public bool? VerifySector(ulong sectorAddress)
{
return null;
}
public bool? VerifySector(ulong sectorAddress, uint track)
{
return null;
}
public bool? VerifySectors(ulong sectorAddress, uint length, out List<ulong> failingLbas,
out List<ulong> unknownLbas)
{
failingLbas = new List<ulong>();
unknownLbas = new List<ulong>();
for(ulong i = sectorAddress; i < sectorAddress + length; i++) unknownLbas.Add(i);
return null;
}
public bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List<ulong> failingLbas,
out List<ulong> unknownLbas)
{
failingLbas = new List<ulong>();
unknownLbas = new List<ulong>();
for(ulong i = sectorAddress; i < sectorAddress + length; i++) unknownLbas.Add(i);
return null;
}
public bool? VerifyMediaImage()
{
return null;
}
public List<DumpHardwareType> DumpHardware => null;
public CICMMetadataType CicmMetadata => null;
public List<Track> GetSessionTracks(Session session)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public List<Track> GetSessionTracks(ushort session)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSector(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectors(ulong sectorAddress, uint length, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorLong(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsLong(ulong sectorAddress, uint length, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorTag(ulong sectorAddress, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorLong(ulong sectorAddress)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsLong(ulong sectorAddress, uint length)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadDiskTag(MediaTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public List<Partition> Partitions =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public byte[] ReadSectorTag(ulong sectorAddress, uint track, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, uint track, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public IEnumerable<MediaTagType> SupportedMediaTags => new MediaTagType[] { };
public IEnumerable<SectorTagType> SupportedSectorTags => new SectorTagType[] { };
public IEnumerable<MediaType> SupportedMediaTypes => new[] {MediaType.Apple33SS};
public IEnumerable<(string name, Type type, string description)> SupportedOptions =>
new (string name, Type type, string description)[] { };
public IEnumerable<string> KnownExtensions => new[] {".do", ".po"};
public bool IsWriting { get; private set; }
public string ErrorMessage { get; private set; }
public bool Create(string path, MediaType mediaType, Dictionary<string, string> options, ulong sectors,
uint sectorSize)
{
if(sectorSize != 256)
{
ErrorMessage = "Unsupported sector size";
return false;
}
if(mediaType != MediaType.Apple33SS)
{
ErrorMessage = $"Unsupport media format {mediaType}";
return false;
}
if(sectors > uint.MaxValue)
{
ErrorMessage = "Too many sectors";
return false;
}
imageInfo = new ImageInfo {MediaType = mediaType, SectorSize = sectorSize, Sectors = sectors};
try { writingStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); }
catch(IOException e)
{
ErrorMessage = $"Could not create new image file, exception {e.Message}";
return false;
}
deinterleaved = new byte[35 * 16 * 256];
extension = Path.GetExtension(path);
IsWriting = true;
ErrorMessage = null;
return true;
}
public bool WriteMediaTag(byte[] data, MediaTagType tag)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool WriteSector(byte[] data, ulong sectorAddress)
{
return WriteSectors(data, sectorAddress, 1);
}
public bool WriteSectors(byte[] data, ulong sectorAddress, uint length)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
if(data.Length % imageInfo.SectorSize != 0)
{
ErrorMessage = "Incorrect data size";
return false;
}
if(sectorAddress + length > imageInfo.Sectors)
{
ErrorMessage = "Tried to write past image size";
return false;
}
Array.Copy(data, 0, deinterleaved, (int)(sectorAddress * imageInfo.SectorSize), data.Length);
ErrorMessage = "";
return true;
}
public bool WriteSectorLong(byte[] data, ulong sectorAddress)
{
ErrorMessage = "Writing sectors with tags is not supported.";
return false;
}
public bool WriteSectorsLong(byte[] data, ulong sectorAddress, uint length)
{
ErrorMessage = "Writing sectors with tags is not supported.";
return false;
}
public bool SetTracks(List<Track> tracks)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool Close()
{
if(!IsWriting)
{
ErrorMessage = "Image is not opened for writing";
return false;
}
bool isDos = deinterleaved[0x11001] == 17 && deinterleaved[0x11002] < 16 &&
deinterleaved[0x11027] <= 122 &&
deinterleaved[0x11034] == 35 && deinterleaved[0x11035] == 16 &&
deinterleaved[0x11036] == 0 &&
deinterleaved[0x11037] == 1;
byte[] tmp = new byte[deinterleaved.Length];
int[] offsets = extension == ".do"
? (isDos ? deinterleave : interleave)
: (isDos ? interleave : deinterleave);
for(int t = 0; t < 35; t++)
{
for(int s = 0; s < 16; s++)
Array.Copy(deinterleaved, t * 16 * 256 + offsets[s] * 256, tmp, t * 16 * 256 + s * 256, 256);
}
writingStream.Seek(0, SeekOrigin.Begin);
writingStream.Write(tmp, 0, tmp.Length);
writingStream.Flush();
writingStream.Close();
IsWriting = false;
ErrorMessage = "";
return true;
}
public bool SetMetadata(ImageInfo metadata)
{
return true;
}
public bool SetGeometry(uint cylinders, uint heads, uint sectorsPerTrack)
{
// Geometry is not stored in image
return true;
}
public bool WriteSectorTag(byte[] data, ulong sectorAddress, SectorTagType tag)
{
ErrorMessage = "Writing sectors with tags is not supported.";
return false;
}
public bool WriteSectorsTag(byte[] data, ulong sectorAddress, uint length, SectorTagType tag)
{
ErrorMessage = "Writing sectors with tags is not supported.";
return false;
}
public bool SetDumpHardware(List<DumpHardwareType> dumpHardware)
{
// Not supported
return false;
}
public bool SetCicmMetadata(CICMMetadataType metadata)
{
// Not supported
return false;
}
}
}

View File

@@ -0,0 +1,75 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : AppleDOS.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Manages interleaved Apple ][ disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Collections.Generic;
using System.IO;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
namespace DiscImageChef.DiscImages
{
public partial class AppleDos : IWritableImage
{
byte[] deinterleaved;
string extension;
ImageInfo imageInfo;
FileStream writingStream;
public AppleDos()
{
imageInfo = new ImageInfo
{
ReadableSectorTags = new List<SectorTagType>(),
ReadableMediaTags = new List<MediaTagType>(),
HasPartitions = false,
HasSessions = false,
Version = null,
Application = null,
ApplicationVersion = null,
Creator = null,
Comments = null,
MediaManufacturer = null,
MediaModel = null,
MediaSerialNumber = null,
MediaBarcode = null,
MediaPartNumber = null,
MediaSequence = 0,
LastMediaSequence = 0,
DriveManufacturer = null,
DriveModel = null,
DriveSerialNumber = null,
DriveFirmwareRevision = null
};
}
}
}

View File

@@ -0,0 +1,40 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Constants.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains constants for interleaved Apple ][ disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
namespace DiscImageChef.DiscImages
{
public partial class AppleDos
{
readonly int[] deinterleave = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
readonly int[] interleave = {0, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 15};
}
}

View File

@@ -0,0 +1,47 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Identify.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Identifies interleaved Apple ][ disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.IO;
using DiscImageChef.CommonTypes.Interfaces;
namespace DiscImageChef.DiscImages
{
public partial class AppleDos
{
public bool Identify(IFilter imageFilter)
{
extension = Path.GetExtension(imageFilter.GetFilename())?.ToLower();
return imageFilter.GetDataForkLength() == 143360 && (extension == ".po" || extension == ".do");
}
}
}

View File

@@ -0,0 +1,73 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Properties.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains properties for interleaved Apple ][ disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Exceptions;
using DiscImageChef.CommonTypes.Structs;
using Schemas;
namespace DiscImageChef.DiscImages
{
public partial class AppleDos
{
public ImageInfo Info => imageInfo;
public string Name => "Apple ][ Interleaved Disk Image";
public Guid Id => new Guid("A5828AC0-62C9-4304-81D4-EFD4AAE47360");
public string Format =>
extension == ".po"
? "Apple ][ Interleaved Disk Image (ProDOS order)"
: "Apple ][ Interleaved Disk Image (DOS order)";
public List<Track> Tracks =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<Session> Sessions =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<DumpHardwareType> DumpHardware => null;
public CICMMetadataType CicmMetadata => null;
public List<Partition> Partitions =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public IEnumerable<MediaTagType> SupportedMediaTags => new MediaTagType[] { };
public IEnumerable<SectorTagType> SupportedSectorTags => new SectorTagType[] { };
public IEnumerable<MediaType> SupportedMediaTypes => new[] {MediaType.Apple33SS};
public IEnumerable<(string name, Type type, string description)> SupportedOptions =>
new (string name, Type type, string description)[] { };
public IEnumerable<string> KnownExtensions => new[] {".do", ".po"};
public bool IsWriting { get; private set; }
public string ErrorMessage { get; private set; }
}
}

View File

@@ -0,0 +1,103 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Read.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Reads interleaved Apple ][ disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.IO;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
namespace DiscImageChef.DiscImages
{
public partial class AppleDos
{
public bool Open(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin);
byte[] tmp = new byte[imageFilter.GetDataForkLength()];
stream.Read(tmp, 0, tmp.Length);
bool isDos = tmp[0x11001] == 17 && tmp[0x11002] < 16 && tmp[0x11027] <= 122 && tmp[0x11034] == 35 &&
tmp[0x11035] == 16 && tmp[0x11036] == 0 && tmp[0x11037] == 1;
deinterleaved = new byte[tmp.Length];
extension = Path.GetExtension(imageFilter.GetFilename())?.ToLower();
int[] offsets = extension == ".do"
? (isDos ? deinterleave : interleave)
: (isDos ? interleave : deinterleave);
for(int t = 0; t < 35; t++)
{
for(int s = 0; s < 16; s++)
Array.Copy(tmp, t * 16 * 256 + s * 256, deinterleaved, t * 16 * 256 + offsets[s] * 256, 256);
}
imageInfo.SectorSize = 256;
imageInfo.ImageSize = (ulong)imageFilter.GetDataForkLength();
imageInfo.CreationTime = imageFilter.GetCreationTime();
imageInfo.LastModificationTime = imageFilter.GetLastWriteTime();
imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename());
imageInfo.Sectors = 560;
imageInfo.MediaType = MediaType.Apple33SS;
imageInfo.XmlMediaType = XmlMediaType.BlockMedia;
imageInfo.Cylinders = 35;
imageInfo.Heads = 2;
imageInfo.SectorsPerTrack = 16;
return true;
}
public byte[] ReadSector(ulong sectorAddress)
{
return ReadSectors(sectorAddress, 1);
}
public byte[] ReadSectors(ulong sectorAddress, uint length)
{
if(sectorAddress > imageInfo.Sectors - 1)
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
if(sectorAddress + length > imageInfo.Sectors)
throw new ArgumentOutOfRangeException(nameof(length), "Requested more sectors than available");
byte[] buffer = new byte[length * imageInfo.SectorSize];
Array.Copy(deinterleaved, (int)(sectorAddress * imageInfo.SectorSize), buffer, 0, buffer.Length);
return buffer;
}
}
}

View File

@@ -0,0 +1,144 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Unsupported.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains features unsupported by interleaved Apple ][ disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Collections.Generic;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Exceptions;
using DiscImageChef.CommonTypes.Structs;
namespace DiscImageChef.DiscImages
{
public partial class AppleDos
{
public bool? VerifySector(ulong sectorAddress)
{
return null;
}
public bool? VerifySector(ulong sectorAddress, uint track)
{
return null;
}
public bool? VerifySectors(ulong sectorAddress, uint length, out List<ulong> failingLbas,
out List<ulong> unknownLbas)
{
failingLbas = new List<ulong>();
unknownLbas = new List<ulong>();
for(ulong i = sectorAddress; i < sectorAddress + length; i++) unknownLbas.Add(i);
return null;
}
public bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List<ulong> failingLbas,
out List<ulong> unknownLbas)
{
failingLbas = new List<ulong>();
unknownLbas = new List<ulong>();
for(ulong i = sectorAddress; i < sectorAddress + length; i++) unknownLbas.Add(i);
return null;
}
public bool? VerifyMediaImage()
{
return null;
}
public List<Track> GetSessionTracks(Session session)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public List<Track> GetSessionTracks(ushort session)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSector(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectors(ulong sectorAddress, uint length, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorLong(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsLong(ulong sectorAddress, uint length, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorTag(ulong sectorAddress, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorLong(ulong sectorAddress)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsLong(ulong sectorAddress, uint length)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadDiskTag(MediaTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorTag(ulong sectorAddress, uint track, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, uint track, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
}
}

View File

@@ -0,0 +1,210 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Write.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Writes interleaved Apple ][ disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Structs;
using Schemas;
namespace DiscImageChef.DiscImages
{
public partial class AppleDos
{
public bool Create(string path, MediaType mediaType, Dictionary<string, string> options, ulong sectors,
uint sectorSize)
{
if(sectorSize != 256)
{
ErrorMessage = "Unsupported sector size";
return false;
}
if(mediaType != MediaType.Apple33SS)
{
ErrorMessage = $"Unsupport media format {mediaType}";
return false;
}
if(sectors > uint.MaxValue)
{
ErrorMessage = "Too many sectors";
return false;
}
imageInfo = new ImageInfo {MediaType = mediaType, SectorSize = sectorSize, Sectors = sectors};
try { writingStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); }
catch(IOException e)
{
ErrorMessage = $"Could not create new image file, exception {e.Message}";
return false;
}
deinterleaved = new byte[35 * 16 * 256];
extension = Path.GetExtension(path);
IsWriting = true;
ErrorMessage = null;
return true;
}
public bool WriteMediaTag(byte[] data, MediaTagType tag)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool WriteSector(byte[] data, ulong sectorAddress)
{
return WriteSectors(data, sectorAddress, 1);
}
public bool WriteSectors(byte[] data, ulong sectorAddress, uint length)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
if(data.Length % imageInfo.SectorSize != 0)
{
ErrorMessage = "Incorrect data size";
return false;
}
if(sectorAddress + length > imageInfo.Sectors)
{
ErrorMessage = "Tried to write past image size";
return false;
}
Array.Copy(data, 0, deinterleaved, (int)(sectorAddress * imageInfo.SectorSize), data.Length);
ErrorMessage = "";
return true;
}
public bool WriteSectorLong(byte[] data, ulong sectorAddress)
{
ErrorMessage = "Writing sectors with tags is not supported.";
return false;
}
public bool WriteSectorsLong(byte[] data, ulong sectorAddress, uint length)
{
ErrorMessage = "Writing sectors with tags is not supported.";
return false;
}
public bool SetTracks(List<Track> tracks)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool Close()
{
if(!IsWriting)
{
ErrorMessage = "Image is not opened for writing";
return false;
}
bool isDos = deinterleaved[0x11001] == 17 && deinterleaved[0x11002] < 16 &&
deinterleaved[0x11027] <= 122 &&
deinterleaved[0x11034] == 35 && deinterleaved[0x11035] == 16 &&
deinterleaved[0x11036] == 0 &&
deinterleaved[0x11037] == 1;
byte[] tmp = new byte[deinterleaved.Length];
int[] offsets = extension == ".do"
? (isDos ? deinterleave : interleave)
: (isDos ? interleave : deinterleave);
for(int t = 0; t < 35; t++)
{
for(int s = 0; s < 16; s++)
Array.Copy(deinterleaved, t * 16 * 256 + offsets[s] * 256, tmp, t * 16 * 256 + s * 256, 256);
}
writingStream.Seek(0, SeekOrigin.Begin);
writingStream.Write(tmp, 0, tmp.Length);
writingStream.Flush();
writingStream.Close();
IsWriting = false;
ErrorMessage = "";
return true;
}
public bool SetMetadata(ImageInfo metadata)
{
return true;
}
public bool SetGeometry(uint cylinders, uint heads, uint sectorsPerTrack)
{
// Geometry is not stored in image
return true;
}
public bool WriteSectorTag(byte[] data, ulong sectorAddress, SectorTagType tag)
{
ErrorMessage = "Writing sectors with tags is not supported.";
return false;
}
public bool WriteSectorsTag(byte[] data, ulong sectorAddress, uint length, SectorTagType tag)
{
ErrorMessage = "Writing sectors with tags is not supported.";
return false;
}
public bool SetDumpHardware(List<DumpHardwareType> dumpHardware)
{
// Not supported
return false;
}
public bool SetCicmMetadata(CICMMetadataType metadata)
{
// Not supported
return false;
}
}
}

View File

@@ -0,0 +1,75 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : AppleNIB.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Manages Apple nibbelized disc images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Collections.Generic;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
namespace DiscImageChef.DiscImages
{
// TODO: Checksum sectors
public partial class AppleNib : IMediaImage
{
Dictionary<ulong, byte[]> addressFields;
Dictionary<ulong, byte[]> cookedSectors;
ImageInfo imageInfo;
Dictionary<ulong, byte[]> longSectors;
public AppleNib()
{
imageInfo = new ImageInfo
{
ReadableSectorTags = new List<SectorTagType>(),
ReadableMediaTags = new List<MediaTagType>(),
HasPartitions = false,
HasSessions = false,
Version = null,
Application = null,
ApplicationVersion = null,
Creator = null,
Comments = null,
MediaManufacturer = null,
MediaModel = null,
MediaSerialNumber = null,
MediaBarcode = null,
MediaPartNumber = null,
MediaSequence = 0,
LastMediaSequence = 0,
DriveManufacturer = null,
DriveModel = null,
DriveSerialNumber = null,
DriveFirmwareRevision = null
};
}
}
}

View File

@@ -0,0 +1,53 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Constants.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains constants for Apple nibbelized disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
namespace DiscImageChef.DiscImages
{
public partial class AppleNib
{
readonly byte[] apple3_sign = {0x8D, 0xD0, 0x03, 0x4C, 0xC7, 0xA4};
readonly byte[] cpm_sign = {0xA2, 0x55, 0xA9, 0x00, 0x9D, 0x00, 0x0D, 0xCA};
readonly byte[] dos_sign = {0xA2, 0x02, 0x8E, 0x52};
readonly ulong[] dosSkewing = {0, 7, 14, 6, 13, 5, 12, 4, 11, 3, 10, 2, 9, 1, 8, 15};
readonly byte[] dri_string =
{
0x43, 0x4F, 0x50, 0x59, 0x52, 0x49, 0x47, 0x48, 0x54, 0x20, 0x28, 0x43, 0x29, 0x20, 0x31, 0x39, 0x37, 0x39,
0x2C, 0x20, 0x44, 0x49, 0x47, 0x49, 0x54, 0x41, 0x4C, 0x20, 0x52, 0x45, 0x53, 0x45, 0x41, 0x52, 0x43, 0x48
};
readonly byte[] pascal_sign = {0x08, 0xA5, 0x0F, 0x29};
readonly byte[] pascal_string = {0x53, 0x59, 0x53, 0x54, 0x45, 0x2E, 0x41, 0x50, 0x50, 0x4C, 0x45};
readonly byte[] pascal2_sign = {0xFF, 0xA2, 0x00, 0x8E};
readonly byte[] prodos_string = {0x50, 0x52, 0x4F, 0x44, 0x4F, 0x53};
readonly ulong[] proDosSkewing = {0, 8, 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15};
readonly byte[] sos_sign = {0xC9, 0x20, 0xF0, 0x3E};
}
}

View File

@@ -0,0 +1,49 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Helpers.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains helpers for Apple nibbelized disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using DiscImageChef.CommonTypes;
namespace DiscImageChef.DiscImages
{
public partial class AppleNib
{
MediaType GetMediaType()
{
switch(imageInfo.Sectors)
{
case 455: return MediaType.Apple32SS;
case 560: return MediaType.Apple33SS;
default: return MediaType.Unknown;
}
}
}
}

View File

@@ -0,0 +1,54 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Identify.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Identifies Apple nibbelized disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.IO;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.Decoders.Floppy;
namespace DiscImageChef.DiscImages
{
public partial class AppleNib
{
public bool Identify(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin);
if(stream.Length < 512) return false;
byte[] test = new byte[512];
stream.Read(test, 0, 512);
return Apple2.IsApple2GCR(test);
}
}
}

View File

@@ -0,0 +1,62 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Properties.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains properties for Apple nibbelized disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Exceptions;
using DiscImageChef.CommonTypes.Structs;
using Schemas;
namespace DiscImageChef.DiscImages
{
public partial class AppleNib
{
public ImageInfo Info => imageInfo;
public string Name => "Apple NIB";
public Guid Id => new Guid("AE171AE8-6747-49CC-B861-9D450B7CD42E");
public string Format => "Apple nibbles";
public List<Partition> Partitions =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<Track> Tracks =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<Session> Sessions =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<DumpHardwareType> DumpHardware => null;
public CICMMetadataType CicmMetadata => null;
}
}

View File

@@ -1,15 +1,15 @@
// /***************************************************************************
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : AppleNIB.cs
// Filename : Read.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Manages Apple nibbelized disc images.
// Reads Apple nibbelized disk images.
//
// --[ License ] --------------------------------------------------------------
//
@@ -38,92 +38,13 @@ using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Exceptions;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
using DiscImageChef.Console;
using DiscImageChef.Decoders.Floppy;
using Schemas;
namespace DiscImageChef.DiscImages
{
// TODO: Checksum sectors
public class AppleNib : IMediaImage
public partial class AppleNib
{
readonly byte[] apple3_sign = {0x8D, 0xD0, 0x03, 0x4C, 0xC7, 0xA4};
readonly byte[] cpm_sign = {0xA2, 0x55, 0xA9, 0x00, 0x9D, 0x00, 0x0D, 0xCA};
readonly byte[] dos_sign = {0xA2, 0x02, 0x8E, 0x52};
readonly ulong[] dosSkewing = {0, 7, 14, 6, 13, 5, 12, 4, 11, 3, 10, 2, 9, 1, 8, 15};
readonly byte[] dri_string =
{
0x43, 0x4F, 0x50, 0x59, 0x52, 0x49, 0x47, 0x48, 0x54, 0x20, 0x28, 0x43, 0x29, 0x20, 0x31, 0x39, 0x37, 0x39,
0x2C, 0x20, 0x44, 0x49, 0x47, 0x49, 0x54, 0x41, 0x4C, 0x20, 0x52, 0x45, 0x53, 0x45, 0x41, 0x52, 0x43, 0x48
};
readonly byte[] pascal_sign = {0x08, 0xA5, 0x0F, 0x29};
readonly byte[] pascal_string = {0x53, 0x59, 0x53, 0x54, 0x45, 0x2E, 0x41, 0x50, 0x50, 0x4C, 0x45};
readonly byte[] pascal2_sign = {0xFF, 0xA2, 0x00, 0x8E};
readonly byte[] prodos_string = {0x50, 0x52, 0x4F, 0x44, 0x4F, 0x53};
readonly ulong[] proDosSkewing = {0, 8, 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15};
readonly byte[] sos_sign = {0xC9, 0x20, 0xF0, 0x3E};
Dictionary<ulong, byte[]> addressFields;
Dictionary<ulong, byte[]> cookedSectors;
ImageInfo imageInfo;
Dictionary<ulong, byte[]> longSectors;
public AppleNib()
{
imageInfo = new ImageInfo
{
ReadableSectorTags = new List<SectorTagType>(),
ReadableMediaTags = new List<MediaTagType>(),
HasPartitions = false,
HasSessions = false,
Version = null,
Application = null,
ApplicationVersion = null,
Creator = null,
Comments = null,
MediaManufacturer = null,
MediaModel = null,
MediaSerialNumber = null,
MediaBarcode = null,
MediaPartNumber = null,
MediaSequence = 0,
LastMediaSequence = 0,
DriveManufacturer = null,
DriveModel = null,
DriveSerialNumber = null,
DriveFirmwareRevision = null
};
}
public ImageInfo Info => imageInfo;
public string Name => "Apple NIB";
public Guid Id => new Guid("AE171AE8-6747-49CC-B861-9D450B7CD42E");
public string Format => "Apple nibbles";
public List<Partition> Partitions =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<Track> Tracks =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<Session> Sessions =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public bool Identify(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin);
if(stream.Length < 512) return false;
byte[] test = new byte[512];
stream.Read(test, 0, 512);
return Apple2.IsApple2GCR(test);
}
public bool Open(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
@@ -328,94 +249,5 @@ namespace DiscImageChef.DiscImages
return ms.ToArray();
}
public byte[] ReadDiskTag(MediaTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSector(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorTag(ulong sectorAddress, uint track, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectors(ulong sectorAddress, uint length, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, uint track, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorLong(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsLong(ulong sectorAddress, uint length, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public List<Track> GetSessionTracks(Session session)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public List<Track> GetSessionTracks(ushort session)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public bool? VerifySector(ulong sectorAddress)
{
return null;
}
public bool? VerifySector(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public bool? VerifySectors(ulong sectorAddress, uint length, out List<ulong> failingLbas,
out List<ulong> unknownLbas)
{
failingLbas = new List<ulong>();
unknownLbas = new List<ulong>();
for(ulong i = 0; i < imageInfo.Sectors; i++) unknownLbas.Add(i);
return null;
}
public bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List<ulong> failingLbas,
out List<ulong> unknownLbas)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public bool? VerifyMediaImage()
{
return null;
}
public List<DumpHardwareType> DumpHardware => null;
public CICMMetadataType CicmMetadata => null;
MediaType GetMediaType()
{
switch(imageInfo.Sectors)
{
case 455: return MediaType.Apple32SS;
case 560: return MediaType.Apple33SS;
default: return MediaType.Unknown;
}
}
}
}

View File

@@ -0,0 +1,118 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Unsupported.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains features unsupported by Apple nibbelized disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Collections.Generic;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Exceptions;
using DiscImageChef.CommonTypes.Structs;
namespace DiscImageChef.DiscImages
{
public partial class AppleNib
{
public byte[] ReadDiskTag(MediaTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSector(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorTag(ulong sectorAddress, uint track, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectors(ulong sectorAddress, uint length, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, uint track, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorLong(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsLong(ulong sectorAddress, uint length, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public List<Track> GetSessionTracks(Session session)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public List<Track> GetSessionTracks(ushort session)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public bool? VerifySector(ulong sectorAddress)
{
return null;
}
public bool? VerifySector(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public bool? VerifySectors(ulong sectorAddress, uint length, out List<ulong> failingLbas,
out List<ulong> unknownLbas)
{
failingLbas = new List<ulong>();
unknownLbas = new List<ulong>();
for(ulong i = 0; i < imageInfo.Sectors; i++) unknownLbas.Add(i);
return null;
}
public bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List<ulong> failingLbas,
out List<ulong> unknownLbas)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public bool? VerifyMediaImage()
{
return null;
}
}
}

View File

@@ -1,772 +0,0 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Apridisk.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Manages Apridisk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Exceptions;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
using DiscImageChef.Console;
using Schemas;
namespace DiscImageChef.DiscImages
{
// TODO: Check writing
public class Apridisk : IWritableImage
{
readonly byte[] signature =
{
0x41, 0x43, 0x54, 0x20, 0x41, 0x70, 0x72, 0x69, 0x63, 0x6F, 0x74, 0x20, 0x64, 0x69, 0x73, 0x6B, 0x20, 0x69,
0x6D, 0x61, 0x67, 0x65, 0x1A, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00
};
ImageInfo imageInfo;
// Cylinder by head, sector data matrix
byte[][][][] sectorsData;
FileStream writingStream;
public Apridisk()
{
imageInfo = new ImageInfo
{
ReadableSectorTags = new List<SectorTagType>(),
ReadableMediaTags = new List<MediaTagType>(),
HasPartitions = false,
HasSessions = false,
Version = null,
Application = null,
ApplicationVersion = null,
Creator = null,
Comments = null,
MediaManufacturer = null,
MediaModel = null,
MediaSerialNumber = null,
MediaBarcode = null,
MediaPartNumber = null,
MediaSequence = 0,
LastMediaSequence = 0,
DriveManufacturer = null,
DriveModel = null,
DriveSerialNumber = null,
DriveFirmwareRevision = null
};
}
public ImageInfo Info => imageInfo;
public string Name => "ACT Apricot Disk Image";
public Guid Id => new Guid("43408CF3-6DB3-449F-A779-2B0E497C5B14");
public string Format => "ACT Apricot disk image";
public List<Partition> Partitions =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<Track> Tracks =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<Session> Sessions =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public bool Identify(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin);
if(stream.Length < signature.Length) return false;
byte[] sigB = new byte[signature.Length];
stream.Read(sigB, 0, signature.Length);
return sigB.SequenceEqual(signature);
}
public bool Open(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin);
// Skip signature
stream.Seek(signature.Length, SeekOrigin.Begin);
int totalCylinders = -1;
int totalHeads = -1;
int maxSector = -1;
int recordSize = Marshal.SizeOf(typeof(ApridiskRecord));
// Count cylinders
while(stream.Position < stream.Length)
{
byte[] recB = new byte[recordSize];
stream.Read(recB, 0, recordSize);
GCHandle handle = GCHandle.Alloc(recB, GCHandleType.Pinned);
ApridiskRecord record =
(ApridiskRecord)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(ApridiskRecord));
handle.Free();
switch(record.type)
{
// Deleted record, just skip it
case RecordType.Deleted:
DicConsole.DebugWriteLine("Apridisk plugin", "Found deleted record at {0}", stream.Position);
stream.Seek(record.headerSize - recordSize + record.dataSize, SeekOrigin.Current);
break;
case RecordType.Comment:
DicConsole.DebugWriteLine("Apridisk plugin", "Found comment record at {0}", stream.Position);
stream.Seek(record.headerSize - recordSize, SeekOrigin.Current);
byte[] commentB = new byte[record.dataSize];
stream.Read(commentB, 0, commentB.Length);
imageInfo.Comments = StringHandlers.CToString(commentB);
DicConsole.DebugWriteLine("Apridisk plugin", "Comment: \"{0}\"", imageInfo.Comments);
break;
case RecordType.Creator:
DicConsole.DebugWriteLine("Apridisk plugin", "Found creator record at {0}", stream.Position);
stream.Seek(record.headerSize - recordSize, SeekOrigin.Current);
byte[] creatorB = new byte[record.dataSize];
stream.Read(creatorB, 0, creatorB.Length);
imageInfo.Creator = StringHandlers.CToString(creatorB);
DicConsole.DebugWriteLine("Apridisk plugin", "Creator: \"{0}\"", imageInfo.Creator);
break;
case RecordType.Sector:
if(record.compression != CompressType.Compressed &&
record.compression != CompressType.Uncompresed)
throw new
ImageNotSupportedException($"Found record with unknown compression type 0x{(ushort)record.compression:X4} at {stream.Position}");
DicConsole.DebugWriteLine("Apridisk plugin",
"Found {4} sector record at {0} for cylinder {1} head {2} sector {3}",
stream.Position, record.cylinder, record.head, record.sector,
record.compression == CompressType.Compressed
? "compressed"
: "uncompressed");
if(record.cylinder > totalCylinders) totalCylinders = record.cylinder;
if(record.head > totalHeads) totalHeads = record.head;
if(record.sector > maxSector) maxSector = record.sector;
stream.Seek(record.headerSize - recordSize + record.dataSize, SeekOrigin.Current);
break;
default:
throw new
ImageNotSupportedException($"Found record with unknown type 0x{(uint)record.type:X8} at {stream.Position}");
}
}
totalCylinders++;
totalHeads++;
if(totalCylinders <= 0 || totalHeads <= 0)
throw new ImageNotSupportedException("No cylinders or heads found");
sectorsData = new byte[totalCylinders][][][];
// Total sectors per track
uint[][] spts = new uint[totalCylinders][];
imageInfo.Cylinders = (ushort)totalCylinders;
imageInfo.Heads = (byte)totalHeads;
DicConsole.DebugWriteLine("Apridisk plugin",
"Found {0} cylinders and {1} heads with a maximum sector number of {2}",
totalCylinders, totalHeads, maxSector);
// Create heads
for(int i = 0; i < totalCylinders; i++)
{
sectorsData[i] = new byte[totalHeads][][];
spts[i] = new uint[totalHeads];
for(int j = 0; j < totalHeads; j++) sectorsData[i][j] = new byte[maxSector + 1][];
}
imageInfo.SectorSize = uint.MaxValue;
ulong headersizes = 0;
// Read sectors
stream.Seek(signature.Length, SeekOrigin.Begin);
while(stream.Position < stream.Length)
{
byte[] recB = new byte[recordSize];
stream.Read(recB, 0, recordSize);
GCHandle handle = GCHandle.Alloc(recB, GCHandleType.Pinned);
ApridiskRecord record =
(ApridiskRecord)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(ApridiskRecord));
handle.Free();
switch(record.type)
{
// Not sector record, just skip it
case RecordType.Deleted:
case RecordType.Comment:
case RecordType.Creator:
stream.Seek(record.headerSize - recordSize + record.dataSize, SeekOrigin.Current);
headersizes += record.headerSize + record.dataSize;
break;
case RecordType.Sector:
stream.Seek(record.headerSize - recordSize, SeekOrigin.Current);
byte[] data = new byte[record.dataSize];
stream.Read(data, 0, data.Length);
spts[record.cylinder][record.head]++;
uint realLength = record.dataSize;
if(record.compression == CompressType.Compressed)
realLength =
Decompress(data, out sectorsData[record.cylinder][record.head][record.sector]);
else sectorsData[record.cylinder][record.head][record.sector] = data;
if(realLength < imageInfo.SectorSize) imageInfo.SectorSize = realLength;
headersizes += record.headerSize + record.dataSize;
break;
}
}
DicConsole.DebugWriteLine("Apridisk plugin", "Found a minimum of {0} bytes per sector",
imageInfo.SectorSize);
// Count sectors per track
uint spt = uint.MaxValue;
for(ushort cyl = 0; cyl < imageInfo.Cylinders; cyl++)
{
for(ushort head = 0; head < imageInfo.Heads; head++)
if(spts[cyl][head] < spt)
spt = spts[cyl][head];
}
imageInfo.SectorsPerTrack = spt;
DicConsole.DebugWriteLine("Apridisk plugin", "Found a minimum of {0} sectors per track",
imageInfo.SectorsPerTrack);
imageInfo.MediaType = Geometry.GetMediaType(((ushort)imageInfo.Cylinders, (byte)imageInfo.Heads,
(ushort)imageInfo.SectorsPerTrack, 512, MediaEncoding.MFM,
false));
imageInfo.ImageSize = (ulong)stream.Length - headersizes;
imageInfo.CreationTime = imageFilter.GetCreationTime();
imageInfo.LastModificationTime = imageFilter.GetLastWriteTime();
imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename());
imageInfo.Sectors = imageInfo.Cylinders * imageInfo.Heads * imageInfo.SectorsPerTrack;
imageInfo.XmlMediaType = XmlMediaType.BlockMedia;
return true;
}
public byte[] ReadSector(ulong sectorAddress)
{
(ushort cylinder, byte head, byte sector) = LbaToChs(sectorAddress);
if(cylinder >= sectorsData.Length)
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
if(head >= sectorsData[cylinder].Length)
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
if(sector > sectorsData[cylinder][head].Length)
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
return sectorsData[cylinder][head][sector];
}
public byte[] ReadSectors(ulong sectorAddress, uint length)
{
if(sectorAddress > imageInfo.Sectors - 1)
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
if(sectorAddress + length > imageInfo.Sectors)
throw new ArgumentOutOfRangeException(nameof(length), "Requested more sectors than available");
MemoryStream buffer = new MemoryStream();
for(uint i = 0; i < length; i++)
{
byte[] sector = ReadSector(sectorAddress + i);
buffer.Write(sector, 0, sector.Length);
}
return buffer.ToArray();
}
public byte[] ReadDiskTag(MediaTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorTag(ulong sectorAddress, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSector(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorTag(ulong sectorAddress, uint track, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectors(ulong sectorAddress, uint length, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, uint track, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorLong(ulong sectorAddress)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorLong(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsLong(ulong sectorAddress, uint length)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsLong(ulong sectorAddress, uint length, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public List<Track> GetSessionTracks(Session session)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public List<Track> GetSessionTracks(ushort session)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public bool? VerifySector(ulong sectorAddress)
{
return null;
}
public bool? VerifySector(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public bool? VerifySectors(ulong sectorAddress, uint length, out List<ulong> failingLbas,
out List<ulong> unknownLbas)
{
failingLbas = new List<ulong>();
unknownLbas = new List<ulong>();
for(ulong i = 0; i < imageInfo.Sectors; i++) unknownLbas.Add(i);
return null;
}
public bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List<ulong> failingLbas,
out List<ulong> unknownLbas)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public bool? VerifyMediaImage()
{
return null;
}
public List<DumpHardwareType> DumpHardware => null;
public CICMMetadataType CicmMetadata => null;
public IEnumerable<MediaTagType> SupportedMediaTags => new MediaTagType[] { };
public IEnumerable<SectorTagType> SupportedSectorTags => new SectorTagType[] { };
// TODO: Test with real hardware to see real supported media
public IEnumerable<MediaType> SupportedMediaTypes =>
new[]
{
MediaType.ACORN_35_DS_DD, MediaType.ACORN_35_DS_HD, MediaType.Apricot_35, MediaType.ATARI_35_DS_DD,
MediaType.ATARI_35_DS_DD_11, MediaType.ATARI_35_SS_DD, MediaType.ATARI_35_SS_DD_11, MediaType.DMF,
MediaType.DMF_82, MediaType.DOS_35_DS_DD_8, MediaType.DOS_35_DS_DD_9, MediaType.DOS_35_ED,
MediaType.DOS_35_HD, MediaType.DOS_35_SS_DD_8, MediaType.DOS_35_SS_DD_9, MediaType.DOS_525_DS_DD_8,
MediaType.DOS_525_DS_DD_9, MediaType.DOS_525_HD, MediaType.DOS_525_SS_DD_8, MediaType.DOS_525_SS_DD_9,
MediaType.FDFORMAT_35_DD, MediaType.FDFORMAT_35_HD, MediaType.FDFORMAT_525_DD,
MediaType.FDFORMAT_525_HD, MediaType.RX50, MediaType.XDF_35, MediaType.XDF_525
};
public IEnumerable<(string name, Type type, string description)> SupportedOptions =>
new[] {("compress", typeof(bool), "Enable Apridisk compression.")};
public IEnumerable<string> KnownExtensions => new[] {".dsk"};
public bool IsWriting { get; private set; }
public string ErrorMessage { get; private set; }
public bool Create(string path, MediaType mediaType, Dictionary<string, string> options, ulong sectors,
uint sectorSize)
{
if(!SupportedMediaTypes.Contains(mediaType))
{
ErrorMessage = $"Unsupport media format {mediaType}";
return false;
}
imageInfo = new ImageInfo {MediaType = mediaType, SectorSize = sectorSize, Sectors = sectors};
try { writingStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); }
catch(IOException e)
{
ErrorMessage = $"Could not create new image file, exception {e.Message}";
return false;
}
IsWriting = true;
ErrorMessage = null;
return true;
}
public bool WriteMediaTag(byte[] data, MediaTagType tag)
{
ErrorMessage = "Writing media tags is not supported.";
return false;
}
public bool WriteSector(byte[] data, ulong sectorAddress)
{
(ushort cylinder, byte head, byte sector) = LbaToChs(sectorAddress);
if(cylinder >= sectorsData.Length)
{
ErrorMessage = "Sector address not found";
return false;
}
if(head >= sectorsData[cylinder].Length)
{
ErrorMessage = "Sector address not found";
return false;
}
if(sector > sectorsData[cylinder][head].Length)
{
ErrorMessage = "Sector address not found";
return false;
}
sectorsData[cylinder][head][sector] = data;
return true;
}
public bool WriteSectors(byte[] data, ulong sectorAddress, uint length)
{
for(uint i = 0; i < length; i++)
{
(ushort cylinder, byte head, byte sector) = LbaToChs(sectorAddress);
if(cylinder >= sectorsData.Length)
{
ErrorMessage = "Sector address not found";
return false;
}
if(head >= sectorsData[cylinder].Length)
{
ErrorMessage = "Sector address not found";
return false;
}
if(sector > sectorsData[cylinder][head].Length)
{
ErrorMessage = "Sector address not found";
return false;
}
sectorsData[cylinder][head][sector] = data;
}
return true;
}
public bool WriteSectorLong(byte[] data, ulong sectorAddress)
{
ErrorMessage = "Writing sectors with tags is not supported.";
return false;
}
public bool WriteSectorsLong(byte[] data, ulong sectorAddress, uint length)
{
ErrorMessage = "Writing sectors with tags is not supported.";
return false;
}
public bool SetTracks(List<Track> tracks)
{
ErrorMessage = "Unsupported feature";
return false;
}
// TODO: Try if apridisk software supports finding other chunks, to extend metadata support
public bool Close()
{
writingStream.Seek(0, SeekOrigin.Begin);
writingStream.Write(signature, 0, signature.Length);
byte[] hdr = new byte[Marshal.SizeOf(typeof(ApridiskRecord))];
IntPtr hdrPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ApridiskRecord)));
for(ushort c = 0; c < imageInfo.Cylinders; c++)
{
for(byte h = 0; h < imageInfo.Heads; h++)
{
for(byte s = 0; s < imageInfo.SectorsPerTrack; s++)
{
if(sectorsData[c][h][s] == null || sectorsData[c][h][s].Length == 0) continue;
ApridiskRecord record = new ApridiskRecord
{
type = RecordType.Sector,
compression = CompressType.Uncompresed,
headerSize = (ushort)Marshal.SizeOf(typeof(ApridiskRecord)),
dataSize = (uint)sectorsData[c][h][s].Length,
head = h,
sector = s,
cylinder = c
};
Marshal.StructureToPtr(record, hdrPtr, true);
Marshal.Copy(hdrPtr, hdr, 0, hdr.Length);
writingStream.Write(hdr, 0, hdr.Length);
writingStream.Write(sectorsData[c][h][s], 0, sectorsData[c][h][s].Length);
}
}
}
if(!string.IsNullOrEmpty(imageInfo.Creator))
{
byte[] creatorBytes = Encoding.UTF8.GetBytes(imageInfo.Creator);
ApridiskRecord creatorRecord = new ApridiskRecord
{
type = RecordType.Creator,
compression = CompressType.Uncompresed,
headerSize = (ushort)Marshal.SizeOf(typeof(ApridiskRecord)),
dataSize = (uint)creatorBytes.Length + 1,
head = 0,
sector = 0,
cylinder = 0
};
Marshal.StructureToPtr(creatorRecord, hdrPtr, true);
Marshal.Copy(hdrPtr, hdr, 0, hdr.Length);
writingStream.Write(hdr, 0, hdr.Length);
writingStream.Write(creatorBytes, 0, creatorBytes.Length);
writingStream.WriteByte(0); // Termination
}
if(!string.IsNullOrEmpty(imageInfo.Comments))
{
byte[] commentBytes = Encoding.UTF8.GetBytes(imageInfo.Comments);
ApridiskRecord commentRecord = new ApridiskRecord
{
type = RecordType.Comment,
compression = CompressType.Uncompresed,
headerSize = (ushort)Marshal.SizeOf(typeof(ApridiskRecord)),
dataSize = (uint)commentBytes.Length + 1,
head = 0,
sector = 0,
cylinder = 0
};
Marshal.StructureToPtr(commentRecord, hdrPtr, true);
Marshal.Copy(hdrPtr, hdr, 0, hdr.Length);
writingStream.Write(hdr, 0, hdr.Length);
writingStream.Write(commentBytes, 0, commentBytes.Length);
writingStream.WriteByte(0); // Termination
}
Marshal.FreeHGlobal(hdrPtr);
writingStream.Flush();
writingStream.Close();
IsWriting = false;
ErrorMessage = "";
return true;
}
public bool SetMetadata(ImageInfo metadata)
{
imageInfo.Comments = metadata.Comments;
imageInfo.Creator = metadata.Creator;
return true;
}
public bool SetGeometry(uint cylinders, uint heads, uint sectorsPerTrack)
{
if(cylinders > ushort.MaxValue)
{
ErrorMessage = "Too many cylinders";
return false;
}
if(heads > byte.MaxValue)
{
ErrorMessage = "Too many heads";
return false;
}
if(sectorsPerTrack > byte.MaxValue)
{
ErrorMessage = "Too many sectors per track";
return false;
}
sectorsData = new byte[cylinders][][][];
for(ushort c = 0; c < cylinders; c++)
{
sectorsData[c] = new byte[heads][][];
for(byte h = 0; h < heads; h++) sectorsData[c][h] = new byte[sectorsPerTrack][];
}
imageInfo.Cylinders = cylinders;
imageInfo.Heads = heads;
imageInfo.SectorsPerTrack = sectorsPerTrack;
return true;
}
public bool WriteSectorTag(byte[] data, ulong sectorAddress, SectorTagType tag)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool WriteSectorsTag(byte[] data, ulong sectorAddress, uint length, SectorTagType tag)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool SetDumpHardware(List<DumpHardwareType> dumpHardware)
{
// Not supported
return false;
}
public bool SetCicmMetadata(CICMMetadataType metadata)
{
// Not supported
return false;
}
static uint Decompress(byte[] compressed, out byte[] decompressed)
{
int readp = 0;
int cLen = compressed.Length;
MemoryStream buffer = new MemoryStream();
uint uLen = 0;
while(cLen >= 3)
{
ushort blklen = BitConverter.ToUInt16(compressed, readp);
readp += 2;
for(int i = 0; i < blklen; i++) buffer.WriteByte(compressed[readp]);
uLen += blklen;
readp++;
cLen -= 3;
}
decompressed = buffer.ToArray();
return uLen;
}
(ushort cylinder, byte head, byte sector) LbaToChs(ulong lba)
{
ushort cylinder = (ushort)(lba / (imageInfo.Heads * imageInfo.SectorsPerTrack));
byte head = (byte)(lba / imageInfo.SectorsPerTrack % imageInfo.Heads);
byte sector = (byte)(lba % imageInfo.SectorsPerTrack + 1);
return (cylinder, head, sector);
}
enum RecordType : uint
{
Deleted = 0xE31D0000,
Sector = 0xE31D0001,
Comment = 0xE31D0002,
Creator = 0xE31D0003
}
enum CompressType : ushort
{
Uncompresed = 0x9E90,
Compressed = 0x3E5A
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ApridiskRecord
{
public RecordType type;
public CompressType compression;
public ushort headerSize;
public uint dataSize;
public byte head;
public byte sector;
public ushort cylinder;
}
}
}

View File

@@ -0,0 +1,77 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Apridisk.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Manages Apridisk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Collections.Generic;
using System.IO;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
namespace DiscImageChef.DiscImages
{
// TODO: Check writing
public partial class Apridisk : IWritableImage
{
ImageInfo imageInfo;
// Cylinder by head, sector data matrix
byte[][][][] sectorsData;
FileStream writingStream;
public Apridisk()
{
imageInfo = new ImageInfo
{
ReadableSectorTags = new List<SectorTagType>(),
ReadableMediaTags = new List<MediaTagType>(),
HasPartitions = false,
HasSessions = false,
Version = null,
Application = null,
ApplicationVersion = null,
Creator = null,
Comments = null,
MediaManufacturer = null,
MediaModel = null,
MediaSerialNumber = null,
MediaBarcode = null,
MediaPartNumber = null,
MediaSequence = 0,
LastMediaSequence = 0,
DriveManufacturer = null,
DriveModel = null,
DriveSerialNumber = null,
DriveFirmwareRevision = null
};
}
}
}

View File

@@ -0,0 +1,64 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Compression.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains compression algorithm for Apridisk disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.IO;
namespace DiscImageChef.DiscImages
{
public partial class Apridisk
{
static uint Decompress(byte[] compressed, out byte[] decompressed)
{
int readp = 0;
int cLen = compressed.Length;
MemoryStream buffer = new MemoryStream();
uint uLen = 0;
while(cLen >= 3)
{
ushort blklen = BitConverter.ToUInt16(compressed, readp);
readp += 2;
for(int i = 0; i < blklen; i++) buffer.WriteByte(compressed[readp]);
uLen += blklen;
readp++;
cLen -= 3;
}
decompressed = buffer.ToArray();
return uLen;
}
}
}

View File

@@ -0,0 +1,49 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Constants.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains constants for Apridisk disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
namespace DiscImageChef.DiscImages
{
public partial class Apridisk
{
readonly byte[] signature =
{
0x41, 0x43, 0x54, 0x20, 0x41, 0x70, 0x72, 0x69, 0x63, 0x6F, 0x74, 0x20, 0x64, 0x69, 0x73, 0x6B, 0x20, 0x69,
0x6D, 0x61, 0x67, 0x65, 0x1A, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00
};
}
}

View File

@@ -0,0 +1,51 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Enums.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains enumerations for Apridisk disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
namespace DiscImageChef.DiscImages
{
public partial class Apridisk
{
enum RecordType : uint
{
Deleted = 0xE31D0000,
Sector = 0xE31D0001,
Comment = 0xE31D0002,
Creator = 0xE31D0003
}
enum CompressType : ushort
{
Uncompresed = 0x9E90,
Compressed = 0x3E5A
}
}
}

View File

@@ -0,0 +1,46 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Helpers.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains helpers for Apridisk disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
namespace DiscImageChef.DiscImages
{
public partial class Apridisk
{
(ushort cylinder, byte head, byte sector) LbaToChs(ulong lba)
{
ushort cylinder = (ushort)(lba / (imageInfo.Heads * imageInfo.SectorsPerTrack));
byte head = (byte)(lba / imageInfo.SectorsPerTrack % imageInfo.Heads);
byte sector = (byte)(lba % imageInfo.SectorsPerTrack + 1);
return (cylinder, head, sector);
}
}
}

View File

@@ -0,0 +1,54 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Identify.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Identifies Apridisk disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.IO;
using System.Linq;
using DiscImageChef.CommonTypes.Interfaces;
namespace DiscImageChef.DiscImages
{
public partial class Apridisk
{
public bool Identify(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin);
if(stream.Length < signature.Length) return false;
byte[] sigB = new byte[signature.Length];
stream.Read(sigB, 0, signature.Length);
return sigB.SequenceEqual(signature);
}
}
}

View File

@@ -0,0 +1,83 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Properties.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains properties for Apridisk disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Exceptions;
using DiscImageChef.CommonTypes.Structs;
using Schemas;
namespace DiscImageChef.DiscImages
{
public partial class Apridisk
{
public ImageInfo Info => imageInfo;
public string Name => "ACT Apricot Disk Image";
public Guid Id => new Guid("43408CF3-6DB3-449F-A779-2B0E497C5B14");
public string Format => "ACT Apricot disk image";
public List<Partition> Partitions =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<Track> Tracks =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<Session> Sessions =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<DumpHardwareType> DumpHardware => null;
public CICMMetadataType CicmMetadata => null;
public IEnumerable<MediaTagType> SupportedMediaTags => new MediaTagType[] { };
public IEnumerable<SectorTagType> SupportedSectorTags => new SectorTagType[] { };
// TODO: Test with real hardware to see real supported media
public IEnumerable<MediaType> SupportedMediaTypes =>
new[]
{
MediaType.ACORN_35_DS_DD, MediaType.ACORN_35_DS_HD, MediaType.Apricot_35, MediaType.ATARI_35_DS_DD,
MediaType.ATARI_35_DS_DD_11, MediaType.ATARI_35_SS_DD, MediaType.ATARI_35_SS_DD_11, MediaType.DMF,
MediaType.DMF_82, MediaType.DOS_35_DS_DD_8, MediaType.DOS_35_DS_DD_9, MediaType.DOS_35_ED,
MediaType.DOS_35_HD, MediaType.DOS_35_SS_DD_8, MediaType.DOS_35_SS_DD_9, MediaType.DOS_525_DS_DD_8,
MediaType.DOS_525_DS_DD_9, MediaType.DOS_525_HD, MediaType.DOS_525_SS_DD_8, MediaType.DOS_525_SS_DD_9,
MediaType.FDFORMAT_35_DD, MediaType.FDFORMAT_35_HD, MediaType.FDFORMAT_525_DD,
MediaType.FDFORMAT_525_HD, MediaType.RX50, MediaType.XDF_35, MediaType.XDF_525
};
public IEnumerable<(string name, Type type, string description)> SupportedOptions =>
new[] {("compress", typeof(bool), "Enable Apridisk compression.")};
public IEnumerable<string> KnownExtensions => new[] {".dsk"};
public bool IsWriting { get; private set; }
public string ErrorMessage { get; private set; }
}
}

View File

@@ -0,0 +1,256 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Read.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Reads Apridisk disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.IO;
using System.Runtime.InteropServices;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Exceptions;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.Console;
namespace DiscImageChef.DiscImages
{
public partial class Apridisk
{
public bool Open(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin);
// Skip signature
stream.Seek(signature.Length, SeekOrigin.Begin);
int totalCylinders = -1;
int totalHeads = -1;
int maxSector = -1;
int recordSize = Marshal.SizeOf(typeof(ApridiskRecord));
// Count cylinders
while(stream.Position < stream.Length)
{
byte[] recB = new byte[recordSize];
stream.Read(recB, 0, recordSize);
GCHandle handle = GCHandle.Alloc(recB, GCHandleType.Pinned);
ApridiskRecord record =
(ApridiskRecord)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(ApridiskRecord));
handle.Free();
switch(record.type)
{
// Deleted record, just skip it
case RecordType.Deleted:
DicConsole.DebugWriteLine("Apridisk plugin", "Found deleted record at {0}", stream.Position);
stream.Seek(record.headerSize - recordSize + record.dataSize, SeekOrigin.Current);
break;
case RecordType.Comment:
DicConsole.DebugWriteLine("Apridisk plugin", "Found comment record at {0}", stream.Position);
stream.Seek(record.headerSize - recordSize, SeekOrigin.Current);
byte[] commentB = new byte[record.dataSize];
stream.Read(commentB, 0, commentB.Length);
imageInfo.Comments = StringHandlers.CToString(commentB);
DicConsole.DebugWriteLine("Apridisk plugin", "Comment: \"{0}\"", imageInfo.Comments);
break;
case RecordType.Creator:
DicConsole.DebugWriteLine("Apridisk plugin", "Found creator record at {0}", stream.Position);
stream.Seek(record.headerSize - recordSize, SeekOrigin.Current);
byte[] creatorB = new byte[record.dataSize];
stream.Read(creatorB, 0, creatorB.Length);
imageInfo.Creator = StringHandlers.CToString(creatorB);
DicConsole.DebugWriteLine("Apridisk plugin", "Creator: \"{0}\"", imageInfo.Creator);
break;
case RecordType.Sector:
if(record.compression != CompressType.Compressed &&
record.compression != CompressType.Uncompresed)
throw new
ImageNotSupportedException($"Found record with unknown compression type 0x{(ushort)record.compression:X4} at {stream.Position}");
DicConsole.DebugWriteLine("Apridisk plugin",
"Found {4} sector record at {0} for cylinder {1} head {2} sector {3}",
stream.Position, record.cylinder, record.head, record.sector,
record.compression == CompressType.Compressed
? "compressed"
: "uncompressed");
if(record.cylinder > totalCylinders) totalCylinders = record.cylinder;
if(record.head > totalHeads) totalHeads = record.head;
if(record.sector > maxSector) maxSector = record.sector;
stream.Seek(record.headerSize - recordSize + record.dataSize, SeekOrigin.Current);
break;
default:
throw new
ImageNotSupportedException($"Found record with unknown type 0x{(uint)record.type:X8} at {stream.Position}");
}
}
totalCylinders++;
totalHeads++;
if(totalCylinders <= 0 || totalHeads <= 0)
throw new ImageNotSupportedException("No cylinders or heads found");
sectorsData = new byte[totalCylinders][][][];
// Total sectors per track
uint[][] spts = new uint[totalCylinders][];
imageInfo.Cylinders = (ushort)totalCylinders;
imageInfo.Heads = (byte)totalHeads;
DicConsole.DebugWriteLine("Apridisk plugin",
"Found {0} cylinders and {1} heads with a maximum sector number of {2}",
totalCylinders, totalHeads, maxSector);
// Create heads
for(int i = 0; i < totalCylinders; i++)
{
sectorsData[i] = new byte[totalHeads][][];
spts[i] = new uint[totalHeads];
for(int j = 0; j < totalHeads; j++) sectorsData[i][j] = new byte[maxSector + 1][];
}
imageInfo.SectorSize = uint.MaxValue;
ulong headersizes = 0;
// Read sectors
stream.Seek(signature.Length, SeekOrigin.Begin);
while(stream.Position < stream.Length)
{
byte[] recB = new byte[recordSize];
stream.Read(recB, 0, recordSize);
GCHandle handle = GCHandle.Alloc(recB, GCHandleType.Pinned);
ApridiskRecord record =
(ApridiskRecord)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(ApridiskRecord));
handle.Free();
switch(record.type)
{
// Not sector record, just skip it
case RecordType.Deleted:
case RecordType.Comment:
case RecordType.Creator:
stream.Seek(record.headerSize - recordSize + record.dataSize, SeekOrigin.Current);
headersizes += record.headerSize + record.dataSize;
break;
case RecordType.Sector:
stream.Seek(record.headerSize - recordSize, SeekOrigin.Current);
byte[] data = new byte[record.dataSize];
stream.Read(data, 0, data.Length);
spts[record.cylinder][record.head]++;
uint realLength = record.dataSize;
if(record.compression == CompressType.Compressed)
realLength =
Decompress(data, out sectorsData[record.cylinder][record.head][record.sector]);
else sectorsData[record.cylinder][record.head][record.sector] = data;
if(realLength < imageInfo.SectorSize) imageInfo.SectorSize = realLength;
headersizes += record.headerSize + record.dataSize;
break;
}
}
DicConsole.DebugWriteLine("Apridisk plugin", "Found a minimum of {0} bytes per sector",
imageInfo.SectorSize);
// Count sectors per track
uint spt = uint.MaxValue;
for(ushort cyl = 0; cyl < imageInfo.Cylinders; cyl++)
{
for(ushort head = 0; head < imageInfo.Heads; head++)
if(spts[cyl][head] < spt)
spt = spts[cyl][head];
}
imageInfo.SectorsPerTrack = spt;
DicConsole.DebugWriteLine("Apridisk plugin", "Found a minimum of {0} sectors per track",
imageInfo.SectorsPerTrack);
imageInfo.MediaType = Geometry.GetMediaType(((ushort)imageInfo.Cylinders, (byte)imageInfo.Heads,
(ushort)imageInfo.SectorsPerTrack, 512, MediaEncoding.MFM,
false));
imageInfo.ImageSize = (ulong)stream.Length - headersizes;
imageInfo.CreationTime = imageFilter.GetCreationTime();
imageInfo.LastModificationTime = imageFilter.GetLastWriteTime();
imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename());
imageInfo.Sectors = imageInfo.Cylinders * imageInfo.Heads * imageInfo.SectorsPerTrack;
imageInfo.XmlMediaType = XmlMediaType.BlockMedia;
return true;
}
public byte[] ReadSector(ulong sectorAddress)
{
(ushort cylinder, byte head, byte sector) = LbaToChs(sectorAddress);
if(cylinder >= sectorsData.Length)
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
if(head >= sectorsData[cylinder].Length)
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
if(sector > sectorsData[cylinder][head].Length)
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
return sectorsData[cylinder][head][sector];
}
public byte[] ReadSectors(ulong sectorAddress, uint length)
{
if(sectorAddress > imageInfo.Sectors - 1)
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
if(sectorAddress + length > imageInfo.Sectors)
throw new ArgumentOutOfRangeException(nameof(length), "Requested more sectors than available");
MemoryStream buffer = new MemoryStream();
for(uint i = 0; i < length; i++)
{
byte[] sector = ReadSector(sectorAddress + i);
buffer.Write(sector, 0, sector.Length);
}
return buffer.ToArray();
}
}
}

View File

@@ -0,0 +1,51 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Structs.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains structures for Apridisk disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Runtime.InteropServices;
namespace DiscImageChef.DiscImages
{
public partial class Apridisk
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ApridiskRecord
{
public RecordType type;
public CompressType compression;
public ushort headerSize;
public uint dataSize;
public byte head;
public byte sector;
public ushort cylinder;
}
}
}

View File

@@ -0,0 +1,138 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Unsupported.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains features unsupported by Apridisk disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Collections.Generic;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Exceptions;
using DiscImageChef.CommonTypes.Structs;
namespace DiscImageChef.DiscImages
{
public partial class Apridisk
{
public byte[] ReadDiskTag(MediaTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorTag(ulong sectorAddress, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSector(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorTag(ulong sectorAddress, uint track, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectors(ulong sectorAddress, uint length, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, uint track, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorLong(ulong sectorAddress)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorLong(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsLong(ulong sectorAddress, uint length)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsLong(ulong sectorAddress, uint length, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public List<Track> GetSessionTracks(Session session)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public List<Track> GetSessionTracks(ushort session)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public bool? VerifySector(ulong sectorAddress)
{
return null;
}
public bool? VerifySector(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public bool? VerifySectors(ulong sectorAddress, uint length, out List<ulong> failingLbas,
out List<ulong> unknownLbas)
{
failingLbas = new List<ulong>();
unknownLbas = new List<ulong>();
for(ulong i = 0; i < imageInfo.Sectors; i++) unknownLbas.Add(i);
return null;
}
public bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List<ulong> failingLbas,
out List<ulong> unknownLbas)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public bool? VerifyMediaImage()
{
return null;
}
}
}

View File

@@ -0,0 +1,307 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Write.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Writes Apridisk disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Structs;
using Schemas;
namespace DiscImageChef.DiscImages
{
public partial class Apridisk
{
public bool Create(string path, MediaType mediaType, Dictionary<string, string> options, ulong sectors,
uint sectorSize)
{
if(!SupportedMediaTypes.Contains(mediaType))
{
ErrorMessage = $"Unsupport media format {mediaType}";
return false;
}
imageInfo = new ImageInfo {MediaType = mediaType, SectorSize = sectorSize, Sectors = sectors};
try { writingStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); }
catch(IOException e)
{
ErrorMessage = $"Could not create new image file, exception {e.Message}";
return false;
}
IsWriting = true;
ErrorMessage = null;
return true;
}
public bool WriteMediaTag(byte[] data, MediaTagType tag)
{
ErrorMessage = "Writing media tags is not supported.";
return false;
}
public bool WriteSector(byte[] data, ulong sectorAddress)
{
(ushort cylinder, byte head, byte sector) = LbaToChs(sectorAddress);
if(cylinder >= sectorsData.Length)
{
ErrorMessage = "Sector address not found";
return false;
}
if(head >= sectorsData[cylinder].Length)
{
ErrorMessage = "Sector address not found";
return false;
}
if(sector > sectorsData[cylinder][head].Length)
{
ErrorMessage = "Sector address not found";
return false;
}
sectorsData[cylinder][head][sector] = data;
return true;
}
public bool WriteSectors(byte[] data, ulong sectorAddress, uint length)
{
for(uint i = 0; i < length; i++)
{
(ushort cylinder, byte head, byte sector) = LbaToChs(sectorAddress);
if(cylinder >= sectorsData.Length)
{
ErrorMessage = "Sector address not found";
return false;
}
if(head >= sectorsData[cylinder].Length)
{
ErrorMessage = "Sector address not found";
return false;
}
if(sector > sectorsData[cylinder][head].Length)
{
ErrorMessage = "Sector address not found";
return false;
}
sectorsData[cylinder][head][sector] = data;
}
return true;
}
public bool WriteSectorLong(byte[] data, ulong sectorAddress)
{
ErrorMessage = "Writing sectors with tags is not supported.";
return false;
}
public bool WriteSectorsLong(byte[] data, ulong sectorAddress, uint length)
{
ErrorMessage = "Writing sectors with tags is not supported.";
return false;
}
public bool SetTracks(List<Track> tracks)
{
ErrorMessage = "Unsupported feature";
return false;
}
// TODO: Try if apridisk software supports finding other chunks, to extend metadata support
public bool Close()
{
writingStream.Seek(0, SeekOrigin.Begin);
writingStream.Write(signature, 0, signature.Length);
byte[] hdr = new byte[Marshal.SizeOf(typeof(ApridiskRecord))];
IntPtr hdrPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ApridiskRecord)));
for(ushort c = 0; c < imageInfo.Cylinders; c++)
{
for(byte h = 0; h < imageInfo.Heads; h++)
{
for(byte s = 0; s < imageInfo.SectorsPerTrack; s++)
{
if(sectorsData[c][h][s] == null || sectorsData[c][h][s].Length == 0) continue;
ApridiskRecord record = new ApridiskRecord
{
type = RecordType.Sector,
compression = CompressType.Uncompresed,
headerSize = (ushort)Marshal.SizeOf(typeof(ApridiskRecord)),
dataSize = (uint)sectorsData[c][h][s].Length,
head = h,
sector = s,
cylinder = c
};
Marshal.StructureToPtr(record, hdrPtr, true);
Marshal.Copy(hdrPtr, hdr, 0, hdr.Length);
writingStream.Write(hdr, 0, hdr.Length);
writingStream.Write(sectorsData[c][h][s], 0, sectorsData[c][h][s].Length);
}
}
}
if(!string.IsNullOrEmpty(imageInfo.Creator))
{
byte[] creatorBytes = Encoding.UTF8.GetBytes(imageInfo.Creator);
ApridiskRecord creatorRecord = new ApridiskRecord
{
type = RecordType.Creator,
compression = CompressType.Uncompresed,
headerSize = (ushort)Marshal.SizeOf(typeof(ApridiskRecord)),
dataSize = (uint)creatorBytes.Length + 1,
head = 0,
sector = 0,
cylinder = 0
};
Marshal.StructureToPtr(creatorRecord, hdrPtr, true);
Marshal.Copy(hdrPtr, hdr, 0, hdr.Length);
writingStream.Write(hdr, 0, hdr.Length);
writingStream.Write(creatorBytes, 0, creatorBytes.Length);
writingStream.WriteByte(0); // Termination
}
if(!string.IsNullOrEmpty(imageInfo.Comments))
{
byte[] commentBytes = Encoding.UTF8.GetBytes(imageInfo.Comments);
ApridiskRecord commentRecord = new ApridiskRecord
{
type = RecordType.Comment,
compression = CompressType.Uncompresed,
headerSize = (ushort)Marshal.SizeOf(typeof(ApridiskRecord)),
dataSize = (uint)commentBytes.Length + 1,
head = 0,
sector = 0,
cylinder = 0
};
Marshal.StructureToPtr(commentRecord, hdrPtr, true);
Marshal.Copy(hdrPtr, hdr, 0, hdr.Length);
writingStream.Write(hdr, 0, hdr.Length);
writingStream.Write(commentBytes, 0, commentBytes.Length);
writingStream.WriteByte(0); // Termination
}
Marshal.FreeHGlobal(hdrPtr);
writingStream.Flush();
writingStream.Close();
IsWriting = false;
ErrorMessage = "";
return true;
}
public bool SetMetadata(ImageInfo metadata)
{
imageInfo.Comments = metadata.Comments;
imageInfo.Creator = metadata.Creator;
return true;
}
public bool SetGeometry(uint cylinders, uint heads, uint sectorsPerTrack)
{
if(cylinders > ushort.MaxValue)
{
ErrorMessage = "Too many cylinders";
return false;
}
if(heads > byte.MaxValue)
{
ErrorMessage = "Too many heads";
return false;
}
if(sectorsPerTrack > byte.MaxValue)
{
ErrorMessage = "Too many sectors per track";
return false;
}
sectorsData = new byte[cylinders][][][];
for(ushort c = 0; c < cylinders; c++)
{
sectorsData[c] = new byte[heads][][];
for(byte h = 0; h < heads; h++) sectorsData[c][h] = new byte[sectorsPerTrack][];
}
imageInfo.Cylinders = cylinders;
imageInfo.Heads = heads;
imageInfo.SectorsPerTrack = sectorsPerTrack;
return true;
}
public bool WriteSectorTag(byte[] data, ulong sectorAddress, SectorTagType tag)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool WriteSectorsTag(byte[] data, ulong sectorAddress, uint length, SectorTagType tag)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool SetDumpHardware(List<DumpHardwareType> dumpHardware)
{
// Not supported
return false;
}
public bool SetCicmMetadata(CICMMetadataType metadata)
{
// Not supported
return false;
}
}
}

View File

@@ -1,782 +0,0 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : BLU.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Manages Basic Lisa Utility disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Exceptions;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
using DiscImageChef.Console;
using DiscImageChef.Decoders;
using Schemas;
using Version = DiscImageChef.CommonTypes.Interop.Version;
namespace DiscImageChef.DiscImages
{
public class Blu : IWritableImage
{
const string PROFILE_NAME = "PROFILE ";
const string PROFILE10_NAME = "PROFILE 10 ";
const string WIDGET_NAME = "WIDGET-10 ";
const string PRIAM_NAME = "PRIAMDTATOWER";
IFilter bluImageFilter;
int bptag;
BluHeader imageHeader;
ImageInfo imageInfo;
FileStream writingStream;
public Blu()
{
imageInfo = new ImageInfo
{
ReadableSectorTags = new List<SectorTagType>(),
ReadableMediaTags = new List<MediaTagType>(),
HasPartitions = false,
HasSessions = false,
Version = null,
Application = null,
ApplicationVersion = null,
Creator = null,
Comments = null,
MediaManufacturer = null,
MediaModel = null,
MediaSerialNumber = null,
MediaBarcode = null,
MediaPartNumber = null,
MediaSequence = 0,
LastMediaSequence = 0,
DriveManufacturer = null,
DriveModel = null,
DriveSerialNumber = null,
DriveFirmwareRevision = null
};
}
public ImageInfo Info => imageInfo;
public string Name => "Basic Lisa Utility";
public Guid Id => new Guid("A153E2F8-4235-432D-9A7F-20807B0BCD74");
public string Format => "Basic Lisa Utility";
public List<Partition> Partitions =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<Track> Tracks =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<Session> Sessions =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public bool Identify(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin);
if(stream.Length < 0x200) return false;
byte[] header = new byte[0x17];
stream.Read(header, 0, 0x17);
BluHeader tmpHdr = new BluHeader {DeviceName = new byte[0x0D]};
BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;
Array.Copy(header, 0, tmpHdr.DeviceName, 0, 0x0D);
tmpHdr.DeviceType = BigEndianBitConverter.ToUInt32(header, 0x0C) & 0x00FFFFFF;
tmpHdr.DeviceBlocks = BigEndianBitConverter.ToUInt32(header, 0x11) & 0x00FFFFFF;
tmpHdr.BytesPerBlock = BigEndianBitConverter.ToUInt16(header, 0x15);
for(int i = 0; i < 0xD; i++)
if(tmpHdr.DeviceName[i] < 0x20)
return false;
return (tmpHdr.BytesPerBlock & 0xFE00) == 0x200;
}
public bool Open(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin);
imageHeader = new BluHeader {DeviceName = new byte[0x0D]};
BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;
byte[] header = new byte[0x17];
stream.Read(header, 0, 0x17);
Array.Copy(header, 0, imageHeader.DeviceName, 0, 0x0D);
imageHeader.DeviceType = BigEndianBitConverter.ToUInt32(header, 0x0C) & 0x00FFFFFF;
imageHeader.DeviceBlocks = BigEndianBitConverter.ToUInt32(header, 0x11) & 0x00FFFFFF;
imageHeader.BytesPerBlock = BigEndianBitConverter.ToUInt16(header, 0x15);
DicConsole.DebugWriteLine("BLU plugin", "ImageHeader.deviceName = \"{0}\"",
StringHandlers.CToString(imageHeader.DeviceName));
DicConsole.DebugWriteLine("BLU plugin", "ImageHeader.deviceType = {0}", imageHeader.DeviceType);
DicConsole.DebugWriteLine("BLU plugin", "ImageHeader.deviceBlock = {0}", imageHeader.DeviceBlocks);
DicConsole.DebugWriteLine("BLU plugin", "ImageHeader.bytesPerBlock = {0}", imageHeader.BytesPerBlock);
for(int i = 0; i < 0xD; i++)
if(imageHeader.DeviceName[i] < 0x20)
return false;
if((imageHeader.BytesPerBlock & 0xFE00) != 0x200) return false;
stream.Seek(0, SeekOrigin.Begin);
header = new byte[imageHeader.BytesPerBlock];
stream.Read(header, 0, imageHeader.BytesPerBlock);
imageInfo.SectorSize = 0x200;
imageInfo.Sectors = imageHeader.DeviceBlocks;
imageInfo.ImageSize = imageHeader.DeviceBlocks * imageHeader.BytesPerBlock;
bptag = imageHeader.BytesPerBlock - 0x200;
byte[] hdrTag = new byte[bptag];
Array.Copy(header, 0x200, hdrTag, 0, bptag);
switch(StringHandlers.CToString(imageHeader.DeviceName))
{
case PROFILE_NAME:
imageInfo.MediaType =
imageInfo.Sectors == 0x2600 ? MediaType.AppleProfile : MediaType.GENERIC_HDD;
imageInfo.Cylinders = 152;
imageInfo.Heads = 4;
imageInfo.SectorsPerTrack = 16;
break;
case PROFILE10_NAME:
imageInfo.MediaType =
imageInfo.Sectors == 0x4C00 ? MediaType.AppleProfile : MediaType.GENERIC_HDD;
imageInfo.Cylinders = 304;
imageInfo.Heads = 4;
imageInfo.SectorsPerTrack = 16;
break;
case WIDGET_NAME:
imageInfo.MediaType =
imageInfo.Sectors == 0x4C00 ? MediaType.AppleWidget : MediaType.GENERIC_HDD;
imageInfo.Cylinders = 304;
imageInfo.Heads = 4;
imageInfo.SectorsPerTrack = 16;
break;
case PRIAM_NAME:
imageInfo.MediaType =
imageInfo.Sectors == 0x022C7C ? MediaType.PriamDataTower : MediaType.GENERIC_HDD;
// This values are invented...
imageInfo.Cylinders = 419;
imageInfo.Heads = 4;
imageInfo.SectorsPerTrack = 85;
break;
default:
imageInfo.MediaType = MediaType.GENERIC_HDD;
imageInfo.Cylinders = (uint)(imageInfo.Sectors / 16 / 63);
imageInfo.Heads = 16;
imageInfo.SectorsPerTrack = 63;
break;
}
imageInfo.Application = StringHandlers.CToString(hdrTag);
imageInfo.CreationTime = imageFilter.GetCreationTime();
imageInfo.LastModificationTime = imageFilter.GetLastWriteTime();
imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename());
bluImageFilter = imageFilter;
imageInfo.XmlMediaType = XmlMediaType.BlockMedia;
if(bptag > 0) imageInfo.ReadableSectorTags.Add(SectorTagType.AppleSectorTag);
DicConsole.VerboseWriteLine("BLU image contains a disk of type {0}", imageInfo.MediaType);
return true;
}
public byte[] ReadSector(ulong sectorAddress)
{
return ReadSectors(sectorAddress, 1);
}
public byte[] ReadSectorTag(ulong sectorAddress, SectorTagType tag)
{
return ReadSectorsTag(sectorAddress, 1, tag);
}
public byte[] ReadSectors(ulong sectorAddress, uint length)
{
if(sectorAddress > imageInfo.Sectors - 1)
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
if(sectorAddress + length > imageInfo.Sectors)
throw new ArgumentOutOfRangeException(nameof(length), "Requested more sectors than available");
MemoryStream buffer = new MemoryStream();
int seek = 0;
int read = 0x200;
int skip = bptag;
Stream stream = bluImageFilter.GetDataForkStream();
stream.Seek((long)((sectorAddress + 1) * imageHeader.BytesPerBlock), SeekOrigin.Begin);
for(int i = 0; i < length; i++)
{
stream.Seek(seek, SeekOrigin.Current);
byte[] sector = new byte[read];
stream.Read(sector, 0, read);
buffer.Write(sector, 0, read);
stream.Seek(skip, SeekOrigin.Current);
}
return buffer.ToArray();
}
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, SectorTagType tag)
{
if(tag != SectorTagType.AppleSectorTag)
throw new FeatureUnsupportedImageException($"Tag {tag} not supported by image format");
if(bptag == 0) throw new FeatureNotPresentImageException("Disk image does not have tags");
if(sectorAddress > imageInfo.Sectors - 1)
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
if(sectorAddress + length > imageInfo.Sectors)
throw new ArgumentOutOfRangeException(nameof(length), "Requested more sectors than available");
MemoryStream buffer = new MemoryStream();
int seek = 0x200;
int read = bptag;
int skip = 0;
Stream stream = bluImageFilter.GetDataForkStream();
stream.Seek((long)((sectorAddress + 1) * imageHeader.BytesPerBlock), SeekOrigin.Begin);
for(int i = 0; i < length; i++)
{
stream.Seek(seek, SeekOrigin.Current);
byte[] sector = new byte[read];
stream.Read(sector, 0, read);
buffer.Write(sector, 0, read);
stream.Seek(skip, SeekOrigin.Current);
}
return buffer.ToArray();
}
public byte[] ReadSectorLong(ulong sectorAddress)
{
return ReadSectorsLong(sectorAddress, 1);
}
public byte[] ReadSectorsLong(ulong sectorAddress, uint length)
{
if(sectorAddress > imageInfo.Sectors - 1)
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
if(sectorAddress + length > imageInfo.Sectors)
throw new ArgumentOutOfRangeException(nameof(length), "Requested more sectors than available");
byte[] buffer = new byte[length * imageHeader.BytesPerBlock];
Stream stream = bluImageFilter.GetDataForkStream();
stream.Seek((long)((sectorAddress + 1) * imageHeader.BytesPerBlock), SeekOrigin.Begin);
stream.Read(buffer, 0, buffer.Length);
return buffer;
}
public byte[] ReadDiskTag(MediaTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public List<Track> GetSessionTracks(Session session)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public List<Track> GetSessionTracks(ushort session)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSector(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorTag(ulong sectorAddress, uint track, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectors(ulong sectorAddress, uint length, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, uint track, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorLong(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsLong(ulong sectorAddress, uint length, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
// TODO: Check tag checkums
public bool? VerifySector(ulong sectorAddress)
{
return null;
}
public bool? VerifySector(ulong sectorAddress, uint track)
{
return null;
}
public bool? VerifySectors(ulong sectorAddress, uint length, out List<ulong> failingLbas,
out List<ulong> unknownLbas)
{
failingLbas = new List<ulong>();
unknownLbas = new List<ulong>();
for(ulong i = sectorAddress; i < sectorAddress + length; i++) unknownLbas.Add(i);
return null;
}
public bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List<ulong> failingLbas,
out List<ulong> unknownLbas)
{
failingLbas = new List<ulong>();
unknownLbas = new List<ulong>();
for(ulong i = sectorAddress; i < sectorAddress + length; i++) unknownLbas.Add(i);
return null;
}
public bool? VerifyMediaImage()
{
return null;
}
public List<DumpHardwareType> DumpHardware => null;
public CICMMetadataType CicmMetadata => null;
public IEnumerable<MediaTagType> SupportedMediaTags => new MediaTagType[] { };
public IEnumerable<SectorTagType> SupportedSectorTags => new[] {SectorTagType.AppleSectorTag};
public IEnumerable<MediaType> SupportedMediaTypes =>
new[]
{
MediaType.AppleProfile, MediaType.AppleWidget, MediaType.PriamDataTower, MediaType.GENERIC_HDD,
MediaType.Unknown, MediaType.FlashDrive, MediaType.CompactFlash, MediaType.CompactFlashType2,
MediaType.PCCardTypeI, MediaType.PCCardTypeII, MediaType.PCCardTypeIII, MediaType.PCCardTypeIV
};
public IEnumerable<(string name, Type type, string description)> SupportedOptions =>
new (string name, Type type, string description)[] { };
public IEnumerable<string> KnownExtensions => new[] {".blu"}; // Just invented
public bool IsWriting { get; private set; }
public string ErrorMessage { get; private set; }
public bool Create(string path, MediaType mediaType, Dictionary<string, string> options, ulong sectors,
uint sectorSize)
{
if(sectorSize != 512)
{
ErrorMessage = "Unsupported sector size";
return false;
}
if(sectors > 0xFFFFFF)
{
ErrorMessage = "Too many sectors";
return false;
}
if(!SupportedMediaTypes.Contains(mediaType))
{
ErrorMessage = $"Unsupport media format {mediaType}";
return false;
}
imageInfo = new ImageInfo {MediaType = mediaType, SectorSize = sectorSize, Sectors = sectors};
try { writingStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); }
catch(IOException e)
{
ErrorMessage = $"Could not create new image file, exception {e.Message}";
return false;
}
IsWriting = true;
ErrorMessage = null;
return true;
}
public bool WriteMediaTag(byte[] data, MediaTagType tag)
{
ErrorMessage = "Writing media tags is not supported.";
return false;
}
public bool WriteSector(byte[] data, ulong sectorAddress)
{
int longSectorSize = imageInfo.MediaType == MediaType.PriamDataTower ? 536 : 532;
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
if(data.Length != 512)
{
ErrorMessage = "Incorrect data size";
return false;
}
if(sectorAddress >= imageInfo.Sectors)
{
ErrorMessage = "Tried to write past image size";
return false;
}
writingStream.Seek(longSectorSize + (long)sectorAddress * longSectorSize, SeekOrigin.Begin);
writingStream.Write(data, 0, data.Length);
ErrorMessage = "";
return true;
}
public bool WriteSectors(byte[] data, ulong sectorAddress, uint length)
{
int longSectorSize = imageInfo.MediaType == MediaType.PriamDataTower ? 536 : 532;
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
if(data.Length % 512 != 0)
{
ErrorMessage = "Incorrect data size";
return false;
}
if(sectorAddress + length > imageInfo.Sectors)
{
ErrorMessage = "Tried to write past image size";
return false;
}
writingStream.Seek(longSectorSize + (long)sectorAddress * longSectorSize, SeekOrigin.Begin);
writingStream.Write(data, 0, data.Length);
ErrorMessage = "";
return true;
}
public bool WriteSectorLong(byte[] data, ulong sectorAddress)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
if(sectorAddress >= imageInfo.Sectors)
{
ErrorMessage = "Tried to write past image size";
return false;
}
int longSectorSize = imageInfo.MediaType == MediaType.PriamDataTower ? 536 : 532;
byte[] oldTag;
byte[] newTag;
switch(data.Length - 512)
{
// Sony tag, convert to Profile
case 12 when longSectorSize == 532:
oldTag = new byte[12];
Array.Copy(data, 512, oldTag, 0, 12);
newTag = LisaTag.DecodeSonyTag(oldTag)?.ToProfile().GetBytes();
break;
// Sony tag, convert to Priam
case 12 when longSectorSize == 536:
oldTag = new byte[12];
Array.Copy(data, 512, oldTag, 0, 12);
newTag = LisaTag.DecodeSonyTag(oldTag)?.ToPriam().GetBytes();
break;
// Profile tag, copy to Profile
case 20 when longSectorSize == 532:
newTag = new byte[20];
Array.Copy(data, 512, newTag, 0, 20);
break;
// Profile tag, convert to Priam
case 20 when longSectorSize == 536:
oldTag = new byte[20];
Array.Copy(data, 512, oldTag, 0, 20);
newTag = LisaTag.DecodeProfileTag(oldTag)?.ToPriam().GetBytes();
break;
// Priam tag, convert to Profile
case 24 when longSectorSize == 532:
oldTag = new byte[24];
Array.Copy(data, 512, oldTag, 0, 24);
newTag = LisaTag.DecodePriamTag(oldTag)?.ToProfile().GetBytes();
break;
// Priam tag, copy to Priam
case 12 when longSectorSize == 536:
newTag = new byte[24];
Array.Copy(data, 512, newTag, 0, 24);
break;
case 0:
newTag = null;
break;
default:
ErrorMessage = "Incorrect data size";
return false;
}
if(newTag == null) newTag = new byte[longSectorSize - 512];
writingStream.Seek(longSectorSize + (long)sectorAddress * longSectorSize, SeekOrigin.Begin);
writingStream.Write(data, 0, 512);
writingStream.Write(newTag, 0, newTag.Length);
ErrorMessage = "";
return true;
}
public bool WriteSectorsLong(byte[] data, ulong sectorAddress, uint length)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
if(sectorAddress + length > imageInfo.Sectors)
{
ErrorMessage = "Tried to write past image size";
return false;
}
int longSectorSize = imageInfo.MediaType == MediaType.PriamDataTower ? 536 : 532;
long givenSectorSize = data.Length / length;
switch(givenSectorSize)
{
case 536:
case 532:
case 524:
case 512: break;
default:
ErrorMessage = "Incorrect data size";
return false;
}
for(uint i = 0; i < length; i++)
{
byte[] oldTag;
byte[] newTag;
switch(givenSectorSize - 512)
{
// Sony tag, convert to Profile
case 12 when longSectorSize == 532:
oldTag = new byte[12];
Array.Copy(data, givenSectorSize * i + 512, oldTag, 0, 12);
newTag = LisaTag.DecodeSonyTag(oldTag)?.ToProfile().GetBytes();
break;
// Sony tag, convert to Priam
case 12 when longSectorSize == 536:
oldTag = new byte[12];
Array.Copy(data, givenSectorSize * i + 512, oldTag, 0, 12);
newTag = LisaTag.DecodeSonyTag(oldTag)?.ToPriam().GetBytes();
break;
// Profile tag, copy to Profile
case 20 when longSectorSize == 532:
newTag = new byte[20];
Array.Copy(data, givenSectorSize * i + 512, newTag, 0, 20);
break;
// Profile tag, convert to Priam
case 20 when longSectorSize == 536:
oldTag = new byte[20];
Array.Copy(data, givenSectorSize * i + 512, oldTag, 0, 20);
newTag = LisaTag.DecodeProfileTag(oldTag)?.ToPriam().GetBytes();
break;
// Priam tag, convert to Profile
case 24 when longSectorSize == 532:
oldTag = new byte[24];
Array.Copy(data, givenSectorSize * i + 512, oldTag, 0, 24);
newTag = LisaTag.DecodePriamTag(oldTag)?.ToProfile().GetBytes();
break;
// Priam tag, copy to Priam
case 12 when longSectorSize == 536:
newTag = new byte[24];
Array.Copy(data, givenSectorSize * i + 512, newTag, 0, 24);
break;
case 0:
newTag = null;
break;
default:
ErrorMessage = "Incorrect data size";
return false;
}
if(newTag == null) newTag = new byte[longSectorSize - 512];
writingStream.Seek(longSectorSize + (long)sectorAddress * longSectorSize, SeekOrigin.Begin);
writingStream.Write(data, (int)(givenSectorSize * i), 512);
writingStream.Write(newTag, 0, newTag.Length);
}
ErrorMessage = "";
return true;
}
public bool SetTracks(List<Track> tracks)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool Close()
{
if(!IsWriting)
{
ErrorMessage = "Image is not opened for writing";
return false;
}
BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;
byte[] markerTag = Encoding.UTF8.GetBytes("DiscImageChef " + Version.GetVersion());
byte[] driveName;
byte[] driveType = new byte[3];
byte[] driveBlocks = BigEndianBitConverter.GetBytes((uint)imageInfo.Sectors);
int longSectorSize = imageInfo.MediaType == MediaType.PriamDataTower ? 536 : 532;
byte[] blockSize = BigEndianBitConverter.GetBytes((ushort)longSectorSize);
switch(imageInfo.MediaType)
{
case MediaType.AppleProfile when imageInfo.Sectors == 0x4C00:
driveName = Encoding.ASCII.GetBytes(PROFILE10_NAME);
break;
case MediaType.AppleWidget when imageInfo.Sectors == 0x4C00:
driveType[1] = 0x01;
driveName = Encoding.ASCII.GetBytes(PROFILE10_NAME);
break;
case MediaType.PriamDataTower when imageInfo.Sectors == 0x22C7C:
driveType[1] = 0xFF;
driveName = Encoding.ASCII.GetBytes(PRIAM_NAME);
break;
default:
driveName = Encoding.ASCII.GetBytes(PROFILE_NAME);
break;
}
writingStream.Seek(0, SeekOrigin.Begin);
writingStream.Write(driveName, 0, driveName.Length >= 0xD ? 0xD : driveName.Length);
writingStream.Seek(0xD, SeekOrigin.Begin);
writingStream.Write(driveType, 0, 3);
writingStream.Seek(0x12, SeekOrigin.Begin);
writingStream.Write(driveBlocks, 1, 3);
writingStream.Seek(0x15, SeekOrigin.Begin);
writingStream.Write(blockSize, 1, 2);
writingStream.Seek(512, SeekOrigin.Begin);
writingStream.Write(markerTag, 0,
markerTag.Length >= longSectorSize - 512 ? longSectorSize - 512 : markerTag.Length);
writingStream.Flush();
writingStream.Close();
IsWriting = false;
ErrorMessage = "";
return true;
}
public bool SetMetadata(ImageInfo metadata)
{
return true;
}
public bool SetGeometry(uint cylinders, uint heads, uint sectorsPerTrack)
{
// Geometry is not stored in image
return true;
}
public bool WriteSectorTag(byte[] data, ulong sectorAddress, SectorTagType tag)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool WriteSectorsTag(byte[] data, ulong sectorAddress, uint length, SectorTagType tag)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool SetDumpHardware(List<DumpHardwareType> dumpHardware)
{
// Not supported
return false;
}
public bool SetCicmMetadata(CICMMetadataType metadata)
{
// Not supported
return false;
}
struct BluHeader
{
public byte[] DeviceName;
public uint DeviceType;
public uint DeviceBlocks;
public ushort BytesPerBlock;
}
}
}

View File

@@ -0,0 +1,84 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : BLU.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Manages Basic Lisa Utility disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Collections.Generic;
using System.IO;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
namespace DiscImageChef.DiscImages
{
public partial class Blu : IWritableImage
{
IFilter bluImageFilter;
int bptag;
BluHeader imageHeader;
ImageInfo imageInfo;
FileStream writingStream;
public Blu()
{
imageInfo = new ImageInfo
{
ReadableSectorTags = new List<SectorTagType>(),
ReadableMediaTags = new List<MediaTagType>(),
HasPartitions = false,
HasSessions = false,
Version = null,
Application = null,
ApplicationVersion = null,
Creator = null,
Comments = null,
MediaManufacturer = null,
MediaModel = null,
MediaSerialNumber = null,
MediaBarcode = null,
MediaPartNumber = null,
MediaSequence = 0,
LastMediaSequence = 0,
DriveManufacturer = null,
DriveModel = null,
DriveSerialNumber = null,
DriveFirmwareRevision = null
};
}
struct BluHeader
{
public byte[] DeviceName;
public uint DeviceType;
public uint DeviceBlocks;
public ushort BytesPerBlock;
}
}
}

View File

@@ -0,0 +1,42 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Constants.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains constants for Basic Lisa Utility disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
namespace DiscImageChef.DiscImages
{
public partial class Blu
{
const string PROFILE_NAME = "PROFILE ";
const string PROFILE10_NAME = "PROFILE 10 ";
const string WIDGET_NAME = "WIDGET-10 ";
const string PRIAM_NAME = "PRIAMDTATOWER";
}
}

View File

@@ -0,0 +1,66 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Identify.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Identifies Basic Lisa Utility disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.IO;
using DiscImageChef.CommonTypes.Interfaces;
namespace DiscImageChef.DiscImages
{
public partial class Blu
{
public bool Identify(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin);
if(stream.Length < 0x200) return false;
byte[] header = new byte[0x17];
stream.Read(header, 0, 0x17);
BluHeader tmpHdr = new BluHeader {DeviceName = new byte[0x0D]};
BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;
Array.Copy(header, 0, tmpHdr.DeviceName, 0, 0x0D);
tmpHdr.DeviceType = BigEndianBitConverter.ToUInt32(header, 0x0C) & 0x00FFFFFF;
tmpHdr.DeviceBlocks = BigEndianBitConverter.ToUInt32(header, 0x11) & 0x00FFFFFF;
tmpHdr.BytesPerBlock = BigEndianBitConverter.ToUInt16(header, 0x15);
for(int i = 0; i < 0xD; i++)
if(tmpHdr.DeviceName[i] < 0x20)
return false;
return (tmpHdr.BytesPerBlock & 0xFE00) == 0x200;
}
}
}

View File

@@ -0,0 +1,72 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Properties.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains properties for Basic Lisa Utility disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Exceptions;
using DiscImageChef.CommonTypes.Structs;
using Schemas;
namespace DiscImageChef.DiscImages
{
public partial class Blu
{
public ImageInfo Info => imageInfo;
public string Name => "Basic Lisa Utility";
public Guid Id => new Guid("A153E2F8-4235-432D-9A7F-20807B0BCD74");
public string Format => "Basic Lisa Utility";
public List<Partition> Partitions =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<Track> Tracks =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<Session> Sessions =>
throw new FeatureUnsupportedImageException("Feature not supported by image format");
public List<DumpHardwareType> DumpHardware => null;
public CICMMetadataType CicmMetadata => null;
public IEnumerable<MediaTagType> SupportedMediaTags => new MediaTagType[] { };
public IEnumerable<SectorTagType> SupportedSectorTags => new[] {SectorTagType.AppleSectorTag};
public IEnumerable<MediaType> SupportedMediaTypes =>
new[]
{
MediaType.AppleProfile, MediaType.AppleWidget, MediaType.PriamDataTower, MediaType.GENERIC_HDD,
MediaType.Unknown, MediaType.FlashDrive, MediaType.CompactFlash, MediaType.CompactFlashType2,
MediaType.PCCardTypeI, MediaType.PCCardTypeII, MediaType.PCCardTypeIII, MediaType.PCCardTypeIV
};
public IEnumerable<(string name, Type type, string description)> SupportedOptions =>
new (string name, Type type, string description)[] { };
public IEnumerable<string> KnownExtensions => new[] {".blu"}; // Just invented
public bool IsWriting { get; private set; }
public string ErrorMessage { get; private set; }
}
}

View File

@@ -0,0 +1,250 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Read.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Reads Basic Lisa Utility disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Exceptions;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.Console;
namespace DiscImageChef.DiscImages
{
public partial class Blu
{
public bool Open(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin);
imageHeader = new BluHeader {DeviceName = new byte[0x0D]};
BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;
byte[] header = new byte[0x17];
stream.Read(header, 0, 0x17);
Array.Copy(header, 0, imageHeader.DeviceName, 0, 0x0D);
imageHeader.DeviceType = BigEndianBitConverter.ToUInt32(header, 0x0C) & 0x00FFFFFF;
imageHeader.DeviceBlocks = BigEndianBitConverter.ToUInt32(header, 0x11) & 0x00FFFFFF;
imageHeader.BytesPerBlock = BigEndianBitConverter.ToUInt16(header, 0x15);
DicConsole.DebugWriteLine("BLU plugin", "ImageHeader.deviceName = \"{0}\"",
StringHandlers.CToString(imageHeader.DeviceName));
DicConsole.DebugWriteLine("BLU plugin", "ImageHeader.deviceType = {0}", imageHeader.DeviceType);
DicConsole.DebugWriteLine("BLU plugin", "ImageHeader.deviceBlock = {0}", imageHeader.DeviceBlocks);
DicConsole.DebugWriteLine("BLU plugin", "ImageHeader.bytesPerBlock = {0}", imageHeader.BytesPerBlock);
for(int i = 0; i < 0xD; i++)
if(imageHeader.DeviceName[i] < 0x20)
return false;
if((imageHeader.BytesPerBlock & 0xFE00) != 0x200) return false;
stream.Seek(0, SeekOrigin.Begin);
header = new byte[imageHeader.BytesPerBlock];
stream.Read(header, 0, imageHeader.BytesPerBlock);
imageInfo.SectorSize = 0x200;
imageInfo.Sectors = imageHeader.DeviceBlocks;
imageInfo.ImageSize = imageHeader.DeviceBlocks * imageHeader.BytesPerBlock;
bptag = imageHeader.BytesPerBlock - 0x200;
byte[] hdrTag = new byte[bptag];
Array.Copy(header, 0x200, hdrTag, 0, bptag);
switch(StringHandlers.CToString(imageHeader.DeviceName))
{
case PROFILE_NAME:
imageInfo.MediaType =
imageInfo.Sectors == 0x2600 ? MediaType.AppleProfile : MediaType.GENERIC_HDD;
imageInfo.Cylinders = 152;
imageInfo.Heads = 4;
imageInfo.SectorsPerTrack = 16;
break;
case PROFILE10_NAME:
imageInfo.MediaType =
imageInfo.Sectors == 0x4C00 ? MediaType.AppleProfile : MediaType.GENERIC_HDD;
imageInfo.Cylinders = 304;
imageInfo.Heads = 4;
imageInfo.SectorsPerTrack = 16;
break;
case WIDGET_NAME:
imageInfo.MediaType =
imageInfo.Sectors == 0x4C00 ? MediaType.AppleWidget : MediaType.GENERIC_HDD;
imageInfo.Cylinders = 304;
imageInfo.Heads = 4;
imageInfo.SectorsPerTrack = 16;
break;
case PRIAM_NAME:
imageInfo.MediaType =
imageInfo.Sectors == 0x022C7C ? MediaType.PriamDataTower : MediaType.GENERIC_HDD;
// This values are invented...
imageInfo.Cylinders = 419;
imageInfo.Heads = 4;
imageInfo.SectorsPerTrack = 85;
break;
default:
imageInfo.MediaType = MediaType.GENERIC_HDD;
imageInfo.Cylinders = (uint)(imageInfo.Sectors / 16 / 63);
imageInfo.Heads = 16;
imageInfo.SectorsPerTrack = 63;
break;
}
imageInfo.Application = StringHandlers.CToString(hdrTag);
imageInfo.CreationTime = imageFilter.GetCreationTime();
imageInfo.LastModificationTime = imageFilter.GetLastWriteTime();
imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename());
bluImageFilter = imageFilter;
imageInfo.XmlMediaType = XmlMediaType.BlockMedia;
if(bptag > 0) imageInfo.ReadableSectorTags.Add(SectorTagType.AppleSectorTag);
DicConsole.VerboseWriteLine("BLU image contains a disk of type {0}", imageInfo.MediaType);
return true;
}
public byte[] ReadSector(ulong sectorAddress)
{
return ReadSectors(sectorAddress, 1);
}
public byte[] ReadSectorTag(ulong sectorAddress, SectorTagType tag)
{
return ReadSectorsTag(sectorAddress, 1, tag);
}
public byte[] ReadSectors(ulong sectorAddress, uint length)
{
if(sectorAddress > imageInfo.Sectors - 1)
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
if(sectorAddress + length > imageInfo.Sectors)
throw new ArgumentOutOfRangeException(nameof(length), "Requested more sectors than available");
MemoryStream buffer = new MemoryStream();
int seek = 0;
int read = 0x200;
int skip = bptag;
Stream stream = bluImageFilter.GetDataForkStream();
stream.Seek((long)((sectorAddress + 1) * imageHeader.BytesPerBlock), SeekOrigin.Begin);
for(int i = 0; i < length; i++)
{
stream.Seek(seek, SeekOrigin.Current);
byte[] sector = new byte[read];
stream.Read(sector, 0, read);
buffer.Write(sector, 0, read);
stream.Seek(skip, SeekOrigin.Current);
}
return buffer.ToArray();
}
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, SectorTagType tag)
{
if(tag != SectorTagType.AppleSectorTag)
throw new FeatureUnsupportedImageException($"Tag {tag} not supported by image format");
if(bptag == 0) throw new FeatureNotPresentImageException("Disk image does not have tags");
if(sectorAddress > imageInfo.Sectors - 1)
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
if(sectorAddress + length > imageInfo.Sectors)
throw new ArgumentOutOfRangeException(nameof(length), "Requested more sectors than available");
MemoryStream buffer = new MemoryStream();
int seek = 0x200;
int read = bptag;
int skip = 0;
Stream stream = bluImageFilter.GetDataForkStream();
stream.Seek((long)((sectorAddress + 1) * imageHeader.BytesPerBlock), SeekOrigin.Begin);
for(int i = 0; i < length; i++)
{
stream.Seek(seek, SeekOrigin.Current);
byte[] sector = new byte[read];
stream.Read(sector, 0, read);
buffer.Write(sector, 0, read);
stream.Seek(skip, SeekOrigin.Current);
}
return buffer.ToArray();
}
public byte[] ReadSectorLong(ulong sectorAddress)
{
return ReadSectorsLong(sectorAddress, 1);
}
public byte[] ReadSectorsLong(ulong sectorAddress, uint length)
{
if(sectorAddress > imageInfo.Sectors - 1)
throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
if(sectorAddress + length > imageInfo.Sectors)
throw new ArgumentOutOfRangeException(nameof(length), "Requested more sectors than available");
byte[] buffer = new byte[length * imageHeader.BytesPerBlock];
Stream stream = bluImageFilter.GetDataForkStream();
stream.Seek((long)((sectorAddress + 1) * imageHeader.BytesPerBlock), SeekOrigin.Begin);
stream.Read(buffer, 0, buffer.Length);
return buffer;
}
// TODO: Check tag checkums
public bool? VerifySector(ulong sectorAddress)
{
return null;
}
public bool? VerifySectors(ulong sectorAddress, uint length, out List<ulong> failingLbas,
out List<ulong> unknownLbas)
{
failingLbas = new List<ulong>();
unknownLbas = new List<ulong>();
for(ulong i = sectorAddress; i < sectorAddress + length; i++) unknownLbas.Add(i);
return null;
}
}
}

View File

@@ -0,0 +1,108 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Unsupported.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains features unsupported by Basic Lisa Utility disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Collections.Generic;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Exceptions;
using DiscImageChef.CommonTypes.Structs;
namespace DiscImageChef.DiscImages
{
public partial class Blu
{
public byte[] ReadDiskTag(MediaTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public List<Track> GetSessionTracks(Session session)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public List<Track> GetSessionTracks(ushort session)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSector(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorTag(ulong sectorAddress, uint track, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectors(ulong sectorAddress, uint length, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, uint track, SectorTagType tag)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorLong(ulong sectorAddress, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public byte[] ReadSectorsLong(ulong sectorAddress, uint length, uint track)
{
throw new FeatureUnsupportedImageException("Feature not supported by image format");
}
public bool? VerifySector(ulong sectorAddress, uint track)
{
return null;
}
public bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List<ulong> failingLbas,
out List<ulong> unknownLbas)
{
failingLbas = new List<ulong>();
unknownLbas = new List<ulong>();
for(ulong i = sectorAddress; i < sectorAddress + length; i++) unknownLbas.Add(i);
return null;
}
public bool? VerifyMediaImage()
{
return null;
}
}
}

View File

@@ -0,0 +1,404 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Write.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Writes Basic Lisa Utility disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Structs;
using DiscImageChef.Decoders;
using Schemas;
using Version = DiscImageChef.CommonTypes.Interop.Version;
namespace DiscImageChef.DiscImages
{
public partial class Blu
{
public bool Create(string path, MediaType mediaType, Dictionary<string, string> options, ulong sectors,
uint sectorSize)
{
if(sectorSize != 512)
{
ErrorMessage = "Unsupported sector size";
return false;
}
if(sectors > 0xFFFFFF)
{
ErrorMessage = "Too many sectors";
return false;
}
if(!SupportedMediaTypes.Contains(mediaType))
{
ErrorMessage = $"Unsupport media format {mediaType}";
return false;
}
imageInfo = new ImageInfo {MediaType = mediaType, SectorSize = sectorSize, Sectors = sectors};
try { writingStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); }
catch(IOException e)
{
ErrorMessage = $"Could not create new image file, exception {e.Message}";
return false;
}
IsWriting = true;
ErrorMessage = null;
return true;
}
public bool WriteMediaTag(byte[] data, MediaTagType tag)
{
ErrorMessage = "Writing media tags is not supported.";
return false;
}
public bool WriteSector(byte[] data, ulong sectorAddress)
{
int longSectorSize = imageInfo.MediaType == MediaType.PriamDataTower ? 536 : 532;
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
if(data.Length != 512)
{
ErrorMessage = "Incorrect data size";
return false;
}
if(sectorAddress >= imageInfo.Sectors)
{
ErrorMessage = "Tried to write past image size";
return false;
}
writingStream.Seek(longSectorSize + (long)sectorAddress * longSectorSize, SeekOrigin.Begin);
writingStream.Write(data, 0, data.Length);
ErrorMessage = "";
return true;
}
public bool WriteSectors(byte[] data, ulong sectorAddress, uint length)
{
int longSectorSize = imageInfo.MediaType == MediaType.PriamDataTower ? 536 : 532;
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
if(data.Length % 512 != 0)
{
ErrorMessage = "Incorrect data size";
return false;
}
if(sectorAddress + length > imageInfo.Sectors)
{
ErrorMessage = "Tried to write past image size";
return false;
}
writingStream.Seek(longSectorSize + (long)sectorAddress * longSectorSize, SeekOrigin.Begin);
writingStream.Write(data, 0, data.Length);
ErrorMessage = "";
return true;
}
public bool WriteSectorLong(byte[] data, ulong sectorAddress)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
if(sectorAddress >= imageInfo.Sectors)
{
ErrorMessage = "Tried to write past image size";
return false;
}
int longSectorSize = imageInfo.MediaType == MediaType.PriamDataTower ? 536 : 532;
byte[] oldTag;
byte[] newTag;
switch(data.Length - 512)
{
// Sony tag, convert to Profile
case 12 when longSectorSize == 532:
oldTag = new byte[12];
Array.Copy(data, 512, oldTag, 0, 12);
newTag = LisaTag.DecodeSonyTag(oldTag)?.ToProfile().GetBytes();
break;
// Sony tag, convert to Priam
case 12 when longSectorSize == 536:
oldTag = new byte[12];
Array.Copy(data, 512, oldTag, 0, 12);
newTag = LisaTag.DecodeSonyTag(oldTag)?.ToPriam().GetBytes();
break;
// Profile tag, copy to Profile
case 20 when longSectorSize == 532:
newTag = new byte[20];
Array.Copy(data, 512, newTag, 0, 20);
break;
// Profile tag, convert to Priam
case 20 when longSectorSize == 536:
oldTag = new byte[20];
Array.Copy(data, 512, oldTag, 0, 20);
newTag = LisaTag.DecodeProfileTag(oldTag)?.ToPriam().GetBytes();
break;
// Priam tag, convert to Profile
case 24 when longSectorSize == 532:
oldTag = new byte[24];
Array.Copy(data, 512, oldTag, 0, 24);
newTag = LisaTag.DecodePriamTag(oldTag)?.ToProfile().GetBytes();
break;
// Priam tag, copy to Priam
case 12 when longSectorSize == 536:
newTag = new byte[24];
Array.Copy(data, 512, newTag, 0, 24);
break;
case 0:
newTag = null;
break;
default:
ErrorMessage = "Incorrect data size";
return false;
}
if(newTag == null) newTag = new byte[longSectorSize - 512];
writingStream.Seek(longSectorSize + (long)sectorAddress * longSectorSize, SeekOrigin.Begin);
writingStream.Write(data, 0, 512);
writingStream.Write(newTag, 0, newTag.Length);
ErrorMessage = "";
return true;
}
public bool WriteSectorsLong(byte[] data, ulong sectorAddress, uint length)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
if(sectorAddress + length > imageInfo.Sectors)
{
ErrorMessage = "Tried to write past image size";
return false;
}
int longSectorSize = imageInfo.MediaType == MediaType.PriamDataTower ? 536 : 532;
long givenSectorSize = data.Length / length;
switch(givenSectorSize)
{
case 536:
case 532:
case 524:
case 512: break;
default:
ErrorMessage = "Incorrect data size";
return false;
}
for(uint i = 0; i < length; i++)
{
byte[] oldTag;
byte[] newTag;
switch(givenSectorSize - 512)
{
// Sony tag, convert to Profile
case 12 when longSectorSize == 532:
oldTag = new byte[12];
Array.Copy(data, givenSectorSize * i + 512, oldTag, 0, 12);
newTag = LisaTag.DecodeSonyTag(oldTag)?.ToProfile().GetBytes();
break;
// Sony tag, convert to Priam
case 12 when longSectorSize == 536:
oldTag = new byte[12];
Array.Copy(data, givenSectorSize * i + 512, oldTag, 0, 12);
newTag = LisaTag.DecodeSonyTag(oldTag)?.ToPriam().GetBytes();
break;
// Profile tag, copy to Profile
case 20 when longSectorSize == 532:
newTag = new byte[20];
Array.Copy(data, givenSectorSize * i + 512, newTag, 0, 20);
break;
// Profile tag, convert to Priam
case 20 when longSectorSize == 536:
oldTag = new byte[20];
Array.Copy(data, givenSectorSize * i + 512, oldTag, 0, 20);
newTag = LisaTag.DecodeProfileTag(oldTag)?.ToPriam().GetBytes();
break;
// Priam tag, convert to Profile
case 24 when longSectorSize == 532:
oldTag = new byte[24];
Array.Copy(data, givenSectorSize * i + 512, oldTag, 0, 24);
newTag = LisaTag.DecodePriamTag(oldTag)?.ToProfile().GetBytes();
break;
// Priam tag, copy to Priam
case 12 when longSectorSize == 536:
newTag = new byte[24];
Array.Copy(data, givenSectorSize * i + 512, newTag, 0, 24);
break;
case 0:
newTag = null;
break;
default:
ErrorMessage = "Incorrect data size";
return false;
}
if(newTag == null) newTag = new byte[longSectorSize - 512];
writingStream.Seek(longSectorSize + (long)sectorAddress * longSectorSize, SeekOrigin.Begin);
writingStream.Write(data, (int)(givenSectorSize * i), 512);
writingStream.Write(newTag, 0, newTag.Length);
}
ErrorMessage = "";
return true;
}
public bool SetTracks(List<Track> tracks)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool Close()
{
if(!IsWriting)
{
ErrorMessage = "Image is not opened for writing";
return false;
}
BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;
byte[] markerTag = Encoding.UTF8.GetBytes("DiscImageChef " + Version.GetVersion());
byte[] driveName;
byte[] driveType = new byte[3];
byte[] driveBlocks = BigEndianBitConverter.GetBytes((uint)imageInfo.Sectors);
int longSectorSize = imageInfo.MediaType == MediaType.PriamDataTower ? 536 : 532;
byte[] blockSize = BigEndianBitConverter.GetBytes((ushort)longSectorSize);
switch(imageInfo.MediaType)
{
case MediaType.AppleProfile when imageInfo.Sectors == 0x4C00:
driveName = Encoding.ASCII.GetBytes(PROFILE10_NAME);
break;
case MediaType.AppleWidget when imageInfo.Sectors == 0x4C00:
driveType[1] = 0x01;
driveName = Encoding.ASCII.GetBytes(PROFILE10_NAME);
break;
case MediaType.PriamDataTower when imageInfo.Sectors == 0x22C7C:
driveType[1] = 0xFF;
driveName = Encoding.ASCII.GetBytes(PRIAM_NAME);
break;
default:
driveName = Encoding.ASCII.GetBytes(PROFILE_NAME);
break;
}
writingStream.Seek(0, SeekOrigin.Begin);
writingStream.Write(driveName, 0, driveName.Length >= 0xD ? 0xD : driveName.Length);
writingStream.Seek(0xD, SeekOrigin.Begin);
writingStream.Write(driveType, 0, 3);
writingStream.Seek(0x12, SeekOrigin.Begin);
writingStream.Write(driveBlocks, 1, 3);
writingStream.Seek(0x15, SeekOrigin.Begin);
writingStream.Write(blockSize, 1, 2);
writingStream.Seek(512, SeekOrigin.Begin);
writingStream.Write(markerTag, 0,
markerTag.Length >= longSectorSize - 512 ? longSectorSize - 512 : markerTag.Length);
writingStream.Flush();
writingStream.Close();
IsWriting = false;
ErrorMessage = "";
return true;
}
public bool SetMetadata(ImageInfo metadata)
{
return true;
}
public bool SetGeometry(uint cylinders, uint heads, uint sectorsPerTrack)
{
// Geometry is not stored in image
return true;
}
public bool WriteSectorTag(byte[] data, ulong sectorAddress, SectorTagType tag)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool WriteSectorsTag(byte[] data, ulong sectorAddress, uint length, SectorTagType tag)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool SetDumpHardware(List<DumpHardwareType> dumpHardware)
{
// Not supported
return false;
}
public bool SetCicmMetadata(CICMMetadataType metadata)
{
// Not supported
return false;
}
}
}

View File

@@ -0,0 +1,77 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : BlindWrite4.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disc image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Manages BlindWrite 4 disc images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Collections.Generic;
using System.IO;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
namespace DiscImageChef.DiscImages
{
// TODO: Too many unknowns, plus a completely unknown footer, to make this writable
public partial class BlindWrite4 : IMediaImage
{
List<Bw4TrackDescriptor> bwTracks;
IFilter dataFilter, subFilter;
Bw4Header header;
ImageInfo imageInfo;
Stream imageStream;
Dictionary<uint, ulong> offsetmap;
Dictionary<uint, byte> trackFlags;
public BlindWrite4()
{
imageInfo = new ImageInfo
{
ReadableSectorTags = new List<SectorTagType>(),
ReadableMediaTags = new List<MediaTagType>(),
HasPartitions = true,
HasSessions = true,
Version = null,
ApplicationVersion = null,
MediaTitle = null,
Creator = null,
MediaManufacturer = null,
MediaModel = null,
MediaPartNumber = null,
MediaSequence = 0,
LastMediaSequence = 0,
DriveManufacturer = null,
DriveModel = null,
DriveSerialNumber = null,
DriveFirmwareRevision = null
};
}
}
}

View File

@@ -0,0 +1,44 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Constants.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains constants for BlindWrite 4 disc images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
namespace DiscImageChef.DiscImages
{
public partial class BlindWrite4
{
/// <summary>"BLINDWRITE TOC FILE"</summary>
readonly byte[] bw4Signature =
{
0x42, 0x4C, 0x49, 0x4E, 0x44, 0x57, 0x52, 0x49, 0x54, 0x45, 0x20, 0x54, 0x4F, 0x43, 0x20, 0x46, 0x49, 0x4C,
0x45
};
}
}

View File

@@ -0,0 +1,44 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Enums.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains enumerations for BlindWrite 4 disc images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
namespace DiscImageChef.DiscImages
{
public partial class BlindWrite4
{
enum Bw4TrackType : byte
{
Audio = 0,
Mode1 = 1,
Mode2 = 2
}
}
}

View File

@@ -0,0 +1,53 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Identify.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Identifies BlindWrite 4 disc images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.IO;
using System.Linq;
using DiscImageChef.CommonTypes.Interfaces;
namespace DiscImageChef.DiscImages
{
public partial class BlindWrite4
{
public bool Identify(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin);
if(stream.Length < 19) return false;
byte[] signature = new byte[19];
stream.Read(signature, 0, 19);
return bw4Signature.SequenceEqual(signature);
}
}
}

View File

@@ -0,0 +1,58 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Properties.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains properties for BlindWrite 4 disc images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Structs;
using Schemas;
namespace DiscImageChef.DiscImages
{
public partial class BlindWrite4
{
public ImageInfo Info => imageInfo;
public string Name => "BlindWrite 4";
public Guid Id => new Guid("664568B2-15D4-4E64-8A7A-20BDA8B8386F");
public string Format => "BlindWrite 4 TOC file";
public List<Partition> Partitions { get; set; }
public List<Track> Tracks { get; set; }
public List<Session> Sessions { get; set; }
public List<DumpHardwareType> DumpHardware => null;
public CICMMetadataType CicmMetadata => null;
}
}

View File

@@ -1,15 +1,15 @@
// /***************************************************************************
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : BlindWrite4.cs
// Filename : Read.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disc image plugins.
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Manages BlindWrite 4 disc images.
// Reads BlindWrite 4 disc images.
//
// --[ License ] --------------------------------------------------------------
//
@@ -35,7 +35,6 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using DiscImageChef.Checksums;
using DiscImageChef.CommonTypes;
@@ -44,79 +43,11 @@ using DiscImageChef.CommonTypes.Exceptions;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
using DiscImageChef.Console;
using DiscImageChef.Filters;
using Schemas;
using TrackType = DiscImageChef.CommonTypes.Enums.TrackType;
namespace DiscImageChef.DiscImages
{
// TODO: Too many unknowns, plus a completely unknown footer, to make this writable
public class BlindWrite4 : IMediaImage
public partial class BlindWrite4
{
/// <summary>"BLINDWRITE TOC FILE"</summary>
readonly byte[] bw4Signature =
{
0x42, 0x4C, 0x49, 0x4E, 0x44, 0x57, 0x52, 0x49, 0x54, 0x45, 0x20, 0x54, 0x4F, 0x43, 0x20, 0x46, 0x49, 0x4C,
0x45
};
List<Bw4TrackDescriptor> bwTracks;
IFilter dataFilter, subFilter;
Bw4Header header;
ImageInfo imageInfo;
Stream imageStream;
Dictionary<uint, ulong> offsetmap;
Dictionary<uint, byte> trackFlags;
public BlindWrite4()
{
imageInfo = new ImageInfo
{
ReadableSectorTags = new List<SectorTagType>(),
ReadableMediaTags = new List<MediaTagType>(),
HasPartitions = true,
HasSessions = true,
Version = null,
ApplicationVersion = null,
MediaTitle = null,
Creator = null,
MediaManufacturer = null,
MediaModel = null,
MediaPartNumber = null,
MediaSequence = 0,
LastMediaSequence = 0,
DriveManufacturer = null,
DriveModel = null,
DriveSerialNumber = null,
DriveFirmwareRevision = null
};
}
public ImageInfo Info => imageInfo;
public string Name => "BlindWrite 4";
public Guid Id => new Guid("664568B2-15D4-4E64-8A7A-20BDA8B8386F");
public string Format => "BlindWrite 4 TOC file";
public List<Partition> Partitions { get; set; }
public List<Track> Tracks { get; set; }
public List<Session> Sessions { get; set; }
public bool Identify(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin);
if(stream.Length < 19) return false;
byte[] signature = new byte[19];
stream.Read(signature, 0, 19);
return bw4Signature.SequenceEqual(signature);
}
public bool Open(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
@@ -1197,126 +1128,5 @@ namespace DiscImageChef.DiscImages
{
return null;
}
public List<DumpHardwareType> DumpHardware => null;
public CICMMetadataType CicmMetadata => null;
struct Bw4Header
{
public byte[] Signature;
public uint Unknown1;
public ulong Timestamp;
public uint VolumeIdLength;
public byte[] VolumeIdBytes;
public uint SysIdLength;
public byte[] SysIdBytes;
public uint CommentsLength;
public byte[] CommentsBytes;
public uint TrackDescriptors;
public uint DataFileLength;
public byte[] DataFileBytes;
public uint SubchannelFileLength;
public byte[] SubchannelFileBytes;
public uint Unknown2;
public byte Unknown3;
public byte[] Unknown4;
// On memory only
#pragma warning disable 649
public string VolumeIdentifier;
public string SystemIdentifier;
public string Comments;
public IFilter DataFilter;
public IFilter SubchannelFilter;
public string DataFile;
public string SubchannelFile;
#pragma warning restore 649
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Bw4TrackDescriptor
{
public uint filenameLen;
public byte[] filenameBytes;
public uint offset;
public byte subchannel;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public byte[] unknown1;
public uint unknown2;
public byte unknown3;
public byte session;
public byte unknown4;
public byte adrCtl;
public byte unknown5;
public Bw4TrackType trackMode;
public byte unknown6;
public byte point;
public uint unknown7;
public uint unknown8;
public uint unknown9;
public uint unknown10;
public ushort unknown11;
public uint lastSector;
public byte unknown12;
public int pregap;
public int startSector;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public uint[] unknown13;
public uint titleLen;
public byte[] titleBytes;
public uint performerLen;
public byte[] performerBytes;
public uint unkStrLen1;
public byte[] unkStrBytes1;
public uint unkStrLen2;
public byte[] unkStrBytes2;
public uint unkStrLen3;
public byte[] unkStrBytes3;
public uint unkStrLen4;
public byte[] unkStrBytes4;
public uint discIdLen;
public byte[] discIdBytes;
public uint unkStrLen5;
public byte[] unkStrBytes5;
public uint unkStrLen6;
public byte[] unkStrBytes6;
public uint unkStrLen7;
public byte[] unkStrBytes7;
public uint unkStrLen8;
public byte[] unkStrBytes8;
public uint unkStrLen9;
public byte[] unkStrBytes9;
public uint unkStrLen10;
public byte[] unkStrBytes10;
public uint unkStrLen11;
public byte[] unkStrBytes11;
public uint isrcLen;
public byte[] isrcBytes;
// On memory only
public string filename;
public string title;
public string performer;
public string unkString1;
public string unkString2;
public string unkString3;
public string unkString4;
public string discId;
public string unkString5;
public string unkString6;
public string unkString7;
public string unkString8;
public string unkString9;
public string unkString10;
public string unkString11;
public string isrcUpc;
}
enum Bw4TrackType : byte
{
Audio = 0,
Mode1 = 1,
Mode2 = 2
}
}
}

View File

@@ -0,0 +1,151 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Structs.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains structures for BlindWrite 4 disc images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Runtime.InteropServices;
using DiscImageChef.CommonTypes.Interfaces;
namespace DiscImageChef.DiscImages
{
public partial class BlindWrite4
{
struct Bw4Header
{
public byte[] Signature;
public uint Unknown1;
public ulong Timestamp;
public uint VolumeIdLength;
public byte[] VolumeIdBytes;
public uint SysIdLength;
public byte[] SysIdBytes;
public uint CommentsLength;
public byte[] CommentsBytes;
public uint TrackDescriptors;
public uint DataFileLength;
public byte[] DataFileBytes;
public uint SubchannelFileLength;
public byte[] SubchannelFileBytes;
public uint Unknown2;
public byte Unknown3;
public byte[] Unknown4;
// On memory only
#pragma warning disable 649
public string VolumeIdentifier;
public string SystemIdentifier;
public string Comments;
public IFilter DataFilter;
public IFilter SubchannelFilter;
public string DataFile;
public string SubchannelFile;
#pragma warning restore 649
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Bw4TrackDescriptor
{
public uint filenameLen;
public byte[] filenameBytes;
public uint offset;
public byte subchannel;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public byte[] unknown1;
public uint unknown2;
public byte unknown3;
public byte session;
public byte unknown4;
public byte adrCtl;
public byte unknown5;
public Bw4TrackType trackMode;
public byte unknown6;
public byte point;
public uint unknown7;
public uint unknown8;
public uint unknown9;
public uint unknown10;
public ushort unknown11;
public uint lastSector;
public byte unknown12;
public int pregap;
public int startSector;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public uint[] unknown13;
public uint titleLen;
public byte[] titleBytes;
public uint performerLen;
public byte[] performerBytes;
public uint unkStrLen1;
public byte[] unkStrBytes1;
public uint unkStrLen2;
public byte[] unkStrBytes2;
public uint unkStrLen3;
public byte[] unkStrBytes3;
public uint unkStrLen4;
public byte[] unkStrBytes4;
public uint discIdLen;
public byte[] discIdBytes;
public uint unkStrLen5;
public byte[] unkStrBytes5;
public uint unkStrLen6;
public byte[] unkStrBytes6;
public uint unkStrLen7;
public byte[] unkStrBytes7;
public uint unkStrLen8;
public byte[] unkStrBytes8;
public uint unkStrLen9;
public byte[] unkStrBytes9;
public uint unkStrLen10;
public byte[] unkStrBytes10;
public uint unkStrLen11;
public byte[] unkStrBytes11;
public uint isrcLen;
public byte[] isrcBytes;
// On memory only
public string filename;
public string title;
public string performer;
public string unkString1;
public string unkString2;
public string unkString3;
public string unkString4;
public string discId;
public string unkString5;
public string unkString6;
public string unkString7;
public string unkString8;
public string unkString9;
public string unkString10;
public string unkString11;
public string isrcUpc;
}
}
}

View File

@@ -0,0 +1,90 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : BlindWrite5.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disc image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Manages BlindWrite 5 disc images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Collections.Generic;
using System.IO;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
namespace DiscImageChef.DiscImages
{
// TODO: Too many unknowns to make this writable
public partial class BlindWrite5 : IMediaImage
{
byte[] atip;
byte[] bca;
List<Bw5SessionDescriptor> bwSessions;
byte[] cdtext;
List<Bw5DataFile> dataFiles;
string dataPath;
byte[] discInformation;
byte[] dmi;
byte[] dpm;
List<DataFileCharacteristics> filePaths;
byte[] fullToc;
Bw5Header header;
ImageInfo imageInfo;
Stream imageStream;
byte[] mode2A;
Dictionary<uint, ulong> offsetmap;
byte[] pfi;
byte[] pma;
Dictionary<uint, byte> trackFlags;
byte[] unkBlock;
public BlindWrite5()
{
imageInfo = new ImageInfo
{
ReadableSectorTags = new List<SectorTagType>(),
ReadableMediaTags = new List<MediaTagType>(),
HasPartitions = true,
HasSessions = true,
Version = null,
ApplicationVersion = null,
MediaTitle = null,
Creator = null,
MediaManufacturer = null,
MediaModel = null,
MediaPartNumber = null,
MediaSequence = 0,
LastMediaSequence = 0,
DriveManufacturer = null,
DriveModel = null,
DriveSerialNumber = null,
DriveFirmwareRevision = null
};
}
}
}

View File

@@ -0,0 +1,44 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Constants.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains constants for BlindWrite 5 disc images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
namespace DiscImageChef.DiscImages
{
public partial class BlindWrite5
{
/// <summary>"BWT5 STREAM FOOT"</summary>
readonly byte[] bw5Footer =
{0x42, 0x57, 0x54, 0x35, 0x20, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4D, 0x20, 0x46, 0x4F, 0x4F, 0x54};
/// <summary>"BWT5 STREAM SIGN"</summary>
readonly byte[] bw5Signature =
{0x42, 0x57, 0x54, 0x35, 0x20, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4D, 0x20, 0x53, 0x49, 0x47, 0x4E};
}
}

View File

@@ -0,0 +1,55 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Enums.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains enumerations for BlindWrite 5 disc images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
namespace DiscImageChef.DiscImages
{
public partial class BlindWrite5
{
enum Bw5TrackType : byte
{
NotData = 0,
Audio = 1,
Mode1 = 2,
Mode2 = 3,
Mode2F1 = 4,
Mode2F2 = 5,
Dvd = 6
}
enum Bw5TrackSubchannel : byte
{
None = 0,
Q16 = 2,
Linear = 4
}
}
}

View File

@@ -0,0 +1,97 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Helpers.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains helpers for BlindWrite 5 disc images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.Decoders.SCSI.MMC;
namespace DiscImageChef.DiscImages
{
public partial class BlindWrite5
{
static TrackType BlindWriteTrackTypeToTrackType(Bw5TrackType trackType)
{
switch(trackType)
{
case Bw5TrackType.Mode1: return TrackType.CdMode1;
case Bw5TrackType.Mode2F1: return TrackType.CdMode2Form1;
case Bw5TrackType.Mode2F2: return TrackType.CdMode2Form2;
case Bw5TrackType.Mode2: return TrackType.CdMode2Formless;
case Bw5TrackType.Audio: return TrackType.Audio;
default: return TrackType.Data;
}
}
static MediaType BlindWriteProfileToMediaType(ProfileNumber profile)
{
switch(profile)
{
case ProfileNumber.BDRE: return MediaType.BDRE;
case ProfileNumber.BDROM: return MediaType.BDROM;
case ProfileNumber.BDRRdm:
case ProfileNumber.BDRSeq: return MediaType.BDR;
case ProfileNumber.CDR:
case ProfileNumber.HDBURNR: return MediaType.CDR;
case ProfileNumber.CDROM:
case ProfileNumber.HDBURNROM: return MediaType.CDROM;
case ProfileNumber.CDRW:
case ProfileNumber.HDBURNRW: return MediaType.CDRW;
case ProfileNumber.DDCDR: return MediaType.DDCDR;
case ProfileNumber.DDCDROM: return MediaType.DDCD;
case ProfileNumber.DDCDRW: return MediaType.DDCDRW;
case ProfileNumber.DVDDownload: return MediaType.DVDDownload;
case ProfileNumber.DVDRAM: return MediaType.DVDRAM;
case ProfileNumber.DVDRDLJump:
case ProfileNumber.DVDRDLSeq: return MediaType.DVDRDL;
case ProfileNumber.DVDRDLPlus: return MediaType.DVDPRDL;
case ProfileNumber.DVDROM: return MediaType.DVDROM;
case ProfileNumber.DVDRPlus: return MediaType.DVDPR;
case ProfileNumber.DVDRSeq: return MediaType.DVDR;
case ProfileNumber.DVDRWDL: return MediaType.DVDRWDL;
case ProfileNumber.DVDRWDLPlus: return MediaType.DVDPRWDL;
case ProfileNumber.DVDRWPlus: return MediaType.DVDPRW;
case ProfileNumber.DVDRWRes:
case ProfileNumber.DVDRWSeq: return MediaType.DVDRW;
case ProfileNumber.HDDVDR: return MediaType.HDDVDR;
case ProfileNumber.HDDVDRAM: return MediaType.HDDVDRAM;
case ProfileNumber.HDDVDRDL: return MediaType.HDDVDRDL;
case ProfileNumber.HDDVDROM: return MediaType.HDDVDROM;
case ProfileNumber.HDDVDRW: return MediaType.HDDVDRW;
case ProfileNumber.HDDVDRWDL: return MediaType.HDDVDRWDL;
case ProfileNumber.ASMO:
case ProfileNumber.MOErasable: return MediaType.UnknownMO;
case ProfileNumber.NonRemovable: return MediaType.GENERIC_HDD;
default: return MediaType.CD;
}
}
}
}

View File

@@ -0,0 +1,57 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Identify.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Identifies BlindWrite 5 disc images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.IO;
using System.Linq;
using DiscImageChef.CommonTypes.Interfaces;
namespace DiscImageChef.DiscImages
{
public partial class BlindWrite5
{
public bool Identify(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin);
if(stream.Length < 276) return false;
byte[] signature = new byte[16];
stream.Read(signature, 0, 16);
byte[] footer = new byte[16];
stream.Seek(-16, SeekOrigin.End);
stream.Read(footer, 0, 16);
return bw5Signature.SequenceEqual(signature) && bw5Footer.SequenceEqual(footer);
}
}
}

View File

@@ -0,0 +1,59 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Properties.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains properties for BlindWrite 5 disc images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Structs;
using Schemas;
namespace DiscImageChef.DiscImages
{
public partial class BlindWrite5
{
public ImageInfo Info => imageInfo;
public string Name => "BlindWrite 5";
public Guid Id => new Guid("9CB7A381-0509-4F9F-B801-3F65434BC3EE");
public string Format => "BlindWrite 5 TOC file";
public List<Partition> Partitions { get; private set; }
public List<Track> Tracks { get; private set; }
public List<Session> Sessions { get; private set; }
public List<DumpHardwareType> DumpHardware => null;
public CICMMetadataType CicmMetadata => null;
}
}

View File

@@ -1,15 +1,15 @@
// /***************************************************************************
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : BlindWrite5.cs
// Filename : Read.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disc image plugins.
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Manages BlindWrite 5 disc images.
// Reads BlindWrite 5 disc images.
//
// --[ License ] --------------------------------------------------------------
//
@@ -48,98 +48,13 @@ using DiscImageChef.Decoders.CD;
using DiscImageChef.Decoders.DVD;
using DiscImageChef.Decoders.SCSI;
using DiscImageChef.Decoders.SCSI.MMC;
using DiscImageChef.Filters;
using Schemas;
using DMI = DiscImageChef.Decoders.Xbox.DMI;
using Session = DiscImageChef.CommonTypes.Structs.Session;
using TrackType = DiscImageChef.CommonTypes.Enums.TrackType;
namespace DiscImageChef.DiscImages
{
// TODO: Too many unknowns to make this writable
public class BlindWrite5 : IMediaImage
public partial class BlindWrite5
{
/// <summary>"BWT5 STREAM FOOT"</summary>
readonly byte[] bw5Footer =
{0x42, 0x57, 0x54, 0x35, 0x20, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4D, 0x20, 0x46, 0x4F, 0x4F, 0x54};
/// <summary>"BWT5 STREAM SIGN"</summary>
readonly byte[] bw5Signature =
{0x42, 0x57, 0x54, 0x35, 0x20, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4D, 0x20, 0x53, 0x49, 0x47, 0x4E};
byte[] atip;
byte[] bca;
List<Bw5SessionDescriptor> bwSessions;
byte[] cdtext;
List<Bw5DataFile> dataFiles;
string dataPath;
byte[] discInformation;
byte[] dmi;
byte[] dpm;
List<DataFileCharacteristics> filePaths;
byte[] fullToc;
Bw5Header header;
ImageInfo imageInfo;
Stream imageStream;
byte[] mode2A;
Dictionary<uint, ulong> offsetmap;
byte[] pfi;
byte[] pma;
Dictionary<uint, byte> trackFlags;
byte[] unkBlock;
public BlindWrite5()
{
imageInfo = new ImageInfo
{
ReadableSectorTags = new List<SectorTagType>(),
ReadableMediaTags = new List<MediaTagType>(),
HasPartitions = true,
HasSessions = true,
Version = null,
ApplicationVersion = null,
MediaTitle = null,
Creator = null,
MediaManufacturer = null,
MediaModel = null,
MediaPartNumber = null,
MediaSequence = 0,
LastMediaSequence = 0,
DriveManufacturer = null,
DriveModel = null,
DriveSerialNumber = null,
DriveFirmwareRevision = null
};
}
public ImageInfo Info => imageInfo;
public string Name => "BlindWrite 5";
public Guid Id => new Guid("9CB7A381-0509-4F9F-B801-3F65434BC3EE");
public string Format => "BlindWrite 5 TOC file";
public List<Partition> Partitions { get; private set; }
public List<Track> Tracks { get; private set; }
public List<Session> Sessions { get; private set; }
public bool Identify(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin);
if(stream.Length < 276) return false;
byte[] signature = new byte[16];
stream.Read(signature, 0, 16);
byte[] footer = new byte[16];
stream.Seek(-16, SeekOrigin.End);
stream.Read(footer, 0, 16);
return bw5Signature.SequenceEqual(signature) && bw5Footer.SequenceEqual(footer);
}
public bool Open(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
@@ -1761,203 +1676,5 @@ namespace DiscImageChef.DiscImages
{
return null;
}
public List<DumpHardwareType> DumpHardware => null;
public CICMMetadataType CicmMetadata => null;
static TrackType BlindWriteTrackTypeToTrackType(Bw5TrackType trackType)
{
switch(trackType)
{
case Bw5TrackType.Mode1: return TrackType.CdMode1;
case Bw5TrackType.Mode2F1: return TrackType.CdMode2Form1;
case Bw5TrackType.Mode2F2: return TrackType.CdMode2Form2;
case Bw5TrackType.Mode2: return TrackType.CdMode2Formless;
case Bw5TrackType.Audio: return TrackType.Audio;
default: return TrackType.Data;
}
}
static MediaType BlindWriteProfileToMediaType(ProfileNumber profile)
{
switch(profile)
{
case ProfileNumber.BDRE: return MediaType.BDRE;
case ProfileNumber.BDROM: return MediaType.BDROM;
case ProfileNumber.BDRRdm:
case ProfileNumber.BDRSeq: return MediaType.BDR;
case ProfileNumber.CDR:
case ProfileNumber.HDBURNR: return MediaType.CDR;
case ProfileNumber.CDROM:
case ProfileNumber.HDBURNROM: return MediaType.CDROM;
case ProfileNumber.CDRW:
case ProfileNumber.HDBURNRW: return MediaType.CDRW;
case ProfileNumber.DDCDR: return MediaType.DDCDR;
case ProfileNumber.DDCDROM: return MediaType.DDCD;
case ProfileNumber.DDCDRW: return MediaType.DDCDRW;
case ProfileNumber.DVDDownload: return MediaType.DVDDownload;
case ProfileNumber.DVDRAM: return MediaType.DVDRAM;
case ProfileNumber.DVDRDLJump:
case ProfileNumber.DVDRDLSeq: return MediaType.DVDRDL;
case ProfileNumber.DVDRDLPlus: return MediaType.DVDPRDL;
case ProfileNumber.DVDROM: return MediaType.DVDROM;
case ProfileNumber.DVDRPlus: return MediaType.DVDPR;
case ProfileNumber.DVDRSeq: return MediaType.DVDR;
case ProfileNumber.DVDRWDL: return MediaType.DVDRWDL;
case ProfileNumber.DVDRWDLPlus: return MediaType.DVDPRWDL;
case ProfileNumber.DVDRWPlus: return MediaType.DVDPRW;
case ProfileNumber.DVDRWRes:
case ProfileNumber.DVDRWSeq: return MediaType.DVDRW;
case ProfileNumber.HDDVDR: return MediaType.HDDVDR;
case ProfileNumber.HDDVDRAM: return MediaType.HDDVDRAM;
case ProfileNumber.HDDVDRDL: return MediaType.HDDVDRDL;
case ProfileNumber.HDDVDROM: return MediaType.HDDVDROM;
case ProfileNumber.HDDVDRW: return MediaType.HDDVDRW;
case ProfileNumber.HDDVDRWDL: return MediaType.HDDVDRWDL;
case ProfileNumber.ASMO:
case ProfileNumber.MOErasable: return MediaType.UnknownMO;
case ProfileNumber.NonRemovable: return MediaType.GENERIC_HDD;
default: return MediaType.CD;
}
}
enum Bw5TrackType : byte
{
NotData = 0,
Audio = 1,
Mode1 = 2,
Mode2 = 3,
Mode2F1 = 4,
Mode2F2 = 5,
Dvd = 6
}
enum Bw5TrackSubchannel : byte
{
None = 0,
Q16 = 2,
Linear = 4
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Bw5Header
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] signature;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public uint[] unknown1;
public ProfileNumber profile;
public ushort sessions;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public uint[] unknown2;
[MarshalAs(UnmanagedType.U1, SizeConst = 3)]
public bool mcnIsValid;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 13)]
public byte[] mcn;
public ushort unknown3;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public uint[] unknown4;
public ushort pmaLen;
public ushort atipLen;
public ushort cdtLen;
public ushort cdInfoLen;
public uint bcaLen;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public uint[] unknown5;
public uint dvdStrLen;
public uint dvdInfoLen;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] unknown6;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public byte[] manufacturer;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] product;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] revision;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public byte[] vendor;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] volumeId;
public uint mode2ALen;
public uint unkBlkLen;
public uint dataLen;
public uint sessionsLen;
public uint dpmLen;
}
struct Bw5DataFile
{
public uint Type;
public uint Length;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public uint[] Unknown1;
public uint Offset;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public uint[] Unknown2;
public int StartLba;
public int Sectors;
public uint FilenameLen;
public byte[] FilenameBytes;
public uint Unknown3;
public string Filename;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Bw5TrackDescriptor
{
public Bw5TrackType type;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public byte[] unknown1;
public uint unknown2;
public Bw5TrackSubchannel subchannel;
public byte unknown3;
public byte ctl;
public byte adr;
public byte point;
public byte tno;
public byte min;
public byte sec;
public byte frame;
public byte zero;
public byte pmin;
public byte psec;
public byte pframe;
public byte unknown5;
public uint pregap;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public uint[] unknown6;
public int startLba;
public int sectors;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public uint[] unknown7;
public uint session;
public ushort unknown8;
// Seems to be only on non DVD track descriptors
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public uint[] unknown9;
}
struct Bw5SessionDescriptor
{
public ushort Sequence;
public byte Entries;
public byte Unknown;
public int Start;
public int End;
public ushort FirstTrack;
public ushort LastTrack;
public Bw5TrackDescriptor[] Tracks;
}
struct DataFileCharacteristics
{
public IFilter FileFilter;
public string FilePath;
public TrackSubchannelType Subchannel;
public long SectorSize;
public int StartLba;
public int Sectors;
}
}
}

View File

@@ -0,0 +1,166 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Structs.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains structures for BlindWrite 5 disc images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Runtime.InteropServices;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.Decoders.SCSI.MMC;
namespace DiscImageChef.DiscImages
{
public partial class BlindWrite5
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Bw5Header
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] signature;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public uint[] unknown1;
public ProfileNumber profile;
public ushort sessions;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public uint[] unknown2;
[MarshalAs(UnmanagedType.U1, SizeConst = 3)]
public bool mcnIsValid;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 13)]
public byte[] mcn;
public ushort unknown3;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public uint[] unknown4;
public ushort pmaLen;
public ushort atipLen;
public ushort cdtLen;
public ushort cdInfoLen;
public uint bcaLen;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public uint[] unknown5;
public uint dvdStrLen;
public uint dvdInfoLen;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] unknown6;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public byte[] manufacturer;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] product;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] revision;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public byte[] vendor;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] volumeId;
public uint mode2ALen;
public uint unkBlkLen;
public uint dataLen;
public uint sessionsLen;
public uint dpmLen;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Bw5DataFile
{
public uint Type;
public uint Length;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public uint[] Unknown1;
public uint Offset;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public uint[] Unknown2;
public int StartLba;
public int Sectors;
public uint FilenameLen;
public byte[] FilenameBytes;
public uint Unknown3;
public string Filename;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Bw5TrackDescriptor
{
public Bw5TrackType type;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public byte[] unknown1;
public uint unknown2;
public Bw5TrackSubchannel subchannel;
public byte unknown3;
public byte ctl;
public byte adr;
public byte point;
public byte tno;
public byte min;
public byte sec;
public byte frame;
public byte zero;
public byte pmin;
public byte psec;
public byte pframe;
public byte unknown5;
public uint pregap;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public uint[] unknown6;
public int startLba;
public int sectors;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public uint[] unknown7;
public uint session;
public ushort unknown8;
// Seems to be only on non DVD track descriptors
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public uint[] unknown9;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Bw5SessionDescriptor
{
public ushort Sequence;
public byte Entries;
public byte Unknown;
public int Start;
public int End;
public ushort FirstTrack;
public ushort LastTrack;
public Bw5TrackDescriptor[] Tracks;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct DataFileCharacteristics
{
public IFilter FileFilter;
public string FilePath;
public TrackSubchannelType Subchannel;
public long SectorSize;
public int StartLba;
public int Sectors;
}
}
}

View File

@@ -0,0 +1,84 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : CDRDAO.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disc image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Manages cdrdao cuesheets (toc/bin).
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Collections.Generic;
using System.IO;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
namespace DiscImageChef.DiscImages
{
// TODO: Doesn't support compositing from several files
// TODO: Doesn't support silences that are not in files
public partial class Cdrdao : IWritableImage
{
IFilter cdrdaoFilter;
StreamWriter descriptorStream;
CdrdaoDisc discimage;
ImageInfo imageInfo;
Stream imageStream;
/// <summary>Dictionary, index is track #, value is TrackFile</summary>
Dictionary<uint, ulong> offsetmap;
bool separateTracksWriting;
StreamReader tocStream;
Dictionary<byte, byte> trackFlags;
Dictionary<byte, string> trackIsrcs;
string writingBaseName;
Dictionary<uint, FileStream> writingStreams;
List<Track> writingTracks;
public Cdrdao()
{
imageInfo = new ImageInfo
{
ReadableSectorTags = new List<SectorTagType>(),
ReadableMediaTags = new List<MediaTagType>(),
HasPartitions = true,
HasSessions = true,
Version = null,
ApplicationVersion = null,
MediaTitle = null,
Creator = null,
MediaManufacturer = null,
MediaModel = null,
MediaPartNumber = null,
MediaSequence = 0,
LastMediaSequence = 0,
DriveManufacturer = null,
DriveModel = null,
DriveSerialNumber = null,
DriveFirmwareRevision = null
};
}
}
}

View File

@@ -0,0 +1,90 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Constants.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains constants for cdrdao cuesheets (toc/bin).
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
namespace DiscImageChef.DiscImages
{
public partial class Cdrdao
{
/// <summary>Audio track, 2352 bytes/sector</summary>
const string CDRDAO_TRACK_TYPE_AUDIO = "AUDIO";
/// <summary>Mode 1 track, cooked, 2048 bytes/sector</summary>
const string CDRDAO_TRACK_TYPE_MODE1 = "MODE1";
/// <summary>Mode 1 track, raw, 2352 bytes/sector</summary>
const string CDRDAO_TRACK_TYPE_MODE1_RAW = "MODE1_RAW";
/// <summary>Mode 2 mixed formless, cooked, 2336 bytes/sector</summary>
const string CDRDAO_TRACK_TYPE_MODE2 = "MODE2";
/// <summary>Mode 2 form 1 track, cooked, 2048 bytes/sector</summary>
const string CDRDAO_TRACK_TYPE_MODE2_FORM1 = "MODE2_FORM1";
/// <summary>Mode 2 form 2 track, cooked, 2324 bytes/sector</summary>
const string CDRDAO_TRACK_TYPE_MODE2_FORM2 = "MODE2_FORM2";
/// <summary>Mode 2 mixed forms track, cooked, 2336 bytes/sector</summary>
const string CDRDAO_TRACK_TYPE_MODE2_MIX = "MODE2_FORM_MIX";
/// <summary>Mode 2 track, raw, 2352 bytes/sector</summary>
const string CDRDAO_TRACK_TYPE_MODE2_RAW = "MODE2_RAW";
const string REGEX_COMMENT = @"^\s*\/{2}(?<comment>.+)$";
const string REGEX_COPY = @"^\s*(?<no>NO)?\s*COPY";
const string REGEX_DISCTYPE = @"^\s*(?<type>(CD_DA|CD_ROM_XA|CD_ROM|CD_I))";
const string REGEX_EMPHASIS = @"^\s*(?<no>NO)?\s*PRE_EMPHASIS";
const string REGEX_FILE_AUDIO =
@"^\s*(AUDIO)?FILE\s*""(?<filename>.+)""\s*(#(?<base_offset>\d+))?\s*((?<start>[\d]+:[\d]+:[\d]+)|(?<start_num>\d+))\s*(?<length>[\d]+:[\d]+:[\d]+)?";
const string REGEX_FILE_DATA =
@"^\s*DATAFILE\s*""(?<filename>.+)""\s*(#(?<base_offset>\d+))?\s*(?<length>[\d]+:[\d]+:[\d]+)?";
const string REGEX_INDEX = @"^\s*INDEX\s*(?<address>\d+:\d+:\d+)";
const string REGEX_ISRC = @"^\s*ISRC\s*""(?<isrc>[A-Z0-9]{5,5}[0-9]{7,7})""";
const string REGEX_MCN = @"^\s*CATALOG\s*""(?<catalog>[\x21-\x7F]{13,13})""";
const string REGEX_PREGAP = @"^\s*START\s*(?<address>\d+:\d+:\d+)?";
const string REGEX_STEREO = @"^\s*(?<num>(TWO|FOUR))_CHANNEL_AUDIO";
const string REGEX_TRACK =
@"^\s*TRACK\s*(?<type>(AUDIO|MODE1_RAW|MODE1|MODE2_FORM1|MODE2_FORM2|MODE2_FORM_MIX|MODE2_RAW|MODE2))\s*(?<subchan>(RW_RAW|RW))?";
const string REGEX_ZERO_AUDIO = @"^\s*SILENCE\s*(?<length>\d+:\d+:\d+)";
const string REGEX_ZERO_DATA = @"^\s*ZERO\s*(?<length>\d+:\d+:\d+)";
const string REGEX_ZERO_PREGAP = @"^\s*PREGAP\s*(?<length>\d+:\d+:\d+)";
// CD-Text
const string REGEX_ARRANGER = @"^\s*ARRANGER\s*""(?<arranger>.+)""";
const string REGEX_COMPOSER = @"^\s*COMPOSER\s*""(?<composer>.+)""";
const string REGEX_DISC_ID = @"^\s*DISC_ID\s*""(?<discid>.+)""";
const string REGEX_MESSAGE = @"^\s*MESSAGE\s*""(?<message>.+)""";
const string REGEX_PERFORMER = @"^\s*PERFORMER\s*""(?<performer>.+)""";
const string REGEX_SONGWRITER = @"^\s*SONGWRITER\s*""(?<songwriter>.+)""";
const string REGEX_TITLE = @"^\s*TITLE\s*""(?<title>.+)""";
const string REGEX_UPC = @"^\s*UPC_EAN\s*""(?<catalog>[\d]{13,13})""";
// Unused
const string REGEX_CD_TEXT = @"^\s*CD_TEXT\s*\{";
const string REGEX_CLOSURE = @"^\s*\}";
const string REGEX_LANGUAGE = @"^\s*LANGUAGE\s*(?<code>\d+)\s*\{";
const string REGEX_LANGUAGE_MAP = @"^\s*LANGUAGE_MAP\s*\{";
const string REGEX_LANGUAGE_MAPPING = @"^\s*(?<code>\d+)\s?\:\s?(?<language>\d+|\w+)";
}
}

View File

@@ -0,0 +1,114 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Helpers.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains helpers for cdrdao cuesheets (toc/bin).
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Structs;
namespace DiscImageChef.DiscImages
{
public partial class Cdrdao
{
static ushort CdrdaoTrackTypeToBytesPerSector(string trackType)
{
switch(trackType)
{
case CDRDAO_TRACK_TYPE_MODE1:
case CDRDAO_TRACK_TYPE_MODE2_FORM1: return 2048;
case CDRDAO_TRACK_TYPE_MODE2_FORM2: return 2324;
case CDRDAO_TRACK_TYPE_MODE2:
case CDRDAO_TRACK_TYPE_MODE2_MIX: return 2336;
case CDRDAO_TRACK_TYPE_AUDIO:
case CDRDAO_TRACK_TYPE_MODE1_RAW:
case CDRDAO_TRACK_TYPE_MODE2_RAW: return 2352;
default: return 0;
}
}
static ushort CdrdaoTrackTypeToCookedBytesPerSector(string trackType)
{
switch(trackType)
{
case CDRDAO_TRACK_TYPE_MODE1:
case CDRDAO_TRACK_TYPE_MODE2_FORM1:
case CDRDAO_TRACK_TYPE_MODE1_RAW: return 2048;
case CDRDAO_TRACK_TYPE_MODE2_FORM2: return 2324;
case CDRDAO_TRACK_TYPE_MODE2:
case CDRDAO_TRACK_TYPE_MODE2_MIX:
case CDRDAO_TRACK_TYPE_MODE2_RAW: return 2336;
case CDRDAO_TRACK_TYPE_AUDIO: return 2352;
default: return 0;
}
}
static TrackType CdrdaoTrackTypeToTrackType(string trackType)
{
switch(trackType)
{
case CDRDAO_TRACK_TYPE_MODE1:
case CDRDAO_TRACK_TYPE_MODE1_RAW: return TrackType.CdMode1;
case CDRDAO_TRACK_TYPE_MODE2_FORM1: return TrackType.CdMode2Form1;
case CDRDAO_TRACK_TYPE_MODE2_FORM2: return TrackType.CdMode2Form2;
case CDRDAO_TRACK_TYPE_MODE2:
case CDRDAO_TRACK_TYPE_MODE2_MIX:
case CDRDAO_TRACK_TYPE_MODE2_RAW: return TrackType.CdMode2Formless;
case CDRDAO_TRACK_TYPE_AUDIO: return TrackType.Audio;
default: return TrackType.Data;
}
}
static (byte minute, byte second, byte frame) LbaToMsf(ulong sector)
{
return ((byte)(sector / 75 / 60), (byte)(sector / 75 % 60), (byte)(sector % 75));
}
static string GetTrackMode(Track track)
{
switch(track.TrackType)
{
case TrackType.Audio when track.TrackRawBytesPerSector == 2352: return CDRDAO_TRACK_TYPE_AUDIO;
case TrackType.Data: return CDRDAO_TRACK_TYPE_MODE1;
case TrackType.CdMode1 when track.TrackRawBytesPerSector == 2352: return CDRDAO_TRACK_TYPE_MODE1_RAW;
case TrackType.CdMode2Formless
when track.TrackRawBytesPerSector != 2352: return CDRDAO_TRACK_TYPE_MODE2;
case TrackType.CdMode2Form1
when track.TrackRawBytesPerSector != 2352: return CDRDAO_TRACK_TYPE_MODE2_FORM1;
case TrackType.CdMode2Form2
when track.TrackRawBytesPerSector != 2352: return CDRDAO_TRACK_TYPE_MODE2_FORM2;
case TrackType.CdMode2Formless when track.TrackRawBytesPerSector == 2352:
case TrackType.CdMode2Form1 when track.TrackRawBytesPerSector == 2352:
case TrackType.CdMode2Form2
when track.TrackRawBytesPerSector == 2352: return CDRDAO_TRACK_TYPE_MODE2_RAW;
default: return CDRDAO_TRACK_TYPE_MODE1;
}
}
}
}

View File

@@ -0,0 +1,98 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Identify.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Identifies cdrdao cuesheets (toc/bin).
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.IO;
using System.Text.RegularExpressions;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.Console;
namespace DiscImageChef.DiscImages
{
public partial class Cdrdao
{
public bool Identify(IFilter imageFilter)
{
try
{
imageFilter.GetDataForkStream().Seek(0, SeekOrigin.Begin);
byte[] testArray = new byte[512];
imageFilter.GetDataForkStream().Read(testArray, 0, 512);
imageFilter.GetDataForkStream().Seek(0, SeekOrigin.Begin);
// Check for unexpected control characters that shouldn't be present in a text file and can crash this plugin
bool twoConsecutiveNulls = false;
for(int i = 0; i < 512; i++)
{
if(i >= imageFilter.GetDataForkStream().Length) break;
if(testArray[i] == 0)
{
if(twoConsecutiveNulls) return false;
twoConsecutiveNulls = true;
}
else twoConsecutiveNulls = false;
if(testArray[i] < 0x20 && testArray[i] != 0x0A && testArray[i] != 0x0D && testArray[i] != 0x00)
return false;
}
tocStream = new StreamReader(imageFilter.GetDataForkStream());
Regex cr = new Regex(REGEX_COMMENT);
Regex dr = new Regex(REGEX_DISCTYPE);
while(tocStream.Peek() >= 0)
{
string line = tocStream.ReadLine();
Match dm = dr.Match(line ?? throw new InvalidOperationException());
Match cm = cr.Match(line);
// Skip comments at start of file
if(cm.Success) continue;
return dm.Success;
}
return false;
}
catch(Exception ex)
{
DicConsole.ErrorWriteLine("Exception trying to identify image file {0}", cdrdaoFilter.GetFilename());
DicConsole.ErrorWriteLine("Exception: {0}", ex.Message);
DicConsole.ErrorWriteLine("Stack trace: {0}", ex.StackTrace);
return false;
}
}
}
}

View File

@@ -0,0 +1,124 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Properties.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains properties for cdrdao cuesheets (toc/bin).
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Structs;
using Schemas;
namespace DiscImageChef.DiscImages
{
public partial class Cdrdao
{
public ImageInfo Info => imageInfo;
public string Name => "CDRDAO tocfile";
public Guid Id => new Guid("04D7BA12-1BE8-44D4-97A4-1B48A505463E");
public string Format => "CDRDAO tocfile";
public List<Partition> Partitions { get; private set; }
public List<Session> Sessions => throw new NotImplementedException();
public List<Track> Tracks
{
get
{
List<Track> tracks = new List<Track>();
foreach(CdrdaoTrack cdrTrack in discimage.Tracks)
{
Track dicTrack = new Track
{
Indexes = cdrTrack.Indexes,
TrackDescription = cdrTrack.Title,
TrackStartSector = cdrTrack.StartSector,
TrackPregap = cdrTrack.Pregap,
TrackSession = 1,
TrackSequence = cdrTrack.Sequence,
TrackType = CdrdaoTrackTypeToTrackType(cdrTrack.Tracktype),
TrackFilter = cdrTrack.Trackfile.Datafilter,
TrackFile = cdrTrack.Trackfile.Datafilter.GetFilename(),
TrackFileOffset = cdrTrack.Trackfile.Offset,
TrackFileType = cdrTrack.Trackfile.Filetype,
TrackRawBytesPerSector = cdrTrack.Bps,
TrackBytesPerSector = CdrdaoTrackTypeToCookedBytesPerSector(cdrTrack.Tracktype)
};
dicTrack.TrackEndSector = dicTrack.TrackStartSector + cdrTrack.Sectors - 1;
if(!cdrTrack.Indexes.TryGetValue(0, out dicTrack.TrackStartSector))
cdrTrack.Indexes.TryGetValue(1, out dicTrack.TrackStartSector);
if(cdrTrack.Subchannel)
{
dicTrack.TrackSubchannelType = cdrTrack.Packedsubchannel
? TrackSubchannelType.PackedInterleaved
: TrackSubchannelType.RawInterleaved;
dicTrack.TrackSubchannelFilter = cdrTrack.Trackfile.Datafilter;
dicTrack.TrackSubchannelFile = cdrTrack.Trackfile.Datafilter.GetFilename();
dicTrack.TrackSubchannelOffset = cdrTrack.Trackfile.Offset;
}
else dicTrack.TrackSubchannelType = TrackSubchannelType.None;
tracks.Add(dicTrack);
}
return tracks;
}
}
public List<DumpHardwareType> DumpHardware => null;
public CICMMetadataType CicmMetadata => null;
// TODO: Decode CD-Text to text
public IEnumerable<MediaTagType> SupportedMediaTags => new[] {MediaTagType.CD_MCN};
public IEnumerable<SectorTagType> SupportedSectorTags =>
new[]
{
SectorTagType.CdSectorEcc, SectorTagType.CdSectorEccP, SectorTagType.CdSectorEccQ,
SectorTagType.CdSectorEdc, SectorTagType.CdSectorHeader, SectorTagType.CdSectorSubchannel,
SectorTagType.CdSectorSubHeader, SectorTagType.CdSectorSync, SectorTagType.CdTrackFlags,
SectorTagType.CdTrackIsrc
};
public IEnumerable<MediaType> SupportedMediaTypes =>
new[]
{
MediaType.CD, MediaType.CDDA, MediaType.CDEG, MediaType.CDG, MediaType.CDI, MediaType.CDMIDI,
MediaType.CDMRW, MediaType.CDPLUS, MediaType.CDR, MediaType.CDROM, MediaType.CDROMXA, MediaType.CDRW,
MediaType.CDV, MediaType.DDCD, MediaType.DDCDR, MediaType.DDCDRW, MediaType.JaguarCD, MediaType.MEGACD,
MediaType.PD650, MediaType.PD650_WORM, MediaType.PS1CD, MediaType.PS2CD, MediaType.SuperCDROM2,
MediaType.SVCD, MediaType.SATURNCD, MediaType.ThreeDO, MediaType.VCD, MediaType.VCDHD,
MediaType.NeoGeoCD, MediaType.PCFX
};
public IEnumerable<(string name, Type type, string description)> SupportedOptions =>
new[] {("separate", typeof(bool), "Write each track to a separate file.")};
public IEnumerable<string> KnownExtensions => new[] {".toc"};
public bool IsWriting { get; private set; }
public string ErrorMessage { get; private set; }
}
}

View File

@@ -0,0 +1,139 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Structs.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains structures for cdrdao cuesheets (toc/bin).
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Interfaces;
namespace DiscImageChef.DiscImages
{
public partial class Cdrdao
{
[SuppressMessage("ReSharper", "NotAccessedField.Local")]
struct CdrdaoTrackFile
{
/// <summary>Track #</summary>
public uint Sequence;
/// <summary>Filter of file containing track</summary>
public IFilter Datafilter;
/// <summary>Path of file containing track</summary>
public string Datafile;
/// <summary>Offset of track start in file</summary>
public ulong Offset;
/// <summary>Type of file</summary>
public string Filetype;
}
#pragma warning disable 169
[SuppressMessage("ReSharper", "NotAccessedField.Local")]
struct CdrdaoTrack
{
/// <summary>Track #</summary>
public uint Sequence;
/// <summary>Track title (from CD-Text)</summary>
public string Title;
/// <summary>Track genre (from CD-Text)</summary>
public string Genre;
/// <summary>Track arranger (from CD-Text)</summary>
public string Arranger;
/// <summary>Track composer (from CD-Text)</summary>
public string Composer;
/// <summary>Track performer (from CD-Text)</summary>
public string Performer;
/// <summary>Track song writer (from CD-Text)</summary>
public string Songwriter;
/// <summary>Track ISRC</summary>
public string Isrc;
/// <summary>Disk provider's message (from CD-Text)</summary>
public string Message;
/// <summary>File struct for this track</summary>
public CdrdaoTrackFile Trackfile;
/// <summary>Indexes on this track</summary>
public Dictionary<int, ulong> Indexes;
/// <summary>Track pre-gap in sectors</summary>
public ulong Pregap;
/// <summary>Track post-gap in sectors</summary>
public ulong Postgap;
/// <summary>Digical Copy Permitted</summary>
public bool FlagDcp;
/// <summary>Track is quadraphonic</summary>
public bool Flag_4Ch;
/// <summary>Track has preemphasis</summary>
public bool FlagPre;
/// <summary>Bytes per sector</summary>
public ushort Bps;
/// <summary>Sectors in track</summary>
public ulong Sectors;
/// <summary>Starting sector in track</summary>
public ulong StartSector;
/// <summary>Track type</summary>
public string Tracktype;
public bool Subchannel;
public bool Packedsubchannel;
}
[SuppressMessage("ReSharper", "NotAccessedField.Local")]
struct CdrdaoDisc
{
/// <summary>Disk title (from CD-Text)</summary>
public string Title;
/// <summary>Disk genre (from CD-Text)</summary>
public string Genre;
/// <summary>Disk arranger (from CD-Text)</summary>
public string Arranger;
/// <summary>Disk composer (from CD-Text)</summary>
public string Composer;
/// <summary>Disk performer (from CD-Text)</summary>
public string Performer;
/// <summary>Disk song writer (from CD-Text)</summary>
public string Songwriter;
/// <summary>Disk provider's message (from CD-Text)</summary>
public string Message;
/// <summary>Media catalog number</summary>
public string Mcn;
/// <summary>Disk type</summary>
public MediaType Disktype;
/// <summary>Disk type string</summary>
public string Disktypestr;
/// <summary>Disk CDDB ID</summary>
public string DiskId;
/// <summary>Disk UPC/EAN</summary>
public string Barcode;
/// <summary>Tracks</summary>
public List<CdrdaoTrack> Tracks;
/// <summary>Disk comment</summary>
public string Comment;
}
#pragma warning restore 169
}
}

View File

@@ -0,0 +1,42 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Unsupported.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains features unsupported by cdrdao cuesheets (toc/bin).
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
namespace DiscImageChef.DiscImages
{
public partial class Cdrdao
{
public bool? VerifyMediaImage()
{
return null;
}
}
}

View File

@@ -0,0 +1,698 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Write.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Writes cdrdao cuesheets (toc/bin).
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Structs;
using Schemas;
using TrackType = DiscImageChef.CommonTypes.Enums.TrackType;
namespace DiscImageChef.DiscImages
{
public partial class Cdrdao
{
public bool Create(string path, MediaType mediaType, Dictionary<string, string> options, ulong sectors,
uint sectorSize)
{
if(options != null)
{
if(options.TryGetValue("separate", out string tmpValue))
{
if(!bool.TryParse(tmpValue, out separateTracksWriting))
{
ErrorMessage = "Invalid value for split option";
return false;
}
if(separateTracksWriting)
{
ErrorMessage = "Separate tracksnot yet implemented";
return false;
}
}
}
else separateTracksWriting = false;
if(!SupportedMediaTypes.Contains(mediaType))
{
ErrorMessage = $"Unsupport media format {mediaType}";
return false;
}
imageInfo = new ImageInfo {MediaType = mediaType, SectorSize = sectorSize, Sectors = sectors};
// TODO: Separate tracks
try
{
writingBaseName = Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path));
descriptorStream = new StreamWriter(path, false, Encoding.ASCII);
}
catch(IOException e)
{
ErrorMessage = $"Could not create new image file, exception {e.Message}";
return false;
}
discimage = new CdrdaoDisc {Disktype = mediaType, Tracks = new List<CdrdaoTrack>()};
trackFlags = new Dictionary<byte, byte>();
trackIsrcs = new Dictionary<byte, string>();
IsWriting = true;
ErrorMessage = null;
return true;
}
public bool WriteMediaTag(byte[] data, MediaTagType tag)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
switch(tag)
{
case MediaTagType.CD_MCN:
discimage.Mcn = Encoding.ASCII.GetString(data);
return true;
default:
ErrorMessage = $"Unsupported media tag {tag}";
return false;
}
}
public bool WriteSector(byte[] data, ulong sectorAddress)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
Track track =
writingTracks.FirstOrDefault(trk => sectorAddress >= trk.TrackStartSector &&
sectorAddress <= trk.TrackEndSector);
if(track.TrackSequence == 0)
{
ErrorMessage = $"Can't found track containing {sectorAddress}";
return false;
}
FileStream trackStream = writingStreams.FirstOrDefault(kvp => kvp.Key == track.TrackSequence).Value;
if(trackStream == null)
{
ErrorMessage = $"Can't found file containing {sectorAddress}";
return false;
}
if(track.TrackBytesPerSector != track.TrackRawBytesPerSector)
{
ErrorMessage = "Invalid write mode for this sector";
return false;
}
if(data.Length != track.TrackRawBytesPerSector)
{
ErrorMessage = "Incorrect data size";
return false;
}
// cdrdao audio tracks are endian swapped corresponding to DiscImageChef
if(track.TrackType == TrackType.Audio)
{
byte[] swapped = new byte[data.Length];
for(long i = 0; i < swapped.Length; i += 2)
{
swapped[i] = data[i + 1];
swapped[i + 1] = data[i];
}
data = swapped;
}
trackStream.Seek((long)(track.TrackFileOffset + (sectorAddress - track.TrackStartSector) * (ulong)track.TrackRawBytesPerSector),
SeekOrigin.Begin);
trackStream.Write(data, 0, data.Length);
return true;
}
public bool WriteSectors(byte[] data, ulong sectorAddress, uint length)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
Track track =
writingTracks.FirstOrDefault(trk => sectorAddress >= trk.TrackStartSector &&
sectorAddress <= trk.TrackEndSector);
if(track.TrackSequence == 0)
{
ErrorMessage = $"Can't found track containing {sectorAddress}";
return false;
}
FileStream trackStream = writingStreams.FirstOrDefault(kvp => kvp.Key == track.TrackSequence).Value;
if(trackStream == null)
{
ErrorMessage = $"Can't found file containing {sectorAddress}";
return false;
}
if(track.TrackBytesPerSector != track.TrackRawBytesPerSector)
{
ErrorMessage = "Invalid write mode for this sector";
return false;
}
if(sectorAddress + length > track.TrackEndSector + 1)
{
ErrorMessage = "Can't cross tracks";
return false;
}
if(data.Length % track.TrackRawBytesPerSector != 0)
{
ErrorMessage = "Incorrect data size";
return false;
}
// cdrdao audio tracks are endian swapped corresponding to DiscImageChef
if(track.TrackType == TrackType.Audio)
{
byte[] swapped = new byte[data.Length];
for(long i = 0; i < swapped.Length; i += 2)
{
swapped[i] = data[i + 1];
swapped[i + 1] = data[i];
}
data = swapped;
}
switch(track.TrackSubchannelType)
{
case TrackSubchannelType.None:
trackStream
.Seek((long)(track.TrackFileOffset + (sectorAddress - track.TrackStartSector) * (ulong)track.TrackRawBytesPerSector),
SeekOrigin.Begin);
trackStream.Write(data, 0, data.Length);
ErrorMessage = "";
return true;
case TrackSubchannelType.Raw:
case TrackSubchannelType.RawInterleaved:
trackStream
.Seek((long)(track.TrackFileOffset + (sectorAddress - track.TrackStartSector) * (ulong)(track.TrackRawBytesPerSector + 96)),
SeekOrigin.Begin);
for(uint i = 0; i < length; i++)
{
trackStream.Write(data, (int)(i * track.TrackRawBytesPerSector), track.TrackRawBytesPerSector);
trackStream.Position += 96;
}
ErrorMessage = "";
return true;
default:
ErrorMessage = "Invalid subchannel mode for this sector";
return false;
}
}
public bool WriteSectorLong(byte[] data, ulong sectorAddress)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
Track track =
writingTracks.FirstOrDefault(trk => sectorAddress >= trk.TrackStartSector &&
sectorAddress <= trk.TrackEndSector);
if(track.TrackSequence == 0)
{
ErrorMessage = $"Can't found track containing {sectorAddress}";
return false;
}
FileStream trackStream = writingStreams.FirstOrDefault(kvp => kvp.Key == track.TrackSequence).Value;
if(trackStream == null)
{
ErrorMessage = $"Can't found file containing {sectorAddress}";
return false;
}
if(data.Length != track.TrackRawBytesPerSector)
{
ErrorMessage = "Incorrect data size";
return false;
}
// cdrdao audio tracks are endian swapped corresponding to DiscImageChef
if(track.TrackType == TrackType.Audio)
{
byte[] swapped = new byte[data.Length];
for(long i = 0; i < swapped.Length; i += 2)
{
swapped[i] = data[i + 1];
swapped[i + 1] = data[i];
}
data = swapped;
}
uint subchannelSize = (uint)(track.TrackSubchannelType != TrackSubchannelType.None ? 96 : 0);
trackStream.Seek((long)(track.TrackFileOffset + (sectorAddress - track.TrackStartSector) * (ulong)(track.TrackRawBytesPerSector + subchannelSize)),
SeekOrigin.Begin);
trackStream.Write(data, 0, data.Length);
return true;
}
public bool WriteSectorsLong(byte[] data, ulong sectorAddress, uint length)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
Track track =
writingTracks.FirstOrDefault(trk => sectorAddress >= trk.TrackStartSector &&
sectorAddress <= trk.TrackEndSector);
if(track.TrackSequence == 0)
{
ErrorMessage = $"Can't found track containing {sectorAddress}";
return false;
}
FileStream trackStream = writingStreams.FirstOrDefault(kvp => kvp.Key == track.TrackSequence).Value;
if(trackStream == null)
{
ErrorMessage = $"Can't found file containing {sectorAddress}";
return false;
}
if(sectorAddress + length > track.TrackEndSector + 1)
{
ErrorMessage = "Can't cross tracks";
return false;
}
if(data.Length % track.TrackRawBytesPerSector != 0)
{
ErrorMessage = "Incorrect data size";
return false;
}
// cdrdao audio tracks are endian swapped corresponding to DiscImageChef
if(track.TrackType == TrackType.Audio)
{
byte[] swapped = new byte[data.Length];
for(long i = 0; i < swapped.Length; i += 2)
{
swapped[i] = data[i + 1];
swapped[i + 1] = data[i];
}
data = swapped;
}
uint subchannelSize = (uint)(track.TrackSubchannelType != TrackSubchannelType.None ? 96 : 0);
for(uint i = 0; i < length; i++)
{
trackStream.Seek((long)(track.TrackFileOffset + (i + sectorAddress - track.TrackStartSector) * (ulong)(track.TrackRawBytesPerSector + subchannelSize)),
SeekOrigin.Begin);
trackStream.Write(data, (int)(i * track.TrackRawBytesPerSector), track.TrackRawBytesPerSector);
}
return true;
}
public bool SetTracks(List<Track> tracks)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
if(tracks == null || tracks.Count == 0)
{
ErrorMessage = "Invalid tracks sent";
return false;
}
if(writingTracks != null && writingStreams != null)
foreach(FileStream oldTrack in writingStreams.Select(t => t.Value).Distinct())
oldTrack.Close();
ulong currentOffset = 0;
writingTracks = new List<Track>();
foreach(Track track in tracks.OrderBy(t => t.TrackSequence))
{
if(track.TrackSubchannelType == TrackSubchannelType.Q16 ||
track.TrackSubchannelType == TrackSubchannelType.Q16Interleaved)
{
ErrorMessage =
$"Unsupported subchannel type {track.TrackSubchannelType} for track {track.TrackSequence}";
return false;
}
Track newTrack = track;
newTrack.TrackFile = separateTracksWriting
? writingBaseName + $"_track{track.TrackSequence:D2}.bin"
: writingBaseName + ".bin";
newTrack.TrackFileOffset = separateTracksWriting ? 0 : currentOffset;
writingTracks.Add(newTrack);
currentOffset += (ulong)newTrack.TrackRawBytesPerSector *
(newTrack.TrackEndSector - newTrack.TrackStartSector + 1);
if(track.TrackSubchannelType != TrackSubchannelType.None)
currentOffset += 96 * (newTrack.TrackEndSector - newTrack.TrackStartSector + 1);
}
writingStreams = new Dictionary<uint, FileStream>();
if(separateTracksWriting)
foreach(Track track in writingTracks)
writingStreams.Add(track.TrackSequence,
new FileStream(track.TrackFile, FileMode.OpenOrCreate, FileAccess.ReadWrite,
FileShare.None));
else
{
FileStream jointstream = new FileStream(writingBaseName + ".bin", FileMode.OpenOrCreate,
FileAccess.ReadWrite, FileShare.None);
foreach(Track track in writingTracks) writingStreams.Add(track.TrackSequence, jointstream);
}
return true;
}
public bool Close()
{
if(!IsWriting)
{
ErrorMessage = "Image is not opened for writing";
return false;
}
if(separateTracksWriting)
foreach(FileStream writingStream in writingStreams.Values)
{
writingStream.Flush();
writingStream.Close();
}
else
{
writingStreams.First().Value.Flush();
writingStreams.First().Value.Close();
}
bool data = writingTracks.Count(t => t.TrackType != TrackType.Audio) > 0;
bool mode2 = writingTracks.Count(t => t.TrackType == TrackType.CdMode2Form1 ||
t.TrackType == TrackType.CdMode2Form2 ||
t.TrackType == TrackType.CdMode2Formless) > 0;
if(mode2) descriptorStream.WriteLine("CD_ROM_XA");
else if(data) descriptorStream.WriteLine("CD_ROM");
else descriptorStream.WriteLine("CD_DA");
if(!string.IsNullOrWhiteSpace(discimage.Comment))
{
string[] commentLines = discimage.Comment.Split(new[] {'\n'}, StringSplitOptions.RemoveEmptyEntries);
foreach(string line in commentLines) descriptorStream.WriteLine("// {0}", line);
}
descriptorStream.WriteLine();
if(!string.IsNullOrEmpty(discimage.Mcn)) descriptorStream.WriteLine("CATALOG {0}", discimage.Mcn);
foreach(Track track in writingTracks)
{
descriptorStream.WriteLine();
descriptorStream.WriteLine("// Track {0}", track.TrackSequence);
string subchannelType;
switch(track.TrackSubchannelType)
{
case TrackSubchannelType.Packed:
case TrackSubchannelType.PackedInterleaved:
subchannelType = " RW";
break;
case TrackSubchannelType.Raw:
case TrackSubchannelType.RawInterleaved:
subchannelType = " RW_RAW";
break;
default:
subchannelType = "";
break;
}
descriptorStream.WriteLine("TRACK {0}{1}", GetTrackMode(track), subchannelType);
trackFlags.TryGetValue((byte)track.TrackSequence, out byte flagsByte);
CdFlags flags = (CdFlags)flagsByte;
descriptorStream.WriteLine("{0}COPY", flags.HasFlag(CdFlags.CopyPermitted) ? "" : "NO ");
if(track.TrackType == TrackType.Audio)
{
descriptorStream.WriteLine("{0}PRE_EMPHASIS", flags.HasFlag(CdFlags.PreEmphasis) ? "" : "NO ");
descriptorStream.WriteLine("{0}_CHANNEL_AUDIO",
flags.HasFlag(CdFlags.FourChannel) ? "FOUR" : "TWO");
}
if(trackIsrcs.TryGetValue((byte)track.TrackSequence, out string isrc))
descriptorStream.WriteLine("ISRC {0}", isrc);
(byte minute, byte second, byte frame)
msf = LbaToMsf(track.TrackEndSector - track.TrackStartSector + 1);
descriptorStream.WriteLine("DATAFILE \"{0}\" #{1} {2:D2}:{3:D2}:{4:D2} // length in bytes: {5}",
Path.GetFileName(track.TrackFile), track.TrackFileOffset, msf.minute,
msf.second, msf.frame,
(track.TrackEndSector - track.TrackStartSector + 1) *
(ulong)(track.TrackRawBytesPerSector +
(track.TrackSubchannelType != TrackSubchannelType.None ? 96 : 0)));
descriptorStream.WriteLine();
}
descriptorStream.Flush();
descriptorStream.Close();
IsWriting = false;
ErrorMessage = "";
return true;
}
public bool SetMetadata(ImageInfo metadata)
{
discimage.Barcode = metadata.MediaBarcode;
discimage.Comment = metadata.Comments;
return true;
}
public bool SetGeometry(uint cylinders, uint heads, uint sectorsPerTrack)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool WriteSectorTag(byte[] data, ulong sectorAddress, SectorTagType tag)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
Track track =
writingTracks.FirstOrDefault(trk => sectorAddress >= trk.TrackStartSector &&
sectorAddress <= trk.TrackEndSector);
if(track.TrackSequence == 0)
{
ErrorMessage = $"Can't found track containing {sectorAddress}";
return false;
}
switch(tag)
{
case SectorTagType.CdTrackFlags:
{
if(data.Length != 1)
{
ErrorMessage = "Incorrect data size for track flags";
return false;
}
trackFlags.Add((byte)track.TrackSequence, data[0]);
return true;
}
case SectorTagType.CdTrackIsrc:
{
if(data != null) trackIsrcs.Add((byte)track.TrackSequence, Encoding.UTF8.GetString(data));
return true;
}
case SectorTagType.CdSectorSubchannel:
{
if(track.TrackSubchannelType == 0)
{
ErrorMessage =
$"Trying to write subchannel to track {track.TrackSequence}, that does not have subchannel";
return false;
}
if(data.Length != 96)
{
ErrorMessage = "Incorrect data size for subchannel";
return false;
}
FileStream trackStream = writingStreams.FirstOrDefault(kvp => kvp.Key == track.TrackSequence).Value;
if(trackStream == null)
{
ErrorMessage = $"Can't found file containing {sectorAddress}";
return false;
}
trackStream
.Seek((long)(track.TrackFileOffset + (sectorAddress - track.TrackStartSector) * (ulong)(track.TrackRawBytesPerSector + 96)) + track.TrackRawBytesPerSector,
SeekOrigin.Begin);
trackStream.Write(data, 0, data.Length);
return true;
}
default:
ErrorMessage = $"Unsupported tag type {tag}";
return false;
}
}
public bool WriteSectorsTag(byte[] data, ulong sectorAddress, uint length, SectorTagType tag)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
Track track =
writingTracks.FirstOrDefault(trk => sectorAddress >= trk.TrackStartSector &&
sectorAddress <= trk.TrackEndSector);
if(track.TrackSequence == 0)
{
ErrorMessage = $"Can't found track containing {sectorAddress}";
return false;
}
switch(tag)
{
case SectorTagType.CdTrackFlags:
case SectorTagType.CdTrackIsrc: return WriteSectorTag(data, sectorAddress, tag);
case SectorTagType.CdSectorSubchannel:
{
if(track.TrackSubchannelType == 0)
{
ErrorMessage =
$"Trying to write subchannel to track {track.TrackSequence}, that does not have subchannel";
return false;
}
if(data.Length % 96 != 0)
{
ErrorMessage = "Incorrect data size for subchannel";
return false;
}
FileStream trackStream = writingStreams.FirstOrDefault(kvp => kvp.Key == track.TrackSequence).Value;
if(trackStream == null)
{
ErrorMessage = $"Can't found file containing {sectorAddress}";
return false;
}
for(uint i = 0; i < length; i++)
{
trackStream
.Seek((long)(track.TrackFileOffset + (i + sectorAddress - track.TrackStartSector) * (ulong)(track.TrackRawBytesPerSector + 96)) + track.TrackRawBytesPerSector,
SeekOrigin.Begin);
trackStream.Write(data, (int)(i * 96), 96);
}
return true;
}
default:
ErrorMessage = $"Unsupported tag type {tag}";
return false;
}
}
public bool SetDumpHardware(List<DumpHardwareType> dumpHardware)
{
// Not supported
return false;
}
public bool SetCicmMetadata(CICMMetadataType metadata)
{
// Not supported
return false;
}
}
}

View File

@@ -0,0 +1,83 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : CDRWin.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disc image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Manages CDRWin cuesheets (cue/bin).
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Collections.Generic;
using System.IO;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
namespace DiscImageChef.DiscImages
{
// TODO: Implement track flags
public partial class CdrWin : IWritableImage
{
IFilter cdrwinFilter;
StreamReader cueStream;
StreamWriter descriptorStream;
CdrWinDisc discimage;
ImageInfo imageInfo;
Stream imageStream;
/// <summary>Dictionary, index is track #, value is TrackFile</summary>
Dictionary<uint, ulong> offsetmap;
bool separateTracksWriting;
Dictionary<byte, byte> trackFlags;
Dictionary<byte, string> trackIsrcs;
string writingBaseName;
Dictionary<uint, FileStream> writingStreams;
List<Track> writingTracks;
public CdrWin()
{
imageInfo = new ImageInfo
{
ReadableSectorTags = new List<SectorTagType>(),
ReadableMediaTags = new List<MediaTagType>(),
HasPartitions = true,
HasSessions = true,
Version = null,
ApplicationVersion = null,
MediaTitle = null,
Creator = null,
MediaManufacturer = null,
MediaModel = null,
MediaPartNumber = null,
MediaSequence = 0,
LastMediaSequence = 0,
DriveManufacturer = null,
DriveModel = null,
DriveSerialNumber = null,
DriveFirmwareRevision = null
};
}
}
}

View File

@@ -0,0 +1,161 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Constants.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains constants for CDRWin cuesheets (cue/bin).
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
namespace DiscImageChef.DiscImages
{
public partial class CdrWin
{
// Type for FILE entity
/// <summary>Data as-is in little-endian</summary>
const string CDRWIN_DISK_TYPE_LITTLE_ENDIAN = "BINARY";
/// <summary>Data as-is in big-endian</summary>
const string CDRWIN_DISK_TYPE_BIG_ENDIAN = "MOTOROLA";
/// <summary>Audio in Apple AIF file</summary>
const string CDRWIN_DISK_TYPE_AIFF = "AIFF";
/// <summary>Audio in Microsoft WAV file</summary>
const string CDRWIN_DISK_TYPE_RIFF = "WAVE";
/// <summary>Audio in MP3 file</summary>
const string CDRWIN_DISK_TYPE_MP3 = "MP3";
// Type for TRACK entity
/// <summary>Audio track, 2352 bytes/sector</summary>
const string CDRWIN_TRACK_TYPE_AUDIO = "AUDIO";
/// <summary>CD+G track, 2448 bytes/sector (audio+subchannel)</summary>
const string CDRWIN_TRACK_TYPE_CDG = "CDG";
/// <summary>Mode 1 track, cooked, 2048 bytes/sector</summary>
const string CDRWIN_TRACK_TYPE_MODE1 = "MODE1/2048";
/// <summary>Mode 1 track, raw, 2352 bytes/sector</summary>
const string CDRWIN_TRACK_TYPE_MODE1_RAW = "MODE1/2352";
/// <summary>Mode 2 form 1 track, cooked, 2048 bytes/sector</summary>
const string CDRWIN_TRACK_TYPE_MODE2_FORM1 = "MODE2/2048";
/// <summary>Mode 2 form 2 track, cooked, 2324 bytes/sector</summary>
const string CDRWIN_TRACK_TYPE_MODE2_FORM2 = "MODE2/2324";
/// <summary>Mode 2 formless track, cooked, 2336 bytes/sector</summary>
const string CDRWIN_TRACK_TYPE_MODE2_FORMLESS = "MODE2/2336";
/// <summary>Mode 2 track, raw, 2352 bytes/sector</summary>
const string CDRWIN_TRACK_TYPE_MODE2_RAW = "MODE2/2352";
/// <summary>CD-i track, cooked, 2336 bytes/sector</summary>
const string CDRWIN_TRACK_TYPE_CDI = "CDI/2336";
/// <summary>CD-i track, raw, 2352 bytes/sector</summary>
const string CDRWIN_TRACK_TYPE_CDI_RAW = "CDI/2352";
// Type for REM ORIGINAL MEDIA-TYPE entity
/// <summary>DiskType.CD</summary>
const string CDRWIN_DISK_TYPE_CD = "CD";
/// <summary>DiskType.CDRW</summary>
const string CDRWIN_DISK_TYPE_CDRW = "CD-RW";
/// <summary>DiskType.CDMRW</summary>
const string CDRWIN_DISK_TYPE_CDMRW = "CD-MRW";
/// <summary>DiskType.CDMRW</summary>
const string CDRWIN_DISK_TYPE_CDMRW2 = "CD-(MRW)";
/// <summary>DiskType.DVDROM</summary>
const string CDRWIN_DISK_TYPE_DVD = "DVD";
/// <summary>DiskType.DVDPRW</summary>
const string CDRWIN_DISK_TYPE_DVDPMRW = "DVD+MRW";
/// <summary>DiskType.DVDPRW</summary>
const string CDRWIN_DISK_TYPE_DVDPMRW2 = "DVD+(MRW)";
/// <summary>DiskType.DVDPRWDL</summary>
const string CDRWIN_DISK_TYPE_DVDPMRWDL = "DVD+MRW DL";
/// <summary>DiskType.DVDPRWDL</summary>
const string CDRWIN_DISK_TYPE_DVDPMRWDL2 = "DVD+(MRW) DL";
/// <summary>DiskType.DVDPR</summary>
const string CDRWIN_DISK_TYPE_DVDPR = "DVD+R";
/// <summary>DiskType.DVDPRDL</summary>
const string CDRWIN_DISK_TYPE_DVDPRDL = "DVD+R DL";
/// <summary>DiskType.DVDPRW</summary>
const string CDRWIN_DISK_TYPE_DVDPRW = "DVD+RW";
/// <summary>DiskType.DVDPRWDL</summary>
const string CDRWIN_DISK_TYPE_DVDPRWDL = "DVD+RW DL";
/// <summary>DiskType.DVDPR</summary>
const string CDRWIN_DISK_TYPE_DVDPVR = "DVD+VR";
/// <summary>DiskType.DVDRAM</summary>
const string CDRWIN_DISK_TYPE_DVDRAM = "DVD-RAM";
/// <summary>DiskType.DVDR</summary>
const string CDRWIN_DISK_TYPE_DVDR = "DVD-R";
/// <summary>DiskType.DVDRDL</summary>
const string CDRWIN_DISK_TYPE_DVDRDL = "DVD-R DL";
/// <summary>DiskType.DVDRW</summary>
const string CDRWIN_DISK_TYPE_DVDRW = "DVD-RW";
/// <summary>DiskType.DVDRWDL</summary>
const string CDRWIN_DISK_TYPE_DVDRWDL = "DVD-RW DL";
/// <summary>DiskType.DVDR</summary>
const string CDRWIN_DISK_TYPE_DVDVR = "DVD-VR";
/// <summary>DiskType.DVDRW</summary>
const string CDRWIN_DISK_TYPE_DVDRW2 = "DVDRW";
/// <summary>DiskType.HDDVDROM</summary>
const string CDRWIN_DISK_TYPE_HDDVD = "HD DVD";
/// <summary>DiskType.HDDVDRAM</summary>
const string CDRWIN_DISK_TYPE_HDDVDRAM = "HD DVD-RAM";
/// <summary>DiskType.HDDVDR</summary>
const string CDRWIN_DISK_TYPE_HDDVDR = "HD DVD-R";
/// <summary>DiskType.HDDVDR</summary>
const string CDRWIN_DISK_TYPE_HDDVDRDL = "HD DVD-R DL";
/// <summary>DiskType.HDDVDRW</summary>
const string CDRWIN_DISK_TYPE_HDDVDRW = "HD DVD-RW";
/// <summary>DiskType.HDDVDRW</summary>
const string CDRWIN_DISK_TYPE_HDDVDRWDL = "HD DVD-RW DL";
/// <summary>DiskType.BDROM</summary>
const string CDRWIN_DISK_TYPE_BD = "BD";
/// <summary>DiskType.BDR</summary>
const string CDRWIN_DISK_TYPE_BDR = "BD-R";
/// <summary>DiskType.BDRE</summary>
const string CDRWIN_DISK_TYPE_BDRE = "BD-RE";
/// <summary>DiskType.BDR</summary>
const string CDRWIN_DISK_TYPE_BDRDL = "BD-R DL";
/// <summary>DiskType.BDRE</summary>
const string CDRWIN_DISK_TYPE_BDREDL = "BD-RE DL";
const string REGEX_SESSION = @"\bREM\s+SESSION\s+(?<number>\d+).*$";
const string REGEX_MEDIA_TYPE = @"\bREM\s+ORIGINAL MEDIA-TYPE:\s+(?<mediatype>.+)$";
const string REGEX_LEAD_OUT = @"\bREM\s+LEAD-OUT\s+(?<msf>[\d]+:[\d]+:[\d]+)$";
// Not checked
const string REGEX_LBA = @"\bREM MSF:\s+(?<msf>[\d]+:[\d]+:[\d]+)\s+=\s+LBA:\s+(?<lba>[\d]+)$";
const string REGEX_DISC_ID = @"\bDISC_ID\s+(?<diskid>[\da-f]{8})$";
const string REGEX_BARCODE = @"\bUPC_EAN\s+(?<barcode>[\d]{12,13})$";
const string REGEX_COMMENT = @"\bREM\s+(?<comment>.+)$";
const string REGEX_CDTEXT = @"\bCDTEXTFILE\s+(?<filename>.+)$";
const string REGEX_MCN = @"^\s*CATALOG\s*(?<catalog>[\x21-\x7F]{13})$";
const string REGEX_TITLE = @"\bTITLE\s+(?<title>.+)$";
const string REGEX_GENRE = @"\bGENRE\s+(?<genre>.+)$";
const string REGEX_ARRANGER = @"\bARRANGER\s+(?<arranger>.+)$";
const string REGEX_COMPOSER = @"\bCOMPOSER\s+(?<composer>.+)$";
const string REGEX_PERFORMER = @"\bPERFORMER\s+(?<performer>.+)$";
const string REGEX_SONGWRITER = @"\bSONGWRITER\s+(?<songwriter>.+)$";
const string REGEX_FILE = @"\bFILE\s+(?<filename>.+)\s+(?<type>\S+)$";
const string REGEX_TRACK = @"\bTRACK\s+(?<number>\d+)\s+(?<type>\S+)$";
const string REGEX_ISRC = @"\bISRC\s+(?<isrc>\w{12})$";
const string REGEX_INDEX = @"\bINDEX\s+(?<index>\d+)\s+(?<msf>[\d]+:[\d]+:[\d]+)$";
const string REGEX_PREGAP = @"\bPREGAP\s+(?<msf>[\d]+:[\d]+:[\d]+)$";
const string REGEX_POSTGAP = @"\bPOSTGAP\s+(?<msf>[\d]+:[\d]+:[\d]+)$";
const string REGEX_FLAGS = @"\bFLAGS\s+(((?<dcp>DCP)|(?<quad>4CH)|(?<pre>PRE)|(?<scms>SCMS))\s*)+$";
}
}

View File

@@ -0,0 +1,242 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Helpers.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains helpers for CDRWin cuesheets (cue/bin).
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Structs;
namespace DiscImageChef.DiscImages
{
public partial class CdrWin
{
static ulong CdrWinMsftoLba(string msf)
{
string[] msfElements = msf.Split(':');
ulong minute = ulong.Parse(msfElements[0]);
ulong second = ulong.Parse(msfElements[1]);
ulong frame = ulong.Parse(msfElements[2]);
ulong sectors = minute * 60 * 75 + second * 75 + frame;
return sectors;
}
static ushort CdrWinTrackTypeToBytesPerSector(string trackType)
{
switch(trackType)
{
case CDRWIN_TRACK_TYPE_MODE1:
case CDRWIN_TRACK_TYPE_MODE2_FORM1: return 2048;
case CDRWIN_TRACK_TYPE_MODE2_FORM2: return 2324;
case CDRWIN_TRACK_TYPE_MODE2_FORMLESS:
case CDRWIN_TRACK_TYPE_CDI: return 2336;
case CDRWIN_TRACK_TYPE_AUDIO:
case CDRWIN_TRACK_TYPE_MODE1_RAW:
case CDRWIN_TRACK_TYPE_MODE2_RAW:
case CDRWIN_TRACK_TYPE_CDG:
case CDRWIN_TRACK_TYPE_CDI_RAW: return 2352;
default: return 0;
}
}
static ushort CdrWinTrackTypeToCookedBytesPerSector(string trackType)
{
switch(trackType)
{
case CDRWIN_TRACK_TYPE_MODE1:
case CDRWIN_TRACK_TYPE_MODE2_FORM1:
case CDRWIN_TRACK_TYPE_MODE1_RAW: return 2048;
case CDRWIN_TRACK_TYPE_MODE2_FORM2: return 2324;
case CDRWIN_TRACK_TYPE_MODE2_FORMLESS:
case CDRWIN_TRACK_TYPE_CDI:
case CDRWIN_TRACK_TYPE_MODE2_RAW:
case CDRWIN_TRACK_TYPE_CDI_RAW: return 2336;
case CDRWIN_TRACK_TYPE_CDG:
case CDRWIN_TRACK_TYPE_AUDIO: return 2352;
default: return 0;
}
}
static TrackType CdrWinTrackTypeToTrackType(string trackType)
{
switch(trackType)
{
case CDRWIN_TRACK_TYPE_MODE1:
case CDRWIN_TRACK_TYPE_MODE1_RAW: return TrackType.CdMode1;
case CDRWIN_TRACK_TYPE_MODE2_FORM1: return TrackType.CdMode2Form1;
case CDRWIN_TRACK_TYPE_MODE2_FORM2: return TrackType.CdMode2Form2;
case CDRWIN_TRACK_TYPE_CDI_RAW:
case CDRWIN_TRACK_TYPE_CDI:
case CDRWIN_TRACK_TYPE_MODE2_RAW:
case CDRWIN_TRACK_TYPE_MODE2_FORMLESS: return TrackType.CdMode2Formless;
case CDRWIN_TRACK_TYPE_AUDIO:
case CDRWIN_TRACK_TYPE_CDG: return TrackType.Audio;
default: return TrackType.Data;
}
}
static MediaType CdrWinIsoBusterDiscTypeToMediaType(string discType)
{
switch(discType)
{
case CDRWIN_DISK_TYPE_CD: return MediaType.CD;
case CDRWIN_DISK_TYPE_CDRW:
case CDRWIN_DISK_TYPE_CDMRW:
case CDRWIN_DISK_TYPE_CDMRW2: return MediaType.CDRW;
case CDRWIN_DISK_TYPE_DVD: return MediaType.DVDROM;
case CDRWIN_DISK_TYPE_DVDPRW:
case CDRWIN_DISK_TYPE_DVDPMRW:
case CDRWIN_DISK_TYPE_DVDPMRW2: return MediaType.DVDPRW;
case CDRWIN_DISK_TYPE_DVDPRWDL:
case CDRWIN_DISK_TYPE_DVDPMRWDL:
case CDRWIN_DISK_TYPE_DVDPMRWDL2: return MediaType.DVDPRWDL;
case CDRWIN_DISK_TYPE_DVDPR:
case CDRWIN_DISK_TYPE_DVDPVR: return MediaType.DVDPR;
case CDRWIN_DISK_TYPE_DVDPRDL: return MediaType.DVDPRDL;
case CDRWIN_DISK_TYPE_DVDRAM: return MediaType.DVDRAM;
case CDRWIN_DISK_TYPE_DVDVR:
case CDRWIN_DISK_TYPE_DVDR: return MediaType.DVDR;
case CDRWIN_DISK_TYPE_DVDRDL: return MediaType.DVDRDL;
case CDRWIN_DISK_TYPE_DVDRW:
case CDRWIN_DISK_TYPE_DVDRWDL:
case CDRWIN_DISK_TYPE_DVDRW2: return MediaType.DVDRW;
case CDRWIN_DISK_TYPE_HDDVD: return MediaType.HDDVDROM;
case CDRWIN_DISK_TYPE_HDDVDRAM: return MediaType.HDDVDRAM;
case CDRWIN_DISK_TYPE_HDDVDR:
case CDRWIN_DISK_TYPE_HDDVDRDL: return MediaType.HDDVDR;
case CDRWIN_DISK_TYPE_HDDVDRW:
case CDRWIN_DISK_TYPE_HDDVDRWDL: return MediaType.HDDVDRW;
case CDRWIN_DISK_TYPE_BD: return MediaType.BDROM;
case CDRWIN_DISK_TYPE_BDR:
case CDRWIN_DISK_TYPE_BDRDL: return MediaType.BDR;
case CDRWIN_DISK_TYPE_BDRE:
case CDRWIN_DISK_TYPE_BDREDL: return MediaType.BDRE;
default: return MediaType.Unknown;
}
}
static (byte minute, byte second, byte frame) LbaToMsf(ulong sector)
{
return ((byte)(sector / 75 / 60), (byte)(sector / 75 % 60), (byte)(sector % 75));
}
static string GetTrackMode(Track track)
{
switch(track.TrackType)
{
case TrackType.Audio when track.TrackRawBytesPerSector == 2352: return CDRWIN_TRACK_TYPE_AUDIO;
case TrackType.Data: return CDRWIN_TRACK_TYPE_MODE1;
case TrackType.CdMode1 when track.TrackRawBytesPerSector == 2352: return CDRWIN_TRACK_TYPE_MODE1_RAW;
case TrackType.CdMode2Formless
when track.TrackRawBytesPerSector != 2352: return CDRWIN_TRACK_TYPE_MODE2_FORMLESS;
case TrackType.CdMode2Form1
when track.TrackRawBytesPerSector != 2352: return CDRWIN_TRACK_TYPE_MODE2_FORM1;
case TrackType.CdMode2Form2
when track.TrackRawBytesPerSector != 2352: return CDRWIN_TRACK_TYPE_MODE2_FORM2;
case TrackType.CdMode2Formless when track.TrackRawBytesPerSector == 2352:
case TrackType.CdMode2Form1 when track.TrackRawBytesPerSector == 2352:
case TrackType.CdMode2Form2
when track.TrackRawBytesPerSector == 2352: return CDRWIN_TRACK_TYPE_MODE2_RAW;
default: return CDRWIN_TRACK_TYPE_MODE1;
}
}
static string MediaTypeToCdrwinType(MediaType type)
{
switch(type)
{
case MediaType.BDRXL:
case MediaType.BDR: return CDRWIN_DISK_TYPE_BDR;
case MediaType.BDREXL:
case MediaType.BDRE: return CDRWIN_DISK_TYPE_BDRE;
case MediaType.BDROM:
case MediaType.CBHD:
case MediaType.PS3BD:
case MediaType.PS4BD:
case MediaType.UDO:
case MediaType.UDO2:
case MediaType.UDO2_WORM: return CDRWIN_DISK_TYPE_BD;
case MediaType.CDV:
case MediaType.DDCD:
case MediaType.DDCDR:
case MediaType.DDCDRW:
case MediaType.CDPLUS:
case MediaType.CDR:
case MediaType.CDROM:
case MediaType.CDROMXA:
case MediaType.CD:
case MediaType.CDDA:
case MediaType.CDEG:
case MediaType.CDG:
case MediaType.CDI:
case MediaType.CDMIDI:
case MediaType.DTSCD:
case MediaType.JaguarCD:
case MediaType.MEGACD:
case MediaType.PD650:
case MediaType.PD650_WORM:
case MediaType.PS1CD:
case MediaType.PS2CD:
case MediaType.SuperCDROM2:
case MediaType.SVCD:
case MediaType.SVOD:
case MediaType.SATURNCD:
case MediaType.ThreeDO:
case MediaType.VCD:
case MediaType.VCDHD: return CDRWIN_DISK_TYPE_CD;
case MediaType.CDMRW: return CDRWIN_DISK_TYPE_CDMRW;
case MediaType.CDRW: return CDRWIN_DISK_TYPE_CDRW;
case MediaType.DVDPR: return CDRWIN_DISK_TYPE_DVDPR;
case MediaType.DVDPRDL: return CDRWIN_DISK_TYPE_DVDPRDL;
case MediaType.DVDPRW: return CDRWIN_DISK_TYPE_DVDPRW;
case MediaType.DVDPRWDL: return CDRWIN_DISK_TYPE_DVDPRWDL;
case MediaType.DVDR: return CDRWIN_DISK_TYPE_DVDR;
case MediaType.DVDRAM: return CDRWIN_DISK_TYPE_DVDRAM;
case MediaType.DVDRDL: return CDRWIN_DISK_TYPE_DVDRDL;
case MediaType.DVDDownload:
case MediaType.DVDROM:
case MediaType.UMD:
case MediaType.PS2DVD:
case MediaType.PS3DVD: return CDRWIN_DISK_TYPE_DVD;
case MediaType.DVDRW: return CDRWIN_DISK_TYPE_DVDRW;
case MediaType.DVDRWDL: return CDRWIN_DISK_TYPE_DVDRWDL;
case MediaType.HDDVDR: return CDRWIN_DISK_TYPE_HDDVDR;
case MediaType.HDDVDRAM: return CDRWIN_DISK_TYPE_HDDVDRAM;
case MediaType.HDDVDRDL: return CDRWIN_DISK_TYPE_HDDVDRDL;
case MediaType.HDDVDROM: return CDRWIN_DISK_TYPE_HDDVD;
case MediaType.HDDVDRW: return CDRWIN_DISK_TYPE_HDDVDRW;
case MediaType.HDDVDRWDL: return CDRWIN_DISK_TYPE_HDDVDRWDL;
default: return "";
}
}
}
}

View File

@@ -0,0 +1,105 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Identify.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Identifies CDRWin cuesheets (cue/bin).
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.IO;
using System.Text.RegularExpressions;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.Console;
namespace DiscImageChef.DiscImages
{
public partial class CdrWin
{
// Due to .cue format, this method must parse whole file, ignoring errors (those will be thrown by OpenImage()).
public bool Identify(IFilter imageFilter)
{
cdrwinFilter = imageFilter;
try
{
imageFilter.GetDataForkStream().Seek(0, SeekOrigin.Begin);
byte[] testArray = new byte[512];
imageFilter.GetDataForkStream().Read(testArray, 0, 512);
imageFilter.GetDataForkStream().Seek(0, SeekOrigin.Begin);
// Check for unexpected control characters that shouldn't be present in a text file and can crash this plugin
bool twoConsecutiveNulls = false;
for(int i = 0; i < 512; i++)
{
if(i >= imageFilter.GetDataForkStream().Length) break;
if(testArray[i] == 0)
{
if(twoConsecutiveNulls) return false;
twoConsecutiveNulls = true;
}
else twoConsecutiveNulls = false;
if(testArray[i] < 0x20 && testArray[i] != 0x0A && testArray[i] != 0x0D && testArray[i] != 0x00)
return false;
}
cueStream = new StreamReader(cdrwinFilter.GetDataForkStream());
while(cueStream.Peek() >= 0)
{
string line = cueStream.ReadLine();
Regex sr = new Regex(REGEX_SESSION);
Regex rr = new Regex(REGEX_COMMENT);
Regex cr = new Regex(REGEX_MCN);
Regex fr = new Regex(REGEX_FILE);
Regex tr = new Regex(REGEX_CDTEXT);
// First line must be SESSION, REM, CATALOG, FILE or CDTEXTFILE.
Match sm = sr.Match(line ?? throw new InvalidOperationException());
Match rm = rr.Match(line);
Match cm = cr.Match(line);
Match fm = fr.Match(line);
Match tm = tr.Match(line);
return sm.Success || rm.Success || cm.Success || fm.Success || tm.Success;
}
return false;
}
catch(Exception ex)
{
DicConsole.ErrorWriteLine("Exception trying to identify image file {0}", cdrwinFilter);
DicConsole.ErrorWriteLine("Exception: {0}", ex.Message);
DicConsole.ErrorWriteLine("Stack trace: {0}", ex.StackTrace);
return false;
}
}
}
}

View File

@@ -0,0 +1,129 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Properties.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains properties for CDRWin cuesheets (cue/bin).
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Structs;
using Schemas;
namespace DiscImageChef.DiscImages
{
public partial class CdrWin
{
public ImageInfo Info => imageInfo;
public string Name => "CDRWin cuesheet";
public Guid Id => new Guid("664568B2-15D4-4E64-8A7A-20BDA8B8386F");
public string Format => "CDRWin CUESheet";
public List<Partition> Partitions { get; private set; }
public List<Track> Tracks
{
get
{
List<Track> tracks = new List<Track>();
ulong previousStartSector = 0;
foreach(CdrWinTrack cdrTrack in discimage.Tracks)
{
Track dicTrack = new Track
{
Indexes = cdrTrack.Indexes,
TrackDescription = cdrTrack.Title,
TrackStartSector = previousStartSector,
TrackPregap = cdrTrack.Pregap,
TrackSession = cdrTrack.Session,
TrackSequence = cdrTrack.Sequence,
TrackType = CdrWinTrackTypeToTrackType(cdrTrack.Tracktype),
TrackFile = cdrTrack.Trackfile.Datafilter.GetFilename(),
TrackFilter = cdrTrack.Trackfile.Datafilter,
TrackFileOffset = cdrTrack.Trackfile.Offset,
TrackFileType = cdrTrack.Trackfile.Filetype,
TrackRawBytesPerSector = cdrTrack.Bps,
TrackBytesPerSector = CdrWinTrackTypeToCookedBytesPerSector(cdrTrack.Tracktype)
};
dicTrack.TrackEndSector = dicTrack.TrackStartSector + cdrTrack.Sectors - 1;
/*if(!cdrTrack.Indexes.TryGetValue(0, out dicTrack.TrackStartSector))
cdrTrack.Indexes.TryGetValue(1, out dicTrack.TrackStartSector);*/
if(cdrTrack.Tracktype == CDRWIN_TRACK_TYPE_CDG)
{
dicTrack.TrackSubchannelFilter = cdrTrack.Trackfile.Datafilter;
dicTrack.TrackSubchannelFile = cdrTrack.Trackfile.Datafilter.GetFilename();
dicTrack.TrackSubchannelOffset = cdrTrack.Trackfile.Offset;
dicTrack.TrackSubchannelType = TrackSubchannelType.RawInterleaved;
}
else dicTrack.TrackSubchannelType = TrackSubchannelType.None;
tracks.Add(dicTrack);
previousStartSector = dicTrack.TrackEndSector + 1;
}
return tracks;
}
}
public List<Session> Sessions => discimage.Sessions;
public List<DumpHardwareType> DumpHardware => null;
public CICMMetadataType CicmMetadata => null;
public IEnumerable<MediaTagType> SupportedMediaTags => new[] {MediaTagType.CD_MCN, MediaTagType.CD_TEXT};
public IEnumerable<SectorTagType> SupportedSectorTags =>
new[]
{
SectorTagType.CdSectorEcc, SectorTagType.CdSectorEccP, SectorTagType.CdSectorEccQ,
SectorTagType.CdSectorEdc, SectorTagType.CdSectorHeader, SectorTagType.CdSectorSubHeader,
SectorTagType.CdSectorSync, SectorTagType.CdTrackFlags, SectorTagType.CdTrackIsrc
};
public IEnumerable<MediaType> SupportedMediaTypes =>
new[]
{
MediaType.BDR, MediaType.BDRE, MediaType.BDREXL, MediaType.BDROM, MediaType.BDRXL, MediaType.CBHD,
MediaType.CD, MediaType.CDDA, MediaType.CDEG, MediaType.CDG, MediaType.CDI, MediaType.CDMIDI,
MediaType.CDMRW, MediaType.CDPLUS, MediaType.CDR, MediaType.CDROM, MediaType.CDROMXA, MediaType.CDRW,
MediaType.CDV, MediaType.DDCD, MediaType.DDCDR, MediaType.DDCDRW, MediaType.DVDDownload,
MediaType.DVDPR, MediaType.DVDPRDL, MediaType.DVDPRW, MediaType.DVDPRWDL, MediaType.DVDR,
MediaType.DVDRAM, MediaType.DVDRDL, MediaType.DVDROM, MediaType.DVDRW, MediaType.DVDRWDL, MediaType.EVD,
MediaType.FDDVD, MediaType.DTSCD, MediaType.FVD, MediaType.HDDVDR, MediaType.HDDVDRAM,
MediaType.HDDVDRDL, MediaType.HDDVDROM, MediaType.HDDVDRW, MediaType.HDDVDRWDL, MediaType.HDVMD,
MediaType.HVD, MediaType.JaguarCD, MediaType.MEGACD, MediaType.PD650, MediaType.PD650_WORM,
MediaType.PS1CD, MediaType.PS2CD, MediaType.PS2DVD, MediaType.PS3BD, MediaType.PS3DVD, MediaType.PS4BD,
MediaType.SuperCDROM2, MediaType.SVCD, MediaType.SVOD, MediaType.SATURNCD, MediaType.ThreeDO,
MediaType.UDO, MediaType.UDO2, MediaType.UDO2_WORM, MediaType.UMD, MediaType.VCD, MediaType.VCDHD,
MediaType.NeoGeoCD, MediaType.PCFX
};
public IEnumerable<(string name, Type type, string description)> SupportedOptions =>
new[] {("separate", typeof(bool), "Write each track to a separate file.")};
public IEnumerable<string> KnownExtensions => new[] {".cue"};
public bool IsWriting { get; private set; }
public string ErrorMessage { get; private set; }
}
}

View File

@@ -0,0 +1,132 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Structs.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains structures for CDRWin cuesheets (cue/bin).
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Collections.Generic;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
namespace DiscImageChef.DiscImages
{
public partial class CdrWin
{
struct CdrWinTrackFile
{
/// <summary>Track #</summary>
public uint Sequence;
/// <summary>Filter of file containing track</summary>
public IFilter Datafilter;
/// <summary>Offset of track start in file</summary>
public ulong Offset;
/// <summary>Type of file</summary>
public string Filetype;
}
struct CdrWinTrack
{
/// <summary>Track #</summary>
public uint Sequence;
/// <summary>Track title (from CD-Text)</summary>
public string Title;
/// <summary>Track genre (from CD-Text)</summary>
public string Genre;
/// <summary>Track arranger (from CD-Text)</summary>
public string Arranger;
/// <summary>Track composer (from CD-Text)</summary>
public string Composer;
/// <summary>Track performer (from CD-Text)</summary>
public string Performer;
/// <summary>Track song writer (from CD-Text)</summary>
public string Songwriter;
/// <summary>Track ISRC</summary>
public string Isrc;
/// <summary>File struct for this track</summary>
public CdrWinTrackFile Trackfile;
/// <summary>Indexes on this track</summary>
public Dictionary<int, ulong> Indexes;
/// <summary>Track pre-gap in sectors</summary>
public ulong Pregap;
/// <summary>Track post-gap in sectors</summary>
public ulong Postgap;
/// <summary>Digical Copy Permitted</summary>
public bool FlagDcp;
/// <summary>Track is quadraphonic</summary>
public bool Flag4ch;
/// <summary>Track has preemphasis</summary>
public bool FlagPre;
/// <summary>Track has SCMS</summary>
public bool FlagScms;
/// <summary>Bytes per sector</summary>
public ushort Bps;
/// <summary>Sectors in track</summary>
public ulong Sectors;
/// <summary>Track type</summary>
public string Tracktype;
/// <summary>Track session</summary>
public ushort Session;
}
struct CdrWinDisc
{
/// <summary>Disk title (from CD-Text)</summary>
public string Title;
/// <summary>Disk genre (from CD-Text)</summary>
public string Genre;
/// <summary>Disk arranger (from CD-Text)</summary>
public string Arranger;
/// <summary>Disk composer (from CD-Text)</summary>
public string Composer;
/// <summary>Disk performer (from CD-Text)</summary>
public string Performer;
/// <summary>Disk song writer (from CD-Text)</summary>
public string Songwriter;
/// <summary>Media catalog number</summary>
public string Mcn;
/// <summary>Disk type</summary>
public MediaType Disktype;
/// <summary>Disk type string</summary>
public string Disktypestr;
/// <summary>Disk CDDB ID</summary>
public string DiskId;
/// <summary>Disk UPC/EAN</summary>
public string Barcode;
/// <summary>Sessions</summary>
public List<Session> Sessions;
/// <summary>Tracks</summary>
public List<CdrWinTrack> Tracks;
/// <summary>Disk comment</summary>
public string Comment;
/// <summary>File containing CD-Text</summary>
public string Cdtextfile;
}
}
}

View File

@@ -0,0 +1,42 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Unsupported.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains features unsupported by CDRWin cuesheets (cue/bin).
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
namespace DiscImageChef.DiscImages
{
public partial class CdrWin
{
public bool? VerifyMediaImage()
{
return null;
}
}
}

View File

@@ -0,0 +1,508 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Write.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Writes CDRWin cuesheets (cue/bin).
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Structs;
using Schemas;
namespace DiscImageChef.DiscImages
{
public partial class CdrWin
{
public bool Create(string path, MediaType mediaType, Dictionary<string, string> options, ulong sectors,
uint sectorSize)
{
if(options != null)
{
if(options.TryGetValue("separate", out string tmpValue))
{
if(!bool.TryParse(tmpValue, out separateTracksWriting))
{
ErrorMessage = "Invalid value for split option";
return false;
}
if(separateTracksWriting)
{
ErrorMessage = "Separate tracksnot yet implemented";
return false;
}
}
}
else separateTracksWriting = false;
if(!SupportedMediaTypes.Contains(mediaType))
{
ErrorMessage = $"Unsupport media format {mediaType}";
return false;
}
imageInfo = new ImageInfo {MediaType = mediaType, SectorSize = sectorSize, Sectors = sectors};
// TODO: Separate tracks
try
{
writingBaseName = Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path));
descriptorStream = new StreamWriter(path, false, Encoding.ASCII);
}
catch(IOException e)
{
ErrorMessage = $"Could not create new image file, exception {e.Message}";
return false;
}
discimage = new CdrWinDisc
{
Disktype = mediaType,
Sessions = new List<Session>(),
Tracks = new List<CdrWinTrack>()
};
trackFlags = new Dictionary<byte, byte>();
trackIsrcs = new Dictionary<byte, string>();
IsWriting = true;
ErrorMessage = null;
return true;
}
public bool WriteMediaTag(byte[] data, MediaTagType tag)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
switch(tag)
{
case MediaTagType.CD_MCN:
discimage.Mcn = Encoding.ASCII.GetString(data);
return true;
case MediaTagType.CD_TEXT:
FileStream cdTextStream = new FileStream(writingBaseName + "_cdtext.bin", FileMode.Create,
FileAccess.ReadWrite, FileShare.None);
cdTextStream.Write(data, 0, data.Length);
discimage.Cdtextfile = Path.GetFileName(cdTextStream.Name);
cdTextStream.Close();
return true;
default:
ErrorMessage = $"Unsupported media tag {tag}";
return false;
}
}
public bool WriteSector(byte[] data, ulong sectorAddress)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
Track track =
writingTracks.FirstOrDefault(trk => sectorAddress >= trk.TrackStartSector &&
sectorAddress <= trk.TrackEndSector);
if(track.TrackSequence == 0)
{
ErrorMessage = $"Can't found track containing {sectorAddress}";
return false;
}
FileStream trackStream = writingStreams.FirstOrDefault(kvp => kvp.Key == track.TrackSequence).Value;
if(trackStream == null)
{
ErrorMessage = $"Can't found file containing {sectorAddress}";
return false;
}
if(track.TrackBytesPerSector != track.TrackRawBytesPerSector)
{
ErrorMessage = "Invalid write mode for this sector";
return false;
}
if(data.Length != track.TrackRawBytesPerSector)
{
ErrorMessage = "Incorrect data size";
return false;
}
trackStream.Seek((long)(track.TrackFileOffset + (sectorAddress - track.TrackStartSector) * (ulong)track.TrackRawBytesPerSector),
SeekOrigin.Begin);
trackStream.Write(data, 0, data.Length);
return true;
}
public bool WriteSectors(byte[] data, ulong sectorAddress, uint length)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
Track track =
writingTracks.FirstOrDefault(trk => sectorAddress >= trk.TrackStartSector &&
sectorAddress <= trk.TrackEndSector);
if(track.TrackSequence == 0)
{
ErrorMessage = $"Can't found track containing {sectorAddress}";
return false;
}
FileStream trackStream = writingStreams.FirstOrDefault(kvp => kvp.Key == track.TrackSequence).Value;
if(trackStream == null)
{
ErrorMessage = $"Can't found file containing {sectorAddress}";
return false;
}
if(track.TrackBytesPerSector != track.TrackRawBytesPerSector)
{
ErrorMessage = "Invalid write mode for this sector";
return false;
}
if(sectorAddress + length > track.TrackEndSector + 1)
{
ErrorMessage = "Can't cross tracks";
return false;
}
if(data.Length % track.TrackRawBytesPerSector != 0)
{
ErrorMessage = "Incorrect data size";
return false;
}
trackStream.Seek((long)(track.TrackFileOffset + (sectorAddress - track.TrackStartSector) * (ulong)track.TrackRawBytesPerSector),
SeekOrigin.Begin);
trackStream.Write(data, 0, data.Length);
return true;
}
public bool WriteSectorLong(byte[] data, ulong sectorAddress)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
Track track =
writingTracks.FirstOrDefault(trk => sectorAddress >= trk.TrackStartSector &&
sectorAddress <= trk.TrackEndSector);
if(track.TrackSequence == 0)
{
ErrorMessage = $"Can't found track containing {sectorAddress}";
return false;
}
FileStream trackStream = writingStreams.FirstOrDefault(kvp => kvp.Key == track.TrackSequence).Value;
if(trackStream == null)
{
ErrorMessage = $"Can't found file containing {sectorAddress}";
return false;
}
if(data.Length != track.TrackRawBytesPerSector)
{
ErrorMessage = "Incorrect data size";
return false;
}
trackStream.Seek((long)(track.TrackFileOffset + (sectorAddress - track.TrackStartSector) * (ulong)track.TrackRawBytesPerSector),
SeekOrigin.Begin);
trackStream.Write(data, 0, data.Length);
return true;
}
public bool WriteSectorsLong(byte[] data, ulong sectorAddress, uint length)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
Track track =
writingTracks.FirstOrDefault(trk => sectorAddress >= trk.TrackStartSector &&
sectorAddress <= trk.TrackEndSector);
if(track.TrackSequence == 0)
{
ErrorMessage = $"Can't found track containing {sectorAddress}";
return false;
}
FileStream trackStream = writingStreams.FirstOrDefault(kvp => kvp.Key == track.TrackSequence).Value;
if(trackStream == null)
{
ErrorMessage = $"Can't found file containing {sectorAddress}";
return false;
}
if(sectorAddress + length > track.TrackEndSector + 1)
{
ErrorMessage = "Can't cross tracks";
return false;
}
if(data.Length % track.TrackRawBytesPerSector != 0)
{
ErrorMessage = "Incorrect data size";
return false;
}
trackStream.Seek((long)(track.TrackFileOffset + (sectorAddress - track.TrackStartSector) * (ulong)track.TrackRawBytesPerSector),
SeekOrigin.Begin);
trackStream.Write(data, 0, data.Length);
return true;
}
public bool SetTracks(List<Track> tracks)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
if(tracks == null || tracks.Count == 0)
{
ErrorMessage = "Invalid tracks sent";
return false;
}
if(writingTracks != null && writingStreams != null)
foreach(FileStream oldTrack in writingStreams.Select(t => t.Value).Distinct())
oldTrack.Close();
ulong currentOffset = 0;
writingTracks = new List<Track>();
foreach(Track track in tracks.OrderBy(t => t.TrackSequence))
{
Track newTrack = track;
newTrack.TrackFile = separateTracksWriting
? writingBaseName + $"_track{track.TrackSequence:D2}.bin"
: writingBaseName + ".bin";
newTrack.TrackFileOffset = separateTracksWriting ? 0 : currentOffset;
writingTracks.Add(newTrack);
currentOffset += (ulong)newTrack.TrackRawBytesPerSector *
(newTrack.TrackEndSector - newTrack.TrackStartSector + 1);
}
writingStreams = new Dictionary<uint, FileStream>();
if(separateTracksWriting)
foreach(Track track in writingTracks)
writingStreams.Add(track.TrackSequence,
new FileStream(track.TrackFile, FileMode.OpenOrCreate, FileAccess.ReadWrite,
FileShare.None));
else
{
FileStream jointstream = new FileStream(writingBaseName + ".bin", FileMode.OpenOrCreate,
FileAccess.ReadWrite, FileShare.None);
foreach(Track track in writingTracks) writingStreams.Add(track.TrackSequence, jointstream);
}
return true;
}
public bool Close()
{
if(!IsWriting)
{
ErrorMessage = "Image is not opened for writing";
return false;
}
if(separateTracksWriting)
foreach(FileStream writingStream in writingStreams.Values)
{
writingStream.Flush();
writingStream.Close();
}
else
{
writingStreams.First().Value.Flush();
writingStreams.First().Value.Close();
}
int currentSession = 0;
if(!string.IsNullOrWhiteSpace(discimage.Comment))
{
string[] commentLines = discimage.Comment.Split(new[] {'\n'}, StringSplitOptions.RemoveEmptyEntries);
foreach(string line in commentLines) descriptorStream.WriteLine("REM {0}", line);
}
descriptorStream.WriteLine("REM ORIGINAL MEDIA-TYPE {0}", MediaTypeToCdrwinType(imageInfo.MediaType));
if(!string.IsNullOrEmpty(discimage.Cdtextfile))
descriptorStream.WriteLine("CDTEXTFILE \"{0}\"", Path.GetFileName(discimage.Cdtextfile));
if(!string.IsNullOrEmpty(discimage.Title)) descriptorStream.WriteLine("TITLE {0}", discimage.Title);
if(!string.IsNullOrEmpty(discimage.Mcn)) descriptorStream.WriteLine("CATALOG {0}", discimage.Mcn);
if(!string.IsNullOrEmpty(discimage.Barcode)) descriptorStream.WriteLine("UPC_EAN {0}", discimage.Barcode);
if(!separateTracksWriting)
descriptorStream.WriteLine("FILE \"{0}\" BINARY", Path.GetFileName(writingStreams.First().Value.Name));
foreach(Track track in writingTracks)
{
if(track.TrackSession > currentSession) descriptorStream.WriteLine("REM SESSION {0}", ++currentSession);
if(separateTracksWriting)
descriptorStream.WriteLine("FILE \"{0}\" BINARY", Path.GetFileName(track.TrackFile));
(byte minute, byte second, byte frame) msf = LbaToMsf(track.TrackStartSector);
descriptorStream.WriteLine(" TRACK {0:D2} {1}", track.TrackSequence, GetTrackMode(track));
if(trackFlags.TryGetValue((byte)track.TrackSequence, out byte flagsByte))
if(flagsByte != 0 && flagsByte != (byte)CdFlags.DataTrack)
{
CdFlags flags = (CdFlags)flagsByte;
descriptorStream.WriteLine(" FLAGS{0}{1}{2}",
flags.HasFlag(CdFlags.CopyPermitted) ? " DCP" : "",
flags.HasFlag(CdFlags.FourChannel) ? " 4CH" : "",
flags.HasFlag(CdFlags.PreEmphasis) ? " PRE" : "");
}
if(trackIsrcs.TryGetValue((byte)track.TrackSequence, out string isrc))
descriptorStream.WriteLine(" ISRC {0}", isrc);
descriptorStream.WriteLine(" INDEX {0:D2} {1:D2}:{2:D2}:{3:D2}", 1, msf.minute, msf.second,
msf.frame);
}
descriptorStream.Flush();
descriptorStream.Close();
IsWriting = false;
ErrorMessage = "";
return true;
}
public bool SetGeometry(uint cylinders, uint heads, uint sectorsPerTrack)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool WriteSectorTag(byte[] data, ulong sectorAddress, SectorTagType tag)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
Track track =
writingTracks.FirstOrDefault(trk => sectorAddress >= trk.TrackStartSector &&
sectorAddress <= trk.TrackEndSector);
if(track.TrackSequence == 0)
{
ErrorMessage = $"Can't found track containing {sectorAddress}";
return false;
}
switch(tag)
{
case SectorTagType.CdTrackFlags:
{
if(data.Length != 1)
{
ErrorMessage = "Incorrect data size for track flags";
return false;
}
trackFlags.Add((byte)track.TrackSequence, data[0]);
return true;
}
case SectorTagType.CdTrackIsrc:
{
if(data != null) trackIsrcs.Add((byte)track.TrackSequence, Encoding.UTF8.GetString(data));
return true;
}
default:
ErrorMessage = $"Unsupported tag type {tag}";
return false;
}
}
public bool WriteSectorsTag(byte[] data, ulong sectorAddress, uint length, SectorTagType tag)
{
return WriteSectorTag(data, sectorAddress, tag);
}
public bool SetDumpHardware(List<DumpHardwareType> dumpHardware)
{
// Not supported
return false;
}
public bool SetCicmMetadata(CICMMetadataType metadata)
{
// Not supported
return false;
}
public bool SetMetadata(ImageInfo metadata)
{
discimage.Barcode = metadata.MediaBarcode;
discimage.Comment = metadata.Comments;
discimage.Title = metadata.MediaTitle;
return true;
}
}
}

View File

@@ -0,0 +1,100 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : CHD.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disc image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Manages MAME Compressed Hunks of Data disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
using System.Collections.Generic;
using System.IO;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
namespace DiscImageChef.DiscImages
{
// TODO: Implement PCMCIA support
public partial class Chd : IMediaImage
{
/// <summary>"MComprHD"</summary>
readonly byte[] chdTag = {0x4D, 0x43, 0x6F, 0x6D, 0x70, 0x72, 0x48, 0x44};
uint bytesPerHunk;
byte[] cis;
byte[] expectedChecksum;
uint hdrCompression;
uint hdrCompression1;
uint hdrCompression2;
uint hdrCompression3;
Dictionary<ulong, byte[]> hunkCache;
byte[] hunkMap;
ulong[] hunkTable;
uint[] hunkTableSmall;
byte[] identify;
ImageInfo imageInfo;
Stream imageStream;
bool isCdrom;
bool isGdrom;
bool isHdd;
uint mapVersion;
int maxBlockCache;
int maxSectorCache;
Dictionary<ulong, uint> offsetmap;
List<Partition> partitions;
Dictionary<ulong, byte[]> sectorCache;
uint sectorsPerHunk;
bool swapAudio;
uint totalHunks;
Dictionary<uint, Track> tracks;
public Chd()
{
imageInfo = new ImageInfo
{
ReadableSectorTags = new List<SectorTagType>(),
ReadableMediaTags = new List<MediaTagType>(),
HasPartitions = false,
HasSessions = false,
Application = "MAME",
Creator = null,
Comments = null,
MediaManufacturer = null,
MediaModel = null,
MediaSerialNumber = null,
MediaBarcode = null,
MediaPartNumber = null,
MediaSequence = 0,
LastMediaSequence = 0,
DriveManufacturer = null,
DriveModel = null,
DriveSerialNumber = null,
DriveFirmwareRevision = null
};
}
}
}

View File

@@ -0,0 +1,90 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Constants.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains constants for MAME Compressed Hunks of Data disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
namespace DiscImageChef.DiscImages
{
public partial class Chd
{
/// <summary>"GDDD"</summary>
const uint HARD_DISK_METADATA = 0x47444444;
/// <summary>"IDNT"</summary>
const uint HARD_DISK_IDENT_METADATA = 0x49444E54;
/// <summary>"KEY "</summary>
const uint HARD_DISK_KEY_METADATA = 0x4B455920;
/// <summary>"CIS "</summary>
const uint PCMCIA_CIS_METADATA = 0x43495320;
/// <summary>"CHCD"</summary>
const uint CDROM_OLD_METADATA = 0x43484344;
/// <summary>"CHTR"</summary>
const uint CDROM_TRACK_METADATA = 0x43485452;
/// <summary>"CHT2"</summary>
const uint CDROM_TRACK_METADATA2 = 0x43485432;
/// <summary>"CHGT"</summary>
const uint GDROM_OLD_METADATA = 0x43484754;
/// <summary>"CHGD"</summary>
const uint GDROM_METADATA = 0x43484744;
/// <summary>"AVAV"</summary>
const uint AV_METADATA = 0x41564156;
/// <summary>"AVLD"</summary>
const uint AV_LASER_DISC_METADATA = 0x41564C44;
const string REGEX_METADATA_HDD =
@"CYLS:(?<cylinders>\d+),HEADS:(?<heads>\d+),SECS:(?<sectors>\d+),BPS:(?<bps>\d+)";
const string REGEX_METADATA_CDROM =
@"TRACK:(?<track>\d+) TYPE:(?<track_type>\S+) SUBTYPE:(?<sub_type>\S+) FRAMES:(?<frames>\d+)";
const string REGEX_METADATA_CDROM2 =
@"TRACK:(?<track>\d+) TYPE:(?<track_type>\S+) SUBTYPE:(?<sub_type>\S+) FRAMES:(?<frames>\d+) PREGAP:(?<pregap>\d+) PGTYPE:(?<pgtype>\S+) PGSUB:(?<pgsub>\S+) POSTGAP:(?<postgap>\d+)";
const string REGEX_METADATA_GDROM =
@"TRACK:(?<track>\d+) TYPE:(?<track_type>\S+) SUBTYPE:(?<sub_type>\S+) FRAMES:(?<frames>\d+) PAD:(?<pad>\d+) PREGAP:(?<pregap>\d+) PGTYPE:(?<pgtype>\S+) PGSUB:(?<pgsub>\S+) POSTGAP:(?<postgap>\d+)";
const string TRACK_TYPE_MODE1 = "MODE1";
const string TRACK_TYPE_MODE1_2K = "MODE1/2048";
const string TRACK_TYPE_MODE1_RAW = "MODE1_RAW";
const string TRACK_TYPE_MODE1_RAW_2K = "MODE1/2352";
const string TRACK_TYPE_MODE2 = "MODE2";
const string TRACK_TYPE_MODE2_2K = "MODE2/2336";
const string TRACK_TYPE_MODE2_F1 = "MODE2_FORM1";
const string TRACK_TYPE_MODE2_F1_2K = "MODE2/2048";
const string TRACK_TYPE_MODE2_F2 = "MODE2_FORM2";
const string TRACK_TYPE_MODE2_F2_2K = "MODE2/2324";
const string TRACK_TYPE_MODE2_FM = "MODE2_FORM_MIX";
const string TRACK_TYPE_MODE2_RAW = "MODE2_RAW";
const string TRACK_TYPE_MODE2_RAW_2K = "MODE2/2352";
const string TRACK_TYPE_AUDIO = "AUDIO";
const string SUB_TYPE_COOKED = "RW";
const string SUB_TYPE_RAW = "RW_RAW";
const string SUB_TYPE_NONE = "NONE";
const int MAX_CACHE_SIZE = 16777216;
}
}

View File

@@ -0,0 +1,88 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Enums.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains enumerations for MAME Compressed Hunks of Data disk images.
//
// --[ 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-2018 Natalia Portillo
// ****************************************************************************/
namespace DiscImageChef.DiscImages
{
public partial class Chd
{
enum ChdCompression : uint
{
None = 0,
Zlib = 1,
ZlibPlus = 2,
Av = 3
}
enum ChdFlags : uint
{
HasParent = 1,
Writable = 2
}
enum Chdv3EntryFlags : byte
{
/// <summary>Invalid</summary>
Invalid = 0,
/// <summary>Compressed with primary codec</summary>
Compressed = 1,
/// <summary>Uncompressed</summary>
Uncompressed = 2,
/// <summary>Use offset as data</summary>
Mini = 3,
/// <summary>Same as another hunk in file</summary>
SelfHunk = 4,
/// <summary>Same as another hunk in parent</summary>
ParentHunk = 5,
/// <summary>Compressed with secondary codec (FLAC)</summary>
SecondCompressed = 6
}
enum ChdOldTrackType : uint
{
Mode1 = 0,
Mode1Raw,
Mode2,
Mode2Form1,
Mode2Form2,
Mode2FormMix,
Mode2Raw,
Audio
}
enum ChdOldSubType : uint
{
Cooked = 0,
Raw,
None
}
}
}

Some files were not shown because too many files have changed in this diff Show More