File and Folder Management, Create, Find, Delete

Introduction In order to handle files and folders in Unreal Engine 4. And stay cross-platform you need to use their file system. The UFS(Unreal File System), Here I will show you how to use UFS to ...

Updated over 4 years ago Edit Page Revisions

Introduction

In order to handle files and folders in Unreal Engine 4. And stay cross-platform you need to use their file system. The UFS(Unreal File System), Here I will show you how to use UFS to Manipulate the Files and folders itself. This means I won't show how to change the content of the Files. I will do this in another article.

First, We will take a look at how we can find the right path to your file or folder. Finding either a project path or an engine path.

Paths

One of the first things you need to do when dealing with paths and folders is finding the right path or folder. For standard UE4 folders, there is an excellent way to get to them.

Now let's get the project Directory and log it.


// Get the project directory and save it in ProjectDirectory
FString ProjectDirectory = FPaths::ProjectDir();
UE_LOG(LogTemp, Warning, TEXT("FilePaths: ProjectDirectory: %s"),*ProjectDirectory);

Great! This gets us.


LogTemp: Warning: FilePaths: ProjectDirectory: D:/Projects/UE4/UFS/

I can see that my project is located in D:/Projects/UE4/UFS/ This is correct.

Now there is a whole load of other folders lets take a look at them.


    FString ProjectDirectory = FPaths::ProjectDir();
    UE_LOG(LogTemp, Warning, TEXT("FilePaths: ProjectDir: %s"),*ProjectDirectory);

    FString ProjectConfigDirectory = FPaths::ProjectConfigDir();
    UE_LOG(LogTemp, Warning, TEXT("FilePaths: ProjectConfigDir: %s"),*ProjectConfigDirectory);

    FString ProjectContentDirectory = FPaths::ProjectContentDir();
    UE_LOG(LogTemp, Warning, TEXT("FilePaths: ProjectContentDir: %s"),*ProjectContentDirectory);
    
    FString ProjectIntermediateDirectory = FPaths::ProjectIntermediateDir();
    UE_LOG(LogTemp, Warning, TEXT("FilePaths: ProjectIntermediateDirectory: %s"),*ProjectIntermediateDirectory);

    FString ProjectLogDirectory = FPaths::ProjectLogDir();
    UE_LOG(LogTemp, Warning, TEXT("FilePaths: ProjectLogDir: %s"),*ProjectLogDirectory);

    FString ProjectModsDirectory = FPaths::ProjectModsDir();
    UE_LOG(LogTemp, Warning, TEXT("FilePaths: ProjectModsDir: %s"),*ProjectModsDirectory);

    FString ProjectPluginsDirectory = FPaths::ProjectPluginsDir();
    UE_LOG(LogTemp, Warning, TEXT("FilePaths: ProjectPluginsDir: %s"),*ProjectPluginsDirectory);

    FString ProjectSavedDirectory = FPaths::ProjectSavedDir();
    UE_LOG(LogTemp, Warning, TEXT("FilePaths: ProjectSavedDir: %s"),*ProjectSavedDirectory);

    FString ProjectUserDirectory = FPaths::ProjectUserDir();
    UE_LOG(LogTemp, Warning, TEXT("FilePaths: ProjectUserDir: %s"),*ProjectUserDirectory);

    FString ProjectPersistentDownloadDirectory = FPaths::ProjectPersistentDownloadDir();
    UE_LOG(LogTemp, Warning, TEXT("FilePaths: ProjectPersistentDownloadDir: %s"),*ProjectPersistentDownloadDirectory);

    FString ProjectPlatformExtensionsDirectory = FPaths::ProjectPlatformExtensionsDir();
    UE_LOG(LogTemp, Warning, TEXT("FilePaths: ProjectPlatformExtensionsDir: %s"),*ProjectPlatformExtensionsDirectory);

Okey This gives me:


