This document details how to add additional filesystems, partition types and formats.


Program structure
-----------------
Partition Manager uses dynamically loaded libraries and overlays to allow future filesystems and partition types to be added without modifying the base program.

!RunImage imports the following Libraries:
  Init         - performs start-up variable and filesystem identification and initialisation
  Wimp         - WIMP GUI
  FS.*         - FileSystem handlers
  Partitions.* - Partition support
  Formats.*    - Partition Format identification and initialisation
  Pi.Internet  - HTTP/HTTPS download and ZIP extract functions
  Debug        - Debug output functions

The rough calling order of procedures and functions is as follows, where:
  <filesystem> = FS.*
  <partition>  = Partition.*
  <format>     = Format.*

PROClocal_init
  PROCinit
    PROCassm
    PROCwimp_initialise
    PROCinitialise_filesystems
      FNinitialise_<filesystem>    - identify if filesystem is active
  FNinitialise_<format>            - allow Format's to create global variables
PROCread_drives
  FNadd_drives_<filesystem>        - asks every filesystem to add known drives
  PROCread_partitions
    FNread_partition_table_<filesystem>(<drive>)  - identify the partitioning.  For a modern filesystem this call is passed onto FNGPT_MBR_FileCore(<drive>)
      FNread_partition_table_<partition>(<drive>) - trawl the partition table
        FNread_bytes_<filesystem>(<drive>, <LBA low>, <LBA high>, <address>, <size in bytes>) - read data from a drive
        PROCunallocated_area(<drive>, <partition>, <LBA low>, <LBA high>, <size in LBA low>, <size in LBA high>) - add an unallocated block
        FNvolume_info_<format>(<drive>, <partition>) - read format specific settings for a partition
PROCfinalise_filesystems
  FNfinalise_<filesystem>          - clean up prior to exit




Global functions
----------------
The following global functions are available for general use:
  Where:
  <drive>      the Drive_Info%(<drive>)/Drive_Text$(<drive>) reference
  <partition>  the Partition_Info%(_,<partition>)/
               Partition_Text$(_,<partition>) reference
  <filesystem> the filename of the FS.* library

PROCadd64(RETURN <low A>, RETURN <high A>, <low B>, <high B>)
                  - 64bit add.  A=A+B

PROCsub64(RETURN <low A>, RETURN <high A>, <low B>, <high B>)
                  - 64bit subtract.  A=A-B

PROCadd_drives_progress(<current drive>, <max drives>)
                  - updates the progress bar while querying drives

PROCadd_unallocated_space(<drive>, <LBA low>, <LBA high>, <unusable LBA low>, <unusable LBA high>, <min partition size in LBA>)
                  - adds any unallocated area, starting at LBA low/high.
                    If the filesystem hosting the drive has address limits,
                    the unusable LBA low/high are the first LBA the filesystem
                    can't address.  Min partition size determines the smallest
                    area that should be shown in the GUI.

PROCalign_to_MB(<drive>, RETURN <LBA low>, RETURN <LBA high>)
                  - aligns to the next MB boundary for the drive

FNbackup_LBA(<drive>, <file handle>, <LBA low>, <LBA high>)
                  - called by FNbackup_partition_table_* to save a sector to a
                    to a partition backup file
                    Returns TRUE if successful, FALSE if failed

PROCchs_lba_mapping(<drive>)
                  - must be called when a drive is added to a filesystem that
                    relies on cylinder based partitioning, to ensure the drive
                    is restricted to the max addressable cylinders.  It will
                    switch the drive addressing to CHS to ensure FileCore
                    uses the drive CHS geometry, instead of calculating it.
                    Must be called after Drive_Info%(<drive>, D_Addressing%)
                    has been set to the drive's preferred addressing mode.
                    ie after calling FNdecode_IDENTIFY()

FNcmp64(RETURN <low A>, RETURN <high A>, <low B>, <high B>)
                  - 64bit compare.  =0 (A=B) =1 (A>B) =-1 (A<B)

FNcrc32(<address>, <size>, <seed>)
                  - returns the CRC for a block of memory, using a starting
                    seed

FNcylinder_CHS(<drive>, <LBA>)
                  - returns the CHS cylinder number for <LBA>

