/*****************************************************************************/
/* CascPort.h                             Copyright (c) Ladislav Zezula 2014 */
/*---------------------------------------------------------------------------*/
/* Portability module for the CascLib library. Contains a wrapper symbols    */
/* to make the compilation under Linux work                                  */
/*---------------------------------------------------------------------------*/
/*   Date    Ver   Who  Comment                                              */
/* --------  ----  ---  -------                                              */
/* 29.04.14  1.00  Lad  Created                                              */
/*****************************************************************************/

#ifndef __CASCPORT_H__
#define __CASCPORT_H__

#ifndef __cplusplus
  #include <stdbool.h>
#endif

//-----------------------------------------------------------------------------
// Defines for Windows

#if !defined(CASCLIB_PLATFORM_DEFINED) && (defined(_WIN32) || defined(_WIN64))

  // Make sure that headers are only included once in newer SDKs
  #if defined (_MSC_VER) && (_MSC_VER >= 1020)
  #pragma once
  #endif

  // In MSVC 8.0, there are some functions declared as deprecated.
  #define _CRT_SECURE_NO_DEPRECATE
  #define _CRT_NON_CONFORMING_SWPRINTFS

  // Prevent duplicate symbols defined by Windows headers
  #ifndef WIN32_LEAN_AND_MEAN
    #define WIN32_LEAN_AND_MEAN
  #endif

  // Suppress definitions of `min` and `max` macros by <windows.h>:
  #define NOMINMAX 1

  #include <tchar.h>
  #include <assert.h>
  #include <ctype.h>
  #include <io.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <direct.h>
  #include <malloc.h>
  #include <windows.h>
  #include <strsafe.h>

  #define CASCLIB_PLATFORM_LITTLE_ENDIAN

  #pragma intrinsic(memset, memcmp, memcpy)     // Make these functions intrinsic (inline)

  #define URL_SEP_CHAR              '/'
  #define PATH_SEP_CHAR             '\\'
  #define PATH_SEP_STRING           "\\"

  #define CASCLIB_PLATFORM_WINDOWS
  #define CASCLIB_PLATFORM_DEFINED              // The platform is known now

#endif

#ifndef FIELD_OFFSET
#define FIELD_OFFSET(type, field)    ((LONG)(size_t)&(((type *)0)->field))
#endif

//-----------------------------------------------------------------------------
// Defines for Mac

#if !defined(CASCLIB_PLATFORM_DEFINED) && defined(__APPLE__)  // Mac BSD API

  // Macintosh
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <sys/socket.h>
  #include <sys/mman.h>
  #include <fcntl.h>
  #include <dirent.h>
  #include <unistd.h>
  #include <stddef.h>
  #include <stdint.h>
  #include <stdlib.h>
  #include <stdio.h>
  #include <stdarg.h>
  #include <string.h>
  #include <ctype.h>
  #include <wchar.h>
  #include <cassert>
  #include <errno.h>
  #include <pthread.h>
  #include <netdb.h>

  // Support for PowerPC on Max OS X
  #if(__ppc__ == 1) || (__POWERPC__ == 1) || (_ARCH_PPC == 1)
    #include <stdint.h>
    #include <CoreFoundation/CFByteOrder.h>
  #endif

  #define    PKEXPORT

  #ifndef __BIG_ENDIAN__
    #define CASCLIB_PLATFORM_LITTLE_ENDIAN
  #endif

  #define URL_SEP_CHAR              '/'
  #define PATH_SEP_CHAR             '/'
  #define PATH_SEP_STRING           "/"

  typedef int SOCKET;

  #define CASCLIB_PLATFORM_MAC
  #define CASCLIB_PLATFORM_DEFINED          // The platform is known now

#endif

//-----------------------------------------------------------------------------
// Assumption: we are not on Windows nor Macintosh, so this must be linux *grin*

#if !defined(CASCLIB_PLATFORM_DEFINED)
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <sys/socket.h>
  #include <sys/mman.h>
  #include <fcntl.h>
  #include <dirent.h>
  #include <unistd.h>
  #include <stddef.h>
  #include <stdint.h>
  #include <stdlib.h>
  #include <stdio.h>
  #include <stdarg.h>
  #include <string.h>
  #include <ctype.h>
  #include <wchar.h>
  #include <assert.h>
  #include <errno.h>
  #include <pthread.h>
  #include <netdb.h>

  #define URL_SEP_CHAR              '/'
  #define PATH_SEP_CHAR             '/'
  #define PATH_SEP_STRING           "/"

  typedef int SOCKET;

  #define CASCLIB_PLATFORM_LITTLE_ENDIAN
  #define CASCLIB_PLATFORM_LINUX
  #define CASCLIB_PLATFORM_DEFINED

#endif

//-----------------------------------------------------------------------------
// Definition of Windows-specific types for non-Windows platforms

