
/*---------------------------------------------------------------------------*/
/* file.cpp                                                                  */
/* copyright (c) innovaphone 2015 - 2026                                     */
/*                                                                           */
/*---------------------------------------------------------------------------*/

#include <platform.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <pwd.h>
#include <grp.h>
#include <dirent.h> 
#include <unistd.h>
#include <stdio.h>

#include <stdexcept>
#include <memory>

//#include <elf.h>
#ifdef __ANDROID__
#include <sys/vfs.h>
#define statvfs statfs
#else
#include <sys/statvfs.h>
#endif

#include "sdk/common/interface/file.h"

#include "file.h"

/*--------------------------------------------------------------------------------------------------------------------------------------*/
/*															 class FileSystem															*/
/*--------------------------------------------------------------------------------------------------------------------------------------*/

IFileWrite * FileSystem::OpenWriteFile(const char * fileName, bool append, class IInstanceLog * const log)
{
    if (log) log->Log(LOG_FILE, "FileSystem::OpenWriteFile fileName:%s append:%i", fileName, append);
    FileWrite * fileWrite = new FileWrite(log, fileName, append);
    if (fileWrite->IsOpen())
        return fileWrite;
    delete fileWrite;
    return NULL;
}

IFileRead * FileSystem::OpenReadFile(const char * fileName, class IInstanceLog * const log)
{
    if (log) log->Log(LOG_FILE, "FileSystem::OpenReadFile fileName:%s", fileName);
    FileRead * fileRead = new FileRead(log, fileName);
    if (fileRead->IsOpen())
        return fileRead;
    delete fileRead;
    return NULL;
}

IDirectory * FileSystem::OpenDirectory(const char * directoryPath, class IInstanceLog * const log)
{
    if (log) log->Log(LOG_FILE, "FileSystem::OpenDirectory directoryPath:%s", directoryPath);
    Directory * directory = new Directory(log, directoryPath);
    if (directory->IsOpen())
        return directory;
    delete directory;
    return NULL;
}

IDirectoryMonitor * FileSystem::MonitorDirectory(const char * directoryPath, class IInstanceLog * log)
{
    if (log) log->Log(LOG_FILE, "FileSystem::MonitorDirectory not implemented! directoryPath: %s", directoryPath);
    return 0;
}

bool FileSystem::ReadWholeFile(const char * filename, char * readBuffer, size_t size, class IInstanceLog * const log)
{
    FILE * file = fopen(filename, "r");
    if (file) {
        char * read;
        size_t len = 0;
        while ((read = fgets(readBuffer + len, size - len, file))) {
            if (strlen(read) == 0) {
                fclose(file);
                if (log) log->Log(LOG_FILE, "FileSystem::ReadWholeFile filename:%s couldn't be read, buffer size %i should be increased", filename, size);
                return false;
            }                
            len += strlen(read);
        }            
        if(log) log->Log(LOG_FILE, "FileSystem::ReadWholeFile filename:%s size:%i buffer:%s", filename, size, readBuffer);
        return fclose(file) == 0;
    }
    else {
        if (log) log->Log(LOG_FILE, "FileSystem::ReadWholeFile filename:%s couldn't be opened (%u : %s)", filename, errno, strerror(errno));
    }
    return false;
}

bool FileSystem::ReadBinaryFile(const char * filename, byte * readBuffer, size_t size, class IInstanceLog * log)
{
    FILE * file = fopen(filename, "r");
    if (file) {
        size_t len = fread(readBuffer, sizeof(byte), size, file);
        if (len == size) {
            // try to read one more byte to trigger feof so that the feof check below succeeds
            byte tmp[1];
            fread(tmp, sizeof(byte), sizeof(tmp), file);
        }
        bool ret = false;
        if (ferror(file)) {
            if (log) log->Log(LOG_FILE, "FileSystem::ReadBinaryFile filename:%s couldn't be read (%u : %s)", filename, errno, strerror(errno));
        }
        else if (!feof(file)) {
            if (log) log->Log(LOG_FILE, "FileSystem::ReadBinaryFile filename:%s buffer too small to read whole file", filename);
        }
        else {
            ret = true;
            if (log) log->Log(LOG_FILE, "FileSystem::ReadBinaryFile filename:%s size:%i read len:%i", filename, size, len);
        }
        fclose(file);
        return ret;
    }
    else {
        if (log) log->Log(LOG_FILE, "FileSystem::ReadBinaryFile filename:%s couldn't be opened (%u : %s)", filename, errno, strerror(errno));
    }
    return false;
}

