<commit_before>#include "FileSystem.h"
#include "Log.h"

#include <string.h>

/*
===============================================================================================

 Platform independent functionality

===============================================================================================
*/

bool FS_IsSpecial(const char* path)
{
    size_t  len;

    len = strlen(path);

    if (len == 1 && path[0] == '.')
        return true;
    if (len == 2 && path[0] == '.' && path[1] == '.')
        return true;

    return false;
}

/*
===============================================================================================

 Unix compatible filesystem

===============================================================================================
*/

#ifndef PLATFORM_WINDOWS
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/uio.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <alloca.h>
#include <stdio.h>

FD FS_Open(const char* filename, int flags)
{
    int     mode;
    int     oflags;
    int     ret;

    mode = S_IRUSR | S_IWUSR;
    oflags = 0;
    if ((flags & FS_CREATE) == FS_CREATE)
        oflags |= O_CREAT;
    if ((flags & FS_READWRITE) == FS_READWRITE)
        oflags |= O_RDWR;
    if ((flags & FS_READONLY) == FS_READONLY)
        oflags |= O_RDONLY;

    ret = open(filename, oflags, mode);
    if (ret < 0)
    {
        Log_Errno();
        return INVALID_FD;
    }
    return ret;
}

void FS_FileClose(FD fd)
{
    int ret;

    ret = close(fd);
    if (ret < 0)
        Log_Errno();
}

int64_t FS_FileSeek(FD fd, uint64_t offset, int whence_)
{
    off_t   ret;
    int     whence;

    whence = -1;
    switch (whence_)
    {
    case FS_SEEK_SET:
        whence = SEEK_SET;
        break;
    case FS_SEEK_CUR:
        whence = SEEK_CUR;
        break;
    case FS_SEEK_END:
        whence = SEEK_END;
        break;
    default:
        return -1;
    }

    ret = lseek(fd, offset, whence);
    if (ret < 0)
        Log_Errno();
    return ret;
}

int FS_FileTruncate(FD fd, uint64_t length)
{
    int ret;

    ret = ftruncate(fd, length);
    if (ret < 0)
        Log_Errno();
    return ret;
}

int64_t FS_FileSize(FD fd)
{
    int64_t     ret;
    struct stat buf;

    ret = fstat(fd, &buf);
    if (ret < 0)
    {
        Log_Errno();
        return ret;
    }

    return buf.st_size;
}

ssize_t FS_FileWrite(FD fd, const void* buf, size_t count)
{
    ssize_t ret;

    ret = write(fd, buf, count);
    if (ret < 0)
        Log_Errno();
    return ret;
}

ssize_t FS_FileWriteVector(FD fd, unsigned num, const void** buf, size_t *count)
{
    ssize_t         ret;
    struct iovec    vecbuf[num];
    unsigned        i;

    for (i = 0; i < num; i++)
    {
        vecbuf[i].iov_base = (void*) buf[i];
        vecbuf[i].iov_len = count[i];
    }

    ret = writev(fd, vecbuf, num);
    if (ret < 0)
        Log_Errno();
    return ret;
}

ssize_t FS_FileRead(FD fd, void* buf, size_t count)
{
    ssize_t ret;

    ret = read(fd, buf, count);
    if (ret < 0)
        Log_Errno();
    return ret;
}

ssize_t FS_FileWriteOffs(FD fd, const void* buf, size_t count, uint64_t offset)
{
    ssize_t ret;

    ret = pwrite(fd, buf, count, offset);
    if (ret < 0)
        Log_Errno();
    return ret;
}

ssize_t FS_FileReadOffs(FD fd, void* buf, size_t count, uint64_t offset)
{
    ssize_t ret;

    ret = pread(fd, buf, count, offset);
    if (ret < 0)
        Log_Errno();
    return ret;
}

bool FS_Delete(const char* filename)
{
    int ret;

    ret = unlink(filename);
    if (ret < 0)
    {
        Log_Errno();
        return false;
    }

    return true;
}

FS_Dir FS_OpenDir(const char* filename)
{
    DIR*    dir;

    dir = opendir(filename);
    if (dir == NULL)
    {
        Log_Errno();
        return NULL;
    }

    return (FS_Dir) dir;
}

FS_DirEntry FS_ReadDir(FS_Dir dir)
{
    if ((DIR*) dir == NULL)
        return FS_INVALID_DIR_ENTRY;
    return (FS_DirEntry) readdir((DIR*) dir);
}

void FS_CloseDir(FS_Dir dir)
{
    int ret;

    if ((DIR*) dir == NULL)
        return;

    ret = closedir((DIR*) dir);
    if (ret < 0)
        Log_Errno();
}

bool FS_CreateDir(const char* filename)
{
    int ret;

    ret = mkdir(filename, S_IRUSR | S_IWUSR | S_IXUSR);
    if (ret < 0)
    {
        Log_Errno();
        return false;
    }
    return true;
}

bool FS_DeleteDir(const char* filename)
{
    int ret;

    ret = rmdir(filename);
    if (ret < 0)
    {
        Log_Errno();
        return false;
    }
    return true;
}

bool FS_RecDeleteDir(const char* path)
{
    FS_Dir      dir;
    FS_DirEntry entry;
    bool        ret;
    const char* dirname;
    size_t      pathlen;
    size_t      tmplen;
    char*       tmp;

    if (!FS_IsDirectory(path))
        return false;

    if (FS_IsSpecial(path))
        return false;

    dir = FS_OpenDir(path);
    if (dir == FS_INVALID_DIR)
        return false;

    pathlen = strlen(path);
    while ((entry = FS_ReadDir(dir)) != FS_INVALID_DIR_ENTRY)
    {
        dirname = FS_DirEntryName(entry);
        if (FS_IsSpecial(dirname))
            continue;

        // create relative path in temporary buffer
        tmplen = pathlen + 1 + strlen(dirname) + 1;
        tmp = new char[tmplen];
        memcpy(tmp, path, pathlen);
        tmp[pathlen] = FS_Separator();
        memcpy(tmp + pathlen + 1, dirname, strlen(dirname));
        tmp[tmplen - 1] = '\0';

        if (FS_IsDirectory(tmp))
            ret = FS_RecDeleteDir(tmp);
        else
            ret = FS_Delete(tmp);

        delete[] tmp;
        if (!ret)
            return false;
    }

    FS_CloseDir(dir);
    return FS_DeleteDir(path);
}

const char* FS_DirEntryName(FS_DirEntry dirent)
{
    return ((struct dirent*) dirent)->d_name;
}

bool FS_IsFile(const char* path)
{
    struct stat s;
    if (stat(path, &s) != 0)
        return false;
    if (s.st_mode & S_IFREG)
        return true;
    return false;
}

bool FS_IsDirectory(const char* path)
{
    struct stat s;
    if (stat(path, &s) != 0)
        return false;
    if (s.st_mode & S_IFDIR)
        return true;
    return false;
}

bool FS_Exists(const char* path)
{
    struct stat s;
    if (stat(path, &s) != 0)
        return false;
    return true;
}

int64_t FS_FreeDiskSpace(const char* path)
{
    struct statvfs sv;

    if (statvfs(path, &sv) < 0)
        return -1;

    return ((int64_t) sv.f_bavail * sv.f_frsize);
}

int64_t FS_DiskSpace(const char* path)
{
    struct statvfs sv;

    if (statvfs(path, &sv) < 0)
        return -1;

    return ((int64_t) sv.f_blocks * sv.f_frsize);
}

int64_t FS_FileSize(const char* path)
{
    int64_t     ret;
    struct stat buf;

    ret = stat(path, &buf);
    if (ret < 0)
    {
        Log_Errno();
        return ret;
    }

    return buf.st_size;
}

bool FS_Rename(const char* src, const char* dst)
{
    int     ret;

    ret = rename(src, dst);
    if (ret < 0)
    {
        Log_Errno();
        return false;
    }

    return true;
}

void FS_Sync()
{
    sync();
}

char FS_Separator()
{
    return '/';
}

#else

/*
===============================================================================================

 Windows filesystem

===============================================================================================
*/

// TODO: Windows port

int64_t FS_FreeDiskSpace(const char* path)
{
    ULARGE_INTEGER  bytes;

    if (!GetDiskFreeSpaceEx(path, &bytes, NULL, NULL))
        return -1;
    return bytes.QuadPart;
}

char FS_Separator()
{
    return '\\';
}

#endif
<commit_msg>Added Windows specific filesystem implementation.<commit_after>#include "FileSystem.h"
#include "Log.h"

#include <string.h>

/*
===============================================================================================

 Platform independent functionality

===============================================================================================
*/

bool FS_IsSpecial(const char* path)
{
    size_t  len;

    len = strlen(path);

    if (len == 1 && path[0] == '.')
        return true;
    if (len == 2 && path[0] == '.' && path[1] == '.')
        return true;

    return false;
}

bool FS_RecDeleteDir(const char* path)
{
    FS_Dir      dir;
    FS_DirEntry entry;
    bool        ret;
    const char* dirname;
    size_t      pathlen;
    size_t      tmplen;
    char*       tmp;

    if (!FS_IsDirectory(path))
        return false;

    if (FS_IsSpecial(path))
        return false;

    dir = FS_OpenDir(path);
    if (dir == FS_INVALID_DIR)
        return false;

    pathlen = strlen(path);
    while ((entry = FS_ReadDir(dir)) != FS_INVALID_DIR_ENTRY)
    {
        dirname = FS_DirEntryName(entry);
        if (FS_IsSpecial(dirname))
            continue;

        // create relative path in temporary buffer
        tmplen = pathlen + 1 + strlen(dirname) + 1;
        tmp = new char[tmplen];
        memcpy(tmp, path, pathlen);
        tmp[pathlen] = FS_Separator();
        memcpy(tmp + pathlen + 1, dirname, strlen(dirname));
        tmp[tmplen - 1] = '\0';

        if (FS_IsDirectory(tmp))
            ret = FS_RecDeleteDir(tmp);
        else
            ret = FS_Delete(tmp);

        delete[] tmp;
        if (!ret)
            return false;
    }

    FS_CloseDir(dir);
    return FS_DeleteDir(path);
}

/*
===============================================================================================

 Unix compatible filesystem

===============================================================================================
*/

#ifndef PLATFORM_WINDOWS
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/uio.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <alloca.h>
#include <stdio.h>