LogTemp: Warning: FilePaths: ProjectDir: D:/Projects/UE4/UFS/
LogTemp: Warning: FilePaths: ProjectConfigDir: D:/Projects/UE4/UFS/Config/
LogTemp: Warning: FilePaths: ProjectContentDir: D:/Projects/UE4/UFS/Content/
LogTemp: Warning: FilePaths: ProjectIntermediateDirectory: D:/Projects/UE4/UFS/Intermediate/
LogTemp: Warning: FilePaths: ProjectLogDir: D:/Projects/UE4/UFS/Saved/Logs/
LogTemp: Warning: FilePaths: ProjectModsDir: D:/Projects/UE4/UFS/Mods/
LogTemp: Warning: FilePaths: ProjectPluginsDir: D:/Projects/UE4/UFS/Plugins/
LogTemp: Warning: FilePaths: ProjectSavedDir: D:/Projects/UE4/UFS/Saved/
LogTemp: Warning: FilePaths: ProjectUserDir: D:/Projects/UE4/UFS/
LogTemp: Warning: FilePaths: ProjectPersistentDownloadDir: D:/Projects/UE4/UFS/PersistentDownloadDir
LogTemp: Warning: FilePaths: ProjectPlatformExtensionsDir: D:/Projects/UE4/UFS/Platforms/

In Unreal Engine 4 the paths are often stored in FString. But the UFS(Unreal File System) deals with TCHAR* So we need to be able to convert TString To TCHAR* We do this by adding a * in front of the variable

Beside Project folders. You can also get engine folders.


    FString EngineDirectory = FPaths::EngineDir();
    UE_LOG(LogTemp, Warning, TEXT("FilePaths: EngineDir: %s"),*EngineDirectory);

    FString EngineConfigDirectory = FPaths::EngineConfigDir();
    UE_LOG(LogTemp, Warning, TEXT("FilePaths: EngineConfigDir: %s"),*EngineConfigDirectory);

    FString EngineContentDirectory = FPaths::EngineConfigDir();
    UE_LOG(LogTemp, Warning, TEXT("FilePaths: EngineContentDir: %s"),*EngineContentDirectory);
    
    FString EngineIntermediateDirectory = FPaths::EngineIntermediateDir();
    UE_LOG(LogTemp, Warning, TEXT("FilePaths: EngineIntermediateDirectory: %s"),*EngineIntermediateDirectory);

    FString EnginePluginsDirectory = FPaths::EnginePluginsDir();
    UE_LOG(LogTemp, Warning, TEXT("FilePaths: EnginePluginsDir: %s"),*EnginePluginsDirectory);

    FString EngineSavedDirectory = FPaths::EngineSavedDir();
    UE_LOG(LogTemp, Warning, TEXT("FilePaths: EngineSavedDir: %s"),*EngineSavedDirectory);

    FString EngineUserDirectory = FPaths::EngineUserDir();
    UE_LOG(LogTemp, Warning, TEXT("FilePaths: EngineUserDir: %s"),*EngineUserDirectory);

    FString EngineDefaultLayoutDir = FPaths::EngineDefaultLayoutDir();
    UE_LOG(LogTemp, Warning, TEXT("FilePaths: EngineDefaultLayoutDir: %s"),*EngineDefaultLayoutDir);

    FString EnginePlatformExtensionsDir = FPaths::EnginePlatformExtensionsDir();
    UE_LOG(LogTemp, Warning, TEXT("FilePaths: EnginePlatformExtensionsDir: %s"),*EnginePlatformExtensionsDir);

    FString EngineUserLayoutDir = FPaths::EngineUserLayoutDir();
    UE_LOG(LogTemp, Warning, TEXT("FilePaths: EngineUserLayoutDir: %s"),*EngineUserLayoutDir);  

Okey This gives me:


LogTemp: Warning: FilePaths: EngineDir: ../../../Engine/
LogTemp: Warning: FilePaths: EngineConfigDir: ../../../Engine/Config/
LogTemp: Warning: FilePaths: EngineContentDir: ../../../Engine/Config/
LogTemp: Warning: FilePaths: EngineIntermediateDirectory: ../../../Engine/Intermediate/
LogTemp: Warning: FilePaths: EnginePluginsDir: ../../../Engine/Plugins/
LogTemp: Warning: FilePaths: EngineSavedDir: C:/Users/Rene/AppData/Local/UnrealEngine/4.25/Saved/
LogTemp: Warning: FilePaths: EngineUserDir: C:/Users/Rene/AppData/Local/UnrealEngine/4.25/
LogTemp: Warning: FilePaths: EngineDefaultLayoutDir: ../../../Engine/Config/Layouts/
LogTemp: Warning: FilePaths: EnginePlatformExtensionsDir: ../../../Engine/Platforms/
LogTemp: Warning: FilePaths: EngineUserLayoutDir: C:/Users/Rene/AppData/Local/UnrealEngine/4.25/Saved/Config/Layouts/

