Names that break when a storage folder moves between Linux and Windows #14

Closed
opened 2026-10-07 07:08:32 +02:00 by kyuubi · 1 comment
Owner

Requirement: a storage folder copied between Linux and Windows keeps working.

On Linux the engine, and with it this mod, runs on native Linux .NET (only the renderer runs under Proton), so file names follow Linux rules there. Reproduced against the built mod on Linux .NET and on Windows .NET under Wine:

  • Long names fail to save on Linux. SanitizeName caps names at 120 UTF-16 characters, but Linux allows 255 bytes per file name. 83 Japanese characters (245 bytes) or 60 emoji fail with PathTooLongException on the .partial file. The 120-character cut can also split an emoji, leaving half a surrogate pair.
  • Windows device names followed by a dot. AUX.old, nul.v2, COM1.5 … save on Linux, but Windows resolves them to the device: Path escapes the storage root before anything is written. A folder holding such a file can't be copied to Windows. COM0, LPT0, COM¹²³, LPT¹²³, CONIN$ and CONOUT$ are missing from the list too.
  • Names that differ only in case. On Linux, GetUniquePath gives hat.rlsitem next to Hat.rlsitem, and new folders can do the same; Windows can only hold one of them.

A scan of a real storage folder (83,994 entries) found none of these yet.

Not covered here: folder names lose rich text (the in-game folder name is the disk name), and a quoted, ~ or relative StoragePath resolves against the game folder on Linux.

  • Device names: treat a reserved name followed by a dot part as reserved, and complete the list
  • Cap names by UTF-8 bytes too, so the longest file written for a name fits 255 bytes
  • Cut names only between whole characters; replace lone surrogates
  • GetUniquePath, pending saves and new folders treat names differing only in case as taken
  • Tests for each rule
  • README: storage folders move between Windows and Linux
Requirement: a storage folder copied between Linux and Windows keeps working. On Linux the engine, and with it this mod, runs on native Linux .NET (only the renderer runs under Proton), so file names follow Linux rules there. Reproduced against the built mod on Linux .NET and on Windows .NET under Wine: - **Long names fail to save on Linux.** `SanitizeName` caps names at 120 UTF-16 characters, but Linux allows 255 bytes per file name. 83 Japanese characters (245 bytes) or 60 emoji fail with `PathTooLongException` on the `.partial` file. The 120-character cut can also split an emoji, leaving half a surrogate pair. - **Windows device names followed by a dot.** `AUX.old`, `nul.v2`, `COM1.5 …` save on Linux, but Windows resolves them to the device: `Path escapes the storage root` before anything is written. A folder holding such a file can't be copied to Windows. COM0, LPT0, COM¹²³, LPT¹²³, CONIN$ and CONOUT$ are missing from the list too. - **Names that differ only in case.** On Linux, `GetUniquePath` gives `hat.rlsitem` next to `Hat.rlsitem`, and new folders can do the same; Windows can only hold one of them. A scan of a real storage folder (83,994 entries) found none of these yet. Not covered here: folder names lose rich text (the in-game folder name is the disk name), and a quoted, `~` or relative `StoragePath` resolves against the game folder on Linux. - [x] Device names: treat a reserved name followed by a dot part as reserved, and complete the list - [x] Cap names by UTF-8 bytes too, so the longest file written for a name fits 255 bytes - [x] Cut names only between whole characters; replace lone surrogates - [x] `GetUniquePath`, pending saves and new folders treat names differing only in case as taken - [x] Tests for each rule - [x] README: storage folders move between Windows and Linux
Author
Owner

Fixed in 7df63be (0.7.16), deployed to MonkeyLoader/Mods.

Verified:

  • Test suite: 110 passed, 1 skipped (the optional RLS_ROUNDTRIP_DIR round trip). 14 new checks failed before the fix, for the expected reasons.
  • The reproduction against the built DLL, native Linux .NET and Windows .NET under Wine: the 83-character Japanese name saves as 68 characters (200 bytes), 60 emoji as 50, AUX.old and nul.v2 save as _AUX.old.rlsitem and _nul.v2.rlsitem on both, and hat next to Hat becomes hat (2) on both.
  • Not checked in-game yet.

Found while scanning a real storage folder: 6,121 of its 38,536 variant files in .assets/Variants have paths over 260 characters under a typical Windows root (the longest is 238 characters relative to the storage root). That's the store layout, not item names, so it's outside this issue.

Fixed in 7df63be (0.7.16), deployed to MonkeyLoader/Mods. Verified: - Test suite: 110 passed, 1 skipped (the optional `RLS_ROUNDTRIP_DIR` round trip). 14 new checks failed before the fix, for the expected reasons. - The reproduction against the built DLL, native Linux .NET and Windows .NET under Wine: the 83-character Japanese name saves as 68 characters (200 bytes), 60 emoji as 50, `AUX.old` and `nul.v2` save as `_AUX.old.rlsitem` and `_nul.v2.rlsitem` on both, and `hat` next to `Hat` becomes `hat (2)` on both. - Not checked in-game yet. Found while scanning a real storage folder: 6,121 of its 38,536 variant files in `.assets/Variants` have paths over 260 characters under a typical Windows root (the longest is 238 characters relative to the storage root). That's the store layout, not item names, so it's outside this issue.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kyuubi/ResoLocalStorage#14
No description provided.