Add more MapLoaderSystem TextReader/TextWriter overloads

Co-authored-by: kaylie <moony@hellomouse.net>
This commit is contained in:
PJB3005
2026-02-08 14:04:09 +01:00
parent fe1648d290
commit fd27f315cb
5 changed files with 342 additions and 78 deletions

View File

@@ -41,6 +41,7 @@ END TEMPLATE-->
* If a sandbox error is caused by a compiler-generated method, the engine will now attempt to point out which using code is responsible.
* Added `OrderedDictionary<TKey, TValue>` and `System.StringComparer` to the sandbox whitelist.
* Added more overloads to `MapLoaderSystem` taking `TextReader`/`TextWriter` where appropriate.
### Bugfixes

View File

@@ -19,8 +19,8 @@ namespace Robust.Shared.EntitySerialization.Systems;
public sealed partial class MapLoaderSystem
{
/// <summary>
/// Tries to load entities from a yaml file. Whenever possible, you should try to use <see cref="TryLoadMap"/>,
/// <see cref="TryLoadGrid"/>, or <see cref="TryLoadEntity"/> instead.
/// Tries to load entities from a YAML file. Whenever possible, you should try to use <see cref="TryLoadMap"/>,
/// <see cref="TryLoadGrid"/>, or <see cref="TryLoadEntity"/> instead.
/// </summary>
public bool TryLoadGeneric(
ResPath file,
@@ -30,6 +30,7 @@ public sealed partial class MapLoaderSystem
{
grids = null;
maps = null;
if (!TryLoadGeneric(file, out var data, options))
return false;
@@ -39,33 +40,29 @@ public sealed partial class MapLoaderSystem
}
/// <summary>
/// Tries to load entities from a YAML file, taking in a raw byte stream.
/// Tries to load entities from a YAML text stream. Whenever possible, you should try to use <see cref="TryLoadMap"/>,
/// <see cref="TryLoadGrid"/>, or <see cref="TryLoadEntity"/> instead.
/// </summary>
/// <param name="file">The file contents to load from.</param>
/// <param name="fileName">
/// The name of the file being loaded. This is used purely for logging/informational purposes.
/// </param>
/// <param name="result">The result of the load operation.</param>
/// <param name="options">Options for the load operation.</param>
/// <returns>True if the load succeeded, false otherwise.</returns>
/// <seealso cref="M:Robust.Shared.EntitySerialization.Systems.MapLoaderSystem.TryLoadGeneric(Robust.Shared.Utility.ResPath,Robust.Shared.EntitySerialization.LoadResult@,System.Nullable{Robust.Shared.EntitySerialization.MapLoadOptions})"/>
public bool TryLoadGeneric(
Stream file,
string fileName,
[NotNullWhen(true)] out LoadResult? result,
TextReader reader,
string source,
[NotNullWhen(true)] out HashSet<Entity<MapComponent>>? maps,
[NotNullWhen(true)] out HashSet<Entity<MapGridComponent>>? grids,
MapLoadOptions? options = null)
{
result = null;
if (!TryReadFile(new StreamReader(file), out var data))
grids = null;
maps = null;
if (!TryLoadGeneric(reader, source, out var data, options))
return false;
return TryLoadGeneric(data, fileName, out result, options);
maps = data.Maps;
grids = data.Grids;
return true;
}
/// <summary>
/// Tries to load entities from a yaml file. Whenever possible, you should try to use <see cref="TryLoadMap"/>,
/// <see cref="TryLoadGrid"/>, or <see cref="TryLoadEntity"/> instead.
/// Tries to load entities from a YAML file. Whenever possible, you should try to use <see cref="TryLoadMap"/>,
/// <see cref="TryLoadGrid"/>, or <see cref="TryLoadEntity"/> instead.
/// </summary>
/// <param name="file">The file to load.</param>
/// <param name="result">Data class containing information about the loaded entities</param>
@@ -80,9 +77,45 @@ public sealed partial class MapLoaderSystem
return TryLoadGeneric(data, file.ToString(), out result, options);
}
private bool TryLoadGeneric(
/// <summary>
/// Tries to load entities from a YAML text stream. Whenever possible, you should try to use <see cref="TryLoadMap"/>,
/// <see cref="TryLoadGrid"/>, or <see cref="TryLoadEntity"/> instead.
/// </summary>
/// <param name="reader">The text to load.</param>
/// <param name="source">The name of the source, if any. This should be your file path (for example)</param>
/// <param name="result">Data class containing information about the loaded entities</param>
/// <param name="options">Optional Options for configuring loading behaviour.</param>
public bool TryLoadGeneric(TextReader reader, string source, [NotNullWhen(true)] out LoadResult? result, MapLoadOptions? options = null)
{
result = null;
if (!TryReadFile(reader, out var data))
return false;
return TryLoadGeneric(data, source, out result, options);
}
/// <summary>
/// Tries to load entities from a YAML text stream. Whenever possible, you should try to use <see cref="TryLoadMap"/>,
/// <see cref="TryLoadGrid"/>, or <see cref="TryLoadEntity"/> instead.
/// </summary>
/// <param name="stream">The stream containing the text to load.</param>
/// <param name="source">The name of the source, if any. This should be your file path (for example)</param>
/// <param name="result">Data class containing information about the loaded entities</param>
/// <param name="options">Optional Options for configuring loading behaviour.</param>
public bool TryLoadGeneric(Stream stream, string source, [NotNullWhen(true)] out LoadResult? result, MapLoadOptions? options = null)
{
result = null;
if (!TryReadFile(new StreamReader(stream, leaveOpen: true), out var data))
return false;
return TryLoadGeneric(data, source, out result, options);
}
public bool TryLoadGeneric(
MappingDataNode data,
string fileName,
string source,
[NotNullWhen(true)] out LoadResult? result,
MapLoadOptions? options = null)
{
@@ -118,7 +151,7 @@ public sealed partial class MapLoaderSystem
if (!deserializer.TryProcessData())
{
Log.Debug($"Failed to process entity data in {fileName}");
Log.Debug($"Failed to process entity data in {source}");
return false;
}
@@ -128,7 +161,7 @@ public sealed partial class MapLoaderSystem
&& deserializer.Result.Category != FileCategory.Unknown)
{
// Did someone try to load a map file as a grid or vice versa?
Log.Error($"Map {fileName} does not contain the expected data. Expected {expected} but got {deserializer.Result.Category}");
Log.Error($"Map {source} does not contain the expected data. Expected {expected} but got {deserializer.Result.Category}");
Delete(deserializer.Result);
return false;
}
@@ -139,7 +172,7 @@ public sealed partial class MapLoaderSystem
}
catch (Exception e)
{
Log.Error($"Caught exception while creating entities for map {fileName}: {e}");
Log.Error($"Caught exception while creating entities for map {source}: {e}");
Delete(deserializer.Result);
throw;
}
@@ -149,7 +182,7 @@ public sealed partial class MapLoaderSystem
if (opts.ExpectedCategory is { } exp && exp != deserializer.Result.Category)
{
// Did someone try to load a map file as a grid or vice versa?
Log.Error($"Map {fileName} does not contain the expected data. Expected {exp} but got {deserializer.Result.Category}");
Log.Error($"Map {source} does not contain the expected data. Expected {exp} but got {deserializer.Result.Category}");
Delete(deserializer.Result);
return false;
}
@@ -184,12 +217,33 @@ public sealed partial class MapLoaderSystem
}
/// <summary>
/// Tries to load a regular (non-map, non-grid) entity from a file.
/// The loaded entity will initially be in null-space.
/// If the file does not contain exactly one orphaned entity, this will return false and delete loaded entities.
/// Tries to load a regular (non-map, non-grid) entity from a YAML file.
/// The loaded entity will initially be in null-space.
/// If the file does not contain exactly one orphaned entity, this will return false and delete loaded entities.
/// </summary>
public bool TryLoadEntity(
ResPath path,
ResPath file,
[NotNullWhen(true)] out Entity<TransformComponent>? entity,
DeserializationOptions? options = null)
{
entity = null;
if (!TryGetReader(file, out var reader))
return false;
using (reader)
{
return TryLoadEntity(reader, file.ToString(), out entity, options);
}
}
/// <summary>
/// Tries to load a regular (non-map, non-grid) entity from a YAML text stream.
/// The loaded entity will initially be in null-space.
/// If the file does not contain exactly one orphaned entity, this will return false and delete loaded entities.
/// </summary>
public bool TryLoadEntity(
TextReader reader,
string source,
[NotNullWhen(true)] out Entity<TransformComponent>? entity,
DeserializationOptions? options = null)
{
@@ -200,7 +254,7 @@ public sealed partial class MapLoaderSystem
};
entity = null;
if (!TryLoadGeneric(path, out var result, opts))
if (!TryLoadGeneric(reader, source, out var result, opts))
return false;
if (result.Orphans.Count == 1)
@@ -215,12 +269,35 @@ public sealed partial class MapLoaderSystem
}
/// <summary>
/// Tries to load a grid entity from a file and parent it to the given map.
/// If the file does not contain exactly one grid, this will return false and delete loaded entities.
/// Tries to load a grid entity from a YAML file and parent it to the given map.
/// If the file does not contain exactly one grid, this will return false and delete loaded entities.
/// </summary>
public bool TryLoadGrid(
MapId map,
ResPath path,
ResPath file,
[NotNullWhen(true)] out Entity<MapGridComponent>? grid,
DeserializationOptions? options = null,
Vector2 offset = default,
Angle rot = default)
{
grid = null;
if (!TryGetReader(file, out var reader))
return false;
using (reader)
{
return TryLoadGrid(map, reader, file.ToString(), out grid, options, offset, rot);
}
}
/// <summary>
/// Tries to load a grid entity from a YAML text stream and parent it to the given map.
/// If the file does not contain exactly one grid, this will return false and delete loaded entities.
/// </summary>
public bool TryLoadGrid(
MapId map,
TextReader reader,
string source,
[NotNullWhen(true)] out Entity<MapGridComponent>? grid,
DeserializationOptions? options = null,
Vector2 offset = default,
@@ -236,7 +313,7 @@ public sealed partial class MapLoaderSystem
};
grid = null;
if (!TryLoadGeneric(path, out var result, opts))
if (!TryLoadGeneric(reader, source, out var result, opts))
return false;
if (result.Grids.Count == 1)
@@ -250,11 +327,35 @@ public sealed partial class MapLoaderSystem
}
/// <summary>
/// Tries to load a grid entity from a file and parent it to a newly created map.
/// Tries to load a grid entity from a YAML file and parent it to a newly created map.
/// If the file does not contain exactly one grid, this will return false and delete loaded entities.
/// </summary>
public bool TryLoadGrid(
ResPath path,
ResPath file,
[NotNullWhen(true)] out Entity<MapComponent>? map,
[NotNullWhen(true)] out Entity<MapGridComponent>? grid,
DeserializationOptions? options = null,
Vector2 offset = default,
Angle rot = default)
{
grid = null;
map = null;
if (!TryGetReader(file, out var reader))
return false;
using (reader)
{
return TryLoadGrid(reader, file.ToString(), out map, out grid, options, offset, rot);
}
}
/// <summary>
/// Tries to load a grid entity from a YAML text stream and parent it to a newly created map.
/// If the file does not contain exactly one grid, this will return false and delete loaded entities.
/// </summary>
public bool TryLoadGrid(
TextReader reader,
string source,
[NotNullWhen(true)] out Entity<MapComponent>? map,
[NotNullWhen(true)] out Entity<MapGridComponent>? grid,
DeserializationOptions? options = null,
@@ -267,7 +368,7 @@ public sealed partial class MapLoaderSystem
if (opts.PauseMaps)
_mapSystem.SetPaused(mapUid, true);
if (!TryLoadGrid(mapId, path, out grid, options, offset, rot))
if (!TryLoadGrid(mapId, reader, source, out grid, options, offset, rot))
{
Del(mapUid);
map = null;

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Numerics;
using Robust.Shared.GameObjects;
@@ -15,14 +16,41 @@ namespace Robust.Shared.EntitySerialization.Systems;
public sealed partial class MapLoaderSystem
{
/// <summary>
/// Attempts to load a file containing a single map.
/// If the file does not contain exactly one map, this will return false and delete all loaded entities.
/// Attempts to load a YAML file containing a single map.
/// If the file does not contain exactly one map, this will return false and delete all loaded entities.
/// </summary>
/// <remarks>
/// Note that this will not automatically initialize the map, unless specified via the <see cref="DeserializationOptions"/>.
/// Note that this will not automatically initialize the map, unless specified via the <see cref="DeserializationOptions"/>.
/// </remarks>
public bool TryLoadMap(
ResPath path,
ResPath file,
[NotNullWhen(true)] out Entity<MapComponent>? map,
[NotNullWhen(true)] out HashSet<Entity<MapGridComponent>>? grids,
DeserializationOptions? options = null,
Vector2 offset = default,
Angle rot = default)
{
map = null;
grids = null;
if (!TryGetReader(file, out var reader))
return false;
using (reader)
{
return TryLoadMap(reader, file.ToString(), out map, out grids, options, offset, rot);
}
}
/// <summary>
/// Attempts to load a YAML stream containing a single map.
/// If the file does not contain exactly one map, this will return false and delete all loaded entities.
/// </summary>
/// <remarks>
/// Note that this will not automatically initialize the map, unless specified via the <see cref="DeserializationOptions"/>.
/// </remarks>
public bool TryLoadMap(
TextReader reader,
string source,
[NotNullWhen(true)] out Entity<MapComponent>? map,
[NotNullWhen(true)] out HashSet<Entity<MapGridComponent>>? grids,
DeserializationOptions? options = null,
@@ -39,7 +67,7 @@ public sealed partial class MapLoaderSystem
map = null;
grids = null;
if (!TryLoadGeneric(path, out var result, opts))
if (!TryLoadGeneric(reader, source, out var result, opts))
return false;
if (result.Maps.Count == 1)
@@ -54,17 +82,47 @@ public sealed partial class MapLoaderSystem
}
/// <summary>
/// Attempts to load a file containing a single map, assign it the given map id.
/// Attempts to load a YAML file containing a single map, assign it the given map id.
/// </summary>
/// <remarks>
/// If possible, it is better to use <see cref="TryLoadMap"/> which automatically assigns a <see cref="MapId"/>.
/// If possible, it is better to use <see cref="TryLoadMap"/> which automatically assigns a <see cref="MapId"/>.
/// </remarks>
/// <remarks>
/// Note that this will not automatically initialize the map, unless specified via the <see cref="DeserializationOptions"/>.
/// Note that this will not automatically initialize the map, unless specified via the <see cref="DeserializationOptions"/>.
/// </remarks>
public bool TryLoadMapWithId(
MapId mapId,
ResPath path,
ResPath file,
[NotNullWhen(true)] out Entity<MapComponent>? map,
[NotNullWhen(true)] out HashSet<Entity<MapGridComponent>>? grids,
DeserializationOptions? options = null,
Vector2 offset = default,
Angle rot = default)
{
map = null;
grids = null;
if (!TryGetReader(file, out var reader))
return false;
using (reader)
{
return TryLoadMapWithId(mapId, reader, file.ToString(), out map, out grids, options, offset, rot);
}
}
/// <summary>
/// Attempts to load a YAML text stream containing a single map, assign it the given map id.
/// </summary>
/// <remarks>
/// If possible, it is better to use <see cref="TryLoadMap"/> which automatically assigns a <see cref="MapId"/>.
/// </remarks>
/// <remarks>
/// Note that this will not automatically initialize the map, unless specified via the <see cref="DeserializationOptions"/>.
/// </remarks>
public bool TryLoadMapWithId(
MapId mapId,
TextReader reader,
string source,
[NotNullWhen(true)] out Entity<MapComponent>? map,
[NotNullWhen(true)] out HashSet<Entity<MapGridComponent>>? grids,
DeserializationOptions? options = null,
@@ -86,7 +144,7 @@ public sealed partial class MapLoaderSystem
throw new Exception($"Target map already exists");
opts.ForceMapId = mapId;
if (!TryLoadGeneric(path, out var result, opts))
if (!TryLoadGeneric(reader, source, out var result, opts))
return false;
if (!_mapSystem.TryGetMap(mapId, out var uid) || !TryComp(uid, out MapComponent? comp))
@@ -98,12 +156,35 @@ public sealed partial class MapLoaderSystem
}
/// <summary>
/// Attempts to load a file containing a single map, and merge its children onto another map. After which the
/// loaded map gets deleted.
/// Attempts to load a YAML text stream containing a single map, and merge its children onto another map. After which
/// the loaded map gets deleted.
/// </summary>
public bool TryMergeMap(
MapId mapId,
ResPath path,
ResPath file,
[NotNullWhen(true)] out HashSet<Entity<MapGridComponent>>? grids,
DeserializationOptions? options = null,
Vector2 offset = default,
Angle rot = default)
{
grids = null;
if (!TryGetReader(file, out var reader))
return false;
using (reader)
{
return TryMergeMap(mapId, reader, file.ToString(), out grids, options, offset, rot);
}
}
/// <summary>
/// Attempts to load a YAML file containing a single map, and merge its children onto another map. After which
/// the loaded map gets deleted.
/// </summary>
public bool TryMergeMap(
MapId mapId,
TextReader reader,
string source,
[NotNullWhen(true)] out HashSet<Entity<MapGridComponent>>? grids,
DeserializationOptions? options = null,
Vector2 offset = default,
@@ -123,7 +204,7 @@ public sealed partial class MapLoaderSystem
throw new Exception($"Target map {mapId} does not exist");
opts.MergeMap = mapId;
if (!TryLoadGeneric(path, out var result, opts))
if (!TryLoadGeneric(reader, source, out var result, opts))
return false;
if (!_mapSystem.TryGetMap(mapId, out var uid) || !TryComp(uid, out MapComponent? comp))

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
@@ -59,10 +60,19 @@ public sealed partial class MapLoaderSystem
}
/// <summary>
/// Serialize a standard (non-grid, non-map) entity and all of its children and write the result to a
/// yaml file.
/// Serialize a standard (non-grid, non-map) entity and all of its children and write the result to a YAML file.
/// </summary>
public bool TrySaveEntity(EntityUid entity, ResPath path, SerializationOptions? options = null)
public bool TrySaveEntity(EntityUid entity, ResPath target, SerializationOptions? options = null)
{
using var writer = GetWriterForPath(target);
return TrySaveEntity(entity, writer, options);
}
/// <summary>
/// Serialize a standard (non-grid, non-map) entity and all of its children and write the result to a YAML text
/// stream.
/// </summary>
public bool TrySaveEntity(EntityUid entity, TextWriter target, SerializationOptions? options = null)
{
if (_mapQuery.HasComp(entity))
{
@@ -97,12 +107,12 @@ public sealed partial class MapLoaderSystem
return false;
}
Write(path, data);
Write(target, data);
return true;
}
/// <summary>
/// Serialize a map and all of its children and write the result to a yaml file.
/// Serialize a map and all of its children and write the result to a YAML file.
/// </summary>
public bool TrySaveMap(MapId mapId, ResPath path, SerializationOptions? options = null)
{
@@ -114,9 +124,18 @@ public sealed partial class MapLoaderSystem
}
/// <summary>
/// Serialize a map and all of its children and write the result to a yaml file.
/// Serialize a map and all of its children and write the result to a YAML file.
/// </summary>
public bool TrySaveMap(EntityUid map, ResPath path, SerializationOptions? options = null)
public bool TrySaveMap(EntityUid map, ResPath target, SerializationOptions? options = null)
{
using var writer = GetWriterForPath(target);
return TrySaveMap(map, writer, options);
}
/// <summary>
/// Serialize a map and all of its children and write the result to a YAML text stream.
/// </summary>
public bool TrySaveMap(EntityUid map, TextWriter target, SerializationOptions? options = null)
{
if (!_mapQuery.HasComp(map))
{
@@ -145,14 +164,23 @@ public sealed partial class MapLoaderSystem
return false;
}
Write(path, data);
Write(target, data);
return true;
}
/// <summary>
/// Serialize a grid and all of its children and write the result to a yaml file.
/// Serialize a grid and all of its children and write the result to a YAML file.
/// </summary>
public bool TrySaveGrid(EntityUid grid, ResPath path, SerializationOptions? options = null)
public bool TrySaveGrid(EntityUid map, ResPath target, SerializationOptions? options = null)
{
using var writer = GetWriterForPath(target);
return TrySaveGrid(map, writer, options);
}
/// <summary>
/// Serialize a grid and all of its children and write the result to a YAML text stream.
/// </summary>
public bool TrySaveGrid(EntityUid grid, TextWriter target, SerializationOptions? options = null)
{
if (!_gridQuery.HasComp(grid))
{
@@ -187,32 +215,62 @@ public sealed partial class MapLoaderSystem
return false;
}
Write(path, data);
Write(target, data);
return true;
}
/// <summary>
/// Serialize an entities and all of their children to a yaml file.
/// This makes no assumptions about the expected entity or resulting file category.
/// If possible, use the map/grid specific variants instead.
/// Serialize an entity and all of their children to a YAML file.
/// This makes no assumptions about the expected entity or resulting file category.
/// If possible, use the map/grid specific variants instead.
/// </summary>
public bool TrySaveGeneric(
EntityUid uid,
ResPath path,
ResPath target,
out FileCategory category,
SerializationOptions? options = null)
{
return TrySaveGeneric([uid], path, out category, options);
using var writer = GetWriterForPath(target);
return TrySaveGeneric(uid, writer, out category, options);
}
/// <summary>
/// Serialize one or more entities and all of their children to a yaml file.
/// This makes no assumptions about the expected entity or resulting file category.
/// If possible, use the map/grid specific variants instead.
/// Serialize an entity and all of their children to a YAML text stream.
/// This makes no assumptions about the expected entity or resulting file category.
/// If possible, use the map/grid specific variants instead.
/// </summary>
public bool TrySaveGeneric(
EntityUid uid,
TextWriter target,
out FileCategory category,
SerializationOptions? options = null)
{
return TrySaveGeneric([uid], target, out category, options);
}
/// <summary>
/// Serialize one or more entities and all of their children to a YAML file.
/// This makes no assumptions about the expected entity or resulting file category.
/// If possible, use the map/grid specific variants instead.
/// </summary>
public bool TrySaveGeneric(
HashSet<EntityUid> uid,
ResPath target,
out FileCategory category,
SerializationOptions? options = null)
{
using var writer = GetWriterForPath(target);
return TrySaveGeneric(uid, writer, out category, options);
}
/// <summary>
/// Serialize one or more entities and all of their children to a YAML text stream.
/// This makes no assumptions about the expected entity or resulting file category.
/// If possible, use the map/grid specific variants instead.
/// </summary>
public bool TrySaveGeneric(
HashSet<EntityUid> entities,
ResPath path,
TextWriter target,
out FileCategory category,
SerializationOptions? options = null)
{
@@ -233,10 +291,21 @@ public sealed partial class MapLoaderSystem
return false;
}
Write(path, data);
Write(target, data);
return true;
}
/// <inheritdoc cref="TrySerializeAllEntities(out MappingDataNode, SerializationOptions?)"/>
public bool TrySaveAllEntities(TextWriter target, SerializationOptions? options = null)
{
if (!TrySerializeAllEntities(out var data, options))
return false;
Write(target, data);
return true;
}
/// <inheritdoc cref="TrySerializeAllEntities(out MappingDataNode, SerializationOptions?)"/>
public bool TrySaveAllEntities(ResPath path, SerializationOptions? options = null)
{

View File

@@ -42,17 +42,29 @@ public sealed partial class MapLoaderSystem : EntitySystem
_gridQuery = GetEntityQuery<MapGridComponent>();
}
private void Write(TextWriter target, MappingDataNode data)
{
var document = new YamlDocument(data.ToYaml());
var stream = new YamlStream {document};
stream.Save(new YamlMappingFix(new Emitter(target)), false);
}
private StreamWriter GetWriterForPath(ResPath path)
{
Log.Info($"Saving serialized results to {path}");
path = path.ToRootedPath();
_resourceManager.UserData.CreateDir(path.Directory);
return _resourceManager.UserData.OpenWriteText(path);
}
private void Write(ResPath path, MappingDataNode data)
{
Log.Info($"Saving serialized results to {path}");
path = path.ToRootedPath();
var document = new YamlDocument(data.ToYaml());
_resourceManager.UserData.CreateDir(path.Directory);
using var writer = _resourceManager.UserData.OpenWriteText(path);
{
var stream = new YamlStream {document};
stream.Save(new YamlMappingFix(new Emitter(writer)), false);
}
Write(writer, data);
}
public bool TryReadFile(ResPath file, [NotNullWhen(true)] out MappingDataNode? data)