Skip to content

Chapter 6.8: File I/O & JSON

Home | << Previous: Timers & CallQueue | File I/O & JSON | Next: Networking & RPC >>


Introduction

DayZ provides file I/O operations for reading and writing text files, JSON serialization/deserialization, directory management, and file enumeration. All file operations use special path prefixes ($profile:, $saves:, $mission:) rather than absolute filesystem paths. This chapter covers every file operation available in Enforce Script.


Path Prefixes

PrefixLocationWritable
$profile:Server/client profile directory (e.g., DayZServer/profiles/)Yes
$saves:Save directoryYes
$mission:Current mission folder (e.g., mpmissions/dayzOffline.chernarusplus/)Read typically
$CurrentDir:Current working directoryDepends
No prefixRelative to game rootRead only

Important: Most file write operations are restricted to $profile: and $saves:. Attempting to write elsewhere may silently fail.


File Existence Check

c
proto bool FileExist(string name);

Returns true if the file exists at the given path.

Example:

c
if (FileExist("$profile:MyMod/config.json"))
{
    Print("Config file found");
}
else
{
    Print("Config file not found, creating defaults");
}

Opening & Closing Files

c
proto FileHandle OpenFile(string name, FileMode mode);
proto void CloseFile(FileHandle file);

FileMode Enum

c
enum FileMode
{
    READ,     // Open for reading (file must exist)
    WRITE,    // Open for writing (creates new / overwrites existing)
    APPEND    // Open for appending (creates if not exists)
}

FileHandle is an integer handle. A return value of 0 indicates failure.

Example:

c
FileHandle fh = OpenFile("$profile:MyMod/log.txt", FileMode.WRITE);
if (fh != 0)
{
    // File opened successfully
    // ... do work ...
    CloseFile(fh);
}

Critical: Always call CloseFile() when done. Failure to close files can cause data loss and resource leaks.


Writing Files

FPrintln (Write Line)

c
proto void FPrintln(FileHandle file, void var);

Writes the value followed by a newline character.

FPrint (Write Without Newline)

c
proto void FPrint(FileHandle file, void var);

Writes the value without a trailing newline.

Example --- write a log file:

c
void WriteLog(string message)
{
    FileHandle fh = OpenFile("$profile:MyMod/log.txt", FileMode.APPEND);
    if (fh != 0)
    {
        int year, month, day, hour, minute;
        GetGame().GetWorld().GetDate(year, month, day, hour, minute);
        string timestamp = string.Format("[%1-%2-%3 %4:%5]", year, month, day, hour, minute);

        FPrintln(fh, timestamp + " " + message);
        CloseFile(fh);
    }
}

Reading Files

FGets (Read Line)

c
proto int FGets(FileHandle file, string var);

Reads one line from the file into var. Returns the number of characters read, or -1 at end of file.

Example --- read a file line by line:

c
void ReadConfigFile()
{
    FileHandle fh = OpenFile("$profile:MyMod/settings.txt", FileMode.READ);
    if (fh != 0)
    {
        string line;
        while (FGets(fh, line) >= 0)
        {
            Print("Line: " + line);
            ProcessLine(line);
        }
        CloseFile(fh);
    }
}

ReadFile (Raw Binary Read)

c
proto int ReadFile(FileHandle file, void param_array, int length);

Reads raw bytes into a buffer. Used for binary data.


Directory Operations

MakeDirectory

c
proto native bool MakeDirectory(string name);

Creates a directory. Returns true on success. Creates only the final directory --- parent directories must already exist.

Example --- ensure directory structure:

c
void EnsureDirectories()
{
    MakeDirectory("$profile:MyMod");
    MakeDirectory("$profile:MyMod/data");
    MakeDirectory("$profile:MyMod/logs");
}

DeleteFile

c
proto native bool DeleteFile(string name);

Deletes a file. Only works in $profile: and $saves: directories.

CopyFile

c
proto native bool CopyFile(string sourceName, string destName);

Copies a file from source to destination.

Example:

c
// Backup before overwriting
if (FileExist("$profile:MyMod/config.json"))
{
    CopyFile("$profile:MyMod/config.json", "$profile:MyMod/config.json.bak");
}

File Enumeration (FindFile / FindNextFile)

Enumerate files matching a pattern in a directory.

c
proto FindFileHandle FindFile(string pattern, out string fileName,
                               out FileAttr fileAttributes, FindFileFlags flags);
proto bool FindNextFile(FindFileHandle handle, out string fileName,
                         out FileAttr fileAttributes);
proto native void CloseFindFile(FindFileHandle handle);

FileAttr Enum