FD FS_Open(const char* filename, int flags)
{
    int     mode;
    int     oflags;
    int     ret;

    mode = S_IRUSR | S_IWUSR;
    oflags = 0;
    if ((flags & FS_CREATE) == FS_CREATE)
        oflags |= O_CREAT;
    if ((flags & FS_READWRITE) == FS_READWRITE)
        oflags |= O_RDWR;
    if ((flags & FS_READONLY) == FS_READONLY)
        oflags |= O_RDONLY;

    ret = open(filename, oflags, mode);
    if (ret < 0)
    {
        Log_Errno();
        return INVALID_FD;
    }
    return ret;
}

void FS_FileClose(FD fd)
{
    int ret;

    ret = close(fd);
    if (ret < 0)
        Log_Errno();
}

int64_t FS_FileSeek(FD fd, uint64_t offset, int whence_)
{
    off_t   ret;
    int     whence;

    whence = -1;
    switch (whence_)
    {
    case FS_SEEK_SET:
        whence = SEEK_SET;
        break;
    case FS_SEEK_CUR:
        whence = SEEK_CUR;
        break;
    case FS_SEEK_END:
        whence = SEEK_END;
        break;
    default:
        return -1;
    }

    ret = lseek(fd, offset, whence);
    if (ret < 0)
        Log_Errno();
    return ret;
}

int FS_FileTruncate(FD fd, uint64_t length)
{
    int ret;

    ret = ftruncate(fd, length);
    if (ret < 0)
        Log_Errno();
    return ret;
}

int64_t FS_FileSize(FD fd)
{
    int64_t     ret;
    struct stat buf;

    ret = fstat(fd, &buf);
    if (ret < 0)
    {
        Log_Errno();
        return ret;
    }

    return buf.st_size;
}

ssize_t FS_FileWrite(FD fd, const void* buf, size_t count)
{
    ssize_t ret;

    ret = write(fd, buf, count);
    if (ret < 0)
        Log_Errno();
    return ret;
}

ssize_t FS_FileWriteVector(FD fd, unsigned num, const void** buf, size_t *count)
{
    ssize_t         ret;
    struct iovec    vecbuf[num];
    unsigned        i;

    for (i = 0; i < num; i++)
    {
        vecbuf[i].iov_base = (void*) buf[i];
        vecbuf[i].iov_len = count[i];
    }

    ret = writev(fd, vecbuf, num);
    if (ret < 0)
        Log_Errno();
    return ret;
}

ssize_t FS_FileRead(FD fd, void* buf, size_t count)
{
    ssize_t ret;

    ret = read(fd, buf, count);
    if (ret < 0)
        Log_Errno();
    return ret;
}

ssize_t FS_FileWriteOffs(FD fd, const void* buf, size_t count, uint64_t offset)
{
    ssize_t ret;

    ret = pwrite(fd, buf, count, offset);
    if (ret < 0)
        Log_Errno();
    return ret;
}

ssize_t FS_FileReadOffs(FD fd, void* buf, size_t count, uint64_t offset)
{
    ssize_t ret;

    ret = pread(fd, buf, count, offset);
    if (ret < 0)
        Log_Errno();
    return ret;
}

bool FS_Delete(const char* filename)
{
    int ret;

    ret = unlink(filename);
    if (ret < 0)
    {
        Log_Errno();
        return false;
    }

    return true;
}

FS_Dir FS_OpenDir(const char* filename)
{
    DIR*    dir;

    dir = opendir(filename);
    if (dir == NULL)
    {
        Log_Errno();
        return NULL;
    }

    return (FS_Dir) dir;
}

FS_DirEntry FS_ReadDir(FS_Dir dir)
{
    if ((DIR*) dir == NULL)
        return FS_INVALID_DIR_ENTRY;
    return (FS_DirEntry) readdir((DIR*) dir);
}

void FS_CloseDir(FS_Dir dir)
{
    int ret;

    if ((DIR*) dir == NULL)
        return;

    ret = closedir((DIR*) dir);
    if (ret < 0)
        Log_Errno();
}

bool FS_CreateDir(const char* filename)
{
    int ret;

    ret = mkdir(filename, S_IRUSR | S_IWUSR | S_IXUSR);
    if (ret < 0)
    {
        Log_Errno();
        return false;
    }
    return true;
}

bool FS_DeleteDir(const char* filename)
{
    int ret;

    ret = rmdir(filename);
    if (ret < 0)
    {
        Log_Errno();
        return false;
    }
    return true;
}

const char* FS_DirEntryName(FS_DirEntry dirent)
{
    return ((struct dirent*) dirent)->d_name;
}

bool FS_IsFile(const char* path)
{
    struct stat s;
    if (stat(path, &s) != 0)
        return false;
    if (s.st_mode & S_IFREG)
        return true;
    return false;
}

bool FS_IsDirectory(const char* path)
{
    struct stat s;
    if (stat(path, &s) != 0)
        return false;
    if (s.st_mode & S_IFDIR)
        return true;
    return false;
}

bool FS_Exists(const char* path)
{
    struct stat s;
    if (stat(path, &s) != 0)
        return false;
    return true;
}

int64_t FS_FreeDiskSpace(const char* path)
{
    struct statvfs sv;

    if (statvfs(path, &sv) < 0)
        return -1;

    return ((int64_t) sv.f_bavail * sv.f_frsize);
}

int64_t FS_DiskSpace(const char* path)
{
    struct statvfs sv;

    if (statvfs(path, &sv) < 0)
        return -1;

    return ((int64_t) sv.f_blocks * sv.f_frsize);
}

int64_t FS_FileSize(const char* path)
{
    int64_t     ret;
    struct stat buf;

    ret = stat(path, &buf);
    if (ret < 0)
    {
        Log_Errno();
        return ret;
    }

    return buf.st_size;
}

bool FS_Rename(const char* src, const char* dst)
{
    int     ret;

    ret = rename(src, dst);
    if (ret < 0)
    {
        Log_Errno();
        return false;
    }

    return true;
}

void FS_Sync()
{
    sync();
}

char FS_Separator()
{
    return '/';
}

#else

/*
===============================================================================================

 Windows filesystem

===============================================================================================
*/

#include <windows.h>
// TODO: Windows port

struct FS_Dir_Windows
{
    HANDLE              handle;
    LPWIN32_FIND_DATA   findData;
};

FD FS_Open(const char* filename, int flags)
{
    FD      fd;
    DWORD   dwCreationDisposition;
    DWORD   dwDesiredAccess;
    DWORD   dwFlagsAndAttributes;

    dwCreationDisposition = OPEN_EXISTING;
    if ((flags & FS_CREATE) == FS_CREATE)
        dwCreationDisposition = OPEN_ALWAYS;

    dwDesiredAccess = 0;
    if ((flags & FS_READONLY) == FS_READONLY)
        dwDesiredAccess = GENERIC_READ;
    if ((flags & FS_READWRITE) == FS_READWRITE)
        dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;

    // TODO: unbuffered file IO
    dwFlagsAndAttributes = FILE_ATTRIBUTE_ARCHIVE /* | FILE_FLAG_NO_BUFFERING */;

    fd = CreateFile(filename, dwDesiredAccess, 0, NULL,
     dwCreationDisposition, dwFlagsAndAttributes, NULL);

    if (fd == INVALID_HANDLE_VALUE)
    {
        Log_Errno();
        fd = INVALID_FD;
    }

    return fd;
}

void FS_FileClose(FD fd)
{
    BOOL    ret;

    ret = CloseHandle(fd);
    if (!ret)
        Log_Errno();
}

int64_t FS_FileSeek(FD fd, uint64_t offset, int whence_)
{
    BOOL            ret;
    DWORD           dwMoveMethod;
    LARGE_INTEGER   distanceToMove
    LARGE_INTEGER   newFilePointer;

    distanceToMove = (LARGE_INTEGER) offset;
    newFilePointer = 0;
    dwMoveMethod = -1;
    switch (whence_)
    {
    case FS_SEEK_SET:
        dwMoveMethod = FILE_BEGIN;
        break;
    case FS_SEEK_CUR:
        dwMoveMethod = FILE_CURRENT;
        break;
    case FS_SEEK_END:
        dwMoveMethod = FILE_END;
        break;
    default:
        return -1;
    }

    ret = SetFilePointerEx(fd, distanceToMove, &newFilePointer, dwMoveMethod);
    if (!ret)
    {
        Log_Errno();
        return -1;
    }

    return (int64_t) newFilePointer;
}

int FS_FileTruncate(FD fd, uint64_t length)
{
    BOOL    ret;

    if (!FS_FileSeek(fd, length, FS_SEEK_SET))
        return -1;

    ret = SetEndOfFile(fd);
    if (!ret)
    {
        Log_Errno();
        return -1;
    }

    return 0;
}

ssize_t FS_FileWrite(FD fd, const void* buf, size_t count)
{
    BOOL    ret;
    DWORD   numWritten;

    ret = WriteFile(fd, buf, (DWORD) count, &numWritten, NULL);
    if (!ret)
    {
        Log_Errno();
        return -1;
    }

    return (ssize_t) numWritten;
}

ssize_t FS_FileRead(FD fd, void* buf, size_t count)
{
    BOOL    ret;
    DWORD   numRead;

    ret = ReadFile(fd, buf, (DWORD) count, &numRead, NULL);
    if (!ret)
    {
        Log_Errno();
        return -1;
    }
    return (ssize_t) numRead;
}

ssize_t FS_FileWriteOffs(FD fd, const void* buf, size_t count, uint64_t offset)
{
    BOOL        ret;
    DWORD       numWritten;
    OVERLAPPED  overlapped;

    overlapped.OffsetHigh = offset;
    ret = WriteFile(fd, buf, (DWORD) count, &numWritten, &overlapped);
    if (!ret)
    {
        Log_Errno();
        return -1;
    }

    return (ssize_t) numWritten;
}

ssize_t FS_FileReadOffs(FD fd, void* buf, size_t count, uint64_t offset)
{
    BOOL    ret;
    DWORD   numRead;
    OVERLAPPED  overlapped;

    overlapped.OffsetHigh = offset;
    ret = ReadFile(fd, buf, (DWORD) count, &numRead, &overlapped);
    if (!ret)
    {
        Log_Errno();
        return -1;
    }
    return (ssize_t) numRead;
}

bool FS_Delete(const char* filename)
{
    BOOL    ret;

    ret = DeleteFile(filename);
    if (!ret)
    {
        Log_Errno();
        return false;
    }

    return true;
}

