IO API

The Microsoft Flight Simulator 2024 IO API permits you to read from and write to files that are included as part of the package that the module belongs to.

 

This API replaces some features provided by the standard C and C++ libraries (std::open, std::read, etc…) with asynchronous functions. The change to an asynchronous API is required since packages in Microsoft Flight Simulator 2024 are streamed and this introduces potential lag and stutter to the game if a non-asynchronous file operation blocks the simulation while trying to read an inaccessible or not-yet-available file from the VFS. The asynchronous functions of the IO API do not need to stop the simulation to read a file, thus allowing a better user experience. This does mean, however, that the API relies on callbacks to deal with requested file operations.

 

The functions available for this API are as follows:

 

Function Description
fsIOOpen Open a file, ready to be read from or written to.
fsIORead Read from a previously opened file.
fsIOOpenRead Open a file, and read some data from it, all in the same operation.
fsIOWrite Write to a previously opened file.
fsIOClose Close a previously opened file.
fsIOIsOpen Check to see if a file is currently open or not.
fsIOInProgress Check to see if there are any operations currently in porgress on a file or not.
fsIOIsDone Check to see if all operations on a file are finished or not.
fsIOHasError Check to see if a given file has previously triggered an error.
fsIOGetLastError Retrieve the last error that a file caused to be generated.
fsIOGetFileSize Get the size (in bytes) of a file.

 

You can find a sample project to use as a reference when using the Event API here:

 

 

Structs And Enums

This API has the following structs and enums that will be used by some of the functions and/or callbacks:

 

Enum Description
FsIOErr This enum is used for the transmission of error information.
FsIOOpenFlags This enum contains the bit-flag definitions used when opening files.

 

 

Typedefs

When using this API the following typedefs exist to help:

 

 

FsIOFile

The ID of a file open with different IO API functions.

typedef unsigned long long FsIOFile;
#define FS_IO_ERROR_FILE 0

 

 

FsIOFileOpenCallback

This defines the callback called when an open file operation is finished:

typedef void(*FsIOFileOpenCallback)(FsIOFile file, void* pUserData);

It has the following parameters:

 

Parameters Description
file The file that has been opened.
pUserData A pointer given with the callback by the user.

 

 

FsIOFileReadCallback

This defines the callback called when a read operation on a file is completed:

typedef void(*FsIOFileReadCallback)(FsIOFile file, char* outBuffer, int byteOffset, int byteRead, void* pUserData

It has the following parameters:

 

Parameters Description
file The file from which the data has been read from.
outBuffer This is a buffer containing the data read from the file. This is the same buffer which would have been given to the function that initiated the read operation.
byteOffset This is the offset (in bytes) from which the data began getting read from the file. This should be the same value which would have been given to the function that initiated the read operation.
byteRead This is the number of bytes of data that has been read from the file.
pUserData A pointer given with the callback by the user.

 

 

FsIOFileWriteCallback

This defines the callback returned when a write operation on a file is completed:

typedef void(*FsIOFileWriteCallback)(FsIOFile file, const char* pInBuffer, int byteOffset, int bytesWritten, void* pUserData);

It has the following parameters:

 

Parameters Description
file The file from which the data has been written to.
outBuffer This is a buffer containing the data that was written to the file.
byteOffset This is the offset (in bytes) at which the data began getting written to the file.
byteRead This is the number of bytes of data that has been written to the file.
pUserData A pointer given with the callback by the user.

 

 

Infinite Loops

It may be tempting to use the fsIOIsOpen and fsIOHasError functions to create a loop that checks when an open action has finished - and thus not rely on callbacks - by creating a kind of "active wait" code setup, something similar to the following code:

FsIOFile file = fsIOOpen("myFile", FsIOOpenFlag_RDONLY, myOpenCallback, nullptr);
// "Active wait" loop
while (!fsIOIsOpen(file) || !fsIOHasError(file))
{
    // Do nothing or maybe sleep
}
// Continue processing

However, not only is this a waste of resources as it keeps a thread open and busy doing nothing, it may lead to a situation where you create an infinite loop which will never end. Indeed, to process opening, reading, or writing, the game may need to continue to update some other managers and continue to run frames. But, by using the while loop, the code will block the module and the simulation engine may then wait for the module to continue to run, and therefor not update the other managers. It is for this reason that we strongly recommend that you do not do this and instead rely on the specified callbacks for open/read/write operations.

 

 

Inaccessible Files

It is important to note that many files will not be accessible to the IO API due to the way that packages are encrypted and archived. Specifically, the following file types will not be accessible within the main package:

  • *.cfg
  • *.xml
  • *.gltf

Trying to open these will cause an FsIOErr_AccessNotAllowed error. If you need to access data from one of these types of files, then you can workaround this limitation by creating a Copy asset group, and duplicating the files you want to read from into it. These files will not be encrypted or archived and will thus be accessible to the IO API, however be aware that this means that the files will not be protected against tampering or viewing by users.

 

Also note that even if a package file is not encrypted or archived, you cannot write to it unless it is part of the /work/ directory. See the fsIOOpen page for more details on package paths.