c
enum FileAttr
{
    DIRECTORY,   // Entry is a directory
    HIDDEN,      // Entry is hidden
    READONLY,    // Entry is read-only
    INVALID      // Invalid entry
}

FindFileFlags Enum

c
enum FindFileFlags
{
    DIRECTORIES,  // Return only directories
    ARCHIVES,     // Return only files
    ALL           // Return both
}

Example --- enumerate all JSON files in a directory:

c
void ListJsonFiles()
{
    string fileName;
    FileAttr fileAttr;
    FindFileHandle handle = FindFile(
        "$profile:MyMod/missions/*.json", fileName, fileAttr, FindFileFlags.ALL
    );

    if (handle)
    {
        // Process first result
        if (!(fileAttr & FileAttr.DIRECTORY))
        {
            Print("Found: " + fileName);
        }

        // Process remaining results
        while (FindNextFile(handle, fileName, fileAttr))
        {
            if (!(fileAttr & FileAttr.DIRECTORY))
            {
                Print("Found: " + fileName);
            }
        }

        CloseFindFile(handle);
    }
}

Important: FindFile returns just the file name, not the full path. You must prepend the directory path yourself when processing the files.

Example --- count files in a directory:

c
int CountFiles(string pattern)
{
    int count = 0;
    string fileName;
    FileAttr fileAttr;
    FindFileHandle handle = FindFile(pattern, fileName, fileAttr, FindFileFlags.ARCHIVES);

    if (handle)
    {
        count++;
        while (FindNextFile(handle, fileName, fileAttr))
        {
            count++;
        }
        CloseFindFile(handle);
    }

    return count;
}

JsonFileLoader (Generic JSON)

File: 3_Game/tools/jsonfileloader.c (173 lines)

The recommended way to load and save JSON data. Works with any class that has public fields.

Modern API (Preferred)

c
class JsonFileLoader<Class T>
{
    // Load JSON file into object
    static bool LoadFile(string filename, out T data, out string errorMessage);

    // Save object to JSON file
    static bool SaveFile(string filename, T data, out string errorMessage);

    // Parse JSON string into object
    static bool LoadData(string string_data, out T data, out string errorMessage);

    // Serialize object to JSON string
    static bool MakeData(T inputData, out string outputData,
                          out string errorMessage, bool prettyPrint = true);
}

All methods return bool --- true on success, false on failure with the error in errorMessage.

Legacy API (Deprecated)

c
class JsonFileLoader<Class T>
{
    static void JsonLoadFile(string filename, out T data);    // Returns void!
    static void JsonSaveFile(string filename, T data);
    static void JsonLoadData(string string_data, out T data);
    static string JsonMakeData(T data);
}

Critical Gotcha: JsonLoadFile() returns void. You CANNOT use it in an if condition:

c
// WRONG - will not compile or will always be false
if (JsonFileLoader<MyConfig>.JsonLoadFile(path, cfg)) { }

// CORRECT - use the modern LoadFile() which returns bool
if (JsonFileLoader<MyConfig>.LoadFile(path, cfg, error)) { }

Data Class Requirements

The target class must have public fields with default values. The JSON serializer maps field names directly to JSON keys.

c
class MyConfig
{
    int MaxPlayers = 60;
    float SpawnRadius = 150.0;
    string ServerName = "My Server";
    bool EnablePVP = true;
    ref array<string> AllowedItems = new array<string>;
    ref map<string, int> ItemPrices = new map<string, int>;

    void MyConfig()
    {
        AllowedItems.Insert("BandageDressing");
        AllowedItems.Insert("Canteen");
    }
}

This produces JSON:

json
{
    "MaxPlayers": 60,
    "SpawnRadius": 150.0,
    "ServerName": "My Server",
    "EnablePVP": true,
    "AllowedItems": ["BandageDressing", "Canteen"],
    "ItemPrices": {}
}

Complete Load/Save Example

c
class MyModConfig
{
    int Version = 1;
    float RespawnTime = 300.0;
    ref array<string> SpawnItems = new array<string>;
}

class MyModConfigManager
{
    protected static const string CONFIG_PATH = "$profile:MyMod/config.json";
    protected ref MyModConfig m_Config;

    void Init()
    {
        MakeDirectory("$profile:MyMod");
        m_Config = new MyModConfig();
        Load();
    }

    void Load()
    {
        if (!FileExist(CONFIG_PATH))
        {
            Save();  // Create default config
            return;
        }

        string error;
        if (!JsonFileLoader<MyModConfig>.LoadFile(CONFIG_PATH, m_Config, error))
        {
            Print("[MyMod] Config load error: " + error);
            m_Config = new MyModConfig();  // Reset to defaults
            Save();
        }
    }

