Database read ​

Once the game database has loaded, you can read a whole typed value or pick out a field whose type you know. To work with rows, look one up by its label or use a cursor to walk a table. You use the same dotted paths as the database operations, hashed with fnv1a64, to identify what to read.

Headerswofty/db.hpp
Namespaceswofty::db
TableSwoftyApi::db (SwoftyDb)
Featuredb — stable (shared with the database write operations)
API version5
ThreadCaller thread

Check the swofty::db::Result returned by every call: ok, late, unavailable or invalid. The result codes explain each one.

Strings ​

The game stores database text XOR-encoded (GdbStringRecord, key k_Gdb_StringXorKey). From core 0.2.11, string_at, the s of a string get or cell, and row_name return the decoded text: printable UTF-8 of at most 16 KiB, or no string. A record that does not decode gives invalid from string_at and a null s elsewhere. If the core cannot decode on this build (the key or the record layout is missing), string_at returns unavailable. Earlier cores returned the encoded bytes.

The core keeps one decoded copy per record, so a returned pointer normally stays valid for the whole session. That table holds at most 32768 strings (8 MiB); past that, a string's pointer stays valid only until the next db call on the same thread. Copy a string if you keep it.

Calls ​

CallReturns
available()true once the database has loaded
get(key, out) / get(hash, out)whole SwoftyDbValue by dotted key or pre-hashed key
int_at(key, out)one int64_t field
float_at(key, out)one double field
string_at(key, out)one const char * field
bool_at(key, out)one int32_t field
open(table, cursor)positions a cursor at the first row
row(table, label, cursor)positions a cursor at the row whose _metadata_ is label, among rows 0 to _<row>_instancecount_ - 1
step(cursor)advances; late when the table is exhausted
cell(cursor, field, out)one field of the cursor's current row
row_name(cursor)the current row's label (its _metadata_), or nullptr

When you read a whole SwoftyDbValue, its tag tells you which of i, f or s holds the value. The tag is a swofty::db::Tag: integer, real, string, enumeration, id or boolean. You own the SwoftyDbCursor, so zero-initialise it before calling open or row.

Example ​

Here's how you read one field, then walk a table to list every row:

cpp
#include <swofty/db.hpp>

namespace db = swofty::db;

SWOFTY_ON_LOAD
{
    if (!db::available())
        return;

    int64_t secs = 0;
    if (db::int_at("rules.stop_go_penalty.penalty_duration", &secs) == db::Result::ok)
        swofty::log("[my-addon] stop-go penalty is %lld s", (long long)secs);

    db::Cursor cur{};
    for (db::Result r = db::open("tracks", &cur); r == db::Result::ok; r = db::step(&cur))
        swofty::log("[my-addon] track %s", db::row_name(&cur));
}

SWOFTY_ADDON()

The loop prints the 43 rows of tracks. Name your plugin in operations.native:

json
"operations": {
  "native": [{ "dll": "native/my_plugin.dll" }]
}

Look up dotted keys in the game database reference. For a row count, use <container>._<Row>_instancecount_.

Availability ​

The db-read engine lane hasn't landed yet, so every call on current builds returns unavailable. Guard every call with available(). See the native-libraries design.

Publishing