#ifndef CASCLIB_PLATFORM_WINDOWS

  // Typedefs for ANSI C
  typedef unsigned char  BYTE;
  typedef unsigned short USHORT;
  typedef int            LONG;
  typedef unsigned int   DWORD;
  typedef long long      LONGLONG;
  typedef signed long long LONGLONG;
  typedef signed long long *PLONGLONG;
  typedef unsigned long long ULONGLONG;
  typedef unsigned long long *PULONGLONG;
  typedef void         * HANDLE;
  typedef char           TCHAR;
  typedef unsigned int   LCID;
  typedef LONG         * PLONG;
  typedef DWORD        * PDWORD;
  typedef BYTE         * LPBYTE;
  typedef char         * LPSTR;
  typedef const char   * LPCSTR;
  typedef TCHAR        * LPTSTR;
  typedef const TCHAR  * LPCTSTR;

  #ifndef __LP64__
    #define _LZMA_UINT32_IS_ULONG
  #endif

  // Some Windows-specific defines
  #ifndef MAX_PATH
    #define MAX_PATH 1024
  #endif

  #define WINAPI

  #define FILE_BEGIN    SEEK_SET
  #define FILE_CURRENT  SEEK_CUR
  #define FILE_END      SEEK_END

  #define INVALID_HANDLE_VALUE ((HANDLE)-1)

  #define _T(x)     x
  #define _tcslen   strlen
  #define _tcscat   strcat
  #define _tcschr   strchr
  #define _tcsrchr  strrchr
  #define _tcsstr   strstr
  #define _tcsspn   strspn
  #define _tcsncmp  strncmp
  #define _tprintf  printf
  #define _tremove  remove
  #define _taccess  access
  #define _access   access
  #define _tfopen   fopen

  #define _stricmp  strcasecmp
  #define _strnicmp strncasecmp
  #define _tcsicmp  strcasecmp
  #define _tcsnicmp strncasecmp

  #define closesocket close

#endif // !CASCLIB_PLATFORM_WINDOWS

// 64-bit calls are supplied by "normal" calls on Mac
#if defined(CASCLIB_PLATFORM_MAC)
  #define stat64  stat
  #define fstat64 fstat
  #define lseek64 lseek
  #define ftruncate64 ftruncate
  #define off64_t off_t
  #define O_LARGEFILE 0
#endif

// Platform-specific error codes for UNIX-based platforms
#if defined(CASCLIB_PLATFORM_MAC) || defined(CASCLIB_PLATFORM_LINUX)
  #define ERROR_SUCCESS                 0
  #define ERROR_FILE_NOT_FOUND          ENOENT
  #define ERROR_PATH_NOT_FOUND          ENOENT
  #define ERROR_ACCESS_DENIED           EPERM
  #define ERROR_INVALID_HANDLE          EBADF
  #define ERROR_NOT_ENOUGH_MEMORY       ENOMEM
  #define ERROR_NOT_SUPPORTED           ENOTSUP
  #define ERROR_INVALID_PARAMETER       EINVAL
  #define ERROR_DISK_FULL               ENOSPC
  #define ERROR_ALREADY_EXISTS          EEXIST
  #define ERROR_INSUFFICIENT_BUFFER     ENOBUFS
  #define ERROR_BAD_FORMAT              1000        // No such error code under Linux
  #define ERROR_NO_MORE_FILES           1001        // No such error code under Linux
  #define ERROR_HANDLE_EOF              1002        // No such error code under Linux
  #define ERROR_CAN_NOT_COMPLETE        1003        // No such error code under Linux
  #define ERROR_FILE_CORRUPT            1004        // No such error code under Linux
  #define ERROR_FILE_ENCRYPTED          1005        // Returned by encrypted stream when can't find file key
  #define ERROR_FILE_TOO_LARGE          1006        // No such error code under Linux
  #define ERROR_ARITHMETIC_OVERFLOW     1007        // The string value is too large to fit in the given type
  #define ERROR_NETWORK_NOT_AVAILABLE   1008        // Cannot connect to the internet
#endif

#ifndef ERROR_FILE_INCOMPLETE
#define ERROR_FILE_INCOMPLETE           1006        // The required file part is missing
#endif

#ifndef ERROR_FILE_OFFLINE
#define ERROR_FILE_OFFLINE              1007        // The file is not available in the local storage
#endif

#ifndef ERROR_BUFFER_OVERFLOW
#define ERROR_BUFFER_OVERFLOW           1008
#endif

#ifndef ERROR_CANCELLED
#define ERROR_CANCELLED                 1009
#endif

#ifndef ERROR_INDEX_PARSING_DONE
#define ERROR_INDEX_PARSING_DONE        1010
#endif

#ifndef ERROR_REPARSE_ROOT
#define ERROR_REPARSE_ROOT              1011
#endif