bool FileSystem::WriteWholeFile(const char * filename, const char * writeBuffer, size_t size, class IInstanceLog * const log)
{
    FILE * file = fopen(filename, "w");
    if (file) {
        int ret = fputs(writeBuffer, file);
        fsync(fileno(file));
        if (ret == EOF) {
            if (log) log->Log(LOG_FILE, "FileSystem::WriteWholeFile filename:%s couldn't be written (%u : %s)", filename, errno, strerror(errno));
        }
        else {
            if (log) log->Log(LOG_FILE, "FileSystem::WriteWholeFile filename:%s size:%i buffer:%s", filename, size, writeBuffer);
        }
        return fclose(file) == 0;
    }
    else {
        if (log) log->Log(LOG_FILE, "FileSystem::WriteWholeFile filename:%s couldn't be opened (%u : %s)", filename, errno, strerror(errno));
    }
    return false;
}

bool FileSystem::WriteBinaryFile(const char * filename, const byte * writeBuffer, size_t size, class IInstanceLog * log)
{
    FILE * file = fopen(filename, "w");
    if (file) {
        size_t written = fwrite(writeBuffer, sizeof(byte), size, file);
        fsync(fileno(file));
        if (written != size) {
            if (log) log->Log(LOG_FILE, "FileSystem::WriteBinaryFile filename:%s couldn't be written (%u : %s)", filename, errno, strerror(errno));
        }
        else {
            if (log) log->Log(LOG_FILE, "FileSystem::WriteBinaryFile filename:%s size:%i buffer:%s", filename, size, writeBuffer);
        }
        return fclose(file) == 0;
    }
    else {
        if (log) log->Log(LOG_FILE, "FileSystem::WriteBinaryFile filename:%s couldn't be opened (%u : %s)", filename, errno, strerror(errno));
    }
    return false;
}

bool FileSystem::ScanFile(class IInstanceLog * const log, const char * filename, const char * format, ...)
{
    FILE * f = fopen(filename, "r");
    if (f) {
        va_list ap;
        va_start(ap, format);
        vfscanf(f, format, ap);
        if (log) log->Log(LOG_FILE, "FileSystem::ScanFile filename: %s ", filename);
        va_end(ap);
        return fclose(f) == 0;
    }
    else {
        if (log) log->Log(LOG_FILE, "FileSystem::ScanFile filename: %s couldn't be opened (%u : %s)", filename, errno, strerror(errno));
    }
    return false;
}

bool FileSystem::FileExists(const char * filenamePath, class IInstanceLog * const log)
{
    struct stat st;
    bool ret = (stat(filenamePath, &st) == 0);
    if (log) log->Log(LOG_FILE, "FileSystem::FileExists filenamePath:%s exists:%i", filenamePath, ret);
    return ret;
}

time_t FileSystem::GetLastWriteTime(const char * filenamePath, class IInstanceLog * const log)
{
    struct stat st;
    if (stat(filenamePath, &st) == 0) {
#if (defined(__ANDROID__) || defined(_IOS_))
        if (log) log->Log(LOG_FILE, "FileSystem::GetLastWriteTime filenamePath:%s sec:%lli", filenamePath, (long64)st.st_mtime);
        return st.st_mtime;
#else
        if (log) log->Log(LOG_FILE, "FileSystem::GetLastWriteTime filenamePath:%s sec:%lli", filenamePath, st.st_mtim.tv_sec);
        return st.st_mtim.tv_sec;
#endif
    }
    if (log) log->Log(LOG_FILE, "FileSystem::GetLastWriteTime filenamePath:%s stat() failed (%u : %s)", filenamePath, errno, strerror(errno));
    return -1;
}