FS_Dir FS_OpenDir(const char* filename)
{
    FS_Dir_Windows*     dir;

    dir = new FS_Dir_Windows;
    dir->handle = FindFirstFile(filename, &dir->findData);
    if (dir->handle == NULL)
    {
        delete dir;
        Log_Errno();
        return NULL;
    }

    return (FS_Dir) dir;
}

FS_DirEntry FS_ReadDir(FS_Dir dir_)
{
    FS_Dir_Windows*     dir;

    dir = (FS_Dir_Windows*) dir_;
    if (dir == NULL)
        return FS_INVALID_DIR_ENTRY;
    if (!FindNextFile(dir->handle, &dir->findData))
        return FS_INVALID_DIR_ENTRY;

    return (FD_DirEntry) dir->findData;
}

void FS_CloseDir(FS_Dir dir)
{
    BOOL                ret;
    FS_Dir_Windows*     dir;

    dir = (FS_Dir_Windows*) dir_;
    if (dir == NULL)
        return;

    ret = FindClose(dir->handle);
    delete dir;

    if (!ret)
        Log_Errno();
}

bool FS_CreateDir(const char* filename)
{
    BOOL    ret;

    ret = CreateDirectory(filename, NULL);
    if (!ret)
    {
        Log_Errno();
        return false;
    }
    return true;
}

bool FS_DeleteDir(const char* filename)
{
    BOOL    ret;

    ret = RemoveDirectory(filename);
    if (!ret)
    {
        Log_Errno();
        return false;
    }
    return true;
}

const char* FS_DirEntryName(FS_DirEntry dirent)
{
    return ((WIN32_FIND_DATA*) dirent)->cFileName;
}

bool FS_IsFile(const char* path)
{
    GET_FILEEX_INFO_LEVELS      infoLevels;
    WIN32_FILE_ATTRIBUTE_DATA   attrData;
    BOOL                        ret;

    ret = GetFileAttributesEx(path, infoLevels, &attrData);
    if (!ret)
        return false;
    if ((attrData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0 &&
     (attrData.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) == 0)
        return true;

    return false;
}

bool FS_IsDirectory(const char* path)
{
    GET_FILEEX_INFO_LEVELS      infoLevels;
    WIN32_FILE_ATTRIBUTE_DATA   attrData;
    BOOL                        ret;

    ret = GetFileAttributesEx(path, infoLevels, &attrData);
    if (!ret)
        return false;
    if ((attrData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
        return true;

    return false;
}

bool FS_Exists(const char* path)
{
    GET_FILEEX_INFO_LEVELS      infoLevels;
    WIN32_FILE_ATTRIBUTE_DATA   attrData;
    BOOL                        ret;

    ret = GetFileAttributesEx(path, infoLevels, &attrData);
    if (!ret)
        return false;
    return true;
}

int64_t FS_FreeDiskSpace(const char* path)
{
    ULARGE_INTEGER  bytes;

    if (!GetDiskFreeSpaceEx(path, &bytes, NULL, NULL))
        return -1;
    return bytes.QuadPart;
}

int64_t FS_DiskSpace(const char* path)
{
    ULARGE_INTEGER  bytes;

    if (!GetDiskFreeSpaceEx(path, NULL, &bytes, NULL))
        return -1;
    return bytes.QuadPart;
}

int64_t FS_FileSize(const char* path)
{
    GET_FILEEX_INFO_LEVELS      infoLevels;
    WIN32_FILE_ATTRIBUTE_DATA   attrData;
    BOOL                        ret;

    ret = GetFileAttributesEx(path, infoLevels, &attrData);
    if (!ret)
        return -1;

    return (int64_t) attrData.nFileSizeHigh << 32 | attrData.nFileSizeLow;
}

bool FS_Rename(const char* src, const char* dst)
{
    BOOL    ret;

    ret = MoveFileEx(src, dst, MOVEFILE_WRITE_THROUGH);
    if (!ret)
    {
        Log_Errno();
        return false;
    }

    return true;
}

void FS_Sync()
{
    // TODO: To flush all open files on a volume, call FlushFileBuffers with a handle to the volume.
    // http://msdn.microsoft.com/en-us/library/aa364439(v=VS.85).aspx
}

char FS_Separator()
{
    return '\\';
}

#endif
<|endoftext|>


<commit_before>/*
    This file is part of Magnum.

    Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included
    in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.
*/

#include <array>
#include <vector>
#include <Containers/Array.h>

#include "Buffer.h"
#include "Context.h"
#include "Extensions.h"
#include "Test/AbstractOpenGLTester.h"

namespace Magnum { namespace Test {

class BufferGLTest: public AbstractOpenGLTester {
    public:
        explicit BufferGLTest();

        void construct();
        void data();
        void map();
        #ifdef MAGNUM_TARGET_GLES2
        void mapSub();
        #endif
        void mapRange();
        void mapRangeExplicitFlush();
        #ifndef MAGNUM_TARGET_GLES2
        void copy();
        #endif
        #ifndef MAGNUM_TARGET_GLES2
        void invalidate();
        #endif
};

BufferGLTest::BufferGLTest() {
    addTests({&BufferGLTest::construct,
              &BufferGLTest::data,
              &BufferGLTest::map,
              #ifdef MAGNUM_TARGET_GLES2
              &BufferGLTest::mapSub,
              #endif
              &BufferGLTest::mapRange,
              &BufferGLTest::mapRangeExplicitFlush,
              #ifndef MAGNUM_TARGET_GLES2
              &BufferGLTest::copy,
              #endif
              #ifndef MAGNUM_TARGET_GLES
              &BufferGLTest::invalidate
              #endif
              });
}

void BufferGLTest::construct() {
    Buffer buffer;
    MAGNUM_VERIFY_NO_ERROR();

    CORRADE_COMPARE(buffer.targetHint(), Buffer::Target::Array);

    CORRADE_COMPARE(buffer.size(), 0);
    MAGNUM_VERIFY_NO_ERROR();
}

void BufferGLTest::data() {
    Buffer buffer;

    /* Plain array */
    constexpr Int data[] = {2, 7, 5, 13, 25};
    buffer.setData({data, 5}, Buffer::Usage::StaticDraw);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /* STL vector */
    std::vector<Int> data2{2, 7, 5, 13, 25};
    buffer.setData(data2, Buffer::Usage::StaticDraw);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /* STL array */
    std::array<Int, 5> data3{2, 7, 5, 13, 25};
    buffer.setData(data3, Buffer::Usage::StaticDraw);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    const Containers::Array<Int> contents = buffer.data<Int>();
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(contents.size(), 5);
    CORRADE_COMPARE(contents[0], 2);
    CORRADE_COMPARE(contents[1], 7);
    CORRADE_COMPARE(contents[2], 5);
    CORRADE_COMPARE(contents[3], 13);
    CORRADE_COMPARE(contents[4], 25);
    #endif

    /* Plain array */
    constexpr Int subData[] = {125, 3, 15};
    buffer.setSubData(4, {subData, 3});
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /* STL vector */
    std::vector<Int> subData2{125, 3, 15};
    buffer.setSubData(4, subData2);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /* STL array */
    std::array<Int, 3> subData3{125, 3, 15};
    buffer.setSubData(4, subData3);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    const Containers::Array<Int> subContents = buffer.subData<Int>(4, 3);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(subContents.size(), 3);
    CORRADE_COMPARE(subContents[0], 125);
    CORRADE_COMPARE(subContents[1], 3);
    CORRADE_COMPARE(subContents[2], 15);
    #endif
}

#ifndef MAGNUM_TARGET_GLES3
void BufferGLTest::map() {
    #ifdef MAGNUM_TARGET_GLES2
    if(!Context::current()->isExtensionSupported<Extensions::GL::OES::mapbuffer>())
        CORRADE_SKIP(Extensions::GL::OES::mapbuffer::string() + std::string(" is not supported"));
    #endif
    Buffer buffer;

    constexpr char data[] = {2, 7, 5, 13, 25};
    buffer.setData(data, Buffer::Usage::StaticDraw);

    #ifndef MAGNUM_TARGET_GLES2
    char* contents = reinterpret_cast<char*>(buffer.map(Buffer::MapAccess::ReadWrite));
    #else
    char* contents = reinterpret_cast<char*>(buffer.map(Buffer::MapAccess::WriteOnly));
    #endif
    MAGNUM_VERIFY_NO_ERROR();

    CORRADE_VERIFY(contents);
    #ifndef MAGNUM_TARGET_GLES2
    CORRADE_COMPARE(contents[2], 5);
    #endif
    contents[3] = 107;

    CORRADE_VERIFY(buffer.unmap());
    MAGNUM_VERIFY_NO_ERROR();

    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    Containers::Array<char> changedContents = buffer.data<char>();
    CORRADE_COMPARE(changedContents.size(), 5);
    CORRADE_COMPARE(changedContents[3], 107);
    #endif
}
#endif

#ifdef MAGNUM_TARGET_GLES2
void BufferGLTest::mapSub() {
    if(!Context::current()->isExtensionSupported<Extensions::GL::CHROMIUM::map_sub>())
        CORRADE_SKIP(Extensions::GL::CHROMIUM::map_sub::string() + std::string(" is not supported"));

    Buffer buffer;

    constexpr char data[] = {2, 7, 5, 13, 25};
    buffer.setData(data, Buffer::Usage::StaticDraw);

    char* contents = reinterpret_cast<char*>(buffer.mapSub(1, 4, Buffer::MapAccess::WriteOnly));
    MAGNUM_VERIFY_NO_ERROR();

    CORRADE_VERIFY(contents);
    contents[3] = 107;

    buffer.unmapSub();
    MAGNUM_VERIFY_NO_ERROR();

    /** @todo How to verify the contents in ES? */
}
#endif

void BufferGLTest::mapRange() {
    #ifndef MAGNUM_TARGET_GLES2
    if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::map_buffer_range>())
        CORRADE_SKIP(Extensions::GL::ARB::map_buffer_range::string() + std::string(" is not supported"));
    #else
    if(!Context::current()->isExtensionSupported<Extensions::GL::EXT::map_buffer_range>())
        CORRADE_SKIP(Extensions::GL::EXT::map_buffer_range::string() + std::string(" is not supported"));
    #endif

    constexpr char data[] = {2, 7, 5, 13, 25};
    Buffer buffer;
    buffer.setData(data, Buffer::Usage::StaticDraw);

    char* contents = reinterpret_cast<char*>(buffer.map(1, 4, Buffer::MapFlag::Read|Buffer::MapFlag::Write));
    MAGNUM_VERIFY_NO_ERROR();

    CORRADE_VERIFY(contents);
    CORRADE_COMPARE(contents[2], 13);
    contents[3] = 107;

    CORRADE_VERIFY(buffer.unmap());
    MAGNUM_VERIFY_NO_ERROR();

    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    Containers::Array<char> changedContents = buffer.data<char>();
    CORRADE_COMPARE(changedContents.size(), 5);
    CORRADE_COMPARE(changedContents[4], 107);
    #endif
}

void BufferGLTest::mapRangeExplicitFlush() {
    #ifndef MAGNUM_TARGET_GLES2
    if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::map_buffer_range>())
        CORRADE_SKIP(Extensions::GL::ARB::map_buffer_range::string() + std::string(" is not supported"));
    #else
    if(!Context::current()->isExtensionSupported<Extensions::GL::EXT::map_buffer_range>())
        CORRADE_SKIP(Extensions::GL::EXT::map_buffer_range::string() + std::string(" is not supported"));
    #endif

    constexpr char data[] = {2, 7, 5, 13, 25};
    Buffer buffer;
    buffer.setData(data, Buffer::Usage::StaticDraw);

    /* Map, set byte, don't flush and unmap */
    char* contents = reinterpret_cast<char*>(buffer.map(1, 4, Buffer::MapFlag::Write|Buffer::MapFlag::FlushExplicit));
    CORRADE_VERIFY(contents);
    contents[2] = 99;
    CORRADE_VERIFY(buffer.unmap());
    MAGNUM_VERIFY_NO_ERROR();

    /* Unflushed range _might_ not be changed, thus nothing to test */

    /* Map, set byte, flush and unmap */
    contents = reinterpret_cast<char*>(buffer.map(1, 4, Buffer::MapFlag::Write|Buffer::MapFlag::FlushExplicit));
    CORRADE_VERIFY(contents);
    contents[3] = 107;
    buffer.flushMappedRange(3, 1);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_VERIFY(buffer.unmap());
    MAGNUM_VERIFY_NO_ERROR();

    /* Flushed range should be changed */
    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    Containers::Array<char> changedContents = buffer.data<char>();
    CORRADE_COMPARE(changedContents.size(), 5);
    CORRADE_COMPARE(changedContents[4], 107);
    #endif
}

#ifndef MAGNUM_TARGET_GLES2
void BufferGLTest::copy() {
    Buffer buffer1;
    constexpr char data[] = {2, 7, 5, 13, 25};
    buffer1.setData(data, Buffer::Usage::StaticDraw);

    Buffer buffer2;
    buffer2.setData({nullptr, 5}, Buffer::Usage::StaticDraw);

    Buffer::copy(buffer1, buffer2, 1, 2, 3);
    MAGNUM_VERIFY_NO_ERROR();

    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    const Containers::Array<char> subContents = buffer2.subData<char>(2, 3);
    CORRADE_COMPARE(subContents.size(), 3);
    CORRADE_COMPARE(subContents[0], 7);
    CORRADE_COMPARE(subContents[1], 5);
    CORRADE_COMPARE(subContents[2], 13);
    #endif
}
#endif

#ifndef MAGNUM_TARGET_GLES
void BufferGLTest::invalidate() {
    if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::invalidate_subdata>())
        CORRADE_SKIP(Extensions::GL::ARB::invalidate_subdata::string() + std::string(" is not supported"));

    Buffer buffer;
    constexpr char data[] = {2, 7, 5, 13, 25};
    buffer.setData(data, Buffer::Usage::StaticDraw);

    /* Just test that no errors are emitted */

    buffer.invalidateSubData(3, 2);
    MAGNUM_VERIFY_NO_ERROR();

    buffer.invalidateData();
    MAGNUM_VERIFY_NO_ERROR();
}
#endif

}}

