diff --git a/Documentation/Changes.txt b/Documentation/Changes.txt index 470cf2a..72fa3d7 100755 --- a/Documentation/Changes.txt +++ b/Documentation/Changes.txt @@ -884,6 +884,43 @@ Bug fixes * A failed delete, or the user clicking on 'No' to confirm a deletion, would leave the confirmation window open until it was a successful delete, or the user clicked 'Yes'. * The configuration option to compress UEF images did not get saved to the registry. +1.44 - 18th December 2022 +------------------------- +New or improved features +* Added a new class for accessing the registry. +* Added an option, when creating ADFS hard drive images, to add the 512-byte 'emulator header'. +* Changed some of the internal procedures so that they return a Boolean, instead of a structure, to make it easier to add multi-partition support. +* MMFS support has been temporarily removed. +* Added an option, when creating ADFS hard drive images, whether it will be an IDE drive or not. This fixes an issue where the resultant drive could not be read by RISC OS 3.11 in Arculator. +* ADFS Directories which have not been read in for reasons other than the flag not being set, are now reported as broken. +* ADFS broken directory error codes have been expanded. Now checks for non-sector aligned directories, invalid Start or End names (i.e. not 'Hugo' or 'Nick'), and any other reason it is found to be broken. +* When adding multiple files to an image, no format checking is done and the files are just added. +* When adding directories to an image, the progress indicator is shown. +* When IDing DFS images, any image over 400KB (200KB double sided) is rejected. +* Added the address of the file to the CSV output. +* Extra range checking when updating ADFS New Map fragments, and if any part fails, the original is restored from the backup. +* Moved the 'Waiting' and 'Progress Update' labels on the Progress dialogue window. +* Performed some code optimisation on ADFS New Map. +* Writing objects to ADFS New Map will now create, or re-use, shared fragments. +* When outputting a CSV file of the contents, a dialogue box is now presented to select which columns, with some options, are to be included. +* CSV output now prefixes all hex numbers with '0x' for easier importing into Excel/Numbers/etc. +* Added 'Show Report' button and window. +* Image report can also be included in the CSV output. +* ADFS Hardware information is now saved into hard drive images (ADFS New Map). + +Bug fixes +* In ADFS and Acorn FS, if a file had the same sector as the root then the filename would be blank. This would usually happen with New Map ADFS (usually an 'E' floppy) where the root was addressed directly, but the files were addressed indirectly. +* Changing time/date on a file now updates the 'unsaved' icon in the status bar. +* Some ADFS New Map fragment IDs were being used more than once, so the checking on unique IDs has been tightened up. +* The ADFS disc size was being read in as the root size. +* The heads and sectors per track fields were not being read in with ADFS New Map. +* Sometimes the root on ADFS New Map was not being found, so this has been improved. +* Renaming a directory in ADFS caused issues when then adding files to it as it kept it's old name. +* Sometimes when writing a file on ADFS New Map, two (or more) files would have the same indirect address (fragment ID). +* When writing a file to ADFS New Map, the end of free space marker on a zone would be beyond the zone and trample the following zone. When subsequent files where then written, this would continue into the following zone(s). +* On ADFS New Map, the free space would sometimes be incorrectly reported meaning that some files were stored where there was not enough space to store the file. +* On ADFS New Map, the smallest possible space to contain a fragment ID and the stop bit was not taken into consideration, meaning that a file stored could then not be retrieved. +* Some ADFS New and Big directory object's attributes, that only had a single attribute, where having '00' appended to the end. Platform History ---------------- diff --git a/Documentation/Disc Image Manager User Guide.docx b/Documentation/Disc Image Manager User Guide.docx index 041c178..3b14f19 100644 Binary files a/Documentation/Disc Image Manager User Guide.docx and b/Documentation/Disc Image Manager User Guide.docx differ diff --git a/Documentation/Disc Image Manager User Guide.pdf b/Documentation/Disc Image Manager User Guide.pdf index de8de8b..b1aa514 100644 Binary files a/Documentation/Disc Image Manager User Guide.pdf and b/Documentation/Disc Image Manager User Guide.pdf differ diff --git a/Documentation/Guide To Disc Formats.pdf b/Documentation/Guide To Disc Formats.pdf index 7a8af19..9250839 100644 Binary files a/Documentation/Guide To Disc Formats.pdf and b/Documentation/Guide To Disc Formats.pdf differ diff --git a/Documentation/ToDo.txt b/Documentation/ToDo.txt index cd4b812..7f5db05 100644 --- a/Documentation/ToDo.txt +++ b/Documentation/ToDo.txt @@ -4,18 +4,19 @@ In no particular order Bugs * Scaling - there is a report that icons 'grow' on scaled screens. I have been unable to reproduce this. - + This appears to happen on Windows (and probably Linux), with single screen only (dual or more it does not do this), resolution of 1400 by anything (or more), and scaled to more than 100%. The icons in the Directory Listing grow as the mouse moves. - TO BE TESTED + + This appears to happen on Windows (and probably Linux), with single screen only (dual or more it does not do this), resolution of 1400 by anything (or more), and scaled to more than 100%. The icons in the Directory Listing grow as the mouse moves. - UNABLE TO REPLICATE. This may have been fixed by the upgrade to Lazarus 2.2.0. * 'Bad FS Map' from BeebEm when loading an ADFS/AFS hybrid image after making a change to the ADFS partition. - UNABLE TO REPLICATE -* Some SS DFS images are IDed as DS images when 'Allow zero sectors' is selected in the preferences. +* Some SS DFS images are IDed as DS images when 'Allow zero sectors' is selected in the preferences. - UNABLE TO REPLICATE * Access violation has been reported when creating an ADFS HDD image (default options) on Windows. - UNABLE TO REPLICATE * Does not work on Windows 11. - UNABLE TO REPLICATE -* Changing time/date on a file does not update the 'unsaved' icon in the status bar. -General +General - Ideas, currently unlikely to be added * Drag and Drop facilities out of the application...currently looking unlikely for cross platform. This will need to be done using 'code-per-platform' directives. * Extend drag and drop copy/move to multiple selection. * Application to application communications. * Copy entire files onto clipboard? And then paste from clipboard? - as Drag and Drop, this will need to be done using 'code-per-platform' directives. + +General - Ideas which are maybe possible * Ask to force overwrite if many files are getting written. * Export the contents of an open image to another image, or set of images if the selected format is too small or are more directory entries than the format allows. * Cancel button for progress display? @@ -23,25 +24,16 @@ General * For macOS, change the settings being saved to the registry to being saved in a plist file, or within the application directory itself. * Ablity to add/remove/delete partitions using the command line. * Allow for multiple (>2) partitions - i.e. two or more DOS partitions on an ADFS structure, or ADFS/AFS/DOS hybrid. - -DFS - -ADFS -* Import an existing AFS or DOS image into ADFS as a new partition (importing DOS image as parition is as easy as just adding a DOS Image file). - -AFS -* AFS0 images do not get created correctly (in particular the free space allocation maps) - AWAITING MORE INFO ON AFS FORMATS. -* Add option to new Level 3 images for pre-1988 or post-1988 format - AWAITING MORE INFO ON AFS FORMATS. -* When extracting the AFS partition, remove the ADFS partition and re-address all the objects, instead of just blanking out the ADFS part - IN PROGRESS. - -Amiga - -Spectrum/Amstrad -* Write entire module - REQUIRE MORE INFO ON SPECTRUM FORMAT. - -MMFS -* Rewrite/rethink entire MMFS idea. Possibly remove from Disc Image Manager. - -SparkFS - -DOS Plus \ No newline at end of file +* ADFS: When a DOS image file is added to an ADFS image (and the "Open DOS Partition" setting is set), automatically open as the second partition (if there isn't one already). +* AFS: AFS0 images do not get created correctly (in particular the free space allocation maps) - AWAITING MORE INFO ON AFS FORMATS. +* AFS: Add option to new Level 3 images for pre-1988 or post-1988 format - AWAITING MORE INFO ON AFS FORMATS. +* AFS: When extracting the AFS partition, remove the ADFS partition and re-address all the objects, instead of just blanking out the ADFS part - IN PROGRESS. +* Amiga: Add in support for Rigid Disks (multi-partition hard discs) - NEED TO IMPLEMENT MULTI-PARTITION SUPPORT FIRST. +* Amiga: Add in support for Professional File System - AWAITING FORMAT DETAILS AND SAMPLE(S). +* Amiga: Add in support for Smart File System - AWAITING SAMPLE(S). +* Spectrum/Amstrad: Write entire module - REQUIRE MORE INFO ON SPECTRUM FORMAT. +* MMFS: Rewrite/rethink entire MMFS idea - might be more viable once multi-partitions are implemented. +* DOS: Add in support for partitioned hard drive images - NEED TO IMPLEMENT MULTI_PARTITION SUPPORT FIRST. +* Have different TImageList components for each format. +* File Viewer: ability to export contents in a different format (e.g. BASIC -> Text, or Sprite -> PNG). +* Function to check every file and directory on a volume and produce a report detailing number of files with error (CRC32) and number of broken directories (ADFS), and list them, with option to save to a text file. \ No newline at end of file diff --git a/Graphics/Buttons/Show Report/Show Report.png b/Graphics/Buttons/Show Report/Show Report.png new file mode 100755 index 0000000..d352f2a Binary files /dev/null and b/Graphics/Buttons/Show Report/Show Report.png differ diff --git a/Graphics/Misc/Marble_Tile.png b/Graphics/Misc/Marble_Tile.png new file mode 100644 index 0000000..49a4318 Binary files /dev/null and b/Graphics/Misc/Marble_Tile.png differ diff --git a/LazarusSource/.DS_Store b/LazarusSource/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/LazarusSource/.DS_Store differ diff --git a/LazarusSource/AboutUnit.lfm b/LazarusSource/AboutUnit.lfm index b205c0d..87b01cf 100755 --- a/LazarusSource/AboutUnit.lfm +++ b/LazarusSource/AboutUnit.lfm @@ -76,7 +76,7 @@ object AboutForm: TAboutForm Align = alTop Alignment = taCenter BorderSpacing.Bottom = 4 - Caption = 'http://www.geraldholdsworth.co.uk' + Caption = 'https://www.geraldholdsworth.co.uk' Font.Color = clNavy Font.Height = -16 Font.Name = 'Tahoma' diff --git a/LazarusSource/CSVPrefUnit.lfm b/LazarusSource/CSVPrefUnit.lfm new file mode 100644 index 0000000..e1f13df --- /dev/null +++ b/LazarusSource/CSVPrefUnit.lfm @@ -0,0 +1,171 @@ +object CSVPrefForm: TCSVPrefForm + Left = 632 + Height = 309 + Top = 378 + Width = 225 + BorderIcons = [] + BorderStyle = bsDialog + Caption = 'CSV Preferences' + ClientHeight = 309 + ClientWidth = 225 + OnPaint = FormPaint + Position = poMainFormCenter + LCLVersion = '2.2.0.4' + object cb_IncDir: TCheckBox + Left = 40 + Height = 18 + Top = 32 + Width = 129 + Caption = 'Include Directories' + TabOrder = 0 + end + object cb_Parent: TCheckBox + Left = 40 + Height = 18 + Top = 112 + Width = 59 + Caption = 'Parent' + Checked = True + State = cbChecked + TabOrder = 3 + end + object Label1: TLabel + Left = 32 + Height = 16 + Top = 88 + Width = 53 + Caption = 'Columns' + end + object cb_Filename: TCheckBox + Left = 40 + Height = 18 + Top = 130 + Width = 73 + Caption = 'Filename' + Checked = True + State = cbChecked + TabOrder = 4 + end + object cb_LoadAddr: TCheckBox + Left = 40 + Height = 18 + Top = 148 + Width = 100 + Caption = 'Load Address' + Checked = True + State = cbChecked + TabOrder = 5 + end + object cb_ExecAddr: TCheckBox + Left = 40 + Height = 18 + Top = 166 + Width = 127 + Caption = 'Execution Address' + Checked = True + State = cbChecked + TabOrder = 6 + end + object cb_Length: TCheckBox + Left = 40 + Height = 18 + Top = 184 + Width = 62 + Caption = 'Length' + Checked = True + State = cbChecked + TabOrder = 7 + end + object cb_Attributes: TCheckBox + Left = 40 + Height = 18 + Top = 202 + Width = 78 + Caption = 'Attributes' + Checked = True + State = cbChecked + TabOrder = 8 + end + object cb_Address: TCheckBox + Left = 40 + Height = 18 + Top = 220 + Width = 69 + Caption = 'Address' + TabOrder = 9 + end + object cb_CRC32: TCheckBox + Left = 40 + Height = 18 + Top = 238 + Width = 62 + Caption = 'CRC32' + Checked = True + State = cbChecked + TabOrder = 10 + end + object Label2: TLabel + Left = 32 + Height = 16 + Top = 8 + Width = 47 + Caption = 'Options' + end + object CancelButton: TBitBtn + Left = 8 + Height = 30 + Top = 272 + Width = 100 + Cancel = True + Caption = 'Cancel' + Color = 15527148 + ModalResult = 2 + TabOrder = 11 + end + object OKBtnBack: TPanel + Left = 112 + Height = 30 + Top = 272 + Width = 100 + BevelColor = clYellow + BevelInner = bvLowered + BevelOuter = bvLowered + BevelWidth = 4 + ClientHeight = 30 + ClientWidth = 100 + Color = clYellow + ParentColor = False + TabOrder = 12 + object OKButton: TBitBtn + Left = 0 + Height = 30 + Top = 0 + Width = 100 + Caption = '&OK' + Color = 15527148 + Default = True + ModalResult = 1 + TabOrder = 0 + end + end + object cb_IncFilename: TCheckBox + Left = 40 + Height = 18 + Top = 50 + Width = 118 + Caption = 'Include Filename' + Checked = True + State = cbChecked + TabOrder = 1 + end + object cb_IncReport: TCheckBox + Left = 40 + Height = 18 + Top = 68 + Width = 143 + Caption = 'Include Image Report' + Checked = True + State = cbChecked + TabOrder = 2 + end +end diff --git a/LazarusSource/CSVPrefUnit.pas b/LazarusSource/CSVPrefUnit.pas new file mode 100644 index 0000000..d70d9eb --- /dev/null +++ b/LazarusSource/CSVPrefUnit.pas @@ -0,0 +1,59 @@ +unit CSVPrefUnit; + +{$mode ObjFPC}{$H+} + +interface + +uses + Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, Buttons, + ExtCtrls; + +type + + { TCSVPrefForm } + + TCSVPrefForm = class(TForm) + CancelButton: TBitBtn; + cb_IncDir: TCheckBox; + cb_Parent: TCheckBox; + cb_Filename: TCheckBox; + cb_LoadAddr: TCheckBox; + cb_ExecAddr: TCheckBox; + cb_Length: TCheckBox; + cb_Attributes: TCheckBox; + cb_Address: TCheckBox; + cb_CRC32: TCheckBox; + cb_IncFilename: TCheckBox; + cb_IncReport: TCheckBox; + Label1: TLabel; + Label2: TLabel; + OKBtnBack: TPanel; + OKButton: TBitBtn; + procedure FormPaint(Sender: TObject); + private + + public + + end; + +var + CSVPrefForm: TCSVPrefForm; + +implementation + +uses MainUnit; + +{$R *.lfm} + +{ TCSVPrefForm } + +{------------------------------------------------------------------------------} +//Tile the form +{------------------------------------------------------------------------------} +procedure TCSVPrefForm.FormPaint(Sender: TObject); +begin + MainForm.FileInfoPanelPaint(Sender); +end; + +end. + diff --git a/LazarusSource/DiscImage.pas b/LazarusSource/DiscImage.pas index d44d2d1..adbed48 100755 --- a/LazarusSource/DiscImage.pas +++ b/LazarusSource/DiscImage.pas @@ -1,7 +1,7 @@ unit DiscImage; { -TDiscImage class V1.43 +TDiscImage class V1.44 Manages retro disc images, presenting a list of files and directories to the parent application. Will also extract files and write new files. Almost a complete filing system in itself. Compatible with Acorn DFS, Acorn ADFS, UEF, Commodore @@ -54,11 +54,44 @@ TDir = record DOSPartition, //Is this in the DOS Plus partition? (ADFS/DOS Plus) AFSPartition: Boolean; //Is this in the AFS partition? (ADFS/AFS) Sector, //Where is this directory located (same as TDirEntry) + Length, //How big is the directory (same as TDirEntry) Partition : Cardinal; //Which partition (side) is this on? Parent : Integer; //What is the TDir reference of the parent (-1 if none) end; //Collection of directories TDisc = array of TDir; + //Partitions + TPartition = record //Details about the partition + Directories : TDisc; //All the directories + Title, //Title of the partition + RootTitle, //Title of the root directory + RootName : String; //Root name ($, A:, C:, DF0, DH0, etc.) + DirSep : Char; //Directory separator + HeaderAddr, //Offset(s) of the header(s) + FSMAddr : array of Cardinal;//Offset(s) of the FSM/Map/Bitmap/FAT(s) + FreeSpaceMap : array of TTrack; //The free space map + DOSVolInRoot : Boolean; //Volume name is stored in the root (DOS) + RootAddress, //Offset of the root + SectorSize, //Sector Size + DOSalloc, //Allocation Unit (DOS Plus) + Version, //Format version + Root_size, //Size of the root directory + DOSBlocks, //Size of the DOS partition in blocks + DOSCluster_size : Cardinal; //Size of a DOS cluster + FreeSpace, //Amount of free space in bytes + PartitionSize : QWord; //Size of the partition in bytes + Format, //Major format of this partition + DOSFATSize, //Size of DOS Plus FAT in blocks + DOSResSecs : Word; //Number of reserved blocks + SecsPerTrack, //Number of sectors per track + Heads, //Number of heads (Acorn ADFS New) + Density, //Density (Acorn ADFS New) + DOSFATType, //FAT Type - 12: FAT12, 16: FAT16, 32: FAT32 + DOSNumFATs, //Number of FATs in a DOS Plus image + AmigaMapType : Byte; //OFS/FFS/PFS/OFS + end; + //Partitions + TPartitions = array of TPartition; //Fragment TFragment = record //For retrieving the ADFS E/F fragment information Offset, @@ -71,6 +104,7 @@ TFragment = record //For retrieving the ADFS E/F fragment informati TProgressProc = procedure(Fupdate: String) of Object; private FDisc : TDisc; //Container for the entire catalogue + FPartitions : TPartitions; //Container for the entire catalogue (partitioned) Fdata : TDIByteArray; //Container for the image to be loaded into FDSD, //Double sided flag (Acorn DFS) FMap, //Old/New Map flag (Acorn ADFS) OFS/FFS (Amiga) @@ -146,25 +180,29 @@ TFragment = record //For retrieving the ADFS E/F fragment informati SparkFile : TSpark; //For reading in Spark archives //Private methods procedure ResetVariables; + procedure AddPartition; function ReadString(ptr,term: Integer;control: Boolean=True): String; function ReadString(ptr,term: Integer;var buffer: TDIByteArray; - control: Boolean=True): String; overload; + control: Boolean=True): String; overload; procedure WriteString(str: String;ptr,len: Cardinal;pad: Byte); procedure WriteString(str: String;ptr,len: Cardinal;pad: Byte; var buffer: TDIByteArray); overload; function FormatToString: String; function FormatToExt: String; + function GetMajorFormatNumber: Word; + function GetMinorFormatNumber: Byte; function ReadBits(offset,start,length: Cardinal): Cardinal; procedure WriteBits(value,offset,start,length: Cardinal); - procedure WriteBits(value,offset,start,length: Cardinal;buffer: TDIByteArray); overload; + procedure WriteBits(value,offset,start,length: Cardinal; + buffer: TDIByteArray); overload; function RISCOSToTimeDate(filedatetime: Int64): TDateTime; function TimeDateToRISCOS(delphitime: TDateTime): Int64; function Read32b(offset: Cardinal; bigendian: Boolean=False): Cardinal; function Read32b(offset: Cardinal; var buffer: TDIByteArray; - bigendian: Boolean=False): Cardinal; overload; + bigendian: Boolean=False): Cardinal; overload; function Read24b(offset: Cardinal; bigendian: Boolean=False): Cardinal; function Read24b(offset: Cardinal; var buffer: TDIByteArray; - bigendian: Boolean=False): Cardinal; overload; + bigendian: Boolean=False): Cardinal; overload; function Read16b(offset: Cardinal; bigendian: Boolean=False): Word; function Read16b(offset: Cardinal; var buffer: TDIByteArray; bigendian: Boolean=False): Word; overload; @@ -181,7 +219,8 @@ TFragment = record //For retrieving the ADFS E/F fragment informati procedure Write16b(value: Word; offset: Cardinal; var buffer: TDIByteArray; bigendian: Boolean=False); overload; procedure WriteByte(value: Byte; offset: Cardinal); - procedure WriteByte(value: Byte; offset: Cardinal; var buffer: TDIByteArray); overload; + procedure WriteByte(value: Byte; offset: Cardinal; + var buffer: TDIByteArray); overload; function GetDataLength: Cardinal; procedure SetDataLength(newlen: Cardinal); function ROR13(v: Cardinal): Cardinal; @@ -189,7 +228,8 @@ TFragment = record //For retrieving the ADFS E/F fragment informati function MapFlagToByte: Byte; function MapTypeToString: String; function DirTypeToString: String; - function GeneralChecksum(offset,length,chkloc,start: Cardinal;carry: Boolean): Cardinal; + function GeneralChecksum(offset,length,chkloc,start: Cardinal; + carry: Boolean): Cardinal; function GetImageCrc: String; function GetCRC(var buffer: TDIByteArray): String; function GetCRC16(start,len: Cardinal;var buffer: TDIByteArray): Cardinal; @@ -202,36 +242,43 @@ TFragment = record //For retrieving the ADFS E/F fragment informati function ID_ADFS: Boolean; function ReadADFSDir(dirname: String; sector: Cardinal): TDir; function CalculateADFSDirCheck(sector: Cardinal): Byte; - function CalculateADFSDirCheck(sector: Cardinal;buffer:TDIByteArray): Byte; overload; - function NewDiscAddrToOffset(addr: Cardinal;offset:Boolean=True): TFragmentArray; + function CalculateADFSDirCheck(sector: Cardinal; + buffer: TDIByteArray): Byte; overload; + function NewDiscAddrToOffset(addr: Cardinal; + offset: Boolean=True): TFragmentArray; function OffsetToOldDiscAddr(offset: Cardinal): Cardinal; function ByteChecksum(offset,size: Cardinal): Byte; - function ByteChecksum(offset,size: Cardinal;var buffer: TDIByteArray): Byte; overload; - function ReadADFSDisc: TDisc; + function ByteChecksum(offset,size: Cardinal; + var buffer: TDIByteArray): Byte; overload; + function ReadADFSDisc: Boolean; procedure ADFSFreeSpaceMap; procedure ADFSFillFreeSpaceMap(address: Cardinal;usage: Byte); - function FormatADFSFloppy(minor: Byte): TDisc; + function FormatADFSFloppy(minor: Byte): Boolean; procedure FormatOldMapADFS(disctitle: String); - procedure FormatNewMapADFS(disctitle: String); - function FormatADFSHDD(harddrivesize:Cardinal;newmap:Boolean;dirtype:Byte):TDisc; + procedure FormatNewMapADFS(disctitle: String; ide: Boolean); + function FormatADFSHDD(harddrivesize: Cardinal; newmap: Boolean; dirtype:Byte; + ide,addheader: Boolean): Boolean; function UpdateADFSDiscTitle(title: String): Boolean; function UpdateADFSBootOption(option: Byte): Boolean; function ADFSGetFreeFragments(offset:Boolean=True): TFragmentArray; function WriteADFSFile(var file_details: TDirEntry;var buffer: TDIByteArray; extend:Boolean=True): Integer; - function ADFSFindFreeSpace(filelen: Cardinal;var fragid: Cardinal): TFragmentArray; + function ADFSFindFreeSpace(filelen: Cardinal; + var fragid: Cardinal): TFragmentArray; function WriteFragmentedData(fragments: TFragmentArray; - var buffer: TDIByteArray): Boolean; - procedure ADFSAllocateFreeSpace(filelen,freeptr: Cardinal); - procedure ADFSAllocateFreeSpace(filelen,fragid: Cardinal;fragments: TFragmentArray); overload; - function ADFSSectorAlignLength(filelen: Cardinal): Cardinal; + var buffer: TDIByteArray): Boolean; + function ADFSAllocateFreeSpace(filelen,freeptr: Cardinal): Boolean; + function ADFSAllocateFreeSpace(filelen,fragid: Cardinal; + fragments: TFragmentArray): Boolean; overload; + function ADFSSectorAlignLength(filelen: Cardinal; + bpmbalign: Boolean=True): Cardinal; procedure ADFSCalcFileDate(var Entry: TDirEntry); function CreateADFSDirectory(var dirname,parent,attributes: String): Integer; procedure UpdateADFSCat(directory: String;newname: String=''); function UpdateADFSFileAttributes(filename,attributes: String): Boolean; function ValidateADFSFilename(filename: String): String; function RetitleADFSDirectory(filename,newtitle: String): Boolean; - function RenameADFSFile(oldfilename: String;var newfilename: String):Integer; + function RenameADFSFile(oldfilename: String;var newfilename: String): Integer; procedure ConsolodateADFSFreeSpaceMap; procedure ConsolodateADFSFragments(fragid: Cardinal); function DeleteADFSFile(filename: String; @@ -248,26 +295,30 @@ TFragment = record //For retrieving the ADFS E/F fragment informati procedure ReduceADFSCat(dir,entry: Cardinal); function FixBrokenADFSDirectories: Boolean; function FixADFSDirectory(dir,entry: Integer):Boolean; - function ADFSGetHardDriveParams(Ldiscsize:Cardinal;bigmap:Boolean; - var Lidlen,Lzone_spare,Lnzones,Llog2bpmb,Lroot: Cardinal):Boolean; - function UpdateADFSFileAddr(filename:String;newaddr:Cardinal;load:Boolean):Boolean; + function ADFSGetHardDriveParams(Ldiscsize: Cardinal; bigmap,ide: Boolean; + var Lidlen,Lzone_spare,Lnzones,Llog2bpmb,Lroot, + Llog2secsize,Llowsec: Cardinal): Boolean; + function UpdateADFSFileAddr(filename: String; newaddr: Cardinal; + load: Boolean): Boolean; function UpdateADFSFileType(filename:String;newtype:String):Boolean; function UpdateADFSTimeStamp(filename:String;newtimedate:TDateTime):Boolean; function ExtractADFSPartition(side: Cardinal): TDIByteArray; function GetADFSMaxLength(lastentry:Boolean): Cardinal; + function ADFSReport(CSV: Boolean): TStringList; //Acorn FileStore Routines function ID_AFS: Boolean; procedure ReadAFSPartition; - function ReadAFSDirectory(dirname:String;addr: Cardinal):TDir; + function ReadAFSDirectory(dirname: String; addr: Cardinal): TDir; function ExtractAFSFile(filename: String;var buffer: TDIByteArray): Boolean; function ReadAFSObject(offset: Cardinal): TDIByteArray; function GetAFSObjLength(offset: Cardinal): Cardinal; function GetAllocationMap: Cardinal; - function GetAllocationMap(sector:Cardinal;var spt:Cardinal):Cardinal; overload; + function GetAllocationMap(sector: Cardinal; + var spt: Cardinal): Cardinal; overload; procedure ReadAFSFSM; function AFSGetFreeSectors(used: Boolean=False): TFragmentArray; function AFSAllocateFreeSpace(size :Cardinal; - var fragments: TFragmentArray;addheader:Boolean=True): Cardinal; + var fragments: TFragmentArray; addheader: Boolean=True): Cardinal; procedure AFSDeAllocateFreeSpace(addr: Cardinal); procedure FinaliseAFSL2Map; function FormatAFS(harddrivesize: Cardinal;afslevel: Byte): Boolean; @@ -277,8 +328,8 @@ TFragment = record //For retrieving the ADFS E/F fragment informati function CreateAFSPassword(Accounts: TUserAccounts): Integer; function ReadAFSPassword: TUserAccounts; function WriteAFSFile(var file_details: TDirEntry; - var buffer: TDIByteArray):Integer; - function AFSAttrToByte(attr: String):Byte; + var buffer: TDIByteArray): Integer; + function AFSAttrToByte(attr: String): Byte; procedure WriteAFSObject(offset: Cardinal;var buffer: TDIByteArray); function RenameAFSFile(oldname:String;var newname: String): Integer; procedure UpdateAFSDirectory(dirname: String); @@ -287,72 +338,82 @@ TFragment = record //For retrieving the ADFS E/F fragment informati function RemoveAFSEntry(dir,entry: Cardinal): Boolean; function InsertAFSEntry(dir: Cardinal;file_details:TDirEntry): Integer; function MoveAFSFile(filename,directory: String): Integer; - function UpdateAFSFileAddr(filename:String;newaddr:Cardinal;load:Boolean):Boolean; - function UpdateAFSTimeStamp(filename:String;newtimedate:TDateTime):Boolean; - function AFSTimeToWord(timedate: TDateTime):Word; + function UpdateAFSFileAddr(filename: String; newaddr: Cardinal; + load: Boolean): Boolean; + function UpdateAFSTimeStamp(filename: String; newtimedate: TDateTime):Boolean; + function AFSTimeToWord(timedate: TDateTime): Word; function UpdateAFSAttributes(filename,attributes: String): Boolean; function UpdateAFSDiscTitle(title: String): Boolean; function AddAFSPartition(size: Cardinal): Boolean; + function AFSReport(CSV: Boolean): TStringList; //DFS Routines function ID_DFS: Boolean; - function ReadDFSDisc(mmbdisc:Integer=-1): TDisc; - procedure DFSFreeSpaceMap(LDisc: TDisc); + function ReadDFSDisc(mmbdisc:Integer=-1): Boolean; + procedure DFSFreeSpaceMap; function IsWatford(s: Integer): Boolean; function ConvertDFSSector(address,side: Integer): Integer; - function WriteDFSFile(var file_details: TDirEntry;var buffer: TDIByteArray): Integer; + function WriteDFSFile(var file_details: TDirEntry; + var buffer: TDIByteArray): Integer; procedure UpdateDFSCat(side: Integer); function ValidateDFSFilename(filename: String): String; function RenameDFSFile(oldfilename: String;var newfilename: String):Integer; function DeleteDFSFile(filename: String):Boolean; function UpdateDFSFileAttributes(filename,attributes: String): Boolean; - function FormatDFS(minor,tracks: Byte): TDisc; + function FormatDFS(minor,tracks: Byte): Boolean; function UpdateDFSDiscTitle(title: String;side: Byte): Boolean; function UpdateDFSBootOption(option,side: Byte): Boolean; function ExtractDFSFile(filename: String;var buffer: TDIByteArray): Boolean; - function UpdateDFSFileAddr(filename:String;newaddr:Cardinal;load:Boolean):Boolean; + function UpdateDFSFileAddr(filename: String; newaddr: Cardinal; + load: Boolean):Boolean; function ExtractDFSPartition(side: Cardinal): TDIByteArray; function AddDFSSide(var buffer: TDIByteArray): Boolean; function AddDFSSide(filename: String): Boolean; overload; function MoveDFSFile(filename,directory: String): Integer; + function DFSReport(CSV: Boolean): TStringList; //Commodore 1541/1571/1581 Routines function ID_CDR: Boolean; function ConvertDxxTS(format,track,sector: Integer): Integer; - function ReadCDRDisc: TDisc; - function FormatCDR(minor: Byte): TDisc; + function ReadCDRDisc: Boolean; + function FormatCDR(minor: Byte): Boolean; procedure CDRFreeSpaceMap; - procedure CDRSetClearBAM(track,sector: Byte;used: Boolean); + procedure CDRSetClearBAM(track,sector: Byte; used: Boolean); function UpdateCDRDiscTitle(title: String): Boolean; - function ExtractCDRFile(filename:String;var buffer:TDIByteArray): Boolean; - function WriteCDRFile(file_details: TDirEntry;var buffer: TDIByteArray): Integer; + function ExtractCDRFile(filename: String; var buffer:TDIByteArray): Boolean; + function WriteCDRFile(file_details: TDirEntry; + var buffer: TDIByteArray): Integer; procedure UpdateCDRCat; function CDRFindNextSector(var track,sector: Byte): Boolean; function CDRFindNextTrack(var track,sector: Byte): Boolean; function RenameCDRFile(oldfilename: String;var newfilename: String):Integer; function DeleteCDRFile(filename: String):Boolean; function UpdateCDRFileAttributes(filename,attributes: String): Boolean; + function CDRReport(CSV: Boolean): TStringList; //Sinclair Spectrum +3/Amstrad Routines function ID_Sinclair: Boolean; - function ReadSinclairDisc: TDisc; - function FormatSpectrum(minor: Byte): TDisc; - function WriteSpectrumFile(file_details: TDirEntry;var buffer: TDIByteArray): Integer; - function RenameSpectrumFile(oldfilename: String;var newfilename: String):Integer; + function ReadSinclairDisc: Boolean; + function FormatSpectrum(minor: Byte): Boolean; + function WriteSpectrumFile(file_details: TDirEntry; + var buffer: TDIByteArray): Integer; + function RenameSpectrumFile(oldfilename: String; + var newfilename: String):Integer; function DeleteSinclairFile(filename: String):Boolean; function UpdateSinclairFileAttributes(filename,attributes: String): Boolean; function UpdateSinclairDiscTitle(title: String): Boolean; function ExtractSpectrumFile(filename:String;var buffer:TDIByteArray):Boolean; //Commodore Amiga Routines function ID_Amiga: Boolean; - function ReadAmigaDisc: TDisc; + function ReadAmigaDisc: Boolean; function ReadAmigaDir(dirname: String; offset: Cardinal): TDir; function AmigaBootChecksum(offset: Cardinal): Cardinal; function AmigaChecksum(offset: Cardinal): Cardinal; - function ExtractAmigaFile(filename:String;var buffer:TDIByteArray):Boolean; + function ExtractAmigaFile(filename: String; var buffer: TDIByteArray):Boolean; function ExtractAmigaData(sector,filelen: Cardinal; var buffer: TDIByteArray): Boolean; - function FormatAmigaFDD(minor: Byte): TDisc; - function FormatAmigaHDD(harddrivesize: Cardinal): TDisc; + function FormatAmigaFDD(minor: Byte): Boolean; + function FormatAmigaHDD(harddrivesize: Cardinal): Boolean; procedure FormatAmiga(size: Cardinal); - function WriteAmigaFile(var file_details: TDirEntry;var buffer: TDIByteArray): Integer; + function WriteAmigaFile(var file_details: TDirEntry; + var buffer: TDIByteArray): Integer; function CreateAmigaDirectory(var dirname,parent,attributes: String): Integer; function RenameAmigaFile(oldfilename: String;var newfilename: String):Integer; function DeleteAmigaFile(filename: String):Boolean; @@ -366,54 +427,63 @@ TFragment = record //For retrieving the ADFS E/F fragment informati function AmigaIntToStrAttr(attr: Cardinal): String; function AmigaStrToIntAttr(attr: String): Cardinal; function AmigaCalculateHashValue(filename: String): Cardinal; - procedure AmigaAllocateFSMBlock(addr:Cardinal;used:Boolean;var fsm:TDIByteArray); + procedure AmigaAllocateFSMBlock(addr:Cardinal;used:Boolean; + var fsm:TDIByteArray); function GetAmigaFSMOffset(addr: Cardinal;var bit: Byte): Cardinal; function AmigaReadBitmap(var fsm: TDIByteArray): TFragmentArray; procedure AmigaWriteBitmap(fsmlist: TFragmentArray;var fsm: TDIByteArray); function AmigaFindFreeSpace(filelen: Cardinal): TFragmentArray; - function UpdateAmigaTimeStamp(filename: String;newtimedate: TDateTime): Boolean; + function UpdateAmigaTimeStamp(filename: String; + newtimedate: TDateTime): Boolean; function GetAmigaChain(sector: Cardinal): TFragmentArray; procedure AmigaAddToChain(filename: String;paraddr,sector: Cardinal); - function AmigaRemoveFromChain(filename: String;paraddr,sector: Cardinal):Boolean; + function AmigaRemoveFromChain(filename: String; + paraddr,sector: Cardinal):Boolean; procedure ValidateAmigaFile(var filename: String); + function AmigaReport(CSV: Boolean): TStringList; //Acorn CFS Routines function ID_CFS: Boolean; - function ReadUEFFile: TDisc; + function ReadUEFFile: Boolean; function CFSBlockStatus(status: Byte): String; function CFSTargetMachine(machine: Byte): String; - function ExtractCFSFile(entry: Integer;var buffer:TDIByteArray):Boolean; - procedure WriteUEFFile(filename: String;uncompress: Boolean=False); - function FormatCFS:TDisc; + function ExtractCFSFile(entry: Integer; var buffer:TDIByteArray): Boolean; + procedure WriteUEFFile(filename: String; uncompress: Boolean=False); + function FormatCFS: Boolean; function DeleteCFSFile(entry: Cardinal): Boolean; - function UpdateCFSAttributes(entry: Cardinal;attributes:String): Boolean; + function UpdateCFSAttributes(entry: Cardinal; attributes:String): Boolean; function MoveCFSFile(entry: Cardinal;dest: Integer): Integer; function CopyCFSFile(entry: Cardinal;dest: Integer): Integer; - function WriteCFSFile(var file_details: TDirEntry;var buffer: TDIByteArray): Integer; - function RenameCFSFile(entry: Cardinal;newfilename: String): Integer; - function UpdateCFSFileAddr(entry,newaddr:Cardinal;load:Boolean):Boolean; + function WriteCFSFile(var file_details: TDirEntry; + var buffer: TDIByteArray): Integer; + function RenameCFSFile(entry: Cardinal; newfilename: String): Integer; + function UpdateCFSFileAddr(entry,newaddr: Cardinal; load: Boolean): Boolean; //MMFS Routines function ID_MMB: Boolean; - function ReadMMBDisc: TDisc; + function ReadMMBDisc: Boolean; //Spark Routines function ID_Spark: Boolean; - function ReadSparkArchive: TDisc; + function ReadSparkArchive: Boolean; function ExtractSparkFile(filename: String;var buffer: TDIByteArray): Boolean; - function FormatSpark(Zipfilename: String): TDisc; + function FormatSpark(Zipfilename: String): Boolean; function DeleteSparkFile(filename: String): Boolean; function UpdateSparkAttributes(filename,attributes: String): Boolean; function MoveSparkFile(filename, dest: String): Integer; - function WriteSparkFile(var file_details:TDirEntry;var buffer:TDIByteArray):Integer; + function WriteSparkFile(var file_details: TDirEntry; + var buffer: TDIByteArray): Integer; function RenameSparkFile(filename, newfilename: String): Integer; function UpdateSparkFileType(filename: String; newtype: String): Boolean; - function UpdateSparkTimeStamp(filename: String;newtimedate:TDateTime): Boolean; - function UpdateSparkFileAddr(filename:String;newaddr:Cardinal;load:Boolean):Boolean; + function UpdateSparkTimeStamp(filename: String; + newtimedate: TDateTime): Boolean; + function UpdateSparkFileAddr(filename: String; newaddr: Cardinal; + load: Boolean): Boolean; function CreateSparkDirectory(filename, parent, attributes: String): Integer; //DOS Plus Routines function ID_DOSPlus: Boolean; function IDDOSPartition(ctr: Cardinal): Boolean; procedure ReadDOSPartition; function ReadDOSHeader: Boolean; - function ReadDOSDirectory(dirname:String;addr:Cardinal;var len:Cardinal):TDir; + function ReadDOSDirectory(dirname: String; addr: Cardinal; + var len: Cardinal): TDir; function DOSExtToFileType(ext: String): String; function ConvertDOSTimeDate(time,date: Word): TDateTime; function DOSTime(time: TDateTime): Word; @@ -424,18 +494,19 @@ TFragment = record //For retrieving the ADFS E/F fragment informati function GetClusterEntry(cluster: Cardinal): Cardinal; procedure SetClusterEntry(cluster,entry: Cardinal); function IsClusterValid(cluster: Cardinal): Boolean; - function GetClusterChain(cluster:Cardinal;len:Cardinal=0):TFragmentArray; + function GetClusterChain(cluster: Cardinal; len: Cardinal=0): TFragmentArray; procedure SetClusterChain(fragments: TFragmentArray); - function ExtractDOSFile(filename: String;var buffer: TDIByteArray): Boolean; + function ExtractDOSFile(filename: String; var buffer: TDIByteArray): Boolean; function ReadDOSObject(cluster: Cardinal;len: Cardinal=0): TDIByteArray; procedure ReadDOSFSM; function DOSGetFreeSectors(used: Boolean=False): TFragmentArray; - function RenameDOSFile(oldname:String;var newname: String): Integer; + function RenameDOSFile(oldname: String; var newname: String): Integer; function ValidateDOSFilename(filename: String;long: Boolean=False): String; procedure UpdateDOSDirectory(dirname: String); - procedure AllocateDOSClusters(len:Cardinal;var fragments:TFragmentArray); - procedure DeAllocateDOSClusters(len:Cardinal;var fragments:TFragmentArray); - function WriteDOSObject(buffer:TDIByteArray;fragments:TFragmentArray):Boolean; + procedure AllocateDOSClusters(len: Cardinal; var fragments: TFragmentArray); + procedure DeAllocateDOSClusters(len: Cardinal; var fragments: TFragmentArray); + function WriteDOSObject(buffer: TDIByteArray; + fragments: TFragmentArray): Boolean; function WriteDOSFile(var file_details: TDirEntry; var buffer: TDIByteArray):Integer; function InsertDOSEntry(dir: Cardinal;direntry: TDirEntry): Integer; @@ -444,15 +515,16 @@ TFragment = record //For retrieving the ADFS E/F fragment informati procedure RemoveDOSEntry(dir, entry: Cardinal); function UpdateDOSAttributes(filename,attributes: String): Boolean; function UpdateDOSDiscTitle(title: String): Boolean; - function UpdateDOSTimeStamp(filename:String;newtimedate:TDateTime):Boolean; + function UpdateDOSTimeStamp(filename: String; newtimedate: TDateTime):Boolean; function AddDOSPartition(size: Cardinal): Boolean; - function FormatDOS(size: QWord;fat: Byte): TDisc; + function FormatDOS(size: QWord;fat: Byte): Boolean; procedure WriteDOSHeader(offset, size: QWord;fat: Byte;bootable: Boolean); procedure WriteDOSHeader(offset, size: QWord;fat: Byte;bootable: Boolean; - var buffer:TDIByteArray);overload; + var buffer: TDIByteArray);overload; function MoveDOSFile(filename,directory: String): Integer; function DOSShortFilename(path,LFN: String;SFN :String=''): String; function BuildDOSFilename(f,e: String): String; + function DOSReport(CSV: Boolean): TStringList; //Private constants const //When the change of number of sectors occurs on Commodore 1541/1571 discs @@ -478,27 +550,35 @@ TFragment = record //For retrieving the ADFS E/F fragment informati function LoadFromFile(filename: String;readdisc: Boolean=True): Boolean; function IDImage: Boolean; procedure ReadImage; - procedure SaveToFile(filename: String;uncompress: Boolean=False); + function SaveToFile(filename: String;uncompress: Boolean=False): Boolean; procedure Close; - function FormatFDD(major:Word;minor:Byte=0;tracks: Byte=0;filename: String=''): Boolean; - function FormatHDD(major:Word;harddrivesize:Cardinal;newmap:Boolean;dirtype:Byte):Boolean; - function ExtractFile(filename:String;var buffer:TDIByteArray;entry:Cardinal=0): Boolean; - function WriteFile(var file_details: TDirEntry; var buffer: TDIByteArray): Integer; - function FileExists(filename: String;var Ref: Cardinal;sfn: Boolean=False): Boolean; - function FileExists(filename: String;var dir,entry: Cardinal;sfn: Boolean=False): Boolean; overload; + function FormatFDD(major:Word;minor:Byte=0;tracks: Byte=0; + filename: String=''): Boolean; + function FormatHDD(major:Word;harddrivesize:Cardinal;ide,newmap:Boolean; + dirtype:Byte;addheader:Boolean):Boolean; + function ExtractFile(filename:String;var buffer:TDIByteArray; + entry:Cardinal=0): Boolean; + function WriteFile(var file_details: TDirEntry; + var buffer: TDIByteArray;ShowFSM: Boolean=False): Integer; + function FileExists(filename: String;var Ref: Cardinal; + sfn: Boolean=False): Boolean; + function FileExists(filename: String;var dir,entry: Cardinal; + sfn: Boolean=False): Boolean; overload; function ReadDiscData(addr,count,side,offset: Cardinal; var buffer: TDIByteArray): Boolean; function WriteDiscData(addr,side: Cardinal;var buffer: TDIByteArray; count: Cardinal;start: Cardinal=0): Boolean; function FileSearch(search: TDirEntry): TSearchResults; - function RenameFile(oldfilename: String;var newfilename: String;entry: Cardinal=0): Integer; + function RenameFile(oldfilename: String;var newfilename: String; + entry: Cardinal=0): Integer; function DeleteFile(filename: String;entry: Cardinal=0): Boolean; function MoveFile(filename,directory: String): Integer; function MoveFile(source: Cardinal;dest: Integer): Integer; overload; function CopyFile(filename,directory: String): Integer; function CopyFile(filename,directory,newfilename: String): Integer; overload; function CopyFile(source: Cardinal;dest: Integer): Integer; overload; - function UpdateAttributes(filename,attributes: String;entry:Cardinal=0): Boolean; + function UpdateAttributes(filename,attributes: String; + entry:Cardinal=0): Boolean; function UpdateDiscTitle(title: String;side: Byte): Boolean; function UpdateBootOption(option,side: Byte): Boolean; function CreateDirectory(var filename,parent,attributes: String): Integer; @@ -506,8 +586,10 @@ TFragment = record //For retrieving the ADFS E/F fragment informati function GetFileCRC(filename: String;entry:Cardinal=0): String; function FixDirectories: Boolean; function SaveFilter(var FilterIndex: Integer;thisformat: Integer=-1):String; - function UpdateLoadAddr(filename:String;newaddr:Cardinal;entry:Cardinal=0): Boolean; - function UpdateExecAddr(filename:String;newaddr:Cardinal;entry:Cardinal=0): Boolean; + function UpdateLoadAddr(filename:String;newaddr:Cardinal; + entry:Cardinal=0): Boolean; + function UpdateExecAddr(filename:String;newaddr:Cardinal; + entry:Cardinal=0): Boolean; function TimeStampFile(filename: String;newtimedate: TDateTime): Boolean; function ChangeFileType(filename,newtype: String): Boolean; function GetFileTypeFromNumber(filetype: Integer): String; @@ -528,12 +610,16 @@ TFragment = record //For retrieving the ADFS E/F fragment informati function ChangeInterleaveMethod(NewMethod: Byte): Boolean; function GetDirSep(partition: Byte): Char; function ReadDirectory(dirname: String): Integer; + function ImageReport(CSV: Boolean): TStringList; //Published properties property AFSPresent: Boolean read FAFSPresent; property AFSRoot: Cardinal read Fafsroot; - property AllowDFSZeroSectors: Boolean read FDFSzerosecs write FDFSzerosecs; - property DFSBeyondEdge: Boolean read FDFSBeyondEdge write FDFSBeyondEdge; - property DFSAllowBlanks: Boolean read FDFSAllowBlank write FDFSAllowBlank; + property AllowDFSZeroSectors: Boolean read FDFSzerosecs + write FDFSzerosecs; + property DFSBeyondEdge: Boolean read FDFSBeyondEdge + write FDFSBeyondEdge; + property DFSAllowBlanks: Boolean read FDFSAllowBlank + write FDFSAllowBlank; property BootOpt: TDIByteArray read bootoption; property CRC32: String read GetImageCrc; property DirectoryType: Byte read FDirType; @@ -543,23 +629,31 @@ TFragment = record //For retrieving the ADFS E/F fragment informati property DOSPlusRoot: Cardinal read Fdosroot; property DOSPresent: Boolean read FDOSPresent; property DoubleSided: Boolean read FDSD; - property Filename: String read imagefilename write imagefilename; + property Filename: String read imagefilename + write imagefilename; property FormatExt: String read FormatToExt; property FormatNumber: Word read FFormat; property FormatString: String read FormatToString; property FreeSpaceMap: TSide read free_space_map; property InterleaveInUse: String read InterleaveString; property InterleaveInUseNum: Byte read FInterleave; - property InterleaveMethod: Byte read FForceInter write FForceInter; + property InterleaveMethod: Byte read FForceInter + write FForceInter; + property MajorFormatNumber: Word read GetMajorFormatNumber; property MapType: Byte read MapFlagToByte; property MapTypeString: String read MapTypeToString; property MaxDirectoryEntries: Cardinal read FMaxDirEnt; - property OpenDOSPartitions: Boolean read FOpenDOSPart write FOpenDOSPart; + property MinorFormatNumber: Byte read GetMinorFormatNumber; + property OpenDOSPartitions: Boolean read FOpenDOSPart + write FOpenDOSPart; + property Partitions: TPartitions read FPartitions; property ProgressIndicator: TProgressProc write FProgress; property RAWData: TDIByteArray read Fdata; property RootAddress: Cardinal read GetRootAddress; - property ScanSubDirs: Boolean read FScanSubDirs write FScanSubDirs; - property SparkAsFS: Boolean read FSparkAsFS write FSparkAsFS; + property ScanSubDirs: Boolean read FScanSubDirs + write FScanSubDirs; + property SparkAsFS: Boolean read FSparkAsFS + write FSparkAsFS; public destructor Destroy; override; End; diff --git a/LazarusSource/DiscImageManager.lpi b/LazarusSource/DiscImageManager.lpi index dd31511..7ac03db 100644 --- a/LazarusSource/DiscImageManager.lpi +++ b/LazarusSource/DiscImageManager.lpi @@ -323,7 +323,7 @@ - + @@ -460,6 +460,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/LazarusSource/DiscImageManager.lpr b/LazarusSource/DiscImageManager.lpr index 7c05387..429d66e 100644 --- a/LazarusSource/DiscImageManager.lpr +++ b/LazarusSource/DiscImageManager.lpr @@ -23,7 +23,9 @@ ImportSelectorUnit in 'ImportSelectorUnit.pas', PWordEditorUnit in 'PWordEditorUnit.pas', AFSPartitionUnit in 'AFSParitionUnit.pas', - ChangeInterleaveUnit in 'ChangeInterleaveUnit.pas'; + ChangeInterleaveUnit in 'ChangeInterleaveUnit.pas', GJHRegistryClass, + CSVPrefUnit in 'CSVPrefUnit.pas', + ImageReportUnit in 'ImageReportUnit.pas'; {$R *.res} @@ -45,5 +47,7 @@ Application.CreateForm(TPwordEditorForm, PwordEditorForm); Application.CreateForm(TAFSPartitionForm, AFSPartitionForm); Application.CreateForm(TChangeInterleaveForm, ChangeInterleaveForm); + Application.CreateForm(TCSVPrefForm, CSVPrefForm); + Application.CreateForm(TImageReportForm, ImageReportForm); Application.Run; end. diff --git a/LazarusSource/DiscImageManager.lps b/LazarusSource/DiscImageManager.lps index 025a16a..c659b78 100644 --- a/LazarusSource/DiscImageManager.lps +++ b/LazarusSource/DiscImageManager.lps @@ -4,12 +4,12 @@ - + - - + + @@ -20,8 +20,8 @@ - - + + @@ -29,9 +29,9 @@ - - - + + + @@ -41,9 +41,8 @@ - - - + + @@ -54,9 +53,9 @@ - - - + + + @@ -67,7 +66,7 @@ - + @@ -77,17 +76,17 @@ - - + + + - - - + + @@ -97,9 +96,9 @@ - - - + + + @@ -110,9 +109,9 @@ - - - + + + @@ -131,7 +130,7 @@ - + @@ -143,7 +142,7 @@ - + @@ -153,9 +152,7 @@ - - - + @@ -165,7 +162,7 @@ - + @@ -178,9 +175,9 @@ - - - + + + @@ -191,7 +188,7 @@ - + @@ -204,7 +201,7 @@ - + @@ -217,7 +214,7 @@ - + @@ -230,7 +227,7 @@ - + @@ -243,7 +240,7 @@ - + @@ -256,244 +253,270 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - + + + - - + + - + - - + + - - - + + + - - + + - + + + - - + + - - - + + - - + + - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - + - - - - - - - - + - + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - - - - @@ -505,16 +528,16 @@ - - + + - - + + - - + + diff --git a/LazarusSource/DiscImageManager.res b/LazarusSource/DiscImageManager.res index 66ee94d..0718d95 100644 Binary files a/LazarusSource/DiscImageManager.res and b/LazarusSource/DiscImageManager.res differ diff --git a/LazarusSource/DiscImage_ADFS.pas b/LazarusSource/DiscImage_ADFS.pas index d11b053..31cd46b 100644 --- a/LazarusSource/DiscImage_ADFS.pas +++ b/LazarusSource/DiscImage_ADFS.pas @@ -114,21 +114,21 @@ function TDiscImage.ID_ADFS: Boolean; if FAFSPresent then //We'll just check the end name, and set to AFS L3 if ReadString($6FB,-4)<>'Hugo' then FFormat:=diAcornFS<<4+2; end; - if(FFormat mod $10<3)or(FFormat=diAcornADFS<<4+$0E)then //ADFS S,M,L or unknown + if(GetMinorFormatNumber<3)or(FFormat=diAcornADFS<<4+$0E)then //ADFS S,M,L or unknown begin //Set the number of sectors per track - this is not held in the disc secspertrack:= 16; //Size of the sectors in bytes secsize :=256; end; - if FFormat mod $10=3 then //ADFS D + if GetMinorFormatNumber=3 then //ADFS D begin //Set the number of sectors per track - this is not held in the disc secspertrack:= 5; //Size of the sectors in bytes secsize :=1024; end; - if FFormat mod $10=$F then //ADFS Hard drive + if GetMinorFormatNumber=$F then //ADFS Hard drive begin secspertrack:= 16; secsize :=256; @@ -155,13 +155,16 @@ function TDiscImage.ID_ADFS: Boolean; if emuheader+dr_ptr+$402 then @@ -255,7 +258,7 @@ function TDiscImage.ID_ADFS: Boolean; end; end; //Return a true or false - Result:=FFormat>>4=diAcornADFS; + Result:=GetMajorFormatNumber=diAcornADFS; if Result then root_name:='$'; end; end; @@ -286,7 +289,7 @@ function TDiscImage.ReadADFSDir(dirname: String; sector: Cardinal): TDir; //Attributes - not to be confused with what is returned from OSFILE //See the function GetAttributes in DiscImageUtils OldAtts: array[0..9] of Char = ('R','W','L','D','E','r','w','e','P',' '); - NewAtts: array[0..7] of Char = ('R','W','L','D','r','w',' ',' '); + NewAtts: array[0..5] of Char = ('R','W','L','D','r','w'); begin SetLength(dirbuffer,0); RemoveControl(dirname); @@ -336,14 +339,26 @@ function TDiscImage.ReadADFSDir(dirname: String; sector: Cardinal): TDir; //New Map, so the sector will be an internal disc address if dirname=root_name then //root address begin - addr:=NewDiscAddrToOffset(rootfrag); + if rootfrag=sector then addr:=NewDiscAddrToOffset(rootfrag) + else + begin + SetLength(addr,1); + addr[0].Offset:=sector; + addr[0].Length:=root_size; + end; Result.Sector:=rootfrag; + dirsize:=addr[0].Length; end else //other object address addr:=NewDiscAddrToOffset(sector); //We need the total length of the big directory if Length(addr)>0 then - for amt:=0 to Length(addr)-1 do inc(dirsize,addr[amt].Length); + begin + if FDirType=diADFSOldDir then dirsize:=1280; + if FDirType=diADFSNewDir then dirsize:=2048; + if FDirType=diADFSBigDir then + for amt:=0 to Length(addr)-1 do inc(dirsize,addr[amt].Length); + end; end else begin @@ -357,6 +372,7 @@ function TDiscImage.ReadADFSDir(dirname: String; sector: Cardinal): TDir; //But big type directories the length varies - we worked this out above dirsize:=addr[0].Length; end; + Result.Length:=dirsize; //Read the entire directory into a buffer if ExtractFragmentedData(addr,dirsize,dirbuffer) then begin @@ -432,12 +448,22 @@ function TDiscImage.ReadADFSDir(dirname: String; sector: Cardinal): TDir; //ADFS normally refuses to list broken directories, but we will list them anyway, //just marking the directory as broken and return an error code Result.ErrorCode:=0; + //Start and End sequence numbers do not match if EndSeq<>StartSeq then Result.ErrorCode:=Result.ErrorCode OR $01; - if(FDirTypeEndName)then - Result.ErrorCode:=Result.ErrorCode OR $02; + if FDirTypeEndName then//Start and End names do not match (Hugo or Nick) + Result.ErrorCode:=Result.ErrorCode OR $02 + else //Start and End names are not valid for Old or New Directories + if((StartName<>'Hugo')and(StartName<>'Nick')) + or((EndName<>'Hugo')and(EndName<>'Nick'))then + Result.ErrorCode:=Result.ErrorCode OR $40; + //Start and End names are not valid for Big Directories if(FDirType=diADFSBigDir)and((StartName<>'SBPr') or (EndName<>'oven'))then Result.ErrorCode:=Result.ErrorCode OR $04; + //Not sector aligned + if sector mod secsize<>0 then + Result.ErrorCode:=Result.ErrorCode OR $20; Result.Broken:=Result.ErrorCode<>$00; //Check for valid directory //We won't try and get the directory structure if it appears that it is invalid @@ -481,7 +507,7 @@ function TDiscImage.ReadADFSDir(dirname: String; sector: Cardinal): TDir; endofentry:=False; if Length(Entry.Filename)>0 then begin - for amt:=0 to 9 do //Length(Entry.Filename)-1 do + for amt:=0 to 9 do begin //if ord(Entry.Filename[amt+1])>>7=1 then if ReadByte(offset+amt,dirbuffer)>>7=1 then @@ -525,12 +551,13 @@ function TDiscImage.ReadADFSDir(dirname: String; sector: Cardinal): TDir; if FDirType>diADFSOldDir then begin temp:=''; - for amt:=0 to 7 do + for amt:=0 to 5 do if IsBitSet(NewDirAtts,amt) then temp:=temp+NewAtts[amt]; //Reverse the attribute order to match actual ADFS - if Length(temp)>0 then + if Length(temp)>1 then for amt:=Length(temp) downto 1 do - Entry.Attributes:=Entry.Attributes+temp[amt];//Attributes + Entry.Attributes:=Entry.Attributes+temp[amt]; + if Length(temp)=1 then Entry.Attributes:=temp; end; //If we have a valid entry then we can see if it is filetyped/datestamped //and add it to the list @@ -564,6 +591,11 @@ function TDiscImage.ReadADFSDir(dirname: String; sector: Cardinal): TDir; end; end; Result.BeenRead:=True; + end + else + begin //Could not be read in for some other reason + if not Result.Broken then Result.ErrorCode:=Result.ErrorCode OR $10; + Result.Broken:=True; end; end; if Result.Broken then inc(brokendircount); @@ -578,8 +610,8 @@ procedure TDiscImage.ADFSCalcFileDate(var Entry: TDirEntry); rotd: Int64; begin //Only valid for New and Big directories in ADFS or SparkFS - if((FFormat>>4=diAcornADFS)and((FDirType=diADFSNewDir)or(FDirType=diADFSBigDir))) - or(FFormat>>4=diSpark)then + if((GetMajorFormatNumber=diAcornADFS)and((FDirType=diADFSNewDir)or(FDirType=diADFSBigDir))) + or(GetMajorFormatNumber=diSpark)then if Entry.LoadAddr>>20=$FFF then //Only if the top 12 bits are set begin //Get the 12 bit filetype @@ -694,28 +726,28 @@ function TDiscImage.CalculateADFSDirCheck(sector:Cardinal;buffer:TDIByteArray): -------------------------------------------------------------------------------} function TDiscImage.NewDiscAddrToOffset(addr: Cardinal;offset:Boolean=True): TFragmentArray; var - fragid : TFragmentArray; i,j,sector,id, allmap,len,off, zone,start, start_zone, zonecounter, + fragid, id_per_zone : Cardinal; const dr_size = $40; //Size of disc record + header (zone 0) header = 4; //Size of zone header only (zones >0) begin + //Reset the result Result:=nil; - sector:=0; SetLength(Result,0); if FMap then //Only works for new maps begin - if addr=0 then //Root + if(addr=0)or(addr=rootfrag)then //Root begin //We've been given the address of the root, but we know where this is so no //need to calculate it. SetLength(Result,1); - Result[0].Offset:=root;//bootmap+(nzones*secsize*2); + Result[0].Offset:=bootmap+(nzones*secsize*2); case FDirType of diADFSOldDir: Result[0].Length:=$500; diADFSNewDir: Result[0].Length:=$800; @@ -724,13 +756,17 @@ function TDiscImage.NewDiscAddrToOffset(addr: Cardinal;offset:Boolean=True): TFr end else begin - //Set up an array - SetLength(fragid,0); + //Extract the fragment ID part of the address + fragid:=(addr div $100)mod(1<=1 + if sector>=1 then dec(sector); //Go through the allocation map, looking for the fragment //First we need to know how many ids per zone there are (max) id_per_zone:=((secsize*8)-zone_spare)div(idlen+1); //Then work out the start zone - start_zone:=((addr DIV $100) mod $10000)div id_per_zone; + start_zone:=((addr DIV $100)mod(1<=allmap; end; - //Now we need to set up the result array and convert from addresses to offsets - SetLength(Result,Length(fragid)); - if Length(fragid)>0 then - for i:=0 to Length(fragid)-1 do - begin - sector:=addr mod $100; - //Sector needs to have 1 subtracted, if >=1 - if sector>=1 then dec(sector); - //Then calculate the offset - if offset then - Result[i].Offset:=(fragid[i].Offset+(sector*secsize)) - else - Result[i].Offset:=fragid[i].Offset; - //Add the length of this fragment - Result[i].Length:=fragid[i].Length; - //Add the zone of this fragment - Result[i].Zone :=fragid[i].Zone; - end; + //If offsets have been called for, then convert them + if Length(Result)>0 then + if offset then + for i:=0 to Length(Result)-1 do + Result[i].Offset:=(Result[i].Offset+(sector*secsize)); //Root indirect address if(addr=rootfrag)and(Length(Result)>1)and(nzones>1) and(Result[0].Offset=sector*secsize)then @@ -833,7 +856,7 @@ function TDiscImage.OffsetToOldDiscAddr(offset: Cardinal): Cardinal; begin Result:=offset; //ADFS L or AFS with 'INT' option - if((FFormat=diAcornADFS<<4+$02)or(FFormat>>4=diAcornFS)) + if((FFormat=diAcornADFS<<4+$02)or(GetMajorFormatNumber=diAcornFS)) and(Finterleave=2)then begin //Track Size; @@ -885,8 +908,8 @@ function TDiscImage.ByteChecksum(offset,size: Cardinal;var buffer:TDIByteArray): {------------------------------------------------------------------------------- Read ADFS Disc -------------------------------------------------------------------------------} -function TDiscImage.ReadADFSDisc: TDisc; - function ReadTheADFSDisc: TDisc; +function TDiscImage.ReadADFSDisc: Boolean; + function ReadTheADFSDisc: Boolean; type TVisit = record Sector : Cardinal; @@ -901,8 +924,9 @@ TVisit = record begin //Initialise some variables root :=$00; //Root address (set to zero so we can id the disc) - Result:=nil; - SetLength(Result,0); + FDisc:=nil; + Result:=False; + SetLength(FDisc,0); brokendircount:=0; //Read in the header information (that hasn't already been read in during //the initial checks @@ -978,11 +1002,12 @@ TVisit = record disctype :=Read32b(bootmap+$24); //Newer attributes for E+ and F+ disc_size[0]:=disc_size[0]+Read32b(bootmap+$28)*$100000000; - share_size :=1<0 then begin //The above method removes the initial system fragment, if present - root:=addr[0].Offset; + if Length(addr)>1 then + begin + i:=0; + while i<=Length(addr)-1 do + begin + if addr[i].Length=nzones*secsize*2+root_size then + root:=addr[i].Offset; + inc(i); + end; + end + else root:=addr[0].Offset; //This failed to find it, so 'guess' - we'll assume it is after the bootmap if root=0 then for d:=Length(addr)-1 downto 0 do @@ -1007,13 +1042,13 @@ TVisit = record if root>$00 then //If root is still $00, then we have failed to id the disc begin //Create an entry for the root - SetLength(Result,1); + SetLength(FDisc,1); //Blank the values - ResetDir(Result[0]); + ResetDir(FDisc[0]); //Calculate the Free Space Map ADFSFreeSpaceMap; //Read the root - Result[0]:=ReadADFSDir(root_name,root); + FDisc[0]:=ReadADFSDir(root_name,root); //Now iterate through the entries and find the sub-directories d:=0; //Add the root as a visited directory @@ -1022,50 +1057,51 @@ TVisit = record visited[0].Name:=root_name; repeat //If there are actually any entries - if Length(Result[d].Entries)>0 then + if Length(FDisc[d].Entries)>0 then begin //Go through the entries - for ptr:=0 to Length(Result[d].Entries)-1 do + for ptr:=0 to Length(FDisc[d].Entries)-1 do begin //Make sure we haven't seen this before. If a directory references a higher //directory we will end up in an infinite loop. if Length(visited)>0 then for i:=0 to Length(visited)-1 do - if(visited[i].Sector=Result[d].Entries[ptr].Sector) - and(visited[i].Name=Result[d].Entries[ptr].Filename)then - Result[d].Entries[ptr].Filename:='';//Blank off the filename so we can remove it later + if(visited[i].Sector=FDisc[d].Entries[ptr].Sector) + and(visited[i].Name=FDisc[d].Entries[ptr].Filename)then + FDisc[d].Entries[ptr].Filename:='';//Blank off the filename so we can remove it later //And add them if they are valid - if Result[d].Entries[ptr].Filename<>'' then + if FDisc[d].Entries[ptr].Filename<>'' then begin //Attribute has a 'D', so drill down - if Pos('D',Result[d].Entries[ptr].Attributes)>0 then + if Pos('D',FDisc[d].Entries[ptr].Attributes)>0 then begin //Once found, list their entries - SetLength(Result,Length(Result)+1); + SetLength(FDisc,Length(FDisc)+1); //Read in the contents of the directory if FScanSubDirs then - Result[Length(Result)-1]:=ReadADFSDir(GetParent(d) + FDisc[Length(FDisc)-1]:=ReadADFSDir(GetParent(d) +dir_sep - +Result[d].Entries[ptr].Filename, - Result[d].Entries[ptr].Sector); - Result[Length(Result)-1].Parent:=d; + +FDisc[d].Entries[ptr].Filename, + FDisc[d].Entries[ptr].Sector); + FDisc[Length(FDisc)-1].Parent:=d; //Remember it SetLength(visited,Length(visited)+1); - visited[Length(visited)-1].Sector:=Result[d].Entries[ptr].Sector; - visited[Length(visited)-1].Name:=Result[d].Entries[ptr].Filename; + visited[Length(visited)-1].Sector:=FDisc[d].Entries[ptr].Sector; + visited[Length(visited)-1].Name:=FDisc[d].Entries[ptr].Filename; //Update the directory reference - Result[d].Entries[ptr].DirRef:=Length(Result)-1; + FDisc[d].Entries[ptr].DirRef:=Length(FDisc)-1; end; end; end; end; inc(d); //The length of disc will increase as more directories are found - until d=Cardinal(Length(Result)); + until d=Cardinal(Length(FDisc)); end; + Result:=Length(FDisc)>0; end; begin - if FFormat>>4=diAcornADFS then //But only if the format is ADFS + if GetMajorFormatNumber=diAcornADFS then //But only if the format is ADFS begin //Read in the disc Result:=ReadTheADFSDisc; @@ -1083,6 +1119,7 @@ TVisit = record until(brokendircount=0)or(Finterleave=2);//Until we get back to the start end; end; + Result:=Result; end; {------------------------------------------------------------------------------- @@ -1100,7 +1137,7 @@ procedure TDiscImage.ADFSFreeSpaceMap; if not Fupdating then //Only if we are not updating begin //Update progress - UpdateProgress('Reading ADFS Free Space Map.'); +// UpdateProgress('Reading ADFS Free Space Map.'); //Reset the free space counter free_space[0]:=0; //Reset the array @@ -1122,7 +1159,7 @@ procedure TDiscImage.ADFSFreeSpaceMap; free_space_map[0,c,d]:=$FF; end; //Update progress - UpdateProgress('Reading ADFS Free Space Map..'); +// UpdateProgress('Reading ADFS Free Space Map..'); end; //Old Map (ADFS S,M,L, and D) if not FMap then @@ -1159,7 +1196,7 @@ procedure TDiscImage.ADFSFreeSpaceMap; //Move to the next entry inc(d,3); //Update progress - UpdateProgress('Reading ADFS Free Space Map..'+AddChar('.','',d div 3)); +// UpdateProgress('Reading ADFS Free Space Map..'+AddChar('.','',d div 3)); end; end; //New Map (ADFS E,E+,F,F+) @@ -1250,7 +1287,7 @@ procedure TDiscImage.ADFSFillFreeSpaceMap(address: Cardinal;usage: Byte); {------------------------------------------------------------------------------- Create ADFS blank floppy image -------------------------------------------------------------------------------} -function TDiscImage.FormatADFSFloppy(minor: Byte): TDisc; +function TDiscImage.FormatADFSFloppy(minor: Byte): Boolean; var t : Integer; dirid, @@ -1344,7 +1381,7 @@ function TDiscImage.FormatADFSFloppy(minor: Byte): TDisc; if not FMap then //Old Map FormatOldMapADFS(disctitle); if FMap then //New Map - FormatNewMapADFS(disctitle); + FormatNewMapADFS(disctitle,False); //Now write the root dirid:='$'; att:='DLR'; @@ -1404,7 +1441,7 @@ procedure TDiscImage.FormatOldMapADFS(disctitle: String); {------------------------------------------------------------------------------- Format a new map ADFS image -------------------------------------------------------------------------------} -procedure TDiscImage.FormatNewMapADFS(disctitle: String); +procedure TDiscImage.FormatNewMapADFS(disctitle: String; ide: Boolean); var log2secsize, log2bpmb : Byte; @@ -1424,6 +1461,30 @@ procedure TDiscImage.FormatNewMapADFS(disctitle: String); //Defect List Write32b($20000000,$C00); //Terminate the list Write32b($FFFFFFFF,$DAC); //Not sure - is never explained + //ADFS Hardware Information + if disc_size[0]>1638400 then //Hard drive only + begin + Write32b($00000000,$DB0); //SL + if not ide then + begin + Write32b($0D0C200A,$DB4); //GPL2 GPL3 SH GPL1 + Write16b($03FF,$DB8); //Low Current Cylinder + Write16b($0080,$DBA); //Pre Compensation Cylinder + end + else + begin + Write32b($00000000,$DB4); + Write16b($0000,$DB8); + WriteByte($00,$DBA); //LBA Flag + WriteByte($01,$DBB); //Init Flag + end; + //Parking Cylinder + t:=disc_size[0]div(secspertrack*heads*secsize); + if FDirType=diADFSBigDir then + Write32b(secspertrack*heads*(t-1),$DBC) + else + Write32b(secsize*secspertrack*heads*(t-1),$DBC); + end; //Partial Disc Record WriteByte(log2secsize,$DC0); //log2secsize WriteByte(secspertrack,$DC1); @@ -1543,7 +1604,8 @@ procedure TDiscImage.FormatNewMapADFS(disctitle: String); {------------------------------------------------------------------------------- Create ADFS blank hard disc image -------------------------------------------------------------------------------} -function TDiscImage.FormatADFSHDD(harddrivesize:Cardinal;newmap:Boolean;dirtype:Byte):TDisc; +function TDiscImage.FormatADFSHDD(harddrivesize: Cardinal; newmap: Boolean; + dirtype: Byte; ide,addheader: Boolean): Boolean; var bigmap, ok : Boolean; @@ -1551,7 +1613,9 @@ function TDiscImage.FormatADFSHDD(harddrivesize:Cardinal;newmap:Boolean;dirtype: Lzone_spare, Lnzones, Llog2bpmb, - Lroot : Cardinal; + Lroot, + Llog2secsize, + Llowsec : Cardinal; t : Byte; dirid,att : String; begin @@ -1563,6 +1627,8 @@ function TDiscImage.FormatADFSHDD(harddrivesize:Cardinal;newmap:Boolean;dirtype: Lnzones:=0; Llog2bpmb:=0; Lroot:=0; + Llog2secsize:=9; + Llowsec:=1; //Old or new map? dirtype:=dirtype mod 3;//Can only be 0, 1 or 2 if dirtype=0 then newmap:=False; //Old directory only on old map @@ -1570,8 +1636,8 @@ function TDiscImage.FormatADFSHDD(harddrivesize:Cardinal;newmap:Boolean;dirtype: harddrivesize:=512*1024*1024; //512MB max on old map //Work out the parameters based on the drive size if newmap then //But only for new map - ok:=ADFSGetHardDriveParams(harddrivesize,bigmap,Lidlen,Lzone_spare,Lnzones, - Llog2bpmb,Lroot) + ok:=ADFSGetHardDriveParams(harddrivesize,bigmap,ide,Lidlen,Lzone_spare, + Lnzones,Llog2bpmb,Lroot,Llog2secsize,Llowsec) else ok:=True; //old map is easier //Got some figures OK, so now do the formatting @@ -1589,7 +1655,7 @@ function TDiscImage.FormatADFSHDD(harddrivesize:Cardinal;newmap:Boolean;dirtype: //Set the filename imagefilename:='Untitled.'+FormatExt; //Setup the data area - emuheader:=0;//If required, put $200 in here. + if addheader then emuheader:=$200 else emuheader:=0; SetDataLength(harddrivesize+emuheader); //Fill with zeros for t:=0 to GetDataLength-1 do WriteByte(0,t); @@ -1608,12 +1674,20 @@ function TDiscImage.FormatADFSHDD(harddrivesize:Cardinal;newmap:Boolean;dirtype: nzones:=Lnzones; bpmb:=1<512*1024*1024 then big_flag:=1; end; - FormatNewMapADFS(disctitle); + FormatNewMapADFS(disctitle,ide); end; //Now write the root dirid:=root_name; @@ -1736,7 +1810,7 @@ function TDiscImage.ADFSGetFreeFragments(offset:Boolean=True): TFragmentArray; startoffset:=0; while startoffset<=Ceil(nzones/2) do begin - zone:=(zonecounter{+startzone}){mod nzones}; + zone:=zonecounter; //i is the bit counter...number of bits from the first freelink //Get the first freelink of the zone and set our counter i:=ReadBits(bootmap+(zone*secsize)+1,0,15); @@ -1762,7 +1836,7 @@ function TDiscImage.ADFSGetFreeFragments(offset:Boolean=True): TFragmentArray; //Read in the next free link number of bits - this can be idlen bits freelink:=ReadBits(bootmap+(zone*secsize)+1+(i DIV 8),i mod 8,idlen); //Now find the end of the fragment length - j:=(i+idlen)-1; //Start after the freelink + j:=i-1; //Start after the freelink repeat inc(j); //Length counter //Continue until we find a set bit @@ -1809,19 +1883,60 @@ function TDiscImage.ADFSGetFreeFragments(offset:Boolean=True): TFragmentArray; {------------------------------------------------------------------------------- Write a file to ADFS image -------------------------------------------------------------------------------} -function TDiscImage.WriteADFSFile(var file_details:TDirEntry;var buffer:TDIByteArray; - extend:Boolean=True): Integer; +function TDiscImage.WriteADFSFile(var file_details: TDirEntry; + var buffer: TDIByteArray;extend: Boolean=True): Integer; //Set extend to FALSE to ensure that the ExtendDirectory is run (Big Directory) var - dir : Integer; timestamp : Int64; - l,ref,ptr, + dir,entry, + l,ptr, freeptr, safilelen, - dest,fragid : Cardinal; + dest,fragid : Cardinal; success, spacefound : Boolean; fragments : TFragmentArray; + sharedbyte : Byte; + //Set the shared byte + procedure SetSharedByte(entrylength: Cardinal;setinitial: Boolean); + begin + //Get the sharing offset + if setinitial then + sharedbyte:=ADFSSectorAlignLength(entrylength,False)div secsize; + //If these are equal, we need to move on 1 + if ADFSSectorAlignLength(entrylength,False)=entrylength then inc(sharedbyte); + end; + //Check for shared fragment + procedure CheckForShared(start: Integer); + var + entry2 : Cardinal; + currfile: String; + begin + //Don't check the current file, as this will be the same (obvs) + if start>=0 then currfile:=FDisc[dir].Entries[start].Filename + else currfile:=''; + //Iterate through the other files in the directory + if Length(FDisc[dir].Entries)>0 then + for entry2:=0 to Length(FDisc[dir].Entries)-1 do + if (FDisc[dir].Entries[entry2].Sector>>8=fragid) + and(FDisc[dir].Entries[entry2].Sector mod$100>=sharedbyte) + and(FDisc[dir].Entries[entry2].Filename<>currfile)then + begin + //Work out the shared byte + sharedbyte:=(ADFSSectorAlignLength(FDisc[dir].Entries[entry2].Length + ,False)div secsize) + +(FDisc[dir].Entries[entry2].Sector mod $100); + //As before, if the lengths are equal, move on 1 + SetSharedByte(FDisc[dir].Entries[entry2].Length,False); + end; + //No space sharing with siblings, so reset the flag + if safilelen>fragments[0].Length-sharedbyte*secsize then + begin + sharedbyte:=$00; + SetLength(fragments,0); + end; + end; +//Main procedure begin //Is this on the AFS partition? if FAFSPresent then @@ -1847,10 +1962,11 @@ function TDiscImage.WriteADFSFile(var file_details:TDirEntry;var buffer:TDIByteA if not((file_details.Filename=root_name)and(FDirType=diADFSBigDir))then file_details.Filename:=ValidateADFSFilename(file_details.Filename); //First make sure it doesn't exist already - if(not FileExists(file_details.Parent+dir_sep+file_details.Filename,ref)) + if(not FileExists(file_details.Parent+dir_sep+file_details.Filename,dir,entry)) or((file_details.Filename=root_name)and(FDirType=diADFSBigDir))then //Get the directory where we are adding it to, and make sure it exists - if(FileExists(file_details.Parent,ref))OR(file_details.Parent=root_name)then + if(FileExists(file_details.Parent,dir,entry)) + OR(file_details.Parent=root_name)then begin if file_details.filename<>root_name then begin @@ -1859,7 +1975,7 @@ function TDiscImage.WriteADFSFile(var file_details:TDirEntry;var buffer:TDIByteA if file_details.Parent=root_name then dir :=0 else - dir :=FDisc[ref div $10000].Entries[ref mod $10000].DirRef; + dir :=FDisc[dir].Entries[entry].DirRef; //Has it been read in? if not FDisc[dir].BeenRead then ReadDirectory(file_details.Parent); //Big Dir - Verify directory is big enough or if it needs extending and moved. @@ -1872,7 +1988,7 @@ function TDiscImage.WriteADFSFile(var file_details:TDirEntry;var buffer:TDIByteA //Get the length of the file l:=Length(FDisc[dir].Entries); //Work out the "sector aligned file length" - safilelen:=Ceil(file_details.Length/secsize)*secsize; + safilelen:=ADFSSectorAlignLength(file_details.Length,False); end else safilelen:=file_details.Length; //New length of the root Result:=-4;//Catalogue full //Make sure it will actually fit on the disc @@ -1887,9 +2003,55 @@ function TDiscImage.WriteADFSFile(var file_details:TDirEntry;var buffer:TDIByteA spacefound:=False; success:=False; dest:=disc_size[0]; + sharedbyte:=$00; //Clear this to indicate we're not reusing //Find a big enough space - fragid:=0; //The function returns the fragment ID, or free pointer, with this - fragments:=ADFSFindFreeSpace(file_details.Length,fragid); + if (FMap) + and(Pos('D',file_details.Attributes)=0) + and(file_details.filename<>root_name) + and(file_details.Length<((idlen+1)*bpmb)-secsize)then + begin + //Looks like we can share this - is there any already shared we can reuse + //First check the parent - if this is '1' then see if there's any space + if (FDisc[dir].Sector mod $100=$01) + and(FDisc[dir].Directory<>root_name)then //Don't share with the root though + begin + //Get the fragments for this object + fragments:=NewDiscAddrToOffset(FDisc[dir].Sector); + //If there is not just the one, then skip + if Length(fragments)=1 then + begin + //Get the fragment ID + fragid:=FDisc[dir].Sector>>8; + //And the sharing offset + SetSharedByte(FDisc[dir].Length,True); + //And check the children for the same ID + CheckForShared(-1); + end; + end; + //No space, so go through the children and do the same checks + if(sharedbyte=$00)and(Length(FDisc[dir].Entries)>0)then + for entry:=0 to Length(FDisc[dir].Entries)-1 do + if(FDisc[dir].Entries[entry].Sector mod$100=$01) + and(entry>8; + //And the sharing offset + SetSharedByte(FDisc[dir].Entries[entry].Length,True); + CheckForShared(entry); + end; + end; + end; + //Nothing we can reuse, so look for a new one + if sharedbyte=$00 then + begin + fragid:=0; //The function returns the fragment ID, or free pointer, with this + fragments:=ADFSFindFreeSpace(file_details.Length,fragid); + end; //Set the flag, if space has been found spacefound:=Length(fragments)>0; //Need to set up some variables for old map @@ -1903,6 +2065,9 @@ function TDiscImage.WriteADFSFile(var file_details:TDirEntry;var buffer:TDIByteA if(spacefound)and(Length(fragments)>0)then begin Result:=-5;//Unknown error + //Compensate for shared fragment + if(Length(fragments)=1)and(sharedbyte>$01)then + inc(fragments[0].Offset,(sharedbyte-1)*secsize); //Write the data back to the image success:=WriteFragmentedData(fragments,buffer); //Set this marker to the start of the first offset @@ -1912,7 +2077,7 @@ function TDiscImage.WriteADFSFile(var file_details:TDirEntry;var buffer:TDIByteA if success then begin //Update the checksum, if it is a directory - if(Pos('D',file_details.Attributes)>0){or(file_details.filename='$')}then + if(Pos('D',file_details.Attributes)>0)then if FDirType>diADFSOldDir then //New/Big Directory (Old Dir has zero for checksum) WriteByte(CalculateADFSDirCheck(dest),dest+(file_details.Length-1)); //Now update the free space map @@ -1920,14 +2085,24 @@ function TDiscImage.WriteADFSFile(var file_details:TDirEntry;var buffer:TDIByteA ADFSAllocateFreeSpace(file_details.Length,freeptr); if FMap then //New map begin - file_details.Sector:=fragid*$100; //take account of the sector offset - ADFSAllocateFreeSpace(file_details.Length,fragid,fragments); + //Can this fragment be shared, if not already? + if (sharedbyte=$00) + and(Length(fragments)=1) + and(file_details.Length<(idlen+1)*bpmb) + and(fragments[0].Length-file_details.Length>secsize)then sharedbyte:=$01; + //Unless it's Big Map and is a directory (not shared because they grow) + if(FDirType=diADFSBigDir) + and(Pos('D',file_details.Attributes)>0) + and(file_details.filename<>root_name)then sharedbyte:=$00; + //Make note of the fragment ID + file_details.Sector:=fragid<<8+sharedbyte; + //Only need to allocate the free space if we're not reusing + if sharedbyte<$02 then + ADFSAllocateFreeSpace(file_details.Length,fragid,fragments); end; //Now update the directory (local copy) if file_details.filename<>root_name then begin - //Get the number of entries in the directory - ref:=Length(FDisc[dir].Entries); //Convert load/exec address into filetype and datestamp, if necessary ADFSCalcFileDate(file_details); //Now we add the entry into the directory catalogue @@ -2014,14 +2189,15 @@ function TDiscImage.ADFSFindFreeSpace(filelen: Cardinal; safilelen, i,j, freelink, - zone : Cardinal; + zone, + idperzone : Cardinal; spacefound : Boolean; fsfragments: TFragmentArray; begin Result:=nil; spacefound:=False; //Work out the "sector aligned file length" - safilelen:=Ceil(filelen/secsize)*secsize; + safilelen:=ADFSSectorAlignLength(filelen,False);//Ceil(filelen/secsize)*secsize; //Find some free space if not FMap then //Old map begin @@ -2042,6 +2218,7 @@ function TDiscImage.ADFSFindFreeSpace(filelen: Cardinal; //New map if FMap then begin + idperzone:=((secsize*8)-zone_spare)div(idlen+1); //Max IDs per zone //Get the free space fragments fsfragments:=ADFSGetFreeFragments; if Length(fsfragments)>0 then @@ -2049,7 +2226,8 @@ function TDiscImage.ADFSFindFreeSpace(filelen: Cardinal; //Go through them to find a big enough fragment i:=0; repeat - if(fsfragments[i].Length>=filelen) + if(fsfragments[i].Length>=safilelen) + and(fsfragments[i].Length>=(idlen+1)*bpmb)//Smallest possible length and(fsfragments[i].Offset+filelen0)and(fragid=0)then begin - // Now scan the zone again and find a unique ID - zone:=Result[0].Zone; //Zone of the first fragment - // IDs start here for this zone - fragid:=zone*(((secsize*8)-zone_spare)div(idlen+1)); - if fragid<3 then fragid:=3; //Can't be 0,1 or 2 - {if zone=0 then j:=3 else }j:=0; //Highest used ID in this zone - //Check each fragment ID until we find one that doesn't exist - while Length(NewDiscAddrToOffset((fragid+j)*$100))>0 do - inc(j); - inc(fragid,j); //We'll use this one + i:=0; + while(fragid=0)and(i0)and(j=1<ref then begin @@ -2162,42 +2358,54 @@ procedure TDiscImage.ADFSAllocateFreeSpace(filelen,freeptr: Cardinal); //Update the checksums WriteByte(ByteCheckSum($0000,$100),$0FF); WriteByte(ByteCheckSum($0100,$100),$1FF); + Result:=True; end; end; {------------------------------------------------------------------------------- Allocate the space in the free space map (new map) -------------------------------------------------------------------------------} -procedure TDiscImage.ADFSAllocateFreeSpace(filelen,fragid: Cardinal; - fragments: TFragmentArray); +function TDiscImage.ADFSAllocateFreeSpace(filelen,fragid: Cardinal; + fragments: TFragmentArray): Boolean; var ref, safilelen, freeptr, freelink, zone, + zoneptr, ptr,i,j : Cardinal; + zonesize : QWord; freelen : Byte; + //freelen is set to 15 bits if it is the first entry in the zone. + procedure SetFreeLen(pos: Cardinal); + begin + if pos=8 then freelen:=15 else freelen:=idlen; + end; begin + Result:=False; if FMap then //New map begin + Result:=True; //Recalculate the file length to be bpmb-aligned safilelen:=ADFSSectorAlignLength(filelen); + //Zone size, so we don't accidentally go over the end + zonesize:=((secsize*8)-zone_spare)+$20; //Take account of the 4-byte zone header //For each fragment: for i:=0 to Length(fragments)-1 do begin //Scan the zone, from the beginning, until we find a pointer to our fragment zone:=fragments[i].Zone; //Zone - freeptr:=(1+zone*secsize)*8;//Pointer to the next freelink, start at the beginning + zoneptr:=zone*secsize; //Pointer to the start of the zone + freeptr:=(1+zoneptr)*8; //Pointer to the next freelink, start at the beginning repeat //Next freelink - if freeptr-(zone*secsize*8)=8 then freelen:=15 else freelen:=idlen; - freelink:=ReadBits(bootmap+(freeptr DIV 8), - freeptr MOD 8,freelen); + SetFreeLen(freeptr-(zoneptr*8)); + freelink:=ReadBits(bootmap+(freeptr DIV 8),freeptr MOD 8,freelen); //move the pointer on inc(freeptr,freelink); //we need it as an absolute address - ref:=((freeptr-$200)-zone_spare*zone)*bpmb; + ref:=((freeptr-$200)-zone_spare*zone)*bpmb; // $200 is 64 bytes in bits //Continue until it points to our fragment until ref=fragments[i].Offset; //Make a note of the length of data we are laying down for this fragment @@ -2212,59 +2420,80 @@ procedure TDiscImage.ADFSAllocateFreeSpace(filelen,fragid: Cardinal; //Smallest object size has to be bigger than idlen if reffragments[i].Length then begin //Adjust the previous freelink to point to after this fragment - if(freeptr-(zone*secsize))-freelink=8 then freelen:=15 else freelen:=idlen; - WriteBits(freelink+ref, - bootmap+(freeptr-freelink)DIV 8,(freeptr-freelink)MOD 8, - freelen); + SetFreeLen((freeptr-zoneptr*8)-freelink); + if (((freeptr-freelink)-zoneptr*8)+freelen<=zonesize) + and(Result)then + WriteBits(freelink+ref, + bootmap+(freeptr-freelink)DIV 8,(freeptr-freelink)MOD 8, + freelen) + else Result:=False; //Reduce the freelink for this fragment by the above length and move afterwards //If it is not zero - if (freeptr-(zone*secsize))+ref=8 then freelen:=15 else freelen:=idlen; + SetFreeLen((freeptr-zoneptr*8)+ref); if ptr<>0 then if ReadBits(bootmap+(freeptr+ref)DIV 8,(freeptr+ref)MOD 8,freelen)=0 then - WriteBits(ptr-ref,bootmap+(freeptr+ref)DIV 8,(freeptr+ref)MOD 8,freelen); + if (((freeptr+ref)-zoneptr*8)+freelen<=zonesize)and(Result)then + WriteBits(ptr-ref,bootmap+(freeptr+ref)DIV 8,(freeptr+ref)MOD 8,freelen) + else Result:=False; end else //If the data we are laying down fits exactly into the space begin //Take this pointer and add it to the previous pointer - if (freeptr-(zone*secsize))-freelink=8 then freelen:=15 else freelen:=idlen; + SetFreeLen((freeptr-zoneptr*8)-freelink); //Unless this pointer is zero, then the previous will need to be zero if ptr<>0 then - WriteBits(0, - bootmap+(freeptr-freelink)DIV 8,(freeptr-freelink)MOD 8, - freelen) + begin + if (((freeptr-freelink)-zoneptr*8)+freelen<=zonesize)and(Result)then + WriteBits(0, + bootmap+(freeptr-freelink)DIV 8,(freeptr-freelink)MOD 8, + freelen) + else Result:=False; + end else - WriteBits(freelink+ptr+ref,bootmap+(freeptr-freelink)DIV 8, - (freeptr-freelink)MOD 8, - freelen); + begin + if (((freeptr-freelink)-zoneptr*8)+freelen>4=diAcornADFS then //Can't fix it if it isn't ADFS + if GetMajorFormatNumber=diAcornADFS then //Can't fix it if it isn't ADFS begin //Go through each directory and find any reported as broken if Length(FDisc)>0 then @@ -3790,7 +4020,7 @@ function TDiscImage.FixBrokenADFSDirectories: Boolean; if FDisc[FDisc[dir].Entries[entry].DirRef].Broken then Result:=FixADFSDirectory(dir,entry)or Result;//Set to true to indicate it has changed end; - FDisc:=ReadADFSDisc; //Rescan the image + ReadADFSDisc; //Rescan the image end; end; @@ -3919,8 +4149,9 @@ function TDiscImage.FixADFSDirectory(dir,entry: Integer): Boolean; {------------------------------------------------------------------------------- Calculates the parameters for a new map hard drive -------------------------------------------------------------------------------} -function TDiscImage.ADFSGetHardDriveParams(Ldiscsize:Cardinal;bigmap:Boolean; - var Lidlen,Lzone_spare,Lnzones,Llog2bpmb,Lroot: Cardinal):Boolean; +function TDiscImage.ADFSGetHardDriveParams(Ldiscsize:Cardinal;bigmap,ide:Boolean; + var Lidlen,Lzone_spare,Lnzones,Llog2bpmb,Lroot, + Llog2secsize,Llowsec: Cardinal):Boolean; //Adapted from the RISC OS RamFS ARM code procedure InitDiscRec in RamFS50 var r0,r1,r2,r3,r4,r6,r7,r8,r9,r10,r11, @@ -3938,8 +4169,18 @@ function TDiscImage.ADFSGetHardDriveParams(Ldiscsize:Cardinal;bigmap:Boolean; zone0bits=8*60; bigdirminsize=2048; newdirsize=$500; - Llog2secsize=9; //Min is 8, max is 12. HForm fixes this at 9. + //Llog2secsize=9; //Min is 8, max is 12. HForm fixes this at 9. begin + if ide then //IDE format + begin + Llog2secsize:=9; + Llowsec:=1; + end + else //ST506 format + begin + Llog2secsize:=8; + Llowsec:=0; + end; //heads should be 16, and secspertrack should be 63 Result:=False; minidlen:=Llog2secsize+3; //idlen MUST be at least log2secsize+3 @@ -4381,7 +4622,7 @@ function TDiscImage.GetADFSMaxLength(lastentry:Boolean): Cardinal; begin Result:=0; //Only for adding AFS partition to 8 bit ADFS - if(FFormat>>4=diAcornADFS)and(not FMap)and(FDirType=diADFSOldDir)then + if(GetMajorFormatNumber=diAcornADFS)and(not FMap)and(FDirType=diADFSOldDir)then begin //Is there enough space? Must be contiguous at the end fsptr:=ReadByte($1FE); //Pointer to next free space entry @@ -4407,3 +4648,78 @@ function TDiscImage.GetADFSMaxLength(lastentry:Boolean): Cardinal; end; end; end; + +{------------------------------------------------------------------------------- +Produce a report of the image's details +-------------------------------------------------------------------------------} +function TDiscImage.ADFSReport(CSV: Boolean): TStringList; +var + temp: String; +begin + Result:=TStringList.Create; + if FMap then + begin + temp:='New Map'; + if FDirType=diADFSNewDir then temp:=temp+' New Directory'; + if FDirType=diADFSBigDir then temp:=temp+' Big Directory'; + Result.Add(temp); + if not CSV then Result.Add(''); + Result.Add('Disc Record'); + if not CSV then Result.Add('-----------'); + Result.Add('Sector Size: '+IntToStr(secsize)+' bytes'); + Result.Add('Sectors per Track: '+IntToStr(secspertrack)); + Result.Add('Heads: '+IntToStr(heads)); + temp:=IntToStr(density); + case density of + 0: temp:='Hard Drive'; + 1: temp:='Single'; + 2: temp:='Double'; + 3: temp:='Double+'; + 4: temp:='Quad'; + 8: temp:='Octal'; + end; + Result.Add('Density: '+temp); + Result.Add('ID Length: '+IntToStr(idlen)+' bits'); + Result.Add('Bits per Map Byte (LFAU): '+IntToStr(bpmb)+' bytes'); + Result.Add('Skew: '+IntToStr(skew)); + temp:=IntToStr(bootoption[0]); + case bootoption[0] of + 0: temp:='None'; + 1: temp:='Load'; + 2: temp:='Run'; + 3: temp:='Exec'; + end; + Result.Add('Boot Option: '+temp); + Result.Add('Low Sector: '+IntToStr(lowsector)); + Result.Add('Number of Zones: '+IntToStr(nzones)); + Result.Add('Zone Spare Bits: '+IntToStr(zone_spare)+' bits'); + Result.Add('Root Indirect Address: 0x'+IntToHex(rootfrag,8)); + Result.Add('Disc Size: '+IntToStr(disc_size[0])+' bytes'); + Result.Add('Disc ID: 0x'+IntToHex(disc_id,4)); + Result.Add('Disc Name: '+disc_name[0]); + Result.Add('Disc Type: 0x'+IntToHex(disctype,4)); + if FDirType=diADFSBigDir then + begin + Result.Add('Share Size: 0x'+IntToHex(share_size,4)); + Result.Add('Big Flag: '+IntToStr(big_flag)); + Result.Add('Format Version: '+IntToStr(format_vers)); + Result.Add('Root Size: '+IntToStr(root_size)+' bytes'); + end; + if not CSV then Result.Add(''); + Result.Add('Root Address: 0x'+IntToHex(root,8)); + end + else + begin + temp:='Old Map'; + if FDirType=diADFSNewDir then temp:=temp+' New Directory'; + if FDirType=diADFSOldDir then temp:=temp+' Old Directory'; + Result.Add(temp); + Result.Add('Disc Size: '+IntToStr(disc_size[0])+' bytes'); + Result.Add('Disc Name: '+disc_name[0]); + Result.Add('Root Address: 0x'+IntToHex(root<<8,8)); + end; + Result.Add('Boot Map Location: 0x'+IntToHex(bootmap,8)); + Result.Add('Free Space: '+IntToStr(free_space[0])+' bytes'); + Result.Add('Cylinders: '+IntToStr(Length(free_space_map[0]))); + Result.Add('Broken Directory Count: '+IntToStr(brokendircount)); +end; diff --git a/LazarusSource/DiscImage_AFS.pas b/LazarusSource/DiscImage_AFS.pas index 49f12ba..d4b9e0e 100644 --- a/LazarusSource/DiscImage_AFS.pas +++ b/LazarusSource/DiscImage_AFS.pas @@ -80,13 +80,13 @@ function TDiscImage.ID_AFS: Boolean; end else FFormat:=diInvalidImg; //Headers not matching, no format end else FFormat:=diInvalidImg; //No header ID, invalid image end; - Result:=FFormat>>4=diAcornFS; + Result:=GetMajorFormatNumber=diAcornFS; end; var start: Byte; begin Result:=False; - if(FFormat=diInvalidImg)or(FFormat>>4=diAcornFS)then + if(FFormat=diInvalidImg)or(GetMajorFormatNumber=diAcornFS)then begin //Interleaving, depending on the option Finterleave:=FForceInter; @@ -119,10 +119,10 @@ procedure TDiscImage.ReadAFSPartition; begin visited:=nil; //Is this an ADFS disc with Acorn FileStore partition? - if((FFormat>>4=diAcornADFS)and(FAFSPresent)) - or(FFormat>>4=diAcornFS)then + if((GetMajorFormatNumber=diAcornADFS)and(FAFSPresent)) + or(GetMajorFormatNumber=diAcornFS)then begin - if FFormat>>4=diAcornADFS then + if GetMajorFormatNumber=diAcornADFS then begin afshead:=Read24b($0F6)*secsize; afshead2:=Read24b($1F6)*secsize; @@ -139,7 +139,7 @@ procedure TDiscImage.ReadAFSPartition; if FFormat=diAcornFS<<4+2 then //Level 3 disc_size[0]:=Read24b(afshead+$16)*secsize; i:=0; - if FFormat>>4=diAcornADFS then //Level 3/ADFS Hybrid + if GetMajorFormatNumber=diAcornADFS then //Level 3/ADFS Hybrid begin SetLength(disc_size,2); SetLength(free_space,2); @@ -163,7 +163,7 @@ procedure TDiscImage.ReadAFSPartition; d:=Length(FDisc); SetLength(FDisc,d+1); //Start the chain by reading the root - if FFormat>>4=diAcornADFS then startdir:=afsrootname else startdir:='$'; + if GetMajorFormatNumber=diAcornADFS then startdir:=afsrootname else startdir:='$'; FDisc[d]:=ReadAFSDirectory(startdir,allocmap); //Add the root as a visited directory SetLength(visited,1); @@ -277,7 +277,7 @@ function TDiscImage.ReadAFSDirectory(dirname:String;addr: Cardinal):TDir; //And set the partition flag to true Result.AFSPartition:=True; //ADFS hybrid? - if FFormat>>4=diAcornADFS then side:=1 else side:=0; + if GetMajorFormatNumber=diAcornADFS then side:=1 else side:=0; Result.Partition:=side; //Directory title Result.Directory:=ReadString($03,-10,buffer); @@ -386,7 +386,7 @@ function TDiscImage.ReadAFSObject(offset: Cardinal): TDIByteArray; Result:=nil; //Make sure it is a valid allocation map for Level 3 if(ReadString(offset,-6)='JesMap') - and((FFormat=diAcornFS<<4+2)or(FFormat>>4=diAcornADFS))then + and((FFormat=diAcornFS<<4+2)or(GetMajorFormatNumber=diAcornADFS))then begin //Start of the list ptr:=$0A; @@ -480,7 +480,7 @@ function TDiscImage.GetAFSObjLength(offset: Cardinal): Cardinal; end; //Level 3 if(ReadString(offset,-6)='JesMap') - and((FFormat=diAcornFS<<4+2)or(FFormat>>4=diAcornADFS))then + and((FFormat=diAcornFS<<4+2)or(GetMajorFormatNumber=diAcornADFS))then begin lenLSB:=ReadByte(offset+$08); //LSB of the length ptr:=$0A; @@ -524,7 +524,7 @@ function TDiscImage.GetAllocationMap(sector:Cardinal;var spt:Cardinal):Cardinal; if ReadByte(Read24b(afshead+$1B)*secsize)>ReadByte(Read24b(afshead+$1E)*secsize)then Result:=Read24b(afshead+$1B)*secsize else Result:=Read24b(afshead+$1E)*secsize; //Level 3 and Hybrid - if(FFormat=diAcornFS<<4+2)or(FFormat>>4=diAcornADFS)then + if(FFormat=diAcornFS<<4+2)or(GetMajorFormatNumber=diAcornADFS)then begin spt:=Read16b(afshead+$1A); //Level 3 @@ -575,18 +575,18 @@ procedure TDiscImage.ReadAFSFSM; //Get all the used sectors fragments:=AFSGetFreeSectors(True); //Initialise the variables - if FFormat>>4=diAcornFS then //AFS Level 2 and 3 + if GetMajorFormatNumber=diAcornFS then //AFS Level 2 and 3 begin SetLength(free_space_map,1); part:=0; end; - if FFormat>>4=diAcornADFS then //Hybrids - AFS will take up the second 'side' + if GetMajorFormatNumber=diAcornADFS then //Hybrids - AFS will take up the second 'side' begin SetLength(free_space_map,2); part:=1; end; //Get the local sectors per track - if(FFormat>>4=diAcornADFS)or(FFormat=diAcornFS<<4+2)then + if(GetMajorFormatNumber=diAcornADFS)or(FFormat=diAcornFS<<4+2)then spt:=Read16b(afshead+$1A) else spt:=secspertrack; //Initialise the free space free_space[part]:=disc_size[part]; @@ -811,11 +811,11 @@ function TDiscImage.AFSAllocateFreeSpace(size :Cardinal; if Length(FSM)>0 then begin //The above array will have offsets relative to the start of the AFS partition - if FFormat>>4=diAcornADFS then + if GetMajorFormatNumber=diAcornADFS then for index:=0 to Length(FSM)-1 do inc(FSM[index].Offset,disc_size[0]); //So add the ADFS size to the offset //Level 3 includes a 256 byte object header - if((FFormat=diAcornFS<<4+2)or(FFormat>>4=diAcornADFS)) + if((FFormat=diAcornFS<<4+2)or(GetMajorFormatNumber=diAcornADFS)) and(addheader)then inc(size,$100); //Are there any that will fit the data without fragmenting? index:=0; @@ -910,7 +910,7 @@ function TDiscImage.AFSAllocateFreeSpace(size :Cardinal; //Mark as written WriteBits(1,allocmap+(alloc[index].Offset*2)+6,7,1); end; - if(FFormat=diAcornFS<<4+2)or(FFormat>>4=diAcornADFS)then + if(FFormat=diAcornFS<<4+2)or(GetMajorFormatNumber=diAcornADFS)then begin for fragsize:=0 to Ceil(alloc[index].Length/secsize)-1 do begin @@ -961,7 +961,7 @@ procedure TDiscImage.AFSDeAllocateFreeSpace(addr: Cardinal); FinaliseAFSL2Map; end; //Level 3 and Hybrid - if(FFormat=diAcornFS<<4+2)or(FFormat>>4=diAcornADFS)then + if(FFormat=diAcornFS<<4+2)or(GetMajorFormatNumber=diAcornADFS)then begin addr:=addr*secsize; if ReadString(addr,-6)='JesMap' then //Make sure it is a valid block @@ -1390,7 +1390,7 @@ function TDiscImage.CreateAFSDirectory(dirname,parent,attributes: String): Integ //Copy of cycle number at end of root WriteByte(cycle,dirsize-1,buffer); //First entry at $11 (pointer is $FFFF to indicate parent), for Level 3 - if(FFormat>>4=diAcornADFS)or(FFormat=diAcornFS<<4+2)then + if(GetMajorFormatNumber=diAcornADFS)or(FFormat=diAcornFS<<4+2)then begin //Get the parent directory address if dirname<>'$' then @@ -1413,7 +1413,7 @@ function TDiscImage.CreateAFSDirectory(dirname,parent,attributes: String): Integ begin //Then we'll just copy the data across and we're done addr:=Fafsroot; - if(FFormat>>4=diAcornADFS)or(FFormat=diAcornFS<<4+2)then addr:=Fafsroot+$100; + if(GetMajorFormatNumber=diAcornADFS)or(FFormat=diAcornFS<<4+2)then addr:=Fafsroot+$100; for ptr:=0 to Length(buffer)-1 do WriteByte(buffer[ptr],addr+ptr); //Replace with bulk copy function Result:=0; @@ -1454,7 +1454,7 @@ function TDiscImage.ExtendAFSDirectory(sector: Cardinal):Cardinal; buffer:=nil; l3offset:=0; //Take account of the JesMap header - if(FFormat=diAcornFS<<4+2)or(FFormat>>4=diAcornADFS)then l3offset:=secsize; + if(FFormat=diAcornFS<<4+2)or(GetMajorFormatNumber=diAcornADFS)then l3offset:=secsize; //Only continue if the directory needs extending if Read16b(sector*secsize+$0D+l3offset)=0 then begin @@ -1482,7 +1482,7 @@ function TDiscImage.ExtendAFSDirectory(sector: Cardinal):Cardinal; //Set the directory's next free chain to point into here Write16b(addr,$0D,buffer); //Level 3 : Update the JesMap pointers to include this - if(FFormat=diAcornFS<<4+2)or(FFormat>>4=diAcornADFS)then + if(FFormat=diAcornFS<<4+2)or(GetMajorFormatNumber=diAcornADFS)then begin //Start of chain addr:=$0A-5; @@ -1560,8 +1560,8 @@ function TDiscImage.CreateAFSPassword(Accounts: TUserAccounts): Integer; afslevel: Byte; begin afslevel:=0; - if FFormat>>4=diAcornFS then afslevel:=(FFormat AND$F)+1; - if FFormat>>4=diAcornADFS then afslevel:=3; + if GetMajorFormatNumber=diAcornFS then afslevel:=(FFormat AND$F)+1; + if GetMajorFormatNumber=diAcornADFS then afslevel:=3; //Default response Result:=-5; buffer:=nil; //To stop 'hints' or 'warnings' from the compiler @@ -1641,7 +1641,7 @@ function TDiscImage.CreateAFSPassword(Accounts: TUserAccounts): Integer; //Set up the file entry ResetDirEntry(newentry); newentry.Filename:='Passwords'; - if FFormat>>4=diAcornADFS then + if GetMajorFormatNumber=diAcornADFS then begin newentry.Parent :=afsrootname; newentry.Side :=1; @@ -1676,7 +1676,7 @@ function TDiscImage.ReadAFSPassword: TUserAccounts; //Start with a blank array Result:=nil; //Get the full pathname for the password file - if FFormat>>4=diAcornADFS then pwordfile:=afsrootname+dir_sep+'Passwords' + if GetMajorFormatNumber=diAcornADFS then pwordfile:=afsrootname+dir_sep+'Passwords' else pwordfile:=FDisc[0].Directory+dir_sep+'Passwords'; //Make sure it exists if FileExists(pwordfile,dir,entry) then @@ -1815,7 +1815,7 @@ function TDiscImage.WriteAFSFile(var file_details: TDirEntry; //Set the date file_details.TimeStamp:=Floor(Now); //Write the file data - if(FFormat=diAcornFS<<4+2)or(FFormat>>4=diAcornADFS)then + if(FFormat=diAcornFS<<4+2)or(GetMajorFormatNumber=diAcornADFS)then begin //First, for level 3, we'll need a header SetLength(block,$100); @@ -1892,7 +1892,7 @@ procedure TDiscImage.WriteAFSObject(offset: Cardinal;var buffer: TDIByteArray); begin //Make sure it is a valid allocation map for Level 3 if(ReadString(offset,-6)='JesMap') - and((FFormat=diAcornFS<<4+2)or(FFormat>>4=diAcornADFS))then + and((FFormat=diAcornFS<<4+2)or(GetMajorFormatNumber=diAcornADFS))then begin //Start of the list ptr:=$0A; @@ -1974,7 +1974,7 @@ function TDiscImage.RenameAFSFile(oldname:String;var newname: String): Integer; FDisc[FDisc[dir].Entries[entry].DirRef].Directory:=newname; //Get the address of the directory ptr:=FDisc[dir].Entries[entry].Sector*secsize; - if(FFormat>>4=diAcornADFS)or(FFormat=diAcornFS<<4+2)then //Level 3 + if(GetMajorFormatNumber=diAcornADFS)or(FFormat=diAcornFS<<4+2)then //Level 3 ptr:=Read24b(ptr+$0A)*secsize; //And write it to the header WriteString(newname,ptr+$03,10,32); @@ -2391,7 +2391,7 @@ function TDiscImage.UpdateAFSDiscTitle(title: String): Boolean; //Make sure it is not overlength title:=LeftStr(title,16); //And update the internal variable - if FFormat>>4=diAcornADFS then disc_name[1]:=title else disc_name[0]:=title; + if GetMajorFormatNumber=diAcornADFS then disc_name[1]:=title else disc_name[0]:=title; //Write to the image header WriteString(title,afshead+4,16,32); //And the copy @@ -2414,7 +2414,7 @@ function TDiscImage.AddAFSPartition(size: Cardinal): Boolean; Result:=False; if size<9*secsize then exit; //Minimum size is 9 sectors //Only for adding AFS partition to 8 bit ADFS - if(FFormat>>4=diAcornADFS)and(not FMap)and(FDirType=diADFSOldDir)then + if(GetMajorFormatNumber=diAcornADFS)and(not FMap)and(FDirType=diADFSOldDir)then begin fsed:=GetADFSMaxLength(False); //Is there enough free space? @@ -2463,3 +2463,29 @@ function TDiscImage.AddAFSPartition(size: Cardinal): Boolean; end; end; end; + +{------------------------------------------------------------------------------- +Produce a report of the image's details +-------------------------------------------------------------------------------} +function TDiscImage.AFSReport(CSV: Boolean): TStringList; +var + side: Integer; +begin + Result:=TStringList.Create; + side:=0; + if GetMajorFormatNumber=diAcornADFS then + begin + side:=1; + if not CSV then Result.Add(''); + Result.Add('Acorn FS partition'); + if not CSV then Result.Add('------------------'); + end; + Result.Add('Sector Size: '+IntToStr(secsize)+' bytes'); + Result.Add('Sectors per Track: '+IntToStr(secspertrack)); + Result.Add('Root Address: 0x'+IntToHex(Fafsroot,8)); + Result.Add('Root Size: '+IntToStr(afsroot_size)+' bytes'); + Result.Add('Disc Size: '+IntToStr(disc_size[side])+' bytes'); + Result.Add('Free Space: '+IntToStr(free_space[side])+' bytes'); + Result.Add('Boot Map Location: 0x'+IntToHex(afshead,8)); + Result.Add('Disc Name: '+disc_name[side]); +end; diff --git a/LazarusSource/DiscImage_Amiga.pas b/LazarusSource/DiscImage_Amiga.pas index 3b13e85..7059d06 100644 --- a/LazarusSource/DiscImage_Amiga.pas +++ b/LazarusSource/DiscImage_Amiga.pas @@ -32,7 +32,7 @@ function TDiscImage.ID_Amiga: Boolean; FDirType :=$00; //Get more details from the boot block disc ID FMap :=IsBitSet(ReadByte($03),0); //AmigaDOS OFS/FFS - FDirType:=(ReadByte($03) AND $4)shr 2; //AmigaDOS DIRC + FDirType:=(ReadByte($03) AND $4)<<2; //AmigaDOS DIRC //Look at the checksum if not FMap then //OFS should have a checksum begin @@ -111,7 +111,7 @@ function TDiscImage.ID_Amiga: Boolean; ResetVariables; end; end; - Result:=FFormat>>4=diAmiga; + Result:=GetMajorFormatNumber=diAmiga; end; end; end; @@ -119,14 +119,14 @@ function TDiscImage.ID_Amiga: Boolean; {------------------------------------------------------------------------------- Read Commodore Amiga Disc -------------------------------------------------------------------------------} -function TDiscImage.ReadAmigaDisc: TDisc; +function TDiscImage.ReadAmigaDisc: Boolean; var d,ptr, sectors : Integer; begin - Result:=nil; + FDisc:=nil; //Initialise some variables - SetLength(Result,0); + SetLength(FDisc,0); if FFormat<>diInvalidImg then begin //Total number of sectors will be double where the root is @@ -136,44 +136,45 @@ function TDiscImage.ReadAmigaDisc: TDisc; //Disc name disc_name[0]:=ReadString(root*secsize+$1B1,-(root*secsize+$1B0)); //Create an entry for the root - SetLength(Result,1); + SetLength(FDisc,1); //Blank the values - ResetDir(Result[0]); + ResetDir(FDisc[0]); //We'll start by reading the root - Result[0]:=ReadAmigaDir(root_name,root); + FDisc[0]:=ReadAmigaDir(root_name,root); //Now iterate through the entries and find the sub-directories d:=0; repeat //If there are actually any entries - if Length(Result[d].Entries)>0 then + if Length(FDisc[d].Entries)>0 then begin //Go through the entries - for ptr:=0 to Length(Result[d].Entries)-1 do + for ptr:=0 to Length(FDisc[d].Entries)-1 do //And add them if they are valid - if Result[d].Entries[ptr].Filename<>'' then + if FDisc[d].Entries[ptr].Filename<>'' then begin //Attribute has a 'F', so drill down - if Pos('F',Result[d].Entries[ptr].Attributes)>0 then + if Pos('F',FDisc[d].Entries[ptr].Attributes)>0 then begin //Once found, list their entries - SetLength(Result,Length(Result)+1); + SetLength(FDisc,Length(FDisc)+1); //Read in the contents of the directory if FScanSubDirs then - Result[Length(Result)-1]:=ReadAmigaDir(GetParent(d)+dir_sep - +Result[d].Entries[ptr].Filename, - Result[d].Entries[ptr].Sector); - Result[Length(Result)-1].Parent:=d; + FDisc[Length(FDisc)-1]:=ReadAmigaDir(GetParent(d)+dir_sep + +FDisc[d].Entries[ptr].Filename, + FDisc[d].Entries[ptr].Sector); + FDisc[Length(FDisc)-1].Parent:=d; //Update the directory reference - Result[d].Entries[ptr].DirRef:=Length(Result)-1; + FDisc[d].Entries[ptr].DirRef:=Length(FDisc)-1; end; end; end; inc(d); //The length of disc will increase as more directories are found - until d>=Length(Result); + until d>=Length(FDisc); //Get the free space map ReadAmigaFSM; end; + Result:=Length(FDisc)>0; end; {------------------------------------------------------------------------------- @@ -582,7 +583,7 @@ function TDiscImage.CreateAmigaDirectory(var dirname,parent,attributes: String): {------------------------------------------------------------------------------- Create a new Amiga image - Floppy -------------------------------------------------------------------------------} -function TDiscImage.FormatAmigaFDD(minor: Byte): TDisc; +function TDiscImage.FormatAmigaFDD(minor: Byte): Boolean; begin //Blank everything ResetVariables; @@ -590,8 +591,8 @@ function TDiscImage.FormatAmigaFDD(minor: Byte): TDisc; //Set the format //FFormat:=diAmiga<<4+minor; //Start with blank result - Result:=nil; - SetLength(Result,0); + FDisc:=nil; + SetLength(FDisc,0); //Format the drive case minor of 0: FormatAmiga(880*1024); @@ -606,7 +607,7 @@ function TDiscImage.FormatAmigaFDD(minor: Byte): TDisc; {------------------------------------------------------------------------------- Create a new Amiga image - Hard Disc -------------------------------------------------------------------------------} -function TDiscImage.FormatAmigaHDD(harddrivesize: Cardinal): TDisc; +function TDiscImage.FormatAmigaHDD(harddrivesize: Cardinal): Boolean; begin //Blank everything ResetVariables; @@ -614,8 +615,8 @@ function TDiscImage.FormatAmigaHDD(harddrivesize: Cardinal): TDisc; //Set the format //FFormat:=diAmiga<<4+$F; //Start with blank result - Result:=nil; - SetLength(Result,0); + FDisc:=nil; + SetLength(FDisc,0); //Format the drive FormatAmiga(harddrivesize); //Read it back in to set the rest up @@ -936,7 +937,7 @@ procedure TDiscImage.ReadAmigaFSM; c,d : Cardinal; bit : Byte; begin - UpdateProgress('Reading Free Space Map'); + //UpdateProgress('Reading Free Space Map'); //Set up the variables free_space[0]:=0; secspertrack:=22;//Not used anywhere else @@ -1395,3 +1396,31 @@ procedure TDiscImage.ValidateAmigaFile(var filename: String); //If nothing was supplied, then supply something if Length(filename)=0 then filename:='Unnamed'; end; + +{------------------------------------------------------------------------------- +Produce a report of the image's details +-------------------------------------------------------------------------------} +function TDiscImage.AmigaReport(CSV: Boolean): TStringList; +var + temp: String; +begin + Result:=TStringList.Create; + if FMap then temp:='Fast File System' else temp:='Original File System'; + if FDirType=diAmigaDir then temp:=temp+' AmigaDOS Directory'; + if FDirType=diAmigaCache then temp:=temp+' AmigaDOS Directory Cache'; + Result.Add(temp); + Result.Add('Sector Size: '+IntToStr(secsize)+' bytes'); + temp:=IntToStr(density); + case density of + 0: temp:='Hard Drive'; + 1: temp:='Single'; + 2: temp:='Double'; + 4: temp:='Quad'; + 8: temp:='Octal'; + end; + Result.Add('Density: '+temp); + Result.Add('Root Address: 0x'+IntToHex(root,8)); + Result.Add('Disc Size: '+IntToStr(disc_size[0])+' bytes'); + Result.Add('Free Space: '+IntToStr(free_space[0])+' bytes'); + Result.Add('Disc Name: '+disc_name[0]); +end; diff --git a/LazarusSource/DiscImage_C64.pas b/LazarusSource/DiscImage_C64.pas index 168c8ab..b4d62f7 100644 --- a/LazarusSource/DiscImage_C64.pas +++ b/LazarusSource/DiscImage_C64.pas @@ -80,8 +80,8 @@ function TDiscImage.ID_CDR: Boolean; //Successful checks if ctr=16 then FFormat:=diCommodore<<4+2; //1581 end; - FDSD :=(FFormat mod$10>0)and(FFormat mod$10<$F); //Set/reset the DoubleSided flag - Result:=FFormat>>4=diCommodore; //Return TRUE if succesful ID + FDSD :=(GetMinorFormatNumber>0)and(GetMinorFormatNumber<$F); //Set/reset the DoubleSided flag + Result:=GetMajorFormatNumber=diCommodore; //Return TRUE if succesful ID If Result then FMap:=False; //and reset the NewMap flag end; end; @@ -135,7 +135,7 @@ function TDiscImage.ConvertDxxTS(format,track,sector: Integer): Integer; {------------------------------------------------------------------------------- Read Commodore Disc -------------------------------------------------------------------------------} -function TDiscImage.ReadCDRDisc: TDisc; +function TDiscImage.ReadCDRDisc: Boolean; var ptr,t,s,amt, file_chain, @@ -143,9 +143,9 @@ function TDiscImage.ReadCDRDisc: TDisc; ch,c,f,dirTr :Integer; temp : String; begin - Result:=nil; - SetLength(Result,1); - ResetDir(Result[0]); + FDisc:=nil; + SetLength(FDisc,1); + ResetDir(FDisc[0]); //Get the format f:=FFormat AND $F; //'f' is the format - 0: D64, 1: D71, 2: D81 dirTr:=18; //D64 and D71 disc info is on track 18, sector 0 @@ -179,9 +179,9 @@ function TDiscImage.ReadCDRDisc: TDisc; ptr:=ConvertDxxTS(f,t,s); amt:=0; //Set the root directory name - Result[0].Directory:=root_name; - Result[0].Sector:=root; - Result[0].BeenRead:=True; + FDisc[0].Directory:=root_name; + FDisc[0].Sector:=root; + FDisc[0].BeenRead:=True; repeat //Track/Sector for next link or 00/FF for end t:=ReadByte(ptr); @@ -189,34 +189,34 @@ function TDiscImage.ReadCDRDisc: TDisc; for c:=0 to 7 do if ReadByte(ptr+(c*$20)+2)>$00 then begin - SetLength(Result[0].Entries,amt+1); - ResetDirEntry(Result[0].Entries[amt]); - Result[0].Entries[amt].Parent:=root_name; + SetLength(FDisc[0].Entries,amt+1); + ResetDirEntry(FDisc[0].Entries[amt]); + FDisc[0].Entries[amt].Parent:=root_name; //First track/sector of Fdata - Result[0].Entries[amt].Track :=ReadByte(ptr+(c*$20)+3); - Result[0].Entries[amt].Sector:=ReadByte(ptr+(c*$20)+4); + FDisc[0].Entries[amt].Track :=ReadByte(ptr+(c*$20)+3); + FDisc[0].Entries[amt].Sector:=ReadByte(ptr+(c*$20)+4); //Filetype - Result[0].Entries[amt].ShortFiletype:= + FDisc[0].Entries[amt].ShortFiletype:= LeftStr(CDRFileTypes[ReadByte(ptr+(c*$20)+2) AND $0F],3); - Result[0].Entries[amt].Filetype:= + FDisc[0].Entries[amt].Filetype:= Copy(CDRFileTypes[ReadByte(ptr+(c*$20)+2) AND $0F],4); //Attributes if (ReadByte(ptr+(c*$20)+2) AND $40)=$40 then //Locked - Result[0].Entries[amt].Attributes:=Result[0].Entries[amt].Attributes+'L'; + FDisc[0].Entries[amt].Attributes:=FDisc[0].Entries[amt].Attributes+'L'; if (ReadByte(ptr+(c*$20)+2) AND $80)=$80 then // Closed - Result[0].Entries[amt].Attributes:=Result[0].Entries[amt].Attributes+'C'; + FDisc[0].Entries[amt].Attributes:=FDisc[0].Entries[amt].Attributes+'C'; //Length of file - in sectors - Result[0].Entries[amt].Length:=Read16b(ptr+(c*$20)+$1E); + FDisc[0].Entries[amt].Length:=Read16b(ptr+(c*$20)+$1E); //now follow the chain to find the exact file length} file_ptr:=ConvertDxxTS(f, - Result[0].Entries[amt].Track,Result[0].Entries[amt].Sector); //first sector + FDisc[0].Entries[amt].Track,FDisc[0].Entries[amt].Sector); //first sector //Now read the rest of the chain - for file_chain:=1 to Result[0].Entries[amt].Length-1 do + for file_chain:=1 to FDisc[0].Entries[amt].Length-1 do file_ptr:=ConvertDxxTS(f,ReadByte(file_ptr),ReadByte(file_ptr+1)); //and get the partial usage of final sector if ReadByte(file_ptr)=$00 then - Result[0].Entries[amt].Length:= - ((Result[0].Entries[amt].Length-1)*254)+ReadByte(file_ptr+1)-1; + FDisc[0].Entries[amt].Length:= + ((FDisc[0].Entries[amt].Length-1)*254)+ReadByte(file_ptr+1)-1; //Filename temp:=''; for ch:=0 to 15 do @@ -224,20 +224,21 @@ function TDiscImage.ReadCDRDisc: TDisc; p:=ReadByte(ptr+(c*$20)+5+ch); if (p>32) and (p<>$A0) then temp:=temp+chr(p AND $7F); end; - Result[0].Entries[amt].Filename:=temp; + FDisc[0].Entries[amt].Filename:=temp; //Not a directory - not used by D64/D71/D81 - Result[0].Entries[amt].DirRef:=-1; + FDisc[0].Entries[amt].DirRef:=-1; inc(amt); end; //If not end of directory, go to next block if (t<>$00) and (s<>$FF) then ptr:=ConvertDxxTS(f,t,s); until (t=$00) and (s=$FF); + Result:=Length(FDisc)>0; end; {------------------------------------------------------------------------------- Create a new, blank, disc -------------------------------------------------------------------------------} -function TDiscImage.FormatCDR(minor: Byte): TDisc; +function TDiscImage.FormatCDR(minor: Byte): Boolean; var t,i : Integer; c : Byte; @@ -376,7 +377,8 @@ function TDiscImage.FormatCDR(minor: Byte): TDisc; //First directory entry WriteByte($FF,$61B01); end; - Result:=ReadCDRDisc; + ReadCDRDisc; + Result:=Length(FDisc)>0; end; {------------------------------------------------------------------------------- @@ -491,7 +493,7 @@ procedure TDiscImage.CDRSetClearBAM(track,sector: Byte;used: Boolean); ptr, ptr1 : Cardinal; begin - f:=FFormat mod $10; + f:=GetMinorFormatNumber; //Pointer to BAM number of free sectors: //ptr will be the number of free sectors //ptr1 will be the allocation bits for the track @@ -543,8 +545,8 @@ function TDiscImage.UpdateCDRDiscTitle(title: String): Boolean; begin disc_name[0]:=title; //Get the location of the disc title, less one - if FFormat mod $10<2 then ptr:=ConvertDxxTS(FFormat mod $10,18,0)+$8F; - if FFormat mod $10=2 then ptr:=ConvertDxxTS(FFormat mod $10,40,0)+$03; + if GetMinorFormatNumber<2 then ptr:=ConvertDxxTS(GetMinorFormatNumber,18,0)+$8F; + if GetMinorFormatNumber=2 then ptr:=ConvertDxxTS(GetMinorFormatNumber,40,0)+$03; //Fill the 16 bytes for i:=1 to 16 do begin @@ -627,7 +629,7 @@ function TDiscImage.WriteCDRFile(file_details: TDirEntry; count:=file_details.Length; if count>0 then //Make sure that there is something to write begin - f:=FFormat MOD $10; //Minor format (sub format) + f:=GetMinorFormatNumber; //Minor format (sub format) //Overwrite the parent file_details.Parent:=root_name; //Check that the filename is valid @@ -770,7 +772,7 @@ procedure TDiscImage.UpdateCDRCat; sector:=1; //Sector where the first directory is maxsector:=19; //Maximum sectors on this track //1581 track and sector number - if FFormat mod $10=2 then + if GetMinorFormatNumber=2 then begin track:=40; sector:=3; @@ -804,7 +806,7 @@ procedure TDiscImage.UpdateCDRCat; free_space_map[0,track-1,dirsector]:=$FE; sectors[i DIV 8]:=dirsector; end; - ptr:=ConvertDxxTS(FFormat mod $10,track,dirsector)+(i MOD 8)*$20; + ptr:=ConvertDxxTS(GetMinorFormatNumber,track,dirsector)+(i MOD 8)*$20; //t/s link to next directory - they are all 00/00 except for the first WriteByte($00,ptr ); //Track WriteByte($00,ptr+1); //Sector @@ -862,7 +864,7 @@ procedure TDiscImage.UpdateCDRCat; for i:=0 to j do begin //Pointer to this directory - ptr:=ConvertDxxTS(FFormat mod $10,track,sectors[i]); + ptr:=ConvertDxxTS(GetMinorFormatNumber,track,sectors[i]); if i=j then WriteByte($00,ptr) else WriteByte(track,ptr); WriteByte(sectors[i+1],ptr+1); //Update the BAM for the system track @@ -921,7 +923,7 @@ function TDiscImage.CDRFindNextTrack(var track,sector: Byte): Boolean; freesecs : Boolean; begin //Number of sides - also, number of times to repeat main loop - if FFormat mod $10=1 then cycles:=2 else cycles:=1; + if GetMinorFormatNumber=1 then cycles:=2 else cycles:=1; //Counter for distance from root counter:=1; repeat @@ -931,7 +933,7 @@ function TDiscImage.CDRFindNextTrack(var track,sector: Byte): Boolean; while i$00 do begin CDRSetClearBAM(track,sector,False); - ptr:=ConvertDxxTS(FFormat MOD$10,track,sector); + ptr:=ConvertDxxTS(GetMinorFormatNumber,track,sector); track:=ReadByte(ptr); sector:=ReadByte(ptr+1); for i:=0 to $FF do WriteByte($00,ptr+i);// Delete the data @@ -1071,3 +1073,17 @@ function TDiscImage.UpdateCDRFileAttributes(filename,attributes: String):Boolean Result:=True; end; end; + +{------------------------------------------------------------------------------- +Produce a report of the image's details +-------------------------------------------------------------------------------} +function TDiscImage.CDRReport(CSV: Boolean): TStringList; +begin + Result:=TStringList.Create; + if FDSD then Result.Add('Double Sided') else Result.Add('Single Sided'); + Result.Add('Disc Size: '+IntToStr(disc_size[0])+' bytes'); + Result.Add('Free Space: '+IntToStr(free_space[0])+' bytes'); + Result.Add('Disc Name: '+disc_name[0]); + Result.Add('Root Address: 0x'+IntToHex(root,8)); + Result.Add('Tracks: '+IntToStr(Length(free_space_map[0]))); +end; diff --git a/LazarusSource/DiscImage_CFS.pas b/LazarusSource/DiscImage_CFS.pas index c701679..180fb5c 100644 --- a/LazarusSource/DiscImage_CFS.pas +++ b/LazarusSource/DiscImage_CFS.pas @@ -31,7 +31,7 @@ function TDiscImage.ID_CFS: Boolean; {------------------------------------------------------------------------------- Read in and decode the file -------------------------------------------------------------------------------} -function TDiscImage.ReadUEFFile: TDisc; +function TDiscImage.ReadUEFFile: Boolean; var i,j : Integer; filenum, @@ -52,10 +52,10 @@ function TDiscImage.ReadUEFFile: TDisc; crcok : Boolean; dummy : TDIByteArray; begin - Result:=nil; + FDisc:=nil; SetLength(dummy,0); //Set up the TDisc structure for return - Result:=FormatCFS; + FormatCFS; { SetLength(Result,1); ResetDir(Result[0]); //Set the root directory name @@ -106,54 +106,54 @@ function TDiscImage.ReadUEFFile: TDisc; //Sometimes a file has no filename, so give it one if temp='' then temp:='?'; //Create a new entry in our array, if need be - if filenum>=Length(Result[0].Entries) then + if filenum>=Length(FDisc[0].Entries) then begin //If the last file failed CRC checks on any block, clear the data if (not crcok) and (Length(FilesData)>0) then SetLength(FilesData[filenum],0); //Now create the entry for this file - SetLength(Result[0].Entries,filenum+1); - ResetDirEntry(Result[0].Entries[filenum]); - Result[0].Entries[filenum].Length :=0; //Length counter - Result[0].Entries[filenum].Filename:=FilenameToASCII(temp);//Filename - Result[0].Entries[filenum].Sector :=pos-6; //Where to find it (first block) - Result[0].Entries[filenum].Parent :=Result[0].Directory; - Result[0].Entries[filenum].DirRef :=-1; + SetLength(FDisc[0].Entries,filenum+1); + ResetDirEntry(FDisc[0].Entries[filenum]); + FDisc[0].Entries[filenum].Length :=0; //Length counter + FDisc[0].Entries[filenum].Filename:=FilenameToASCII(temp);//Filename + FDisc[0].Entries[filenum].Sector :=pos-6; //Where to find it (first block) + FDisc[0].Entries[filenum].Parent :=FDisc[0].Directory; + FDisc[0].Entries[filenum].DirRef :=-1; SetLength(FilesData,filenum+1); firstblck:=True; //Read in the load address - Result[0].Entries[filenum].LoadAddr:=Read32b(pos+i); + FDisc[0].Entries[filenum].LoadAddr:=Read32b(pos+i); //Read in the execution address - Result[0].Entries[filenum].ExecAddr:=Read32b(pos+i+4); + FDisc[0].Entries[filenum].ExecAddr:=Read32b(pos+i+4); //CRC Checks crcok:=True; end; //Read in the block number blocknum:=Read16b(pos+i+8); //Is it a new block, or copy protection? - if(blocknum>0)and(firstblck)and(Length(Result[0].Entries)>1)then + if(blocknum>0)and(firstblck)and(Length(FDisc[0].Entries)>1)then if (lastblock=blocknum-1) - and(Result[0].Entries[filenum-1].Filename=Result[0].Entries[filenum].Filename) + and(FDisc[0].Entries[filenum-1].Filename=FDisc[0].Entries[filenum].Filename) {and(files[filenum-1].LoadAddr=files[filenum].LoadAddr) and(files[filenum-1].ExecAddr=files[filenum].ExecAddr)}then begin - SetLength(Result[0].Entries,Length(Result[0].Entries)-1); + SetLength(FDisc[0].Entries,Length(FDisc[0].Entries)-1); dec(filenum); firstblck:=False; end; lastblock:=blocknum; //Take a note of where we are in the file's data, as we build it up - ptr:=Result[0].Entries[filenum].Length; + ptr:=FDisc[0].Entries[filenum].Length; //Get the length of this block blocklen:=Read16B(pos+i+10); //And add it to the total length - inc(Result[0].Entries[filenum].Length,blocklen); + inc(FDisc[0].Entries[filenum].Length,blocklen); //Get the block status blockst:=ReadByte(pos+i+12); if IsBitSet(blockst,0) then - Result[0].Entries[filenum].Attributes:='L' + FDisc[0].Entries[filenum].Attributes:='L' else - Result[0].Entries[filenum].Attributes:=''; + FDisc[0].Entries[filenum].Attributes:=''; //Get the CRC16 value for the header headcrc:=Read16b(pos+i+17); //Check it is valid @@ -161,7 +161,7 @@ function TDiscImage.ReadUEFFile: TDisc; //Move our chunk pointer onto the data inc(i,19);//Points to the data //Increase the file's data length to match the total length, so far - SetLength(FilesData[filenum],Result[0].Entries[filenum].Length); + SetLength(FilesData[filenum],FDisc[0].Entries[filenum].Length); //And copy in the data in this block for j:=0 to blocklen-1 do FilesData[filenum][ptr+j]:=ReadByte(pos+i+j); //Move to after the data @@ -181,6 +181,7 @@ function TDiscImage.ReadUEFFile: TDisc; //Move our offset pointer to the next chunk inc(pos,chunklen); end; + Result:=Length(FDisc)>0; end; {------------------------------------------------------------------------------- @@ -395,20 +396,21 @@ procedure TDiscImage.WriteUEFFile(filename: String;uncompress: Boolean=False); {------------------------------------------------------------------------------- Create a new, empty, UEF file for CFS -------------------------------------------------------------------------------} -function TDiscImage.FormatCFS:TDisc; +function TDiscImage.FormatCFS: Boolean; begin - Result:=nil; + FDisc:=nil; //Set up the TDisc structure for return - SetLength(Result,1); - ResetDir(Result[0]); + SetLength(FDisc,1); + ResetDir(FDisc[0]); //Set the root directory name root_name:='tape'; - Result[0].Directory:=root_name; - Result[0].BeenRead:=True; + FDisc[0].Directory:=root_name; + FDisc[0].BeenRead:=True; //Set the format FFormat:=diAcornUEF<<4; //Set the filename imagefilename:='Untitled.'+FormatExt; + Result:=True; end; {------------------------------------------------------------------------------- diff --git a/LazarusSource/DiscImage_DFS.pas b/LazarusSource/DiscImage_DFS.pas index d0cc251..2140014 100644 --- a/LazarusSource/DiscImage_DFS.pas +++ b/LazarusSource/DiscImage_DFS.pas @@ -15,7 +15,7 @@ function TDiscImage.ID_DFS: Boolean; begin ResetVariables; //Is there actually any data? - if GetDataLength>0 then + if(GetDataLength>0)and(GetDataLength<=400*1024)then begin chk:=True; //Offset 0x0001 should have 9 bytes >31 @@ -164,7 +164,7 @@ function TDiscImage.ID_DFS: Boolean; for i:=0 to 3 do if ReadByte($0300+i)=$00 then inc(c); if c=12 then - if FFormat>>4=diAcornDFS then + if GetMajorFormatNumber=diAcornDFS then t0:=1;//Set side 1 to Watford //Now we check side 2 if dbl then @@ -177,7 +177,7 @@ function TDiscImage.ID_DFS: Boolean; for i:=0 to 3 do if ReadByte($0D00+i)=$00 then inc(c); if c=12 then - if FFormat>>4=diAcornDFS then + if GetMajorFormatNumber=diAcornDFS then t1:=1;//Set side 1 to Watford end; //Determine the format @@ -202,7 +202,7 @@ function TDiscImage.ID_DFS: Boolean; end; end; end; - Result:=FFormat>>4=diAcornDFS; + Result:=GetMajorFormatNumber=diAcornDFS; end; {------------------------------------------------------------------------------- @@ -226,7 +226,7 @@ function TDiscImage.ConvertDFSSector(address,side: Integer): Integer; Result:=(((sector MOD 10)+(20*(sector DIV 10))+(10*side))*$100)+offset; end; //MMB - if FFormat>>4=diMMFS then + if GetMajorFormatNumber=diMMFS then begin if(side<0)or(side>511)then side:=0; Result:=Result+side*$32000+$2000; @@ -236,7 +236,7 @@ function TDiscImage.ConvertDFSSector(address,side: Integer): Integer; {------------------------------------------------------------------------------- Read Acorn DFS Disc -------------------------------------------------------------------------------} -function TDiscImage.ReadDFSDisc(mmbdisc:Integer=-1): TDisc; +function TDiscImage.ReadDFSDisc(mmbdisc:Integer=-1): Boolean; var s,t,f, locked, @@ -244,44 +244,47 @@ function TDiscImage.ReadDFSDisc(mmbdisc:Integer=-1): TDisc; diroff : Integer; temp : String; begin - Result:=nil; + Result:=False; + FDisc:=nil; //Determine how many sides if FDSD then //Double sided image begin - SetLength(Result,2); + SetLength(FDisc,2); SetLength(bootoption,2); SetLength(disc_size,2); SetLength(disc_name,2); + //SetLength(FPartitions,2); end else //Single sided image begin - SetLength(Result,1); + SetLength(FDisc,1); SetLength(bootoption,1); SetLength(free_space,1); SetLength(disc_name,1); + //SetLength(FPartitions,1); end; //Used by MMB. For DFS, this should be 0 if(mmbdisc<0)or(mmbdisc>511)then mmbdisc:=0; s:=mmbdisc; repeat - ResetDir(Result[s-mmbdisc]); + ResetDir(FDisc[s-mmbdisc]); //Number of entries on disc side t:=ReadByte(ConvertDFSSector($105,s)) div 8; - if(FFormat mod$10>$1)and(FFormat mod$10<$4)then //Extra files on Watford DFS + if(GetMinorFormatNumber>$1)and(GetMinorFormatNumber<$4)then //Extra files on Watford DFS inc(t,ReadByte(ConvertDFSSector($305,s))div 8); - SetLength(Result[s-mmbdisc].Entries,t); + SetLength(FDisc[s-mmbdisc].Entries,t); //Directory name - as DFS only has $, this will be the drive number + '$' - Result[s-mmbdisc].Directory:=':'+IntToStr(s*2)+dir_sep+root_name; - Result[s-mmbdisc].Partition:=s; - Result[s-mmbdisc].BeenRead :=True; + FDisc[s-mmbdisc].Directory:=':'+IntToStr(s*2)+dir_sep+root_name; + FDisc[s-mmbdisc].Partition:=s; + FDisc[s-mmbdisc].BeenRead :=True; //Get the disc title(s) - Result[s-mmbdisc].Title:=ReadString(ConvertDFSSector($000,s),-8) + FDisc[s-mmbdisc].Title:=ReadString(ConvertDFSSector($000,s),-8) +ReadString(ConvertDFSSector($100,s),-4); - RemoveSpaces(Result[s-mmbdisc].Title); - RemoveControl(Result[s-mmbdisc].Title); - disc_name[s]:=Result[s-mmbdisc].Title; + RemoveSpaces(FDisc[s-mmbdisc].Title); + RemoveControl(FDisc[s-mmbdisc].Title); + disc_name[s]:=FDisc[s-mmbdisc].Title; //Boot Option - if FFormat>>4=diAcornDFS then + if GetMajorFormatNumber=diAcornDFS then bootoption[s]:=(ReadByte(ConvertDFSSector($106,s))AND$30)>>4; //Disc Size disc_size[s]:=(ReadByte(ConvertDFSSector($107,s)) @@ -292,11 +295,11 @@ function TDiscImage.ReadDFSDisc(mmbdisc:Integer=-1): TDisc; for f:=1 to t do begin //Reset the variables - ResetDirEntry(Result[s-mmbdisc].Entries[f-1]); + ResetDirEntry(FDisc[s-mmbdisc].Entries[f-1]); //Is it a Watford, and are we in the Watford area? diroff:=$000; ptr:=f; - if(FFormat mod$10=2)or(FFormat mod$10=3)then + if(GetMinorFormatNumber=2)or(GetMinorFormatNumber=3)then if (f>31) then begin diroff:=$200; @@ -306,54 +309,60 @@ function TDiscImage.ReadDFSDisc(mmbdisc:Integer=-1): TDisc; temp:=ReadString(ConvertDFSSector(diroff+($08*ptr),s),-7); RemoveTopBit(temp); //Attributes are in the top bit RemoveSpaces(temp); //Remove extraneous spaces - Result[s-mmbdisc].Entries[f-1].Filename:=temp; + FDisc[s-mmbdisc].Entries[f-1].Filename:=temp; //Get the directory character temp:=chr(ReadByte(ConvertDFSSector(diroff+($08*ptr)+7,s))AND$7F); if temp=' 'then temp:=root_name; //Acorn Atom DOS root is ' ' //If the directory is not root, add it to the filename if temp<>root_name then - Result[s-mmbdisc].Entries[f-1].Filename:=temp+dir_sep - +Result[s-mmbdisc].Entries[f-1].Filename; + FDisc[s-mmbdisc].Entries[f-1].Filename:=temp+dir_sep + +FDisc[s-mmbdisc].Entries[f-1].Filename; //Make up a parent directory pathname so this can be found - Result[s-mmbdisc].Entries[f-1].Parent:=':'+IntToStr(s*2)+dir_sep+root_name; + FDisc[s-mmbdisc].Entries[f-1].Parent:=':'+IntToStr(s*2)+dir_sep+root_name; //Is it locked? This is actually the top bit of the final filename character locked:=(ReadByte(ConvertDFSSector(diroff+($08*ptr)+7,s))AND$80)>>7; if locked=1 then - Result[s-mmbdisc].Entries[f-1].Attributes:='L' + FDisc[s-mmbdisc].Entries[f-1].Attributes:='L' else - Result[s-mmbdisc].Entries[f-1].Attributes:=''; + FDisc[s-mmbdisc].Entries[f-1].Attributes:=''; //Load address - need to multiply bits 16/17 by $55 to expand it to 8 bits - Result[s-mmbdisc].Entries[f-1].LoadAddr:= + FDisc[s-mmbdisc].Entries[f-1].LoadAddr:= (((ReadByte(ConvertDFSSector(diroff+$106+($08*ptr),s))AND$0C)<<14)*$55) +Read16b( ConvertDFSSector(diroff+$100+($08*ptr),s)); //Execution address - need to multiply bits 16/17 by $55 to expand it to 8 bits - Result[s-mmbdisc].Entries[f-1].ExecAddr:= + FDisc[s-mmbdisc].Entries[f-1].ExecAddr:= (((ReadByte(ConvertDFSSector(diroff+$106+($08*ptr),s))AND$C0)<<10)*$55) + Read16b( ConvertDFSSector(diroff+$102+($08*ptr),s)); //Length - Result[s-mmbdisc].Entries[f-1].Length:= + FDisc[s-mmbdisc].Entries[f-1].Length:= (((ReadByte(ConvertDFSSector(diroff+$106+($08*ptr),s))AND$30)<<12)) + Read16b( ConvertDFSSector(diroff+$104+($08*ptr),s)); //Sector of start of data - Result[s-mmbdisc].Entries[f-1].Sector:= + FDisc[s-mmbdisc].Entries[f-1].Sector:= ((ReadByte(ConvertDFSSector(diroff+$106+($08*ptr),s))AND$03)<<8) +ReadByte(ConvertDFSSector(diroff+$107+($08*ptr),s)); //Which side it is on - Result[s-mmbdisc].Entries[f-1].Side:=s; + FDisc[s-mmbdisc].Entries[f-1].Side:=s; //Not a directory - not used in DFS - Result[s-mmbdisc].Entries[f-1].DirRef:=-1; + FDisc[s-mmbdisc].Entries[f-1].DirRef:=-1; end; //Next side if(FFormat AND $1=1)then inc(s) else s:=2+mmbdisc; + {FPartitions[s-mmbdisc].Directories:=Result; + FPartitions[s-mmbdisc].DirSep:=dir_sep; + FPartitions[s-mmbdisc].Format:=diAcornDFS; + FPartitions[s-mmbdisc].Title:=disc_name[s]; + FPartitions[s-mmbdisc].RootName:=root_name;} until s=2+mmbdisc; //Free Space Map (not MMB) - if FFormat>>4=diAcornDFS then DFSFreeSpaceMap(Result); + if GetMajorFormatNumber=diAcornDFS then DFSFreeSpaceMap; + Result:=Length(FDisc)>0; end; {------------------------------------------------------------------------------- Update the DFS Free Space Map, and update the free space counter -------------------------------------------------------------------------------} -procedure TDiscImage.DFSFreeSpaceMap(LDisc: TDisc); +procedure TDiscImage.DFSFreeSpaceMap; var f,s,c,e,fs: Cardinal; begin @@ -389,20 +398,22 @@ procedure TDiscImage.DFSFreeSpaceMap(LDisc: TDisc); free_space_map[s,0,2]:=$FE; free_space_map[s,0,3]:=$FE; end; - if Length(LDisc[s].Entries)>0 then - for e:=0 to Length(LDisc[s].Entries)-1 do + if Length(FDisc[s].Entries)>0 then + for e:=0 to Length(FDisc[s].Entries)-1 do begin - inc(free_space[s],(LDisc[s].Entries[e].Length div $100)*$100); - if LDisc[s].Entries[e].Length mod $100>0 then inc(free_space[s],$100); + inc(free_space[s],(FDisc[s].Entries[e].Length div $100)*$100); + if FDisc[s].Entries[e].Length mod $100>0 then inc(free_space[s],$100); //Add it to the free space map - c:=LDisc[s].Entries[e].Length div $100; - if LDisc[s].Entries[e].Length mod $100>0 then inc(c); + c:=FDisc[s].Entries[e].Length div $100; + if FDisc[s].Entries[e].Length mod $100>0 then inc(c); if c>0 then //Take care of zero length files for fs:=0 to c-1 do - if(LDisc[s].Entries[e].Sector+fs)div 100; end; {------------------------------------------------------------------------------- @@ -979,13 +991,13 @@ function TDiscImage.AddDFSSide(filename: String): Boolean; if SysUtils.FileExists(filename) then begin //Only for Acorn DFS - if(FFormat>>4=diAcornDFS)and(not FDSD)then //Single sided images only + if(GetMajorFormatNumber=diAcornDFS)and(not FDSD)then //Single sided images only begin //Pre-load the proposed image NewImage:=TDiscImage.Create; NewImage.LoadFromFile(filename,False); //And make sure it is a DFS SS image - if (NewImage.FormatNumber>>4=diAcornDFS) + if (NewImage.MajorFormatNumber=diAcornDFS) and(not NewImage.DoubleSided)then begin //Load the file in @@ -1017,3 +1029,35 @@ function TDiscImage.MoveDFSFile(filename,directory: String): Integer; //We just need to delete the original once copied if Result>-1 then DeleteFile(oldfn); end; + +{------------------------------------------------------------------------------- +Produce a report of the image's details +-------------------------------------------------------------------------------} +function TDiscImage.DFSReport(CSV: Boolean): TStringList; +var + temp: String; + side: Integer; +begin + Result:=TStringList.Create; + if FDSD then Result.Add('Double Sided') else Result.Add('Single Sided'); + side:=0; + while side>4=diDOSPlus; + Result:=GetMajorFormatNumber=diDOSPlus; end; end; @@ -109,14 +109,14 @@ procedure TDiscImage.ReadDOSPartition; part : Byte; begin //Is this an ADFS disc with DOS Plus partition? - if((FFormat>>4=diAcornADFS)and(FDOSPresent)) - or(FFormat>>4=diDOSPlus)then //Or a straight DOS Plus? + if((GetMajorFormatNumber=diAcornADFS)and(FDOSPresent)) + or(GetMajorFormatNumber=diDOSPlus)then //Or a straight DOS Plus? begin i:=0; part:=0; - if FFormat>>4=diDOSPlus then dir_sep:='\'; + if GetMajorFormatNumber=diDOSPlus then dir_sep:='\'; //ADFS Hybrid? - if FFormat>>4=diAcornADFS then + if GetMajorFormatNumber=diAcornADFS then begin part:=1; //Set up the second partition @@ -308,7 +308,7 @@ function TDiscImage.ReadDOSDirectory(dirname: String;addr: Cardinal; //Don't need the parent or self referrals if(RightStr(dirname,1)='.')or(RightStr(dirname,2)='..')then exit; //ADFS hybrid? - if FFormat>>4=diAcornADFS then side:=1 else side:=0; + if GetMajorFormatNumber=diAcornADFS then side:=1 else side:=0; //Directory name index:=Pos(GetDirSep(side),dirname); while Pos(GetDirSep(side),dirname,index+1)>index do @@ -776,12 +776,12 @@ procedure TDiscImage.ReadDOSFSM; //Get all the used sectors fragments:=DOSGetFreeSectors(True); //Initialise the variables - if FFormat>>4=diDOSPlus then + if GetMajorFormatNumber=diDOSPlus then begin SetLength(free_space_map,1); part:=0; end; - if FFormat>>4=diAcornADFS then //Hybrids - DOS will take up the second 'side' + if GetMajorFormatNumber=diAcornADFS then //Hybrids - DOS will take up the second 'side' begin SetLength(free_space_map,2); part:=1; @@ -896,7 +896,7 @@ function TDiscImage.RenameDOSFile(oldname:String;var newname: String): Integer; side : Byte; begin //ADFS hybrid? - if FFormat>>4=diAcornADFS then side:=1 else side:=0; + if GetMajorFormatNumber=diAcornADFS then side:=1 else side:=0; Result:=-2; //Original file does not exist //Validate the filename newname:=ValidateDOSFilename(newname); @@ -1354,7 +1354,7 @@ function TDiscImage.WriteDOSFile(var file_details: TDirEntry; dir:=0; entry:=0; //ADFS hybrid? - if FFormat>>4=diAcornADFS then partition:=1 else partition:=0; + if GetMajorFormatNumber=diAcornADFS then partition:=1 else partition:=0; SetLength(fragments,0); //Start with a negative result Result:=-3;//File already exists @@ -1374,7 +1374,7 @@ function TDiscImage.WriteDOSFile(var file_details: TDirEntry; end; //Validate the proposed filename (ADFS rules the same as AFS) if(file_details.Filename<>dosrootname) - and((FFormat>>4=diAcornADFS)or(FFormat=diDOSPlus))then //ADFS partition or DOS Plus + and((GetMajorFormatNumber=diAcornADFS)or(FFormat=diDOSPlus))then //ADFS partition or DOS Plus file_details.Filename:=ValidateDOSFilename(file_details.Filename); //First make sure it doesn't exist already if not FileExists(file_details.Parent+GetDirSep(partition)+file_details.Filename,pdir,entry)then @@ -1479,7 +1479,7 @@ function TDiscImage.CreateDOSDirectory(dirname,parent,attributes: String): Integ side : Byte; begin //ADFS hybrid? - if FFormat>>4=diAcornADFS then side:=1 else side:=0; + if GetMajorFormatNumber=diAcornADFS then side:=1 else side:=0; Result:=-3; //Directory already exists ok:=False; //Make sure that the directory does not already exists @@ -1547,7 +1547,7 @@ function TDiscImage.DeleteDOSFile(filename: String): Boolean; side : Byte; begin //ADFS hybrid? - if FFormat>>4=diAcornADFS then side:=1 else side:=0; + if GetMajorFormatNumber=diAcornADFS then side:=1 else side:=0; Result:=False; //Make sure the file exists, and is not the root if filename<>dosrootname then @@ -1678,7 +1678,7 @@ function TDiscImage.AddDOSPartition(size: Cardinal): Boolean; Result:=False; if size<9*secsize then exit; //Minimum size is 9 sectors //Only for adding DOS partition to 8 bit ADFS - if(FFormat>>4=diAcornADFS)and(not FMap)and(FDirType=diADFSOldDir)then + if(GetMajorFormatNumber=diAcornADFS)and(not FMap)and(FDirType=diADFSOldDir)then begin fsed:=GetADFSMaxLength(False); //Is there enough free space? @@ -1729,7 +1729,7 @@ function TDiscImage.AddDOSPartition(size: Cardinal): Boolean; {------------------------------------------------------------------------------- Create a new DOS Plus or DOS image -------------------------------------------------------------------------------} -function TDiscImage.FormatDOS(size: QWord;fat: Byte): TDisc; +function TDiscImage.FormatDOS(size: QWord;fat: Byte): Boolean; var index: Cardinal; const @@ -1738,7 +1738,8 @@ function TDiscImage.FormatDOS(size: QWord;fat: Byte): TDisc; GB = 1024*1024*1024; begin //Default return value - Result:=nil; + Result:=False; + FDisc:=nil; //Ensure that we don't create an illegal shape //Min sizes - these are Microsoft specs if size< 180*KB then size:= 180*KB; // 180KB @@ -1761,15 +1762,16 @@ function TDiscImage.FormatDOS(size: QWord;fat: Byte): TDisc; UpdateProgress('Formatting...'); for index:=0 to size-1 do WriteByte(0,index); //Master 512 DOS Plus or Standard DOS? - if(size<>800*1024)and(size<>640*1024)then //Standard DOS + if(size<>800*KB)and(size<>640*KB)then //Standard DOS WriteDOSHeader(0,size,fat,True) else begin //Master 512 //BBC Master DOS Plus images do not have a DOS header, which makes them easier. - if size=640*1024 then //Master 512 hybrid image + if size=640*KB then //Master 512 hybrid image begin //Create the ADFS image (ADFS 'L') - Result:=FormatADFSFloppy(2); + FormatADFSFloppy(2); + Result:=Length(FDisc)>0; //Create the 636K DOS partition for index:=0 to $1FA do WriteByte(0,index);//Most of the ADFS header needs blanked Write24b($000AA0,$FC); //Signature for 640K DOS Plus @@ -1779,7 +1781,7 @@ function TDiscImage.FormatDOS(size: QWord;fat: Byte): TDisc; WriteByte(ByteCheckSum($0000,$100),$0FF);//Checksum sector 0 WriteByte(ByteCheckSum($0100,$100),$1FF);//Checksum sector 1 end; - if size=800*1024 then //Master 512 800K image + if size=800*KB then //Master 512 800K image begin //Very simplistic this format. WriteByte($FD,0); //Media descriptor byte @@ -1794,7 +1796,7 @@ function TDiscImage.FormatDOS(size: QWord;fat: Byte): TDisc; //And read it in ReadImage; //Returning a result - Result:=FDisc; + Result:=Length(FDisc)>0; //Set the filename imagefilename:='Untitled.'+FormatExt; end; @@ -1909,7 +1911,7 @@ procedure TDiscImage.WriteDOSHeader(offset, size: QWord;fat: Byte; if totBlocks<=$FFFF then //If it'll fit in the original BIOS block Write16b(totBlocks,offset+$13,buffer) else //Otherwise it'll need to go in the extended block - only for FAT16 & FAT32 - Write32b(totBlocks,offset+$20,buffer); + if(fat=diFAT16)or(fat=diFAT32)then Write32b(totBlocks,offset+$20,buffer); //Media descriptor byte WriteByte(mdb,offset+$15,buffer); //FAT Size @@ -2071,10 +2073,10 @@ function TDiscImage.DOSShortFilename(path,LFN: String;SFN :String=''): String; begin Result:=''; //Validate the path exists, and check for DOS FAT12, 16 or 32 - if(FileExists(path,dir,entry))and(FFormat>>4=diDOSPlus)then + if(FileExists(path,dir,entry))and(GetMajorFormatNumber=diDOSPlus)then begin //DOS Plus - then just return the shortened, validated, filename - if FFormat mod$10=0 then + if GetMinorFormatNumber=0 then begin Result:=ValidateDOSFilename(LFN); exit; @@ -2132,3 +2134,36 @@ function TDiscImage.BuildDOSFilename(f,e: String): String; Result:=f; if Length(e)>0 then Result:=Result+'.'+e; end; + +{------------------------------------------------------------------------------- +Produce a report of the image's details +-------------------------------------------------------------------------------} +function TDiscImage.DOSReport(CSV: Boolean): TStringList; +var + side: Integer; +begin + Result:=TStringList.Create; + side:=0; + if GetMajorFormatNumber=diAcornADFS then + begin + if not CSV then Result.Add(''); + Result.Add('DOS Plus partition'); + if not CSV then Result.Add('------------------'); + side:=1; + end; + Result.Add('Sector Size: '+IntToStr(secsize)+' bytes'); + Result.Add('Sectors per Track: '+IntToStr(secspertrack)); + Result.Add('Root Address: 0x'+IntToHex(Fdosroot,8)); + Result.Add('Root Size: '+IntToStr(dosroot_size)+' bytes'); + Result.Add('Disc Size: '+IntToStr(disc_size[side])+' bytes'); + Result.Add('Free Space: '+IntToStr(free_space[side])+' bytes'); + Result.Add('Boot Map Location: 0x'+IntToHex(doshead,8)); + Result.Add('FAT Location: 0x'+IntToHex(dosmap,8)); + Result.Add('FAT Size: '+IntToStr(DOSFATSize*secsize)+' bytes'); + Result.Add('Number of FATs: '+IntToStr(NumFATs)); + Result.Add('Cluster Size: '+IntToStr(cluster_size)+' bytes'); + Result.Add('Allocation Unit: '+IntToStr(dosalloc)+' blocks'); + Result.Add('Number of Blocks: '+IntToStr(DOSblocks)); + Result.Add('Reserved Sectors: '+IntToStr(DOSResSecs)); + Result.Add('Disc Name: '+disc_name[side]); +end; diff --git a/LazarusSource/DiscImage_MMB.pas b/LazarusSource/DiscImage_MMB.pas index 9f4ffda..6823fab 100644 --- a/LazarusSource/DiscImage_MMB.pas +++ b/LazarusSource/DiscImage_MMB.pas @@ -4,10 +4,11 @@ Identifies an MMB -------------------------------------------------------------------------------} function TDiscImage.ID_MMB: Boolean; -var - c,i,ptr : Integer; +{var + c,i,ptr : Integer;} begin - if FFormat=diInvalidImg then + Result:=False; +{ if FFormat=diInvalidImg then begin ResetVariables; //Check it is of the correct length (511 images * 200K + 0x2000 bytes) @@ -24,22 +25,22 @@ function TDiscImage.ID_MMB: Boolean; if c<511 then FFormat:=diInvalidImg; end; end; - Result:=FFormat>>4=diMMFS; + Result:=GetMajorFormatNumber=diMMFS;} end; {------------------------------------------------------------------------------- Read MMB file -------------------------------------------------------------------------------} -function TDiscImage.ReadMMBDisc: TDisc; -var +function TDiscImage.ReadMMBDisc: Boolean; +{var i,j, dir : Integer; ptr : Cardinal; c : Byte; - d : TDisc; + d : TDisc; } begin - Result:=nil; - //511 entries in an MMB file + Result:=False; +{ //511 entries in an MMB file SetLength(Result,511); //Now go through them and read them in for i:=0 to 510 do @@ -75,5 +76,5 @@ function TDiscImage.ReadMMBDisc: TDisc; Result[i].Directory:=IntToStr(i)+': empty'; end; disc_name[0]:='MMFS File'; - disc_size[0]:=511; + disc_size[0]:=511; } end; diff --git a/LazarusSource/DiscImage_Private.pas b/LazarusSource/DiscImage_Private.pas index 6dc1ee1..e88aa2e 100644 --- a/LazarusSource/DiscImage_Private.pas +++ b/LazarusSource/DiscImage_Private.pas @@ -63,6 +63,45 @@ procedure TDiscImage.ResetVariables; Fupdating :=False; end; +{------------------------------------------------------------------------------- +Reset the partition to match the global variables +-------------------------------------------------------------------------------} +procedure TDiscImage.AddPartition; +var + part: Integer; +begin + part:=Length(FPartitions); + SetLength(FPartitions,part+1); + FPartitions[part].Directories:=FDisc; + FPartitions[part].Title:=disc_name[part]; +// FPartitions[part].RootTitle:= + FPartitions[part].RootName:=root_name; + FPartitions[part].DirSep:=dir_sep; +// FPartitions[part].HeaderAddr:= +// FPartitions[part].FSMAddr +{ FreeSpaceMap : array of TTrack; //The free space map + DOSVolInRoot : Boolean; //Volume name is stored in the root (DOS) + RootAddress, //Offset of the root + SectorSize, //Sector Size + DOSalloc, //Allocation Unit (DOS Plus) + Version, //Format version + Root_size, //Size of the root directory + DOSBlocks, //Size of the DOS partition in blocks + DOSCluster_size : Cardinal; //Size of a DOS cluster + FreeSpace, //Amount of free space in bytes + PartitionSize : QWord; //Size of the partition in bytes + Format, //Major format of this partition + DOSFATSize, //Size of DOS Plus FAT in blocks + DOSResSecs : Word; //Number of reserved blocks + SecsPerTrack, //Number of sectors per track + Heads, //Number of heads (Acorn ADFS New) + Density, //Density (Acorn ADFS New) + DOSFATType, //FAT Type - 12: FAT12, 16: FAT16, 32: FAT32 + DOSNumFATs, //Number of FATs in a DOS Plus image + AmigaMapType : Byte; //OFS/FFS/PFS/OFS +} +end; + {------------------------------------------------------------------------------- Extract a string from ptr to the next chr(term) or length(-term) -------------------------------------------------------------------------------} @@ -162,12 +201,12 @@ function TDiscImage.FormatToString: String; ('Plus','FAT12','FAT16','FAT32','','','','','','','','','','','','')); begin Result:=''; - if FFormat>>4<=High(FS) then - if FFormat mod $10<=High(SUB[FFormat>>4]) then + if GetMajorFormatNumber<=High(FS) then + if GetMinorFormatNumber<=High(SUB[GetMajorFormatNumber]) then begin - Result:= FS[FFormat>>4]; - if SUB[FFormat>>4,FFormat MOD $10]<>'' then - Result:=Result+' '+SUB[FFormat>>4,FFormat MOD $10]; + Result:= FS[GetMajorFormatNumber]; + if SUB[GetMajorFormatNumber,GetMinorFormatNumber]<>'' then + Result:=Result+' '+SUB[GetMajorFormatNumber,GetMinorFormatNumber]; end; //ADFS with AFS partition if(FFormat=diAcornADFS<<4+ 2)and(FAFSPresent)then @@ -197,9 +236,25 @@ function TDiscImage.FormatToExt: String; ('img','fat12','fat16','fat32','','','','','','','','','','','',''));//DOS and DOS Plus begin Result:='img'; - if FFormat>>4<=High(EXT) then - if FFormat mod $10<=High(EXT[FFormat>>4]) then - Result:=EXT[FFormat>>4,FFormat MOD $10]; + if GetMajorFormatNumber<=High(EXT) then + if GetMinorFormatNumber<=High(EXT[GetMajorFormatNumber]) then + Result:=EXT[GetMajorFormatNumber,GetMinorFormatNumber]; +end; + +{------------------------------------------------------------------------------- +Get the major format number +-------------------------------------------------------------------------------} +function TDiscImage.GetMajorFormatNumber: Word; +begin + Result:=FFormat>>4; +end; + +{------------------------------------------------------------------------------- +Get the minor format number +-------------------------------------------------------------------------------} +function TDiscImage.GetMinorFormatNumber: Byte; +begin + Result:=FFormat mod $10; end; {------------------------------------------------------------------------------- @@ -461,10 +516,10 @@ function TDiscImage.DiscAddrToIntOffset(disc_addr: Cardinal): Cardinal; oldheads = 2; begin Result:=disc_addr; - if(FForceInter=0)and(FFormat>>4=diAcornADFS)then + if(FForceInter=0)and(GetMajorFormatNumber=diAcornADFS)then if(FFormat<>diAcornADFS<<4+$02)then exit; //ADFS L or AFS with 'INT' option - if((FFormat>>4=diAcornADFS{<<4+$02})or(FFormat>>4=diAcornFS)) + if((GetMajorFormatNumber=diAcornADFS{<<4+$02})or(GetMajorFormatNumber=diAcornFS)) and(Finterleave>1)then begin //Variables not set, then set them to default @@ -658,17 +713,17 @@ procedure TDiscImage.ResetDir(var Entry: TDir); function TDiscImage.MapFlagToByte: Byte; begin Result:=diUnknownDir; //Default value for non-ADFS - if FFormat>>4=diAcornADFS then //Is it ADFS? + if GetMajorFormatNumber=diAcornADFS then //Is it ADFS? begin Result:=diADFSOldMap; // ADFS Old Map if FMap then Result:=diADFSNewMap; // ADFS New Map end; - if FFormat>>4=diAmiga then //Is it Amiga? + if GetMajorFormatNumber=diAmiga then //Is it Amiga? begin Result:=diAmigaOFS; // AmigaDOS OFS if FMap then Result:=diAmigaFFS; // AmigaDOS FFS end; - if FFormat>>4=diDOSPlus then //Is it DOS + if GetMajorFormatNumber=diDOSPlus then //Is it DOS Result:=FATType; end; @@ -679,7 +734,7 @@ function TDiscImage.MapTypeToString: String; begin Result:=''; //ADFS and AmigaDOS - if(FFormat>>4=diAcornADFS)or(FFormat>>4=diAmiga)then + if(GetMajorFormatNumber=diAcornADFS)or(GetMajorFormatNumber=diAmiga)then begin case MapFlagToByte of diADFSOldMap: Result:='ADFS Old Map'; @@ -688,7 +743,7 @@ function TDiscImage.MapTypeToString: String; diAmigaFFS : Result:='AmigaDOS FFS'; end; end; - if FFormat>>4=diDOSPlus then + if GetMajorFormatNumber=diDOSPlus then begin case FATType of diFAT12 : Result:='FAT12'; @@ -817,7 +872,7 @@ procedure TDiscImage.UpdateProgress(Fupdate: String); function TDiscImage.GetRootAddress: Cardinal; begin Result:=root; - if FFormat>>4=diAcornADFS then //New map will return the fragment ID + if GetMajorFormatNumber=diAcornADFS then //New map will return the fragment ID if FMap then Result:=rootfrag; end; @@ -939,11 +994,11 @@ function TDiscImage.InterleaveString: String; ints:array[0..2] of String=('Sequential','Interleave','Multiplex'); begin Result:=''; - if(FFormat>>4=diAcornDFS)and(FDSD)then Result:=ints[1]; + if(GetMajorFormatNumber=diAcornDFS)and(FDSD)then Result:=ints[1]; {if(FFormat=diAcornADFS<<4+2) or(FFormat=diAcornADFS<<4+$E)} - if(FFormat>>4=diAcornADFS) - or(FFormat>>4=diAcornFS)then + if(GetMajorFormatNumber=diAcornADFS) + or(GetMajorFormatNumber=diAcornFS)then if FInterleave-1<=High(ints) then Result:=ints[FInterleave-1]; end; diff --git a/LazarusSource/DiscImage_Published.pas b/LazarusSource/DiscImage_Published.pas index e8e9746..b6bf6ef 100644 --- a/LazarusSource/DiscImage_Published.pas +++ b/LazarusSource/DiscImage_Published.pas @@ -79,7 +79,7 @@ function TDiscImage.FixDirectories: Boolean; begin Result:=False; //Only for ADFS - if FFormat>>4=diAcornADFS then Result:=FixBrokenADFSDirectories; + if GetMajorFormatNumber=diAcornADFS then Result:=FixBrokenADFSDirectories; end; {------------------------------------------------------------------------------- @@ -133,7 +133,7 @@ function TDiscImage.LoadFromFile(filename: String;readdisc: Boolean=True): Boole //Make a note of the file being read in FFilename:=filename; //Free up the Spark instance, if used - if FFormat>>4=diSpark then SparkFile.Free; + if GetMajorFormatNumber=diSpark then SparkFile.Free; //Blank off the variables ResetVariables; //Read the file in, uncompressing if need be @@ -185,25 +185,25 @@ procedure TDiscImage.ReadImage; var d: Cardinal; begin - case FFormat>>4 of - diAcornDFS : FDisc:=ReadDFSDisc; //Acorn DFS + case GetMajorFormatNumber of + diAcornDFS : ReadDFSDisc; //Acorn DFS diAcornADFS: begin - FDisc:=ReadADFSDisc; //Acorn ADFS + ReadADFSDisc; //Acorn ADFS ReadAFSPartition;//Read in the AFS partition, if one is present ReadDOSPartition;//Read in the DOS Plus partition, if one is present end; diAcornFS : ReadAFSPartition; //Acorn File Server - diCommodore: FDisc:=ReadCDRDisc; //Commodore - diSinclair : FDisc:=ReadSinclairDisc;//Sinclair/Amstrad - diAmiga : FDisc:=ReadAmigaDisc; //Amiga - diAcornUEF : FDisc:=ReadUEFFile; //Acorn CFS - diMMFS : FDisc:=ReadMMBDisc; //MMFS - diSpark : FDisc:=ReadSparkArchive;//Spark archive + diCommodore: ReadCDRDisc; //Commodore + diSinclair : ReadSinclairDisc; //Sinclair/Amstrad + diAmiga : ReadAmigaDisc; //Amiga + diAcornUEF : ReadUEFFile; //Acorn CFS + diMMFS : ReadMMBDisc; //MMFS + diSpark : ReadSparkArchive; //Spark archive diDOSPlus : ReadDOSPartition; //DOS Plus end; //Find the maximum directory entries - if FFormat>>4<>diSpark then //Not Spark, as this already provides this info + if GetMajorFormatNumber<>diSpark then //Not Spark, as this already provides this info begin FMaxDirEnt:=0; if Length(FDisc)>0 then @@ -217,11 +217,12 @@ procedure TDiscImage.ReadImage; {------------------------------------------------------------------------------- Saves an image to a file -------------------------------------------------------------------------------} -procedure TDiscImage.SaveToFile(filename: String;uncompress: Boolean=False); +function TDiscImage.SaveToFile(filename: String;uncompress: Boolean=False): Boolean; var FDiscDrive: TFileStream; ext: String; begin + Result:=False; //Validate the filename ext:=ExtractFileExt(filename); //First extract the extension if ext='' then //If it hasn't been given an extension, then give it the default @@ -229,7 +230,7 @@ procedure TDiscImage.SaveToFile(filename: String;uncompress: Boolean=False); filename:=LeftStr(filename,Length(filename)-Length(ext)); filename:=filename+'.'+FormatToExt; end; - if FFormat>>4<>diAcornUEF then //Not CFS + if GetMajorFormatNumber<>diAcornUEF then //Not CFS begin //Create the stream try @@ -242,11 +243,13 @@ procedure TDiscImage.SaveToFile(filename: String;uncompress: Boolean=False); FDiscDrive.Free; //Change the image's filename imagefilename:=filename; + Result:=True; except //Could not create + Result:=False; end; end; - if FFormat>>4=diAcornUEF then WriteUEFFile(filename,uncompress); //CFS + if GetMajorFormatNumber=diAcornUEF then WriteUEFFile(filename,uncompress); //CFS end; {------------------------------------------------------------------------------- @@ -269,52 +272,23 @@ function TDiscImage.FormatFDD(major:Word;minor:Byte=0;tracks: Byte=0;filename: S if major<>diDOSPlus then tracks:=tracks MOD 2; case major of - diAcornDFS://Create DFS - begin - FDisc:=FormatDFS(minor,tracks); - Result:=Length(FDisc)>0; - end; - diAcornADFS://Create ADFS - begin - FDisc:=FormatADFSFloppy(minor); - Result:=Length(FDisc)>0; - end; - diCommodore://Create Commodore 64/128 - begin - FDisc:=FormatCDR(minor); - Result:=Length(FDisc)>0; - end; - diSinclair://Create Sinclair/Amstrad - begin - FDisc:=FormatSpectrum(minor); - Result:=Length(FDisc)>0; - end; - diAmiga://Create AmigaDOS - begin - FDisc:=FormatAmigaFDD(minor); - Result:=Length(FDisc)>0; - end; - diAcornUEF://Create CFS - begin - FDisc:=FormatCFS; - Result:=Length(FDisc)>0; - end; - diSpark://Create Spark - begin - FDisc:=FormatSpark(filename); - Result:=Length(FDisc)>0; - end; - diDOSPlus://Create DOS or DOS Plus + diAcornDFS : Result:=FormatDFS(minor,tracks); //Create DFS + diAcornADFS: Result:=FormatADFSFloppy(minor); //Create ADFS + diCommodore: Result:=FormatCDR(minor); //Create Commodore 64/128 + diSinclair : Result:=FormatSpectrum(minor); //Create Sinclair/Amstrad + diAmiga : Result:=FormatAmigaFDD(minor); //Create AmigaDOS + diAcornUEF : Result:=FormatCFS; //Create CFS + diSpark : Result:=FormatSpark(filename); //Create Spark + diDOSPlus : //Create DOS or DOS Plus begin case minor of - 0: FDisc:=FormatDOS( 640*1024,diFAT12);// 640KB ADFS/DOS Plus - 1: FDisc:=FormatDOS( 800*1024,diFAT12);// 800KB DOS Plus - 2: FDisc:=FormatDOS( 360*1024,diFAT12);// 360KB DOS - 3: FDisc:=FormatDOS( 720*1024,diFAT12);// 720KB DOS - 4: FDisc:=FormatDOS(1440*1024,diFAT12);//1.44MB DOS - 5: FDisc:=FormatDOS(2880*1024,diFAT12);//2.88MB DOS + 0: Result:=FormatDOS( 640*1024,diFAT12); // 640KB ADFS/DOS Plus + 1: Result:=FormatDOS( 800*1024,diFAT12); // 800KB DOS Plus + 2: Result:=FormatDOS( 360*1024,diFAT12); // 360KB DOS + 3: Result:=FormatDOS( 720*1024,diFAT12); // 720KB DOS + 4: Result:=FormatDOS(1440*1024,diFAT12); //1.44MB DOS + 5: Result:=FormatDOS(2880*1024,diFAT12); //2.88MB DOS end; - Result:=Length(FDisc)>0; end; end; end; @@ -322,29 +296,21 @@ function TDiscImage.FormatFDD(major:Word;minor:Byte=0;tracks: Byte=0;filename: S {------------------------------------------------------------------------------- Create and format a new hard disc image -------------------------------------------------------------------------------} -function TDiscimage.FormatHDD(major:Word;harddrivesize:Cardinal;newmap:Boolean; - dirtype:Byte):Boolean; +function TDiscimage.FormatHDD(major: Word;harddrivesize: Cardinal; + ide,newmap: Boolean;dirtype: Byte;addheader: Boolean): Boolean; begin Result:=False; //Make sure the numbers are within bounds major :=major AND $FFF; case major of - diAcornADFS: //Create ADFS - begin - FDisc:=FormatADFSHDD(harddrivesize,newmap,dirtype); - Result:=Length(FDisc)>0; - end; - diAcornFS: Result:=FormatAFS(harddrivesize,dirtype);//Create Acorn FS - diDOSPlus: //Create DOS HDD - begin - FDisc:=FormatDOS(harddrivesize,dirtype); - Result:=Length(FDisc)>0; - end; - diAmiga : //Create Amiga HDD - begin - FDisc:=FormatAmigaHDD(harddrivesize); - Result:=Length(FDisc)>0; - end; + diAcornADFS: Result:=FormatADFSHDD(harddrivesize, + newmap, + dirtype, + ide, + addheader); //Create ADFS + diAcornFS : Result:=FormatAFS(harddrivesize,dirtype);//Create Acorn FS + diDOSPlus : Result:=FormatDOS(harddrivesize,dirtype);//Create DOS HDD + diAmiga : Result:=FormatAmigaHDD(harddrivesize); //Create Amiga HDD end; end; @@ -352,11 +318,11 @@ function TDiscimage.FormatHDD(major:Word;harddrivesize:Cardinal;newmap:Boolean; Extracts a file, filename contains complete path (CFS, entry is entry number) -------------------------------------------------------------------------------} function TDiscImage.ExtractFile(filename:String;var buffer:TDIByteArray; - entry:Cardinal=0): Boolean; + entry:Cardinal=0): Boolean; begin //Start with a false result Result:=False; - case FFormat>>4 of + case GetMajorFormatNumber of diAcornDFS :Result:=ExtractDFSFile(filename,buffer); //Extract DFS diAcornADFS:Result:=ExtractADFSFile(filename,buffer); //Extract ADFS diCommodore:Result:=ExtractCDRFile(filename,buffer); //Extract Commodore 64/128 @@ -372,7 +338,8 @@ function TDiscImage.ExtractFile(filename:String;var buffer:TDIByteArray; {------------------------------------------------------------------------------- Save a file into the disc image, from buffer -------------------------------------------------------------------------------} -function TDiscImage.WriteFile(var file_details: TDirEntry; var buffer: TDIByteArray): Integer; +function TDiscImage.WriteFile(var file_details: TDirEntry; + var buffer: TDIByteArray;ShowFSM: Boolean=False): Integer; var count : Integer; begin @@ -381,15 +348,15 @@ function TDiscImage.WriteFile(var file_details: TDirEntry; var buffer: TDIByteAr //Get the length of data to be written count:=file_details.Length; //Only write a file if there is actually any data to be written - if(count>0)and((Length(free_space)>0)or(FFormat>>4=diSpark))then + if(count>0)and((Length(free_space)>0)or(GetMajorFormatNumber=diSpark))then begin - if FFormat>>4<>diSpark then + if GetMajorFormatNumber<>diSpark then file_details.Side:=file_details.Side mod Length(free_space); //Can only write a file that will fit on the disc, or CFS if(count<=free_space[file_details.Side]) - or(FFormat>>4=diAcornUEF) - or(FFormat>>4=diSpark)then - case FFormat>>4 of + or(GetMajorFormatNumber=diAcornUEF) + or(GetMajorFormatNumber=diSpark)then + case GetMajorFormatNumber of diAcornDFS :Result:=WriteDFSFile(file_details,buffer); //Write DFS diAcornADFS:Result:=WriteADFSFile(file_details,buffer); //Write ADFS diCommodore:Result:=WriteCDRFile(file_details,buffer); //Write Commodore 64/128 @@ -411,7 +378,7 @@ function TDiscImage.CreateDirectory(var filename,parent,attributes: String): Int begin //Start with a false result Result:=-1; - case FFormat>>4 of + case GetMajorFormatNumber of diAcornDFS : exit;//Can't create directories on DFS diAcornADFS: //Create directory on ADFS Result:=CreateADFSDirectory(filename,parent,attributes); @@ -436,7 +403,7 @@ function TDiscImage.RetitleDirectory(var filename,newtitle: String): Boolean; begin //Start with a false result Result:=False; - case FFormat>>4 of + case GetMajorFormatNumber of diAcornDFS : //DFS doesn't have directories begin //Update the disc title instead @@ -513,15 +480,15 @@ function TDiscImage.FileExists(filename: String;var dir,entry: Cardinal;sfn: Boo begin SetLength(Path,0); //Explode the pathname into an array, without the '.' - if(FFormat>>4<>diAcornDFS)and(FFormat>>4<>diCommodore)then //Not DFS or Commodore + if(GetMajorFormatNumber<>diAcornDFS)and(GetMajorFormatNumber<>diCommodore)then //Not DFS or Commodore begin - if(FFormat>>4=diAcornADFS)and(FDOSPresent) + if(GetMajorFormatNumber=diAcornADFS)and(FDOSPresent) and(LeftStr(filename,Length(dosrootname))=dosrootname)then//Is this on a DOS Partition of an ADFS? Path:=filename.Split('\') else Path:=filename.Split(dir_sep); end; - if FFormat>>4=diAcornDFS then //With DFS, we need the initial root name, including the '.' + if GetMajorFormatNumber=diAcornDFS then //With DFS, we need the initial root name, including the '.' begin //So should only be 2 entries SetLength(Path,2); @@ -545,7 +512,7 @@ function TDiscImage.FileExists(filename: String;var dir,entry: Cardinal;sfn: Boo Path[1]:=filename; end; end; - if FFormat>>4=diCommodore then //Commodore is similar to DFS + if GetMajorFormatNumber=diCommodore then //Commodore is similar to DFS begin //So should only be 2 entries SetLength(Path,2); @@ -657,11 +624,11 @@ function TDiscImage.ReadDiscData(addr,count,side,offset: Cardinal; begin if offset+i>4<>diAcornDFS then //All but DFS + if GetMajorFormatNumber<>diAcornDFS then //All but DFS if addr+i>4=diAcornDFS then //DFS only + if GetMajorFormatNumber=diAcornDFS then //DFS only if ConvertDFSSector(addr+i,side)>4<>diAcornDFS then //not DFS + if GetMajorFormatNumber<>diAcornDFS then //not DFS begin //Ensure that the entire block will fit into the available space Result:=(addr+count)<=GetDataLength; @@ -842,7 +809,7 @@ function TDiscImage.RenameFile(oldfilename: String;var newfilename: String; entry:Cardinal=0): Integer; begin Result:=-1;//Failed to rename - case FFormat>>4 of + case GetMajorFormatNumber of diAcornDFS : Result:=RenameDFSFile(oldfilename,newfilename); //Rename DFS diAcornADFS: Result:=RenameADFSFile(oldfilename,newfilename); //Rename ADFS diCommodore: Result:=RenameCDRFile(oldfilename,newfilename); //Rename Commodore 64/128 @@ -861,7 +828,7 @@ function TDiscImage.RenameFile(oldfilename: String;var newfilename: String; function TDiscImage.DeleteFile(filename: String;entry: Cardinal=0): Boolean; begin Result:=False; - case FFormat>>4 of + case GetMajorFormatNumber of diAcornDFS : Result:=DeleteDFSFile(filename); //Delete DFS diAcornADFS: Result:=DeleteADFSFile(filename); //Delete ADFS diCommodore: Result:=DeleteCDRFile(filename); //Delete Commodore 64/128 @@ -880,7 +847,7 @@ function TDiscImage.DeleteFile(filename: String;entry: Cardinal=0): Boolean; function TDiscImage.MoveFile(filename,directory: String): Integer; begin Result:=-12; - case FFormat>>4 of + case GetMajorFormatNumber of diAcornDFS : Result:=MoveDFSFile(filename,directory); //Move on DFS diAcornADFS: Result:=MoveADFSFile(filename,directory); //Move ADFS File diAmiga : Result:=MoveAmigaFile(filename,directory);//Move Amiga File @@ -893,7 +860,7 @@ function TDiscImage.MoveFile(source: Cardinal;dest: Integer): Integer; begin Result:=-12; //Can only move files on DFS (between drives), ADFS, Amiga, AFS and CFS - if FFormat>>4=diAcornUEF then //Move on CFS + if GetMajorFormatNumber=diAcornUEF then //Move on CFS Result:=MoveCFSFile(source,dest); end; @@ -938,7 +905,7 @@ function TDiscImage.CopyFile(filename,directory,newfilename: String): Integer; DeleteFile(directory+dir_sep+file_details.Filename); //Set up the filedetails file_details.Parent:=directory; - if FFormat>>4=diAcornDFS then //DFS + if GetMajorFormatNumber=diAcornDFS then //DFS if(directory[1]=':')and(directory[2]='2') then file_details.Side:=1 else @@ -978,7 +945,7 @@ function TDiscImage.CopyFile(source: Cardinal;dest: Integer): Integer; begin Result:=-12; //Can only copy files on DFS (between drives), ADFS, Amiga, AFS and CFS - if FFormat>>4=diAcornUEF then //Copy on CFS + if GetMajorFormatNumber=diAcornUEF then //Copy on CFS Result:=CopyCFSFile(source,dest); end; @@ -989,7 +956,7 @@ function TDiscImage.UpdateAttributes(filename,attributes: String;entry:Cardinal= begin Result:=False; if(filename<>root_name)and(filename<>afsrootname)then - case FFormat>>4 of + case GetMajorFormatNumber of diAcornDFS : Result:=UpdateDFSFileAttributes(filename,attributes); //Update DFS attributes diAcornADFS: Result:=UpdateADFSFileAttributes(filename,attributes); //Update ADFS attributes diCommodore: Result:=UpdateCDRFileAttributes(filename,attributes); //Update Commodore 64/128 attributes @@ -1008,7 +975,7 @@ function TDiscImage.UpdateAttributes(filename,attributes: String;entry:Cardinal= function TDiscImage.UpdateDiscTitle(title: String;side: Byte): Boolean; begin Result:=False; - case FFormat>>4 of + case GetMajorFormatNumber of diAcornDFS : Result:=UpdateDFSDiscTitle(title,side);//Title DFS Disc diAcornADFS: begin @@ -1032,7 +999,7 @@ function TDiscImage.UpdateDiscTitle(title: String;side: Byte): Boolean; function TDiscImage.UpdateBootOption(option,side: Byte): Boolean; begin Result:=False; - case FFormat>>4 of + case GetMajorFormatNumber of diAcornDFS : Result:=UpdateDFSBootOption(option,side);//Update DFS Boot diAcornADFS: Result:=UpdateADFSBootOption(option); //Update ADFS Boot diCommodore: exit;//Update Commodore 64/128 Boot ++++++++++++++++++++++++++++++++++++++ @@ -1050,7 +1017,7 @@ function TDiscImage.UpdateBootOption(option,side: Byte): Boolean; function TDiscImage.UpdateLoadAddr(filename:String;newaddr:Cardinal;entry:Cardinal=0): Boolean; begin Result:=False; - case FFormat>>4 of + case GetMajorFormatNumber of diAcornDFS : Result:=UpdateDFSFileAddr(filename,newaddr,True); //Update DFS Load Address diAcornADFS: Result:=UpdateADFSFileAddr(filename,newaddr,True);//Update ADFS Load Address diCommodore: exit;//Update Commodore 64/128 Load Address @@ -1069,7 +1036,7 @@ function TDiscImage.UpdateLoadAddr(filename:String;newaddr:Cardinal;entry:Cardin function TDiscImage.UpdateExecAddr(filename:String;newaddr:Cardinal;entry:Cardinal=0): Boolean; begin Result:=False; - case FFormat>>4 of + case GetMajorFormatNumber of diAcornDFS : Result:=UpdateDFSFileAddr(filename,newaddr,False); //Update DFS Execution Address diAcornADFS: Result:=UpdateADFSFileAddr(filename,newaddr,False);//Update ADFS Execution Address diCommodore: exit;//Update Commodore 64/128 Execution Address @@ -1088,7 +1055,7 @@ function TDiscImage.UpdateExecAddr(filename:String;newaddr:Cardinal;entry:Cardin function TDiscImage.TimeStampFile(filename:String;newtimedate:TDateTime):Boolean; begin Result:=False; - case FFormat>>4 of + case GetMajorFormatNumber of diAcornDFS : exit;//Update DFS Timestamp diAcornADFS: Result:=UpdateADFSTimeStamp(filename,newtimedate);//Update ADFS Timestamp diCommodore: exit;//Update Commodore 64/128 Timestamp @@ -1107,7 +1074,7 @@ function TDiscImage.TimeStampFile(filename:String;newtimedate:TDateTime):Boolean function TDiscImage.ChangeFileType(filename,newtype: String): Boolean; begin Result:=False; - case FFormat>>4 of + case GetMajorFormatNumber of diAcornDFS : exit;//Update DFS Filetype diAcornADFS: Result:=UpdateADFSFileType(filename,newtype);//Update ADFS Filetype diCommodore: exit;//Update Commodore 64/128 Filetype @@ -1168,7 +1135,7 @@ procedure TDiscImage.BeginUpdate; procedure TDiscImage.EndUpdate; begin Fupdating:=False; - case FFormat>>4 of + case GetMajorFormatNumber of diAcornDFS : exit;//Update DFS diAcornADFS: ADFSFreeSpaceMap;//Update ADFS diCommodore: exit;//Update Commodore 64/128 @@ -1200,7 +1167,7 @@ function TDiscImage.ValidateFilename(parent:String;var filename:String): Boolean Result:=False; len:=0; //Maximum file length //Work out the max file length for the system - case FFormat>>4 of + case GetMajorFormatNumber of diAcornDFS : len:=7; diAcornADFS: begin @@ -1221,7 +1188,7 @@ function TDiscImage.ValidateFilename(parent:String;var filename:String): Boolean diAcornFS : len:=10; diDOSPlus : begin - if FFormat mod$10=0 then len:=12 //Filename+'.'+extension - Master 512 DOS Plus + if GetMinorFormatNumber=0 then len:=12 //Filename+'.'+extension - Master 512 DOS Plus else len:=255; //FAT12, 16, and 32 end; end; @@ -1230,7 +1197,7 @@ function TDiscImage.ValidateFilename(parent:String;var filename:String): Boolean while Pos(GetDirSep(part),filename)>0 do filename:=Copy(filename,Pos(GetDirSep(part),filename)+1); //CFS files can have multiple files with the same name - if FFormat>>4=diAcornUEF then + if GetMajorFormatNumber=diAcornUEF then begin Result:=True; exit; @@ -1286,8 +1253,8 @@ function TDiscImage.Title(partition: Cardinal):String; function TDiscImage.CreatePasswordFile(Accounts: TUserAccounts): Integer; begin Result:=-5; - if(FFormat>>4=diAcornFS) - or((FFormat>>4=diAcornADFS)and(Fafspresent)) then + if(GetMajorFormatNumber=diAcornFS) + or((GetMajorFormatNumber=diAcornADFS)and(Fafspresent)) then Result:=CreateAFSPassword(Accounts); end; @@ -1297,8 +1264,8 @@ function TDiscImage.CreatePasswordFile(Accounts: TUserAccounts): Integer; function TDiscImage.ReadPasswordFile: TUserAccounts; begin Result:=nil; - if(FFormat>>4=diAcornFS) - or((FFormat>>4=diAcornADFS)and(Fafspresent)) then + if(GetMajorFormatNumber=diAcornFS) + or((GetMajorFormatNumber=diAcornADFS)and(Fafspresent)) then Result:=ReadAFSPassword; end; @@ -1346,7 +1313,7 @@ function TDiscImage.SeparatePartition(side: Cardinal;filename: String=''): Boole if side=1 then side:=0; end; //Extract the partition/side into a buffer. - case FFormat>>4 of + case GetMajorFormatNumber of diAcornDFS : buffer:=ExtractDFSPartition(side); //DFS diAcornADFS: buffer:=ExtractADFSPartition(side); //ADFS/AFS or DOS end; @@ -1364,10 +1331,10 @@ function TDiscImage.SeparatePartition(side: Cardinal;filename: String=''): Boole //Remember the current format oldformat:=FFormat; //If we have an ADFS/AFS hybrid, and are saving the AFS partition - if(FFormat>>4=diAcornADFS)and(side<>0)and(FAFSPresent)then + if(GetMajorFormatNumber=diAcornADFS)and(side<>0)and(FAFSPresent)then FFormat:=diAcornFS<<4; //If we have an ADFS/DOS hybrid, and are saving the DOS partition - if(FFormat>>4=diAcornADFS)and(side<>0)and(FDOSPresent)then + if(GetMajorFormatNumber=diAcornADFS)and(side<>0)and(FDOSPresent)then FFormat:=diDOSPlus<<4; filename:=filename+'.'+FormatToExt; //Change back @@ -1413,7 +1380,7 @@ function TDiscImage.GetMaxLength: Cardinal; begin Result:=0; //Only 8 bit ADFS - if(FFormat>>4=diAcornADFS)and(not FMap)and(FDirType=diADFSOldDir)then + if(GetMajorFormatNumber=diAcornADFS)and(not FMap)and(FDirType=diADFSOldDir)then Result:=GetADFSMaxLength(False); end; @@ -1424,7 +1391,7 @@ function TDiscImage.AddPartition(size: Cardinal;format: Byte): Boolean; begin Result:=False; //Only for adding AFS or DOS Plus partition to 8 bit ADFS - if(FFormat>>4=diAcornADFS)and(not FMap)and(FDirType=diADFSOldDir)then + if(GetMajorFormatNumber=diAcornADFS)and(not FMap)and(FDirType=diADFSOldDir)then case format of 0: Result:=AddAFSPartition(size); //Add AFS partition 1: Result:=AddDOSPartition(size); //Add DOS Plus partition @@ -1434,7 +1401,7 @@ function TDiscImage.AddPartition(filename: String): Boolean; begin Result:=False; //Only for Acorn DFS, given a filename - if(FFormat>>4=diAcornDFS)and(not FDSD)then //Single sided images only + if(GetMajorFormatNumber=diAcornDFS)and(not FDSD)then //Single sided images only Result:=AddDFSSide(filename); end; @@ -1452,8 +1419,8 @@ function TDiscImage.ChangeInterleaveMethod(NewMethod: Byte): Boolean; //Only works with ADFS L and Acorn FS, or ADFS with non-auto interleave if(FFormat=diAcornADFS<<4+2) or(FFormat=diAcornADFS<<4+$E) - or((FFormat>>4=diAcornADFS)and(FForceInter>0)) - or(FFormat>>4=diAcornFS) then + or((GetMajorFormatNumber=diAcornADFS)and(FForceInter>0)) + or(GetMajorFormatNumber=diAcornFS) then begin //Set up the buffer SetLength(buffer,GetDataLength); @@ -1508,7 +1475,7 @@ function TDiscImage.ReadDirectory(dirname: String): Integer; begin sector:=FDisc[dir].Entries[entry].Sector; //Divert to the appropriate function - case FFormat>>4 of + case GetMajorFormatNumber of diAcornADFS: NewDir:=ReadADFSDir(dirname,sector); diAcornFS : NewDir:=ReadAFSDirectory(dirname,sector); diAmiga : NewDir:=ReadAmigaDir(dirname,sector); @@ -1536,3 +1503,51 @@ function TDiscImage.ReadDirectory(dirname: String): Integer; end; end; end; + +{------------------------------------------------------------------------------- +Produce a report of the image's details +-------------------------------------------------------------------------------} +function TDiscImage.ImageReport(CSV: Boolean): TStringList; +var + temp, + uline : String; + side : Integer; + report: TStringList; +begin + Result:=TStringList.Create; + if FFormat<>diInvalidImg then + begin + //Header + temp:='Container format: '+FormatToString; + Result.Add(temp); + if not(CSV) then + begin + uline:=''; + uline:=AddChar('=',uline,Length(temp)); + Result.Add(uline); + Result.Add(''); + end; + //Main Report, depending on format + if GetMajorFormatNumber=diAcornDFS then report:=DFSReport(CSV); //DFS + if GetMajorFormatNumber=diAcornADFS then report:=ADFSReport(CSV);//ADFS + if(GetMajorFormatNumber=diAcornFS) + or((GetMajorFormatNumber=diAcornADFS) + and(FAFSPresent)) then report:=AFSReport(CSV);//AFS + if(GetMajorFormatNumber=diDOSPlus) + or((GetMajorFormatNumber=diAcornADFS) + and(FDOSPresent)) then report:=DOSReport(CSV);//DOS + if GetMajorFormatNumber=diCommodore then report:=CDRReport(CSV);//C64 + if GetMajorFormatNumber=diAmiga then report:=AmigaReport(CSV);//Amiga + if GetMajorFormatNumber=diSinclair then report:=TStringList.Create;//Not written yet + if GetMajorFormatNumber=diAcornUEF then report:=TStringList.Create;//Nothing to report + if GetMajorFormatNumber=diSpark then report:=TStringList.Create;//Nothing to report + //Incorporate the report + if report.Count>0 then + for side:=0 to report.Count-1 do Result.Add(report[side]); + report.Free; + //Re-jig the output if CSV has been specified + if(CSV)and(Result.Count>0)then + for side:=0 to Result.Count-1 do + Result[side]:='"'+StringReplace(Result[side],': ','","',[rfReplaceAll])+'"'; + end; +end; diff --git a/LazarusSource/DiscImage_Spark.pas b/LazarusSource/DiscImage_Spark.pas index 875bd08..18e1d28 100644 --- a/LazarusSource/DiscImage_Spark.pas +++ b/LazarusSource/DiscImage_Spark.pas @@ -28,19 +28,19 @@ function TDiscImage.ID_Spark: Boolean; {------------------------------------------------------------------------------- Read Spark archive -------------------------------------------------------------------------------} -function TDiscImage.ReadSparkArchive: TDisc; +function TDiscImage.ReadSparkArchive: Boolean; var index, ref : Integer; d, e : Cardinal; pnt : String; - OldDisc: TDisc; +// OldDisc: TDisc; begin //In order to be able to use FileExists, we need to populate FDisc - OldDisc:=FDisc; //So take a note +// OldDisc:=FDisc; //So take a note //We will be returning a TDisc in Result - Result:=nil; + Result:=False; //Set up the array for the basic root entry SetLength(FDisc,1); ResetDir(FDisc[0]); @@ -107,15 +107,16 @@ function TDiscImage.ReadSparkArchive: TDisc; //Disc size (total uncompressed size) disc_size[0]:=SparkFile.UncompressedSize; //Return a result - Result:=FDisc; + Result:=Length(FDisc)>0; //And restore FDisc to what it was - FDisc:=OldDisc; + //FDisc:=OldDisc; end; {------------------------------------------------------------------------------- Extract a file from Spark archive -------------------------------------------------------------------------------} -function TDiscImage.ExtractSparkFile(filename: String;var buffer: TDIByteArray): Boolean; +function TDiscImage.ExtractSparkFile(filename: String; + var buffer: TDIByteArray): Boolean; var d,e : Cardinal; index: Integer; @@ -142,17 +143,17 @@ function TDiscImage.ExtractSparkFile(filename: String;var buffer: TDIByteArray): {------------------------------------------------------------------------------- Create a new Spark archive -------------------------------------------------------------------------------} -function TDiscImage.FormatSpark(Zipfilename: String): TDisc; +function TDiscImage.FormatSpark(Zipfilename: String): Boolean; begin - Result:=nil; + Result:=True; if Zipfilename='' then exit; //Set up the TDisc structure for return - SetLength(Result,1); - ResetDir(Result[0]); + SetLength(FDisc,1); + ResetDir(FDisc[0]); //Set the root directory name root_name:='$'; - Result[0].Directory:=root_name; - Result[0].BeenRead:=True; + FDisc[0].Directory:=root_name; + FDisc[0].BeenRead:=True; //Set the format FFormat:=diSpark<<4; //Create a blank file diff --git a/LazarusSource/DiscImage_Spectrum.pas b/LazarusSource/DiscImage_Spectrum.pas index 71f00ec..e7857d6 100644 --- a/LazarusSource/DiscImage_Spectrum.pas +++ b/LazarusSource/DiscImage_Spectrum.pas @@ -20,9 +20,9 @@ function TDiscImage.ID_Sinclair: Boolean; {------------------------------------------------------------------------------- Read Spectrum Disc -------------------------------------------------------------------------------} -function TDiscImage.ReadSinclairDisc: TDisc; +function TDiscImage.ReadSinclairDisc: Boolean; begin - Result:=nil; + Result:=False; {This functionality is not written yet} end; @@ -30,7 +30,7 @@ function TDiscImage.ReadSinclairDisc: TDisc; Write a file to Spectrum image -------------------------------------------------------------------------------} function TDiscImage.WriteSpectrumFile(file_details: TDirEntry; - var buffer: TDIByteArray): Integer; + var buffer: TDIByteArray): Integer; begin Result:=-1; end; @@ -38,9 +38,9 @@ function TDiscImage.WriteSpectrumFile(file_details: TDirEntry; {------------------------------------------------------------------------------- Create a new Spectrum image -------------------------------------------------------------------------------} -function TDiscImage.FormatSpectrum(minor: Byte): TDisc; +function TDiscImage.FormatSpectrum(minor: Byte): Boolean; begin - Result:=nil; + Result:=False; end; {------------------------------------------------------------------------------- diff --git a/LazarusSource/GJHRegistryClass.pas b/LazarusSource/GJHRegistryClass.pas new file mode 100644 index 0000000..530c368 --- /dev/null +++ b/LazarusSource/GJHRegistryClass.pas @@ -0,0 +1,224 @@ +unit GJHRegistryClass; + +{ +TGJHRegistry class V1.00 +Copyright (C) 2022 Gerald Holdsworth gerald@hollypops.co.uk + +This source is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public Licence as published by the Free +Software Foundation; either version 3 of the Licence, or (at your option) +any later version. + +This code 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 General Public Licence for more +details. + +A copy of the GNU General Public Licence is available on the World Wide Web +at . You can also obtain it by writing +to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, +Boston, MA 02110-1335, USA. +} + +{$mode ObjFPC}{$H+} + +interface + +uses + Classes, Registry; + +{$M+} + +type + TGJHRegistry = class + private + FRegistry : TRegistry; + FRegKey : String; + procedure OpenReg(key: String); + function ExtractKey(var V: String):String; + published + constructor Create(RegKey: String); + function DeleteKey(key: String): Boolean; + function GetRegValS(V: String;D: String): String; + procedure GetRegValA(V: String;var D: array of Byte); + function GetRegValI(V: String;D: Cardinal): Cardinal; + function GetRegValB(V: String;D: Boolean): Boolean; + procedure SetRegValS(V: String;D: String); + procedure SetRegValA(V: String;var D: array of Byte); + procedure SetRegValI(V: String;D: Cardinal); + procedure SetRegValB(V: String;D: Boolean); + function DoesKeyExist(V: String):Boolean; + public + destructor Destroy; override; + end; + +implementation + +{------------------------------------------------------------------------------- +Class creator - initialises the global variables +-------------------------------------------------------------------------------} +constructor TGJHRegistry.Create(RegKey: String); +begin + inherited Create; + FRegKey:=RegKey; +end; + +{------------------------------------------------------------------------------- +Class destructor +-------------------------------------------------------------------------------} +destructor TGJHRegistry.Destroy; +begin + inherited; +end; + +{------------------------------------------------------------------------------- +Open the registry key +-------------------------------------------------------------------------------} +procedure TGJHRegistry.OpenReg(key: String); +begin + FRegistry:=TRegistry.Create; + if key<>'' then key:='\'+key; + FRegistry.OpenKey(FRegKey+key,true); +end; + +{------------------------------------------------------------------------------- +Function to delete a key from the registry +-------------------------------------------------------------------------------} +function TGJHRegistry.DeleteKey(key: String): Boolean; +var + x: Boolean; +begin + x:=True; + OpenReg(ExtractKey(key)); + if FRegistry.ValueExists(key) then x:=FRegistry.DeleteValue(key); + FRegistry.Free; + Result:=x; +end; + +{------------------------------------------------------------------------------- +Function to read a string from the registry, or create it if it doesn't exist +-------------------------------------------------------------------------------} +function TGJHRegistry.GetRegValS(V: String;D: String): String; +var + X: String; +begin + OpenReg(ExtractKey(V)); + If FRegistry.ValueExists(V)then X:=FRegistry.ReadString(V) + else begin X:=D;FRegistry.WriteString(V,X);end; + FRegistry.Free; + Result:=X; +end; + +{------------------------------------------------------------------------------- +Function to read an array from the registry, or create it if it doesn't exist +-------------------------------------------------------------------------------} +procedure TGJHRegistry.GetRegValA(V: String;var D: array of Byte); +var + s: Integer; +begin + OpenReg(ExtractKey(V)); + If FRegistry.ValueExists(V)then + begin + s:=FRegistry.GetDataSize(V); + FRegistry.ReadBinaryData(V,D,s); + end + else + begin + FRegistry.WriteBinaryData(V,D,SizeOf(D)); + end; + FRegistry.Free; +end; + +{------------------------------------------------------------------------------- +Function to read an integer from the registry, or create it if it doesn't exist +-------------------------------------------------------------------------------} +function TGJHRegistry.GetRegValI(V: String;D: Cardinal): Cardinal; +var + X: Cardinal; +begin + OpenReg(ExtractKey(V)); + If FRegistry.ValueExists(V)then X:=FRegistry.ReadInteger(V) + else begin X:=D;FRegistry.WriteInteger(V,X);end; + FRegistry.Free; + Result:=X; +end; + +{------------------------------------------------------------------------------- +Function to read a boolean from the registry, or create it if it doesn't exist +-------------------------------------------------------------------------------} +function TGJHRegistry.GetRegValB(V: String;D: Boolean): Boolean; +var + X: Boolean; +begin + OpenReg(ExtractKey(V)); + If FRegistry.ValueExists(V)then X:=FRegistry.ReadBool(V) + else begin X:=D;FRegistry.WriteBool(V,X);end; + FRegistry.Free; + Result:=X; +end; + +{------------------------------------------------------------------------------- +Does the specified key exist? +-------------------------------------------------------------------------------} +function TGJHRegistry.DoesKeyExist(V: String):Boolean; +begin + OpenReg(ExtractKey(V)); + Result:=FRegistry.ValueExists(V); + FRegistry.Free; +end; + +{------------------------------------------------------------------------------- +Function to save a string to the registry +-------------------------------------------------------------------------------} +procedure TGJHRegistry.SetRegValS(V: String;D: String); +begin + OpenReg(ExtractKey(V)); + FRegistry.WriteString(V,D); + FRegistry.Free; +end; + +{------------------------------------------------------------------------------- +Function to save an array to the registry +-------------------------------------------------------------------------------} +procedure TGJHRegistry.SetRegValA(V: String;var D: array of Byte); +begin + OpenReg(ExtractKey(V)); + FRegistry.WriteBinaryData(V,D,SizeOf(D)); + FRegistry.Free; +end; + +{------------------------------------------------------------------------------- +Function to save an integer to the registry +-------------------------------------------------------------------------------} +procedure TGJHRegistry.SetRegValI(V: String;D: Cardinal); +begin + OpenReg(ExtractKey(V)); + FRegistry.WriteInteger(V,D); + FRegistry.Free; +end; + +{------------------------------------------------------------------------------- +Function to save a boolean to the registry +-------------------------------------------------------------------------------} +procedure TGJHRegistry.SetRegValB(V: String;D: Boolean); +begin + OpenReg(ExtractKey(V)); + FRegistry.WriteBool(V,D); + FRegistry.Free; +end; + +{------------------------------------------------------------------------------- +Function to extract key part of string +-------------------------------------------------------------------------------} +function TGJHRegistry.ExtractKey(var V: String):String; +begin + Result:=''; + if Pos('\',V)>0 then + begin + Result:=Copy(V,1,Pos('\',V)-1); + V:=Copy(V,Pos('\',V)+1); + end; +end; + +end. + diff --git a/LazarusSource/Global.pas b/LazarusSource/Global.pas index 740167e..c0f2042 100644 --- a/LazarusSource/Global.pas +++ b/LazarusSource/Global.pas @@ -23,31 +23,10 @@ interface -uses - Classes,SysUtils,Registry -{ {$IFDEF Darwin} - ,MacOSAll - {$ENDIF}} - ; +uses Classes; function ReadLine(var Stream: TFileStream;var Line: string): boolean; function WriteLine(var Stream: TFileStream;Line: string): boolean; -procedure OpenReg(key: String); -function DeleteKey(key: String): Boolean; -function GetRegValS(V: String;D: String): String; -procedure GetRegValA(V: String;var D: array of Byte); -function GetRegValI(V: String;D: Cardinal): Cardinal; -function GetRegValB(V: String;D: Boolean): Boolean; -procedure SetRegValS(V: String;D: String); -procedure SetRegValA(V: String;var D: array of Byte); -procedure SetRegValI(V: String;D: Cardinal); -procedure SetRegValB(V: String;D: Boolean); -function ExtractKey(var V: String):String; -var - DIMReg : TRegistry; -const - //Registry Key to use - RegKey = '\Software\GJH Software\Disc Image Manager'; implementation @@ -96,143 +75,4 @@ function WriteLine(var Stream: TFileStream;Line: string): boolean; Result:=x=l; end; -{------------------------------------------------------------------------------- -Open the registry key --------------------------------------------------------------------------------} -procedure OpenReg(key: String); -begin - DIMReg:=TRegistry.Create; - if key<>'' then key:='\'+key; - DIMReg.OpenKey(RegKey+key,true); -end; - -{------------------------------------------------------------------------------- -Function to delete a key from the registry --------------------------------------------------------------------------------} -function DeleteKey(key: String): Boolean; -var - x: Boolean; -begin - x:=True; - OpenReg(ExtractKey(key)); - if DIMReg.ValueExists(key) then x:=DIMReg.DeleteValue(key); - DIMReg.Free; - Result:=x; -end; - -{------------------------------------------------------------------------------- -Function to read a string from the registry, or create it if it doesn't exist --------------------------------------------------------------------------------} -function GetRegValS(V: String;D: String): String; -var - X: String; -begin - OpenReg(ExtractKey(V)); - If DIMReg.ValueExists(V)then X:=DIMReg.ReadString(V) - else begin X:=D;DIMReg.WriteString(V,X);end; - DIMReg.Free; - Result:=X; -end; - -{------------------------------------------------------------------------------- -Function to read an array from the registry, or create it if it doesn't exist --------------------------------------------------------------------------------} -procedure GetRegValA(V: String;var D: array of Byte); -var - s: Integer; -begin - OpenReg(ExtractKey(V)); - If DIMReg.ValueExists(V)then - begin - s:=DIMReg.GetDataSize(V); - DIMReg.ReadBinaryData(V,D,s); - end - else - begin - DIMReg.WriteBinaryData(V,D,SizeOf(D)); - end; - DIMReg.Free; -end; - -{------------------------------------------------------------------------------- -Function to read an integer from the registry, or create it if it doesn't exist --------------------------------------------------------------------------------} -function GetRegValI(V: String;D: Cardinal): Cardinal; -var - X: Cardinal; -begin - OpenReg(ExtractKey(V)); - If DIMReg.ValueExists(V)then X:=DIMReg.ReadInteger(V) - else begin X:=D;DIMReg.WriteInteger(V,X);end; - DIMReg.Free; - Result:=X; -end; - -{------------------------------------------------------------------------------- -Function to read a boolean from the registry, or create it if it doesn't exist --------------------------------------------------------------------------------} -function GetRegValB(V: String;D: Boolean): Boolean; -var - X: Boolean; -begin - OpenReg(ExtractKey(V)); - If DIMReg.ValueExists(V)then X:=DIMReg.ReadBool(V) - else begin X:=D;DIMReg.WriteBool(V,X);end; - DIMReg.Free; - Result:=X; -end; - -{------------------------------------------------------------------------------- -Function to save a string to the registry --------------------------------------------------------------------------------} -procedure SetRegValS(V: String;D: String); -begin - OpenReg(ExtractKey(V)); - DIMReg.WriteString(V,D); - DIMReg.Free; -end; - -{------------------------------------------------------------------------------- -Function to save an array to the registry --------------------------------------------------------------------------------} -procedure SetRegValA(V: String;var D: array of Byte); -begin - OpenReg(ExtractKey(V)); - DIMReg.WriteBinaryData(V,D,SizeOf(D)); - DIMReg.Free; -end; - -{------------------------------------------------------------------------------- -Function to save an integer to the registry --------------------------------------------------------------------------------} -procedure SetRegValI(V: String;D: Cardinal); -begin - OpenReg(ExtractKey(V)); - DIMReg.WriteInteger(V,D); - DIMReg.Free; -end; - -{------------------------------------------------------------------------------- -Function to save a boolean to the registry --------------------------------------------------------------------------------} -procedure SetRegValB(V: String;D: Boolean); -begin - OpenReg(ExtractKey(V)); - DIMReg.WriteBool(V,D); - DIMReg.Free; -end; - -{------------------------------------------------------------------------------- -Function to extract key part of string --------------------------------------------------------------------------------} -function ExtractKey(var V: String):String; -begin - Result:=''; - if Pos('\',V)>0 then - begin - Result:=Copy(V,1,Pos('\',V)-1); - V:=Copy(V,Pos('\',V)+1); - end; -end; - end. diff --git a/LazarusSource/HardDriveUnit.lfm b/LazarusSource/HardDriveUnit.lfm index 4188049..81b8e1e 100644 --- a/LazarusSource/HardDriveUnit.lfm +++ b/LazarusSource/HardDriveUnit.lfm @@ -146,6 +146,44 @@ object HardDriveForm: THardDriveForm Width = 34 Caption = '10MB' end + object DOSControls: TPanel + Left = 80 + Height = 20 + Top = 56 + Width = 402 + BevelOuter = bvNone + ClientHeight = 20 + ClientWidth = 402 + TabOrder = 4 + OnPaint = FormPaint + object rb_FAT12: TRadioButton + Left = 27 + Height = 18 + Top = 0 + Width = 56 + Caption = 'FAT12' + OnChange = rb_FAT12Change + TabOrder = 0 + end + object rb_FAT16: TRadioButton + Left = 136 + Height = 18 + Top = 0 + Width = 57 + Caption = 'FAT16' + OnChange = rb_FAT16Change + TabOrder = 1 + end + object rb_FAT32: TRadioButton + Left = 256 + Height = 18 + Top = 0 + Width = 58 + Caption = 'FAT32' + OnChange = rb_FAT32Change + TabOrder = 2 + end + end object ADFSControls: TPanel Left = 80 Height = 20 @@ -157,7 +195,7 @@ object HardDriveForm: THardDriveForm TabOrder = 3 OnPaint = FormPaint object cb_NewMap: TCheckBox - Left = 16 + Left = 0 Height = 18 Top = 0 Width = 75 @@ -166,14 +204,14 @@ object HardDriveForm: THardDriveForm TabOrder = 0 end object DirectoryLabel: TLabel - Left = 144 + Left = 216 Height = 16 - Top = 0 + Top = 1 Width = 60 Caption = 'Directory:' end object rb_OldDir: TRadioButton - Left = 224 + Left = 276 Height = 18 Top = 0 Width = 42 @@ -183,7 +221,7 @@ object HardDriveForm: THardDriveForm TabStop = True end object rb_NewDir: TRadioButton - Left = 277 + Left = 318 Height = 18 Top = 0 Width = 48 @@ -191,50 +229,30 @@ object HardDriveForm: THardDriveForm TabOrder = 2 end object rb_BigDir: TRadioButton - Left = 333 + Left = 365 Height = 18 Top = 0 Width = 41 Caption = 'Big' TabOrder = 3 end - end - object DOSControls: TPanel - Left = 80 - Height = 20 - Top = 56 - Width = 402 - BevelOuter = bvNone - ClientHeight = 20 - ClientWidth = 402 - TabOrder = 4 - OnPaint = FormPaint - object rb_FAT12: TRadioButton - Left = 27 + object cb_IDE: TCheckBox + Left = 76 Height = 18 Top = 0 - Width = 56 - Caption = 'FAT12' - OnChange = rb_FAT12Change - TabOrder = 0 - end - object rb_FAT16: TRadioButton - Left = 136 - Height = 18 - Top = 0 - Width = 57 - Caption = 'FAT16' - OnChange = rb_FAT16Change - TabOrder = 1 + Width = 42 + Caption = 'IDE' + Checked = True + State = cbChecked + TabOrder = 4 end - object rb_FAT32: TRadioButton - Left = 256 + object cb_AddHeader: TCheckBox + Left = 120 Height = 18 Top = 0 - Width = 58 - Caption = 'FAT32' - OnChange = rb_FAT32Change - TabOrder = 2 + Width = 76 + Caption = 'Emulator' + TabOrder = 5 end end end diff --git a/LazarusSource/HardDriveUnit.pas b/LazarusSource/HardDriveUnit.pas index 605bbf8..a4a9c79 100644 --- a/LazarusSource/HardDriveUnit.pas +++ b/LazarusSource/HardDriveUnit.pas @@ -33,6 +33,8 @@ interface THardDriveForm = class(TForm) cb_NewMap: TCheckBox; + cb_AddHeader: TCheckBox; + cb_IDE: TCheckBox; Image1: TImage; CapacityHeaderLabel: TLabel; CapacityLabel: TLabel; @@ -93,6 +95,7 @@ procedure THardDriveForm.cb_NewMapChange(Sender: TObject); //Enable/disable the appropriate radio boxes rb_BigDir.Enabled:=cb_NewMap.Checked; rb_OldDir.Enabled:=not cb_NewMap.Checked; + cb_IDE.Enabled:=cb_NewMap.Checked; //If Old map is selected if not cb_NewMap.Checked then begin @@ -133,15 +136,19 @@ procedure THardDriveForm.FormShow(Sender: TObject); ADFSControls.Visible:=True; DOSControls.Visible:=False; Caption:='Create ADFS Hard Drive'; - //Set directory type to 'Old' - rb_OldDir.Checked:=True; - rb_NewDir.Checked:=False; - rb_BigDir.Checked:=False; - //Enable/Disable the appropriate radio boxes - rb_OldDir.Enabled:=True; - rb_BigDir.Enabled:=False; - cb_NewMap.Checked:=False; end; + //Set directory type to 'Old' + rb_OldDir.Checked:=True; + rb_NewDir.Checked:=False; + rb_BigDir.Checked:=False; + //Enable/Disable the appropriate radio boxes + rb_OldDir.Enabled:=True; + rb_BigDir.Enabled:=False; + //Other options + cb_NewMap.Checked:=False; + cb_AddHeader.Checked:=False; + cb_IDE.Checked:=True; + cb_IDE.Enabled:=cb_NewMap.Checked; if AmigaHDD then begin //Set capacity to 40MB @@ -165,10 +172,10 @@ procedure THardDriveForm.FormShow(Sender: TObject); ADFSControls.Visible:=False; DOSControls.Visible:=True; Caption:='Create DOS Hard Drive'; - rb_FAT12.Checked:=True; - rb_FAT16.Checked:=False; - rb_FAT32.Checked:=False; end; + rb_FAT12.Checked:=True; + rb_FAT16.Checked:=False; + rb_FAT32.Checked:=False; end; {------------------------------------------------------------------------------- diff --git a/LazarusSource/ImageReportUnit.lfm b/LazarusSource/ImageReportUnit.lfm new file mode 100644 index 0000000..4648251 --- /dev/null +++ b/LazarusSource/ImageReportUnit.lfm @@ -0,0 +1,26 @@ +object ImageReportForm: TImageReportForm + Left = 304 + Height = 401 + Top = 113 + Width = 518 + Caption = 'Image Report' + ClientHeight = 401 + ClientWidth = 518 + Position = poMainFormCenter + LCLVersion = '2.2.0.4' + object Report: TMemo + Left = 0 + Height = 401 + Top = 0 + Width = 518 + Align = alClient + Font.Name = 'Courier New' + Lines.Strings = ( + 'Report' + ) + ParentFont = False + ReadOnly = True + ScrollBars = ssAutoBoth + TabOrder = 0 + end +end diff --git a/LazarusSource/ImageReportUnit.pas b/LazarusSource/ImageReportUnit.pas new file mode 100644 index 0000000..f710fc6 --- /dev/null +++ b/LazarusSource/ImageReportUnit.pas @@ -0,0 +1,30 @@ +unit ImageReportUnit; + +{$mode ObjFPC}{$H+} + +interface + +uses + Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls; + +type + + { TImageReportForm } + + TImageReportForm = class(TForm) + Report: TMemo; + private + + public + + end; + +var + ImageReportForm: TImageReportForm; + +implementation + +{$R *.lfm} + +end. + diff --git a/LazarusSource/MainUnit.lfm b/LazarusSource/MainUnit.lfm index f7a10f7..21ed2b1 100755 --- a/LazarusSource/MainUnit.lfm +++ b/LazarusSource/MainUnit.lfm @@ -2,10 +2,10 @@ object MainForm: TMainForm Left = 393 Height = 599 Top = 185 - Width = 751 + Width = 745 AllowDropFiles = True ClientHeight = 599 - ClientWidth = 751 + ClientWidth = 745 Color = 15527148 Constraints.MinHeight = 500 Constraints.MinWidth = 664 @@ -4298,18 +4298,18 @@ object MainForm: TMainForm Left = 0 Height = 505 Top = 76 - Width = 391 + Width = 385 Align = alClient BevelColor = 15527148 ClientHeight = 505 - ClientWidth = 391 + ClientWidth = 385 TabOrder = 0 OnPaint = FileInfoPanelPaint object lb_contents: TLabel Left = 1 Height = 16 Top = 1 - Width = 389 + Width = 383 Align = alTop Alignment = taCenter Caption = 'Image Contents' @@ -4322,7 +4322,7 @@ object MainForm: TMainForm Left = 1 Height = 487 Top = 17 - Width = 389 + Width = 383 Align = alClient BackgroundColor = clNone BorderStyle = bsNone @@ -4373,7 +4373,7 @@ object MainForm: TMainForm Left = 0 Height = 18 Top = 581 - Width = 751 + Width = 745 Panels = < item Style = psOwnerDraw @@ -4477,7 +4477,7 @@ object MainForm: TMainForm Visible = False end object ToolSplitter: TSplitter - Left = 391 + Left = 385 Height = 505 Top = 76 Width = 5 @@ -4487,7 +4487,7 @@ object MainForm: TMainForm ResizeAnchor = akRight end object FileInfoPanel: TPanel - Left = 396 + Left = 390 Height = 505 Top = 76 Width = 355 @@ -9569,7 +9569,7 @@ object MainForm: TMainForm Left = 0 Height = 76 Top = 0 - Width = 751 + Width = 745 AutoSize = True BandBorderStyle = bsNone BandMaximize = bmNone @@ -9581,7 +9581,7 @@ object MainForm: TMainForm FixedSize = True MinWidth = 274 ParentColor = False - Width = 274 + Width = 311 end item Break = False @@ -9619,13 +9619,13 @@ object MainForm: TMainForm object ToolsToolBar: TToolBar AnchorSideLeft.Control = ToolBarContainer AnchorSideTop.Control = ToolBarContainer - Left = 12 + Left = 208 Height = 37 Top = 39 Width = 186 Align = alNone AutoSize = True - BorderSpacing.Left = 12 + BorderSpacing.Left = 208 BorderSpacing.Top = 39 ButtonHeight = 37 ButtonWidth = 37 @@ -9685,7 +9685,7 @@ object MainForm: TMainForm Left = 12 Height = 37 Top = 0 - Width = 260 + Width = 297 Align = alNone AutoSize = True BorderSpacing.Left = 12 @@ -9759,16 +9759,24 @@ object MainForm: TMainForm ImageIndex = 8 OnClick = btn_FileSearchClick end + object btn_ShowReport: TToolButton + Left = 260 + Hint = 'Show Image Report' + Top = 0 + AutoSize = True + ImageIndex = 24 + OnClick = btn_ShowReportClick + end end object FilesToolBar: TToolBar AnchorSideLeft.Control = ToolBarContainer AnchorSideTop.Control = ToolBarContainer - Left = 286 + Left = 323 Height = 37 Top = 0 Width = 260 Align = alNone - BorderSpacing.Left = 286 + BorderSpacing.Left = 323 ButtonHeight = 37 ButtonWidth = 37 Caption = 'Files' @@ -9844,12 +9852,13 @@ object MainForm: TMainForm object PartitionToolBar: TToolBar AnchorSideLeft.Control = ToolBarContainer AnchorSideTop.Control = ToolBarContainer - Left = 560 + Left = 12 Height = 37 - Top = 0 + Top = 39 Width = 182 Align = alNone - BorderSpacing.Left = 560 + BorderSpacing.Left = 12 + BorderSpacing.Top = 39 ButtonHeight = 37 ButtonWidth = 37 Caption = 'Partition' @@ -9974,7 +9983,7 @@ object MainForm: TMainForm Left = 80 Top = 152 Bitmap = { - 4C7A1800000044000000440000008AC501000000000078DAE49D075854D7F6F6 + 4C7A190000004400000044000000A8D501000000000078DAE49D075854D7F6F6 C7129368346A8CA9E62637B9E5FFE5DE7463622276052C181B6A62EFBD61EFBD 170494DE417A95228AA22845A508A2220A22166CD8626288C9FAD6BB67F67866 98A1054B72E779DEE7CC9CBAF7EFACBDF6DAE59C51A91EEFA7C5DB6FFFFD1FEF @@ -13365,244 +13374,373 @@ object MainForm: TMainForm A7F4060CB0213ED61DC693CB6371E78250C4959CFD88AF5F585524851746DEAA B38D8A31B4A1ECDC1D0573C37625BEB60B09FFDD86686921A27B6DC0F97367EE 6E2F3E9292CF2EF1F3AE43C2C11AA41CAEC791CCAB4849B98AF8C3400C61F8E8 - 1848B7AEC57D8F6DC5FD4F981176A0C70326B8EBFFAA3BF3B81AF7F5EFDFD866 - 4AA492296556868450C624438650941476D98568B38D99C9944D64A84849E60C - 5B6C691E08A5C81C453491E65AB59ADEBF6F6DCF3ECF39AFF33B673FCFCBF93D - AFE78FCFEBDB4AD6BABFEFFBBA3ED775ADFB6EA51E8BF3D56CA290F1482E27AA - 58F847819C2BA2B7F4CD92E3F1B1821D29E5FCF24C865D7225E3025219EEFA08 - 0D111F2A9BE278FAB576F8FED0757334F677793CF6F8EC8C5BF7E27EFCABC79C - 7DF3F9A43B3DD6A6DFD45A9E9E119964F53D6B49DAFBAA95D7038B88BF5750CF - 23E5752DCF93FFE0111E0B332C1ED0A0B9EB1F3CBA78A2D4E5082DDA1FA1A54A - 282B02B20817F171BF424E68510541A2B69CCFA9C03BB31CB7F7325C5E89D878 - 2263BEE8A74607A430A29E471C3D76A5941D8D0F3BBDE69A2DCDCE7741F29290 - 6C54587D604F600595AAF57954FBAFFB6F5966DE0F85A9E98DBE776DF5F2CD71 - 080D2E213E369FA44745BC11BD61F2D31AE21E40A8E889669A3F123C76D3A6A3 - 3B4A9D4F081DA4B9CA219A2B85B0EC641677058F28E1A7770A2BB89E276A6D8E - 8CA31925EC4C2D61D58B12EC1E17332BBE0C8333AF31D81D8FE6B60728ACBB57 - E5127C5ABC7C319F29C4F2B513528060325962C832A382C0674196FFEEB86BCA - E562B6ADF9EEBDC6C5C05C9B9B374A78189D4FE2C3425E88D8487C5C438C888D - E0C83A1E75F1B147F0F0A08DBA174AAA5E346FE7293C2404879319DC16F91226 - 780415C8B89C5BC169C1C35DF0D82A7838BD2862C1E342263F2C6284DF4BC1A3 - 2E3E1ED06A7538375FBFA66E202910AA104C4F7DB946E7E05E486B0497598DD8 - E0BED5AF0C59EBFFE9F9EC5142A9D9954B85C44516921057C433E11D09F13544 - 8BD8B8132E78CC7D88D4E8240A0A775154BE8A82BA2F4D5B5EA389F4029B5FE5 - 44882749A4BE7DE6AAF0E2539F455DF95482CBBB121C9F1761995088515C21C3 - 048F51BB1FD6F350581BC3D5B4E7E27FBDE055E5439EC86304971C9E92C2BC57 - 3F221D134CA6490CB6D6CB887A1C39B5EE384B29ADAF45FF691EE131852637AE - 15722FBC8047F78A484EAC21FE610D9191B5DC09038BB96982C746A41E13453C - EB232D1F86646F81346C355D577BB1FC710CEE2FDF72EA5321FE5FC157E8A8A8 - 4FDB7361D907519F9ED732F65139FAFE6F19EB9680E68E785AAE8BE6744A1C72 - 927859F98024C123B6229817B50FC922038FCFDEA8DF14BE6226B8E84A6C7173 - 3996C1A7FA1A945B962BC96BE5FF311EC92F2BC65E3C5F404C683E0F6384A726 - D4F230AE86F0F05A6E07C318E31446DAAE6247983DB37E9D87FE1913BADDEC4F - D3C36D907E698864D918699E8AF0435D5457CE61A0D746C65DF361EA6FE14C0D - 7A89C99DCF0C8DAA44D73F9FB1BBD3D1589F2CFAB1083C5362C9E53E8FE59124 - C845FD95871259FE1B2165977923BA9AB0E250A647CEAAFFAC56A9ABC488697A - 299149A163EB8E59E49154CB7FE67DB192921AFDB367F28912B3D283A802121F - D51277AF8690905A826E419F918938EDF5153FFA95A2820C523F2472FF491057 - 434E71E4E64E7EBE22385D30456FAF1E1D5676A5D16605A48D4DC43E5A222DEB - 4CE36583696E634A8BF97674B6DF493BCB33B4587897E399F7C5DC132518DC26 - A6E277A1202265D7B85B7241F4F89EDC2D3FC7839A30363FDE44FBEDEA48BD25 - 9A6848B87A6DDFFB678DA9927D771E0F1E950CBC10904FE49D02EE47E6132F62 - 2336BA86E03BB5FC1604030DDE32C7E508F9C239138B42492CB84372DE5D91E9 - 51A4714FB86138C9E537B8577086DF3EB8E3F3700BAEB79D703C3F8FA99E6318 - B653879EF62A283848B414E7BA8DE52C941725D49ADF38C4A57C7731D68773AB - 2290DF6597B8531AC0AD225FAEE77B11F0E5A0F0D85DDC94FB703CED00A38F8D - 411A2D62A59DC4041BA327C9E9C9F5F3716DEDF78D936DBB32FA040A3F8DB893 - 474CD8571EC4D6101551C3EDDB355C17F3B6EEC834A6ADF5205B0CDF09B921C4 - 6505119B7595A892CB84C9C4F1171CE3EAE77D5CCCDCCAF9F4F55C485B47E05B - 17FC92D6B2EDEE8F2C3D6BC128BF6128B92AA0642578CC1E4BDBA5F76A0F3F7E - E47428767DE08A0467AE70912BD5FE5C283CC1C582A39CCF3DC4999C7D78656D - C72D6D158773D670F0D366A69F9F8ECA2255A48E82CB1009AF70CF9FBF777C9C - BB98A771E15C2177AEE7121592CDBDA81A22426BB8F95B0D57AFC390911F98B0 - FC5752C5513FC80D1673D87522B32F1321BBC85DD919820A8F11F8651FE73E6D - E1F4DB5F38FE6C291EF1766C0D33C7FAF2188C4E0EA4B347071456B64175DE0F - 82C970E1351749ADA41B5595D2F6DF77BBCDBA3D0BD7DC2DF8551FE3C417573C - B377703C6333473EAEE3D7B4D56C7F66877D8229D64FA63321601C3DB7F6409A - 2DB1E4A45D588DF091EADAEFF7DEE993E4C276FE7EF965970372B8733393A8B0 - 6A4283456C5CABE14A20E81BA463E0E0262AA33FB139B789F820E222E702C1F2 - 006E947973B9E020019F77E2F3693DC7DF3AE1FECC9E83F10BD91436074BC163 - F4A9FE747657A78DB3126A0B9AA1347F244D6D2F1094259B51FABFCE49E2A549 - 33AFCCCCB68A9FCDAEEAF56CA85EC696FC65ECFCB40CD7949F589B608945A431 - 06D706D3DBB33BBAA7B4D1DADF998FF91F7BD7E7CC77F4D6ACECCA563E27BFE6 - FB9FCAE6CAF90CEEDEAE24F8763581576AB87019468A99566FF15EA26B8F7123 - F332D7DE099FC8394360950F01A547385D20CE67CE460EA7AFC4ED8D3DAE4F17 - B0F3E13C56879832EBD270869DEA4587232AB45955C7A3294AF3F4E962739E75 - 39E55336D5F599FCD1537CA92C9496463BEF9D7D79728D6BE02AD686D9B2F499 - 19F6EFA762F66A18431E76A7F365557A081E759FFFEC70D6F1DA7FA8E6363EE3 - 9B97E3E591C159DF74826E94F37B5035172F5613705ED4DB513968CF73E5AC6C - 9798597D38FACA03EF9CE3F8541FE558C91E0EE5B9B02FCB991D69766C7A359F - 7549B359736F3A0EC113987C5197013EDD68EFD10EA5D5ED509DDF9036E60368 - 62794ED411A6FCEDFAA4D00B3ABC738FD970C1C0ADE692EE1A4E1939E06BBD8B - FD6B7F66C5EED94C3B38946EFB5569FE6B13D40EA9CB722869C8970A49FEAEE8 - BBF2C8C949957C7D723E1C3F9C83DFC934AE5D9171F37A35E702AAF10F80F186 - B9F4B2DA840F9BF0C83AC5BE1707D89FE3865BD55E5C8B37B1F9EB4AD665FCC8 - CFEF2C59F67C064B1E9960176984759021E3CF6BD3D7A713CA1E4AB45DF58DC7 - 9CDE74700EE77844F2D4B0A04049FEA1AC7394F9899BFE5DD77241612D97BB6E - C053DB91081DE1DF3D6379AC114A92C61DE2BB8B1AD4DF936D431D711FB3A6EC - FDFAFB87D397DC5DFB715FD094DA9A8AEF36D7E9E88C967E754B797DD6A78CD3 - 5EEF09BC24134C040BBF2A7CCF887EDD288F96066E58A4ADC439D79B559927D8 - CB51F6B09F0DF2B5387FB567C9C7B9D8BE31C5F2A911E6F70D9815A28FE9F5C1 - 189CED452F1F75948FD4F15046CDA6116D6669A2E27813FF17D9535FBE7A2E7D - 894A5D96B4EB14EF8E4793B92F95A7F6C1C44FFD9D47BAA296750824B4C5596E - 35F4E6B6E44DF40F6778DFFA1A2FA55384484EFCD66A3EC7BBD893FB2C6FE0F7 - E271ECC41BC9DB332BC9E3D74C4E9D48E7F27931979DAFC2E75415277D60CE82 - 425A4917316DA6865D9FC6CC9BACC9B4DD6398946CC1D84A6B8CB1622AB3999E - 6FC2D4144326DE1F8CD1EDFE8CBAD49B417E5DD13AA98AF22125DAAD56416DB1 - 880F335554ADAF73B6327F4ADD757BF9BD7CFBEAD769F0E5DB1BD42F84A2E414 - 5DCA24FBD0735EAD89E2F1C2206E99787366F0562E6938E0D7C9920BDD6D7153 - 9B886133676A3F31E2BBF969168247E6BDBD3B3FE0753495F3FE259CF3AFC2DB - 53CE096F309C56C424AD037CDC68C1F51993D8D15B93E98A2D19DA4AA2B7A684 - C678892ECB7FA0CB0925BADE5547E37157341235E916A949D76B9DE8E8AF42BB - 634A62866B4BF3753FD0CC6A000A220622E435136BA8964A23321617DE7E4845 - 781E55BFCB90DF2CA2EA461135D78AA9BD5A0CD74415BA2803EFAFD4B8BFE3CD - 247F023B2C619BDA5C96359DC45CCD7DB8DA78E8C63F7DF0DD3CE4A45746C88E - 4D291C3FFC8E333EC59C395DC5F163951C3D0E23C666317188A7387F3728CFB8 - 4FDAE320926FFA12736017B7ED96E233663ADB7A0C64815A7B467668C0A03E12 - 3A82512F5B09B5F50D51706B2A82B0050DB734A2F79456A8EAEE405A9A441C0C - AF7FFDE4E2899581AF205CD009AA447EB1888AB3F9949FCA45E69143B95B16B2 - 9D1F29DDF4868AED2FC9187F1DAF668B58D4D2849F14CCD866E8898E34486FCC - 0883BFEF33B76D93264D9A24A5A6A64A7A7A7A92939393545DFDEFFB94BA6BAC - FE7E05D737AF7B87C7C1544E7996E0E355C511F74ADC0F8381C917A6F4F1A03A - 743FC5626629B97582F2601138D17EF05414E464D1A4449DA524E0082FB76CE0 - AE852DA7874DC0554B9B5F3AAB63A7A5C858BD96D8F56E468024B15245833946 - 73B8B9DAFC49E2CD7D2EB77C3CBDA2571C24F3F05BAACF8A23124FC939A8F294 - 51BAEF23653B33906DF948C9BAD794AD7DCA9BD101B82BD8B044D1147BC1C365 - 983B13A4F17A8BE6DB8A3E04E9D2E54069A3CB967FFAB7D55EBD7CF997E2E3C6 - F5D2F3AB57BCE3E0FEB79CF028C553C4C6C10315B8FD2AF2654A11337B7922BB - BA85EC9B47C9BE7C904F17DC28B9B01B0236537D72359C58095EABC0671D9CDE - 08273750E4BA9C4C5B0B1E8F9B4454773D229BB725DCBA3F41AB7B123242E2AA - 9859CF8E93383D4D22C0AC3981D6BDB866358DAB761B79E47401D9B68FC877E6 - 51EA928E6CDD7B4A9C5F52E69CC453435F0E2858B15C7106B60AD3593DF8A888 - 0F3DEDEB776F492F9FC6D5EF5B414151D2EAAADAC460600FD5E906837BCD19D2 - 77C262B3694322EFDD97D2D33FFC4B167979D5D2A95345A77F767ACB2E118F07 - DD8A397AB892FD7BE5ECD95B8B81690533B44E5279622959A7B6917E6C13E947 - 37203BB1965AEF5FA83CEA4CE501472A772D46BED112F92A336A5698C2622398 - 3C98FC217D78DC439D6B2236DE5DDE420DEFF870DF9B87A75611B26F1661CB87 - 126ED58508B3C6DC359438A227E12638C5982DA2667D3565EB3F51F6731AC5CB - 9E53B2348127067EB8292E60459B69CC53988949FBB5B547E7FA7691BDB93BF6 - D1D5C5E7422F2D08FB18EF929419B62AA3F8CE46511C9CC9DE2C46BFE025A877 - 506937D4D0E45F5F9B4C92492E2EF9C77F5EF182B5AB1FB37B6731EEBF56B27B - 979C1DBB6A193D438E51A793C876DB907DC099D73B9691BEFD27AA76D951B56F - 09957BEC916FB141BE761E72E759C81DA722B737A1DC72348586FD48D7EAC023 - D536DC6820F1CACB41BC649850607D6294579CE6C37337E2039D093D388FA865 - BAC4CF6BCBADE9124113C653B04E78C8AA6C4A97BFA3F8A7648AED1F923CC217 - F736B6AC10F932B7F56C26B477FD90E87F7E7DF987C3E48BFE88527F8ADF7B50 - 91E64DD9CBE37C0E71267E6347F28E74C0B05F3BE3D64AAAD2DEBD7BEB7DA5B4 - B4F49F5CAFAC95FCFD4B7FB5B57EC6CAE5F16CDF5A84DBDE4AB66DAD60DB7630 - 9A9387818A1B79EBACF8B46B09C9EB1793BEDE86EA8D5654BACCA762DD5C2A56 - 9821FFC9948A4513A9B01A4B858521E5A643281CD283F4AEAA240A1E414D245E - 7A2EA6A2F00A6579EEC80BB75199B59AF2274ED4242F85B70E7C081D4FAC656B - 6EF590B83DD68C9CCD5F285D9A4391DD5B0A173EA1C8368E2782C721251B9CDA - 4CC1BCD56C4CD5F77F78E0714C246B10B5827579FE71CA33DD294B71A534612D - 25114B78E5D69BDCDD4D38BD63D62F92D4F04F3F515252FAA7311219C1F639B3 - 9EB25CC4A3CBFA7C76EFA864D3C60AB60B1E26F3F218A1BC93DC65334917FB8F - 779A47BAF31C6A579B090ED329B79F44B9CD04CAE78EA6C26C04E55387526EA2 - 8B6CB43605DA5D49EFD88E4415456E3695787E7C01E59F7D29CDDD23B8ACA7E4 - F90AF222ED298EB1A2306C262997F489B35020BCAFC4CD71B3C9D9F09912BB6C - 0A16BCE3EBBCA77C9D7B9FA7FA7EB8B713F1D16632335BCE62B2F2A6C29787FC - 0621BBF2A228EF08059F9C297CBD9C82F8C5E487CEE1EB85F1E49CD6E5D1CE56 - 3CF31E7AAF6DF3BFF7D87F64111A764B725E796BDD32C7AF382E89E79755B96C - DD2CAFE7B16B0798DA14A3D7561CBFFD10642E26BC5A6A4AF18A69B0623295B6 - E32817B1209BA18F6CD26064E3065066D89732FD5E94EA6A91DF539DF76A6D79 - 2578DC6A2691E4664ED9FBC3947C76A1FCEB728AE2ED2888B4A6347A3605A153 - 79153084C4058A84F6173C8C447CACC9A278412605966FF93AE7095F67DDE7C9 - 505FF6B5B3C649D184E92DCD58A4B5BBC66DC1BAD6AF12DD6E54951F42F675A1 - 888D19943E1C4BC9EF03F97A4C91C2234DB9EC204AFB55EBE8B58E0BFE258F88 - 88DFA4154ED79C96391662BFF8014ECB32D9B8BE82CD2EA2BEEC85D9569F91A4 - A358759CCF9EF1465C98A6C72BDB3E60DF0BE68A71DB5447249590411F2A8774 - A76C8006A57D3B53D24BE46C3735DEAB28F146A50DC1223E12B71953FC6A17C5 - 9F575096B988C2581117824559C4540A422690767110CFEDDA706780F00F6373 - B2576651649149DEECD77C99F698AFA6313C1EE2C3F6765638B436668AE0B16B - 8C57AD669B01D2C6D5464EA2B0892DAD1126BCE22BE98BD2786A9D24CC2E82F8 - F5375F9C5FB9ABACBAAA4DDD9E9F3D7B2EDDB87143DABD7BF73FCD9767C9FC38 - 69E27316D9C6F293FD07D6ACAA107923FC635315EB561531CF22959E835FD346 - F9162D1A1E4051B263BC82113B7A681334A83DEF86364336B82968B7829E6DA1 - 9B0A7455A1B4A3329F94154912F111DCB4014FD68F242FF1178AB217539A6A4E - 7EC40C8AA2A7501A664461C828B2AEE8F0C24E89DB223E6E4FB6256779168533 - 330587D77C9E94408EB1E8DDF5BCD9AE6CC9A2D6133169398313A33D6BC7771E - D2BD45F32692F1883E83461BEA74339D325E61DC687D698A91A1E4BAE75729FE - 79C6FF517F9A94543E7FCAA4146CACE3B05BFC81954EE5AC154C56AD94E1B24E - F4EE1E70DE17D1BFD6B2668D9C19730AD01996825A9710149BED434D32C7401A - 8863838E9C6EDD8C785589CF1D254AD47EA050A91929223EEE0A3F4DF8599BDC - 078E22C72D287E39998270112FD1E328093510BD9E3E9F03FB916C2BEA8BF08F - B06976B0B21CCCE56096C167E37832478793A0EBC5CEF656A2F710FED1622AD7 - C77956CC1C304EFBDFFE5DF9FD07A49A9ABF763D2FEE816CD694496FB1127EB5 - D0368D650EE5383B55887A538ED3D272962E2967FD6A39C70E547341CC78D7CF - 8B8A297A48AF13B0612B58FC58CC08E36768F5BD8C92E236142433B4A5DE984B - 6D7011297A5341C2BB9144E4CF22C71E5952F9652665CFC75314395AF0182178 - 0CA52C7C189FAE6AF3C4A61D09431AE139C808D71EE7F1D7B84672CF500A86C7 - 933D2A8C848127D8DD7E3E960A9358D4623AD1463E6506FDF47B7DDBB682504B - A13AD76C26D4B44183068D1515151B686A6A4A55557FED7A5644448989E9E454 - CC67DF17319286C312394B7F2AC7B14E82C5921FCB71B09789DC91B1698D8899 - D562FDA59C9D1BE51CD95783AFE0E22F4AFFF1637236BB16B1D0E913C666B10C - D2F7A453277BFA361C485B6934665ABADC3BD29BC212D11F7D1D206AAC585F8B - 31E6E1706A1E8D24FD376D926CDA5234A1393F6B7617BE355068041A6DD712D9 - 3F84EA11B13C1C708C6D2AF39823FC63450B731E8DF72BD2EBADDB53ECBDA1D8 - BB521D13B1B6AAE3D1B469D3464252BF7EFD24030303492EFFF7D7B1E415B552 - 6C4CF1E869A6EF98392D0ECBB96982891C5BEB7216D9C8586CFB87162E90B164 - B18C654BFE90D34F32563A08399689AF4B586E5788B343019B56177160670947 - 0F9670F0601E5B76A5B378752253C48C3F6CC82E7407D9606C3A4E78555F0E2D - EBC4FD031D29BCDD1D12FB93E92DEAD18F4DB92DE61D1D8529B456DE84468F83 - FCD07B1F7EFD450F37289AB8FEC77051B1C0BAF5781C9B9B1131E644DEA0EE62 - 8A14FB171CDA8BB5AD90625D9C08164D9B3717C62258356EDCF82FDE3F86F4F4 - 69C5F0C92629984E89C16CE6272191B2332B9833AB0C0BF332E699CBEA656921 - 13395586F53C190B2C65D85A09CD2F659175A1502E8BADB3853EB1D02A8D0516 - 29589BBFC66EC15BD638A5B36DC34736AC4B15F99884D1A8DFD1E97F8A9E3D37 - A2AD6986D1A0812CD26FCB16D1AF2F1DAC4827D18F77E874869EDD4FA0ACEA48 - 53354702FB0609BF8E244EFB189B542D58DA6A0CB39B9BE26BE8FE7564B741FD - C59E5B0B751052116A27D4A68E8B6054973B0D5C5D5DEBEFA3BD7AF5AAC88708 - 293B3BBB7EFEFDA7F79764D7F4EAD7379ADE3D8F3151F8D6D429854C995CC4E4 - 493562453CAEC674AA8C6953CB982E34C354685A9988A712CCA617327B7A2EB3 - 67643367C647CC67A609A508BDC4DC2C59AC89CC327D80E9C418264F88619AC9 - 032C66460B66312C14E570FACC28748707A0D97B331A9A963457DA8E72A70B74 - 5477A68DE2649454C6D2B68B05B7BADFA646F84878BF23AC559DC3D296E3306E - 361DE7510773F4BBE9F4FBC6A0B3D8BFBA90EAB758511471D1B051A34652FFFE - FD256565E57A93A9F3127D7DFD7FDA7FD4DF4B5143C3E39E6F57188E3C12D3A7 - D7D9AA0E6AB174EE182D623B9971E3D2319EF01923A3528CC6D508C184F15542 - A5188F2F60A2D1174C2664097D144A65D284374C327EC164E3A762FF8F851E0A - C532756234A62651824B18538CAF6232FE06C663C219671886E1F030060DBA82 - B6CE3934BA9EA565CBC5288A7E4B55652AED3B0CA36DB7D9DCD4FC9DCA2EB789 - EDED818B9A250E8D8763DB7A11FBE604CA7A76EADAF55B6C74131CBA08751252 - ABCB19C1A299508B6F1EDBF41FD4E4BFBD0757562D656494488F13B23A7A7BBF - 9FBBD4F1B1D7A85177DF6874FD0D95F6C174D378C890C16F186598C668C34F18 - 8ECCC7D0A04448AC23B31865902AF44AE8B950925002A3463E108A65B44124A3 - 4786887D0733D6309089E3BC852E3261CC1DC68FBAC5A8E157E8D5E3049D3A9C - A05D5B175AB79E8172BBF98287252AEA2350D69A4790D61DE8124942CFC3EC69 - 3999ABEA0BB93ED34B3E718485A31849EA4E7C9DA776171C34C5AAF12D5FFECC - 21F1FD36828B8250AB6F35A8BE0EFD773CCACA2AFFE17B7229F76BB1141B9B3D - 70B7EBEB157366C7DDE8D3FBF73C55951B82CF0DBA748E40776002FAC3121836 - F42943F5DE0AA5D7AF43F49E08767142D142E14277D11B748B41FD83183CE83C - 2387793066A427630C4E337AC429F1F818DA7DF6A1D9F528AAED77A2A8309FF6 - CA735153B541B5D374BA682DE6A45600316A8779A4B185744377CE4CD97047B7 - 573F836FE75D47A82E67EAEA6E1FB1F73A068DEA3C56EC5FB161C3862DFFB7F8 - 68FC4D3FD4FDCC5FBF7FFDEFAF79A5A57D968283D35AF9F9A64D747048DC633C - 21E48146D773828D2F0AAD8FA3A9E1457F1D7F060DB8C200511B07E824D05F3B - 49E83E3AFD82D1EE775DAC37D0EE1B40FFBE47059B930CD13DCE505D0F06E978 - D043F380E0E18D9ACA76912B7351113D6807B53974509F4D3F9DF535360397F9 - AFE96676E3D2D055CF9C0CAD368ADD297F3BF77A4203BF31A9F355ADBA3CD0D0 - D090ECECECA4E1C387FFD993E9E8E8488255FDD7898989929F9FDFFFF57BAC72 - F9DF7ABBA74FBE88782A95E2E2323536AC4FB0B6B78BF2D5EEEBFDA1558B2DB4 - 6CFE0B8AAD5788F859439FDEBBE8D3EBA8F0E9BA7CB85BAFDE3D7F130A105FFB - 89D55FC88FEE9AA7E9D2C99B6EC23B3AA86C153E6A2EE2C49A8E1DCC515733C1 - 60B81BADB5FACFA8EBB01AB56A5A17079D847485F4BFF118FC6DAD63D2AA4993 - 26D283077FBCBF5C5757162E5C281D3E7CB8FEF1EBD7AFA5C18307D7F7AA7FA5 - 1FF94BF78C1457FED9BB4446A64BA9695FA4FBF7321ADCBE9531DC6149CC06A3 - F167C37AF7DA56D1BC990D0DA46928B432177C7E44B3DB6A2157B4BA1D454BE3 - A288A940C1E0121A9DCF8958F012EB693AAAED103CAC44AE58D04E69AAF85927 - 51D38EC93AAA7737127BED2D344CC8E09B0C85467D5BEB7828ABA9A9D57DBEC9 - BF3CFE828202293737570A0909F9AED7F6B23265E239D3A557AFBE0ADE7FE457 - 6969A59492922705DF4957DDB8EEE11CCB7941C7F5871D78D54E6981E835C708 - 0DA169E3C122CF4CE9A4BE14F50E9BE9A0EA869292BBA8695E224E560B3F3516 - 31668AEE808DA28E6D08EBD6AD8FD1B7733F5668F83716238406D579A8906A9D - 3FF4ECD953CACACAFACBC79F9999F93FF33B81B57F70F9733E8ACB965C77C5F5 - DFBCE99EA3DD8F972FF4EBEB90D95070116540A80B8D7FE82B6AAC9E60F123ED - DB398A1AE32072647FD970FDF95B5BB56A54B7DF3EDF18A80B0F68A6A4A4D4AA - AE869A989834F0F2F29256AD5A25999B9B4B2FFFE2FBE7FFAF959A9A2FFCE66F - B95A545CDD24262663F4962DBF6F9D3C694F54A78E569592D49E869231FA7A67 - 19A1BFEA4E8FEEFD867DAB01755E51D74BD4F5DDA2DEC5D6F752F3E7CFFFFFF6 - F704F3F2CAFEDB7F2B2D45785F8EFAD9B30F2CAC2C0FBB0F196CBEB065CBBFCD - E9C6C6C6527474B4A4A5A555FFF8EEDDBBF57EB862C58AEF726CFF0518F8A521 + 1848B7AEC57D8F6DC5FD4F981176A0C70326B8EBFFBAFB0AB02AB6EEFDB11BC4 + 006CEC2E6C05131103031504030CB031AF89AD58784D0C5014111B0BC4A04301 + 4114EC00452594AE030781F7BFD7D1CD1DCF05C47BF1F77DDF7F9E673D733867 + CECCEC77BF6BAD77AD3D407D7F2CB9120B1F48F0402A854F1A8B1FC9525C66DA + F2548C14873E6463CBEB2CFCF14402D3F01C0C768C401FCB075063FC505E1780 + B084FC3EBBDD574D54DBDDF8E1A023133EDDBC1730ABA4F71CEBFC74F8ED962B + A39C9B2F8CFAE4FD687269E692C8775F175F734A45F0BD64191EAF5FE6E369F8 + 373C3CFD81B106812853C5F21B1E8D8F41A9F14154AD7B10D594DDB1C831069E + 8C1FF7B3A5704FCD860BCB2DE7E2B2611B9D05AB771258BC60DC782CC114A6A7 + 0638BE465F191E0168B9ED75A675B0C7C915574D50F95C630836020463652CDF + B3C3291B392A323FCA2F5E7F4BA213CBA74444952BEDDC6A732A6EAEFB9D7404 + FB27E1D18354BC62DA303C2C0F0181803BD344E3F41F303CB6A36683FD506A74 + 94D95E5451DE872A4A6E58703C0677191E3E2C9EDE4EC9C6B544966BE324B0FE + 948EAD11E958F62C1DA60FD3303E38131AA75F42637B309A6D0A84C2AA7B5F2D + EE9C64974FC367A4C0E8A539044786C908013D1668253B3D7131FAD97DE76549 + 596D9B57EA5AE38253BCB1F3F57404F92621342805CF1837421FE6C18F71E38E + 37E141FCD8C1F038849AF56DA0A462832AB58FB118E286B9C73FC195F98B07C3 + C32559824BF1D938C9F0D8CFF0D8C8F0307F968A690F53302228157DED9F333C + 881F81A8BEDC13CE2F5F820A926466D90CD3135FAEA2D19DD61056305CC697C3 + 9AFD1BED3321A9F17F5D9F3D08C9D0BB7C310501DE29080948C513163B4282F3 + E0CBB871DB93E131290842B9E35050B80BC53A57A050FF142A55BB8A8AC23318 + FF2985173B492864F21957582C3EF199E5958FE9B0789B8E794F5361149202AD + 8014F46278F4DF1E24C34361A51FAE443E65DF7A86173941782CF563B8C4210C + AF61F8621684C30C93D102BA4DEDFEC9E7A1F728BACF0C64C872D1EFC6C3D32F + 45E7FAD514DCF34CC6837BA9080FCD4370501EBCBDF371DB03309814C9F0580B + A1E530C6E7DE1016F682606600A1D77234596E83850FFDB0FFF91B9CF8980287 + 04E014336B969F36C7030BDEB3FCF4341F831E64A1B7C31B0CB20A41B32DC1A8 + B6CA17275F07408A47789E1388470C0FFFEC3B78961F84187CC2A1CFB6A8EFCC + E28A1EC3455DC0062B8BC39FF0519683E233E30569BEF4B7E111FE3C7BD08573 + C9F0734F42901F8BA921F9080AC883A7673E5CEF0003B55FA39FC9326CF130C3 + F83F0DD1FBB40E9A3A7742A5033521FC51168251050886CA2C1EAA4365F14474 + B1598BC157ED30EA862746B93C87CEEDCFE8E993037587240CDA1E05B5D5E14C + 8F79E1D86B7FC4E33E1E4ABD112265F957EA0EEFAC1B70CBBC84574CD578A4B9 + 638CF778D9DF6A159A08E83BBAFB6BEF47EE83E89E991F09F9F83D7DB1F4F4BC + DE674E27C187D54A813EC9087D908F807B797073CB87CB4DA06DBF5098EF3CC5 + 0E4D406AF22744BC0FC5FDC72EB8E27602079DB762E96586D3795D74DFD91DF5 + 163741B9F50A10D65664E3A8066141235458D00D558C7551758A291A996D456D + A3D3A83AFD2E8E44DF67758F0FC3C0157ED9B798B9C05B721577D3CF338D7F0C + 77B3CE2230CF03EB1FAE43DDCDF521B41150514D80A5CDE69D0539E6ABA4D4F1 + 087C90DEE5BC6312BC6F27E3BE7712821937FC7DF370E7763E6EB8005D34DE60 + A2C54124B1C8199AEA8ED0E4DB084FBCCB3CDD0791B8C7A2A127C2B3AEE35EF2 + 69DC78BF1F76411B60E96A8E79E70C31EAD840F4DADA11ADCC94A13057403536 + D7358DC6A3CE8C907CFDEBFB7031693F2BEB3D7133DB09B72417713BC3113753 + 4FE15A920D1CBFEC6531761B9CA5763812B907030E0F84308071A5B680A1C65A + 8FC3A3C265F5717E7EE9F264D3B64F6D9D583CF5BA9D083F8F0404FAE7C1C72B + 0FAEAE79B8C6EA6DF57E9118BDF2106259F11D12EF86801817F8C75C814FFA25 + 7848D8FD271FC695CFBB70217A23CE45ADC6F9C855707A6301FB472BB1E9EE2C + CC3F6380FEF6BDA064A900A5C90C8F0983506BFEBDFC030F1F98EFF35FEDB428 + 64092EE3022EE73AE07CCA515C48B6C6B9F87D381DB70B36319B6115B90C07E2 + 5660EFC7F518736E0C9467A84068C070E921C0C6F3D8D2D2E6C7D90B896AE7CF + A6E0F6B578F8B8C5E29E4F1EBCDCF3E07C230F57AE013DFABDC7D0857F2282DD + 7560FC1D56875D8377EC2578492EE0AEE4345C520EC3E9CB2E9CFDB80127DFFC + 81234FE6E350B029367AE863EAA581D03ADE058D0ED583C2E29A50312CCF30E9 + C362CD0544E4A029BEE6089B6F6DB71AEF3A1E96F11B609F7B1847BF58E258EC + 161CF9B41E073FACC29F91CBB1F98929CC427431F5F1180C751C8C561B5B4298 + 2060F671538F3C164772F34BAF77FA383CA5B6837D52E625C738DC768E868F47 + 2EDCEF306E5CCDC36527A0B7461434E65AB1CCE800FF385778BD67BC883B8F3B + 52475CCFB4C5A5E4BD70FCBC15761F57E3C81B73EC7F6286BDC1D3B1CE63228C + 181E034E7442A3FDF55173891254A75586D2947EA864721E2E3192B1197C4E42 + 2F0E1F77795CECE4E009D896BB1A6B72176043D2026CFDB80096AFE760658811 + 0CBCB5A171B51BDA1C6B01F5131DD07C77237C48FAD046E633A5185B636273AA + DB1D4F487238118BCBE73EE1AE6B0EEEB8E6C2E9721ECE5F02FAB19AB6FBCC9D + F0CD3F8CEBD19770F52D8B1371A7E1F4D50E8E1907713299CD67DC5A1C885A0C + AB5766B00C9B86AD418658EEA68BF117FBA0D789D6A87750193597111E95A064 + D81B8D8DCF61555CD6C875A433F14D537CC94911E6FB2ED939E1D2883C4BA765 + 58E96182F94FF460F66E14F45EF4428FA016687449052D191EF4F79FE79E9977 + F537E5DC0AA74F25C6D91CFA8433A7A2E0723D0BB75C7271E1422E1CCFB17CDB + 3F0E1D0C2D7146B28DD5AC76B07E7108B6714760976B8DC3E93BB02FD102BB62 + 96604BA429D6BD9882558F2660C5BD31987B6728465C504767BBA6A87BA83694 + 96D786CA94B2A8A9DF19158DCEB23C82917FAD4F327B867A6FF7FBAD39AF6195 + 77517D054E68CDC5A9A9DBB07BE5522CDA3E01A3F7F644D3DD2AA8F26745A8EE + AB2F89437A597CC916A46F534B158FB8B808E1945DDCFB2307E2607F3C12572F + 4BE07C2D17671D73E1E0080CD18C47EBC9EB60877538147302BB9EEDC1EE382B + 587DDD09CBB475589FB018AB3ECDC2D2B74658F0742C663FD081A9B716A6BA68 + 62C8B90E6867D710750E29A1D6B2EF784C6C837A4B3C71C42B7C94878B93207D + 9FD9C847FFA8B343939538AFB012979AACC1B10EF3E0D591C5EF56FE78A8E68E + 476AB711DC82E5A04EC7B0A9E73CEC1FB822F3DDEAFB07A266DF5DF96197CBC8 + FCBCEC52ABEB3A761C20FC69F5FAE519BB4C9CB47907A78B128609C3C2FE2B4E + 9D667A5D2B11D534AC6010B9184BE26DB12CFA2876C21A3BB01B6BA42BB124C1 + 0CB33F4C82C92B5D18856941FFBE06C6BBF586EEB56ED038D31AADEDEAA3CE41 + C2A30E548DCBA1E6F866509EE70C8767B1A39EBF782A7CF18958F068DB09BC3D + E28BE85D110833BB83E051B7F0409DE5B27A4E70AF7A0637CBDAC255B0856FF9 + D37857E32A9E0B27E02698E346F52938D2D80CF14F12BB94161E878FBE126C8F + C53C3AF467344E1C8DC2A573AC2E3BF7157627BEE2B81D30715A0AAA0B17A05B + 5915A66D2BC07044338CDE3E10C3C30D3028672AB43119A3300163927430EAB5 + 2686DDEF062DD74EE87FB10DBADA3741F3E32AA8B34F09B5972B437526E3879E + 0A54A65EC3999CA491B46E2FBD976496FB3212F8F2BD41FD8C998F14A917A311 + BBEF295EACF0C1C3E92EB8A9638BD3DD36E2A2DA5CD83734C2F91626B0521D06 + CDCA4B90FF117D4B2D9EC680E1117D6FE7D6F7B0B18EC03987749C75F80ADB63 + 521CB5053447A76278F33DF8B0D600D7C60EC79636CD3046B11A7A5617D0A699 + 00B521021A2F2C8FC64795D0E46E7DA83D6C02B5D06668EADD0C4DAE36440307 + 65D43EACC46AB85AA8B2AA3C2A4FEE0C05C6012F69DEB03CE40A195E9F66A6B8 + 0621DB33115F6F4920754EC5D7EBA9C8BB9A86FC2B69C05596852E4800DB04E4 + ED7F8B57C31DE0546F3636A94EC2824AC331A9D92E581A1F520F0E0B2CB51872 + DCE693DB9675AF71E4C05B9CB64BC3E9935F71E4700EAC8F007D07C560588F63 + 6CFEAE23EBD37D443E7441B8F329F8EDD90657D3F9B01B38069B5A76C134D5BA + E857AF0CBAB615D09161D4DA4480EAEAB250B0AAC4485815653794439B91D5A1 + A2BE05C2FC470800FAC8AE1F9E362CC7E905E0C9D071C981F4422AB2CF2421EB + 443C2487E290651503C9D60FC858F70AD99B9FE3D3906BB0A93C0333AAE9608E + 821E36691E4347A16BF7817D357ED4999B3609C3870F1722222284EEDDBB0BE6 + E6E6426EEECF750AADB13AD8275F5BBFEA2D0EED8DC08963E9B0B3F98A83FB73 + B0FF00A0A1F30523DB1E42AEFB6EA4B19A25FDE65164DD61C4F1B507C258420E + 6722C5E70CD21D0FE2F98635B86B608293BD86C2B27907FCD1A83E4C9B2B6250 + F76A306D53198E8280C5CA6A98A83511CECBF51F873AEFB2B86977CCC677D15E + 441F7883DC33EC8ED8297116F87A4C828C5D1F90B9F513241B3E207DD54B64AE + 0CC3AB018ED8AF608CD98ABA30637858F4DA8FA1C290EE33A698301D02E1E225 + 2761ADC58642FFB7DA8BE7CF4BC48FEBD732CE2D5FF4167B77BFC1D1431938C6 + B8B1774F36ACFE64FE323215E35A1F83E4CA06C43A5B23F6D25E7C3C6F85F4F3 + DB01C7F5C83DBE1C38BA18B05906D8AD024EAE058EAF41AAE542449B18E0E1E0 + E1F069D11DDE556AC1736A27B82C6F05B7BE02AEB09AF5CC600127470B70D4AB + 02A7A9AD7175F2685C315D8B07E6E721D9F401D2AD89C8B0888264D53BA42F79 + 8ECC258F10A6790A7B142663A1E25898288CC1F26ED68C1FDD3B5CBB7B53781E + 16201BB78282A2D0BC894A458D2E2D55C668746B3DB147BBA133F546F7F0BE77 + 5F888A7A5F2C168989B9C28913A927979ABFC136C6C7BD5669B03E9083DD3BA5 + D8B1331F1ABAD918DBFC38728ECE47CC894D883ABC0E51D66B2039BA12F9B67F + 20C77A0972F6CC43CEB69990AE358274991EF216E90233B58011DD90D4A32D1E + B6AC8FAB8C1B6F2F6D401EDEE2FD7D5B049D5806B75DE3E1B1B0273C27378697 + 5E05DCD51470B0BB002B86939FDE0CE4ADCE45E6EA8FC85C1A89B4054F913E3F + 048F35EC61A5380D8B6A8E86A1C238E8D45D996F3DE95463C9ABBB831E5C9979 + D6FDE2348F0FC1168FA23D967D4ABBBD96258725885DCF4ABF3BB351BF9E72ED + 9E9A3AC5AF4D3E920816164947962E7A8695CB1F62FBD634ECFF3307DBB749B1 + 655B3E068C9542ABE17148B61B2376CF12BCDCB200519BE7E0EB36537CDD351B + 393BCC20DD600CE94A4348978C8774DE2848CD7490653400299AED11D5BC1E1E + A8D4C4F532025ED8CC6597F460E624738CACEC9378FFD40AC14E4BE0BED7103E + 0BD4116C580B37C70870193A04C9AB580C59168B8C856F9136271C69664108EF + 7B0AFB6B9A6011F39749352660685DCBF7A10EE75667BD3F8024A68F90E180B4 + 7787901D698BCCE747F0D96D0982D73640E2C17AD06C5F5BBB86928AB073E74E + 595CC9C8C82864BD325F7070C8F8D364EA132C5E188CCD1B5361B533079B3666 + 63D366406B62223494AD90B86A323E6E9B8DF0D53311B5DA18B96B2723C7620A + B2574D42F6223D48E7E8227BC630644F1E846C034D64E9F6404A8F96886AA282 + 5086874B4501CF8FCD4476CA656426EE8734651372629623EBB139F2C2E7036F + E6E2BDFB10F81BD5C0CD96025C07E9216EFD1764CC8F43AAE91BA44C7F8C5493 + 003C6678EC53328679CD91D0AF3E01BAF577BF0F3C749839AB0BF219D6594947 + 9015BD1F99AF2D9111B212E95EB3F1C2AA0DE2B757C4C92DE3FF1084B205F144 + 4949A9508E787B61F3C4F16158C8F868B13A09DBB7E460DDDA6C6C6678E81826 + A26F9DAD885F300E516CFCC1E686885A3211F9CBF5180E639065361C59C64391 + 356900B2F5FA226B544F64E9A84332A003923B34415483DA085556847325014F + 8F4C43D6E753C888DFC170598DF4A78B90E86D8634BFC948F11887D7177B23C0 + 40019EED04380F9E80B8359F916E1A8BE4696F916018868449F711D6DB1EFB6B + 337ED41C8171D5C663449D7529CFF7D97785E4F2B3D4C48348FEB804292F1722 + 39782692DC2722E1FC10C49D54C783ADD5F1C4B6E7BD5A557E8CB1F258B87BDC + 14962CBEB96AC1BC04CC9B1D8C3F96C563E37AA90C8F6D5B005DE33474AFC5EE + DFAC0724163A78315F17698B46038B4620C76430B2181724637B4332BC1B2483 + 3B2353B31D327BB746867A7324B5AA8F77AAB5F082E171B3B2804756FAC87C77 + 00E99F2D9095B010A9C1A648F69E8A0CDF0948761F85178E3D103A4D11EE9D18 + 1E5A8C1F2B6290362D1AC9466F9030F13112C6DFC7E39EA7B0ABF654982BEA60 + 4C353DCC68BE3DCF6ADAAA1A2F42ADAE7FCDDA0749C274C68DB1C8081A84F45B + 5D9070581129072BE1D25C96DAAF4CF55D396F5AB1787879DD1016995F355F30 + 2F0566330361BE201A6B576763BD05CB2F3B8109933F4310AC31B9C114EC18A2 + 85F3A3BBE385495BC0AC35308995DBBA1D995331D3688B9C1E2D90D9590D19ED + 1A21BD35F3D9A6AA78A7AC8457CA357187F123749336D25E6C43DAE745C88C9E + 81147FC60B8645A6D72824BB0D45E485AE786A5A13B73BB3F8A1AD8FD8C53148 + 358846E28497F832FA211274FDF0B0871D36D79E8CB935B43192E1B16DA04D7E + B39A9D85B5CBB5CC596263435AC182F0A20444CD8844D8D4472CD8792178B5F3 + B3738BB765E67EAD49637EF2E4A970FDFA7561FBF6ED85FACB9370CC1A3EEC29 + 6698F8638ED97BAC5896CDFC86C58F755FB16A592A0C0D22D0AADB4BD4AC7313 + 55CBEE81A2608A210A5AD8D2B2035CBAD6C5DB9E9521E95609E8501D68550B68 + AA0C34514646833AF85847118F183FEE542A83C7ABFB2131F40FA4C6CE444684 + 3E92BCC622D57724323CB490E2D61F31973BE299A9125C193F5C4798206E610C + 52C645331C5EE2F3F010C46933EDDEDD169BEB1861468D61D0A9361647071CCB + 1FD2A8478BAA552A0ADA7DDB761DA0D9B1A9EEC8210A8307F416466A690A963B + FE14829F7EFA257DFAE851D69491C35FC3786A004C67BEC762F32CAC64982C5B + 2C81C52AA6DD0F01E74E81E9D77CAC5821C5D889C9E8D8EB35541BBB41B1F22E + A80AFAD010BA605E99063859A3328255047C6E20205DB53C52942AE335E3C75D + 164F439676407CE03CE6E306487B3E02C99E8C2FBE8391EEAEC1B45E6F7C766A + 8F7013965F58FCF0186D0A2CCE02F4A580DE277CD60E46F4004F84A8DB606BDD + C94C7BB0F8517514AE0D3E963DAEF3E00E3FFDBFF2BBF7087979255BCF0B0894 + 8C1F39FC0D26B37835DD24120BE66661897936CB3759309F9F85F9B3B3B07AB9 + 1487F7E4E23CABF1AE9D63199369489BA3C09A8D80C1AC34F4D57E82E6ED2E41 + 49711314043D7410DA405FA8090BE6A2CE0A026CCB09F05ECA7CEC811172BE8C + 43E6D32148F51EC0F0E8CBF0E8894CCF5EF878A5031E1BD746488F7238D6550B + 962DCFC141ED2AC25BB923B94F3062FB7B20A4CB516CAF3B05460AC331A3EA18 + F86AD9656AB4EFDDFAFBB0159855634651B332B34A65CA94A9A0A8A858A659B3 + 66C2D7AF255BCFF2F24AD7D11D1101FD09F71947223177B614F3E764611E19C3 + 62F6AC2CCC359330DF9160DD0AC699E56CFF4716B6AE95E2E0AE3C9C62B838B0 + D47FE4B014EB2D5331DDFC23B4F5FCD1B5F731346C68867665BBA09630007ACD + D571EF601BA4A4337D94D099E558B67FC9CA98A03EC87BD00F51373AE091712D + A40EAD82A5CD5AB0B8D585595FA8D55A09EF4E6EC8EDEB8FA0CE87B149D91013 + 59FC5854551F0F86D8A7766FA3DE8A8DBD2C1BBB1261C2F6D5098F4A952A9563 + 26B46FDF5ED0D0D010A4D29FAF6349B3F3057FBFB401A375DF62DCE800184D8A + 6498486132350B338C259869F2CDA64F9360F64C0916CCFE66E67324583C97D9 + BC4CF63A1D0B4D53B0646E32D62D4FC59EADE9B0DE9B8EBD7B13B1615B14662E + 0FC54856E3F7EAB10DEA5D8DA1AD3B98C5AA76D8B7A021EEEF698014D7164068 + 2744DBB27C34AB125C59BDD35161246AD45907B5967B51BECD2ED877621AAEAB + 2F023A1D8685B201A6D618827955F4E035F06862D716AC8A64E36738D465FB5A + CC1489270C8B4A55AAB0C0C2B0AA50A142099F1F83101696DD6784CE6BE88EF4 + 83DEB88FCC98CB8ECBC6C4F19930D0CF84A1BE4466460612E65399986A28C134 + 23094C26339B928119535398C563E6D458661F317D7224A619BCC654FD97309D + F6062BCCA3B069CD07AC5915C1FCF111B4FADF42C74E27D0AAD55A7468A607AD + AE5D30A3772D6C607A7D7E374534647ABC5EC3D368D5E228EAA8CC4325D57970 + 6AE7C2E2B537023A1CC63A1503CCAF3E1013AAE8E294E6FE847E4DBB766263AE + C1AC1E336566B599D5245C1846E43B652C2D2D65CFD15EB97285F98397101B1B + 2BAB7F0B7DBE2436AF75FB76BE68D3EA3086B1B8356A640A468E48C588E1796C + 0FF6732E744749307A5426C6301BABCB6C7426E3533AF4C6A460C298784C181B + 8B89633F407F5C24B3D7CC9E435F2F9CED43315E3710BAC3FC3062A81F46EB04 + C2609C2FC3CC0FD3593A1C33CE07EA7D1CD1ACCD7AA835334215A5CDA8D3F03C + 1AD45F829A8A23A0A43C08B51A1BE0660B57E4B138E2D9FE2056AA4CC4FC6A83 + A15D790C96F4DF1BD7BB69C7F6DF3168C4C65F9F99CA77AE28325E942D57AE9C + D0A95327A14E9D3AB22043B1A477EFDE85EA0FD9B31479287BE4D89B459AFD0E + FAB56D7DE66B3D557F346AE0CBB81D8EC183A3A03DF433B4B432A035388F1930 + 74C8576619D01E928C615A5FA0333486D9076611183EF415866B3FC308ED3036 + FE87CC8298F963D4305FE8EAF8305C3C3052FB0A74865C87F6404F0CD6F48066 + 1F0F74ED7A191D3A9E855A9333A8566D261499DE52511E85BAF57AA156D30970 + 6E760B398D5DE1DFE6102C548D30B7421F98D498815D139D24AD1A3669F29D1B + 4D190E8D993564A64A3EC3B0A8CCACEAF7185B49CE2A16F90CAE2457F8F4295D + 781812D3C0D6F6DDA4F9F31EDAF4EF7FF7955A931B50AE7B074DD582D0A3DB2B + F4D78CC400CD8FD0EC97044D8D74666CDF2F06FD352298BD60F694D9236621E8 + DF2F90993F06687863403F3736EE3B18A4E98461836D995DC0D081B731A4FF4D + F4EF7319AD5B1E45C37A4751BB96056AD4188B3AB5A7303C8CA05CBF2FEA3437 + 844BF3DB40636F84B43A801DD546E04AFDE9B836CE463AACAFC13C5692D0C453 + 4C6DC17068C6F66ADFFDA5C087D8FB35192E0ACCAA7FCF41B23C54141E999939 + 72EF4985F88434C1DF3FB6CB76CB978B264E08B8DEB6CDAD4415E5EB0C9FEB68 + DCC80BEA5D42D0BB57087AF50C43CFEE6F9845C9F63DBA3F66D80530F365E6C9 + EC2EBA77BD89AE9D5CD0ADEB39F4EB750803FB1DC3408D9318D0F704FBF9303A + B4DD85664DACA152772B1415A6A06E9D49505531864AC33168DC7C268E377784 + 9FEA013C50DB8028CDFD383D72CD6DF5D6ED35BECF7B4766E4339477DBB2B113 + 06E528C6B2F12B962D5BB69A881F15BE5B793AA6E4CFAFFFB8E61519F959B873 + 27B2BAFDA9C86173E786EED01EEA16A8D6E42CC3E614146A1C4133351B74EAE8 + 80AE9D2FA333CB8D9D3B86A0538747CCEEA363FB3BE8D0FE1ADB5F4787768EE8 + D4CE9A61731C3DD48FA0A7FA2174ED78082D9BED6178D842557933F395495066 + 1AB49EEA44D4AB3F01ED3BAECE33EEB2C0614553BDEB177B2E7B62AE39792D1B + 5D9DEF73DF9D5997EF98505C6D4E7EA0A6A626989A9A0A7DFAF429D0641D3B76 + 141856B2D7A1A1A182BDBDFD3FEEB14AA57F69BBB0C75F189F3284808068B535 + AB43A69A99FA9CEAD0CEF67DF5AA1B50ADCA1F50ACB188F16705DAB6D986B6AD + AD599C267FB82BB336AD6E307364AFEDD9DE81993D5A343B89C60D6DD194C58E + 7ACA1B591CD5673C998A06F5F4515F55071A7DAC50A379A7B1A4B0CA55AF443C + 68C84C9D59EFEF7874FBBE274CAA57AC5851080CFCD65FA6BC327DFA74E1C081 + 03B29F5FBE7C2974EBD64DA6554BA2474AF4CC485A4E8176F1F68E122222BF08 + F7EF7D2AE37AF3539FB9B3FDD6680D39E3D1A6F5A6EC2A958D5146180D85EAFA + 0C9F5968D67439334B346F6A8DE66A1718A79C180617A1D6E82CE3820DDB9F44 + 03D52D0C8FC9CC570C505B69143BD69CE5B4C39206F55B68B1B1B661D68B99C6 + 77D364D6FFFB9EF0A8A3AAAA4A7FDFA4D8FB4F4E4E16E2E3E3053737B7525DDB + 8B8996B07346092F5E2430BCBFF95746468EF0FA75A270E77694CADA5541138D + 0C5D8EF4EEB5E7456DA5694C6B0E64D603952A74637EA68B86F5E7A37EBDF5A8 + A7620525A5FD2CA7D9309E2C67F1549B714C17EA9DD7B23CB6C6A369D3B65ADF + E77E10B33EDFB1E8CBAC2BC550662A141F5AB56A25C4C4C494F8FEA3A3A3FF6F + 7E2730FF1B2E05F55140AC60B92DA0D3FA75F7E699CEBA74BE7DBBB9D165192E + 2C0D306B8C0AE5DBB11CDB9D61310B756BCF6339662EF391DD997D7A4FD958BD + 7A391A6FDBEF18D46731A0B292925275CAA13A3A3A656C6C6C8465CB9609FAFA + FAC2F312F6CFFFD3161191C4E2CD5FBE9A9A965BD1CFEFD3800D1B6E6D1C317C + 874FC306937304A12ECA0ADAE8DDFD0CFAF65E76BB658BF6BDBEE7008A15A425 + 4877B37CE72FD35253A64CF99FFD3DC1C4C4CC223FCBC8008B7D71F5CF9C0934 + 986C74607F8F6EFAD3AB55FBAB4ED7D6D6167C7D7D85E6CD9BCB7EBE7BF7AE2C + 1E2E5AB4A8947EF7290B6412890419EC66FE5B2D2D2DED6F76694A653CDB2920 + E7261BCA5D0179B705249D15F0C24A80D76201170C04845CDC03162FFF664949 + 4932A3D75FBE7C81A85FF83FB93D3CB51ABECBBF8D9FB020CBBC2620EA8880A0 + B5029C6794C1799346253A17E171F5EA55FCAFE2214D4FC6F5E902DEEC13907B + EB1B16B48FB513106629C06D8180B3E305C4847995188F8B172F222424E47F92 + 1BB756F4917120C3E92F6EA45E1410C124D6BD9502AE4E63FC58A651E2F3713C + DCDDDD4BFC1D969B4B6D3C2F65CFEEFFDA35F867899161B8692AE0A3CDB77841 + 5850FC88B615F0688B803B73059C195F016971EF7E198F9B376FFEF0FE8D1B37 + B074E952998D1A350A1B366C9019CBE9B27D696D741DF9B1D3CF666666857296 + 8EE7D7779A514B36EEEC1B7F718362C8CB3D822C9E5C36121040EBE9BFB0111E + F4FF92E4F1A079A3FBA27B32343494BD26BB7DFBB6EC7EE87D3EB7F4BE979757 + A1734AC7F073F1CFC5AFC5FC109F935F437C2C3F863E7339B20E9E2CAD7EB1FF + 31867E38CAF2C80641C61B470305D9FF3DE2E7105F9F5F5B7C4D8E87A3A3A30C + F7C236311EFC679A3B1A3F7187D582B2EF93D1CFE28DDEA36BF1F3F3E3F898E8 + 33FA0E9D9B1F6B656525DBD3E7740DFA9E3C6674FD8B46E565B9549C5FE31D04 + 59CE95E5577D01B70FAD2AE0139D9FF6742D7AEFF4E9D33F8C838F8FF3E357F0 + E0E7A5F3D079694FDFA7738B794163E31C179BFC39FCFCFCFEE683F4335D57DE + 67E8E70326EAB25899725EC48D2B02DE1DFA2BBF9E99D2F0876BD075B90F725E + 883FE7E38B8B8BFB657E88CFE3E4E454306E31AFC5732FC642CC0F7E0EE218ED + C5BE459FD379E5710AF2F7928D97C6CEF32B7184F2EB93ED7FE55757FBBD0563 + E2E7E1E72C6C1CB4CFC9C9C1870F1F64734CE32A2ADE89E789F38BF396739BBF + 96F7177A9FCF0FC788DF1BC5223E677CFE781CA2D7DC07F91868BB32AF2D82D7 + FF3DBF92FEE0F9F5B499FA0F715A1C278A1B07695F8A3784075DFB776D9C1BFF + 768B7DE22BCBA1944F39163CBF520CE5F99534DAAF6CD9D9D932CD9F90908067 + CF9EE1E8D1A3B0B3B3FB6D78703EFCDB8DF22B694E9E5FC5358ACFD26FF935E8 + C4CA7F8C454C4C8CCC770F1C3820FB3BB9FFCDDBD36B7B657923FECCDF6B9407 + EBFECAAFFF040BCA29E45B919191B87FFF3E76EDDA85BD7BF7FE762DFAAFB831 + AD3C5EEDFD31BF92F6A0FC4A3A84F2EBBB7B574B7C3EAAE3E5B1A018427165CB + 962DD8B16347A15A8AE7845FD9E89CA5198FBCB64FC0FD35DFE226E706C553CA + 31F43ED57397E774F8C7BC88888890FDFDE8C78F1FCB62BC858505B66EDD5AA8 + DF8B35C5EFE04A615A5DBCBD0D0B84F3AC3232BF90CFAFBC7E75182720E1EDA3 + 12F19A78919A9A2AC382C62FC622303050F6FF80E97F10C9F383B8417996721D + D710949B68DE09237ACDF3A87CEEE07A868E13EB4DD25D94DB786DC4F7FC789E + 9BE9BA5CF752DF827207E92D717E25DFA1FC4A31748F41DB829C4D7BFE9A6B60 + BA073AF7EEDDBB0BB0A0BF297EE9D2A582BC4F58F8FAFACA7A1FC40F718E17F3 + 83D710FC73AE21C4355F51F9B528DDC5F596F8BEF9B15C3F9106586F364E36FF + C4058E05E516AA6743377D8BA1F663CBE3CC49DB82EBD126D638FC1A648405E9 + 4FD25C1E1E1EB2B1CF9A350BF6F6F6F0F6F696CD17D5719B366D92E598A2F010 + 6320C646AC694A130FCEADCBD3AACA34A7D45954A39CF931BF3A59CE2AD054FC + 3EF835F9FDF2FBA17C4A5890E6A2FC4178D0F76C6D6D65581047DCDCDC606969 + F9B77CCB3522F705EE27FC5EC546C789FD955F9F9F83F0231FE0FEC27D8DC75D + B14EE4EF5DDB31BBC81E20E557D2ECC747572AB826F76B7E7DAE6FF9FC91BD7F + FF1EAF5EBD427878380C0C0C606E6E2EDB931FD1F569BE8827946F09A3C2629D + 789CF2AFE5EB67311EE2385CD839E58DF383E690F425E96EF91E20E557E28B87 + F9B71A85F46A71719A6227F5899F3E7D5AC0738A9BF49AE686C64E7A837841EF + D371F7EEDD9371877CA834369A073E2FBFAAE939CF3C36EBCA3820AE51E835F5 + 0029BF96A407486B048405E14BDA429C43286E921116840DBD4F3A9DFC887E3E + 78F0E06FAD5F4A9A77C9A8B7477192FA3AF2F995F70029BF16D7032C0C0BF205 + C282384158D06B8E057D4E79978EA59F2976500FE4BF61BB32ABA16CDC142B78 + 0CA53E07E5D79FF500F3F2F20AB0E03A8BE69DC64D5CE039846341FE4158102F + 28CE127EF433F1431E0F9E138AEAF7FE939AEE6735EE872067596CF87CEAEFF9 + 95F7004F8E2E8F3D3BB6C8F2B158231486058F1B3C56702C882BF419C556E204 + C78274097DAF287FE179B0B8785AD23821DF732A4C97527E955F63E3F955DC03 + A439E27D5CFA1EC7826A54E23BCD37E510FABF25E2B8493FF3B849C71016F47D + C28170240D4FD890F6280E0FAE13786F4B9C27B9EEE19FC973806B597E8C3867 + F36BD2FEF4A2C1C5AEB1518D727854D582F9E1F999DF078D89CE4B7961F1E2C5 + 325D5558DCE4FE41B9578C05F582A8AEA1F7F6EDDB572C1EE27CC8C72BEE0772 + 5DCE8F15CFB97C0F55AC13B83F6E5ABB52365ECA1F45D528945FE74E18F2031E + 743F349F741E1A3FE14358D09EB416F1A2B0B829F60FD2ACC42DC242E6B3EC33 + C283FCB1283CB8D614D72AF2B51E9F7FF91A48AC39B9BEE731896343B569616B + 6CE21EE0F53F06FDD0B7E478D018E99CB48644FA8AC74D710E11FB078D97343B + F70FD228B9B9B97FC530F639E90F793CC4FA94EE5BEC1BBCA74AE3E763E27E50 + 18A6BC9F4B7BEE271C5FBEC6463DBFE2D6D8C2023C65EB62F41D8A175CE3D2B8 + A906E1B5081D43FA52ACB178DCE49CA0757CEE1F74AE1F62FA773C0AD36345C5 + 4B796DC9E7ABA8D85AD43968BB38B5646B6CE2EBD1588817744DE202C5561E37 + 09171E37B9C6E2FE41F196FB879813F2F745F3F66FF429AFDD7F758BF4395764 + 0F907AE8BC07C8FBC33497C471E23BCFA73476C281E790C262857CCC94E7843C + 3FFE2D1EFF74BB3CA5BC8C073CBF522C955F637B7EC35A762CCD276141F34CE3 + A47CCAB5B7180B7A9FFB87387F8863E6CF7432D577274F9EFC3FC5C26797D1DF + D6D8C43D40AA5F9D4C5B1560413C272C88FF34FF3C6E8A73088F9B944BC5FE21 + 1F338BD5848C1F147FFEE97A03EF7F15A6BF0A5B9FA7F7485F16B5C626CEAFD4 + 03E458D0BCD1BC13163C56F01CC26305F70FF227EAF788737B49B77F8BC7AFAE + 41D1FD9D36A8F3D335B65B1B46FE80055F5B1363C1E3268F15DC3F78AE2B2927 + E4F1A0DE696178F0FEBBB867CAD71DC5EBF1E25EBC7C8F8EEBD9827514EF2BB2 + 1C4A3509C782FA5F945FA907489FF11E20F19F9ED3E1CF9DCC9F3F5FD6F3E4EB + 2314C7ADADAD657B8A1BE41F14374827F0BE136125BE1732AE1D0B9B2F8E877C + 3FA8306D595CFF8F73931BD75EF27DA1CBC68A329FE03D409E5FA946E1F995D6 + D8C80FE85AC403636363191ED4CBA23E30D59EC411D2A2FC9EE878CA1FB4262D + E626EFC588D7ADB91E2ACC97080F5A6B90C743BE9F591C1E625CE4D7C1C5EB94 + 3F5B63A37505EA01523EA06BD178A95FB56EDD3A98989860CA9429B2B500DE0F + A76BD2FC734E72FF10F72BC5F7C5639D780DBBA47888D7D8B926A5EF73CDCAFB + A162CCF9F1DC9FF877694F1A82AFB1C9F700C56B6C964B8CE1EAEA2AF30BCA7B + C403FAFE9C3973649A7CC58A15057D1BBA36BD96AFC3F9B5C575B5F8591BDEF7 + 2D6C4D99E3417C2C2CF6F15A8DF38FFB207F5643FCFC8E58A3F26B723FA5F848 + B1B2A835368AA1174DDBCAE222F709FA3ECF21942FF8DCF2BCCBAFCDAF23AEA9 + E59FCD11D73DFC1993C29E5DA35C4DEB95B4C65F5CEFE2DFF413A9B757D41A1B + 6975BEC646350ADD2F61C0B1A01C42B192EB6E5E9392A6284E67FED3AD2478C8 + E3FDABDBF5F9AD64B983F700790C15AFB1396F185FA0BDE95A1437B8C612E752 + 1E337FD74678D07A547178FC9B8D7A80255963F3F3BC5BA037090B8AD15C5790 + 7F70CDFD3B38218F07C595C2F0288D757AF9E7ECC95FC4CFB0D073F6B4F624AE + C92856928FD3BD71FF2849EDF1BBF1F8B79B109B02212119755F4516F99C3D69 + 55B1F6968F15E41FFF4467FED38DFA6C45E1C1FB9CF2BA94E757AE47F99EEB3E + 7ADDE3D25D088CE332235C223260ED652ACBAFA4C7A88F4EF9F5C0BAF9323D48 + E7259DC1FB19840D6142E7E6EB89E29EAD3807F2DEEEAFAE0510CE64C43DF247 + 8A5F749DA2E247513A4C7E3D5AFCEC203F46884FFE8605ED3FA6CAF0109E4950 + 2930BDA046B19ED048A661A89F4D463A9CB0A0F352CCE4E3E76BC085E5399E4B + F9FC10A7C446B51D37BA37C2987C42BC6647F18A9EAFA4B56CFE3C7A69E2A11A + F0FA1B16CC5764DC789F0EE1452684B06C080FBEA2AC7F2EE6AC9A8E65738C65 + 71937380EE47ACF139DF8AAB11C5FD7EFA8CC627FFBFD2896B64346EDE2F21DC + E978D23AA445E93561466BFB3FC3435E97F23E31E730AFED391E321CE2BF6311 + 9506E1CD376E080FA5287B3F0F82673E0C5D5FC9F438DD276951DA739CC59A4E + FCEC6A61FC90EFCFD279E878EA1B92D1BCD3F949DBD2D8E9FBD4FBA2F1D2BA13 + ED49EB514F955ED379C85F686E8AABD9C5BA54BCAEC2E7876B55D9F8090BCE89 + A759DFEC3B3704DF6F78DC0B7B269B3B9A2FE22E8F99F27A915FA3281DC4D742 + B8DF707CF97869ED80D663090FD2BC1C53BA36E571D2758405718670203C8823 + 853D5B5C54AFA7D89C42DC204CD8F8890BADFC32A1E5F919F5D89E63B129EC5B + 5D4EF539698AD25C13E77894E4F9518E058D916A16CE0DC2A6347E5F4CE61F14 + 3B1916FDFD530AFADF5C7BD3CFD7C2DFC9621AC5F3D2D6991C0FEE1B25C182E6 + 85C70CC282D65D4AE3F709577C917EC383C588F391A905CF16500CA31C427193 + 7405C52CAA757F87CE243C48CFFD0C0F8E05D75E1C0BEE0F85E151DCEFF814C9 + 0D1633090B8E01E533E201EF5BD1FE77EA4CE21D5D9BB0280A0F8E05C50EE285 + 3866107FE5FE3EDDDFD6554AF27B86BD3E64C96206C505C281EB87C2F4CFEFDC + 0873DE2F29EC79163116D467E4BC280C8BC2F010F759B8D610AF6DF35E0F6161 + 1C9220E3EA7FEAF96EC29AF84F9CA67C2AAF538AC2828FB1B0BFD356DCF30E62 + CD23FE9D1D7A9F3F8FF49FDC080FE207F59FA9374EF1BB2458D0F145FDDDBAE2 + 9E7790EF93F175EEFF968DF0A079A15E23E55AD2A93FC3823457717FC7AF283C + 380FB896116BF3D27A9EAAB4F0204D4A6B118481388F8873EACF7851547EE17C + E0F592F8D91EAE8FFFD3CF25F28DF216DD27C551F217B1BE10E7D492F0E27FFD + EF19D0467D128A19549F528EE3BA53CC8BA2F2C8FF8F7890CE231D46FC252C08 + 1BD2E0C5E98BFF9FF120DDC16B767A56415E77FE2A16FFCB7850BD4EE3A53A8E + D60AC4B18262895883FFCA46DF93EF1BF0DE01AF09285EF1BA996A1F32D23F64 + 342F747D32EAB190913F53ED4D4679908CF21ED5E2BC2F463539E50432FEBB08 + D41FA27E1919F513C988F3E40364346E1A331FB7FC9E8E2F69DCFC1926548B51 + 5FF5DF6241F66FB0E0381487853C0EF47DBAF77FCA09F1F6FF004DFA3E53 } end object icons: TImageList @@ -13757,6 +13895,12 @@ object MainForm: TMainForm ImageIndex = 8 OnClick = btn_FileSearchClick end + object menuShowReport: TMenuItem + Caption = 'Show Image &Report...' + Enabled = False + ImageIndex = 24 + OnClick = btn_ShowReportClick + end end object FilesMenu: TMenuItem Caption = '&Files' diff --git a/LazarusSource/MainUnit.pas b/LazarusSource/MainUnit.pas index 2a1ad4e..fe59807 100755 --- a/LazarusSource/MainUnit.pas +++ b/LazarusSource/MainUnit.pas @@ -35,7 +35,7 @@ interface SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, DiscImage, Global, DiscImageUtils, ExtCtrls, Buttons, ComCtrls, Menus, DateUtils, ImgList, StrUtils, Clipbrd, HexDumpUnit, Spark, FPImage, IntfGraphics, - ActnList, GraphType, DateTimePicker, Types; + ActnList, GraphType, DateTimePicker, Types, GJHRegistryClass; type //We need a custom TTreeNode, as we want to tag on some extra information @@ -95,6 +95,7 @@ TMainForm = class(TForm) menuFixADFS: TMenuItem; menuDefrag: TMenuItem; menuChangeInterleave: TMenuItem; + menuShowReport: TMenuItem; OAAttrLabelAmiga: TLabel; OthAttrLabelAmiga: TLabel; MiscAttrLabelAmiga: TLabel; @@ -109,6 +110,7 @@ TMainForm = class(TForm) btn_AddPartition: TToolButton; btn_ChangeInterleave: TToolButton; btn_Defrag: TToolButton; + btn_ShowReport: TToolButton; ToolsToolBar: TToolBar; PartitionToolBar: TToolBar; DuplicateFile1: TMenuItem; @@ -269,6 +271,7 @@ TMainForm = class(TForm) procedure btn_SaveAsCSVClick(Sender: TObject); procedure btn_SavePartitionClick(Sender: TObject); procedure btn_SettingsClick(Sender: TObject); + procedure btn_ShowReportClick(Sender: TObject); procedure DuplicateFile1Click(Sender: TObject); procedure ed_timestampEditingDone(Sender: TObject); procedure HexDumpSubItemClick(Sender: TObject); @@ -466,6 +469,8 @@ TMainForm = class(TForm) //What are we running on? platform, arch :String; + //Registry + DIMReg :TGJHRegistry; const //These point to certain icons used when no filetype is found, or non-ADFS //The numbers are indexes into the TImageList component 'FileImages'. @@ -572,7 +577,8 @@ implementation uses AboutUnit,NewImageUnit,ImageDetailUnit,ProgressUnit,SearchUnit, CustomDialogueUnit,ErrorLogUnit,SettingsUnit,ImportSelectorUnit, - PWordEditorUnit,AFSPartitionUnit,ChangeInterleaveUnit; + PWordEditorUnit,AFSPartitionUnit,ChangeInterleaveUnit,CSVPrefUnit, + ImageReportUnit; {------------------------------------------------------------------------------} //Rescale the form @@ -644,15 +650,17 @@ procedure TMainForm.AddDirectoryToImage(dirname: String); fields : array of String; Dir : TSearchRec; begin + Image.ProgressIndicator:=nil; + ProgressForm.Show; //First, if there is no selection, make one, or if multiple, select the root - if (DirList.SelectionCount=0) OR (DirList.SelectionCount>1) then + if(DirList.SelectionCount=0)OR(DirList.SelectionCount>1)then begin DirList.ClearSelection; DirList.Items[0].Selected:=True; end; OriginalNode:=DirList.Selected; //If ADFS, create the directory, then select it - if Image.FormatNumber>>4=diAcornADFS then + if Image.MajorFormatNumber=diAcornADFS then begin importname:=ExtractFilename(dirname); attr :='DLR'; @@ -696,7 +704,10 @@ procedure TMainForm.AddDirectoryToImage(dirname: String); if (Dir.Name<>'.') and (Dir.Name<>'..') then begin if (Dir.Attr AND faDirectory)=faDirectory then - AddDirectoryToImage(dirname+pathdelim+Dir.Name) + begin + UpdateProgress('Adding '+Dir.Name); + AddDirectoryToImage(dirname+pathdelim+Dir.Name); + end else AddFileToImage(dirname+pathdelim+Dir.Name); end; @@ -751,13 +762,14 @@ procedure TMainForm.AddSparkToImage(filename: String); ok:=True; if((SparkFile.MaxDirEnt>47)and(Image.DirectoryType=diADFSOldDir))//Old dir or((SparkFile.MaxDirEnt>77)and(Image.DirectoryType=diADFSNewDir))//New dir - or(Image.FormatNumber>>4<>diAcornADFS) //Acorn ADFS + or(Image.MajorFormatNumber<>diAcornADFS) //Acorn ADFS or(SparkFile.UncompressedSize>Image.FreeSpace(0))then //Not enough space ok:=AskConfirm('The current open image is not suitable for this archive. ' +'Would you like to continue?','Yes','No','')=mrOK; if ok then begin //Show the progress form + Image.ProgressIndicator:=@UpdateProgress; ProgressForm.Show; //Bypass the GUI if over a certain number of files if Length(SparkFile.FileList)>bypassGUIThres then bypassGUI:=True; @@ -789,7 +801,7 @@ procedure TMainForm.AddSparkToImage(filename: String); //Update the attributes filedetails.Attributes:=GetAttributes( IntToHex(SparkFile.FileList[Index].Attributes,2), - Image.FormatNumber>>4); + Image.MajorFormatNumber); //Assign the parent directory (if bypassing the GUI) if bypassGUI then begin @@ -945,7 +957,7 @@ function TMainForm.AddFileToImage(filename:String;filedetails: TDirEntry; ReadInDirectory(DirList.Selected); //Find out which side of a DFS disc it is if (Image.DoubleSided)//FormatNumber mod 2=1) - and(Image.FormatNumber>>4=diAcornDFS)then //Only for DFS double sided + and(Image.MajorFormatNumber=diAcornDFS)then //Only for DFS double sided //As with DFS we can only Add with the root selected, the index will be the side side:=DirList.Selected.Index else @@ -969,9 +981,9 @@ function TMainForm.AddFileToImage(filename:String;filedetails: TDirEntry; //Does the filename contain the filetype? if ((Pos(',',importfilename)>0) or (Pos('.',importfilename)>0)) - and((Image.FormatNumber>>4=diAcornADFS) //ADFS - or (Image.FormatNumber>>4=diCommodore) //Commodore - or (Image.FormatNumber>>4=diSpark))then //!Spark + and((Image.MajorFormatNumber=diAcornADFS) //ADFS + or (Image.MajorFormatNumber=diCommodore) //Commodore + or (Image.MajorFormatNumber=diSpark))then //!Spark begin i:=Length(importfilename); while (importfilename[i]<>'.')and(importfilename[i]<>',')do dec(i); @@ -984,19 +996,19 @@ function TMainForm.AddFileToImage(filename:String;filedetails: TDirEntry; if Copy(Extensions[index],4)=LowerCase(filetype) then filetype:=LeftStr(Extensions[index],3); //ADFS and Spark - if(Image.FormatNumber>>4=diAcornADFS) - or(Image.FormatNumber>>4=diSpark)then + if(Image.MajorFormatNumber=diAcornADFS) + or(Image.MajorFormatNumber=diSpark)then begin filetype:=IntToHex(StrToIntDef('$'+filetype,0),3); if filetype='000' then filetype:='';//None, so reset end; end; //ADFS, AFS, DFS, Spark & CFS only stuff - if((Image.FormatNumber>>4=diAcornDFS) - or(Image.FormatNumber>>4=diAcornADFS) - or(Image.FormatNumber>>4=diAcornUEF) - or(Image.FormatNumber>>4=diSpark) - or(Image.FormatNumber>>4=diAcornFS)) + if((Image.MajorFormatNumber=diAcornDFS) + or(Image.MajorFormatNumber=diAcornADFS) + or(Image.MajorFormatNumber=diAcornUEF) + or(Image.MajorFormatNumber=diSpark) + or(Image.MajorFormatNumber=diAcornFS)) and(filename<>'')then begin //Is there an inf file? @@ -1032,18 +1044,18 @@ function TMainForm.AddFileToImage(filename:String;filedetails: TDirEntry; attributes:=''; //Default if attr1='' then begin - if(Image.FormatNumber>>4=diAcornADFS) - or(Image.FormatNumber>>4=diSpark) then attributes:='WR';//Default for ADFS and Spark - if Image.FormatNumber>>4=diCommodore then attributes:='C' ;//Default for Commodore + if(Image.MajorFormatNumber=diAcornADFS) + or(Image.MajorFormatNumber=diSpark) then attributes:='WR';//Default for ADFS and Spark + if Image.MajorFormatNumber=diCommodore then attributes:='C' ;//Default for Commodore end; - attributes:=attributes+GetAttributes(attr1,Image.FormatNumber>>4); + attributes:=attributes+GetAttributes(attr1,Image.MajorFormatNumber); if importfilename='' then importfilename:='NewFile'; //Validate the filename (ADFS, AFS, DFS, Spark & CFS only) - if(Image.FormatNumber>>4=diAcornDFS) - or(Image.FormatNumber>>4=diAcornADFS) - or(Image.FormatNumber>>4=diAcornUEF) - or(Image.FormatNumber>>4=diSpark) - or(Image.FormatNumber>>4=diAcornFS)then + if(Image.MajorFormatNumber=diAcornDFS) + or(Image.MajorFormatNumber=diAcornADFS) + or(Image.MajorFormatNumber=diAcornUEF) + or(Image.MajorFormatNumber=diSpark) + or(Image.MajorFormatNumber=diAcornFS)then begin //Remove any extraenous specifiers while (importfilename[4]=Image.DirSep) do @@ -1054,10 +1066,10 @@ function TMainForm.AddFileToImage(filename:String;filedetails: TDirEntry; //Convert a Windows filename to a BBC filename WinToBBC(importfilename); //Check to make sure that a DFS directory hasn't been changed - if((Image.FormatNumber>>4=diAcornDFS) - or(Image.FormatNumber>>4=diAcornADFS) - or(Image.FormatNumber>>4=diSpark) - or(Image.FormatNumber>>4=diAcornFS)) + if((Image.MajorFormatNumber=diAcornDFS) + or(Image.MajorFormatNumber=diAcornADFS) + or(Image.MajorFormatNumber=diSpark) + or(Image.MajorFormatNumber=diAcornFS)) and(importfilename[2]='/')then importfilename[2]:=Image.DirSep; //Remove any spaces, unless it is a big directory @@ -1072,11 +1084,11 @@ function TMainForm.AddFileToImage(filename:String;filedetails: TDirEntry; NewFile.Attributes :=attributes; NewFile.DirRef :=-1; //Not a directory NewFile.ShortFileType:=filetype; - if(Image.FormatNumber>>4=diAcornADFS) //Need the selected directory for ADFS - or(Image.FormatNumber>>4=diSpark) //And Spark - or(Image.FormatNumber>>4=diAcornFS) //And Acorn FS - or(Image.FormatNumber>>4=diAmiga) //And Amiga - or(Image.FormatNumber>>4=diDOSPlus)then//And DOS Plus + if(Image.MajorFormatNumber=diAcornADFS) //Need the selected directory for ADFS + or(Image.MajorFormatNumber=diSpark) //And Spark + or(Image.MajorFormatNumber=diAcornFS) //And Acorn FS + or(Image.MajorFormatNumber=diAmiga) //And Amiga + or(Image.MajorFormatNumber=diDOSPlus)then//And DOS Plus if(DirList.Selected.Text='$') or(DirList.Selected.Text='AFS$') or(DirList.Selected.Text='DF0:') @@ -1085,13 +1097,13 @@ function TMainForm.AddFileToImage(filename:String;filedetails: TDirEntry; else NewFile.Parent :=GetImageFilename(TMyTreeNode(DirList.Selected).ParentDir, DirList.Selected.Index); - if Image.FormatNumber>>4=diAcornDFS then //We'll set up a parent for DFS + if Image.MajorFormatNumber=diAcornDFS then //We'll set up a parent for DFS NewFile.Parent:=':'+IntToStr(side*2)+'.$'; //Set the length - the actual length overrides everything else NewFile.Length:=Length(buffer); //Does the file already exist? ok:=True; - if Image.FormatNumber>>4<>diAcornUEF then + if Image.MajorFormatNumber<>diAcornUEF then if Image.FileExists(NewFile.Parent+Image.DirSep+NewFile.Filename,ref) then begin ok:=AskConfirm('"'+NewFile.Filename+'" already exists in the directory "' @@ -1117,7 +1129,7 @@ function TMainForm.AddFileToImage(filename:String;filedetails: TDirEntry; //Function returns pointer to next item (or parent if no children) if Result>-1 then //File added OK begin - if Image.FormatNumber>>4<>diSpark then HasChanged:=True; + if Image.MajorFormatNumber<>diSpark then HasChanged:=True; AddFileToTree(DirList.Selected,NewFile.Filename,Result,False,DirList,False); UpdateImageInfo(side); end @@ -1390,11 +1402,11 @@ function TMainForm.GetWindowsFilename(dir,entry: Integer): String; if (Image.Disc[dir].Entries[entry].ShortFileType<>'') and(Image.Disc[dir].Entries[entry].DirRef=-1) then begin - if(Image.FormatNumber>>4=diAcornDFS) - or(Image.FormatNumber>>4=diAcornADFS) - or(Image.FormatNumber>>4=diAcornFS)then extsep:=','; //DFS, ADFS and AFS - if(Image.FormatNumber>>4=diCommodore) //Commodore - or(Image.FormatNumber>>4=diAmiga)then extsep:='.'; //Amiga + if(Image.MajorFormatNumber=diAcornDFS) + or(Image.MajorFormatNumber=diAcornADFS) + or(Image.MajorFormatNumber=diAcornFS)then extsep:=','; //DFS, ADFS and AFS + if(Image.MajorFormatNumber=diCommodore) //Commodore + or(Image.MajorFormatNumber=diAmiga)then extsep:='.'; //Amiga Result:=Result+extsep+Image.Disc[dir].Entries[entry].ShortFileType; end; end; @@ -1461,7 +1473,7 @@ procedure TMainForm.CreateINFFile(dir,entry: Integer; path: String;filename: Str //Length of the hex numbers hexlen:=8; //6 for DFS (after discussion on Stardot forum) - if Image.FormatNumber>>4=diAcornDFS then hexlen:=6; + if Image.MajorFormatNumber=diAcornDFS then hexlen:=6; if DoCreateInf then begin imagefilename:=Image.Disc[dir].Entries[entry].Filename; @@ -1470,7 +1482,7 @@ procedure TMainForm.CreateINFFile(dir,entry: Integer; path: String;filename: Str else //Otherwise just use the supplied name windowsfilename:=filename; //Add the root, if DFS and no directory specifier - if(Image.FormatNumber>>4=diAcornDFS)and(imagefilename[2]<>'.')then + if(Image.MajorFormatNumber=diAcornDFS)and(imagefilename[2]<>'.')then imagefilename:=RightStr(Image.GetParent(dir),1)+'.'+imagefilename; //Put quotes round the filename if it contains a space if Pos(' ',imagefilename)>0 then imagefilename:='"'+imagefilename+'"'; @@ -1481,11 +1493,11 @@ procedure TMainForm.CreateINFFile(dir,entry: Integer; path: String;filename: Str +IntToHex(Image.Disc[dir].Entries[entry].Length,hexlen); //Create the attributes attributes:=$00; - if(Image.FormatNumber>>4=diAcornDFS) - or(Image.FormatNumber>>4=diAcornUEF)then //DFS and CFS + if(Image.MajorFormatNumber=diAcornDFS) + or(Image.MajorFormatNumber=diAcornUEF)then //DFS and CFS if Image.Disc[dir].Entries[entry].Attributes='L' then attributes:=$08; - if(Image.FormatNumber>>4=diAcornADFS) - or(Image.FormatNumber>>4=diAcornFS)then //ADFS and AFS + if(Image.MajorFormatNumber=diAcornADFS) + or(Image.MajorFormatNumber=diAcornFS)then //ADFS and AFS for t:=0 to 7 do if Pos(adfsattr[t+1],Image.Disc[dir].Entries[entry].Attributes)>0 then inc(attributes,1<0 then begin btn_ImageDetails.Enabled:=True; menuImageDetails.Enabled:=True; end; - if Image.FormatNumber>>4=diAcornADFS then + if Image.MajorFormatNumber=diAcornADFS then begin btn_FixADFS.Enabled:=True; menuFixADFS.Enabled:=True; @@ -1762,7 +1777,7 @@ procedure TMainForm.AddImageToTree(Tree: TTreeView;ImageToUse: TDiscImage); until highdir=Length(ImageToUse.Disc); TMyTreeNode(Tree.Items[0]).ParentDir:=-1; //Expand the top level of the tree (but not MMB) - if ImageToUse.FormatNumber>>4<>diMMFS then Tree.TopItem.Expand(False); + if ImageToUse.MajorFormatNumber<>diMMFS then Tree.TopItem.Expand(False); //And the root for the other side of the disc if ImageToUse.DoubleSided then begin @@ -1806,7 +1821,7 @@ procedure TMainForm.UpdateImageInfo(partition: Cardinal=0); ImageDetails.Panels[4].Text:=ConvertToKMG(Image.FreeSpace(partition)) +' ('+IntToStrComma(Image.FreeSpace(partition))+' Bytes)'; //Double sided or not (DFS only) - if Image.FormatNumber>>4=diAcornDFS then + if Image.MajorFormatNumber=diAcornDFS then if Image.DoubleSided then ImageDetails.Panels[5].Text:='Double Sided' else @@ -1885,8 +1900,8 @@ procedure ArrangeComponent(c,p: TControl;l: TLabel); if(cbpos>0)and(attr)then begin //DFS and UEF - if(Image.FormatNumber>>4=diAcornDFS) - or(Image.FormatNumber>>4=diAcornUEF)then + if(Image.MajorFormatNumber=diAcornDFS) + or(Image.MajorFormatNumber=diAcornUEF)then begin //Make it visible DFSAttrPanel.Visible:=True; @@ -1901,8 +1916,8 @@ procedure ArrangeComponent(c,p: TControl;l: TLabel); DFSAttrPanel.Height:=cb_DFS_l.Top+cb_DFS_l.Height; end; //ADFS and SparkFS - if((Image.FormatNumber>>4=diAcornADFS) - or(Image.FormatNumber>>4=diSpark)) + if((Image.MajorFormatNumber=diAcornADFS) + or(Image.MajorFormatNumber=diSpark)) and(not afs)and(not dos)then begin //Make it visible @@ -1935,13 +1950,13 @@ procedure ArrangeComponent(c,p: TControl;l: TLabel); //And change the panel height to accomodate ADFSAttrPanel.Height:=cb_ADFS_pubw.Top+cb_ADFS_pubw.Height; //Show/hide those not applicable for new/old directories - cb_ADFS_owne.Visible:=Image.FormatNumber mod $10<3; + cb_ADFS_owne.Visible:=Image.MinorFormatNumber<3; cb_ADFS_pube.Visible:=cb_ADFS_owne.Visible; cb_ADFS_pubp.Visible:=cb_ADFS_owne.Visible; end; //Acorn FS - if(Image.FormatNumber>>4=diAcornFS) - or((Image.FormatNumber>>4=diAcornADFS)and(afs))then + if(Image.MajorFormatNumber=diAcornFS) + or((Image.MajorFormatNumber=diAcornADFS)and(afs))then begin //Make it visible AFSAttrPanel.Visible:=True; @@ -1969,8 +1984,8 @@ procedure ArrangeComponent(c,p: TControl;l: TLabel); AFSAttrPanel.Height:=cb_AFS_pubw.Top+cb_AFS_pubw.Height; end; //DOS Plus - if(Image.FormatNumber>>4=diDOSPlus) - or((Image.FormatNumber>>4=diAcornADFS)and(dos))then + if(Image.MajorFormatNumber=diDOSPlus) + or((Image.MajorFormatNumber=diAcornADFS)and(dos))then begin //Make it visible DOSAttrPanel.Visible:=True; @@ -1992,7 +2007,7 @@ procedure ArrangeComponent(c,p: TControl;l: TLabel); DOSAttrPanel.Height:=cb_DOS_hidden.Top+cb_DOS_hidden.Height; end; //Commodore 64 - if Image.FormatNumber>>4=diCommodore then + if Image.MajorFormatNumber=diCommodore then begin //Make it visible C64AttrPanel.Visible:=True; @@ -2011,7 +2026,7 @@ procedure ArrangeComponent(c,p: TControl;l: TLabel); C64AttrPanel.Height:=cb_C64_l.Top+cb_C64_l.Height; end; //Commodore Amiga - if Image.FormatNumber>>4=diAmiga then + if Image.MajorFormatNumber=diAmiga then begin //Make it visible AmigaAttrPanel.Visible:=True; @@ -2082,9 +2097,9 @@ function TMainForm.FindPartitionRoot(filepath: String): Integer; if Length(Image.Disc)>1 then //Definately another root present begin //Then extract the root part - if(Pos('.',filepath)>0)and(Image.FormatNumber>>4<>diAcornDFS)then + if(Pos('.',filepath)>0)and(Image.MajorFormatNumber<>diAcornDFS)then filepath:=LeftStr(filepath,Pos('.',filepath)-1); - if(Pos('.',filepath)>3)and(Image.FormatNumber>>4=diAcornDFS)then + if(Pos('.',filepath)>3)and(Image.MajorFormatNumber=diAcornDFS)then filepath:=LeftStr(filepath,Pos('.',filepath,3)-1); //Then look to find the AFS root Result:=0; @@ -2147,16 +2162,16 @@ procedure TMainForm.DirListChange(Sender: TObject; Node: TTreeNode); DuplicateFile1.Enabled :=btn_DuplicateFile.Enabled; menuDuplicateFile.Enabled:=btn_DuplicateFile.Enabled; //Delete and Save Partition - afspart:=((Image.FormatNumber>>4=diAcornDFS)and(Image.DoubleSided)) //DFS DS - or((Image.FormatNumber>>4=diAcornADFS)and(Image.AFSPresent)) //ADFS/AFS - or((Image.FormatNumber>>4=diAcornADFS)and(Image.DOSPresent));//ADFS/DOS + afspart:=((Image.MajorFormatNumber=diAcornDFS)and(Image.DoubleSided)) //DFS DS + or((Image.MajorFormatNumber=diAcornADFS)and(Image.AFSPresent)) //ADFS/AFS + or((Image.MajorFormatNumber=diAcornADFS)and(Image.DOSPresent));//ADFS/DOS //Show/Hide partition options btn_DeletePartition.Enabled:=(afspart)and(DirList.SelectionCount=1); menuDeletePartition.Enabled:=btn_DeletePartition.Enabled; btn_SavePartition.Enabled :=(afspart)and(DirList.SelectionCount=1); menuSavePartition.Enabled :=btn_SavePartition.Enabled; //Check for 8 bit ADFS - btn_AddPartition.Enabled :=(Image.FormatNumber>>4=diAcornADFS) + btn_AddPartition.Enabled :=(Image.MajorFormatNumber=diAcornADFS) and(Image.DirectoryType=diADFSOldDir) and(Image.MapType=diADFSOldMap) and(not Image.AFSPresent) @@ -2168,13 +2183,13 @@ procedure TMainForm.DirListChange(Sender: TObject; Node: TTreeNode); //Change Interleave btn_ChangeInterleave.Enabled:=(Image.FormatNumber=diAcornADFS<<4+2) //ADFS L or(Image.FormatNumber=diAcornADFS<<4+$E)//ADFS Hybrid - or(Image.FormatNumber>>4=diAcornFS) //Acorn FS - or((Image.FormatNumber>>4=diAcornADFS) //Acorn ADFS + or(Image.MajorFormatNumber=diAcornFS) //Acorn FS + or((Image.MajorFormatNumber=diAcornADFS) //Acorn ADFS and(Image.InterleaveMethod>0)); //with non-auto interleave menuChangeInterleave.Enabled:=btn_ChangeInterleave.Enabled; //Change the captions temp:='Partition'; - if Image.FormatNumber>>4=diAcornDFS then temp:='Side'; + if Image.MajorFormatNumber=diAcornDFS then temp:='Side'; btn_DeletePartition.Hint :='Delete '+temp; menuDeletePartition.Caption:='&Delete '+temp; btn_SavePartition.Hint :='Save '+temp+' As'; @@ -2206,11 +2221,11 @@ procedure TMainForm.DirListChange(Sender: TObject; Node: TTreeNode); btn_Rename.Enabled :=True; menuRenameFile.Enabled:=True; //Enable the create directory button - if(Image.FormatNumber>>4=diAcornADFS) - OR(Image.FormatNumber>>4=diAmiga) - or(Image.FormatNumber>>4=diAcornFS) - or(Image.FormatNumber>>4=diSpark) - or(Image.FormatNumber>>4=diDOSPlus)then //ADFS, Amiga, Acorn FS, Spark and DOS Plus + if(Image.MajorFormatNumber=diAcornADFS) + OR(Image.MajorFormatNumber=diAmiga) + or(Image.MajorFormatNumber=diAcornFS) + or(Image.MajorFormatNumber=diSpark) + or(Image.MajorFormatNumber=diDOSPlus)then //ADFS, Amiga, Acorn FS, Spark and DOS Plus begin NewDirectory1.Enabled :=True; btn_NewDirectory.Enabled:=True; @@ -2240,12 +2255,12 @@ procedure TMainForm.DirListChange(Sender: TObject; Node: TTreeNode); dospart:=Image.Disc[dr].DOSPartition; end; //Enable the add/edit password buttons - if(Image.FormatNumber>>4=diAcornFS) - or((Image.FormatNumber>>4=diAcornADFS)and(Image.AFSPresent))then + if(Image.MajorFormatNumber=diAcornFS) + or((Image.MajorFormatNumber=diAcornADFS)and(Image.AFSPresent))then begin rt:=FindPartitionRoot(GetFilePath(DirList.Selections[0])); //ADFS...find the AFS parent - if(Image.FormatNumber>>4=diAcornADFS)and(rt=0)then rt:=-1; + if(Image.MajorFormatNumber=diAcornADFS)and(rt=0)then rt:=-1; if rt>=0 then begin //Does the password file exist on the root? @@ -2272,13 +2287,13 @@ procedure TMainForm.DirListChange(Sender: TObject; Node: TTreeNode); //Attributes DoNotUpdate :=True; //Make sure the event doesn't fire //DFS and UEF - if(Image.FormatNumber>>4=diAcornDFS) - or(Image.FormatNumber>>4=diAcornUEF)then + if(Image.MajorFormatNumber=diAcornDFS) + or(Image.MajorFormatNumber=diAcornUEF)then //Tick/untick it cb_DFS_l.Checked:=Pos('L',Image.Disc[dir].Entries[entry].Attributes)>0; //ADFS and SparkFS - if((Image.FormatNumber>>4=diAcornADFS) - or(Image.FormatNumber>>4=diSpark)) + if((Image.MajorFormatNumber=diAcornADFS) + or(Image.MajorFormatNumber=diSpark)) and(not afspart)and(not dospart)then begin //Tick/untick them @@ -2292,8 +2307,8 @@ procedure TMainForm.DirListChange(Sender: TObject; Node: TTreeNode); cb_ADFS_pubp.Checked:=Pos('P',Image.Disc[dir].Entries[entry].Attributes)>0; end; //Acorn FS - if(Image.FormatNumber>>4=diAcornFS) - or((Image.FormatNumber>>4=diAcornADFS) + if(Image.MajorFormatNumber=diAcornFS) + or((Image.MajorFormatNumber=diAcornADFS) and(afspart))then begin //Tick/untick them @@ -2304,8 +2319,8 @@ procedure TMainForm.DirListChange(Sender: TObject; Node: TTreeNode); cb_AFS_pubr.Checked:=Pos('r',Image.Disc[dir].Entries[entry].Attributes)>0; end; //DOS Plus - if(Image.FormatNumber>>4=diDOSPlus) - or((Image.FormatNumber>>4=diAcornADFS) + if(Image.MajorFormatNumber=diDOSPlus) + or((Image.MajorFormatNumber=diAcornADFS) and(dospart))then begin //Tick/untick them @@ -2315,14 +2330,14 @@ procedure TMainForm.DirListChange(Sender: TObject; Node: TTreeNode); cb_DOS_archive.Checked:=Pos('A',Image.Disc[dir].Entries[entry].Attributes)>0; end; //Commodore 64 - if Image.FormatNumber>>4=diCommodore then + if Image.MajorFormatNumber=diCommodore then begin //Tick/untick them cb_C64_l.Checked:=Pos('L',Image.Disc[dir].Entries[entry].Attributes)>0; cb_C64_c.Checked:=Pos('C',Image.Disc[dir].Entries[entry].Attributes)>0; end; //Amiga - if Image.FormatNumber>>4=diAmiga then + if Image.MajorFormatNumber=diAmiga then begin //Tick/untick them cb_Amiga_ownw.Checked:=Pos('W',Image.Disc[dir].Entries[entry].Attributes)=0; @@ -2428,7 +2443,7 @@ procedure TMainForm.DirListChange(Sender: TObject; Node: TTreeNode); btn_NewDirectory.Enabled:=False; menuNewDir.Enabled :=False; //Filetype hints - if Image.FormatNumber>>4=diDOSPlus then + if Image.MajorFormatNumber=diDOSPlus then begin //Can't edit img_Filetype.Hint:=''; lb_FileType.Hint :=''; @@ -2463,12 +2478,12 @@ procedure TMainForm.DirListChange(Sender: TObject; Node: TTreeNode); FileImages.StretchDraw(img_FileType.Canvas,ft,R); img_FileType.Tag:=ft; //To keep track of which image it is //Filetype text - only show for certain systems - if(Image.FormatNumber>>4=diAcornADFS) //ADFS - or(Image.FormatNumber>>4=diCommodore) //C64 - or(Image.FormatNumber>>4=diAmiga) //AmigaDOS - or(Image.FormatNumber>>4=diSpark) //Spark - or(Image.FormatNumber>>4=diAcornFS) //Acorn FS - or(Image.FormatNumber>>4=diDOSPlus)then//DOS Plus + if(Image.MajorFormatNumber=diAcornADFS) //ADFS + or(Image.MajorFormatNumber=diCommodore) //C64 + or(Image.MajorFormatNumber=diAmiga) //AmigaDOS + or(Image.MajorFormatNumber=diSpark) //Spark + or(Image.MajorFormatNumber=diAcornFS) //Acorn FS + or(Image.MajorFormatNumber=diDOSPlus)then//DOS Plus lb_FileType.Caption:=filetype; location:=''; //Default location string if dir>=0 then @@ -2486,15 +2501,15 @@ procedure TMainForm.DirListChange(Sender: TObject; Node: TTreeNode); lb_parent.Caption:=temp; //Timestamp - ADFS, Spark, FileStore, Amiga and DOS only if (Image.Disc[dir].Entries[entry].TimeStamp>0) - and((Image.FormatNumber>>4=diAcornADFS) - or (Image.FormatNumber>>4=diSpark) - or (Image.FormatNumber>>4=diAcornFS) - or (Image.FormatNumber>>4=diAmiga) - or (Image.FormatNumber>>4=diDOSPlus))then + and((Image.MajorFormatNumber=diAcornADFS) + or (Image.MajorFormatNumber=diSpark) + or (Image.MajorFormatNumber=diAcornFS) + or (Image.MajorFormatNumber=diAmiga) + or (Image.MajorFormatNumber=diDOSPlus))then lb_timestamp.Caption:=FormatDateTime(TimeDateFormat, Image.Disc[dir].Entries[entry].TimeStamp); if(Image.Disc[dir].Entries[entry].TimeStamp=0) - or(Image.FormatNumber>>4=diAcornFS)then + or(Image.MajorFormatNumber=diAcornFS)then if Image.Disc[dir].Entries[entry].DirRef=-1 then begin //Load address @@ -2517,7 +2532,7 @@ procedure TMainForm.DirListChange(Sender: TObject; Node: TTreeNode); //Location of object - varies between formats //ADFS Old map and Acorn FS - Sector is an offset if(Image.MapType=diADFSOldMap) - or(Image.FormatNumber>>4=diAcornFS)then + or(Image.MajorFormatNumber=diAcornFS)then location:='Sector offset: 0x' +IntToHex(Image.Disc[dir].Entries[entry].Sector,8)+' '; //ADFS New map - Sector is an indirect address (fragment and sector) @@ -2525,23 +2540,23 @@ procedure TMainForm.DirListChange(Sender: TObject; Node: TTreeNode); location:='Indirect address: 0x' +IntToHex(Image.Disc[dir].Entries[entry].Sector,8)+' '; //DOS Plus - Sector is the starting cluster - if(Image.FormatNumber>>4=diDOSPlus) + if(Image.MajorFormatNumber=diDOSPlus) or(Image.Disc[dir].DOSPartition)then location:='Starting Cluster: 0x' +IntToHex(Image.Disc[dir].Entries[entry].Sector,4); //Commodore formats - Sector and Track - if Image.FormatNumber>>4=diCommodore then + if Image.MajorFormatNumber=diCommodore then location:='Track ' +IntToStr(Image.Disc[dir].Entries[entry].Track)+' '; //All other formats - Sector - if(Image.FormatNumber>>4=diAcornDFS) - or(Image.FormatNumber>>4=diAmiga) then + if(Image.MajorFormatNumber=diAcornDFS) + or(Image.MajorFormatNumber=diAmiga) then location:=location+'Sector ' +IntToStr(Image.Disc[dir].Entries[entry].Sector)+' '; //DFS - indicates which side also - if Image.FormatNumber>>4=diAcornDFS then + if Image.MajorFormatNumber=diAcornDFS then location:=location+'Side ' +IntToStr(Image.Disc[dir].Entries[entry].Side); //CFS - indicates offset to starting block - if Image.FormatNumber>>4=diAcornUEF then + if Image.MajorFormatNumber=diAcornUEF then location:='Starting Block 0x' +IntToHex(Image.Disc[dir].Entries[entry].Sector,8); end; @@ -2553,12 +2568,12 @@ procedure TMainForm.DirListChange(Sender: TObject; Node: TTreeNode); location:=''; //ADFS Old map and Acorn File Server - Sector is an offset if(Image.MapType=diADFSOldMap) - or(Image.FormatNumber>>4=diAcornFS) - or((Image.FormatNumber>>4=diDOSPlus)and(Image.MapType<>diFAT32)) + or(Image.MajorFormatNumber=diAcornFS) + or((Image.MajorFormatNumber=diDOSPlus)and(Image.MapType<>diFAT32)) or(Image.Disc[dr].AFSPartition) or(Image.Disc[dr].DOSPartition)then location:='Sector Offset: 0x'+IntToHex(Image.Disc[dr].Sector,8); - if(Image.FormatNumber>>4=diDOSPlus)and(Image.MapType=diFAT32)then + if(Image.MajorFormatNumber=diDOSPlus)and(Image.MapType=diFAT32)then location:='Starting Cluster: 0x'+IntToHex(Image.Disc[dr].Sector,8); //Number of entries in a directory ptr:=Length(Image.Disc[dr].Entries); @@ -2622,9 +2637,9 @@ function TMainForm.GetImageIndex(Node: TTreeNode;ImageToUse: TDiscImage): Intege begin WriteToDebug('Non-directory'); //Are we ADFS, SparkFS, AFS or DOS? - if((ImageToUse.FormatNumber>>4=diAcornADFS) - or(ImageToUse.FormatNumber>>4=diSpark) - or(ImageToUse.FormatNumber>>4=diAcornFS)) + if((ImageToUse.MajorFormatNumber=diAcornADFS) + or(ImageToUse.MajorFormatNumber=diSpark) + or(ImageToUse.MajorFormatNumber=diAcornFS)) and(Length(ImageToUse.Disc)>0)and(not dospart)then begin //Default @@ -2642,7 +2657,7 @@ function TMainForm.GetImageIndex(Node: TTreeNode;ImageToUse: TDiscImage): Intege end; end; //Is it a Commodore format? - if(ImageToUse.FormatNumber>>4=diCommodore) + if(ImageToUse.MajorFormatNumber=diCommodore) and(Length(ImageToUse.Disc)>0)then begin //Default is a PRG file @@ -2651,8 +2666,8 @@ function TMainForm.GetImageIndex(Node: TTreeNode;ImageToUse: TDiscImage): Intege if i<>unknown then ft:=i; end; //DOS Plus (DOS) - if((ImageToUse.FormatNumber>>4=diDOSPlus) - or((ImageToUse.FormatNumber>>4=diAcornADFS)and(dospart))) + if((ImageToUse.MajorFormatNumber=diDOSPlus) + or((ImageToUse.MajorFormatNumber=diAcornADFS)and(dospart))) and(Length(ImageToUse.Disc)>0)then begin i:=GetFileTypeGraphic(filetype,Low(DOSFileTypes),DOSFileTypes); @@ -2671,8 +2686,8 @@ function TMainForm.GetImageIndex(Node: TTreeNode;ImageToUse: TDiscImage): Intege ft:=directory; //If RISC OS, and an application if(not dospart)and(not afspart)then - if(ImageToUse.FormatNumber>>4=diAcornADFS) - or(ImageToUse.FormatNumber>>4=diSpark)then //ADFS and Spark only + if(ImageToUse.MajorFormatNumber=diAcornADFS) + or(ImageToUse.MajorFormatNumber=diSpark)then //ADFS and Spark only if(ImageToUse.DirectoryType=diADFSNewDir) OR(ImageToUse.DirectoryType=diADFSBigDir)then //New or Big if Node.Text[1]='!' then @@ -2682,7 +2697,7 @@ function TMainForm.GetImageIndex(Node: TTreeNode;ImageToUse: TDiscImage): Intege if i<>unknown then ft:=i; end; //If MMB - if ImageToUse.FormatNumber>>4=diMMFS then + if ImageToUse.MajorFormatNumber=diMMFS then begin ft:=mmbdisc; if ImageToUse.Disc[Node.Index].Locked then ft:=mmbdisclock; @@ -2822,6 +2837,7 @@ procedure TMainForm.DisableControls; btn_SaveAsCSV.Enabled :=False; btn_FixADFS.Enabled :=False; btn_FileSearch.Enabled :=False; + btn_ShowReport.Enabled :=False; //Pop up Menu items ExtractFile1.Enabled :=False; RenameFile1.Enabled :=False; @@ -2843,6 +2859,7 @@ procedure TMainForm.DisableControls; menuAbout.Enabled :=True; menuFixADFS.Enabled :=False; menuFileSearch.Enabled :=False; + menuShowReport.Enabled :=False; //Close the search window SearchForm.Close; //Disable the directory view @@ -2951,7 +2968,7 @@ procedure TMainForm.ParseCommandLine(cmd: String); if(not newmap)and(harddrivesize>512*1024*1024)then harddrivesize:=512*1024*1024;//512MB max for old map //OK, now create it - if Image.FormatHDD(diAcornADFS,harddrivesize,newmap,dirtype) then + if Image.FormatHDD(diAcornADFS,harddrivesize,True,newmap,dirtype,False) then begin HasChanged:=True; ShowNewImage(Image.Filename); @@ -2973,7 +2990,8 @@ procedure TMainForm.ParseCommandLine(cmd: String); //But not too big if harddrivesize>512*1024 then harddrivesize:=512*1024; //Create it - if Image.FormatHDD(diAcornFS,harddrivesize*1024,False,dirtype) then + if Image.FormatHDD(diAcornFS,harddrivesize*1024, + True,False,dirtype,False) then begin HasChanged:=True; ShowNewImage(Image.Filename); @@ -2992,7 +3010,8 @@ procedure TMainForm.ParseCommandLine(cmd: String); //But not too big if harddrivesize>1024*1024 then harddrivesize:=512*1024; //Create it - if Image.FormatHDD(diDOSPlus,harddrivesize*1024,False,dirtype) then + if Image.FormatHDD(diDOSPlus,harddrivesize*1024, + True,False,dirtype,False) then begin HasChanged:=True; ShowNewImage(Image.Filename); @@ -3009,7 +3028,7 @@ procedure TMainForm.ParseCommandLine(cmd: String); //But not too big if harddrivesize>1024*1024 then harddrivesize:=512*1024; //Create it - if Image.FormatHDD(diAmiga,harddrivesize*1024,False,0) then + if Image.FormatHDD(diAmiga,harddrivesize*1024,True,False,0,False) then begin HasChanged:=True; ShowNewImage(Image.Filename); @@ -3118,7 +3137,7 @@ procedure TMainForm.ParseCommandLine(cmd: String); if(option='--interleave')or(option='-in')then if(Image.FormatNumber=diAcornADFS<<4+2) or(Image.FormatNumber=diAcornADFS<<4+$E) - or(Image.FormatNumber>>4=diAcornFS)then + or(Image.MajorFormatNumber=diAcornFS)then if Image.ChangeInterleaveMethod(StrToIntDef(param,0)) then HasChanged:=True; //Extract files ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ if (option='--extract') or (option='-e') then @@ -3266,39 +3285,41 @@ procedure TMainForm.FormCreate(Sender: TObject); ErrorReporting:=True; //Reset the form shift state FormShiftState:=[]; + //Initiate the Registry + DIMReg:=TGJHRegistry.Create('\Software\GJH Software\Disc Image Manager'); //Texture style - get from the registry - TextureType :=GetRegValI('Texture',1); + TextureType :=DIMReg.GetRegValI('Texture',1); //ADFS L Interleaved type - get from the registry - ADFSInterleave:=GetRegValI('ADFS_L_Interleave',0); + ADFSInterleave:=DIMReg.GetRegValI('ADFS_L_Interleave',0); //Treat Spark as FS? - SparkIsFS :=GetRegValB('Spark_Is_FS',True); + SparkIsFS :=DIMReg.GetRegValB('Spark_Is_FS',True); //Threshold of when to bypass the GUI (during import) - get from registry - bypassGUIThres:={GetRegValI('bypass_GUI_Threshold',}100;//); + bypassGUIThres:={DIMReg.GetRegValI('bypass_GUI_Threshold',}100;//); //Create INF Files? - DoCreateINF :=GetRegValB('CreateINF',True); + DoCreateINF :=DIMReg.GetRegValB('CreateINF',True); //Hide Commodore DEL files - DoHideDEL :=GetRegValB('Hide_CDR_DEL',False); + DoHideDEL :=DIMReg.GetRegValB('Hide_CDR_DEL',False); //Allow DFS images with zero sectors - FDFSZeroSecs :=GetRegValB('DFS_Zero_Sectors',False); + FDFSZeroSecs :=DIMReg.GetRegValB('DFS_Zero_Sectors',False); //Check for files going over the DFS disc edge - FDFSBeyondEdge:=GetRegValB('DFS_Beyond_Edge',False); + FDFSBeyondEdge:=DIMReg.GetRegValB('DFS_Beyond_Edge',False); //Check for blank filenames in DFS - FDFSAllowBlank:=GetRegValB('DFS_Allow_Blanks',False); + FDFSAllowBlank:=DIMReg.GetRegValB('DFS_Allow_Blanks',False); //Compress UEF Files on save - FUEFCompress :=GetRegValB('UEF_Compress',True); + FUEFCompress :=DIMReg.GetRegValB('UEF_Compress',True); //Scan all sub directories on opening - FScanSubDirs :=GetRegValB('Scan_SubDirs',True); + FScanSubDirs :=DIMReg.GetRegValB('Scan_SubDirs',True); //Open DOS Partitions on ADFS - FOpenDOS :=GetRegValB('Open_DOS',True); + FOpenDOS :=DIMReg.GetRegValB('Open_DOS',True); //View menu options - ViewOptions :=GetRegValI('View_Options',$FFFF); + ViewOptions :=DIMReg.GetRegValI('View_Options',$FFFF); //Toolbar order - this doesn't work currently -{ ToolBarContainer.Bands.Items[0]:=GetRegValS('ToolBar0','ImageToolBar'); - ToolBarContainer.Bands.Items[1].Text:=GetRegValS('ToolBar1','FilesToolBar'); - ToolBarContainer.Bands.Items[2].Text:=GetRegValS('ToolBar2','PartitionToolBar'); - ToolBarContainer.Bands.Items[3].Text:=GetRegValS('ToolBar3','ToolsToolBar');} +{ ToolBarContainer.Bands.Items[0]:=DIMReg.GetRegValS('ToolBar0','ImageToolBar'); + ToolBarContainer.Bands.Items[1].Text:=DIMReg.GetRegValS('ToolBar1','FilesToolBar'); + ToolBarContainer.Bands.Items[2].Text:=DIMReg.GetRegValS('ToolBar2','PartitionToolBar'); + ToolBarContainer.Bands.Items[3].Text:=DIMReg.GetRegValS('ToolBar3','ToolsToolBar');} //Produce log files for debugging - Fdebug:=GetRegValB('Debug_Mode',False); + Fdebug:=DIMReg.GetRegValB('Debug_Mode',False); debuglogfile:=GetTempDir+'DIM_LogFile.txt'; //Write some debugging info WriteToDebug('Application Started.'); @@ -3448,6 +3469,7 @@ procedure TMainForm.FormDropFiles(Sender: TObject; sparksize : Cardinal; isspark : Boolean; confirm : TModalResult; + ListOfFile: array of String; begin //Create a new DiscImage instance NewImage:=TDiscImage.Create; @@ -3457,84 +3479,101 @@ procedure TMainForm.FormDropFiles(Sender: TObject; NewImage.DFSBeyondEdge:=FDFSBeyondEdge; NewImage.DFSAllowBlanks:=FDFSAllowBlank; NewImage.ScanSubDirs:=True; + //Extract any *.inf files + SetLength(ListOfFile,0); for FileName in FileNames do + if LowerCase(RightStr(FileName,4))<>'.inf' then + begin + SetLength(ListOfFile,Length(ListOfFile)+1); + ListOfFile[Length(ListOfFile)-1]:=FileName; + end; + //And then iterate through them and add them + for FileName in ListOfFile do begin //If it is not a directory if not DirectoryExists(Filename) then begin //Open or add ($00=ignore, $01=open, $02=add, $03=ask) open:=$00; - //Load and ID the file - NewImage.LoadFromFile(FileName,false); - //Valid image? - if NewImage.FormatNumber<>diInvalidImg then //Is an image - begin - open:=open OR $01; - //Get the detected format string - fmt:=NewImage.FormatString; - end; - //Is there something loaded? - if Image.Filename<>'' then open:=open OR $02; //Something is open - if((NewImage.FormatNumber=diAcornDFS<<4+0) - or (NewImage.FormatNumber=diAcornDFS<<4+2)) - and((Image.FormatNumber=diAcornDFS<<4+0) - or (Image.FormatNumber=diAcornDFS<<4+2))then //Loading a DFS SS while a SS is open - open:=$04; //Might want to convert an SS to a DS - //Go through the different states - if open=$04 then //Convert DFS SS to DFS DS - begin - msg:='The new image is a '+NewImage.FormatString+', ' - +'and the open image is a '+Image.FormatString+'.'#13#10; - msg:=msg+'Would you like to open this as an image, ' - +'or convert the open one by adding this as a second side/partition?'; - confirm:=AskConfirm(msg,'Open','Convert',''); - if confirm=mrOK then open:=$01; - if confirm=mrCancel then open:=$04; - end; - if open=$03 then //Ask user + if Length(ListOfFile)=1 then //If more than one, then just add, otherwise explore other options begin - //Prepare message - msg:='There is already an image open.'#13#10; - msg:=msg+'Open this file as a'; - //Is the first letter a vowel? - if(fmt[1]='A')or(fmt[1]='E')or(fmt[1]='I')or(fmt[1]='O')or(fmt[1]='U')then - msg:=msg+'n'; - msg:=msg+' '+fmt+' image, add this file to existing image, '; - msg:=msg+'or import this file''s contents to existing image?'; - //Pose question - confirm:=AskConfirm(msg,'Open','Add','Import'); - if confirm=mrOK then open:=$01; - if confirm=mrCancel then open:=$02; - if confirm=mrIgnore then open:=$03; - end; - if open=$02 then //Add file - begin - //Is this 32bit ADFS? - if (Image.DirectoryType>0) - and(Image.FormatNumber>>4=diAcornADFS) - and(not SparkIsFS)then //And Spark is not being treated as a filing system + //Load and ID the file + NewImage.LoadFromFile(FileName,false); + //Valid image? + if NewImage.FormatNumber<>diInvalidImg then //Is an image begin - //See if it is a spark archive - SparkFile:=TSpark.Create(FileName); - sparksize:=SparkFile.UncompressedSize; - isspark:=SparkFile.IsSpark; - SparkFile.Free; - //Is it a valid Spark Archive? - if(isspark)and(sparksize'' then open:=open OR $02; //Something is open + if((NewImage.FormatNumber=diAcornDFS<<4+0) + or (NewImage.FormatNumber=diAcornDFS<<4+2)) + and((Image.FormatNumber=diAcornDFS<<4+0) + or (Image.FormatNumber=diAcornDFS<<4+2))then //Loading a DFS SS while a SS is open + open:=$04; //Might want to convert an SS to a DS + //Go through the different states + if open=$04 then //Convert DFS SS to DFS DS + begin + msg:='The new image is a '+NewImage.FormatString+', ' + +'and the open image is a '+Image.FormatString+'.'#13#10; + msg:=msg+'Would you like to open this as an image, ' + +'or convert the open one by adding this as a second side/partition?'; + confirm:=AskConfirm(msg,'Open','Convert',''); + if confirm=mrOK then open:=$01; + if confirm=mrCancel then open:=$04; + end; + if open=$03 then //Ask user + begin + //Prepare message + msg:='There is already an image open.'#13#10; + msg:=msg+'Open this file as a'; + //Is the first letter a vowel? + if(fmt[1]='A')or(fmt[1]='E')or(fmt[1]='I')or(fmt[1]='O')or(fmt[1]='U')then + msg:=msg+'n'; + msg:=msg+' '+fmt+' image, add this file to existing image, '; + msg:=msg+'or import this file''s contents to existing image?'; + //Pose question + confirm:=AskConfirm(msg,'Open','Add','Import'); + if confirm=mrOK then open:=$01; + if confirm=mrCancel then open:=$02; + if confirm=mrIgnore then open:=$03; + end; + if open=$02 then //Add file + begin + //Is this 32bit ADFS? + if (Image.DirectoryType>0) + and(Image.MajorFormatNumber=diAcornADFS) + and(not SparkIsFS)then //And Spark is not being treated as a filing system + begin + //See if it is a spark archive + SparkFile:=TSpark.Create(FileName); + sparksize:=SparkFile.UncompressedSize; + isspark:=SparkFile.IsSpark; + SparkFile.Free; + //Is it a valid Spark Archive? + if(isspark)and(sparksize>4=diAcornADFS) //Check ADFS + if ((Image.MajorFormatNumber=diAcornADFS) //Check ADFS and(((MaxDirEnt>47)and(Image.DirectoryType=diADFSOldDir)) or((MaxDirEnt>77)and(Image.DirectoryType=diADFSNewDir)))) - or ((Image.FormatNumber>>4=diAcornDFS)and(NumFiles>31)) //Check DFS - or ((Image.FormatNumber>>4=diCommodore)and(NumFiles>144)) //Check Commodore + or ((Image.MajorFormatNumber=diAcornDFS)and(NumFiles>31)) //Check DFS + or ((Image.MajorFormatNumber=diCommodore)and(NumFiles>144)) //Check Commodore then if Dialogue then ok:=AskConfirm( 'The current open image is not suitable for this archive. ' @@ -3676,6 +3717,7 @@ procedure TMainForm.ImportFiles(NewImage: TDiscImage;Dialogue: Boolean=True); begin //We don't need progress update from the class as we'll produce our own NewImage.ProgressIndicator:=nil; + Image.ProgressIndicator:=@UpdateProgress; //Show the progress form again ProgressForm.Show; Application.ProcessMessages; @@ -3703,8 +3745,8 @@ procedure TMainForm.ImportFiles(NewImage: TDiscImage;Dialogue: Boolean=True); end else //Nothing selected, then this is the root rootname:=Image.Disc[0].Directory; - curformat:=Image.FormatNumber>>4; //Format of the current open image - newformat:=NewImage.FormatNumber>>4;//Format of the importing image + curformat:=Image.MajorFormatNumber; //Format of the current open image + newformat:=NewImage.MajorFormatNumber;//Format of the importing image //Go through each directory if Length(NewImage.Disc)>0 then for dir:=0 to Length(NewImage.Disc)-1 do @@ -3823,8 +3865,8 @@ procedure TMainForm.sb_FileTypeClick(Sender: TObject); entry: Integer; begin //ADFS or Spark non directories only - if((Image.FormatNumber>>4=diAcornADFS) - or(Image.FormatNumber>>4=diSpark)) + if((Image.MajorFormatNumber=diAcornADFS) + or(Image.MajorFormatNumber=diSpark)) and(not TMyTreeNode(DirList.Selected).IsDir)then begin //Get the references @@ -3883,7 +3925,7 @@ procedure TMainForm.FileTypeClick(Sender: TObject); //If all went OK, update the display DirListChange(Sender,DirList.Selected); //And mark as changed - if Image.FormatNumber>>4<>diSpark then HasChanged:=True; + if Image.MajorFormatNumber<>diSpark then HasChanged:=True; end; end; end; @@ -3956,7 +3998,7 @@ procedure TMainForm.ShowHideToolbar(Sender: TObject); //Then set it, if needed if TMenuItem(Sender).Checked then ViewOptions:=ViewOptions OR(1<>4=diAcornADFS) - or(Image.FormatNumber>>4=diAcornFS) - or(Image.FormatNumber>>4=diDOSPlus) - or(Image.FormatNumber>>4=diAmiga) - or(Image.FormatNumber>>4=diSpark)then + if(Image.MajorFormatNumber=diAcornADFS) + or(Image.MajorFormatNumber=diAcornFS) + or(Image.MajorFormatNumber=diDOSPlus) + or(Image.MajorFormatNumber=diAmiga) + or(Image.MajorFormatNumber=diSpark)then begin //Get the references entry:=DirList.Selected.Index; @@ -4038,7 +4080,11 @@ procedure TMainForm.ed_timestampEditingDone(Sender: TObject); //Display the new details lb_timestamp.Caption:=FormatDateTime(TimeDateFormat, Image.Disc[dir].Entries[entry].TimeStamp); - if Image.FormatNumber>>4<>diSpark then HasChanged:=True; + if Image.MajorFormatNumber<>diSpark then + begin + HasChanged:=True; + UpdateImageInfo; + end; end; end; @@ -4144,7 +4190,7 @@ procedure TMainForm.ed_execaddrEditingDone(Sender: TObject); //If success, then change the text lb_execaddr.Caption:='0x'+IntToHex(Image.Disc[dir].Entries[entry].ExecAddr,8); lb_loadaddr.Caption:='0x'+IntToHex(Image.Disc[dir].Entries[entry].LoadAddr,8); - if Image.FormatNumber>>4<>diSpark then HasChanged:=True; + if Image.MajorFormatNumber<>diSpark then HasChanged:=True; end; end; end; @@ -4200,7 +4246,7 @@ procedure TMainForm.btn_ImageDetailsClick(Sender: TObject); ImageDetailForm.lbImgFormat.Caption:=Image.FormatString; //Map type ImageDetailForm.lbMap.Caption :=Image.MapTypeString; - if Image.FormatNumber>>4<>diDOSPlus then + if Image.MajorFormatNumber<>diDOSPlus then ImageDetailForm.MapLabel.Caption:='Map Type' else ImageDetailForm.MapLabel.Caption:='FAT Type'; @@ -4248,7 +4294,7 @@ procedure TMainForm.btn_ImageDetailsClick(Sender: TObject); ImageDetailForm.BBCMasterLogo.Visible:=False; ImageDetailForm.MicrosoftLogo.Visible:=False; //Now display the appropriate one - case Image.FormatNumber>>4 of + case Image.MajorFormatNumber of diAcornDFS, diAcornADFS, diAcornUEF, @@ -4257,7 +4303,7 @@ procedure TMainForm.btn_ImageDetailsClick(Sender: TObject); diAmiga : ImageDetailForm.AmigaLogo.Visible :=True; diSinclair : ImageDetailForm.SinclairLogo.Visible :=True; diDOSPlus : - if Image.FormatNumber mod $10<>0 then + if Image.MinorFormatNumber<>0 then ImageDetailForm.MicrosoftLogo.Visible:=True else ImageDetailForm.BBCMasterLogo.Visible:=True; @@ -4348,16 +4394,16 @@ procedure TMainForm.btn_ImageDetailsClick(Sender: TObject); titles[side].Text:=title; titles[0].Enabled:=not Image.AFSPresent; //Limit the length - if Image.FormatNumber>>4=diAcornDFS then titles[side].MaxLength:=12; //DFS - if Image.FormatNumber>>4=diAcornADFS then + if Image.MajorFormatNumber=diAcornDFS then titles[side].MaxLength:=12; //DFS + if Image.MajorFormatNumber=diAcornADFS then begin if(Image.DOSPresent)and(side=1)then titles[side].MaxLength:=11 //DOS Partition in an ADFS disc else titles[side].MaxLength:=10; //ADFS end; - if Image.FormatNumber>>4=diAcornFS then titles[side].MaxLength:=10; //AFS - if Image.FormatNumber>>4=diDOSPlus then titles[side].MaxLength:=11; //DOS + if Image.MajorFormatNumber=diAcornFS then titles[side].MaxLength:=10; //AFS + if Image.MajorFormatNumber=diDOSPlus then titles[side].MaxLength:=11; //DOS //Boot Option boots[side].Visible :=True; if Length(Image.BootOpt)>side then @@ -4367,13 +4413,13 @@ procedure TMainForm.btn_ImageDetailsClick(Sender: TObject); bootlbs[side].Visible:=boots[side].Visible; end; //Is this an ADFS/AFS Hybrid? - if(Image.FormatNumber>>4=diAcornADFS)and(Image.AFSPresent)then + if(Image.MajorFormatNumber=diAcornADFS)and(Image.AFSPresent)then begin FSMlabel[0].Caption:=FSMlabel[0].Caption+' ADFS Partition'; FSMlabel[1].Caption:=FSMlabel[1].Caption+' Acorn FS Partition'; end; //Is this an ADFS/DOS Hybrid? - if(Image.FormatNumber>>4=diAcornADFS)and(Image.DOSPresent)then + if(Image.MajorFormatNumber=diAcornADFS)and(Image.DOSPresent)then begin FSMlabel[0].Caption:=FSMlabel[0].Caption+' ADFS Partition'; FSMlabel[1].Caption:=FSMlabel[1].Caption+' DOS Plus Partition'; @@ -4465,18 +4511,18 @@ procedure TMainForm.btn_SavePartitionClick(Sender: TObject); //Open the Save As dialogue box SaveImage.Title:='Save Partition As'; //DS DFS Image, so target is SS DFS - if(Image.FormatNumber>>4=diAcornDFS) + if(Image.MajorFormatNumber=diAcornDFS) and(Image.DoubleSided)then targetformat:=Image.FormatNumber-1; //ADFS/AFS Hybrid, with AFS partition selected, so target will be AFS Level 3 - if(Image.FormatNumber>>4=diAcornADFS) + if(Image.MajorFormatNumber=diAcornADFS) and(Image.AFSPresent) and(side<>0)then targetformat:=diAcornFS<<4+2; //ADFS/DOS Hybrid, with DOS partition selected, so target will be DOS Plus - if(Image.FormatNumber>>4=diAcornADFS) + if(Image.MajorFormatNumber=diAcornADFS) and(Image.DOSPresent) and(side<>0)then targetformat:=diDOSPlus<<4; //ADFS/AFS Hybrid, with ADFS partition selected, so target will be ADFS Hard Disc - if(Image.FormatNumber>>4=diAcornADFS) + if(Image.MajorFormatNumber=diAcornADFS) and(side=0)then targetformat:=diAcornADFS<<4+$F; //Populate the filter part of the dialogue index:=0; @@ -4491,6 +4537,7 @@ procedure TMainForm.btn_SavePartitionClick(Sender: TObject); if SaveImage.Execute then begin //Show a progress message + Image.ProgressIndicator:=@UpdateProgress; ProgressForm.Show; //Process the messages to close the file dialogue box Application.ProcessMessages; @@ -4701,6 +4748,7 @@ procedure TMainForm.btn_FixADFSClick(Sender: TObject); c: Boolean; begin //Show a progress message + Image.ProgressIndicator:=@UpdateProgress; ProgressForm.Show; //Process the messages to close the file dialogue box Application.ProcessMessages; @@ -4740,7 +4788,7 @@ procedure TMainForm.btn_NewDirectoryClick(Sender: TObject); inc(x); dirname:=dirname+IntToStr(x); //Create the directory - if Image.FormatNumber>>4<>diAmiga then + if Image.MajorFormatNumber<>diAmiga then CreateDirectory(dirname,'DLR') else CreateDirectory(dirname,''); //Create with no protection flags for Amiga @@ -4755,54 +4803,125 @@ procedure TMainForm.btn_SaveAsCSVClick(Sender: TObject); dir, entry : Integer; filename, - ext : String; + ext, + line : String; hexlen : Byte; + report : TStringList; begin - //Remove the existing part of the original filename - filename:=ExtractFileName(Image.Filename); - ext:=ExtractFileExt(filename); - filename:=LeftStr(filename,Length(filename)-Length(ext)); - //Populate the save dialogue box - SaveImage.DefaultExt:='.csv'; - SaveImage.Filter:='CSV File|*.csv'; - SaveImage.FilterIndex:=1; - SaveImage.Title:='Save CSV of image contents'; - //Add the csv extension - SaveImage.Filename:=filename+'.csv'; - //Show the dialogue box - if SaveImage.Execute then + //Get the last used settings from the registry + CSVPrefForm.cb_IncDir.Checked :=DIMReg.GetRegValB('CSVIncDir' ,False); + CSVPrefForm.cb_IncFilename.Checked:=DIMReg.GetRegValB('CSVIncFilename',True); + CSVPrefForm.cb_IncReport.Checked :=DIMReg.GetRegValB('CSVIncReport' ,True); + CSVPrefForm.cb_Parent.Checked :=DIMReg.GetRegValB('CSVParent' ,True); + CSVPrefForm.cb_Filename.Checked :=DIMReg.GetRegValB('CSVFilename' ,True); + CSVPrefForm.cb_LoadAddr.Checked :=DIMReg.GetRegValB('CSVLoadAddr' ,True); + CSVPrefForm.cb_ExecAddr.Checked :=DIMReg.GetRegValB('CSVExecAddr' ,True); + CSVPrefForm.cb_Length.Checked :=DIMReg.GetRegValB('CSVLength' ,True); + CSVPrefForm.cb_Attributes.Checked :=DIMReg.GetRegValB('CSVAttributes' ,True); + CSVPrefForm.cb_Address.Checked :=DIMReg.GetRegValB('CSVAddress' ,False); + CSVPrefForm.cb_CRC32.Checked :=DIMReg.GetRegValB('CSVCRC32' ,True); + //Ask user what they want in the CSV file + CSVPrefForm.ShowModal; + if CSVPrefForm.ModalResult=mrOK then //Unless they clicked Cancel begin - hexlen:=8; - if Image.FormatNumber>>4=diAcornDFS then hexlen:=6; - //Show a progress message - ProgressForm.Show; - //Process the messages to close the file dialogue box - Application.ProcessMessages; - //Open a new file - F:=TFileStream.Create(SaveImage.FileName,fmCreate OR fmShareDenyNone); - //Write the image details - WriteLine(F,'"'+Image.Filename+'","'+Image.CRC32+'"'); - //Write the headers - WriteLine(F,'"Parent","Filename","Load Address","Execution Address","Length","Attributes","CRC32"'); - //Go through each directory - for dir:=0 to Length(Image.Disc)-1 do - //And each entry in that directory - for entry:=0 to Length(Image.Disc[dir].Entries)-1 do - //write out each entry - if Image.Disc[dir].Entries[entry].DirRef=-1 then - WriteLine(F,'"'+Image.GetParent(dir)+'","' - +Image.Disc[dir].Entries[entry].Filename+'","' - +IntToHex(Image.Disc[dir].Entries[entry].LoadAddr,hexlen)+'","' - +IntToHex(Image.Disc[dir].Entries[entry].ExecAddr,hexlen)+'","' - +IntToHex(Image.Disc[dir].Entries[entry].Length,hexlen)+'","' - +Image.Disc[dir].Entries[entry].Attributes+'","' - +Image.GetFileCRC(Image.GetParent(dir) - +Image.GetDirSep(Image.Disc[dir].Partition) - +Image.Disc[dir].Entries[entry].Filename)+'"'); - //Finally free up the file stream - F.Free; - //Close the progress window - ProgressForm.Hide; + //Save the settings to the registry + DIMReg.SetRegValB('CSVIncDir' ,CSVPrefForm.cb_IncDir.Checked); + DIMReg.SetRegValB('CSVIncFilename',CSVPrefForm.cb_IncFilename.Checked); + DIMReg.SetRegValB('CSVIncReport' ,CSVPrefForm.cb_IncReport.Checked); + DIMReg.SetRegValB('CSVParent' ,CSVPrefForm.cb_Parent.Checked); + DIMReg.SetRegValB('CSVFilename' ,CSVPrefForm.cb_Filename.Checked); + DIMReg.SetRegValB('CSVLoadAddr' ,CSVPrefForm.cb_LoadAddr.Checked); + DIMReg.SetRegValB('CSVExecAddr' ,CSVPrefForm.cb_ExecAddr.Checked); + DIMReg.SetRegValB('CSVLength' ,CSVPrefForm.cb_Length.Checked); + DIMReg.SetRegValB('CSVAttributes' ,CSVPrefForm.cb_Attributes.Checked); + DIMReg.SetRegValB('CSVAddress' ,CSVPrefForm.cb_Address.Checked); + DIMReg.SetRegValB('CSVCRC32' ,CSVPrefForm.cb_CRC32.Checked); + //Remove the existing part of the original filename + filename:=ExtractFileName(Image.Filename); + ext:=ExtractFileExt(filename); + filename:=LeftStr(filename,Length(filename)-Length(ext)); + //Populate the save dialogue box + SaveImage.DefaultExt:='.csv'; + SaveImage.Filter:='CSV File|*.csv'; + SaveImage.FilterIndex:=1; + SaveImage.Title:='Save CSV of image contents'; + //Add the csv extension + SaveImage.Filename:=filename+'.csv'; + //Show the dialogue box + if SaveImage.Execute then + begin + hexlen:=8; + if Image.MajorFormatNumber=diAcornDFS then hexlen:=6; + //Show a progress message + Image.ProgressIndicator:=@UpdateProgress; + ProgressForm.Show; + //Process the messages to close the file dialogue box + Application.ProcessMessages; + //Open a new file + F:=TFileStream.Create(SaveImage.FileName,fmCreate OR fmShareDenyNone); + //Write the image details + if CSVPrefForm.cb_IncFilename.Checked then + WriteLine(F,'"'+Image.Filename+'","0x'+Image.CRC32+'"'); + //Write the report + if CSVPrefForm.cb_IncReport.Checked then + begin + report:=Image.ImageReport(True); + if report.Count>0 then + for dir:=0 to report.Count-1 do + WriteLine(F,report[dir]); + end; + //Write the headers + line:=''; + if CSVPrefForm.cb_Parent.Checked then line:=line+'"Parent",'; + if CSVPrefForm.cb_Filename.Checked then line:=line+'"Filename",'; + if CSVPrefForm.cb_LoadAddr.Checked then line:=line+'"Load Address",'; + if CSVPrefForm.cb_ExecAddr.Checked then line:=line+'"Execution Address",'; + if CSVPrefForm.cb_Length.Checked then line:=line+'"Length",'; + if CSVPrefForm.cb_Attributes.Checked then line:=line+'"Attributes",'; + if CSVPrefForm.cb_Address.Checked then line:=line+'"Address",'; + if CSVPrefForm.cb_CRC32.Checked then line:=line+'"CRC32,"'; + line:=LeftStr(line,Length(line)-1); + WriteLine(F,line); + //Go through each directory + for dir:=0 to Length(Image.Disc)-1 do + //And each entry in that directory + for entry:=0 to Length(Image.Disc[dir].Entries)-1 do + //write out each entry + if(Image.Disc[dir].Entries[entry].DirRef=-1) //Exclude directories? + or(CSVPrefForm.cb_IncDir.Checked)then //Or include them? + begin + line:=''; + if CSVPrefForm.cb_Parent.Checked then + line:=line+'"'+Image.GetParent(dir)+'",'; + if CSVPrefForm.cb_Filename.Checked then + line:=line+'"'+Image.Disc[dir].Entries[entry].Filename+'",'; + if CSVPrefForm.cb_LoadAddr.Checked then + line:=line+'"0x'+IntToHex(Image.Disc[dir].Entries[entry].LoadAddr,hexlen)+'",'; + if CSVPrefForm.cb_ExecAddr.Checked then + line:=line+'"0x'+IntToHex(Image.Disc[dir].Entries[entry].ExecAddr,hexlen)+'",'; + if CSVPrefForm.cb_Length.Checked then + line:=line+'"0x'+IntToHex(Image.Disc[dir].Entries[entry].Length,hexlen)+'",'; + if CSVPrefForm.cb_Attributes.Checked then + line:=line+'"'+Image.Disc[dir].Entries[entry].Attributes+'",'; + if CSVPrefForm.cb_Address.Checked then + line:=line+'"0x'+IntToHex(Image.Disc[dir].Entries[entry].Sector,hexlen)+'",'; + if CSVPrefForm.cb_CRC32.Checked then + line:=line+'"0x'+Image.GetFileCRC(Image.GetParent(dir) + +Image.GetDirSep(Image.Disc[dir].Partition) + +Image.Disc[dir].Entries[entry].Filename)+',"'; + line:=LeftStr(line,Length(line)-1); + WriteLine(F,line); + end; + //Write a footer + WriteLine(F,'""'); + WriteLine(F,'"'+ApplicationTitle+' v'+ApplicationVersion+'",' + +'"by Gerald J Holdsworth",' + +'"gerald@geraldholdsworth.co.uk"'); + //Finally free up the file stream + F.Free; + //Close the progress window + ProgressForm.Hide; + end; end; end; @@ -4866,21 +4985,36 @@ procedure TMainForm.btn_SettingsClick(Sender: TObject); end; end; +{------------------------------------------------------------------------------} +//Produces a report on the image +{------------------------------------------------------------------------------} +procedure TMainForm.btn_ShowReportClick(Sender: TObject); +begin + ImageReportForm.Report.Clear; + ImageReportForm.Report.Lines:=Image.ImageReport(False); + //Add a footer + ImageReportForm.Report.Lines.Add('__________________________________________'); + ImageReportForm.Report.Lines.Add(ApplicationTitle+' v'+ApplicationVersion); + ImageReportForm.Report.Lines.Add('by Gerald J Holdsworth'); + ImageReportForm.Report.Lines.Add('gerald@geraldholdsworth.co.uk'); + ImageReportForm.ShowModal; +end; + {------------------------------------------------------------------------------} //Saves the configuration settings to the registry {------------------------------------------------------------------------------} procedure TMainForm.SaveConfigSettings; begin - SetRegValI('Texture', TextureType); - SetRegValI('ADFS_L_Interleave',ADFSInterleave); - SetRegValB('CreateINF', DoCreateINF); - SetRegValB('Debug_Mode', Fdebug); - SetRegValB('DFS_Zero_Sectors', FDFSZeroSecs); - SetRegValB('DFS_Beyond_Edge', FDFSBeyondEdge); - SetRegValB('DFS_Allow_Blanks', FDFSAllowBlank); - SetRegValB('Scan_SubDirs', FScanSubDirs); - SetRegValB('Open_DOS', FOpenDOS); - SetRegValB('UEF_Compress', FUEFCompress); + DIMReg.SetRegValI('Texture', TextureType); + DIMReg.SetRegValI('ADFS_L_Interleave',ADFSInterleave); + DIMReg.SetRegValB('CreateINF', DoCreateINF); + DIMReg.SetRegValB('Debug_Mode', Fdebug); + DIMReg.SetRegValB('DFS_Zero_Sectors', FDFSZeroSecs); + DIMReg.SetRegValB('DFS_Beyond_Edge', FDFSBeyondEdge); + DIMReg.SetRegValB('DFS_Allow_Blanks', FDFSAllowBlank); + DIMReg.SetRegValB('Scan_SubDirs', FScanSubDirs); + DIMReg.SetRegValB('Open_DOS', FOpenDOS); + DIMReg.SetRegValB('UEF_Compress', FUEFCompress); end; {------------------------------------------------------------------------------} @@ -5163,7 +5297,7 @@ function TMainForm.CreateDirectory(dirname, attr: String): TTreeNode; if index>-1 then //Directory added OK begin //Mark as changed - if Image.FormatNumber>>4<>diSpark then HasChanged:=True; + if Image.MajorFormatNumber<>diSpark then HasChanged:=True; //Create the node as a file Node:=AddFileToTree(DirList.Selected,dirname,index,True,DirList,False); //Update the directory reference and the directory flag @@ -5318,16 +5452,16 @@ procedure TMainForm.DirListMouseMove(Sender: TObject; Shift: TShiftState; X, //Are we over another node? Dst:=GetNodeAt(Y);//Node under the cursor if (DraggedItem<>nil)AND(Dst<>nil) - AND((DraggedItem<>Dst)or(Image.FormatNumber>>4=diAcornUEF))then + AND((DraggedItem<>Dst)or(Image.MajorFormatNumber=diAcornUEF))then begin //If it is not a directory, and not CFS, then get the parent - if(not TMyTreeNode(Dst).IsDir)and(Image.FormatNumber>>4<>diAcornUEF)then + if(not TMyTreeNode(Dst).IsDir)and(Image.MajorFormatNumber<>diAcornUEF)then Dst:=Dst.Parent; //Clear any selections and then highlight the original item, unless it is CFS DirList.ClearSelection; - if Image.FormatNumber>>4<>diAcornUEF then DraggedItem.Selected:=True; + if Image.MajorFormatNumber<>diAcornUEF then DraggedItem.Selected:=True; //Only allow copying if it is a different parent and not within itself - if(DraggedItem<>Dst)or(Image.FormatNumber>>4=diAcornUEF)then//Or UEF + if(DraggedItem<>Dst)or(Image.MajorFormatNumber=diAcornUEF)then//Or UEF begin //Highlight it Dst.Selected:=True; @@ -5458,7 +5592,7 @@ function TMainForm.GetCopyMode(Shift: TShiftState): Boolean; end; //If the destination is the same as the source, copy only (not UEF) if(DraggedItem<>nil)and(Dst<>nil)then - if(DraggedItem.Parent=Dst)and(Image.FormatNumber>>4<>diAcornUEF)then + if(DraggedItem.Parent=Dst)and(Image.MajorFormatNumber<>diAcornUEF)then Result:=True; end; @@ -5522,13 +5656,13 @@ procedure TMainForm.DoCopyMove(copymode: Boolean); begin //Are we dragging something, over something and is not the same as the source? if (DraggedItem<>nil)AND(Dst<>nil) - AND((DraggedItem<>Dst)or(Image.FormatNumber>>4=diAcornUEF))then + AND((DraggedItem<>Dst)or(Image.MajorFormatNumber=diAcornUEF))then begin //If it is not a directory, or CFS format, then get the parent if (not TMyTreeNode(Dst).IsDir) - and(Image.FormatNumber>>4<>diAcornUEF)then Dst:=Dst.Parent; + and(Image.MajorFormatNumber<>diAcornUEF)then Dst:=Dst.Parent; //Only allow moving/copying if it is not within itself - if(DraggedItem<>Dst)or(Image.FormatNumber>>4=diAcornUEF)then + if(DraggedItem<>Dst)or(Image.MajorFormatNumber=diAcornUEF)then begin //Read in the destination, if necessary if(TMyTreeNode(Dst).IsDir)and(not TMyTreeNode(Dst).BeenRead)then @@ -5536,7 +5670,7 @@ procedure TMainForm.DoCopyMove(copymode: Boolean); //Take a copy of the filename newfn:=DraggedItem.Text; //Do the copy/move - if Image.FormatNumber>>4<>diAcornUEF then //Everything but UEF + if Image.MajorFormatNumber<>diAcornUEF then //Everything but UEF begin if copymode then //copy - here we rename the file if the destination already if Image.ValidateFilename(GetFilePath(Dst),newfn) then //has one the same @@ -5544,7 +5678,7 @@ procedure TMainForm.DoCopyMove(copymode: Boolean); if not copymode then //move index:=Image.MoveFile(GetFilePath(DraggedItem),GetFilePath(Dst)); end; - if Image.FormatNumber>>4=diAcornUEF then //UEF only + if Image.MajorFormatNumber=diAcornUEF then //UEF only begin if copymode then //copy - Acorn UEF index:=Image.CopyFile(DraggedItem.AbsoluteIndex-1,Dst.AbsoluteIndex-1); @@ -5558,9 +5692,9 @@ procedure TMainForm.DoCopyMove(copymode: Boolean); //new reference ref:=0; if(Image.FileExists(GetFilePath(Dst)+Image.DirSep+newfn,ref)) - or(Image.FormatNumber>>4=diAcornUEF)then + or(Image.MajorFormatNumber=diAcornUEF)then begin - if Image.FormatNumber>>4<>diAcornUEF then + if Image.MajorFormatNumber<>diAcornUEF then begin entry:=ref mod $10000; //Bottom 16 bits - entry reference dir :=ref div $10000; //Top 16 bits - directory reference @@ -5578,7 +5712,7 @@ procedure TMainForm.DoCopyMove(copymode: Boolean); end; end; //Mark as changed - if Image.FormatNumber>>4<>diSpark then HasChanged:=True; + if Image.MajorFormatNumber<>diSpark then HasChanged:=True; if not copymode then //If moving begin //Update any open hexdumps @@ -5728,17 +5862,19 @@ procedure TMainForm.btn_NewImageClick(Sender: TObject); //Show the dialogue and set the filename if SaveImage.Execute then filename:=SaveImage.FileName else exit; end; + Image.ProgressIndicator:=@UpdateProgress; ProgressForm.Show; Application.ProcessMessages; - Image.ProgressIndicator:=@UpdateProgress; hdd:=False; //ADFS Hard Drive if(major=diAcornADFS)and(minor=8)then begin ok:=Image.FormatHDD(diAcornADFS, NewImageForm.harddrivesize, + NewImageForm.ide, NewImageForm.newmap, - NewImageForm.dirtype); + NewImageForm.dirtype, + NewImageForm.addheader); hdd:=True; end; //AFS HardDrive @@ -5747,7 +5883,7 @@ procedure TMainForm.btn_NewImageClick(Sender: TObject); //Create the format ok:=Image.FormatHDD(diAcornFS, NewImageForm.AFSImageSize.Position*10*1024, - False,minor+2); + True,False,minor+2,False); if(ok)and(NewImageForm.cb_AFScreatepword.Checked)then //Create blank password file for AFS if Image.CreatePasswordFile(nil)<0 then //If fails, report an error @@ -5757,14 +5893,14 @@ procedure TMainForm.btn_NewImageClick(Sender: TObject); //DOS Hard Drive if(major=diDOSPlus)and(minor=6)then begin - ok:=Image.FormatHDD(major,NewImageForm.harddrivesize,False, - NewImageForm.fat); + ok:=Image.FormatHDD(major,NewImageForm.harddrivesize,True,False, + NewImageForm.fat,False); hdd:=True; end; //Amiga Hard Drive if(major=diAmiga)and(minor=2)then begin - ok:=Image.FormatHDD(major,NewImageForm.harddrivesize,False,0); + ok:=Image.FormatHDD(major,NewImageForm.harddrivesize,True,False,0,False); hdd:=True; end; //Floppy Drive @@ -5814,12 +5950,12 @@ procedure TMainForm.AttributeChangeClick(Sender: TObject); end; att:=''; //Attributes - DFS and UEF - if(Image.FormatNumber>>4=diAcornDFS) - or(Image.FormatNumber>>4=diAcornUEF)then + if(Image.MajorFormatNumber=diAcornDFS) + or(Image.MajorFormatNumber=diAcornUEF)then if cb_DFS_l.Checked then att:=att+'L'; //Attributes - ADFS - if((Image.FormatNumber>>4=diAcornADFS) - or(Image.FormatNumber>>4=diSpark)) + if((Image.MajorFormatNumber=diAcornADFS) + or(Image.MajorFormatNumber=diSpark)) and(not afs)and(not dos)then begin if cb_ADFS_ownw.Checked then att:=att+'W'; @@ -5832,8 +5968,8 @@ procedure TMainForm.AttributeChangeClick(Sender: TObject); if cb_ADFS_pubp.Checked then att:=att+'P'; end; //Attributes - AFS - if(Image.FormatNumber>>4=diAcornFS) - or((Image.FormatNumber>>4=diAcornADFS)and(afs))then + if(Image.MajorFormatNumber=diAcornFS) + or((Image.MajorFormatNumber=diAcornADFS)and(afs))then begin if cb_AFS_ownw.Checked then att:=att+'W'; if cb_AFS_ownr.Checked then att:=att+'R'; @@ -5842,14 +5978,14 @@ procedure TMainForm.AttributeChangeClick(Sender: TObject); if cb_AFS_pubr.Checked then att:=att+'r'; end; //Attributes - Commodore 64 - if Image.FormatNumber>>4=diCommodore then + if Image.MajorFormatNumber=diCommodore then begin if cb_C64_c.Checked then att:=att+'C'; if cb_C64_l.Checked then att:=att+'L'; end; //Attributes - DOS Plus - if(Image.FormatNumber>>4=diDOSPlus) - or((Image.FormatNumber>>4=diAcornADFS)and(dos))then + if(Image.MajorFormatNumber=diDOSPlus) + or((Image.MajorFormatNumber=diAcornADFS)and(dos))then begin if cb_DOS_hidden.Checked then att:=att+'H'; if cb_DOS_read.Checked then att:=att+'R'; @@ -5857,7 +5993,7 @@ procedure TMainForm.AttributeChangeClick(Sender: TObject); if cb_DOS_archive.Checked then att:=att+'A'; end; //Attributes - Amiga - if Image.FormatNumber>>4=diAmiga then + if Image.MajorFormatNumber=diAmiga then begin if not cb_Amiga_ownd.Checked then att:=att+'D'; if not cb_Amiga_owne.Checked then att:=att+'E'; @@ -5878,13 +6014,13 @@ procedure TMainForm.AttributeChangeClick(Sender: TObject); end; //Add the directory attribute if TMyTreeNode(DirList.Selected).IsDir then - if Image.FormatNumber>>4<>diAmiga then att:=att+'D' else att:=att+'F'; + if Image.MajorFormatNumber<>diAmiga then att:=att+'D' else att:=att+'F'; //Get the file path filepath:=GetFilePath(DirList.Selected); //Update the attributes for the file if Image.UpdateAttributes(filepath,att,DirList.Selected.Index) then begin - if Image.FormatNumber>>4<>diSpark then HasChanged:=True; + if Image.MajorFormatNumber<>diSpark then HasChanged:=True; //Update the status bar UpdateImageInfo; end @@ -5965,7 +6101,7 @@ procedure TMainForm.DeleteFile(confirm: Boolean); ok:=Image.DeleteFile(filepath); if ok then begin - if Image.FormatNumber>>4<>diSpark then HasChanged:=True; + if Image.MajorFormatNumber<>diSpark then HasChanged:=True; //Update the status bar UpdateImageInfo; //Now update the node and filedetails panel @@ -6244,7 +6380,7 @@ procedure TMainForm.DirListEditingEnd(Sender: TObject; Node: TTreeNode; end else begin - if Image.FormatNumber>>4<>diSpark then HasChanged:=True; + if Image.MajorFormatNumber<>diSpark then HasChanged:=True; //Update the status bar UpdateImageInfo; //Otherwise change the text on the tree and the file details panel @@ -6414,7 +6550,7 @@ procedure TMainForm.ToolBarContainerChange(Sender: TObject); index: Integer; begin for index:=0 to ToolBarContainer.Bands.Count-1 do - SetRegValS('ToolBar'+IntToStr(index) + DIMReg.SetRegValS('ToolBar'+IntToStr(index) ,ToolBarContainer.Bands[index].Control.Name); end; @@ -6445,16 +6581,16 @@ procedure TMainForm.ImageDetailsDrawPanel(StatusBar: TStatusBar; { inc(panwid,imgRect.Width+5); Rect.Width:=panwid;} png:=0; - case Image.FormatNumber>>4 of + case Image.MajorFormatNumber of diAcornDFS : png:=bbclogo; //BBC Micro logo for DFS diAcornADFS: begin - if(Image.FormatNumber mod $10<>3) + if (Image.MinorFormatNumber<>3) and(Image.MapType=diADFSOldMap) then - png:=acornlogo; //Acorn logo for 8 bit ADFS - if(Image.FormatNumber mod $10=3) + png:=acornlogo; //Acorn logo for 8 bit ADFS + if(Image.MinorFormatNumber=3) or(Image.MapType=diADFSNewMap) then - png:=riscoslogo; //RISC OS logo for 32 bit ADFS + png:=riscoslogo; //RISC OS logo for 32 bit ADFS end; diCommodore: png:=commodorelogo; //Commodore logo diSinclair : png:=sinclairlogo; //Sinclair logo @@ -6464,8 +6600,10 @@ procedure TMainForm.ImageDetailsDrawPanel(StatusBar: TStatusBar; diSpark : png:=sparklogo; //!SparkFS logo for Spark diAcornFS : png:=bbclogo; //BBC Micro logo for Acorn FS diDOSPlus : - if Image.FormatNumber mod $10<>0 then png:=msdoslogo //MS DOS logo for DOS - else png:=bbcmasterlogo; //BBC Master logo for DOS Plus + if Image.MinorFormatNumber<>0 then + png:=msdoslogo //MS DOS logo for DOS + else + png:=bbcmasterlogo; //BBC Master logo for DOS Plus end; Rect.Height:=Rect.Height-2; //Draw the icon diff --git a/LazarusSource/NewImageUnit.pas b/LazarusSource/NewImageUnit.pas index 768804a..e1dfd0b 100755 --- a/LazarusSource/NewImageUnit.pas +++ b/LazarusSource/NewImageUnit.pas @@ -57,7 +57,9 @@ TNewImageForm = class(TForm) public harddrivesize : Cardinal; - newmap : Boolean; + newmap, + addheader, + ide : Boolean; dirtype, fat : Byte; end; @@ -165,6 +167,8 @@ procedure TNewImageForm.btn_OKClick(Sender: TObject); begin //Selected hard drive size in MB harddrivesize:=HardDriveForm.CapacitySlider.Position*1024*1024; + addheader:=HardDriveForm.cb_AddHeader.Checked; + ide:=HardDriveForm.cb_IDE.Checked; if MainFormat.ItemIndex=1 then //ADFS Specific begin //New or old map diff --git a/LazarusSource/ProgressUnit.lfm b/LazarusSource/ProgressUnit.lfm index ae7a92a..d217820 100644 --- a/LazarusSource/ProgressUnit.lfm +++ b/LazarusSource/ProgressUnit.lfm @@ -31,8 +31,7 @@ object ProgressForm: TProgressForm Height = 18 Top = 181 Width = 595 - Align = alBottom - Alignment = taRightJustify + Alignment = taCenter AutoSize = False Caption = 'UpdateProgress' Font.Color = clBlue @@ -42,9 +41,9 @@ object ProgressForm: TProgressForm end object WaitLabel: TLabel Left = 1 - Height = 37 + Height = 38 Top = 1 - Width = 342 + Width = 331 Alignment = taCenter Caption = 'Please wait, working...' end diff --git a/LazarusSource/ProgressUnit.pas b/LazarusSource/ProgressUnit.pas index 256c45a..73d21df 100644 --- a/LazarusSource/ProgressUnit.pas +++ b/LazarusSource/ProgressUnit.pas @@ -64,7 +64,11 @@ procedure TProgressForm.FormShow(Sender: TObject); UpdateProgress.Caption:=''; //Re-position the 'Waiting' label WaitLabel.Left:=(Width-WaitLabel.Width)div 2; - WaitLabel.Top:=(Height-WaitLabel.Height)div 2; + WaitLabel.Top:=(Height-WaitLabel.Height)div 4; + //Re-position the 'Progress' label + UpdateProgress.Left:=0; + UpdateProgress.Width:=Width; + UpdateProgress.Top:=WaitLabel.Top+WaitLabel.Height+16; end; procedure TProgressForm.FormPaint(Sender: TObject); diff --git a/binaries/Linux/Disc Image Manager 32 bit.zip b/binaries/Linux/Disc Image Manager 32 bit.zip index 09480d8..8cdb840 100644 Binary files a/binaries/Linux/Disc Image Manager 32 bit.zip and b/binaries/Linux/Disc Image Manager 32 bit.zip differ diff --git a/binaries/Linux/Disc Image Manager ARM 32 bit.zip b/binaries/Linux/Disc Image Manager ARM 32 bit.zip index 4964ca7..b064c45 100644 Binary files a/binaries/Linux/Disc Image Manager ARM 32 bit.zip and b/binaries/Linux/Disc Image Manager ARM 32 bit.zip differ diff --git a/binaries/Linux/Disc Image Manager.zip b/binaries/Linux/Disc Image Manager.zip index be506e8..5d4d070 100644 Binary files a/binaries/Linux/Disc Image Manager.zip and b/binaries/Linux/Disc Image Manager.zip differ diff --git a/binaries/Windows/Disc Image Manager 32 bit.zip b/binaries/Windows/Disc Image Manager 32 bit.zip index 638d1af..c722f3d 100644 Binary files a/binaries/Windows/Disc Image Manager 32 bit.zip and b/binaries/Windows/Disc Image Manager 32 bit.zip differ diff --git a/binaries/Windows/Disc Image Manager.zip b/binaries/Windows/Disc Image Manager.zip index 56af77a..95eb0e4 100644 Binary files a/binaries/Windows/Disc Image Manager.zip and b/binaries/Windows/Disc Image Manager.zip differ diff --git a/binaries/macOS/Disc Image Manager 32 bit.dmg b/binaries/macOS/Disc Image Manager 32 bit.dmg index 71beccb..d983234 100644 Binary files a/binaries/macOS/Disc Image Manager 32 bit.dmg and b/binaries/macOS/Disc Image Manager 32 bit.dmg differ diff --git a/binaries/macOS/Disc Image Manager ARM.dmg b/binaries/macOS/Disc Image Manager ARM.dmg index 6dad103..d3fd69a 100644 Binary files a/binaries/macOS/Disc Image Manager ARM.dmg and b/binaries/macOS/Disc Image Manager ARM.dmg differ diff --git a/binaries/macOS/Disc Image Manager.dmg b/binaries/macOS/Disc Image Manager.dmg index a4a7f7c..45f9fa0 100644 Binary files a/binaries/macOS/Disc Image Manager.dmg and b/binaries/macOS/Disc Image Manager.dmg differ