Okey We now have a way to get a path. Let's try to use it.

FPlatformFileManager, finding files and Directories

UFS(Unreal file system) is platform-agnostic Meaning it will work on every platform you build for. So as long as you use this it will work for PC, Mac, Oculus, Android ext...

The first thing we need to do is to create an IPlatformFile& using FPlatformFileManager


IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile();

Now we can use the FileManager to perform all kind of operations.

FPlatformFileManager

Does a directory Exist?

You often want to be safe and make sure a directory exists before you try to use it. We can do this using the function:

 DirectoryExists(TCHAR*) 

This will return a true or false. Here is an example function checking if a directory Exists:


    // Getting the directory we want to check if it exists
    FString MyConfigDirectory = FPaths::ProjectConfigDir();
    MyConfigDirectory.Append(TEXT("MyConfigDir"));
    // Creating the FileManager
    IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile();
    /*This is where the magic happens.
    * The DirectoryExist function takes in one argument a TCHAR*
    * But since we have an FString we need to dereference it Using a *
    */
    if(FileManager.DirectoryExists(*MyConfigDirectory))
    {
        UE_LOG(LogTemp, Warning, TEXT("FilePaths: Directory Exists"));
    }
    else
    {
        UE_LOG(LogTemp, Warning, TEXT("FilePaths: Directory Does not exist"));
    }

In this case, the folder does not exist so I get this in return


LogTemp: Warning: FilePaths: Directory Does not exist

Spawning and Destroying Actors in Blueprint

How do I create a directory?

In this case, we don't have a directory, so we need to create one We can do this using the function:

 CreateDirectory(TChar*) 

This function will return a true if the directory was created and false if it failed to create the directory. Here is an example of creating a directory.


    // Getting the directory we want to check if it exists
    FString MyConfigDirectory = FPaths::ProjectConfigDir();
    MyConfigDirectory.Append(TEXT("MyConfigDir"));
    // Creating the FileManager
    IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile();
    /*This is where the magic happens.
    * The CreateDirectory function takes in one argument a TCHAR*
    * But since we have an FString we need to dereference it Using a *
    */
    if(FileManager.CreateDirectory(*MyConfigDirectory))
    {
        UE_LOG(LogTemp, Warning, TEXT("FilePaths: Directory was created"));
    }
    else
    {
        UE_LOG(LogTemp, Warning, TEXT("FilePaths: Directory was not created"));
    }

In this case, the directory is created. And I get this in return


LogTemp: Warning: FilePaths: Directory was created

How do I create a directory tree

Some times you don't want to create one folder but a whole tree of folders. We can do this using the function:

 CreateDirectoryTree(TChar*) 

This will return a true if the directory was created and false if it failed to create the directory. Here is an example of creating a directory tree.


    // Getting the directory we want to check if it exists
    FString MyConfigDirectoryTree = FPaths::ProjectConfigDir();
    MyConfigDirectoryTree.Append(TEXT("MyConfigDir/SecondFolder/ThirdFolder"));
    // Creating the FileManager
    IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile();
    /*This is where the magic happens.
    * The CreateDirectoryTree function takes in one argument a TCHAR*
    * But since we have an FString we need to dereference it Using a *
    */
    if(FileManager.CreateDirectoryTree(*MyConfigDirectoryTree))
    {
        UE_LOG(LogTemp, Warning, TEXT("FilePaths: DirectoryTree was created"));
    }
    else
    {
        UE_LOG(LogTemp, Warning, TEXT("FilePaths: Directory Was not Created"));
    }

In this case, the directory tree is created so I get this in return.


LogTemp: Warning: FilePaths: DirectoryTree was created