FNcylinder_size(<drive>)
                  - returns the drive cylinder size in LBA (ie Heads*Sectors)

FNdecode_IDENTIFY(<drive>, <partitioning type>, <max partitions>, <podule>, <FS library>, <drive prefix>, <physical drive no>)
                  - decodes an IDE drive IDENTIFY response and fills the
                    Drive_Text$()/Drive_Info%() fields.
                    NOTE: the IDENTIFY must have already been performed and be
                    in a 512 byte memory block pointed at by data%
eg. FNdecode_IDENTIFY(Drive_Count%, "HCCS", 4, "HCCS", "IDEFS", "IDEFS", D%)

In this example, Drive_Count% points to the next unused Drive array entry,
which will be filled with information for physical drive D%, decoded from the
IDENTIFY response data% points too.  Where:
<partitioning type> is the partioning used, if known.
<max partitions>    is the maximum number of partitions the filesystem
                    supports on a drive
<podule>            is the Podule manufacturer, if known
<FS library>        is the name of the filesystem library that is adding the
                    drive (eg IDEFS)
<drive prefix>      is the prefix the user sees (eg IDEFS)
<physical drive no> is the low-level drive no. used by the filesystem to
                    address the drive


FNdiscop(<drive>, <action>, <LBA low>, <LBA high>, <buffer>, <size>)
                  - will perform an appropriate discop to read or write 
                    to a drive.  The SWI used is the first available SWI in
                    the following order:
                      *_DiscOp64
                      *_SectorDiscOp
                      *_SectorOp
                      *_DiscOp
                    If <action> is ID (discop 3), *_DiscOp will always be used
                    Returns TRUE if <action> was successful

FNdismount_logical_drives(<drive>)
                  - will perform *DISMOUNT for all partitions on the drive
                    that have a known logical drive (ie
                    Partition_Text$(<drive>, P_Logical_Drive%) has been set)
                    Returns TRUE if successful

FNdismount_partition(<drive>, <partition>, <open files>)
                  - will perform *DISMOUNT on the partition if its logical
                    drive is known.  If <open files> is TRUE, it will check if
                    there are any open files on the partition and prompt the
                    user.
                    Returns TRUE if successful

PROCerror_fatal(<error no>, <text>)
                  - report a fatal error and exits Partition Manager

FNerror_recover(<error no>, <text>)
                  - report a recoverable error and returns one of the
                    following:
                     SUCCESS% - if <error no>=-1 on entry (ie <error no> is a
                                function call that returns TRUE if successful)
                     RETRY%   - Retry button pressed
                     SKIP%    - Skip button pressed
                     CANCEL%  - Cancel button pressed

                    This function can be called with <error no> coming from a
                    function call.  If the function returns anything other
                    than TRUE, a Retry/Skip/Cancel dialogue will be shown.
                    eg REPEAT UNTIL FNerror_recover(FNdiscop(..), "Read
                     failed")=SUCCESS%

FNfatal(<internal error number>)
                  - will return the text for an internal error

FNfilecore_inaccessible(<drive>, <partition>)
                  - returns TRUE if a partition is not fully addressible by
                    FileCore and will call PROCpartition_inaccessible if
                    required

FNgetvarval(<environment variable>)
                  - returns the environment value as a string

FNGPT_MBR_FileCore(<drive>)
                  - checks <drive> for partitioning in the following order:
                      GUID Partition Table
                      MBR Partition table
                      non-partitioned full-disc FileCore
                    The relevant partition functions are then called to
                    populate the partition arrays.

FNhead_CHS(<drive>, <LBA>)
                  - returns the CHS head number for <LBA>

FNlcase(<string>) - returns <string> in lower-case

FNlog2(<number>)  - returns the number of bits required for <number>

PROCmem_copy(<source>, <destination>, <size>)
                  - copy a word-aligned block of memory

FNmax_drives      - returns TRUE if the maximum number of drives that can be
                    added to Drive_Info() arrays has been reached.  Use in
                    FNadd_drives_<filesystem> to exit early

FNmodule_version(<string>)
                  - returns a Module version * 100.  ie 1.00 will return 100

PROCpartition_inaccessible(<drive>, <partition>)
                  - will mark the partition as inaccessible by FileCore

FNpretty_size(<LBA low>, <LBA, high>, <LBA log2>)
                  - returns a string in the form "24 MB", "48 GB", "2 TB" 

PROCprogress_format(<text>, <percentage>)
                  - should be called during a partition format to show
                    progress.  If <text> is blank, the current text is not
                    changed.  <percentage> should be in the range 0-100

PROCprogress_partition(<text>, <percentage>)
                  - should be called when writing the partition table to show
                    progress.  If <text> is blank, the current text is not
                    changed.  <percentage> should be in the range 0-100

PROCrestart_filesystem(<FS prefix>, <Modules to RMReInit>, <Filer Module>, <Filer taskname>, <Filer *command>)
                  - restarts a filesystem using the following method:
                     1. Checks if there are any open files (if <FS prefix> is
                        set)
                     2. *RMReInit <Modules to RMReInit>  (if the list is not
                        blank)
                     3. If <Filer Module> is not blank then:
                       3.1. Send a Quit message to <Filer taskname>
                       3.2. *RMReInit <Filer>
                       3.3. *WimpTask <Filer *command>)