bool FileSystem::SetLastWriteTime(const char * filenamePath, long64 writeTimeMs, class IInstanceLog * log)
{
    struct timeval times[2];
    times[0].tv_sec = times[1].tv_sec = (time_t)(writeTimeMs / 1000);
    times[0].tv_usec = times[1].tv_usec = (suseconds_t)(writeTimeMs % 1000);
    if (utimes(filenamePath, times) == 0) {
        if (log) log->Log(LOG_FILE, "FileSystem::SetLastWriteTime filenamePath:%s ts:%lli", filenamePath, writeTimeMs);
        return true;
    }
    if (log) log->Log(LOG_FILE, "FileSystem::SetLastWriteTime filenamePath:%s stat() failed (%u : %s)", filenamePath, errno, strerror(errno));
    return false;
}

off_t FileSystem::GetFileSize(const char * filename, class IInstanceLog * const log)
{
    struct stat st;
    if (stat(filename, &st) == 0) {
        if (log) log->Log(LOG_FILE, "FileSystem::GetFileSize filenamePath:%s size:%llu", filename, st.st_size);
        return st.st_size;
    }
    if (log) log->Log(LOG_FILE, "FileSystem::GetFileSize filenamePath:%s stat() failed (%u : %s)", filename, errno, strerror(errno));
    return -1;
}
off_t FileSystem::GetFileBlockSize(const char * filename, class IInstanceLog * const log)
{
    struct stat st;
    if (stat(filename, &st) == 0) {
        if (log) log->Log(LOG_FILE, "FileSystem::GetFileBlockSize filenamePath: %s size: %llu", filename, st.st_blocks * 512);
        return st.st_blocks * 512;
    }
    if (log) log->Log(LOG_FILE, "FileSystem::GetFileBlockSize filenamePath: %s stat() failed (%u : %s)", filename, errno, strerror(errno));
    return -1;
}

bool FileSystem::Rename(const char * oldName, const char * newName, class IInstanceLog * const log)
{
    if (rename(oldName, newName) == 0) {
        if (log) log->Log(LOG_FILE, "FileSystem::Rename oldName:%s newName:%s", oldName, newName);
        sync();
        return 1;
    }
    if (log) log->Log(LOG_FILE, "FileSystem::Rename oldName:%s newName:%s rename() failed (%u : %s)", oldName, newName, errno, strerror(errno));
    return 0;
}

bool DoRemoveFile(const char * filenamePath, class IInstanceLog * const log)
{
    bool ret = (remove(filenamePath) == 0);
    if (ret) {
        if (log) log->Log(LOG_FILE, "FileSystem::RemoveFile filenamePath:%s", filenamePath);
    }
    else {
        if (log) log->Log(LOG_FILE, "FileSystem::RemoveFile filenamePath:%s remove() failed (%u : %s)", filenamePath, errno, strerror(errno));
    }
    return ret;
}

bool FileSystem::RemoveFile(const char * filenamePath, class IInstanceLog * const log)
{
    bool ret = DoRemoveFile(filenamePath, log);
    sync();
    return ret;
}

bool FileSystem::CreateDirectory(const char * absolutePath, int mode, class IInstanceLog * const log)
{
    struct stat st;
    bool ret = false;
    if (stat(absolutePath, &st) == -1) {
        ret = (mkdir(absolutePath, mode) == 0);
    }
    if (ret) {
        if (log) log->Log(LOG_FILE, "FileSystem::CreateDirectory absolutePath:%s mode:%i", absolutePath, mode);
        sync();
    }
    else {
        if (log) log->Log(LOG_FILE, "FileSystem::CreateDirectory absolutePath:%s mode:%i mkdir() failed (%u : %s)", absolutePath, mode, errno, strerror(errno));
    }
    return ret;
}