How do I Delete a directory?

In this case, We already have a directory, but we don't want it any more. We can do this using the function:

 .DeleteDirectory(TChar*) 

This will return a true if the directory was Deleted and false if it failed to Delete the directory. Here is an example of Deleting a directory.


    // Getting the directory we want to check if it exists
    FString MyConfigDirectory = FPaths::ProjectConfigDir();
    MyConfigDirectory.Append(TEXT("MyConfigDir"));
    // Creating the FileManager
    IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile();
    /*This is where the magic happens.
    * The DeleteDirectory function takes in one argument a TCHAR*
    * But since we have an FString we need to dereference it Using a *
    */
    if(FileManager.DeleteDirectory(*MyConfigDirectory))
    {
        UE_LOG(LogTemp, Warning, TEXT("FilePaths: Directory was Deleted"));
    }
    else
    {
        UE_LOG(LogTemp, Warning, TEXT("FilePaths: Directory was not Deleted"));
    }

In this case, the directory is Deleted, so I get this in return.


LogTemp: Warning: FilePaths: Directory was Deleted

Some times this function fails. A reason for this might be that the folder contains a file.

How do I Delete a directory with files in it?

This will delete all files in the directory

If a directory has files in it, we can't just use the DeleteDirectory() function. In this case, we need to use the function:

 .DeleteDirectoryRecursively(TCHAR*) 

This returns true if the directory and all its containing files and folders are Deleted and false if it failed to Delete the directory. Here is an example of Deleting a directory with all its sub-files and folders.


    // Getting the directory we want to check if it exists
    FString MyConfigDirectory = FPaths::ProjectConfigDir();
    MyConfigDirectory.Append(TEXT("MyConfigDir"));
    // Creating the FileManager
    IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile();
    /*This is where the magic happens.
    * The DeleteDirectoryRecursively function takes in one argument a TCHAR*
    * But since we have an FString we need to dereference it Using a *
    */
    if(FileManager.DeleteDirectoryRecursively(*MyConfigDirectory))
    {
        UE_LOG(LogTemp, Warning, TEXT("FilePaths: Directory and sub-files and folders are Deleted"));
    }
    else
    {
        UE_LOG(LogTemp, Warning, TEXT("FilePaths: Directory and sub-files and folders are not Deleted"));
    }

In this case, the directory and it sub-files and folder are all deleted, and we get this return


LogTemp: Warning: FilePaths: Directory was Deleted

How do I find all file's and folders in a directory

This is a bit more complicated and needs some extra coding. The base is still the same we ask the FileManager to do something, But it requires a little bit of additional setup. We need to create a Struct that inherits from IPlatformFile::FDirectoryVisitor And Overid


  struct DirectoryVisitor : public IPlatformFile::FDirectoryVisitor
{
    //This function is called for every file or directory it finds.
    bool Visit(const TCHAR* FilenameOrDirectory, bool bIsDirectory) override
    {
        // did we find a Directory or a file?
        if(bIsDirectory)
        {
            UE_LOG(LogTemp, Warning, TEXT("FilePaths: Directory found: %s"),FilenameOrDirectory);
        }
        else
        {
            UE_LOG(LogTemp, Warning, TEXT("FilePaths: File Found: %s"),FilenameOrDirectory);
        }
        return true;
    }
};

Now we have created the DirectoryVisitor struct we can use it in our code.


   FString MyConfigDirectoryTree = FPaths::ProjectConfigDir();
    MyConfigDirectoryTree.Append(TEXT("MyConfigDir"));
    IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile();
    //Now we need to create a DirectoryVisitor 
    DirectoryVisitor Visitor;
    // The ItterateDirectory takes two arguments The directory and the Visitor we created above.
    if(FileManager.IterateDirectory(*MyConfigDirectoryTree,Visitor))
    {
        UE_LOG(LogTemp, Warning, TEXT("FilePaths: Directory's or files found"));
    }
    else
    {
        UE_LOG(LogTemp, Warning, TEXT("FilePaths: Directory did not exist or visitor returned false"));
    }

In this case, A bunch of folders and files are found that I put in the MyConfigDir as a test.


