# ADF S3 Class Objects

The `adfExplorer` package has defined several S3 class objects to make interaction with virtual ADF devices easier. This page presents these objects, what they are for and how they can be used.

## `adf_device`

### What is it?

Amiga Disk Files (ADF) or file representations of hardware disks. The `adf_device` class is used to represent a connection to such files. It can be seen as a virtual device. The file remains on disk, the `adf_device` opens a file connection to it. Underneath the S3 class the object has the type `externalptr`.

### How can it be initialized?

The `adf_device` can be initiated by opening an ADF or ADZ (a zipped ADF) file with `connect_adf()`.

    library(adfExplorer, warn.conflicts = FALSE)
    adz_file <- system.file("example.adz", package = "adfExplorer")
    my_device <- connect_adf(adz_file)

It can also be initiated by creating a new device with `create_adf_device()`. As the device needs to be stored as a file, you need to specify its destination path. The example below uses a temporary file for this purpose.

    adf_file <- tempfile(fileext = ".adf")
    new_device <- create_adf_device(adf_file)

### What can I do with it?

Well, that depends. When you just created a device with `create_adf_device()`, like the object named `new_device` in the example above, it does not contain a file system (see `vignette("file_system_modes")`). Virtual disks without a file system, could contain unspecified data or a custom track loader, which can contain instructions running independently from the operating system. You can inspect those disks by reading and writing blocks, the logical unit of data on a disk.

When a virtual disk does contain a file system, like the one opened with `connect_adf()` stored as the object named `my_device`, you can do a lot more. You can query the files and directories on the disk. Read, write, copy, move, manipulate and delete those files. The example below shows how to list the entries (files and directories) on the disk’s root.

    list_adf_entries(my_device)
    #> DIR  DEWR...     Devs
    #> DIR  DEWR...        S
    #> DIR  DEWR...     this
    #> DIR  DEWR...     mods

### End of life

As any `externalptr` object an `adf_device` will be cleaned up automatically by R’s garbage collector when it goes out of scope. However, as it maintains an open connection to a file on disk, it is always wise to `close()` the device when you are done with it. Calling `close()` on an `adf_device` will also automatically close all nested connections to files on the virtual device (see also `adf_file_con`)

    close(new_device)
    ## Let's keep `my_device` open to be used in examples below

## `adf_file_con`

### What is it?

It is a connection to a file on a virtual device represented by the `adf_device` class objects. Well… Technically, it isn’t a connection really, because CRAN’s policy does not allow to call non API entry points into R. This would be required to setup a proper connection. Instead, the `adf_file_con` is a mockup using an `externalptr` type to mimic R connections. In essence, it behaves very much like any other connection in R (more details below).

### How can it be initialized?

An `adf_file_con` object can be initiated using a call to `adf_file_con()`. For this purpose, you first need to connect to a virtual device containing a file system. We’ll use `my_device` opened in earlier examples. We can use the path to a file on a virtual device to open a connection as shown below.

    con <- adf_file_con(my_device, "DF0:mods/mod.intro")

    summary(con)
    #> $description
    #> [1] "mod.intro"
    #>
    #> $class
    #> [1] "adf_file_con"
    #>
    #> $mode
    #> [1] "rb"
    #>
    #> $text
    #> [1] "binary"
    #>
    #> $opened
    #> [1] "opened"
    #>
    #> $`can read`
    #> [1] "yes"
    #>
    #> $`can write`
    #> [1] "no"

If you want to open writable connections to a virtual device, you need to initiate the device without write protection. A writable connection can also be used to create new files on the virtual device.

### What can I do with it?

Depending on how you set the option `writable` when calling `adf_file_con`, you can either read and / or write to the connection. By default the connection is opened as read-only. You can use `readBin()` to read binary data from the connection.

    readBin(con, "raw", 20L)
    #>  [1] 69 6e 74 72 6f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Note that `adf_file_con()` always opens the connection as ‘binary’, but you can also use it for reading and writing text. So basically, `readLines()`, `writeBin()` and `writeLines()` are all available (the latter two obviously when the connection is writable).

In addition, you can tell where the current byte offset in the file is located with `seek()`. You can also use it to set the offset to a specific location.

    seek(con)
    #> [1] 20
    seek(con, 30L)
    #> [1] 30

### End of life

Like any connection it is good practice to `close()` it when you are done. Any `adf_file_con` still open when its parent `adf_device` is closed, will automatically be closed. It can no longer be accessed when the virtual device is not available.

    close(con)

## `virtual_path`

### What is it?

It is a vectorised `list` of `list`s. The nested list contains two elements:

1. An `adf_device` class object
2. A `character` string, specifying a path to a file or directory on the virtual device.

The outer list just contains a collection of these.

### How can it be initialized?

It can be initialised by calling `virtual_path()`.

    virtual_path(my_device, "DF0:s/startup-sequence")
    #> FILE DEWR----   0.6 kB Startup-Sequence
    virtual_path(my_device, "idontexist")
    #> Invalid path

Note that the file does not necessarily have to exist in order to create a virtual path. It doesn’t create any kind of connection to the file, but you can use it to open one.

### What can I do with it?

The `virtual_path` is a means to refer to files and directory on a virtual device. It also helps to set up processes with the pipe operator (`|>`). There is a `vignette("virtual_path")` dedicated to describing the object and its usage in more detail.

### End of life

It is just a list. You can just call `rm()` on it to get rid of it.

## `adf_block`

### What is it?

A representation of `raw` data of a logical unit on a virtual device of 512 bytes. It is simply a `vector` of `raw` data.

### How can it be initialized?

A new block block can be created with `new_adf_block()`. It will be initialised with null bytes. You can also coerce a `raw` `vector` to an `adf_block`.

    block1 <- new_adf_block()
    ## a block with random data
    block2 <- as_adf_block(as.raw(sample.int(n=256L, size = 512L, replace = TRUE) - 1L))

You can also intialise a block by reading it from a virtual device

    ## This will read the initial 'boot' block
    ## from the virtual device
    block3 <- read_adf_block(my_device, 0L)

### What can I do with it?

You can read blocks from a virtual disk with `read_adf_block()` and write them to a specific sector on the virtual disk with `write_adf_block()`. But be careful you can damage the file system or track loader of the virtual disk if you don’t know what you are doing.

### End of life

It can just be removed from memory by calling `rm()`. As there is no actual link to the virtual device, removing an `adf_block` class object from memory does not affect your virtual device.

    rm(block1, block2, block3)