diff --git a/src/AcDream.Core/Journal/JournalFile.cs b/src/AcDream.Core/Journal/JournalFile.cs new file mode 100644 index 00000000..6cd08331 --- /dev/null +++ b/src/AcDream.Core/Journal/JournalFile.cs @@ -0,0 +1,219 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; + +namespace AcDream.Core.Journal; + +/// The outcome of reading a journal file. +/// The pages read, oldest first. +/// +/// Retail's own message when the file is unusable, or null on success. An +/// ABSENT file is success with no pages, not an error — a character who has +/// never written a page has no file. +/// +public readonly record struct JournalReadResult( + IReadOnlyList Pages, + string? Error); + +/// +/// Retail's journal file — gmJournalUI::LoadPages @0x00496AC0 and +/// SavePages @0x00497270. +/// +/// +/// +/// A plain tagged text file, one tag per line, <NEWP> opening each +/// page. Retail writes it with fopen mode w+ — the whole file is +/// rewritten, never appended. +/// +/// +/// A file that does not OPEN with a page marker is refused outright, with its +/// own message. That is retail being strict about a file the player could have +/// hand-edited, and it is ported rather than softened: silently accepting a +/// malformed file would scatter the first page's text into no page at all. +/// +/// +public static class JournalFile +{ + private const string NewPage = ""; + private const string PageNumber = ""; + private const string Label = ""; + private const string Title = ""; + private const string Notes = ""; + private const string Days = ""; + private const string Hours = ""; + private const string Minutes = ""; + private const string LocationX = ""; + private const string LocationY = ""; + private const string RunningTime = "