using Sandbox.Modals;
using System.Text.Json.Nodes;
namespace Sandbox;
public static partial class Storage
{
///
/// A folder of content stored on disk
///
public sealed class Entry
{
///
/// The identity of this content
///
public string Id { get; private set; }
///
/// The type of content, eg "save", "dupe"
///
public string Type { get; private set; }
///
/// Metadata about this content. This gets saved to disk automatically.
///
private Dictionary Meta { get; } = new();
///
/// When this content was created
///
public DateTimeOffset Created { get; private set; } = DateTimeOffset.UtcNow;
///
/// This is where you save and load your files to
///
public BaseFileSystem Files { get; private set; }
private string DataPath => $"/storage/{Type}/{Id}/";
private string MetaPath => "_meta.json";
private string ThumbPath => "_thumb.png";
public Entry( string type )
{
Type = type;
Id = Guid.NewGuid().ToString();
ValidateType();
Sandbox.FileSystem.Data.CreateDirectory( DataPath );
Files = Sandbox.FileSystem.Data.CreateSubSystem( DataPath );
SaveMeta();
}
internal Entry( StorageMeta meta )
{
Id = meta.Id;
Type = meta.Type;
Meta = meta.Meta?.ToDictionary() ?? this.Meta;
Created = meta.Timestamp;
ValidateType();
Sandbox.FileSystem.Data.CreateDirectory( DataPath );
Files = Sandbox.FileSystem.Data.CreateSubSystem( DataPath );
}
internal Entry( BaseFileSystem fs )
{
var meta = fs.ReadJsonOrDefault( MetaPath );
if ( meta == null ) throw new System.Exception( "meta file not found" );
Id = meta.Id;
Type = meta.Type;
Meta = meta.Meta?.ToDictionary() ?? this.Meta;
Created = meta.Timestamp;
ValidateType();
Files = fs;
}
private void ValidateType()
{
ArgumentNullException.ThrowIfNull( Type );
if ( Type.Length < 1 ) throw new System.ArgumentException( "type cannot be empty", nameof( Type ) );
if ( Type.Length > 16 ) throw new System.ArgumentException( "type should be under 16 characters", nameof( Type ) );
// TODO - validate type and filename
// type should be letters only, no symbols
if ( !System.Text.RegularExpressions.Regex.IsMatch( Type, "^[a-zA-Z]+$" ) )
{
throw new System.ArgumentException( "Invalid storage type", nameof( Type ) );
}
}
///
/// Set a meta value
///
public void SetMeta( string key, T value )
{
if ( Files.IsReadOnly ) return;
if ( value == null )
{
Meta.Remove( key );
return;
}
Meta[key] = JsonValue.Create( value )?.ToJsonString();
SaveMeta();
}
///
/// Get a meta value
///
public T GetMeta( string key, T defaultValue = default )
{
if ( Meta.TryGetValue( key, out var val ) )
{
return Json.Deserialize( val );
}
return defaultValue;
}
void SaveMeta()
{
var meta = new StorageMeta
{
Id = this.Id,
Type = this.Type,
Meta = this.Meta,
Timestamp = this.Created
};
Files.WriteJson( MetaPath, meta );
}
bool _thumbGenerated = false;
Texture _thumbnail;
public Texture Thumbnail
{
get
{
if ( _thumbGenerated ) return _thumbnail;
_thumbGenerated = true;
if ( !Files.FileExists( ThumbPath ) ) return null;
var data = Files.ReadAllBytes( ThumbPath );
using var bitmap = Bitmap.CreateFromBytes( data.ToArray() );
_thumbnail = bitmap?.ToTexture();
return _thumbnail;
}
}
public void SetThumbnail( Bitmap bitmap )
{
if ( Files.IsReadOnly ) return;
_thumbGenerated = false;
var png = bitmap.ToPng();
Files.WriteAllBytes( ThumbPath, png );
}
public void Delete()
{
Sandbox.FileSystem.Data.DeleteDirectory( DataPath, true );
Storage.OnDeleted( this );
}
public void Publish( string title = "Unnammed", string[] tags = null, Dictionary keyvalues = null )
{
var meta = Files.ReadAllText( MetaPath );
var o = new WorkshopPublishOptions
{
Title = title,
StorageEntry = this,
KeyValues = [],
Tags = [Type],
Metadata = meta
};
if ( tags != null )
{
foreach ( var t in tags )
{
o.Tags.Add( t );
}
}
if ( keyvalues != null )
{
foreach ( var t in keyvalues )
{
o.KeyValues[t.Key] = t.Value;
}
}
// assign the thumbnail
if ( Files.FileExists( ThumbPath ) )
{
var thumbData = Files.ReadAllBytes( ThumbPath );
var bitmap = Bitmap.CreateFromBytes( thumbData.ToArray() );
o.Thumbnail = bitmap;
}
o.KeyValues["type"] = Type;
o.KeyValues["source"] = "storage";
Game.Overlay.WorkshopPublish( o );
}
}
}