CORRADE_TEST_MAIN(Magnum::Test::BufferGLTest)
<commit_msg>Test: fixed std::array construction.<commit_after>/*
    This file is part of Magnum.

    Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included
    in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.
*/

#include <array>
#include <vector>
#include <Containers/Array.h>

#include "Buffer.h"
#include "Context.h"
#include "Extensions.h"
#include "Test/AbstractOpenGLTester.h"

namespace Magnum { namespace Test {

class BufferGLTest: public AbstractOpenGLTester {
    public:
        explicit BufferGLTest();

        void construct();
        void data();
        void map();
        #ifdef MAGNUM_TARGET_GLES2
        void mapSub();
        #endif
        void mapRange();
        void mapRangeExplicitFlush();
        #ifndef MAGNUM_TARGET_GLES2
        void copy();
        #endif
        #ifndef MAGNUM_TARGET_GLES2
        void invalidate();
        #endif
};

BufferGLTest::BufferGLTest() {
    addTests({&BufferGLTest::construct,
              &BufferGLTest::data,
              &BufferGLTest::map,
              #ifdef MAGNUM_TARGET_GLES2
              &BufferGLTest::mapSub,
              #endif
              &BufferGLTest::mapRange,
              &BufferGLTest::mapRangeExplicitFlush,
              #ifndef MAGNUM_TARGET_GLES2
              &BufferGLTest::copy,
              #endif
              #ifndef MAGNUM_TARGET_GLES
              &BufferGLTest::invalidate
              #endif
              });
}

void BufferGLTest::construct() {
    Buffer buffer;
    MAGNUM_VERIFY_NO_ERROR();

    CORRADE_COMPARE(buffer.targetHint(), Buffer::Target::Array);

    CORRADE_COMPARE(buffer.size(), 0);
    MAGNUM_VERIFY_NO_ERROR();
}

void BufferGLTest::data() {
    Buffer buffer;

    /* Plain array */
    constexpr Int data[] = {2, 7, 5, 13, 25};
    buffer.setData({data, 5}, Buffer::Usage::StaticDraw);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /* STL vector */
    std::vector<Int> data2{2, 7, 5, 13, 25};
    buffer.setData(data2, Buffer::Usage::StaticDraw);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /* STL array */
    std::array<Int, 5> data3{{2, 7, 5, 13, 25}};
    buffer.setData(data3, Buffer::Usage::StaticDraw);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    const Containers::Array<Int> contents = buffer.data<Int>();
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(contents.size(), 5);
    CORRADE_COMPARE(contents[0], 2);
    CORRADE_COMPARE(contents[1], 7);
    CORRADE_COMPARE(contents[2], 5);
    CORRADE_COMPARE(contents[3], 13);
    CORRADE_COMPARE(contents[4], 25);
    #endif

    /* Plain array */
    constexpr Int subData[] = {125, 3, 15};
    buffer.setSubData(4, {subData, 3});
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /* STL vector */
    std::vector<Int> subData2{125, 3, 15};
    buffer.setSubData(4, subData2);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /* STL array */
    std::array<Int, 3> subData3{{125, 3, 15}};
    buffer.setSubData(4, subData3);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    const Containers::Array<Int> subContents = buffer.subData<Int>(4, 3);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(subContents.size(), 3);
    CORRADE_COMPARE(subContents[0], 125);
    CORRADE_COMPARE(subContents[1], 3);
    CORRADE_COMPARE(subContents[2], 15);
    #endif
}

#ifndef MAGNUM_TARGET_GLES3
void BufferGLTest::map() {
    #ifdef MAGNUM_TARGET_GLES2
    if(!Context::current()->isExtensionSupported<Extensions::GL::OES::mapbuffer>())
        CORRADE_SKIP(Extensions::GL::OES::mapbuffer::string() + std::string(" is not supported"));
    #endif
    Buffer buffer;

    constexpr char data[] = {2, 7, 5, 13, 25};
    buffer.setData(data, Buffer::Usage::StaticDraw);

    #ifndef MAGNUM_TARGET_GLES2
    char* contents = reinterpret_cast<char*>(buffer.map(Buffer::MapAccess::ReadWrite));
    #else
    char* contents = reinterpret_cast<char*>(buffer.map(Buffer::MapAccess::WriteOnly));
    #endif
    MAGNUM_VERIFY_NO_ERROR();

    CORRADE_VERIFY(contents);
    #ifndef MAGNUM_TARGET_GLES2
    CORRADE_COMPARE(contents[2], 5);
    #endif
    contents[3] = 107;

    CORRADE_VERIFY(buffer.unmap());
    MAGNUM_VERIFY_NO_ERROR();

    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    Containers::Array<char> changedContents = buffer.data<char>();
    CORRADE_COMPARE(changedContents.size(), 5);
    CORRADE_COMPARE(changedContents[3], 107);
    #endif
}
#endif

#ifdef MAGNUM_TARGET_GLES2
void BufferGLTest::mapSub() {
    if(!Context::current()->isExtensionSupported<Extensions::GL::CHROMIUM::map_sub>())
        CORRADE_SKIP(Extensions::GL::CHROMIUM::map_sub::string() + std::string(" is not supported"));

    Buffer buffer;

    constexpr char data[] = {2, 7, 5, 13, 25};
    buffer.setData(data, Buffer::Usage::StaticDraw);

    char* contents = reinterpret_cast<char*>(buffer.mapSub(1, 4, Buffer::MapAccess::WriteOnly));
    MAGNUM_VERIFY_NO_ERROR();

    CORRADE_VERIFY(contents);
    contents[3] = 107;

    buffer.unmapSub();
    MAGNUM_VERIFY_NO_ERROR();

    /** @todo How to verify the contents in ES? */
}
#endif

void BufferGLTest::mapRange() {
    #ifndef MAGNUM_TARGET_GLES2
    if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::map_buffer_range>())
        CORRADE_SKIP(Extensions::GL::ARB::map_buffer_range::string() + std::string(" is not supported"));
    #else
    if(!Context::current()->isExtensionSupported<Extensions::GL::EXT::map_buffer_range>())
        CORRADE_SKIP(Extensions::GL::EXT::map_buffer_range::string() + std::string(" is not supported"));
    #endif

    constexpr char data[] = {2, 7, 5, 13, 25};
    Buffer buffer;
    buffer.setData(data, Buffer::Usage::StaticDraw);

