200 lines
4.9 KiB
C#
200 lines
4.9 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
using System.Reflection;
|
|
using System.Text.Encodings.Web;
|
|
|
|
static public class json
|
|
{
|
|
static public T load<T>( string filename ) where T : lib.JsonData
|
|
{
|
|
return (T)lib.JsonData.load<T>( filename );
|
|
}
|
|
|
|
static public lib.JsonData load( string filename )
|
|
{
|
|
return lib.JsonData.load( filename );
|
|
}
|
|
}
|
|
|
|
namespace lib
|
|
{
|
|
// Merged the Interface and Class logic to remove ambiguity
|
|
public interface JsonData
|
|
{
|
|
#region Configuration
|
|
|
|
// Centralized JSON Options for consistency across Load/Save
|
|
private static readonly JsonSerializerOptions _options = new()
|
|
{
|
|
WriteIndented = true, // Pretty print
|
|
PropertyNameCaseInsensitive = true, // Forgiving load
|
|
IncludeFields = true, // Serialize public fields (game dev standard)
|
|
ReferenceHandler = ReferenceHandler.Preserve, // Handles circular refs and polymorphism $id metadata
|
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, // Cleaner files
|
|
AllowTrailingCommas = true,
|
|
ReadCommentHandling = JsonCommentHandling.Skip,
|
|
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
|
};
|
|
|
|
#endregion
|
|
|
|
#region SaveLoad
|
|
|
|
// Entry point for Generic Load
|
|
static public T res_load<T>( string filename ) where T : JsonData
|
|
{
|
|
return (T)actual_load( filename, typeof( T ) );
|
|
}
|
|
|
|
// Entry point for untyped Load
|
|
static public JsonData load( string filename )
|
|
{
|
|
return actual_load( filename, null );
|
|
}
|
|
|
|
// Wrapper for generic load
|
|
static public T load<T>( string filename ) where T : JsonData
|
|
{
|
|
return (T)actual_load( filename, typeof( T ) );
|
|
}
|
|
|
|
static public JsonData actual_load( string filename, Type t )
|
|
{
|
|
JsonData json = null;
|
|
|
|
try
|
|
{
|
|
// 1. Path Sanitization
|
|
if( !File.Exists( filename ) && filename.StartsWith( "res://" ) )
|
|
{
|
|
filename = filename.Replace( "res://", "" );
|
|
}
|
|
|
|
// 2. Load or Create
|
|
if( File.Exists( filename ) )
|
|
{
|
|
string jsonContent = File.ReadAllText( filename );
|
|
|
|
// If T is null, we can't deserialize effectively without Type info.
|
|
// Defaulting to JsonData or the passed type.
|
|
Type targetType = t ?? typeof( JsonData );
|
|
|
|
json = (JsonData)JsonSerializer.Deserialize( jsonContent, targetType, _options );
|
|
|
|
if( json != null )
|
|
{
|
|
json.Filename = filename;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
json = CreateTemplate( filename, t );
|
|
}
|
|
}
|
|
catch( Exception ex ) when( ex is IOException || ex is JsonException )
|
|
{
|
|
log.error( $"Failed to load {filename}: {ex.Message}" );
|
|
json = CreateTemplate( filename, t );
|
|
}
|
|
|
|
return json;
|
|
}
|
|
|
|
private static JsonData CreateTemplate( string filename, Type t )
|
|
{
|
|
log.debug( $"JsonData file {filename} not found, creating template." );
|
|
|
|
JsonData json = null;
|
|
|
|
// Handle potential null type if called generically without constraints
|
|
if( t == null )
|
|
{
|
|
log.error( "Cannot create template for unknown type." );
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
json = refl.CreateObject( t ) as JsonData;
|
|
}
|
|
catch( Exception e )
|
|
{
|
|
log.error( $"Exception while creating config {t}, Msg {e.Message}" );
|
|
}
|
|
|
|
// Write out the template if configured to do so
|
|
if( json != null && ConfigCfg.s_cfg.writeOutTemplateFiles )
|
|
{
|
|
var templateFile = $"templates/{filename}";
|
|
var dirName = Path.GetDirectoryName( templateFile );
|
|
|
|
Util.checkAndAddDirectory( dirName );
|
|
|
|
log.info( $"Writing out template config of type {t?.Name} in {templateFile}" );
|
|
|
|
// Force set filename for the template save, then restore?
|
|
// Or just pass path to saveDebug.
|
|
json.Filename = templateFile;
|
|
json.save();
|
|
}
|
|
|
|
return json;
|
|
}
|
|
|
|
static public void actual_save( JsonData json, string filename )
|
|
{
|
|
try
|
|
{
|
|
// Ensure directory exists
|
|
string dir = Path.GetDirectoryName( filename );
|
|
if( !string.IsNullOrEmpty( dir ) && !Directory.Exists( dir ) )
|
|
{
|
|
Directory.CreateDirectory( dir );
|
|
}
|
|
|
|
// Serialize to string first (safer than open stream + fail)
|
|
string jsonString = JsonSerializer.Serialize( json, json.GetType(), _options );
|
|
File.WriteAllText( filename, jsonString );
|
|
}
|
|
catch( Exception ex )
|
|
{
|
|
log.exception( ex, $"Type { json.GetType().Name } {filename}: {ex.Message}" );
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Instance Logic
|
|
|
|
//private string _filename = "{unknown}";
|
|
public string Filename { get; set; }
|
|
|
|
static public void startup()
|
|
{
|
|
// Assuming res.Mgr handles the delegates correctly
|
|
//res.Mgr.Register( JsonData.load );
|
|
res.Mgr.RegisterSub( typeof( ConfigBase ) );
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
public class JsonDataRes : JsonData, res.Resource
|
|
{
|
|
public string Filename { get; set; }
|
|
}
|
|
|
|
static public class JsonDataEx
|
|
{
|
|
|
|
static public void save( this JsonData json )
|
|
{
|
|
// Allow overriding path for templates, otherwise use object's filename
|
|
JsonData.actual_save( json, json.Filename );
|
|
}
|
|
|
|
}
|
|
|
|
}
|