//===== Copyright © 1996-2010, Valve Corporation, All rights reserved. ======//
//
// Purpose: A convenient, clean interface for communicating between UI and
// the PS3 save system.
//
// $NoKeywords: $
//===========================================================================//

#ifndef PS3_SAVEUIAPI_H
#define PS3_SAVEUIAPI_H

#include "threadtools.h"
#include "ps3_pathinfo.h"


class CUtlBuffer;

 
enum eSaveOperationTag
{
	kSAVE_TAG_UNKNOWN = 0,
	kSAVE_TAG_INITIALIZE,
	kSAVE_TAG_WRITE_STEAMINFO,
	kSAVE_TAG_WRITE_AUTOSAVE,		// writing an autosave to the container
	kSAVE_TAG_READ_SAVE,			// reading a save from the container
	kSAVE_TAG_WRITE_SAVE,			// writing a manual save to the container
	kSAVE_TAG_READ_SCREENSHOT,		// reading a screenshot from the container
	kSAVE_TAG_DELETE_SAVE,			// deleting a save from the container
};


// this class will hold the result of an async operation.
// poll JobDone() until it returns true, then you can
// look at the other fields for what actually transpired.
class CPS3SaveRestoreAsyncStatus
{
public:
	inline bool JobDone() { return !!m_bDone; }

	inline int GetSonyReturnValue(); // will return either an error from the enum below or one of these (defined by Sony):
	/*
		CELL_SAVEDATA_RET_OK
		success

		CELL_SAVEDATA_ERROR_CBRESULT
		Callback function returned an error

		CELL_SAVEDATA_ERROR_ACCESS_ERROR
		HDD access error

		CELL_SAVEDATA_ERROR_INTERNAL
		Fatal internal error

		CELL_SAVEDATA_ERROR_PARAM
		Error in parameter to be set to utility (application bug)

		CELL_SAVEDATA_ERROR_NOSPACE
		Insufficient free space (application bug: lack of free space must be judged and handled within the callback function)

		CELL_SAVEDATA_ERROR_BROKEN
		Save data corrupted (modification detected, etc.)

		CELL_SAVEDATA_ERROR_FAILURE
		Save/load of save data failed (file could not be found, etc.)

		CELL_SAVEDATA_ERROR_BUSY
		Save data utility function was called simultaneously

		CELL_SAVEDATA_ERROR_NOUSER
		Specified user does not exist

		CELL_SAVEDATA_ERROR_SIZEOVER			
		Exceeds the maximum size of the saved data 

		CELL_SAVEDATA_ERROR_NODATA				
		Specified save data does not exist on the HDD 

		CELL_SAVEDATA_ERROR_NOTSUPPORTED		
		Called in an unsupported state
	*/
	enum eSaveErrors // our own error types; not from Sony but formatted the same so as to look uniform.
	{
		CELL_SAVEDATA_ERROR_THREAD_WAS_BUSY = -11,
		CELL_SAVEDATA_ERROR_FILE_NOT_FOUND = -12, // you tried to load a file that wasn't in the container
		CELL_SAVEDATA_ERROR_WRONG_USER = -13, // tried to open or operate on a container belonging to a different user
		CELL_SAVEDATA_ERROR_NO_TOC = -14, // failed to read TOC
		CELL_SAVEDATA_ERROR_WRAPPER = -15, // a general failure somewhere in the wrapper classes.
		CELL_SAVEDATA_ERROR_NO_WORK_TO_DO = -16,
		CELL_SAVEDATA_WARNING_ASYNC_FAILSAFE = -17, // an operation completed but somehow forgot to trip the async object (so a failsafe triggered)
		
	};
	
	uint32 m_nCurrentOperationTag; // an arbitrary enum that you can set to whatever you like, except 0. Zero means "not doing anything."
	
	int m_nSonyRetValue;
	bool m_bUseSystemDialogs; ///< when true, says that we should have the OS pop up dialogs for error conditions, rather than letting the game handle them based on the return value here.
	CInterlockedInt m_bDone;
	uint32 m_uiAdditionalDetails;

	CPS3SaveRestoreAsyncStatus() : m_bDone(true), m_bUseSystemDialogs(false), m_nSonyRetValue(0), m_nCurrentOperationTag(kSAVE_TAG_UNKNOWN), m_uiAdditionalDetails( 0 )
	{};
};

inline int CPS3SaveRestoreAsyncStatus::GetSonyReturnValue()
{
	return m_nSonyRetValue;
}

class IPS3SaveSteamInfoProvider
{
public:
	virtual CUtlBuffer * GetInitialLoadBuffer() = 0;
	virtual CUtlBuffer * GetSaveBufferForCommit() = 0;
	virtual CUtlBuffer * PrepareSaveBufferForCommit() = 0;
};

// the interface class
class IPS3SaveRestoreToUI : public IAppSystem
{
public:
	// the maximum possible size of a COMMENT field (including the terminal zero)
	enum { PS3_SAVE_COMMENT_LENGTH = 128 };

	enum FileSecurity_t
	{
		kSECURE,
		kHACKABLE,
		kSYSTEM,
	};

	// information about one file in the container 
	struct CPS3ContainerFileInfo 
	{		

		char fileName[64];
		char comment[PS3_SAVE_COMMENT_LENGTH];
		FileSecurity_t fileType;	
		uint32 size;		// in bytes					
		time_t modtime;		// modification time as a POSIX time_t

		CPS3ContainerFileInfo() { memset(fileName, 0, sizeof(fileName));  memset(comment, 0, sizeof(comment)); }
	};

