using System.Text.Json; namespace Sandbox.Resources; /// /// A collection of helper methods for making your own resource compiler. /// public abstract partial class ResourceCompiler { /// /// Writes resource to a JSON file, using the ResourceGenerator to create the resource. /// /// /// protected async Task WriteToJson() where T : Resource { if ( !TryParseEmbeddedResource( out var serialized ) || !serialized.HasValue ) return false; var generator = ResourceGenerator.Create( serialized.Value ); if ( generator is null || !generator.CacheToDisk ) return false; var resource = await generator.CreateAsync( new ResourceGenerator.Options { ForDisk = true, Compiler = this }, default ); if ( resource is null ) return false; Context.Data.Write( JsonSerializer.Serialize( resource ) ); return true; } /// /// Try to parse the source as an EmbeddedResource /// Returns false if the source is not valid JSON or doesn't contain a ResourceGenerator. /// protected bool TryParseEmbeddedResource( out EmbeddedResource? resource ) { resource = null; var json = Context.ReadSourceAsString(); if ( json is null ) return false; // It's keyvalues if ( json.StartsWith( "<" ) ) return false; try { var parsed = JsonSerializer.Deserialize( json ); if ( string.IsNullOrEmpty( parsed.ResourceGenerator ) ) return false; resource = parsed; return true; } catch { // invalid json probably means it isn't json! return false; } } /// /// Create a deterministic path for a generated resource based on the embedded resource data. /// protected string CreateGeneratedResourcePath( EmbeddedResource embed, string subfolder, string extension ) where T : Resource { var generator = ResourceGenerator.Create( embed ); if ( generator is null ) return null; var di = DisplayInfo.For( generator ); var crc = Sandbox.Utility.Crc64.FromString( embed.Data.ToJsonString() ); var generatorName = (di.ClassName ?? di.Name).ToLower(); generatorName = generatorName.GetFilenameSafe(); return $"/{subfolder}/generated/{generatorName}/{crc:x}.{extension}"; } /// /// Generic method to compile an embedded resource by creating a child context. /// This handles the common pattern of creating a generator, generating a path, /// creating a child context, and setting the compiled path. /// protected bool CompileEmbeddedResource( ref EmbeddedResource embed, string subfolder, string extension, BaseFileSystem fs ) where T : Resource { var generator = ResourceGenerator.Create( embed ); if ( generator is null ) return false; if ( !generator.CacheToDisk ) return false; var transientPath = CreateGeneratedResourcePath( embed, subfolder, extension ); if ( transientPath is null ) return false; var absPath = fs.GetFullPath( transientPath ); // // generate a filename for the resource // var child = Context.CreateChild( absPath ); child.SetInputData( JsonSerializer.Serialize( embed ) ); child.Compile(); // // store it in the json, so the compiled json will load the resource // embed.CompiledPath = transientPath.Trim( '/' ); return true; } }