# Opening a Nonemm log in N1MM Checked on 2026-08-27 against N1MM Logger+ 1.0.11031 by writing a log here, copying it to a Windows machine and opening it there. N1MM read the contest, its categories and the contacts. Three things had to be right, and none of them is obvious from the schema. ## The `Contest` table needs a row for the contest `ContestInstance` says which contest a log is; the `Contest` table says what that contest *is*. N1MM reads it in `Contest.FromRow` when it opens a log and throws `InvalidOperationException: No current row` if the row is missing, which reaches the operator as "A runtime error occurred". So the definition row is written whenever a contest is opened, not only when it is created — a log made before this was understood gets its row the next time it is opened. `ContestDefinitions.For` builds the row from the contest's own rules: the display and Cabrillo names, the mode, the dupe type and the multiplier names. ## The overlay category cannot be empty An empty `ContestInstance.OverlayCategory` is answered with "Invalid Overlay Category:" and the log will not open. An entry with no overlay says `N/A`. N1MM's list is not the Cabrillo specification's list. N1MM offers: N/A, ROOKIE, BAND-LIMITED, TB-WIRES, OVER-50, HQ, NOVICE-TECH, EXPERT so that is what the contest dialog offers. ## The sent exchange omits the report N1MM's contest dialog says "Omit RST: CQWW: 05". `SentExchange` for CQ WW is `14`, not `599 14`; for a serial number contest it is `001`. The report is fixed for the whole contest and the entry window fills it in per contact. ## Getting the schema in the first place N1MM ships no template database. `ham.s3db` is built at run time by applying SQL migration files that N1MM writes out from string resources inside `N1MMLogger.net.exe`. To read them: decompile the executable, parse `N1MMLogger.Net.Resources.resx`, and take the untyped `` entries named `DXLogDDL_0001_initial_schema_and_data` through `DXLogDDL_0004_updates`. The QSO table is `DXLOG` and the current `PRAGMA user_version` is 4. `src/Nonemm.Storage/Schema.sql` is that schema, flattened to the state version 4 leaves behind. ## One thing to avoid Do not replace the database file under a running N1MM. It does not notice and throws on the next read. ## A contact belongs to a ContestNR, not to the table key `ContestInstance` has two numbers: `ContestID`, the table's primary key, and `ContestNR`. `DXLOG.ContestNR` refers to the second one. N1MM's `ContestInstance.SQLWhereString` reads: " ContestNR = " + this.ContestNR + " " In a fresh log the two numbers match, so keying on either works. In a log N1MM has been using for a while they drift apart. In one real log of 56284 contacts, joining `DXLOG.ContestNR` to `ContestInstance.ContestID` disagreed with the contact's own `ContestName` 52029 times; joining it to `ContestInstance.ContestNR` disagreed 109 times. So `ContestInstance.ContestNumber` here is `ContestNR`, and `ContestID` is allocated separately when a contest is created. ## N1MM names a contest per mode CQ WW is `CQWWCW`, `CQWWSSB` or `CQWWRTTY`, never plain `CQWW`. The full list lives in the `Contest` table of any N1MM database. Nonemm writes the same names, and `N1mmContestNames` turns one back into a contest and a mode when a log is opened. The name carries the mode, so it beats whatever `ModeCategory` says. Older N1MM versions wrote the CW running of CQ WW as plain `CQWW`. That name is still in the registry, so those logs open.