acdream/docs/plans/2026-08-21-journal-campaign.md
Erik 45c964dbf0 docs(journal): plan Campaign QJ — the two tabs QT deliberately left inert
The Journal tab is not a quest feature at all: it is a per-character notebook,
entirely client-side, with no wire and no dat content. Each page holds a label,
a title, notes, a recorded location and a countdown timer, and the Page List
tab is a searchable index over them. It shares the panel with Contracts and
nothing else — retail's own naming, not a mix-up here.

The file format is recovered whole, including the detail that a journal file
which does not open with <NEWP> is REFUSED with its own error string, and the
authored max lengths (16 label / 32 title / 2048 notes) that the edit boxes
enforce.

One authored fact worth recording before anyone reads the layout as a bug: the
running-timer readout shares x=84 with the three day/hour/minute fields. That
overlap is the data form of ShowEditableTimer versus ShowRunningTimer — the
strip is either three editable numbers or one running readout, never both.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 15:26:20 +02:00

4 KiB

Campaign QJ — the Journal and Page List tabs

Status: ACTIVE 2026-08-21. Completes the panel Campaign QT mounted: QT shipped the Contracts tab and left the other two inert by design.

Scope: retail's gmJournalUI (element type 0x10000048, page 0x10000563) and gmPageListUI (type 0x10000049, page 0x10000564).

What this actually is

A per-character notebook, entirely client-side. No wire, no server involvement, no dat content — the player writes the pages. Each page carries a label, a title, free-form notes, a recorded LOCATION, and a countdown TIMER. The Page List tab is a searchable index over those pages.

Nothing about it depends on quests; it shares the panel with Contracts and nothing else. That it is called "Journal" while the panel is also called "Journal" is retail's own naming, not a mistake here.

Measured ground truth

The file format

gmJournalUI::SavePages @0x00497270 / LoadPages @0x00496AC0. A plain tagged text file, fopen mode w+. Both call sites pass the literal prefix "Journal"; the path template is %s%s-%s-%s.txt, i.e. {dir}Journal-{server}-{character}.txt.

<NEWP>            begins a page (a file that does not open with one is refused)
<PNUM> %d         page number
<LABE> %s         label      (authored max length 16)
<TITL> %s         title      (32)
<NOTE> %s         notes      (2048)
<DAYS> %d         timer days
<HOUR> %d         timer hours
<MINU> %d         timer minutes
<LOCX> %f         recorded location
<LOCY> %f
<TIME> %f         running-timer value

Retail's own load error, byte-decoded: "Problem loading journal: Your journal file does not create a new page!"

The Journal page (0x10000563)

Element Role
0x10000567 "New" button
0x10000569 label edit box (0x1E = 16)
0x1000056A / 0x1000056B "Title:" / title edit box (32)
0x1000056C / 0x1000056D "Notes:" / notes edit box (2048), scrollbar 0x1000056E
0x1000056F / 0x10000570 / 0x10000571 "First" / "~ 1 ~" / "Last"
0x10000572 / 0x10000573 / 0x10000574 "Location:" / "00.0S, 00.0W" / "Record"
0x10000575 "Timer:"
0x10000576 0x10000577 days field, "d"
0x10000578 0x10000579 hours field, "h"
0x1000057A 0x1000057B minutes field, "m"
0x1000057C running-timer text — OVERLAPS the three fields at x=84
0x1000057D "Start" button
0x10000566 bottom-right button (65x32)

0x1000057C sharing x=84 with the day/hour/minute fields is the authored form of ShowEditableTimer @0x00495770 vs ShowRunningTimer: the same strip is either three editable numbers or one running readout, never both.

The Page List page (0x10000564)

Element Role
0x1000057F 0x10000580 0x10000581 0x10000582 headers "#" / "Title" / "Timer" / "Label"
0x10000583 the list, scrollbar 0x10000584
0x10000585 "Delete"
0x10000586 / 0x10000587 / 0x10000588 "Search:" / search box / "Reset"

gmPageListUI::PageContainsString @0x00493B60 is the search predicate; CheckForDoubleClick @0x00493140 opens the page (gmJournalUI::GotoPage @0x00496430).

Slices

  • QJ1 — the page model and its file. JournalPage plus a faithful reader/writer for the tagged format, including retail's refusal of a file that does not open with <NEWP>. Pure; no UI, no state ownership.
  • QJ2 — the owner. RuntimeJournalState: the page collection, the current page, new/delete/goto, and the timer. Per-character.
  • QJ3 — the Journal page. Edit boxes, page navigation, Record, and the editable/running timer swap.
  • QJ4 — the Page List page. The list, the search, delete, and double-click-to-open.
  • QJ5 — persistence. Load on character enter, save on exit, under the client's own data directory.

Definition of done

  1. A page written in one session is there in the next.
  2. Every ported algorithm cites its retail address.
  3. Every slice has a test that would catch its regression.