FNsector_CHS(<drive>, <LBA>)
                  - returns the CHS sector number for <LBA>

FNsize_as_MB(<LBA low>, <LBA, high>, <LBA log2>)
                  - returns the size in MB of the LBA block as a 32bit integer

PROCsr64u(RETURN <low>, RETURN <high>, <shift>)
                  - performs an unsigned 64-bit right shift on <low>, <high>
                    ie (<high><low>)>>>shift

PROCsl64u(RETURN <low>, RETURN <high>, <shift>)
                  - performs a 64-bit left shift on <low>, <high>
                    ie (<high><low>)<<shift

FNstr(<address>, <max length>)
                  - returns the string at <address>, terminated by a char<32
                    <max length> should be set to a sensible maximum size to
                    prevent overrun

FNstr_hex32(<low>)
                  - returns an 8 character hex string "<low>"

FNstr_hex64(<low>, <high>)
                  - returns a 16 character hex string "<high><low>"

FNstr_hexN(<low>, <n>)
                  - returns <low> as an <n> character hex string with leading
                    zeros.  ie "00001" if <n> is 5

FNstr_word(<address>, <max length>)
                  - returns a byte order reversed string at <address>,
                    terminated by a char<32
                    <max length> should be set to a sensible maximum size to
                    prevent overrun
                    eg "aMtxro" would return "Maxtor"

FNstr_utf(<address>, <max length>)
                  - returns a regular string from a UTF string at <address>,
                    terminated by a char<32
                    eg. "U T F S t r i n g" would return "UTFString"

FNstr_to_dword(<string>)
                  - returns a four char string as a DWORD value, ie "ABCD"
                    returns &44434241

FNtrim(<string>)  - returns a string with trailing spaces removed

PROCunallocated_area(<drive>, <partition>, <start LBA low>, <start LBA high>, <size in LBA low>, <size in LBA high>)
                  - adds an unallocated partition area to the
                    Partition_Info%() / Partition_Text$() arrays