bool IsDirectory(const char * path)
{
    struct stat s_buf;
    if (stat(path, &s_buf) != 0)
        return 0;
    return S_ISDIR(s_buf.st_mode);
}

bool DoRemoveDirectory(const char * directoryPath, class IInstanceLog * const log) {
    DIR * dir;
    struct dirent*  entry;
    char buff[2048] = { 0 };
    if ((dir = opendir(directoryPath))) {
        while ((entry = readdir(dir)) != NULL) {
            if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) {
                _snprintf(buff, sizeof(buff), "%s/%s", directoryPath, entry->d_name);
                if (IsDirectory(buff))
                    DoRemoveDirectory(buff, log);
                else
                    DoRemoveFile(buff, log);
            }
        }
        closedir(dir);
    }
    else {
        if (log) log->Log(LOG_FILE, "FileSystem::RemoveDirectory directoryPath:%s opendir() failed (%u : %s)", directoryPath, errno, strerror(errno));
        return false;
    }
    bool ret = (rmdir(directoryPath) == 0);
    if (ret) {
        if (log) log->Log(LOG_FILE, "FileSystem::RemoveDirectory directoryPath:%s", directoryPath);
    }
    else {
        if (log) log->Log(LOG_FILE, "FileSystem::RemoveDirectory directoryPath:%s rmdir() failed (%u : %s)", directoryPath, errno, strerror(errno));
    }
    return ret;
}

bool FileSystem::RemoveDirectory(const char * directoryPath, class IInstanceLog * const log)
{
    bool ret = DoRemoveDirectory(directoryPath, log);
    sync();
    return ret;
}

bool FileSystem::ChangeOwner(const char * path, const char * newOwner, const char * newGroup, class IInstanceLog * const log)
{
    uid_t uid;
    gid_t gid;
    struct passwd * pwd;
    struct group  * grp;
    pwd = getpwnam(newOwner);
    if (pwd == NULL) {
        if (log) log->Log(LOG_FILE, "FileSystem::ChangeOwner path:%s newOwner:%s newGroup:%s getpwnam() failed (%u : %s)", path, newOwner, newGroup, errno, strerror(errno));
        return 0;
    }
    uid = pwd->pw_uid;
    grp = getgrnam(newGroup);
    if (grp == NULL) {
        if (log) log->Log(LOG_FILE, "FileSystem::ChangeOwner path:%s newOwner:%s newGroup:%s getgrnam() failed (%u : %s)", path, newOwner, newGroup, errno, strerror(errno));
        return 0;
    }
    gid = grp->gr_gid;
    bool ret = (chown(path, uid, gid) == 0);
    if (ret) {
        if (log) log->Log(LOG_FILE, "FileSystem::ChangeOwner path:%s newOwner:%s newGroup:%s", path, newOwner, newGroup);
        sync();
    }
    else {
        if (log) log->Log(LOG_FILE, "FileSystem::ChangeOwner path:%s newOwner:%s newGroup:%s chown() failed (%u : %s)", path, newOwner, newGroup, errno, strerror(errno));
    }
    return ret;
}

bool FileSystem::ChangeMode(const char * path, int mode, class IInstanceLog * const log)
{
    if (chmod(path, mode) == 0) {
        struct stat st;
        bool ret = (stat(path, &st) == 0);
        if (ret) {
            if (log) log->Log(LOG_FILE, "FileSystem::ChangeMode path:%s mode:%i", path, mode);
            sync();
        }
        else {
            if (log) log->Log(LOG_FILE, "FileSystem::ChangeMode path:%s mode:%i stat() failed (%u : %s)", path, mode, errno, strerror(errno));
            return false;
        }
    }
    else {
        if (log) log->Log(LOG_FILE, "FileSystem::ChangeMode path:%s mode:%i chmod() failed (%u : %s)", path, mode, errno, strerror(errno));
        return false;
    }
    return true;
}