#ifndef ERROR_CKEY_ALREADY_OPENED
#define ERROR_CKEY_ALREADY_OPENED       1012        // The file with this CKey was already open since CascOpenStorage
#endif

#ifndef _countof
#define _countof(x)   (sizeof(x) / sizeof(x[0]))
#endif

//-----------------------------------------------------------------------------
// Swapping functions

#ifdef CASCLIB_PLATFORM_LITTLE_ENDIAN
    #define    BSWAP_INT16_UNSIGNED(a)          (a)
    #define    BSWAP_INT16_SIGNED(a)            (a)
    #define    BSWAP_INT32_UNSIGNED(a)          (a)
    #define    BSWAP_INT32_SIGNED(a)            (a)
    #define    BSWAP_INT64_SIGNED(a)            (a)
    #define    BSWAP_INT64_UNSIGNED(a)          (a)
    #define    BSWAP_ARRAY16_UNSIGNED(a,b)      {}
    #define    BSWAP_ARRAY32_UNSIGNED(a,b)      {}
    #define    BSWAP_ARRAY64_UNSIGNED(a,b)      {}
#else

#ifdef __cplusplus
  extern "C" {
#endif
    int16_t  SwapInt16(uint16_t);
    uint16_t SwapUInt16(uint16_t);
    int32_t  SwapInt32(uint32_t);
    uint32_t SwapUInt32(uint32_t);
    int64_t  SwapInt64(uint64_t);
    uint64_t SwapUInt64(uint64_t);
    void ConvertUInt16Buffer(void * ptr, size_t length);
    void ConvertUInt32Buffer(void * ptr, size_t length);
    void ConvertUInt64Buffer(void * ptr, size_t length);
#ifdef __cplusplus
  }
#endif
    #define    BSWAP_INT16_SIGNED(a)            SwapInt16((a))
    #define    BSWAP_INT16_UNSIGNED(a)          SwapUInt16((a))
    #define    BSWAP_INT32_SIGNED(a)            SwapInt32((a))
    #define    BSWAP_INT32_UNSIGNED(a)          SwapUInt32((a))
    #define    BSWAP_INT64_SIGNED(a)            SwapInt64((a))
    #define    BSWAP_INT64_UNSIGNED(a)          SwapUInt64((a))
    #define    BSWAP_ARRAY16_UNSIGNED(a,b)      ConvertUInt16Buffer((a),(b))
    #define    BSWAP_ARRAY32_UNSIGNED(a,b)      ConvertUInt32Buffer((a),(b))
    #define    BSWAP_ARRAY64_UNSIGNED(a,b)      ConvertUInt64Buffer((a),(b))
#endif

//-----------------------------------------------------------------------------
// Interlocked operations

inline DWORD CascInterlockedIncrement(DWORD * PtrValue)
{
#ifdef CASCLIB_PLATFORM_WINDOWS
    return (DWORD)InterlockedIncrement((LONG *)(PtrValue));
#elif defined(__GNUC__)
    return __sync_add_and_fetch(PtrValue, 1);
#else
    #define INTERLOCKED_NOT_SUPPORTED
    return ++(*PtrValue);
#endif
}

inline DWORD CascInterlockedDecrement(DWORD * PtrValue)
{
#ifdef CASCLIB_PLATFORM_WINDOWS
    return (DWORD)InterlockedDecrement((LONG *)(PtrValue));
#elif defined(__GNUC__)
    return __sync_sub_and_fetch(PtrValue, 1);
#else
    return --(*PtrValue);
#endif
}

//-----------------------------------------------------------------------------
// Lock functions

#ifdef CASCLIB_PLATFORM_WINDOWS

typedef RTL_CRITICAL_SECTION CASC_LOCK;
#define CascInitLock(Lock)      InitializeCriticalSection(&Lock);
#define CascFreeLock(Lock)      DeleteCriticalSection(&Lock);
#define CascLock(Lock)          EnterCriticalSection(&Lock);
#define CascUnlock(Lock)        LeaveCriticalSection(&Lock);

#else

typedef pthread_mutex_t CASC_LOCK;
#define CascInitLock(Lock)      pthread_mutex_init(&Lock, NULL);
#define CascFreeLock(Lock)      pthread_mutex_destroy(&Lock);
#define CascLock(Lock)          pthread_mutex_lock(&Lock);
#define CascUnlock(Lock)        pthread_mutex_unlock(&Lock);

#endif

//-----------------------------------------------------------------------------
// Forbidden functions, do not use

#ifdef __CASCLIB_SELF__
#define strcpy  UNSAFE_DO_NOT_USE_STRCPY
#define strcat  UNSAFE_DO_NOT_USE_STRCAT
#define sprintf UNSAFE_DO_NOT_USE_SPRINTF
#endif

#endif // __CASCPORT_H__
