Skip to content

Pure JS FFI alternative that fixes Delphi calling convention quirks (UCS-2, void Init/Close, WordBool, GetResultString) #26

@WingedGuardian

Description

@WingedGuardian

Background

While building a Claude Code modding toolkit for Skyrim VR, I needed a way to call XEditLib.dll from Node.js that didn't require a native build toolchain. The existing xelib native addon approach works inside Electron/zEdit, but falls apart as a standalone module outside that environment.

I spent considerable time debugging the Delphi calling convention quirks in XEditLib.dll and built a pure JS FFI wrapper using koffi. No compilation, no node-gyp, just npm install.

The Bugs / Quirks (for anyone who lands here)

These are not obvious and none are documented anywhere I could find:

1. All strings are UCS-2/UTF-16LE, not UTF-8
XEditLib uses Delphi PWideChar throughout. Passing UTF-8 strings causes silent failures.

// Correct encoding helper:
function wcb(s) { const b = Buffer.alloc((s.length+1)*2, 0); b.write(s, 0, 'ucs2'); return b; }

2. InitXEdit() and CloseXEdit() are VOID, not bool
Declaring them as returning bool (or any non-void type) corrupts the call stack for all subsequent calls. This one is particularly nasty -- everything appears to initialize fine, then fails mysteriously later.

3. WordBool is uint16 (2 bytes), not a 1-byte bool
Declaring boolean return types as bool or uint8 reads the wrong number of bytes off the stack.

4. String return pattern requires two calls
Functions don't return strings directly. They write a length to a PInteger out-parameter, then you call GetResultString(buffer, len) to retrieve the value:

function getString(fn) {
    const lenBuf = Buffer.alloc(4, 0);
    fn(lenBuf);
    const len = lenBuf.readInt32LE(0);
    if (len < 1) return '';
    const strBuf = Buffer.alloc(len * 2, 0);
    GetResultString(strBuf, len);
    return strBuf.toString('utf16le', 0, len * 2);
}

5. Game mode for Skyrim VR is gmSSE (4), not a VR-specific value
There is no VR game mode. XEditLib reads the game path from the SSE registry key regardless.

6. Registry key is Skyrim Special Edition, not Skyrim VR
HKLM\SOFTWARE\WOW6432Node\Bethesda Softworks\Skyrim Special Edition

Working Implementation

I've published a working pure JS wrapper with all 163 functions and correct signatures:

It's used in production in the Skyrim VR Claude Code Modding Toolkit.

Why an Issue Rather Than a PR

The pure JS / koffi FFI approach is architecturally different from the native addon approach in this repo -- it wouldn't fit as a patch. Filing this as an issue so anyone searching for these problems has a path to a working solution.

Happy to answer questions if anyone is trying to get XEditLib working outside of Electron.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions