Index Types & Handles Window & Input GDI System & Files File I/O Multimedia

Legacy File I/O & Directory Operations

MSVC CRT-compatible file search API (_findfirst / _findnext / _findclose), POSIX access-mode constants, and directory navigation helpers from direct.h (_chdir, _getcwd, _mkdir).

Include separately <io.h> and <direct.h> are not pulled in by <windows.h>. Include them explicitly: #include <io.h> and #include <direct.h>.

_finddata_t — file search result

The structure filled by _findfirst and _findnext to describe each matching file.

Structure definition
struct _finddata_t {
    unsigned attrib;       // file attributes (_A_NORMAL, _A_SUBDIR, …)
    time_t   time_create;  // creation time (may be 0 if unavailable)
    time_t   time_access;  // last access time
    time_t   time_write;   // last modification time
    long     size;         // file size in bytes
    char     name[260];    // file name (without path), null-terminated
};

#define _MAX_FNAME  260   // maximum file name length including null
      
FieldDescription
attribBitmask of file attributes. _A_SUBDIR (0x10) is set for directories. _A_NORMAL (0x00) for regular files.
time_createFile creation time as time_t. May be 0 on Linux (creation time not stored in most file systems).
time_accessLast access time.
time_writeLast modification time. Most reliable for sorting by recency.
sizeFile size in bytes. Only meaningful for regular files.
nameFile name without directory path, null-terminated. Maximum 259 chars + null.

_findfirst / _findnext / _findclose

Signatures
intptr_t _findfirst(const char* filespec, struct _finddata_t* fileinfo);
// Returns a search handle, or -1 on failure (no match or error).

int _findnext(intptr_t handle, struct _finddata_t* fileinfo);
// Returns 0 on success (next file found), -1 when no more files.

int _findclose(intptr_t handle);
// Always call this to release the handle. Returns 0 on success.
      

Implementation: backed by POSIX opendir / readdir and fnmatch for pattern matching. The filespec path may use Windows backslashes — they are normalised to slashes internally. Wildcard characters * and ? are supported in the file-name part.

STUB status The current Free API marks _findfirst, _findnext, and _findclose as STUB in io.h — they return -1/failure. The implementation in src/crt_io.cpp wraps POSIX readdir. If you need directory listing, test with a real build and check whether your target platform has the implementation compiled in.
C++ — list all .blp files in a directory
#include <windows.h>
#include <io.h>

void ListBlupiFiles(const char* dir)
{
    char pattern[MAX_PATH];
    snprintf(pattern, sizeof(pattern), "%s/*.blp", dir);

    struct _finddata_t fd;
    intptr_t handle = _findfirst(pattern, &fd);

    if (handle == -1) {
        OutputDebugString("No .blp files found\n");
        return;
    }

    do {
        if (!(fd.attrib & _A_SUBDIR)) {   // skip sub-directories
            char msg[MAX_PATH + 16];
            snprintf(msg, sizeof(msg), "  Found: %s (%ld bytes)\n",
                     fd.name, fd.size);
            OutputDebugString(msg);
        }
    } while (_findnext(handle, &fd) == 0);

    _findclose(handle);
}
      
C++ — collect all level files into a vector
#include <vector>
#include <string>
#include <io.h>

std::vector<std::string> FindLevels(const char* basePath)
{
    std::vector<std::string> result;
    char pattern[512];
    snprintf(pattern, sizeof(pattern), "%s/level*.blp", basePath);

    struct _finddata_t fd;
    intptr_t h = _findfirst(pattern, &fd);
    if (h == -1) return result;

    do {
        if (!(fd.attrib & _A_SUBDIR))
            result.push_back(std::string(basePath) + "/" + fd.name);
    } while (_findnext(h, &fd) == 0);

    _findclose(h);
    return result;
}
      

access() / _access() — check file existence

ConstantValueMeaning
F_OK0File exists.
X_OK1Execute permission.
W_OK2Write permission.
R_OK4Read permission.

On non-Windows platforms, the system access() function is already available. On Windows builds, Free API provides a forward declaration of _access() that resolves against the MSVCRT.

C++ — check file existence before loading
#include <io.h>
#if defined(_WIN32)
#  define file_exists(p) (_access((p), F_OK) == 0)
#else
#  define file_exists(p) (access((p), F_OK) == 0)
#endif

if (file_exists("saves/autosave.sav")) {
    LoadGame("saves/autosave.sav");
}

// Check whether a SoundFont exists
const char* sf = "assets/soundfont/default.sf2";
if (!file_exists(sf)) {
    OutputDebugString("WARNING: SoundFont not found — music will be silent\n");
}
      

Directory navigation (_chdir, _getcwd, _mkdir)

FunctionStatusDescription
_chdir(const char* path) PARTIAL Changes the current working directory. Wraps POSIX chdir(path). Returns 0 on success, -1 on failure (sets errno). Path normalisation (backslash → slash) is applied.
_getcwd(char* buf, int maxlen) PARTIAL Gets the current working directory into buf (up to maxlen chars). Wraps POSIX getcwd. Returns buf on success, NULL on failure.
_mkdir(const char* path) PARTIAL Creates a directory with permissions 0755. Wraps POSIX mkdir(path, 0755). Returns 0 on success, -1 on failure. Does not create intermediate directories.
C++ — directory navigation
#include <windows.h>
#include <direct.h>

// Get and print current directory
char cwd[MAX_PATH];
if (_getcwd(cwd, sizeof(cwd))) {
    char msg[MAX_PATH + 16];
    snprintf(msg, sizeof(msg), "Working dir: %s\n", cwd);
    OutputDebugString(msg);
}

// Navigate to the game data directory
char dataDir[MAX_PATH];
snprintf(dataDir, sizeof(dataDir), "%s/data", cwd);

if (_chdir(dataDir) != 0) {
    OutputDebugString("Cannot change to data directory\n");
}

// Create a save sub-directory
if (_mkdir("saves") != 0 && errno != EEXIST) {
    OutputDebugString("Failed to create saves/\n");
}
      
Difference from CreateDirectoryA _mkdir (from direct.h) and CreateDirectoryA (from winbase.h) both create a single directory. The difference: _mkdir returns an int and sets errno; CreateDirectoryA returns BOOL and sets the Win32 last-error code. Either may be used; CreateDirectoryA also normalises backslash paths.
C++ — ensure a directory tree exists
// Free API does not create parent directories automatically.
// Create them one level at a time:
void EnsurePath(const char* path)
{
    char tmp[MAX_PATH];
    snprintf(tmp, sizeof(tmp), "%s", path);
    for (char* p = tmp + 1; *p; p++) {
        if (*p == '/' || *p == '\\') {
            *p = '\0';
            _mkdir(tmp);   // ignore errors (dir may already exist)
            *p = '/';
        }
    }
    _mkdir(tmp);
}

// Usage:
EnsurePath("saves/slot1/data");