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

System & File Utilities

Timing, debug output, error codes, memory helpers, directory and file operations, environment variables, the WinMain entry-point bridge, and the transparent fopen path normaliser.

Timing & synchronisation

FunctionHeaderStatusDescription
Sleep(DWORD dwMilliseconds) synchapi.h IMPLEMENTED Suspends the calling thread for the specified number of milliseconds. Implemented via POSIX usleep(ms * 1000). A value of 0 yields the thread.
GetTickCount(void) sysinfoapi.h IMPLEMENTED Returns milliseconds elapsed since the process started, via SDL_GetTicks(). Wraps after ~49.7 days (32-bit overflow). For timing deltas, always subtract two values.
C++ — timing patterns
// Frame-rate limiter: cap at 20 fps
DWORD frameStart = GetTickCount();
GameUpdate();
GameRender();
DWORD elapsed = GetTickCount() - frameStart;
if (elapsed < 50) Sleep(50 - elapsed);   // wait remainder of 50 ms frame

// Measure elapsed time
DWORD t0 = GetTickCount();
DoWork();
DWORD ms = GetTickCount() - t0;
printf("Took %lu ms\n", (unsigned long)ms);

// Simple delay
Sleep(200);   // pause 200 ms
      

Debug output

Signature
void WINAPI OutputDebugString(LPCSTR lpOutputString);

On Free API, OutputDebugString is implemented via SDL_Log, so output appears in the terminal / logcat depending on platform. On a real Windows build with a debugger attached it would also appear in the debugger output window.

C++ — debug logging
OutputDebugString("Game initialised\n");

// Build a formatted string first (OutputDebugString has no printf formatting)
char buf[256];
snprintf(buf, sizeof(buf), "Score: %d, Level: %d\n", score, level);
OutputDebugString(buf);

// Or use wsprintfA (Win32 printf variant, 1024-byte buffer)
wsprintfA(buf, "HP: %d/%d", hp, maxHp);
OutputDebugString(buf);
      

Error codes

FunctionStatusDescription
GetLastError(void) PARTIAL Returns the last error code set for the calling thread. In Free API, this mirrors errno after POSIX calls. Not all Free API functions set the last error.
SetLastError(DWORD dwErrCode) PARTIAL Sets the thread-local last error code.
C++ — checking for errors
if (!CreateDirectoryA("saves", NULL)) {
    DWORD err = GetLastError();
    char buf[128];
    snprintf(buf, sizeof(buf), "CreateDirectory failed, error %lu\n",
             (unsigned long)err);
    OutputDebugString(buf);
}
      

Memory macros & GlobalMemoryStatus

Memory macros (HEADER_ONLY)

MacroExpands toDescription
ZeroMemory(dst, len)memset(dst, 0, len)Fill a buffer with zeros. Equivalent to C++ value-initialisation.
FillMemory(dst, len, fill)memset(dst, fill, len)Fill a buffer with a byte value.
CopyMemory(dst, src, len)memmove(dst, src, len)Copy bytes; memmove handles overlapping regions correctly.
C++ — memory helper examples
WNDCLASSA wc;
ZeroMemory(&wc, sizeof(wc));   // clear struct (same as = {})

char buf[256];
FillMemory(buf, sizeof(buf), 0xFF);  // fill with 0xFF

BYTE src[64], dst[64];
CopyMemory(dst, src, sizeof(src));  // copy 64 bytes
      

GlobalMemoryStatus

Signature
void WINAPI GlobalMemoryStatus(LPMEMORYSTATUS lpBuffer);

Fills a MEMORYSTATUS structure with approximate memory statistics. On Linux, reads /proc/meminfo. On other platforms, values are approximations. Do not rely on these values for precise resource decisions.

C++ — check available memory
MEMORYSTATUS ms;
ms.dwLength = sizeof(ms);
GlobalMemoryStatus(&ms);

// dwMemoryLoad: 0-100% memory load estimate
// dwTotalPhys / dwAvailPhys: total and free physical RAM in bytes
printf("RAM load: %lu%%  Free: %lu MB\n",
       (unsigned long)ms.dwMemoryLoad,
       (unsigned long)(ms.dwAvailPhys / (1024*1024)));
      

File & directory operations

FunctionStatusDescription
CreateDirectoryA(path, sa) PARTIAL Creates a single directory. Path is normalised (drive letter removed, backslashes → slashes, leading slashes stripped). Calls mkdir(path, 0755). Treats EEXIST as success. Does not create missing parent directories. Security attributes (sa) are ignored.
RemoveDirectoryA(path) PARTIAL Calls POSIX rmdir. Fails if the directory is not empty.
DeleteFileA(path) PARTIAL Calls POSIX remove. Returns TRUE on success. No backslash normalisation applied.
SetEnvironmentVariableA(name, value) PARTIAL Calls POSIX setenv(name, value, 1). Pass NULL for value to unset.
C++ — directory and file operations
// Create save directory (safe to call if already exists)
CreateDirectoryA("saves", NULL);

// Create with Windows-style path (drive + backslashes)
CreateDirectoryA("C:\\MyGame\\Saves", NULL);
// → internally treated as "MyGame/Saves" relative to CWD