    char* contents = reinterpret_cast<char*>(buffer.map(1, 4, Buffer::MapFlag::Read|Buffer::MapFlag::Write));
    MAGNUM_VERIFY_NO_ERROR();

    CORRADE_VERIFY(contents);
    CORRADE_COMPARE(contents[2], 13);
    contents[3] = 107;

    CORRADE_VERIFY(buffer.unmap());
    MAGNUM_VERIFY_NO_ERROR();

    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    Containers::Array<char> changedContents = buffer.data<char>();
    CORRADE_COMPARE(changedContents.size(), 5);
    CORRADE_COMPARE(changedContents[4], 107);
    #endif
}

void BufferGLTest::mapRangeExplicitFlush() {
    #ifndef MAGNUM_TARGET_GLES2
    if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::map_buffer_range>())
        CORRADE_SKIP(Extensions::GL::ARB::map_buffer_range::string() + std::string(" is not supported"));
    #else
    if(!Context::current()->isExtensionSupported<Extensions::GL::EXT::map_buffer_range>())
        CORRADE_SKIP(Extensions::GL::EXT::map_buffer_range::string() + std::string(" is not supported"));
    #endif

    constexpr char data[] = {2, 7, 5, 13, 25};
    Buffer buffer;
    buffer.setData(data, Buffer::Usage::StaticDraw);

    /* Map, set byte, don't flush and unmap */
    char* contents = reinterpret_cast<char*>(buffer.map(1, 4, Buffer::MapFlag::Write|Buffer::MapFlag::FlushExplicit));
    CORRADE_VERIFY(contents);
    contents[2] = 99;
    CORRADE_VERIFY(buffer.unmap());
    MAGNUM_VERIFY_NO_ERROR();

    /* Unflushed range _might_ not be changed, thus nothing to test */

    /* Map, set byte, flush and unmap */
    contents = reinterpret_cast<char*>(buffer.map(1, 4, Buffer::MapFlag::Write|Buffer::MapFlag::FlushExplicit));
    CORRADE_VERIFY(contents);
    contents[3] = 107;
    buffer.flushMappedRange(3, 1);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_VERIFY(buffer.unmap());
    MAGNUM_VERIFY_NO_ERROR();

    /* Flushed range should be changed */
    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    Containers::Array<char> changedContents = buffer.data<char>();
    CORRADE_COMPARE(changedContents.size(), 5);
    CORRADE_COMPARE(changedContents[4], 107);
    #endif
}

#ifndef MAGNUM_TARGET_GLES2
void BufferGLTest::copy() {
    Buffer buffer1;
    constexpr char data[] = {2, 7, 5, 13, 25};
    buffer1.setData(data, Buffer::Usage::StaticDraw);

    Buffer buffer2;
    buffer2.setData({nullptr, 5}, Buffer::Usage::StaticDraw);

    Buffer::copy(buffer1, buffer2, 1, 2, 3);
    MAGNUM_VERIFY_NO_ERROR();

    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    const Containers::Array<char> subContents = buffer2.subData<char>(2, 3);
    CORRADE_COMPARE(subContents.size(), 3);
    CORRADE_COMPARE(subContents[0], 7);
    CORRADE_COMPARE(subContents[1], 5);
    CORRADE_COMPARE(subContents[2], 13);
    #endif
}
#endif

#ifndef MAGNUM_TARGET_GLES
void BufferGLTest::invalidate() {
    if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::invalidate_subdata>())
        CORRADE_SKIP(Extensions::GL::ARB::invalidate_subdata::string() + std::string(" is not supported"));

    Buffer buffer;
    constexpr char data[] = {2, 7, 5, 13, 25};
    buffer.setData(data, Buffer::Usage::StaticDraw);

    /* Just test that no errors are emitted */

    buffer.invalidateSubData(3, 2);
    MAGNUM_VERIFY_NO_ERROR();

    buffer.invalidateData();
    MAGNUM_VERIFY_NO_ERROR();
}
#endif

}}

CORRADE_TEST_MAIN(Magnum::Test::BufferGLTest)
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sc.hxx"
#include "xiname.hxx"
#include "rangenam.hxx"
#include "xistream.hxx"

// for formula compiler
#include "excform.hxx"
// for filter manager
#include "excimp8.hxx"
#include "scextopt.hxx"
#include "document.hxx"
// ============================================================================
// *** Implementation ***
// ============================================================================

XclImpName::XclImpName( XclImpStream& rStrm, sal_uInt16 nXclNameIdx ) :
    XclImpRoot( rStrm.GetRoot() ),
    mpScData( 0 ),
    mcBuiltIn( EXC_BUILTIN_UNKNOWN ),
    mnScTab( SCTAB_MAX ),
    mbFunction( false ),
    mbVBName( false )
{
    ExcelToSc& rFmlaConv = GetOldFmlaConverter();

    // 1) *** read data from stream *** ---------------------------------------

    sal_uInt16 nFlags = 0, nFmlaSize = 0, nExtSheet = EXC_NAME_GLOBAL, nXclTab = EXC_NAME_GLOBAL;
    sal_uInt8 nNameLen = 0, nShortCut;

    switch( GetBiff() )
    {
        case EXC_BIFF2:
        {
            sal_uInt8 nFlagsBiff2;
            rStrm >> nFlagsBiff2;
            rStrm.Ignore( 1 );
            rStrm >> nShortCut >> nNameLen;
            nFmlaSize = rStrm.ReaduInt8();
            ::set_flag( nFlags, EXC_NAME_FUNC, ::get_flag( nFlagsBiff2, EXC_NAME2_FUNC ) );
        }
        break;

        case EXC_BIFF3:
        case EXC_BIFF4:
        {
            rStrm >> nFlags >> nShortCut >> nNameLen >> nFmlaSize;
        }
        break;

        case EXC_BIFF5:
        case EXC_BIFF8:
        {
            rStrm >> nFlags >> nShortCut >> nNameLen >> nFmlaSize >> nExtSheet >> nXclTab;
            rStrm.Ignore( 4 );
        }
        break;

        default: DBG_ERROR_BIFF();
    }

    if( GetBiff() <= EXC_BIFF5 )
        maXclName = rStrm.ReadRawByteString( nNameLen );
    else
        maXclName = rStrm.ReadUniString( nNameLen );

    // 2) *** convert sheet index and name *** --------------------------------

    // functions and VBA
    mbFunction = ::get_flag( nFlags, EXC_NAME_FUNC );
    mbVBName = ::get_flag( nFlags, EXC_NAME_VB );

    // get built-in name, or convert characters invalid in Calc
    bool bBuiltIn = ::get_flag( nFlags, EXC_NAME_BUILTIN );

    // special case for BIFF5 filter range - name appears as plain text without built-in flag
    if( (GetBiff() == EXC_BIFF5) && (maXclName == XclTools::GetXclBuiltInDefName( EXC_BUILTIN_FILTERDATABASE )) )
    {
        bBuiltIn = true;
        maXclName.Assign( EXC_BUILTIN_FILTERDATABASE );
    }

    // convert Excel name to Calc name
    if( mbVBName )
    {
        // VB macro name
        maScName = maXclName;
    }
    else if( bBuiltIn )
    {
        // built-in name
        if( maXclName.Len() )
            mcBuiltIn = maXclName.GetChar( 0 );
        if( mcBuiltIn == '?' )      // NUL character is imported as '?'
            mcBuiltIn = '\0';
        maScName = XclTools::GetBuiltInDefName( mcBuiltIn );
    }
    else
    {
        // any other name
        maScName = maXclName;
        ScfTools::ConvertToScDefinedName( maScName );
    }
    rtl::OUString aRealOrigName = maScName;

    // add index for local names
    if( nXclTab != EXC_NAME_GLOBAL )
    {
        sal_uInt16 nUsedTab = (GetBiff() == EXC_BIFF8) ? nXclTab : nExtSheet;
        // do not rename sheet-local names by default, this breaks VBA scripts
//        maScName.Append( '_' ).Append( String::CreateFromInt32( nUsedTab ) );
        // TODO: may not work for BIFF5, handle skipped sheets (all BIFF)
        mnScTab = static_cast< SCTAB >( nUsedTab - 1 );
    }

    // 3) *** convert the name definition formula *** -------------------------

    rFmlaConv.Reset();
    const ScTokenArray* pTokArr = 0; // pointer to token array, owned by rFmlaConv
    RangeType nNameType = RT_NAME;

    if( ::get_flag( nFlags, EXC_NAME_BIG ) )
    {
        // special, unsupported name
        rFmlaConv.GetDummy( pTokArr );
    }
    else if( bBuiltIn )
    {
        SCsTAB const nLocalTab = (nXclTab == EXC_NAME_GLOBAL) ? SCTAB_MAX : (nXclTab - 1);

        // --- print ranges or title ranges ---
        rStrm.PushPosition();
        switch( mcBuiltIn )
        {
            case EXC_BUILTIN_PRINTAREA:
                if( rFmlaConv.Convert( GetPrintAreaBuffer(), rStrm, nFmlaSize, nLocalTab, FT_RangeName ) == ConvOK )
                    nNameType |= RT_PRINTAREA;
            break;
            case EXC_BUILTIN_PRINTTITLES:
                if( rFmlaConv.Convert( GetTitleAreaBuffer(), rStrm, nFmlaSize, nLocalTab, FT_RangeName ) == ConvOK )
                    nNameType |= RT_COLHEADER | RT_ROWHEADER;
            break;
        }
        rStrm.PopPosition();

        // --- name formula ---
        // JEG : double check this.  It is clearly false for normal names
        //  but some of the builtins (sheettitle?) might be able to handle arrays
        rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize, false, FT_RangeName );

        // --- auto or advanced filter ---
        if( (GetBiff() == EXC_BIFF8) && pTokArr && bBuiltIn )
        {
            ScRange aRange;
            if( pTokArr->IsReference( aRange ) )
            {
                switch( mcBuiltIn )
                {
                    case EXC_BUILTIN_FILTERDATABASE:
                        GetFilterManager().Insert( &GetOldRoot(), aRange);
                    break;
                    case EXC_BUILTIN_CRITERIA:
                        GetFilterManager().AddAdvancedRange( aRange );
                        nNameType |= RT_CRITERIA;
                    break;
                    case EXC_BUILTIN_EXTRACT:
                        if( pTokArr->IsValidReference( aRange ) )
                            GetFilterManager().AddExtractPos( aRange );
                    break;
                }
            }
        }
    }
    else if( nFmlaSize > 0 )
    {
        // regular defined name
        rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize, true, FT_RangeName );
    }

    // 4) *** create a defined name in the Calc document *** ------------------

    // do not ignore hidden names (may be regular names created by VBA scripts)
    if( pTokArr /*&& (bBuiltIn || !::get_flag( nFlags, EXC_NAME_HIDDEN ))*/ && !mbFunction && !mbVBName )
    {
        // create the Calc name data
        ScRangeData* pData = new ScRangeData( GetDocPtr(), maScName, *pTokArr, ScAddress(), nNameType );
        pData->GuessPosition();             // calculate base position for relative refs
        pData->SetIndex( nXclNameIdx );     // used as unique identifier in formulas
        if (nXclTab == EXC_NAME_GLOBAL)
            GetDoc().GetRangeName()->insert(pData);
        else
        {
            ScRangeName* pLocalNames = GetDoc().GetRangeName(mnScTab);
            if (pLocalNames)
                pLocalNames->insert(pData);

            if (GetBiff() == EXC_BIFF8)
            {
                ScRange aRange;
                // discard deleted ranges ( for the moment at least )
                if ( pData->IsValidReference( aRange ) )
                {
                    GetExtDocOptions().GetOrCreateTabSettings( nXclTab );
                }
            }
        }
        mpScData = pData;                   // cache for later use
    }
}