LogTemp: Warning: FilePaths: Directory found: D:/Projects/UE4/UFS/Config/MyConfigDir/ExampleFolder
LogTemp: Warning: FilePaths: Directory found: D:/Projects/UE4/UFS/Config/MyConfigDir/SecondFolder
LogTemp: Warning: FilePaths: File Found: D:/Projects/UE4/UFS/Config/MyConfigDir/TestFile1.txt
LogTemp: Warning: FilePaths: File Found: D:/Projects/UE4/UFS/Config/MyConfigDir/TestFile2.txt
LogTemp: Warning: FilePaths: Directory's or files found

How do I find all file's and folders in a directory Recursively

Doing this is almost exactly the same as the IterateDirectory function. We need to create a Struct that inherits from IPlatformFile::FDirectoryVisitor And Overid


  struct DirectoryVisitor : public IPlatformFile::FDirectoryVisitor
{
    //This function is called for every file or directory it finds.
    bool Visit(const TCHAR* FilenameOrDirectory, bool bIsDirectory) override
    {
        // did we find a Directory or a file?
        if(bIsDirectory)
        {
            UE_LOG(LogTemp, Warning, TEXT("FilePaths: Directory found: %s"),FilenameOrDirectory);
        }
        else
        {
            UE_LOG(LogTemp, Warning, TEXT("FilePaths: File Found: %s"),FilenameOrDirectory);
        }
        return true;
    }
};

Now we have created the DirectoryVisitor struct we can use it in our code.


   FString MyConfigDirectoryTree = FPaths::ProjectConfigDir();
    MyConfigDirectoryTree.Append(TEXT("MyConfigDir"));
    IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile();
    //Now we need to create a DirectoryVisitor 
    DirectoryVisitor Visitor;
    // Here is the only difference We call IterateDirectoryRecursively instead of IterateDirectory
    // The IterateDirectoryRecursively takes two arguments The directory and the Visitor we created above.
    if(FileManager.IterateDirectoryRecursively(*MyConfigDirectoryTree,Visitor))
    {
        UE_LOG(LogTemp, Warning, TEXT("FilePaths: Directory's or files found"));
    }
    else
    {
        UE_LOG(LogTemp, Warning, TEXT("FilePaths: Directory did not exist or visitor returned false"));
    }

In this case, A bunch of folders and files were found that I put in the MyConfigDir as a test.


LogTemp: Warning: FilePaths: Directory found: D:/Projects/UE4/UFS/Config/MyConfigDir/ExampleFolder
LogTemp: Warning: FilePaths: Directory found: D:/Projects/UE4/UFS/Config/MyConfigDir/SecondFolder
LogTemp: Warning: FilePaths: Directory found: D:/Projects/UE4/UFS/Config/MyConfigDir/SecondFolder/ThirdFolder
LogTemp: Warning: FilePaths: File Found: D:/Projects/UE4/UFS/Config/MyConfigDir/SecondFolder/ThirdFolder/ThirdFolderFileExampleOne.txt
LogTemp: Warning: FilePaths: File Found: D:/Projects/UE4/UFS/Config/MyConfigDir/SecondFolder/ThirdFolder/ThirdFolderFileExampleTwo.txt
LogTemp: Warning: FilePaths: File Found: D:/Projects/UE4/UFS/Config/MyConfigDir/TestFile1.txt
LogTemp: Warning: FilePaths: File Found: D:/Projects/UE4/UFS/Config/MyConfigDir/TestFile2.txt
LogTemp: Warning: FilePaths: Directory's or files found

As you can see, it finds more folders and file's because it also checked the folders inside the given directory.

FPlatformFileManager Working with Files and Directories

It is nice being able to get directories and files. But we actually have to do something with them. Here are some useful base functions like Copy, Delete, Move, ext...

Check if a file exist.

Before trying to manipulate a file you have to make sure the file actually exists. If it does not exist you will get into some nasty errors. To check if a file exist we use the simple function.


.FileExist(TCHAR*);