ulong64 FileSystem::GetFileSystemTotalSize(const char * path, class IInstanceLog * const log)
{
    struct statvfs vfs;
    if (statvfs(path, &vfs) == 0) {
        if (log) log->Log(LOG_FILE, "FileSystem::GetFileSystemTotalSize path:%s statvfs() succeeded", path);
        // get the total number of blocks minus the total number of free blocks (root) and add the available blocks (non root)
        // this will then match the output of df -h
        // non root users cannot write files anymore if f_bavail is zero, just root users can
        // the database and apps are running without root, so they already collapse if f_bavail is already zero
        return ((ulong64)(vfs.f_blocks - vfs.f_bfree + vfs.f_bavail) * (ulong64)vfs.f_frsize);
    }
    if (log) log->Log(LOG_FILE, "FileSystem::GetFileSystemTotalSize path:%s statvfs() failed (%u : %s)", path, errno, strerror(errno));
    return -1;
}

ulong64 FileSystem::GetFileSystemUsedSpace(const char * path, class IInstanceLog * const log)
{
    struct statvfs vfs;
    if (statvfs(path, &vfs) == 0) {
        if (log) log->Log(LOG_FILE, "FileSystem::GetFileSystemUsedSpace path:%s statvfs() succeeded", path);
        return ((ulong64)(vfs.f_blocks - vfs.f_bfree) * (ulong64)vfs.f_frsize);
    }
    if (log) log->Log(LOG_FILE, "FileSystem::GetFileSystemUsedSpace path:%s statvfs() failed (%u : %s)", path, errno, strerror(errno));
    return -1;
}

bool FileSystem::CheckSystemUser(const char * user, const char * group, class IInstanceLog * log)
{
    struct passwd * pwd;
    struct group  * grp;
    pwd = getpwnam(user);
    if (pwd != NULL) {
        if (log) log->Log(LOG_FILE, "FileSystem::CheckSystemUser, user %s already in use", user);
        return 0;
    }
    grp = getgrnam(group);
    if (grp != NULL) {
        if (log) log->Log(LOG_FILE, "FileSystem::CheckSystemUser, group %s already in use", group);
        return 0;
    }
    if (log) log->Log(LOG_FILE, "FileSystem::CheckSystemUser, user %s and group %s are available", user, group);
    return true;
}

char * FileSystem::GetFileSystemUser(const char * filepath, class IInstanceLog * log)
{
    struct stat info;
    if (stat(filepath, &info) == 0) {
        struct passwd  * pwd = getpwuid(info.st_uid);
        if (pwd != NULL) {
            if (log) log->Log(LOG_FILE, "FileSystem::GetFileSystemUser path:%s success, username:%s", filepath, pwd->pw_name);
            return pwd->pw_name;
        }
    }
    if (log) log->Log(LOG_FILE, "FileSystem::GetFileSystemUser path:%s failed (%u : %s)", filepath, errno, strerror(errno));
    return NULL;
}

bool FileSystem::OpenFile(const char * fileName, const char * workingPath, class IInstanceLog * log)
{
    if (log) log->Log(LOG_FILE, "FileSystem::OpenFile not implemented! fileName:%s workingPath:%s", fileName, workingPath);
    
    return false;
}

/*--------------------------------------------------------------------------------------------------------------------------------------*/
/*															 class Directory															*/
/*--------------------------------------------------------------------------------------------------------------------------------------*/