// Delete a save file
if (!DeleteFileA("saves/slot1.sav")) {
    OutputDebugString("Delete failed\n");
}

// Set a custom SoundFont path at runtime
SetEnvironmentVariableA("FREE_API_SOUNDFONT", "/usr/share/sounds/sf2/FluidR3_GM.sf2");
      

Low-level file I/O (_lopen / _lread / _lclose)

FunctionStatusDescription
_lopen(path, iReadWrite)PARTIALOpens file read-only via POSIX open(O_RDONLY). The iReadWrite mode is ignored — only reading is supported. Returns a file descriptor (int), or -1 on error.
_lread(hFile, buf, n)PARTIALReads up to n bytes into buf via POSIX read. Returns bytes actually read, or HFILE_ERROR on error.
_lclose(hFile)PARTIALCloses the file descriptor via POSIX close.
C++ — _lopen / _lread pattern
int fh = _lopen("data/level01.dat", OF_READ);
if (fh == -1) { OutputDebugString("open failed\n"); return; }

BYTE header[16];
UINT read = _lread(fh, header, sizeof(header));
// ... process data ...

_lclose(fh);
      

Win32 resource stubs

Win32 embedded resources (icons, strings, bitmaps compiled into the EXE) are not supported. All resource functions compile cleanly and return NULL/0 so that code using them does not crash.

FunctionStatusReturns
GetModuleHandleA(LPCSTR name)STUBNULL
FindResourceA(hMod, name, type)STUBNULL
LoadResource(hMod, hRes)STUBNULL
LockResource(hRes)STUBNULL
SizeofResource(hMod, hRes)STUB0
FreeResource(hRes)STUBFALSE
UnlockResource(hRes)STUBFALSE
LoadStringA(hInst, uID, buf, cch)STUB0

WinMain bridge

Win32 games use WinMain instead of main. Free API provides a macro and a helper function to bridge the two.

SymbolStatusDescription
FREE_API_IMPLEMENT_WINMAIN() PARTIAL C++ macro that generates a main(int argc, char** argv) which calls FreeApiRunWinMain(&WinMain, argc, argv). Only defined when _WIN32 is not defined (non-Windows builds). On Windows, the real WinMain startup code is used instead.
FreeApiRunWinMain(entryPoint, argc, argv) PARTIAL Sets _pgmptr = argv[0], builds lpCmdLine by joining argv[1..] with spaces, then calls entryPoint(NULL, NULL, lpCmdLine, SW_SHOW).
_pgmptr PARTIAL Declared extern char* on non-Windows. Set to argv[0] by FreeApiRunWinMain. Some legacy games read this to locate their executable directory.
C++ — usage of FREE_API_IMPLEMENT_WINMAIN
#include <windows.h>

// This macro generates:
//   int main(int argc, char** argv) {
//       return FreeApiRunWinMain(&WinMain, argc, argv);
//   }
// Followed immediately by the WinMain body you provide.

FREE_API_IMPLEMENT_WINMAIN()
{
    // hInstance   = NULL (not needed)
    // hPrevInstance = NULL
    // lpCmdLine   = command-line args joined by spaces
    // nCmdShow    = SW_SHOW (5)

    WNDCLASSA wc = {};
    wc.lpfnWndProc   = MyWindowProc;
    wc.lpszClassName = "GameWindow";
    RegisterClass(&wc);

    HWND hwnd = CreateWindowA("GameWindow", "My Game",
        WS_OVERLAPPEDWINDOW | WS_VISIBLE,
        100, 100, 800, 600, NULL, NULL, hInstance, NULL);

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (int)msg.wParam;
}
      
Manual bridge If you need to keep main() separate (e.g. for SDL3 Android support), call FreeApiRunWinMain directly and forward-declare WinMain:
C++ — manual bridge in a separate translation unit
#include <windows.h>

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);  // forward declaration

int main(int argc, char** argv)
{
    return FreeApiRunWinMain(&WinMain, argc, argv);
}
      

fopen path-normalisation wrapper

Legacy Win32 games hard-code backslash paths and sometimes include a Windows drive letter. This wrapper is applied transparently via a #define fopen in every C++ translation unit that includes <windows.h>.

The wrapper performs these steps in order:

  1. Remove Windows drive letter — "C:\path""\path"
  2. Convert all \ to /
  3. Strip any remaining leading slashes so the path is relative
  4. Call ::fopen(normalized_path, mode)
  5. On failure, retry with the basename uppercased (case-sensitive filesystem fallback)
C++ — all these paths work on Linux thanks to the wrapper
// Original Win32 game code — unchanged, works on Linux:
FILE* f1 = fopen("data\\config.def", "r");
// → opens "data/config.def"

FILE* f2 = fopen("C:\\MyGame\\DATA\\LEVEL01.BLP", "rb");
// → tries "MyGame/DATA/LEVEL01.BLP"
// → if missing, retries "MyGame/DATA/level01.BLP" (uppercase fallback)

FILE* f3 = fopen("PLANET\\data\\map.blp", "rb");
// → opens "PLANET/data/map.blp"
      
C-only files The fopen wrapper is a C++ inline function and only applies to files compiled as C++. Pure C translation units that include <windows.h> do not get the wrapper. In practice, all game files are C++.