// ----------------------------------------------------------------------------

XclImpNameManager::XclImpNameManager( const XclImpRoot& rRoot ) :
    XclImpRoot( rRoot )
{
}

void XclImpNameManager::ReadName( XclImpStream& rStrm )
{
    sal_uLong nCount = maNameList.size();
    if( nCount < 0xFFFF )
        maNameList.push_back( new XclImpName( rStrm, static_cast< sal_uInt16 >( nCount + 1 ) ) );
}

const XclImpName* XclImpNameManager::FindName( const String& rXclName, SCTAB nScTab ) const
{
    const XclImpName* pGlobalName = 0;   // a found global name
    const XclImpName* pLocalName = 0;    // a found local name
    for( XclImpNameList::const_iterator itName = maNameList.begin(); itName != maNameList.end() && !pLocalName; ++itName )
    {
        if( itName->GetXclName() == rXclName )
        {
            if( itName->GetScTab() == nScTab )
                pLocalName = &(*itName);
            else if( itName->IsGlobal() )
                pGlobalName = &(*itName);
        }
    }
    return pLocalName ? pLocalName : pGlobalName;
}

const XclImpName* XclImpNameManager::GetName( sal_uInt16 nXclNameIdx ) const
{
    DBG_ASSERT( nXclNameIdx > 0, "XclImpNameManager::GetName - index must be >0" );
    return &(maNameList.at( nXclNameIdx - 1 ));
}

// ============================================================================

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>Fix for fdo#37322 (binary import crash)<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sc.hxx"
#include "xiname.hxx"
#include "rangenam.hxx"
#include "xistream.hxx"

// for formula compiler
#include "excform.hxx"
// for filter manager
#include "excimp8.hxx"
#include "scextopt.hxx"
#include "document.hxx"
// ============================================================================
// *** Implementation ***
// ============================================================================

XclImpName::XclImpName( XclImpStream& rStrm, sal_uInt16 nXclNameIdx ) :
    XclImpRoot( rStrm.GetRoot() ),
    mpScData( 0 ),
    mcBuiltIn( EXC_BUILTIN_UNKNOWN ),
    mnScTab( SCTAB_MAX ),
    mbFunction( false ),
    mbVBName( false )
{
    ExcelToSc& rFmlaConv = GetOldFmlaConverter();

    // 1) *** read data from stream *** ---------------------------------------

    sal_uInt16 nFlags = 0, nFmlaSize = 0, nExtSheet = EXC_NAME_GLOBAL, nXclTab = EXC_NAME_GLOBAL;
    sal_uInt8 nNameLen = 0, nShortCut;

    switch( GetBiff() )
    {
        case EXC_BIFF2:
        {
            sal_uInt8 nFlagsBiff2;
            rStrm >> nFlagsBiff2;
            rStrm.Ignore( 1 );
            rStrm >> nShortCut >> nNameLen;
            nFmlaSize = rStrm.ReaduInt8();
            ::set_flag( nFlags, EXC_NAME_FUNC, ::get_flag( nFlagsBiff2, EXC_NAME2_FUNC ) );
        }
        break;

        case EXC_BIFF3:
        case EXC_BIFF4:
        {
            rStrm >> nFlags >> nShortCut >> nNameLen >> nFmlaSize;
        }
        break;

        case EXC_BIFF5:
        case EXC_BIFF8:
        {
            rStrm >> nFlags >> nShortCut >> nNameLen >> nFmlaSize >> nExtSheet >> nXclTab;
            rStrm.Ignore( 4 );
        }
        break;

        default: DBG_ERROR_BIFF();
    }

    if( GetBiff() <= EXC_BIFF5 )
        maXclName = rStrm.ReadRawByteString( nNameLen );
    else
        maXclName = rStrm.ReadUniString( nNameLen );

    // 2) *** convert sheet index and name *** --------------------------------

    // functions and VBA
    mbFunction = ::get_flag( nFlags, EXC_NAME_FUNC );
    mbVBName = ::get_flag( nFlags, EXC_NAME_VB );

    // get built-in name, or convert characters invalid in Calc
    bool bBuiltIn = ::get_flag( nFlags, EXC_NAME_BUILTIN );

    // special case for BIFF5 filter range - name appears as plain text without built-in flag
    if( (GetBiff() == EXC_BIFF5) && (maXclName == XclTools::GetXclBuiltInDefName( EXC_BUILTIN_FILTERDATABASE )) )
    {
        bBuiltIn = true;
        maXclName.Assign( EXC_BUILTIN_FILTERDATABASE );
    }

    // convert Excel name to Calc name
    if( mbVBName )
    {
        // VB macro name
        maScName = maXclName;
    }
    else if( bBuiltIn )
    {
        // built-in name
        if( maXclName.Len() )
            mcBuiltIn = maXclName.GetChar( 0 );
        if( mcBuiltIn == '?' )      // NUL character is imported as '?'
            mcBuiltIn = '\0';
        maScName = XclTools::GetBuiltInDefName( mcBuiltIn );
    }
    else
    {
        // any other name
        maScName = maXclName;
        ScfTools::ConvertToScDefinedName( maScName );
    }
    rtl::OUString aRealOrigName = maScName;

    // add index for local names
    if( nXclTab != EXC_NAME_GLOBAL )
    {
        sal_uInt16 nUsedTab = (GetBiff() == EXC_BIFF8) ? nXclTab : nExtSheet;
        // do not rename sheet-local names by default, this breaks VBA scripts
//        maScName.Append( '_' ).Append( String::CreateFromInt32( nUsedTab ) );
        // TODO: may not work for BIFF5, handle skipped sheets (all BIFF)
        mnScTab = static_cast< SCTAB >( nUsedTab - 1 );
    }

    // 3) *** convert the name definition formula *** -------------------------

    rFmlaConv.Reset();
    const ScTokenArray* pTokArr = 0; // pointer to token array, owned by rFmlaConv
    RangeType nNameType = RT_NAME;

    if( ::get_flag( nFlags, EXC_NAME_BIG ) )
    {
        // special, unsupported name
        rFmlaConv.GetDummy( pTokArr );
    }
    else if( bBuiltIn )
    {
        SCsTAB const nLocalTab = (nXclTab == EXC_NAME_GLOBAL) ? SCTAB_MAX : (nXclTab - 1);

        // --- print ranges or title ranges ---
        rStrm.PushPosition();
        switch( mcBuiltIn )
        {
            case EXC_BUILTIN_PRINTAREA:
                if( rFmlaConv.Convert( GetPrintAreaBuffer(), rStrm, nFmlaSize, nLocalTab, FT_RangeName ) == ConvOK )
                    nNameType |= RT_PRINTAREA;
            break;
            case EXC_BUILTIN_PRINTTITLES:
                if( rFmlaConv.Convert( GetTitleAreaBuffer(), rStrm, nFmlaSize, nLocalTab, FT_RangeName ) == ConvOK )
                    nNameType |= RT_COLHEADER | RT_ROWHEADER;
            break;
        }
        rStrm.PopPosition();

        // --- name formula ---
        // JEG : double check this.  It is clearly false for normal names
        //  but some of the builtins (sheettitle?) might be able to handle arrays
        rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize, false, FT_RangeName );

        // --- auto or advanced filter ---
        if( (GetBiff() == EXC_BIFF8) && pTokArr && bBuiltIn )
        {
            ScRange aRange;
            if( pTokArr->IsReference( aRange ) )
            {
                switch( mcBuiltIn )
                {
                    case EXC_BUILTIN_FILTERDATABASE:
                        GetFilterManager().Insert( &GetOldRoot(), aRange);
                    break;
                    case EXC_BUILTIN_CRITERIA:
                        GetFilterManager().AddAdvancedRange( aRange );
                        nNameType |= RT_CRITERIA;
                    break;
                    case EXC_BUILTIN_EXTRACT:
                        if( pTokArr->IsValidReference( aRange ) )
                            GetFilterManager().AddExtractPos( aRange );
                    break;
                }
            }
        }
    }
    else if( nFmlaSize > 0 )
    {
        // regular defined name
        rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize, true, FT_RangeName );
    }

    // 4) *** create a defined name in the Calc document *** ------------------

    // do not ignore hidden names (may be regular names created by VBA scripts)
    if( pTokArr /*&& (bBuiltIn || !::get_flag( nFlags, EXC_NAME_HIDDEN ))*/ && !mbFunction && !mbVBName )
    {
        // create the Calc name data
        ScRangeData* pData = new ScRangeData( GetDocPtr(), maScName, *pTokArr, ScAddress(), nNameType );
        pData->GuessPosition();             // calculate base position for relative refs
        pData->SetIndex( nXclNameIdx );     // used as unique identifier in formulas
        if (nXclTab == EXC_NAME_GLOBAL)
            GetDoc().GetRangeName()->insert(pData);
        else
        {
            ScRangeName* pLocalNames = GetDoc().GetRangeName(mnScTab);
            if (pLocalNames)
                pLocalNames->insert(pData);

            if (GetBiff() == EXC_BIFF8)
            {
                ScRange aRange;
                // discard deleted ranges ( for the moment at least )
                if ( pData->IsValidReference( aRange ) )
                {
                    GetExtDocOptions().GetOrCreateTabSettings( nXclTab );
                }
            }
        }
        mpScData = pData;                   // cache for later use
    }
}