Directory::Directory(class IInstanceLog * const log, const char * directoryPath) :
log(log)
{
    ASSERT(directoryPath, "Directory::Directory empty directoryPath not allowed");
    this->entry = NULL;
    this->dir = opendir(directoryPath);
    if (this->dir) {
        if (log) log->Log(LOG_FILE, "Directory(%08x)::Directory directoryPath:%s", this, directoryPath);
    }
    else {
        if (log) log->Log(LOG_FILE, "Directory(%08x)::Directory directoryPath:%s opendir failed (%u : %s)", this, directoryPath, errno, strerror(errno));
    }
}

Directory::~Directory()
{
    if (this->dir) Close();
}

bool Directory::IsOpen() const
{
    if (this->dir)
        return 1;
    return 0;
}


bool Directory::Close()
{
    bool ret = (closedir(this->dir) == 0);
    this->dir = NULL;
    if (ret) {
        if (log) log->Log(LOG_FILE, "Directory(%08x)::Close", this);
    }
    else {
        if (log) log->Log(LOG_FILE, "Directory(%08x)::Close closedir() failed (%u : %s)", this, errno, strerror(errno));
    }
    return ret;
}

class IDirectoryEntry * Directory::ReadNext()
{
    struct dirent * dirent;
    while ((dirent = readdir(this->dir)) != NULL) {
        if ((dirent->d_type == DT_REG) || (dirent->d_type == DT_DIR) || (dirent->d_type == DT_UNKNOWN)) {                   //check for type=file or type=directory or type=unknown (which happens for loop devices)
            if (!strcmp(dirent->d_name, ".") || !strcmp(dirent->d_name, ".."))             //exclude self and parent
                continue;
            if (dirent->d_type == DT_REG || dirent->d_type == DT_UNKNOWN) {
                if (log) log->Log(LOG_FILE, "Directory(%08x)::ReadNext file:%s", this, dirent->d_name);
                return new class DirectoryEntry(dirent->d_name, true);
            }
            else if (dirent->d_type == DT_DIR) {
                if (log) log->Log(LOG_FILE, "Directory(%08x)::ReadNext directory:%s", this, dirent->d_name);
                return new class DirectoryEntry(dirent->d_name, false);
            }
        }
    }
    if (log) log->Log(LOG_FILE, "Directory(%08x)::ReadNext no more files/directories", this);
    return 0;
}


/*--------------------------------------------------------------------------------------------------------------------------------------*/
/*															 class FileWrite															*/
/*--------------------------------------------------------------------------------------------------------------------------------------*/

FileWrite::FileWrite(class IInstanceLog * const log, const char * filenamePath, bool append) :
log(log)
{
    this->fp = NULL;
    ASSERT(filenamePath, "FileWrite::FileWrite empty filenamePath not allowed");
    if (!append)
        this->fp = fopen(filenamePath, "wb");
    else
        this->fp = fopen(filenamePath, "ab");
    if (this->fp) {
        if (log) log->Log(LOG_FILE, "FileWrite(%08x)::FileWrite filenamePath:%s append:%i", this, filenamePath, append);
    }
    else {
        if (log) log->Log(LOG_FILE, "FileWrite(%08x)::FileWrite filenamePath:%s append:%i fopen() failed (%u : %s)", this, filenamePath, append, errno, strerror(errno));
    }
}

FileWrite::~FileWrite()
{
    if (this->fp) Close();
}

bool FileWrite::IsOpen() const
{
    if (this->fp)
        return 1;
    return 0;
}

bool FileWrite::Write(const void * buffer, size_t len)
{
    if (this->fp) {
        size_t sz = fwrite(buffer, sizeof(byte), len, this->fp);
        if (log) {
            if (sz == len) log->Log(LOG_FILE, "FileWrite(%08x)::Write len:%i", this, len);
            else log->Log(LOG_FILE, "FileWrite(%08x)::Write len:%i fwrite() failed (%u : %s)", this, len, errno, strerror(errno));
            log->HexDump(LOG_FILE, buffer, len);
        }
        return (sz == len);
    }
    return 0;
}

void FileWrite::Flush()
{
    if (this->fp)
        fflush(this->fp);
}