FNvalid_range(<drive>, <start LBA low>, <start LBA high>, <end LBA low>, (end LBA high>)
                  - returns TRUE if the start/end LBA pair are within the
                    drive size

FNverify(<buffer 1>, <buffer 2>, <size>)
                  - returns TRUE if two buffers match

FNword(<address>) - return a 16bit little-endian word as a big-endian value

PROCword_write(<address>, <value>)
                  - write a 16bit big-endian value as a little-endian word

FNyes_no(<string>)
                  - prompt <string> as a YES / NO question
                    Returns YES% or NO%

PROCzero_block(<address>, <size>)
                  - zero's a block of memory.  If <address>+<size> is not
                    word aligned, the trailing byte will also be zeroed



Global variables
----------------
The following global variables should be used and updated where appropriate:

data%             - a block of memory for general use
data_limit%       - size of the data% memory block.  Care should be taken when
                    using data% that an upscream PROC/FN isn't actively using
                    it.  Use "LOCAL data%" if this is the case
Drive_Count%      - Drive count, increased whenever FNadd_drives_* add a
                    drive.  Drive_Count% always points to the next unused
                    entry in Drive_Info%() / Drive_Text$()
Max_Partitions%   - the maximum number of partitions the GUI can handle on an
                    individual drive.
                    Used to dimension Partition_Info%() / Partition_Text$()
                    Ensure partition count per drive does not exceed this
                    value by checking <n> < Max_Partitions%
                    If there are more partitions on the drive that can't be
                    added, inform the user via PROCinformation() and set
                    Drive_Info%(<drive>, D_Read_Only%)=TRUE to prevent changes
Max_Drives%       - the maximum number of physical drives supported by the GUI
                    Used to dimension Drive_Info%() / Drive_Text$()
                    Before adding a drive, call FNmax_drives and exit early
                    if it returns TRUE.  Inform the user via
                    PROCinformation(), or fail silently
Scan_for_Drive%   - TRUE if a low-level scan for physical drives is being
                    performed.  When TRUE, Drive counts reported by the
                    filesystem should be ignored and all possible drives
                    should be checked and added if found.
                    If there is a mismatch with the number of configured
                    drives, inform the user via FNyes_no and correct the
                    configured drive count if required.
OS_Version%       - OS version * 100.  eg 311 for RISC OS 3.11
WIMP_Version%     - WindowManager Module version * 100.  eg 350 for v3.50
FileCore_Version% - FileCore Module version * 100
FileSwitch_Version% - FileSwitch Module version * 100


Result values
-------------
SUCCESS%          - value returned by FNerror_recover
RETRY%            - value returned by FNerror_recover
SKIP%             - value returned by FNerror_recover
CANCEL%           - value returned by FNerror_recover
YES%              - value returned by FNyes_no
NO%               - value returned by FNyes_no
UNKNOWN%          - can be used to set the initial value of a variable that
                    tracks responses and needs an indeterminate initial value


Drive arrays
------------
The following arrays should be filled when adding drives:

Drive_Info%(<n>, <variable>) where <variable> is,
  D_Drive_No%         - Drive number used internally by the filesystem
  D_LBA_Alignment%    - size in bytes of physical sectors.  Only set if the
                        physical and logical sectors sizes are different
  D_LBA_Size%         - size in bytes of logical sectors
  D_LBA_Log2%         - Log2 of D_LBA_Size%
  D_Partitions%       - Number of partitions on the drive
  D_Max_Partitions    - Max number of partitions supported by the filesystem
  D_Read_Only%        - TRUE if drive is read-only
  D_Removable%        - <>0 if drive is removable
  D_Size_In_LBA_Low%  - Low 32-bits of drive size in LBA
  D_Size_In_LBA_High% - High 32-bits of drive size in LBA
  D_Addressing%       - bitwise field that details the addressing modes the
                        drive supports:
                          bit 0 = LBA_24%
                          bit 1 = LBA_48%
                          bit 2 = CHS%
  D_Heads%            - Heads used when FileCore formatting or CHS addressing
  D_Sectors%          - Sectors used when FileCore formatting or performing
                        CHS addressing 
  D_Cylinders%        - Max addressable cylinders.  Used to restrict
                        filesystems that rely of cylinder based partitioning
  D_Info%             - Drive specific info not covered above that's private
                        to the filesystem adding the info

This array is populated by FNadd_drives_<filesystem> in the form:
  Drive_Info%(Drive_Count%, <variable>)


Drive_Text$(<n>, <variable>) where <variable> is,
  D_Icon%             - Icon name used for drive.  ie "hd", "cd", "sd"
                        Icons are stored in !PartMgr.Sprites22
  D_GUID%             - Drive unique identifier (if known)
  D_Model%            - Drive Model (if known)
  D_Partitioning%     - Partitioning method used on the drive.
                        eg "GPT", "MBR", "FileCore", "HCCS", "ICS"
  D_Physical_Name%    - Name of the physical drive as shown in the GUI. eg
                          "ADFS 0:1" would be a Primary Slave drive
  D_Serial%           - Drive serial number (if known)
  FS_Library%         - FileSystem library that added the drive.  eg "ADFS"
  FS_Podule%          - Identifies the Podule hosting the drive, if the
                        drive can be accessed using regular DiscOp calls
                        then set to "FileCore", anything else denotes a
                        bespoke disc read/write function is required
  FS_Prefix%          - FileSystem prefix used to address drives in RISC OS
                        eg. "ADFS", "SCSI"

This array is populated by FNadd_drives_* in the form:
  Drive_Text%(Drive_Count%, <variable>)

Drive_Count% should be increased after a drive has been added to these arrays



Partition arrays
----------------
The following arrays should be filled when adding partitions to a drive:

Partition_Info%(<drive>, <partition>, <variable>) where <variable> is,
  P_Bootable%            - TRUE if the drive is a boot drive
  P_Colour%              - Wimp colour of the partition.  Values are:
                           White%, Light_Grey%, Mid_Grey%, Red%, Cream% and
                           Light_Blue%
  P_Display%             - TRUE if the partition should be shown in the
                           volume table.  If set to 1 the partition will not
                           be shown in the GUI
  P_End_CHS_Cylinder%    - (MBR only) end cylinder
  P_End_CHS_Head%        - (MBR only) end head
  P_End_CHS_Sector%      - (MBR only) end sector
  P_Free_Low%            - Low 32-bits of Free bytes in LBA
  P_Free_High%           - High 32-bits of Free bytes in LBA.  This should be
                           set to -1 if the free space is unknown
  P_OS_Size_In_LBA_Low%  - Low 32-bits of FileCore reported drive size in LBA
  P_OS_Size_In_LBA_High% - High 32-bits of FileCore reported drive size in LBA
  P_Size_In_LBA_Low%     - Low 32-bits of partition size in LBA
  P_Size_In_LBA_High%    - High 32-bits of partition size in LBA
  P_Start_CHS_Cylinder%  - (MBR only) start cylinder
  P_Start_CHS_Head%      - (MBR only) start head
  P_Start_CHS_Sector%    - (MBR only) start sector
  P_Start_LBA_Low%       - Low 32-bits of start LBA
  P_Start_LBA_High%      - High 32-bits of start LBA
  P_Type%                - partition type
                           Internal values used to identify partition types:
                           -1 indicates a deleted partition
                           -2 indicates an unusable area
                           -3 indicates an unallocated area
                              ie a gap between partitions
                           Any other value is specific to the
                           partitioning scheme functions
  P_Overlayed_FileCore%  - Only applicable to partition 0.  TRUE if a full-
                           disc FileCore overlays another partition scheme
  P_Read_Only%           - TRUE if the partition can't be modified

Array entries are added by FNread_partition_table_* in the form:
  Partition_Info%(<drive>, <partition>, <variable>)


Partition_Text$(<drive>, <partition>, <variable>) where <variable> is,
  P_Additional_Info%     - Additional text to append to the Name shown in the
                           GUI volume table. eg " (ADFS::4)"
  P_Format%              - Format of partition.  eg "FileCore", "FAT"
  P_GUID%                - (GPT only) Global Unique Identifier of the 
                           partition
  P_Type_GUID%           - (GPT only) Global Unique Identifier of the 
                           partition format
  P_Attributes%          - (GPT only) Partition attributes
  P_Layout%              - Textual layout of partition.  eg "E+, Boot"
  P_Logical_Drive%       - Logical drive name RISC OS is using.  eg "ADFS::4"
  P_Volume_Name%         - Volume label of partition.  eg "HardDisc4"
  FS_Prefix%             - FileSystem prefix used to address drives within 
                           RISC OS.  eg. "ADFS"

Array entries are added by FNread_partition_table_* functions, in the form:
  Partition_Info%(<drive>, <partition>, <variable>)




Global errors
-------------
These variable should be passed to FNfatal(<variable>) to report a fatal error:

Buffer_Overflow% - "LBA size too large for buffer"
                   Indicates a DiscOp would exceed the data% buffer size



Implementing a <filesystem>
---------------------------
Refer to FS.SDFS as an example of how to implement a filesystem

Filesystem drivers placed in the FS subfolder should implement all of the
following functions:

FNinitialise_<filesystem>
  This is called once when Partition Manager loads.
  If the filesystem is available, it should initialise any global variables or
  code required later to interrogate the filesystem.

  Return value: =TRUE if filesytem exists
                =FALSE if filesystem does not exist


FNfinalise_<filesystem>
  Called just before Partition Manager exits, allowing any RMA claims etc
  to be released

  Return value: =0


FNadd_drives_<filesystem>
  Add physical drives on the filesystem to Drive_Info%()/Drive_Text$()

  Return value: =0


FNread_partition_table_<filesystem>(<drive>)
  Adds partitions accessible through the filesystem.  If the filesystem
  supports GPT or MBR partitioning, the call should be passed on via:
    =FNGPT_MBR_FileCore(<drive>)

  Return value: =0


FNread_bytes_<filesystem>(<drive>, <LBA low>, <LBA high>, <buffer>, <size>)
  Read <size> bytes from disc starting at the physical LBA into <buffer>.

  For FileCore compliant filesystems that do not support partitioning, this
  call can be passed to FileCore on via:
    FNdiscop(<drive>, 1, <LBA low>, <LBA high>, <buffer>, <size>)

  Return value: =TRUE if the read was successful
                =FALSE if an error occurred


FNwrite_bytes_<filesystem>(<drive>, <LBA low>, <LBA high>, <buffer>, <size>)
  Write <size> bytes from <buffer> to the disc,  starting at the physical LBA.

  For FileCore compliant filesystems that do not support partitioning, this
  call can be passed to FileCore on via:
    FNdiscop(<drive>, 2, <LBA low>, <LBA high>, <buffer>, <size>)

  Return value: =TRUE if the write was successful
                =FALSE if an error occurred


FNattach_<filesystem>(<drive>)
  This should reference Drive_Info%() for the physical drive info and perform
  a detach/attach to refresh the drive info cached by RISC OS and the
  filesystem.
  PROCrestart_filesystem() should be used if the Filer and FileSystem Modules
  need to be reinitialised


FNdetach_<filesystem>(<drive>)
  This should reference Drive_Info%() for the physical drive info and perform
  a dismount on all partitions mounted on the drive.
  For most filesystems, this should be passed on to
  FNdismount_logical_drives(<drive>) which will check for open files first


FNsupported_partitioning_<filesystem>(<drive>, <step>, options$())
  If <step> is >0, options$() should be set to the list of partition types
  the filesystem supports.  eg. options$()="None","FileCore","MBR"

  The length of each option should not exceed 14 characters

  Return value: =n-1 where n is the number of options that will be returned in
                   options$() when <step> is >0


FNsupported_formats_<filesystem>(<drive>, <step>, options$())
  If <step> is >0, options$() should be set to the list of formats
  the filesystem supports.  eg. options$()="FileCore","FAT"
  The first value in the list is the default format used when adding a new
  partition

  The length of each option should not exceed 14 characters

  Return value: =n-1 where n is the number of options that will be returned in
                   options$() when <step> is >0


FNprotected_LBA_<filesystem>(<drive>, <LBA>)
  This allows the filesystem driver to prevent a FileCore format from
  overwriting physical LBA that are used for internal use, for example
  the LBA that contains the partition table information.
  <drive> can be used to reference Drive_Info%() / Drive_Text$() if required

  Return value: =TRUE if the LBA must not be overwritten
                =FALSE otherwise




Implementing a <format>
-----------------------
Files placed in the "Formats" subfolder should include the following
functions:


FNinitialise_<format>
  Setup any global variables or code required later


FNcan_be_resized_<format>
  Returns TRUE if the format supports resizing


FNlayouts_<format>(<step>, options$())
  If <step> is >0, options$() should be set to the list of layouts.  eg.
    options$()="Default","FAT16","FAT32"

  The first value in the list is the default layout presented to the user
  when creating a new partition.

  The length of each option should not exceed 32 characters.

  Visual aids can be appended to options by surrounding text in brackets.
  These are to help the user decide which format is most suitable. eg:
    "E (RISC OS 3.50 and earlier)"
  Once the option is selected, the visual aid text will be removed.  In the
  example above the option will be set to "E"

  Return value: =n-1 where n is the number of options that will be returned in
                   options$() when <step> is >0


FNallocation_unit_size_<format>(<drive>, <step>, <layout>, <size low>, <size high>, options$())
  If <step> is >0, options$() should be set to the list of unit sizes supported
    eg. options$()="Default",STR$Drive_Info%(<drive>, D_LBA_Size%)

  The first value in the list is the default size presented to the user
  when creating a new partition.
  The second value in the list is the default size used if "Default" is
  selected by the user.

  The length of each option should not exceed 8 characters.

  <layout> is the currently selected layout.  ie E, E+
  <size low>, <size high> are the 64bit size of the partition in LBA

  Return value: =n-1 where n is the number of options that will be returned in
                   options$() when <step> is >0



FNvolume_info_<format>(<drive>, <partition>)
  Should attempt to identify the partition as being formatted as <format> and
  fill in the appropriate fields in the partition arrays.
  Returns TRUE if it was recognised, FALSE if not.

  Partition and Drive info can be read from the relevent arrays:
  Drive_Info%(<drive>, <variable>)
  Drive_Text$(<drive>, <variable>)
  Partition_Info%(<drive>, <partition>, <variable>)
  Partition_Text$(<drive>, <partition>, <variable>)

  The partition LBA start/end can be derived from:
  Partition_Info%(<drive>, <partition>, P_Start_LBA_Low%)
  Partition_Info%(<drive>, <partition>, P_Start_LBA_High%)
  Partition_Info%(<drive>, <partition>, P_Size_In_LBA_Low%)
  Partition_Info%(<drive>, <partition>, P_Size_In_LBA_High%)

  To read from the drive use FNread_bytes_<filesystem>.  eg. Reading the
  first LBA of the partition to buffer% could be achieved with the following
  example, where D% is <drive> and P% is <partition>:

  IF NOT EVAL("FNread_bytes_"+Drive_Text$(D%, FS_Library%)+"("+STR$(D%)+",&"+STR$~Partition_Info%(<drive>, P%, P_Start_LBA_Low%)+",&"+STR$~Partition_Info%(D%, P%, P_Start_LBA_High%)+",&"+STR$~buffer%+","+Drive_Info%(D%, D_LBA_Size%)+")") THEN
    PROCpartition_inaccessible(D%, P%)
    =TRUE
  ENDIF


FNvolume_init_<format>(<drive>, <partition>, <allocation size>, <layout>)
  Lay down the volume structure within the partition and returns TRUE if
  successful

    <allocation size> is the requested allocation unit size.  This should be
                      be used as the starting size and increased if required
    <layout>          is the requested layout in string form.  eg "E+"

  The following Partition_Text$() variables are set before this function is
  called and should be used while laying down the structure:

     P_Format%          
     P_Volume_Name%
     P_Bootable%

  The partition LBA start/end can be derived the following Partition_Info%()
  variables:

    P_Start_LBA_Low%    - start LBA lower 32 bits
    P_Start_LBA_High%   - start LBA upper 32 bits
    P_Size_In_LBA_Low%  - size in LBA lower 32 bits
    P_Size_In_LBA_High% - size in LBA upper 32 bits

  FNwrite_bytes_<filesystem>() should be used to write to the drive in
  conjuction with FNerror_recover() to prompt when a write error occurs

  Example, where:
             D% = <drive>
       LBA_low% = lower 32 bits of the LBA
      LBA_high% = upper 32 bits of the LBA
        buffer% = pointer to block to write
          size% = size of block to write

    LOCAL E%
    REPEAT
      E%=FNerror_recover(EVAL("FNwrite_bytes_"+Drive_Text$(D%, FS_Library%)+"("+STR$D%+",&"+STR$~LBA_low%+",&"+STR$~LBA_high%+",&"+STR$~buffer%+",&"+STR$~size%+")"), "Write error laying down volume structure")
    UNTIL E%=SUCCESS% OR E%=SKIP% OR E%=CANCEL%
    IF E%=CANCEL% THEN <cancel action>


(optional) FNvolume_resize_<format>(<drive>, <partition>, <low word of new partition size>, <high word of new partition size>)
  <partition> is being resized and the volume should now be modified to
  fit the new size.

  The partition size in Partition_Info%(<drive>, <partition>, P_Size_In_LBA_Low%)
  is the old size.  It should be altered if the resize is successful.

  If the partition size is being extended, the partition can be assumed to
  have already been resized.  If being shrunk, the partition will be resized
  if this function returns =TRUE

  Return TRUE if the resize was successful, or FALSE if the resize is not
  possible



Implementing a <partition> scheme
---------------------------------
Files placed in the "Partition" subfolder are called by filesystem
FNread_partition_table_* functions that support the partitioning it decodes.


The following functions should be implemented, where <filename> is the
filename of itself:


FNalign_partition_<filename>(<drive>, <partition>)
  This is called before formatting a partition and should adjust the
  partition's start/size if the partition scheme requires partition
  alignment.


FNread_partition_table_<filename>(<drive>)
  This should identify and decode the partition table on the disc, returning
  TRUE if it can decode the partition table and FALSE if it's not recognised.

  To read LBA 0 from the disc FNread_bytes_* should be called in the
  following manor:

    IF NOT EVAL("FNread_bytes_"+Drive_Text$(<drive>, FS_Library%) + "(" + STR$<drive>+",0,0,&"+STR$~data%+","+STR$Drive_Info%(D%, D_LBA_Size%)+")") THEN =FALSE

  The exampe above will read LBA 0 and exit early if the read fails.

  As partitions are decoded, they should be added to the following arrays,
  where:
      <drive> is the drive index passed on entry
      <n> is the partition number starting at zero
 
    Partition_Info%(<drive>, <n>, <variable>)
    Partition_Text$(<drive>, <n>, <variable>)

  When a partition is added to the array, the following variable should be
  increased:
    Drive_Info%(<drive>, D_Partitions%)


FNwrite_partition_table_<filename>(<drive>)
  This should write a partition table based on contents of the partition
  arrays.
  The number of partitions can be obtained from:
    Drive_Info%(<drive>, D_Partitions%)

  Partition info can be obtained from:
    Partition_Info%(<drive>, <n>, <variable>)
    Partition_Text$(<drive>, <n>, <variable>)

  The following Partition_Info%() variables should be used as a minimum to
  build the partition table entries:
    P_Start_LBA_Low%    - start LBA lower 32 bits
    P_Start_LBA_High%   - start LBA upper 32 bits
    P_Size_In_LBA_Low%  - size in LBA lower 32 bits
    P_Size_In_LBA_High% - size in LBA upper 32 bits
    P_Type%             - MBR partition type (if applicable)
  Refer to the Partition array section above for additional variables.

  The following Partition_Text$() variables can be used to build the partition
  table:
    P_Format% - "FAT" or "FileCore".  A value of "-" indicates an unallocated
                area
    P_GUID%   - GUID identifier for this partition (if applicable)
                If blank, generate a new GUID otherwise write this value to
                the partition GUID
    P_Attributes% - Partition specific attributes

  FNwrite_bytes_<filesystem>() should be used to write to the partition in
  conjuction with FNerror_recover() to prompt when a write error occurs.  eg
    where,
             D% = <drive>
       LBA_low% = lower 32 bits of the LBA
      LBA_high% = upper 32 bits of the LBA
        buffer% = pointer to block to write
          size% = size of block to write

    LOCAL E%
    REPEAT
      E%=FNerror_recover(EVAL("FNwrite_bytes_"+Drive_Text$(D%, FS_Library%)+"("+STR$D%+",&"+STR$~LBA_low%+",&"+STR$~LBA_high%+",&"+STR$~buffer%+",&"+STR$~size%+")"), "Failed to write the partition table")
    UNTIL E%=SUCCESS% OR E%=SKIP% OR E%=CANCEL%
    IF E%=CANCEL% THEN <cancel action>


FNbackup_partition_table_<filename>(<drive>, <file handle>)
  This should save the partition table to <file handle>.  FNbackup_LBA()
  should be called for each LBA that needs backing up.  Additional metadata
  should be written in the following structure:

   +0  action.  This should be >0 and is passed to FNrestore_partition_table_*
       when restoring a partition.  It should be used as an identifier for the
       data that follows
   +4  size of data
   +8  parameter 1
   +12 parameter 2
   +16 data block

   Should return =TRUE if successful, =FALSE if the backup failed


(optional) FNrestore_partition_table_<filename>(<drive>, <file handle>, <action>, <size>, <parameter 1>, <parameter 2>)
   This is called if metadata is encountered when restoring a partition table.
   The file pointer will be pointing at the start of the data block.

   Should return =TRUE if successful, =FALSE if the restore failed