// ----------------------------------------------------------------------------

XclImpNameManager::XclImpNameManager( const XclImpRoot& rRoot ) :
    XclImpRoot( rRoot )
{
}

void XclImpNameManager::ReadName( XclImpStream& rStrm )
{
    sal_uLong nCount = maNameList.size();
    if( nCount < 0xFFFF )
        maNameList.push_back( new XclImpName( rStrm, static_cast< sal_uInt16 >( nCount + 1 ) ) );
}

const XclImpName* XclImpNameManager::FindName( const String& rXclName, SCTAB nScTab ) const
{
    const XclImpName* pGlobalName = 0;   // a found global name
    const XclImpName* pLocalName = 0;    // a found local name
    for( XclImpNameList::const_iterator itName = maNameList.begin(); itName != maNameList.end() && !pLocalName; ++itName )
    {
        if( itName->GetXclName() == rXclName )
        {
            if( itName->GetScTab() == nScTab )
                pLocalName = &(*itName);
            else if( itName->IsGlobal() )
                pGlobalName = &(*itName);
        }
    }
    return pLocalName ? pLocalName : pGlobalName;
}

const XclImpName* XclImpNameManager::GetName( sal_uInt16 nXclNameIdx ) const
{
    DBG_ASSERT( nXclNameIdx > 0, "XclImpNameManager::GetName - index must be >0" );
    return ( nXclNameIdx >= maNameList.size() ) ? NULL : &(maNameList.at( nXclNameIdx - 1 ));
}

// ============================================================================

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before>/*************************************************************************/
/*  collision_polygon_2d.cpp                                             */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md)    */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/
#include "collision_polygon_2d.h"

#include "collision_object_2d.h"
#include "engine.h"
#include "scene/resources/concave_polygon_shape_2d.h"
#include "scene/resources/convex_polygon_shape_2d.h"

#include "thirdparty/misc/triangulator.h"

void CollisionPolygon2D::_build_polygon() {

	parent->shape_owner_clear_shapes(owner_id);

	if (polygon.size() == 0)
		return;

	bool solids = build_mode == BUILD_SOLIDS;

	if (solids) {

		//here comes the sun, lalalala
		//decompose concave into multiple convex polygons and add them
		Vector<Vector<Vector2> > decomp = _decompose_in_convex();
		for (int i = 0; i < decomp.size(); i++) {
			Ref<ConvexPolygonShape2D> convex = memnew(ConvexPolygonShape2D);
			convex->set_points(decomp[i]);
			parent->shape_owner_add_shape(owner_id, convex);
		}

	} else {

		Ref<ConcavePolygonShape2D> concave = memnew(ConcavePolygonShape2D);

		PoolVector<Vector2> segments;
		segments.resize(polygon.size() * 2);
		PoolVector<Vector2>::Write w = segments.write();

		for (int i = 0; i < polygon.size(); i++) {
			w[(i << 1) + 0] = polygon[i];
			w[(i << 1) + 1] = polygon[(i + 1) % polygon.size()];
		}

		w = PoolVector<Vector2>::Write();
		concave->set_segments(segments);

		parent->shape_owner_add_shape(owner_id, concave);
	}
}

Vector<Vector<Vector2> > CollisionPolygon2D::_decompose_in_convex() {

	Vector<Vector<Vector2> > decomp;
	List<TriangulatorPoly> in_poly, out_poly;

	TriangulatorPoly inp;
	inp.Init(polygon.size());
	for (int i = 0; i < polygon.size(); i++) {
		inp.GetPoint(i) = polygon[i];
	}
	inp.SetOrientation(TRIANGULATOR_CCW);
	in_poly.push_back(inp);
	TriangulatorPartition tpart;
	if (tpart.ConvexPartition_HM(&in_poly, &out_poly) == 0) { //failed!
		ERR_PRINT("Convex decomposing failed!");
		return decomp;
	}

	decomp.resize(out_poly.size());
	int idx = 0;

	for (List<TriangulatorPoly>::Element *I = out_poly.front(); I; I = I->next()) {

		TriangulatorPoly &tp = I->get();

		decomp[idx].resize(tp.GetNumPoints());

		for (int i = 0; i < tp.GetNumPoints(); i++) {

			decomp[idx][i] = tp.GetPoint(i);
		}

		idx++;
	}

	return decomp;
}

void CollisionPolygon2D::_notification(int p_what) {

	switch (p_what) {
		case NOTIFICATION_PARENTED: {

			parent = Object::cast_to<CollisionObject2D>(get_parent());
			if (parent) {
				owner_id = parent->create_shape_owner(this);
				_build_polygon();
				parent->shape_owner_set_transform(owner_id, get_transform());
				parent->shape_owner_set_disabled(owner_id, disabled);
				parent->shape_owner_set_one_way_collision(owner_id, one_way_collision);
			}

			/*if (Engine::get_singleton()->is_editor_hint()) {
				//display above all else
				set_z_as_relative(false);
				set_z(VS::CANVAS_ITEM_Z_MAX - 1);
			}*/

		} break;
		case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {

			if (parent) {
				parent->shape_owner_set_transform(owner_id, get_transform());
			}

		} break;
		case NOTIFICATION_UNPARENTED: {
			if (parent) {
				parent->remove_shape_owner(owner_id);
			}
			owner_id = 0;
			parent = NULL;
		} break;

		case NOTIFICATION_DRAW: {

			if (!Engine::get_singleton()->is_editor_hint() && !get_tree()->is_debugging_collisions_hint()) {
				break;
			}

			for (int i = 0; i < polygon.size(); i++) {

				Vector2 p = polygon[i];
				Vector2 n = polygon[(i + 1) % polygon.size()];
				draw_line(p, n, Color(0.9, 0.2, 0.0, 0.8), 3);
			}
#define DEBUG_DECOMPOSE
#if defined(TOOLS_ENABLED) && defined(DEBUG_DECOMPOSE)

			Vector<Vector<Vector2> > decomp = _decompose_in_convex();

			Color c(0.4, 0.9, 0.1);
			for (int i = 0; i < decomp.size(); i++) {

				c.set_hsv(Math::fmod(c.get_h() + 0.738, 1), c.get_s(), c.get_v(), 0.5);
				draw_colored_polygon(decomp[i], c);
			}
#else
			draw_colored_polygon(polygon, get_tree()->get_debug_collisions_color());
#endif

			if (one_way_collision) {
				Color dcol = get_tree()->get_debug_collisions_color(); //0.9,0.2,0.2,0.4);
				dcol.a = 1.0;
				Vector2 line_to(0, 20);
				draw_line(Vector2(), line_to, dcol, 3);
				Vector<Vector2> pts;
				float tsize = 8;
				pts.push_back(line_to + (Vector2(0, tsize)));
				pts.push_back(line_to + (Vector2(0.707 * tsize, 0)));
				pts.push_back(line_to + (Vector2(-0.707 * tsize, 0)));
				Vector<Color> cols;
				for (int i = 0; i < 3; i++)
					cols.push_back(dcol);

				draw_primitive(pts, cols, Vector<Vector2>()); //small arrow
			}
		} break;
	}
}

void CollisionPolygon2D::set_polygon(const Vector<Point2> &p_polygon) {

	polygon = p_polygon;

	{
		for (int i = 0; i < polygon.size(); i++) {
			if (i == 0)
				aabb = Rect2(polygon[i], Size2());
			else
				aabb.expand_to(polygon[i]);
		}
		if (aabb == Rect2()) {

			aabb = Rect2(-10, -10, 20, 20);
		} else {
			aabb.position -= aabb.size * 0.3;
			aabb.size += aabb.size * 0.6;
		}
	}

	if (parent) {
		_build_polygon();
	}
	update();
	update_configuration_warning();
}

Vector<Point2> CollisionPolygon2D::get_polygon() const {

	return polygon;
}

void CollisionPolygon2D::set_build_mode(BuildMode p_mode) {

	ERR_FAIL_INDEX(p_mode, 2);
	build_mode = p_mode;
	if (parent) {
		_build_polygon();
	}
}

CollisionPolygon2D::BuildMode CollisionPolygon2D::get_build_mode() const {

	return build_mode;
}

Rect2 CollisionPolygon2D::get_item_rect() const {

	return aabb;
}

String CollisionPolygon2D::get_configuration_warning() const {

	if (!Object::cast_to<CollisionObject2D>(get_parent())) {
		return TTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape.");
	}

	if (polygon.empty()) {
		return TTR("An empty CollisionPolygon2D has no effect on collision.");
	}

	return String();
}

void CollisionPolygon2D::set_disabled(bool p_disabled) {
	disabled = p_disabled;
	update();
	if (parent) {
		parent->shape_owner_set_disabled(owner_id, p_disabled);
	}
}

bool CollisionPolygon2D::is_disabled() const {
	return disabled;
}

void CollisionPolygon2D::set_one_way_collision(bool p_enable) {
	one_way_collision = p_enable;
	update();
	if (parent) {
		parent->shape_owner_set_one_way_collision(owner_id, p_enable);
	}
}

bool CollisionPolygon2D::is_one_way_collision_enabled() const {

	return one_way_collision;
}

void CollisionPolygon2D::_bind_methods() {

	ClassDB::bind_method(D_METHOD("set_polygon", "polygon"), &CollisionPolygon2D::set_polygon);
	ClassDB::bind_method(D_METHOD("get_polygon"), &CollisionPolygon2D::get_polygon);

	ClassDB::bind_method(D_METHOD("set_build_mode", "build_mode"), &CollisionPolygon2D::set_build_mode);
	ClassDB::bind_method(D_METHOD("get_build_mode"), &CollisionPolygon2D::get_build_mode);
	ClassDB::bind_method(D_METHOD("set_disabled", "disabled"), &CollisionPolygon2D::set_disabled);
	ClassDB::bind_method(D_METHOD("is_disabled"), &CollisionPolygon2D::is_disabled);
	ClassDB::bind_method(D_METHOD("set_one_way_collision", "enabled"), &CollisionPolygon2D::set_one_way_collision);
	ClassDB::bind_method(D_METHOD("is_one_way_collision_enabled"), &CollisionPolygon2D::is_one_way_collision_enabled);

	ADD_PROPERTY(PropertyInfo(Variant::INT, "build_mode", PROPERTY_HINT_ENUM, "Solids,Segments"), "set_build_mode", "get_build_mode");
	ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon");
	ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
	ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "one_way_collision"), "set_one_way_collision", "is_one_way_collision_enabled");

	BIND_ENUM_CONSTANT(BUILD_SOLIDS);
	BIND_ENUM_CONSTANT(BUILD_SEGMENTS);
}