Here is an example.


    FString MyFilePath = FPaths::ProjectConfigDir();
    MyFilePath.Append(TEXT("MyConfigDir/FakeConfig.txt"));
    IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile();
    
    if(FileManager.FileExists(*MyFilePath))
    {
        UE_LOG(LogTemp, Warning, TEXT("FilePaths: File found!"));
    }
    else
    {
        UE_LOG(LogTemp, Warning, TEXT("FilePaths: File not found!"));
    }

The file exists and I got this in my LOG


LogTemp: Warning: FilePaths: File found!

LogTemp: Warning: FilePaths: File found!

Copy A file.

When you Copy a file you keep the original. If you don't want that then take a look at the move function.

We can copy the file using the function:


.CopyFile(TCHAR * Destination,TCHAR * source, EPlatformFileRead ReadFlags, EPlatformFileWrite WriteFlags)

Here is an example.


FString MyFileSource = FPaths::ProjectConfigDir();
MyFileSource.Append(TEXT("MyConfigDir/FakeConfig.txt"));

FString MyFileDestination = FPaths::ProjectConfigDir();
MyFileDestination.Append(TEXT("MyConfigDir/SecondFolder/FakeConfig.txt"));

IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile();

UE_LOG(LogTemp, Warning, TEXT("FilePaths: Dest: %s"), *MyFileDestination);
UE_LOG(LogTemp, Warning, TEXT("FilePaths: Source: %s"), *MyFileSource);

if(FileManager.CopyFile(*MyFileDestination,*MyFileSource,EPlatformFileRead::None,EPlatformFileWrite::None))
{
   UE_LOG(LogTemp, Warning, TEXT("FilePaths: File Copied!"));
}
else
{
   UE_LOG(LogTemp, Warning, TEXT("FilePaths: File not Copied!"));
}

The file exists and I got this in my LOG


LogTemp: Warning: FilePaths: File Copied!

MoveFile

Move File is very simulair to copy File but it removes the original. It is also a bit simpler.

You can move files using the function:


.MoveFile(TCHAR* Destination,TCHAR* SOURCE);

Here is an example.


FString MyFileSource = FPaths::ProjectConfigDir();
MyFileSource.Append(TEXT("MyConfigDir/FakeConfig.txt"));

FString MyFileDestination = FPaths::ProjectConfigDir();
MyFileDestination.Append(TEXT("MyConfigDir/SecondFolder/FakeConfig.txt"));

IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile();

if(FileManager.MoveFile(*MyFileDestination,*MyFileSource))
{
   UE_LOG(LogTemp, Warning, TEXT("FilePaths: File Moved!"));
}
else
{
   UE_LOG(LogTemp, Warning, TEXT("FilePaths: File not Moved!"));
}

The file exists and I got this in my LOG


LogTemp: Warning: FilePaths: File Moved!

Delete a file

If you want to delete a file it is quite simple.


.DeleteFile(TCHAR*))

Here is an example.


 FString FileToDelete = FPaths::ProjectConfigDir();
FileToDelete.Append(TEXT("MyConfigDir/FakeConfig.txt"));

IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile();

if(FileManager.DeleteFile(*FileToDelete))
{
   UE_LOG(LogTemp, Warning, TEXT("FilePaths: File Deleted!"));
}
else
{
   UE_LOG(LogTemp, Warning, TEXT("FilePaths: File not Deleted!"));
}

The file exists and I got this in my LOG


LogTemp: Warning: FilePaths: File Deleted!

File Size

Getting the file size is a little bit more different from the other functions. It returns a Int with the size of the file in bytes instead of a bool.

To get the size of the file we use this function:


.FileSize(TCHAR*);

Here is an example.


FString File = FPaths::ProjectConfigDir();
File.Append(TEXT("MyConfigDir/FakeConfig.txt"));

IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile();
// Getting the file size and storring it
int FileSize = FileManager.FileSize(*File);
//The file size is -1 if it is not found
if(FileSize > 0)
{
   UE_LOG(LogTemp, Warning, TEXT("FilePaths: File Size: %d"),FileSize);
}
else
{
   UE_LOG(LogTemp, Warning, TEXT("FilePaths: File not found!"));
}

The file exists and I got this in my LOG


LogTemp: Warning: FilePaths: File Size: 21