# Filesystem Calls

The MetaModule uses newlib for libc. This provides standard library filesystem functions like `fopen`, `fread`, `fclose`.

You can use these exactly as they are used on a desktop computer. However, there are some things to keep in mind:

- Paths are different. Paths start with the volume name (3 characters), then a colon, then a slash. 
  The volumes available on the MetaModule are:
   - `sdc:/` -- The SD Card drive.
   - `usb:/` -- The USB drive.
   - `ram:/` -- The internal RAM disk. This is where plugins live, as well as
     unsaved patches. Everything in here is temporary. You could create temp
     files here, but it's usually a better idea to just store temp data in
     memory as normal C/C++ variables. If you need shared global state, a small
     file could work (like a lock file used in Linux systems).
   - `nor:/` -- NOR Flash (internal storage). There is a ~2MB volume on this
     flash chip that can hold patch files and other data. The filesystem is
     LFS, but you don't need to consider that when using the drive. It's highly
     recommended to avoid using this drive except for very tiny files (and not
     a lot of files). Users will want to save their patches here, so do not
     fill this up with module or patch data.


- Users might eject a disk at any time. Always check for errors when doing any
  file system access and handle them appropriately.

- Filesystem access should never be done from the audio context.

- Disk access is slow. The MetaModule was not designed to read/write to disks
  quickly, like a desktop computer would be designed. Also, SD Cards are by
  nature slow to read and write. Users might be using a slow or heavily
  fragmented disk. USB drives vary widely in performance. Multiple modules may
  be requesting disk access at the same time as your module. All these factors
  mean it's not surprising if a read request occasionally takes up to 4 or 5
  seconds even for just a few kB of data. You must plan for stalls and delays.
  If you have large files, buffering data to/from RAM is a good idea.


- Disk access is not allowed from the audio context. That is, you cannot 
  read or write files in the module's `process()` or `update()` function.
  Accessing the filesystem is allowed only in the module's constructor and
  destructor, `load_state` or `save_state` function (`dataToJson` and `dataFromJson` 
  for VCV ports), in any GUI code (`*_graphic_display()`), VCV context menus,
  and in AsyncThreads. See [CoreProcessor](./coreprocessor.md) for a detailed discussion.


  Note: do not access the filesystem in the audio context. This is 
  also true for desktop computer usage, but due to the threading model
  and speed of modern disks, it might only cause problems on some user's 
  computers some of the time, and appear to work OK other times.
  However, it violates DSP best practices and should be avoided
  regardless of the intended platform.



## Helper functions

See [filesystem/helpers.hh](../core-interface/filesystem/helpers.hh)

Namespace: `MetaModule::Filesystem`

### Filesystem::translate_path_to_local()

```c++
std::string translate_path_to_local(std::string_view path, std::string_view local_path, unsigned num_subdirs = 0);
```

Translate a path for a file on a computer hard drive, to a path for a file on the MetaModule.

The common use case is when a module stores a path to a sample or wavetable
file. The module can call this function to translate the stored path into one
that's relative to the currently playing patch file.

The function works by appending the file name from the computer path to the
provided local path. Typically the local path would be the path to the
currently playing patch file.

Stated another way: you give it a path to a file on a hard drive, and it
creates a path to a file with the same name, but in the patch directory on the MetaModule.
From the user's perspective, they would need to copy the file into the same directory
as the patch .yml file on their USB drive or SD Card, and then everything will work 
as expected.

The returned path may or may not refer to an actual file, so the module must handle the case
where the file does not exist.

As an optional parameter, one or more parent directories from the computer path can be appended.
This lets the module access files in a sub-directory that's in the same directory as the 
patch file (for example `samples/`).

The function checks if the path appears to be local already by seeing if it begins with a
MetaModule volume name. If so, it returns the path unaltered. This allows the module 
to always process all file paths with `translate_path_to_local`.

**Parameters:**
- `path`: the path from a computer that you want to translate. If the path is
  already local, it will be returned unaltered. If `num_subdirs` is 0, the filename will
  be extracted from the path and the rest will be ignored. If `num_subdirs` >
  0, the filename plus `num_subdirs` parent directories will be kept.
- `local_path`: the MetaModule path you want to prepend. Typically this is `Patch::get_dir()`.
- `num_subdirs`: sets how many subdirectories to capture from `path` (default 0, max 2)

This function allocates strings, and is not safe to call from the audio context.

**Example usage:**

The simplest way is to have the user copy any files that a patch uses into the same
directory as the patch .yml file. Then the module calls this:

```c++
std::string mm_path = Filesystem::translate_path_to_local(vcv_computer_path, Patch::get_dir());
```

mm_path will point to a file with the same name as the file in `vcv_computer_path`,
but in the same dir as the currently playing patch file.

```c++
#include "filesystem/helpers.hh"
#include "patch/patch_file.hh"

using namespace MetaModule;

class MyModule : rack::engine::Module {
    
    //...

	void dataFromJson(json_t* rootJ) override {
		json_t* samplePathJ = json_object_get(rootJ, "sampleFilePath");
		if (samplePathJ) {
            // Get the path to a file on a computer hard drive, e.g. /Users/Me/rack/samples/loop.wav
			std::string samplePath = json_string_value(samplePathJ);

            // Translate the path to "usb:/loop.wav"
            std::string localSamplePath = Filesystem::translate_path_to_local(samplePath, Patch::get_dir());

            load_sample_file(localSamplePath);
        }
	}


```

**Examples with different num_subdirs**

`translate_path_to_local("/Users/4ms/music/rack/samples/loop.wav", "sdc:/live-set/", num_subdirs)`
- num_subdirs = 0: => sdc:/live-set/loop.wav
- num_subdirs = 1: => sdc:/live-set/samples/loop.wav
- num_subdirs = 2: => sdc:/live-set/rack/samples/loop.wav

**More Examples:**
```
- translate_path_to_local("/Users/user/rack/samples/a.wav", "sdc:/", 0)				==> sdc:/a.wav
- translate_path_to_local("/Users/user/rack/samples/a.wav", "sdc:/", 1)				==> sdc:/samples/a.wav
- translate_path_to_local("/Users/user/rack/samples/a.wav", "sdc:/patches/", 0)		==> sdc:/patches/a.wav
- translate_path_to_local("/Users/user/rack/samples/a.wav", "sdc:/patches/", 1)		==> sdc:/patches/samples/a.wav
- translate_path_to_local("C:\User\rack\samples\a.wav", "usb:/patches/", 1)			==> usb:/patches/samples/a.wav
- translate_path_to_local("usb:/samples/a.wav", "nor:/ignored/", 1)					==> usb:/samples/a.wav (path is already local)
- translate_path_to_local("/root/a.wav", "usb:/patches/", 0)						==> usb:/patches/a.wav
- translate_path_to_local("/root/a.wav", "usb:/patches/", 1)						==> usb:/patches/root/a.wav
- translate_path_to_local("/root/a.wav", "usb:/patches/", 2)						==> usb:/patches/root/a.wav (only one parent dir)
```

### Filesystem::is_local_path()

```c++
bool is_local_path(std::string_view path);
```

Given a path, returns true if it appears to be a valid MetaModule path.

This only checks if the path starts with a valid volume name and colon,
it does not check if the file or directories actually exist or if the
volume is mounted.

Valid volume names (with colon appended) are:
- `sdc:`
- `usb:`
- `nor:`
- `ram:`

This function is safe to call in the audio context.
