File.h

// File.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2004

#pragma once
#ifndef Ceda_cxUtils_File_H
#define Ceda_cxUtils_File_H

#include "cxUtils.h"
#include "MsWindows.h"
#include "xstring.h"
#include "SubString.h"
#include "Stream.h"
#include "HPTime.h"
#include <stdio.h>
#include <cstdio>

#ifdef _WIN32_WCE
// missing in Windows Mobile,so we provide our own implementation
cxUtils_API void GetSystemTimeAsFileTime(LPFILETIME filetime);
#endif

namespace ceda
{
class PagedBuffer;

///////////////////////////////////////////////////////////////////////////////////////////////////
// IFile

// Is the file opened for reading or writing?
enum EFileDirection
{
    FD_READING,
    FD_WRITING
};

struct IFile : public ISeekableInputStream, public IOutputStream
{
    // The following methods may throw a file exception on error

    virtual void Close() = 0;

    // Is the file opened for reading or writing?
    virtual EFileDirection Direction() const = 0;
};


///////////////////////////////////////////////////////////////////////////////////////////////////
// File

inline FILE* fopen_u8(ConstStringZ filename, ConstStringZ mode)
{
    #ifdef _WIN32
        return cxWIN32::_wfopen(as_LPCWSTR(filename), as_LPCWSTR(mode));
    #else
        return fopen(filename, mode);
    #endif
}

enum EMode
{
    // Text file
    FM_READ_TEXT,
    FM_WRITE_TEXT,
    FM_APPEND_TEXT,

    // Binary file
    FM_READ_BINARY,
    FM_WRITE_BINARY,
    FM_APPEND_BINARY,
};

class cxUtils_API File : public IFile
{
    cxNotCloneable(File)

public:
    // If default ctor used then use Open() to open the file
    File();

	// Will throw exceptions if the file can't be opened
    File(const xstring& filename, EMode mode);

	~File();

    bool IsOpen() const { return m_fp != nullptr; }

    // Try to open the file.  returns false if file could not be opened
    // Will close the existing file that has previously been opened if any.
    bool Open(const xstring& filename, EMode mode);

    // Implementation of IInputStream
    virtual ssize_t ReadStream(void* buffer, ssize_t numBytes);

    // Implementation of ISeekableInputStream
    virtual ssize_t GetStreamLength() const;
    virtual ssize_t GetStreamPosition() const;
    virtual void SetStreamPosition(ssize_t pos);

    // Implementation of IOutputStream
    virtual void WriteStream(const void* buffer, ssize_t numBytes);
    virtual void FlushStream();
    
    // Implementation of IFile
    virtual void Close();
    virtual EFileDirection Direction() const;

    bool SeekFromCurrentPosition(ssize_t position);
    bool SeekFromStart(ssize_t position);
    bool SeekFromEnd(ssize_t position);

private:
	FILE* m_fp;
    EMode m_mode;
    xstring m_filename;
    ssize_t m_numBytesWritten;
};

///////////////////////////////////////////////////////////////////////////////////////////////////

// Read a buffer from a binary file.  Returns false if file not found.  Throws exceptions
// on I/O errors
cxUtils_API bool ReadBufferFromFile(xvector<octet_t>& buffer, const xstring& filename);

// Write a buffer to a binary file.  Throws exceptions on I/O errors.
cxUtils_API void WriteBufferToFile(const void* buffer, ssize_t size, const xstring& filename);

cxUtils_API void WritePagedBufferToFile(const PagedBuffer& pb, const xstring& filename);

///////////////////////////////////////////////////////////////////////////////////////////////////
// Validated versions of ReadBufferFromFile()/WriteBufferToFile()

/*
WriteBufferToFile() and ReadBufferFromFile() are relatively dangerous for using Archive and 
InputArchive to write/read binary files.  This is because InputArchive doesn't use any concept of
checking for writing past the end of the buffer.  It is only appropriate for reading a binary
format from a trusted source.

For various reasons binary files cannot always be trusted.  For example, a process can fail part 
way through writing a file, in which case the file is truncated.

The following functions use a header on the file consisting of:

    -   the length of the buffer in bytes (recorded as an int64)
    
    -   A 32 bit CRC checksum on the buffer.
*/

cxUtils_API void WriteValidatedPagedBufferToFile(int32 magic, const PagedBuffer& pb, const xstring& filename);

cxUtils_API bool ReadValidatedBufferFromFile(int32 magic, xvector<octet_t>& buffer, const xstring& filename);

} // namespace ceda

#endif // include guard