using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using SteamNative; namespace Facepunch.Steamworks { /// /// Handles Steam Cloud related actions. /// public class RemoteStorage : IDisposable { private static string NormalizePath( string path ) { // TODO: DUMB HACK ALERT return SteamNative.Platform.IsWindows ? new FileInfo( $"x:/{path}" ).FullName.Substring( 3 ) : new FileInfo( $"/x/{path}" ).FullName.Substring( 3 ); } internal Client client; internal SteamNative.SteamRemoteStorage native; private bool _filesInvalid = true; private readonly List _files = new List(); internal RemoteStorage( Client c ) { client = c; native = client.native.remoteStorage; } /// /// True if Steam Cloud is currently enabled by the current user. /// public bool IsCloudEnabledForAccount { get { return native.IsCloudEnabledForAccount(); } } /// /// True if Steam Cloud is currently enabled for this app by the current user. /// public bool IsCloudEnabledForApp { get { return native.IsCloudEnabledForApp(); } } /// /// Gets the total number of files in the current user's remote storage for the current game. /// public int FileCount { get { return native.GetFileCount(); } } /// /// Gets all files in the current user's remote storage for the current game. /// public IEnumerable Files { get { UpdateFiles(); return _files; } } /// /// Creates a new with the given . /// If a file exists at that path it will be overwritten. /// public RemoteFile CreateFile( string path ) { path = NormalizePath( path ); InvalidateFiles(); var existing = Files.FirstOrDefault( x => x.FileName == path ); return existing ?? new RemoteFile( this, path, client.SteamId, 0 ); } /// /// Opens the file if it exists, else returns null; /// public RemoteFile OpenFile( string path ) { path = NormalizePath( path ); InvalidateFiles(); var existing = Files.FirstOrDefault( x => x.FileName == path ); return existing; } /// /// Opens a previously shared /// with the given . /// public RemoteFile OpenSharedFile( ulong sharingId ) { return new RemoteFile( this, sharingId ); } /// /// Write all text to the file at the specified path. This /// overwrites the contents - it does not append. /// public bool WriteString( string path, string text, Encoding encoding = null ) { var file = CreateFile( path ); file.WriteAllText( text, encoding ); return file.Exists; } /// /// Write all data to the file at the specified path. This /// overwrites the contents - it does not append. /// public bool WriteBytes( string path, byte[] data ) { var file = CreateFile( path ); file.WriteAllBytes( data ); return file.Exists; } /// /// Read the entire contents of the file as a string. /// Returns null if the file isn't found. /// public string ReadString( string path, Encoding encoding = null ) { var file = OpenFile( path ); if ( file == null ) return null; return file.ReadAllText( encoding ); } /// /// Read the entire contents of the file as raw data. /// Returns null if the file isn't found. /// public byte[] ReadBytes( string path ) { var file = OpenFile( path ); if ( file == null ) return null; return file.ReadAllBytes(); } internal void OnWrittenNewFile( RemoteFile file ) { if ( _files.Any( x => x.FileName == file.FileName ) ) return; _files.Add( file ); file.Exists = true; InvalidateFiles(); } internal void InvalidateFiles() { _filesInvalid = true; } private void UpdateFiles() { if ( !_filesInvalid ) return; _filesInvalid = false; foreach ( var file in _files ) { file.Exists = false; } var count = FileCount; for ( var i = 0; i < count; ++i ) { int size; var name = NormalizePath( native.GetFileNameAndSize( i, out size ) ); var timestamp = native.GetFileTimestamp(name); var existing = _files.FirstOrDefault( x => x.FileName == name ); if ( existing == null ) { existing = new RemoteFile( this, name, client.SteamId, size, timestamp ); _files.Add( existing ); } else { existing.SizeInBytes = size; existing.FileTimestamp = timestamp; } existing.Exists = true; } for ( var i = _files.Count - 1; i >= 0; --i ) { if ( !_files[i].Exists ) _files.RemoveAt( i ); } } /// /// Gets whether a file exists in remote storage at the given . /// public bool FileExists( string path ) { return native.FileExists( path ); } public void Dispose() { client = null; native = null; } /// /// Number of bytes used out of the user's total quota /// public ulong QuotaUsed { get { ulong totalBytes = 0; ulong availableBytes = 0; if ( !native.GetQuota( out totalBytes, out availableBytes ) ) return 0; return totalBytes - availableBytes; } } /// /// Total quota size in bytes /// public ulong QuotaTotal { get { ulong totalBytes = 0; ulong availableBytes = 0; if ( !native.GetQuota( out totalBytes, out availableBytes ) ) return 0; return totalBytes; } } /// /// Number of bytes remaining out of the user's total quota /// public ulong QuotaRemaining { get { ulong totalBytes = 0; ulong availableBytes = 0; if ( !native.GetQuota( out totalBytes, out availableBytes ) ) return 0; return availableBytes; } } } }