CollisionPolygon2D::CollisionPolygon2D() {

	aabb = Rect2(-10, -10, 20, 20);
	build_mode = BUILD_SOLIDS;
	set_notify_local_transform(true);
	parent = NULL;
	owner_id = 0;
	disabled = false;
	one_way_collision = false;
}
<commit_msg>Fix line thickness for CollisionPolygon2D.<commit_after>/*************************************************************************/
/*  collision_polygon_2d.cpp                                             */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md)    */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/
#include "collision_polygon_2d.h"

#include "collision_object_2d.h"
#include "engine.h"
#include "scene/resources/concave_polygon_shape_2d.h"
#include "scene/resources/convex_polygon_shape_2d.h"

#include "thirdparty/misc/triangulator.h"

void CollisionPolygon2D::_build_polygon() {

	parent->shape_owner_clear_shapes(owner_id);

	if (polygon.size() == 0)
		return;

	bool solids = build_mode == BUILD_SOLIDS;

	if (solids) {

		//here comes the sun, lalalala
		//decompose concave into multiple convex polygons and add them
		Vector<Vector<Vector2> > decomp = _decompose_in_convex();
		for (int i = 0; i < decomp.size(); i++) {
			Ref<ConvexPolygonShape2D> convex = memnew(ConvexPolygonShape2D);
			convex->set_points(decomp[i]);
			parent->shape_owner_add_shape(owner_id, convex);
		}

	} else {

		Ref<ConcavePolygonShape2D> concave = memnew(ConcavePolygonShape2D);

		PoolVector<Vector2> segments;
		segments.resize(polygon.size() * 2);
		PoolVector<Vector2>::Write w = segments.write();

		for (int i = 0; i < polygon.size(); i++) {
			w[(i << 1) + 0] = polygon[i];
			w[(i << 1) + 1] = polygon[(i + 1) % polygon.size()];
		}

		w = PoolVector<Vector2>::Write();
		concave->set_segments(segments);

		parent->shape_owner_add_shape(owner_id, concave);
	}
}

Vector<Vector<Vector2> > CollisionPolygon2D::_decompose_in_convex() {

	Vector<Vector<Vector2> > decomp;
	List<TriangulatorPoly> in_poly, out_poly;

	TriangulatorPoly inp;
	inp.Init(polygon.size());
	for (int i = 0; i < polygon.size(); i++) {
		inp.GetPoint(i) = polygon[i];
	}
	inp.SetOrientation(TRIANGULATOR_CCW);
	in_poly.push_back(inp);
	TriangulatorPartition tpart;
	if (tpart.ConvexPartition_HM(&in_poly, &out_poly) == 0) { //failed!
		ERR_PRINT("Convex decomposing failed!");
		return decomp;
	}

	decomp.resize(out_poly.size());
	int idx = 0;

	for (List<TriangulatorPoly>::Element *I = out_poly.front(); I; I = I->next()) {

		TriangulatorPoly &tp = I->get();

		decomp[idx].resize(tp.GetNumPoints());

		for (int i = 0; i < tp.GetNumPoints(); i++) {

			decomp[idx][i] = tp.GetPoint(i);
		}

		idx++;
	}

	return decomp;
}

void CollisionPolygon2D::_notification(int p_what) {

	switch (p_what) {
		case NOTIFICATION_PARENTED: {

			parent = Object::cast_to<CollisionObject2D>(get_parent());
			if (parent) {
				owner_id = parent->create_shape_owner(this);
				_build_polygon();
				parent->shape_owner_set_transform(owner_id, get_transform());
				parent->shape_owner_set_disabled(owner_id, disabled);
				parent->shape_owner_set_one_way_collision(owner_id, one_way_collision);
			}

			/*if (Engine::get_singleton()->is_editor_hint()) {
				//display above all else
				set_z_as_relative(false);
				set_z(VS::CANVAS_ITEM_Z_MAX - 1);
			}*/

		} break;
		case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {

			if (parent) {
				parent->shape_owner_set_transform(owner_id, get_transform());
			}

		} break;
		case NOTIFICATION_UNPARENTED: {
			if (parent) {
				parent->remove_shape_owner(owner_id);
			}
			owner_id = 0;
			parent = NULL;
		} break;

		case NOTIFICATION_DRAW: {

			if (!Engine::get_singleton()->is_editor_hint() && !get_tree()->is_debugging_collisions_hint()) {
				break;
			}

			for (int i = 0; i < polygon.size(); i++) {

				Vector2 p = polygon[i];
				Vector2 n = polygon[(i + 1) % polygon.size()];
				// draw line with width <= 1, so it does not scale with zoom and break pixel exact editing
				draw_line(p, n, Color(0.9, 0.2, 0.0, 0.8), 1);
			}
#define DEBUG_DECOMPOSE
#if defined(TOOLS_ENABLED) && defined(DEBUG_DECOMPOSE)

			Vector<Vector<Vector2> > decomp = _decompose_in_convex();

			Color c(0.4, 0.9, 0.1);
			for (int i = 0; i < decomp.size(); i++) {

				c.set_hsv(Math::fmod(c.get_h() + 0.738, 1), c.get_s(), c.get_v(), 0.5);
				draw_colored_polygon(decomp[i], c);
			}
#else
			draw_colored_polygon(polygon, get_tree()->get_debug_collisions_color());
#endif

			if (one_way_collision) {
				Color dcol = get_tree()->get_debug_collisions_color(); //0.9,0.2,0.2,0.4);
				dcol.a = 1.0;
				Vector2 line_to(0, 20);
				draw_line(Vector2(), line_to, dcol, 3);
				Vector<Vector2> pts;
				float tsize = 8;
				pts.push_back(line_to + (Vector2(0, tsize)));
				pts.push_back(line_to + (Vector2(0.707 * tsize, 0)));
				pts.push_back(line_to + (Vector2(-0.707 * tsize, 0)));
				Vector<Color> cols;
				for (int i = 0; i < 3; i++)
					cols.push_back(dcol);

				draw_primitive(pts, cols, Vector<Vector2>()); //small arrow
			}
		} break;
	}
}

void CollisionPolygon2D::set_polygon(const Vector<Point2> &p_polygon) {

	polygon = p_polygon;

	{
		for (int i = 0; i < polygon.size(); i++) {
			if (i == 0)
				aabb = Rect2(polygon[i], Size2());
			else
				aabb.expand_to(polygon[i]);
		}
		if (aabb == Rect2()) {

			aabb = Rect2(-10, -10, 20, 20);
		} else {
			aabb.position -= aabb.size * 0.3;
			aabb.size += aabb.size * 0.6;
		}
	}

	if (parent) {
		_build_polygon();
	}
	update();
	update_configuration_warning();
}

Vector<Point2> CollisionPolygon2D::get_polygon() const {

	return polygon;
}

void CollisionPolygon2D::set_build_mode(BuildMode p_mode) {

	ERR_FAIL_INDEX(p_mode, 2);
	build_mode = p_mode;
	if (parent) {
		_build_polygon();
	}
}

CollisionPolygon2D::BuildMode CollisionPolygon2D::get_build_mode() const {

	return build_mode;
}

Rect2 CollisionPolygon2D::get_item_rect() const {

	return aabb;
}

String CollisionPolygon2D::get_configuration_warning() const {

	if (!Object::cast_to<CollisionObject2D>(get_parent())) {
		return TTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape.");
	}

	if (polygon.empty()) {
		return TTR("An empty CollisionPolygon2D has no effect on collision.");
	}

	return String();
}

void CollisionPolygon2D::set_disabled(bool p_disabled) {
	disabled = p_disabled;
	update();
	if (parent) {
		parent->shape_owner_set_disabled(owner_id, p_disabled);
	}
}

bool CollisionPolygon2D::is_disabled() const {
	return disabled;
}

void CollisionPolygon2D::set_one_way_collision(bool p_enable) {
	one_way_collision = p_enable;
	update();
	if (parent) {
		parent->shape_owner_set_one_way_collision(owner_id, p_enable);
	}
}

bool CollisionPolygon2D::is_one_way_collision_enabled() const {

	return one_way_collision;
}

void CollisionPolygon2D::_bind_methods() {

	ClassDB::bind_method(D_METHOD("set_polygon", "polygon"), &CollisionPolygon2D::set_polygon);
	ClassDB::bind_method(D_METHOD("get_polygon"), &CollisionPolygon2D::get_polygon);

	ClassDB::bind_method(D_METHOD("set_build_mode", "build_mode"), &CollisionPolygon2D::set_build_mode);
	ClassDB::bind_method(D_METHOD("get_build_mode"), &CollisionPolygon2D::get_build_mode);
	ClassDB::bind_method(D_METHOD("set_disabled", "disabled"), &CollisionPolygon2D::set_disabled);
	ClassDB::bind_method(D_METHOD("is_disabled"), &CollisionPolygon2D::is_disabled);
	ClassDB::bind_method(D_METHOD("set_one_way_collision", "enabled"), &CollisionPolygon2D::set_one_way_collision);
	ClassDB::bind_method(D_METHOD("is_one_way_collision_enabled"), &CollisionPolygon2D::is_one_way_collision_enabled);

	ADD_PROPERTY(PropertyInfo(Variant::INT, "build_mode", PROPERTY_HINT_ENUM, "Solids,Segments"), "set_build_mode", "get_build_mode");
	ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon");
	ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
	ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "one_way_collision"), "set_one_way_collision", "is_one_way_collision_enabled");

	BIND_ENUM_CONSTANT(BUILD_SOLIDS);
	BIND_ENUM_CONSTANT(BUILD_SEGMENTS);
}

CollisionPolygon2D::CollisionPolygon2D() {

	aabb = Rect2(-10, -10, 20, 20);
	build_mode = BUILD_SOLIDS;
	set_notify_local_transform(true);
	parent = NULL;
	owner_id = 0;
	disabled = false;
	one_way_collision = false;
}
<|endoftext|>