bool FileWrite::Close()
{
    if (this->fp) {
        this->Flush();
        bool ret = (fclose(this->fp) == 0);
        if (ret) {
            if (log) log->Log(LOG_FILE, "FileWrite(%08x)::Close", this);
        }
        else {
            if (log) log->Log(LOG_FILE, "FileWrite(%08x)::Close fclose failed (%u : %s)", this, errno, strerror(errno));
        }
        this->fp = NULL;
        return ret;
    }
    return 0;
}

/*--------------------------------------------------------------------------------------------------------------------------------------*/
/*															 class FileRead 															*/
/*--------------------------------------------------------------------------------------------------------------------------------------*/

FileRead::FileRead(class IInstanceLog * const log, const char * filenamePath) :
log(log)
{
    this->fp = NULL;
    ASSERT(filenamePath, "FileRead::FileRead empty filenamePath not allowed");
    this->fp = fopen(filenamePath, "rb");
    if (this->fp) {
        if (log) log->Log(LOG_FILE, "FileRead(%08x)::FileRead filenamePath:%s", this, filenamePath);
    }
    else {
        if (log) log->Log(LOG_FILE, "FileRead(%08x)::FileRead filenamePath:%s fopen() failed (%u : %s)", this, filenamePath, errno, strerror(errno));
    }
}

FileRead::~FileRead()
{
    if (this->fp) Close();
}

bool FileRead::IsOpen() const
{
    if (this->fp)
        return 1;
    return 0;
}

size_t FileRead::Read(void * buffer, size_t len)
{
    if (this->fp) {
        size_t sz = fread(buffer, sizeof(byte), len, this->fp);
        if (log) {
            if (sz) log->Log(LOG_FILE, "FileRead(%08x)::Read len:%i", this, len);
            else log->Log(LOG_FILE, "FileRead(%08x)::Read len:%i fread() end (%u : %s)", this, len, errno, strerror(errno));
            log->HexDump(LOG_FILE, buffer, sz);
        }
        return sz;
    }
    return 0;
}

bool FileRead::Close()
{
    if (this->fp) {
        bool ret = (fclose(this->fp) == 0);
        if (ret) {
            if (log) log->Log(LOG_FILE, "FileRead(%08x)::Close", this);
        }
        else {
            if (log) log->Log(LOG_FILE, "FileRead(%08x)::Close fclose failed (%u : %s)", this, errno, strerror(errno));
        }
        this->fp = NULL;
        return ret;
    }
    return 0;
}

bool FileRead::Seek(size_t offset, Origin origin)
{
    bool result = false;

    if (this->IsOpen()) {
        int o;
        switch (origin)
        {
        case Origin::START:
            o = SEEK_SET;
            break;

        case Origin::END:
            o = SEEK_END;
            break;

        default: // case Origin::CURRENT:
            o = SEEK_CUR;
            break;
        }

        if (fseek(this->fp, offset, o) == 0) result = true;
        else if (log) log->Log(LOG_FILE, "FileRead(%08x)::Seek fseek failed (%u : %s)", this, errno, strerror(errno));
    }
    return result;
}


size_t FileRead::Pos()
{
    return this->IsOpen() ? ftell(this->fp) : 0;
}


bool FileRead::EoF()
{
    return (!this->IsOpen() || feof(this->fp));
}


/*--------------------------------------------------------------------------------------------------------------------------------------*/
/*															 class DirectorEntry														*/
/*--------------------------------------------------------------------------------------------------------------------------------------*/

DirectoryEntry::DirectoryEntry(char * entryName, bool isFile)
{
    this->entryName = _strdup(entryName);
    this->isFile = isFile;
}

DirectoryEntry::~DirectoryEntry()
{
    free(entryName);
}

const char * DirectoryEntry::GetName() const
{
    return this->entryName;
}

bool DirectoryEntry::IsFile() const
{
    return isFile;
}