	// the struct that gets written into by GetContainerInfo()
	struct CPS3ContainerFacts
	{
		int hddFreeSizeKB;	// free size on disk IN KILOBYTES
		int sizeKB;		// current size of container in kilobytes
		bool bOwnedByAnotherUser;  // can't load if this is the case

		CUtlVectorConservative< CPS3ContainerFileInfo > files;
	};


	struct PS3SaveGameInfo_t
	{
		PS3SaveGameInfo_t() : m_nFileTime(0) {}

		CUtlString		m_InternalName; // eg 0000005.SAV
		CUtlString		m_Filename;  // eg autosave.ps3.sav
		CUtlString		m_ScreenshotFilename; // eg autosave.ps3.tga  if one exists
		CUtlString		m_Comment;
		time_t			m_nFileTime;
	};

	virtual ~IPS3SaveRestoreToUI(){};
public:

	/// You have to call this before doing any other save operation. In particular,
	/// there may be an error opening the container. Poll on Async, and when it's done,
	/// look in the return value to see if it succeeded, or if not, why not. 
	/// When bCreateIfMissing is set, it will create a new container where none exists. 
	virtual void Initialize( CPS3SaveRestoreAsyncStatus *pAsync, IPS3SaveSteamInfoProvider *pSteamInfoProvider, bool bCreateIfMissing, int nKBRequired ) = 0;

	// Save the given file into the container. (ie /dev_hdd1/tempsave/foo.ps3.sav will 
	// be written into the container as foo.ps3.sav ). You can optionally specify a second
	// file to be written at the same time, eg a screenshot, because batching two writes
	// to happen at once is far far faster than having two batches one after another.
	// ALL game progress files must be 
	// written as secure; for now, even the screenshots should be, as that puts the work
	// of CRC onto the operating system. 
	virtual void Write( CPS3SaveRestoreAsyncStatus *pAsync, const char *pSourcepath, const char *pScreenshotPath, const char *pComment, FileSecurity_t nSecurity = kSECURE ) = 0;

	// A more complicated and intelligent form of save writing, specifically for the case of autosaves.
	// The source filename given (as an absolute path) will be written to "autosave.ps3.sav". 
	// Meanwhile, the existing "autosave.ps3.sav" will be renamed "autosave01.ps3.sav",
	// any "autosave01.ps3.sav" will be renamed "autosave02.ps3.sav", and so on up to a maximum
	// number of autosaves N, plus the base autosave.ps3.sav. The highest "autosave%02d.ps3.sav"
	// will therefore have a number N. Excess autosaves with numbers >N will be deleted.
	// If you specify a ScreenshotExtension (such as "tga"), the same operation is performed
	// for every file above, where ."sav" is replaced with the given extension. 
	virtual void WriteAutosave(  CPS3SaveRestoreAsyncStatus *pAsync, 
		const char *pSourcePath, // eg "/dev_hdd1/tempsave/autosave.ps3.sav"
		const char *pComment, // the comment field for the new autosave.
		const unsigned int nMaxNumAutosaves ) = 0; // should be at least 1; the highest numbered autosave will be N-1.

	// A way of writing clouded files into container, clouded files over
	// a certain age are purged from container
	virtual void WriteCloudFile( CPS3SaveRestoreAsyncStatus *pAsync,
		const char *pSourcePath,
		const unsigned int nMaxNumCloudFiles ) = 0; // should be at least 1; the highest numbered cloud file will be N-1.

	// Load a file from the container into the given directory. 
	// give it a pointer to a CPS3SaveRestoreAsyncStatus struct that you have created and
	// intend to poll.
	virtual void Load( CPS3SaveRestoreAsyncStatus *pAsync, const char *pFilename, const char *pDestFullPath ) = 0;

	// kill one or two files (eg, save and screenshot). 
	// async will respond when done.
	virtual void Delete( CPS3SaveRestoreAsyncStatus *pAsync, const char *pFilename, const char *pOtherFilename = NULL ) = 0;

	// synchronously retrieve information on the files in the container. Lacks some of the container-wide'
	// info of the above function, and may have slightly out of date information, but is a synchronous call
	// and returns precisely the structure needed by CBaseModPanel::GetSaveGameInfos(). 
	virtual void GetFileInfoSync( CUtlVector< PS3SaveGameInfo_t > &saveGameInfos, bool bFindAll ) = 0;

	// try to find Steam's schema file for the user and stuff it into the container.
	// returns false if it couldn't find the file locally (in which case you should
	// not wait for the async object to be "done", as the job wasn't initiated); 
	// true if it found it locally and queued up an async job to write it.
	virtual void WriteSteamInfo( CPS3SaveRestoreAsyncStatus *pAsync ) = 0;

	// returns whether save thread is busy
	virtual bool IsSaveUtilBusy() = 0;

	// returns the m_nCurrentOperationTag field of the most recent async op to
	// have run, or kSAVE_TAG_UNKNOWN if none has been enqueued yet. This tag
	// changes the moment a job is made active and remains until the next job
	// starts.
	virtual uint32 GetCurrentOpTag() = 0;

	// returns the version of container, used to fire off events when container
	// contents changes.
	virtual uint32 GetContainerModificationVersion() = 0;

	// sets the cloud crypto key.
	virtual void SetCloudFileCryptoKey( uint64 uiCloudCryptoKey ) = 0;
};


#define IPS3SAVEUIAPI_VERSION_STRING "IPS3SAVEUIAPI_001"

#endif