Skip to content

Import book texts

moongate-convert modernuo-books reads the static book texts shipped in ModernUO and writes Moongate readable document templates. It parses C# syntax without compiling or running emulator code. These documents are books: they open the client’s own book, read only, each with the cover of its source.

Terminal window
cd tools/convert
uv run moongate-convert modernuo-books \
--source <ModernUO>/Projects/UOContent \
--destination ../../moongate_root/templates/books/modernuo

The tool is the Python package in tools/convert (it needs uv and Python 3.13 or later); it does not check that a book fits the client’s packets, which the server’s own tests of the shipped catalog do. --source is a folder, searched recursively for .cs files; use the full Projects/UOContent folder to include the Khaldun journals as well as the library. --destination is the folder receiving the generated TOMLs. Source folders/files and output files that are symbolic links are refused.

The shipped catalog contains 62 books, 738 source pages and 5,635 source lines, converted from ModernUO revision 35e3a31b.

Source under Projects/UOContent Books Source pages
Items/Books/Defined/LibraryBooks.cs 28 486
The other six Items/Books/Defined/*.cs files 6 88
Engines/Khaldun/Books/GrimmochJournal.cs 9 46
Engines/Khaldun/Books/LysanderNotebook.cs 6 33
Engines/Khaldun/Books/TavarasJournal.cs 13 85

The six individual definitions are BlackthornWelcomeBook, DrakovsJournal, FropozJournal, KaburJournal, NewAquariumBook and TranslatedGargoyleJournal. The source repository’s license and existing narrative authorship remain upstream provenance. No source code is copied into the converter.

Each static readonly BookContent field supplies its title, author and ordered BookPageInfo lines. Literal strings, including C# escaped/verbatim/raw strings and literal concatenation, are supported. Runtime expressions, missing metadata/pages, invalid text, duplicate IDs and text that cannot fit the current reading packets reject the conversion with exit code 2. All parsing, validation and serialization finish before any output file is changed. A filesystem failure during the writes can leave some files updated; correct the reported path and rerun.

A class becomes a stable <snake_case_class>.toml filename. For example, GrammarOfOrcish becomes grammar_of_orcish. Titles are preserved and are never used for deduplication: the Grimmoch, Lysander and Tavara installments remain distinct. Leading spaces, spelling and annotations stay as written. Lines are joined with a newline and source pages with two newlines; an empty line inside a ModernUO page is written as a line of one space. This preserves the source page breaks in the stored text, including empty source pages, but displayed pagination follows Moongate’s native book limits: 79 UTF-16 code units per line and 8 lines per page. Longer lines wrap and longer source pages continue onto another displayed page. Trailing newlines are trimmed when opening a book, so final empty source pages are not displayed. For example, children_tales_vol2 has 10 source pages and displays 9; the shipped English catalog displays 737 pages in total.

The UTF-8 TOML body is multiline when that representation preserves the text exactly; leading newlines and CR/CRLF sequences use escaped basic strings when needed. Invalid Unicode is rejected, and serialized fields are checked by deserializing them before writing. The importer validates the escaped source limit and doubles literal $ characters so the template formatter displays them literally. Imported books require no variable values or attachments and use item_template = "readable_book".

The cover is the graphic the class gives its base constructor, written as item_id: a literal (base(0xFF2, false)), the first of a random pair (Utility.Random(0xFEF, 2) gives 0x0FEF), or that of the kind of book the class derives from (RedBook, BlueBook, BrownBook, TanBook). A class that tells none gets no item_id and shows the red cover of readable_book. The shipped catalog has 28 brown, 31 red and 3 blue books. A text that needs more than 255 pages of 8 lines, in English or in a kept translation, rejects the conversion.

Each shipped book keeps its English source and includes complete title/body overrides for Italian (ita), French (fre), German (ger), Spanish (spa), Portuguese (por), Polish (pol) and Czech (cze). Author names remain unchanged. The existing document service chooses [localization].language when creating the item; missing translation fields fall back to the English source. Changing that setting does not rewrite issued books.

Rerunning replaces matching generated filenames, keeps unrelated files such as the welcome letter, and does not remove files from an older catalog. Put custom edits in a differently named template if they must survive reruns.

Existing translations.<language> fields in matching generated files are preserved when reimporting. All eight supported language codes, including optional eng overrides, are accepted; partial overrides use the refreshed English fields as fallback. The importer validates the existing TOML, Unicode, literal template text, source/rendered bounds and reading packets before writing any file. Invalid translations stop conversion with exit code 2 and leave existing output unchanged. Other edited fields are regenerated from ModernUO; unrelated files remain untouched. Existing translations are retained verbatim when upstream English changes, so review their meaning after importing a different upstream revision. No network translation service runs during import.

The shipped translations follow the English source page breaks and use the width of the book’s longest English line as a wrapping target. An unbreakable word can exceed that target. They retain the four-space indent where the English page opens a paragraph. A translated source page may need more lines and displayed pages than its English counterpart; the same native book pagination limits apply when it is opened.

The ordinary mgctl init workflow copies missing shipped files into existing roots and preserves files already present. Newly added sources load at the next normal startup. For an existing root whose catalog predates these translations, copy the desired [translations.<language>] tables from the shipped catalog into its matching files. Neither mgctl init nor a reimport invents missing translations in existing files. A root that holds the catalog under its first names, modernuo_<book>.toml, gets the books again under the names without the prefix: delete the modernuo_*.toml files of templates/books/modernuo by hand, or every book is there twice, and use the new ids in book.give and .book. Existing issued documents keep their saved text. No converter connects to the world database or restarts the server.

To create a readable copy from Lua:

book.give(player, "grammar_of_orcish")

The comparison inspected local emulator checkouts and official distribution repositories. ModernUO was selected because its entire static catalog is literal and already fits the Moongate converter workflow. Alternative catalogs overlap but have distinct variants; this command imports ModernUO alone.

Emulator Actual source of the texts Findings
ModernUO Server C# BookContent definitions 62 literal books / 738 pages; complete shipped import
ServUO Server C# definitions 74 definitions / 856 pages: 72 static after title fallback, two fishing guides have runtime location text. Other localized books use client Cliloc strings
UOX3 Server DFN catalog 65 effective books; CP1252 punctuation, repeated sections with last-definition precedence and page-count anomalies
POL ModernDistro / ClassicDistro packages, not the core checkout 46 books in each catalog; p<page>l<line> keys, blank-line padding and different variants
Sphere Scripts-X sp_tm_book.scp, not Source-X itself 45 books / 536 defined page sections; malformed header, missing/out-of-range pages and metadata discrepancies

Player-written world-save books, blank books, skill-teaching scripts and Cliloc-only documents are outside this static lore import.

Made with ❤️ by Tom

Buy me a coffee