    void Save()
    {
        string error;
        if (!JsonFileLoader<MyModConfig>.SaveFile(CONFIG_PATH, m_Config, error))
        {
            Print("[MyMod] Config save error: " + error);
        }
    }

    MyModConfig GetConfig()
    {
        return m_Config;
    }
}

JsonSerializer (Direct Use)

File: 3_Game/gameplay.c

For cases where you need to serialize/deserialize JSON strings directly without file operations:

c
class JsonSerializer : Serializer
{
    proto bool WriteToString(void variable_out, bool nice, out string result);
    proto bool ReadFromString(void variable_in, string jsonString, out string error);
}

Example:

c
MyConfig cfg = new MyConfig();
cfg.MaxPlayers = 100;

JsonSerializer js = new JsonSerializer();

// Serialize to string
string jsonOutput;
js.WriteToString(cfg, true, jsonOutput);  // true = pretty print
Print(jsonOutput);

// Deserialize from string
MyConfig parsed = new MyConfig();
string parseError;
js.ReadFromString(parsed, jsonOutput, parseError);
Print("MaxPlayers: " + parsed.MaxPlayers);

Summary

OperationFunctionNotes
Check existsFileExist(path)Returns bool
OpenOpenFile(path, FileMode)Returns handle (0 = fail)
CloseCloseFile(handle)Always call when done
Write lineFPrintln(handle, data)With newline
WriteFPrint(handle, data)Without newline
Read lineFGets(handle, out line)Returns -1 at EOF
Make dirMakeDirectory(path)Single level only
DeleteDeleteFile(path)Only $profile: / $saves:
CopyCopyFile(src, dst)--
Find filesFindFile(pattern, ...)Returns handle, iterate with FindNextFile
JSON loadJsonFileLoader<T>.LoadFile(path, data, error)Modern API, returns bool
JSON saveJsonFileLoader<T>.SaveFile(path, data, error)Modern API, returns bool
JSON stringJsonSerializer.WriteToString() / ReadFromString()Direct string operations
ConceptKey Point
Path prefixes$profile: (writable), $mission: (read), $saves: (writable)
JsonLoadFileReturns void --- use LoadFile() (bool) instead
Data classesPublic fields with defaults, ref for arrays/maps
Always closeEvery OpenFile must have a matching CloseFile
FindFileReturns only filenames, not full paths

Best Practices

  • Always wrap file operations in existence checks and close handles in all code paths. An unclosed FileHandle leaks resources and can prevent the file from being written to disk. Use guard patterns: check fh != 0, do work, then CloseFile(fh) before every return.
  • Use the modern JsonFileLoader<T>.LoadFile() (returns bool) instead of the legacy JsonLoadFile() (returns void). The legacy API cannot report errors, and attempting to use its void return in a condition silently fails.
  • Create directories with MakeDirectory() in order from parent to child. MakeDirectory only creates the final directory segment. MakeDirectory("$profile:A/B/C") fails if A/B does not exist. Create each level sequentially.
  • Use CopyFile() to create backups before overwriting config files. JSON parse errors from corrupted saves are unrecoverable. A .bak copy lets server owners restore the last good state.
  • Remember that FindFile() returns only filenames, not full paths. You must concatenate the directory prefix yourself when loading files found via FindFile/FindNextFile.

Compatibility & Impact

Mod Compatibility: File I/O is inherently isolated per mod when each mod uses its own $profile: subdirectory. Conflicts occur only when two mods read/write the same file path.

  • Load Order: File I/O has no load-order dependency. Mods read and write independently.
  • Modded Class Conflicts: No class conflicts. The risk is two mods using the same $profile: subdirectory name or filename, causing data corruption.
  • Performance Impact: JSON serialization via JsonFileLoader is synchronous and blocks the main thread. Loading large JSON files (>100KB) during gameplay causes frame hitches. Load configs in OnInit() or OnMissionStart(), never in OnUpdate().
  • Server/Client: File writes are restricted to $profile: and $saves:. On clients, $profile: points to the client profile directory. On dedicated servers, it points to the server profile. $mission: is typically read-only on both sides.

Observed in Real Mods

These patterns were confirmed by studying the source code of professional DayZ mods.

PatternModFile/Location
MakeDirectory chain + FileExist check + LoadFile with fallback to defaultsExpansionSettings manager (ExpansionSettings)
CopyFile backup before config saveCOTPermission file management
FindFile/FindNextFile to enumerate per-player JSON files in $profile:VPP Admin ToolsPlayer data loader
JsonSerializer.WriteToString() for RPC payload serialization (no file)Dabs FrameworkNetwork config sync

<< Previous: Timers & CallQueue | File I/O & JSON | Next: Networking & RPC >>

Released under CC BY-SA 4.0 | Code examples under MIT License