A read-only investigation found a compressed SQLite database linking inventory, machinery, construction, progression, and simulated Landsraad competition.
We wanted to answer an ordinary question: what does Dune: Awakening actually keep on your PC when you play solo? The inspected format exposed 94 application tables, relationships between storage and items, production state, and 60 simulated guild records divided evenly between Atreides and Harkonnen.
Comparing save copies also showed that contributions associated with simulated guilds can change. The political competition has queryable persisted state, alongside the systems that track equipment and production.
Funcom already describes simulated guild competition in its single-player announcement. Our contribution is a reproducible look at how that feature appears in one player’s persisted data, and what useful tools that access could support.
What we examined
We inspected copies of PC Steam single-player saves with the game closed. All database processing happened in memory, and the source files were left unchanged. This article reports the file format and data relationships; individual gameplay records and session metadata are excluded.
Current, autosave, and prepatch database samples opened successfully and returned ok from SQLite’s quick_check. That checks database structure; it does not certify every gameplay relationship. The findings come from a limited sample and may change with game updates.
The file called game.db has an extra layer
On Windows, open this folder in File Explorer:
%LOCALAPPDATA%\DuneSandbox\Saved\Cloud
From there, open PlayerClientStorage → FLS_retail → your Steam account folder. The save file is game.db. No real account identifier is shown here.
Beside it, we found autosave, game_prepatch.db, and SOLO. The last held smaller character-display and exploration files, including Level.json, WornItems.json, and Fog of War data.
Opening game.db directly as SQLite misses a wrapper. The observed layout was:
| Offset | Bytes | Observation |
|---|---|---|
| 0 | 4 | Little-endian unsigned value 1; its exact meaning is unconfirmed |
| 4 | 4 | Little-endian unsigned decompressed byte count |
| 8 | Remainder | A zlib stream containing the SQLite database |
Decompression produced the standard SQLite database signature, SQLite format 3 followed by a null byte. The wrapper’s byte count matched the decompressed length in the examined files. This remains an observed format, not a vendor-supported contract.
What became readable
| Data | Readable structure |
|---|---|
| Character progression | XP, skill-point fields, and ability tags |
| Research | Technology points and purchased/unpurchased states |
| Inventory | Item stacks linked to inventories and their owners |
| Construction | Building records, structural pieces, and placeables |
| Production | Machine inventories, fuel state, stored water, and crafting queues |
| Exploration | Position fields, markers, and discovery-state associations |
| Story | Journey-node conditions and state records |
| Politics | 60 simulated guild records, faction membership, and contributions |
Counts need interpretation. Item records can represent stacks or entries such as emotes. Journey-node records include unreached content. Marker associations have discovery-level and discovery-method fields; their count is not a count of visited locations.
Some values live in ordinary columns; others are nested in binary JSON. SQLite’s JSONB support lets json() turn those blobs into readable JSON. Actor properties and linked component entities exposed skills, health, research, and machinery state without a custom binary decoder.
What an inventory query could actually do
The useful relationship is items.inventory_id to inventories.id, followed by the inventory’s owning actor. Player-set container labels can be resolved through permission_actor. Crafting components add recipe requests and ingredient allocations.
For an illustrative report, imagine a material listed under Container A, with another quantity reserved by Machine B. Those labels are fictional. The point is the relationship: a reader could distinguish carried stock, stored stock, and inputs already allocated to production.
That is enough to make a practical inventory locator plausible. It could answer where a material is stored and whether production already has inputs allocated. A general crafting planner would need more: validated recipe coverage, readable item names, and careful treatment of reserved materials.
The need is recognizable in community discussion about storage management: players describe searching across many containers and ask for a central inventory view. That discussion concerns the broader game; a local-save reader would address the searchable-storage part for PC single-player saves.
The 60-guild finding, checked across saves
Joining landsraad_simulated_guilds to player_faction and factions produced 30 Atreides records and 30 Harkonnen records. All 60 guild identifiers were distinct.
We then joined landsraad_task_player_contributions back to those simulated guild actors. Comparing the same relationships across save copies showed changes in their contribution state. Personal session timestamps and progression values are omitted.
The amount field represents internal contribution units. Interpreting it as XP, currency, or a rate would need additional evidence. The comparison cannot establish progression while the game was closed. Guild records also cannot tell us how many physical NPCs exist or reveal the simulation algorithm.
What the comparison establishes is narrower and interesting: the saved competition contains changing contributions linked to simulated competitors.
Reproduce the first query without editing your save
Close the game and use a separate copy of your own game.db. This compact example needs Python 3.11 or later with Connection.deserialize available and a linked SQLite version supporting the save; our run used Python 3.12 and SQLite 3.45.1. Reading JSONB requires SQLite 3.45 or newer. See the official Python SQLite API and zlib API.
Save this snippet as inspect_save.py and run python3 inspect_save.py /path/to/copy/game.db:
from pathlib import Path
import sqlite3, struct, sys, zlib
with Path(sys.argv[1]).open("rb") as source:
packed = source.read(32 * 1024 * 1024 + 1)
if not 8 < len(packed) <= 32 * 1024 * 1024:
raise ValueError("Input exceeds this example's size limits")
tag, expected = struct.unpack("<II", packed[:8])
if tag != 1 or not 0 < expected <= 128 * 1024 * 1024:
raise ValueError("Unrecognized wrapper or unsupported size")
stream = zlib.decompressobj()
raw = stream.decompress(packed[8:], expected + 1)
if len(raw) != expected or not stream.eof or stream.unused_data:
raise ValueError("Compressed stream does not match wrapper")
if not raw.startswith(b"SQLite format 3\0"):
raise ValueError("Decompressed payload is not SQLite")
db = sqlite3.connect(":memory:")
try:
db.deserialize(raw)
db.execute("PRAGMA query_only=ON")
db.execute("PRAGMA trusted_schema=OFF")
if db.execute("PRAGMA quick_check").fetchall() != [("ok",)]:
raise ValueError("Database check failed")
print(db.execute("""
SELECT f.name, count(*)
FROM landsraad_simulated_guilds AS g
JOIN player_faction AS p ON p.actor_id = g.guild_actor_id
JOIN factions AS f ON f.id = p.faction_id
GROUP BY f.name ORDER BY f.name
""").fetchall())
finally:
db.close()
Our output was [('Atreides', 30), ('Harkonnen', 30)]. This example uses fixed queries and emits faction counts. It does not export account records or write a database file. Future builds may require different handling.
Where this could lead
A small open-source reader could answer “where did I leave that material?” through a local web interface or an assistant backed by fixed queries. It would report the container and quantity in the latest loaded save, with unknown item names clearly marked.
Comparing retained snapshots could also support a daily progress report, optionally narrated as a field journal. A change in stored resources is a net difference, not proof of how much was gathered; a new structure does not reveal the events surrounding its construction. Any narrative would need to stay within those limits. Machine status looks promising too, although interpreting timers requires understanding the game’s clocks and offline simulation.
Those are candidate projects, not released features. An honest reader should preserve internal IDs when display names are unknown, distinguish observed state from inferred meaning, and keep saves local by default.
For now, the result is already useful: our solo save exposes enough structured state to ask specific questions about supplies, progression, construction, and the political simulation surrounding our character.
