Skip to content

Add an option to preserve value whitespace when loading .ini files - #9

Open
DeathFishAtEase wants to merge 1 commit into
SadPencil:mainfrom
DeathFishAtEase:fix/preserve-ini-value-whitespace
Open

DeathFishAtEase wants to merge 1 commit into
SadPencil:mainfrom
DeathFishAtEase:fix/preserve-ini-value-whitespace

Conversation

@DeathFishAtEase

@DeathFishAtEase DeathFishAtEase commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

The .csf format stores each label value as a length-prefixed character
sequence, so it performs no whitespace handling at all: a value starting with
spaces is perfectly valid, and it round-trips through CsfFile.WriteCsfFile()
-> CsfFile.LoadFromCsfFile() unchanged.

This tool, however, cannot produce such a file from an .ini source. A line like

[SOME_LABEL]
Value=    a

is always loaded as a, so the generated .csf contains a instead of a.

This blocks mods that need leading spaces (indented multi-line strings, column
alignment in in-game dialogs and sidebars).

Relationship with engine-side support

This PR only solves the authoring half of the problem: it makes it possible
to author strings with leading/trailing whitespace and to have them survive
into the generated .csf file byte-for-byte.

The rendering half is a separate concern that this PR cannot address. The
vanilla game engine trims such whitespace when displaying strings, so enabling
the option below changes nothing visible in-game on its own.

Runtime support requires the engine extension in
Phobos-developers/Phobos#2479, which is what makes the preserved
whitespace render as intended.

In short:

Concern Solved by
Authoring / generating a .csf that contains the spaces This PR
Rendering those spaces in the game Phobos-developers/Phobos#2479

Users should enable this option only if they ship that engine extension
too; otherwise the generated .csf differs from what the game actually displays.

smile

Root cause

CsfFileIniHelper.LoadFromIniFile() parses the file with
ini-parser-netstandard 2.5.2, which trims the leading and the trailing
whitespace of every parsed value. Upstream ini-parser exposes a
TrimProperties switch for this, but that netstandard fork dropped it -- its
IniParserConfiguration has no Trim* member at all, so the trimming cannot
be turned off from the outside.

Approach

Add an advanced-mode option, off by default, and only act when it is on:

  1. Read the raw file content, then let CsfFileIniHelper.LoadFromIniFile()
    parse it as usual. The official helper stays in charge of the header
    parsing/validation, the label discovery, the language and the key-case
    correction, so the resulting structure is identical to before.
  2. Recover the untrimmed text of every value line with a small line scanner
    and restore those values. The scanner only ever overrides values of
    labels that the helper already found, for exactly as many lines as the
    helper reported, so it can neither add labels nor change the structure.

Keeping it opt-in avoids silently changing the output for existing users. With
the option off, the generated .csf files are byte-for-byte what the current
release produces. Given that the vanilla engine does not render these spaces
anyway, defaulting to off is also the safer choice for users who do not use
the engine extension.

Notes

  • When enabled, trailing whitespace is preserved as well as the leading one,
    because the .ini file is meant to be a lossless representation of the string
    table.
  • The scanner is intentionally naive (no regex, no quoting, no multi-line
    values) because the .ini representation of this format never contains those
    constructs -- the files are written by the library itself.

Testing

  • dotnet build Ra2CsfToolsGUI.sln -c Release: 0 warnings, 0 errors.
  • Verified against SadPencil.Ra2CsfFile 2.2.5 that
    AddLabel("TEST", " a\n b") -> WriteCsfFile() -> LoadFromCsfFile()
    returns " a\n b" unchanged, confirming the .csf format itself is not
    the blocker.
  • Manual acceptance test performed on the real GUI workflow: with the option
    enabled, an .ini file containing Value= a produces a .csf file whose
    label value starts with spaces; with the option disabled, the output is
    unchanged. Values without leading/trailing whitespace are unaffected either
    way.

Alternatives considered

Adding a TrimProperties-like option to CsfFileOptions in
SadPencil.Ra2CsfFile would let this workaround be removed entirely. That
requires upstream cooperation and a coordinated dependency bump, so this PR
keeps the change contained to this repository instead.

The ini parser used by SadPencil.Ra2CsfFile (ini-parser-netstandard 2.5.2)
trims the leading and the trailing whitespace characters of every value, and
that fork exposes no configuration switch to turn the trimming off. As a
result, a line like 'Value=    a' was always loaded as 'a', so a .csf file
whose label values start with spaces could not be generated.

Add an advanced-mode option, off by default, that keeps the raw value of every
line and restores it right after the official helper has loaded the file. The
official helper still owns the header parsing, the label discovery and the
validation, so the loaded structure is not affected at all.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant