From a4dd0ad63c00f4dee3b86dfd3075d1d61b2b3180 Mon Sep 17 00:00:00 2001
From: sanine <sanine.not@pm.me>
Date: Sat, 27 Aug 2022 23:52:56 -0500
Subject: add plibsys

---
 3rdparty/plibsys/src/CMakeLists.txt          |  996 ++++++++++++++++
 3rdparty/plibsys/src/patomic-c11.c           |  182 +++
 3rdparty/plibsys/src/patomic-decc.c          |  286 +++++
 3rdparty/plibsys/src/patomic-sim.c           |  268 +++++
 3rdparty/plibsys/src/patomic-sync.c          |  190 +++
 3rdparty/plibsys/src/patomic-win.c           |  272 +++++
 3rdparty/plibsys/src/patomic.h               |  310 +++++
 3rdparty/plibsys/src/pcondvariable-amiga.c   |  230 ++++
 3rdparty/plibsys/src/pcondvariable-atheos.c  |  234 ++++
 3rdparty/plibsys/src/pcondvariable-beos.c    |  233 ++++
 3rdparty/plibsys/src/pcondvariable-none.c    |   80 ++
 3rdparty/plibsys/src/pcondvariable-os2.c     |  163 +++
 3rdparty/plibsys/src/pcondvariable-posix.c   |  121 ++
 3rdparty/plibsys/src/pcondvariable-solaris.c |  122 ++
 3rdparty/plibsys/src/pcondvariable-win.c     |  312 +++++
 3rdparty/plibsys/src/pcondvariable.h         |  138 +++
 3rdparty/plibsys/src/pcryptohash-gost3411.c  |  484 ++++++++
 3rdparty/plibsys/src/pcryptohash-gost3411.h  |   53 +
 3rdparty/plibsys/src/pcryptohash-md5.c       |  273 +++++
 3rdparty/plibsys/src/pcryptohash-md5.h       |   51 +
 3rdparty/plibsys/src/pcryptohash-sha1.c      |  321 +++++
 3rdparty/plibsys/src/pcryptohash-sha1.h      |   51 +
 3rdparty/plibsys/src/pcryptohash-sha2-256.c  |  286 +++++
 3rdparty/plibsys/src/pcryptohash-sha2-256.h  |   59 +
 3rdparty/plibsys/src/pcryptohash-sha2-512.c  |  300 +++++
 3rdparty/plibsys/src/pcryptohash-sha2-512.h  |   59 +
 3rdparty/plibsys/src/pcryptohash-sha3.c      |  297 +++++
 3rdparty/plibsys/src/pcryptohash-sha3.h      |   79 ++
 3rdparty/plibsys/src/pcryptohash.c           |  250 ++++
 3rdparty/plibsys/src/pcryptohash.h           |  188 +++
 3rdparty/plibsys/src/pdir-none.c             |  124 ++
 3rdparty/plibsys/src/pdir-os2.c              |  381 ++++++
 3rdparty/plibsys/src/pdir-posix.c            |  378 ++++++
 3rdparty/plibsys/src/pdir-win.c              |  291 +++++
 3rdparty/plibsys/src/pdir.c                  |   37 +
 3rdparty/plibsys/src/pdir.h                  |  177 +++
 3rdparty/plibsys/src/perror-private.h        |   67 ++
 3rdparty/plibsys/src/perror.c                |  878 ++++++++++++++
 3rdparty/plibsys/src/perror.h                |  249 ++++
 3rdparty/plibsys/src/perrortypes.h           |  106 ++
 3rdparty/plibsys/src/pfile.c                 |   80 ++
 3rdparty/plibsys/src/pfile.h                 |   87 ++
 3rdparty/plibsys/src/phashtable.c            |  243 ++++
 3rdparty/plibsys/src/phashtable.h            |  167 +++
 3rdparty/plibsys/src/pinifile.c              |  503 ++++++++
 3rdparty/plibsys/src/pinifile.h              |  261 ++++
 3rdparty/plibsys/src/pipc-private.h          |   76 ++
 3rdparty/plibsys/src/pipc.c                  |  178 +++
 3rdparty/plibsys/src/plibraryloader-amiga.c  |  583 +++++++++
 3rdparty/plibsys/src/plibraryloader-beos.c   |  142 +++
 3rdparty/plibsys/src/plibraryloader-none.c   |   75 ++
 3rdparty/plibsys/src/plibraryloader-os2.c    |  155 +++
 3rdparty/plibsys/src/plibraryloader-posix.c  |  148 +++
 3rdparty/plibsys/src/plibraryloader-shl.c    |  140 +++
 3rdparty/plibsys/src/plibraryloader-win.c    |  140 +++
 3rdparty/plibsys/src/plibraryloader.h        |  155 +++
 3rdparty/plibsys/src/plibsys-private.h       |   86 ++
 3rdparty/plibsys/src/plibsys.h               |   64 +
 3rdparty/plibsys/src/plibsysconfig.h.in      |   76 ++
 3rdparty/plibsys/src/plist.c                 |  174 +++
 3rdparty/plibsys/src/plist.h                 |  199 ++++
 3rdparty/plibsys/src/pmacros.h               |  302 +++++
 3rdparty/plibsys/src/pmacroscompiler.h       |  253 ++++
 3rdparty/plibsys/src/pmacroscpu.h            |  627 ++++++++++
 3rdparty/plibsys/src/pmacrosos.h             |  500 ++++++++
 3rdparty/plibsys/src/pmain.c                 |  132 +++
 3rdparty/plibsys/src/pmain.h                 |  209 ++++
 3rdparty/plibsys/src/pmem.c                  |  357 ++++++
 3rdparty/plibsys/src/pmem.h                  |  191 +++
 3rdparty/plibsys/src/pmutex-amiga.c          |  101 ++
 3rdparty/plibsys/src/pmutex-atheos.c         |  111 ++
 3rdparty/plibsys/src/pmutex-beos.c           |  110 ++
 3rdparty/plibsys/src/pmutex-none.c           |   66 ++
 3rdparty/plibsys/src/pmutex-os2.c            |  112 ++
 3rdparty/plibsys/src/pmutex-posix.c          |  104 ++
 3rdparty/plibsys/src/pmutex-solaris.c        |  105 ++
 3rdparty/plibsys/src/pmutex-win.c            |   92 ++
 3rdparty/plibsys/src/pmutex.h                |  136 +++
 3rdparty/plibsys/src/pprocess.c              |   61 +
 3rdparty/plibsys/src/pprocess.h              |   69 ++
 3rdparty/plibsys/src/prwlock-general.c       |  326 +++++
 3rdparty/plibsys/src/prwlock-none.c          |  103 ++
 3rdparty/plibsys/src/prwlock-posix.c         |  151 +++
 3rdparty/plibsys/src/prwlock-solaris.c       |  151 +++
 3rdparty/plibsys/src/prwlock-win.c           |  548 +++++++++
 3rdparty/plibsys/src/prwlock.h               |  185 +++
 3rdparty/plibsys/src/psemaphore-amiga.c      |  250 ++++
 3rdparty/plibsys/src/psemaphore-none.c       |   91 ++
 3rdparty/plibsys/src/psemaphore-os2.c        |   91 ++
 3rdparty/plibsys/src/psemaphore-posix.c      |  272 +++++
 3rdparty/plibsys/src/psemaphore-sysv.c       |  319 +++++
 3rdparty/plibsys/src/psemaphore-win.c        |  204 ++++
 3rdparty/plibsys/src/psemaphore.h            |  191 +++
 3rdparty/plibsys/src/pshm-amiga.c            |  274 +++++
 3rdparty/plibsys/src/pshm-none.c             |   94 ++
 3rdparty/plibsys/src/pshm-os2.c              |  350 ++++++
 3rdparty/plibsys/src/pshm-posix.c            |  311 +++++
 3rdparty/plibsys/src/pshm-sysv.c             |  307 +++++
 3rdparty/plibsys/src/pshm-win.c              |  262 ++++
 3rdparty/plibsys/src/pshm.h                  |  195 +++
 3rdparty/plibsys/src/pshmbuffer.c            |  334 ++++++
 3rdparty/plibsys/src/pshmbuffer.h            |  191 +++
 3rdparty/plibsys/src/psocket.c               | 1644 ++++++++++++++++++++++++++
 3rdparty/plibsys/src/psocket.h               |  779 ++++++++++++
 3rdparty/plibsys/src/psocketaddress.c        |  619 ++++++++++
 3rdparty/plibsys/src/psocketaddress.h        |  284 +++++
 3rdparty/plibsys/src/pspinlock-c11.c         |  103 ++
 3rdparty/plibsys/src/pspinlock-decc.c        |   87 ++
 3rdparty/plibsys/src/pspinlock-sim.c         |   88 ++
 3rdparty/plibsys/src/pspinlock-sync.c        |   83 ++
 3rdparty/plibsys/src/pspinlock-win.c         |   83 ++
 3rdparty/plibsys/src/pspinlock.h             |  142 +++
 3rdparty/plibsys/src/pstdarg.h               |  318 +++++
 3rdparty/plibsys/src/pstring.c               |  206 ++++
 3rdparty/plibsys/src/pstring.h               |  117 ++
 3rdparty/plibsys/src/psysclose-darwin.c      |   93 ++
 3rdparty/plibsys/src/psysclose-private.h     |   47 +
 3rdparty/plibsys/src/psysclose-unix.c        |   54 +
 3rdparty/plibsys/src/psysclose-win.c         |   33 +
 3rdparty/plibsys/src/ptimeprofiler-amiga.c   |   70 ++
 3rdparty/plibsys/src/ptimeprofiler-beos.c    |   51 +
 3rdparty/plibsys/src/ptimeprofiler-generic.c |   58 +
 3rdparty/plibsys/src/ptimeprofiler-mach.c    |   73 ++
 3rdparty/plibsys/src/ptimeprofiler-os2.c     |  107 ++
 3rdparty/plibsys/src/ptimeprofiler-posix.c   |  109 ++
 3rdparty/plibsys/src/ptimeprofiler-private.h |   45 +
 3rdparty/plibsys/src/ptimeprofiler-solaris.c |   53 +
 3rdparty/plibsys/src/ptimeprofiler-win.c     |  169 +++
 3rdparty/plibsys/src/ptimeprofiler.c         |   70 ++
 3rdparty/plibsys/src/ptimeprofiler.h         |   93 ++
 3rdparty/plibsys/src/ptree-avl.c             |  481 ++++++++
 3rdparty/plibsys/src/ptree-avl.h             |   58 +
 3rdparty/plibsys/src/ptree-bst.c             |  140 +++
 3rdparty/plibsys/src/ptree-bst.h             |   58 +
 3rdparty/plibsys/src/ptree-private.h         |   48 +
 3rdparty/plibsys/src/ptree-rb.c              |  484 ++++++++
 3rdparty/plibsys/src/ptree-rb.h              |   58 +
 3rdparty/plibsys/src/ptree.c                 |  315 +++++
 3rdparty/plibsys/src/ptree.h                 |  230 ++++
 3rdparty/plibsys/src/ptypes.h                | 1122 ++++++++++++++++++
 3rdparty/plibsys/src/puthread-amiga.c        |  673 +++++++++++
 3rdparty/plibsys/src/puthread-atheos.c       |  317 +++++
 3rdparty/plibsys/src/puthread-beos.c         |  431 +++++++
 3rdparty/plibsys/src/puthread-none.c         |  142 +++
 3rdparty/plibsys/src/puthread-os2.c          |  458 +++++++
 3rdparty/plibsys/src/puthread-posix.c        |  580 +++++++++
 3rdparty/plibsys/src/puthread-private.h      |   53 +
 3rdparty/plibsys/src/puthread-solaris.c      |  352 ++++++
 3rdparty/plibsys/src/puthread-win.c          |  511 ++++++++
 3rdparty/plibsys/src/puthread.c              |  558 +++++++++
 3rdparty/plibsys/src/puthread.h              |  311 +++++
 151 files changed, 35204 insertions(+)
 create mode 100644 3rdparty/plibsys/src/CMakeLists.txt
 create mode 100644 3rdparty/plibsys/src/patomic-c11.c
 create mode 100644 3rdparty/plibsys/src/patomic-decc.c
 create mode 100644 3rdparty/plibsys/src/patomic-sim.c
 create mode 100644 3rdparty/plibsys/src/patomic-sync.c
 create mode 100644 3rdparty/plibsys/src/patomic-win.c
 create mode 100644 3rdparty/plibsys/src/patomic.h
 create mode 100644 3rdparty/plibsys/src/pcondvariable-amiga.c
 create mode 100644 3rdparty/plibsys/src/pcondvariable-atheos.c
 create mode 100644 3rdparty/plibsys/src/pcondvariable-beos.c
 create mode 100644 3rdparty/plibsys/src/pcondvariable-none.c
 create mode 100644 3rdparty/plibsys/src/pcondvariable-os2.c
 create mode 100644 3rdparty/plibsys/src/pcondvariable-posix.c
 create mode 100644 3rdparty/plibsys/src/pcondvariable-solaris.c
 create mode 100644 3rdparty/plibsys/src/pcondvariable-win.c
 create mode 100644 3rdparty/plibsys/src/pcondvariable.h
 create mode 100644 3rdparty/plibsys/src/pcryptohash-gost3411.c
 create mode 100644 3rdparty/plibsys/src/pcryptohash-gost3411.h
 create mode 100644 3rdparty/plibsys/src/pcryptohash-md5.c
 create mode 100644 3rdparty/plibsys/src/pcryptohash-md5.h
 create mode 100644 3rdparty/plibsys/src/pcryptohash-sha1.c
 create mode 100644 3rdparty/plibsys/src/pcryptohash-sha1.h
 create mode 100644 3rdparty/plibsys/src/pcryptohash-sha2-256.c
 create mode 100644 3rdparty/plibsys/src/pcryptohash-sha2-256.h
 create mode 100644 3rdparty/plibsys/src/pcryptohash-sha2-512.c
 create mode 100644 3rdparty/plibsys/src/pcryptohash-sha2-512.h
 create mode 100644 3rdparty/plibsys/src/pcryptohash-sha3.c
 create mode 100644 3rdparty/plibsys/src/pcryptohash-sha3.h
 create mode 100644 3rdparty/plibsys/src/pcryptohash.c
 create mode 100644 3rdparty/plibsys/src/pcryptohash.h
 create mode 100644 3rdparty/plibsys/src/pdir-none.c
 create mode 100644 3rdparty/plibsys/src/pdir-os2.c
 create mode 100644 3rdparty/plibsys/src/pdir-posix.c
 create mode 100644 3rdparty/plibsys/src/pdir-win.c
 create mode 100644 3rdparty/plibsys/src/pdir.c
 create mode 100644 3rdparty/plibsys/src/pdir.h
 create mode 100644 3rdparty/plibsys/src/perror-private.h
 create mode 100644 3rdparty/plibsys/src/perror.c
 create mode 100644 3rdparty/plibsys/src/perror.h
 create mode 100644 3rdparty/plibsys/src/perrortypes.h
 create mode 100644 3rdparty/plibsys/src/pfile.c
 create mode 100644 3rdparty/plibsys/src/pfile.h
 create mode 100644 3rdparty/plibsys/src/phashtable.c
 create mode 100644 3rdparty/plibsys/src/phashtable.h
 create mode 100644 3rdparty/plibsys/src/pinifile.c
 create mode 100644 3rdparty/plibsys/src/pinifile.h
 create mode 100644 3rdparty/plibsys/src/pipc-private.h
 create mode 100644 3rdparty/plibsys/src/pipc.c
 create mode 100644 3rdparty/plibsys/src/plibraryloader-amiga.c
 create mode 100644 3rdparty/plibsys/src/plibraryloader-beos.c
 create mode 100644 3rdparty/plibsys/src/plibraryloader-none.c
 create mode 100644 3rdparty/plibsys/src/plibraryloader-os2.c
 create mode 100644 3rdparty/plibsys/src/plibraryloader-posix.c
 create mode 100644 3rdparty/plibsys/src/plibraryloader-shl.c
 create mode 100644 3rdparty/plibsys/src/plibraryloader-win.c
 create mode 100644 3rdparty/plibsys/src/plibraryloader.h
 create mode 100644 3rdparty/plibsys/src/plibsys-private.h
 create mode 100644 3rdparty/plibsys/src/plibsys.h
 create mode 100644 3rdparty/plibsys/src/plibsysconfig.h.in
 create mode 100644 3rdparty/plibsys/src/plist.c
 create mode 100644 3rdparty/plibsys/src/plist.h
 create mode 100644 3rdparty/plibsys/src/pmacros.h
 create mode 100644 3rdparty/plibsys/src/pmacroscompiler.h
 create mode 100644 3rdparty/plibsys/src/pmacroscpu.h
 create mode 100644 3rdparty/plibsys/src/pmacrosos.h
 create mode 100644 3rdparty/plibsys/src/pmain.c
 create mode 100644 3rdparty/plibsys/src/pmain.h
 create mode 100644 3rdparty/plibsys/src/pmem.c
 create mode 100644 3rdparty/plibsys/src/pmem.h
 create mode 100644 3rdparty/plibsys/src/pmutex-amiga.c
 create mode 100644 3rdparty/plibsys/src/pmutex-atheos.c
 create mode 100644 3rdparty/plibsys/src/pmutex-beos.c
 create mode 100644 3rdparty/plibsys/src/pmutex-none.c
 create mode 100644 3rdparty/plibsys/src/pmutex-os2.c
 create mode 100644 3rdparty/plibsys/src/pmutex-posix.c
 create mode 100644 3rdparty/plibsys/src/pmutex-solaris.c
 create mode 100644 3rdparty/plibsys/src/pmutex-win.c
 create mode 100644 3rdparty/plibsys/src/pmutex.h
 create mode 100644 3rdparty/plibsys/src/pprocess.c
 create mode 100644 3rdparty/plibsys/src/pprocess.h
 create mode 100644 3rdparty/plibsys/src/prwlock-general.c
 create mode 100644 3rdparty/plibsys/src/prwlock-none.c
 create mode 100644 3rdparty/plibsys/src/prwlock-posix.c
 create mode 100644 3rdparty/plibsys/src/prwlock-solaris.c
 create mode 100644 3rdparty/plibsys/src/prwlock-win.c
 create mode 100644 3rdparty/plibsys/src/prwlock.h
 create mode 100644 3rdparty/plibsys/src/psemaphore-amiga.c
 create mode 100644 3rdparty/plibsys/src/psemaphore-none.c
 create mode 100644 3rdparty/plibsys/src/psemaphore-os2.c
 create mode 100644 3rdparty/plibsys/src/psemaphore-posix.c
 create mode 100644 3rdparty/plibsys/src/psemaphore-sysv.c
 create mode 100644 3rdparty/plibsys/src/psemaphore-win.c
 create mode 100644 3rdparty/plibsys/src/psemaphore.h
 create mode 100644 3rdparty/plibsys/src/pshm-amiga.c
 create mode 100644 3rdparty/plibsys/src/pshm-none.c
 create mode 100644 3rdparty/plibsys/src/pshm-os2.c
 create mode 100644 3rdparty/plibsys/src/pshm-posix.c
 create mode 100644 3rdparty/plibsys/src/pshm-sysv.c
 create mode 100644 3rdparty/plibsys/src/pshm-win.c
 create mode 100644 3rdparty/plibsys/src/pshm.h
 create mode 100644 3rdparty/plibsys/src/pshmbuffer.c
 create mode 100644 3rdparty/plibsys/src/pshmbuffer.h
 create mode 100644 3rdparty/plibsys/src/psocket.c
 create mode 100644 3rdparty/plibsys/src/psocket.h
 create mode 100644 3rdparty/plibsys/src/psocketaddress.c
 create mode 100644 3rdparty/plibsys/src/psocketaddress.h
 create mode 100644 3rdparty/plibsys/src/pspinlock-c11.c
 create mode 100644 3rdparty/plibsys/src/pspinlock-decc.c
 create mode 100644 3rdparty/plibsys/src/pspinlock-sim.c
 create mode 100644 3rdparty/plibsys/src/pspinlock-sync.c
 create mode 100644 3rdparty/plibsys/src/pspinlock-win.c
 create mode 100644 3rdparty/plibsys/src/pspinlock.h
 create mode 100644 3rdparty/plibsys/src/pstdarg.h
 create mode 100644 3rdparty/plibsys/src/pstring.c
 create mode 100644 3rdparty/plibsys/src/pstring.h
 create mode 100644 3rdparty/plibsys/src/psysclose-darwin.c
 create mode 100644 3rdparty/plibsys/src/psysclose-private.h
 create mode 100644 3rdparty/plibsys/src/psysclose-unix.c
 create mode 100644 3rdparty/plibsys/src/psysclose-win.c
 create mode 100644 3rdparty/plibsys/src/ptimeprofiler-amiga.c
 create mode 100644 3rdparty/plibsys/src/ptimeprofiler-beos.c
 create mode 100644 3rdparty/plibsys/src/ptimeprofiler-generic.c
 create mode 100644 3rdparty/plibsys/src/ptimeprofiler-mach.c
 create mode 100644 3rdparty/plibsys/src/ptimeprofiler-os2.c
 create mode 100644 3rdparty/plibsys/src/ptimeprofiler-posix.c
 create mode 100644 3rdparty/plibsys/src/ptimeprofiler-private.h
 create mode 100644 3rdparty/plibsys/src/ptimeprofiler-solaris.c
 create mode 100644 3rdparty/plibsys/src/ptimeprofiler-win.c
 create mode 100644 3rdparty/plibsys/src/ptimeprofiler.c
 create mode 100644 3rdparty/plibsys/src/ptimeprofiler.h
 create mode 100644 3rdparty/plibsys/src/ptree-avl.c
 create mode 100644 3rdparty/plibsys/src/ptree-avl.h
 create mode 100644 3rdparty/plibsys/src/ptree-bst.c
 create mode 100644 3rdparty/plibsys/src/ptree-bst.h
 create mode 100644 3rdparty/plibsys/src/ptree-private.h
 create mode 100644 3rdparty/plibsys/src/ptree-rb.c
 create mode 100644 3rdparty/plibsys/src/ptree-rb.h
 create mode 100644 3rdparty/plibsys/src/ptree.c
 create mode 100644 3rdparty/plibsys/src/ptree.h
 create mode 100644 3rdparty/plibsys/src/ptypes.h
 create mode 100644 3rdparty/plibsys/src/puthread-amiga.c
 create mode 100644 3rdparty/plibsys/src/puthread-atheos.c
 create mode 100644 3rdparty/plibsys/src/puthread-beos.c
 create mode 100644 3rdparty/plibsys/src/puthread-none.c
 create mode 100644 3rdparty/plibsys/src/puthread-os2.c
 create mode 100644 3rdparty/plibsys/src/puthread-posix.c
 create mode 100644 3rdparty/plibsys/src/puthread-private.h
 create mode 100644 3rdparty/plibsys/src/puthread-solaris.c
 create mode 100644 3rdparty/plibsys/src/puthread-win.c
 create mode 100644 3rdparty/plibsys/src/puthread.c
 create mode 100644 3rdparty/plibsys/src/puthread.h

(limited to '3rdparty/plibsys/src')

diff --git a/3rdparty/plibsys/src/CMakeLists.txt b/3rdparty/plibsys/src/CMakeLists.txt
new file mode 100644
index 0000000..2f13fbf
--- /dev/null
+++ b/3rdparty/plibsys/src/CMakeLists.txt
@@ -0,0 +1,996 @@
+# The MIT License
+#
+# Copyright (C) 2018-2019 Alexander Saprykin <saprykin.spb@gmail.com>
+#
+# 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.
+
+if (POLICY CMP0075)
+    cmake_policy (SET CMP0075 NEW)
+endif()
+
+include (CheckCSourceCompiles)
+include (CheckTypeSize)
+include (CheckIncludeFile)
+include (TestBigEndian)
+
+# First of all, detect size of the pointer for the target machine.
+# We can't rely on CMAKE_SIZEOF_VOID_P when building universal
+# binaries.
+
+check_type_size ("void *" PLIBSYS_SIZEOF_VOID_P)
+check_type_size ("size_t" PLIBSYS_SIZEOF_SIZE_T)
+check_type_size ("long" PLIBSYS_SIZEOF_LONG)
+
+# Without generated code multi-arch builds are not supported anyway,
+# so the best we can do is to fallback to detected size values
+
+if (CMAKE_VERSION VERSION_LESS 2.8.1)
+        set (PLIBSYS_SIZEOF_VOID_P_CODE "#define PLIBSYS_SIZEOF_VOID_P ${PLIBSYS_SIZEOF_VOID_P}")
+        set (PLIBSYS_SIZEOF_SIZE_T_CODE "#define PLIBSYS_SIZEOF_SIZE_T ${PLIBSYS_SIZEOF_SIZE_T}")
+        set (PLIBSYS_SIZEOF_LONG_CODE "#define PLIBSYS_SIZEOF_LONG ${PLIBSYS_SIZEOF_LONG}")
+endif()
+
+include (${PROJECT_SOURCE_DIR}/cmake/PlatformDetect.cmake)
+include (${PROJECT_SOURCE_DIR}/cmake/VisibilityDetect.cmake)
+include (${PROJECT_SOURCE_DIR}/cmake/StdargDetect.cmake)
+include (${PROJECT_SOURCE_DIR}/cmake/ThreadNameDetect.cmake)
+set (OUTPUT_DIR ${CMAKE_BINARY_DIR})
+
+# Try to detect target platform
+plibsys_detect_target_platform (PLIBSYS_TARGET_PLATFORM)
+plibsys_detect_c_compiler (PLIBSYS_C_COMPILER)
+plibsys_detect_target_os (PLIBSYS_TARGET_OS)
+plibsys_detect_os_bits (PLIBSYS_OS_BITS)
+
+if (PLIBSYS_OS_BITS STREQUAL "unknown")
+    message (FATAL_ERROR "Failed to detect bitness of the target system")
+endif()
+
+if ((PLIBSYS_TARGET_OS STREQUAL windows) AND NOT (PLIBSYS_TARGET_OS STREQUAL cygwin)
+                                         AND NOT (PLIBSYS_TARGET_OS STREQUAL msys))
+        set (PLIBSYS_NATIVE_WINDOWS TRUE)
+endif()
+
+if (PLIBSYS_COVERAGE)
+        if (PLIBSYS_C_COMPILER MATCHES "gcc|clang")
+                set (CMAKE_BUILD_TYPE "Debug")
+                list (APPEND CMAKE_C_FLAGS "--coverage")
+        endif()
+endif()
+
+# CMP0042, see http://www.cmake.org/Wiki/CMake_RPATH_handling
+if (PLIBSYS_TARGET_OS STREQUAL darwin)
+        if (POLICY CMP0068)
+                cmake_policy (SET CMP0068 NEW)
+        endif()
+
+        set (CMAKE_MACOSX_RPATH TRUE)
+        set (CMAKE_SKIP_BUILD_RPATH FALSE)
+
+        # Fix runtime paths on macOS 10.5 and less
+        if (CMAKE_SYSTEM_VERSION VERSION_LESS "10.0.0")
+                set (CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
+        else()
+                set (CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
+        endif()
+
+        set (CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
+        set (CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
+
+        list (FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/lib" isSystemDir)
+
+        if ("${isSystemDir}" STREQUAL "-1")
+                set (CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
+        endif ("${isSystemDir}" STREQUAL "-1")
+endif()
+
+if (NOT EXISTS "${PROJECT_SOURCE_DIR}/platforms/${PLIBSYS_TARGET_PLATFORM}/")
+        message (FATAL_ERROR "plibsys doesn't support unknown platform ${PLIBSYS_TARGET_PLATFORM}")
+endif()
+
+include (${PROJECT_SOURCE_DIR}/platforms/${PLIBSYS_TARGET_PLATFORM}/platform.cmake)
+
+set (PLIBSYS_INCLUDE_DIRS
+        ${PROJECT_SOURCE_DIR}/src
+        ${CMAKE_BINARY_DIR}
+)
+
+set (PLIBSYS_PUBLIC_HDRS
+        patomic.h
+        ptypes.h
+        pmacros.h
+        pmacroscompiler.h
+        pmacroscpu.h
+        pmacrosos.h
+        pcondvariable.h
+        pcryptohash.h
+        perror.h
+        perrortypes.h
+        pdir.h
+        pfile.h
+        phashtable.h
+        pinifile.h
+        plibsys.h
+        plibraryloader.h
+        plist.h
+        pmain.h
+        pmem.h
+        pmutex.h
+        pprocess.h
+        prwlock.h
+        psemaphore.h
+        pshm.h
+        pshmbuffer.h
+        psocket.h
+        psocketaddress.h
+        pspinlock.h
+        pstdarg.h
+        pstring.h
+        ptimeprofiler.h
+        ptree.h
+        puthread.h
+)
+
+set (PLIBSYS_PRIVATE_HDRS
+        pcryptohash-gost3411.h
+        pcryptohash-md5.h
+        pcryptohash-sha1.h
+        pcryptohash-sha2-256.h
+        pcryptohash-sha2-512.h
+        pcryptohash-sha3.h
+        perror-private.h
+        plibsys-private.h
+        psysclose-private.h
+        ptimeprofiler-private.h
+        ptree-avl.h
+        ptree-bst.h
+        ptree-rb.h
+        ptree-private.h
+        puthread-private.h
+        ${CMAKE_BINARY_DIR}/plibsysconfig.h
+)
+
+set (PLIBSYS_SRCS
+        pcryptohash.c
+        pcryptohash-gost3411.c
+        pcryptohash-md5.c
+        pcryptohash-sha1.c
+        pcryptohash-sha2-256.c
+        pcryptohash-sha2-512.c
+        pcryptohash-sha3.c
+        pdir.c
+        perror.c
+        pfile.c
+        phashtable.c
+        pinifile.c
+        plist.c
+        pmain.c
+        pmem.c
+        pprocess.c
+        pshmbuffer.c
+        psocket.c
+        psocketaddress.c
+        pstring.c
+        ptimeprofiler.c
+        ptree.c
+        ptree-avl.c
+        ptree-bst.c
+        ptree-rb.c
+        puthread.c
+)
+
+if (PLIBSYS_NATIVE_WINDOWS)
+        set (PLIBSYS_CLOSE_MODEL win)
+elseif (PLIBSYS_TARGET_OS STREQUAL darwin)
+        set (PLIBSYS_CLOSE_MODEL darwin)
+else()
+        set (PLIBSYS_CLOSE_MODEL unix)
+endif()
+
+if (PLIBSYS_THREAD_MODEL STREQUAL "")
+        set (PLIBSYS_THREAD_MODEL none)
+endif()
+
+if (PLIBSYS_IPC_MODEL STREQUAL "")
+        set (PLIBSYS_IPC_MODEL none)
+endif()
+
+if (PLIBSYS_TIME_PROFILER_MODEL STREQUAL "")
+        set (PLIBSYS_TIME_PROFILER_MODEL generic)
+endif()
+
+if (PLIBSYS_DIR_MODEL STREQUAL "")
+        set (PLIBSYS_DIR_MODEL none)
+endif()
+
+if (PLIBSYS_LIBRARYLOADER_MODEL STREQUAL "")
+        set (PLIBSYS_LIBRARYLOADER_MODEL none)
+endif()
+
+set (PLIBSYS_PLATFORM_SRCS
+        pcondvariable-${PLIBSYS_THREAD_MODEL}.c
+        pmutex-${PLIBSYS_THREAD_MODEL}.c
+        psemaphore-${PLIBSYS_IPC_MODEL}.c
+        pshm-${PLIBSYS_IPC_MODEL}.c
+        psysclose-${PLIBSYS_CLOSE_MODEL}.c
+        puthread-${PLIBSYS_THREAD_MODEL}.c
+        ptimeprofiler-${PLIBSYS_TIME_PROFILER_MODEL}.c
+        pdir-${PLIBSYS_DIR_MODEL}.c
+        plibraryloader-${PLIBSYS_LIBRARYLOADER_MODEL}.c
+)
+
+if (NOT PLIBSYS_IPC_MODEL STREQUAL none)
+        list (APPEND PLIBSYS_PRIVATE_HDRS pipc-private.h)
+        list (APPEND PLIBSYS_SRCS pipc.c)
+endif()
+
+# Save compiler flags
+set (SAVED_CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS})
+set (SAVED_CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
+set (SAVED_CMAKE_REQUIRED_FLAGS ${SAVED_CMAKE_REQUIRED_FLAGS})
+
+list (APPEND CMAKE_REQUIRED_DEFINITIONS ${PLIBSYS_PLATFORM_DEFINES})
+list (APPEND CMAKE_REQUIRED_LIBRARIES ${PLIBSYS_PLATFORM_LINK_LIBRARIES})
+list (APPEND CMAKE_REQUIRED_FLAGS ${PLIBSYS_PLATFORM_LDFLAGS})
+
+set (PLIBSYS_ATOMIC_LOCK_FREE FALSE)
+
+if (NOT PLIBSYS_ATOMIC_MODEL)
+        message (STATUS "Checking for lock-free atomic intrinsics")
+
+        if (NOT PLIBSYS_C_COMPILER STREQUAL clang)
+                # GCC __atomic* intrinsics
+                check_c_source_compiles (
+                                         "int main () {
+                                          int i, tmp_int = 0;
+                                          void *ptr, *tmp_ptr = 0;
+                                          __atomic_store_4 (&i, 0, __ATOMIC_SEQ_CST);
+                                          __atomic_compare_exchange_n (&i, &tmp_int, 1, 0,
+                                                                       __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
+                                          __atomic_compare_exchange_n ((unsigned long long *) &ptr,
+                                                                       (unsigned long long *) &tmp_ptr,
+                                                                       (unsigned long long) 1, 0,
+                                                                       __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
+                                          __atomic_fetch_add (&i, 1, __ATOMIC_SEQ_CST);
+                                          return 0;
+                                         }"
+                                         PLIBSYS_ATOMIC_IMPL_GCCATOMIC
+                )
+
+                if (PLIBSYS_ATOMIC_IMPL_GCCATOMIC)
+                        set (PLIBSYS_ATOMIC_LOCK_FREE yes)
+                        set (PLIBSYS_ATOMIC_MODEL "c11")
+                endif()
+        endif()
+
+        # GCC __sync* intinsics
+        if (NOT PLIBSYS_ATOMIC_LOCK_FREE)
+                check_c_source_compiles (
+                                         "int main () {
+                                          int i;
+                                          __sync_synchronize ();
+                                          __sync_val_compare_and_swap (&i, 0, 1);
+                                          __sync_fetch_and_add (&i, 1);
+                                          return 0;
+                                         }"
+                                         PLIBSYS_ATOMIC_IMPL_GCCSYNC
+                )
+
+                if (PLIBSYS_ATOMIC_IMPL_GCCSYNC)
+                        set (PLIBSYS_ATOMIC_LOCK_FREE yes)
+                        set (PLIBSYS_ATOMIC_MODEL "sync")
+                endif()
+        endif()
+
+        if (NOT PLIBSYS_ATOMIC_LOCK_FREE)
+                if (PLIBSYS_TARGET_OS STREQUAL windows AND (PLIBSYS_C_COMPILER STREQUAL borland OR
+                                                            PLIBSYS_C_COMPILER STREQUAL watcom  OR
+                                                            PLIBSYS_C_COMPILER STREQUAL icc     OR
+                                                            PLIBSYS_C_COMPILER STREQUAL msvc))
+                        set (PLIBSYS_ATOMIC_LOCK_FREE TRUE)
+                        set (PLIBSYS_ATOMIC_MODEL "win")
+                else()
+                        set (PLIBSYS_ATOMIC_LOCK_FREE FALSE)
+                        set (PLIBSYS_ATOMIC_MODEL "sim")
+                endif()
+        endif()
+
+        if (PLIBSYS_ATOMIC_LOCK_FREE)
+                message (STATUS "Checking for lock-free atomic intrinsics - works")
+        else()
+                message (STATUS "Checking for lock-free atomic intrinsics - not works")
+        endif()
+endif()
+
+list (APPEND PLIBSYS_SRCS
+        patomic-${PLIBSYS_ATOMIC_MODEL}.c
+        pspinlock-${PLIBSYS_ATOMIC_MODEL}.c
+)
+
+if (EXISTS PLIBSYS_CONFIG_FILE)
+        file (REMOVE ${PLIBSYS_CONFIG_FILE})
+endif()
+
+test_big_endian (PLIBSYS_IS_BIGENDIAN)
+
+check_include_file ("float.h" PLIBSYS_HAVE_FLOAT_H)
+check_include_file ("values.h" PLIBSYS_HAVE_VALUES_H)
+check_include_file ("limits.h" PLIBSYS_HAVE_LIMITS_H)
+
+if (PLIBSYS_HAVE_FLOAT_H)
+        set (PLIBSYS_NEED_FLOAT_H TRUE)
+        set (PLIBSYS_FLOAT_MIN FLT_MIN)
+        set (PLIBSYS_FLOAT_MAX FLT_MAX)
+        set (PLIBSYS_DOUBLE_MIN DBL_MIN)
+        set (PLIBSYS_DOUBLE_MAX DBL_MAX)
+elseif (PLIBSYS_HAVE_VALUES_H)
+        set (PLIBSYS_NEED_VALUES_H TRUE)
+        set (PLIBSYS_FLOAT_MIN MINFLOAT)
+        set (PLIBSYS_FLOAT_MAX MAXFLOAT)
+        set (PLIBSYS_DOUBLE_MIN MINDOUBLE)
+        set (PLIBSYS_DOUBLE_MAX MAXDOUBLE)
+endif()
+
+if (PLIBSYS_HAVE_LIMITS_H)
+        set (PLIBSYS_NEED_LIMITS_H TRUE)
+        set (PLIBSYS_SHORT_MIN SHRT_MIN)
+        set (PLIBSYS_SHORT_MAX SHRT_MAX)
+        set (PLIBSYS_USHORT_MAX USHRT_MAX)
+        set (PLIBSYS_INT_MIN INT_MIN)
+        set (PLIBSYS_INT_MAX INT_MAX)
+        set (PLIBSYS_UINT_MAX UINT_MAX)
+        set (PLIBSYS_LONG_MIN LONG_MIN)
+        set (PLIBSYS_LONG_MAX LONG_MAX)
+        set (PLIBSYS_ULONG_MAX ULONG_MAX)
+elseif (PLIBSYS_HAVE_VALUES_H)
+        set (PLIBSYS_NEED_VALUES_H TRUE)
+        set (PLIBSYS_SHORT_MIN MINSHORT)
+        set (PLIBSYS_SHORT_MAX MAXSHORT)
+        set (PLIBSYS_USHORT_MAX "(((pushort) P_MAXSHORT) * 2 + 1)")
+        set (PLIBSYS_INT_MIN MININT)
+        set (PLIBSYS_INT_MAX MAXINT)
+        set (PLIBSYS_UINT_MAX "(((puint) P_MAXINT) * 2 + 1)")
+        set (PLIBSYS_LONG_MIN MINLONG)
+        set (PLIBSYS_LONG_MAX MAXLONG)
+        set (PLIBSYS_ULONG_MAX "(((pulong) P_MAXLONG) * 2 + 1)")
+endif()
+
+if (PLIBSYS_NATIVE_WINDOWS)
+        set (PLIBSYS_NEED_WINDOWS_H TRUE)
+endif()
+
+if (NOT PLIBSYS_NATIVE_WINDOWS)
+        # Check for anonymous mmap()
+        message (STATUS "Checking whether mmap has anonymous mapping")
+
+        check_c_source_compiles (
+                                 "#include <sys/types.h>
+                                  #include <sys/mman.h>
+                                 int main () {
+                                        mmap (0, 1024, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
+                                        return 0;
+                                 }"
+                                 PLIBSYS_MMAP_HAS_MAP_ANON
+                                )
+
+        check_c_source_compiles (
+                                 "#include <sys/types.h>
+                                  #include <sys/mman.h>
+                                 int main () {
+                                        mmap (0, 1024, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+                                        return 0;
+                                 }"
+                                 PLIBSYS_MMAP_HAS_MAP_ANONYMOUS
+                                )
+
+        if (PLIBSYS_MMAP_HAS_MAP_ANONYMOUS OR PLIBSYS_MMAP_HAS_MAP_ANON)
+                message (STATUS "Checking whether mmap has anonymous mapping - yes")
+        else()
+                message (STATUS "Checking whether mmap has anonymous mapping - no")
+        endif()
+
+        if (PLIBSYS_MMAP_HAS_MAP_ANONYMOUS)
+                list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_MMAP_HAS_MAP_ANONYMOUS)
+        elseif (PLIBSYS_MMAP_HAS_MAP_ANON)
+                list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_MMAP_HAS_MAP_ANON)
+        endif()
+
+        # Check for clock_nanosleep() call
+        message (STATUS "Checking whether clock_nanosleep() presents")
+
+        check_c_source_compiles (
+                                 "#include <time.h>
+                                 int main () {
+                                        struct timespec time_sp = {0, 500000000L};
+                                        clock_nanosleep (CLOCK_MONOTONIC, 0, &time_sp, NULL);
+                                        return 0;
+                                 }"
+                                 PLIBSYS_HAS_CLOCKNANOSLEEP
+                                )
+
+        if (PLIBSYS_HAS_CLOCKNANOSLEEP)
+                message (STATUS "Checking whether clock_nanosleep() presents - yes")
+        else()
+                message (STATUS "Checking whether clock_nanosleep() presents - no")
+        endif()
+
+        # Check for nanosleep() call
+        message (STATUS "Checking whether nanosleep() presents")
+
+        check_c_source_compiles (
+                                 "#include <time.h>
+                                 int main () {
+                                        struct timespec time_sp = {0, 500000000L};
+                                        nanosleep (&time_sp, NULL);
+                                        return 0;
+                                 }"
+                                 PLIBSYS_HAS_NANOSLEEP
+                                )
+
+        if (PLIBSYS_HAS_NANOSLEEP)
+                message (STATUS "Checking whether nanosleep() presents - yes")
+                list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_HAS_NANOSLEEP)
+        else()
+                message (STATUS "Checking whether nanosleep() presents - no")
+        endif()
+
+        # Prefere clock_nanosleep() over nanosleep() for power consumption
+        if (PLIBSYS_HAS_CLOCKNANOSLEEP)
+                list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_HAS_CLOCKNANOSLEEP)
+        elseif(PLIBSYS_HAS_NANOSLEEP)
+                list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_HAS_NANOSLEEP)
+        endif()
+
+        # Check for getaddrinfo() call
+        message (STATUS "Checking whether getaddrinfo() presents")
+
+        check_c_source_compiles (
+                                 "#include <sys/socket.h>
+                                  #include <netdb.h>
+                                 int main () {
+                                        getaddrinfo (0, 0, 0, 0);
+                                        freeaddrinfo (0);
+
+                                        return 0;
+                                 }"
+                                 PLIBSYS_HAS_GETADDRINFO
+                                )
+
+        if (PLIBSYS_HAS_GETADDRINFO)
+                message (STATUS "Checking whether getaddrinfo() presents - yes")
+                list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_HAS_GETADDRINFO)
+        else()
+                message (STATUS "Checking whether getaddrinfo() presents - no")
+        endif()
+endif()
+
+if (NOT PLIBSYS_RWLOCK_MODEL)
+        set (PLIBSYS_RWLOCK_MODEL ${PLIBSYS_THREAD_MODEL})
+else()
+        if (NOT PLIBSYS_RWLOCK_MODEL STREQUAL general AND
+            NOT PLIBSYS_RWLOCK_MODEL STREQUAL none)
+                message (WARNING "It's not recommended to mix threading and read-write lock models")
+        endif()
+endif()
+
+if (PLIBSYS_THREAD_MODEL STREQUAL posix)
+        # Some systems only need the difinition to be available
+        if (PLIBSYS_TARGET_OS MATCHES "darwin|aix")
+                set (PLIBSYS_SCHED_CHECK "defined (_POSIX_THREAD_PRIORITY_SCHEDULING)")
+        elseif (PLIBSYS_TARGET_OS STREQUAL android)
+                set (PLIBSYS_SCHED_CHECK "0")
+        else()
+                set (PLIBSYS_SCHED_CHECK "defined (_POSIX_THREAD_PRIORITY_SCHEDULING) && (_POSIX_THREAD_PRIORITY_SCHEDULING - 0 >= 0)")
+        endif()
+
+        # Check for thread scheduling
+        message (STATUS "Checking whether POSIX thread scheduling presents")
+
+        check_c_source_compiles (
+                                 "#include <unistd.h>
+                                  #include <pthread.h>
+                                  #include <sched.h>
+
+                                 int main () {
+                                        #if ${PLIBSYS_SCHED_CHECK}
+                                          sched_get_priority_min (0);
+                                          sched_get_priority_max (0);
+                                        #else
+                                          stop_compile_here
+                                        #endif
+                                        return 0;
+                                 }"
+                                 PLIBSYS_HAS_POSIX_SCHEDULING
+                                )
+
+        if (PLIBSYS_HAS_POSIX_SCHEDULING)
+                message (STATUS "Checking whether POSIX thread scheduling presents - yes")
+                list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_HAS_POSIX_SCHEDULING)
+        else()
+                message (STATUS "Checking whether POSIX thread scheduling presents - no")
+        endif()
+
+        # Check for thread stack size
+        message (STATUS "Checking whether POSIX thread stack size is supported")
+
+        check_c_source_compiles (
+                                 "#include <pthread.h>
+
+                                 int main () {
+                                        pthread_attr_t attr;
+
+                                        pthread_attr_setstacksize (&attr, 0);
+                                        return 0;
+                                 }"
+                                 PLIBSYS_HAS_POSIX_STACKSIZE
+                                )
+
+        if (PLIBSYS_HAS_POSIX_STACKSIZE)
+                message (STATUS "Checking whether POSIX thread stack size is supported - yes")
+                list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_HAS_POSIX_STACKSIZE)
+        else()
+                message (STATUS "Checking whether POSIX thread stack size is supported - no")
+        endif()
+endif()
+
+# Some platforms may have headers, but lack actual implementation,
+# thus we can let platform to override read-write lock model with
+# general implementation
+if (PLIBSYS_THREAD_MODEL STREQUAL posix AND
+    PLIBSYS_RWLOCK_MODEL STREQUAL posix)
+        # Check for read-write lock support
+        message (STATUS "Checking whether POSIX read-write locks are supported")
+
+        check_c_source_compiles (
+                                 "#include <pthread.h>
+
+                                 int main () {
+                                        pthread_rwlock_t rwl;
+
+                                        pthread_rwlock_init (&rwl, 0);
+                                        pthread_rwlock_destroy (&rwl);
+                                        return 0;
+                                 }"
+                                 PLIBSYS_HAS_POSIX_RWLOCK
+                                )
+
+        if (PLIBSYS_HAS_POSIX_RWLOCK)
+                message (STATUS "Checking whether POSIX read-write locks are supported - yes")
+        else()
+                message (STATUS "Checking whether POSIX read-write locks are supported - no")
+                set (PLIBSYS_RWLOCK_MODEL "general")
+        endif()
+endif()
+
+list (APPEND PLIBSYS_PLATFORM_SRCS prwlock-${PLIBSYS_RWLOCK_MODEL}.c)
+
+# POSIX thread naming functions
+check_c_source_compiles (
+                         "#include <pthread.h>
+                          #include <pthread_np.h>
+
+                         int main () {return 0;}"
+                         PLIBSYS_HAS_PTHREAD_NP
+                        )
+
+if(PLIBSYS_HAS_PTHREAD_NP)
+        set (PLIBSYS_NEED_PTHREAD_NP_H TRUE)
+else()
+        set (PLIBSYS_NEED_PTHREAD_NP_H FALSE)
+endif()
+
+message (STATUS "Checking whether POSIX thread names are supported")
+
+plibsys_detect_thread_name (PLIBSYS_NEED_PTHREAD_NP_H PLIBSYS_THREADNAME_DEF)
+
+if (NOT PLIBSYS_THREADNAME_DEF STREQUAL "NONE")
+        list (APPEND PLIBSYS_COMPILE_DEFS -D${PLIBSYS_THREADNAME_DEF})
+endif()
+
+if (PLIBSYS_THREADNAME_DEF STREQUAL "NONE")
+        message (STATUS "Checking whether POSIX thread names are supported - no")
+else()
+        message (STATUS "Checking whether POSIX thread names are supported - yes")
+endif()
+
+# Windows sockets
+if (PLIBSYS_NATIVE_WINDOWS)
+        set (PLIBSYS_SOCKET_INCLUDES "#include <winsock2.h>
+                                      #include <ws2tcpip.h>
+                                      #include <windows.h>")
+else()
+        set (PLIBSYS_SOCKET_INCLUDES "#include <sys/types.h>
+                                      #include <sys/socket.h>
+                                      #include <netinet/in.h>")
+endif()
+
+# Check for socklen_t definition
+message (STATUS "Checking whether socklen_t is defined")
+
+check_c_source_compiles (
+                         "${PLIBSYS_SOCKET_INCLUDES}
+                          int main () {
+                                socklen_t len = sizeof (socklen_t);
+                                return len > 0 ? 0 : -1;
+                          }"
+                          PLIBSYS_HAS_SOCKLEN_T
+                        )
+
+if (PLIBSYS_HAS_SOCKLEN_T)
+        message (STATUS "Checking whether socklen_t is defined - yes")
+        list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_HAS_SOCKLEN_T)
+else()
+        message (STATUS "Checking whether socklen_t is defined - no")
+endif()
+
+# Check for sockaddr_storage structure
+message (STATUS "Checking whether struct sockaddr_storage is defined")
+
+check_c_source_compiles (
+                         "${PLIBSYS_SOCKET_INCLUDES}
+                          int main () {
+                                struct sockaddr_storage sock_addr;
+                                sock_addr.ss_family = AF_INET;
+
+                                return 0;
+                          }"
+                          PLIBSYS_HAS_SOCKADDR_STORAGE
+                        )
+
+if (PLIBSYS_HAS_SOCKADDR_STORAGE)
+        message (STATUS "Checking whether struct sockaddr_storage is defined - yes")
+        list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_HAS_SOCKADDR_STORAGE)
+else()
+        message (STATUS "Checking whether struct sockaddr_storage is defined - no")
+endif()
+
+# Check sa_len field in struct sockaddr
+message (STATUS "Checking whether struct sockaddr has sa_len")
+
+check_c_source_compiles (
+                         "${PLIBSYS_SOCKET_INCLUDES}
+                          int main () {
+                                struct sockaddr sock_addr;
+                                sock_addr.sa_len = 0;
+
+                                return 0;
+                          }"
+                          PLIBSYS_SOCKADDR_HAS_SA_LEN
+                        )
+
+if (PLIBSYS_SOCKADDR_HAS_SA_LEN)
+        message (STATUS "Checking whether struct sockaddr has sa_len - yes")
+        list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_SOCKADDR_HAS_SA_LEN)
+else()
+        message (STATUS "Checking whether struct sockaddr has sa_len - no")
+endif()
+
+# Check sin6_scope_id field in struct sockaddr_in6
+message (STATUS "Checking whether struct sockaddr_in6 has sin6_scope_id")
+
+check_c_source_compiles (
+                         "${PLIBSYS_SOCKET_INCLUDES}
+                          int main () {
+                                struct sockaddr_in6 sock_addr;
+                                sock_addr.sin6_scope_id = 0;
+
+                                return 0;
+                          }"
+                          PLIBSYS_SOCKADDR_IN6_HAS_SCOPEID
+                        )
+
+if (PLIBSYS_SOCKADDR_IN6_HAS_SCOPEID)
+        message (STATUS "Checking whether struct sockaddr_in6 has sin6_scope_id - yes")
+        list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_SOCKADDR_IN6_HAS_SCOPEID)
+else()
+        message (STATUS "Checking whether struct sockaddr_in6 has sin6_scope_id - no")
+endif()
+
+# Check sin6_flowinfo field in struct sockaddr_in6
+message (STATUS "Checking whether struct sockaddr_in6 has sin6_flowinfo")
+
+check_c_source_compiles (
+                         "${PLIBSYS_SOCKET_INCLUDES}
+                          int main () {
+                                struct sockaddr_in6 sock_addr;
+                                sock_addr.sin6_flowinfo = 0;
+
+                                return 0;
+                          }"
+                          PLIBSYS_SOCKADDR_IN6_HAS_FLOWINFO
+                        )
+
+if (PLIBSYS_SOCKADDR_IN6_HAS_FLOWINFO)
+        message (STATUS "Checking whether struct sockaddr_in6 has sin6_flowinfo - yes")
+        list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_SOCKADDR_IN6_HAS_FLOWINFO)
+else()
+        message (STATUS "Checking whether struct sockaddr_in6 has sin6_flowinfo - no")
+endif()
+
+# Check sa_family_t type size in struct sockaddr
+set (CMAKE_EXTRA_INCLUDE_FILES_OLD ${CMAKE_EXTRA_INCLUDE_FILES})
+
+if (PLIBSYS_NATIVE_WINDOWS)
+        set (CMAKE_EXTRA_INCLUDE_FILES
+                winsock2.h
+                ws2tcpip.h
+                windows.h
+        )
+else()
+        set (CMAKE_EXTRA_INCLUDE_FILES
+                sys/types.h
+                sys/socket.h
+                netinet/in.h
+        )
+endif()
+
+check_type_size ("((struct sockaddr *) 0)->sa_family" PLIBSYS_SIZEOF_SAFAMILY_T LANGUAGE C)
+
+set (CMAKE_EXTRA_INCLUDE_FILES ${CMAKE_EXTRA_INCLUDE_FILES_OLD})
+
+# Check for lldiv() call
+message (STATUS "Checking whether lldiv() presents")
+
+check_c_source_compiles (
+                         "#define __USE_ISOC99
+                          #include <stdlib.h>
+                          int main () {
+                                lldiv_t res = lldiv (100LL, 13LL);
+                                res.quot = 0;
+                                res.rem = 0;
+
+                                return 0;
+                          }"
+                          PLIBSYS_HAS_LLDIV
+                        )
+
+if (PLIBSYS_HAS_LLDIV)
+        message (STATUS "Checking whether lldiv() presents - yes")
+        list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_HAS_LLDIV)
+else()
+        message (STATUS "Checking whether lldiv() presents - no")
+endif()
+
+# Symbols visibility attributes
+if (PLIBSYS_VISIBILITY)
+        message (STATUS "Checking whether compiler supports visibility")
+        plibsys_detect_visibility (PLIBSYS_VISIBILITY_CFLAGS PLIBSYS_VISIBILITY_LDFLAGS)
+
+        if (NOT PLIBSYS_VISIBILITY_CFLAGS AND NOT PLIBSYS_VISIBILITY_LDFLAGS)
+                message (STATUS "Checking whether compiler supports visibility - no")
+                set (PLIBSYS_VISIBILITY OFF)
+        else()
+                message (STATUS "Checking whether compiler supports visibility - yes")
+
+                if (PLIBSYS_VISIBILITY_CFLAGS)
+                        list (APPEND PLIBSYS_PLATFORM_CFLAGS ${PLIBSYS_VISIBILITY_CFLAGS})
+                endif()
+
+                if (PLIBSYS_VISIBILITY_LDFLAGS)
+                        list (APPEND PLIBSYS_PLATFORM_LDFLAGS ${PLIBSYS_VISIBILITY_LDFLAGS})
+                endif()
+        endif()
+endif()
+
+# Variable arguments
+check_include_file ("stdarg.h" PLIBSYS_HAVE_STDARG_H)
+
+if (NOT PLIBSYS_HAVE_STDARG_H)
+        message (FATAL_ERROR "Support for <stdarg.h> is required for target plarform, not found")
+endif()
+
+plibsys_detect_va_copy (PLIBSYS_VA_COPY)
+
+configure_file (${CMAKE_CURRENT_SOURCE_DIR}/plibsysconfig.h.in ${CMAKE_BINARY_DIR}/plibsysconfig.h)
+
+# Restore compiler flags
+set (CMAKE_REQUIRED_DEFINITIONS ${SAVED_CMAKE_REQUIRED_DEFINITIONS})
+set (CMAKE_REQUIRED_LIBRARIES ${SAVED_CMAKE_REQUIRED_LIBRARIES})
+set (SAVED_CMAKE_REQUIRED_FLAGS ${SAVED_CMAKE_REQUIRED_FLAGS})
+
+# Disable useless warnings
+if (MSVC)
+        list (APPEND PLIBSYS_COMPILE_DEFS -D_CRT_SECURE_NO_WARNINGS)
+        list (APPEND PLIBSYS_COMPILE_DEFS -D_WINSOCK_DEPRECATED_NO_WARNINGS)
+endif()
+
+# Prepare compile definitions
+list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_COMPILATION)
+
+if (PLIBSYS_PLATFORM_DEFINES)
+        list (APPEND PLIBSYS_COMPILE_DEFS ${PLIBSYS_PLATFORM_DEFINES})
+endif()
+
+# Add targets
+add_library (plibsys SHARED ${PLIBSYS_SRCS} ${PLIBSYS_PLATFORM_SRCS} ${PLIBSYS_PUBLIC_HDRS} ${PLIBSYS_PRIVATE_HDRS})
+
+if (PLIBSYS_BUILD_STATIC)
+        add_library (plibsysstatic STATIC ${PLIBSYS_SRCS} ${PLIBSYS_PLATFORM_SRCS} ${PLIBSYS_PUBLIC_HDRS} ${PLIBSYS_PRIVATE_HDRS})
+endif()
+
+# Add include directories
+if (COMMAND target_include_directories)
+        target_include_directories (plibsys PUBLIC ${PLIBSYS_INCLUDE_DIRS})
+
+        if (PLIBSYS_BUILD_STATIC)
+                target_include_directories (plibsysstatic PUBLIC ${PLIBSYS_INCLUDE_DIRS})
+        endif()
+else()
+        include_directories (${PLIBSYS_INCLUDE_DIRS})
+endif()
+
+# Add compile definitions
+if (COMMAND target_compile_definitions)
+        target_compile_definitions (plibsys PRIVATE ${PLIBSYS_COMPILE_DEFS})
+
+        if (PLIBSYS_BUILD_STATIC)
+                target_compile_definitions (plibsysstatic PRIVATE ${PLIBSYS_COMPILE_DEFS})
+        endif()
+else()
+        add_definitions (${PLIBSYS_COMPILE_DEFS})
+endif()
+
+set_target_properties (plibsys PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIR})
+set_target_properties (plibsys PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_DIR})
+set_target_properties (plibsys PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${OUTPUT_DIR})
+
+# OpenBSD uses a bit different library versioning
+if (PLIBSYS_TARGET_OS STREQUAL openbsd)
+        set (PLIBSYS_SOVERSION ${PLIBSYS_VERSION_MAJOR}.${PLIBSYS_VERSION_MINOR})
+endif()
+
+if (NOT PLIBSYS_TARGET_OS STREQUAL os2 AND NOT PLIBSYS_TARGET_OS STREQUAL amigaos)
+        set_target_properties (plibsys PROPERTIES SOVERSION ${PLIBSYS_SOVERSION})
+endif()
+
+if (PLIBSYS_BUILD_STATIC)
+        set_target_properties (plibsysstatic PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${OUTPUT_DIR})
+
+        if (NOT PLIBSYS_TARGET_OS STREQUAL os2)
+                set_target_properties (plibsysstatic PROPERTIES SOVERSION ${PLIBSYS_SOVERSION})
+        endif()
+endif()
+
+if (PLIBSYS_PLATFORM_CFLAGS)
+        foreach (PLATFORM_CFLAG ${PLIBSYS_PLATFORM_CFLAGS})
+                set (PLIBSYS_PLATFORM_CFLAGS_STR "${PLIBSYS_PLATFORM_CFLAGS_STR} ${PLATFORM_CFLAG}")
+        endforeach()
+
+        set_target_properties (plibsys PROPERTIES COMPILE_FLAGS "${PLIBSYS_PLATFORM_CFLAGS_STR}")
+
+        if (PLIBSYS_BUILD_STATIC)
+                set_target_properties (plibsysstatic PROPERTIES COMPILE_FLAGS "${PLIBSYS_PLATFORM_CFLAGS_STR}")
+        endif()
+endif()
+
+if (PLIBSYS_PLATFORM_LDFLAGS)
+        foreach (PLATFORM_LDFLAG ${PLIBSYS_PLATFORM_LDFLAGS})
+                set (PLIBSYS_PLATFORM_LDFLAGS_STR "${PLIBSYS_PLATFORM_LDFLAGS_STR} ${PLATFORM_LDFLAG}")
+        endforeach()
+
+        set_target_properties (plibsys PROPERTIES LINK_FLAGS "${PLIBSYS_PLATFORM_LDFLAGS_STR}")
+
+        if (PLIBSYS_BUILD_STATIC)
+                set_target_properties (plibsysstatic PROPERTIES LINK_FLAGS "${PLIBSYS_PLATFORM_LDFLAGS_STR}")
+        endif()
+endif()
+
+target_link_libraries (plibsys ${PLIBSYS_PLATFORM_LINK_LIBRARIES})
+
+if (PLIBSYS_BUILD_STATIC)
+        target_link_libraries (plibsysstatic ${PLIBSYS_PLATFORM_LINK_LIBRARIES})
+endif()
+
+if (PLIBSYS_BUILD_STATIC)
+        set (PLIBSYS_INSTALL_TARGETS plibsys plibsysstatic)
+else()
+        set (PLIBSYS_INSTALL_TARGETS plibsys)
+endif()
+
+if (PLIBSYS_NATIVE_WINDOWS)
+        install (TARGETS ${PLIBSYS_INSTALL_TARGETS}
+                DESTINATION lib
+                RUNTIME DESTINATION lib
+                COMPONENT Core
+        )
+
+        if (NOT DEFINED CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_NO_WARNINGS)
+                set (CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_NO_WARNINGS TRUE)
+        endif()
+
+        set (CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
+        include (InstallRequiredSystemLibraries)
+
+        install (PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS}
+                DESTINATION lib
+                COMPONENT Core
+        )
+endif()
+
+# Prepare installation dirs
+if (NOT CMAKE_INSTALL_LIBDIR)
+        set (CMAKE_INSTALL_LIBDIR "lib")
+endif()
+
+if (NOT CMAKE_INSTALL_INCLUDEDIR)
+        set (CMAKE_INSTALL_INCLUDEDIR "include")
+endif()
+
+install (TARGETS ${PLIBSYS_INSTALL_TARGETS} EXPORT plibsys-targets
+        DESTINATION ${CMAKE_INSTALL_LIBDIR}
+        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+        COMPONENT Core
+)
+install (TARGETS ${PLIBSYS_INSTALL_TARGETS}
+        DESTINATION ${CMAKE_INSTALL_LIBDIR}
+        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+        COMPONENT Core
+)
+install (FILES
+        ${PLIBSYS_PUBLIC_HDRS}
+        ${CMAKE_BINARY_DIR}/plibsysconfig.h
+        DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/plibsys
+        COMPONENT Core
+)
+
+if (PLIBSYS_VA_COPY)
+        set(PLIBSYS_VA_COPY_STATUS "YES")
+else()
+        set(PLIBSYS_VA_COPY_STATUS "NO")
+endif()
+
+if (PLIBSYS_OS_BITS STREQUAL "universal")
+    set (PLIBSYS_ADDRESS_MODEL "universal")
+else()
+    set (PLIBSYS_ADDRESS_MODEL "${PLIBSYS_OS_BITS} bit")
+endif()
+
+# Print summary
+SET (PLIBSYS_SUMMARY
+"
+        == Build configuration ==
+
+        Platfrom:               ${PLIBSYS_TARGET_OS}
+        Compiler:               ${PLIBSYS_C_COMPILER}
+        Address model:          ${PLIBSYS_ADDRESS_MODEL}
+
+        Thread model:           ${PLIBSYS_THREAD_MODEL}
+        RW lock model:          ${PLIBSYS_RWLOCK_MODEL}
+        IPC model:              ${PLIBSYS_IPC_MODEL}
+        DIR model:              ${PLIBSYS_DIR_MODEL}
+        Library loader model:   ${PLIBSYS_LIBRARYLOADER_MODEL}
+        Time profiler model:    ${PLIBSYS_TIME_PROFILER_MODEL}
+        Atomic model:           ${PLIBSYS_ATOMIC_MODEL}
+
+        Platform defines:       ${PLIBSYS_PLATFORM_DEFINES}
+        Platform CFLAGS:        ${PLIBSYS_PLATFORM_CFLAGS}
+        Platform LDFLAGS:       ${PLIBSYS_PLATFORM_LDFLAGS}
+        Platform libraries:     ${PLIBSYS_PLATFORM_LINK_LIBRARIES}
+
+        Build static library:   ${PLIBSYS_BUILD_STATIC}
+        Build tests:            ${PLIBSYS_TESTS}
+        Coverage support:       ${PLIBSYS_COVERAGE}
+        Visibility:             ${PLIBSYS_VISIBILITY}
+
+        va_copy availability:   ${PLIBSYS_VA_COPY_STATUS}
+
+")
+
+message ("${PLIBSYS_SUMMARY}")
diff --git a/3rdparty/plibsys/src/patomic-c11.c b/3rdparty/plibsys/src/patomic-c11.c
new file mode 100644
index 0000000..e6ed760
--- /dev/null
+++ b/3rdparty/plibsys/src/patomic-c11.c
@@ -0,0 +1,182 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "patomic.h"
+
+#ifdef P_CC_SUN
+#  define PATOMIC_INT_CAST(x) (pint *) (x)
+#  define PATOMIC_SIZE_CAST(x) (psize *) (x)
+#else
+#  define PATOMIC_INT_CAST(x) x
+#  define PATOMIC_SIZE_CAST(x) x
+#endif
+
+P_LIB_API pint
+p_atomic_int_get (const volatile pint *atomic)
+{
+	return (pint) __atomic_load_4 (PATOMIC_INT_CAST (atomic), __ATOMIC_SEQ_CST);
+}
+
+P_LIB_API void
+p_atomic_int_set (volatile pint	*atomic,
+		  pint		val)
+{
+	__atomic_store_4 (PATOMIC_INT_CAST (atomic), val, __ATOMIC_SEQ_CST);
+}
+
+P_LIB_API void
+p_atomic_int_inc (volatile pint *atomic)
+{
+	(void) __atomic_fetch_add (atomic, 1, __ATOMIC_SEQ_CST);
+}
+
+P_LIB_API pboolean
+p_atomic_int_dec_and_test (volatile pint *atomic)
+{
+	return (__atomic_fetch_sub (atomic, 1, __ATOMIC_SEQ_CST) == 1) ? TRUE : FALSE;
+}
+
+P_LIB_API pboolean
+p_atomic_int_compare_and_exchange (volatile pint	*atomic,
+				   pint			oldval,
+				   pint			newval)
+{
+	pint tmp_int = oldval;
+
+	return (pboolean) __atomic_compare_exchange_n (PATOMIC_INT_CAST (atomic),
+						       &tmp_int,
+						       newval,
+						       0,
+						       __ATOMIC_SEQ_CST,
+						       __ATOMIC_SEQ_CST);
+}
+
+P_LIB_API pint
+p_atomic_int_add (volatile pint	*atomic,
+		  pint		val)
+{
+	return (pint) __atomic_fetch_add (atomic, val, __ATOMIC_SEQ_CST);
+}
+
+P_LIB_API puint
+p_atomic_int_and (volatile puint	*atomic,
+		  puint			val)
+{
+	return (puint) __atomic_fetch_and (atomic, val, __ATOMIC_SEQ_CST);
+}
+
+P_LIB_API puint
+p_atomic_int_or (volatile puint	*atomic,
+		 puint		val)
+{
+	return (puint) __atomic_fetch_or (atomic, val, __ATOMIC_SEQ_CST);
+}
+
+P_LIB_API puint
+p_atomic_int_xor (volatile puint	*atomic,
+		  puint			val)
+{
+	return (puint) __atomic_fetch_xor (atomic, val, __ATOMIC_SEQ_CST);
+}
+
+P_LIB_API ppointer
+p_atomic_pointer_get (const volatile void *atomic)
+{
+#if (PLIBSYS_SIZEOF_VOID_P == 8)
+	return (ppointer) __atomic_load_8 (PATOMIC_SIZE_CAST ((const volatile psize *) atomic), __ATOMIC_SEQ_CST);
+#else
+	return (ppointer) __atomic_load_4 (PATOMIC_SIZE_CAST ((const volatile psize *) atomic), __ATOMIC_SEQ_CST);
+#endif
+}
+
+P_LIB_API void
+p_atomic_pointer_set (volatile void	*atomic,
+		      ppointer		val)
+{
+#if (PLIBSYS_SIZEOF_VOID_P == 8)
+	__atomic_store_8 (PATOMIC_SIZE_CAST ((volatile psize *) atomic), (psize) val, __ATOMIC_SEQ_CST);
+#else
+	__atomic_store_4 (PATOMIC_SIZE_CAST ((volatile psize *) atomic), (psize) val, __ATOMIC_SEQ_CST);
+#endif
+}
+
+P_LIB_API pboolean
+p_atomic_pointer_compare_and_exchange (volatile void	*atomic,
+				       ppointer		oldval,
+				       ppointer		newval)
+{
+	ppointer tmp_pointer = oldval;
+
+	return (pboolean) __atomic_compare_exchange_n (PATOMIC_SIZE_CAST ((volatile psize *) atomic),
+						       (psize *) &tmp_pointer,
+						       PPOINTER_TO_PSIZE (newval),
+						       0,
+						       __ATOMIC_SEQ_CST,
+						       __ATOMIC_SEQ_CST);
+}
+
+P_LIB_API pssize
+p_atomic_pointer_add (volatile void	*atomic,
+		      pssize		val)
+{
+	return (pssize) __atomic_fetch_add ((volatile pssize *) atomic, val, __ATOMIC_SEQ_CST);
+}
+
+P_LIB_API psize
+p_atomic_pointer_and (volatile void	*atomic,
+		      psize		val)
+{
+	return (psize) __atomic_fetch_and ((volatile psize *) atomic, val, __ATOMIC_SEQ_CST);
+}
+
+P_LIB_API psize
+p_atomic_pointer_or (volatile void	*atomic,
+		     psize		val)
+{
+	return (psize) __atomic_fetch_or ((volatile pssize *) atomic, val, __ATOMIC_SEQ_CST);
+}
+
+P_LIB_API psize
+p_atomic_pointer_xor (volatile void	*atomic,
+		      psize		val)
+{
+	return (psize) __atomic_fetch_xor ((volatile pssize *) atomic, val, __ATOMIC_SEQ_CST);
+}
+
+P_LIB_API pboolean
+p_atomic_is_lock_free (void)
+{
+	return TRUE;
+}
+
+void
+p_atomic_thread_init (void)
+{
+}
+
+void
+p_atomic_thread_shutdown (void)
+{
+}
diff --git a/3rdparty/plibsys/src/patomic-decc.c b/3rdparty/plibsys/src/patomic-decc.c
new file mode 100644
index 0000000..d103aa9
--- /dev/null
+++ b/3rdparty/plibsys/src/patomic-decc.c
@@ -0,0 +1,286 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "patomic.h"
+
+#ifdef P_OS_VMS
+#  include <builtins.h>
+#else
+#  include <machine/builtins.h>
+#endif
+
+#ifdef __ia64
+#  define PATOMIC_DECC_CAS_LONG(atomic_src, oldval, newval, atomic_dst)	\
+	  __CMP_SWAP_LONG ((volatile void *) (atomic_src),		\
+			   (pint) (oldval),				\
+			   (pint) (newval))
+#  define PATOMIC_DECC_CAS_QUAD(atomic_src, oldval, newval, atomic_dst)	\
+	  __CMP_SWAP_QUAD ((volatile void *) (atomic_src),		\
+			   (pint64) (oldval),				\
+			   (pint64) (newval))	  
+#else
+#  define PATOMIC_DECC_CAS_LONG(atomic_src, oldval, newval, atomic_dst)	\
+	  __CMP_STORE_LONG ((volatile void *) (atomic_src),		\
+			    (pint) (oldval),				\
+			    (pint) (newval),				\
+			    (volatile void *) (atomic_dst))
+#  define PATOMIC_DECC_CAS_QUAD(atomic_src, oldval, newval, atomic_dst)	\
+	  __CMP_STORE_QUAD ((volatile void *) (atomic_src),		\
+			    (pint64) (oldval),				\
+			    (pint64) (newval),				\
+			    (volatile void *) (atomic_dst))
+#endif
+
+P_LIB_API pint
+p_atomic_int_get (const volatile pint *atomic)
+{
+	__MB ();
+	return (pint) *atomic;
+}
+
+P_LIB_API void
+p_atomic_int_set (volatile pint	*atomic,
+		  pint		val)
+{
+	(void) __ATOMIC_EXCH_LONG ((volatile void *) atomic, val);
+	__MB ();
+}
+
+P_LIB_API void
+p_atomic_int_inc (volatile pint *atomic)
+{
+	__MB ();
+	(void) __ATOMIC_INCREMENT_LONG ((volatile void *) atomic);
+	__MB ();
+}
+
+P_LIB_API pboolean
+p_atomic_int_dec_and_test (volatile pint *atomic)
+{
+	pboolean result;
+
+	__MB ();
+	result = __ATOMIC_DECREMENT_LONG ((volatile void *) atomic) == 1 ? TRUE : FALSE;
+	__MB ();
+
+	return result;
+}
+
+P_LIB_API pboolean
+p_atomic_int_compare_and_exchange (volatile pint	*atomic,
+				   pint			oldval,
+				   pint			newval)
+{
+	pboolean result;
+
+	__MB ();
+	result = PATOMIC_DECC_CAS_LONG (atomic, oldval, newval, atomic) == 1 ? TRUE : FALSE;
+	__MB ();
+
+	return result;
+}
+
+P_LIB_API pint
+p_atomic_int_add (volatile pint	*atomic,
+		  pint		val)
+{
+	pint result;
+
+	__MB ();
+	result = __ATOMIC_ADD_LONG ((volatile void *) atomic, val);
+	__MB ();
+
+	return result;
+}
+
+P_LIB_API puint
+p_atomic_int_and (volatile puint	*atomic,
+		  puint			val)
+{
+	puint result;
+
+	__MB ();
+	result = (puint) __ATOMIC_AND_LONG ((volatile void *) atomic, (pint) val);
+	__MB ();
+
+	return result;
+}
+
+P_LIB_API puint
+p_atomic_int_or (volatile puint	*atomic,
+		 puint		val)
+{
+	puint result;
+
+	__MB ();
+	result = (puint) __ATOMIC_OR_LONG ((volatile void *) atomic, (pint) val);
+	__MB ();
+
+	return result;
+}
+
+P_LIB_API puint
+p_atomic_int_xor (volatile puint	*atomic,
+		  puint			val)
+{
+	pint i;
+
+	do {
+		__MB ();
+		i = (pint) (*atomic);
+	} while (PATOMIC_DECC_CAS_LONG (atomic, i, i ^ ((pint) val), atomic) != 1);
+
+	__MB ();
+
+	return i;
+}
+
+P_LIB_API ppointer
+p_atomic_pointer_get (const volatile void *atomic)
+{
+	__MB ();
+	return (ppointer) (* ((const volatile psize *) atomic));
+}
+
+P_LIB_API void
+p_atomic_pointer_set (volatile void	*atomic,
+		      ppointer		val)
+{
+#if (PLIBSYS_SIZEOF_VOID_P == 8)
+	(void) __ATOMIC_EXCH_QUAD (atomic, (pint64) val);
+#else
+	(void) __ATOMIC_EXCH_LONG (atomic, (pint) val);
+#endif
+	__MB ();
+}
+
+P_LIB_API pboolean
+p_atomic_pointer_compare_and_exchange (volatile void	*atomic,
+				       ppointer		oldval,
+				       ppointer		newval)
+{
+	pboolean result;
+
+	__MB ();
+#if (PLIBSYS_SIZEOF_VOID_P == 8)
+	result = PATOMIC_DECC_CAS_QUAD (atomic, oldval, newval, atomic) == 1 ? TRUE : FALSE;
+#else
+	result = PATOMIC_DECC_CAS_LONG (atomic, oldval, newval, atomic) == 1 ? TRUE : FALSE;
+#endif
+	__MB ();
+
+	return result;
+}
+
+P_LIB_API pssize
+p_atomic_pointer_add (volatile void	*atomic,
+		      pssize		val)
+{
+	pssize result;
+
+	__MB ();
+#if (PLIBSYS_SIZEOF_VOID_P == 8)
+	result = (pssize) __ATOMIC_ADD_QUAD (atomic, (pint64) val);
+#else
+	result = (pssize) __ATOMIC_ADD_LONG (atomic, (pint) val);
+#endif
+	__MB ();
+
+	return result;
+}
+
+P_LIB_API psize
+p_atomic_pointer_and (volatile void	*atomic,
+		      psize		val)
+{
+	psize result;
+
+	__MB ();
+#if (PLIBSYS_SIZEOF_VOID_P == 8)
+	result = (psize) __ATOMIC_AND_QUAD (atomic, (pint64) val);
+#else
+	result = (psize) __ATOMIC_AND_LONG (atomic, (pint) val);
+#endif
+	__MB ();
+
+	return result;
+}
+
+P_LIB_API psize
+p_atomic_pointer_or (volatile void	*atomic,
+		     psize		val)
+{
+	psize result;
+
+	__MB ();
+#if (PLIBSYS_SIZEOF_VOID_P == 8)
+	result = (psize) __ATOMIC_OR_QUAD (atomic, (pint64) val);
+#else
+	result = (psize) __ATOMIC_OR_LONG (atomic, (pint) val);
+#endif
+	__MB ();
+
+	return result;
+}
+
+P_LIB_API psize
+p_atomic_pointer_xor (volatile void	*atomic,
+		      psize		val)
+{
+#if (PLIBSYS_SIZEOF_VOID_P == 8)
+	pint64 i;
+
+	do {
+		__MB ();
+		i = (pint64) (* ((volatile psize *) atomic));
+	} while (PATOMIC_DECC_CAS_QUAD (atomic, i, i ^ ((pint64) val), atomic) != 1);
+#else
+	pint i;
+
+	do {
+		__MB ();
+		i = (pint) (* ((volatile psize *) atomic));
+	} while (PATOMIC_DECC_CAS_LONG (atomic, i, i ^ ((pint) val), atomic) != 1);
+#endif
+	__MB ();
+
+	return (psize) i;
+}
+
+P_LIB_API pboolean
+p_atomic_is_lock_free (void)
+{
+	return TRUE;
+}
+
+void
+p_atomic_thread_init (void)
+{
+}
+
+void
+p_atomic_thread_shutdown (void)
+{
+}
diff --git a/3rdparty/plibsys/src/patomic-sim.c b/3rdparty/plibsys/src/patomic-sim.c
new file mode 100644
index 0000000..12d0604
--- /dev/null
+++ b/3rdparty/plibsys/src/patomic-sim.c
@@ -0,0 +1,268 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "patomic.h"
+#include "pmutex.h"
+
+/* We have to use the slow, but safe locking method. */
+static PMutex *pp_atomic_mutex = NULL;
+
+P_LIB_API pint
+p_atomic_int_get (const volatile pint *atomic)
+{
+	pint value;
+
+	p_mutex_lock (pp_atomic_mutex);
+	value = *atomic;
+	p_mutex_unlock (pp_atomic_mutex);
+
+	return value;
+}
+
+P_LIB_API void
+p_atomic_int_set (volatile pint	*atomic,
+		  pint		val)
+{
+	p_mutex_lock (pp_atomic_mutex);
+	*atomic = val;
+	p_mutex_unlock (pp_atomic_mutex);
+}
+
+P_LIB_API void
+p_atomic_int_inc (volatile pint *atomic)
+{
+	p_mutex_lock (pp_atomic_mutex);
+	(*atomic)++;
+	p_mutex_unlock (pp_atomic_mutex);
+}
+
+P_LIB_API pboolean
+p_atomic_int_dec_and_test (volatile pint *atomic)
+{
+	pboolean is_zero;
+
+	p_mutex_lock (pp_atomic_mutex);
+	is_zero = --(*atomic) == 0;
+	p_mutex_unlock (pp_atomic_mutex);
+
+	return is_zero;
+}
+
+P_LIB_API pboolean
+p_atomic_int_compare_and_exchange (volatile pint	*atomic,
+				   pint			oldval,
+				   pint			newval)
+{
+	pboolean success;
+
+	p_mutex_lock (pp_atomic_mutex);
+
+	if ((success = (*atomic == oldval)))
+		*atomic = newval;
+
+	p_mutex_unlock (pp_atomic_mutex);
+
+	return success;
+}
+
+P_LIB_API pint
+p_atomic_int_add (volatile pint	*atomic,
+		  pint		val)
+{
+	pint oldval;
+
+	p_mutex_lock (pp_atomic_mutex);
+	oldval = *atomic;
+	*atomic = oldval + val;
+	p_mutex_unlock (pp_atomic_mutex);
+
+	return oldval;
+}
+
+P_LIB_API puint
+p_atomic_int_and (volatile puint	*atomic,
+		  puint			val)
+{
+	puint oldval;
+
+	p_mutex_lock (pp_atomic_mutex);
+	oldval = *atomic;
+	*atomic = oldval & val;
+	p_mutex_unlock (pp_atomic_mutex);
+
+	return oldval;
+}
+
+P_LIB_API puint
+p_atomic_int_or (volatile puint	*atomic,
+		 puint		val)
+{
+	puint oldval;
+
+	p_mutex_lock (pp_atomic_mutex);
+	oldval = *atomic;
+	*atomic = oldval | val;
+	p_mutex_unlock (pp_atomic_mutex);
+
+	return oldval;
+}
+
+P_LIB_API puint
+p_atomic_int_xor (volatile puint	*atomic,
+		  puint			val)
+{
+	puint oldval;
+
+	p_mutex_lock (pp_atomic_mutex);
+	oldval = *atomic;
+	*atomic = oldval ^ val;
+	p_mutex_unlock (pp_atomic_mutex);
+
+	return oldval;
+}
+
+P_LIB_API ppointer
+p_atomic_pointer_get (const volatile void *atomic)
+{
+	const volatile ppointer *ptr = atomic;
+	ppointer value;
+
+	p_mutex_lock (pp_atomic_mutex);
+	value = *ptr;
+	p_mutex_unlock (pp_atomic_mutex);
+
+	return value;
+}
+
+P_LIB_API void
+p_atomic_pointer_set (volatile void	*atomic,
+		      ppointer		val)
+{
+	volatile ppointer *ptr = atomic;
+
+	p_mutex_lock (pp_atomic_mutex);
+	*ptr = val;
+	p_mutex_unlock (pp_atomic_mutex);
+}
+
+P_LIB_API pboolean
+p_atomic_pointer_compare_and_exchange (volatile void	*atomic,
+				       ppointer		oldval,
+				       ppointer		newval)
+{
+	volatile ppointer *ptr = atomic;
+	pboolean success;
+
+	p_mutex_lock (pp_atomic_mutex);
+
+	if ((success = (*ptr == oldval)))
+		*ptr = newval;
+
+	p_mutex_unlock (pp_atomic_mutex);
+
+	return success;
+}
+
+P_LIB_API pssize
+p_atomic_pointer_add (volatile void	*atomic,
+		      pssize		val)
+{
+	volatile pssize *ptr = atomic;
+	pssize oldval;
+
+	p_mutex_lock (pp_atomic_mutex);
+	oldval = *ptr;
+	*ptr = oldval + val;
+	p_mutex_unlock (pp_atomic_mutex);
+
+	return oldval;
+}
+
+P_LIB_API psize
+p_atomic_pointer_and (volatile void	*atomic,
+		      psize		val)
+{
+	volatile psize *ptr = atomic;
+	psize oldval;
+
+	p_mutex_lock (pp_atomic_mutex);
+	oldval = *ptr;
+	*ptr = oldval & val;
+	p_mutex_unlock (pp_atomic_mutex);
+
+	return oldval;
+}
+
+P_LIB_API psize
+p_atomic_pointer_or (volatile void	*atomic,
+		     psize		val)
+{
+	volatile psize *ptr = atomic;
+	psize oldval;
+
+	p_mutex_lock (pp_atomic_mutex);
+	oldval = *ptr;
+	*ptr = oldval | val;
+	p_mutex_unlock (pp_atomic_mutex);
+
+	return oldval;
+}
+
+P_LIB_API psize
+p_atomic_pointer_xor (volatile void	*atomic,
+		      psize		val)
+{
+	volatile psize *ptr = atomic;
+	psize oldval;
+
+	p_mutex_lock (pp_atomic_mutex);
+	oldval = *ptr;
+	*ptr = oldval ^ val;
+	p_mutex_unlock (pp_atomic_mutex);
+
+	return oldval;
+}
+
+P_LIB_API pboolean
+p_atomic_is_lock_free (void)
+{
+	return FALSE;
+}
+
+void
+p_atomic_thread_init (void)
+{
+	if (P_LIKELY (pp_atomic_mutex == NULL))
+		pp_atomic_mutex = p_mutex_new ();
+}
+
+void
+p_atomic_thread_shutdown (void)
+{
+	if (P_LIKELY (pp_atomic_mutex != NULL)) {
+		p_mutex_free (pp_atomic_mutex);
+		pp_atomic_mutex = NULL;
+	}
+}
diff --git a/3rdparty/plibsys/src/patomic-sync.c b/3rdparty/plibsys/src/patomic-sync.c
new file mode 100644
index 0000000..04a8d81
--- /dev/null
+++ b/3rdparty/plibsys/src/patomic-sync.c
@@ -0,0 +1,190 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "patomic.h"
+
+#ifdef P_CC_CRAY
+#  include <intrinsics.h>
+#endif
+
+P_LIB_API pint
+p_atomic_int_get (const volatile pint *atomic)
+{
+#ifdef P_CC_CRAY
+	__builtin_ia32_mfence ();
+#else
+	__sync_synchronize ();
+#endif
+	return *atomic;
+}
+
+P_LIB_API void
+p_atomic_int_set (volatile pint	*atomic,
+		  pint		val)
+{
+	*atomic = val;
+#ifdef P_CC_CRAY
+	__builtin_ia32_mfence ();
+#else
+	__sync_synchronize ();
+#endif
+}
+
+P_LIB_API void
+p_atomic_int_inc (volatile pint *atomic)
+{
+	(void) __sync_fetch_and_add (atomic, 1);
+}
+
+P_LIB_API pboolean
+p_atomic_int_dec_and_test (volatile pint *atomic)
+{
+	return __sync_fetch_and_sub (atomic, 1) == 1 ? TRUE : FALSE;
+}
+
+P_LIB_API pboolean
+p_atomic_int_compare_and_exchange (volatile pint	*atomic,
+				   pint			oldval,
+				   pint			newval)
+{
+#ifdef P_CC_CRAY
+	return __sync_val_compare_and_swap (atomic, oldval, newval) == oldval ? TRUE : FALSE;
+#else
+	return (pboolean) __sync_bool_compare_and_swap (atomic, oldval, newval);
+#endif
+}
+
+P_LIB_API pint
+p_atomic_int_add (volatile pint	*atomic,
+		  pint		val)
+{
+	return (pint) __sync_fetch_and_add (atomic, val);
+}
+
+P_LIB_API puint
+p_atomic_int_and (volatile puint	*atomic,
+		  puint			val)
+{
+	return (puint) __sync_fetch_and_and (atomic, val);
+}
+
+P_LIB_API puint
+p_atomic_int_or (volatile puint	*atomic,
+		 puint		val)
+{
+	return (puint) __sync_fetch_and_or (atomic, val);
+}
+
+P_LIB_API puint
+p_atomic_int_xor (volatile puint	*atomic,
+		  puint			val)
+{
+	return (puint) __sync_fetch_and_xor (atomic, val);
+}
+
+P_LIB_API ppointer
+p_atomic_pointer_get (const volatile void *atomic)
+{
+#ifdef P_CC_CRAY
+	__builtin_ia32_mfence ();
+#else
+	__sync_synchronize ();
+#endif
+	return (ppointer) *((const volatile psize *) atomic);
+}
+
+P_LIB_API void
+p_atomic_pointer_set (volatile void	*atomic,
+		      ppointer		val)
+{
+	volatile psize *cur_val = (volatile psize *) atomic;
+
+	*cur_val = (psize) val;
+#ifdef P_CC_CRAY
+	__builtin_ia32_mfence ();
+#else
+	__sync_synchronize ();
+#endif
+}
+
+P_LIB_API pboolean
+p_atomic_pointer_compare_and_exchange (volatile void	*atomic,
+				       ppointer		oldval,
+				       ppointer		newval)
+{
+#ifdef P_CC_CRAY
+	return __sync_val_compare_and_swap ((volatile psize *) atomic,
+					    (psize) oldval,
+					    (psize) newval) == ((psize) oldval) ? TRUE : FALSE;
+#else
+	return (pboolean) __sync_bool_compare_and_swap ((volatile psize *) atomic,
+							(psize) oldval,
+							(psize) newval);
+#endif
+}
+
+P_LIB_API pssize
+p_atomic_pointer_add (volatile void	*atomic,
+		      pssize		val)
+{
+	return (pssize) __sync_fetch_and_add ((volatile pssize *) atomic, val);
+}
+
+P_LIB_API psize
+p_atomic_pointer_and (volatile void	*atomic,
+		      psize		val)
+{
+	return (psize) __sync_fetch_and_and ((volatile psize *) atomic, val);
+}
+
+P_LIB_API psize
+p_atomic_pointer_or (volatile void	*atomic,
+		     psize		val)
+{
+	return (psize) __sync_fetch_and_or ((volatile psize *) atomic, val);
+}
+
+P_LIB_API psize
+p_atomic_pointer_xor (volatile void	*atomic,
+		      psize		val)
+{
+	return (psize) __sync_fetch_and_xor ((volatile psize *) atomic, val);
+}
+
+P_LIB_API pboolean
+p_atomic_is_lock_free (void)
+{
+	return TRUE;
+}
+
+void
+p_atomic_thread_init (void)
+{
+}
+
+void
+p_atomic_thread_shutdown (void)
+{
+}
diff --git a/3rdparty/plibsys/src/patomic-win.c b/3rdparty/plibsys/src/patomic-win.c
new file mode 100644
index 0000000..597151d
--- /dev/null
+++ b/3rdparty/plibsys/src/patomic-win.c
@@ -0,0 +1,272 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "patomic.h"
+
+/* Prepare MemoryBarrier() */
+#if defined (P_CC_WATCOM) || defined (P_CC_BORLAND)
+#  if defined (_M_X64) || defined (_M_AMD64)
+#    define MemoryBarrier __faststorefence
+#  elseif defined (_M_IA64)
+#    define MemoryBarrier __mf
+#  else
+#    ifdef P_CC_WATCOM
+inline
+#    else
+FORCEINLINE
+#    endif /* P_CC_WATCOM */
+VOID MemoryBarrier (VOID)
+{
+	LONG Barrier = 0;
+	(void) (Barrier);
+	__asm {
+		xchg Barrier, eax
+	}
+}
+#  endif /* _M_X64 || _M_AMD64 */
+#endif /* P_CC_WATCOM || P_CC_BORLAND */
+
+#if !defined (P_OS_WIN64) && (defined (P_CC_MSVC) && _MSC_VER > 1200)
+   /* Tell compiler about intrinsics to suppress warnings,
+    * see: https://msdn.microsoft.com/en-us/library/hh977023.aspx */
+#  include <intrin.h>
+#  define InterlockedAnd _InterlockedAnd
+#  define InterlockedOr _InterlockedOr
+#  define InterlockedXor _InterlockedXor
+#  pragma intrinsic(_InterlockedAnd)
+#  pragma intrinsic(_InterlockedOr)
+#  pragma intrinsic(_InterlockedXor)
+#endif
+
+#if (defined (P_CC_MSVC) && _MSC_VER <= 1200) || defined (P_CC_WATCOM) \
+					      || defined (P_CC_BORLAND)
+/* Inlined versions for older compilers */
+static LONG
+ppInterlockedAnd (LONG volatile	*atomic,
+		  LONG		val)
+{
+	LONG i, j;
+
+	j = *atomic;
+	do {
+		i = j;
+		j = InterlockedCompareExchange (atomic, i & val, i);
+	} while (i != j);
+
+	return j;
+}
+
+#  define InterlockedAnd(a,b) ppInterlockedAnd(a,b)
+
+static LONG
+ppInterlockedOr (LONG volatile 	*atomic,
+		 LONG		val)
+{
+	LONG i, j;
+
+	j = *atomic;
+	do {
+		i = j;
+		j = InterlockedCompareExchange (atomic, i | val, i);
+	} while (i != j);
+
+	return j;
+}
+
+#  define InterlockedOr(a,b) ppInterlockedOr(a,b)
+
+static LONG
+ppInterlockedXor (LONG volatile	*atomic,
+		  LONG		val)
+{
+	LONG i, j;
+
+	j = *atomic;
+	do {
+		i = j;
+		j = InterlockedCompareExchange (atomic, i ^ val, i);
+	} while (i != j);
+
+	return j;
+}
+
+#  define InterlockedXor(a,b) ppInterlockedXor(a,b)
+#endif
+
+/* http://msdn.microsoft.com/en-us/library/ms684122(v=vs.85).aspx */
+
+P_LIB_API pint
+p_atomic_int_get (const volatile pint *atomic)
+{
+	MemoryBarrier ();
+	return *atomic;
+}
+
+P_LIB_API void
+p_atomic_int_set (volatile pint	*atomic,
+		  pint		val)
+{
+	*atomic = val;
+	MemoryBarrier ();
+}
+
+P_LIB_API void
+p_atomic_int_inc (volatile pint *atomic)
+{
+	InterlockedIncrement ((LONG volatile *) atomic);
+}
+
+P_LIB_API pboolean
+p_atomic_int_dec_and_test (volatile pint *atomic)
+{
+	return InterlockedDecrement ((LONG volatile *) atomic) == 0 ? TRUE : FALSE;
+}
+
+P_LIB_API pboolean
+p_atomic_int_compare_and_exchange (volatile pint	*atomic,
+				   pint			oldval,
+				   pint			newval)
+{
+	return InterlockedCompareExchange ((LONG volatile *) atomic,
+					   (LONG) newval,
+					   (LONG) oldval) == oldval;
+}
+
+P_LIB_API pint
+p_atomic_int_add (volatile pint	*atomic,
+		  pint		val)
+{
+	return (pint) InterlockedExchangeAdd ((LONG volatile *) atomic, (LONG) val);
+}
+
+P_LIB_API puint
+p_atomic_int_and (volatile puint	*atomic,
+		  puint			val)
+{
+	return (puint) InterlockedAnd ((LONG volatile *) atomic, (LONG) val);
+}
+
+P_LIB_API puint
+p_atomic_int_or (volatile puint	*atomic,
+		 puint		val)
+{
+	return (puint) InterlockedOr ((LONG volatile *) atomic, (LONG) val);
+}
+
+P_LIB_API puint
+p_atomic_int_xor (volatile puint	*atomic,
+		  puint			val)
+{
+	return (puint) InterlockedXor ((LONG volatile *) atomic, (LONG) val);
+}
+
+P_LIB_API ppointer
+p_atomic_pointer_get (const volatile void *atomic)
+{
+	const volatile ppointer *ptr = (const volatile ppointer *) atomic;
+
+	MemoryBarrier ();
+	return *ptr;
+}
+
+P_LIB_API void
+p_atomic_pointer_set (volatile void	*atomic,
+		      ppointer		val)
+{
+	volatile ppointer *ptr = (volatile ppointer *) atomic;
+
+	*ptr = val;
+	MemoryBarrier ();
+}
+
+P_LIB_API pboolean
+p_atomic_pointer_compare_and_exchange (volatile void	*atomic,
+				       ppointer		oldval,
+				       ppointer		newval)
+{
+	return InterlockedCompareExchangePointer ((volatile PVOID *) atomic,
+						  (PVOID) newval,
+						  (PVOID) oldval) == oldval ? TRUE : FALSE;
+}
+
+P_LIB_API pssize
+p_atomic_pointer_add (volatile void	*atomic,
+		      pssize		val)
+{
+#if PLIBSYS_SIZEOF_VOID_P == 8
+	return (pssize) InterlockedExchangeAdd64 ((LONGLONG volatile *) atomic, (LONGLONG) val);
+#else
+	return (pssize) InterlockedExchangeAdd ((LONG volatile *) atomic, (LONG) val);
+#endif
+}
+
+P_LIB_API psize
+p_atomic_pointer_and (volatile void	*atomic,
+		      psize		val)
+{
+#if PLIBSYS_SIZEOF_VOID_P == 8
+	return (psize) InterlockedAnd64 ((LONGLONG volatile *) atomic, (LONGLONG) val);
+#else
+	return (psize) InterlockedAnd ((LONG volatile *) atomic, (LONG) val);
+#endif
+}
+
+P_LIB_API psize
+p_atomic_pointer_or (volatile void	*atomic,
+		     psize		val)
+{
+#if PLIBSYS_SIZEOF_VOID_P == 8
+	return (psize) InterlockedOr64 ((LONGLONG volatile *) atomic, (LONGLONG) val);
+#else
+	return (psize) InterlockedOr ((LONG volatile *) atomic, (LONG) val);
+#endif
+}
+
+P_LIB_API psize
+p_atomic_pointer_xor (volatile void	*atomic,
+		      psize		val)
+{
+#if PLIBSYS_SIZEOF_VOID_P == 8
+	return (psize) InterlockedXor64 ((LONGLONG volatile *) atomic, (LONGLONG) val);
+#else
+	return (psize) InterlockedXor ((LONG volatile *) atomic, (LONG) val);
+#endif
+}
+
+P_LIB_API pboolean
+p_atomic_is_lock_free (void)
+{
+	return TRUE;
+}
+
+void
+p_atomic_thread_init (void)
+{
+}
+
+void
+p_atomic_thread_shutdown (void)
+{
+}
diff --git a/3rdparty/plibsys/src/patomic.h b/3rdparty/plibsys/src/patomic.h
new file mode 100644
index 0000000..fc451fe
--- /dev/null
+++ b/3rdparty/plibsys/src/patomic.h
@@ -0,0 +1,310 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file patomic.h
+ * @brief Atomic operations
+ * @author Alexander Saprykin
+ *
+ * Atomic operations can be used to avoid heavy thread synchronization
+ * primitives such as mutexes, semaphores and so on. These operations are
+ * performed atomically and can't be preempted by another thread.
+ *
+ * Lock-free atomic operations require software and hardware support. Usually
+ * lock-free atomic operations are implemented with low-level using assembly
+ * inlines. Some of the compilers provide built-in routines to perform atomic
+ * operations. You can use the p_atomic_is_lock_free() call to check whether
+ * such a support is provided or not.
+ *
+ * If there is no hardware or software support for lock-free atomic operations
+ * then they can be simulated (though in rather slower manner) using a thread
+ * global synchronization primitive (i.e. mutex), but it could block threads
+ * while performing atomic operations on distinct variables from distinct
+ * threads.
+ *
+ * The Windows platform provides all the required lock-free operations in most
+ * cases, so it always has lock-free support.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PATOMIC_H
+#define PLIBSYS_HEADER_PATOMIC_H
+
+#include <ptypes.h>
+#include <pmacros.h>
+
+P_BEGIN_DECLS
+
+/**
+ * @brief Gets #pint value from @a atomic.
+ * @param atomic Pointer to #pint to get the value from.
+ * @return Integer value.
+ * @since 0.0.1
+ *
+ * This call acts as a full compiler and hardware memory barrier (before the
+ * get).
+ */
+P_LIB_API pint		p_atomic_int_get			(const volatile pint	*atomic);
+
+/**
+ * @brief Sets #pint value to @a atomic.
+ * @param[out] atomic Pointer to #pint to set the value for.
+ * @param val New #pint value.
+ * @since 0.0.1
+ *
+ * This call acts as a full compiler and hardware memory barrier (after the
+ * set).
+ */
+P_LIB_API void		p_atomic_int_set			(volatile pint		*atomic,
+								 pint			val);
+
+/**
+ * @brief Increments #pint value from @a atomic by 1.
+ * @param[in,out] atomic Pointer to #pint to increment the value.
+ * @since 0.0.1
+ *
+ * Think of this operation as an atomic version of `{ *atomic += 1; }`.
+ *
+ * This call acts as a full compiler and hardware memory barrier.
+ */
+P_LIB_API void		p_atomic_int_inc			(volatile pint		*atomic);
+
+/**
+ * @brief Decrements #pint value from @a atomic by 1 and tests the result for
+ * zero.
+ * @param[in,out] atomic Pointer to #pint to decrement the value.
+ * @return TRUE if the new value is equal to zero, FALSE otherwise.
+ * @since 0.0.1
+ *
+ * Think of this operation as an atomic version of
+ * `{ *atomic -= 1; return (*atomic == 0); }`.
+ *
+ * This call acts as a full compiler and hardware memory barrier.
+ */
+P_LIB_API pboolean	p_atomic_int_dec_and_test		(volatile pint		*atomic);
+
+/**
+ * @brief Compares @a oldval with the value pointed to by @a atomic and if
+ * they are equal, atomically exchanges the value of @a atomic with @a newval.
+ * @param[in,out] atomic Pointer to #pint.
+ * @param oldval Old #pint value.
+ * @param newval New #pint value.
+ * @return TRUE if @a atomic value was equal @a oldval, FALSE otherwise.
+ * @since 0.0.1
+ *
+ * This compare and exchange is done atomically.
+ *
+ * Think of this operation as an atomic version of
+ * `{ if (*atomic == oldval) { *atomic = newval; return TRUE; } else return FALSE; }`.
+ *
+ * This call acts as a full compiler and hardware memory barrier.
+ */
+P_LIB_API pboolean	p_atomic_int_compare_and_exchange	(volatile pint		*atomic,
+								 pint			oldval,
+								 pint			newval);
+
+/**
+ * @brief Atomically adds #pint value to @a atomic value.
+ * @param[in,out] atomic Pointer to #pint.
+ * @param val Integer to add to @a atomic value.
+ * @return Old value before the addition.
+ * @since 0.0.1
+ *
+ * Think of this operation as an atomic version of
+ * `{ tmp = *atomic; *atomic += val; return tmp; }`.
+ *
+ * This call acts as a full compiler and hardware memory barrier.
+ */
+P_LIB_API pint		p_atomic_int_add			(volatile pint		*atomic,
+								 pint			val);
+
+/**
+ * @brief Atomically performs the bitwise 'and' operation of @a atomic value
+ * and @a val storing the result back in @a atomic.
+ * @param[in,out] atomic Pointer to #puint.
+ * @param val #puint to perform bitwise 'and' with @a atomic value.
+ * @return Old @a atomic value before the operation.
+ * @since 0.0.1
+ *
+ * This call acts as a full compiler and hardware memory barrier.
+ *
+ * Think of this operation as an atomic version of
+ * `{ tmp = *atomic; *atomic &= val; return tmp; }`.
+ */
+P_LIB_API puint		p_atomic_int_and			(volatile puint		*atomic,
+								 puint			val);
+
+/**
+ * @brief Atomically performs the bitwise 'or' operation of @a atomic value
+ * and @a val storing the result back in @a atomic.
+ * @param[in,out] atomic Pointer to #puint.
+ * @param val #puint to perform bitwise 'or' with @a atomic value.
+ * @return Old @a atomic value before the operation.
+ * @since 0.0.1
+ *
+ * Think of this operation as an atomic version of
+ * `{ tmp = *atomic; *atomic |= val; return tmp; }`.
+ *
+ * This call acts as a full compiler and hardware memory barrier.
+ */
+P_LIB_API puint		p_atomic_int_or				(volatile puint		*atomic,
+								 puint			val);
+
+/**
+ * @brief Atomically performs the bitwise 'xor' operation of @a atomic value
+ * and @a val storing the result back in @a atomic.
+ * @param[in,out] atomic Pointer to #puint.
+ * @param val #puint to perform bitwise 'xor' with @a atomic value.
+ * @return Old @a atomic value before the operation.
+ * @since 0.0.1
+ *
+ * Think of this operation as an atomic version of
+ * `{ tmp = *atomic; *atomic ^= val; return tmp; }`.
+ *
+ * This call acts as a full compiler and hardware memory barrier.
+ */
+P_LIB_API puint		p_atomic_int_xor			(volatile puint		*atomic,
+								 puint			val);
+
+/**
+ * @brief Gets #ppointer-sized value from @a atomic.
+ * @param atomic Pointer to get the value from.
+ * @return Value from the pointer.
+ * @since 0.0.1
+ *
+ * This call acts as a full compiler and hardware memory barrier (before the get).
+ */
+P_LIB_API ppointer	p_atomic_pointer_get			(const volatile void	*atomic);
+
+/**
+ * @brief Sets @a val to #ppointer-sized @a atomic.
+ * @param[out] atomic Pointer to set the value for.
+ * @param val New value for @a atomic.
+ * @since 0.0.1
+ *
+ * This call acts as a full compiler and hardware memory barrier (after the set).
+ */
+P_LIB_API void		p_atomic_pointer_set			(volatile void		*atomic,
+								 ppointer		val);
+
+/**
+ * @brief Compares @a oldval with the value pointed to by @a atomic and if
+ * they are equal, atomically exchanges the value of @a atomic with @a newval.
+ * @param[in,out] atomic Pointer to #ppointer-sized value.
+ * @param oldval Old #ppointer-sized value.
+ * @param newval New #ppointer-sized value.
+ * @return TRUE if @a atomic value was equal @a oldval, FALSE otherwise.
+ * @since 0.0.1
+ *
+ * This compare and exchange is done atomically.
+ *
+ * Think of this operation as an atomic version of
+ * `{ if (*atomic == oldval) { *atomic = newval; return TRUE; } else return FALSE; }`.
+ *
+ * This call acts as a full compiler and hardware memory barrier.
+ */
+P_LIB_API pboolean	p_atomic_pointer_compare_and_exchange	(volatile void		*atomic,
+								 ppointer 		oldval,
+								 ppointer		newval);
+
+/**
+ * @brief Atomically adds #ppointer-sized value to @a atomic value.
+ * @param[in,out] atomic Pointer to #ppointer-sized value.
+ * @param val Value to add to @a atomic value.
+ * @return Old value before the addition.
+ * @since 0.0.1
+ *
+ * Think of this operation as an atomic version of
+ * `{ tmp = *atomic; *atomic += val; return tmp; }`.
+ *
+ * This call acts as a full compiler and hardware memory barrier.
+ */
+P_LIB_API pssize	p_atomic_pointer_add			(volatile void		*atomic,
+								 pssize			val);
+
+/**
+ * @brief Atomically performs the bitwise 'and' operation of #ppointer-sized
+ * @a atomic value and @a val storing the result back in @a atomic.
+ * @param[in,out] atomic Pointer to #ppointer-size value.
+ * @param val #psize to perform bitwise 'and' with @a atomic value.
+ * @return Old @a atomic value before the operation.
+ * @since 0.0.1
+ *
+ * Think of this operation as an atomic version of
+ * `{ tmp = *atomic; *atomic &= val; return tmp; }`.
+ *
+ * This call acts as a full compiler and hardware memory barrier.
+ */
+P_LIB_API psize		p_atomic_pointer_and			(volatile void		*atomic,
+								 psize			val);
+
+/**
+ * @brief Atomically performs the bitwise 'or' operation of #ppointer-sized
+ * @a atomic value and @a val storing the result back in @a atomic.
+ * @param[in,out] atomic Pointer to #ppointer-size value.
+ * @param val #psize to perform bitwise 'or' with @a atomic value.
+ * @return Old @a atomic value before the operation.
+ * @since 0.0.1
+ *
+ * Think of this operation as an atomic version of
+ * `{ tmp = *atomic; *atomic |= val; return tmp; }`.
+ *
+ * This call acts as a full compiler and hardware memory barrier.
+ */
+P_LIB_API psize		p_atomic_pointer_or			(volatile void		*atomic,
+								 psize			val);
+
+/**
+ * @brief Atomically performs the bitwise 'xor' operation of #ppointer-sized
+ * @a atomic value and @a val storing the result back in @a atomic.
+ * @param[in,out] atomic Pointer to #ppointer-size value.
+ * @param val #psize to perform bitwise 'xor' with @a atomic value.
+ * @return Old @a atomic value before the operation.
+ * @since 0.0.1
+ *
+ * Think of this operation as an atomic version of
+ * `{ tmp = *atomic; *atomic ^= val; return tmp; }`.
+ *
+ * This call acts as a full compiler and hardware memory barrier.
+ */
+P_LIB_API psize		p_atomic_pointer_xor			(volatile void		*atomic,
+								 psize			val);
+
+/**
+ * @brief Checks whether atomic operations are lock-free.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ *
+ * Some underlying atomic model implementations may not support lock-free
+ * operations depending on hardware or software.
+ */
+P_LIB_API pboolean	p_atomic_is_lock_free			(void);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PATOMIC_H */
diff --git a/3rdparty/plibsys/src/pcondvariable-amiga.c b/3rdparty/plibsys/src/pcondvariable-amiga.c
new file mode 100644
index 0000000..1c91522
--- /dev/null
+++ b/3rdparty/plibsys/src/pcondvariable-amiga.c
@@ -0,0 +1,230 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pcondvariable.h"
+#include "patomic.h"
+#include "pmem.h"
+#include "pspinlock.h"
+
+#include <stdlib.h>
+
+#include <proto/exec.h>
+
+typedef struct _PCondThread {
+	struct Task		*thread;
+	struct _PCondThread	*next;
+	ULONG			sigmask;
+} PCondThread;
+
+struct PCondVariable_ {
+	PSpinLock 	*lock;
+	PCondThread	*wait_head;
+	pint		wait_count;
+};
+
+P_LIB_API PCondVariable *
+p_cond_variable_new (void)
+{
+	PCondVariable *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PCondVariable))) == NULL)) {
+		P_ERROR ("PCondVariable::p_cond_variable_new: failed to allocate memory");
+		return NULL;
+	}
+
+	if ((ret->lock = p_spinlock_new ()) == NULL) {
+		P_ERROR ("PCondVariable::p_cond_variable_new: failed to initialize");
+		p_free (ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+P_LIB_API void
+p_cond_variable_free (PCondVariable *cond)
+{
+	if (P_UNLIKELY (cond == NULL))
+		return;
+
+	if ((cond->wait_count > 0) || (cond->wait_head != NULL))
+		P_WARNING ("PCondVariable::p_cond_variable_free: destroying while threads are waiting");
+
+	p_spinlock_free (cond->lock);
+	p_free (cond);
+}
+
+P_LIB_API pboolean
+p_cond_variable_wait (PCondVariable	*cond,
+		      PMutex		*mutex)
+{
+	PCondThread	*wait_thread;
+	BYTE		signal;
+	ULONG		wait_singnals;
+
+	if (P_UNLIKELY (cond == NULL || mutex == NULL))
+		return FALSE;
+
+	if ((wait_thread = p_malloc0 (sizeof (PCondThread))) == NULL) {
+		P_ERROR ("PCondVariable::p_cond_variable_wait: failed to allocate memory");
+		return FALSE;
+	}
+
+	wait_thread->thread = IExec->FindTask (NULL);
+	wait_thread->next   = NULL;
+	
+	signal = IExec->AllocSignal (-1);
+	
+	if (signal == -1) {
+		P_WARNING ("PCondVariable::p_cond_variable_wait: no free signal slot left");
+		return FALSE;
+	}
+
+	wait_thread->sigmask = 1 << signal;
+
+	if (p_spinlock_lock (cond->lock) != TRUE) {
+		P_ERROR ("PCondVariable::p_cond_variable_wait: failed to lock internal spinlock");
+		return FALSE;
+	}
+
+	if (cond->wait_head != NULL)
+		cond->wait_head->next = wait_thread;
+	else
+		cond->wait_head = wait_thread;
+
+	p_atomic_int_inc ((volatile pint *) &cond->wait_count);
+	
+	if (p_spinlock_unlock (cond->lock) != TRUE) {
+		P_ERROR ("PCondVariable::p_cond_variable_wait: failed to unlock internal spinlock");
+		return FALSE;
+	}
+
+	if (p_mutex_unlock (mutex) != TRUE) {
+		P_ERROR ("PCondVariable::p_cond_variable_wait: failed to unlock mutex");
+		return FALSE;
+	}
+
+	wait_singnals = IExec->Wait (wait_thread->sigmask);
+
+	if (p_mutex_lock (mutex) != TRUE) {
+		P_ERROR ("PCondVariable::p_cond_variable_wait: failed to lock mutex");
+		return FALSE;
+	}
+
+	IExec->FreeSignal (signal);
+
+	return TRUE;
+}
+
+P_LIB_API pboolean
+p_cond_variable_signal (PCondVariable *cond)
+{
+	PCondThread	*wait_thread;
+
+	if (P_UNLIKELY (cond == NULL))
+		return FALSE;
+
+	if (p_spinlock_lock (cond->lock) != TRUE) {
+		P_ERROR ("PCondVariable::p_cond_variable_signal: failed to lock internal spinlock");
+		return FALSE;
+	}
+
+	if (cond->wait_head == NULL) {
+		if (p_spinlock_unlock (cond->lock) != TRUE) {
+			P_ERROR ("PCondVariable::p_cond_variable_signal(1): failed to unlock internal spinlock");
+			return FALSE;
+		} else
+			return TRUE;
+	}
+
+	wait_thread = cond->wait_head;
+	cond->wait_head = wait_thread->next;
+
+	p_atomic_int_add ((volatile pint *) &cond->wait_count, -1);
+
+	if (p_spinlock_unlock (cond->lock) != TRUE) {
+		P_ERROR ("PCondVariable::p_cond_variable_signal(2): failed to unlock internal spinlock");
+		return FALSE;
+	}
+
+	IExec->Signal (wait_thread->thread, wait_thread->sigmask);
+
+	p_free (wait_thread);
+	return TRUE;
+}
+
+P_LIB_API pboolean
+p_cond_variable_broadcast (PCondVariable *cond)
+{
+	if (P_UNLIKELY (cond == NULL))
+		return FALSE;
+
+	PCondThread	*cur_thread;
+	PCondThread	*next_thread;
+
+	if (p_spinlock_lock (cond->lock) != TRUE) {
+		P_ERROR ("PCondVariable::p_cond_variable_broadcast: failed to lock internal spinlock");
+		return FALSE;
+	}
+
+	if (cond->wait_head == NULL) {
+		if (p_spinlock_unlock (cond->lock) != TRUE) {
+			P_ERROR ("PCondVariable::p_cond_variable_broadcast(1): failed to unlock internal spinlock");
+			return FALSE;
+		} else
+			return TRUE;
+	}
+
+	cur_thread = cond->wait_head;
+
+	do {
+		IExec->Signal (cur_thread->thread, cur_thread->sigmask);
+
+		next_thread = cur_thread->next;
+		p_free (cur_thread);
+
+		cur_thread = next_thread;
+	} while (cur_thread != NULL);
+
+	cond->wait_head  = NULL;
+	cond->wait_count = 0;
+
+	if (p_spinlock_unlock (cond->lock) != TRUE) {
+		P_ERROR ("PCondVariable::p_cond_variable_broadcast(2): failed to unlock internal spinlock");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+void
+p_cond_variable_init (void)
+{
+}
+
+void
+p_cond_variable_shutdown (void)
+{
+}
diff --git a/3rdparty/plibsys/src/pcondvariable-atheos.c b/3rdparty/plibsys/src/pcondvariable-atheos.c
new file mode 100644
index 0000000..f68e98b
--- /dev/null
+++ b/3rdparty/plibsys/src/pcondvariable-atheos.c
@@ -0,0 +1,234 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pcondvariable.h"
+#include "pspinlock.h"
+#include "patomic.h"
+#include "pmem.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <atheos/semaphore.h>
+#include <atheos/threads.h>
+
+typedef struct _PCondThread {
+	thread_id		thread;
+	struct _PCondThread	*next;
+} PCondThread;
+
+struct PCondVariable_ {
+	PSpinLock 	*lock;
+	PCondThread	*wait_head;
+	pint		wait_count;
+};
+
+P_LIB_API PCondVariable *
+p_cond_variable_new (void)
+{
+	PCondVariable *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PCondVariable))) == NULL)) {
+		P_ERROR ("PCondVariable::p_cond_variable_new: failed to allocate memory");
+		return NULL;
+	}
+
+	if ((ret->lock = p_spinlock_new ()) == NULL) {
+		P_ERROR ("PCondVariable::p_cond_variable_new: failed to initialize");
+		p_free (ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+P_LIB_API void
+p_cond_variable_free (PCondVariable *cond)
+{
+	if (P_UNLIKELY (cond == NULL))
+		return;
+
+	if ((cond->wait_count > 0) || (cond->wait_head != NULL))
+		P_WARNING ("PCondVariable::p_cond_variable_free: destroying while threads are waiting");
+
+	p_spinlock_free (cond->lock);
+	p_free (cond);
+}
+
+P_LIB_API pboolean
+p_cond_variable_wait (PCondVariable	*cond,
+		      PMutex		*mutex)
+{
+	PCondThread *wait_thread;
+
+	if (P_UNLIKELY (cond == NULL || mutex == NULL))
+		return FALSE;
+
+	if ((wait_thread = p_malloc0 (sizeof (PCondThread))) == NULL) {
+		P_ERROR ("PCondVariable::p_cond_variable_wait: failed to allocate memory");
+		return FALSE;
+	}
+
+	wait_thread->thread = get_thread_id (NULL);
+	wait_thread->next   = NULL;
+	
+	if (p_spinlock_lock (cond->lock) != TRUE) {
+		P_ERROR ("PCondVariable::p_cond_variable_wait: failed to lock internal spinlock");
+		return FALSE;
+	}
+
+	if (cond->wait_head != NULL)
+		cond->wait_head->next = wait_thread;
+	else
+		cond->wait_head = wait_thread;
+
+	p_atomic_int_inc ((volatile pint *) &cond->wait_count);
+	
+	if (p_spinlock_unlock (cond->lock) != TRUE) {
+		P_ERROR ("PCondVariable::p_cond_variable_wait: failed to unlock internal spinlock");
+		return FALSE;
+	}
+
+	if (p_mutex_unlock (mutex) != TRUE) {
+		P_ERROR ("PCondVariable::p_cond_variable_wait: failed to unlock mutex");
+		return FALSE;
+	}
+
+	suspend_thread (wait_thread->thread);
+
+	if (p_mutex_lock (mutex) != TRUE) {
+		P_ERROR ("PCondVariable::p_cond_variable_wait: failed to lock mutex");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+P_LIB_API pboolean
+p_cond_variable_signal (PCondVariable *cond)
+{
+	PCondThread	*wait_thread;
+	thread_info	thr_info;
+
+	if (P_UNLIKELY (cond == NULL))
+		return FALSE;
+
+	if (p_spinlock_lock (cond->lock) != TRUE) {
+		P_ERROR ("PCondVariable::p_cond_variable_signal: failed to lock internal spinlock");
+		return FALSE;
+	}
+
+	if (cond->wait_head == NULL) {
+		if (p_spinlock_unlock (cond->lock) != TRUE) {
+			P_ERROR ("PCondVariable::p_cond_variable_signal(1): failed to unlock internal spinlock");
+			return FALSE;
+		} else
+			return TRUE;
+	}
+
+	wait_thread = cond->wait_head;
+	cond->wait_head = wait_thread->next;
+
+	p_atomic_int_add ((volatile pint *) &cond->wait_count, -1);
+
+	if (p_spinlock_unlock (cond->lock) != TRUE) {
+		P_ERROR ("PCondVariable::p_cond_variable_signal(2): failed to unlock internal spinlock");
+		return FALSE;
+	}
+
+	memset (&thr_info, 0, sizeof (thr_info));
+
+	while (get_thread_info (wait_thread->thread, &thr_info) == 0) {
+		if (thr_info.ti_state != TS_READY)
+			break;
+	}
+
+	resume_thread (wait_thread->thread);
+
+	p_free (wait_thread);
+	return TRUE;
+}
+
+P_LIB_API pboolean
+p_cond_variable_broadcast (PCondVariable *cond)
+{
+	if (P_UNLIKELY (cond == NULL))
+		return FALSE;
+
+	PCondThread	*cur_thread;
+	PCondThread	*next_thread;
+	thread_info	thr_info;
+
+	if (p_spinlock_lock (cond->lock) != TRUE) {
+		P_ERROR ("PCondVariable::p_cond_variable_broadcast: failed to lock internal spinlock");
+		return FALSE;
+	}
+
+	if (cond->wait_head == NULL) {
+		if (p_spinlock_unlock (cond->lock) != TRUE) {
+			P_ERROR ("PCondVariable::p_cond_variable_broadcast(1): failed to unlock internal spinlock");
+			return FALSE;
+		} else
+			return TRUE;
+	}
+
+	cur_thread = cond->wait_head;
+
+	do {
+		memset (&thr_info, 0, sizeof (thr_info)); 
+
+		while (get_thread_info (cur_thread->thread, &thr_info) == 0) {
+			if (thr_info.ti_state != TS_READY)
+				break;
+		}
+
+		resume_thread (cur_thread->thread);
+
+		next_thread = cur_thread->next;
+		p_free (cur_thread);
+
+		cur_thread = next_thread;
+	} while (cur_thread != NULL);
+
+	cond->wait_head  = NULL;
+	cond->wait_count = 0;
+
+	if (p_spinlock_unlock (cond->lock) != TRUE) {
+		P_ERROR ("PCondVariable::p_cond_variable_broadcast(2): failed to unlock internal spinlock");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+void
+p_cond_variable_init (void)
+{
+}
+
+void
+p_cond_variable_shutdown (void)
+{
+}
diff --git a/3rdparty/plibsys/src/pcondvariable-beos.c b/3rdparty/plibsys/src/pcondvariable-beos.c
new file mode 100644
index 0000000..78199d1
--- /dev/null
+++ b/3rdparty/plibsys/src/pcondvariable-beos.c
@@ -0,0 +1,233 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pcondvariable.h"
+#include "pspinlock.h"
+#include "patomic.h"
+#include "pmem.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <kernel/OS.h>
+
+typedef struct _PCondThread {
+	thread_id		thread;
+	struct _PCondThread	*next;
+} PCondThread;
+
+struct PCondVariable_ {
+	PSpinLock 	*lock;
+	PCondThread	*wait_head;
+	pint		wait_count;
+};
+
+P_LIB_API PCondVariable *
+p_cond_variable_new (void)
+{
+	PCondVariable *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PCondVariable))) == NULL)) {
+		P_ERROR ("PCondVariable::p_cond_variable_new: failed to allocate memory");
+		return NULL;
+	}
+
+	if ((ret->lock = p_spinlock_new ()) == NULL) {
+		P_ERROR ("PCondVariable::p_cond_variable_new: failed to initialize");
+		p_free (ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+P_LIB_API void
+p_cond_variable_free (PCondVariable *cond)
+{
+	if (P_UNLIKELY (cond == NULL))
+		return;
+
+	if ((cond->wait_count > 0) || (cond->wait_head != NULL))
+		P_WARNING ("PCondVariable::p_cond_variable_free: destroying while threads are waiting");
+
+	p_spinlock_free (cond->lock);
+	p_free (cond);
+}
+
+P_LIB_API pboolean
+p_cond_variable_wait (PCondVariable	*cond,
+		      PMutex		*mutex)
+{
+	PCondThread *wait_thread;
+
+	if (P_UNLIKELY (cond == NULL || mutex == NULL))
+		return FALSE;
+
+	if ((wait_thread = p_malloc0 (sizeof (PCondThread))) == NULL) {
+		P_ERROR ("PCondVariable::p_cond_variable_wait: failed to allocate memory");
+		return FALSE;
+	}
+
+	wait_thread->thread = find_thread (NULL);
+	wait_thread->next   = NULL;
+	
+	if (p_spinlock_lock (cond->lock) != TRUE) {
+		P_ERROR ("PCondVariable::p_cond_variable_wait: failed to lock internal spinlock");
+		return FALSE;
+	}
+	
+	if (cond->wait_head != NULL)
+		cond->wait_head->next = wait_thread;
+	else
+		cond->wait_head = wait_thread;
+
+	p_atomic_int_inc ((volatile pint *) &cond->wait_count);
+	
+	if (p_spinlock_unlock (cond->lock) != TRUE) {
+		P_ERROR ("PCondVariable::p_cond_variable_wait: failed to unlock internal spinlock");
+		return FALSE;
+	}
+
+	if (p_mutex_unlock (mutex) != TRUE) {
+		P_ERROR ("PCondVariable::p_cond_variable_wait: failed to unlock mutex");
+		return FALSE;
+	}
+
+	suspend_thread (wait_thread->thread);
+
+	if (p_mutex_lock (mutex) != TRUE) {
+		P_ERROR ("PCondVariable::p_cond_variable_wait: failed to lock mutex");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+P_LIB_API pboolean
+p_cond_variable_signal (PCondVariable *cond)
+{
+	PCondThread	*wait_thread;
+	thread_info	thr_info;
+
+	if (P_UNLIKELY (cond == NULL))
+		return FALSE;
+
+	if (p_spinlock_lock (cond->lock) != TRUE) {
+		P_ERROR ("PCondVariable::p_cond_variable_signal: failed to lock internal spinlock");
+		return FALSE;
+	}
+
+	if (cond->wait_head == NULL) {
+		if (p_spinlock_unlock (cond->lock) != TRUE) {
+			P_ERROR ("PCondVariable::p_cond_variable_signal(1): failed to unlock internal spinlock");
+			return FALSE;
+		} else
+			return TRUE;
+	}
+
+	wait_thread = cond->wait_head;
+	cond->wait_head = wait_thread->next;
+
+	p_atomic_int_add ((volatile pint *) &cond->wait_count, -1);
+
+	if (p_spinlock_unlock (cond->lock) != TRUE) {
+		P_ERROR ("PCondVariable::p_cond_variable_signal(2): failed to unlock internal spinlock");
+		return FALSE;
+	}
+
+	memset (&thr_info, 0, sizeof (thr_info));
+	
+	while (get_thread_info (wait_thread->thread, &thr_info) == B_OK) {
+		if (thr_info.state != B_THREAD_READY)
+			break;
+	}
+
+	resume_thread (wait_thread->thread);
+
+	p_free (wait_thread);
+	return TRUE;
+}
+
+P_LIB_API pboolean
+p_cond_variable_broadcast (PCondVariable *cond)
+{
+	PCondThread	*cur_thread;
+	PCondThread	*next_thread;
+	thread_info	thr_info;
+	
+	if (P_UNLIKELY (cond == NULL))
+		return FALSE;
+
+	if (p_spinlock_lock (cond->lock) != TRUE) {
+		P_ERROR ("PCondVariable::p_cond_variable_broadcast: failed to lock internal spinlock");
+		return FALSE;
+	}
+
+	if (cond->wait_head == NULL) {
+		if (p_spinlock_unlock (cond->lock) != TRUE) {
+			P_ERROR ("PCondVariable::p_cond_variable_broadcast(1): failed to unlock internal spinlock");
+			return FALSE;
+		} else
+			return TRUE;
+	}
+
+	cur_thread = cond->wait_head;
+
+	do {
+		memset (&thr_info, 0, sizeof (thr_info)); 
+
+		while (get_thread_info (cur_thread->thread, &thr_info) == B_OK) {
+			if (thr_info.state != B_THREAD_READY)
+				break;
+		}
+
+		resume_thread (cur_thread->thread);
+
+		next_thread = cur_thread->next;
+		p_free (cur_thread);
+
+		cur_thread = next_thread;
+	} while (cur_thread != NULL);
+
+	cond->wait_head  = NULL;
+	cond->wait_count = 0;
+
+	if (p_spinlock_unlock (cond->lock) != TRUE) {
+		P_ERROR ("PCondVariable::p_cond_variable_broadcast(2): failed to unlock internal spinlock");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+void
+p_cond_variable_init (void)
+{
+}
+
+void
+p_cond_variable_shutdown (void)
+{
+}
diff --git a/3rdparty/plibsys/src/pcondvariable-none.c b/3rdparty/plibsys/src/pcondvariable-none.c
new file mode 100644
index 0000000..b89baa3
--- /dev/null
+++ b/3rdparty/plibsys/src/pcondvariable-none.c
@@ -0,0 +1,80 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pcondvariable.h"
+
+#include <stdlib.h>
+
+struct PCondVariable_ {
+	pint hdl;
+};
+
+P_LIB_API PCondVariable *
+p_cond_variable_new (void)
+{
+	return NULL;
+}
+
+P_LIB_API void
+p_cond_variable_free (PCondVariable *cond)
+{
+	P_UNUSED (cond);
+}
+
+P_LIB_API pboolean
+p_cond_variable_wait (PCondVariable	*cond,
+		      PMutex		*mutex)
+{
+	P_UNUSED (cond);
+	P_UNUSED (mutex);
+
+	return FALSE;
+}
+
+P_LIB_API pboolean
+p_cond_variable_signal (PCondVariable *cond)
+{
+	P_UNUSED (cond);
+
+	return FALSE;
+}
+
+P_LIB_API pboolean
+p_cond_variable_broadcast (PCondVariable *cond)
+{
+	P_UNUSED (cond);
+
+	return FALSE;
+}
+
+void
+p_cond_variable_init (void)
+{
+}
+
+void
+p_cond_variable_shutdown (void)
+{
+}
diff --git a/3rdparty/plibsys/src/pcondvariable-os2.c b/3rdparty/plibsys/src/pcondvariable-os2.c
new file mode 100644
index 0000000..e3f0b0c
--- /dev/null
+++ b/3rdparty/plibsys/src/pcondvariable-os2.c
@@ -0,0 +1,163 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "patomic.h"
+#include "pmem.h"
+#include "pcondvariable.h"
+
+#include <stdlib.h>
+
+#define INCL_DOSSEMAPHORES
+#define INCL_DOSERRORS
+#include <os2.h>
+
+struct PCondVariable_ {
+	HEV	waiters_sema;
+	pint	waiters_count;
+	pint	signaled;
+};
+
+P_LIB_API PCondVariable *
+p_cond_variable_new (void)
+{
+	PCondVariable *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PCondVariable))) == NULL)) {
+		P_ERROR ("PCondVariable::p_cond_variable_new: failed to allocate memory");
+		return NULL;
+	}
+
+	if (P_UNLIKELY (DosCreateEventSem (NULL,
+					   (PHEV) &ret->waiters_sema,
+					   0,
+					   FALSE) != NO_ERROR)) {
+		P_ERROR ("PCondVariable::p_cond_variable_new: failed to initialize");
+		p_free (ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+P_LIB_API void
+p_cond_variable_free (PCondVariable *cond)
+{
+	if (P_UNLIKELY (cond == NULL))
+		return;
+
+	if (P_UNLIKELY (DosCloseEventSem (cond->waiters_sema) != NO_ERROR))
+		P_WARNING ("PCondVariable::p_cond_variable_free: DosCloseEventSem() failed");
+
+	p_free (cond);
+}
+
+P_LIB_API pboolean
+p_cond_variable_wait (PCondVariable	*cond,
+		      PMutex		*mutex)
+{
+	APIRET ulrc;
+	APIRET reset_ulrc;
+
+	if (P_UNLIKELY (cond == NULL || mutex == NULL))
+		return FALSE;
+
+	do {
+		p_atomic_int_inc (&cond->waiters_count);
+		p_mutex_unlock (mutex);
+
+		do {
+			ULONG post_count;
+
+			ulrc = DosWaitEventSem (cond->waiters_sema, SEM_INDEFINITE_WAIT);
+
+			if (ulrc == NO_ERROR) {
+				reset_ulrc = DosResetEventSem (cond->waiters_sema, &post_count);
+				
+				if (P_UNLIKELY (reset_ulrc != NO_ERROR &&
+						reset_ulrc != ERROR_ALREADY_RESET))
+					P_WARNING ("PCondVariable::p_cond_variable_wait: DosResetEventSem() failed");
+			}
+		} while (ulrc == NO_ERROR &&
+			 p_atomic_int_compare_and_exchange (&cond->signaled, 1, 0) == FALSE);
+
+		p_atomic_int_add (&cond->waiters_count, -1);
+		p_mutex_lock (mutex);
+	} while (ulrc == ERROR_INTERRUPT);
+
+	return (ulrc == NO_ERROR) ? TRUE : FALSE;
+}
+
+P_LIB_API pboolean
+p_cond_variable_signal (PCondVariable *cond)
+{
+	pboolean result = TRUE;
+
+	if (P_UNLIKELY (cond == NULL))
+		return FALSE;
+
+	if (p_atomic_int_get (&cond->waiters_count) > 0) {
+		ULONG	post_count;
+		APIRET	ulrc;
+
+		p_atomic_int_set (&cond->signaled, 1);
+
+		ulrc = DosPostEventSem (cond->waiters_sema);
+
+		if (P_UNLIKELY (ulrc != NO_ERROR &&
+				ulrc != ERROR_ALREADY_POSTED &&
+				ulrc != ERROR_TOO_MANY_POSTS)) {
+			P_WARNING ("PCondVariable::p_cond_variable_signal: DosPostEventSem() failed");
+			result = FALSE;
+		}
+	}
+
+	return result;
+}
+
+P_LIB_API pboolean
+p_cond_variable_broadcast (PCondVariable *cond)
+{
+	if (P_UNLIKELY (cond == NULL))
+		return FALSE;
+
+	pboolean result = TRUE;
+
+	while (p_atomic_int_get (&cond->waiters_count) != 0) {
+		if (P_UNLIKELY (p_cond_variable_signal (cond) == FALSE))
+			result = FALSE;
+	}
+
+	return result;
+}
+
+void
+p_cond_variable_init (void)
+{
+}
+
+void
+p_cond_variable_shutdown (void)
+{
+}
diff --git a/3rdparty/plibsys/src/pcondvariable-posix.c b/3rdparty/plibsys/src/pcondvariable-posix.c
new file mode 100644
index 0000000..54d7682
--- /dev/null
+++ b/3rdparty/plibsys/src/pcondvariable-posix.c
@@ -0,0 +1,121 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "pcondvariable.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <pthread.h>
+
+struct PCondVariable_ {
+	pthread_cond_t hdl;
+};
+
+P_LIB_API PCondVariable *
+p_cond_variable_new (void)
+{
+	PCondVariable *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PCondVariable))) == NULL)) {
+		P_ERROR ("PCondVariable::p_cond_variable_new: failed to allocate memory");
+		return NULL;
+	}
+
+	if (P_UNLIKELY (pthread_cond_init (&ret->hdl, NULL) != 0)) {
+		P_ERROR ("PCondVariable::p_cond_variable_new: failed to initialize");
+		p_free (ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+P_LIB_API void
+p_cond_variable_free (PCondVariable *cond)
+{
+	if (P_UNLIKELY (cond == NULL))
+		return;
+
+	if (P_UNLIKELY (pthread_cond_destroy (&cond->hdl) != 0))
+		P_WARNING ("PCondVariable::p_cond_variable_free: pthread_cond_destroy() failed");
+
+	p_free (cond);
+}
+
+P_LIB_API pboolean
+p_cond_variable_wait (PCondVariable	*cond,
+		      PMutex		*mutex)
+{
+	if (P_UNLIKELY (cond == NULL || mutex == NULL))
+		return FALSE;
+
+	/* Cast is eligible since there is only one field in the PMutex structure */
+	if (P_UNLIKELY (pthread_cond_wait (&cond->hdl, (pthread_mutex_t *) mutex) != 0)) {
+		P_ERROR ("PCondVariable::p_cond_variable_wait: pthread_cond_wait() failed");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+P_LIB_API pboolean
+p_cond_variable_signal (PCondVariable *cond)
+{
+	if (P_UNLIKELY (cond == NULL))
+		return FALSE;
+
+	if (P_UNLIKELY (pthread_cond_signal (&cond->hdl) != 0)) {
+		P_ERROR ("PCondVariable::p_cond_variable_signal: pthread_cond_signal() failed");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+P_LIB_API pboolean
+p_cond_variable_broadcast (PCondVariable *cond)
+{
+	if (P_UNLIKELY (cond == NULL))
+		return FALSE;
+
+	if (P_UNLIKELY (pthread_cond_broadcast (&cond->hdl) != 0)) {
+		P_ERROR ("PCondVariable::p_cond_variable_broadcast: thread_cond_broadcast() failed");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+void
+p_cond_variable_init (void)
+{
+}
+
+void
+p_cond_variable_shutdown (void)
+{
+}
diff --git a/3rdparty/plibsys/src/pcondvariable-solaris.c b/3rdparty/plibsys/src/pcondvariable-solaris.c
new file mode 100644
index 0000000..53fdacf
--- /dev/null
+++ b/3rdparty/plibsys/src/pcondvariable-solaris.c
@@ -0,0 +1,122 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "pcondvariable.h"
+
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <thread.h>
+#include <synch.h>
+
+struct PCondVariable_ {
+	cond_t hdl;
+};
+
+P_LIB_API PCondVariable *
+p_cond_variable_new (void)
+{
+	PCondVariable *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PCondVariable))) == NULL)) {
+		P_ERROR ("PCondVariable::p_cond_variable_new: failed to allocate memory");
+		return NULL;
+	}
+
+	if (P_UNLIKELY (cond_init (&ret->hdl, NULL, NULL) != 0)) {
+		P_ERROR ("PCondVariable::p_cond_variable_new: failed to initialize");
+		p_free (ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+P_LIB_API void
+p_cond_variable_free (PCondVariable *cond)
+{
+	if (P_UNLIKELY (cond == NULL))
+		return;
+
+	if (P_UNLIKELY (cond_destroy (&cond->hdl) != 0))
+		P_WARNING ("PCondVariable::p_cond_variable_free: cond_destroy() failed");
+
+	p_free (cond);
+}
+
+P_LIB_API pboolean
+p_cond_variable_wait (PCondVariable	*cond,
+		      PMutex		*mutex)
+{
+	if (P_UNLIKELY (cond == NULL || mutex == NULL))
+		return FALSE;
+
+	/* Cast is eligible since there is only one field in the PMutex structure */
+	if (P_UNLIKELY (cond_wait (&cond->hdl, (mutex_t *) mutex) != 0)) {
+		P_ERROR ("PCondVariable::p_cond_variable_wait: cond_wait() failed");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+P_LIB_API pboolean
+p_cond_variable_signal (PCondVariable *cond)
+{
+	if (P_UNLIKELY (cond == NULL))
+		return FALSE;
+
+	if (P_UNLIKELY (cond_signal (&cond->hdl) != 0)) {
+		P_ERROR ("PCondVariable::p_cond_variable_signal: cond_signal() failed");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+P_LIB_API pboolean
+p_cond_variable_broadcast (PCondVariable *cond)
+{
+	if (P_UNLIKELY (cond == NULL))
+		return FALSE;
+
+	if (P_UNLIKELY (cond_broadcast (&cond->hdl) != 0)) {
+		P_ERROR ("PCondVariable::p_cond_variable_broadcast: cond_broadcast() failed");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+void
+p_cond_variable_init (void)
+{
+}
+
+void
+p_cond_variable_shutdown (void)
+{
+}
diff --git a/3rdparty/plibsys/src/pcondvariable-win.c b/3rdparty/plibsys/src/pcondvariable-win.c
new file mode 100644
index 0000000..0cf7dcb
--- /dev/null
+++ b/3rdparty/plibsys/src/pcondvariable-win.c
@@ -0,0 +1,312 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/* Taken from "Strategies for Implementing POSIX Condition Variables on Win32"
+ * by Douglas C. Schmidt and Irfan Pyarali.
+ * See: http://www.cse.wustl.edu/~schmidt/win32-cv-1.html
+ * See: https://github.com/python/cpython/blob/master/Python/condvar.h
+ */
+
+#include "patomic.h"
+#include "pmem.h"
+#include "pcondvariable.h"
+
+#include <stdlib.h>
+
+typedef VOID (WINAPI * InitializeConditionVariableFunc) (ppointer cv);
+typedef BOOL (WINAPI * SleepConditionVariableCSFunc)    (ppointer cv, PCRITICAL_SECTION cs, DWORD ms);
+typedef VOID (WINAPI * WakeConditionVariableFunc)       (ppointer cv);
+typedef VOID (WINAPI * WakeAllConditionVariableFunc)    (ppointer cv);
+
+typedef pboolean (* PWin32CondInit)    (PCondVariable *cond);
+typedef void     (* PWin32CondClose)   (PCondVariable *cond);
+typedef pboolean (* PWin32CondWait)    (PCondVariable *cond, PMutex *mutex);
+typedef pboolean (* PWin32CondSignal)  (PCondVariable *cond);
+typedef pboolean (* PWin32CondBrdcast) (PCondVariable *cond);
+
+static PWin32CondInit    pp_cond_variable_init_func    = NULL;
+static PWin32CondClose   pp_cond_variable_close_func   = NULL;
+static PWin32CondWait    pp_cond_variable_wait_func    = NULL;
+static PWin32CondSignal  pp_cond_variable_signal_func  = NULL;
+static PWin32CondBrdcast pp_cond_variable_brdcast_func = NULL;
+
+typedef struct PCondVariableVistaTable_ {
+	InitializeConditionVariableFunc	cv_init;
+	SleepConditionVariableCSFunc	cv_wait;
+	WakeConditionVariableFunc	cv_wake;
+	WakeAllConditionVariableFunc	cv_brdcast;
+} PCondVariableVistaTable;
+
+typedef struct PCondVariableXP_ {
+	HANDLE	waiters_sema;
+	pint	waiters_count;
+} PCondVariableXP;
+
+struct PCondVariable_ {
+	ppointer cv;
+};
+
+static PCondVariableVistaTable pp_cond_variable_vista_table = {NULL, NULL, NULL, NULL};
+
+/* CONDITION_VARIABLE routines */
+static pboolean pp_cond_variable_init_vista (PCondVariable *cond);
+static void pp_cond_variable_close_vista (PCondVariable *cond);
+static pboolean pp_cond_variable_wait_vista (PCondVariable *cond, PMutex *mutex);
+static pboolean pp_cond_variable_signal_vista (PCondVariable *cond);
+static pboolean pp_cond_variable_broadcast_vista (PCondVariable *cond);
+
+/* Windows XP emulation routines */
+static pboolean pp_cond_variable_init_xp (PCondVariable *cond);
+static void pp_cond_variable_close_xp (PCondVariable *cond);
+static pboolean pp_cond_variable_wait_xp (PCondVariable *cond, PMutex *mutex);
+static pboolean pp_cond_variable_signal_xp (PCondVariable *cond);
+static pboolean pp_cond_variable_broadcast_xp (PCondVariable *cond);
+
+/* CONDITION_VARIABLE routines */
+
+static pboolean
+pp_cond_variable_init_vista (PCondVariable *cond)
+{
+	pp_cond_variable_vista_table.cv_init (cond);
+
+	return TRUE;
+}
+
+static void
+pp_cond_variable_close_vista (PCondVariable *cond)
+{
+	P_UNUSED (cond);
+}
+
+static pboolean
+pp_cond_variable_wait_vista (PCondVariable *cond, PMutex *mutex)
+{
+	return pp_cond_variable_vista_table.cv_wait (cond,
+						     (PCRITICAL_SECTION) mutex,
+						     INFINITE) != 0 ? TRUE : FALSE;
+}
+
+static pboolean
+pp_cond_variable_signal_vista (PCondVariable *cond)
+{
+	pp_cond_variable_vista_table.cv_wake (cond);
+
+	return TRUE;
+}
+
+static pboolean
+pp_cond_variable_broadcast_vista (PCondVariable *cond)
+{
+	pp_cond_variable_vista_table.cv_brdcast (cond);
+
+	return TRUE;
+}
+
+/* Windows XP emulation routines */
+
+static pboolean
+pp_cond_variable_init_xp (PCondVariable *cond)
+{
+	PCondVariableXP *cv_xp;
+
+	if ((cond->cv = p_malloc0 (sizeof (PCondVariableXP))) == NULL) {
+		P_ERROR ("PCondVariable::pp_cond_variable_init_xp: failed to allocate memory (internal)");
+		return FALSE;
+	}
+
+	cv_xp = ((PCondVariableXP *) cond->cv);
+
+	cv_xp->waiters_count = 0;
+	cv_xp->waiters_sema  = CreateSemaphoreA (NULL, 0, MAXLONG, NULL);
+
+	if (P_UNLIKELY (cv_xp->waiters_sema == NULL)) {
+		P_ERROR ("PCondVariable::pp_cond_variable_init_xp: failed to initialize semaphore");
+		p_free (cond->cv);
+		cond->cv = NULL;
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+pp_cond_variable_close_xp (PCondVariable *cond)
+{
+	CloseHandle (((PCondVariableXP *) cond->cv)->waiters_sema);
+	p_free (cond->cv);
+}
+
+static pboolean
+pp_cond_variable_wait_xp (PCondVariable *cond, PMutex *mutex)
+{
+	PCondVariableXP	*cv_xp = ((PCondVariableXP *) cond->cv);
+	DWORD		wait;
+
+	p_atomic_int_inc (&cv_xp->waiters_count);
+
+	p_mutex_unlock (mutex);
+	wait = WaitForSingleObjectEx (cv_xp->waiters_sema, INFINITE, FALSE);
+	p_mutex_lock (mutex);
+
+	if (wait != WAIT_OBJECT_0)
+		p_atomic_int_add (&cv_xp->waiters_count, -1);
+
+	return wait == WAIT_OBJECT_0 ? TRUE : FALSE;
+}
+
+static pboolean
+pp_cond_variable_signal_xp (PCondVariable *cond)
+{
+	PCondVariableXP *cv_xp = ((PCondVariableXP *) cond->cv);
+
+	if (p_atomic_int_get (&cv_xp->waiters_count) > 0) {
+		p_atomic_int_add (&cv_xp->waiters_count, -1);
+		return ReleaseSemaphore (cv_xp->waiters_sema, 1, 0) != 0 ? TRUE : FALSE;
+	}
+
+	return TRUE;
+}
+
+static pboolean
+pp_cond_variable_broadcast_xp (PCondVariable *cond)
+{
+	PCondVariableXP	*cv_xp = ((PCondVariableXP *) cond->cv);
+	pint		waiters;
+
+	waiters = p_atomic_int_get (&cv_xp->waiters_count);
+
+	if (waiters > 0) {
+		p_atomic_int_set (&cv_xp->waiters_count, 0);
+		return ReleaseSemaphore (cv_xp->waiters_sema, waiters, 0) != 0 ? TRUE : FALSE;
+	}
+
+	return TRUE;
+}
+
+P_LIB_API PCondVariable *
+p_cond_variable_new (void)
+{
+	PCondVariable *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PCondVariable))) == NULL)) {
+		P_ERROR ("PCondVariable::p_cond_variable_new: failed to allocate memory");
+		return NULL;
+	}
+
+	if (P_UNLIKELY (pp_cond_variable_init_func (ret) != TRUE)) {
+		P_ERROR ("PCondVariable::p_cond_variable_new: failed to initialize");
+		p_free (ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+P_LIB_API void
+p_cond_variable_free (PCondVariable *cond)
+{
+	if (P_UNLIKELY (cond == NULL))
+		return;
+
+	pp_cond_variable_close_func (cond);
+	p_free (cond);
+}
+
+P_LIB_API pboolean
+p_cond_variable_wait (PCondVariable	*cond,
+		      PMutex		*mutex)
+{
+	if (P_UNLIKELY (cond == NULL || mutex == NULL))
+		return FALSE;
+
+	return pp_cond_variable_wait_func (cond, mutex);
+}
+
+P_LIB_API pboolean
+p_cond_variable_signal (PCondVariable *cond)
+{
+	if (P_UNLIKELY (cond == NULL))
+		return FALSE;
+
+	return pp_cond_variable_signal_func (cond);
+}
+
+P_LIB_API pboolean
+p_cond_variable_broadcast (PCondVariable *cond)
+{
+	if (P_UNLIKELY (cond == NULL))
+		return FALSE;
+
+	return pp_cond_variable_brdcast_func (cond);
+}
+
+void
+p_cond_variable_init (void)
+{
+	HMODULE hmodule;
+
+	hmodule = GetModuleHandleA ("kernel32.dll");
+
+	if (P_UNLIKELY (hmodule == NULL)) {
+		P_ERROR ("PCondVariable::p_cond_variable_init: failed to load kernel32.dll module");
+		return;
+	}
+
+	pp_cond_variable_vista_table.cv_init = (InitializeConditionVariableFunc) GetProcAddress (hmodule,
+												 "InitializeConditionVariable");
+
+	if (P_LIKELY (pp_cond_variable_vista_table.cv_init != NULL)) {
+		pp_cond_variable_vista_table.cv_wait    = (SleepConditionVariableCSFunc) GetProcAddress (hmodule,
+													 "SleepConditionVariableCS");
+		pp_cond_variable_vista_table.cv_wake    = (WakeConditionVariableFunc) GetProcAddress (hmodule,
+												      "WakeConditionVariable");
+		pp_cond_variable_vista_table.cv_brdcast = (WakeAllConditionVariableFunc) GetProcAddress (hmodule,
+													 "WakeAllConditionVariable");
+
+		pp_cond_variable_init_func    = pp_cond_variable_init_vista;
+		pp_cond_variable_close_func   = pp_cond_variable_close_vista;
+		pp_cond_variable_wait_func    = pp_cond_variable_wait_vista;
+		pp_cond_variable_signal_func  = pp_cond_variable_signal_vista;
+		pp_cond_variable_brdcast_func = pp_cond_variable_broadcast_vista;
+	} else {
+		pp_cond_variable_init_func    = pp_cond_variable_init_xp;
+		pp_cond_variable_close_func   = pp_cond_variable_close_xp;
+		pp_cond_variable_wait_func    = pp_cond_variable_wait_xp;
+		pp_cond_variable_signal_func  = pp_cond_variable_signal_xp;
+		pp_cond_variable_brdcast_func = pp_cond_variable_broadcast_xp;
+	}
+}
+
+void
+p_cond_variable_shutdown (void)
+{
+	memset (&pp_cond_variable_vista_table, 0, sizeof (pp_cond_variable_vista_table));
+
+	pp_cond_variable_init_func    = NULL;
+	pp_cond_variable_close_func   = NULL;
+	pp_cond_variable_wait_func    = NULL;
+	pp_cond_variable_signal_func  = NULL;
+	pp_cond_variable_brdcast_func = NULL;
+}
diff --git a/3rdparty/plibsys/src/pcondvariable.h b/3rdparty/plibsys/src/pcondvariable.h
new file mode 100644
index 0000000..d69c8b9
--- /dev/null
+++ b/3rdparty/plibsys/src/pcondvariable.h
@@ -0,0 +1,138 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file pcondvariable.h
+ * @brief Condition variable
+ * @author Alexander Saprykin
+ *
+ * A condition variable is an inter-thread synchronization primitive, often
+ * used in the classical 'producers-consumers' concurrent data access models.
+ *
+ * The main idea is to notify waiting thread(s) for some events before they
+ * can enter a critical section. Hence the name of the primitive: a thread
+ * enters the critical section upon an accomplished condition. Compare it with a
+ * mutex where the thread enters the critical section as soon as no one holds a
+ * lock.
+ *
+ * Several threads can be notified at once, but only one of them can enter the
+ * critical section. The order of the threads in that case is implementation
+ * dependent.
+ *
+ * As the thread enters the critical section upon a condition it still requires
+ * a mutex to guard its code against concurrent access from other threads. The
+ * mutex provided in pair with a condition variable will be automatically locked
+ * on the condition, the thread should unlock it explicitly after leaving the
+ * critical section. That mutex is unlocked while waiting for the condition and
+ * should be locked prior calling the condition waiting routine.
+ *
+ * The waiting thread behavior: create a new condition variable with
+ * p_cond_variable_new(), create and lock a mutex before a critical section and
+ * wait for a signal from another thread on this condition variable
+ * using p_cond_variable_wait().
+ *
+ * The signaling thread behavior: upon reaching event time emit a signal with
+ * p_cond_variable_signal() to wake up a single waiting thread or
+ * p_cond_variable_broadcast() to wake up all the waiting threads.
+ *
+ * After emitting the signal only the one thread will get the locked mutex back
+ * to continue executing the critical section.
+ *
+ * It is implementation dependent whether a thread will receive a missed signal
+ * (when a notification from the one thread was emitted prior another thread has
+ * been called for waiting), so do not rely on this behavior.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PCONDVARIABLE_H
+#define PLIBSYS_HEADER_PCONDVARIABLE_H
+
+#include <pmacros.h>
+#include <ptypes.h>
+#include <pmutex.h>
+
+P_BEGIN_DECLS
+
+/** Condition variable opaque data structure. */
+typedef struct PCondVariable_ PCondVariable;
+
+/**
+ * @brief Creates a new #PCondVariable.
+ * @return Pointer to a newly created #PCondVariable structure, or NULL if
+ * failed.
+ * @since 0.0.1
+ */
+P_LIB_API PCondVariable *	p_cond_variable_new		(void);
+
+/**
+ * @brief Frees #PCondVariable structure.
+ * @param cond Condtion variable to free.
+ * @since 0.0.1
+ */
+P_LIB_API void			p_cond_variable_free		(PCondVariable	*cond);
+
+/**
+ * @brief Waits for a signal on a given condition variable.
+ * @param cond Condition variable to wait on.
+ * @param mutex Locked mutex which will remain locked after waiting.
+ * @return TRUE on success, FALSE otherwise.
+ * @since 0.0.1
+ *
+ * The calling thread will sleep until the signal on @a cond arrived.
+ */
+P_LIB_API pboolean		p_cond_variable_wait		(PCondVariable	*cond,
+								 PMutex		*mutex);
+
+/**
+ * @brief Emitts a signal on a given condition variable for one waiting thread.
+ * @param cond Condition variable to emit the signal on.
+ * @return TRUE on success, FALSE otherwise.
+ * @since 0.0.1
+ *
+ * After emitting the signal only the one thread waiting for it will be waken
+ * up. Do not rely on a queue concept for waiting threads. Though the
+ * implementation is intended to be much close to a queue, it's not fairly
+ * enough. Due that any thread can be waken up, even if it has just called
+ * p_cond_variable_wait() while there are other waiting threads.
+ */
+P_LIB_API pboolean		p_cond_variable_signal		(PCondVariable	*cond);
+
+/**
+ * @brief Emitts a signal on a given condition variable for all the waiting
+ * threads.
+ * @param cond Condition variable to emit the signal on.
+ * @return TRUE on success, FALSE otherwise.
+ * @since 0.0.1
+ *
+ * After emitting the signal all the threads waiting for it will be waken up.
+ */
+P_LIB_API pboolean		p_cond_variable_broadcast	(PCondVariable	*cond);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PCONDVARIABLE_H */
diff --git a/3rdparty/plibsys/src/pcryptohash-gost3411.c b/3rdparty/plibsys/src/pcryptohash-gost3411.c
new file mode 100644
index 0000000..9430453
--- /dev/null
+++ b/3rdparty/plibsys/src/pcryptohash-gost3411.c
@@ -0,0 +1,484 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "pcryptohash-gost3411.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+struct PHashGOST3411_ {
+	puint32	buf[8];  /* Buffer to handle incoming data. */
+	puint32	hash[8]; /* State of calculated hash.       */
+	puint32	len[8];  /* Length of hashed data, in bits. */
+	puint32	sum[8];  /* 256-bit sum of hashed data.     */
+};
+
+static void pp_crypto_hash_gost3411_swap_bytes (puint32 *data, puint words);
+static void pp_crypto_hash_gost3411_sum_256 (puint32 a[8], const puint32 b[8]);
+static void pp_crypto_hash_gost3411_process (PHashGOST3411 *ctx, const puint32 data[8]);
+
+/* K block data from RFC4357 for GOST 28147-89 */
+/* static const puchar pp_crypto_hash_gost3411_K_block[8][16] = {
+		{0x9, 0x6, 0x3, 0x2, 0x8, 0xB, 0x1, 0x7, 0xA, 0x4, 0xE, 0xF, 0xC, 0x0, 0xD, 0x5},
+		{0x3, 0x7, 0xE, 0x9, 0x8, 0xA, 0xF, 0x0, 0x5, 0x2, 0x6, 0xC, 0xB, 0x4, 0xD, 0x1},
+		{0xE, 0x4, 0x6, 0x2, 0xB, 0x3, 0xD, 0x8, 0xC, 0xF, 0x5, 0xA, 0x0, 0x7, 0x1, 0x9},
+		{0xE, 0x7, 0xA, 0xC, 0xD, 0x1, 0x3, 0x9, 0x0, 0x2, 0xB, 0x4, 0xF, 0x8, 0x5, 0x6},
+		{0xB, 0x5, 0x1, 0x9, 0x8, 0xD, 0xF, 0x0, 0xE, 0x4, 0x2, 0x3, 0xC, 0x7, 0xA, 0x6},
+		{0x3, 0xA, 0xD, 0xC, 0x1, 0x2, 0x0, 0xB, 0x7, 0x5, 0x9, 0x4, 0x8, 0xF, 0xE, 0x6},
+		{0x1, 0xD, 0x2, 0x9, 0x7, 0xA, 0x6, 0x0, 0x8, 0xC, 0x4, 0x5, 0xF, 0x3, 0xB, 0xE},
+		{0xB, 0xA, 0xF, 0x5, 0x0, 0xC, 0xE, 0x8, 0x6, 0x2, 0x3, 0x9, 0x1, 0x7, 0xD, 0x4}
+}; */
+
+/* K block data used by Russian Central Bank (see RFC 4357, sec. 11.2) */
+/* static const puchar pp_crypto_hash_gost3411_K_block[8][16] = {
+		{0x4, 0xA, 0x9, 0x2, 0xD, 0x8, 0x0, 0xE, 0x6, 0xB, 0x1, 0xC, 0x7, 0xF, 0x5, 0x3},
+		{0xE, 0xB, 0x4, 0xC, 0x6, 0xD, 0xF, 0xA, 0x2, 0x3, 0x8, 0x1, 0x0, 0x7, 0x5, 0x9},
+		{0x5, 0x8, 0x1, 0xD, 0xA, 0x3, 0x4, 0x2, 0xE, 0xF, 0xC, 0x7, 0x6, 0x0, 0x9, 0xB},
+		{0x7, 0xD, 0xA, 0x1, 0x0, 0x8, 0x9, 0xF, 0xE, 0x4, 0x6, 0xC, 0xB, 0x2, 0x5, 0x3},
+		{0x6, 0xC, 0x7, 0x1, 0x5, 0xF, 0xD, 0x8, 0x4, 0xA, 0x9, 0xE, 0x0, 0x3, 0xB, 0x2},
+		{0x4, 0xB, 0xA, 0x0, 0x7, 0x2, 0x1, 0xD, 0x3, 0x6, 0x8, 0x5, 0x9, 0xC, 0xF, 0xE},
+		{0xD, 0xB, 0x4, 0x1, 0x3, 0xF, 0x5, 0x9, 0x0, 0xA, 0xE, 0x7, 0x6, 0x8, 0x2, 0xC},
+		{0x1, 0xF, 0xD, 0x0, 0x5, 0x7, 0xA, 0x4, 0x9, 0x2, 0x3, 0xE, 0x6, 0xB, 0x8, 0xC}
+}; */
+
+ /* K block data id-GostR3411-94-CryptoProParamSet (see RFC 4357, sec. 11.2) */
+  static const puchar pp_crypto_hash_gost3411_K_block[8][16] = {
+		{0xA, 0x4, 0x5, 0x6, 0x8, 0x1, 0x3, 0x7, 0xD, 0xC, 0xE, 0x0, 0x9, 0x2, 0xB, 0xF},
+		{0x5, 0xF, 0x4, 0x0, 0x2, 0xD, 0xB, 0x9, 0x1, 0x7, 0x6, 0x3, 0xC, 0xE, 0xA, 0x8},
+		{0x7, 0xF, 0xC, 0xE, 0x9, 0x4, 0x1, 0x0, 0x3, 0xB, 0x5, 0x2, 0x6, 0xA, 0x8, 0xD},
+		{0x4, 0xA, 0x7, 0xC, 0x0, 0xF, 0x2, 0x8, 0xE, 0x1, 0x6, 0x5, 0xD, 0xB, 0x9, 0x3},
+		{0x7, 0x6, 0x4, 0xB, 0x9, 0xC, 0x2, 0xA, 0x1, 0x8, 0x0, 0xE, 0xF, 0xD, 0x3, 0x5},
+		{0x7, 0x6, 0x2, 0x4, 0xD, 0x9, 0xF, 0x0, 0xA, 0x1, 0x5, 0xB, 0x8, 0xE, 0xC, 0x3},
+		{0xD, 0xE, 0x4, 0x1, 0x7, 0x0, 0x5, 0xA, 0x3, 0xC, 0x8, 0xF, 0x6, 0x2, 0x9, 0xB},
+		{0x1, 0x3, 0xA, 0x9, 0x5, 0xB, 0x4, 0xF, 0x8, 0x6, 0x7, 0xE, 0xD, 0x0, 0x2, 0xC}
+ };
+
+/* GOST 28147-89 transformation to generate keys */
+#define P_GOST_28147_ROUND(N, key)							\
+{											\
+	puint32 CM1;									\
+											\
+	CM1 = (N)[0] + (key);								\
+											\
+	CM1 = ((puint32) pp_crypto_hash_gost3411_K_block [0][CM1 & 0xF]			\
+	    |  (puint32) pp_crypto_hash_gost3411_K_block [1][(CM1 >> 4)  & 0xF] << 4	\
+	    |  (puint32) pp_crypto_hash_gost3411_K_block [2][(CM1 >> 8)  & 0xF] << 8	\
+	    |  (puint32) pp_crypto_hash_gost3411_K_block [3][(CM1 >> 12) & 0xF] << 12	\
+	    |  (puint32) pp_crypto_hash_gost3411_K_block [4][(CM1 >> 16) & 0xF] << 16	\
+	    |  (puint32) pp_crypto_hash_gost3411_K_block [5][(CM1 >> 20) & 0xF] << 20	\
+	    |  (puint32) pp_crypto_hash_gost3411_K_block [6][(CM1 >> 24) & 0xF] << 24	\
+	    |  (puint32) pp_crypto_hash_gost3411_K_block [7][(CM1 >> 28) & 0xF] << 28);	\
+											\
+	CM1 = ((CM1 << 11) | (CM1 >> 21)) ^ (N)[1];					\
+	(N)[1] = (N)[0];								\
+	(N)[0] = CM1;									\
+}
+
+/* Core GOST 28147-89 transformation */
+#define P_GOST_28147_E(data, key, out)					\
+{									\
+	puint32 N[2];							\
+									\
+	memcpy (N, data, 8);						\
+									\
+	P_GOST_28147_ROUND (N, (key)[0]);				\
+	P_GOST_28147_ROUND (N, (key)[1]);				\
+	P_GOST_28147_ROUND (N, (key)[2]);				\
+	P_GOST_28147_ROUND (N, (key)[3]);				\
+	P_GOST_28147_ROUND (N, (key)[4]);				\
+	P_GOST_28147_ROUND (N, (key)[5]);				\
+	P_GOST_28147_ROUND (N, (key)[6]);				\
+	P_GOST_28147_ROUND (N, (key)[7]);				\
+									\
+	P_GOST_28147_ROUND (N, (key)[0]);				\
+	P_GOST_28147_ROUND (N, (key)[1]);				\
+	P_GOST_28147_ROUND (N, (key)[2]);				\
+	P_GOST_28147_ROUND (N, (key)[3]);				\
+	P_GOST_28147_ROUND (N, (key)[4]);				\
+	P_GOST_28147_ROUND (N, (key)[5]);				\
+	P_GOST_28147_ROUND (N, (key)[6]);				\
+	P_GOST_28147_ROUND (N, (key)[7]);				\
+									\
+	P_GOST_28147_ROUND (N, (key)[0]);				\
+	P_GOST_28147_ROUND (N, (key)[1]);				\
+	P_GOST_28147_ROUND (N, (key)[2]);				\
+	P_GOST_28147_ROUND (N, (key)[3]);				\
+	P_GOST_28147_ROUND (N, (key)[4]);				\
+	P_GOST_28147_ROUND (N, (key)[5]);				\
+	P_GOST_28147_ROUND (N, (key)[6]);				\
+	P_GOST_28147_ROUND (N, (key)[7]);				\
+									\
+	P_GOST_28147_ROUND (N, (key)[7]);				\
+	P_GOST_28147_ROUND (N, (key)[6]);				\
+	P_GOST_28147_ROUND (N, (key)[5]);				\
+	P_GOST_28147_ROUND (N, (key)[4]);				\
+	P_GOST_28147_ROUND (N, (key)[3]);				\
+	P_GOST_28147_ROUND (N, (key)[2]);				\
+	P_GOST_28147_ROUND (N, (key)[1]);				\
+	P_GOST_28147_ROUND (N, (key)[0]);				\
+									\
+	(out)[0] = N[1];						\
+	(out)[1] = N[0];						\
+}
+
+/* P transformation from GOST R 34.11-94 */
+#define P_GOST_3411_P(data, out)					\
+{									\
+	(out)[0] = ((data) [0] & 0x000000FF)				\
+		 | (((data)[2] << 8)	& 0x0000FF00)			\
+		 | (((data)[4] << 16)	& 0x00FF0000)			\
+		 | (((data)[6] << 24)	& 0xFF000000);			\
+	(out)[1] = (((data)[0] >> 8)	& 0x000000FF)			\
+		 | ((data) [2]		& 0x0000FF00)			\
+		 | (((data)[4] << 8)	& 0x00FF0000)			\
+		 | (((data)[6] << 16)	& 0xFF000000);			\
+	(out)[2] = (((data)[0] >> 16)	& 0x000000FF)			\
+		 | (((data)[2] >> 8)	& 0x0000FF00)			\
+		 | ((data) [4]		& 0x00FF0000)			\
+		 | (((data)[6] << 8)	& 0xFF000000);			\
+	(out)[3] = (((data)[0] >> 24)	& 0x000000FF)			\
+		 | (((data)[2] >> 16)	& 0x0000FF00)			\
+		 | (((data)[4] >> 8)	& 0x00FF0000)			\
+		 | ((data) [6]		& 0xFF000000);			\
+	(out)[4] = ((data) [1]		& 0x000000FF)			\
+		 | (((data)[3] << 8)	& 0x0000FF00)			\
+		 | (((data)[5] << 16)	& 0x00FF0000)			\
+		 | (((data)[7] << 24)	& 0xFF000000);			\
+	(out)[5] = (((data)[1] >> 8)	& 0x000000FF)			\
+		 | ((data) [3]		& 0x0000FF00)			\
+		 | (((data)[5] << 8)	& 0x00FF0000)			\
+		 | (((data)[7] << 16)	& 0xFF000000);			\
+	(out)[6] = (((data)[1] >> 16)	& 0x000000FF)			\
+		 | (((data)[3] >> 8)	& 0x0000FF00)			\
+		 | ((data) [5]		& 0x00FF0000)			\
+		 | (((data)[7] << 8)	& 0xFF000000);			\
+	(out)[7] = (((data)[1] >> 24)	& 0x000000FF)			\
+		 | (((data)[3] >> 16)	& 0x0000FF00)			\
+		 | (((data)[5] >> 8)	& 0x00FF0000)			\
+		 | ((data) [7]		& 0xFF000000);			\
+}
+
+static void
+pp_crypto_hash_gost3411_swap_bytes (puint32	*data,
+				    puint	words)
+{
+#ifndef PLIBSYS_IS_BIGENDIAN
+	P_UNUSED (data);
+	P_UNUSED (words);
+#else
+	while (words-- > 0) {
+		*data = PUINT32_TO_LE (*data);
+		++data;
+	}
+#endif
+}
+
+/* 256-bit sum */
+static void
+pp_crypto_hash_gost3411_sum_256 (puint32	a[8],
+				 const puint32	b[8])
+{
+	puint		i;
+	puint32		old;
+	pboolean	carry;
+
+	carry = FALSE;
+	for (i = 0; i < 8; ++i) {
+		old = a[i];
+		a[i] = a[i] + b[i] + (carry ? 1 : 0);
+		carry = (a[i] < old || a[i] < b[i]) ? TRUE : FALSE;
+	}
+}
+
+/* Core GOST R 34.11-94 transformation */
+static void pp_crypto_hash_gost3411_process (PHashGOST3411	*ctx,
+					     const puint32	data[8])
+{
+	puint32 U[8], V[8], W[8], S[8], K[4][8];
+
+	memcpy (U, ctx->hash, 32);
+	memcpy (V, data, 32);
+
+	/* Generate first key: P (U xor V) */
+	W[0] = U[0] ^ V[0];
+	W[1] = U[1] ^ V[1];
+	W[2] = U[2] ^ V[2];
+	W[3] = U[3] ^ V[3];
+	W[4] = U[4] ^ V[4];
+	W[5] = U[5] ^ V[5];
+	W[6] = U[6] ^ V[6];
+	W[7] = U[7] ^ V[7];
+
+	P_GOST_3411_P (W, K[0]);
+
+	/* Generate second key: P (A (U) xor A^2 (V)) */
+	W[0] = U[2] ^ V[4];
+	W[1] = U[3] ^ V[5];
+	W[2] = U[4] ^ V[6];
+	W[3] = U[5] ^ V[7];
+	W[4] = U[6] ^ (V[0] ^= V[2]);
+	W[5] = U[7] ^ (V[1] ^= V[3]);
+	W[6] = (U[0] ^= U[2]) ^ (V[2] ^= V[4]);
+	W[7] = (U[1] ^= U[3]) ^ (V[3] ^= V[5]);
+
+	P_GOST_3411_P (W, K[1]);
+
+	/* Generate third key: P ((A^2 (U) + C3) xor A^4 (V)) */
+	/* C3 = FF00FFFF 000000FF FF0000FF 00FFFF00 00FF00FF 00FF00FF FF00FF00 FF00FF00 */
+	U[2] ^= U[4] ^ 0x000000FF;
+	U[3] ^= U[5] ^ 0xFF00FFFF;
+	U[4] ^= 0xFF00FF00;
+	U[5] ^= 0xFF00FF00;
+	U[6] ^= 0x00FF00FF;
+	U[7] ^= 0x00FF00FF;
+	U[0] ^= 0x00FFFF00;
+	U[1] ^= 0xFF0000FF;
+
+	W[0] = U[4] ^ V[0];
+	W[2] = U[6] ^ V[2];
+	W[4] = U[0] ^ (V[4] ^= V[6]);
+	W[6] = U[2] ^ (V[6] ^= V[0]);
+	W[1] = U[5] ^ V[1];
+	W[3] = U[7] ^ V[3];
+	W[5] = U[1] ^ (V[5] ^= V[7]);
+	W[7] = U[3] ^ (V[7] ^= V[1]);
+
+	P_GOST_3411_P (W, K[2]);
+
+	/* Generate forth key: P (A (A^2 (U) xor C3) xor A^6 (V)) */
+	W[0] = U[6] ^ V[4];
+	W[1] = U[7] ^ V[5];
+	W[2] = U[0] ^ V[6];
+	W[3] = U[1] ^ V[7];
+	W[4] = U[2] ^ (V[0] ^= V[2]);
+	W[5] = U[3] ^ (V[1] ^= V[3]);
+	W[6] = (U[4] ^= U[6]) ^ (V[2] ^= V[4]);
+	W[7] = (U[5] ^= U[7]) ^ (V[3] ^= V[5]);
+
+	P_GOST_3411_P (W, K[3]);
+
+	/* Perform GOST 28147-89 encryption */
+	P_GOST_28147_E (ctx->hash,      K[0], S);
+	P_GOST_28147_E (ctx->hash + 2,  K[1], S + 2);
+	P_GOST_28147_E (ctx->hash + 4,  K[2], S + 4);
+	P_GOST_28147_E (ctx->hash + 6,  K[3], S + 6);
+
+	/* Step hash function: H (M, Hprev) = PSI^61 (Hprev xor PSI (M xor PSI^12 (S))) */
+
+	/* (12 rounds of LFSR) xor M */
+	U[0] = data[0] ^ S[6];
+
+	U[1] = data[1] ^ S[7];
+
+	U[2] = data[2] ^ (S[0] & 0x0000FFFF)	^ (S[0] >> 16) ^ (S[0] << 16)
+		       ^ (S[1] & 0x0000FFFF)	^ (S[1] >> 16) ^ (S[2] << 16)
+		       ^ (S[7] & 0xFFFF0000)	^ (S[6] << 16) ^ (S[7] >> 16)
+		       ^  S[6];
+
+	U[3] = data[3] ^ (S[0] & 0x0000FFFF)	^ (S[0] << 16) ^ (S[2] << 16)
+		       ^ (S[1] & 0x0000FFFF)	^ (S[1] << 16) ^ (S[1] >> 16)
+		       ^ (S[7] & 0x0000FFFF)	^ (S[2] >> 16) ^ (S[3] << 16)
+		       ^ (S[6] << 16)		^ (S[6] >> 16) ^ (S[7] << 16)
+		       ^ (S[7] >> 16)		^ S[6];
+
+	U[4] = data[4] ^ (S[0] & 0xFFFF0000)	^ (S[0] << 16) ^ (S[0] >> 16)
+		       ^ (S[1] & 0xFFFF0000)	^ (S[1] >> 16) ^ (S[2] << 16)
+		       ^ (S[7] & 0x0000FFFF)	^ (S[3] << 16) ^ (S[3] >> 16)
+		       ^ (S[4] << 16)		^ (S[6] << 16) ^ (S[6] >> 16)
+		       ^ (S[2] >> 16)		^ (S[7] << 16) ^ (S[7] >> 16);
+
+	U[5] = data[5] ^ (S[0] & 0xFFFF0000)	^ (S[0] >> 16) ^ (S[0] << 16)
+		       ^ (S[1] & 0x0000FFFF)	^ (S[7] >> 16) ^ (S[2] >> 16)
+		       ^ (S[7] & 0xFFFF0000)	^ (S[3] >> 16) ^ (S[4] << 16)
+		       ^ (S[4] >> 16)		^ (S[5] << 16) ^ (S[6] << 16)
+		       ^ (S[6] >> 16)		^ (S[3] << 16) ^ (S[7] << 16)
+		       ^ S[2];
+
+	U[6] = data[6] ^ (S[4] >> 16)		^ (S[1] >> 16) ^ (S[2] << 16)
+		       ^ (S[7] << 16)		^ (S[3] >> 16) ^ (S[4] << 16)
+		       ^ (S[5] << 16)		^ (S[5] >> 16) ^ (S[6] << 16)
+		       ^ (S[6] >> 16)		^ S[6]	       ^ S[0]
+		       ^ S[3];
+
+	U[7] = data[7] ^ (S[0] & 0xFFFF0000)	^ (S[0] << 16) ^ (S[1] << 16)
+		       ^ (S[1] & 0x0000FFFF)	^ (S[2] >> 16) ^ (S[3] << 16)
+		       ^ (S[7] & 0x0000FFFF)	^ (S[4] >> 16) ^ (S[5] << 16)
+		       ^ (S[5] >> 16)		^ (S[6] >> 16) ^ (S[7] << 16)
+		       ^ (S[7] >> 16)		^ S[4];
+
+	/* (1 round of LFSR) xor Hprev */
+	V[0] = ctx->hash[0] ^ (U[1] << 16) ^ (U[0] >> 16);
+	V[1] = ctx->hash[1] ^ (U[2] << 16) ^ (U[1] >> 16);
+	V[2] = ctx->hash[2] ^ (U[3] << 16) ^ (U[2] >> 16);
+	V[3] = ctx->hash[3] ^ (U[4] << 16) ^ (U[3] >> 16);
+	V[4] = ctx->hash[4] ^ (U[5] << 16) ^ (U[4] >> 16);
+	V[5] = ctx->hash[5] ^ (U[6] << 16) ^ (U[5] >> 16);
+	V[6] = ctx->hash[6] ^ (U[7] << 16) ^ (U[6] >> 16);
+	V[7] = ctx->hash[7] ^ (U[7] >> 16)
+	     ^ (U[0] << 16) ^ (U[1] & 0xFFFF0000)
+	     ^ (U[1] << 16) ^ (U[7] & 0xFFFF0000)
+	     ^ (U[6] << 16) ^ (U[0] & 0xFFFF0000);
+
+	/* Final 61 rounds of LFSR */
+	ctx->hash[0] = (V[0] & 0xFFFF0000)	^ (V[0] << 16)	^ (V[0] >> 16)
+		     ^ (V[1] & 0xFFFF0000)	^ (V[1] >> 16)	^ (V[2] << 16)
+		     ^ (V[7] & 0x0000FFFF)	^ (V[3] >> 16)	^ (V[4] << 16)
+		     ^ (V[5] >> 16)		^ (V[6] >> 16)	^ (V[7] << 16)
+		     ^ (V[7] >> 16)		^ V[5];
+	ctx->hash[1] = (V[0] & 0xFFFF0000)	^ (V[0] << 16)	^ (V[0] >> 16)
+		     ^ (V[1] & 0x0000FFFF)	^ (V[2] >> 16)	^ (V[3] << 16)
+		     ^ (V[7] & 0xFFFF0000)	^ (V[4] >> 16)	^ (V[5] << 16)
+		     ^ (V[6] << 16)		^ (V[7] >> 16)	^ V[6]
+		     ^ V[2] ;
+	ctx->hash[2] = (V[0] & 0x0000FFFF)	^ (V[0] << 16)	^ (V[1] << 16)
+		     ^ (V[7] & 0x0000FFFF)	^ (V[1] >> 16)	^ (V[2] << 16)
+		     ^ (V[1] & 0xFFFF0000)	^ (V[3] >> 16)  ^ (V[4] << 16)
+		     ^ (V[5] >> 16)		^ (V[6] >> 16)	^ (V[7] << 16)
+		     ^ (V[7] >> 16)		^ V[3]		^ V[6];
+	ctx->hash[3] = (V[0] & 0xFFFF0000)	^ (V[0] << 16)	^ (V[0] >> 16)
+		     ^ (V[1] & 0xFFFF0000)	^ (V[1] >> 16)	^ (V[2] << 16)
+		     ^ (V[7] & 0x0000FFFF)	^ (V[2] >> 16)	^ (V[3] << 16)
+		     ^ (V[4] >> 16)		^ (V[5] << 16)	^ (V[6] << 16)
+		     ^ (V[7] >> 16)		^ V[2]		^ V[4];
+	ctx->hash[4] = (V[0] >> 16)		^ (V[1] << 16)  ^ (V[2] >> 16)
+		     ^ (V[3] << 16)		^ (V[3] >> 16)	^ (V[4] << 16)
+		     ^ (V[5] >> 16)		^ (V[6] << 16)	^ (V[6] >> 16)
+		     ^ (V[7] << 16)		^ V[1]		^ V[2]
+		     ^ V[3]			^ V[5];
+	ctx->hash[5] = (V[0] & 0xFFFF0000)	^ (V[0] << 16)	^ (V[1] << 16)
+		     ^ (V[1] & 0xFFFF0000) 	^ (V[1] >> 16)	^ (V[2] << 16)
+		     ^ (V[7] & 0xFFFF0000)	^ (V[3] >> 16)  ^ (V[4] << 16)
+		     ^ (V[4] >> 16)		^ (V[5] << 16)	^ (V[6] << 16)
+		     ^ (V[6] >> 16)		^ (V[7] << 16)	^ (V[7] >> 16)
+		     ^ V[2]			^ V[3]		^ V[4]
+		     ^ V[6];
+	ctx->hash[6] = (V[2] >> 16)		^ (V[3] << 16)	^ (V[4] >> 16)
+		     ^ (V[5] << 16)		^ (V[5] >> 16)	^ (V[6] << 16)
+		     ^ (V[6] >> 16)		^ (V[7] << 16)	^ V[7]
+		     ^ V[0]			^ V[2]		^ V[3]
+		     ^ V[4]			^ V[5]		^ V[6];
+	ctx->hash[7] = (V[0] >> 16)		^ (V[1] << 16)	^ (V[1] >> 16)
+		     ^ (V[2] << 16)		^ (V[3] >> 16)	^ (V[4] << 16)
+		     ^ (V[5] >> 16)		^ (V[6] << 16)	^ (V[6] >> 16)
+		     ^ (V[7] << 16)		^ V[7]		^ V[0]
+		     ^ V[3]			^ V[4]		^ V[5];
+}
+
+PHashGOST3411 *
+p_crypto_hash_gost3411_new (void)
+{
+	PHashGOST3411 *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PHashGOST3411))) == NULL))
+		return NULL;
+
+	p_crypto_hash_gost3411_reset (ret);
+
+	return ret;
+}
+
+void
+p_crypto_hash_gost3411_update (PHashGOST3411	*ctx,
+			       const puchar	*data,
+			       psize		len)
+{
+	puint32	left, to_fill, len256[8];
+
+	left = (ctx->len[0] & 0xFF) >> 3;
+	to_fill = 32 - left;
+
+	memset (len256, 0, 32);
+	len256[0] = (puint32) (len << 3);
+	len256[1] = (puint32) (len >> 29);
+
+	pp_crypto_hash_gost3411_sum_256 (ctx->len, len256);
+
+	if (left && (puint32) len >= to_fill) {
+		memcpy ((pchar *) ctx->buf + left, data, to_fill);
+		pp_crypto_hash_gost3411_swap_bytes (ctx->buf, 8);
+		pp_crypto_hash_gost3411_process (ctx, ctx->buf);
+		pp_crypto_hash_gost3411_sum_256 (ctx->sum, ctx->buf);
+
+		data += to_fill;
+		len -= to_fill;
+		left = 0;
+	}
+
+	while (len >= 32) {
+		memcpy (ctx->buf, data, 32);
+		pp_crypto_hash_gost3411_swap_bytes (ctx->buf, 8);
+		pp_crypto_hash_gost3411_process (ctx, ctx->buf);
+		pp_crypto_hash_gost3411_sum_256 (ctx->sum, ctx->buf);
+
+		data += 32;
+		len -= 32;
+	}
+
+	if (len > 0)
+		memcpy ((pchar *) ctx->buf + left, data, len);
+}
+
+void
+p_crypto_hash_gost3411_finish (PHashGOST3411 *ctx)
+{
+	puint32 left, last;
+
+	left = ctx->len[0] & 0xFF;
+	last = 32 - (left >> 3);
+
+	if (last % 32 != 0) {
+		memset ((pchar *) ctx->buf + (left >> 3), 0, last);
+		pp_crypto_hash_gost3411_swap_bytes (ctx->buf, 8);
+		pp_crypto_hash_gost3411_process (ctx, ctx->buf);
+		pp_crypto_hash_gost3411_sum_256 (ctx->sum, ctx->buf);
+	}
+
+	pp_crypto_hash_gost3411_process (ctx, ctx->len);
+	pp_crypto_hash_gost3411_process (ctx, ctx->sum);
+
+	pp_crypto_hash_gost3411_swap_bytes (ctx->hash, 8);
+}
+
+const puchar *
+p_crypto_hash_gost3411_digest (PHashGOST3411 *ctx)
+{
+	return (const puchar *) ctx->hash;
+}
+
+void
+p_crypto_hash_gost3411_reset (PHashGOST3411 *ctx)
+{
+	memset (ctx->buf, 0, 32);
+	memset (ctx->hash, 0, 32);
+	memset (ctx->len, 0, 32);
+	memset (ctx->sum, 0, 32);
+}
+
+void
+p_crypto_hash_gost3411_free (PHashGOST3411 *ctx)
+{
+	p_free (ctx);
+}
diff --git a/3rdparty/plibsys/src/pcryptohash-gost3411.h b/3rdparty/plibsys/src/pcryptohash-gost3411.h
new file mode 100644
index 0000000..dd0f0ad
--- /dev/null
+++ b/3rdparty/plibsys/src/pcryptohash-gost3411.h
@@ -0,0 +1,53 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/* GOST R 34.11-94 interface implementation for #PCryptoHash */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PCRYPTOHASHGOST3411_H
+#define PLIBSYS_HEADER_PCRYPTOHASHGOST3411_H
+
+#include "ptypes.h"
+#include "pmacros.h"
+
+P_BEGIN_DECLS
+
+typedef struct PHashGOST3411_ PHashGOST3411;
+
+PHashGOST3411 *	p_crypto_hash_gost3411_new	(void);
+void		p_crypto_hash_gost3411_update	(PHashGOST3411		*ctx,
+						 const puchar		*data,
+						 psize			len);
+void		p_crypto_hash_gost3411_finish	(PHashGOST3411		*ctx);
+const puchar *	p_crypto_hash_gost3411_digest	(PHashGOST3411		*ctx);
+void		p_crypto_hash_gost3411_reset	(PHashGOST3411		*ctx);
+void		p_crypto_hash_gost3411_free	(PHashGOST3411		*ctx);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PCRYPTOHASHGOST3411_H */
diff --git a/3rdparty/plibsys/src/pcryptohash-md5.c b/3rdparty/plibsys/src/pcryptohash-md5.c
new file mode 100644
index 0000000..ee09033
--- /dev/null
+++ b/3rdparty/plibsys/src/pcryptohash-md5.c
@@ -0,0 +1,273 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "pcryptohash-md5.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+struct PHashMD5_ {
+	union buf_ {
+		puchar	buf[64];
+		puint32	buf_w[16];
+	} buf;
+	puint32		hash[4];
+
+	puint32		len_high;
+	puint32		len_low;
+};
+
+static const puchar pp_crypto_hash_md5_pad[64] = {
+	0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static void pp_crypto_hash_md5_swap_bytes (puint32 *data, puint words);
+static void pp_crypto_hash_md5_process (PHashMD5 *ctx, const puint32 data[16]);
+
+#define P_MD5_ROTL(val, shift) ((val) << (shift) |  (val) >> (32 - (shift)))
+
+#define P_MD5_F(x, y, z) (z ^ (x & (y ^ z)))
+#define P_MD5_G(x, y, z) P_MD5_F (z, x, y)
+#define P_MD5_H(x, y, z) (x ^ y ^ z)
+#define P_MD5_I(x, y, z) (y ^ (x | (~z)))
+
+#define P_MD5_ROUND_0(a, b, c, d, k, i, s)				\
+	a += P_MD5_F (b, c, d) + data[k] + i, a = P_MD5_ROTL (a, s) + b
+
+#define P_MD5_ROUND_1(a, b, c, d, k, i, s)				\
+	a += P_MD5_G (b, c, d) + data[k] + i, a = P_MD5_ROTL (a, s) + b
+
+
+#define P_MD5_ROUND_2(a, b, c, d, k, i, s)				\
+	a += P_MD5_H (b, c, d) + data[k] + i, a = P_MD5_ROTL (a, s) + b
+
+#define P_MD5_ROUND_3(a, b, c, d, k, i, s)				\
+	a += P_MD5_I (b, c, d) + data[k] + i, a = P_MD5_ROTL (a, s) + b
+
+static void
+pp_crypto_hash_md5_swap_bytes (puint32	*data,
+			       puint	words)
+{
+#ifndef PLIBSYS_IS_BIGENDIAN
+	P_UNUSED (data);
+	P_UNUSED (words);
+#else
+	while (words-- > 0) {
+		*data = PUINT32_TO_LE (*data);
+		++data;
+	}
+#endif
+}
+
+static void
+pp_crypto_hash_md5_process (PHashMD5		*ctx,
+			    const puint32	data[16])
+{
+	puint32	A, B, C, D;
+
+	A = ctx->hash[0];
+	B = ctx->hash[1];
+	C = ctx->hash[2];
+	D = ctx->hash[3];
+
+	P_MD5_ROUND_0 (A, B, C, D, 0,  0xD76AA478,  7);
+	P_MD5_ROUND_0 (D, A, B, C, 1,  0xE8C7B756, 12);
+	P_MD5_ROUND_0 (C, D, A, B, 2,  0x242070DB, 17);
+	P_MD5_ROUND_0 (B, C, D, A, 3,  0xC1BDCEEE, 22);
+	P_MD5_ROUND_0 (A, B, C, D, 4,  0xF57C0FAF,  7);
+	P_MD5_ROUND_0 (D, A, B, C, 5,  0x4787C62A, 12);
+	P_MD5_ROUND_0 (C, D, A, B, 6,  0xA8304613, 17);
+	P_MD5_ROUND_0 (B, C, D, A, 7,  0xFD469501, 22);
+	P_MD5_ROUND_0 (A, B, C, D, 8,  0x698098D8,  7);
+	P_MD5_ROUND_0 (D, A, B, C, 9,  0x8B44F7AF, 12);
+	P_MD5_ROUND_0 (C, D, A, B, 10, 0xFFFF5BB1, 17);
+	P_MD5_ROUND_0 (B, C, D, A, 11, 0x895CD7BE, 22);
+	P_MD5_ROUND_0 (A, B, C, D, 12, 0x6B901122,  7);
+	P_MD5_ROUND_0 (D, A, B, C, 13, 0xFD987193, 12);
+	P_MD5_ROUND_0 (C, D, A, B, 14, 0xA679438E, 17);
+	P_MD5_ROUND_0 (B, C, D, A, 15, 0x49B40821, 22);
+
+	P_MD5_ROUND_1 (A, B, C, D, 1,  0xF61E2562,  5);
+	P_MD5_ROUND_1 (D, A, B, C, 6,  0xC040B340,  9);
+	P_MD5_ROUND_1 (C, D, A, B, 11, 0x265E5A51, 14);
+	P_MD5_ROUND_1 (B, C, D, A, 0,  0xE9B6C7AA, 20);
+	P_MD5_ROUND_1 (A, B, C, D, 5,  0xD62F105D,  5);
+	P_MD5_ROUND_1 (D, A, B, C, 10, 0x02441453,  9);
+	P_MD5_ROUND_1 (C, D, A, B, 15, 0xD8A1E681, 14);
+	P_MD5_ROUND_1 (B, C, D, A, 4,  0xE7D3FBC8, 20);
+	P_MD5_ROUND_1 (A, B, C, D, 9,  0x21E1CDE6,  5);
+	P_MD5_ROUND_1 (D, A, B, C, 14, 0xC33707D6,  9);
+	P_MD5_ROUND_1 (C, D, A, B, 3,  0xF4D50D87, 14);
+	P_MD5_ROUND_1 (B, C, D, A, 8,  0x455A14ED, 20);
+	P_MD5_ROUND_1 (A, B, C, D, 13, 0xA9E3E905,  5);
+	P_MD5_ROUND_1 (D, A, B, C, 2,  0xFCEFA3F8,  9);
+	P_MD5_ROUND_1 (C, D, A, B, 7,  0x676F02D9, 14);
+	P_MD5_ROUND_1 (B, C, D, A, 12, 0x8D2A4C8A, 20);
+
+	P_MD5_ROUND_2 (A, B, C, D, 5,  0xFFFA3942,  4);
+	P_MD5_ROUND_2 (D, A, B, C, 8,  0x8771F681, 11);
+	P_MD5_ROUND_2 (C, D, A, B, 11, 0x6D9D6122, 16);
+	P_MD5_ROUND_2 (B, C, D, A, 14, 0xFDE5380C, 23);
+	P_MD5_ROUND_2 (A, B, C, D, 1,  0xA4BEEA44,  4);
+	P_MD5_ROUND_2 (D, A, B, C, 4,  0x4BDECFA9, 11);
+	P_MD5_ROUND_2 (C, D, A, B, 7,  0xF6BB4B60, 16);
+	P_MD5_ROUND_2 (B, C, D, A, 10, 0xBEBFBC70, 23);
+	P_MD5_ROUND_2 (A, B, C, D, 13, 0x289B7EC6,  4);
+	P_MD5_ROUND_2 (D, A, B, C, 0,  0xEAA127FA, 11);
+	P_MD5_ROUND_2 (C, D, A, B, 3,  0xD4EF3085, 16);
+	P_MD5_ROUND_2 (B, C, D, A, 6,  0x04881D05, 23);
+	P_MD5_ROUND_2 (A, B, C, D, 9,  0xD9D4D039,  4);
+	P_MD5_ROUND_2 (D, A, B, C, 12, 0xE6DB99E5, 11);
+	P_MD5_ROUND_2 (C, D, A, B, 15, 0x1FA27CF8, 16);
+	P_MD5_ROUND_2 (B, C, D, A, 2,  0xC4AC5665, 23);
+
+	P_MD5_ROUND_3 (A, B, C, D, 0,  0xF4292244,  6);
+	P_MD5_ROUND_3 (D, A, B, C, 7,  0x432AFF97, 10);
+	P_MD5_ROUND_3 (C, D, A, B, 14, 0xAB9423A7, 15);
+	P_MD5_ROUND_3 (B, C, D, A, 5,  0xFC93A039, 21);
+	P_MD5_ROUND_3 (A, B, C, D, 12, 0x655B59C3,  6);
+	P_MD5_ROUND_3 (D, A, B, C, 3,  0x8F0CCC92, 10);
+	P_MD5_ROUND_3 (C, D, A, B, 10, 0xFFEFF47D, 15);
+	P_MD5_ROUND_3 (B, C, D, A, 1,  0x85845DD1, 21);
+	P_MD5_ROUND_3 (A, B, C, D, 8,  0x6FA87E4F,  6);
+	P_MD5_ROUND_3 (D, A, B, C, 15, 0xFE2CE6E0, 10);
+	P_MD5_ROUND_3 (C, D, A, B, 6,  0xA3014314, 15);
+	P_MD5_ROUND_3 (B, C, D, A, 13, 0x4E0811A1, 21);
+	P_MD5_ROUND_3 (A, B, C, D, 4,  0xF7537E82,  6);
+	P_MD5_ROUND_3 (D, A, B, C, 11, 0xBD3AF235, 10);
+	P_MD5_ROUND_3 (C, D, A, B, 2,  0x2AD7D2BB, 15);
+	P_MD5_ROUND_3 (B, C, D, A, 9,  0xEB86D391, 21);
+
+	ctx->hash[0] += A;
+	ctx->hash[1] += B;
+	ctx->hash[2] += C;
+	ctx->hash[3] += D;
+}
+
+void
+p_crypto_hash_md5_reset (PHashMD5 *ctx)
+{
+	memset (ctx->buf.buf, 0, 64);
+
+	ctx->len_low = 0;
+	ctx->len_high = 0;
+
+	ctx->hash[0] = 0x67452301;
+	ctx->hash[1] = 0xEFCDAB89;
+	ctx->hash[2] = 0x98BADCFE;
+	ctx->hash[3] = 0x10325476;
+}
+
+PHashMD5 *
+p_crypto_hash_md5_new (void)
+{
+	PHashMD5 *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PHashMD5))) == NULL))
+		return NULL;
+
+	p_crypto_hash_md5_reset (ret);
+
+	return ret;
+}
+
+void
+p_crypto_hash_md5_update (PHashMD5	*ctx,
+			  const puchar	*data,
+			  psize		len)
+{
+	puint32	left, to_fill;
+
+	left = ctx->len_low & 0x3F;
+	to_fill = 64 - left;
+
+	ctx->len_low += (puint32) len;
+
+	if (ctx->len_low < (puint32) len)
+		++ctx->len_high;
+
+	if (left && (puint32) len >= to_fill) {
+		memcpy (ctx->buf.buf + left, data, to_fill);
+		pp_crypto_hash_md5_swap_bytes (ctx->buf.buf_w, 16);
+		pp_crypto_hash_md5_process (ctx, ctx->buf.buf_w);
+
+		data += to_fill;
+		len -= to_fill;
+		left = 0;
+	}
+
+	while (len >= 64) {
+		memcpy (ctx->buf.buf, data, 64);
+		pp_crypto_hash_md5_swap_bytes (ctx->buf.buf_w, 16);
+		pp_crypto_hash_md5_process (ctx, ctx->buf.buf_w);
+
+		data += 64;
+		len -= 64;
+	}
+
+	if (len > 0)
+		memcpy (ctx->buf.buf + left, data, len);
+}
+
+void
+p_crypto_hash_md5_finish (PHashMD5 *ctx)
+{
+	puint32	high, low;
+	pint	left, last;
+
+	left = ctx->len_low & 0x3F;
+	last = (left < 56) ? (56 - left) : (120 - left);
+
+	low = ctx->len_low << 3;
+	high = ctx->len_high << 3
+	     | ctx->len_low >> 29;
+
+	if (last > 0)
+		p_crypto_hash_md5_update (ctx, pp_crypto_hash_md5_pad, (psize) last);
+
+	ctx->buf.buf_w[14] = low;
+	ctx->buf.buf_w[15] = high;
+
+	pp_crypto_hash_md5_swap_bytes (ctx->buf.buf_w, 14);
+	pp_crypto_hash_md5_process (ctx, ctx->buf.buf_w);
+
+	pp_crypto_hash_md5_swap_bytes (ctx->hash, 4);
+}
+
+const puchar *
+p_crypto_hash_md5_digest (PHashMD5 *ctx)
+{
+	return (const puchar *) ctx->hash;
+}
+
+void
+p_crypto_hash_md5_free (PHashMD5 *ctx)
+{
+	p_free (ctx);
+}
diff --git a/3rdparty/plibsys/src/pcryptohash-md5.h b/3rdparty/plibsys/src/pcryptohash-md5.h
new file mode 100644
index 0000000..142eee0
--- /dev/null
+++ b/3rdparty/plibsys/src/pcryptohash-md5.h
@@ -0,0 +1,51 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/* MD5 interface implementation for #PCryptoHash */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PCRYPTOHASHMD5_H
+#define PLIBSYS_HEADER_PCRYPTOHASHMD5_H
+
+#include "ptypes.h"
+#include "pmacros.h"
+
+P_BEGIN_DECLS
+
+typedef struct PHashMD5_ PHashMD5;
+
+PHashMD5 *	p_crypto_hash_md5_new		(void);
+void		p_crypto_hash_md5_update	(PHashMD5 *ctx, const puchar *data, psize len);
+void		p_crypto_hash_md5_finish	(PHashMD5 *ctx);
+const puchar *	p_crypto_hash_md5_digest	(PHashMD5 *ctx);
+void		p_crypto_hash_md5_reset		(PHashMD5 *ctx);
+void		p_crypto_hash_md5_free		(PHashMD5 *ctx);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PCRYPTOHASHMD5_H */
diff --git a/3rdparty/plibsys/src/pcryptohash-sha1.c b/3rdparty/plibsys/src/pcryptohash-sha1.c
new file mode 100644
index 0000000..62e826c
--- /dev/null
+++ b/3rdparty/plibsys/src/pcryptohash-sha1.c
@@ -0,0 +1,321 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 <string.h>
+#include <stdlib.h>
+
+#include "pmem.h"
+#include "pcryptohash-sha1.h"
+
+struct PHashSHA1_ {
+	union buf_ {
+		puchar	buf[64];
+		puint32	buf_w[16];
+	} buf;
+	puint32		hash[5];
+
+	puint32		len_high;
+	puint32		len_low;
+};
+
+static const puchar pp_crypto_hash_sha1_pad[64] = {
+	0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static void pp_crypto_hash_sha1_swap_bytes (puint32 *data, puint words);
+static void pp_crypto_hash_sha1_process (PHashSHA1 *ctx, const puint32 data[16]);
+
+#define P_SHA1_ROTL(val, shift) ((val) << (shift) |  (val) >> (32 - (shift)))
+
+#define P_SHA1_F1(x, y, z) ((x & y) | ((~x) & z))
+#define P_SHA1_F2(x, y, z) (x ^ y ^ z)
+#define P_SHA1_F3(x, y, z) ((x & y) | (x & z) | (y & z))
+
+#define P_SHA1_W(W, i) 					\
+(							\
+	(W)[i & 0x0F] = P_SHA1_ROTL (			\
+				(W)[(i - 3)  & 0x0F]	\
+			      ^ (W)[(i - 8)  & 0x0F]	\
+			      ^ (W)[(i - 14) & 0x0F]	\
+			      ^ (W)[(i - 16) & 0x0F],	\
+			      1)			\
+)
+
+#define P_SHA1_ROUND_0(a, b, c, d, e, w)		\
+{							\
+	e += P_SHA1_ROTL (a, 5) + P_SHA1_F1 (b, c, d)	\
+	   + 0x5A827999 + w;				\
+	b = P_SHA1_ROTL (b, 30);			\
+}
+
+#define P_SHA1_ROUND_1(a, b, c, d, e, w)		\
+{							\
+	e += P_SHA1_ROTL (a, 5) + P_SHA1_F2 (b, c, d)	\
+	   + 0x6ED9EBA1 + w;				\
+	b = P_SHA1_ROTL (b, 30);			\
+}
+
+#define P_SHA1_ROUND_2(a, b, c, d, e, w)		\
+{							\
+	e += P_SHA1_ROTL (a, 5) + P_SHA1_F3 (b, c, d)	\
+	   + 0x8F1BBCDC + w;				\
+	b = P_SHA1_ROTL (b, 30);			\
+}
+
+#define P_SHA1_ROUND_3(a, b, c, d, e, w)		\
+{							\
+	e += P_SHA1_ROTL (a, 5) + P_SHA1_F2 (b, c, d)	\
+	   + 0xCA62C1D6 + w;				\
+	b = P_SHA1_ROTL (b, 30);			\
+}
+
+static void
+pp_crypto_hash_sha1_swap_bytes (puint32	*data,
+				puint	words)
+{
+#ifdef PLIBSYS_IS_BIGENDIAN
+	P_UNUSED (data);
+	P_UNUSED (words);
+#else
+	while (words-- > 0) {
+		*data = PUINT32_TO_BE (*data);
+		++data;
+	}
+#endif
+}
+
+static void
+pp_crypto_hash_sha1_process (PHashSHA1		*ctx,
+			     const puint32	data[16])
+{
+	puint32	W[16], A, B, C, D, E;
+
+	if (P_UNLIKELY (ctx == NULL))
+		return;
+
+	memcpy (W, data, 64);
+
+	A = ctx->hash[0];
+	B = ctx->hash[1];
+	C = ctx->hash[2];
+	D = ctx->hash[3];
+	E = ctx->hash[4];
+
+	P_SHA1_ROUND_0 (A, B, C, D, E, W[0]);
+	P_SHA1_ROUND_0 (E, A, B, C, D, W[1]);
+	P_SHA1_ROUND_0 (D, E, A, B, C, W[2]);
+	P_SHA1_ROUND_0 (C, D, E, A, B, W[3]);
+	P_SHA1_ROUND_0 (B, C, D, E, A, W[4]);
+	P_SHA1_ROUND_0 (A, B, C, D, E, W[5]);
+	P_SHA1_ROUND_0 (E, A, B, C, D, W[6]);
+	P_SHA1_ROUND_0 (D, E, A, B, C, W[7]);
+	P_SHA1_ROUND_0 (C, D, E, A, B, W[8]);
+	P_SHA1_ROUND_0 (B, C, D, E, A, W[9]);
+	P_SHA1_ROUND_0 (A, B, C, D, E, W[10]);
+	P_SHA1_ROUND_0 (E, A, B, C, D, W[11]);
+	P_SHA1_ROUND_0 (D, E, A, B, C, W[12]);
+	P_SHA1_ROUND_0 (C, D, E, A, B, W[13]);
+	P_SHA1_ROUND_0 (B, C, D, E, A, W[14]);
+	P_SHA1_ROUND_0 (A, B, C, D, E, W[15]);
+	P_SHA1_ROUND_0 (E, A, B, C, D, P_SHA1_W (W, 16));
+	P_SHA1_ROUND_0 (D, E, A, B, C, P_SHA1_W (W, 17));
+	P_SHA1_ROUND_0 (C, D, E, A, B, P_SHA1_W (W, 18));
+	P_SHA1_ROUND_0 (B, C, D, E, A, P_SHA1_W (W, 19));
+
+	P_SHA1_ROUND_1 (A, B, C, D, E, P_SHA1_W (W, 20));
+	P_SHA1_ROUND_1 (E, A, B, C, D, P_SHA1_W (W, 21));
+	P_SHA1_ROUND_1 (D, E, A, B, C, P_SHA1_W (W, 22));
+	P_SHA1_ROUND_1 (C, D, E, A, B, P_SHA1_W (W, 23));
+	P_SHA1_ROUND_1 (B, C, D, E, A, P_SHA1_W (W, 24));
+	P_SHA1_ROUND_1 (A, B, C, D, E, P_SHA1_W (W, 25));
+	P_SHA1_ROUND_1 (E, A, B, C, D, P_SHA1_W (W, 26));
+	P_SHA1_ROUND_1 (D, E, A, B, C, P_SHA1_W (W, 27));
+	P_SHA1_ROUND_1 (C, D, E, A, B, P_SHA1_W (W, 28));
+	P_SHA1_ROUND_1 (B, C, D, E, A, P_SHA1_W (W, 29));
+	P_SHA1_ROUND_1 (A, B, C, D, E, P_SHA1_W (W, 30));
+	P_SHA1_ROUND_1 (E, A, B, C, D, P_SHA1_W (W, 31));
+	P_SHA1_ROUND_1 (D, E, A, B, C, P_SHA1_W (W, 32));
+	P_SHA1_ROUND_1 (C, D, E, A, B, P_SHA1_W (W, 33));
+	P_SHA1_ROUND_1 (B, C, D, E, A, P_SHA1_W (W, 34));
+	P_SHA1_ROUND_1 (A, B, C, D, E, P_SHA1_W (W, 35));
+	P_SHA1_ROUND_1 (E, A, B, C, D, P_SHA1_W (W, 36));
+	P_SHA1_ROUND_1 (D, E, A, B, C, P_SHA1_W (W, 37));
+	P_SHA1_ROUND_1 (C, D, E, A, B, P_SHA1_W (W, 38));
+	P_SHA1_ROUND_1 (B, C, D, E, A, P_SHA1_W (W, 39));
+
+	P_SHA1_ROUND_2 (A, B, C, D, E, P_SHA1_W (W, 40));
+	P_SHA1_ROUND_2 (E, A, B, C, D, P_SHA1_W (W, 41));
+	P_SHA1_ROUND_2 (D, E, A, B, C, P_SHA1_W (W, 42));
+	P_SHA1_ROUND_2 (C, D, E, A, B, P_SHA1_W (W, 43));
+	P_SHA1_ROUND_2 (B, C, D, E, A, P_SHA1_W (W, 44));
+	P_SHA1_ROUND_2 (A, B, C, D, E, P_SHA1_W (W, 45));
+	P_SHA1_ROUND_2 (E, A, B, C, D, P_SHA1_W (W, 46));
+	P_SHA1_ROUND_2 (D, E, A, B, C, P_SHA1_W (W, 47));
+	P_SHA1_ROUND_2 (C, D, E, A, B, P_SHA1_W (W, 48));
+	P_SHA1_ROUND_2 (B, C, D, E, A, P_SHA1_W (W, 49));
+	P_SHA1_ROUND_2 (A, B, C, D, E, P_SHA1_W (W, 50));
+	P_SHA1_ROUND_2 (E, A, B, C, D, P_SHA1_W (W, 51));
+	P_SHA1_ROUND_2 (D, E, A, B, C, P_SHA1_W (W, 52));
+	P_SHA1_ROUND_2 (C, D, E, A, B, P_SHA1_W (W, 53));
+	P_SHA1_ROUND_2 (B, C, D, E, A, P_SHA1_W (W, 54));
+	P_SHA1_ROUND_2 (A, B, C, D, E, P_SHA1_W (W, 55));
+	P_SHA1_ROUND_2 (E, A, B, C, D, P_SHA1_W (W, 56));
+	P_SHA1_ROUND_2 (D, E, A, B, C, P_SHA1_W (W, 57));
+	P_SHA1_ROUND_2 (C, D, E, A, B, P_SHA1_W (W, 58));
+	P_SHA1_ROUND_2 (B, C, D, E, A, P_SHA1_W (W, 59));
+
+	P_SHA1_ROUND_3 (A, B, C, D, E, P_SHA1_W (W, 60));
+	P_SHA1_ROUND_3 (E, A, B, C, D, P_SHA1_W (W, 61));
+	P_SHA1_ROUND_3 (D, E, A, B, C, P_SHA1_W (W, 62));
+	P_SHA1_ROUND_3 (C, D, E, A, B, P_SHA1_W (W, 63));
+	P_SHA1_ROUND_3 (B, C, D, E, A, P_SHA1_W (W, 64));
+	P_SHA1_ROUND_3 (A, B, C, D, E, P_SHA1_W (W, 65));
+	P_SHA1_ROUND_3 (E, A, B, C, D, P_SHA1_W (W, 66));
+	P_SHA1_ROUND_3 (D, E, A, B, C, P_SHA1_W (W, 67));
+	P_SHA1_ROUND_3 (C, D, E, A, B, P_SHA1_W (W, 68));
+	P_SHA1_ROUND_3 (B, C, D, E, A, P_SHA1_W (W, 69));
+	P_SHA1_ROUND_3 (A, B, C, D, E, P_SHA1_W (W, 70));
+	P_SHA1_ROUND_3 (E, A, B, C, D, P_SHA1_W (W, 71));
+	P_SHA1_ROUND_3 (D, E, A, B, C, P_SHA1_W (W, 72));
+	P_SHA1_ROUND_3 (C, D, E, A, B, P_SHA1_W (W, 73));
+	P_SHA1_ROUND_3 (B, C, D, E, A, P_SHA1_W (W, 74));
+	P_SHA1_ROUND_3 (A, B, C, D, E, P_SHA1_W (W, 75));
+	P_SHA1_ROUND_3 (E, A, B, C, D, P_SHA1_W (W, 76));
+	P_SHA1_ROUND_3 (D, E, A, B, C, P_SHA1_W (W, 77));
+	P_SHA1_ROUND_3 (C, D, E, A, B, P_SHA1_W (W, 78));
+	P_SHA1_ROUND_3 (B, C, D, E, A, P_SHA1_W (W, 79));
+
+	ctx->hash[0] += A;
+	ctx->hash[1] += B;
+	ctx->hash[2] += C;
+	ctx->hash[3] += D;
+	ctx->hash[4] += E;
+}
+
+void
+p_crypto_hash_sha1_reset (PHashSHA1 *ctx)
+{
+	memset (ctx->buf.buf, 0, 64);
+
+	ctx->len_low = 0;
+	ctx->len_high = 0;
+
+	ctx->hash[0] = 0x67452301;
+	ctx->hash[1] = 0xEFCDAB89;
+	ctx->hash[2] = 0x98BADCFE;
+	ctx->hash[3] = 0x10325476;
+	ctx->hash[4] = 0xC3D2E1F0;
+}
+
+PHashSHA1 *
+p_crypto_hash_sha1_new (void)
+{
+	PHashSHA1 *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PHashSHA1))) == NULL))
+		return NULL;
+
+	p_crypto_hash_sha1_reset (ret);
+
+	return ret;
+}
+
+void
+p_crypto_hash_sha1_update (PHashSHA1	*ctx,
+			   const puchar	*data,
+			   psize	len)
+{
+	puint32	left, to_fill;
+
+	left = ctx->len_low & 0x3F;
+	to_fill = 64 - left;
+
+	ctx->len_low += (puint32) len;
+
+	if (ctx->len_low < (puint32) len)
+		++ctx->len_high;
+
+	if (left && (puint32) len >= to_fill) {
+		memcpy (ctx->buf.buf + left, data, to_fill);
+		pp_crypto_hash_sha1_swap_bytes (ctx->buf.buf_w, 16);
+		pp_crypto_hash_sha1_process (ctx, ctx->buf.buf_w);
+
+		data += to_fill;
+		len -= to_fill;
+		left = 0;
+	}
+
+	while (len >= 64) {
+		memcpy (ctx->buf.buf, data, 64);
+		pp_crypto_hash_sha1_swap_bytes (ctx->buf.buf_w, 16);
+		pp_crypto_hash_sha1_process (ctx, ctx->buf.buf_w);
+
+		data += 64;
+		len -= 64;
+	}
+
+	if (len > 0)
+		memcpy (ctx->buf.buf + left, data, len);
+}
+
+void
+p_crypto_hash_sha1_finish (PHashSHA1 *ctx)
+{
+	puint32	high, low;
+	pint	left, last;
+
+	left = ctx->len_low & 0x3F;
+	last = (left < 56) ? (56 - left) : (120 - left);
+
+	low = ctx->len_low << 3;
+	high = ctx->len_high << 3
+	     | ctx->len_low >> 29;
+
+	if (last > 0)
+		p_crypto_hash_sha1_update (ctx, pp_crypto_hash_sha1_pad, (psize) last);
+
+	ctx->buf.buf_w[14] = high;
+	ctx->buf.buf_w[15] = low;
+
+	pp_crypto_hash_sha1_swap_bytes (ctx->buf.buf_w, 14);
+	pp_crypto_hash_sha1_process (ctx, ctx->buf.buf_w);
+
+	pp_crypto_hash_sha1_swap_bytes (ctx->hash, 5);
+}
+
+const puchar *
+p_crypto_hash_sha1_digest (PHashSHA1 *ctx)
+{
+	return (const puchar *) ctx->hash;
+}
+
+void
+p_crypto_hash_sha1_free (PHashSHA1 *ctx)
+{
+	p_free (ctx);
+}
diff --git a/3rdparty/plibsys/src/pcryptohash-sha1.h b/3rdparty/plibsys/src/pcryptohash-sha1.h
new file mode 100644
index 0000000..cecbc0d
--- /dev/null
+++ b/3rdparty/plibsys/src/pcryptohash-sha1.h
@@ -0,0 +1,51 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/* SHA1 interface implementation for #PCryptoHash */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PCRYPTOHASHSHA1_H
+#define PLIBSYS_HEADER_PCRYPTOHASHSHA1_H
+
+#include "ptypes.h"
+#include "pmacros.h"
+
+P_BEGIN_DECLS
+
+typedef struct PHashSHA1_ PHashSHA1;
+
+PHashSHA1 *	p_crypto_hash_sha1_new		(void);
+void		p_crypto_hash_sha1_update	(PHashSHA1 *ctx, const puchar *data, psize len);
+void		p_crypto_hash_sha1_finish	(PHashSHA1 *ctx);
+const puchar *	p_crypto_hash_sha1_digest	(PHashSHA1 *ctx);
+void		p_crypto_hash_sha1_reset	(PHashSHA1 *ctx);
+void		p_crypto_hash_sha1_free		(PHashSHA1 *ctx);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PCRYPTOHASHSHA1_H */
diff --git a/3rdparty/plibsys/src/pcryptohash-sha2-256.c b/3rdparty/plibsys/src/pcryptohash-sha2-256.c
new file mode 100644
index 0000000..29bb0ba
--- /dev/null
+++ b/3rdparty/plibsys/src/pcryptohash-sha2-256.c
@@ -0,0 +1,286 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 <string.h>
+#include <stdlib.h>
+
+#include "pmem.h"
+#include "pcryptohash-sha2-256.h"
+
+struct PHashSHA2_256_ {
+	union buf_ {
+		puchar	buf[64];
+		puint32	buf_w[16];
+	} buf;
+	puint32		hash[8];
+
+	puint32		len_high;
+	puint32		len_low;
+
+	pboolean	is224;
+};
+
+static const puchar pp_crypto_hash_sha2_256_pad[64] = {
+	0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const puint32 pp_crypto_hash_sha2_256_K[] = {
+	0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5,
+	0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,
+	0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,
+	0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,
+	0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC,
+	0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
+	0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7,
+	0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,
+	0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,
+	0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,
+	0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3,
+	0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
+	0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5,
+	0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,
+	0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,
+	0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2
+};
+
+static void pp_crypto_hash_sha2_256_swap_bytes (puint32 *data, puint words);
+static void pp_crypto_hash_sha2_256_process (PHashSHA2_256 *ctx, const puint32 data[16]);
+static PHashSHA2_256 * pp_crypto_hash_sha2_256_new_internal (pboolean is224);
+
+#define P_SHA2_256_SHR(val, shift) (((val) & 0xFFFFFFFF) >> (shift))
+#define P_SHA2_256_ROTR(val, shift) (P_SHA2_256_SHR(val, shift) | ((val) << (32 - (shift))))
+
+#define P_SHA2_256_S0(x) (P_SHA2_256_ROTR (x, 7)  ^ P_SHA2_256_ROTR (x, 18) ^ P_SHA2_256_SHR  (x, 3))
+#define P_SHA2_256_S1(x) (P_SHA2_256_ROTR (x, 17) ^ P_SHA2_256_ROTR (x, 19) ^ P_SHA2_256_SHR  (x, 10))
+#define P_SHA2_256_S2(x) (P_SHA2_256_ROTR (x, 2)  ^ P_SHA2_256_ROTR (x, 13) ^ P_SHA2_256_ROTR (x, 22))
+#define P_SHA2_256_S3(x) (P_SHA2_256_ROTR (x, 6)  ^ P_SHA2_256_ROTR (x, 11) ^ P_SHA2_256_ROTR (x, 25))
+
+#define P_SHA2_256_F0(x, y, z) ((x & y) | (z & (x | y)))
+#define P_SHA2_256_F1(x, y, z) (z ^ (x & (y ^ z)))
+
+#define P_SHA2_256_R(t)								\
+(										\
+	W[t] = P_SHA2_256_S1 (W[t -  2]) + W[t -  7] +				\
+	       P_SHA2_256_S0 (W[t - 15]) + W[t - 16]				\
+)
+
+#define P_SHA2_256_P(a, b, c, d, e, f, g, h, x, K)				\
+{										\
+	tmp_sum1 = h + P_SHA2_256_S3 (e) + P_SHA2_256_F1 (e, f, g) + K + x;	\
+	tmp_sum2 = P_SHA2_256_S2 (a) + P_SHA2_256_F0 (a, b, c);			\
+	d += tmp_sum1;								\
+	h = tmp_sum1 + tmp_sum2;						\
+}
+
+static void
+pp_crypto_hash_sha2_256_swap_bytes (puint32	*data,
+				    puint	words)
+{
+#ifdef PLIBSYS_IS_BIGENDIAN
+	P_UNUSED (data);
+	P_UNUSED (words);
+#else
+	while (words-- > 0) {
+		*data = PUINT32_TO_BE (*data);
+		++data;
+	}
+#endif
+}
+
+static void
+pp_crypto_hash_sha2_256_process (PHashSHA2_256	*ctx,
+				 const puint32	data[16])
+{
+	puint32	tmp_sum1, tmp_sum2;
+	puint32 W[64];
+	puint32	A[8];
+	puint	i;
+
+	for (i = 0; i < 8; i++)
+		A[i] = ctx->hash[i];
+
+	memcpy (W, data, 64);
+
+	for (i = 0; i < 16; i += 8) {
+		P_SHA2_256_P (A[0], A[1], A[2], A[3], A[4], A[5], A[6], A[7], W[i + 0], pp_crypto_hash_sha2_256_K[i + 0]);
+		P_SHA2_256_P (A[7], A[0], A[1], A[2], A[3], A[4], A[5], A[6], W[i + 1], pp_crypto_hash_sha2_256_K[i + 1]);
+		P_SHA2_256_P (A[6], A[7], A[0], A[1], A[2], A[3], A[4], A[5], W[i + 2], pp_crypto_hash_sha2_256_K[i + 2]);
+		P_SHA2_256_P (A[5], A[6], A[7], A[0], A[1], A[2], A[3], A[4], W[i + 3], pp_crypto_hash_sha2_256_K[i + 3]);
+		P_SHA2_256_P (A[4], A[5], A[6], A[7], A[0], A[1], A[2], A[3], W[i + 4], pp_crypto_hash_sha2_256_K[i + 4]);
+		P_SHA2_256_P (A[3], A[4], A[5], A[6], A[7], A[0], A[1], A[2], W[i + 5], pp_crypto_hash_sha2_256_K[i + 5]);
+		P_SHA2_256_P (A[2], A[3], A[4], A[5], A[6], A[7], A[0], A[1], W[i + 6], pp_crypto_hash_sha2_256_K[i + 6]);
+		P_SHA2_256_P (A[1], A[2], A[3], A[4], A[5], A[6], A[7], A[0], W[i + 7], pp_crypto_hash_sha2_256_K[i + 7]);
+	}
+
+	for (i = 16; i < 64; i += 8) {
+		P_SHA2_256_P (A[0], A[1], A[2], A[3], A[4], A[5], A[6], A[7], P_SHA2_256_R (i + 0), pp_crypto_hash_sha2_256_K[i + 0]);
+		P_SHA2_256_P (A[7], A[0], A[1], A[2], A[3], A[4], A[5], A[6], P_SHA2_256_R (i + 1), pp_crypto_hash_sha2_256_K[i + 1]);
+		P_SHA2_256_P (A[6], A[7], A[0], A[1], A[2], A[3], A[4], A[5], P_SHA2_256_R (i + 2), pp_crypto_hash_sha2_256_K[i + 2]);
+		P_SHA2_256_P (A[5], A[6], A[7], A[0], A[1], A[2], A[3], A[4], P_SHA2_256_R (i + 3), pp_crypto_hash_sha2_256_K[i + 3]);
+		P_SHA2_256_P (A[4], A[5], A[6], A[7], A[0], A[1], A[2], A[3], P_SHA2_256_R (i + 4), pp_crypto_hash_sha2_256_K[i + 4]);
+		P_SHA2_256_P (A[3], A[4], A[5], A[6], A[7], A[0], A[1], A[2], P_SHA2_256_R (i + 5), pp_crypto_hash_sha2_256_K[i + 5]);
+		P_SHA2_256_P (A[2], A[3], A[4], A[5], A[6], A[7], A[0], A[1], P_SHA2_256_R (i + 6), pp_crypto_hash_sha2_256_K[i + 6]);
+		P_SHA2_256_P (A[1], A[2], A[3], A[4], A[5], A[6], A[7], A[0], P_SHA2_256_R (i + 7), pp_crypto_hash_sha2_256_K[i + 7]);
+	}
+
+	for (i = 0; i < 8; i++)
+		ctx->hash[i] += A[i];
+}
+
+static PHashSHA2_256 *
+pp_crypto_hash_sha2_256_new_internal (pboolean is224)
+{
+	PHashSHA2_256 *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PHashSHA2_256))) == NULL))
+		return NULL;
+
+	ret->is224 = is224;
+
+	p_crypto_hash_sha2_256_reset (ret);
+
+	return ret;
+}
+
+void
+p_crypto_hash_sha2_256_reset (PHashSHA2_256 *ctx)
+{
+	memset (ctx->buf.buf, 0, 64);
+
+	ctx->len_low = 0;
+	ctx->len_high = 0;
+
+	if (ctx->is224 == FALSE) {
+		/* SHA2-256 */
+		ctx->hash[0] = 0x6A09E667;
+		ctx->hash[1] = 0xBB67AE85;
+		ctx->hash[2] = 0x3C6EF372;
+		ctx->hash[3] = 0xA54FF53A;
+		ctx->hash[4] = 0x510E527F;
+		ctx->hash[5] = 0x9B05688C;
+		ctx->hash[6] = 0x1F83D9AB;
+		ctx->hash[7] = 0x5BE0CD19;
+	} else {
+		/* SHA2-224 */
+		ctx->hash[0] = 0xC1059ED8;
+		ctx->hash[1] = 0x367CD507;
+		ctx->hash[2] = 0x3070DD17;
+		ctx->hash[3] = 0xF70E5939;
+		ctx->hash[4] = 0xFFC00B31;
+		ctx->hash[5] = 0x68581511;
+		ctx->hash[6] = 0x64F98FA7;
+		ctx->hash[7] = 0xBEFA4FA4;
+	}
+}
+
+PHashSHA2_256 *
+p_crypto_hash_sha2_256_new (void)
+{
+	return pp_crypto_hash_sha2_256_new_internal (FALSE);
+}
+
+PHashSHA2_256 *
+p_crypto_hash_sha2_224_new (void)
+{
+	return pp_crypto_hash_sha2_256_new_internal (TRUE);
+}
+
+void
+p_crypto_hash_sha2_256_update (PHashSHA2_256	*ctx,
+			       const puchar	*data,
+			       psize		len)
+{
+	puint32	left, to_fill;
+
+	left = ctx->len_low & 0x3F;
+	to_fill = 64 - left;
+
+	ctx->len_low += (puint32) len;
+
+	if (ctx->len_low < (puint32) len)
+		++ctx->len_high;
+
+	if (left && (puint32) len >= to_fill) {
+		memcpy (ctx->buf.buf + left, data, to_fill);
+		pp_crypto_hash_sha2_256_swap_bytes (ctx->buf.buf_w, 16);
+		pp_crypto_hash_sha2_256_process (ctx, ctx->buf.buf_w);
+
+		data += to_fill;
+		len -= to_fill;
+		left = 0;
+	}
+
+	while (len >= 64) {
+		memcpy (ctx->buf.buf, data, 64);
+		pp_crypto_hash_sha2_256_swap_bytes (ctx->buf.buf_w, 16);
+		pp_crypto_hash_sha2_256_process (ctx, ctx->buf.buf_w);
+
+		data += 64;
+		len -= 64;
+	}
+
+	if (len > 0)
+		memcpy (ctx->buf.buf + left, data, len);
+}
+
+void
+p_crypto_hash_sha2_256_finish (PHashSHA2_256 *ctx)
+{
+	puint32	high, low;
+	pint	left, last;
+
+	left = ctx->len_low & 0x3F;
+	last = (left < 56) ? (56 - left) : (120 - left);
+
+	low = ctx->len_low << 3;
+	high = ctx->len_high << 3
+	     | ctx->len_low >> 29;
+
+	if (last > 0)
+		p_crypto_hash_sha2_256_update (ctx, pp_crypto_hash_sha2_256_pad, (psize) last);
+
+	ctx->buf.buf_w[14] = high;
+	ctx->buf.buf_w[15] = low;
+
+	pp_crypto_hash_sha2_256_swap_bytes (ctx->buf.buf_w, 14);
+	pp_crypto_hash_sha2_256_process (ctx, ctx->buf.buf_w);
+
+	pp_crypto_hash_sha2_256_swap_bytes (ctx->hash, ctx->is224 == FALSE ? 8 : 7);
+}
+
+const puchar *
+p_crypto_hash_sha2_256_digest (PHashSHA2_256 *ctx)
+{
+	return (const puchar *) ctx->hash;
+}
+
+void
+p_crypto_hash_sha2_256_free (PHashSHA2_256 *ctx)
+{
+	p_free (ctx);
+}
diff --git a/3rdparty/plibsys/src/pcryptohash-sha2-256.h b/3rdparty/plibsys/src/pcryptohash-sha2-256.h
new file mode 100644
index 0000000..0255011
--- /dev/null
+++ b/3rdparty/plibsys/src/pcryptohash-sha2-256.h
@@ -0,0 +1,59 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/* SHA2-256 interface implementation for #PCryptoHash */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PCRYPTOHASHSHA2_256_H
+#define PLIBSYS_HEADER_PCRYPTOHASHSHA2_256_H
+
+#include "ptypes.h"
+#include "pmacros.h"
+
+P_BEGIN_DECLS
+
+typedef struct PHashSHA2_256_ PHashSHA2_256;
+
+PHashSHA2_256 *	p_crypto_hash_sha2_256_new	(void);
+void		p_crypto_hash_sha2_256_update	(PHashSHA2_256 *ctx, const puchar *data, psize len);
+void		p_crypto_hash_sha2_256_finish	(PHashSHA2_256 *ctx);
+const puchar *	p_crypto_hash_sha2_256_digest	(PHashSHA2_256 *ctx);
+void		p_crypto_hash_sha2_256_reset	(PHashSHA2_256 *ctx);
+void		p_crypto_hash_sha2_256_free	(PHashSHA2_256 *ctx);
+
+PHashSHA2_256 *	p_crypto_hash_sha2_224_new	(void);
+
+#define p_crypto_hash_sha2_224_update p_crypto_hash_sha2_256_update
+#define p_crypto_hash_sha2_224_finish p_crypto_hash_sha2_256_finish
+#define p_crypto_hash_sha2_224_digest p_crypto_hash_sha2_256_digest
+#define p_crypto_hash_sha2_224_reset  p_crypto_hash_sha2_256_reset
+#define p_crypto_hash_sha2_224_free   p_crypto_hash_sha2_256_free
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PCRYPTOHASHSHA2_256_H */
diff --git a/3rdparty/plibsys/src/pcryptohash-sha2-512.c b/3rdparty/plibsys/src/pcryptohash-sha2-512.c
new file mode 100644
index 0000000..7864da3
--- /dev/null
+++ b/3rdparty/plibsys/src/pcryptohash-sha2-512.c
@@ -0,0 +1,300 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 <string.h>
+#include <stdlib.h>
+
+#include "pmem.h"
+#include "pcryptohash-sha2-512.h"
+
+struct PHashSHA2_512_ {
+	union buf_ {
+		puchar	buf[128];
+		puint64	buf_w[16];
+	} buf;
+	puint64		hash[8];
+
+	puint64		len_high;
+	puint64		len_low;
+
+	pboolean	is384;
+};
+
+static const puchar pp_crypto_hash_sha2_512_pad[128] = {
+	0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const puint64 pp_crypto_hash_sha2_512_K[] = {
+	0x428A2F98D728AE22ULL,  0x7137449123EF65CDULL,
+	0xB5C0FBCFEC4D3B2FULL,  0xE9B5DBA58189DBBCULL,
+	0x3956C25BF348B538ULL,  0x59F111F1B605D019ULL,
+	0x923F82A4AF194F9BULL,  0xAB1C5ED5DA6D8118ULL,
+	0xD807AA98A3030242ULL,  0x12835B0145706FBEULL,
+	0x243185BE4EE4B28CULL,  0x550C7DC3D5FFB4E2ULL,
+	0x72BE5D74F27B896FULL,  0x80DEB1FE3B1696B1ULL,
+	0x9BDC06A725C71235ULL,  0xC19BF174CF692694ULL,
+	0xE49B69C19EF14AD2ULL,  0xEFBE4786384F25E3ULL,
+	0x0FC19DC68B8CD5B5ULL,  0x240CA1CC77AC9C65ULL,
+	0x2DE92C6F592B0275ULL,  0x4A7484AA6EA6E483ULL,
+	0x5CB0A9DCBD41FBD4ULL,  0x76F988DA831153B5ULL,
+	0x983E5152EE66DFABULL,  0xA831C66D2DB43210ULL,
+	0xB00327C898FB213FULL,  0xBF597FC7BEEF0EE4ULL,
+	0xC6E00BF33DA88FC2ULL,  0xD5A79147930AA725ULL,
+	0x06CA6351E003826FULL,  0x142929670A0E6E70ULL,
+	0x27B70A8546D22FFCULL,  0x2E1B21385C26C926ULL,
+	0x4D2C6DFC5AC42AEDULL,  0x53380D139D95B3DFULL,
+	0x650A73548BAF63DEULL,  0x766A0ABB3C77B2A8ULL,
+	0x81C2C92E47EDAEE6ULL,  0x92722C851482353BULL,
+	0xA2BFE8A14CF10364ULL,  0xA81A664BBC423001ULL,
+	0xC24B8B70D0F89791ULL,  0xC76C51A30654BE30ULL,
+	0xD192E819D6EF5218ULL,  0xD69906245565A910ULL,
+	0xF40E35855771202AULL,  0x106AA07032BBD1B8ULL,
+	0x19A4C116B8D2D0C8ULL,  0x1E376C085141AB53ULL,
+	0x2748774CDF8EEB99ULL,  0x34B0BCB5E19B48A8ULL,
+	0x391C0CB3C5C95A63ULL,  0x4ED8AA4AE3418ACBULL,
+	0x5B9CCA4F7763E373ULL,  0x682E6FF3D6B2B8A3ULL,
+	0x748F82EE5DEFB2FCULL,  0x78A5636F43172F60ULL,
+	0x84C87814A1F0AB72ULL,  0x8CC702081A6439ECULL,
+	0x90BEFFFA23631E28ULL,  0xA4506CEBDE82BDE9ULL,
+	0xBEF9A3F7B2C67915ULL,  0xC67178F2E372532BULL,
+	0xCA273ECEEA26619CULL,  0xD186B8C721C0C207ULL,
+	0xEADA7DD6CDE0EB1EULL,  0xF57D4F7FEE6ED178ULL,
+	0x06F067AA72176FBAULL,  0x0A637DC5A2C898A6ULL,
+	0x113F9804BEF90DAEULL,  0x1B710B35131C471BULL,
+	0x28DB77F523047D84ULL,  0x32CAAB7B40C72493ULL,
+	0x3C9EBE0A15C9BEBCULL,  0x431D67C49C100D4CULL,
+	0x4CC5D4BECB3E42B6ULL,  0x597F299CFC657E2AULL,
+	0x5FCB6FAB3AD6FAECULL,  0x6C44198C4A475817ULL
+};
+
+static void pp_crypto_hash_sha2_512_swap_bytes (puint64 *data, puint words);
+static void pp_crypto_hash_sha2_512_process (PHashSHA2_512 *ctx, const puint64 data[16]);
+static PHashSHA2_512 * pp_crypto_hash_sha2_512_new_internal (pboolean is384);
+
+#define P_SHA2_512_SHR(val, shift) ((val) >> (shift))
+#define P_SHA2_512_ROTR(val, shift) (P_SHA2_512_SHR(val, shift) | ((val) << (64 - (shift))))
+
+#define P_SHA2_512_S0(x) (P_SHA2_512_ROTR (x, 1)  ^ P_SHA2_512_ROTR (x, 8)  ^ P_SHA2_512_SHR  (x, 7))
+#define P_SHA2_512_S1(x) (P_SHA2_512_ROTR (x, 19) ^ P_SHA2_512_ROTR (x, 61) ^ P_SHA2_512_SHR  (x, 6))
+#define P_SHA2_512_S2(x) (P_SHA2_512_ROTR (x, 28) ^ P_SHA2_512_ROTR (x, 34) ^ P_SHA2_512_ROTR (x, 39))
+#define P_SHA2_512_S3(x) (P_SHA2_512_ROTR (x, 14) ^ P_SHA2_512_ROTR (x, 18) ^ P_SHA2_512_ROTR (x, 41))
+
+#define P_SHA2_512_F0(x, y, z) ((x & y) | (z & (x | y)))
+#define P_SHA2_512_F1(x, y, z) (z ^ (x & (y ^ z)))
+
+#define P_SHA2_512_P(a, b, c, d, e, f, g, h, x, K)				\
+{										\
+	tmp_sum1 = h + P_SHA2_512_S3 (e) + P_SHA2_512_F1 (e, f, g) + K + x;	\
+	tmp_sum2 = P_SHA2_512_S2 (a) + P_SHA2_512_F0 (a, b, c);			\
+	d += tmp_sum1;								\
+	h = tmp_sum1 + tmp_sum2;						\
+}
+
+static void
+pp_crypto_hash_sha2_512_swap_bytes (puint64	*data,
+				    puint	words)
+{
+#ifdef PLIBSYS_IS_BIGENDIAN
+	P_UNUSED (data);
+	P_UNUSED (words);
+#else
+	while (words-- > 0) {
+		*data = PUINT64_TO_BE (*data);
+		++data;
+	}
+#endif
+}
+
+static void
+pp_crypto_hash_sha2_512_process (PHashSHA2_512	*ctx,
+				 const puint64	data[16])
+{
+	puint64	tmp_sum1, tmp_sum2;
+	puint64 W[80];
+	puint64	A[8];
+	puint	i;
+
+	for (i = 0; i < 8; ++i)
+		A[i] = ctx->hash[i];
+
+	memcpy (W, data, 128);
+
+	for (i = 16; i < 80; ++i)
+		W[i] = P_SHA2_512_S1 (W[i -  2]) + W[i -  7] + P_SHA2_512_S0 (W[i - 15]) + W[i - 16];
+
+	for (i = 0; i < 80; i += 8) {
+		P_SHA2_512_P (A[0], A[1], A[2], A[3], A[4], A[5], A[6], A[7], W[i + 0], pp_crypto_hash_sha2_512_K[i + 0]);
+		P_SHA2_512_P (A[7], A[0], A[1], A[2], A[3], A[4], A[5], A[6], W[i + 1], pp_crypto_hash_sha2_512_K[i + 1]);
+		P_SHA2_512_P (A[6], A[7], A[0], A[1], A[2], A[3], A[4], A[5], W[i + 2], pp_crypto_hash_sha2_512_K[i + 2]);
+		P_SHA2_512_P (A[5], A[6], A[7], A[0], A[1], A[2], A[3], A[4], W[i + 3], pp_crypto_hash_sha2_512_K[i + 3]);
+		P_SHA2_512_P (A[4], A[5], A[6], A[7], A[0], A[1], A[2], A[3], W[i + 4], pp_crypto_hash_sha2_512_K[i + 4]);
+		P_SHA2_512_P (A[3], A[4], A[5], A[6], A[7], A[0], A[1], A[2], W[i + 5], pp_crypto_hash_sha2_512_K[i + 5]);
+		P_SHA2_512_P (A[2], A[3], A[4], A[5], A[6], A[7], A[0], A[1], W[i + 6], pp_crypto_hash_sha2_512_K[i + 6]);
+		P_SHA2_512_P (A[1], A[2], A[3], A[4], A[5], A[6], A[7], A[0], W[i + 7], pp_crypto_hash_sha2_512_K[i + 7]);
+	}
+
+	for (i = 0; i < 8; ++i)
+		ctx->hash[i] += A[i];
+}
+
+static PHashSHA2_512 *
+pp_crypto_hash_sha2_512_new_internal (pboolean is384)
+{
+	PHashSHA2_512 *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PHashSHA2_512))) == NULL))
+		return NULL;
+
+	ret->is384 = is384;
+
+	p_crypto_hash_sha2_512_reset (ret);
+
+	return ret;
+}
+
+void
+p_crypto_hash_sha2_512_reset (PHashSHA2_512 *ctx)
+{
+	memset (ctx->buf.buf, 0, 128);
+
+	ctx->len_low = 0;
+	ctx->len_high = 0;
+
+	if (ctx->is384 == FALSE) {
+		/* SHA2-512 */
+		ctx->hash[0] = 0x6A09E667F3BCC908ULL;
+		ctx->hash[1] = 0xBB67AE8584CAA73BULL;
+		ctx->hash[2] = 0x3C6EF372FE94F82BULL;
+		ctx->hash[3] = 0xA54FF53A5F1D36F1ULL;
+		ctx->hash[4] = 0x510E527FADE682D1ULL;
+		ctx->hash[5] = 0x9B05688C2B3E6C1FULL;
+		ctx->hash[6] = 0x1F83D9ABFB41BD6BULL;
+		ctx->hash[7] = 0x5BE0CD19137E2179ULL;
+	} else {
+		/* SHA2-384 */
+		ctx->hash[0] = 0xCBBB9D5DC1059ED8ULL;
+		ctx->hash[1] = 0x629A292A367CD507ULL;
+		ctx->hash[2] = 0x9159015A3070DD17ULL;
+		ctx->hash[3] = 0x152FECD8F70E5939ULL;
+		ctx->hash[4] = 0x67332667FFC00B31ULL;
+		ctx->hash[5] = 0x8EB44A8768581511ULL;
+		ctx->hash[6] = 0xDB0C2E0D64F98FA7ULL;
+		ctx->hash[7] = 0x47B5481DBEFA4FA4ULL;
+	}
+}
+
+PHashSHA2_512 *
+p_crypto_hash_sha2_512_new (void)
+{
+	return pp_crypto_hash_sha2_512_new_internal (FALSE);
+}
+
+PHashSHA2_512 *
+p_crypto_hash_sha2_384_new (void)
+{
+	return pp_crypto_hash_sha2_512_new_internal (TRUE);
+}
+
+void
+p_crypto_hash_sha2_512_update (PHashSHA2_512	*ctx,
+			       const puchar	*data,
+			       psize		len)
+{
+	puint32	left, to_fill;
+
+	left = (puint32) (ctx->len_low & 0x7F);
+	to_fill = 128 - left;
+
+	ctx->len_low += (puint64) len;
+
+	if (ctx->len_low < (puint64) len)
+		++ctx->len_high;
+
+	if (left && (puint64) len >= to_fill) {
+		memcpy (ctx->buf.buf + left, data, to_fill);
+		pp_crypto_hash_sha2_512_swap_bytes (ctx->buf.buf_w, 16);
+		pp_crypto_hash_sha2_512_process (ctx, ctx->buf.buf_w);
+
+		data += to_fill;
+		len -= to_fill;
+		left = 0;
+	}
+
+	while (len >= 128) {
+		memcpy (ctx->buf.buf, data, 128);
+		pp_crypto_hash_sha2_512_swap_bytes (ctx->buf.buf_w, 16);
+		pp_crypto_hash_sha2_512_process (ctx, ctx->buf.buf_w);
+
+		data += 128;
+		len -= 128;
+	}
+
+	if (len > 0)
+		memcpy (ctx->buf.buf + left, data, len);
+}
+
+void
+p_crypto_hash_sha2_512_finish (PHashSHA2_512 *ctx)
+{
+	puint64	high, low;
+	pint	left, last;
+
+	left = (pint) (ctx->len_low & 0x7F);
+	last = (left < 112) ? (112 - left) : (240 - left);
+
+	low = ctx->len_low << 3;
+	high = ctx->len_high << 3
+	     | ctx->len_low >> 61;
+
+	if (last > 0)
+		p_crypto_hash_sha2_512_update (ctx, pp_crypto_hash_sha2_512_pad, (psize) last);
+
+	ctx->buf.buf_w[14] = high;
+	ctx->buf.buf_w[15] = low;
+
+	pp_crypto_hash_sha2_512_swap_bytes (ctx->buf.buf_w, 14);
+	pp_crypto_hash_sha2_512_process (ctx, ctx->buf.buf_w);
+
+	pp_crypto_hash_sha2_512_swap_bytes (ctx->hash, ctx->is384 == FALSE ? 8 : 6);
+}
+
+const puchar *
+p_crypto_hash_sha2_512_digest (PHashSHA2_512 *ctx)
+{
+	return (const puchar *) ctx->hash;
+}
+
+void
+p_crypto_hash_sha2_512_free (PHashSHA2_512 *ctx)
+{
+	p_free (ctx);
+}
diff --git a/3rdparty/plibsys/src/pcryptohash-sha2-512.h b/3rdparty/plibsys/src/pcryptohash-sha2-512.h
new file mode 100644
index 0000000..ce0c2f7
--- /dev/null
+++ b/3rdparty/plibsys/src/pcryptohash-sha2-512.h
@@ -0,0 +1,59 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/* SHA2-512 interface implementation for #PCryptoHash */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PCRYPTOHASHSHA2_512_H
+#define PLIBSYS_HEADER_PCRYPTOHASHSHA2_512_H
+
+#include "ptypes.h"
+#include "pmacros.h"
+
+P_BEGIN_DECLS
+
+typedef struct PHashSHA2_512_ PHashSHA2_512;
+
+PHashSHA2_512 *	p_crypto_hash_sha2_512_new	(void);
+void		p_crypto_hash_sha2_512_update	(PHashSHA2_512 *ctx, const puchar *data, psize len);
+void		p_crypto_hash_sha2_512_finish	(PHashSHA2_512 *ctx);
+const puchar *	p_crypto_hash_sha2_512_digest	(PHashSHA2_512 *ctx);
+void		p_crypto_hash_sha2_512_reset	(PHashSHA2_512 *ctx);
+void		p_crypto_hash_sha2_512_free	(PHashSHA2_512 *ctx);
+
+PHashSHA2_512 *	p_crypto_hash_sha2_384_new	(void);
+
+#define p_crypto_hash_sha2_384_update p_crypto_hash_sha2_512_update
+#define p_crypto_hash_sha2_384_finish p_crypto_hash_sha2_512_finish
+#define p_crypto_hash_sha2_384_digest p_crypto_hash_sha2_512_digest
+#define p_crypto_hash_sha2_384_reset  p_crypto_hash_sha2_512_reset
+#define p_crypto_hash_sha2_384_free   p_crypto_hash_sha2_512_free
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PCRYPTOHASHSHA2_512_H */
diff --git a/3rdparty/plibsys/src/pcryptohash-sha3.c b/3rdparty/plibsys/src/pcryptohash-sha3.c
new file mode 100644
index 0000000..c86545e
--- /dev/null
+++ b/3rdparty/plibsys/src/pcryptohash-sha3.c
@@ -0,0 +1,297 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 <string.h>
+#include <stdlib.h>
+
+#include "pmem.h"
+#include "pcryptohash-sha3.h"
+
+struct PHashSHA3_ {
+	union buf_ {
+		puchar	buf[200];
+		puint64	buf_w[25];
+	} buf;
+	puint64		hash[25];
+
+	puint32		len;
+	puint32		block_size;
+};
+
+static const puint64 pp_crypto_hash_sha3_K[] = {
+	0x0000000000000001ULL, 0x0000000000008082ULL,
+	0x800000000000808AULL, 0x8000000080008000ULL,
+	0x000000000000808BULL, 0x0000000080000001ULL,
+	0x8000000080008081ULL, 0x8000000000008009ULL,
+	0x000000000000008AULL, 0x0000000000000088ULL,
+	0x0000000080008009ULL, 0x000000008000000AULL,
+	0x000000008000808BULL, 0x800000000000008BULL,
+	0x8000000000008089ULL, 0x8000000000008003ULL,
+	0x8000000000008002ULL, 0x8000000000000080ULL,
+	0x000000000000800AULL, 0x800000008000000AULL,
+	0x8000000080008081ULL, 0x8000000000008080ULL,
+	0x0000000080000001ULL, 0x8000000080008008ULL
+};
+
+static void pp_crypto_hash_sha3_swap_bytes (puint64 *data, puint words);
+static void pp_crypto_hash_sha3_keccak_theta (PHashSHA3 *ctx);
+static void pp_crypto_hash_sha3_keccak_rho_pi (PHashSHA3 *ctx);
+static void pp_crypto_hash_sha3_keccak_chi (PHashSHA3 *ctx);
+static void pp_crypto_hash_sha3_keccak_permutate (PHashSHA3 *ctx);
+static void pp_crypto_hash_sha3_process (PHashSHA3 *ctx, const puint64 *data);
+static PHashSHA3 * pp_crypto_hash_sha3_new_internal (puint bits);
+
+#define P_SHA3_SHL(val, shift) ((val) << (shift))
+#define P_SHA3_ROTL(val, shift) (P_SHA3_SHL(val, shift) | ((val) >> (64 - (shift))))
+
+static void
+pp_crypto_hash_sha3_swap_bytes (puint64	*data,
+				puint	words)
+{
+#ifndef PLIBSYS_IS_BIGENDIAN
+	P_UNUSED (data);
+	P_UNUSED (words);
+#else
+	while (words-- > 0) {
+		*data = PUINT64_TO_LE (*data);
+		++data;
+	}
+#endif
+}
+
+/* Theta step (see [Keccak Reference, Section 2.3.2]) */
+static void
+pp_crypto_hash_sha3_keccak_theta (PHashSHA3 *ctx)
+{
+	puint	i;
+	puint64	C[5], D[5];
+
+	/* Compute the parity of the columns */
+	for (i = 0; i < 5; ++i)
+		C[i] = ctx->hash[i] ^ ctx->hash[i + 5] ^ ctx->hash[i + 10] ^ ctx->hash[i + 15] ^ ctx->hash[i + 20];
+
+	/* Compute the theta effect for a given column */
+	D[0] = P_SHA3_ROTL (C[1], 1) ^ C[4];
+	D[1] = P_SHA3_ROTL (C[2], 1) ^ C[0];
+	D[2] = P_SHA3_ROTL (C[3], 1) ^ C[1];
+	D[3] = P_SHA3_ROTL (C[4], 1) ^ C[2];
+	D[4] = P_SHA3_ROTL (C[0], 1) ^ C[3];
+
+	/* Add the theta effect to the whole column */
+	for (i = 0; i < 5; ++i) {
+		ctx->hash[i]      ^= D[i];
+		ctx->hash[i + 5]  ^= D[i];
+		ctx->hash[i + 10] ^= D[i];
+		ctx->hash[i + 15] ^= D[i];
+		ctx->hash[i + 20] ^= D[i];
+	}
+}
+
+/* Rho and pi steps (see [Keccak Reference, Sections 2.3.3 and 2.3.4]) */
+static void
+pp_crypto_hash_sha3_keccak_rho_pi (PHashSHA3 *ctx)
+{
+	puint64 tmp_A;
+
+	/* Unroll the loop over ((0 1)(2 3))^t * (1 0) for 0 ≤ t ≤ 23 */
+	tmp_A = ctx->hash[1];
+	ctx->hash[1]  = P_SHA3_ROTL (ctx->hash[6],  44);
+	ctx->hash[6]  = P_SHA3_ROTL (ctx->hash[9],  20);
+	ctx->hash[9]  = P_SHA3_ROTL (ctx->hash[22], 61);
+	ctx->hash[22] = P_SHA3_ROTL (ctx->hash[14], 39);
+	ctx->hash[14] = P_SHA3_ROTL (ctx->hash[20], 18);
+	ctx->hash[20] = P_SHA3_ROTL (ctx->hash[2],  62);
+	ctx->hash[2]  = P_SHA3_ROTL (ctx->hash[12], 43);
+	ctx->hash[12] = P_SHA3_ROTL (ctx->hash[13], 25);
+	ctx->hash[13] = P_SHA3_ROTL (ctx->hash[19],  8);
+	ctx->hash[19] = P_SHA3_ROTL (ctx->hash[23], 56);
+	ctx->hash[23] = P_SHA3_ROTL (ctx->hash[15], 41);
+	ctx->hash[15] = P_SHA3_ROTL (ctx->hash[4],  27);
+	ctx->hash[4]  = P_SHA3_ROTL (ctx->hash[24], 14);
+	ctx->hash[24] = P_SHA3_ROTL (ctx->hash[21],  2);
+	ctx->hash[21] = P_SHA3_ROTL (ctx->hash[8],  55);
+	ctx->hash[8]  = P_SHA3_ROTL (ctx->hash[16], 45);
+	ctx->hash[16] = P_SHA3_ROTL (ctx->hash[5],  36);
+	ctx->hash[5]  = P_SHA3_ROTL (ctx->hash[3],  28);
+	ctx->hash[3]  = P_SHA3_ROTL (ctx->hash[18], 21);
+	ctx->hash[18] = P_SHA3_ROTL (ctx->hash[17], 15);
+	ctx->hash[17] = P_SHA3_ROTL (ctx->hash[11], 10);
+	ctx->hash[11] = P_SHA3_ROTL (ctx->hash[7],   6);
+	ctx->hash[7]  = P_SHA3_ROTL (ctx->hash[10],  3);
+	ctx->hash[10] = P_SHA3_ROTL (tmp_A, 1);
+}
+
+/* Chi step (see [Keccak Reference, Section 2.3.1]) */
+static void
+pp_crypto_hash_sha3_keccak_chi (PHashSHA3 *ctx)
+{
+	puint i;
+	puint64	tmp_A1, tmp_A2;
+
+	for (i = 0; i < 25; i += 5) {
+		tmp_A1 = ctx->hash[i + 0];
+		tmp_A2 = ctx->hash[i + 1];
+
+		ctx->hash[i + 0] ^= ~tmp_A2 & ctx->hash[i + 2];
+		ctx->hash[i + 1] ^= ~ctx->hash[i + 2] & ctx->hash[i + 3];
+		ctx->hash[i + 2] ^= ~ctx->hash[i + 3] & ctx->hash[i + 4];
+		ctx->hash[i + 3] ^= ~ctx->hash[i + 4] & tmp_A1;
+		ctx->hash[i + 4] ^= ~tmp_A1 & tmp_A2;
+	}
+}
+
+static void
+pp_crypto_hash_sha3_keccak_permutate (PHashSHA3 *ctx)
+{
+	puint i;
+
+	for (i = 0; i < 24; ++i) {
+		pp_crypto_hash_sha3_keccak_theta (ctx);
+		pp_crypto_hash_sha3_keccak_rho_pi (ctx);
+		pp_crypto_hash_sha3_keccak_chi (ctx);
+
+		/* Iota step (see [Keccak Reference, Section 2.3.5]) */
+		ctx->hash[0] ^= pp_crypto_hash_sha3_K[i];
+	}
+}
+
+static void
+pp_crypto_hash_sha3_process (PHashSHA3		*ctx,
+			     const puint64	*data)
+{
+	puint i;
+	puint qwords = ctx->block_size / 8;
+
+	for (i = 0; i < qwords; ++i)
+		ctx->hash[i] ^= data[i];
+
+	/* Make the Keccak permutation */
+	pp_crypto_hash_sha3_keccak_permutate (ctx);
+}
+
+static PHashSHA3 *
+pp_crypto_hash_sha3_new_internal (puint bits)
+{
+	PHashSHA3 *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PHashSHA3))) == NULL))
+		return NULL;
+
+	ret->block_size = (1600 - bits * 2) / 8;
+
+	return ret;
+}
+
+void
+p_crypto_hash_sha3_reset (PHashSHA3 *ctx)
+{
+	memset (ctx->buf.buf, 0, 200);
+	memset (ctx->hash, 0, sizeof (ctx->hash));
+
+	ctx->len = 0;
+}
+
+PHashSHA3 *
+p_crypto_hash_sha3_224_new (void)
+{
+	return pp_crypto_hash_sha3_new_internal (224);
+}
+
+PHashSHA3 *
+p_crypto_hash_sha3_256_new (void)
+{
+	return pp_crypto_hash_sha3_new_internal (256);
+}
+
+PHashSHA3 *
+p_crypto_hash_sha3_384_new (void)
+{
+	return pp_crypto_hash_sha3_new_internal (384);
+}
+
+PHashSHA3 *
+p_crypto_hash_sha3_512_new (void)
+{
+	return pp_crypto_hash_sha3_new_internal (512);
+}
+
+void
+p_crypto_hash_sha3_update (PHashSHA3	*ctx,
+			   const puchar	*data,
+			   psize	len)
+{
+	puint32	left, to_fill;
+
+	left     = ctx->len;
+	to_fill  = ctx->block_size - left;
+	ctx->len = (puint32) (((psize) ctx->len + len) % (psize) ctx->block_size);
+
+	if (left && (puint64) len >= to_fill) {
+		memcpy (ctx->buf.buf + left, data, to_fill);
+		pp_crypto_hash_sha3_swap_bytes (ctx->buf.buf_w, ctx->block_size >> 3);
+		pp_crypto_hash_sha3_process (ctx, ctx->buf.buf_w);
+
+		data += to_fill;
+		len -= to_fill;
+		left = 0;
+	}
+
+	while (len >= ctx->block_size) {
+		memcpy (ctx->buf.buf, data, ctx->block_size);
+		pp_crypto_hash_sha3_swap_bytes (ctx->buf.buf_w, ctx->block_size >> 3);
+		pp_crypto_hash_sha3_process (ctx, ctx->buf.buf_w);
+
+		data += ctx->block_size;
+		len -= ctx->block_size;
+	}
+
+	if (len > 0)
+		memcpy (ctx->buf.buf + left, data, len);
+}
+
+void
+p_crypto_hash_sha3_finish (PHashSHA3 *ctx)
+{
+	memset (ctx->buf.buf + ctx->len, 0, ctx->block_size - ctx->len);
+	ctx->buf.buf[ctx->len]            |= 0x06;
+	ctx->buf.buf[ctx->block_size - 1] |= 0x80;
+
+	pp_crypto_hash_sha3_swap_bytes (ctx->buf.buf_w, ctx->block_size >> 3);
+	pp_crypto_hash_sha3_process (ctx, ctx->buf.buf_w);
+
+	pp_crypto_hash_sha3_swap_bytes (ctx->hash, (100 - (ctx->block_size >> 2)) >> 3);
+}
+
+const puchar *
+p_crypto_hash_sha3_digest (PHashSHA3 *ctx)
+{
+	return (const puchar *) ctx->hash;
+}
+
+void
+p_crypto_hash_sha3_free (PHashSHA3 *ctx)
+{
+	p_free (ctx);
+}
diff --git a/3rdparty/plibsys/src/pcryptohash-sha3.h b/3rdparty/plibsys/src/pcryptohash-sha3.h
new file mode 100644
index 0000000..ad64e70
--- /dev/null
+++ b/3rdparty/plibsys/src/pcryptohash-sha3.h
@@ -0,0 +1,79 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/* SHA-3 (Keccak) interface implementation for #PCryptoHash */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PCRYPTOHASHSHA3_H
+#define PLIBSYS_HEADER_PCRYPTOHASHSHA3_H
+
+#include "ptypes.h"
+#include "pmacros.h"
+
+P_BEGIN_DECLS
+
+typedef struct PHashSHA3_ PHashSHA3;
+
+void		p_crypto_hash_sha3_update	(PHashSHA3 *ctx, const puchar *data, psize len);
+void		p_crypto_hash_sha3_finish	(PHashSHA3 *ctx);
+const puchar *	p_crypto_hash_sha3_digest	(PHashSHA3 *ctx);
+void		p_crypto_hash_sha3_reset	(PHashSHA3 *ctx);
+void		p_crypto_hash_sha3_free		(PHashSHA3 *ctx);
+
+PHashSHA3 *	p_crypto_hash_sha3_224_new	(void);
+PHashSHA3 *	p_crypto_hash_sha3_256_new	(void);
+PHashSHA3 *	p_crypto_hash_sha3_384_new	(void);
+PHashSHA3 *	p_crypto_hash_sha3_512_new	(void);
+
+#define p_crypto_hash_sha3_224_update p_crypto_hash_sha3_update
+#define p_crypto_hash_sha3_224_finish p_crypto_hash_sha3_finish
+#define p_crypto_hash_sha3_224_digest p_crypto_hash_sha3_digest
+#define p_crypto_hash_sha3_224_reset  p_crypto_hash_sha3_reset
+#define p_crypto_hash_sha3_224_free   p_crypto_hash_sha3_free
+
+#define p_crypto_hash_sha3_256_update p_crypto_hash_sha3_update
+#define p_crypto_hash_sha3_256_finish p_crypto_hash_sha3_finish
+#define p_crypto_hash_sha3_256_digest p_crypto_hash_sha3_digest
+#define p_crypto_hash_sha3_256_reset  p_crypto_hash_sha3_reset
+#define p_crypto_hash_sha3_256_free   p_crypto_hash_sha3_free
+
+#define p_crypto_hash_sha3_384_update p_crypto_hash_sha3_update
+#define p_crypto_hash_sha3_384_finish p_crypto_hash_sha3_finish
+#define p_crypto_hash_sha3_384_digest p_crypto_hash_sha3_digest
+#define p_crypto_hash_sha3_384_reset  p_crypto_hash_sha3_reset
+#define p_crypto_hash_sha3_384_free   p_crypto_hash_sha3_free
+
+#define p_crypto_hash_sha3_512_update p_crypto_hash_sha3_update
+#define p_crypto_hash_sha3_512_finish p_crypto_hash_sha3_finish
+#define p_crypto_hash_sha3_512_digest p_crypto_hash_sha3_digest
+#define p_crypto_hash_sha3_512_reset  p_crypto_hash_sha3_reset
+#define p_crypto_hash_sha3_512_free   p_crypto_hash_sha3_free
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PCRYPTOHASHSHA3_H */
diff --git a/3rdparty/plibsys/src/pcryptohash.c b/3rdparty/plibsys/src/pcryptohash.c
new file mode 100644
index 0000000..2ccb6a6
--- /dev/null
+++ b/3rdparty/plibsys/src/pcryptohash.c
@@ -0,0 +1,250 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "pcryptohash.h"
+#include "pcryptohash-gost3411.h"
+#include "pcryptohash-md5.h"
+#include "pcryptohash-sha1.h"
+#include "pcryptohash-sha2-256.h"
+#include "pcryptohash-sha2-512.h"
+#include "pcryptohash-sha3.h"
+
+#include <string.h>
+
+#define P_HASH_FUNCS(ctx, type) \
+	ctx->create = (void * (*) (void)) p_crypto_hash_##type##_new;				\
+	ctx->update = (void (*) (void *, const puchar *, psize)) p_crypto_hash_##type##_update;	\
+	ctx->finish = (void (*) (void *)) p_crypto_hash_##type##_finish;			\
+	ctx->digest = (const puchar * (*) (void *)) p_crypto_hash_##type##_digest;		\
+	ctx->reset = (void (*) (void *)) p_crypto_hash_##type##_reset;				\
+	ctx->free = (void (*) (void *)) p_crypto_hash_##type##_free;
+
+struct PCryptoHash_ {
+	PCryptoHashType	type;
+	ppointer	context;
+	puint		hash_len;
+	pboolean	closed;
+	ppointer	(*create)	(void);
+	void		(*update)	(void *hash, const puchar *data, psize len);
+	void		(*finish)	(void *hash);
+	const puchar *	(*digest)	(void *hash);
+	void		(*reset)	(void *hash);
+	void		(*free)		(void *hash);
+};
+
+static pchar pp_crypto_hash_hex_str[]= "0123456789abcdef";
+
+static void
+pp_crypto_hash_digest_to_hex (const puchar *digest, puint len, pchar *out);
+
+static void
+pp_crypto_hash_digest_to_hex (const puchar *digest, puint len, pchar *out)
+{
+	puint i;
+
+	for (i = 0; i < len; ++i) {
+		*(out + (i << 1)    ) = pp_crypto_hash_hex_str[(digest[i] >> 4) & 0x0F];
+		*(out + (i << 1) + 1) = pp_crypto_hash_hex_str[(digest[i]     ) & 0x0F];
+	}
+}
+
+P_LIB_API PCryptoHash *
+p_crypto_hash_new (PCryptoHashType type)
+{
+	PCryptoHash *ret;
+
+	if (P_UNLIKELY (!(type >= P_CRYPTO_HASH_TYPE_MD5 && type <= P_CRYPTO_HASH_TYPE_GOST)))
+		return NULL;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PCryptoHash))) == NULL)) {
+		P_ERROR ("PCryptoHash::p_crypto_hash_new: failed to allocate memory");
+		return NULL;
+	}
+
+	switch (type) {
+	case P_CRYPTO_HASH_TYPE_MD5:
+		P_HASH_FUNCS (ret, md5);
+		ret->hash_len = 16;
+		break;
+	case P_CRYPTO_HASH_TYPE_SHA1:
+		P_HASH_FUNCS (ret, sha1);
+		ret->hash_len = 20;
+		break;
+	case P_CRYPTO_HASH_TYPE_SHA2_224:
+		P_HASH_FUNCS (ret, sha2_224);
+		ret->hash_len = 28;
+		break;
+	case P_CRYPTO_HASH_TYPE_SHA2_256:
+		P_HASH_FUNCS (ret, sha2_256);
+		ret->hash_len = 32;
+		break;
+	case P_CRYPTO_HASH_TYPE_SHA2_384:
+		P_HASH_FUNCS (ret, sha2_384);
+		ret->hash_len = 48;
+		break;
+	case P_CRYPTO_HASH_TYPE_SHA2_512:
+		P_HASH_FUNCS (ret, sha2_512);
+		ret->hash_len = 64;
+		break;
+	case P_CRYPTO_HASH_TYPE_SHA3_224:
+		P_HASH_FUNCS (ret, sha3_224);
+		ret->hash_len = 28;
+		break;
+	case P_CRYPTO_HASH_TYPE_SHA3_256:
+		P_HASH_FUNCS (ret, sha3_256);
+		ret->hash_len = 32;
+		break;
+	case P_CRYPTO_HASH_TYPE_SHA3_384:
+		P_HASH_FUNCS (ret, sha3_384);
+		ret->hash_len = 48;
+		break;
+	case P_CRYPTO_HASH_TYPE_SHA3_512:
+		P_HASH_FUNCS (ret, sha3_512);
+		ret->hash_len = 64;
+		break;
+	case P_CRYPTO_HASH_TYPE_GOST:
+		P_HASH_FUNCS (ret, gost3411);
+		ret->hash_len = 32;
+		break;
+	}
+
+	ret->type   = type;
+	ret->closed = FALSE;
+
+	if (P_UNLIKELY ((ret->context = ret->create ()) == NULL)) {
+		p_free (ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+P_LIB_API void
+p_crypto_hash_update (PCryptoHash *hash, const puchar *data, psize len)
+{
+	if (P_UNLIKELY (hash == NULL || data == NULL || len == 0))
+		return;
+
+	if (P_UNLIKELY (hash->closed))
+		return;
+
+	hash->update (hash->context, data, len);
+}
+
+P_LIB_API void
+p_crypto_hash_reset (PCryptoHash *hash)
+{
+	if (P_UNLIKELY (hash == NULL))
+		return;
+
+	hash->reset (hash->context);
+	hash->closed = FALSE;
+}
+
+P_LIB_API pchar *
+p_crypto_hash_get_string (PCryptoHash *hash)
+{
+	pchar		*ret;
+	const puchar	*digest;
+
+	if (P_UNLIKELY (hash == NULL))
+		return NULL;
+
+	if (!hash->closed) {
+		hash->finish (hash->context);
+		hash->closed = TRUE;
+	}
+
+	if (P_UNLIKELY ((digest = hash->digest (hash->context)) == NULL))
+		return NULL;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (hash->hash_len * 2 + 1)) == NULL))
+		return NULL;
+
+	pp_crypto_hash_digest_to_hex (digest, hash->hash_len, ret);
+
+	return ret;
+}
+
+P_LIB_API void
+p_crypto_hash_get_digest (PCryptoHash *hash, puchar *buf, psize *len)
+{
+	const puchar *digest;
+
+	if (P_UNLIKELY (len == NULL))
+		return;
+
+	if (P_UNLIKELY (hash == NULL || buf == NULL)) {
+		*len = 0;
+		return;
+	}
+
+	if (P_UNLIKELY (hash->hash_len > *len)) {
+		*len = 0;
+		return;
+	}
+
+	if (!hash->closed) {
+		hash->finish (hash->context);
+		hash->closed = TRUE;
+	}
+
+	if (P_UNLIKELY ((digest = hash->digest (hash->context)) == NULL)) {
+		*len = 0;
+		return;
+	}
+
+	memcpy (buf, digest, hash->hash_len);
+	*len = hash->hash_len;
+}
+
+P_LIB_API pssize
+p_crypto_hash_get_length (const PCryptoHash *hash)
+{
+	if (P_UNLIKELY (hash == NULL))
+		return 0;
+
+	return hash->hash_len;
+}
+
+P_LIB_API PCryptoHashType
+p_crypto_hash_get_type (const PCryptoHash *hash)
+{
+	if (P_UNLIKELY (hash == NULL))
+		return (PCryptoHashType) -1;
+
+	return hash->type;
+}
+
+P_LIB_API void
+p_crypto_hash_free (PCryptoHash *hash)
+{
+	if (P_UNLIKELY (hash == NULL))
+		return;
+
+	hash->free (hash->context);
+	p_free (hash);
+}
diff --git a/3rdparty/plibsys/src/pcryptohash.h b/3rdparty/plibsys/src/pcryptohash.h
new file mode 100644
index 0000000..47a8a6c
--- /dev/null
+++ b/3rdparty/plibsys/src/pcryptohash.h
@@ -0,0 +1,188 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file pcryptohash.h
+ * @brief Cryptographic hash function
+ * @author Alexander Saprykin
+ *
+ * A cryptographic hash function is an algorithm which performs a transformation
+ * of the income data to a hash value.
+ *
+ * One of the main requirements to all of the cryptographic hashing algorithms
+ * is that any (even a considerably small) change in the input data must lead to
+ * notable changes in the result hash value. It is the so called avalanche
+ * effect. It helps to avoid collisions (the same hash value for different
+ * input arrays).
+ *
+ * The cryptographic hash function is designed to be a one-way so you couldn't
+ * revert the output hash value to the input data back. The length of the
+ * resulting hash is a constant value depending on the algorithm being used.
+ *
+ * A cryptographic hash works with the incoming data using fixed length blocks
+ * so it is possible to feed as many data as required.
+ *
+ * The cryptographic hash module supports the following hash functions:
+ * - MD5;
+ * - SHA-1;
+ * - SHA-2/224;
+ * - SHA-2/256;
+ * - SHA-2/384;
+ * - SHA-2/512;
+ * - SHA-3/224;
+ * - SHA-3/256;
+ * - SHA-3/384;
+ * - SHA-3/512;
+ * - GOST (R 34.11-94).
+ *
+ * Use p_crypto_hash_new() to initialize a new hash context with one of the
+ * mentioned above types. Data for hashing can be added in several chunks using
+ * the p_crypto_hash_update() routine. You can add more chunks as long as the
+ * hash context is open.
+ *
+ * The hash context becomes close in two cases: p_crypto_hash_get_string() or
+ * p_crypto_hash_get_digest() was called. After that you can only get a hash in
+ * a hexidemical string or in a raw representation.
+ *
+ * A hashing algorithm couldn't be changed after the context initialization.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PCRYPTOHASH_H
+#define PLIBSYS_HEADER_PCRYPTOHASH_H
+
+#include <pmacros.h>
+#include <ptypes.h>
+
+P_BEGIN_DECLS
+
+/** Opaque data structure for handling a cryptographic hash context. */
+typedef struct PCryptoHash_ PCryptoHash;
+
+/** Cryptographic hash function types for #PCryptoHash. */
+typedef enum PCryptoHashType_ {
+	P_CRYPTO_HASH_TYPE_MD5		= 0, /**< MD5 hash function.			@since 0.0.1	*/
+	P_CRYPTO_HASH_TYPE_SHA1		= 1, /**< SHA-1 hash function.			@since 0.0.1	*/
+	P_CRYPTO_HASH_TYPE_SHA2_224	= 2, /**< SHA-2/224 hash function.		@since 0.0.2	*/
+	P_CRYPTO_HASH_TYPE_SHA2_256	= 3, /**< SHA-2/256 hash function.		@since 0.0.2	*/
+	P_CRYPTO_HASH_TYPE_SHA2_384	= 4, /**< SHA-2/384 hash function.		@since 0.0.2	*/
+	P_CRYPTO_HASH_TYPE_SHA2_512	= 5, /**< SHA-2/512 hash function.		@since 0.0.2	*/
+	P_CRYPTO_HASH_TYPE_SHA3_224	= 6, /**< SHA-2/224 hash function.		@since 0.0.2	*/
+	P_CRYPTO_HASH_TYPE_SHA3_256	= 7, /**< SHA-2/256 hash function.		@since 0.0.2	*/
+	P_CRYPTO_HASH_TYPE_SHA3_384	= 8, /**< SHA-2/384 hash function.		@since 0.0.2	*/
+	P_CRYPTO_HASH_TYPE_SHA3_512	= 9, /**< SHA-3/512 hash function.		@since 0.0.2	*/
+	P_CRYPTO_HASH_TYPE_GOST		= 10 /**< GOST (R 34.11-94) hash function.	@since 0.0.1	*/
+} PCryptoHashType;
+
+/**
+ * @brief Initializes a new #PCryptoHash context.
+ * @param type Hash function type to use, can't be changed later.
+ * @return Newly initialized #PCryptoHash context in case of success, NULL
+ * otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API PCryptoHash *		p_crypto_hash_new		(PCryptoHashType	type);
+
+/**
+ * @brief Adds a new chunk of data for hashing.
+ * @param hash #PCryptoHash context to add @a data to.
+ * @param data Data to add for hashing.
+ * @param len Data length, in bytes.
+ * @note After calling p_crypto_hash_get_string() or p_crypto_hash_get_digest()
+ * the hash couldn't be updated anymore as it becomes close.
+ * @since 0.0.1
+ */
+P_LIB_API void			p_crypto_hash_update		(PCryptoHash		*hash,
+								 const puchar		*data,
+								 psize			len);
+
+/**
+ * @brief Resets a hash state.
+ * @param hash #PCryptoHash context to reset.
+ * @since 0.0.1
+ *
+ * After a reset the hash context becomes open for updating, but all previously
+ * added data will be lost. A hash function type couldn't be changed during or
+ * after the resets.
+ */
+P_LIB_API void			p_crypto_hash_reset		(PCryptoHash		*hash);
+
+/**
+ * @brief Gets a hash in a hexidemical representation.
+ * @param hash #PCryptoHash context to get a string from.
+ * @return NULL-terminated string with the hexidemical representation of a hash
+ * state in case of success, NULL otherwise. The string should be freed with
+ * p_free() after using it.
+ * @note Before returning the string the hash context will be closed for further
+ * updates.
+ * @since 0.0.1
+ */
+P_LIB_API pchar *		p_crypto_hash_get_string	(PCryptoHash		*hash);
+
+/**
+ * @brief Gets a hash in a raw representation.
+ * @param hash #PCryptoHash context to get a digest from.
+ * @param buf Buffer to store the digest with the hash raw representation.
+ * @param[in,out] len Size of @a buf when calling, count of written bytes
+ * after.
+ * @note Before getting the raw digest the hash context will be closed for
+ * further updates.
+ * @since 0.0.1
+ */
+P_LIB_API void			p_crypto_hash_get_digest	(PCryptoHash		*hash,
+								 puchar			*buf,
+								 psize			*len);
+
+/**
+ * @brief Gets a hash digest length depending on its type.
+ * @param hash #PCryptoHash context to get the length for.
+ * @return Length (in bytes) of the given hash depending on its type in case of
+ * success, -1 otherwise.
+ * @note This length doesn't match a string hash representation.
+ * @since 0.0.1
+ */
+P_LIB_API pssize		p_crypto_hash_get_length	(const PCryptoHash	*hash);
+
+/**
+ * @brief Gets a hash function type.
+ * @param hash #PCryptoHash context to get the type for.
+ * @return Hash function type used in the given context.
+ * @since 0.0.1
+ */
+P_LIB_API PCryptoHashType	p_crypto_hash_get_type		(const PCryptoHash	*hash);
+
+/**
+ * @brief Frees a previously initialized hash context.
+ * @param hash #PCryptoHash context to free.
+ * @since 0.0.1
+ */
+P_LIB_API void			p_crypto_hash_free		(PCryptoHash		*hash);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PCRYPTOHASH_H */
diff --git a/3rdparty/plibsys/src/pdir-none.c b/3rdparty/plibsys/src/pdir-none.c
new file mode 100644
index 0000000..eaaa8b9
--- /dev/null
+++ b/3rdparty/plibsys/src/pdir-none.c
@@ -0,0 +1,124 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2015-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pdir.h"
+
+#include <stdlib.h>
+
+struct PDir_ {
+	pint	hdl;
+};
+
+P_LIB_API PDir *
+p_dir_new (const pchar	*path,
+	   PError	**error)
+{
+	P_UNUSED (path);
+
+	p_error_set_error_p (error,
+			     (pint) P_ERROR_IO_NOT_IMPLEMENTED,
+			     0,
+			     "No directory implementation");
+
+	return NULL;
+}
+
+P_LIB_API pboolean
+p_dir_create (const pchar	*path,
+	      pint		mode,
+	      PError		**error)
+{
+	P_UNUSED (path);
+	P_UNUSED (mode);
+
+	p_error_set_error_p (error,
+			     (pint) P_ERROR_IO_NOT_IMPLEMENTED,
+			     0,
+			     "No directory implementation");
+
+	return FALSE;
+}
+
+P_LIB_API pboolean
+p_dir_remove (const pchar	*path,
+	      PError		**error)
+{
+	P_UNUSED (path);
+
+	p_error_set_error_p (error,
+			     (pint) P_ERROR_IO_NOT_IMPLEMENTED,
+			     0,
+			     "No directory implementation");
+
+	return FALSE;
+}
+
+P_LIB_API pboolean
+p_dir_is_exists (const pchar *path)
+{
+	P_UNUSED (path);
+	return FALSE;
+}
+
+P_LIB_API pchar *
+p_dir_get_path (const PDir *dir)
+{
+	P_UNUSED (dir);
+	return NULL;
+}
+
+P_LIB_API PDirEntry *
+p_dir_get_next_entry (PDir	*dir,
+		      PError	**error)
+{
+	P_UNUSED (dir);
+
+	p_error_set_error_p (error,
+			     (pint) P_ERROR_IO_NOT_IMPLEMENTED,
+			     0,
+			     "No directory implementation");
+
+	return NULL;
+}
+
+P_LIB_API pboolean
+p_dir_rewind (PDir	*dir,
+	      PError	**error)
+{
+	P_UNUSED (dir);
+
+	p_error_set_error_p (error,
+			     (pint) P_ERROR_IO_NOT_IMPLEMENTED,
+			     0,
+			     "No directory implementation");
+
+	return FALSE;
+}
+
+P_LIB_API void
+p_dir_free (PDir *dir)
+{
+	P_UNUSED (dir);
+}
diff --git a/3rdparty/plibsys/src/pdir-os2.c b/3rdparty/plibsys/src/pdir-os2.c
new file mode 100644
index 0000000..8869329
--- /dev/null
+++ b/3rdparty/plibsys/src/pdir-os2.c
@@ -0,0 +1,381 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pdir.h"
+#include "perror.h"
+#include "pmem.h"
+#include "pstring.h"
+#include "perror-private.h"
+
+#define INCL_DOSFILEMGR
+#define INCL_DOSERRORS
+#include <os2.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+struct PDir_ {
+	FILEFINDBUF3	find_data;
+	HDIR		search_handle;
+	pboolean	cached;
+	pchar		path[CCHMAXPATH];
+	pchar		*orig_path;
+};
+
+P_LIB_API PDir *
+p_dir_new (const pchar	*path,
+	   PError	**error)
+{
+	PDir	*ret;
+	pchar	*pathp;
+	pchar	*adj_path;
+	pint	path_len;
+	APIRET	ulrc;
+	ULONG	find_count;
+
+	if (P_UNLIKELY (path == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PDir))) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for directory structure");
+		return NULL;
+	}
+
+	adj_path = NULL;
+	path_len = strlen (path);
+
+	if (P_UNLIKELY (path[path_len - 1] == '\\' || path[path_len - 1] == '/') && path_len > 1) {
+		while ((path[path_len - 1] == '\\' || path[path_len - 1] == '/') && path_len > 1)
+			--path_len;
+
+		if (P_UNLIKELY ((adj_path = p_malloc0 (path_len + 1)) == NULL)) {
+			p_free (ret);
+			p_error_set_error_p (error,
+					     (pint) P_ERROR_IO_NO_RESOURCES,
+					     0,
+					     "Failed to allocate memory for directory path");
+			return NULL;
+		}
+
+		memcpy (adj_path, path, path_len);
+		
+		adj_path[path_len] = '\0';
+		ret->orig_path = p_strdup (path);
+		path = (const pchar *) adj_path;
+	}
+
+	ret->search_handle = HDIR_CREATE;
+
+	ulrc = DosQueryPathInfo ((PSZ) path, FIL_QUERYFULLNAME, ret->path, sizeof (ret->path) - 2);
+
+	if (P_UNLIKELY (ulrc != NO_ERROR)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_io_from_system ((pint) ulrc),
+				     (pint) ulrc,
+				     "Failed to call DosQueryPathInfo() to get directory path");
+
+		if (P_UNLIKELY (adj_path != NULL)) {
+			p_free (adj_path);
+			p_free (ret->orig_path);
+		}
+
+		p_free (ret);
+		return NULL;
+	}
+
+	/* Append the search pattern "\\*\0" to the directory name */
+	pathp = strchr (ret->path, '\0');
+
+	if (ret->path < pathp  &&  *(pathp - 1) != '\\'  &&  *(pathp - 1) != ':')
+		*pathp++ = '\\';
+
+	*pathp++ = '*';
+	*pathp = '\0';
+
+	find_count = 1;
+
+	/* Open directory stream and retrieve the first entry */
+	ulrc = DosFindFirst (ret->path,
+			     &ret->search_handle,
+			     FILE_NORMAL | FILE_DIRECTORY,
+			     &ret->find_data,
+			     sizeof (ret->find_data),
+			     &find_count,
+			     FIL_STANDARD);
+
+	if (P_UNLIKELY (ulrc != NO_ERROR && ulrc != ERROR_NO_MORE_FILES)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_io_from_system ((pint) ulrc),
+				     (pint) ulrc,
+				     "Failed to call DosFindFirst() to open directory stream");
+
+		if (P_UNLIKELY (adj_path != NULL)) {
+			p_free (adj_path);
+			p_free (ret->orig_path);
+		}
+
+		p_free (ret);
+		return NULL;
+	}
+
+	ret->cached = TRUE;
+
+	if (P_UNLIKELY (adj_path != NULL))
+		p_free (adj_path);
+	else
+		ret->orig_path = p_strdup (path);
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_dir_create (const pchar	*path,
+	      pint		mode,
+	      PError		**error)
+{
+	APIRET ulrc;
+
+	P_UNUSED (mode);
+
+	if (P_UNLIKELY (path == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	if (p_dir_is_exists (path))
+		return TRUE;
+
+	if (P_UNLIKELY ((ulrc = DosCreateDir ((PSZ) path, NULL)) != NO_ERROR)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_io_from_system ((pint) ulrc),
+				     (pint) ulrc,
+				     "Failed to call DosCreateDir() to create directory");
+		return FALSE;
+	} else
+		return TRUE;
+}
+
+P_LIB_API pboolean
+p_dir_remove (const pchar	*path,
+	      PError		**error)
+{
+	APIRET ulrc;
+
+	if (P_UNLIKELY (path == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	if (!p_dir_is_exists (path)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_NOT_EXISTS,
+				     0,
+				     "Specified directory doesn't exist");
+		return FALSE;
+	}
+
+	if (P_UNLIKELY ((ulrc = DosDeleteDir ((PSZ) path)) != NO_ERROR)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_io_from_system ((pint) ulrc),
+				     (pint) ulrc,
+				     "Failed to call DosDeleteDir() to remove directory");
+		return FALSE;
+	} else
+		return TRUE;
+}
+
+P_LIB_API pboolean
+p_dir_is_exists (const pchar *path)
+{
+	FILESTATUS3	status;
+
+	if (P_UNLIKELY (path == NULL))
+		return FALSE;
+
+	if (DosQueryPathInfo ((PSZ) path, FIL_STANDARD, (PVOID) &status, sizeof (status)) != NO_ERROR)
+		return FALSE;
+
+	return (status.attrFile & FILE_DIRECTORY) != 0;
+}
+
+P_LIB_API pchar *
+p_dir_get_path (const PDir *dir)
+{
+	if (P_UNLIKELY (dir == NULL))
+		return NULL;
+
+	return p_strdup (dir->orig_path);
+}
+
+P_LIB_API PDirEntry *
+p_dir_get_next_entry (PDir	*dir,
+		      PError	**error)
+{
+	PDirEntry	*ret;
+	APIRET		ulrc;
+	ULONG		find_count;
+
+	if (P_UNLIKELY (dir == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return NULL;
+	}
+
+	if (dir->cached == TRUE) {
+		dir->cached = FALSE;
+
+		/* Opened directory is empty */
+		if (P_UNLIKELY (dir->search_handle == HDIR_CREATE)) {
+			p_error_set_error_p (error,
+					     (pint) P_ERROR_IO_NO_MORE,
+					     (pint) ERROR_NO_MORE_FILES,
+					     "Directory is empty to get the next entry");
+			return NULL;
+		}
+	} else {
+		if (P_UNLIKELY (dir->search_handle == HDIR_CREATE)) {
+			p_error_set_error_p (error,
+					     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+					     0,
+					     "Not a valid (or closed) directory stream");
+			return NULL;
+		}
+
+		find_count = 1;
+
+		ulrc = DosFindNext (dir->search_handle,
+				    (PVOID) &dir->find_data,
+				    sizeof (dir->find_data),
+				    &find_count);
+
+		if (P_UNLIKELY (ulrc != NO_ERROR)) {
+			p_error_set_error_p (error,
+					     (pint) p_error_get_io_from_system ((pint) ulrc),
+					     (pint) ulrc,
+					     "Failed to call DosFindNext() to read directory stream");
+			DosFindClose (dir->search_handle);
+			dir->search_handle = HDIR_CREATE;
+			return NULL;
+		}
+	}
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PDirEntry))) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for directory entry");
+		return NULL;
+	}
+
+	ret->name = p_strdup (dir->find_data.achName);
+
+	if ((dir->find_data.attrFile & FILE_DIRECTORY) != 0)
+		ret->type = P_DIR_ENTRY_TYPE_DIR;
+	else
+		ret->type = P_DIR_ENTRY_TYPE_FILE;
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_dir_rewind (PDir	*dir,
+	      PError	**error)
+{
+	APIRET	ulrc;
+	ULONG	find_count;
+
+	if (P_UNLIKELY (dir == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	if (dir->search_handle != HDIR_CREATE) {
+		if (P_UNLIKELY ((ulrc = DosFindClose (dir->search_handle)) != NO_ERROR)) {
+			p_error_set_error_p (error,
+					     (pint) p_error_get_io_from_system ((pint) ulrc),
+					     (pint) ulrc,
+					     "Failed to call DosFindClose() to close directory stream");
+			return FALSE;
+		}
+
+		dir->search_handle = HDIR_CREATE;
+	}
+
+	find_count = 1;
+
+	ulrc = DosFindFirst (dir->path,
+			     &dir->search_handle,
+			     FILE_NORMAL | FILE_DIRECTORY,
+			     &dir->find_data,
+			     sizeof (dir->find_data),
+			     &find_count,
+			     FIL_STANDARD);
+
+	if (P_UNLIKELY (ulrc != NO_ERROR && ulrc != ERROR_NO_MORE_FILES)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_io_from_system ((pint) ulrc),
+				     (pint) ulrc,
+				     "Failed to call DosFindFirst() to open directory stream");
+		dir->cached = FALSE;
+		return FALSE;
+	} else {
+		dir->cached = TRUE;
+		return TRUE;
+	}
+}
+
+P_LIB_API void
+p_dir_free (PDir *dir)
+{
+	if (dir == NULL)
+		return;
+
+	if (P_LIKELY (dir->search_handle != HDIR_CREATE)) {
+		if (P_UNLIKELY (DosFindClose (dir->search_handle) != NO_ERROR))
+			P_ERROR ("PDir::p_dir_free: DosFindClose() failed");
+	}
+
+	p_free (dir->orig_path);
+	p_free (dir);
+}
diff --git a/3rdparty/plibsys/src/pdir-posix.c b/3rdparty/plibsys/src/pdir-posix.c
new file mode 100644
index 0000000..22b7863
--- /dev/null
+++ b/3rdparty/plibsys/src/pdir-posix.c
@@ -0,0 +1,378 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2015-2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pdir.h"
+#include "perror.h"
+#include "pfile.h"
+#include "pmem.h"
+#include "pstring.h"
+#include "perror-private.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#if defined (__GLIBC__) && (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 24)
+#  define P_DIR_NON_REENTRANT 1
+# elif defined (P_OS_SOLARIS) || defined (P_OS_QNX6) || defined (P_OS_UNIXWARE) || \
+       defined (P_OS_SCO)     || defined (P_OS_IRIX) || defined (P_OS_HAIKU)
+#  define P_DIR_NEED_BUF_ALLOC 1
+#endif
+
+#ifdef P_DIR_NEED_BUF_ALLOC
+#  if defined (P_OS_SCO)
+#    define P_DIR_NEED_SIMPLE_R 1
+#  endif
+#else
+#  if defined (P_OS_BEOS) || defined (P_OS_AMIGA)
+#    define P_DIR_NON_REENTRANT 1
+#  endif
+#endif
+
+struct PDir_ {
+	DIR *		dir;
+	struct dirent	*dir_result;
+	pchar		*path;
+	pchar		*orig_path;
+};
+
+P_LIB_API PDir *
+p_dir_new (const pchar	*path,
+	   PError	**error)
+{
+	PDir	*ret;
+	DIR	*dir;
+	pchar	*pathp;
+
+	if (P_UNLIKELY (path == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((dir = opendir (path)) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_io (),
+				     p_error_get_last_system (),
+				     "Failed to call opendir() to open directory stream");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PDir))) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for directory structure");
+		closedir (dir);
+		return NULL;
+	}
+
+	ret->dir       = dir;
+	ret->path      = p_strdup (path);
+	ret->orig_path = p_strdup (path);
+
+	pathp = ret->path + strlen (ret->path) - 1;
+
+	if (*pathp == '/' || *pathp == '\\')
+		*pathp = '\0';
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_dir_create (const pchar	*path,
+	      pint		mode,
+	      PError		**error)
+{
+	if (P_UNLIKELY (path == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	if (p_dir_is_exists (path))
+		return TRUE;
+
+	if (P_UNLIKELY (mkdir (path, (mode_t) mode) != 0)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_io (),
+				     p_error_get_last_system (),
+				     "Failed to call mkdir() to create directory");
+		return FALSE;
+	} else
+		return TRUE;
+}
+
+P_LIB_API pboolean
+p_dir_remove (const pchar	*path,
+	      PError		**error)
+{
+	if (P_UNLIKELY (path == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	if (!p_dir_is_exists (path)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_NOT_EXISTS,
+				     0,
+				     "Specified directory doesn't exist");
+		return FALSE;
+	}
+
+	if (P_UNLIKELY (rmdir (path) != 0)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_io (),
+				     p_error_get_last_system (),
+				     "Failed to call rmdir() to remove directory");
+		return FALSE;
+	} else
+		return TRUE;
+}
+
+P_LIB_API pboolean
+p_dir_is_exists (const pchar *path)
+{
+	struct stat sb;
+
+	if (P_UNLIKELY (path == NULL))
+		return FALSE;
+
+	return (stat (path, &sb) == 0 && S_ISDIR (sb.st_mode)) ? TRUE : FALSE;
+}
+
+P_LIB_API pchar *
+p_dir_get_path (const PDir *dir)
+{
+	if (P_UNLIKELY (dir == NULL))
+		return NULL;
+
+	return p_strdup (dir->orig_path);
+}
+
+P_LIB_API PDirEntry *
+p_dir_get_next_entry (PDir	*dir,
+		      PError	**error)
+{
+	PDirEntry	*ret;
+#ifdef P_DIR_NEED_BUF_ALLOC
+	struct dirent	*dirent_st;
+#elif !defined (P_DIR_NON_REENTRANT)
+	struct dirent	dirent_st;
+#endif
+	struct stat	sb;
+	pchar		*entry_path;
+	psize		path_len;
+#ifdef P_DIR_NEED_BUF_ALLOC
+	pint		name_max;
+#endif
+
+	if (P_UNLIKELY (dir == NULL || dir->dir == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return NULL;
+	}
+
+#ifdef P_DIR_NEED_BUF_ALLOC
+#  if defined (P_OS_SOLARIS)
+	name_max = (pint) (FILENAME_MAX);
+#  elif defined (P_OS_SCO) || defined (P_OS_IRIX)
+	name_max = (pint) pathconf (dir->orig_path, _PC_NAME_MAX);
+
+	if (name_max == -1) {
+		if (p_error_get_last_system () == 0)
+			name_max = _POSIX_PATH_MAX;
+		else {
+			p_error_set_error_p (error,
+					     (pint) P_ERROR_IO_FAILED,
+					     0,
+					     "Failed to get NAME_MAX using pathconf()");
+			return NULL;
+		}
+	}
+#  elif defined (P_OS_QNX6) || defined (P_OS_UNIXWARE) || defined (P_OS_HAIKU)
+	name_max = (pint) (NAME_MAX);
+#  endif
+
+	if (P_UNLIKELY ((dirent_st = p_malloc0 (sizeof (struct dirent) + name_max + 1)) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for internal directory entry");
+		return NULL;
+	}
+
+#  ifdef P_DIR_NEED_SIMPLE_R
+	p_error_set_last_system (0);
+
+	if ((dir->dir_result = readdir_r (dir->dir, dirent_st)) == NULL) {
+		if (P_UNLIKELY (p_error_get_last_system () != 0)) {
+			p_error_set_error_p (error,
+					     (pint) p_error_get_last_io (),
+					     p_error_get_last_system (),
+					     "Failed to call readdir_r() to read directory stream");
+			p_free (dirent_st);
+			return NULL;
+		}
+	}
+#  else
+	if (P_UNLIKELY (readdir_r (dir->dir, dirent_st, &dir->dir_result) != 0)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_io (),
+				     p_error_get_last_system (),
+				     "Failed to call readdir_r() to read directory stream");
+		p_free (dirent_st);
+		return NULL;
+	}
+#  endif
+#else
+#  ifdef P_DIR_NON_REENTRANT
+	p_error_set_last_system (0);
+
+	if ((dir->dir_result = readdir (dir->dir)) == NULL) {
+		if (P_UNLIKELY (p_error_get_last_system () != 0)) {
+			p_error_set_error_p (error,
+					     (pint) p_error_get_last_io (),
+					     p_error_get_last_system (),
+					     "Failed to call readdir() to read directory stream");
+			return NULL;
+		}
+	}
+#  else
+	if (P_UNLIKELY (readdir_r (dir->dir, &dirent_st, &dir->dir_result) != 0)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_io (),
+				     p_error_get_last_system (),
+				     "Failed to call readdir_r() to read directory stream");
+		return NULL;
+	}
+#  endif
+#endif
+
+	if (dir->dir_result == NULL) {
+#ifdef P_DIR_NEED_BUF_ALLOC
+		p_free (dirent_st);
+#endif
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PDirEntry))) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for directory entry");
+#ifdef P_DIR_NEED_BUF_ALLOC
+		p_free (dirent_st);
+#endif
+		return NULL;
+	}
+
+#ifdef P_DIR_NEED_BUF_ALLOC
+	ret->name = p_strdup (dirent_st->d_name);
+	p_free (dirent_st);
+#else
+#  ifdef P_DIR_NON_REENTRANT
+	ret->name = p_strdup (dir->dir_result->d_name);
+#  else
+	ret->name = p_strdup (dirent_st.d_name);
+#  endif
+#endif
+
+	path_len = strlen (dir->path);
+
+	if (P_UNLIKELY ((entry_path = p_malloc0 (path_len + strlen (ret->name) + 2)) == NULL)) {
+		P_WARNING ("PDir::p_dir_get_next_entry: failed to allocate memory for stat()");
+		ret->type = P_DIR_ENTRY_TYPE_OTHER;
+		return ret;
+	}
+
+	strcat (entry_path, dir->path);
+	*(entry_path + path_len) = '/';
+	strcat (entry_path + path_len + 1, ret->name);
+
+	if (P_UNLIKELY (stat (entry_path, &sb) != 0)) {
+		P_WARNING ("PDir::p_dir_get_next_entry: stat() failed");
+		ret->type = P_DIR_ENTRY_TYPE_OTHER;
+		p_free (entry_path);
+		return ret;
+	}
+
+	p_free (entry_path);
+
+	if (S_ISDIR (sb.st_mode))
+		ret->type = P_DIR_ENTRY_TYPE_DIR;
+	else if (S_ISREG (sb.st_mode))
+		ret->type = P_DIR_ENTRY_TYPE_FILE;
+	else
+		ret->type = P_DIR_ENTRY_TYPE_OTHER;
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_dir_rewind (PDir	*dir,
+	      PError	**error)
+{
+	if (P_UNLIKELY (dir == NULL || dir->dir == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	rewinddir (dir->dir);
+
+	return TRUE;
+}
+
+P_LIB_API void
+p_dir_free (PDir *dir)
+{
+	if (P_UNLIKELY (dir == NULL))
+		return;
+
+	if (P_LIKELY (dir->dir != NULL)) {
+		if (P_UNLIKELY (closedir (dir->dir) != 0))
+			P_ERROR ("PDir::p_dir_free: closedir() failed");
+	}
+
+	p_free (dir->path);
+	p_free (dir->orig_path);
+	p_free (dir);
+}
diff --git a/3rdparty/plibsys/src/pdir-win.c b/3rdparty/plibsys/src/pdir-win.c
new file mode 100644
index 0000000..8c6df15
--- /dev/null
+++ b/3rdparty/plibsys/src/pdir-win.c
@@ -0,0 +1,291 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2015-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pdir.h"
+#include "perror.h"
+#include "pmem.h"
+#include "pstring.h"
+#include "perror-private.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+struct PDir_ {
+	WIN32_FIND_DATAA	find_data;
+	HANDLE			search_handle;
+	pboolean		cached;
+	pchar			path[MAX_PATH + 3];
+	pchar			*orig_path;
+};
+
+P_LIB_API PDir *
+p_dir_new (const pchar	*path,
+	   PError	**error)
+{
+	PDir	*ret;
+	pchar	*pathp;
+
+	if (P_UNLIKELY (path == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PDir))) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for directory structure");
+		return NULL;
+	}
+
+	if (P_UNLIKELY (!GetFullPathNameA (path, MAX_PATH, ret->path, NULL))) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_io (),
+				     p_error_get_last_system (),
+				     "Failed to call GetFullPathNameA() to get directory path");
+		p_free (ret);
+		return NULL;
+	}
+
+	/* Append the search pattern "\\*\0" to the directory name */
+	pathp = strchr (ret->path, '\0');
+
+	if (ret->path < pathp  &&  *(pathp - 1) != '\\'  &&  *(pathp - 1) != ':')
+		*pathp++ = '\\';
+
+	*pathp++ = '*';
+	*pathp = '\0';
+
+	/* Open directory stream and retrieve the first entry */
+	ret->search_handle = FindFirstFileA (ret->path, &ret->find_data);
+
+	if (P_UNLIKELY (ret->search_handle == INVALID_HANDLE_VALUE)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_io (),
+				     p_error_get_last_system (),
+				     "Failed to call FindFirstFileA() to open directory stream");
+		p_free (ret);
+		return NULL;
+	}
+
+	ret->cached    = TRUE;
+	ret->orig_path = p_strdup (path);
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_dir_create (const pchar	*path,
+	      pint		mode,
+	      PError		**error)
+{
+	P_UNUSED (mode);
+
+	if (P_UNLIKELY (path == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	if (p_dir_is_exists (path))
+		return TRUE;
+
+	if (P_UNLIKELY (CreateDirectoryA (path, NULL) == 0)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_io (),
+				     p_error_get_last_system (),
+				     "Failed to call CreateDirectoryA() to create directory");
+		return FALSE;
+	} else
+		return TRUE;
+}
+
+P_LIB_API pboolean
+p_dir_remove (const pchar	*path,
+	      PError		**error)
+{
+	if (P_UNLIKELY (path == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	if (!p_dir_is_exists (path)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_NOT_EXISTS,
+				     0,
+				     "Specified directory doesn't exist");
+		return FALSE;
+	}
+
+	if (P_UNLIKELY (RemoveDirectoryA (path) == 0)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_io (),
+				     p_error_get_last_system (),
+				     "Failed to call RemoveDirectoryA() to remove directory");
+		return FALSE;
+	} else
+		return TRUE;
+}
+
+P_LIB_API pboolean
+p_dir_is_exists (const pchar *path)
+{
+	DWORD	dwAttrs;
+
+	if (P_UNLIKELY (path == NULL))
+		return FALSE;
+
+	dwAttrs = GetFileAttributesA (path);
+
+	return (dwAttrs != INVALID_FILE_ATTRIBUTES) && (dwAttrs & FILE_ATTRIBUTE_DIRECTORY);
+}
+
+P_LIB_API pchar *
+p_dir_get_path (const PDir *dir)
+{
+	if (P_UNLIKELY (dir == NULL))
+		return NULL;
+
+	return p_strdup (dir->orig_path);
+}
+
+P_LIB_API PDirEntry *
+p_dir_get_next_entry (PDir	*dir,
+		      PError	**error)
+{
+	PDirEntry	*ret;
+	DWORD		dwAttrs;
+
+	if (P_UNLIKELY (dir == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return NULL;
+	}
+
+	if (dir->cached == TRUE)
+		dir->cached = FALSE;
+	else {
+		if (P_UNLIKELY (dir->search_handle == INVALID_HANDLE_VALUE)) {
+			p_error_set_error_p (error,
+					     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+					     0,
+					     "Not a valid (or closed) directory stream");
+			return NULL;
+		}
+
+		if (P_UNLIKELY (!FindNextFileA (dir->search_handle, &dir->find_data))) {
+			p_error_set_error_p (error,
+					     (pint) p_error_get_last_io (),
+					     p_error_get_last_system (),
+					     "Failed to call FindNextFileA() to read directory stream");
+			FindClose (dir->search_handle);
+			dir->search_handle = INVALID_HANDLE_VALUE;
+			return NULL;
+		}
+	}
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PDirEntry))) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for directory entry");
+		return NULL;
+	}
+
+	ret->name = p_strdup (dir->find_data.cFileName);
+
+	dwAttrs = dir->find_data.dwFileAttributes;
+
+	if (dwAttrs & FILE_ATTRIBUTE_DIRECTORY)
+		ret->type = P_DIR_ENTRY_TYPE_DIR;
+	else if (dwAttrs & FILE_ATTRIBUTE_DEVICE)
+		ret->type = P_DIR_ENTRY_TYPE_OTHER;
+	else
+		ret->type = P_DIR_ENTRY_TYPE_FILE;
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_dir_rewind (PDir	*dir,
+	      PError	**error)
+{
+	if (P_UNLIKELY (dir == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	if (dir->search_handle != INVALID_HANDLE_VALUE) {
+		if (P_UNLIKELY (FindClose (dir->search_handle) == 0)) {
+			p_error_set_error_p (error,
+					     (pint) p_error_get_last_io (),
+					     p_error_get_last_system (),
+					     "Failed to call FindClose() to close directory stream");
+			return FALSE;
+		}
+	}
+
+	dir->search_handle = FindFirstFileA (dir->path, &dir->find_data);
+
+	if (P_UNLIKELY (dir->search_handle == INVALID_HANDLE_VALUE)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_io (),
+				     p_error_get_last_system (),
+				     "Failed to call FindFirstFileA() to open directory stream");
+		dir->cached = FALSE;
+		return FALSE;
+	} else {
+		dir->cached = TRUE;
+		return TRUE;
+	}
+}
+
+P_LIB_API void
+p_dir_free (PDir *dir)
+{
+	if (dir == NULL)
+		return;
+
+	if (P_LIKELY (dir->search_handle != INVALID_HANDLE_VALUE)) {
+		if (P_UNLIKELY (!FindClose (dir->search_handle)))
+			P_ERROR ("PDir::p_dir_free: FindClose() failed");
+	}
+
+	p_free (dir->orig_path);
+	p_free (dir);
+}
diff --git a/3rdparty/plibsys/src/pdir.c b/3rdparty/plibsys/src/pdir.c
new file mode 100644
index 0000000..4166cf7
--- /dev/null
+++ b/3rdparty/plibsys/src/pdir.c
@@ -0,0 +1,37 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2015-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "pdir.h"
+
+P_LIB_API void
+p_dir_entry_free (PDirEntry *entry)
+{
+	if (P_UNLIKELY (entry == NULL))
+		return;
+
+	p_free (entry->name);
+	p_free (entry);
+}
diff --git a/3rdparty/plibsys/src/pdir.h b/3rdparty/plibsys/src/pdir.h
new file mode 100644
index 0000000..9438a34
--- /dev/null
+++ b/3rdparty/plibsys/src/pdir.h
@@ -0,0 +1,177 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2015-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file pdir.h
+ * @brief Filesystem interface
+ * @author Alexander Saprykin
+ *
+ * A traditional filesystem can be presented as a combination of directories and
+ * files within a defined hierarchy. A directory contains the so called entries:
+ * files and other directories. #PDir allows to iterate through these entries
+ * without reading their contents, thus building a filesystem hierarchy tree.
+ *
+ * Think of this module as an interface to the well-known `dirent` API.
+ *
+ * First you need to open a directory for iterating through its content entries
+ * using p_dir_new(). After that every next entry inside the directory can be
+ * read with the p_dir_get_next_entry() call until it returns NULL (though it's
+ * better to check an error code to be sure no error occurred).
+ *
+ * Also some directory manipulation routines are provided to create, remove and
+ * check existance.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PDIR_H
+#define PLIBSYS_HEADER_PDIR_H
+
+#include <pmacros.h>
+#include <ptypes.h>
+#include <perror.h>
+
+P_BEGIN_DECLS
+
+/** Directory opaque data structure. */
+typedef struct PDir_ PDir;
+
+/** Directory entry types. */
+typedef enum PDirEntryType_ {
+	P_DIR_ENTRY_TYPE_DIR	= 1,	/**< Directory.	*/
+	P_DIR_ENTRY_TYPE_FILE	= 2,	/**< File.	*/
+	P_DIR_ENTRY_TYPE_OTHER	= 3	/**< Other.	*/
+} PDirEntryType;
+
+/** Structure with directory entry information. */
+typedef struct PDirEntry_ {
+	char		*name;	/**< Name.	*/
+	PDirEntryType	type;	/**< Type.	*/
+} PDirEntry;
+
+/**
+ * @brief Creates a new #PDir object.
+ * @param path Directory path.
+ * @return Pointer to a newly created #PDir object in case of success, NULL
+ * otherwise.
+ * @param[out] error Error report object, NULL to ignore.
+ * @since 0.0.1
+ * @note If you want to create a new directory on a filesystem, use
+ * p_dir_create() instead.
+ */
+P_LIB_API PDir *	p_dir_new		(const pchar	*path,
+						 PError		**error);
+
+/**
+ * @brief Creates a new directory on a filesystem.
+ * @param path Directory path.
+ * @param mode Directory permissions to use, ignored on Windows.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ * @note Call returns TRUE if the directory @a path is already exists.
+ * @note On OpenVMS operating system it creates intermediate directories as
+ * well.
+ */
+P_LIB_API pboolean	p_dir_create		(const pchar	*path,
+						 pint		mode,
+						 PError		**error);
+
+/**
+ * @brief Removes an empty directory.
+ * @param path Directory path to remove.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ *
+ * The directory @a path should be empty to be removed successfully.
+ */
+P_LIB_API pboolean	p_dir_remove		(const pchar	*path,
+						 PError		**error);
+
+/**
+ * @brief Checks whether a directory exists or not.
+ * @param path Directory path.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API pboolean	p_dir_is_exists		(const pchar	*path);
+
+/**
+ * @brief Gets the original directory path used to create a #PDir object.
+ * @param dir #PDir object to retrieve the path from.
+ * @return The directory path in case of success, NULL otherwise.
+ * @since 0.0.1
+ *
+ * Caller takes ownership of the returned string. Use p_free() to free memory
+ * after usage.
+ */
+P_LIB_API pchar *	p_dir_get_path		(const PDir	*dir);
+
+/**
+ * @brief Gets the next directory entry info.
+ * @param dir Directory to get the next entry from.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return Info for the next entry in case of success, NULL otherwise.
+ * @since 0.0.1
+ *
+ * Caller takes ownership of the returned object. Use p_dir_entry_free() to free
+ * memory of the directory entry after usage.
+ *
+ * An error is set only if it is occurred. You should check the @a error object
+ * for #P_ERROR_IO_NO_MORE code.
+ */
+P_LIB_API PDirEntry *	p_dir_get_next_entry	(PDir		*dir,
+						 PError		**error);
+
+/**
+ * @brief Resets a directory entry pointer.
+ * @param dir Directory to reset the entry pointer.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API pboolean	p_dir_rewind		(PDir		*dir,
+						 PError		**error);
+
+/**
+ * @brief Frees #PDirEntry object.
+ * @param entry #PDirEntry to free.
+ * @since 0.0.1
+ */
+P_LIB_API void		p_dir_entry_free	(PDirEntry	*entry);
+
+/**
+ * @brief Frees #PDir object.
+ * @param dir #PDir to free.
+ * @since 0.0.1
+ */
+P_LIB_API void		p_dir_free		(PDir		*dir);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PDIR_H */
diff --git a/3rdparty/plibsys/src/perror-private.h b/3rdparty/plibsys/src/perror-private.h
new file mode 100644
index 0000000..b108583
--- /dev/null
+++ b/3rdparty/plibsys/src/perror-private.h
@@ -0,0 +1,67 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PERROR_PRIVATE_H
+#define PLIBSYS_HEADER_PERROR_PRIVATE_H
+
+#include "pmacros.h"
+#include "ptypes.h"
+#include "perrortypes.h"
+
+P_BEGIN_DECLS
+
+/**
+ * @brief Gets an IO error code from a system error code.
+ * @param err_code System error code.
+ * @return IO error code.
+ */
+PErrorIO	p_error_get_io_from_system	(pint	err_code);
+
+/**
+ * @brief Gets an IO error code from the last call result.
+ * @return IO error code.
+ */
+PErrorIO	p_error_get_last_io		(void);
+
+/**
+ * @brief Gets an IPC error code from a system error code
+ * @param err_code System error code.
+ * @return IPC error code.
+ */
+PErrorIPC	p_error_get_ipc_from_system	(pint	err_code);
+
+/**
+ * @brief Gets an IPC error code from the last call result.
+ * @return IPC error code.
+ */
+PErrorIPC	p_error_get_last_ipc		(void);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PERROR_PRIVATE_H */
diff --git a/3rdparty/plibsys/src/perror.c b/3rdparty/plibsys/src/perror.c
new file mode 100644
index 0000000..c26dfa1
--- /dev/null
+++ b/3rdparty/plibsys/src/perror.c
@@ -0,0 +1,878 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016-2018 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "perror.h"
+#include "pmem.h"
+#include "pstring.h"
+#include "perror-private.h"
+
+#ifndef P_OS_WIN
+#  if defined (P_OS_OS2)
+#    define INCL_DOSERRORS
+#    include <os2.h>
+#    include <sys/socket.h>
+#  endif
+#  include <errno.h>
+#endif
+
+struct PError_ {
+	pint	code;
+	pint	native_code;
+	pchar	*message;
+};
+
+PErrorIO
+p_error_get_io_from_system (pint err_code)
+{
+	switch (err_code) {
+	case 0:
+		return P_ERROR_IO_NONE;
+#if defined (P_OS_WIN)
+#  ifdef WSAEADDRINUSE
+	case WSAEADDRINUSE:
+		return P_ERROR_IO_ADDRESS_IN_USE;
+#  endif
+#  ifdef WSAEWOULDBLOCK
+	case WSAEWOULDBLOCK:
+		return P_ERROR_IO_WOULD_BLOCK;
+#  endif
+#  ifdef WSAEACCES
+	case WSAEACCES:
+		return P_ERROR_IO_ACCESS_DENIED;
+#  endif
+#  ifdef WSA_INVALID_HANDLE
+	case WSA_INVALID_HANDLE:
+		return P_ERROR_IO_INVALID_ARGUMENT;
+#  endif
+#  ifdef WSA_INVALID_PARAMETER
+	case WSA_INVALID_PARAMETER:
+		return P_ERROR_IO_INVALID_ARGUMENT;
+#  endif
+#  ifdef WSAEBADF
+	case WSAEBADF:
+		return P_ERROR_IO_INVALID_ARGUMENT;
+#  endif
+#  ifdef WSAENOTSOCK
+	case WSAENOTSOCK:
+		return P_ERROR_IO_INVALID_ARGUMENT;
+#  endif
+#  ifdef WSAEINVAL
+	case WSAEINVAL:
+		return P_ERROR_IO_INVALID_ARGUMENT;
+#  endif
+#  ifdef WSAESOCKTNOSUPPORT
+	case WSAESOCKTNOSUPPORT:
+		return P_ERROR_IO_NOT_SUPPORTED;
+#  endif
+#  ifdef WSAEOPNOTSUPP
+	case WSAEOPNOTSUPP:
+		return P_ERROR_IO_NOT_SUPPORTED;
+#  endif
+#  ifdef WSAEPFNOSUPPORT
+	case WSAEPFNOSUPPORT:
+		return P_ERROR_IO_NOT_SUPPORTED;
+#  endif
+#  ifdef WSAEAFNOSUPPORT
+	case WSAEAFNOSUPPORT:
+		return P_ERROR_IO_NOT_SUPPORTED;
+#  endif
+#  ifdef WSAEPROTONOSUPPORT
+	case WSAEPROTONOSUPPORT:
+		return P_ERROR_IO_NOT_SUPPORTED;
+#  endif
+#  ifdef WSAECANCELLED
+	case WSAECANCELLED:
+		return P_ERROR_IO_ABORTED;
+#  endif
+#  ifdef ERROR_ALREADY_EXISTS
+	case ERROR_ALREADY_EXISTS:
+		return P_ERROR_IO_EXISTS;
+#  endif
+#  ifdef ERROR_FILE_NOT_FOUND
+	case ERROR_FILE_NOT_FOUND:
+		return P_ERROR_IO_NOT_EXISTS;
+#  endif
+#  ifdef ERROR_NO_MORE_FILES
+	case ERROR_NO_MORE_FILES:
+		return P_ERROR_IO_NO_MORE;
+#  endif
+#  ifdef ERROR_ACCESS_DENIED
+	case ERROR_ACCESS_DENIED:
+		return P_ERROR_IO_ACCESS_DENIED;
+#  endif
+#  ifdef ERROR_OUTOFMEMORY
+	case ERROR_OUTOFMEMORY:
+		return P_ERROR_IO_NO_RESOURCES;
+#  endif
+#  ifdef ERROR_NOT_ENOUGH_MEMORY
+	case ERROR_NOT_ENOUGH_MEMORY:
+		return P_ERROR_IO_NO_RESOURCES;
+#  endif
+#  ifdef ERROR_INVALID_HANDLE
+#    if !defined(WSA_INVALID_HANDLE) || (ERROR_INVALID_HANDLE != WSA_INVALID_HANDLE)
+	case ERROR_INVALID_HANDLE:
+		return P_ERROR_IO_INVALID_ARGUMENT;
+#    endif
+#  endif
+#  ifdef ERROR_INVALID_PARAMETER
+#    if !defined(WSA_INVALID_PARAMETER) || (ERROR_INVALID_PARAMETER != WSA_INVALID_PARAMETER)
+	case ERROR_INVALID_PARAMETER:
+		return P_ERROR_IO_INVALID_ARGUMENT;
+#    endif
+#  endif
+#  ifdef ERROR_NOT_SUPPORTED
+	case ERROR_NOT_SUPPORTED:
+		return P_ERROR_IO_NOT_SUPPORTED;
+#  endif
+#elif defined (P_OS_OS2)
+#  ifdef ERROR_FILE_NOT_FOUND
+	case ERROR_FILE_NOT_FOUND:
+		return P_ERROR_IO_NOT_EXISTS;
+#  endif
+#  ifdef ERROR_PATH_NOT_FOUND
+	case ERROR_PATH_NOT_FOUND:
+		return P_ERROR_IO_NOT_EXISTS;
+#  endif
+#  ifdef ERROR_TOO_MANY_OPEN_FILES
+	case ERROR_TOO_MANY_OPEN_FILES:
+		return P_ERROR_IO_NO_RESOURCES;
+#  endif
+#  ifdef ERROR_NOT_ENOUGH_MEMORY
+	case ERROR_NOT_ENOUGH_MEMORY:
+		return P_ERROR_IO_NO_RESOURCES;
+#  endif
+#  ifdef ERROR_ACCESS_DENIED
+	case ERROR_ACCESS_DENIED:
+		return P_ERROR_IO_ACCESS_DENIED;
+#  endif
+#  ifdef ERROR_INVALID_HANDLE
+	case ERROR_INVALID_HANDLE:
+		return P_ERROR_IO_INVALID_ARGUMENT;
+#  endif
+#  ifdef ERROR_INVALID_PARAMETER
+	case ERROR_INVALID_PARAMETER:
+		return P_ERROR_IO_INVALID_ARGUMENT;
+#  endif
+#  ifdef ERROR_INVALID_ADDRESS
+	case ERROR_INVALID_ADDRESS:
+		return P_ERROR_IO_INVALID_ARGUMENT;
+#  endif
+#  ifdef ERROR_NO_MORE_FILES
+	case ERROR_NO_MORE_FILES:
+		return P_ERROR_IO_NO_MORE;
+#  endif
+#  ifdef ERROR_NOT_SUPPORTED
+	case ERROR_NOT_SUPPORTED:
+		return P_ERROR_IO_NOT_SUPPORTED;
+#  endif
+#  ifdef ERROR_FILE_EXISTS
+	case ERROR_FILE_EXISTS:
+		return P_ERROR_IO_EXISTS;
+#  endif
+#else /* !P_OS_WIN && !P_OS_OS2 */
+#  ifdef EACCES
+	case EACCES:
+		return P_ERROR_IO_ACCESS_DENIED;
+#  endif
+
+#  ifdef EPERM
+	case EPERM:
+		return P_ERROR_IO_ACCESS_DENIED;
+#  endif
+
+#  ifdef ENOMEM
+	case ENOMEM:
+		return P_ERROR_IO_NO_RESOURCES;
+#  endif
+
+#  ifdef ENOSR
+	case ENOSR:
+		return P_ERROR_IO_NO_RESOURCES;
+#  endif
+
+#  ifdef ENOBUFS
+	case ENOBUFS:
+		return P_ERROR_IO_NO_RESOURCES;
+#  endif
+
+#  ifdef ENFILE
+	case ENFILE:
+		return P_ERROR_IO_NO_RESOURCES;
+#  endif
+
+#  ifdef ENOSPC
+	case ENOSPC:
+		return P_ERROR_IO_NO_RESOURCES;
+#  endif
+
+#  ifdef EMFILE
+	case EMFILE:
+		return P_ERROR_IO_NO_RESOURCES;
+#  endif
+
+#  ifdef EINVAL
+	case EINVAL:
+		return P_ERROR_IO_INVALID_ARGUMENT;
+#  endif
+
+#  ifdef EBADF
+	case EBADF:
+		return P_ERROR_IO_INVALID_ARGUMENT;
+#  endif
+
+#  ifdef ENOTSOCK
+	case ENOTSOCK:
+		return P_ERROR_IO_INVALID_ARGUMENT;
+#  endif
+
+#  ifdef EFAULT
+	case EFAULT:
+		return P_ERROR_IO_INVALID_ARGUMENT;
+#  endif
+
+#  ifdef EPROTOTYPE
+	case EPROTOTYPE:
+		return P_ERROR_IO_INVALID_ARGUMENT;
+#  endif
+
+	/* On Linux these errors can have same codes */
+#  if defined(ENOTSUP) && (!defined(EOPNOTSUPP) || ENOTSUP != EOPNOTSUPP)
+	case ENOTSUP:
+			return P_ERROR_IO_NOT_SUPPORTED;
+#  endif
+
+#  ifdef ENOPROTOOPT
+	case ENOPROTOOPT:
+		return P_ERROR_IO_NOT_SUPPORTED;
+#  endif
+
+#  ifdef EPROTONOSUPPORT
+	case EPROTONOSUPPORT:
+		return P_ERROR_IO_NOT_SUPPORTED;
+#  endif
+
+#  ifdef EAFNOSUPPORT
+	case EAFNOSUPPORT:
+		return P_ERROR_IO_NOT_SUPPORTED;
+#  endif
+
+#  ifdef EOPNOTSUPP
+	case EOPNOTSUPP:
+		return P_ERROR_IO_NOT_SUPPORTED;
+#  endif
+
+#  ifdef EADDRNOTAVAIL
+	case EADDRNOTAVAIL:
+		return P_ERROR_IO_NOT_AVAILABLE;
+#  endif
+
+#  ifdef ENETUNREACH
+	case ENETUNREACH:
+		return P_ERROR_IO_NOT_AVAILABLE;
+#  endif
+
+#  ifdef ENETDOWN
+	case ENETDOWN:
+		return P_ERROR_IO_NOT_AVAILABLE;
+#  endif
+
+#  ifdef EHOSTDOWN
+	case EHOSTDOWN:
+		return P_ERROR_IO_NOT_AVAILABLE;
+#  endif
+
+#  ifdef ENONET
+	case ENONET:
+		return P_ERROR_IO_NOT_AVAILABLE;
+#  endif
+
+#  ifdef EHOSTUNREACH
+	case EHOSTUNREACH:
+		return P_ERROR_IO_NOT_AVAILABLE;
+#  endif
+
+#  ifdef EINPROGRESS
+	case EINPROGRESS:
+		return P_ERROR_IO_IN_PROGRESS;
+#  endif
+
+#  ifdef EALREADY
+	case EALREADY:
+		return P_ERROR_IO_IN_PROGRESS;
+#  endif
+
+#  ifdef EISCONN
+	case EISCONN:
+		return P_ERROR_IO_CONNECTED;
+#  endif
+
+#  ifdef ECONNREFUSED
+	case ECONNREFUSED:
+		return P_ERROR_IO_CONNECTION_REFUSED;
+#  endif
+
+#  ifdef ENOTCONN
+	case ENOTCONN:
+		return P_ERROR_IO_NOT_CONNECTED;
+#  endif
+
+#  ifdef ECONNABORTED
+	case ECONNABORTED:
+		return P_ERROR_IO_ABORTED;
+#  endif
+
+#  ifdef EADDRINUSE
+	case EADDRINUSE:
+		return P_ERROR_IO_ADDRESS_IN_USE;
+#  endif
+
+#  ifdef ETIMEDOUT
+	case ETIMEDOUT:
+		return P_ERROR_IO_TIMED_OUT;
+#  endif
+
+#  ifdef EDQUOT
+	case EDQUOT:
+		return P_ERROR_IO_QUOTA;
+#  endif
+
+#  ifdef EISDIR
+	case EISDIR:
+		return P_ERROR_IO_IS_DIRECTORY;
+#  endif
+
+#  ifdef ENOTDIR
+	case ENOTDIR:
+		return P_ERROR_IO_NOT_DIRECTORY;
+#  endif
+
+#  ifdef EEXIST
+	case EEXIST:
+		return P_ERROR_IO_EXISTS;
+#  endif
+
+#  ifdef ENOENT
+	case ENOENT:
+		return P_ERROR_IO_NOT_EXISTS;
+#  endif
+
+#  ifdef ENAMETOOLONG
+	case ENAMETOOLONG:
+		return P_ERROR_IO_NAMETOOLONG;
+#  endif
+
+#  ifdef ENOSYS
+	case ENOSYS:
+		return P_ERROR_IO_NOT_IMPLEMENTED;
+#  endif
+
+	/* Some magic to deal with EWOULDBLOCK and EAGAIN.
+	 * Apparently on HP-UX these are actually defined to different values,
+	 * but on Linux, for example, they are the same. */
+#  if defined(EWOULDBLOCK) && defined(EAGAIN) && EWOULDBLOCK == EAGAIN
+	/* We have both and they are the same: only emit one case. */
+	case EAGAIN:
+		return P_ERROR_IO_WOULD_BLOCK;
+#  else
+	/* Else: consider each of them separately. This handles both the
+	 * case of having only one and the case where they are different values. */
+#    ifdef EAGAIN
+	case EAGAIN:
+		return P_ERROR_IO_WOULD_BLOCK;
+#    endif
+
+#    ifdef EWOULDBLOCK
+	case EWOULDBLOCK:
+		return P_ERROR_IO_WOULD_BLOCK;
+#    endif
+#  endif
+#endif /* !P_OS_WIN */
+	default:
+		return P_ERROR_IO_FAILED;
+	}
+}
+
+PErrorIO
+p_error_get_last_io (void)
+{
+	return p_error_get_io_from_system (p_error_get_last_system ());
+}
+
+PErrorIPC
+p_error_get_ipc_from_system (pint err_code)
+{
+	switch (err_code) {
+	case 0:
+		return P_ERROR_IPC_NONE;
+#ifdef P_OS_WIN
+#  ifdef ERROR_ALREADY_EXISTS
+	case ERROR_ALREADY_EXISTS:
+		return P_ERROR_IPC_EXISTS;
+#  endif
+#  ifdef ERROR_SEM_OWNER_DIED
+	case ERROR_SEM_OWNER_DIED:
+		return P_ERROR_IPC_NOT_EXISTS;
+#  endif
+#  ifdef ERROR_SEM_NOT_FOUND
+	case ERROR_SEM_NOT_FOUND:
+		return P_ERROR_IPC_NOT_EXISTS;
+#  endif
+#  ifdef ERROR_SEM_USER_LIMIT
+	case ERROR_SEM_USER_LIMIT:
+		return P_ERROR_IPC_NO_RESOURCES;
+#  endif
+#  ifdef ERROR_TOO_MANY_SEMAPHORES
+	case ERROR_TOO_MANY_SEMAPHORES:
+		return P_ERROR_IPC_NO_RESOURCES;
+#  endif
+#  ifdef ERROR_ACCESS_DENIED
+	case ERROR_ACCESS_DENIED:
+		return P_ERROR_IPC_ACCESS;
+#  endif
+#  ifdef ERROR_EXCL_SEM_ALREADY_OWNED
+	case ERROR_EXCL_SEM_ALREADY_OWNED:
+		return P_ERROR_IPC_ACCESS;
+#  endif
+#  ifdef ERROR_TOO_MANY_SEM_REQUESTS
+	case ERROR_TOO_MANY_SEM_REQUESTS:
+		return P_ERROR_IPC_NO_RESOURCES;
+#  endif
+#  ifdef ERROR_TOO_MANY_POSTS
+	case ERROR_TOO_MANY_POSTS:
+		return P_ERROR_IPC_NO_RESOURCES;
+#  endif
+#  ifdef ERROR_OUTOFMEMORY
+	case ERROR_OUTOFMEMORY:
+		return P_ERROR_IPC_NO_RESOURCES;
+#  endif
+#  ifdef ERROR_NOT_ENOUGH_MEMORY
+	case ERROR_NOT_ENOUGH_MEMORY:
+		return P_ERROR_IPC_NO_RESOURCES;
+#  endif
+#  ifdef ERROR_INVALID_HANDLE
+	case ERROR_INVALID_HANDLE:
+		return P_ERROR_IPC_INVALID_ARGUMENT;
+#  endif
+#  ifdef ERROR_INVALID_PARAMETER
+	case ERROR_INVALID_PARAMETER:
+		return P_ERROR_IPC_INVALID_ARGUMENT;
+#  endif
+#  ifdef ERROR_NOT_SUPPORTED
+	case ERROR_NOT_SUPPORTED:
+		return P_ERROR_IPC_NOT_IMPLEMENTED;
+#  endif
+#elif defined (P_OS_OS2)
+#  ifdef ERROR_NOT_ENOUGH_MEMORY
+	case ERROR_NOT_ENOUGH_MEMORY:
+		return P_ERROR_IPC_NO_RESOURCES;
+#  endif
+#  ifdef ERROR_INVALID_PARAMETER
+	case ERROR_INVALID_PARAMETER:
+		return P_ERROR_IPC_INVALID_ARGUMENT;
+#  endif
+#  ifdef ERROR_INVALID_NAME
+	case ERROR_INVALID_NAME:
+		return P_ERROR_IPC_INVALID_ARGUMENT;
+#  endif
+#  ifdef ERROR_INVALID_HANDLE
+	case ERROR_INVALID_HANDLE:
+		return P_ERROR_IPC_INVALID_ARGUMENT;
+#  endif
+#  ifdef ERROR_FILE_NOT_FOUND
+	case ERROR_FILE_NOT_FOUND:
+		return P_ERROR_IPC_NOT_EXISTS;
+#  endif
+#  ifdef ERROR_INVALID_ADDRESS
+	case ERROR_INVALID_ADDRESS:
+		return P_ERROR_IPC_INVALID_ARGUMENT;
+#  endif
+#  ifdef ERROR_GEN_FAILURE
+	case ERROR_GEN_FAILURE:
+		return P_ERROR_IPC_FAILED;
+#  endif
+#  ifdef ERROR_LOCKED
+	case ERROR_LOCKED:
+		return P_ERROR_IPC_ACCESS;
+#  endif
+#  ifdef ERROR_DUPLICATE_NAME
+	case ERROR_DUPLICATE_NAME:
+		return P_ERROR_IPC_EXISTS;
+#  endif
+#  ifdef ERROR_TOO_MANY_HANDLES
+	case ERROR_TOO_MANY_HANDLES:
+		return P_ERROR_IPC_NO_RESOURCES;
+#  endif
+#  ifdef ERROR_TOO_MANY_OPENS
+	case ERROR_TOO_MANY_OPENS:
+		return P_ERROR_IPC_NO_RESOURCES;
+#  endif
+#  ifdef ERROR_TOO_MANY_SEM_REQUESTS
+	case ERROR_TOO_MANY_SEM_REQUESTS:
+		return P_ERROR_IPC_NO_RESOURCES;
+#  endif
+#  ifdef ERROR_SEM_OWNER_DIED
+	case ERROR_SEM_OWNER_DIED:
+		return P_ERROR_IPC_NOT_EXISTS;
+#  endif
+#  ifdef ERROR_NOT_OWNER
+	case ERROR_NOT_OWNER:
+		return P_ERROR_IPC_ACCESS;
+#  endif
+#  ifdef ERROR_SEM_NOT_FOUND
+	case ERROR_SEM_NOT_FOUND:
+		return P_ERROR_IPC_NOT_EXISTS;
+#  endif
+#else /* !P_OS_WINDOWS && !P_OS_OS2 */
+#  ifdef EACCES
+	case EACCES:
+		return P_ERROR_IPC_ACCESS;
+#  endif
+
+#  ifdef EPERM
+	case EPERM:
+		return P_ERROR_IPC_ACCESS;
+#  endif
+
+#  ifdef EEXIST
+	case EEXIST:
+		return P_ERROR_IPC_EXISTS;
+#  endif
+
+#  ifdef E2BIG
+	case E2BIG:
+		return P_ERROR_IPC_INVALID_ARGUMENT;
+#  endif
+
+#  ifdef EFAULT
+	case EFAULT:
+		return P_ERROR_IPC_INVALID_ARGUMENT;
+#  endif
+
+#  ifdef EFBIG
+	case EFBIG:
+		return P_ERROR_IPC_INVALID_ARGUMENT;
+#  endif
+
+#  ifdef EINVAL
+	case EINVAL:
+		return P_ERROR_IPC_INVALID_ARGUMENT;
+#  endif
+
+#  ifdef ELOOP
+	case ELOOP:
+		return P_ERROR_IPC_INVALID_ARGUMENT;
+#  endif
+
+#  ifdef ERANGE
+	case ERANGE:
+		return P_ERROR_IPC_INVALID_ARGUMENT;
+#  endif
+
+#  ifdef ENOMEM
+	case ENOMEM:
+		return P_ERROR_IPC_NO_RESOURCES;
+#  endif
+
+#  ifdef EMFILE
+	case EMFILE:
+		return P_ERROR_IPC_NO_RESOURCES;
+#  endif
+
+#  ifdef ENFILE
+	case ENFILE:
+		return P_ERROR_IPC_NO_RESOURCES;
+#  endif
+
+#  ifdef ENOSPC
+	case ENOSPC:
+		return P_ERROR_IPC_NO_RESOURCES;
+#  endif
+
+#  ifdef EIDRM
+	case EIDRM:
+		return P_ERROR_IPC_NOT_EXISTS;
+#  endif
+
+#  ifdef ENOENT
+	case ENOENT:
+		return P_ERROR_IPC_NOT_EXISTS;
+#  endif
+
+#  ifdef EOVERFLOW
+	case EOVERFLOW:
+		return P_ERROR_IPC_OVERFLOW;
+#  endif
+
+#  ifdef ENOSYS
+	case ENOSYS:
+		return P_ERROR_IPC_NOT_IMPLEMENTED;
+#  endif
+
+#  ifdef EDEADLK
+	case EDEADLK:
+		return P_ERROR_IPC_DEADLOCK;
+#  endif
+
+#  ifdef ENAMETOOLONG
+	case ENAMETOOLONG:
+		return P_ERROR_IPC_NAMETOOLONG;
+#  endif
+#endif /* !P_OS_WIN */
+	default:
+		return P_ERROR_IPC_FAILED;
+	}
+}
+
+PErrorIPC
+p_error_get_last_ipc (void)
+{
+	return p_error_get_ipc_from_system (p_error_get_last_system ());
+}
+
+P_LIB_API PError *
+p_error_new (void)
+{
+	PError *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PError))) == NULL))
+		return NULL;
+
+	return ret;
+}
+
+P_LIB_API PError *
+p_error_new_literal (pint		code,
+		     pint		native_code,
+		     const pchar	*message)
+{
+	PError *ret;
+
+	if (P_UNLIKELY ((ret = p_error_new ()) == NULL))
+		return NULL;
+
+	ret->code        = code;
+	ret->native_code = native_code;
+	ret->message     = p_strdup (message);
+
+	return ret;
+}
+
+P_LIB_API const pchar *
+p_error_get_message (PError *error)
+{
+	if (P_UNLIKELY (error == NULL))
+		return NULL;
+
+	return error->message;
+}
+
+P_LIB_API pint
+p_error_get_code (PError *error)
+{
+	if (P_UNLIKELY (error == NULL))
+		return 0;
+
+	return error->code;
+}
+
+P_LIB_API pint
+p_error_get_native_code	(PError	*error)
+{
+	if (P_UNLIKELY (error == NULL))
+		return 0;
+
+	return error->native_code;
+}
+
+P_LIB_API PErrorDomain
+p_error_get_domain (PError *error)
+{
+	if (P_UNLIKELY (error == NULL))
+		return P_ERROR_DOMAIN_NONE;
+
+	if (error->code >= (pint) P_ERROR_DOMAIN_IPC)
+		return P_ERROR_DOMAIN_IPC;
+	else if (error->code >= (pint) P_ERROR_DOMAIN_IO)
+		return P_ERROR_DOMAIN_IO;
+	else
+		return P_ERROR_DOMAIN_NONE;
+}
+
+P_LIB_API PError *
+p_error_copy (PError *error)
+{
+	PError *ret;
+
+	if (P_UNLIKELY (error == NULL))
+		return NULL;
+
+	if (P_UNLIKELY ((ret = p_error_new_literal (error->code,
+						    error->native_code,
+						    error->message)) == NULL))
+		return NULL;
+
+	return ret;
+}
+
+P_LIB_API void
+p_error_set_error (PError	*error,
+		   pint		code,
+		   pint		native_code,
+		   const pchar	*message)
+{
+	if (P_UNLIKELY (error == NULL))
+		return;
+
+	if (error->message != NULL)
+		p_free (error->message);
+
+	error->code        = code;
+	error->native_code = native_code;
+	error->message     = p_strdup (message);
+}
+
+P_LIB_API void
+p_error_set_error_p (PError		**error,
+		     pint		code,
+		     pint		native_code,
+		     const pchar	*message)
+{
+	if (error == NULL || *error != NULL)
+		return;
+
+	*error = p_error_new_literal (code, native_code, message);
+}
+
+P_LIB_API void
+p_error_set_code (PError	*error,
+		  pint		code)
+{
+	if (P_UNLIKELY (error == NULL))
+		return;
+
+	error->code = code;
+}
+
+P_LIB_API void
+p_error_set_native_code	(PError	*error,
+			 pint	native_code)
+{
+	if (P_UNLIKELY (error == NULL))
+		return;
+
+	error->native_code = native_code;
+}
+
+P_LIB_API void
+p_error_set_message (PError		*error,
+		     const pchar	*message)
+{
+	if (P_UNLIKELY (error == NULL))
+		return;
+
+	if (error->message != NULL)
+		p_free (error->message);
+
+	error->message = p_strdup (message);
+}
+
+P_LIB_API void
+p_error_clear (PError *error)
+{
+	if (P_UNLIKELY (error == NULL))
+		return;
+
+	if (error->message != NULL)
+		p_free (error->message);
+
+	error->message     = NULL;
+	error->code        = 0;
+	error->native_code = 0;
+}
+
+P_LIB_API void
+p_error_free (PError	*error)
+{
+	if (P_UNLIKELY (error == NULL))
+		return;
+
+	if (error->message != NULL)
+		p_free (error->message);
+
+	p_free (error);
+}
+
+P_LIB_API pint
+p_error_get_last_system (void)
+{
+#ifdef P_OS_WIN
+	return (pint) GetLastError ();
+#else
+#  ifdef P_OS_VMS
+	pint error_code = errno;
+
+	if (error_code == EVMSERR)
+		return vaxc$errno;
+	else
+		return error_code;
+#  else
+	return errno;
+#  endif
+#endif
+}
+
+P_LIB_API pint
+p_error_get_last_net (void)
+{
+#if defined (P_OS_WIN)
+	return WSAGetLastError ();
+#elif defined (P_OS_OS2)
+	return sock_errno ();
+#else
+	return p_error_get_last_system ();
+#endif
+}
+
+P_LIB_API void
+p_error_set_last_system (pint code)
+{
+#ifdef P_OS_WIN
+	SetLastError (code);
+#else
+	errno = code;
+#endif
+}
+
+P_LIB_API void
+p_error_set_last_net (pint code)
+{
+#if defined (P_OS_WIN)
+	WSASetLastError (code);
+#elif defined (P_OS_OS2)
+	P_UNUSED (code);
+#else
+	errno = code;
+#endif
+}
diff --git a/3rdparty/plibsys/src/perror.h b/3rdparty/plibsys/src/perror.h
new file mode 100644
index 0000000..c08f53f
--- /dev/null
+++ b/3rdparty/plibsys/src/perror.h
@@ -0,0 +1,249 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file perror.h
+ * @brief Error report system
+ * @author Alexander Saprykin
+ *
+ * An error report system is used to notify a caller about fatal situations
+ * during the library API invocation. Usually the sequence is as following:
+ * @code
+ * PError *error = NULL;
+ * ...
+ *
+ * if (error != NULL) {
+ *         ...
+ *         p_error_free (error);
+ * }
+ * @endcode
+ * Note that you should not initialize a new #PError object before passing the
+ * pointer into an API call. Simply initialize it with zero and check the result
+ * after. Therefore you need to free memory if an error occurred.
+ *
+ * Most operating systems store the last error code of the most system calls in
+ * a thread-specific variable. Moreover, Windows stores the error code of the
+ * last socket related call in a separate variable. Use
+ * p_error_get_last_system() and p_error_set_last_system() to access the last
+ * system error code, p_error_get_last_net() and p_error_set_last_net() to
+ * access the last network error code.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PERROR_H
+#define PLIBSYS_HEADER_PERROR_H
+
+#include <pmacros.h>
+#include <ptypes.h>
+#include <perrortypes.h>
+
+P_BEGIN_DECLS
+
+/** Opaque data structure for an error object. */
+typedef struct PError_ PError;
+
+/**
+ * @brief Initializes a new empty #PError.
+ * @return Newly initialized #PError object in case of success, NULL otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API PError *	p_error_new		(void);
+
+/**
+ * @brief Initializes a new #PError with data.
+ * @param code Error code.
+ * @param native_code Native error code, leave 0 to ignore.
+ * @param message Error message.
+ * @return Newly initialized #PError object in case of success, NULL otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API PError *	p_error_new_literal	(pint		code,
+						 pint		native_code,
+						 const pchar	*message);
+
+/**
+ * @brief Gets an error message.
+ * @param error #PError object to get the message from.
+ * @return Error message in case of success, NULL otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API const pchar *	p_error_get_message	(PError		*error);
+
+/**
+ * @brief Gets an error code.
+ * @param error #PError object to get the code from.
+ * @return Error code in case of success, 0 otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API pint		p_error_get_code	(PError		*error);
+
+/**
+ * @brief Gets a platform native error code, if any.
+ * @param error #PError object to get the native code from.
+ * @return Error code in case of success, 0 otherwise.
+ * @since 0.0.1
+ * @note In some situations there can be no native code error, i.e. when an
+ * internal library call failed. Do not rely on this code.
+ */
+P_LIB_API pint		p_error_get_native_code	(PError		*error);
+
+/**
+ * @brief Gets an error domain.
+ * @param error #PError object to get the domain from.
+ * @return Error domain in case of success, #P_ERROR_DOMAIN_NONE otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API PErrorDomain	p_error_get_domain	(PError		*error);
+
+/**
+ * @brief Creates a copy of a #PError object.
+ * @param error #PError object to copy.
+ * @return Newly created #PError object in case of success, NULL otherwise.
+ * @since 0.0.1
+ * @note The caller is responsible to free memory of the created object after
+ * usage.
+ */
+P_LIB_API PError *	p_error_copy		(PError		*error);
+
+/**
+ * @brief Sets error data.
+ * @param error #PError object to set the data for.
+ * @param code Error code.
+ * @param native_code Native error code, leave 0 to ignore.
+ * @param message Error message.
+ * @since 0.0.1
+ */
+P_LIB_API void		p_error_set_error	(PError		*error,
+						 pint		code,
+						 pint		native_code,
+						 const pchar	*message);
+
+/**
+ * @brief Sets error data through a double pointer.
+ * @param error #PError object to set the data for.
+ * @param code Error code.
+ * @param native_code Native error code, leave 0 to ignore.
+ * @param message Error message.
+ * @since 0.0.1
+ *
+ * If @a error is NULL it does nothing. If @a error is not NULL then @a *error
+ * should be NULL, otherwise it does nothing. It creates a #PError object, sets
+ * error data and assigns it to @a *error. The caller is responsible to free
+ * memory of the created object after usage.
+ */
+P_LIB_API void		p_error_set_error_p	(PError		**error,
+						 pint		code,
+						 pint		native_code,
+						 const pchar	*message);
+
+/**
+ * @brief Sets an error code.
+ * @param error #PError object to set the code for.
+ * @param code Error code.
+ * @since 0.0.1
+ */
+P_LIB_API void		p_error_set_code	(PError		*error,
+						 pint		code);
+
+/**
+ * @brief Sets a platform native error code.
+ * @param error #PError object to set the native error code for.
+ * @param native_code Platform native error code.
+ * @since 0.0.1
+ */
+P_LIB_API void		p_error_set_native_code	(PError		*error,
+						 pint		native_code);
+
+/**
+ * @brief Sets an error message.
+ * @param error #PError object to set the message for.
+ * @param message Error message.
+ * @since 0.0.1
+ */
+P_LIB_API void		p_error_set_message	(PError		*error,
+						 const pchar	*message);
+
+/**
+ * @brief Clears error data.
+ * @param error #PError object to clear the data for.
+ * @since 0.0.1
+ * @note Error code is reseted to 0.
+ */
+P_LIB_API void		p_error_clear		(PError		*error);
+
+/**
+ * @brief Frees a previously initialized error object.
+ * @param error #PError object to free.
+ * @since 0.0.1
+ */
+P_LIB_API void		p_error_free		(PError		*error);
+
+/**
+ * @brief Gets the last system native error code.
+ * @return Last system native error code.
+ * @since 0.0.2
+ * @sa p_error_get_last_net(), p_error_set_last_system(),
+ * p_error_set_last_net()
+ * @note If you want get an error code for socket-related calls, use
+ * p_error_get_last_net() instead.
+ */
+
+P_LIB_API pint		p_error_get_last_system	(void);
+
+/**
+ * @brief Gets the last network native error code.
+ * @return Last network native error code.
+ * @since 0.0.2
+ * @sa p_error_get_last_system(), p_error_set_last_net(),
+ * p_error_set_last_system()
+ */
+P_LIB_API pint		p_error_get_last_net	(void);
+
+/**
+ * @brief Sets the last system native error code.
+ * @param code Error code to set.
+ * @since 0.0.2
+ * @sa p_error_set_last_net(), p_error_get_last_system(),
+ * p_error_get_last_net()
+ * @note If you want set an error code for socket-related calls, use
+ * p_error_set_last_net() instead.
+ */
+P_LIB_API void		p_error_set_last_system	(pint code);
+
+/**
+ * @brief Sets the last network native error code.
+ * @param code Error code to set.
+ * @since 0.0.2
+ * @sa p_error_set_last_system(), p_error_get_last_net(),
+ * p_error_get_last_system()
+ */
+P_LIB_API void		p_error_set_last_net	(pint code);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PERROR_H */
diff --git a/3rdparty/plibsys/src/perrortypes.h b/3rdparty/plibsys/src/perrortypes.h
new file mode 100644
index 0000000..e935c8a
--- /dev/null
+++ b/3rdparty/plibsys/src/perrortypes.h
@@ -0,0 +1,106 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file perrortypes.h
+ * @brief Error data types
+ * @author Alexander Saprykin
+ *
+ * All error codes are splitted into the several domains. Every error should
+ * belong to one of the domains described in #PErrorDomain. Think of an error
+ * domain as a logical subsystem.
+ *
+ * Every error domain has its own enumeration with the list of possible error
+ * codes. System error codes are converted to specified domains using internal
+ * routines.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PERRORTYPES_H
+#define PLIBSYS_HEADER_PERRORTYPES_H
+
+#include <pmacros.h>
+
+P_BEGIN_DECLS
+
+/** Enum with error domains. */
+typedef enum PErrorDomain_ {
+	P_ERROR_DOMAIN_NONE		= 0,	/**< No domain was specified.				*/
+	P_ERROR_DOMAIN_IO		= 500,	/**< Input/output domain.				*/
+	P_ERROR_DOMAIN_IPC		= 600	/**< Interprocess communication domain.			*/
+} PErrorDomain;
+
+/** Enum with IO errors. */
+typedef enum PErrorIO_ {
+	P_ERROR_IO_NONE			= 500,	/**< No error.						*/
+	P_ERROR_IO_NO_RESOURCES		= 501,	/**< Operating system hasn't enough resources.		*/
+	P_ERROR_IO_NOT_AVAILABLE	= 502,	/**< Resource isn't available.				*/
+	P_ERROR_IO_ACCESS_DENIED	= 503,	/**< Access denied.					*/
+	P_ERROR_IO_CONNECTED		= 504,	/**< Already connected.					*/
+	P_ERROR_IO_IN_PROGRESS		= 505,	/**< Operation in progress.				*/
+	P_ERROR_IO_ABORTED		= 506,	/**< Operation aborted.					*/
+	P_ERROR_IO_INVALID_ARGUMENT	= 507,	/**< Invalid argument specified.			*/
+	P_ERROR_IO_NOT_SUPPORTED	= 508,	/**< Operation not supported.				*/
+	P_ERROR_IO_TIMED_OUT		= 509,	/**< Operation timed out.				*/
+	P_ERROR_IO_WOULD_BLOCK		= 510,	/**< Operation cannot be completed immediatly.		*/
+	P_ERROR_IO_ADDRESS_IN_USE	= 511,	/**< Address is already under usage.			*/
+	P_ERROR_IO_CONNECTION_REFUSED	= 512,	/**< Connection refused.				*/
+	P_ERROR_IO_NOT_CONNECTED	= 513,	/**< Connection required first.				*/
+	P_ERROR_IO_QUOTA		= 514,  /**< User quota exceeded.				*/
+	P_ERROR_IO_IS_DIRECTORY		= 515,  /**< Trying to open directory for writing.		*/
+	P_ERROR_IO_NOT_DIRECTORY	= 516,  /**< Component of the path prefix is not a directory.	*/
+	P_ERROR_IO_NAMETOOLONG		= 517,  /**< Specified name is too long.			*/
+	P_ERROR_IO_EXISTS		= 518,  /**< Specified entry already exists.			*/
+	P_ERROR_IO_NOT_EXISTS		= 519,  /**< Specified entry doesn't exist.			*/
+	P_ERROR_IO_NO_MORE		= 520,  /**< No more data left.					*/
+	P_ERROR_IO_NOT_IMPLEMENTED	= 521,	/**< Operation is not implemented.			*/
+	P_ERROR_IO_FAILED		= 522	/**< General error.					*/
+} PErrorIO;
+
+/** Enum with IPC errors */
+typedef enum PErrorIPC_ {
+	P_ERROR_IPC_NONE		= 600,	/**< No error.						*/
+	P_ERROR_IPC_ACCESS		= 601,	/**< Not enough rights to access object or its key.	*/
+	P_ERROR_IPC_EXISTS		= 602,	/**< Object already exists and no proper open flags
+						     were specified.					*/
+	P_ERROR_IPC_NOT_EXISTS		= 603,	/**< Object doesn't exist or was removed before, and
+						     no proper create flags were specified.		*/
+	P_ERROR_IPC_NO_RESOURCES	= 604,	/**< Not enough system resources or memory to perform
+						     operation.						*/
+	P_ERROR_IPC_OVERFLOW		= 605,	/**< Semaphore value overflow.				*/
+	P_ERROR_IPC_NAMETOOLONG		= 606,	/**< Object name is too long.				*/
+	P_ERROR_IPC_INVALID_ARGUMENT	= 607,	/**< Invalid argument (parameter) specified.		*/
+	P_ERROR_IPC_NOT_IMPLEMENTED	= 608,	/**< Operation is not implemented (for example when
+						     using some filesystems).				*/
+	P_ERROR_IPC_DEADLOCK		= 609,	/**< Deadlock detected.					*/
+	P_ERROR_IPC_FAILED		= 610	/**< General error.					*/
+} PErrorIPC;
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PERRORTYPES_H */
diff --git a/3rdparty/plibsys/src/pfile.c b/3rdparty/plibsys/src/pfile.c
new file mode 100644
index 0000000..905b2d7
--- /dev/null
+++ b/3rdparty/plibsys/src/pfile.c
@@ -0,0 +1,80 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "perror.h"
+#include "pfile.h"
+#include "perror-private.h"
+
+#ifndef P_OS_WIN
+#  include <unistd.h>
+#endif
+
+P_LIB_API pboolean
+p_file_is_exists (const pchar *file)
+{
+#ifdef P_OS_WIN
+	DWORD attrs;
+#endif
+
+	if (P_UNLIKELY (file == NULL))
+		return FALSE;
+
+#ifdef P_OS_WIN
+	attrs = GetFileAttributesA ((LPCSTR) file);
+
+	return (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY) == 0);
+#else
+	return access (file, F_OK) == 0;
+#endif
+}
+
+P_LIB_API pboolean
+p_file_remove (const pchar	*file,
+	       PError		**error)
+{
+	pboolean result;
+
+	if (P_UNLIKELY (file == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+#ifdef P_OS_WIN
+	result = (DeleteFileA ((LPCSTR) file) != 0);
+#else
+	result = (unlink (file) == 0);
+#endif
+
+	if (P_UNLIKELY (!result))
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_io (),
+				     p_error_get_last_system (),
+				     "Failed to remove file");
+
+	return result;
+}
diff --git a/3rdparty/plibsys/src/pfile.h b/3rdparty/plibsys/src/pfile.h
new file mode 100644
index 0000000..19748c9
--- /dev/null
+++ b/3rdparty/plibsys/src/pfile.h
@@ -0,0 +1,87 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file pfile.h
+ * @brief File operations
+ * @author Alexander Saprykin
+ *
+ * To check file existance use p_file_is_exists(). To remove an exisiting file
+ * use p_file_remove().
+ *
+ * #P_DIR_SEPARATOR provides a platform independent directory separator symbol
+ * which you can use to form file or directory path.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PFILE_H
+#define PLIBSYS_HEADER_PFILE_H
+
+#include <pmacros.h>
+#include <ptypes.h>
+#include <perror.h>
+
+/**
+ * @def P_DIR_SEPARATOR
+ * @brief A directory separator.
+ */
+#if defined (P_OS_WIN) || defined (P_OS_OS2)
+#  define P_DIR_SEPARATOR "\\"
+#else
+#  define P_DIR_SEPARATOR "/"
+#endif
+
+P_BEGIN_DECLS
+
+/**
+ * @brief Checks whether a file exists or not.
+ * @param file File name to check.
+ * @return TRUE if the file exists, FALSE otherwise.
+ * @since 0.0.1
+ *
+ * On Windows this call doesn't resolve symbolic links, while on UNIX systems
+ * does.
+ */
+P_LIB_API pboolean p_file_is_exists	(const pchar	*file);
+
+/**
+ * @brief Removes a file from the disk.
+ * @param file File name to remove.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return TRUE if the file was successully removed, FALSE otherwise.
+ * @since 0.0.1
+ *
+ * This call doesn't resolve symbolic links and remove a symbolic link if the
+ * given path points to it.
+ */
+P_LIB_API pboolean p_file_remove	(const pchar	*file,
+					 PError		**error);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PFILE_H */
diff --git a/3rdparty/plibsys/src/phashtable.c b/3rdparty/plibsys/src/phashtable.c
new file mode 100644
index 0000000..a9fc052
--- /dev/null
+++ b/3rdparty/plibsys/src/phashtable.c
@@ -0,0 +1,243 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2019 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/* Hash table organized like this: table[hash key]->[list with values]
+ * Note: this implementation is not intended to use on huge loads */
+
+#include "pmem.h"
+#include "phashtable.h"
+
+#include <stdlib.h>
+
+typedef struct PHashTableNode_ PHashTableNode;
+
+struct PHashTableNode_ {
+	PHashTableNode	*next;
+	ppointer	key;
+	ppointer	value;
+};
+
+struct PHashTable_ {
+	PHashTableNode	**table;
+	psize		size;
+};
+
+/* Size of unique hash keys in hash table */
+#define P_HASH_TABLE_SIZE 101
+
+static puint pp_hash_table_calc_hash (pconstpointer pointer, psize modulo);
+static PHashTableNode * pp_hash_table_find_node (const PHashTable *table, pconstpointer key, puint hash);
+
+static puint
+pp_hash_table_calc_hash (pconstpointer pointer, psize modulo)
+{
+	/* As simple as we can :) */
+	return (puint) (((psize) (P_POINTER_TO_INT (pointer) + 37)) % modulo);
+}
+
+static PHashTableNode *
+pp_hash_table_find_node (const PHashTable *table, pconstpointer key, puint hash)
+{
+	PHashTableNode *ret;
+
+	for (ret = table->table[hash]; ret != NULL; ret = ret->next)
+		if (ret->key == key)
+			return ret;
+
+	return NULL;
+}
+
+P_LIB_API PHashTable *
+p_hash_table_new (void)
+{
+	PHashTable *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PHashTable))) == NULL)) {
+		P_ERROR ("PHashTable::p_hash_table_new: failed(1) to allocate memory");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((ret->table = p_malloc0 (P_HASH_TABLE_SIZE * sizeof (PHashTableNode *))) == NULL)) {
+		P_ERROR ("PHashTable::p_hash_table_new: failed(2) to allocate memory");
+		p_free (ret);
+		return NULL;
+	}
+
+	ret->size = P_HASH_TABLE_SIZE;
+
+	return ret;
+}
+
+P_LIB_API void
+p_hash_table_insert (PHashTable *table, ppointer key, ppointer value)
+{
+	PHashTableNode	*node;
+	puint		hash;
+
+	if (P_UNLIKELY (table == NULL))
+		return;
+
+	hash = pp_hash_table_calc_hash (key, table->size);
+
+	if ((node = pp_hash_table_find_node (table, key, hash)) == NULL) {
+		if (P_UNLIKELY ((node = p_malloc0 (sizeof (PHashTableNode))) == NULL)) {
+			P_ERROR ("PHashTable::p_hash_table_insert: failed to allocate memory");
+			return;
+		}
+
+		/* Insert a new node in front of others */
+		node->key   = key;
+		node->value = value;
+		node->next  = table->table[hash];
+
+		table->table[hash] = node;
+	} else
+		node->value = value;
+}
+
+P_LIB_API ppointer
+p_hash_table_lookup (const PHashTable *table, pconstpointer key)
+{
+	PHashTableNode	*node;
+	puint		hash;
+
+	if (P_UNLIKELY (table == NULL))
+		return NULL;
+
+	hash = pp_hash_table_calc_hash (key, table->size);
+
+	return ((node = pp_hash_table_find_node (table, key, hash)) == NULL) ? (ppointer) (-1) : node->value;
+}
+
+P_LIB_API PList *
+p_hash_table_keys (const PHashTable *table)
+{
+	PList		*ret = NULL;
+	PHashTableNode	*node;
+	puint		i;
+
+	if (P_UNLIKELY (table == NULL))
+		return NULL;
+
+	for (i = 0; i < table->size; ++i)
+		for (node = table->table[i]; node != NULL; node = node->next)
+			ret = p_list_append (ret, node->key);
+
+	return ret;
+}
+
+P_LIB_API PList *
+p_hash_table_values (const PHashTable *table)
+{
+	PList		*ret = NULL;
+	PHashTableNode	*node;
+	puint		i;
+
+	if (P_UNLIKELY (table == NULL))
+		return NULL;
+
+	for (i = 0; i < table->size; ++i)
+		for (node = table->table[i]; node != NULL; node = node->next)
+			ret = p_list_append (ret, node->value);
+
+	return ret;
+}
+
+P_LIB_API void
+p_hash_table_free (PHashTable *table)
+{
+	PHashTableNode	*node, *next_node;
+	puint		i;
+
+	if (P_UNLIKELY (table == NULL))
+		return;
+
+	for (i = 0; i < table->size; ++i)
+		for (node = table->table[i]; node != NULL; ) {
+			next_node = node->next;
+			p_free (node);
+			node = next_node;
+		}
+
+	p_free (table->table);
+	p_free (table);
+}
+
+P_LIB_API void
+p_hash_table_remove (PHashTable *table, pconstpointer key)
+{
+	PHashTableNode	*node, *prev_node;
+	puint		hash;
+
+	if (P_UNLIKELY (table == NULL))
+		return;
+
+	hash = pp_hash_table_calc_hash (key, table->size);
+
+	if (pp_hash_table_find_node (table, key, hash) != NULL) {
+		node = table->table[hash];
+		prev_node = NULL;
+
+		while (node != NULL) {
+			if (node->key == key) {
+				if (prev_node == NULL)
+					table->table[hash] = node->next;
+				else
+					prev_node->next = node->next;
+
+				p_free (node);
+				break;
+			} else {
+				prev_node = node;
+				node = node->next;
+			}
+		}
+	}
+}
+
+P_LIB_API PList *
+p_hash_table_lookup_by_value (const PHashTable *table, pconstpointer val, PCompareFunc func)
+{
+	PList		*ret = NULL;
+	PHashTableNode	*node;
+	puint		i;
+	pboolean	res;
+
+	if (P_UNLIKELY (table == NULL))
+		return NULL;
+
+	for (i = 0; i < table->size; ++i)
+		for (node = table->table[i]; node != NULL; node = node->next) {
+			if (func == NULL)
+				res = (node->value == val);
+			else
+				res = (func (node->value, val) == 0);
+
+			if (res)
+				ret = p_list_append (ret, node->key);
+		}
+
+	return ret;
+}
diff --git a/3rdparty/plibsys/src/phashtable.h b/3rdparty/plibsys/src/phashtable.h
new file mode 100644
index 0000000..702bbe7
--- /dev/null
+++ b/3rdparty/plibsys/src/phashtable.h
@@ -0,0 +1,167 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file phashtable.h
+ * @brief Hash table
+ * @author Alexander Saprykin
+ *
+ * A hash table is a data structure used to map keys to values. The hash table
+ * consists of several internal slots which hold a list of values. A hash
+ * function is used to compute an index in the array of the slots from a given
+ * key. The hash function itself is fast and it takes a constant time to compute
+ * the internal slot index.
+ *
+ * When the number of pairs in the hash table is small the lookup and insert
+ * (remove) operations are very fast and have average complexity O(1), because
+ * every slot holds almost the only one pair. As the number of internal slots is
+ * fixed, the increasing number of pairs will lead to degraded performance and
+ * the average complexity of the operations can drop to O(N) in the worst case.
+ * This is because the more pairs are inserted the more longer the list of
+ * values is placed in every slot.
+ *
+ * This is a simple hash table implementation which is not intended for heavy
+ * usage (several thousands), see #PTree if you need the best performance on
+ * large data sets. This implementation doesn't support multi-inserts when
+ * several values belong to the same key.
+ *
+ * Note that #PHashTable stores keys and values only as pointers, so you need
+ * to free used memory manually, p_hash_table_free() will not do it in any way.
+ *
+ * Integers (up to 32 bits) can be stored in pointers using #P_POINTER_TO_INT
+ * and #P_INT_TO_POINTER macros.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PHASHTABLE_H
+#define PLIBSYS_HEADER_PHASHTABLE_H
+
+#include <pmacros.h>
+#include <ptypes.h>
+#include <plist.h>
+
+P_BEGIN_DECLS
+
+/** Opaque data structure for a hash table. */
+typedef struct PHashTable_ PHashTable;
+
+/**
+ * @brief Initializes a new hash table.
+ * @return Pointer to a	 newly initialized #PHashTable structure in case of
+ * success, NULL otherwise.
+ * @since 0.0.1
+ * @note Free with p_hash_table_free() after usage.
+ */
+P_LIB_API PHashTable *	p_hash_table_new		(void);
+
+/**
+ * @brief Inserts a new key-value pair into a hash table.
+ * @param table Initialized hash table.
+ * @param key Key to insert.
+ * @param value Value to insert.
+ * @since 0.0.1
+ *
+ * This function only stores pointers, so you need to manually free pointed
+ * data after using the hash table.
+ */
+P_LIB_API void		p_hash_table_insert		(PHashTable		*table,
+							 ppointer		key,
+							 ppointer		value);
+
+/**
+ * @brief Searches for a specifed key in the hash table.
+ * @param table Hash table to lookup in.
+ * @param key Key to lookup for.
+ * @return Value related to its key pair (can be NULL), (#ppointer) -1 if no
+ * value was found.
+ * @since 0.0.1
+ */
+P_LIB_API ppointer	p_hash_table_lookup		(const PHashTable	*table,
+							 pconstpointer		key);
+
+/**
+ * @brief Gives a list of all the stored keys in the hash table.
+ * @param table Hash table to collect the keys from.
+ * @return List of all the stored keys, the list can be empty if no keys were
+ * found.
+ * @since 0.0.1
+ * @note You should manually free the returned list with p_list_free() after
+ * using it.
+ */
+P_LIB_API PList *	p_hash_table_keys		(const PHashTable	*table);
+
+/**
+ * @brief Gives a list of all the stored values in the hash table.
+ * @param table Hash table to collect the values from.
+ * @return List of all the stored values, the list can be empty if no keys were
+ * found.
+ * @since 0.0.1
+ * @note You should manually free the returned list with p_list_free() after
+ * using it.
+ */
+P_LIB_API PList *	p_hash_table_values		(const PHashTable	*table);
+
+/**
+ * @brief Frees a previously initialized #PHashTable.
+ * @param table Hash table to free.
+ * @since 0.0.1
+ */
+P_LIB_API void		p_hash_table_free		(PHashTable		*table);
+
+/**
+ * @brief Removes @a key from a hash table.
+ * @param table Hash table to remove the key from.
+ * @param key Key to remove (if exists).
+ * @since 0.0.1
+ */
+P_LIB_API void		p_hash_table_remove		(PHashTable		*table,
+							 pconstpointer		key);
+
+/**
+ * @brief Searches for a specifed key in the hash table by its value.
+ * @param table Hash table to lookup in.
+ * @param val Value to lookup keys for.
+ * @param func Function to compare table's values with @a val, if NULL then
+ * values will be compared as pointers.
+ * @return List of the keys with @a val (can be NULL), NULL if no keys were
+ * found.
+ * @since 0.0.1
+ * @note Caller is responsible to call p_list_free() on the returned list after
+ * usage.
+ *
+ * The compare function should return 0 if a value from the hash table (the
+ * first parameter) is accepted related to the given lookup value (the second
+ * parameter), and -1 or 1 otherwise.
+ */
+P_LIB_API PList *	p_hash_table_lookup_by_value	(const PHashTable	*table,
+							 pconstpointer		val,
+							 PCompareFunc		func);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PHASHTABLE_H */
diff --git a/3rdparty/plibsys/src/pinifile.c b/3rdparty/plibsys/src/pinifile.c
new file mode 100644
index 0000000..d8361fc
--- /dev/null
+++ b/3rdparty/plibsys/src/pinifile.c
@@ -0,0 +1,503 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2012-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "perror.h"
+#include "pinifile.h"
+#include "plist.h"
+#include "pmem.h"
+#include "pstring.h"
+#include "perror-private.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#define	P_INI_FILE_MAX_LINE	1024
+
+typedef struct PIniParameter_ {
+	pchar		*name;
+	pchar		*value;
+} PIniParameter;
+
+typedef struct PIniSection_ {
+	pchar		*name;
+	PList		*keys;
+} PIniSection;
+
+struct PIniFile_ {
+	pchar		*path;
+	PList		*sections;
+	pboolean	is_parsed;
+};
+
+static PIniParameter * pp_ini_file_parameter_new (const pchar *name, const pchar *val);
+static void pp_ini_file_parameter_free (PIniParameter *param);
+static PIniSection * pp_ini_file_section_new (const pchar *name);
+static void pp_ini_file_section_free (PIniSection *section);
+static pchar * pp_ini_file_find_parameter (const PIniFile *file, const pchar *section, const pchar *key);
+
+static PIniParameter *
+pp_ini_file_parameter_new (const pchar	*name,
+			   const pchar	*val)
+{
+	PIniParameter *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PIniParameter))) == NULL))
+		return NULL;
+
+	if (P_UNLIKELY ((ret->name = p_strdup (name)) == NULL)) {
+		p_free (ret);
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((ret->value = p_strdup (val)) == NULL)) {
+		p_free (ret->name);
+		p_free (ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+static void
+pp_ini_file_parameter_free (PIniParameter *param)
+{
+	p_free (param->name);
+	p_free (param->value);
+	p_free (param);
+}
+
+static PIniSection *
+pp_ini_file_section_new (const pchar *name)
+{
+	PIniSection *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PIniSection))) == NULL))
+		return NULL;
+
+	if (P_UNLIKELY ((ret->name = p_strdup (name)) == NULL)) {
+		p_free (ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+static void
+pp_ini_file_section_free (PIniSection *section)
+{
+	p_list_foreach (section->keys, (PFunc) pp_ini_file_parameter_free, NULL);
+	p_list_free (section->keys);
+	p_free (section->name);
+	p_free (section);
+}
+
+static pchar *
+pp_ini_file_find_parameter (const PIniFile *file, const pchar *section, const pchar *key)
+{
+	PList	*item;
+
+	if (P_UNLIKELY (file == NULL || file->is_parsed == FALSE || section == NULL || key == NULL))
+		return NULL;
+
+	for (item = file->sections; item != NULL; item = item->next)
+		if (strcmp (((PIniSection *) item->data)->name, section) == 0)
+			break;
+
+	if (item == NULL)
+		return NULL;
+
+	for (item = ((PIniSection *) item->data)->keys; item != NULL; item = item->next)
+		if (strcmp (((PIniParameter *) item->data)->name, key) == 0)
+			return p_strdup (((PIniParameter *) item->data)->value);
+
+	return NULL;
+}
+
+P_LIB_API PIniFile *
+p_ini_file_new (const pchar *path)
+{
+	PIniFile	*ret;
+
+	if (P_UNLIKELY (path == NULL))
+		return NULL;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PIniFile))) == NULL))
+		return NULL;
+
+	if (P_UNLIKELY ((ret->path = p_strdup (path)) == NULL)) {
+		p_free (ret);
+		return NULL;
+	}
+
+	ret->is_parsed = FALSE;
+
+	return ret;
+}
+
+P_LIB_API void
+p_ini_file_free (PIniFile *file)
+{
+	if (P_UNLIKELY (file == NULL))
+		return;
+
+	p_list_foreach (file->sections, (PFunc) pp_ini_file_section_free, NULL);
+	p_list_free (file->sections);
+	p_free (file->path);
+	p_free (file);
+}
+
+P_LIB_API pboolean
+p_ini_file_parse (PIniFile	*file,
+		  PError	**error)
+{
+	PIniSection	*section;
+	PIniParameter	*param;
+	FILE		*in_file;
+	pchar		*dst_line, *tmp_str;
+	pchar		src_line[P_INI_FILE_MAX_LINE + 1],
+			key[P_INI_FILE_MAX_LINE + 1],
+			value[P_INI_FILE_MAX_LINE + 1];
+	pint		bom_shift;
+
+	if (P_UNLIKELY (file == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	if (file->is_parsed)
+		return TRUE;
+
+	if (P_UNLIKELY ((in_file = fopen (file->path, "r")) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_io (),
+				     p_error_get_last_system (),
+				     "Failed to open file for reading");
+		return FALSE;
+	}
+
+	dst_line = NULL;
+	section  = NULL;
+	param    = NULL;
+
+	memset (src_line, 0, sizeof (src_line));
+
+	while (fgets (src_line, sizeof (src_line), in_file) != NULL) {
+		/* UTF-8, UTF-16 and UTF-32 BOM detection */
+		if ((puchar) src_line[0] == 0xEF && (puchar) src_line[1] == 0xBB && (puchar) src_line[2] == 0xBF)
+			bom_shift = 3;
+		else if (((puchar) src_line[0] == 0xFE && (puchar) src_line[1] == 0xFF) ||
+			 ((puchar) src_line[0] == 0xFF && (puchar) src_line[1] == 0xFE))
+			bom_shift = 2;
+		else if ((puchar) src_line[0] == 0x00 && (puchar) src_line[1] == 0x00 &&
+			 (puchar) src_line[2] == 0xFE && (puchar) src_line[3] == 0xFF)
+			bom_shift = 4;
+		else if ((puchar) src_line[0] == 0xFF && (puchar) src_line[1] == 0xFE &&
+			 (puchar) src_line[2] == 0x00 && (puchar) src_line[3] == 0x00)
+			bom_shift = 4;
+		else
+			bom_shift = 0;
+
+		dst_line = p_strchomp (src_line + bom_shift);
+
+		if (dst_line == NULL)
+			continue;
+
+		/* This should not happen */
+		if (P_UNLIKELY (strlen (dst_line) > P_INI_FILE_MAX_LINE))
+			dst_line[P_INI_FILE_MAX_LINE] = '\0';
+
+		if (dst_line[0] == '[' && dst_line[strlen (dst_line) - 1] == ']' &&
+		    sscanf (dst_line, "[%[^]]", key) == 1) {
+			/* New section found */
+			if ((tmp_str = p_strchomp (key)) != NULL) {
+				/* This should not happen */
+				if (P_UNLIKELY (strlen (tmp_str) > P_INI_FILE_MAX_LINE))
+					tmp_str[P_INI_FILE_MAX_LINE] = '\0';
+
+				strcpy (key, tmp_str);
+				p_free (tmp_str);
+
+				if (section != NULL) {
+					if (section->keys == NULL)
+						pp_ini_file_section_free (section);
+					else
+						file->sections = p_list_prepend (file->sections, section);
+				}
+
+				section = pp_ini_file_section_new (key);
+			}
+		} else if (sscanf (dst_line, "%[^=] = \"%[^\"]\"", key, value) == 2 ||
+			   sscanf (dst_line, "%[^=] = '%[^\']'", key, value) == 2 ||
+			   sscanf (dst_line, "%[^=] = %[^;#]", key, value) == 2) {
+			/* New parameter found */
+			if ((tmp_str = p_strchomp (key)) != NULL) {
+				/* This should not happen */
+				if (P_UNLIKELY (strlen (tmp_str) > P_INI_FILE_MAX_LINE))
+					tmp_str[P_INI_FILE_MAX_LINE] = '\0';
+
+				strcpy (key, tmp_str);
+				p_free (tmp_str);
+
+				if ((tmp_str = p_strchomp (value)) != NULL) {
+					/* This should not happen */
+					if (P_UNLIKELY (strlen (tmp_str) > P_INI_FILE_MAX_LINE))
+						tmp_str[P_INI_FILE_MAX_LINE] = '\0';
+
+					strcpy (value, tmp_str);
+					p_free (tmp_str);
+
+					if (strcmp (value, "\"\"") == 0 || (strcmp (value, "''") == 0))
+						value[0] = '\0';
+
+					if (section != NULL && (param = pp_ini_file_parameter_new (key, value)) != NULL)
+						section->keys = p_list_prepend (section->keys, param);
+				}
+			}
+		}
+
+		p_free (dst_line);
+		memset (src_line, 0, sizeof (src_line));
+	}
+
+	if (section != NULL) {
+		if (section->keys == NULL)
+			pp_ini_file_section_free (section);
+		else
+			file->sections = p_list_append (file->sections, section);
+	}
+
+	if (P_UNLIKELY (fclose (in_file) != 0))
+		P_WARNING ("PIniFile::p_ini_file_parse: fclose() failed");
+
+	file->is_parsed = TRUE;
+
+	return TRUE;
+}
+
+P_LIB_API pboolean
+p_ini_file_is_parsed (const PIniFile *file)
+{
+	if (P_UNLIKELY (file == NULL))
+		return FALSE;
+
+	return file->is_parsed;
+}
+
+P_LIB_API PList *
+p_ini_file_sections (const PIniFile *file)
+{
+	PList	*ret;
+	PList	*sec;
+
+	if (P_UNLIKELY (file == NULL || file->is_parsed == FALSE))
+		return NULL;
+
+	ret = NULL;
+
+	for (sec = file->sections; sec != NULL; sec = sec->next)
+		ret = p_list_prepend (ret, p_strdup (((PIniSection *) sec->data)->name));
+
+	return ret;
+}
+
+P_LIB_API PList *
+p_ini_file_keys (const PIniFile	*file,
+		 const pchar	*section)
+{
+	PList	*ret;
+	PList	*item;
+
+	if (P_UNLIKELY (file == NULL || file->is_parsed == FALSE || section == NULL))
+		return NULL;
+
+	ret = NULL;
+
+	for (item = file->sections; item != NULL; item = item->next)
+		if (strcmp (((PIniSection *) item->data)->name, section) == 0)
+			break;
+
+	if (item == NULL)
+		return NULL;
+
+	for (item = ((PIniSection *) item->data)->keys; item != NULL; item = item->next)
+		ret = p_list_prepend (ret, p_strdup (((PIniParameter *) item->data)->name));
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_ini_file_is_key_exists (const PIniFile	*file,
+			  const pchar		*section,
+			  const pchar		*key)
+{
+	PList	*item;
+
+	if (P_UNLIKELY (file == NULL || file->is_parsed == FALSE || section == NULL || key == NULL))
+		return FALSE;
+
+	for (item = file->sections; item != NULL; item = item->next)
+		if (strcmp (((PIniSection *) item->data)->name, section) == 0)
+			break;
+
+	if (item == NULL)
+		return FALSE;
+
+	for (item = ((PIniSection *) item->data)->keys; item != NULL; item = item->next)
+		if (strcmp (((PIniParameter *) item->data)->name, key) == 0)
+			return TRUE;
+
+	return FALSE;
+}
+
+P_LIB_API pchar *
+p_ini_file_parameter_string (const PIniFile	*file,
+			     const pchar	*section,
+			     const pchar	*key,
+			     const pchar	*default_val)
+{
+	pchar *val;
+
+	if ((val = pp_ini_file_find_parameter (file, section, key)) == NULL)
+		return p_strdup (default_val);
+
+	return val;
+}
+
+P_LIB_API pint
+p_ini_file_parameter_int (const PIniFile	*file,
+			  const pchar		*section,
+			  const pchar		*key,
+			  pint			default_val)
+{
+	pchar	*val;
+	pint	ret;
+
+	if ((val = pp_ini_file_find_parameter (file, section, key)) == NULL)
+		return default_val;
+
+	ret = atoi (val);
+	p_free (val);
+
+	return ret;
+}
+
+P_LIB_API double
+p_ini_file_parameter_double (const PIniFile	*file,
+			     const pchar	*section,
+			     const pchar	*key,
+			     double		default_val)
+{
+	pchar	*val;
+	pdouble	ret;
+
+	if ((val = pp_ini_file_find_parameter (file, section, key)) == NULL)
+		return default_val;
+
+	ret = p_strtod (val);
+	p_free (val);
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_ini_file_parameter_boolean (const PIniFile	*file,
+			      const pchar	*section,
+			      const pchar	*key,
+			      pboolean		default_val)
+{
+	pchar		*val;
+	pboolean	ret;
+
+	if ((val = pp_ini_file_find_parameter (file, section, key)) == NULL)
+		return default_val;
+
+	if (strcmp (val, "true") == 0 || strcmp (val, "TRUE") == 0)
+		ret = TRUE;
+	else if (strcmp (val, "false") == 0 || strcmp (val, "FALSE") == 0)
+		ret = FALSE;
+	else if (atoi (val) > 0)
+		ret = TRUE;
+	else
+		ret = FALSE;
+
+	p_free (val);
+
+	return ret;
+}
+
+P_LIB_API PList *
+p_ini_file_parameter_list (const PIniFile	*file,
+			   const pchar		*section,
+			   const pchar		*key)
+{
+	PList	*ret = NULL;
+	pchar	*val, *str;
+	pchar	buf[P_INI_FILE_MAX_LINE + 1];
+	psize	len, buf_cnt;
+
+	if ((val = pp_ini_file_find_parameter (file, section, key)) == NULL)
+		return NULL;
+
+	len = strlen (val);
+
+	if (len < 3 || val[0] != '{' || val[len - 1] != '}') {
+		p_free (val);
+		return NULL;
+	}
+
+	/* Skip first brace '{' symbol */
+	str = val + 1;
+	buf[0] = '\0';
+	buf_cnt = 0;
+
+	while (*str && *str != '}') {
+		if (!isspace (* ((const puchar *) str)))
+			buf[buf_cnt++] = *str;
+		else {
+			buf[buf_cnt] = '\0';
+
+			if (buf_cnt > 0)
+				ret = p_list_append (ret, p_strdup (buf));
+
+			buf_cnt = 0;
+		}
+
+		++str;
+	}
+
+	if (buf_cnt > 0) {
+		buf[buf_cnt] = '\0';
+		ret = p_list_append (ret, p_strdup (buf));
+	}
+
+	p_free (val);
+
+	return ret;
+}
diff --git a/3rdparty/plibsys/src/pinifile.h b/3rdparty/plibsys/src/pinifile.h
new file mode 100644
index 0000000..b2a2e3d
--- /dev/null
+++ b/3rdparty/plibsys/src/pinifile.h
@@ -0,0 +1,261 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2012-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file pinifile.h
+ * @brief INI file parser
+ * @author Alexander Saprykin
+ *
+ * An INI file is usually used for storing configuration information. It
+ * consists of sections: every section starts with a line containing the name in
+ * square brackets (i.e. [section_name]). After that line all the following
+ * parameters will belong to that section until another section begins.
+ *
+ * Each section has a list of key-value pairs. Empty sections are not permitted
+ * (they will be skipped). Every key-value pair is represented with a line in
+ * the `key = value` format. If a section has several values with the same key
+ * the last one will be used. A value is parsed by the first in-order '='
+ * symbol. All the following '=' occurrences belong to the value.
+ *
+ * All symbols after '#' and ';' (even at the line ending) are the comments and
+ * wouldn't be read. If you want to use them in values take the value inside the
+ * "" or '' symbols. A section name line is not allowed to use the comment
+ * symbols after the section name in the square brackets itself.
+ *
+ * Integer values can be written in the usual form.
+ *
+ * Floating point values can be written in any commonly used notation (i.e. with
+ * the decimal point, in the exponential form using the 'e' character). The only
+ * valid decimal point symbol is the '.'. There is no locale dependency on the
+ * decimal point.
+ *
+ * Boolean values can be written in the form of 'true/false' or 'TRUE/FALSE', or
+ * simply '0/1'.
+ *
+ * Any value can be interpreted as a string at any moment. Actually all the
+ * values are stored internally as strings.
+ *
+ * A list of values can be stored between the '{}' symbols separated with
+ * spaces. The list only supports string values, so you should convert them to
+ * numbers manually. The list doesn't support strings with spaces - such strings
+ * will be splitted.
+ *
+ * To parse a file, create #PIniFile with p_ini_file_new() and then parse it
+ * with the p_ini_file_parse() routine.
+ *
+ * #PIniFile handles (skips) UTF-8/16/32 BOM characters (marks).
+ *
+ * Example of the INI file contents:
+ * @code
+ * [numeric_section]
+ * numeric_value_1 = 1234 # One type of the comment
+ * numeric_value_2 = 123  ; Comment is allowed here
+ *
+ * [floating_section]
+ * float_value_1 = 123.3e10
+ * float_value_2 = 123.19
+ *
+ * [boolean_section]
+ * boolean_value_1 = TRUE
+ * boolean_value_2 = 0
+ * boolean_value_3 = false
+ *
+ * [string_section]
+ * string_value_1 = "Test string"
+ * string_value_2 = 'Another test string'
+ *
+ * [list_section]
+ * list_value = {123 val 7654}
+ * @endcode
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PINIFILE_H
+#define PLIBSYS_HEADER_PINIFILE_H
+
+#include <pmacros.h>
+#include <ptypes.h>
+#include <plist.h>
+#include <perror.h>
+
+P_BEGIN_DECLS
+
+/** INI file opaque data structure. */
+typedef struct PIniFile_ PIniFile;
+
+/**
+ * @brief Creates a new #PIniFile for parsing.
+ * @param path Path to a file to parse.
+ * @return Newly allocated #PIniFile in case of success, NULL otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API PIniFile *	p_ini_file_new			(const pchar	*path);
+
+/**
+ * @brief Frees memory and allocated resources of #PIniFile.
+ * @param file #PIniFile to free.
+ * @since 0.0.1
+ */
+P_LIB_API void		p_ini_file_free			(PIniFile	*file);
+
+/**
+ * @brief Parses given #PIniFile.
+ * @param file #PIniFile file to parse.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API pboolean	p_ini_file_parse		(PIniFile	*file,
+							 PError		**error);
+
+/**
+ * @brief Checks whether #PIniFile was already parsed or not.
+ * @param file #PIniFile to check.
+ * @return TRUE if the file was already parsed, FALSE otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API pboolean	p_ini_file_is_parsed		(const PIniFile	*file);
+
+/**
+ * @brief Gets all the sections from a given file.
+ * @param file #PIniFile to get the sections from. The @a file should be parsed
+ * before.
+ * @return #PList of section names.
+ * @since 0.0.1
+ * @note It's a caller responsibility to p_free() each returned string and to
+ * free the returned list with p_list_free().
+ */
+P_LIB_API PList	*	p_ini_file_sections		(const PIniFile	*file);
+
+/**
+ * @brief Gets all the keys from a given section.
+ * @param file #PIniFile to get the keys from. The @a file should be parsed
+ * before.
+ * @param section Section name to get the keys from.
+ * @return #PList of key names.
+ * @since 0.0.1
+ * @note It's a caller responsibility to p_free() each returned string and to
+ * free the returned list with p_list_free().
+ */
+P_LIB_API PList *	p_ini_file_keys			(const PIniFile	*file,
+							 const pchar	*section);
+
+/**
+ * @brief Checks whether a key exists.
+ * @param file #PIniFile to check in. The @a file should be parsed before.
+ * @param section Section to check the key in.
+ * @param key Key to check.
+ * @return TRUE if @a key exists, FALSE otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API pboolean	p_ini_file_is_key_exists	(const PIniFile	*file,
+							 const pchar	*section,
+							 const pchar	*key);
+
+/**
+ * @brief Gets specified parameter's value as a string.
+ * @param file #PIniFile to get the value from. The @a file should be parsed
+ * before.
+ * @param section Section to get the value from.
+ * @param key Key to get the value from.
+ * @param default_val Default value to return if no specified key exists.
+ * @return Key's value in case of success, @a default_value otherwise.
+ * @since 0.0.1
+ * @note It's a caller responsibility to p_free() the returned string after
+ * usage.
+ */
+P_LIB_API pchar *	p_ini_file_parameter_string	(const PIniFile	*file,
+							 const pchar	*section,
+							 const pchar	*key,
+							 const pchar	*default_val);
+
+/**
+ * @brief Gets specified parameter's value as an integer.
+ * @param file #PIniFile to get the value from. The @a file should be parsed
+ * before.
+ * @param section Section to get the value from.
+ * @param key Key to get the value from.
+ * @param default_val Default value to return if no specified key exists.
+ * @return Key's value in case of success, @a default_value otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API pint		p_ini_file_parameter_int	(const PIniFile	*file,
+							 const pchar	*section,
+							 const pchar	*key,
+							 pint		default_val);
+
+/**
+ * @brief Gets specified parameter's value as a floating point.
+ * @param file #PIniFile to get the value from. The @a file should be parsed
+ * before.
+ * @param section Section to get the value from.
+ * @param key Key to get the value from.
+ * @param default_val Default value to return if no specified key exists.
+ * @return Key's value in case of success, @a default_value otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API double	p_ini_file_parameter_double	(const PIniFile	*file,
+							 const pchar	*section,
+							 const pchar	*key,
+							 double		default_val);
+/**
+ * @brief Gets specified parameter's value as a boolean.
+ * @param file #PIniFile to get the value from. The @a file should be parsed
+ * before.
+ * @param section Section to get the value from.
+ * @param key Key to get the value from.
+ * @param default_val Default value to return if no specified key exists.
+ * @return Key's value in case of success, @a default_value otherwise.
+ * @since 0.0.1
+ */
+
+P_LIB_API pboolean	p_ini_file_parameter_boolean	(const PIniFile	*file,
+							 const pchar	*section,
+							 const pchar	*key,
+							 pboolean	default_val);
+
+/**
+ * @brief Gets specified parameter's value as a list of strings separated with
+ * the spaces or tabs.
+ * @param file #PIniFile to get the value from. The @a file should be parsed
+ * before.
+ * @param section Section to get the value from.
+ * @param key Key to get the value from.
+ * @return #PList of strings. NULL will be returned if no parameter with the
+ * given name exists.
+ * @since 0.0.1
+ * @note It's a caller responsibility to p_free() each returned string and to
+ * free the returned list with p_list_free().
+ */
+P_LIB_API PList *	p_ini_file_parameter_list	(const PIniFile	*file,
+							 const pchar	*section,
+							 const pchar	*key);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PINIFILE_H */
diff --git a/3rdparty/plibsys/src/pipc-private.h b/3rdparty/plibsys/src/pipc-private.h
new file mode 100644
index 0000000..312d378
--- /dev/null
+++ b/3rdparty/plibsys/src/pipc-private.h
@@ -0,0 +1,76 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016-2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PIPC_PRIVATE_H
+#define PLIBSYS_HEADER_PIPC_PRIVATE_H
+
+#include "pmacros.h"
+#include "ptypes.h"
+
+P_BEGIN_DECLS
+
+#if !defined (P_OS_WIN) && !defined (P_OS_OS2)
+/**
+ * @brief Gets a temporary directory on UNIX systems.
+ * @return Temporary directory.
+ */
+pchar *		p_ipc_unix_get_temp_dir		(void);
+
+/* Create file for System V IPC, if needed
+ * Returns: -1 = error, 0 = file successfully created, 1 = file already exists */
+/**
+ * @brief Creates a file for System V IPC usage.
+ * @param file_name File name to create.
+ * @return -1 in case of error, 0 if all was OK, and 1 if the file already
+ * exists.
+ */
+pint		p_ipc_unix_create_key_file	(const pchar	*file_name);
+
+/**
+ * @brief Wrapps the ftok() UNIX call for a unique IPC key.
+ * @param file_name File name for ftok() call.
+ * @return Key in case of success, -1 otherwise.
+ */
+pint		p_ipc_unix_get_ftok_key		(const pchar	*file_name);
+#endif /* !P_OS_WIN && !P_OS_OS2 */
+
+/**
+ * @brief Generates a platform independent key for IPC usage, an object name for
+ * Windows and a file name to use with ftok () for UNIX-like systems.
+ * @param name Object name.
+ * @param posix TRUE if the key will be used for the POSIX IPC calls, otherwise
+ * FALSE. This parameter is not used on the Windows platform.
+ * @return Platform independent key for IPC usage.
+ */
+pchar *		p_ipc_get_platform_key		(const pchar	*name,
+						 pboolean	posix);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PIPC_PRIVATE_H */
diff --git a/3rdparty/plibsys/src/pipc.c b/3rdparty/plibsys/src/pipc.c
new file mode 100644
index 0000000..60093ee
--- /dev/null
+++ b/3rdparty/plibsys/src/pipc.c
@@ -0,0 +1,178 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "pcryptohash.h"
+#include "pstring.h"
+#include "psysclose-private.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#if !defined (P_OS_WIN) && !defined (P_OS_OS2) && !defined (P_OS_AMIGA)
+#  include <unistd.h>
+#  include <errno.h>
+#  include <fcntl.h>
+#  include <sys/stat.h>
+#  include <sys/types.h>
+#  include <sys/ipc.h>
+#endif
+
+#if !defined (P_OS_WIN) && !defined (P_OS_OS2) && !defined (P_OS_AMIGA)
+pchar *
+p_ipc_unix_get_temp_dir (void)
+{
+	pchar	*str, *ret;
+	psize	len;
+
+#ifdef P_tmpdir
+	if (strlen (P_tmpdir) > 0)
+		str = p_strdup (P_tmpdir);
+	else
+		return p_strdup ("/tmp/");
+#else
+	const pchar *tmp_env;
+
+	tmp_env = getenv ("TMPDIR");
+
+	if (tmp_env != NULL)
+		str = p_strdup (tmp_env);
+	else
+		return p_strdup ("/tmp/");
+#endif /* P_tmpdir */
+
+	/* Now we need to ensure that we have only the one trailing slash */
+	len = strlen (str);
+	while (*(str + --len) == '/')
+		;
+	*(str + ++len) = '\0';
+
+	/* len + / + zero symbol */
+	if (P_UNLIKELY ((ret = p_malloc0 (len + 2)) == NULL)) {
+		p_free (str);
+		return NULL;
+	}
+
+	strcpy (ret, str);
+	strcat (ret, "/");
+
+	return ret;
+}
+
+/* Create file for System V IPC, if needed
+ * Returns: -1 = error, 0 = file successfully created, 1 = file already exists */
+pint
+p_ipc_unix_create_key_file (const pchar *file_name)
+{
+	pint fd;
+
+	if (P_UNLIKELY (file_name == NULL))
+		return -1;
+
+	if ((fd = open (file_name, O_CREAT | O_EXCL | O_RDONLY, 0640)) == -1)
+		/* file already exists */
+		return (errno == EEXIST) ? 1 : -1;
+	else
+		return p_sys_close (fd);
+}
+
+pint
+p_ipc_unix_get_ftok_key (const pchar *file_name)
+{
+	struct stat st_info;
+
+	if (P_UNLIKELY (file_name == NULL))
+		return -1;
+
+	if (P_UNLIKELY (stat (file_name, &st_info) == -1))
+		return -1;
+
+	return ftok (file_name, 'P');
+}
+#endif /* !P_OS_WIN && !P_OS_OS2 && !P_OS_AMIGA */
+
+/* Returns a platform-independent key for IPC usage, object name for Windows and
+ * a file name to use with ftok () for UNIX-like systems */
+pchar *
+p_ipc_get_platform_key (const pchar *name, pboolean posix)
+{
+	PCryptoHash	*sha1;
+	pchar		*hash_str;
+
+#if defined (P_OS_WIN) || defined (P_OS_OS2) || defined (P_OS_AMIGA)
+	P_UNUSED (posix);
+#else
+	pchar		*path_name, *tmp_path;
+#endif
+
+	if (P_UNLIKELY (name == NULL))
+		return NULL;
+
+	if (P_UNLIKELY ((sha1 = p_crypto_hash_new (P_CRYPTO_HASH_TYPE_SHA1)) == NULL))
+		return NULL;
+
+	p_crypto_hash_update (sha1, (const puchar *) name, strlen (name));
+
+	hash_str = p_crypto_hash_get_string (sha1);
+	p_crypto_hash_free (sha1);
+
+	if (P_UNLIKELY (hash_str == NULL))
+		return NULL;
+
+#if defined (P_OS_WIN) || defined (P_OS_OS2) || defined (P_OS_AMIGA)
+	return hash_str;
+#else
+	if (posix) {
+		/* POSIX semaphores which are named kinda like '/semname'.
+		 * Some implementations of POSIX semaphores has restriction for
+		 * the name as of max 14 characters, best to use this limit */
+		if (P_UNLIKELY ((path_name = p_malloc0 (15)) == NULL)) {
+			p_free (hash_str);
+			return NULL;
+		}
+
+		strcpy (path_name, "/");
+		strncat (path_name, hash_str, 13);
+	} else {
+		tmp_path = p_ipc_unix_get_temp_dir ();
+
+		/* tmp dir + filename + zero symbol */
+		path_name = p_malloc0 (strlen (tmp_path) + strlen (hash_str) + 1);
+
+		if (P_UNLIKELY ((path_name) == NULL)) {
+			p_free (tmp_path);
+			p_free (hash_str);
+			return NULL;
+		}
+
+		strcpy (path_name, tmp_path);
+		strcat (path_name, hash_str);
+		p_free (tmp_path);
+	}
+
+	p_free (hash_str);
+	return path_name;
+#endif
+}
diff --git a/3rdparty/plibsys/src/plibraryloader-amiga.c b/3rdparty/plibsys/src/plibraryloader-amiga.c
new file mode 100644
index 0000000..463e350
--- /dev/null
+++ b/3rdparty/plibsys/src/plibraryloader-amiga.c
@@ -0,0 +1,583 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2017-2018 Alexander Saprykin <saprykin.spb@gmail.com>
+ * Copyright (C) 2002-2015 by Olaf Barthel <obarthel (at) gmx.net>
+ *
+ * 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.
+ *
+ * Path conversion code was taken and adopted from clib2 project:
+ * https://github.com/adtools/clib2
+ *
+ * This part of code is distributed under the BSD-3-Clause license:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   - Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *   - Neither the name of Olaf Barthel nor the names of contributors
+ *     may be used to endorse or promote products derived from this
+ *     software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "perror.h"
+#include "pfile.h"
+#include "plibraryloader.h"
+#include "pmem.h"
+#include "pstring.h"
+
+#include <string.h>
+
+#include <proto/dos.h>
+#include <proto/elf.h>
+
+#if defined (__CLIB2__)
+#  include <dos.h>
+#elif defined (__NEWLIB__)
+#  include <amiga_platform.h>
+#endif
+
+typedef APTR plibrary_handle;
+
+struct PLibraryLoader_ {
+	plibrary_handle	handle;
+	Elf32_Error	last_error;
+};
+
+static Elf32_Handle pp_library_loader_elf_root = NULL;
+
+static void pp_library_loader_clean_handle (plibrary_handle handle);
+static pint pp_library_loader_translate_path (const pchar *in, pchar *out, psize out_len);
+
+/*
+ * The following patterns must translate properly:
+ *
+ *		foo/
+ *		///
+ *		foo//bar
+ *		foo//bar//baz
+ *		./foo
+ *		././foo
+ *		foo/./baz
+ *		foo/./bar/./baz
+ *		foo/./././bar
+ *		foo/.
+ *		/.
+ *		/tmp
+ *		/tmp/foo
+ *		/dev/null
+ *		/dev/null/foo
+ *		/dev/nullX
+ *		/foo
+ *		/foo/
+ *		/foo/bar
+ *		/foo/bar/baz
+ *		foo/../bar
+ *		foo/bar/../../baz
+ *		foo/bar/..
+ *		../foo
+ *		../../foo
+ *		.
+ *		..
+ */
+
+static pint
+pp_library_loader_translate_path (const pchar *in, pchar *out, psize out_len)
+{
+	pchar volume_name[MAXPATHLEN];
+	psize len, volume_name_len;
+	pint i, j;
+
+	len = strlen (in);
+
+	if (out_len < MAXPATHLEN || len > MAXPATHLEN)
+		return -1;
+
+	strcpy (out, in);
+
+	/* Just copy is path is already an Amiga one */
+
+	if (strchr (in, ':') != NULL)
+		return 0;
+
+	in = out;
+
+	/* Strip neighbouring slashes: ('foo//bar' -> 'foo/bar').
+	 * The "//" pattern in a Unix file name is apparently harmless,
+	 * but on AmigaDOS it has a very definite meaning. */
+
+	if (len > 2) {
+		pboolean have_double_slash = FALSE;
+
+		for (i = 0; i < len - 1; ++i) {
+			if (in[i] == '/' && in[i + 1] == '/') {
+				have_double_slash = TRUE;
+				break;
+			}
+		}
+
+		if (have_double_slash) {
+			pboolean have_slash;
+			pchar c;
+
+			have_slash = FALSE;
+
+			for (i = j = 0; i < len; ++i) {
+				c = in[i];
+
+				if (c == '/') {
+					if (!have_slash)
+						out[j++] = c;
+
+					have_slash = TRUE;
+				} else {
+					out[j++] = c;
+					have_slash = FALSE;
+				}
+			}
+
+			len = j;
+			out[len] = '\0';
+		}
+	}
+
+	/* Strip trailing slashes ('foo/' -> 'foo'). A leading '/' must
+	 * be preserved, though ('///' -> '/'). */
+
+	if(len > 1) {
+		psize num_trailing_slashes = 0;
+
+		while ((num_trailing_slashes < len - 1) && (in[len - (num_trailing_slashes + 1)] == '/'))
+			num_trailing_slashes++;
+
+		if (num_trailing_slashes > 0) {
+			len -= num_trailing_slashes;
+			out[len] = '\0';
+		}
+	}
+
+	/* Ditch all leading './' ('./foo' -> 'foo'). */
+
+	while (len > 2 && out[0] == '.' && out[1] == '/') {
+		len -= 2;
+		memmove (out, &out[2], len);
+		out[len] = '\0';
+	}
+
+	/* Ditch all embedded '/./' ('foo/./bar' -> 'foo/bar', 'foo/././bar' -> 'foo/bar'). */
+
+	if (len > 2) {
+		pboolean have_slash_dot_slash = FALSE;
+
+		for (i = j = 0; i < len - 2; ++i) {
+			if(in[i] == '/' && in[i + 1] == '.' && in[i + 2] == '/') {
+				have_slash_dot_slash = TRUE;
+				break;
+			}
+		}
+
+		if (have_slash_dot_slash) {
+			for (i = j = 0; i < len; ++i) {
+				while (i < len - 2 && in[i] == '/' && in[i + 1] == '.' && in[i + 2] == '/')
+					i += 2;
+
+				if (i < len)
+					out[j++] = in[i];
+			}
+	
+			len = j;
+			out[len] = '\0';
+		}
+	}
+
+	/* Special case: the path name may end with "/." signifying that the
+	 * directory itself is requested ('foo/.' -> 'foo'). */
+
+	if (len >= 2 && strncmp (&in[len - 2], "/.", 2) == 0) {
+		/* If it's just '/.' then it's really '/'. */
+		if (len == 2) {
+			strcpy (out, "/");
+			len = 1;
+		} else {
+			len -= 2;
+			out[len] = '\0';
+		}
+	}
+
+	/* Check for absolute path. */
+
+	if (in[0] == '/') {
+		/* OK, so this is an absolute path. We begin by checking
+		 * for a few special cases, the first being a reference
+		 * to "/tmp". */
+		if ((strncmp (in, "/tmp", 4) == 0) && (in[4] == '/' || len == 4)) {
+			if (in[4] == '/') {
+				/* Convert "/tmp/foo" to "T:foo". */
+				memmove (&out[2], &in[5], len - 5);
+				memmove (out, "T:", 2);
+
+				len -= 3;
+			} else {
+				/* Convert "/tmp" to "T:". */
+				strcpy (out, "T:");
+
+				len = 2;
+			}
+
+			out[len] = '\0';
+		} else if ((strncmp (in, "/dev/null", 9)) == 0 && (len == 9 || in[9] == '/')) {
+			strcpy (out, "NIL:");
+			len = 4;
+		} else {
+			psize path_name_start = 0;
+			volume_name_len       = 0;
+
+			/* Find out how long the first component of the absolute path is. */
+			for (i = 1; i <= len; ++i) {
+				if (i == len || in[i] == '/') {
+					volume_name_len = i - 1;
+
+					/* Is there anything following the path name? */
+					if (i < len)
+						path_name_start = i + 1;
+
+					break;
+				}
+			}
+
+			/* Copy the first component and attach a colon. "/foo" becomes "foo:". */
+			memmove (out, &in[1], volume_name_len);
+			out[volume_name_len++] = ':';
+
+			/* Now add the finishing touches. "/foo/bar" finally
+			 * becomes "foo:bar" and "/foo" becomes "foo:". */
+			if (path_name_start > 0) {
+				memmove (&out[volume_name_len], &in[path_name_start], len - path_name_start);
+
+				len--;
+			}
+
+			out[len] = '\0';
+		}
+	}
+
+	/* Extract and remove the volume name from the path. We
+	 * are going to need it later. */
+
+	volume_name_len = 0;
+
+	for (i = 0; i < len; ++i) {
+		if (in[i] == ':') {
+			/* Look for extra colon characters embedded in the name
+		         * (as in "foo/bar:baz") which really don't belong here. */
+			for (j = 0 ; j < i ; j++) {
+				if(in[j] == '/')
+					return -1;
+			}
+
+			volume_name_len = i + 1;
+			len -= volume_name_len;
+
+			memmove (volume_name, in, volume_name_len);
+			memmove (out, &out[volume_name_len], len);
+			
+			out[len] = '\0';
+
+			break;
+		}
+	}
+
+	/* Look for extra colon characters embedded in the name
+         * (as in "foo:bar:baz") which really don't belong here. */
+
+	for (i = 0; i < len; ++i) {
+		if (in[i] == ':')
+			return -1;
+	}	
+
+	/* Now parse the path name and replace all embedded '..' with
+	 * the AmigaDOS counterparts ('foo/../bar' -> 'foo//bar'). */
+
+	if (len > 3) {
+		pboolean have_slash_dot_dot_slash = FALSE;
+
+		for (i = j = 0; i < len - 3; ++i) {
+			if (in[i] == '/' && in[i + 1] == '.' && in[i + 2] == '.' && in[i + 3] == '/') {
+				have_slash_dot_dot_slash = TRUE;
+				break;
+			}
+		}
+
+		if (have_slash_dot_dot_slash) {
+			pboolean have_before = FALSE;
+
+			for (i = j = 0; i < len; ++i) {
+				if(i < len - 3 && in[i] == '/' && in[i + 1] == '.' && in[i + 2] == '.' && in[i + 3] == '/') {
+					out[j++] = in[i];
+
+					if (have_before)
+						out[j++] = '/';
+
+					i += 2;
+
+					have_before = TRUE;
+				} else {
+					have_before = FALSE;
+					out[j++] = in[i];
+				}
+			}
+
+			len = j;
+			out[len] = '\0';
+		}
+	}
+
+	/* Translate a trailing '/..' to '//' */
+
+	if (len >= 3 && strncmp (&in[len - 3], "/..", 3) == 0) {
+		len -= 2;
+		out[len++] = '/';
+		out[len] = '\0';
+	}
+
+	/* Translate a leading '../' ('../foo' -> '/foo') */
+
+	if (len >= 3 && strncmp (in, "../", 3) == 0) {
+		memmove (out, &in[2], len - 2);
+		
+		len -= 2;
+		out[len] = '\0';
+	}
+
+	/* Translate the '..' ('..' -> '/') */
+
+	if (len == 2 && strncmp (in, "..", 2) == 0) {
+		strcpy (out, "/");
+
+		len = 1;
+	}
+
+	/* Translate the '.' ('.' -> '') */
+
+	if (len == 1 && in[0] == '.') {
+		strcpy (out, "");
+
+		len = 0;
+	}
+
+	/* Now put it all together again. */
+
+	if(volume_name_len > 0) {
+		memmove (&out[volume_name_len], in, len);
+		memmove (out, volume_name, volume_name_len);
+
+		len += volume_name_len;
+		out[len] = '\0';
+	}
+
+	return 0;
+}
+
+static void
+pp_library_loader_clean_handle (plibrary_handle handle)
+{
+	IElf->DLClose (pp_library_loader_elf_root, handle);
+}
+
+P_LIB_API PLibraryLoader *
+p_library_loader_new (const pchar *path)
+{
+	PLibraryLoader	*loader = NULL;
+	plibrary_handle	handle  = NULL;
+	pchar		path_buffer[MAXPATHLEN];
+	
+	if (!p_file_is_exists (path))
+		return NULL;
+
+	if (pp_library_loader_elf_root == NULL) {
+		P_ERROR ("PLibraryLoader::p_library_loader_new: shared library subsystem is not initialized");
+		return NULL;
+	}
+
+	if (strlen (path) >= MAXPATHLEN) {
+		P_ERROR ("PLibraryLoader::p_library_loader_new: too long file path or name");
+		return NULL;
+	}
+
+	if (pp_library_loader_translate_path (path, path_buffer, MAXPATHLEN) != 0) {
+		P_ERROR ("PLibraryLoader::p_library_loader_new: failed to convert to UNIX path");
+		return NULL;
+	}
+
+	path = path_buffer;
+
+	handle = IElf->DLOpen (pp_library_loader_elf_root, (CONST_STRPTR) path, ELF32_RTLD_LOCAL);
+
+	if (P_UNLIKELY (handle == NULL)) {
+		P_ERROR ("PLibraryLoader::p_library_loader_new: DLOpen() failed");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((loader = p_malloc0 (sizeof (PLibraryLoader))) == NULL)) {
+		P_ERROR ("PLibraryLoader::p_library_loader_new: failed to allocate memory");
+		pp_library_loader_clean_handle (handle);
+		return NULL;
+	}
+
+	loader->handle     = handle;
+	loader->last_error = ELF32_NO_ERROR;
+
+	return loader;
+}
+
+P_LIB_API PFuncAddr
+p_library_loader_get_symbol (PLibraryLoader *loader, const pchar *sym)
+{
+	APTR func_addr = NULL;
+
+	if (P_UNLIKELY (loader == NULL || sym == NULL || loader->handle == NULL))
+		return NULL;
+
+	if (pp_library_loader_elf_root == NULL) {
+		P_ERROR ("PLibraryLoader::p_library_loader_new: shared library subsystem is not initialized");
+		return NULL;
+	}
+
+	loader->last_error = IElf->DLSym (pp_library_loader_elf_root,
+					  loader->handle,
+					  (CONST_STRPTR) sym,
+					  &func_addr);
+
+	return (PFuncAddr) func_addr;
+}
+
+P_LIB_API void
+p_library_loader_free (PLibraryLoader *loader)
+{
+	if (P_UNLIKELY (loader == NULL))
+		return;
+
+	pp_library_loader_clean_handle (loader->handle);
+
+	p_free (loader);
+}
+
+P_LIB_API pchar *
+p_library_loader_get_last_error (PLibraryLoader *loader)
+{
+	if (P_UNLIKELY (loader == NULL))
+		return NULL;
+
+	switch (loader->last_error) {
+		case ELF32_NO_ERROR:
+			return NULL;
+		case ELF32_OUT_OF_MEMORY:
+			return p_strdup ("Out of memory");
+		case ELF32_INVALID_HANDLE:
+			return p_strdup ("Invalid resource handler");
+		case ELF32_NO_MORE_RELOCS:
+			return p_strdup ("No more relocations left");
+		case ELF32_SECTION_NOT_LOADED:
+			return p_strdup ("Section not loaded");
+		case ELF32_UNKNOWN_RELOC:
+			return p_strdup ("Unknown relocation");
+		case ELF32_READ_ERROR:
+			return p_strdup ("Read error");
+		case ELF32_INVALID_SDA_BASE:
+			return p_strdup ("Invalid SDA base");
+		case ELF32_SYMBOL_NOT_FOUND:
+			return p_strdup ("Symbol not found");
+		case ELF32_INVALID_NAME:
+			return p_strdup ("Invalid procedure name");
+		case ELF32_REQUIRED_OBJECT_MISSING:
+			return p_strdup ("Required object is missing");
+		default:
+			return p_strdup ("Unknown error");
+	}
+}
+
+P_LIB_API pboolean
+p_library_loader_is_ref_counted (void)
+{
+	return TRUE;
+}
+
+void
+p_library_loader_init (void)
+{
+	BPTR		segment_list;
+	Elf32_Handle	elf_handle;
+
+	if (pp_library_loader_elf_root != NULL)
+		return;
+
+	segment_list = IDOS->GetProcSegList (NULL, GPSLF_RUN);
+
+	if (P_UNLIKELY (segment_list == 0)) {
+		P_ERROR ("PLibraryLoader::p_library_loader_init: GetProcSegList() failed");
+		return;
+	}
+
+	if (P_UNLIKELY (IDOS->GetSegListInfoTags (segment_list,
+						  GSLI_ElfHandle,
+						  &elf_handle,
+						  TAG_DONE) != 1)) {
+		P_ERROR ("PLibraryLoader::p_library_loader_init: GetSegListInfoTags() failed");
+		return;
+	}
+
+	if (P_UNLIKELY (elf_handle == NULL)) {
+		P_ERROR ("PLibraryLoader::p_library_loader_init: failed to finf proper GSLI_ElfHandle");
+		return;
+	}
+
+	pp_library_loader_elf_root = IElf->OpenElfTags (OET_ElfHandle, elf_handle, TAG_DONE);
+
+	if (P_UNLIKELY (pp_library_loader_elf_root == NULL)) {
+		P_ERROR ("PLibraryLoader::p_library_loader_init: OpenElfTags() failed");
+		return;
+	}
+}
+
+void
+p_library_loader_shutdown (void)
+{
+	if (pp_library_loader_elf_root == NULL)
+		return;
+
+	IElf->CloseElfTags (pp_library_loader_elf_root, CET_ReClose, TRUE, TAG_DONE);
+
+	pp_library_loader_elf_root = NULL;
+}
diff --git a/3rdparty/plibsys/src/plibraryloader-beos.c b/3rdparty/plibsys/src/plibraryloader-beos.c
new file mode 100644
index 0000000..403f7f4
--- /dev/null
+++ b/3rdparty/plibsys/src/plibraryloader-beos.c
@@ -0,0 +1,142 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016-2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "perror.h"
+#include "pfile.h"
+#include "plibraryloader.h"
+#include "pmem.h"
+#include "pstring.h"
+
+#include <be/kernel/image.h>
+
+typedef image_id plibrary_handle;
+
+struct PLibraryLoader_ {
+	plibrary_handle	handle;
+	status_t	last_status;
+};
+
+static void pp_library_loader_clean_handle (plibrary_handle handle);
+
+static void
+pp_library_loader_clean_handle (plibrary_handle handle)
+{
+	if (P_UNLIKELY (unload_add_on (handle) != B_OK))
+		P_ERROR ("PLibraryLoader::pp_library_loader_clean_handle: unload_add_on() failed");
+}
+
+P_LIB_API PLibraryLoader *
+p_library_loader_new (const pchar *path)
+{
+	PLibraryLoader	*loader = NULL;
+	plibrary_handle	handle;
+
+	if (!p_file_is_exists (path))
+		return NULL;
+
+	if (P_UNLIKELY ((handle = load_add_on (path)) == B_ERROR)) {
+		P_ERROR ("PLibraryLoader::p_library_loader_new: load_add_on() failed");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((loader = p_malloc0 (sizeof (PLibraryLoader))) == NULL)) {
+		P_ERROR ("PLibraryLoader::p_library_loader_new: failed to allocate memory");
+		pp_library_loader_clean_handle (handle);
+		return NULL;
+	}
+
+	loader->handle      = handle;
+	loader->last_status = B_OK;
+
+	return loader;
+}
+
+P_LIB_API PFuncAddr
+p_library_loader_get_symbol (PLibraryLoader *loader, const pchar *sym)
+{
+	ppointer	location = NULL;
+	status_t	status;
+
+	if (P_UNLIKELY (loader == NULL || sym == NULL))
+		return NULL;
+
+	if (P_UNLIKELY ((status = get_image_symbol (loader->handle,
+						    (pchar *) sym,
+						    B_SYMBOL_TYPE_ANY,
+						    &location)) != B_OK)) {
+		P_ERROR ("PLibraryLoader::p_library_loader_get_symbol: get_image_symbol() failed");
+		loader->last_status = status;
+		return NULL;
+	}
+
+	loader->last_status = B_OK;
+
+	return (PFuncAddr) location;
+}
+
+P_LIB_API void
+p_library_loader_free (PLibraryLoader *loader)
+{
+	if (P_UNLIKELY (loader == NULL))
+		return;
+
+	pp_library_loader_clean_handle (loader->handle);
+
+	p_free (loader);
+}
+
+P_LIB_API pchar *
+p_library_loader_get_last_error (PLibraryLoader *loader)
+{
+	if (loader == NULL)
+		return NULL;
+
+	switch (loader->last_status) {
+		case B_OK:
+			return NULL;
+		case B_BAD_IMAGE_ID:
+			return p_strdup ("Image handler doesn't identify an existing image");
+		case B_BAD_INDEX:
+			return p_strdup ("Invalid symbol index");
+		default:
+			return p_strdup ("Unknown error");
+	}
+}
+
+P_LIB_API pboolean
+p_library_loader_is_ref_counted (void)
+{
+	return TRUE;
+}
+
+void
+p_library_loader_init (void)
+{
+}
+
+void
+p_library_loader_shutdown (void)
+{
+}
diff --git a/3rdparty/plibsys/src/plibraryloader-none.c b/3rdparty/plibsys/src/plibraryloader-none.c
new file mode 100644
index 0000000..aab6d87
--- /dev/null
+++ b/3rdparty/plibsys/src/plibraryloader-none.c
@@ -0,0 +1,75 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016-2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "plibraryloader.h"
+
+P_LIB_API PLibraryLoader *
+p_library_loader_new (const pchar *path)
+{
+	P_ERROR ("PLibraryLoader::p_library_loader_new: not implemented");
+	return NULL;
+}
+
+P_LIB_API PFuncAddr
+p_library_loader_get_symbol (PLibraryLoader *loader, const pchar *sym)
+{
+	P_UNUSED (loader);
+	P_UNUSED (sym);
+
+	P_ERROR ("PLibraryLoader::p_library_loader_get_symbol: not implemented");
+	return NULL;
+}
+
+P_LIB_API void
+p_library_loader_free (PLibraryLoader *loader)
+{
+	P_UNUSED (loader);
+	P_ERROR ("PLibraryLoader::p_library_loader_free: not implemented");
+}
+
+P_LIB_API pchar *
+p_library_loader_get_last_error (PLibraryLoader *loader)
+{
+	P_UNUSED (loader);
+
+	P_ERROR ("PLibraryLoader::p_library_loader_get_last_error: not implemented");
+	return NULL;
+}
+
+P_LIB_API pboolean
+p_library_loader_is_ref_counted (void)
+{
+	return FALSE;
+}
+
+void
+p_library_loader_init (void)
+{
+}
+
+void
+p_library_loader_shutdown (void)
+{
+}
diff --git a/3rdparty/plibsys/src/plibraryloader-os2.c b/3rdparty/plibsys/src/plibraryloader-os2.c
new file mode 100644
index 0000000..0f6c95d
--- /dev/null
+++ b/3rdparty/plibsys/src/plibraryloader-os2.c
@@ -0,0 +1,155 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "perror.h"
+#include "pfile.h"
+#include "plibraryloader.h"
+#include "pmem.h"
+#include "pstring.h"
+
+#define INCL_DOSMODULEMGR
+#define INCL_DOSERRORS
+#include <os2.h>
+
+typedef HMODULE plibrary_handle;
+
+struct PLibraryLoader_ {
+	plibrary_handle	handle;
+	APIRET		last_error;
+};
+
+static void pp_library_loader_clean_handle (plibrary_handle handle);
+
+static void
+pp_library_loader_clean_handle (plibrary_handle handle)
+{
+	APIRET ulrc;
+
+	while ((ulrc = DosFreeModule (handle)) == ERROR_INTERRUPT)
+		;
+
+	if (P_UNLIKELY (ulrc != NO_ERROR))
+		P_ERROR ("PLibraryLoader::pp_library_loader_clean_handle: DosFreeModule() failed");
+}
+
+P_LIB_API PLibraryLoader *
+p_library_loader_new (const pchar *path)
+{
+	PLibraryLoader	*loader = NULL;
+	plibrary_handle	handle  = NULLHANDLE;
+	UCHAR		load_err[256];
+	APIRET		ulrc;
+
+
+	if (!p_file_is_exists (path))
+		return NULL;
+
+	while ((ulrc = DosLoadModule ((PSZ) load_err,
+				      sizeof (load_err),
+				      (PSZ) path,
+				      (PHMODULE) &handle)) == ERROR_INTERRUPT)
+		;
+
+	if (P_UNLIKELY (ulrc != NO_ERROR)) {
+		P_ERROR ("PLibraryLoader::p_library_loader_new: DosLoadModule() failed");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((loader = p_malloc0 (sizeof (PLibraryLoader))) == NULL)) {
+		P_ERROR ("PLibraryLoader::p_library_loader_new: failed to allocate memory");
+		pp_library_loader_clean_handle (handle);
+		return NULL;
+	}
+
+	loader->handle     = handle;
+	loader->last_error = NO_ERROR;
+
+	return loader;
+}
+
+P_LIB_API PFuncAddr
+p_library_loader_get_symbol (PLibraryLoader *loader, const pchar *sym)
+{
+	PFN	func_addr = NULL;
+	APIRET	ulrc;
+
+	if (P_UNLIKELY (loader == NULL || sym == NULL || loader->handle == NULL))
+		return NULL;
+
+	if (P_UNLIKELY ((ulrc = DosQueryProcAddr (loader->handle, 0, (PSZ) sym, &func_addr)) != NO_ERROR)) {
+		P_ERROR ("PLibraryLoader::p_library_loader_get_symbol: DosQueryProcAddr() failed");
+		loader->last_error = ulrc;
+		return NULL;
+	}
+
+	loader->last_error = NO_ERROR;
+
+	return (PFuncAddr) func_addr;
+}
+
+P_LIB_API void
+p_library_loader_free (PLibraryLoader *loader)
+{
+	if (P_UNLIKELY (loader == NULL))
+		return;
+
+	pp_library_loader_clean_handle (loader->handle);
+
+	p_free (loader);
+}
+
+P_LIB_API pchar *
+p_library_loader_get_last_error (PLibraryLoader *loader)
+{
+	if (loader == NULL)
+		return NULL;
+
+	switch (loader->last_error) {
+		case NO_ERROR:
+			return NULL;
+		case ERROR_INVALID_HANDLE:
+			return p_strdup ("Invalid resource handler");
+		case ERROR_INVALID_NAME:
+			return p_strdup ("Invalid procedure name");
+		default:
+			return p_strdup ("Unknown error");
+	}
+}
+
+P_LIB_API pboolean
+p_library_loader_is_ref_counted (void)
+{
+	return TRUE;
+}
+
+void
+p_library_loader_init (void)
+{
+}
+
+void
+p_library_loader_shutdown (void)
+{
+}
diff --git a/3rdparty/plibsys/src/plibraryloader-posix.c b/3rdparty/plibsys/src/plibraryloader-posix.c
new file mode 100644
index 0000000..75ef8ed
--- /dev/null
+++ b/3rdparty/plibsys/src/plibraryloader-posix.c
@@ -0,0 +1,148 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2015-2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "perror.h"
+#include "pfile.h"
+#include "plibraryloader.h"
+#include "pmem.h"
+#include "pstring.h"
+
+#include <dlfcn.h>
+
+/* FreeBSD may cause a segfault: https://reviews.freebsd.org/D5112,
+ * DragonFlyBSD as well, so we need to check a file size before calling dlopen()
+ */
+#if defined (P_OS_FREEBSD) || defined (P_OS_DRAGONFLY)
+#  include <unistd.h>
+#  include <sys/types.h>
+#  include <sys/stat.h>
+#endif
+
+typedef ppointer plibrary_handle;
+
+struct PLibraryLoader_ {
+	plibrary_handle	handle;
+};
+
+static void pp_library_loader_clean_handle (plibrary_handle handle);
+
+static void
+pp_library_loader_clean_handle (plibrary_handle handle)
+{
+	if (P_UNLIKELY (dlclose (handle) != 0))
+		P_ERROR ("PLibraryLoader::pp_library_loader_clean_handle: dlclose() failed");
+}
+
+P_LIB_API PLibraryLoader *
+p_library_loader_new (const pchar *path)
+{
+	PLibraryLoader	*loader = NULL;
+	plibrary_handle	handle;
+#if defined (P_OS_FREEBSD) || defined (P_OS_DRAGONFLY)
+	struct stat	stat_buf;
+#endif
+
+	if (!p_file_is_exists (path))
+		return NULL;
+
+#if defined (P_OS_FREEBSD) || defined (P_OS_DRAGONFLY)
+	if (P_UNLIKELY (stat (path, &stat_buf) != 0)) {
+		P_ERROR ("PLibraryLoader::p_library_loader_new: stat() failed");
+		return NULL;
+	}
+
+	if (P_UNLIKELY (stat_buf.st_size == 0)) {
+		P_ERROR ("PLibraryLoader::p_library_loader_new: unable to handle zero-size file");
+		return NULL;
+	}
+#endif
+
+	if (P_UNLIKELY ((handle = dlopen (path, RTLD_NOW)) == NULL)) {
+		P_ERROR ("PLibraryLoader::p_library_loader_new: dlopen() failed");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((loader = p_malloc0 (sizeof (PLibraryLoader))) == NULL)) {
+		P_ERROR ("PLibraryLoader::p_library_loader_new: failed to allocate memory");
+		pp_library_loader_clean_handle (handle);
+		return NULL;
+	}
+
+	loader->handle = handle;
+
+	return loader;
+}
+
+P_LIB_API PFuncAddr
+p_library_loader_get_symbol (PLibraryLoader *loader, const pchar *sym)
+{
+	if (P_UNLIKELY (loader == NULL || sym == NULL || loader->handle == NULL))
+		return NULL;
+
+	return (PFuncAddr) dlsym (loader->handle, sym);
+}
+
+P_LIB_API void
+p_library_loader_free (PLibraryLoader *loader)
+{
+	if (P_UNLIKELY (loader == NULL))
+		return;
+
+	pp_library_loader_clean_handle (loader->handle);
+
+	p_free (loader);
+}
+
+P_LIB_API pchar *
+p_library_loader_get_last_error (PLibraryLoader *loader)
+{
+	pchar *res = NULL;
+	pchar *msg;
+
+	P_UNUSED (loader);
+
+	msg = dlerror ();
+
+	if (msg != NULL)
+		res = p_strdup (msg);
+
+	return res;
+}
+
+P_LIB_API pboolean
+p_library_loader_is_ref_counted (void)
+{
+	return TRUE;
+}
+
+void
+p_library_loader_init (void)
+{
+}
+
+void
+p_library_loader_shutdown (void)
+{
+}
diff --git a/3rdparty/plibsys/src/plibraryloader-shl.c b/3rdparty/plibsys/src/plibraryloader-shl.c
new file mode 100644
index 0000000..8129697
--- /dev/null
+++ b/3rdparty/plibsys/src/plibraryloader-shl.c
@@ -0,0 +1,140 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "perror.h"
+#include "pfile.h"
+#include "plibraryloader.h"
+#include "pmem.h"
+#include "pstring.h"
+
+#include <dl.h>
+#include <errno.h>
+#include <string.h>
+
+typedef shl_t plibrary_handle;
+
+struct PLibraryLoader_ {
+	plibrary_handle	handle;
+	int		last_error;
+};
+
+static void pp_library_loader_clean_handle (plibrary_handle handle);
+
+static void
+pp_library_loader_clean_handle (plibrary_handle handle)
+{
+	if (P_UNLIKELY (shl_unload (handle) != 0))
+		P_ERROR ("PLibraryLoader::pp_library_loader_clean_handle: shl_unload() failed");
+}
+
+P_LIB_API PLibraryLoader *
+p_library_loader_new (const pchar *path)
+{
+	PLibraryLoader	*loader = NULL;
+	plibrary_handle	handle;
+
+	if (!p_file_is_exists (path))
+		return NULL;
+
+	if (P_UNLIKELY ((handle = shl_load (path, BIND_IMMEDIATE | BIND_NONFATAL | DYNAMIC_PATH, 0)) == NULL)) {
+		P_ERROR ("PLibraryLoader::p_library_loader_new: shl_load() failed");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((loader = p_malloc0 (sizeof (PLibraryLoader))) == NULL)) {
+		P_ERROR ("PLibraryLoader::p_library_loader_new: failed to allocate memory");
+		pp_library_loader_clean_handle (handle);
+		return NULL;
+	}
+
+	loader->handle     = handle;
+	loader->last_error = 0;
+
+	return loader;
+}
+
+P_LIB_API PFuncAddr
+p_library_loader_get_symbol (PLibraryLoader *loader, const pchar *sym)
+{
+	PFuncAddr func_addr = NULL;
+
+	if (P_UNLIKELY (loader == NULL || sym == NULL || loader->handle == NULL))
+		return NULL;
+
+	if (P_UNLIKELY (shl_findsym (&loader->handle, sym, TYPE_UNDEFINED, (ppointer) &func_addr) != 0)) {
+		P_ERROR ("PLibraryLoader::p_library_loader_get_symbol: shl_findsym() failed");
+		loader->last_error = (errno == 0 ? -1 : errno);
+		return NULL;
+	}
+
+	loader->last_error = 0;
+
+	return func_addr;
+}
+
+P_LIB_API void
+p_library_loader_free (PLibraryLoader *loader)
+{
+	if (P_UNLIKELY (loader == NULL))
+		return;
+
+	pp_library_loader_clean_handle (loader->handle);
+
+	p_free (loader);
+}
+
+P_LIB_API pchar *
+p_library_loader_get_last_error (PLibraryLoader *loader)
+{
+	if (loader == NULL)
+		return NULL;
+
+	if (loader->last_error == 0)
+		return NULL;
+	else if (loader->last_error == -1)
+		return p_strdup ("Failed to find a symbol");
+	else
+		return p_strdup (strerror (loader->last_error));
+}
+
+P_LIB_API pboolean
+p_library_loader_is_ref_counted (void)
+{
+#if defined (P_OS_HPUX) && defined (P_CPU_HPPA) && (PLIBSYS_SIZEOF_VOID_P == 4)
+	return FALSE;
+#else
+	return TRUE;
+#endif
+}
+
+void
+p_library_loader_init (void)
+{
+}
+
+void
+p_library_loader_shutdown (void)
+{
+}
diff --git a/3rdparty/plibsys/src/plibraryloader-win.c b/3rdparty/plibsys/src/plibraryloader-win.c
new file mode 100644
index 0000000..80c5aba
--- /dev/null
+++ b/3rdparty/plibsys/src/plibraryloader-win.c
@@ -0,0 +1,140 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2015-2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "perror.h"
+#include "pfile.h"
+#include "plibraryloader.h"
+#include "pmem.h"
+#include "pstring.h"
+
+typedef HINSTANCE	plibrary_handle;
+
+struct PLibraryLoader_ {
+	plibrary_handle	handle;
+};
+
+static void pp_library_loader_clean_handle (plibrary_handle handle);
+
+static void
+pp_library_loader_clean_handle (plibrary_handle handle)
+{
+	if (P_UNLIKELY (!FreeLibrary (handle)))
+		P_ERROR ("PLibraryLoader::pp_library_loader_clean_handle: FreeLibrary() failed");
+}
+
+P_LIB_API PLibraryLoader *
+p_library_loader_new (const pchar *path)
+{
+	PLibraryLoader	*loader = NULL;
+	plibrary_handle	handle;
+
+	if (!p_file_is_exists (path))
+		return NULL;
+
+	if (P_UNLIKELY ((handle = LoadLibraryA (path)) == NULL)) {
+		P_ERROR ("PLibraryLoader::p_library_loader_new: LoadLibraryA() failed");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((loader = p_malloc0 (sizeof (PLibraryLoader))) == NULL)) {
+		P_ERROR ("PLibraryLoader::p_library_loader_new: failed to allocate memory");
+		pp_library_loader_clean_handle (handle);
+		return NULL;
+	}
+
+	loader->handle = handle;
+
+	return loader;
+}
+
+P_LIB_API PFuncAddr
+p_library_loader_get_symbol (PLibraryLoader *loader, const pchar *sym)
+{
+	PFuncAddr ret_sym = NULL;
+
+	if (P_UNLIKELY (loader == NULL || sym == NULL || loader->handle == NULL))
+		return NULL;
+
+	ret_sym = (PFuncAddr) GetProcAddress (loader->handle, sym);
+
+	return ret_sym;
+}
+
+P_LIB_API void
+p_library_loader_free (PLibraryLoader *loader)
+{
+	if (P_UNLIKELY (loader == NULL))
+		return;
+
+	pp_library_loader_clean_handle (loader->handle);
+
+	p_free (loader);
+}
+
+P_LIB_API pchar *
+p_library_loader_get_last_error (PLibraryLoader *loader)
+{
+	pchar	*res = NULL;
+	DWORD	err_code;
+	LPVOID	msg_buf;
+
+	P_UNUSED (loader);
+
+	err_code = p_error_get_last_system ();
+
+	if (err_code == 0)
+		return NULL;
+
+	if (P_LIKELY (FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER |
+				      FORMAT_MESSAGE_FROM_SYSTEM |
+				      FORMAT_MESSAGE_IGNORE_INSERTS,
+				      NULL,
+				      err_code,
+				      MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
+				      (LPSTR) &msg_buf,
+				      0,
+				      NULL) != 0)) {
+		res = p_strdup ((pchar *) msg_buf);
+		LocalFree (msg_buf);
+	}
+
+	return res;
+}
+
+P_LIB_API pboolean
+p_library_loader_is_ref_counted (void)
+{
+	return TRUE;
+}
+
+void
+p_library_loader_init (void)
+{
+}
+
+void
+p_library_loader_shutdown (void)
+{
+}
diff --git a/3rdparty/plibsys/src/plibraryloader.h b/3rdparty/plibsys/src/plibraryloader.h
new file mode 100644
index 0000000..d44bef2
--- /dev/null
+++ b/3rdparty/plibsys/src/plibraryloader.h
@@ -0,0 +1,155 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2015-2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file plibraryloader.h
+ * @brief Shared library loader
+ * @author Alexander Saprykin
+ *
+ * All modern operating systems support dynamic loadable objects. Such objects
+ * are compiled with special flags and can be loaded by other programs and
+ * libraries later at the runtime. These loadable objects often called as the
+ * shared libraries, though some platforms even allow to treat the program
+ * binary as a loadable object, too.
+ *
+ * When the program is linked with a shared library its dependency would be
+ * resolved by the operating system automatically before starting the program.
+ * But in some circumstances you may need to load a shared library object
+ * explicitly (i.e. implementing a plugin subsystem, checking for API
+ * availability).
+ *
+ * All functions and variables which a shared library is exporting are called
+ * symbols. Usually only the exported symbols are available from outside the
+ * shared library. Actually all those symbols represent a library API.
+ *
+ * Use p_library_loader_new() to load a shared library and
+ * p_library_loader_get_symbol() to retrieve a pointer to a symbol within it.
+ * Close the library after usage with p_library_loader_free().
+ *
+ * Please note the following platform specific differences:
+ *
+ * - HP-UX doesn't support loading libraries containing TLS and built with
+ * static TLS model. The same rule applies to any library used as dependency.
+ * HP-UX on 32-bit PA-RISC systems doesn't support reference counting for loaded
+ * libraries when using shl_* family of functions (always removes all library
+ * references on unload).
+ *
+ * - On OpenVMS only shareable images (linked with /SHAREABLE) can be used for
+ * dynamic symbol resolving. Usually they have .EXE extension.
+ *
+ * - BeOS supports dynamic loading for add-ons only. It is also possible to
+ * load the same library several times independently (not like a traditional
+ * shared library).
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PLIBRARYLOADER_H
+#define PLIBSYS_HEADER_PLIBRARYLOADER_H
+
+#include <pmacros.h>
+#include <ptypes.h>
+
+P_BEGIN_DECLS
+
+/** Opaque data structure to handle a shared library. */
+typedef struct PLibraryLoader_ PLibraryLoader;
+
+/** Pointer to a function address. */
+typedef void (*PFuncAddr) (void);
+
+/**
+ * @brief Loads a shared library.
+ * @param path Path to the shared library file.
+ * @return Pointer to #PLibraryLoader in case of success, NULL otherwise.
+ * @since 0.0.1
+ *
+ * If you are loading the already loaded shared library, an operating system
+ * increments corresponding reference count and decrements it after freeing
+ * #PLibraryLoader, thus the shared library would be unloaded from the address
+ * space only when the counter becomes zero.
+ */
+P_LIB_API PLibraryLoader *	p_library_loader_new		(const pchar	*path);
+
+/**
+ * @brief Gets a pointer to a symbol in the loaded shared library.
+ * @param loader Pointer to the loaded shared library handle.
+ * @param sym Name of the symbol.
+ * @return Pointer to the symbol in case of success, NULL otherwise.
+ * @since 0.0.1
+ *
+ * Since the symbol may have a NULL value, the returned NULL value from this
+ * call actually doesn't mean the failed result. You can additionally check the
+ * error result using p_library_loader_get_last_error().
+ */
+P_LIB_API PFuncAddr		p_library_loader_get_symbol	(PLibraryLoader	*loader,
+								 const pchar	*sym);
+
+/**
+ * @brief Frees memory and allocated resources of #PLibraryLoader.
+ * @param loader #PLibraryLoader object to free.
+ * @since 0.0.1
+ */
+P_LIB_API void			p_library_loader_free		(PLibraryLoader	*loader);
+
+/**
+ * @brief Gets the last occurred error.
+ * @param loader #PLibraryLoader object to get error for.
+ * @return Human readable error string in case of success, NULL otherwise.
+ * @since 0.0.1
+ * @version 0.0.3 @p loader parameter was added.
+ * @note Caller takes ownership of the returned string.
+ *
+ * The NULL result may indicate that no error was occurred since the last call.
+ *
+ * Different operating systems have different behavior on error indicating.
+ * Some systems reset an error status before the call, some do not. Some
+ * systems write the successful call result (usually zero) to the error status,
+ * thus resetting an error from the previous call.
+ *
+ * Some operating systems may return last error even if library handler was not
+ * created. In that case try to pass NULL value as a parameter.
+ */
+P_LIB_API pchar *		p_library_loader_get_last_error	(PLibraryLoader	*loader);
+
+/**
+ * @brief Checks whether library loading subsystem uses reference counting.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.3
+ *
+ * When reference counting is supported, the same shared library can be opened
+ * several times, but it would be completely unloaded from the memory only when
+ * the last reference to it is removed.
+ *
+ * @note For now, only HP-UX on 32-bit PA-RISC systems with shl_* model doesn't
+ * support reference counting.
+ */
+P_LIB_API pboolean		p_library_loader_is_ref_counted (void);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PLIBRARYLOADER_H */
diff --git a/3rdparty/plibsys/src/plibsys-private.h b/3rdparty/plibsys/src/plibsys-private.h
new file mode 100644
index 0000000..75a9507
--- /dev/null
+++ b/3rdparty/plibsys/src/plibsys-private.h
@@ -0,0 +1,86 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2013-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PLIBSYS_PRIVATE_H
+#define PLIBSYS_HEADER_PLIBSYS_PRIVATE_H
+
+#include "pmacros.h"
+#include "ptypes.h"
+
+P_BEGIN_DECLS
+
+#ifndef PLIBSYS_HAS_SOCKLEN_T
+#  ifdef P_OS_VMS
+typedef unsigned int socklen_t;
+#  else
+typedef int socklen_t;
+#  endif
+#endif
+
+#ifndef PLIBSYS_HAS_SOCKADDR_STORAGE
+/* According to RFC 2553 */
+#  define _PLIBSYS_SS_MAXSIZE	128
+#  define _PLIBSYS_SS_ALIGNSIZE	(sizeof (pint64))
+
+#  ifdef PLIBSYS_SOCKADDR_HAS_SA_LEN
+#    define _PLIBSYS_SS_PAD1SIZE	(_PLIBSYS_SS_ALIGNSIZE - (sizeof (puchar) + sizeof (puchar)))
+#  else
+#    define _PLIBSYS_SS_PAD1SIZE	(_PLIBSYS_SS_ALIGNSIZE - sizeof (puchar))
+#  endif
+
+#  define _PLIBSYS_SS_PAD2SIZE	(_PLIBSYS_SS_MAXSIZE - (sizeof (puchar) + _PLIBSYS_SS_PAD1SIZE + _PLIBSYS_SS_ALIGNSIZE))
+
+struct sockaddr_storage {
+#  ifdef PLIBSYS_SOCKADDR_HAS_SA_LEN
+	puchar		ss_len;
+#  endif
+#  ifdef PLIBSYS_SIZEOF_SAFAMILY_T
+#    if (PLIBSYS_SIZEOF_SAFAMILY_T == 1)
+	puchar		ss_family;
+#    elif (PLIBSYS_SIZEOF_SAFAMILY_T == 2)
+	pushort		ss_family;
+#    else
+	puint		ss_family;
+#    endif
+#  else
+#    ifdef PLIBSYS_SOCKADDR_HAS_SA_LEN
+	puchar		ss_family;
+#    else
+	pushort		ss_family;
+#    endif
+#  endif
+	pchar		__ss_pad1[_PLIBSYS_SS_PAD1SIZE];
+	pint64		__ss_align;
+	pchar		__ss_pad2[_PLIBSYS_SS_PAD2SIZE];
+};
+#endif
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PLIBSYS_PRIVATE_H */
diff --git a/3rdparty/plibsys/src/plibsys.h b/3rdparty/plibsys/src/plibsys.h
new file mode 100644
index 0000000..c322747
--- /dev/null
+++ b/3rdparty/plibsys/src/plibsys.h
@@ -0,0 +1,64 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+#ifndef PLIBSYS_HEADER_PLIBSYS_H
+#define PLIBSYS_HEADER_PLIBSYS_H
+
+#define PLIBSYS_H_INSIDE
+
+#include "plibsysconfig.h"
+#include "patomic.h"
+#include "pcondvariable.h"
+#include "pcryptohash.h"
+#include "pdir.h"
+#include "perror.h"
+#include "pfile.h"
+#include "phashtable.h"
+#include "pinifile.h"
+#include "plibraryloader.h"
+#include "plist.h"
+#include "pmacros.h"
+#include "pmacroscompiler.h"
+#include "pmacroscpu.h"
+#include "pmacrosos.h"
+#include "pmain.h"
+#include "pmem.h"
+#include "pmutex.h"
+#include "pprocess.h"
+#include "prwlock.h"
+#include "psemaphore.h"
+#include "pshm.h"
+#include "pshmbuffer.h"
+#include "psocket.h"
+#include "psocketaddress.h"
+#include "pspinlock.h"
+#include "pstdarg.h"
+#include "pstring.h"
+#include "ptimeprofiler.h"
+#include "ptree.h"
+#include "ptypes.h"
+#include "puthread.h"
+
+#endif /* PLIBSYS_HEADER_PLIBSYS_H */
diff --git a/3rdparty/plibsys/src/plibsysconfig.h.in b/3rdparty/plibsys/src/plibsysconfig.h.in
new file mode 100644
index 0000000..db06020
--- /dev/null
+++ b/3rdparty/plibsys/src/plibsysconfig.h.in
@@ -0,0 +1,76 @@
+#ifndef PLIBSYS_HEADER_PLIBSYSCONFIG_H
+#define PLIBSYS_HEADER_PLIBSYSCONFIG_H
+
+#define PLIBSYS_VERSION_MAJOR @PLIBSYS_VERSION_MAJOR@
+#define PLIBSYS_VERSION_MINOR @PLIBSYS_VERSION_MINOR@
+#define PLIBSYS_VERSION_PATCH @PLIBSYS_VERSION_PATCH@
+#define PLIBSYS_VERSION_STR "@PLIBSYS_VERSION@"
+#define PLIBSYS_VERSION @PLIBSYS_VERSION_NUM@
+
+#cmakedefine PLIBSYS_NEED_WINDOWS_H
+#cmakedefine PLIBSYS_NEED_FLOAT_H
+#cmakedefine PLIBSYS_NEED_LIMITS_H
+#cmakedefine PLIBSYS_NEED_VALUES_H
+#cmakedefine PLIBSYS_NEED_PTHREAD_NP_H
+#cmakedefine PLIBSYS_IS_BIGENDIAN
+#cmakedefine PLIBSYS_SIZEOF_SAFAMILY_T @PLIBSYS_SIZEOF_SAFAMILY_T@
+#cmakedefine PLIBSYS_VA_COPY @PLIBSYS_VA_COPY@
+
+#define PLIBSYS_NTDDI_VERSION_FROM_WIN32_WINNT2(ver)    ver##0000
+#define PLIBSYS_NTDDI_VERSION_FROM_WIN32_WINNT(ver)     PLIBSYS_NTDDI_VERSION_FROM_WIN32_WINNT2(ver)
+
+#ifdef PLIBSYS_NEED_WINDOWS_H
+#  ifndef _WIN32_WINNT
+#    define _WIN32_WINNT 0x501
+#  endif
+#  ifndef NTDDI_VERSION
+#    define NTDDI_VERSION PLIBSYS_NTDDI_VERSION_FROM_WIN32_WINNT(_WIN32_WINNT)
+#  endif
+#  include <winsock2.h>
+#  include <ws2tcpip.h>
+#  include <windows.h>
+#endif
+
+#include <pmacros.h>
+
+#ifdef PLIBSYS_NEED_FLOAT_H
+#  include <float.h>
+#endif
+
+#ifdef PLIBSYS_NEED_LIMITS_H
+#  include <limits.h>
+#endif
+
+#ifdef PLIBSYS_NEED_VALUES_H
+#  include <values.h>
+#endif
+
+P_BEGIN_DECLS
+
+#define P_MINFLOAT    @PLIBSYS_FLOAT_MIN@
+#define P_MAXFLOAT    @PLIBSYS_FLOAT_MAX@
+#define P_MINDOUBLE   @PLIBSYS_DOUBLE_MIN@
+#define P_MAXDOUBLE   @PLIBSYS_DOUBLE_MAX@
+#define P_MINSHORT    @PLIBSYS_SHORT_MIN@
+#define P_MAXSHORT    @PLIBSYS_SHORT_MAX@
+#define P_MAXUSHORT   @PLIBSYS_USHORT_MAX@
+#define P_MININT      @PLIBSYS_INT_MIN@
+#define P_MAXINT      @PLIBSYS_INT_MAX@
+#define P_MAXUINT     @PLIBSYS_UINT_MAX@
+#define P_MINLONG     @PLIBSYS_LONG_MIN@
+#define P_MAXLONG     @PLIBSYS_LONG_MAX@
+#define P_MAXULONG    @PLIBSYS_ULONG_MAX@
+
+@PLIBSYS_SIZEOF_VOID_P_CODE@
+@PLIBSYS_SIZEOF_SIZE_T_CODE@
+@PLIBSYS_SIZEOF_LONG_CODE@
+
+#ifdef PLIBSYS_IS_BIGENDIAN
+#  define P_BYTE_ORDER P_BIG_ENDIAN
+#else
+#  define P_BYTE_ORDER P_LITTLE_ENDIAN
+#endif
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PLIBSYSCONFIG_H */
diff --git a/3rdparty/plibsys/src/plist.c b/3rdparty/plibsys/src/plist.c
new file mode 100644
index 0000000..ccb5dee
--- /dev/null
+++ b/3rdparty/plibsys/src/plist.c
@@ -0,0 +1,174 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "plist.h"
+
+#include <stdlib.h>
+
+P_LIB_API PList *
+p_list_append (PList *list, ppointer data)
+{
+	PList *item, *cur;
+
+	if (P_UNLIKELY ((item = p_malloc0 (sizeof (PList))) == NULL)) {
+		P_ERROR ("PList::p_list_append: failed to allocate memory");
+		return list;
+	}
+
+	item->data = data;
+
+	/* List is empty */
+	if (P_UNLIKELY (list == NULL))
+		return item;
+
+	for (cur = list; cur->next != NULL; cur = cur->next)
+		;
+	cur->next = item;
+
+	return list;
+}
+
+P_LIB_API PList *
+p_list_remove (PList *list, ppointer data)
+{
+	PList *cur, *prev, *head;
+
+	if (P_UNLIKELY (list == NULL))
+		return NULL;
+
+	for (head = list, prev = NULL, cur = list; cur != NULL;  prev = cur, cur = cur->next) {
+		if (cur->data == data) {
+			if (prev == NULL)
+				head = cur->next;
+			else
+				prev->next = cur->next;
+
+			p_free (cur);
+
+			break;
+		}
+	}
+
+	return head;
+}
+
+P_LIB_API void
+p_list_foreach (PList *list, PFunc func, ppointer user_data)
+{
+	PList *cur;
+
+	if (P_UNLIKELY (list == NULL || func == NULL))
+		return;
+
+	for (cur = list; cur != NULL; cur = cur->next)
+		func (cur->data, user_data);
+}
+
+P_LIB_API void
+p_list_free (PList *list)
+{
+	PList *cur, *next;
+
+	if (P_UNLIKELY (list == NULL))
+		return;
+
+	for (next = cur = list; cur != NULL && next != NULL; cur = next)  {
+		next = cur->next;
+		p_free (cur);
+	}
+}
+
+P_LIB_API PList *
+p_list_last (PList *list)
+{
+	PList *cur;
+
+	if (P_UNLIKELY (list == NULL))
+		return NULL;
+
+	for (cur = list; cur->next != NULL; cur = cur->next)
+		;
+
+	return cur;
+}
+
+P_LIB_API psize
+p_list_length (const PList *list)
+{
+	const PList	*cur;
+	psize		ret;
+
+	if (P_UNLIKELY (list == NULL))
+		return 0;
+
+	for (cur = list, ret = 1; cur->next != NULL; cur = cur->next, ++ret)
+		;
+
+	return ret;
+}
+
+P_LIB_API PList *
+p_list_prepend	(PList *list, ppointer data)
+{
+	PList *item;
+
+	if (P_UNLIKELY ((item = p_malloc0 (sizeof (PList))) == NULL)) {
+		P_ERROR ("PList::p_list_prepend: failed to allocate memory");
+		return list;
+	}
+
+	item->data = data;
+
+	/* List is empty */
+	if (P_UNLIKELY (list == NULL))
+		return item;
+
+	item->next = list;
+
+	return item;
+}
+
+P_LIB_API PList *
+p_list_reverse	(PList *list)
+{
+	PList *prev, *cur, *tmp;
+
+	if (P_UNLIKELY (list == NULL))
+		return NULL;
+
+	prev	   = list;
+	cur	   = list->next;
+	prev->next = NULL;
+
+	while (cur != NULL) {
+		tmp	  = cur->next;
+		cur->next = prev;
+		prev	  = cur;
+		cur	  = tmp;
+	}
+
+	return prev;
+}
diff --git a/3rdparty/plibsys/src/plist.h b/3rdparty/plibsys/src/plist.h
new file mode 100644
index 0000000..e72ba0c
--- /dev/null
+++ b/3rdparty/plibsys/src/plist.h
@@ -0,0 +1,199 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file plist.h
+ * @brief Singly linked list
+ * @author Alexander Saprykin
+ *
+ * A singly linked list is a data structure consists of the nodes which
+ * represent a sequence. Each node contains a data pointer and a pointer to the
+ * next node. Every node has a link only to the next node, hence list is a
+ * singly linked (in a single direction).
+ *
+ * As the singly linked list is a linear collection of the nodes with the
+ * sequential access, it has an O(N) average complexity for appending, removing
+ * and searching operations. Prepending a node takes O(1) constant time. Thus it
+ * is not intended for heavy usage, please refer to #PHashTable or #PTree if you
+ * are working with large data sets.
+ *
+ * Before the first usage you must initialize a #PList variable to NULL. After
+ * that you can use the p_list_append(), p_list_prepend(), p_list_remove() and
+ * p_list_reverse() routines to update that variable:
+ * @code
+ * PList       *list;
+ * ppointer    data;
+ *
+ * list = NULL;
+ * data = my_obj_new ();
+ *
+ * list = p_list_append (list, data);
+ * @endcode
+ * #PList stores only the pointers to the data, so you must free used memory
+ * manually, p_list_free() only frees list's internal memory, not the data it
+ * stores the pointers for. The best approach to free used memory is the
+ * p_list_foreach() routine:
+ * @code
+ * PList    *list;
+ * ...
+ * p_list_foreach (list, (PFunc) my_free_func, my_data);
+ * p_list_free (list);
+ * @endcode
+ * Also you can use #P_INT_TO_POINTER and #P_POINTER_TO_INT macros to store
+ * integers (up to 32-bit) without allocating memory for them:
+ * @code
+ * PList    *list;
+ * pint     a;
+ *
+ * list = p_list_append (list, P_INT_TO_POINTER (12));
+ * a = P_POINTER_TO_INT (list->data);
+ * @endcode
+ * #PList can store several nodes with the same pointer value, but
+ * p_list_remove() will remove only the first matching node.
+ *
+ * If you need to add large amount of nodes at once it is better to prepend them
+ * and then reverse the list.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PLIST_H
+#define PLIBSYS_HEADER_PLIST_H
+
+#include <pmacros.h>
+#include <ptypes.h>
+
+P_BEGIN_DECLS
+
+/** Typedef for a list node. */
+typedef struct PList_ PList;
+
+/** Node for a singly linked list. */
+struct PList_ {
+	ppointer	data;	/**< Pointer to the node data.	*/
+	PList		*next;	/**< Next list node.		*/
+};
+
+/**
+ * @brief Appends data to a list.
+ * @param list #PList for appending the data.
+ * @param data Data to append.
+ * @return Pointer to the updated list in case of success, @a list otherwise.
+ * @since 0.0.1
+ *
+ * Before appending the first node to the list, @a list argument must be
+ * initialized with NULL. Otherwise behavior is unpredictable.
+ */
+P_LIB_API PList *	p_list_append	(PList		*list,
+					 ppointer	data) P_GNUC_WARN_UNUSED_RESULT;
+
+/**
+ * @brief Removes data from a list.
+ * @param list List to remove the data from.
+ * @param data Data to remove.
+ * @return Pointer to the updated list in case of success, @a list otherwise.
+ * @since 0.0.1
+ *
+ * It searches for the first matching occurrence in the @a list and removes
+ * that node. Note that it removes only the pointer from the @a list, not the
+ * data it pointers to, so you need to free the data manually.
+ */
+P_LIB_API PList *	p_list_remove	(PList		*list,
+					 ppointer	data) P_GNUC_WARN_UNUSED_RESULT;
+
+/**
+ * @brief Calls a specified function for each list node.
+ * @param list List to go through.
+ * @param func Pointer for the callback function.
+ * @param user_data User defined data, may be NULL.
+ * @since 0.0.1
+ *
+ * This function goes through the whole @a list and calls @a func for each node.
+ * The @a func will receive pointer to the node's data and @a user_data. You can
+ * use it to free the data:
+ * @code
+ * p_list_foreach (list, (PFunc) free, NULL);
+ * p_list_free (list);
+ * @endcode
+ */
+P_LIB_API void		p_list_foreach	(PList		*list,
+					 PFunc		func,
+					 ppointer	user_data);
+
+/**
+ * @brief Frees list memory.
+ * @param list List to free.
+ * @since 0.0.1
+ *
+ * This function frees only the list's internal memory, not the data in the
+ * pointers stored in the nodes. Don't forget to free all the data stored in the
+ * list manually.
+ */
+P_LIB_API void		p_list_free	(PList		*list);
+
+/**
+ * @brief Gets the last node from the list.
+ * @param list List to get the node from.
+ * @return Pointer to the last @a list node, NULL if the @a list is empty.
+ * @since 0.0.1
+ */
+P_LIB_API PList *	p_list_last	(PList		*list);
+
+/**
+ * @brief Gets the number of list nodes.
+ * @param list List to count nodes in.
+ * @return Number of nodes in the @a list.
+ * @since 0.0.1
+ * @note This function will iterate through the whole @a list, so don't use it
+ * in condition of the for-loop or in the code which is repeated a lot of times.
+ */
+P_LIB_API psize		p_list_length	(const PList	*list);
+
+/**
+ * @brief Prepends data to a list.
+ * @param list #PList for prepending the data.
+ * @param data Data to prepend.
+ * @return Pointer to the updated list in case of success, @a list otherwise.
+ * @since 0.0.1
+ *
+ * Before prepending the first node to the list, @a list argument must be
+ * initialized with NULL. Otherwise behavior is unpredictable.
+ */
+P_LIB_API PList *	p_list_prepend	(PList		*list,
+					 ppointer	data) P_GNUC_WARN_UNUSED_RESULT;
+
+/**
+ * @brief Reverses the list order.
+ * @param list #PList to reverse the order.
+ * @return Pointer to the top of the reversed list.
+ * @since 0.0.1
+ */
+P_LIB_API PList *	p_list_reverse	(PList		*list) P_GNUC_WARN_UNUSED_RESULT;
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PLIST_H */
diff --git a/3rdparty/plibsys/src/pmacros.h b/3rdparty/plibsys/src/pmacros.h
new file mode 100644
index 0000000..7751337
--- /dev/null
+++ b/3rdparty/plibsys/src/pmacros.h
@@ -0,0 +1,302 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file pmacros.h
+ * @brief Miscellaneous macros
+ * @author Alexander Saprykin
+ *
+ * All the macros are completely independent of any other platform-specific
+ * headers, thus gurantee to work with any compiler under any operating system
+ * in the same way as they are used within the library.
+ *
+ * This family of macros provides various additional capabilities (compiler
+ * hints, attributes, version, etc.).
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PMACROS_H
+#define PLIBSYS_HEADER_PMACROS_H
+
+#include <pmacroscompiler.h>
+#include <pmacroscpu.h>
+#include <pmacrosos.h>
+
+#include <stdio.h>
+
+/* For Clang */
+#ifndef __has_attribute
+#  define __has_attribute(x) 0
+#endif
+
+#ifndef __has_builtin
+#  define __has_builtin(x) 0
+#endif
+
+/**
+ * @def P_GNUC_WARN_UNUSED_RESULT
+ * @brief Gives a warning if the result returned from a function is not being
+ * used.
+ * @since 0.0.1
+ */
+
+#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) || \
+    __has_attribute(warn_unused_result)
+#  define P_GNUC_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+#  define P_GNUC_WARN_UNUSED_RESULT
+#endif
+
+/**
+ * @def P_LIB_INTERNAL_API
+ * @brief Marks a symbol (variable, function) as local.
+ * @since 0.0.4
+ *
+ * Local symbols are not exported during the linkage and are not available from
+ * the outside of the module they are defined in. Use it for internal API.
+ *
+ * @note Some compilers allow to put this attribute at the beginning of the
+ * symbol declaration, and some also at the end of the declaration. Thus it is
+ * better to put it in the beginning for more portability.
+ */
+
+/**
+ * @def P_LIB_GLOBAL_API
+ * @brief Marks a symbol (variable, function) as global.
+ * @since 0.0.4
+ *
+ * Global symbols are exported during the linkage and are available from the
+ * outside of the module they are defined in. Use it for public API.
+ *
+ * @note Some compilers allow to put this attribute at the beginning of the
+ * symbol declaration, and some also at the end of the declaration. Thus it is
+ * better to put it in the beginning for more portability.
+ */
+
+/*
+ * Oracle Solaris Studio since version 12 has visibility attribute for C
+ * compiler, and since version 12.2 for C++ compiler, or since version 8.0
+ * specific __global attribute which is the same.
+ * IBM XL C has support for visibility attributes since version 13.1.
+ * HP C/aC++ has support for visibility attributes since version A.06.15.
+ */
+
+#if defined(P_CC_MSVC) || defined(P_CC_BORLAND) || defined(P_CC_WATCOM) || \
+    defined(P_OS_OS2)  || (defined(P_OS_BEOS)  && !defined(P_CC_GNU))   || \
+    (defined(P_OS_WIN) && defined(P_CC_PGI)) || \
+    ((defined(P_OS_WIN) || defined(P_OS_CYGWIN) || defined(P_OS_MSYS)) && defined(P_CC_GNU))
+#  define P_LIB_GLOBAL_API __declspec(dllexport)
+#  define P_LIB_INTERNAL_API
+#elif ((__GNUC__ >= 4) && !defined(P_OS_SOLARIS) && !defined(P_OS_HPUX) && !defined(P_OS_AIX)) || \
+      (defined(P_CC_SUN) && __SUNPRO_C  >= 0x590)  || \
+      (defined(P_CC_SUN) && __SUNPRO_CC >= 0x5110) || \
+      (defined(P_CC_XLC) && __xlC__ >= 0x0D01)     || \
+      (defined(P_CC_HP)  && __HP_aCC >= 0x061500)  || \
+      (defined(P_CC_HP)  && __HP_cc >= 0x061500)   || \
+      __has_attribute(visibility)
+#  define P_LIB_GLOBAL_API __attribute__ ((visibility ("default")))
+#  define P_LIB_INTERNAL_API __attribute__ ((visibility ("hidden")))
+#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550)
+#  define P_LIB_GLOBAL_API __global
+#  define P_LIB_INTERNAL_API __hidden
+#else
+#  define P_LIB_GLOBAL_API
+#  define P_LIB_INTERNAL_API
+#endif
+
+/**
+ * @def P_LIB_API
+ * @brief Exports a symbol from a shared library.
+ * @since 0.0.1
+ */
+
+#define P_LIB_API P_LIB_GLOBAL_API
+
+/* Oracle Solaris Studio at least since 12.2 has ((noreturn)) attribute */
+
+/**
+ * @def P_NO_RETURN
+ * @brief Notifies a compiler that a function will never return a value (i.e.
+ * due to the abort () call).
+ * @since 0.0.1
+ */
+
+#if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
+#  define P_NO_RETURN _Noreturn
+#elif defined(P_CC_MSVC) || (defined(P_CC_BORLAND) && __BORLANDC__ >= 0x0550)
+#  define P_NO_RETURN __declspec(noreturn)
+#elif __has_attribute(noreturn) || \
+      defined(P_CC_GNU) || \
+     (defined(P_CC_SUN) && __SUNPRO_C >= 0x5110) || \
+     (defined(P_CC_SUN) && __SUNPRO_CC >= 0x5110)
+#  define P_NO_RETURN __attribute__((noreturn))
+#else
+#  define P_NO_RETURN
+#endif
+
+/**
+ * @def P_LIKELY
+ * @brief Hints a compiler that a condition is likely to be true so it can
+ * perform code optimizations.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_UNLIKELY
+ * @brief Hints a compiler that a condition is likely to be false so it can
+ * perform code optimizations.
+ * @since 0.0.1
+ */
+
+#if (defined(P_CC_GNU) && (__GNUC__ > 2 && __GNUC_MINOR__ > 0)) || \
+    (defined(P_CC_INTEL) && __INTEL_COMPILER >= 800) || \
+    (defined(P_CC_XLC) && __xlC__ >= 0x0900) || \
+    __has_builtin(__builtin_expect)
+#  define P_LIKELY(x) __builtin_expect(!!(x), 1)
+#  define P_UNLIKELY(x) __builtin_expect(!!(x), 0)
+#else
+#  define P_LIKELY(x) (x)
+#  define P_UNLIKELY(x) (x)
+#endif
+
+/**
+ * @def P_UNUSED
+ * @brief Macro to by-pass a compiler warning on unused variables.
+ * @since 0.0.1
+ */
+#define P_UNUSED(a) ((void) a)
+
+/**
+ * @def P_WARNING
+ * @brief Prints a warning message.
+ * @param msg Message to print.
+ * @since 0.0.1
+ */
+#define P_WARNING(msg) printf ("** Warning: %s **\n", msg)
+
+/**
+ * @def P_ERROR
+ * @brief Prints an error message.
+ * @param msg Message to print.
+ * @since 0.0.1
+ */
+#define P_ERROR(msg) printf ("** Error: %s **\n", msg)
+
+/**
+ * @def P_DEBUG
+ * @brief Prints a debug message.
+ * @param msg Message to print.
+ * @since 0.0.1
+ */
+#define P_DEBUG(msg) printf ("** Debug: %s **\n", msg)
+
+#ifdef DOXYGEN
+#  define PLIBSYS_VERSION_MAJOR
+#  define PLIBSYS_VERSION_MINOR
+#  define PLIBSYS_VERSION_PATCH
+#  define PLIBSYS_VERSION_STR
+#  define PLIBSYS_VERSION
+#endif
+
+/**
+ * @def PLIBSYS_VERSION_MAJOR
+ * @brief Library major version number.
+ * @since 0.0.1
+ * @note This is the version against which the application is compiled.
+ */
+
+/**
+ * @def PLIBSYS_VERSION_MINOR
+ * @brief Library minor version number.
+ * @since 0.0.1
+ * @note This is the version against which the application is compiled.
+ */
+
+/**
+ * @def PLIBSYS_VERSION_PATCH
+ * @brief Library patch version number.
+ * @since 0.0.1
+ * @note This is the version against which the application is compiled.
+ */
+
+/**
+ * @def PLIBSYS_VERSION_STR
+ * @brief Library full version in the string form, i.e. "0.0.1".
+ * @since 0.0.1
+ * @note This is the version against which the application is compiled.
+ * @sa p_libsys_version()
+ */
+
+/**
+ * @def PLIBSYS_VERSION
+ * @brief Library full version in the form 0xMMNNPP (MM = major, NN = minor,
+ * PP = patch), i.e. 0x000001.
+ * @since 0.0.1
+ * @note This is the version against which the application is compiled.
+ * @sa p_libsys_version()
+ */
+
+/**
+ * @def PLIBSYS_VERSION_CHECK
+ * @brief Makes a library version number which can be used to check the library
+ * version against which the application is compiled.
+ * @param major Major version number to check.
+ * @param minor Minor version number to check.
+ * @param patch Minor version number to check.
+ * @since 0.0.1
+ * @sa p_libsys_version()
+ *
+ * @code
+ * if (PLIBSYS_VERSION >= PLIBSYS_VERSION_CHECK (0, 0, 1))
+ * ...
+ * @endcode
+ */
+#define PLIBSYS_VERSION_CHECK(major, minor, patch) ((major << 16) | (minor << 8) | (patch))
+
+/**
+ * @def P_BEGIN_DECLS
+ * @brief Starts .h file declarations to be exported as C functions.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_END_DECLS
+ * @brief Closes .h file declarations to be exported as C functions, should be
+ * always used after #P_BEGIN_DECLS.
+ * @since 0.0.1
+ */
+
+#ifdef __cplusplus
+#  define P_BEGIN_DECLS extern "C" {
+#  define P_END_DECLS }
+#else
+#  define P_BEGIN_DECLS
+#  define P_END_DECLS
+#endif
+
+#endif /* PLIBSYS_HEADER_PMACROS_H */
diff --git a/3rdparty/plibsys/src/pmacroscompiler.h b/3rdparty/plibsys/src/pmacroscompiler.h
new file mode 100644
index 0000000..cbe4cc0
--- /dev/null
+++ b/3rdparty/plibsys/src/pmacroscompiler.h
@@ -0,0 +1,253 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file pmacroscompiler.h
+ * @brief Compiler detection macros
+ * @author Alexander Saprykin
+ *
+ * All the macros are completely independent of any other platform-specific
+ * headers, thus gurantee to work with any compiler under any operating system
+ * in the same way as they are used within the library.
+ *
+ * This family of macros provides compiler detection and defines one or several
+ * of P_CC_x macros.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PMACROSCOMPILER_H
+#define PLIBSYS_HEADER_PMACROSCOMPILER_H
+
+/*
+ * List of supported compilers (P_CC_x):
+ *
+ * MSVC            - Microsoft Visual C/C++
+ * GNU             - GNU C/C++
+ * MINGW           - MinGW C/C++
+ * INTEL           - Intel C/C++
+ * CLANG           - LLVM Clang C/C++
+ * SUN             - Sun WorkShop/Studio C/C++
+ * XLC             - IBM XL C/C++
+ * HP              - HP C/aC++
+ * DEC             - DEC C/C++
+ * MIPS            - MIPSpro C/C++
+ * USLC            - SCO OUDK and UDK C/C++
+ * WATCOM          - Watcom C/C++
+ * BORLAND         - Borland C/C++
+ * PGI             - Portland Group C/C++
+ * CRAY            - CRAY C/C++
+ */
+
+/**
+ * @def P_CC_MSVC
+ * @brief Microsoft Visual C/C++ compiler.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_CC_GNU
+ * @brief GNU C/C++ compiler.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_CC_MINGW
+ * @brief MinGW C/C++ compiler.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_CC_INTEL
+ * @brief Intel C/C++ compiler.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_CC_CLANG
+ * @brief LLVM Clang C/C++ compiler.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_CC_SUN
+ * @brief Sun WorkShop/Studio C/C++ compiler.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_CC_XLC
+ * @brief IBM XL C/C++ compiler.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_CC_HP
+ * @brief HP C/aC++ compiler.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_CC_DEC
+ * @brief DEC C/C++ compiler.
+ * @since 0.0.2
+ */
+
+/**
+ * @def P_CC_MIPS
+ * @brief MIPSpro C/C++ compiler.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_CC_USLC
+ * @brief SCO OUDK and UDK C/C++ compiler.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_CC_WATCOM
+ * @brief Watcom C/C++ compiler.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_CC_BORLAND
+ * @brief Borland C/C++ compiler.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_CC_PGI
+ * @brief Portland Group C/C++ compiler.
+ * @since 0.0.3
+ */
+
+/**
+ * @def P_CC_CRAY
+ * @brief Cray C/C++ compiler.
+ * @since 0.0.4
+ */
+
+#if defined(_MSC_VER)
+#  define P_CC_MSVC
+#  if defined(__INTEL_COMPILER)
+#    define P_CC_INTEL
+#  endif
+#  if defined(__clang__)
+#    define P_CC_CLANG
+#  endif
+#elif defined(__GNUC__)
+#  define P_CC_GNU
+#  if defined(__MINGW32__)
+#    define P_CC_MINGW
+#  endif
+#  if defined(__INTEL_COMPILER)
+#    define P_CC_INTEL
+#  endif
+#  if defined(__clang__)
+#    define P_CC_CLANG
+#  endif
+#  if defined(_CRAYC)
+#    define P_CC_CRAY
+#  endif
+#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
+#  define P_CC_SUN
+#elif defined(__xlc__) || defined(__xlC__)
+#  define P_CC_XLC
+#elif defined(__HP_cc) || defined(__HP_aCC)
+#  define P_CC_HP
+#elif defined (__DECC) || defined(__DECCXX)
+#  define P_CC_DEC
+#elif (defined(__sgi) || defined(sgi)) && \
+      (defined(_COMPILER_VERSION) || defined(_SGI_COMPILER_VERSION))
+#  define P_CC_MIPS
+#elif defined(__USLC__) && defined(__SCO_VERSION__)
+#  define P_CC_USLC
+#elif defined(__WATCOMC__)
+#  define P_CC_WATCOM
+#elif defined(__BORLANDC__)
+#  define P_CC_BORLAND
+#elif defined(__INTEL_COMPILER)
+#  define P_CC_INTEL
+#elif defined(__PGI)
+#  define P_CC_PGI
+#elif defined(_CRAYC)
+#  define P_CC_CRAY
+#endif
+
+/* We need this to generate full Doxygen documentation */
+
+#ifdef DOXYGEN
+#  ifndef P_CC_MSVC
+#    define P_CC_MSVC
+#  endif
+#  ifndef P_CC_GNU
+#    define P_CC_GNU
+#  endif
+#  ifndef P_CC_MINGW
+#    define P_CC_MINGW
+#  endif
+#  ifndef P_CC_INTEL
+#    define P_CC_INTEL
+#  endif
+#  ifndef P_CC_CLANG
+#    define P_CC_CLANG
+#  endif
+#  ifndef P_CC_SUN
+#    define P_CC_SUN
+#  endif
+#  ifndef P_CC_XLC
+#    define P_CC_XLC
+#  endif
+#  ifndef P_CC_HP
+#    define P_CC_HP
+#  endif
+#  ifndef P_CC_DEC
+#    define P_CC_DEC
+#  endif
+#  ifndef P_CC_MIPS
+#    define P_CC_MIPS
+#  endif
+#  ifndef P_CC_USLC
+#    define P_CC_USLC
+#  endif
+#  ifndef P_CC_WATCOM
+#    define P_CC_WATCOM
+#  endif
+#  ifndef P_CC_BORLAND
+#    define P_CC_BORLAND
+#  endif
+#  ifndef P_CC_PGI
+#    define P_CC_PGI
+#  endif
+#  ifndef P_CC_CRAY
+#    define P_CC_CRAY
+#  endif
+#endif
+
+#endif /* PLIBSYS_HEADER_PMACROSCOMPILER_H */
diff --git a/3rdparty/plibsys/src/pmacroscpu.h b/3rdparty/plibsys/src/pmacroscpu.h
new file mode 100644
index 0000000..d1b0cbb
--- /dev/null
+++ b/3rdparty/plibsys/src/pmacroscpu.h
@@ -0,0 +1,627 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file pmacroscpu.h
+ * @brief CPU detection macros
+ * @author Alexander Saprykin
+ *
+ * All the macros are completely independent of any other platform-specific
+ * headers, thus gurantee to work with any compiler under any operating system
+ * in the same way as they are used within the library.
+ *
+ * This family of macros provides CPU detection and defines one or several of
+ * P_CPU_x macros.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PMACROSCPU_H
+#define PLIBSYS_HEADER_PMACROSCPU_H
+
+/*
+ * List of supported CPU architectures (P_CPU_x):
+ *
+ * ALPHA           - Alpha
+ * ARM             - ARM architecture revision:
+ *                   v2, v3, v4, v5, v6, v7, v8
+ * ARM_32          - ARM 32-bit
+ * ARM_64          - ARM 64-bit
+ * ARM_V2          - ARMv2 instruction set
+ * ARM_V3          - ARMv3 instruction set
+ * ARM_V4          - ARMv4 instruction set
+ * ARM_V5          - ARMv5 instruction set
+ * ARM_V6          - ARMv6 instruction set
+ * ARM_V7          - ARMv7 instruction set
+ * ARM_V8          - ARMv8 instruction set
+ * X86             - x86 architecture revision:
+ *                   3, 4, 5, 6 (Intel P6 or better)
+ * X86_32          - x86 32-bit
+ * X86_64          - x86 64-bit
+ * IA64            - Intel Itanium (IA-64)
+ * MIPS            - MIPS
+ * MIPS_I          - MIPS I
+ * MIPS_II         - MIPS II
+ * MIPS_III        - MIPS III
+ * MIPS_IV         - MIPS IV
+ * MIPS_32         - MIPS32
+ * MIPS_64         - MIPS64
+ * POWER           - PowerPC
+ * POWER_32        - PowerPC 32-bit
+ * POWER_64        - PowerPC 64-bit
+ * SPARC           - Sparc
+ * SPARC_V8        - Sparc V8
+ * SPARC_V9        - Sparc V9
+ * HPPA            - HPPA-RISC
+ * HPPA_32         - HPPA-RISC 32-bit
+ * HPPA_64         - HPPA-RISC 64-bit
+ */
+
+/**
+ * @def P_CPU_ALPHA
+ * @brief DEC Alpha architecture.
+ * @since 0.0.3
+ */
+
+/**
+ * @def P_CPU_ARM
+ * @brief ARM architecture.
+ * @since 0.0.3
+ *
+ * This macro is defined for any ARM target. It contains an architecture
+ * revision number. One of the revision specific macros (P_CPU_ARM_Vx) is also
+ * defined, as well as #P_CPU_ARM_32 or #P_CPU_ARM_64.
+ */
+
+/**
+ * @def P_CPU_ARM_32
+ * @brief ARM 32-bit architecture.
+ * @since 0.0.3
+ *
+ * This macro is defined for ARM 32-bit target. One of the revision specific
+ * macros (P_CPU_ARM_Vx) is also defined, as well as #P_CPU_ARM.
+ */
+
+/**
+ * @def P_CPU_ARM_64
+ * @brief ARM 64-bit architecture.
+ * @since 0.0.3
+ *
+ * This macro is defined for ARM 64-bit target. One of the revision specific
+ * macros (P_CPU_ARM_Vx) is also defined, as well as #P_CPU_ARM.
+ */
+
+/**
+ * @def P_CPU_ARM_V2
+ * @brief ARMv2 architecture revision.
+ * @since 0.0.3
+ *
+ * This macro is defined for ARMv2 target. #P_CPU_ARM_32 and #P_CPU_ARM macros
+ * are also defined.
+ */
+
+/**
+ * @def P_CPU_ARM_V3
+ * @brief ARMv3 architecture revision.
+ * @since 0.0.3
+ *
+ * This macro is defined for ARMv3 target. #P_CPU_ARM_32 and #P_CPU_ARM macros
+ * are also defined.
+ */
+
+/**
+ * @def P_CPU_ARM_V4
+ * @brief ARMv4 architecture revision.
+ * @since 0.0.3
+ *
+ * This macro is defined for ARMv4 target. #P_CPU_ARM_32 and #P_CPU_ARM macros
+ * are also defined.
+ */
+
+/**
+ * @def P_CPU_ARM_V5
+ * @brief ARMv5 architecture revision.
+ * @since 0.0.3
+ *
+ * This macro is defined for ARMv5 target. #P_CPU_ARM_32 and #P_CPU_ARM macros
+ * are also defined.
+ */
+
+/**
+ * @def P_CPU_ARM_V6
+ * @brief ARMv6 architecture revision.
+ * @since 0.0.3
+ *
+ * This macro is defined for ARMv6 target. #P_CPU_ARM_32 and #P_CPU_ARM macros
+ * are also defined.
+ */
+
+/**
+ * @def P_CPU_ARM_V7
+ * @brief ARMv7 architecture revision.
+ * @since 0.0.3
+ *
+ * This macro is defined for ARMv7 target. #P_CPU_ARM_32 and #P_CPU_ARM macros
+ * are also defined.
+ */
+
+/**
+ * @def P_CPU_ARM_V8
+ * @brief ARMv8 architecture revision.
+ * @since 0.0.3
+ *
+ * This macro is defined for ARMv8 target. #P_CPU_ARM_32 or #P_CPU_ARM_64 macro
+ * is defined, as well as #P_CPU_ARM.
+ */
+
+/**
+ * @def P_CPU_X86
+ * @brief Intel x86 architecture.
+ * @since 0.0.3
+ *
+ * This macro is defined for any x86 target. It contains an architecture
+ * revision number (3 for i386 and lower, 4 for i486, 5 for i586, 6 for i686 and
+ * better). One of the architecture specific macros (P_CPU_X86_xx) is also
+ * defined.
+ */
+
+/**
+ * @def P_CPU_X86_32
+ * @brief Intel x86 32-bit architecture.
+ * @since 0.0.3
+ *
+ * This macro is defined for x86 32-bit target. #P_CPU_X86 macro is also
+ * defined.
+ */
+
+/**
+ * @def P_CPU_X86_64
+ * @brief Intel x86 64-bit architecture.
+ * @since 0.0.3
+ *
+ * This macro is defined for x86 64-bit target. #P_CPU_X86 macro is also
+ * defined.
+ */
+
+/**
+ * @def P_CPU_IA64
+ * @brief Intel Itanium (IA-64) architecture.
+ * @since 0.0.3
+ *
+ * This macro is defined for Intel Itanium (IA-64) target.
+ */
+
+/**
+ * @def P_CPU_MIPS
+ * @brief MIPS architecture.
+ * @since 0.0.3
+ *
+ * This macro is defined for any MIPS target. Some other specific macros
+ * (P_CPU_MIPS_xx) for different MIPS ISAs may be defined.
+ */
+
+/**
+ * @def P_CPU_MIPS_I
+ * @brief MIPS I ISA.
+ * @since 0.0.3
+ *
+ * This macro is defined for MIPS I target. #P_CPU_MIPS is also defined, as well
+ * as probably some other ISA macros (P_CPU_MIPS_xx).
+ */
+
+/**
+ * @def P_CPU_MIPS_II
+ * @brief MIPS II ISA.
+ * @since 0.0.3
+ *
+ * This macro is defined for MIPS II target. #P_CPU_MIPS and #P_CPU_MIPS_I are
+ * also defined, as well as probably some other ISA macros (P_CPU_MIPS_xx).
+ */
+
+/**
+ * @def P_CPU_MIPS_III
+ * @brief MIPS III ISA.
+ * @since 0.0.3
+ *
+ * This macro is defined for MIPS III target. #P_CPU_MIPS, #P_CPU_MIPS_I and
+ * #P_CPU_MIPS_II are also defined, as well as probably some other ISA macros
+ * (P_CPU_MIPS_xx).
+ */
+
+/**
+ * @def P_CPU_MIPS_IV
+ * @brief MIPS IV ISA.
+ * @since 0.0.3
+ *
+ * This macro is defined for MIPS IV target. #P_CPU_MIPS, #P_CPU_MIPS_I,
+ * #P_CPU_MIPS_II and #P_CPU_MIPS_III are also defined, as well as probably some
+ * other ISA macros (P_CPU_MIPS_xx).
+ */
+
+/**
+ * @def P_CPU_MIPS_32
+ * @brief MIPS32 ISA.
+ * @since 0.0.3
+ *
+ * This macro is defined for MIPS32 target. #P_CPU_MIPS, #P_CPU_MIPS_I and
+ * #P_CPU_MIPS_II.
+ */
+
+/**
+ * @def P_CPU_MIPS_64
+ * @brief MIPS64 ISA.
+ * @since 0.0.3
+ *
+ * This macro is defined for MIPS64 target. #P_CPU_MIPS, #P_CPU_MIPS_I,
+ * #P_CPU_MIPS_II, #P_CPU_MIPS_III, #P_CPU_MIPS_IV and are also defined.
+ */
+
+/**
+ * @def P_CPU_POWER
+ * @brief PowerPC architecture.
+ * @since 0.0.3
+ *
+ * This macro is defined for any PowerPC target. One of the architecture
+ * specific macros (P_CPU_POWER_xx) is also defined.
+ */
+
+/**
+ * @def P_CPU_POWER_32
+ * @brief PowerPC 32-bit architecture.
+ * @since 0.0.3
+ *
+ * This macro is defined for PowerPC 32-bit target. #P_CPU_POWER macro is also
+ * defined.
+ */
+
+/**
+ * @def P_CPU_POWER_64
+ * @brief PowerPC 64-bit architecture.
+ * @since 0.0.3
+ *
+ * This macro is defined for PowerPC 64-bit target. #P_CPU_POWER macro is also
+ * defined.
+ */
+
+/**
+ * @def P_CPU_SPARC
+ * @brief Sun SPARC architecture.
+ * @since 0.0.3
+ *
+ * This macro is defined for any SPARC target. One of the architecture
+ * specific macros (P_CPU_SPARC_xx) is also may be defined.
+ */
+
+/**
+ * @def P_CPU_SPARC_V8
+ * @brief Sun SPARC V8 architecture.
+ * @since 0.0.3
+ *
+ * This macro is defined for SPARC V8 target. #P_CPU_SPARC macro is also
+ * defined.
+ */
+
+/**
+ * @def P_CPU_SPARC_V9
+ * @brief Sun SPARC V9 architecture.
+ * @since 0.0.3
+ *
+ * This macro is defined for SPARC V9 target. #P_CPU_SPARC macro is also
+ * defined.
+ */
+
+/**
+ * @def P_CPU_HPPA
+ * @brief HP PA-RISC architecture.
+ * @since 0.0.3
+ *
+ * This macro is defined for any PA-RISC target. One of the architecture
+ * specific macros (P_CPU_HPPA_xx) is also defined.
+ */
+
+/**
+ * @def P_CPU_HPPA_32
+ * @brief HP PA-RISC 32-bit (1.0, 1.1) architecture.
+ * @since 0.0.3
+ *
+ * This macro is defined for PA-RISC 32-bit target. #P_CPU_HPPA macro is also
+ * defined.
+ */
+
+/**
+ * @def P_CPU_HPPA_64
+ * @brief HP PA-RISC 64-bit (2.0) architecture.
+ * @since 0.0.3
+ *
+ * This macro is defined for PA-RISC 64-bit target. #P_CPU_HPPA macro is also
+ * defined.
+ */
+
+#if defined(__alpha__) || defined(__alpha) || defined(_M_ALPHA)
+#  define P_CPU_ALPHA
+#elif defined(__arm__) || defined(__TARGET_ARCH_ARM) || defined(_ARM) || \
+      defined(_M_ARM)  || defined(_M_ARM_64) || defined(__arm) || defined(__aarch64__) || \
+      defined(__ARM64__)
+#  if defined(__aarch64__) || defined(_M_ARM64) || defined(__ARM64__)
+#    define P_CPU_ARM_64
+#  else
+#    define P_CPU_ARM_32
+#  endif
+#  if defined(__ARM_ARCH) && __ARM_ARCH > 1
+#    define P_CPU_ARM __ARM_ARCH
+#  elif defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM > 1
+#    define P_CPU_ARM __TARGET_ARCH_ARM
+#  elif defined(_M_ARM) && _M_ARM > 1
+#    define P_CPU_ARM _M_ARM
+#  elif defined(__ARM64_ARCH_8__)  || \
+	defined(__ARM_ARCH_8__)    || \
+	defined(__ARM_ARCH_8A__)   || \
+	defined(__aarch64__)       || \
+	defined(__ARMv8__)         || \
+	defined(__ARMv8_A__)       || \
+	defined(_M_ARM_64)         || \\
+	defined(__CORE_CORTEXAV8__)
+#    define P_CPU_ARM 8
+#  elif defined(__ARM_ARCH_7__)    || \
+	defined(__ARM_ARCH_7A__)   || \
+	defined(__ARM_ARCH_7R__)   || \
+	defined(__ARM_ARCH_7M__)   || \
+	defined(__ARM_ARCH_7S__)   || \
+	defined(_ARM_ARCH_7)       || \
+	defined(__CORE_CORTEXA__)
+#    define P_CPU_ARM 7
+#  elif defined(__ARM_ARCH_6__)    || \
+	defined(__ARM_ARCH_6J__)   || \
+	defined(__ARM_ARCH_6T2__)  || \
+	defined(__ARM_ARCH_6Z__)   || \
+	defined(__ARM_ARCH_6K__)   || \
+	defined(__ARM_ARCH_6ZK__)  || \
+	defined(__ARM_ARCH_6M__)
+#    define P_CPU_ARM 6
+#  elif defined(__ARM_ARCH_5__)    || \
+	defined(__ARM_ARCH_5E__)   || \
+	defined(__ARM_ARCH_5T__)   || \
+	defined(__ARM_ARCH_5TE__)  || \
+	defined(__ARM_ARCH_5TEJ__)
+#    define P_CPU_ARM 5
+#  elif defined(__ARM_ARCH_4__)    || \
+	defined(__ARM_ARCH_4T__)
+#    define P_CPU_ARM 4
+#  elif defined(__ARM_ARCH_3__)    || \
+	defined(__ARM_ARCH_3M__)
+#    define P_CPU_ARM 3
+#  elif defined(__ARM_ARCH_2__)
+#    define P_CPU_ARM 2
+#  else
+#    define P_CPU_ARM 0
+#  endif
+#  if P_CPU_ARM == 8
+#    define P_CPU_ARM_V8
+#  elif P_CPU_ARM == 7
+#    define P_CPU_ARM_V7
+#  elif P_CPU_ARM == 6
+#    define P_CPU_ARM_V6
+#  elif P_CPU_ARM == 5
+#    define P_CPU_ARM_V5
+#  elif P_CPU_ARM == 4
+#    define P_CPU_ARM_V4
+#  elif P_CPU_ARM == 3
+#    define P_CPU_ARM_V3
+#  elif P_CPU_ARM == 2
+#    define P_CPU_ARM_V2
+#  else
+#    error "ARM architecture is uknown or too old"
+#  endif
+#elif defined(__i386__) || defined(__i386) || defined(_M_IX86)
+#  define P_CPU_X86_32
+#  if defined(_M_IX86)
+#    if (_M_IX86 >= 300 &&_M_IX86 <= 600)
+#      define P_CPU_X86 (_M_IX86 / 100)
+#    else
+#      define P_CPU_X86 6
+#    endif
+#  elif defined(__i686__) || defined(__athlon__) || defined(__SSE__) || defined(__pentiumpro__)
+#    define P_CPU_X86 6
+#  elif defined(__i586__) || defined(__k6__) || defined(__pentium__)
+#    define P_CPU_X86 5
+#  elif defined(__i486__) || defined(__80486__)
+#    define P_CPU_X86 4
+#  else
+#    define P_CPU_X86 3
+#  endif
+#elif defined(__x86_64__) || defined(__x86_64) || \
+      defined(__amd64__)  || defined(__amd64)  || \
+      defined(_M_X64)     || defined(_M_AMD64)
+#  define P_CPU_X86_64
+#  define P_CPU_X86 6
+#elif defined(__ia64__) || defined(__ia64) || defined(_M_IA64)
+#  define P_CPU_IA64
+#elif defined(__mips__) || defined(__mips) || defined(_M_MRX000)
+#  define P_CPU_MIPS
+#  if defined(_M_MRX000)
+#    if (_M_MRX000 >= 10000)
+#      define P_CPU_MIPS_IV
+#    else
+#      define P_CPU_MIPS_III
+#    endif
+#  endif
+#  if defined(_MIPS_ARCH_MIPS64) || (defined(__mips) && __mips - 0 >= 64) || \
+     (defined(_MIPS_ISA) && defined(_MIPS_ISA_MIPS64) && __MIPS_ISA - 0 >= _MIPS_ISA_MIPS64)
+#    define P_CPU_MIPS_64
+#  elif defined(_MIPS_ARCH_MIPS32) || (defined(__mips) && __mips - 0 >= 32) || \
+       (defined(_MIPS_ISA) && defined(_MIPS_ISA_MIPS32) && __MIPS_ISA - 0 >= _MIPS_ISA_MIPS32)
+#    define P_CPU_MIPS_32
+#  elif defined(_MIPS_ARCH_MIPS4) || (defined(__mips) && __mips - 0 >= 4) || \
+       (defined(_MIPS_ISA) && defined(_MIPS_ISA_MIPS4) && __MIPS_ISA - 0 >= _MIPS_ISA_MIPS4)
+#    define P_CPU_MIPS_IV
+#  elif defined(_MIPS_ARCH_MIPS3) || (defined(__mips) && __mips - 0 >= 3) || \
+       (defined(_MIPS_ISA)&& defined(_MIPS_ISA_MIPS3) && __MIPS_ISA - 0 >= _MIPS_ISA_MIPS3)
+#    define P_CPU_MIPS_III
+#  elif defined(_MIPS_ARCH_MIPS2) || (defined(__mips) && __mips - 0 >= 2) || \
+       (defined(_MIPS_ISA) && defined(_MIPS_ISA_MIPS2) && __MIPS_ISA - 0 >= _MIPS_ISA_MIPS2)
+#    define P_CPU_MIPS_II
+#  elif defined(_MIPS_ARCH_MIPS1) || (defined(__mips) && __mips - 0 >= 1) || \
+     (defined(_MIPS_ISA) && defined(_MIPS_ISA_MIPS1) && __MIPS_ISA - 0 >= _MIPS_ISA_MIPS1)
+#    define P_CPU_MIPS_I
+#  endif
+#  if defined(P_CPU_MIPS_64)
+#    define P_CPU_MIPS_IV
+#  endif
+#  if defined(P_CPU_MIPS_IV)
+#    define P_CPU_MIPS_III
+#  endif
+#  if defined(P_CPU_MIPS_32) || defined(P_CPU_MIPS_III)
+#    define P_CPU_MIPS_II
+#  endif
+#  if defined(P_CPU_MIPS_II)
+#    define P_CPU_MIPS_I
+#  endif
+#elif defined(__powerpc__) || defined(__powerpc) || defined(__ppc__)   || defined(__ppc) || \
+      defined(_ARCH_PPC)   || defined(_ARCH_PWR) || defined(_ARCH_COM) || \
+      defined(_M_PPC)      || defined(_M_MPPC)
+#  define P_CPU_POWER
+#  if defined(__powerpc64__) || defined(__powerpc64) || defined(__ppc64__) || defined(__ppc64) || \
+      defined(__64BIT__)     || defined(__LP64__)    || defined(_LP64)
+#    define P_CPU_POWER_64
+#  else
+#    define P_CPU_POWER_32
+#  endif
+#elif defined(__sparc__) || defined(__sparc)
+#  define P_CPU_SPARC
+#  if defined(__sparc_v9__) || defined(__sparcv9)
+#    define P_CPU_SPARC_V9
+#  elif defined(__sparc_v8__) || defined(__sparcv8)
+#    define P_CPU_SPARC_V8
+#  endif
+#elif defined(__hppa__) || defined(__hppa)
+#  define P_CPU_HPPA
+#  if defined(_PA_RISC2_0) || defined(__RISC2_0__) || defined(__HPPA20__) || defined(__PA8000__)
+#    define P_CPU_HPPA_64
+#  else
+#    define P_CPU_HPPA_32
+#  endif
+#endif
+
+/* We need this to generate full Doxygen documentation */
+
+#ifdef DOXYGEN
+#  ifndef P_CPU_ALPHA
+#    define P_CPU_ALPHA
+#  endif
+#  ifndef P_CPU_ARM
+#    define P_CPU_ARM
+#  endif
+#  ifndef P_CPU_ARM_32
+#    define P_CPU_ARM_32
+#  endif
+#  ifndef P_CPU_ARM_64
+#    define P_CPU_ARM_64
+#  endif
+#  ifndef P_CPU_ARM_V2
+#    define P_CPU_ARM_V2
+#  endif
+#  ifndef P_CPU_ARM_V3
+#    define P_CPU_ARM_V3
+#  endif
+#  ifndef P_CPU_ARM_V4
+#    define P_CPU_ARM_V4
+#  endif
+#  ifndef P_CPU_ARM_V5
+#    define P_CPU_ARM_V5
+#  endif
+#  ifndef P_CPU_ARM_V6
+#    define P_CPU_ARM_V6
+#  endif
+#  ifndef P_CPU_ARM_V7
+#    define P_CPU_ARM_V7
+#  endif
+#  ifndef P_CPU_ARM_V8
+#    define P_CPU_ARM_V8
+#  endif
+#  ifndef P_CPU_X86
+#    define P_CPU_X86
+#  endif
+#  ifndef P_CPU_X86_32
+#    define P_CPU_X86_32
+#  endif
+#  ifndef P_CPU_X86_64
+#    define P_CPU_X86_64
+#  endif
+#  ifndef P_CPU_IA64
+#    define P_CPU_IA64
+#  endif
+#  ifndef P_CPU_MIPS
+#    define P_CPU_MIPS
+#  endif
+#  ifndef P_CPU_MIPS_I
+#    define P_CPU_MIPS_I
+#  endif
+#  ifndef P_CPU_MIPS_II
+#    define P_CPU_MIPS_II
+#  endif
+#  ifndef P_CPU_MIPS_III
+#    define P_CPU_MIPS_III
+#  endif
+#  ifndef P_CPU_MIPS_IV
+#    define P_CPU_MIPS_IV
+#  endif
+#  ifndef P_CPU_MIPS_32
+#    define P_CPU_MIPS_32
+#  endif
+#  ifndef P_CPU_MIPS_64
+#    define P_CPU_MIPS_64
+#  endif
+#  ifndef P_CPU_POWER
+#    define P_CPU_POWER
+#  endif
+#  ifndef P_CPU_POWER_32
+#    define P_CPU_POWER_32
+#  endif
+#  ifndef P_CPU_POWER_64
+#    define P_CPU_POWER_64
+#  endif
+#  ifndef P_CPU_SPARC
+#    define P_CPU_SPARC
+#  endif
+#  ifndef P_CPU_SPARC_V8
+#    define P_CPU_SPARC_V8
+#  endif
+#  ifndef P_CPU_SPARC_V9
+#    define P_CPU_SPARC_V9
+#  endif
+#  ifndef P_CPU_HPPA
+#    define P_CPU_HPPA
+#  endif
+#  ifndef P_CPU_HPPA_32
+#    define P_CPU_HPPA_32
+#  endif
+#  ifndef P_CPU_HPPA_64
+#    define P_CPU_HPPA_64
+#  endif
+#endif
+
+#endif /* PLIBSYS_HEADER_PMACROSCPU_H */
diff --git a/3rdparty/plibsys/src/pmacrosos.h b/3rdparty/plibsys/src/pmacrosos.h
new file mode 100644
index 0000000..c14a27a
--- /dev/null
+++ b/3rdparty/plibsys/src/pmacrosos.h
@@ -0,0 +1,500 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2017-2018 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file pmacrosos.h
+ * @brief OS detection macros
+ * @author Alexander Saprykin
+ *
+ * All the macros are completely independent of any other platform-specific
+ * headers, thus gurantee to work with any compiler under any operating system
+ * in the same way as they are used within the library.
+ *
+ * This family of macros provides OS detection and defines one or several of
+ * P_OS_x macros.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PMACROSOS_H
+#define PLIBSYS_HEADER_PMACROSOS_H
+
+/*
+ * List of supported operating systems (P_OS_x):
+ *
+ * DARWIN            - Any Darwin based system
+ * DARWIN32          - Any 32-bit Darwin based system
+ * DARWIN64          - Any 64-bit Darwin based system
+ * BSD4              - Any BSD 4.x based system
+ * FREEBSD           - FreeBSD
+ * DRAGONFLY         - DragonFlyBSD
+ * NETBSD            - NetBSD
+ * OPENBSD           - OpenBSD
+ * AIX               - IBM AIX
+ * HPUX              - HP-UX
+ * TRU64             - Tru64
+ * SOLARIS           - Sun (Oracle) Solaris
+ * QNX               - QNX 4.x
+ * QNX6              - QNX Neutrino 6.x
+ * BB10              - BlackBerry 10
+ * SCO               - SCO OpenServer 5/6
+ * UNIXWARE          - UnixWare 7
+ * IRIX              - SGI IRIX
+ * HAIKU             - Haiku
+ * SYLLABLE          - Syllable
+ * BEOS              - BeOS
+ * OS2               - OS/2
+ * VMS               - OpenVMS
+ * AMIGA             - AmigaOS
+ * UNIX              - Any UNIX BSD/SYSV based system
+ * LINUX             - Linux
+ * MAC9              - Mac OS 9 (Classic)
+ * MAC               - Any macOS
+ * MAC32             - 32-bit macOS
+ * MAC64             - 64-bit macOS
+ * CYGWIN            - Cygwin
+ * MSYS              - MSYS
+ * WIN               - 32-bit Windows
+ * WIN64             - 64-bit Windows
+ * ANDROID           - Android
+ */
+
+/**
+ * @def P_OS_DARWIN
+ * @brief Darwin based operating system (i.e. macOS).
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_DARWIN32
+ * @brief Darwin based 32-bit operating system.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_DARWIN64
+ * @brief Darwin based 64-bit operating system.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_BSD4
+ * @brief BSD 4.x based operating system.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_FREEBSD
+ * @brief FreeBSD operating system.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_DRAGONFLY
+ * @brief DragonFlyBSD operating system.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_NETBSD
+ * @brief NetBSD operating system.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_OPENBSD
+ * @brief OpenBSD operating system.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_AIX
+ * @brief IBM AIX operating system.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_HPUX
+ * @brief HP-UX operating system.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_TRU64
+ * @brief Tru64 operating system.
+ * @since 0.0.2
+ */
+
+/**
+ * @def P_OS_SOLARIS
+ * @brief Sun (Oracle) Solaris operating system.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_QNX
+ * @brief QNX 4.x operating system.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_QNX6
+ * @brief QNX Neutrino 6.x operating system.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_BB10
+ * @brief BlackBerry 10 operating system.
+ * @since 0.0.4
+ */
+
+/**
+ * @def P_OS_SCO
+ * @brief SCO OpenServer operating system.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_UNIXWARE
+ * @brief UnixWare 7 operating system.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_IRIX
+ * @brief SGI's IRIX operating system.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_HAIKU
+ * @brief Haiku operating system.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_SYLLABLE
+ * @brief Syllable operating system.
+ * @since 0.0.2
+ */
+
+/**
+ * @def P_OS_BEOS
+ * @brief BeOS operating system.
+ * @since 0.0.3
+ */
+
+/**
+ * @def P_OS_OS2
+ * @brief OS/2 operating system.
+ * @since 0.0.3
+ */
+
+/**
+ * @def P_OS_VMS
+ * @brief OpenVMS operating system.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_AMIGA
+ * @brief AmigaOS operating system.
+ * @since 0.0.4
+ */
+
+/**
+ * @def P_OS_UNIX
+ * @brief UNIX based operating system.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_LINUX
+ * @brief Linux based operating system.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_MAC9
+ * @brief Apple's Mac OS 9 operating system.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_MAC
+ * @brief Apple's macOS operating system.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_MAC32
+ * @brief Apple's macOS 32-bit operating system.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_MAC64
+ * @brief Apple's macOS 64-bit operating system.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_CYGWIN
+ * @brief Microsoft Windows POSIX runtime environment.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_MSYS
+ * @brief Microsoft Windows POSIX development environment.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_WIN
+ * @brief Microsoft Windows 32-bit operating system.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_WIN64
+ * @brief Microsoft Windows 64-bit operating system.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_OS_ANDROID
+ * @brief Google Android
+ * @since 0.0.4
+ */
+
+#if defined(__APPLE__) && (defined(__GNUC__) || defined(__xlC__) || defined(__xlc__))
+#  define P_OS_DARWIN
+#  define P_OS_BSD4
+#  ifdef __LP64__
+#    define P_OS_DARWIN64
+#  else
+#    define P_OS_DARWIN32
+#  endif
+# elif defined(Macintosh) || defined(macintosh)
+#  define P_OS_MAC9
+#elif defined(__MSYS__)
+#  define P_OS_MSYS
+#elif defined(__CYGWIN__)
+#  define P_OS_CYGWIN
+#elif defined(_WIN64) || defined(_M_X64) || defined(_M_AMD64)
+#  define P_OS_WIN64
+#elif defined(__WIN32__) || defined(_WIN32) || defined(WIN32)
+#  define P_OS_WIN
+#elif defined(__ANDROID__)
+#  define P_OS_ANDROID
+#  define P_OS_LINUX
+#elif defined(__linux) || defined(__linux__)
+#  define P_OS_LINUX
+#elif defined(__FreeBSD__)
+#  define P_OS_FREEBSD
+#  define P_OS_BSD4
+#elif defined(__DragonFly__)
+#  define P_OS_DRAGONFLY
+#  define P_OS_BSD4
+#elif defined(__NetBSD__)
+#  define P_OS_NETBSD
+#  define P_OS_BSD4
+#elif defined(__OpenBSD__)
+#  define P_OS_OPENBSD
+#  define P_OS_BSD4
+#elif defined(_AIX)
+#  define P_OS_AIX
+#elif defined(hpux) || defined(__hpux)
+#  define P_OS_HPUX
+#elif defined(__osf__) || defined(__osf)
+#  define P_OS_TRU64
+#elif defined(__sun) || defined(sun)
+#  define P_OS_SOLARIS
+#elif defined(__QNXNTO__)
+#  ifdef __BLACKBERRY10__
+#    define P_OS_BB10
+#  else
+#    define P_OS_QNX6
+#  endif
+#elif defined(__QNX__)
+#  define P_OS_QNX
+#elif defined(_SCO_DS)
+#  define P_OS_SCO
+#elif defined(__USLC__) || defined(__UNIXWARE__)
+#  define P_OS_UNIXWARE
+#elif defined(__svr4__) && defined(i386)
+#  define P_OS_UNIXWARE
+#elif defined(__sgi) || defined(sgi)
+#  define P_OS_IRIX
+#elif defined(__HAIKU__)
+#  define P_OS_HAIKU
+#elif defined(__SYLLABLE__)
+#  define P_OS_SYLLABLE
+#elif defined(__BEOS__)
+#  define P_OS_BEOS
+#elif defined(__OS2__)
+#  define P_OS_OS2
+#elif defined(VMS) || defined(__VMS)
+#  define P_OS_VMS
+#elif defined(AMIGA) || defined(__amigaos__)
+#  define P_OS_AMIGA
+#endif
+
+#ifdef P_OS_WIN64
+#  define P_OS_WIN
+#endif
+
+#if defined(P_OS_DARWIN)
+#  define P_OS_MAC
+#  if defined(P_OS_DARWIN64)
+#     define P_OS_MAC64
+#  elif defined(P_OS_DARWIN32)
+#     define P_OS_MAC32
+#  endif
+#endif
+
+#if defined(P_OS_WIN)  || defined(P_OS_MAC9) || defined(P_OS_HAIKU) || \
+    defined(P_OS_BEOS) || defined(P_OS_OS2)  || defined(P_OS_VMS)   || \
+    defined(P_OS_AMIGA)
+#  undef P_OS_UNIX
+#elif !defined(P_OS_UNIX)
+#  define P_OS_UNIX
+#endif
+
+/* We need this to generate full Doxygen documentation */
+
+#ifdef DOXYGEN
+#  ifndef P_OS_DARWIN
+#    define P_OS_DARWIN
+#  endif
+#  ifndef P_OS_DARWIN32
+#    define P_OS_DARWIN32
+#  endif
+#  ifndef P_OS_DARWIN64
+#    define P_OS_DARWIN64
+#  endif
+#  ifndef P_OS_BSD4
+#    define P_OS_BSD4
+#  endif
+#  ifndef P_OS_FREEBSD
+#    define P_OS_FREEBSD
+#  endif
+#  ifndef P_OS_DRAGONFLY
+#    define P_OS_DRAGONFLY
+#  endif
+#  ifndef P_OS_NETBSD
+#    define P_OS_NETBSD
+#  endif
+#  ifndef P_OS_OPENBSD
+#    define P_OS_OPENBSD
+#  endif
+#  ifndef P_OS_AIX
+#    define P_OS_AIX
+#  endif
+#  ifndef P_OS_HPUX
+#    define P_OS_HPUX
+#  endif
+#  ifndef P_OS_TRU64
+#    define P_OS_TRU64
+#  endif
+#  ifndef P_OS_SOLARIS
+#    define P_OS_SOLARIS
+#  endif
+#  ifndef P_OS_QNX
+#    define P_OS_QNX
+#  endif
+#  ifndef P_OS_QNX6
+#    define P_OS_QNX6
+#  endif
+#  ifndef P_OS_BB10
+#    define P_OS_BB10
+#  endif
+#  ifndef P_OS_SCO
+#    define P_OS_SCO
+#  endif
+#  ifndef P_OS_UNIXWARE
+#    define P_OS_UNIXWARE
+#  endif
+#  ifndef P_OS_IRIX
+#    define P_OS_IRIX
+#  endif
+#  ifndef P_OS_HAIKU
+#    define P_OS_HAIKU
+#  endif
+#  ifndef P_OS_SYLLABLE
+#    define P_OS_SYLLABLE
+#  endif
+#  ifndef P_OS_BEOS
+#    define P_OS_BEOS
+#  endif
+#  ifndef P_OS_OS2
+#    define P_OS_OS2
+#  endif
+#  ifndef P_OS_VMS
+#    define P_OS_VMS
+#  endif
+#  ifndef P_OS_AMIGA
+#    define P_OS_AMIGA
+#  endif
+#  ifndef P_OS_UNIX
+#    define P_OS_UNIX
+#  endif
+#  ifndef P_OS_LINUX
+#    define P_OS_LINUX
+#  endif
+#  ifndef P_OS_MAC9
+#    define P_OS_MAC9
+#  endif
+#  ifndef P_OS_MAC
+#    define P_OS_MAC
+#  endif
+#  ifndef P_OS_MAC32
+#    define P_OS_MAC32
+#  endif
+#  ifndef P_OS_MAC64
+#    define P_OS_MAC64
+#  endif
+#  ifndef P_OS_CYGWIN
+#    define P_OS_CYGWIN
+#  endif
+#  ifndef P_OS_MSYS
+#    define P_OS_MSYS
+#  endif
+#  ifndef P_OS_WIN
+#    define P_OS_WIN
+#  endif
+#  ifndef P_OS_WIN64
+#    define P_OS_WIN64
+#  endif
+#  ifndef P_OS_ANDROID
+#    define P_OS_ANDROID
+#  endif
+#endif
+
+#endif /* PLIBSYS_HEADER_PMACROSOS_H */
diff --git a/3rdparty/plibsys/src/pmain.c b/3rdparty/plibsys/src/pmain.c
new file mode 100644
index 0000000..ad40dfb
--- /dev/null
+++ b/3rdparty/plibsys/src/pmain.c
@@ -0,0 +1,132 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "pmain.h"
+
+extern void p_mem_init			(void);
+extern void p_mem_shutdown		(void);
+extern void p_atomic_thread_init	(void);
+extern void p_atomic_thread_shutdown	(void);
+extern void p_socket_init_once		(void);
+extern void p_socket_close_once		(void);
+extern void p_uthread_init		(void);
+extern void p_uthread_shutdown		(void);
+extern void p_cond_variable_init	(void);
+extern void p_cond_variable_shutdown	(void);
+extern void p_rwlock_init		(void);
+extern void p_rwlock_shutdown		(void);
+extern void p_time_profiler_init	(void);
+extern void p_time_profiler_shutdown	(void);
+extern void p_library_loader_init	(void);
+extern void p_library_loader_shutdown	(void);
+
+static pboolean pp_plibsys_inited = FALSE;
+static pchar pp_plibsys_version[] = PLIBSYS_VERSION_STR;
+
+P_LIB_API void
+p_libsys_init (void)
+{
+	if (P_UNLIKELY (pp_plibsys_inited == TRUE))
+		return;
+
+	pp_plibsys_inited = TRUE;
+
+	p_mem_init ();
+	p_atomic_thread_init ();
+	p_socket_init_once ();
+	p_uthread_init ();
+	p_cond_variable_init ();
+	p_rwlock_init ();
+	p_time_profiler_init ();
+	p_library_loader_init ();
+}
+
+P_LIB_API void
+p_libsys_init_full (const PMemVTable *vtable)
+{
+	if (p_mem_set_vtable (vtable) == FALSE)
+		P_ERROR ("MAIN::p_libsys_init_full: failed to initialize memory table");
+
+	p_libsys_init ();
+}
+
+P_LIB_API void
+p_libsys_shutdown (void)
+{
+	if (P_UNLIKELY (pp_plibsys_inited == FALSE))
+		return;
+
+	pp_plibsys_inited = FALSE;
+
+	p_library_loader_init ();
+	p_time_profiler_shutdown ();
+	p_rwlock_shutdown ();
+	p_cond_variable_shutdown ();
+	p_uthread_shutdown ();
+	p_socket_close_once ();
+	p_atomic_thread_shutdown ();
+	p_mem_shutdown ();
+}
+
+P_LIB_API const pchar *
+p_libsys_version (void)
+{
+	return (const pchar *) pp_plibsys_version;
+}
+
+#ifdef P_OS_WIN
+extern void p_uthread_win32_thread_detach (void);
+
+BOOL WINAPI DllMain (HINSTANCE	hinstDLL,
+		     DWORD	fdwReason,
+		     LPVOID	lpvReserved);
+
+BOOL WINAPI
+DllMain (HINSTANCE	hinstDLL,
+	 DWORD		fdwReason,
+	 LPVOID		lpvReserved)
+{
+	P_UNUSED (hinstDLL);
+	P_UNUSED (lpvReserved);
+
+	switch (fdwReason) {
+	case DLL_PROCESS_ATTACH:
+		break;
+
+	case DLL_THREAD_DETACH:
+		p_uthread_win32_thread_detach ();
+		break;
+
+	case DLL_PROCESS_DETACH:
+		break;
+
+	default:
+		;
+	}
+
+	return TRUE;
+}
+#endif
diff --git a/3rdparty/plibsys/src/pmain.h b/3rdparty/plibsys/src/pmain.h
new file mode 100644
index 0000000..eff615d
--- /dev/null
+++ b/3rdparty/plibsys/src/pmain.h
@@ -0,0 +1,209 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file pmain.h
+ * @brief Library initialization
+ * @author Alexander Saprykin
+ *
+ * Before using the library you must to initialize it properly. Use
+ * p_libsys_init() to initialize the library. Please note that you need to call
+ * it only once, not in every thread. This call is not MT-safe (because it also
+ * initializes the threading subsystem itself), so it is best to place it in the
+ * program's main thread, when the program starts.
+ *
+ * The only difference between p_libsys_init() and p_libsys_init_full() is that
+ * the latter one allows to setup memory management routines before doing any
+ * internal library call. This way you can ensure to use provided memory
+ * management everywhere (even for library initialization).
+ *
+ * When you do not need the library anymore release used resourses with the
+ * p_libsys_shutdown() routine. You should only call it once, too. This call is
+ * not MT-safe (because it also deinitializes the threading subsystem itself),
+ * so it is best to place it in the program's main thread, when the program
+ * finishes.
+ *
+ * It is not recommended to call the initialization and deinitialization
+ * routines on Windows in the DllMain() call because it may require libraries
+ * other than kernel32.dll.
+ */
+
+/**
+ * @mainpage
+ * Basic
+ * - @link
+ * pmain.h Library initialization
+ * @endlink
+ * - @link
+ * ptypes.h Data types
+ * @endlink
+ * - @link
+ * pmacroscpu.h CPU detection macros
+ * @endlink
+ * - @link
+ * pmacrosos.h OS detection macros
+ * @endlink
+ * - @link
+ * pmacroscompiler.h Compiler detection macros
+ * @endlink
+ * - @link
+ * pmacros.h Miscellaneous macros
+ * @endlink
+ * - @link
+ * pstring.h Strings
+ * @endlink
+ *
+ * System
+ * - @link
+ * pmem.h Memory management
+ * @endlink
+ * - @link
+ * pprocess.h Process
+ * @endlink
+ * - @link
+ * plibraryloader.h Shared library loader
+ * @endlink
+ * - @link
+ * ptimeprofiler.h Time profiler
+ * @endlink
+ * - @link
+ * perror.h Errors
+ * @endlink
+ *
+ * Data structures
+ * - @link
+ * plist.h Singly linked list
+ * @endlink
+ * - @link
+ * phashtable.h Hash table
+ * @endlink
+ * - @link
+ * pcryptohash.h Cryptographic hash
+ * @endlink
+ * - @link
+ * ptree.h Binary search tree
+ * @endlink
+ *
+ * Multithreading
+ * - @link
+ * puthread.h Thread
+ * @endlink
+ * - @link
+ * pmutex.h Mutex
+ * @endlink
+ * - @link
+ * pcondvariable.h Condition variable
+ * @endlink
+ * - @link
+ * prwlock.h Read-write lock
+ * @endlink
+ * - @link
+ * pspinlock.h Spinlock
+ * @endlink
+ * - @link
+ * patomic.h Atomic operations
+ * @endlink
+ *
+ * Interprocess communication
+ * - @link
+ * psemaphore.h Semaphore
+ * @endlink
+ * - @link
+ * pshm.h Shared memory
+ * @endlink
+ * - @link
+ * pshmbuffer.h Shared memory buffer
+ * @endlink
+ *
+ * Networking
+ * - @link
+ * psocketaddress.h Socket address
+ * @endlink
+ * - @link
+ * psocket.h Socket
+ * @endlink
+ *
+ * File and directories
+ * - @link
+ * pfile.h Files
+ * @endlink
+ * - @link
+ * pdir.h Directories
+ * @endlink
+ * - @link
+ * pinifile.h INI file parser
+ * @endlink
+ *
+ * Stack
+ * - @link
+ * pstdarg.h Variable number of arguments
+ * @endlink
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PMAIN_H
+#define PLIBSYS_HEADER_PMAIN_H
+
+#include <pmacros.h>
+#include <pmem.h>
+
+P_BEGIN_DECLS
+
+/**
+ * @brief Initializes library resources.
+ * @since 0.0.1
+ */
+P_LIB_API void		p_libsys_init		(void);
+
+/**
+ * @brief Initializes library resources along with the memory table.
+ * @param vtable Memory management table.
+ * @since 0.0.1
+ */
+P_LIB_API void		p_libsys_init_full	(const PMemVTable *vtable);
+
+/**
+ * @brief Frees library resources. You should stop using any of the library
+ * routines after calling this one.
+ * @since 0.0.1
+ */
+P_LIB_API void		p_libsys_shutdown	(void);
+
+/**
+ * @brief Gets the library version against which it was compiled at run-time.
+ * @return Library version.
+ * @since 0.0.1
+ * @note This version may differ from the version the application was compiled
+ * against.
+ * @sa #PLIBSYS_VERSION, #PLIBSYS_VERSION_STR, #PLIBSYS_VERSION_CHECK
+ */
+P_LIB_API const pchar *	p_libsys_version	(void);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PMAIN_H */
diff --git a/3rdparty/plibsys/src/pmem.c b/3rdparty/plibsys/src/pmem.c
new file mode 100644
index 0000000..04701fa
--- /dev/null
+++ b/3rdparty/plibsys/src/pmem.c
@@ -0,0 +1,357 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 <string.h>
+#include <stdlib.h>
+
+#include "perror.h"
+#include "pmem.h"
+#include "perror-private.h"
+#include "psysclose-private.h"
+
+#ifndef P_OS_WIN
+#  if defined (P_OS_BEOS)
+#    include <be/kernel/OS.h>
+#  elif defined (P_OS_OS2)
+#    define INCL_DOSMEMMGR
+#    define INCL_DOSERRORS
+#    include <os2.h>
+#  elif !defined (P_OS_AMIGA)
+#    include <unistd.h>
+#    include <sys/types.h>
+#    include <sys/mman.h>
+#    include <sys/stat.h>
+#    include <fcntl.h>
+#  endif
+#endif
+
+static pboolean		p_mem_table_inited = FALSE;
+static PMemVTable	p_mem_table;
+
+void
+p_mem_init (void)
+{
+	if (P_UNLIKELY (p_mem_table_inited == TRUE))
+		return;
+
+	p_mem_restore_vtable ();
+}
+
+void
+p_mem_shutdown (void)
+{
+	if (P_UNLIKELY (!p_mem_table_inited))
+		return;
+
+	p_mem_table.malloc  = NULL;
+	p_mem_table.realloc = NULL;
+	p_mem_table.free    = NULL;
+
+	p_mem_table_inited = FALSE;
+}
+
+P_LIB_API ppointer
+p_malloc (psize n_bytes)
+{
+	if (P_LIKELY (n_bytes > 0))
+		return p_mem_table.malloc (n_bytes);
+	else
+		return NULL;
+}
+
+P_LIB_API ppointer
+p_malloc0 (psize n_bytes)
+{
+	ppointer ret;
+
+	if (P_LIKELY (n_bytes > 0)) {
+		if (P_UNLIKELY ((ret = p_mem_table.malloc (n_bytes)) == NULL))
+			return NULL;
+
+		memset (ret, 0, n_bytes);
+		return ret;
+	} else
+		return NULL;
+}
+
+P_LIB_API ppointer
+p_realloc (ppointer mem, psize n_bytes)
+{
+	if (P_UNLIKELY (n_bytes == 0))
+		return NULL;
+
+	if (P_UNLIKELY (mem == NULL))
+		return p_mem_table.malloc (n_bytes);
+	else
+		return p_mem_table.realloc (mem, n_bytes);
+}
+
+P_LIB_API void
+p_free (ppointer mem)
+{
+	if (P_LIKELY (mem != NULL))
+		p_mem_table.free (mem);
+}
+
+P_LIB_API pboolean
+p_mem_set_vtable (const PMemVTable *table)
+{
+	if (P_UNLIKELY (table == NULL))
+		return FALSE;
+
+	if (P_UNLIKELY (table->free == NULL || table->malloc == NULL || table->realloc == NULL))
+		return FALSE;
+
+	p_mem_table.malloc  = table->malloc;
+	p_mem_table.realloc = table->realloc;
+	p_mem_table.free    = table->free;
+
+	p_mem_table_inited = TRUE;
+
+	return TRUE;
+}
+
+P_LIB_API void
+p_mem_restore_vtable (void)
+{
+	p_mem_table.malloc  = (ppointer (*)(psize)) malloc;
+	p_mem_table.realloc = (ppointer (*)(ppointer, psize)) realloc;
+	p_mem_table.free    = (void (*)(ppointer)) free;
+
+	p_mem_table_inited = TRUE;
+}
+
+P_LIB_API ppointer
+p_mem_mmap (psize	n_bytes,
+	    PError	**error)
+{
+	ppointer	addr;
+#if defined (P_OS_WIN)
+	HANDLE		hdl;
+#elif defined (P_OS_BEOS)
+	area_id		area;
+#elif defined (P_OS_OS2)
+	APIRET		ulrc;
+#elif !defined (P_OS_AMIGA)
+	int		fd;
+	int		map_flags = MAP_PRIVATE;
+#endif
+
+	if (P_UNLIKELY (n_bytes == 0)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return NULL;
+	}
+
+#if defined (P_OS_WIN)
+	if (P_UNLIKELY ((hdl = CreateFileMappingA (INVALID_HANDLE_VALUE,
+						   NULL,
+						   PAGE_READWRITE,
+						   0,
+						   (DWORD) n_bytes,
+						   NULL)) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_io (),
+				     p_error_get_last_system (),
+				     "Failed to call CreateFileMapping() to create file mapping");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((addr = MapViewOfFile (hdl,
+					       FILE_MAP_READ | FILE_MAP_WRITE,
+					       0,
+					       0,
+					       n_bytes)) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_io (),
+				     p_error_get_last_system (),
+				     "Failed to call MapViewOfFile() to map file view");
+		CloseHandle (hdl);
+		return NULL;
+	}
+
+	if (P_UNLIKELY (!CloseHandle (hdl))) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_io (),
+				     p_error_get_last_system (),
+				     "Failed to call CloseHandle() to close file mapping");
+		UnmapViewOfFile (addr);
+		return NULL;
+	}
+#elif defined (P_OS_BEOS)
+	if (P_LIKELY ((n_bytes % B_PAGE_SIZE)) != 0)
+		n_bytes = (n_bytes / B_PAGE_SIZE + 1) * B_PAGE_SIZE;
+
+	area = create_area ("", &addr, B_ANY_ADDRESS, n_bytes, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
+
+	if (P_UNLIKELY (area < B_NO_ERROR)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_io (),
+				     p_error_get_last_system (),
+				     "Failed to call create_area() to create memory area");
+		return NULL;
+	}
+#elif defined (P_OS_OS2)
+	if (P_UNLIKELY ((ulrc = DosAllocMem ((PPVOID) &addr,
+					     (ULONG) n_bytes,
+					     PAG_READ | PAG_WRITE | PAG_COMMIT |
+					     OBJ_ANY)) != NO_ERROR)) {
+		/* Try to remove OBJ_ANY */
+		if (P_UNLIKELY ((ulrc = DosAllocMem ((PPVOID) &addr,
+						     (ULONG) n_bytes,
+						     PAG_READ | PAG_WRITE)) != NO_ERROR)) {
+			p_error_set_error_p (error,
+					     (pint) p_error_get_io_from_system ((pint) ulrc),
+					     ulrc,
+					     "Failed to call DosAllocMemory() to alocate memory");
+			return NULL;
+		}
+	}
+#elif defined (P_OS_AMIGA)
+	addr = malloc (n_bytes);
+
+	if (P_UNLIKELY (addr == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_io (),
+				     p_error_get_last_system (),
+				     "Failed to allocate system memory");
+		return NULL;
+	}
+#else
+#  if !defined (PLIBSYS_MMAP_HAS_MAP_ANONYMOUS) && !defined (PLIBSYS_MMAP_HAS_MAP_ANON)
+	if (P_UNLIKELY ((fd = open ("/dev/zero", O_RDWR | O_EXCL, 0754)) == -1)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_io (),
+				     p_error_get_last_system (),
+				     "Failed to open /dev/zero for file mapping");
+		return NULL;
+	}
+#  else
+	fd = -1;
+#  endif
+
+#  ifdef PLIBSYS_MMAP_HAS_MAP_ANONYMOUS
+	map_flags |= MAP_ANONYMOUS;
+#  elif defined (PLIBSYS_MMAP_HAS_MAP_ANON)
+	map_flags |= MAP_ANON;
+#  endif
+
+	if (P_UNLIKELY ((addr = mmap (NULL,
+				      n_bytes,
+				      PROT_READ | PROT_WRITE,
+				      map_flags,
+				      fd,
+				      0)) == (void *) -1)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_io (),
+				     p_error_get_last_system (),
+				     "Failed to call mmap() to create file mapping");
+#  if !defined (PLIBSYS_MMAP_HAS_MAP_ANONYMOUS) && !defined (PLIBSYS_MMAP_HAS_MAP_ANON)
+		if (P_UNLIKELY (p_sys_close (fd) != 0))
+			P_WARNING ("PMem::p_mem_mmap: failed to close file descriptor to /dev/zero");
+#  endif
+		return NULL;
+	}
+
+#  if !defined (PLIBSYS_MMAP_HAS_MAP_ANONYMOUS) && !defined (PLIBSYS_MMAP_HAS_MAP_ANON)
+	if (P_UNLIKELY (p_sys_close (fd) != 0)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_io (),
+				     p_error_get_last_system (),
+				     "Failed to close /dev/zero handle");
+		munmap (addr, n_bytes);
+		return NULL;
+	}
+#  endif
+#endif
+
+	return addr;
+}
+
+P_LIB_API pboolean
+p_mem_munmap (ppointer	mem,
+	      psize	n_bytes,
+	      PError	**error)
+{
+#if defined (P_OS_BEOS)
+	area_id	area;
+#elif defined (P_OS_OS2)
+	APIRET	ulrc;
+#elif defined (P_OS_AMIGA)
+	P_UNUSED (n_bytes);
+	P_UNUSED (error);
+#endif
+
+	if (P_UNLIKELY (mem == NULL || n_bytes == 0)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+#if defined (P_OS_WIN)
+	if (P_UNLIKELY (UnmapViewOfFile (mem) == 0)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_io (),
+				     p_error_get_last_system (),
+				     "Failed to call UnmapViewOfFile() to remove file mapping");
+#elif defined (P_OS_BEOS)
+	if (P_UNLIKELY ((area = area_for (mem)) == B_ERROR)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_io (),
+				     p_error_get_last_system (),
+				     "Failed to call area_for() to find allocated memory area");
+		return FALSE;
+	}
+
+	if (P_UNLIKELY ((delete_area (area)) != B_OK)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_io (),
+				     p_error_get_last_system (),
+				     "Failed to call delete_area() to remove memory area");
+#elif defined (P_OS_OS2)
+	if (P_UNLIKELY ((ulrc = DosFreeMem ((PVOID) mem)) != NO_ERROR)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_io_from_system ((pint) ulrc),
+				     ulrc,
+				     "Failed to call DosFreeMem() to free memory");
+#elif defined (P_OS_AMIGA)
+	free (mem);
+
+	if (P_UNLIKELY (FALSE)) {
+#else
+	if (P_UNLIKELY (munmap (mem, n_bytes) != 0)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_io (),
+				     p_error_get_last_system (),
+				     "Failed to call munmap() to remove file mapping");
+#endif
+		return FALSE;
+	} else
+		return TRUE;
+}
diff --git a/3rdparty/plibsys/src/pmem.h b/3rdparty/plibsys/src/pmem.h
new file mode 100644
index 0000000..3643942
--- /dev/null
+++ b/3rdparty/plibsys/src/pmem.h
@@ -0,0 +1,191 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file pmem.h
+ * @brief Memory management
+ * @author Alexander Saprykin
+ *
+ * Usually the system routines for memory management are used: malloc(),
+ * realloc(), free() and so on. But it is highly encouraged to use a more
+ * general approach: p_malloc(), p_malloc0(), p_realloc() and p_free() family of
+ * memory management routines. It gives you several advantages:
+ * - automatical checking of all input parameters for the NULL values;
+ * - ability to use a custom memory allocator.
+ * You can also mix these two families of calls, but it is not recommended.
+ *
+ * By default p_* routines are mapped to system calls, thus only NULL-checking
+ * is additionally performed. If you want to use the custom memory allocator,
+ * then fill in #PMemVTable structure and pass it to the p_mem_set_vtable(). To
+ * restore system calls back use p_mem_restore_vtable().
+ *
+ * Be careful when using the custom memory allocator: all memory chunks
+ * allocated with the custom allocator must be freed with the same allocator. If
+ * the custom allocator was installed after the library initialization call
+ * p_libsys_init() then you must to restore the original allocator before
+ * calling p_libsys_shutdown().
+ *
+ * Use p_mem_mmap() to allocate system memory using memory mapping and
+ * p_mem_munmap() to release the mapped memory. This type of allocated memory
+ * is not backed physically (does not consume any physical storage) by operating
+ * system. It means that every memory page within the allocated region will be
+ * committed to physical backend only when you first touch it. Until that
+ * untouched pages will be reserved for future usage. It can be useful when
+ * dealing with large memory blocks which should be filled with data on demand,
+ * i.e. custom memory allocator can request a large block first, and then it
+ * allocates chunks of memory within the block upon request.
+ *
+ * @note OS/2 supports non-backed memory pages allocation, but in a specific
+ * way: an exception handler to control access to uncommitted pages must be
+ * allocated on the stack of each thread before using the mapped memory. To
+ * unify the behaviour, on OS/2 all memory mapped allocations are already
+ * committed to the backing storage.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PMEM_H
+#define PLIBSYS_HEADER_PMEM_H
+
+#include <ptypes.h>
+#include <pmacros.h>
+#include <perror.h>
+
+P_BEGIN_DECLS
+
+/** Memory management table. */
+typedef struct PMemVTable_ {
+	ppointer	(*malloc)	(psize		n_bytes);	/**< malloc() implementation.	*/
+	ppointer	(*realloc)	(ppointer	mem,
+					 psize		n_bytes);	/**< realloc() implementation.	*/
+	void		(*free)		(ppointer	mem);		/**< free() implementation.	*/
+} PMemVTable;
+
+/**
+ * @brief Allocates a memory block for the specified number of bytes.
+ * @param n_bytes Size of the memory block in bytes.
+ * @return Pointer to a newly allocated memory block in case of success, NULL
+ * otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API ppointer	p_malloc		(psize			n_bytes);
+
+/**
+ * @brief Allocates a memory block for the specified number of bytes and fills
+ * it with zeros.
+ * @param n_bytes Size of the memory block in bytes.
+ * @return Pointer to a newly allocated memory block filled with zeros in case
+ * of success, NULL otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API ppointer	p_malloc0		(psize			n_bytes);
+
+/**
+ * @brief Changes the memory block size.
+ * @param mem Pointer to the memory block.
+ * @param n_bytes New size for @a mem block.
+ * @return Pointer to a newlly allocated memory block in case of success (if
+ * @a mem is NULL then it acts like p_malloc()), NULL otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API ppointer	p_realloc		(ppointer		mem,
+						 psize			n_bytes);
+
+/**
+ * @brief Frees a memory block by its pointer.
+ * @param mem Pointer to the memory block to free.
+ * @since 0.0.1
+ *
+ * You should only call this function for the pointers which were obtained using
+ * the p_malloc(), p_malloc0() and p_realloc() routines, otherwise behavior is
+ * unpredictable.
+ *
+ * Checks the pointer for the NULL value.
+ */
+P_LIB_API void		p_free			(ppointer		mem);
+
+/**
+ * @brief Sets custom memory management routines.
+ * @param table Table of the memory routines to use.
+ * @return TRUE if the table was accepted, FALSE otherwise.
+ * @note All members of @a table must be non-NULL.
+ * @note This call is not thread-safe.
+ * @warning Do not forget to set the original memory management routines before
+ * calling p_libsys_shutdown() if you have used p_mem_set_vtable() after the
+ * library initialization.
+ * @since 0.0.1
+ *
+ * In most cases you do not need to use this function. Use it only when you know
+ * what are you doing!
+ */
+P_LIB_API pboolean	p_mem_set_vtable	(const PMemVTable	*table);
+
+/**
+ * @brief Restores system memory management routines.
+ * @note This call is not thread-safe.
+ * @since 0.0.1
+ *
+ * The following system routines are restored: malloc(), free(), realloc().
+ */
+P_LIB_API void		p_mem_restore_vtable	(void);
+
+/**
+ * @brief Gets a memory mapped block from the system.
+ * @param n_bytes Size of the memory block in bytes.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return Pointer to the allocated memory block in case of success, NULL
+ * otherwise.
+ * @since 0.0.1
+ *
+ * Note that some systems can allocate memory only in chunks of the page size,
+ * so if @a n_bytes is less than the page size it will try to allocate a chunk
+ * of memory equal to the page size instead.
+ *
+ * On most systems returned memory is mapped to the null or swap device.
+ *
+ * @warning On OS/2 returned memory is mapped to physical storage and can be
+ * swapped.
+ */
+P_LIB_API ppointer	p_mem_mmap		(psize			n_bytes,
+						 PError			**error);
+
+/**
+ * @brief Unmaps memory back to the system.
+ * @param mem Pointer to a memory block previously allocated using the
+ * p_mem_mmap() call.
+ * @param n_bytes Size of the memory block in bytes.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API pboolean	p_mem_munmap		(ppointer		mem,
+						 psize			n_bytes,
+						 PError			**error);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PMEM_H */
diff --git a/3rdparty/plibsys/src/pmutex-amiga.c b/3rdparty/plibsys/src/pmutex-amiga.c
new file mode 100644
index 0000000..0c1216a
--- /dev/null
+++ b/3rdparty/plibsys/src/pmutex-amiga.c
@@ -0,0 +1,101 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "pmutex.h"
+
+#include <stdlib.h>
+
+#include <exec/exectags.h>
+#include <proto/exec.h>
+
+typedef APTR mutex_hdl;
+
+struct PMutex_ {
+	mutex_hdl	hdl;
+};
+
+P_LIB_API PMutex *
+p_mutex_new (void)
+{
+	PMutex *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PMutex))) == NULL)) {
+		P_ERROR ("PMutex::p_mutex_new: failed to allocate memory");
+		return NULL;
+	}
+
+	ret->hdl = IExec->AllocSysObjectTags (ASOT_MUTEX, ASOMUTEX_Recursive, TRUE, TAG_END);
+
+	if (P_UNLIKELY (ret->hdl == NULL)) {
+		P_ERROR ("PMutex::p_mutex_new: AllocSysObjectTags() failed");
+		p_free (ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_mutex_lock (PMutex *mutex)
+{
+	if (P_UNLIKELY (mutex == NULL))
+		return FALSE;
+
+	IExec->MutexObtain (mutex->hdl);
+
+	return TRUE;
+}
+
+P_LIB_API pboolean
+p_mutex_trylock (PMutex *mutex)
+{
+	if (P_UNLIKELY (mutex == NULL))
+		return FALSE;
+
+	return IExec->MutexAttempt (mutex->hdl);
+}
+
+P_LIB_API pboolean
+p_mutex_unlock (PMutex *mutex)
+{
+	if (P_UNLIKELY (mutex == NULL))
+		return FALSE;
+
+	IExec->MutexRelease (mutex->hdl);
+
+	return TRUE;
+}
+
+P_LIB_API void
+p_mutex_free (PMutex *mutex)
+{
+	if (P_UNLIKELY (mutex == NULL))
+		return;
+
+	IExec->FreeSysObject (ASOT_MUTEX, mutex->hdl);
+
+	p_free (mutex);
+}
diff --git a/3rdparty/plibsys/src/pmutex-atheos.c b/3rdparty/plibsys/src/pmutex-atheos.c
new file mode 100644
index 0000000..352e5c5
--- /dev/null
+++ b/3rdparty/plibsys/src/pmutex-atheos.c
@@ -0,0 +1,111 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "pmutex.h"
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include <atheos/semaphore.h>
+
+typedef sem_id mutex_hdl;
+
+struct PMutex_ {
+	mutex_hdl	hdl;
+};
+
+P_LIB_API PMutex *
+p_mutex_new (void)
+{
+	PMutex *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PMutex))) == NULL)) {
+		P_ERROR ("PMutex::p_mutex_new: failed to allocate memory");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((ret->hdl = create_semaphore ("", 1, 0)) < 0)) {
+		P_ERROR ("PMutex::p_mutex_new: create_semaphore() failed");
+		p_free (ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_mutex_lock (PMutex *mutex)
+{
+	status_t ret_status;
+
+	if (P_UNLIKELY (mutex == NULL))
+		return FALSE;
+
+	while ((ret_status = lock_semaphore (mutex->hdl)) == EINTR)
+		;
+
+	if (P_LIKELY (ret_status == 0))
+		return TRUE;
+	else {
+		P_ERROR ("PMutex::p_mutex_lock: lock_semaphore() failed");
+		return FALSE;
+	}
+}
+
+P_LIB_API pboolean
+p_mutex_trylock (PMutex *mutex)
+{
+	if (P_UNLIKELY (mutex == NULL))
+		return FALSE;
+
+	return (lock_semaphore_x (mutex->hdl, 1, 0, 0)) == 0 ? TRUE : FALSE;
+}
+
+P_LIB_API pboolean
+p_mutex_unlock (PMutex *mutex)
+{
+	if (P_UNLIKELY (mutex == NULL))
+		return FALSE;
+
+	if (P_LIKELY (unlock_semaphore (mutex->hdl) == 0))
+		return TRUE;
+	else {
+		P_ERROR ("PMutex::p_mutex_unlock: unlock_semaphore() failed");
+		return FALSE;
+	}
+}
+
+P_LIB_API void
+p_mutex_free (PMutex *mutex)
+{
+	if (P_UNLIKELY (mutex == NULL))
+		return;
+
+	if (P_UNLIKELY (delete_semaphore (mutex->hdl) != 0))
+		P_ERROR ("PMutex::p_mutex_free: delete_semaphore() failed");
+
+	p_free (mutex);
+}
diff --git a/3rdparty/plibsys/src/pmutex-beos.c b/3rdparty/plibsys/src/pmutex-beos.c
new file mode 100644
index 0000000..c16bf8b
--- /dev/null
+++ b/3rdparty/plibsys/src/pmutex-beos.c
@@ -0,0 +1,110 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "pmutex.h"
+
+#include <stdlib.h>
+
+#include <kernel/OS.h>
+
+typedef sem_id mutex_hdl;
+
+struct PMutex_ {
+	mutex_hdl	hdl;
+};
+
+P_LIB_API PMutex *
+p_mutex_new (void)
+{
+	PMutex *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PMutex))) == NULL)) {
+		P_ERROR ("PMutex::p_mutex_new: failed to allocate memory");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((ret->hdl = create_sem (1, "")) < B_OK)) {
+		P_ERROR ("PMutex::p_mutex_new: create_sem() failed");
+		p_free (ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_mutex_lock (PMutex *mutex)
+{
+	status_t ret_status;
+
+	if (P_UNLIKELY (mutex == NULL))
+		return FALSE;
+
+	while ((ret_status = acquire_sem (mutex->hdl)) == B_INTERRUPTED)
+		;
+
+	if (P_LIKELY (ret_status == B_NO_ERROR))
+		return TRUE;
+	else {
+		P_ERROR ("PMutex::p_mutex_lock: acquire_sem() failed");
+		return FALSE;
+	}
+}
+
+P_LIB_API pboolean
+p_mutex_trylock (PMutex *mutex)
+{
+	if (P_UNLIKELY (mutex == NULL))
+		return FALSE;
+
+	return (acquire_sem_etc (mutex->hdl, 1, B_RELATIVE_TIMEOUT, 0)) == B_NO_ERROR ? TRUE : FALSE;
+}
+
+P_LIB_API pboolean
+p_mutex_unlock (PMutex *mutex)
+{
+	if (P_UNLIKELY (mutex == NULL))
+		return FALSE;
+
+	if (P_LIKELY (release_sem (mutex->hdl) == B_NO_ERROR))
+		return TRUE;
+	else {
+		P_ERROR ("PMutex::p_mutex_unlock: release_sem() failed");
+		return FALSE;
+	}
+}
+
+P_LIB_API void
+p_mutex_free (PMutex *mutex)
+{
+	if (P_UNLIKELY (mutex == NULL))
+		return;
+
+	if (P_UNLIKELY (delete_sem (mutex->hdl) != B_NO_ERROR))
+		P_ERROR ("PMutex::p_mutex_free: delete_sem() failed");
+
+	p_free (mutex);
+}
diff --git a/3rdparty/plibsys/src/pmutex-none.c b/3rdparty/plibsys/src/pmutex-none.c
new file mode 100644
index 0000000..1bc9b50
--- /dev/null
+++ b/3rdparty/plibsys/src/pmutex-none.c
@@ -0,0 +1,66 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmutex.h"
+
+#include <stdlib.h>
+
+struct PMutex_ {
+	pint	hdl;
+};
+
+P_LIB_API PMutex *
+p_mutex_new (void)
+{
+	return NULL;
+}
+
+P_LIB_API pboolean
+p_mutex_lock (PMutex *mutex)
+{
+	return FALSE;
+}
+
+P_LIB_API pboolean
+p_mutex_trylock (PMutex *mutex)
+{
+	P_UNUSED (mutex);
+
+	return FALSE;
+}
+
+P_LIB_API pboolean
+p_mutex_unlock (PMutex *mutex)
+{
+	P_UNUSED (mutex);
+
+	return FALSE;
+}
+
+P_LIB_API void
+p_mutex_free (PMutex *mutex)
+{
+	P_UNUSED (mutex);
+}
diff --git a/3rdparty/plibsys/src/pmutex-os2.c b/3rdparty/plibsys/src/pmutex-os2.c
new file mode 100644
index 0000000..1040044
--- /dev/null
+++ b/3rdparty/plibsys/src/pmutex-os2.c
@@ -0,0 +1,112 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "pmutex.h"
+
+#include <stdlib.h>
+
+#define INCL_DOSSEMAPHORES
+#define INCL_DOSERRORS
+#include <os2.h>
+
+typedef HMTX mutex_hdl;
+
+struct PMutex_ {
+	mutex_hdl	hdl;
+};
+
+P_LIB_API PMutex *
+p_mutex_new (void)
+{
+	PMutex *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PMutex))) == NULL)) {
+		P_ERROR ("PMutex::p_mutex_new: failed to allocate memory");
+		return NULL;
+	}
+
+	if (P_UNLIKELY (DosCreateMutexSem (NULL, (PHMTX) &ret->hdl, 0, FALSE) != NO_ERROR)) {
+		P_ERROR ("PMutex::p_mutex_new: DosCreateMutexSem() failed");
+		p_free (ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_mutex_lock (PMutex *mutex)
+{
+	APIRET ulrc;
+
+	if (P_UNLIKELY (mutex == NULL))
+		return FALSE;
+
+	while ((ulrc = DosRequestMutexSem (mutex->hdl, SEM_INDEFINITE_WAIT)) == ERROR_INTERRUPT)
+		;
+
+	if (P_LIKELY (ulrc == NO_ERROR))
+		return TRUE;
+	else {
+		P_ERROR ("PMutex::p_mutex_lock: DosRequestMutexSem() failed");
+		return FALSE;
+	}
+}
+
+P_LIB_API pboolean
+p_mutex_trylock (PMutex *mutex)
+{
+	if (P_UNLIKELY (mutex == NULL))
+		return FALSE;
+
+	return (DosRequestMutexSem (mutex->hdl, SEM_IMMEDIATE_RETURN)) == NO_ERROR ? TRUE : FALSE;
+}
+
+P_LIB_API pboolean
+p_mutex_unlock (PMutex *mutex)
+{
+	if (P_UNLIKELY (mutex == NULL))
+		return FALSE;
+
+	if (P_LIKELY (DosReleaseMutexSem (mutex->hdl) == NO_ERROR))
+		return TRUE;
+	else {
+		P_ERROR ("PMutex::p_mutex_unlock: DosReleaseMutexSem() failed");
+		return FALSE;
+	}
+}
+
+P_LIB_API void
+p_mutex_free (PMutex *mutex)
+{
+	if (P_UNLIKELY (mutex == NULL))
+		return;
+
+	if (P_UNLIKELY (DosCloseMutexSem (mutex->hdl) != NO_ERROR))
+		P_ERROR ("PMutex::p_mutex_free: DosCloseMutexSem() failed");
+
+	p_free (mutex);
+}
diff --git a/3rdparty/plibsys/src/pmutex-posix.c b/3rdparty/plibsys/src/pmutex-posix.c
new file mode 100644
index 0000000..4c471cb
--- /dev/null
+++ b/3rdparty/plibsys/src/pmutex-posix.c
@@ -0,0 +1,104 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "pmutex.h"
+
+#include <stdlib.h>
+#include <pthread.h>
+
+typedef pthread_mutex_t mutex_hdl;
+
+struct PMutex_ {
+	mutex_hdl	hdl;
+};
+
+P_LIB_API PMutex *
+p_mutex_new (void)
+{
+	PMutex *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PMutex))) == NULL)) {
+		P_ERROR ("PMutex::p_mutex_new: failed to allocate memory");
+		return NULL;
+	}
+
+	if (P_UNLIKELY (pthread_mutex_init (&ret->hdl, NULL) != 0)) {
+		P_ERROR ("PMutex::p_mutex_new: pthread_mutex_init() failed");
+		p_free (ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_mutex_lock (PMutex *mutex)
+{
+	if (P_UNLIKELY (mutex == NULL))
+		return FALSE;
+
+	if (P_LIKELY (pthread_mutex_lock (&mutex->hdl) == 0))
+		return TRUE;
+	else {
+		P_ERROR ("PMutex::p_mutex_lock: pthread_mutex_lock() failed");
+		return FALSE;
+	}
+}
+
+P_LIB_API pboolean
+p_mutex_trylock (PMutex *mutex)
+{
+	if (P_UNLIKELY (mutex == NULL))
+		return FALSE;
+
+	return (pthread_mutex_trylock (&mutex->hdl) == 0) ? TRUE : FALSE;
+}
+
+P_LIB_API pboolean
+p_mutex_unlock (PMutex *mutex)
+{
+	if (P_UNLIKELY (mutex == NULL))
+		return FALSE;
+
+	if (P_LIKELY (pthread_mutex_unlock (&mutex->hdl) == 0))
+		return TRUE;
+	else {
+		P_ERROR ("PMutex::p_mutex_unlock: pthread_mutex_unlock() failed");
+		return FALSE;
+	}
+}
+
+P_LIB_API void
+p_mutex_free (PMutex *mutex)
+{
+	if (P_UNLIKELY (mutex == NULL))
+		return;
+
+	if (P_UNLIKELY (pthread_mutex_destroy (&mutex->hdl) != 0))
+		P_ERROR ("PMutex::p_mutex_free: pthread_mutex_destroy() failed");
+
+	p_free (mutex);
+}
diff --git a/3rdparty/plibsys/src/pmutex-solaris.c b/3rdparty/plibsys/src/pmutex-solaris.c
new file mode 100644
index 0000000..bb7adda
--- /dev/null
+++ b/3rdparty/plibsys/src/pmutex-solaris.c
@@ -0,0 +1,105 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "pmutex.h"
+
+#include <stdlib.h>
+#include <synch.h>
+#include <thread.h>
+
+typedef mutex_t mutex_hdl;
+
+struct PMutex_ {
+	mutex_hdl hdl;
+};
+
+P_LIB_API PMutex *
+p_mutex_new (void)
+{
+	PMutex *ret;
+
+	if ((P_UNLIKELY (ret = p_malloc0 (sizeof (PMutex))) == NULL)) {
+		P_ERROR ("PMutex::p_mutex_new: failed to allocate memory");
+		return NULL;
+	}
+
+	if (P_UNLIKELY (mutex_init (&ret->hdl, USYNC_THREAD, NULL) != 0)) {
+		P_ERROR ("PMutex::p_mutex_new: mutex_init() failed");
+		p_free (ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_mutex_lock (PMutex *mutex)
+{
+	if (P_UNLIKELY (mutex == NULL))
+		return FALSE;
+
+	if (P_LIKELY (mutex_lock (&mutex->hdl) == 0))
+		return TRUE;
+	else {
+		P_ERROR ("PMutex::p_mutex_lock: mutex_lock() failed");
+		return FALSE;
+	}
+}
+
+P_LIB_API pboolean
+p_mutex_trylock (PMutex *mutex)
+{
+	if (P_UNLIKELY (mutex == NULL))
+		return FALSE;
+
+	return (mutex_trylock (&mutex->hdl) == 0) ? TRUE : FALSE;
+}
+
+P_LIB_API pboolean
+p_mutex_unlock (PMutex *mutex)
+{
+	if (P_UNLIKELY (mutex == NULL))
+		return FALSE;
+
+	if (P_LIKELY (mutex_unlock (&mutex->hdl) == 0))
+		return TRUE;
+	else {
+		P_ERROR ("PMutex::p_mutex_unlock: mutex_unlock() failed");
+		return FALSE;
+	}
+}
+
+P_LIB_API void
+p_mutex_free (PMutex *mutex)
+{
+	if (P_UNLIKELY (mutex == NULL))
+		return;
+
+	if (P_UNLIKELY (mutex_destroy (&mutex->hdl) != 0))
+		P_ERROR ("PMutex::p_mutex_unlock: mutex_destroy() failed");
+
+	p_free (mutex);
+}
diff --git a/3rdparty/plibsys/src/pmutex-win.c b/3rdparty/plibsys/src/pmutex-win.c
new file mode 100644
index 0000000..2a801b6
--- /dev/null
+++ b/3rdparty/plibsys/src/pmutex-win.c
@@ -0,0 +1,92 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "pmutex.h"
+
+#include <stdlib.h>
+
+typedef CRITICAL_SECTION mutex_hdl;
+
+struct PMutex_ {
+	mutex_hdl hdl;
+};
+
+P_LIB_API PMutex *
+p_mutex_new (void)
+{
+	PMutex *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PMutex))) == NULL)) {
+		P_ERROR ("PMutex::p_mutex_new: failed to allocate memory");
+		return NULL;
+	}
+
+	InitializeCriticalSection (&ret->hdl);
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_mutex_lock (PMutex *mutex)
+{
+	if (P_UNLIKELY (mutex == NULL))
+		return FALSE;
+
+	EnterCriticalSection (&mutex->hdl);
+
+	return TRUE;
+}
+
+P_LIB_API pboolean
+p_mutex_trylock (PMutex *mutex)
+{
+	if (P_UNLIKELY (mutex == NULL))
+		return FALSE;
+
+	return TryEnterCriticalSection (&mutex->hdl) != 0 ? TRUE : FALSE;
+}
+
+P_LIB_API pboolean
+p_mutex_unlock (PMutex *mutex)
+{
+	if (P_UNLIKELY (mutex == NULL))
+		return FALSE;
+
+	LeaveCriticalSection (&mutex->hdl);
+
+	return TRUE;
+}
+
+P_LIB_API void
+p_mutex_free (PMutex *mutex)
+{
+	if (P_UNLIKELY (mutex == NULL))
+		return;
+
+	DeleteCriticalSection (&mutex->hdl);
+
+	p_free (mutex);
+}
diff --git a/3rdparty/plibsys/src/pmutex.h b/3rdparty/plibsys/src/pmutex.h
new file mode 100644
index 0000000..449a652
--- /dev/null
+++ b/3rdparty/plibsys/src/pmutex.h
@@ -0,0 +1,136 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file pmutex.h
+ * @brief Mutex routines
+ * @author Alexander Saprykin
+ *
+ * A mutex is a mutual exclusive (hence mutex) synchronization primitive which
+ * allows access to a critical section only to one of the concurrently running
+ * threads. It is used to protected shared data structures from concurrent
+ * modifications which could lead to unpredictable behavior.
+ *
+ * When entering a critical section a thread must call p_mutex_lock() to get a
+ * lock. If another thread is already holding the lock all other threads will
+ * be suspended until the lock is released with p_mutex_unlock(). After
+ * releasing the lock one of the waiting threads is resumed to continue
+ * execution. On most systems it is not specified whether a mutex waiting queue
+ * is fair (FIFO) or not.
+ *
+ * The typical mutex usage:
+ * @code
+ * p_mutex_lock (mutex);
+ *
+ * ... code in critical section ...
+ *
+ * p_mutex_unlock (mutex);
+ * @endcode
+ * You can also think of the mutex as a binary semaphore.
+ *
+ * It is implementation dependent whether recursive locking or non-locked mutex
+ * unlocking is allowed, but such actions can lead to unpredictable behavior.
+ * Do not rely on such behavior in cross-platform applications.
+ *
+ * This is the thread scoped mutex implementation. You could not share this
+ * mutex outside the process adress space, but you can share it between the
+ * threads of the same process.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PMUTEX_H
+#define PLIBSYS_HEADER_PMUTEX_H
+
+#include <pmacros.h>
+#include <ptypes.h>
+
+P_BEGIN_DECLS
+
+/** Mutex opaque data structure. */
+typedef struct PMutex_ PMutex;
+
+/**
+ * @brief Creates a new #PMutex object.
+ * @return Pointer to a newly created #PMutex object.
+ * @since 0.0.1
+ */
+P_LIB_API PMutex *	p_mutex_new	(void);
+
+/**
+ * @brief Locks a mutex.
+ * @param mutex #PMutex to lock.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ * @warning Do not lock the mutex recursively - it may lead to an application
+ * deadlock (implementation dependent).
+ *
+ * Forces the calling thread to sleep until @a mutex becomes available for
+ * locking.
+ */
+P_LIB_API pboolean	p_mutex_lock	(PMutex *mutex);
+
+/**
+ * @brief Tries to lock a mutex immediately.
+ * @param mutex #PMutex to lock.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ * @warning Do not lock the mutex recursively - it may lead to an application
+ * deadlock (implementation dependent).
+ *
+ * Tries to lock @a mutex and returns immediately if it is not available for
+ * locking.
+ */
+P_LIB_API pboolean	p_mutex_trylock	(PMutex *mutex);
+
+/**
+ * @brief Releases a locked mutex.
+ * @param mutex #PMutex to release.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ * @warning Do not use this function on non-locked mutexes - behavior may be
+ * unpredictable.
+ *
+ * If @a mutex was previously locked then it becomes unlocked.
+ *
+ * It's implementation dependent whether only the same thread can lock and
+ * unlock the same mutex.
+ */
+P_LIB_API pboolean	p_mutex_unlock	(PMutex *mutex);
+
+/**
+ * @brief Frees #PMutex object.
+ * @param mutex #PMutex to free.
+ * @since 0.0.1
+ * @warning It doesn't unlock @a mutex before freeing memory, so you should do
+ * it manually.
+ */
+P_LIB_API void		p_mutex_free	(PMutex *mutex);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PMUTEX_H */
diff --git a/3rdparty/plibsys/src/pprocess.c b/3rdparty/plibsys/src/pprocess.c
new file mode 100644
index 0000000..d509230
--- /dev/null
+++ b/3rdparty/plibsys/src/pprocess.c
@@ -0,0 +1,61 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pprocess.h"
+
+#ifndef P_OS_WIN
+#  include <sys/types.h>
+#  include <signal.h>
+#  include <unistd.h>
+#endif
+
+P_LIB_API puint32
+p_process_get_current_pid (void)
+{
+#ifdef P_OS_WIN
+	return (puint32) GetCurrentProcessId ();
+#else
+	return (puint32) getpid ();
+#endif
+}
+
+P_LIB_API pboolean
+p_process_is_running (puint32 pid)
+{
+#ifdef P_OS_WIN
+	HANDLE proc;
+	DWORD ret;
+
+	if ((proc = OpenProcess (SYNCHRONIZE, FALSE, pid)) == NULL)
+		return FALSE;
+
+	ret = WaitForSingleObject (proc, 0);
+	CloseHandle (proc);
+
+	return ret == WAIT_TIMEOUT;
+#else
+	return kill ((pid_t) pid, 0) == 0;
+#endif
+}
diff --git a/3rdparty/plibsys/src/pprocess.h b/3rdparty/plibsys/src/pprocess.h
new file mode 100644
index 0000000..272bbb5
--- /dev/null
+++ b/3rdparty/plibsys/src/pprocess.h
@@ -0,0 +1,69 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file pprocess.h
+ * @brief Process information
+ * @author Alexander Saprykin
+ *
+ * A process is an executing unit in an operating system with its own address
+ * space. Every process can be identified with a unique identifier called PID.
+ * To get a PID of the currently running process call
+ * p_process_get_current_pid(). To check whether a process with a given PID is
+ * running up use p_process_is_running().
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PPROCESS_H
+#define PLIBSYS_HEADER_PPROCESS_H
+
+#include <pmacros.h>
+#include <ptypes.h>
+
+P_BEGIN_DECLS
+
+/**
+ * @brief Gets a PID of the calling process.
+ * @return PID of the calling process.
+ * @since 0.0.1
+ */
+P_LIB_API puint32	p_process_get_current_pid	(void);
+
+/**
+ * @brief Checks whether a process with a given PID is running or not.
+ * @param pid PID to check for.
+ * @return TRUE if the process with the given PID exists and is running up,
+ * FALSE otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API pboolean	p_process_is_running		(puint32 pid);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PPROCESS_H */
+
diff --git a/3rdparty/plibsys/src/prwlock-general.c b/3rdparty/plibsys/src/prwlock-general.c
new file mode 100644
index 0000000..5d79ed1
--- /dev/null
+++ b/3rdparty/plibsys/src/prwlock-general.c
@@ -0,0 +1,326 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "pmutex.h"
+#include "pcondvariable.h"
+#include "prwlock.h"
+
+#include <stdlib.h>
+
+#define P_RWLOCK_SET_READERS(lock, readers) (((lock) & (~0x00007FFF)) | (readers))
+#define P_RWLOCK_READER_COUNT(lock) ((lock) & 0x00007FFF)
+#define P_RWLOCK_SET_WRITERS(lock, writers) (((lock) & (~0x3FFF8000)) | ((writers) << 15))
+#define P_RWLOCK_WRITER_COUNT(lock) (((lock) & 0x3FFF8000) >> 15)
+
+struct PRWLock_ {
+	PMutex		*mutex;
+	PCondVariable	*read_cv;
+	PCondVariable	*write_cv;
+	puint32		active_threads;
+	puint32		waiting_threads;
+};
+
+P_LIB_API PRWLock *
+p_rwlock_new (void)
+{
+	PRWLock *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PRWLock))) == NULL)) {
+		P_ERROR ("PRWLock::p_rwlock_new: failed to allocate memory");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((ret->mutex = p_mutex_new ()) == NULL)) {
+		P_ERROR ("PRWLock::p_rwlock_new: failed to allocate mutex");
+		p_free (ret);
+	}
+
+	if (P_UNLIKELY ((ret->read_cv = p_cond_variable_new ()) == NULL)) {
+		P_ERROR ("PRWLock::p_rwlock_new: failed to allocate condition variable for read");
+		p_mutex_free (ret->mutex);
+		p_free (ret);
+	}
+
+	if (P_UNLIKELY ((ret->write_cv = p_cond_variable_new ()) == NULL)) {
+		P_ERROR ("PRWLock::p_rwlock_new: failed to allocate condition variable for write");
+		p_cond_variable_free (ret->read_cv);
+		p_mutex_free (ret->mutex);
+		p_free (ret);
+	}
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_rwlock_reader_lock (PRWLock *lock)
+{
+	pboolean wait_ok;
+
+	if (P_UNLIKELY (lock == NULL))
+		return FALSE;
+
+	if (P_UNLIKELY (p_mutex_lock (lock->mutex) == FALSE)) {
+		P_ERROR ("PRWLock::p_rwlock_reader_lock: p_mutex_lock() failed");
+		return FALSE;
+	}
+
+	wait_ok = TRUE;
+
+	if (P_RWLOCK_WRITER_COUNT (lock->active_threads)) {
+		lock->waiting_threads = P_RWLOCK_SET_READERS (lock->waiting_threads,
+							      P_RWLOCK_READER_COUNT (lock->waiting_threads) + 1);
+
+		while (P_RWLOCK_WRITER_COUNT (lock->active_threads)) {
+			wait_ok = p_cond_variable_wait (lock->read_cv, lock->mutex);
+
+			if (P_UNLIKELY (wait_ok == FALSE)) {
+				P_ERROR ("PRWLock::p_rwlock_reader_lock: p_cond_variable_wait() failed");
+				break;
+			}
+		}
+
+		lock->waiting_threads = P_RWLOCK_SET_READERS (lock->waiting_threads,
+							      P_RWLOCK_READER_COUNT (lock->waiting_threads) - 1);
+	}
+
+	if (P_LIKELY (wait_ok == TRUE))
+		lock->active_threads = P_RWLOCK_SET_READERS (lock->active_threads,
+							     P_RWLOCK_READER_COUNT (lock->active_threads) + 1);
+
+	if (P_UNLIKELY (p_mutex_unlock (lock->mutex) == FALSE)) {
+		P_ERROR ("PRWLock::p_rwlock_reader_lock: p_mutex_unlock() failed");
+		return FALSE;
+	}
+
+	return wait_ok;
+}
+
+P_LIB_API pboolean
+p_rwlock_reader_trylock (PRWLock *lock)
+{
+	if (P_UNLIKELY (lock == NULL))
+		return FALSE;
+
+	if (P_UNLIKELY (p_mutex_lock (lock->mutex) == FALSE)) {
+		P_ERROR ("PRWLock::p_rwlock_reader_trylock: p_mutex_lock() failed");
+		return FALSE;
+	}
+
+	if (P_RWLOCK_WRITER_COUNT (lock->active_threads)) {
+		if (P_UNLIKELY (p_mutex_unlock (lock->mutex) == FALSE))
+			P_ERROR ("PRWLock::p_rwlock_reader_trylock: p_mutex_unlock() failed(1)");
+
+		return FALSE;
+	}
+
+	lock->active_threads = P_RWLOCK_SET_READERS (lock->active_threads,
+						     P_RWLOCK_READER_COUNT (lock->active_threads) + 1);
+
+	if (P_UNLIKELY (p_mutex_unlock (lock->mutex) == FALSE)) {
+		P_ERROR ("PRWLock::p_rwlock_reader_trylock: p_mutex_unlock() failed(2)");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+P_LIB_API pboolean
+p_rwlock_reader_unlock (PRWLock *lock)
+{
+	puint32		reader_count;
+	pboolean	signal_ok;
+
+	if (P_UNLIKELY (lock == NULL))
+		return FALSE;
+
+	if (P_UNLIKELY (p_mutex_lock (lock->mutex) == FALSE)) {
+		P_ERROR ("PRWLock::p_rwlock_reader_unlock: p_mutex_lock() failed");
+		return FALSE;
+	}
+
+	reader_count = P_RWLOCK_READER_COUNT (lock->active_threads);
+
+	if (P_UNLIKELY (reader_count == 0)) {
+		if (P_UNLIKELY (p_mutex_unlock (lock->mutex) == FALSE))
+			P_ERROR ("PRWLock::p_rwlock_reader_unlock: p_mutex_unlock() failed(1)");
+
+		return TRUE;
+	}
+
+	lock->active_threads = P_RWLOCK_SET_READERS (lock->active_threads, reader_count - 1);
+
+	signal_ok = TRUE;
+
+	if (reader_count == 1 && P_RWLOCK_WRITER_COUNT (lock->waiting_threads))
+		signal_ok = p_cond_variable_signal (lock->write_cv);
+
+	if (P_UNLIKELY (signal_ok == FALSE))
+		P_ERROR ("PRWLock::p_rwlock_reader_unlock: p_cond_variable_signal() failed");
+
+	if (P_UNLIKELY (p_mutex_unlock (lock->mutex) == FALSE)) {
+		P_ERROR ("PRWLock::p_rwlock_reader_unlock: p_mutex_unlock() failed(2)");
+		return FALSE;
+	}
+
+	return signal_ok;
+}
+
+P_LIB_API pboolean
+p_rwlock_writer_lock (PRWLock *lock)
+{
+	pboolean wait_ok;
+
+	if (P_UNLIKELY (lock == NULL))
+		return FALSE;
+
+	if (P_UNLIKELY (p_mutex_lock (lock->mutex) == FALSE)) {
+		P_ERROR ("PRWLock::p_rwlock_writer_lock: p_mutex_lock() failed");
+		return FALSE;
+	}
+
+	wait_ok = TRUE;
+
+	if (lock->active_threads) {
+		lock->waiting_threads = P_RWLOCK_SET_WRITERS (lock->waiting_threads,
+							      P_RWLOCK_WRITER_COUNT (lock->waiting_threads) + 1);
+
+		while (lock->active_threads) {
+			wait_ok = p_cond_variable_wait (lock->write_cv, lock->mutex);
+
+			if (P_UNLIKELY (wait_ok == FALSE)) {
+				P_ERROR ("PRWLock::p_rwlock_writer_lock: p_cond_variable_wait() failed");
+				break;
+			}
+		}
+
+		lock->waiting_threads = P_RWLOCK_SET_WRITERS (lock->waiting_threads,
+							      P_RWLOCK_WRITER_COUNT (lock->waiting_threads) - 1);
+	}
+
+	if (P_LIKELY (wait_ok == TRUE))
+		lock->active_threads = P_RWLOCK_SET_WRITERS (lock->active_threads, 1);
+
+	if (P_UNLIKELY (p_mutex_unlock (lock->mutex) == FALSE)) {
+		P_ERROR ("PRWLock::p_rwlock_writer_lock: p_mutex_unlock() failed");
+		return FALSE;
+	}
+
+	return wait_ok;
+}
+
+P_LIB_API pboolean
+p_rwlock_writer_trylock (PRWLock *lock)
+{
+	if (P_UNLIKELY (lock == NULL))
+		return FALSE;
+
+	if (P_UNLIKELY (p_mutex_lock (lock->mutex) == FALSE)) {
+		P_ERROR ("PRWLock::p_rwlock_writer_trylock: p_mutex_lock() failed");
+		return FALSE;
+	}
+
+	if (lock->active_threads) {
+		if (P_UNLIKELY (p_mutex_unlock (lock->mutex) == FALSE))
+			P_ERROR ("PRWLock::p_rwlock_writer_trylock: p_mutex_unlock() failed(1)");
+
+		return FALSE;
+	}
+
+	lock->active_threads = P_RWLOCK_SET_WRITERS (lock->active_threads, 1);
+
+	if (P_UNLIKELY (p_mutex_unlock (lock->mutex) == FALSE)) {
+		P_ERROR ("PRWLock::p_rwlock_writer_trylock: p_mutex_unlock() failed(2)");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+P_LIB_API pboolean
+p_rwlock_writer_unlock (PRWLock *lock)
+{
+	pboolean signal_ok;
+
+	if (P_UNLIKELY (lock == NULL))
+		return FALSE;
+
+	if (P_UNLIKELY (p_mutex_lock (lock->mutex) == FALSE)) {
+		P_ERROR ("PRWLock::p_rwlock_writer_unlock: p_mutex_lock() failed");
+		return FALSE;
+	}
+
+	lock->active_threads = P_RWLOCK_SET_WRITERS (lock->active_threads, 0);
+
+	signal_ok = TRUE;
+
+	if (P_RWLOCK_WRITER_COUNT (lock->waiting_threads)) {
+		if (P_UNLIKELY (p_cond_variable_signal (lock->write_cv) == FALSE)) {
+			P_ERROR ("PRWLock::p_rwlock_writer_unlock: p_cond_variable_signal() failed");
+			signal_ok = FALSE;
+		}
+	} else if (P_RWLOCK_READER_COUNT (lock->waiting_threads)) {
+		if (P_UNLIKELY (p_cond_variable_broadcast (lock->read_cv) == FALSE)) {
+			P_ERROR ("PRWLock::p_rwlock_writer_unlock: p_cond_variable_broadcast() failed");
+			signal_ok = FALSE;
+		}
+	}
+
+	if (P_UNLIKELY (p_mutex_unlock (lock->mutex) == FALSE)) {
+		P_ERROR ("PRWLock::p_rwlock_writer_unlock: p_mutex_unlock() failed");
+		return FALSE;
+	}
+
+	return signal_ok;
+}
+
+P_LIB_API void
+p_rwlock_free (PRWLock *lock)
+{
+	if (P_UNLIKELY (lock == NULL))
+		return;
+
+	if (P_UNLIKELY (lock->active_threads))
+		P_WARNING ("PRWLock::p_rwlock_free: destroying while active threads are present");
+
+	if (P_UNLIKELY (lock->waiting_threads))
+		P_WARNING ("PRWLock::p_rwlock_free: destroying while waiting threads are present");
+
+	p_mutex_free (lock->mutex);
+	p_cond_variable_free (lock->read_cv);
+	p_cond_variable_free (lock->write_cv);
+
+	p_free (lock);
+}
+
+void
+p_rwlock_init (void)
+{
+}
+
+void
+p_rwlock_shutdown (void)
+{
+}
diff --git a/3rdparty/plibsys/src/prwlock-none.c b/3rdparty/plibsys/src/prwlock-none.c
new file mode 100644
index 0000000..53fdb76
--- /dev/null
+++ b/3rdparty/plibsys/src/prwlock-none.c
@@ -0,0 +1,103 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "prwlock.h"
+
+#include <stdlib.h>
+
+struct PRWLock_ {
+	pint hdl;
+};
+
+P_LIB_API PRWLock *
+p_rwlock_new (void)
+{
+	return NULL;
+}
+
+P_LIB_API pboolean
+p_rwlock_reader_lock (PRWLock *lock)
+{
+	P_UNUSED (lock);
+
+	return FALSE;
+}
+
+P_LIB_API pboolean
+p_rwlock_reader_trylock (PRWLock *lock)
+{
+	P_UNUSED (lock);
+
+	return FALSE;
+}
+
+P_LIB_API pboolean
+p_rwlock_reader_unlock (PRWLock *lock)
+{
+	P_UNUSED (lock);
+
+	return FALSE;
+}
+
+P_LIB_API pboolean
+p_rwlock_writer_lock (PRWLock *lock)
+{
+	P_UNUSED (lock);
+
+	return FALSE;
+}
+
+P_LIB_API pboolean
+p_rwlock_writer_trylock (PRWLock *lock)
+{
+	P_UNUSED (lock);
+
+	return FALSE;
+}
+
+P_LIB_API pboolean
+p_rwlock_writer_unlock (PRWLock *lock)
+{
+	P_UNUSED (lock);
+
+	return FALSE;
+}
+
+P_LIB_API void
+p_rwlock_free (PRWLock *lock)
+{
+	P_UNUSED (lock);
+}
+
+void
+p_rwlock_init (void)
+{
+}
+
+void
+p_rwlock_shutdown (void)
+{
+}
+
diff --git a/3rdparty/plibsys/src/prwlock-posix.c b/3rdparty/plibsys/src/prwlock-posix.c
new file mode 100644
index 0000000..817de8c
--- /dev/null
+++ b/3rdparty/plibsys/src/prwlock-posix.c
@@ -0,0 +1,151 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "prwlock.h"
+
+#include <stdlib.h>
+#include <pthread.h>
+
+typedef pthread_rwlock_t rwlock_hdl;
+
+struct PRWLock_ {
+	rwlock_hdl hdl;
+};
+
+static pboolean pp_rwlock_unlock_any (PRWLock *lock);
+
+static pboolean
+pp_rwlock_unlock_any (PRWLock *lock)
+{
+	if (P_UNLIKELY (lock == NULL))
+		return FALSE;
+
+	if (P_LIKELY (pthread_rwlock_unlock (&lock->hdl) == 0))
+		return TRUE;
+	else {
+		P_ERROR ("PRWLock::pp_rwlock_unlock_any: pthread_rwlock_unlock() failed");
+		return FALSE;
+	}
+}
+
+P_LIB_API PRWLock *
+p_rwlock_new (void)
+{
+	PRWLock *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PRWLock))) == NULL)) {
+		P_ERROR ("PRWLock::p_rwlock_new: failed to allocate memory");
+		return NULL;
+	}
+
+	if (P_UNLIKELY (pthread_rwlock_init (&ret->hdl, NULL) != 0)) {
+		P_ERROR ("PRWLock::p_rwlock_new: pthread_rwlock_init() failed");
+		p_free (ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_rwlock_reader_lock (PRWLock *lock)
+{
+	if (P_UNLIKELY (lock == NULL))
+		return FALSE;
+
+	if (P_UNLIKELY (pthread_rwlock_rdlock (&lock->hdl) == 0))
+		return TRUE;
+	else {
+		P_ERROR ("PRWLock::p_rwlock_reader_lock: pthread_rwlock_rdlock() failed");
+		return FALSE;
+	}
+}
+
+P_LIB_API pboolean
+p_rwlock_reader_trylock (PRWLock *lock)
+{
+	if (P_UNLIKELY (lock == NULL))
+		return FALSE;
+
+	return (pthread_rwlock_tryrdlock (&lock->hdl) == 0) ? TRUE : FALSE;
+}
+
+P_LIB_API pboolean
+p_rwlock_reader_unlock (PRWLock *lock)
+{
+	return pp_rwlock_unlock_any (lock);
+}
+
+P_LIB_API pboolean
+p_rwlock_writer_lock (PRWLock *lock)
+{
+	if (P_UNLIKELY (lock == NULL))
+		return FALSE;
+
+	if (P_UNLIKELY (pthread_rwlock_wrlock (&lock->hdl) == 0))
+		return TRUE;
+	else {
+		P_ERROR ("PRWLock::p_rwlock_writer_lock: pthread_rwlock_wrlock() failed");
+		return FALSE;
+	}
+}
+
+P_LIB_API pboolean
+p_rwlock_writer_trylock (PRWLock *lock)
+{
+	if (P_UNLIKELY (lock == NULL))
+		return FALSE;
+
+	return (pthread_rwlock_trywrlock (&lock->hdl) == 0) ? TRUE : FALSE;
+}
+
+P_LIB_API pboolean
+p_rwlock_writer_unlock (PRWLock *lock)
+{
+	return pp_rwlock_unlock_any (lock);
+}
+
+P_LIB_API void
+p_rwlock_free (PRWLock *lock)
+{
+	if (P_UNLIKELY (lock == NULL))
+		return;
+
+	if (P_UNLIKELY (pthread_rwlock_destroy (&lock->hdl) != 0))
+		P_ERROR ("PRWLock::p_rwlock_free: pthread_rwlock_destroy() failed");
+
+	p_free (lock);
+}
+
+void
+p_rwlock_init (void)
+{
+}
+
+void
+p_rwlock_shutdown (void)
+{
+}
diff --git a/3rdparty/plibsys/src/prwlock-solaris.c b/3rdparty/plibsys/src/prwlock-solaris.c
new file mode 100644
index 0000000..c29d345
--- /dev/null
+++ b/3rdparty/plibsys/src/prwlock-solaris.c
@@ -0,0 +1,151 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "prwlock.h"
+
+#include <stdlib.h>
+#include <thread.h>
+
+typedef rwlock_t rwlock_hdl;
+
+struct PRWLock_ {
+	rwlock_hdl hdl;
+};
+
+static pboolean pp_rwlock_unlock_any (PRWLock *lock);
+
+static pboolean
+pp_rwlock_unlock_any (PRWLock *lock)
+{
+	if (P_UNLIKELY (lock == NULL))
+		return FALSE;
+
+	if (P_LIKELY (rw_unlock (&lock->hdl) == 0))
+		return TRUE;
+	else {
+		P_ERROR ("PRWLock::pp_rwlock_unlock_any: rw_unlock() failed");
+		return FALSE;
+	}
+}
+
+P_LIB_API PRWLock *
+p_rwlock_new (void)
+{
+	PRWLock *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PRWLock))) == NULL)) {
+		P_ERROR ("PRWLock::p_rwlock_new: failed to allocate memory");
+		return NULL;
+	}
+
+	if (P_UNLIKELY (rwlock_init (&ret->hdl, USYNC_THREAD, NULL) != 0)) {
+		P_ERROR ("PRWLock::p_rwlock_new: rwlock_init() failed");
+		p_free (ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_rwlock_reader_lock (PRWLock *lock)
+{
+	if (P_UNLIKELY (lock == NULL))
+		return FALSE;
+
+	if (P_UNLIKELY (rw_rdlock (&lock->hdl) == 0))
+		return TRUE;
+	else {
+		P_ERROR ("PRWLock::p_rwlock_reader_lock: rw_rdlock() failed");
+		return FALSE;
+	}
+}
+
+P_LIB_API pboolean
+p_rwlock_reader_trylock (PRWLock *lock)
+{
+	if (P_UNLIKELY (lock == NULL))
+		return FALSE;
+
+	return (rw_tryrdlock (&lock->hdl) == 0) ? TRUE : FALSE;
+}
+
+P_LIB_API pboolean
+p_rwlock_reader_unlock (PRWLock *lock)
+{
+	return pp_rwlock_unlock_any (lock);
+}
+
+P_LIB_API pboolean
+p_rwlock_writer_lock (PRWLock *lock)
+{
+	if (P_UNLIKELY (lock == NULL))
+		return FALSE;
+
+	if (P_UNLIKELY (rw_wrlock (&lock->hdl) == 0))
+		return TRUE;
+	else {
+		P_ERROR ("PRWLock::p_rwlock_writer_lock: rw_wrlock() failed");
+		return FALSE;
+	}
+}
+
+P_LIB_API pboolean
+p_rwlock_writer_trylock (PRWLock *lock)
+{
+	if (P_UNLIKELY (lock == NULL))
+		return FALSE;
+
+	return (rw_trywrlock (&lock->hdl) == 0) ? TRUE : FALSE;
+}
+
+P_LIB_API pboolean
+p_rwlock_writer_unlock (PRWLock *lock)
+{
+	return pp_rwlock_unlock_any (lock);
+}
+
+P_LIB_API void
+p_rwlock_free (PRWLock *lock)
+{
+	if (P_UNLIKELY (lock == NULL))
+		return;
+
+	if (P_UNLIKELY (rwlock_destroy (&lock->hdl) != 0))
+		P_ERROR ("PRWLock::p_rwlock_free: rwlock_destroy() failed");
+
+	p_free (lock);
+}
+
+void
+p_rwlock_init (void)
+{
+}
+
+void
+p_rwlock_shutdown (void)
+{
+}
diff --git a/3rdparty/plibsys/src/prwlock-win.c b/3rdparty/plibsys/src/prwlock-win.c
new file mode 100644
index 0000000..3064223
--- /dev/null
+++ b/3rdparty/plibsys/src/prwlock-win.c
@@ -0,0 +1,548 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/* More emulation variants: https://github.com/neosmart/RWLock */
+
+#include "pmem.h"
+#include "patomic.h"
+#include "puthread.h"
+#include "prwlock.h"
+
+#include <stdlib.h>
+
+#define P_RWLOCK_XP_MAX_SPIN 4000
+#define P_RWLOCK_XP_IS_CLEAR(lock) (((lock) & 0x40007FFF) == 0)
+#define P_RWLOCK_XP_IS_WRITER(lock) (((lock) & 0x40000000) != 0)
+#define P_RWLOCK_XP_SET_WRITER(lock) ((lock) | 0x40000000)
+#define P_RWLOCK_XP_UNSET_WRITER(lock) ((lock) & (~0x40000000))
+#define P_RWLOCK_XP_SET_READERS(lock, readers) (((lock) & (~0x00007FFF)) | (readers))
+#define P_RWLOCK_XP_READER_COUNT(lock) ((lock) & 0x00007FFF)
+#define P_RWLOCK_XP_SET_WAITING(lock, waiting) (((lock) & (~0x3FFF8000)) | ((waiting) << 15))
+#define P_RWLOCK_XP_WAITING_COUNT(lock) (((lock) & 0x3FFF8000) >> 15)
+
+typedef VOID    (WINAPI *InitializeSRWLockFunc)          (ppointer lock);
+typedef VOID    (WINAPI *AcquireSRWLockExclusiveFunc)    (ppointer lock);
+typedef BOOLEAN (WINAPI *TryAcquireSRWLockExclusiveFunc) (ppointer lock);
+typedef VOID    (WINAPI *ReleaseSRWLockExclusiveFunc)    (ppointer lock);
+typedef VOID    (WINAPI *AcquireSRWLockSharedFunc)       (ppointer lock);
+typedef BOOLEAN (WINAPI *TryAcquireSRWLockSharedFunc)    (ppointer lock);
+typedef VOID    (WINAPI *ReleaseSRWLockSharedFunc)       (ppointer lock);
+
+typedef pboolean (* PWin32LockInit)          (PRWLock *lock);
+typedef void     (* PWin32LockClose)         (PRWLock *lock);
+typedef pboolean (* PWin32LockStartRead)     (PRWLock *lock);
+typedef pboolean (* PWin32LockStartReadTry)  (PRWLock *lock);
+typedef pboolean (* PWin32LockEndRead)       (PRWLock *lock);
+typedef pboolean (* PWin32LockStartWrite)    (PRWLock *lock);
+typedef pboolean (* PWin32LockStartWriteTry) (PRWLock *lock);
+typedef pboolean (* PWin32LockEndWrite)      (PRWLock *lock);
+
+static PWin32LockInit          pp_rwlock_init_func            = NULL;
+static PWin32LockClose         pp_rwlock_close_func           = NULL;
+static PWin32LockStartRead     pp_rwlock_start_read_func      = NULL;
+static PWin32LockStartReadTry  pp_rwlock_start_read_try_func  = NULL;
+static PWin32LockEndRead       pp_rwlock_end_read_func        = NULL;
+static PWin32LockStartWrite    pp_rwlock_start_write_func     = NULL;
+static PWin32LockStartWriteTry pp_rwlock_start_write_try_func = NULL;
+static PWin32LockEndWrite      pp_rwlock_end_write_func       = NULL;
+
+typedef struct PRWLockVistaTable_ {
+	InitializeSRWLockFunc		rwl_init;
+	AcquireSRWLockExclusiveFunc	rwl_excl_lock;
+	TryAcquireSRWLockExclusiveFunc	rwl_excl_lock_try;
+	ReleaseSRWLockExclusiveFunc	rwl_excl_rel;
+	AcquireSRWLockSharedFunc	rwl_shr_lock;
+	TryAcquireSRWLockSharedFunc	rwl_shr_lock_try;
+	ReleaseSRWLockSharedFunc	rwl_shr_rel;
+} PRWLockVistaTable;
+
+typedef struct PRWLockXP_ {
+	volatile puint32	lock;
+	HANDLE			event;
+} PRWLockXP;
+
+struct PRWLock_ {
+	ppointer lock;
+};
+
+static PRWLockVistaTable pp_rwlock_vista_table = {NULL, NULL, NULL, NULL,
+						  NULL, NULL, NULL};
+
+/* SRWLock routines */
+static pboolean pp_rwlock_init_vista (PRWLock *lock);
+static void pp_rwlock_close_vista (PRWLock *lock);
+static pboolean pp_rwlock_start_read_vista (PRWLock *lock);
+static pboolean pp_rwlock_start_read_try_vista (PRWLock *lock);
+static pboolean pp_rwlock_end_read_vista (PRWLock *lock);
+static pboolean pp_rwlock_start_write_vista (PRWLock *lock);
+static pboolean pp_rwlock_start_write_try_vista (PRWLock *lock);
+static pboolean pp_rwlock_end_write_vista (PRWLock *lock);
+
+/* Windows XP emulation routines */
+static pboolean pp_rwlock_init_xp (PRWLock *lock);
+static void pp_rwlock_close_xp (PRWLock *lock);
+static pboolean pp_rwlock_start_read_xp (PRWLock *lock);
+static pboolean pp_rwlock_start_read_try_xp (PRWLock *lock);
+static pboolean pp_rwlock_end_read_xp (PRWLock *lock);
+static pboolean pp_rwlock_start_write_xp (PRWLock *lock);
+static pboolean pp_rwlock_start_write_try_xp (PRWLock *lock);
+static pboolean pp_rwlock_end_write_xp (PRWLock *lock);
+
+/* SRWLock routines */
+
+static pboolean
+pp_rwlock_init_vista (PRWLock *lock)
+{
+	pp_rwlock_vista_table.rwl_init (lock);
+
+	return TRUE;
+}
+
+static void
+pp_rwlock_close_vista (PRWLock *lock)
+{
+	P_UNUSED (lock);
+}
+
+static pboolean
+pp_rwlock_start_read_vista (PRWLock *lock)
+{
+	pp_rwlock_vista_table.rwl_shr_lock (lock);
+
+	return TRUE;
+}
+
+static pboolean
+pp_rwlock_start_read_try_vista (PRWLock *lock)
+{
+	return pp_rwlock_vista_table.rwl_shr_lock_try (lock) != 0 ? TRUE : FALSE;
+}
+
+static pboolean
+pp_rwlock_end_read_vista (PRWLock *lock)
+{
+	pp_rwlock_vista_table.rwl_shr_rel (lock);
+
+	return TRUE;
+}
+
+static pboolean
+pp_rwlock_start_write_vista (PRWLock *lock)
+{
+	pp_rwlock_vista_table.rwl_excl_lock (lock);
+
+	return TRUE;
+}
+
+static pboolean
+pp_rwlock_start_write_try_vista (PRWLock *lock)
+{
+	return pp_rwlock_vista_table.rwl_excl_lock_try (lock) != 0 ? TRUE : FALSE;
+}
+
+static pboolean
+pp_rwlock_end_write_vista (PRWLock *lock)
+{
+	pp_rwlock_vista_table.rwl_excl_rel (lock);
+
+	return TRUE;
+}
+
+/* Windows XP emulation routines */
+
+static pboolean
+pp_rwlock_init_xp (PRWLock *lock)
+{
+	PRWLockXP *rwl_xp;
+
+	if ((lock->lock = p_malloc0 (sizeof (PRWLockXP))) == NULL) {
+		P_ERROR ("PRWLock::pp_rwlock_init_xp: failed to allocate memory");
+		return FALSE;
+	}
+
+	rwl_xp = ((PRWLockXP *) lock->lock);
+
+	rwl_xp->lock  = 0;
+	rwl_xp->event = CreateEventA (NULL, FALSE, FALSE, NULL);
+
+	if (P_UNLIKELY (rwl_xp->event == NULL)) {
+		P_ERROR ("PRWLock::pp_rwlock_init_xp: CreateEventA() failed");
+		p_free (lock->lock);
+		lock->lock = NULL;
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+pp_rwlock_close_xp (PRWLock *lock)
+{
+	CloseHandle (((PRWLockXP *) lock->lock)->event);
+	p_free (lock->lock);
+}
+
+static pboolean
+pp_rwlock_start_read_xp (PRWLock *lock)
+{
+	PRWLockXP	*rwl_xp = ((PRWLockXP *) lock->lock);
+	int		i;
+	puint32		tmp_lock;
+	puint32		counter;
+
+	for (i = 0; ; ++i) {
+		tmp_lock = (puint32) p_atomic_int_get ((const volatile pint *) &rwl_xp->lock);
+
+		if (!P_RWLOCK_XP_IS_WRITER (tmp_lock)) {
+			counter = P_RWLOCK_XP_SET_READERS (tmp_lock, P_RWLOCK_XP_READER_COUNT (tmp_lock) + 1);
+
+			if (p_atomic_int_compare_and_exchange ((volatile pint *) &rwl_xp->lock,
+							       (pint) tmp_lock,
+							       (pint) counter) == TRUE)
+				return TRUE;
+			else
+				continue;
+		} else {
+			if (P_LIKELY (i < P_RWLOCK_XP_MAX_SPIN)) {
+				p_uthread_yield ();
+				continue;
+			}
+
+			counter = P_RWLOCK_XP_SET_WAITING (tmp_lock, P_RWLOCK_XP_WAITING_COUNT (tmp_lock) + 1);
+
+			if (p_atomic_int_compare_and_exchange ((volatile pint *) &rwl_xp->lock,
+							       (pint) tmp_lock,
+							       (pint) counter) != TRUE)
+				continue;
+
+			i = 0;
+
+			if (P_UNLIKELY (WaitForSingleObject (rwl_xp->event, INFINITE) != WAIT_OBJECT_0))
+				P_WARNING ("PRWLock::pp_rwlock_start_read_xp: WaitForSingleObject() failed, go ahead");
+
+			do {
+				tmp_lock = p_atomic_int_get ((const volatile pint *) &rwl_xp->lock);
+				counter  = P_RWLOCK_XP_SET_WAITING (tmp_lock, P_RWLOCK_XP_WAITING_COUNT (tmp_lock) - 1);
+			} while (p_atomic_int_compare_and_exchange ((volatile pint *) &rwl_xp->lock,
+								    (pint) tmp_lock,
+								    (pint) counter) != TRUE);
+		}
+	}
+
+	return TRUE;
+}
+
+static pboolean
+pp_rwlock_start_read_try_xp (PRWLock *lock)
+{
+	PRWLockXP	*rwl_xp = ((PRWLockXP *) lock->lock);
+	puint32		tmp_lock;
+	puint32		counter;
+
+	tmp_lock = (puint32) p_atomic_int_get ((const volatile pint *) &rwl_xp->lock);
+
+	if (P_RWLOCK_XP_IS_WRITER (tmp_lock))
+		return FALSE;
+
+	counter = P_RWLOCK_XP_SET_READERS (tmp_lock, P_RWLOCK_XP_READER_COUNT (tmp_lock) + 1);
+
+	return p_atomic_int_compare_and_exchange ((volatile pint *) &rwl_xp->lock,
+						  (pint) tmp_lock,
+						  (pint) counter);
+}
+
+static pboolean
+pp_rwlock_end_read_xp (PRWLock *lock)
+{
+	PRWLockXP	*rwl_xp = ((PRWLockXP *) lock->lock);
+	puint32		tmp_lock;
+	puint32		counter;
+
+	while (TRUE) {
+		tmp_lock = (puint32) p_atomic_int_get ((const volatile pint *) &rwl_xp->lock);
+		counter  = P_RWLOCK_XP_READER_COUNT (tmp_lock);
+
+		if (P_UNLIKELY (counter == 0))
+			return TRUE;
+
+		if (counter == 1 && P_RWLOCK_XP_WAITING_COUNT (tmp_lock) != 0) {
+			/* A duplicate wake up notification is possible */
+			if (P_UNLIKELY (SetEvent (rwl_xp->event) == 0))
+				P_WARNING ("PRWLock::pp_rwlock_end_read_xp: SetEvent() failed");
+		}
+
+		counter = P_RWLOCK_XP_SET_READERS (tmp_lock, counter - 1);
+
+		if (p_atomic_int_compare_and_exchange ((volatile pint *) &rwl_xp->lock,
+						       (pint) tmp_lock,
+						       (pint) counter) == TRUE)
+			break;
+	}
+
+	return TRUE;
+}
+
+static pboolean
+pp_rwlock_start_write_xp (PRWLock *lock)
+{
+	PRWLockXP	*rwl_xp = ((PRWLockXP *) lock->lock);
+	int		i;
+	puint32		tmp_lock;
+	puint32		counter;
+
+	for (i = 0; ; ++i) {
+		tmp_lock = (puint32) p_atomic_int_get ((const volatile pint *) &rwl_xp->lock);
+
+		if (P_RWLOCK_XP_IS_CLEAR (tmp_lock)) {
+			counter = P_RWLOCK_XP_SET_WRITER (tmp_lock);
+
+			if (p_atomic_int_compare_and_exchange ((volatile pint *) &rwl_xp->lock,
+							       (pint) tmp_lock,
+							       (pint) counter) == TRUE)
+				return TRUE;
+			else
+				continue;
+		} else {
+			if (P_LIKELY (i < P_RWLOCK_XP_MAX_SPIN)) {
+				p_uthread_yield ();
+				continue;
+			}
+
+			counter = P_RWLOCK_XP_SET_WAITING (tmp_lock, P_RWLOCK_XP_WAITING_COUNT (tmp_lock) + 1);
+
+			if (p_atomic_int_compare_and_exchange ((volatile pint *) &rwl_xp->lock,
+							       (pint) tmp_lock,
+							       (pint) counter) != TRUE)
+				continue;
+
+			i = 0;
+
+			if (P_UNLIKELY (WaitForSingleObject (rwl_xp->event, INFINITE) != WAIT_OBJECT_0))
+				P_WARNING ("PRWLock::pp_rwlock_start_write_xp: WaitForSingleObject() failed, go ahead");
+
+			do {
+				tmp_lock = p_atomic_int_get ((const volatile pint *) &rwl_xp->lock);
+				counter  = P_RWLOCK_XP_SET_WAITING (tmp_lock, P_RWLOCK_XP_WAITING_COUNT (tmp_lock) - 1);
+			} while (p_atomic_int_compare_and_exchange ((volatile pint *) &rwl_xp->lock,
+								    (pint) tmp_lock,
+								    (pint) counter) != TRUE);
+		}
+	}
+
+	return TRUE;
+}
+
+static pboolean
+pp_rwlock_start_write_try_xp (PRWLock *lock)
+{
+	PRWLockXP	*rwl_xp = ((PRWLockXP *) lock->lock);
+	puint32		tmp_lock;
+
+	tmp_lock = (puint32) p_atomic_int_get ((const volatile pint *) &rwl_xp->lock);
+
+	if (P_RWLOCK_XP_IS_CLEAR (tmp_lock)) {
+		return p_atomic_int_compare_and_exchange ((volatile pint *) &rwl_xp->lock,
+							  (pint) tmp_lock,
+							  (pint) P_RWLOCK_XP_SET_WRITER (tmp_lock));
+	}
+
+	return FALSE;
+}
+
+static pboolean
+pp_rwlock_end_write_xp (PRWLock *lock)
+{
+	PRWLockXP	*rwl_xp = ((PRWLockXP *) lock->lock);
+	puint32		tmp_lock;
+
+	while (TRUE) {
+		while (TRUE) {
+			tmp_lock = (puint32) p_atomic_int_get ((const volatile pint *) &rwl_xp->lock);
+
+			if (P_UNLIKELY (!P_RWLOCK_XP_IS_WRITER (tmp_lock)))
+				return TRUE;
+
+			if (P_RWLOCK_XP_WAITING_COUNT (tmp_lock) == 0)
+				break;
+
+			/* Only the one end-of-write call can be */
+			if (P_UNLIKELY (SetEvent (rwl_xp->event) == 0))
+				P_WARNING ("PRWLock::pp_rwlock_end_write_xp: SetEvent() failed");
+		}
+
+		if (p_atomic_int_compare_and_exchange ((volatile pint *) &rwl_xp->lock,
+						       (pint) tmp_lock,
+						       (pint) P_RWLOCK_XP_UNSET_WRITER (tmp_lock)) == TRUE)
+			break;
+	}
+
+	return TRUE;
+}
+
+P_LIB_API PRWLock *
+p_rwlock_new (void)
+{
+	PRWLock *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PRWLock))) == NULL)) {
+		P_ERROR ("PRWLock::p_rwlock_new: failed to allocate memory");
+		return NULL;
+	}
+
+	if (P_UNLIKELY (pp_rwlock_init_func (ret) != TRUE)) {
+		P_ERROR ("PRWLock::p_rwlock_new: failed to initialize");
+		p_free (ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_rwlock_reader_lock (PRWLock *lock)
+{
+	if (P_UNLIKELY (lock == NULL))
+		return FALSE;
+
+	return pp_rwlock_start_read_func (lock);
+}
+
+P_LIB_API pboolean
+p_rwlock_reader_trylock (PRWLock *lock)
+{
+	if (P_UNLIKELY (lock == NULL))
+		return FALSE;
+
+	return pp_rwlock_start_read_try_func (lock);
+}
+
+P_LIB_API pboolean
+p_rwlock_reader_unlock (PRWLock *lock)
+{
+	if (P_UNLIKELY (lock == NULL))
+		return FALSE;
+
+	return pp_rwlock_end_read_func (lock);
+}
+
+P_LIB_API pboolean
+p_rwlock_writer_lock (PRWLock *lock)
+{
+	if (P_UNLIKELY (lock == NULL))
+		return FALSE;
+
+	return pp_rwlock_start_write_func (lock);
+}
+
+P_LIB_API pboolean
+p_rwlock_writer_trylock (PRWLock *lock)
+{
+	if (P_UNLIKELY (lock == NULL))
+		return FALSE;
+
+	return pp_rwlock_start_write_try_func (lock);
+}
+
+P_LIB_API pboolean
+p_rwlock_writer_unlock (PRWLock *lock)
+{
+	if (P_UNLIKELY (lock == NULL))
+		return FALSE;
+
+	return pp_rwlock_end_write_func (lock);
+}
+
+P_LIB_API void
+p_rwlock_free (PRWLock *lock)
+{
+	if (P_UNLIKELY (lock == NULL))
+		return;
+
+	pp_rwlock_close_func (lock);
+	p_free (lock);
+}
+
+void
+p_rwlock_init (void)
+{
+	HMODULE hmodule;
+
+	hmodule = GetModuleHandleA ("kernel32.dll");
+
+	if (P_UNLIKELY (hmodule == NULL)) {
+		P_ERROR ("PRWLock::p_rwlock_init: failed to load kernel32.dll module");
+		return;
+	}
+
+	pp_rwlock_vista_table.rwl_init = (InitializeSRWLockFunc) GetProcAddress (hmodule,
+										 "InitializeSRWLock");
+
+	if (P_LIKELY (pp_rwlock_vista_table.rwl_init != NULL)) {
+		pp_rwlock_vista_table.rwl_excl_lock     = (AcquireSRWLockExclusiveFunc) GetProcAddress (hmodule,
+													"AcquireSRWLockExclusive");
+		pp_rwlock_vista_table.rwl_excl_lock_try = (TryAcquireSRWLockExclusiveFunc) GetProcAddress (hmodule,
+													   "TryAcquireSRWLockExclusive");
+		pp_rwlock_vista_table.rwl_excl_rel      = (ReleaseSRWLockExclusiveFunc) GetProcAddress (hmodule,
+													"ReleaseSRWLockExclusive");
+		pp_rwlock_vista_table.rwl_shr_lock      = (AcquireSRWLockSharedFunc) GetProcAddress (hmodule,
+												     "AcquireSRWLockShared");
+		pp_rwlock_vista_table.rwl_shr_lock_try  = (TryAcquireSRWLockSharedFunc) GetProcAddress (hmodule,
+													"TryAcquireSRWLockShared");
+		pp_rwlock_vista_table.rwl_shr_rel       = (ReleaseSRWLockSharedFunc) GetProcAddress (hmodule,
+												     "ReleaseSRWLockShared");
+		pp_rwlock_init_func            = pp_rwlock_init_vista;
+		pp_rwlock_close_func           = pp_rwlock_close_vista;
+		pp_rwlock_start_read_func      = pp_rwlock_start_read_vista;
+		pp_rwlock_start_read_try_func  = pp_rwlock_start_read_try_vista;
+		pp_rwlock_end_read_func        = pp_rwlock_end_read_vista;
+		pp_rwlock_start_write_func     = pp_rwlock_start_write_vista;
+		pp_rwlock_start_write_try_func = pp_rwlock_start_write_try_vista;
+		pp_rwlock_end_write_func       = pp_rwlock_end_write_vista;
+	} else {
+		pp_rwlock_init_func            = pp_rwlock_init_xp;
+		pp_rwlock_close_func           = pp_rwlock_close_xp;
+		pp_rwlock_start_read_func      = pp_rwlock_start_read_xp;
+		pp_rwlock_start_read_try_func  = pp_rwlock_start_read_try_xp;
+		pp_rwlock_end_read_func        = pp_rwlock_end_read_xp;
+		pp_rwlock_start_write_func     = pp_rwlock_start_write_xp;
+		pp_rwlock_start_write_try_func = pp_rwlock_start_write_try_xp;
+		pp_rwlock_end_write_func       = pp_rwlock_end_write_xp;
+	}
+}
+
+void
+p_rwlock_shutdown (void)
+{
+	memset (&pp_rwlock_vista_table, 0, sizeof (pp_rwlock_vista_table));
+
+	pp_rwlock_init_func            = NULL;
+	pp_rwlock_close_func           = NULL;
+	pp_rwlock_start_read_func      = NULL;
+	pp_rwlock_start_read_try_func  = NULL;
+	pp_rwlock_end_read_func        = NULL;
+	pp_rwlock_start_write_func     = NULL;
+	pp_rwlock_start_write_try_func = NULL;
+	pp_rwlock_end_write_func       = NULL;
+}
diff --git a/3rdparty/plibsys/src/prwlock.h b/3rdparty/plibsys/src/prwlock.h
new file mode 100644
index 0000000..9f61e4a
--- /dev/null
+++ b/3rdparty/plibsys/src/prwlock.h
@@ -0,0 +1,185 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file prwlock.h
+ * @brief Read-write lock
+ * @author Alexander Saprykin
+ *
+ * A read-write lock is a synchronization primitive which allows access to a
+ * critical section to not only the one thread, but instead it splits all
+ * threads into the two groups:
+ * - reader threads which perform only the reading operations without any shared
+ * data modifications;
+ * - writer threads which may perform the shared data modifitcations as well as
+ * its reading.
+ *
+ * When there are only the reader threads inside a critical section it is called
+ * a shared lock - actually you do not need any locking mechanism and all the
+ * threads share the lock. In this situation an arbitrary number of reader
+ * threads can perform shared data reading.
+ *
+ * The situation changes when a writer thread requests access to the same
+ * critical section. It will wait until all the current readers finish
+ * executing the critical section before acquiring the lock in exclusive manner:
+ * no one else can access the critical section until the writer finishes with
+ * it. Even another writer thread will have to wait for the lock to be released
+ * by the first writer before entering the critical section.
+ *
+ * To prevent writer startvation usually writers are in favor over readers,
+ * which is actually implementation dependent, though most operating systems try
+ * to follow this rule.
+ *
+ * A read-write lock is usually used when the writing operations are not
+ * performed too frequently, or when the number of reader threads is a way more
+ * than writer ones.
+ *
+ * A reader enters a critical section with p_rwlock_reader_lock() or
+ * p_rwlock_reader_trylock() and exits with p_rwlock_reader_unlock().
+ *
+ * A writer enters the critical section with p_rwlock_writer_lock() or
+ * p_rwlock_writer_trylock() and exits with p_rwlock_writer_unlock().
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PRWLOCK_H
+#define PLIBSYS_HEADER_PRWLOCK_H
+
+#include <pmacros.h>
+#include <ptypes.h>
+
+P_BEGIN_DECLS
+
+/** Read-write lock opaque data structure. */
+typedef struct PRWLock_ PRWLock;
+
+/**
+ * @brief Creates a new #PRWLock object.
+ * @return Pointer to a newly created #PRWLock object.
+ * @since 0.0.1
+ */
+P_LIB_API PRWLock *	p_rwlock_new		(void);
+
+/**
+ * @brief Locks a read-write lock for reading.
+ * @param lock #PRWLock to lock.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ * @warning Do not lock a read-write lock recursively - it may lead to an
+ * application deadlock (implementation dependent).
+ *
+ * Forces the calling thread to sleep until the @a lock becomes available for
+ * locking.
+ */
+P_LIB_API pboolean	p_rwlock_reader_lock	(PRWLock *lock);
+
+/**
+ * @brief Tries to lock a read-write lock for reading immediately.
+ * @param lock #PRWLock to lock.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ * @warning Do not lock a read-write lock recursively - it may lead to an
+ * application deadlock (implementation dependent).
+ *
+ * Tries to lock the @a lock and returns immediately if it is not available for
+ * locking.
+ */
+P_LIB_API pboolean	p_rwlock_reader_trylock	(PRWLock *lock);
+
+/**
+ * @brief Releases a locked for reading read-write lock.
+ * @param lock #PRWLock to release.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ * @warning Do not use this function on non-locked read-write locks - behavior
+ * may be unpredictable.
+ * @warning Do not use this function to unlock a read-write lock which was
+ * locked for writing.
+ *
+ * If the @a lock was previously locked for reading then it becomes unlocked.
+ *
+ * It's implementation dependent whether only the same thread can lock and
+ * unlock the same read-write lock.
+ */
+P_LIB_API pboolean	p_rwlock_reader_unlock	(PRWLock *lock);
+
+/**
+ * @brief Locks a read-write lock for writing.
+ * @param lock #PRWLock to lock.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ * @warning Do not lock a read-write lock recursively - it may lead to an
+ * application deadlock (implementation dependent).
+ *
+ * Forces the calling thread to sleep until the @a lock becomes available for
+ * locking.
+ */
+P_LIB_API pboolean	p_rwlock_writer_lock	(PRWLock *lock);
+
+/**
+ * @brief Tries to lock a read-write lock immediately.
+ * @param lock #PRWLock to lock.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ * @warning Do not lock a read-write lock recursively - it may lead to an
+ * application deadlock (implementation dependent).
+ *
+ * Tries to lock the @a lock and returns immediately if it is not available for
+ * locking.
+ */
+P_LIB_API pboolean	p_rwlock_writer_trylock	(PRWLock *lock);
+
+/**
+ * @brief Releases a locked for writing read-write lock.
+ * @param lock #PRWLock to release.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ * @warning Do not use this function on non-locked read-write locks - behavior
+ * may be unpredictable.
+ * @warning Do not use this function to unlock a read-write lock which was
+ * locked for reading.
+ *
+ * If the @a lock was previously locked for writing then it becomes unlocked.
+ *
+ * It's implementation dependent whether only the same thread can lock and
+ * unlock the same read-write lock.
+ */
+P_LIB_API pboolean	p_rwlock_writer_unlock	(PRWLock *lock);
+
+/**
+ * @brief Frees a #PRWLock object.
+ * @param lock #PRWLock to free.
+ * @since 0.0.1
+ * @warning It doesn't unlock the @a lock before freeing memory, so you should
+ * do it manually.
+ */
+P_LIB_API void		p_rwlock_free		(PRWLock *lock);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PRWLOCK_H */
diff --git a/3rdparty/plibsys/src/psemaphore-amiga.c b/3rdparty/plibsys/src/psemaphore-amiga.c
new file mode 100644
index 0000000..bff0acf
--- /dev/null
+++ b/3rdparty/plibsys/src/psemaphore-amiga.c
@@ -0,0 +1,250 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2017-2018 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "perror.h"
+#include "pmem.h"
+#include "psemaphore.h"
+#include "pipc-private.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <exec/types.h>
+#include <exec/semaphores.h>
+#include <proto/exec.h>
+
+#define P_SEM_SUFFIX	"_p_sem_object"
+#define P_SEM_PRIV_SIZE	(sizeof (psize))
+
+struct PSemaphore_ {
+	struct SignalSemaphore	*sem_shared;
+	pchar			*platform_key;
+	pboolean		is_owner;
+	PSemaphoreAccessMode	mode;
+};
+
+static pboolean pp_semaphore_create_handle (PSemaphore *sem, PError **error);
+
+static pboolean
+pp_semaphore_create_handle (PSemaphore	*sem,
+			    PError	**error)
+{
+	struct SignalSemaphore	*sem_sys;
+	psize			name_len;
+	pchar			*name;
+
+	if (P_UNLIKELY (sem == NULL || sem->platform_key == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	IExec->Forbid ();
+
+	sem_sys = (struct SignalSemaphore *) IExec->FindSemaphore (sem->platform_key);
+
+	if (sem_sys != NULL && sem->mode != P_SEM_ACCESS_CREATE) {
+		sem->sem_shared = sem_sys;
+	} else {
+		if (sem_sys != NULL && sem->mode == P_SEM_ACCESS_CREATE) {
+			IExec->RemSemaphore (sem_sys);
+			IExec->ObtainSemaphore (sem_sys);
+			IExec->ReleaseSemaphore (sem_sys);
+
+			IExec->FreeVec (sem_sys->ss_Link.ln_Name);
+			IExec->FreeVec (((psize *) sem_sys) - 1);
+		}
+
+		sem_sys = (struct SignalSemaphore *) IExec->AllocVecTags (sizeof (struct SignalSemaphore) + P_SEM_PRIV_SIZE,
+									  AVT_Type, MEMF_SHARED,
+									  AVT_ClearWithValue, 0,
+									  TAG_END);
+
+		if (P_UNLIKELY (sem_sys == NULL)) {
+			IExec->Permit ();
+			p_error_set_error_p (error,
+					     (pint) P_ERROR_IPC_NO_RESOURCES,
+					     0,
+					     "Failed to call AllocMem() to create semaphore");
+			return FALSE;
+		}
+
+		name_len = strlen (sem->platform_key);
+		name     = (pchar *) IExec->AllocVecTags (name_len + 1,
+							  AVT_ClearWithValue, 0,
+							  TAG_END);
+
+		if (P_UNLIKELY (name == NULL)) {
+			IExec->FreeVec (sem_sys);
+			IExec->Permit ();
+			p_error_set_error_p (error,
+					     (pint) P_ERROR_IPC_NO_RESOURCES,
+					     0,
+					     "Failed to call AllocMem() to create semaphore name");
+			return FALSE;
+		}
+
+		memcpy (name, sem->platform_key, name_len);
+
+		/* Leave space in memory for counter */
+		sem_sys = (struct SignalSemaphore *) (((psize *) sem_sys) + 1);
+
+		sem_sys->ss_Link.ln_Name = name;
+		sem->sem_shared = sem_sys;
+
+		/* Add to system list */
+		IExec->AddSemaphore (sem_sys);
+	}
+
+	*(((psize *) sem_sys) - 1) += 1;
+
+	IExec->Permit ();
+
+	return TRUE;
+}
+
+P_LIB_API PSemaphore *
+p_semaphore_new (const pchar		*name,
+		 pint			init_val,
+		 PSemaphoreAccessMode	mode,
+		 PError			**error)
+{
+	PSemaphore	*ret;
+	pchar		*new_name;
+
+	if (P_UNLIKELY (name == NULL || init_val < 0)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSemaphore))) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for semaphore");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((new_name = p_malloc0 (strlen (name) + strlen (P_SEM_SUFFIX) + 1)) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for semaphore");
+		p_free (ret);
+		return NULL;
+	}
+
+	strcpy (new_name, name);
+	strcat (new_name, P_SEM_SUFFIX);
+
+	ret->platform_key = p_ipc_get_platform_key (new_name, FALSE);
+	ret->mode         = mode;
+
+	p_free (new_name);
+
+	if (P_UNLIKELY (pp_semaphore_create_handle (ret, error) == FALSE)) {
+		p_semaphore_free (ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+P_LIB_API void
+p_semaphore_take_ownership (PSemaphore *sem)
+{
+	if (P_UNLIKELY (sem == NULL))
+		return;
+
+	sem->is_owner = TRUE;
+}
+
+P_LIB_API pboolean
+p_semaphore_acquire (PSemaphore	*sem,
+		     PError	**error)
+{
+	if (P_UNLIKELY (sem == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	IExec->ObtainSemaphore (sem->sem_shared);
+
+	return TRUE;
+}
+
+P_LIB_API pboolean
+p_semaphore_release (PSemaphore	*sem,
+		     PError	**error)
+{
+	if (P_UNLIKELY (sem == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	IExec->ReleaseSemaphore (sem->sem_shared);
+
+	return TRUE;
+}
+
+P_LIB_API void
+p_semaphore_free (PSemaphore *sem)
+{
+	if (P_UNLIKELY (sem == NULL))
+		return;
+
+	if (P_UNLIKELY (sem->sem_shared != NULL)) {
+		IExec->Forbid ();
+
+		*(((psize *) sem->sem_shared) - 1) -= 1;
+
+		if (*(((psize *) sem->sem_shared) - 1) == 0 || sem->is_owner == TRUE) {
+			IExec->RemSemaphore (sem->sem_shared);
+			IExec->ObtainSemaphore (sem->sem_shared);
+			IExec->ReleaseSemaphore (sem->sem_shared);
+
+			IExec->FreeVec (sem->sem_shared->ss_Link.ln_Name);
+			IExec->FreeVec (((psize *) sem->sem_shared) - 1);
+		}
+
+		IExec->Permit ();
+	}
+
+	if (P_LIKELY (sem->platform_key != NULL))
+		p_free (sem->platform_key);
+
+	p_free (sem);
+}
diff --git a/3rdparty/plibsys/src/psemaphore-none.c b/3rdparty/plibsys/src/psemaphore-none.c
new file mode 100644
index 0000000..8744123
--- /dev/null
+++ b/3rdparty/plibsys/src/psemaphore-none.c
@@ -0,0 +1,91 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2018 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "psemaphore.h"
+
+#include <stdlib.h>
+
+struct PSemaphore_ {
+	pint	hdl;
+};
+
+P_LIB_API PSemaphore *
+p_semaphore_new (const pchar		*name,
+		 pint			init_val,
+		 PSemaphoreAccessMode	mode,
+		 PError			**error)
+{
+	P_UNUSED (name);
+	P_UNUSED (init_val);
+	P_UNUSED (mode);
+
+	p_error_set_error_p (error,
+			     (pint) P_ERROR_IPC_NOT_IMPLEMENTED,
+			     0,
+			     "No semaphore implementation");
+
+	return NULL;
+}
+
+P_LIB_API void
+p_semaphore_take_ownership (PSemaphore *sem)
+{
+	P_UNUSED (sem);
+}
+
+P_LIB_API pboolean
+p_semaphore_acquire (PSemaphore	*sem,
+		     PError	**error)
+{
+	P_UNUSED (sem);
+
+	p_error_set_error_p (error,
+			     (pint) P_ERROR_IPC_NOT_IMPLEMENTED,
+			     0,
+			     "No semaphore implementation");
+
+	return FALSE;
+}
+
+P_LIB_API pboolean
+p_semaphore_release (PSemaphore	*sem,
+		     PError	**error)
+{
+	P_UNUSED (sem);
+
+	p_error_set_error_p (error,
+			     (pint) P_ERROR_IPC_NOT_IMPLEMENTED,
+			     0,
+			     "No semaphore implementation");
+
+	return FALSE;
+}
+
+P_LIB_API void
+p_semaphore_free (PSemaphore *sem)
+{
+	P_UNUSED (sem);
+}
+
diff --git a/3rdparty/plibsys/src/psemaphore-os2.c b/3rdparty/plibsys/src/psemaphore-os2.c
new file mode 100644
index 0000000..72cc7d3
--- /dev/null
+++ b/3rdparty/plibsys/src/psemaphore-os2.c
@@ -0,0 +1,91 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2017-2018 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "psemaphore.h"
+
+#include <stdlib.h>
+
+struct PSemaphore_ {
+	pint	hdl;
+};
+
+P_LIB_API PSemaphore *
+p_semaphore_new (const pchar		*name,
+		 pint			init_val,
+		 PSemaphoreAccessMode	mode,
+		 PError			**error)
+{
+	P_UNUSED (name);
+	P_UNUSED (init_val);
+	P_UNUSED (mode);
+
+	p_error_set_error_p (error,
+			     (pint) P_ERROR_IPC_NOT_IMPLEMENTED,
+			     0,
+			     "No semaphore implementation");
+
+	return NULL;
+}
+
+P_LIB_API void
+p_semaphore_take_ownership (PSemaphore *sem)
+{
+	P_UNUSED (sem);
+}
+
+P_LIB_API pboolean
+p_semaphore_acquire (PSemaphore	*sem,
+		     PError	**error)
+{
+	P_UNUSED (sem);
+
+	p_error_set_error_p (error,
+			     (pint) P_ERROR_IPC_NOT_IMPLEMENTED,
+			     0,
+			     "No semaphore implementation");
+
+	return FALSE;
+}
+
+P_LIB_API pboolean
+p_semaphore_release (PSemaphore	*sem,
+		     PError	**error)
+{
+	P_UNUSED (sem);
+
+	p_error_set_error_p (error,
+			     (pint) P_ERROR_IPC_NOT_IMPLEMENTED,
+			     0,
+			     "No semaphore implementation");
+
+	return FALSE;
+}
+
+P_LIB_API void
+p_semaphore_free (PSemaphore *sem)
+{
+	P_UNUSED (sem);
+}
+
diff --git a/3rdparty/plibsys/src/psemaphore-posix.c b/3rdparty/plibsys/src/psemaphore-posix.c
new file mode 100644
index 0000000..f98a045
--- /dev/null
+++ b/3rdparty/plibsys/src/psemaphore-posix.c
@@ -0,0 +1,272 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2018 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "perror.h"
+#include "pmem.h"
+#include "psemaphore.h"
+#include "perror-private.h"
+#include "pipc-private.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <semaphore.h>
+#include <errno.h>
+
+#define P_SEM_SUFFIX		"_p_sem_object"
+
+typedef sem_t psem_hdl;
+
+/* On some HP-UX versions it may not be defined */
+#ifndef SEM_FAILED
+#  define SEM_FAILED ((sem_t *) -1)
+#endif
+
+#ifdef P_OS_SOLARIS
+#  define P_SEM_INVALID_HDL	(sem_t *) -1
+#else
+#  define P_SEM_INVALID_HDL	SEM_FAILED
+#endif
+
+struct PSemaphore_ {
+	pboolean		sem_created;
+	pchar			*platform_key;
+#if defined (P_OS_VMS) && (PLIBSYS_SIZEOF_VOID_P == 4)
+#  pragma __pointer_size 64
+#endif
+	psem_hdl		*sem_hdl;
+#if defined (P_OS_VMS) && (PLIBSYS_SIZEOF_VOID_P == 4)
+#  pragma __pointer_size 32
+#endif
+	PSemaphoreAccessMode	mode;
+	pint			init_val;
+};
+
+static pboolean pp_semaphore_create_handle (PSemaphore *sem, PError **error);
+static void pp_semaphore_clean_handle (PSemaphore *sem);
+
+static pboolean
+pp_semaphore_create_handle (PSemaphore	*sem,
+			    PError	**error)
+{
+	pint init_val;
+
+	if (P_UNLIKELY (sem == NULL || sem->platform_key == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	init_val = sem->init_val;
+
+	/* Solaris may interrupt sem_open() call */
+	while ((sem->sem_hdl = sem_open (sem->platform_key,
+					 O_CREAT | O_EXCL,
+					 0660,
+					 init_val)) == P_SEM_INVALID_HDL &&
+		p_error_get_last_system () == EINTR)
+	;
+
+	if (sem->sem_hdl == P_SEM_INVALID_HDL) {
+		if (p_error_get_last_system () == EEXIST) {
+			if (sem->mode == P_SEM_ACCESS_CREATE)
+				sem_unlink (sem->platform_key);
+			else
+				init_val = 0;
+
+			while ((sem->sem_hdl = sem_open (sem->platform_key,
+							 0,
+							 0,
+							 init_val)) == P_SEM_INVALID_HDL &&
+				p_error_get_last_system () == EINTR)
+			;
+		}
+	} else
+		sem->sem_created = TRUE;
+
+	if (P_UNLIKELY (sem->sem_hdl == P_SEM_INVALID_HDL)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_ipc (),
+				     p_error_get_last_system (),
+				     "Failed to call sem_open() to create semaphore");
+		pp_semaphore_clean_handle (sem);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+pp_semaphore_clean_handle (PSemaphore *sem)
+{
+	if (P_UNLIKELY (sem->sem_hdl != P_SEM_INVALID_HDL &&
+			sem_close (sem->sem_hdl) == -1))
+		P_ERROR ("PSemaphore::pp_semaphore_clean_handle: sem_close() failed");
+
+	if (sem->sem_hdl != P_SEM_INVALID_HDL &&
+	    sem->sem_created == TRUE &&
+	    sem_unlink (sem->platform_key) == -1)
+		P_ERROR ("PSemaphore::pp_semaphore_clean_handle: sem_unlink() failed");
+
+	sem->sem_created = FALSE;
+	sem->sem_hdl = P_SEM_INVALID_HDL;
+}
+
+P_LIB_API PSemaphore *
+p_semaphore_new (const pchar		*name,
+		 pint			init_val,
+		 PSemaphoreAccessMode	mode,
+		 PError			**error)
+{
+	PSemaphore	*ret;
+	pchar		*new_name;
+
+	if (P_UNLIKELY (name == NULL || init_val < 0)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSemaphore))) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for semaphore");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((new_name = p_malloc0 (strlen (name) + strlen (P_SEM_SUFFIX) + 1)) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for semaphore");
+		p_free (ret);
+		return NULL;
+	}
+
+	strcpy (new_name, name);
+	strcat (new_name, P_SEM_SUFFIX);
+
+#if defined (P_OS_IRIX) || defined (P_OS_TRU64)
+	/* IRIX and Tru64 prefer filename styled IPC names */
+	ret->platform_key = p_ipc_get_platform_key (new_name, FALSE);
+#else
+	ret->platform_key = p_ipc_get_platform_key (new_name, TRUE);
+#endif
+	ret->init_val = init_val;
+	ret->mode = mode;
+
+	p_free (new_name);
+
+	if (P_UNLIKELY (pp_semaphore_create_handle (ret, error) == FALSE)) {
+		p_semaphore_free (ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+P_LIB_API void
+p_semaphore_take_ownership (PSemaphore *sem)
+{
+	if (P_UNLIKELY (sem == NULL))
+		return;
+
+	sem->sem_created = TRUE;
+}
+
+P_LIB_API pboolean
+p_semaphore_acquire (PSemaphore	*sem,
+		     PError	**error)
+{
+	pboolean	ret;
+	pint		res;
+
+	if (P_UNLIKELY (sem == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	while ((res = sem_wait (sem->sem_hdl)) == -1 && p_error_get_last_system () == EINTR)
+		;
+
+	ret = (res == 0);
+
+	if (P_UNLIKELY (ret == FALSE))
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_ipc (),
+				     p_error_get_last_system (),
+				     "Failed to call sem_wait() on semaphore");
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_semaphore_release (PSemaphore	*sem,
+		     PError	**error)
+{
+	pboolean ret;
+
+	if (P_UNLIKELY (sem == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	ret = (sem_post (sem->sem_hdl) == 0);
+
+	if (P_UNLIKELY (ret == FALSE))
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_ipc (),
+				     p_error_get_last_system (),
+				     "Failed to call sem_post() on semaphore");
+
+	return ret;
+}
+
+P_LIB_API void
+p_semaphore_free (PSemaphore *sem)
+{
+	if (P_UNLIKELY (sem == NULL))
+		return;
+
+	pp_semaphore_clean_handle (sem);
+
+	if (P_LIKELY (sem->platform_key != NULL))
+		p_free (sem->platform_key);
+
+	p_free (sem);
+}
diff --git a/3rdparty/plibsys/src/psemaphore-sysv.c b/3rdparty/plibsys/src/psemaphore-sysv.c
new file mode 100644
index 0000000..34b33fc
--- /dev/null
+++ b/3rdparty/plibsys/src/psemaphore-sysv.c
@@ -0,0 +1,319 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2018 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "perror.h"
+#include "pmem.h"
+#include "psemaphore.h"
+#include "perror-private.h"
+#include "pipc-private.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/sem.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+
+#define P_SEM_SUFFIX		"_p_sem_object"
+#define P_SEM_INVALID_HDL	-1
+
+struct sembuf sem_lock = {0, -1, SEM_UNDO};
+struct sembuf sem_unlock = {0, 1, SEM_UNDO};
+
+typedef union p_semun_ {
+	pint		val;
+	struct semid_ds	*buf;
+	pushort		*array;
+} p_semun;
+
+typedef int psem_hdl;
+
+struct PSemaphore_ {
+	pboolean		file_created;
+	pboolean		sem_created;
+	key_t			unix_key;
+	pchar			*platform_key;
+	psem_hdl		sem_hdl;
+	PSemaphoreAccessMode	mode;
+	pint			init_val;
+};
+
+static pboolean pp_semaphore_create_handle (PSemaphore *sem, PError **error);
+static void pp_semaphore_clean_handle (PSemaphore *sem);
+
+static pboolean
+pp_semaphore_create_handle (PSemaphore *sem, PError **error)
+{
+	pint	built;
+	p_semun	semun_op;
+
+	if (P_UNLIKELY (sem == NULL || sem->platform_key == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	if (P_UNLIKELY ((built = p_ipc_unix_create_key_file (sem->platform_key)) == -1)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_ipc (),
+				     p_error_get_last_system (),
+				     "Failed to create key file");
+		pp_semaphore_clean_handle (sem);
+		return FALSE;
+	} else if (built == 0)
+		sem->file_created = TRUE;
+
+	if (P_UNLIKELY ((sem->unix_key = p_ipc_unix_get_ftok_key (sem->platform_key)) == -1)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_ipc (),
+				     p_error_get_last_system (),
+				     "Failed to get unique IPC key");
+		pp_semaphore_clean_handle (sem);
+		return FALSE;
+	}
+
+	if ((sem->sem_hdl = semget (sem->unix_key,
+				    1,
+				    IPC_CREAT | IPC_EXCL | 0660)) == P_SEM_INVALID_HDL) {
+		if (p_error_get_last_system () == EEXIST)
+			sem->sem_hdl = semget (sem->unix_key, 1, 0660);
+	} else {
+		sem->sem_created = TRUE;
+
+		/* Maybe key file left after the crash, so take it */
+		sem->file_created = (built == 1);
+	}
+
+	if (P_UNLIKELY (sem->sem_hdl == P_SEM_INVALID_HDL)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_ipc (),
+				     p_error_get_last_system (),
+				     "Failed to call semget() to create semaphore");
+		pp_semaphore_clean_handle (sem);
+		return FALSE;
+	}
+
+	if (sem->sem_created == TRUE || sem->mode == P_SEM_ACCESS_CREATE) {
+		semun_op.val = sem->init_val;
+
+		if (P_UNLIKELY (semctl (sem->sem_hdl, 0, SETVAL, semun_op) == -1)) {
+			p_error_set_error_p (error,
+					     (pint) p_error_get_last_ipc (),
+					     p_error_get_last_system (),
+					     "Failed to set semaphore initial value with semctl()");
+			pp_semaphore_clean_handle (sem);
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+static void
+pp_semaphore_clean_handle (PSemaphore *sem)
+{
+	if (sem->sem_hdl != P_SEM_INVALID_HDL &&
+	    sem->sem_created == TRUE &&
+	    semctl (sem->sem_hdl, 0, IPC_RMID) == -1)
+		P_ERROR ("PSemaphore::pp_semaphore_clean_handle: semctl() with IPC_RMID failed");
+
+	if (sem->file_created == TRUE &&
+	    sem->platform_key != NULL &&
+	    unlink (sem->platform_key) == -1)
+		P_ERROR ("PSemaphore::pp_semaphore_clean_handle: unlink() failed");
+
+	sem->file_created = FALSE;
+	sem->sem_created  = FALSE;
+	sem->unix_key     = -1;
+	sem->sem_hdl      = P_SEM_INVALID_HDL;
+}
+
+P_LIB_API PSemaphore *
+p_semaphore_new (const pchar		*name,
+		 pint			init_val,
+		 PSemaphoreAccessMode	mode,
+		 PError			**error)
+{
+	PSemaphore	*ret;
+	pchar		*new_name;
+
+	if (P_UNLIKELY (name == NULL || init_val < 0)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSemaphore))) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for semaphore");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((new_name = p_malloc0 (strlen (name) + strlen (P_SEM_SUFFIX) + 1)) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for semaphore");
+		p_free (ret);
+		return NULL;
+	}
+
+	strcpy (new_name, name);
+	strcat (new_name, P_SEM_SUFFIX);
+
+	ret->platform_key = p_ipc_get_platform_key (new_name, FALSE);
+	ret->init_val     = init_val;
+	ret->mode         = mode;
+
+	p_free (new_name);
+
+	if (P_UNLIKELY (pp_semaphore_create_handle (ret, error) == FALSE)) {
+		p_semaphore_free (ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+P_LIB_API void
+p_semaphore_take_ownership (PSemaphore *sem)
+{
+	if (P_UNLIKELY (sem == NULL))
+		return;
+
+	sem->sem_created = TRUE;
+}
+
+P_LIB_API pboolean
+p_semaphore_acquire (PSemaphore	*sem,
+		     PError	**error)
+{
+	pboolean	ret;
+	pint		res;
+
+	if (P_UNLIKELY (sem == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	while ((res = semop (sem->sem_hdl, &sem_lock, 1)) == -1 &&
+		p_error_get_last_system () == EINTR)
+		;
+
+	ret = (res == 0);
+
+	if (P_UNLIKELY (ret == FALSE &&
+			(p_error_get_last_system () == EIDRM ||
+			 p_error_get_last_system () == EINVAL))) {
+		P_WARNING ("PSemaphore::p_semaphore_acquire: trying to recreate");
+		pp_semaphore_clean_handle (sem);
+
+		if (P_UNLIKELY (pp_semaphore_create_handle (sem, error) == FALSE))
+			return FALSE;
+
+		while ((res = semop (sem->sem_hdl, &sem_lock, 1)) == -1 &&
+			p_error_get_last_system () == EINTR)
+			;
+
+		ret = (res == 0);
+	}
+
+	if (P_UNLIKELY (ret == FALSE))
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_ipc (),
+				     p_error_get_last_system (),
+				     "Failed to call semop() on semaphore");
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_semaphore_release (PSemaphore	*sem,
+		     PError	**error)
+{
+	pboolean	ret;
+	pint		res;
+
+	if (P_UNLIKELY (sem == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	while ((res = semop (sem->sem_hdl, &sem_unlock, 1)) == -1 &&
+		p_error_get_last_system () == EINTR)
+		;
+
+	ret = (res == 0);
+
+	if (P_UNLIKELY (ret == FALSE &&
+			(p_error_get_last_system () == EIDRM ||
+			 p_error_get_last_system () == EINVAL))) {
+		P_WARNING ("PSemaphore::p_semaphore_release: trying to recreate");
+		pp_semaphore_clean_handle (sem);
+
+		if (P_UNLIKELY (pp_semaphore_create_handle (sem, error) == FALSE))
+			return FALSE;
+
+		return TRUE;
+	}
+
+	if (P_UNLIKELY (ret == FALSE))
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_ipc (),
+				     p_error_get_last_system (),
+				     "Failed to call semop() on semaphore");
+
+	return ret;
+}
+
+P_LIB_API void
+p_semaphore_free (PSemaphore *sem)
+{
+	if (P_UNLIKELY (sem == NULL))
+		return;
+
+	pp_semaphore_clean_handle (sem);
+
+	if (P_LIKELY (sem->platform_key != NULL))
+		p_free (sem->platform_key);
+
+	p_free (sem);
+}
diff --git a/3rdparty/plibsys/src/psemaphore-win.c b/3rdparty/plibsys/src/psemaphore-win.c
new file mode 100644
index 0000000..228c054
--- /dev/null
+++ b/3rdparty/plibsys/src/psemaphore-win.c
@@ -0,0 +1,204 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2018 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "perror.h"
+#include "pmem.h"
+#include "psemaphore.h"
+#include "perror-private.h"
+#include "pipc-private.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#define P_SEM_SUFFIX		"_p_sem_object"
+#define P_SEM_INVALID_HDL	NULL
+
+typedef HANDLE psem_hdl;
+
+struct PSemaphore_ {
+	pchar		*platform_key;
+	psem_hdl	sem_hdl;
+	pint		init_val;
+};
+
+static pboolean pp_semaphore_create_handle (PSemaphore *sem, PError **error);
+static void pp_semaphore_clean_handle (PSemaphore *sem);
+
+static pboolean
+pp_semaphore_create_handle (PSemaphore *sem, PError **error)
+{
+	if (P_UNLIKELY (sem == NULL || sem->platform_key == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	/* Multibyte character set must be enabled */
+	if (P_UNLIKELY ((sem->sem_hdl = CreateSemaphoreA (NULL,
+							  sem->init_val,
+							  MAXLONG,
+							  sem->platform_key)) == P_SEM_INVALID_HDL)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_ipc (),
+				     p_error_get_last_system (),
+				     "Failed to call CreateSemaphore() to create semaphore");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+pp_semaphore_clean_handle (PSemaphore *sem)
+{
+	if (P_UNLIKELY (sem->sem_hdl != P_SEM_INVALID_HDL && CloseHandle (sem->sem_hdl) == 0))
+		P_ERROR ("PSemaphore::pp_semaphore_clean_handle: CloseHandle() failed");
+
+	sem->sem_hdl = P_SEM_INVALID_HDL;
+}
+
+P_LIB_API PSemaphore *
+p_semaphore_new (const pchar		*name,
+		 pint			init_val,
+		 PSemaphoreAccessMode	mode,
+		 PError			**error)
+{
+	PSemaphore	*ret;
+	pchar		*new_name;
+
+	P_UNUSED (mode);
+
+	if (P_UNLIKELY (name == NULL || init_val < 0)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSemaphore))) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for semaphore");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((new_name = p_malloc0 (strlen (name) + strlen (P_SEM_SUFFIX) + 1)) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for semaphore");
+		p_free (ret);
+		return NULL;
+	}
+
+	strcpy (new_name, name);
+	strcpy (new_name, P_SEM_SUFFIX);
+
+	ret->platform_key = p_ipc_get_platform_key (new_name, FALSE);
+	ret->init_val = init_val;
+
+	p_free (new_name);
+
+	if (P_UNLIKELY (pp_semaphore_create_handle (ret, error) == FALSE)) {
+		p_semaphore_free (ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+P_LIB_API void
+p_semaphore_take_ownership (PSemaphore *sem)
+{
+	P_UNUSED (sem);
+}
+
+P_LIB_API pboolean
+p_semaphore_acquire (PSemaphore	*sem,
+		     PError	**error)
+{
+	pboolean ret;
+
+	if (P_UNLIKELY (sem == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	ret = (WaitForSingleObject (sem->sem_hdl, INFINITE) == WAIT_OBJECT_0) ? TRUE : FALSE;
+
+	if (P_UNLIKELY (ret == FALSE))
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_ipc (),
+				     p_error_get_last_system (),
+				     "Failed to call WaitForSingleObject() on semaphore");
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_semaphore_release (PSemaphore	*sem,
+		     PError	**error)
+{
+	pboolean ret;
+
+	if (P_UNLIKELY (sem == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	ret = ReleaseSemaphore (sem->sem_hdl, 1, NULL) ? TRUE : FALSE;
+
+	if (P_UNLIKELY (ret == FALSE))
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_ipc (),
+				     p_error_get_last_system (),
+				     "Failed to call ReleaseSemaphore() on semaphore");
+
+	return ret;
+}
+
+P_LIB_API void
+p_semaphore_free (PSemaphore *sem)
+{
+	if (P_UNLIKELY (sem == NULL))
+		return;
+
+	pp_semaphore_clean_handle (sem);
+
+	if (P_LIKELY (sem->platform_key != NULL))
+		p_free (sem->platform_key);
+
+	p_free (sem);
+}
diff --git a/3rdparty/plibsys/src/psemaphore.h b/3rdparty/plibsys/src/psemaphore.h
new file mode 100644
index 0000000..29fb48e
--- /dev/null
+++ b/3rdparty/plibsys/src/psemaphore.h
@@ -0,0 +1,191 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file psemaphore.h
+ * @brief Semaphore routines
+ * @author Alexander Saprykin
+ *
+ * A semaphore is a synchronization primitive which controls access to shared
+ * data from the concurrently running threads. Unlike a mutex (which is a
+ * particular case of a binary semaphore) it allows concurrent access not to
+ * only the one thread.
+ *
+ * The semaphore has a counter which means the number of available resources
+ * (units). Before entering a critical section a thread must perform the so
+ * called P (acquire) operation: if the counter is positive it decrements the
+ * counter by 1 and continues execution; otherwise the thread is suspended until
+ * the counter becomes positive. Before leaving the critical section the thread
+ * must perform the so called V (release) operation: increments the counter by 1
+ * and wakes up a waiting thread from the queue (if any).
+ *
+ * You can think about the semaphore as a resource controller: the P operation
+ * takes one unit, while the V operation gives one unit back. The thread could
+ * not continue execution without taking a resource unit. By setting the initial
+ * semaphore counter value you can control how much concurrent threads can work
+ * with a shared resource.
+ *
+ * This semaphore implementation is process-wide so you can synchronize not only
+ * the threads. But it makes this IPC primitive (actually like any other IPC
+ * primitive, as well) relatively heavy. Consider using a mutex or a spinlock
+ * instead if you do not need to cross a process boundary.
+ *
+ * A process-wide semaphore is identified by its name across the system, thus it
+ * is also called a named semaphore. Use p_semaphore_new() to open the named
+ * semaphore and p_semaphore_free() to close it.
+ *
+ * Please note the following platform specific differences:
+ *
+ * - Windows doesn't own IPC objects (processes own them), which means that a
+ * semaphore will be removed from the system after the last process or thread
+ * closes it (or after terminating all the processes and threads holding open
+ * semaphore).
+ *
+ * - UNIX systems own IPC objects. Because of that UNIX IPC objects can survive
+ * an application crash: an already used semaphore can be opened in a locked
+ * state and an application can fail into a deadlock or an inconsistent state.
+ * This could happen if you have not closed all the open semaphores explicitly
+ * before terminating the application.
+ *
+ * - IRIX allows to open several instances of a semaphore within the single
+ * process, but it will close the object after the first close call from any of
+ * the threads within the process.
+ *
+ * - AmigaOS has process-wide semaphores without actual tracking of counter,
+ * which means that semaphore behaves the same way as recursive mutex.
+ *
+ * - OpenVMS (as of 8.4 release) has declared prototypes for process-wide named
+ * semaphores but the actual implementation is broken.
+ *
+ * - OS/2 lacks support for process-wide named semaphores.
+ *
+ * - Syllable lacks support for process-wide named semaphores.
+ *
+ * - BeOS lacks support for process-wide named semaphores.
+ *
+ * Use the third argument as #P_SEM_ACCESS_CREATE in p_semaphore_new() to reset
+ * a semaphore value while opening it. This argument is ignored on Windows. You
+ * can also take ownership of the semaphore with p_semaphore_take_ownership() to
+ * explicitly remove it from the system after closing.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PSEMAPHORE_H
+#define PLIBSYS_HEADER_PSEMAPHORE_H
+
+#include <pmacros.h>
+#include <ptypes.h>
+#include <perror.h>
+
+P_BEGIN_DECLS
+
+/** Enum with semaphore creation modes. */
+typedef enum PSemaphoreAccessMode_ {
+	P_SEM_ACCESS_OPEN	= 0,	/**< Opens an existing semaphore or creates one with a given value.	*/
+	P_SEM_ACCESS_CREATE	= 1	/**< Creates semaphore, resets to a given value if exists.		*/
+} PSemaphoreAccessMode;
+
+/** Semaphore opaque data structure. */
+typedef struct PSemaphore_ PSemaphore;
+
+/**
+ * @brief Creates a new #PSemaphore object.
+ * @param name Semaphore name.
+ * @param init_val Initial semaphore value.
+ * @param mode Creation mode.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return Pointer to a newly created #PSemaphore object in case of success,
+ * NULL otherwise.
+ * @since 0.0.1
+ *
+ * The @a init_val is used only in one of following cases: a semaphore with the
+ * such name doesn't exist, or the semaphore with the such name exists but
+ * @a mode specified as #P_SEM_ACCESS_CREATE (non-Windows platforms only). In
+ * other cases @a init_val is ignored. The @a name is system-wide, so any other
+ * process can open that semaphore passing the same name.
+ */
+P_LIB_API PSemaphore *	p_semaphore_new			(const pchar		*name,
+							 pint			init_val,
+							 PSemaphoreAccessMode	mode,
+							 PError			**error);
+
+/**
+ * @brief Takes ownership of a semaphore.
+ * @param sem Semaphore to take ownership.
+ * @since 0.0.1
+ *
+ * If you take ownership of a semaphore object, p_semaphore_free() will try to
+ * completely unlink it and remove from the system. This is useful on UNIX
+ * systems where the semaphore can survive an application crash. On the Windows
+ * platform this call has no effect.
+ *
+ * The common usage of this call is upon application startup to ensure that the
+ * semaphore from the previous crash will be unlinked from the system. To do
+ * that, call p_semaphore_new(), take ownership of the semaphore object and
+ * remove it with the p_semaphore_free() call. After that, create it again.
+ *
+ * You can also do the same thing upon semaphore creation passing
+ * #P_SEM_ACCESS_CREATE to p_semaphore_new(). The only difference is that you
+ * should already know whether this semaphore object is from the previous crash
+ * or not.
+ */
+P_LIB_API void		p_semaphore_take_ownership	(PSemaphore		*sem);
+
+/**
+ * @brief Acquires (P operation) a semaphore.
+ * @param sem #PSemaphore to acquire.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API pboolean	p_semaphore_acquire		(PSemaphore		*sem,
+							 PError			**error);
+
+/**
+ * @brief Releases (V operation) a semaphore.
+ * @param sem #PSemaphore to release.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API pboolean	p_semaphore_release		(PSemaphore		*sem,
+							 PError			**error);
+
+/**
+ * @brief Frees #PSemaphore object.
+ * @param sem #PSemaphore to free.
+ * @since 0.0.1
+ *
+ * It doesn't release an acquired semaphore, be careful to not to make a
+ * deadlock while removing the acquired semaphore.
+ */
+P_LIB_API void		p_semaphore_free		(PSemaphore		*sem);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PSEMAPHORE_H */
diff --git a/3rdparty/plibsys/src/pshm-amiga.c b/3rdparty/plibsys/src/pshm-amiga.c
new file mode 100644
index 0000000..4010ea2
--- /dev/null
+++ b/3rdparty/plibsys/src/pshm-amiga.c
@@ -0,0 +1,274 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2018 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "perror.h"
+#include "pmem.h"
+#include "psemaphore.h"
+#include "pshm.h"
+#include "pipc-private.h"
+
+#include <string.h>
+
+#include <exec/types.h>
+#include <proto/exec.h>
+
+#define P_SHM_NAMESPACE	"pshm"
+#define P_SHM_SUFFIX	"_p_shm_object"
+#define P_SHM_PRIV_SIZE	(2 * sizeof (psize))
+
+struct PShm_ {
+	pboolean	is_owner;
+	pchar		*platform_key;
+	ppointer	addr;
+	psize		size;
+	PSemaphore	*sem;
+	PShmAccessPerms	perms;
+};
+
+static PErrorIPC pp_shm_get_ipc_error (puint32 err_code);
+static pboolean pp_shm_create_handle (PShm *shm, PError **error);
+
+static PErrorIPC
+pp_shm_get_ipc_error (puint32 err_code)
+{
+	if (err_code == ANMERROR_NOERROR)
+		return P_ERROR_IPC_NONE;
+	else if (err_code == ANMERROR_NOMEMORY)
+		return P_ERROR_IPC_NO_RESOURCES;
+	else if (err_code == ANMERROR_DUPLICATENAME)
+		return P_ERROR_IPC_EXISTS;
+	else if (err_code == ANMERROR_PARAMETER)
+		return P_ERROR_IPC_INVALID_ARGUMENT;
+	else
+		return P_ERROR_IPC_FAILED;
+}
+
+static pboolean
+pp_shm_create_handle (PShm	*shm,
+		      PError	**error)
+{
+	ppointer	mem_area;
+	puint32		err_code;
+	pboolean	is_exists;
+
+	if (P_UNLIKELY (shm == NULL || shm->platform_key == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	IExec->Forbid ();
+
+	mem_area = IExec->FindNamedMemory (P_SHM_NAMESPACE, shm->platform_key);
+
+	if (mem_area == NULL) {
+		mem_area = IExec->AllocNamedMemoryTags (shm->size + P_SHM_PRIV_SIZE,
+							P_SHM_NAMESPACE,
+							shm->platform_key,
+							ANMT_Error, &err_code,
+							TAG_END);
+
+		if (P_UNLIKELY (mem_area == NULL)) {
+			IExec->Permit ();
+			p_error_set_error_p (error,
+				     (pint) pp_shm_get_ipc_error (err_code),
+				     (pint) err_code,
+				     "Failed to call AllocNamedMemoryTags() to create memory segment");
+			return FALSE;
+		}
+
+		memset (mem_area, 0, shm->size + P_SHM_PRIV_SIZE);
+
+		/* Set size and counter */
+		*((psize *) mem_area)     = shm->size;
+		*((psize *) mem_area + 1) = 1;
+
+		is_exists = FALSE;
+	} else {
+		*((psize *) mem_area + 1) += 1;
+
+		shm->size = *((psize *) mem_area);
+		is_exists = TRUE;
+	}
+
+	shm->addr = ((pchar *) mem_area) + P_SHM_PRIV_SIZE;
+
+	if (P_UNLIKELY ((shm->sem = p_semaphore_new (shm->platform_key, 1,
+						     is_exists ? P_SEM_ACCESS_OPEN : P_SEM_ACCESS_CREATE,
+						     error)) == NULL)) {
+		IExec->FreeNamedMemory (P_SHM_NAMESPACE, shm->platform_key);
+		IExec->Permit ();
+		return FALSE;
+	}
+
+	IExec->Permit ();
+
+	return TRUE;
+}
+
+P_LIB_API PShm *
+p_shm_new (const pchar		*name,
+	   psize		size,
+	   PShmAccessPerms	perms,
+	   PError		**error)
+{
+	PShm	*ret;
+	pchar	*new_name;
+
+	if (P_UNLIKELY (name == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PShm))) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for shared segment");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((new_name = p_malloc0 (strlen (name) + strlen (P_SHM_SUFFIX) + 1)) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for segment name");
+		p_shm_free (ret);
+		return NULL;
+	}
+
+	strcpy (new_name, name);
+	strcat (new_name, P_SHM_SUFFIX);
+
+	ret->platform_key = p_ipc_get_platform_key (new_name, FALSE);
+	ret->perms        = perms;
+	ret->size         = size;
+
+	p_free (new_name);
+
+	if (P_UNLIKELY (pp_shm_create_handle (ret, error) == FALSE)) {
+		p_shm_free (ret);
+		return NULL;
+	}
+
+	if (P_LIKELY (ret->size > size && size != 0))
+		ret->size = size;
+
+	return ret;
+}
+
+P_LIB_API void
+p_shm_take_ownership (PShm *shm)
+{
+	if (P_UNLIKELY (shm == NULL))
+		return;
+
+	shm->is_owner = TRUE;
+	p_semaphore_take_ownership (shm->sem);
+}
+
+P_LIB_API void
+p_shm_free (PShm *shm)
+{
+	if (P_UNLIKELY (shm == NULL))
+		return;
+
+	if (shm->addr != NULL) {
+		IExec->Forbid ();
+
+		*((psize *) shm->addr - 1) -= 1;
+
+		if (shm->is_owner || *((psize *) shm->addr - 1) == 0) {
+			p_semaphore_free (shm->sem);
+			shm->sem = NULL;
+
+			IExec->FreeNamedMemory (P_SHM_NAMESPACE, shm->platform_key);			
+		}
+
+		IExec->Permit ();
+	}
+
+	shm->is_owner = FALSE;
+	shm->addr     = NULL;
+	shm->size     = 0;
+
+	if (P_LIKELY (shm->platform_key != NULL))
+		p_free (shm->platform_key);
+
+	p_free (shm);
+}
+
+P_LIB_API pboolean
+p_shm_lock (PShm	*shm,
+	    PError	**error)
+{
+	if (P_UNLIKELY (shm == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	return p_semaphore_acquire (shm->sem, error);
+}
+
+P_LIB_API pboolean
+p_shm_unlock (PShm	*shm,
+	      PError	**error)
+{
+	if (P_UNLIKELY (shm == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	return p_semaphore_release (shm->sem, error);
+}
+
+P_LIB_API ppointer
+p_shm_get_address (const PShm *shm)
+{
+	if (P_UNLIKELY (shm == NULL))
+		return NULL;
+
+	return shm->addr;
+}
+
+P_LIB_API psize
+p_shm_get_size (const PShm *shm)
+{
+	if (P_UNLIKELY (shm == NULL))
+		return 0;
+
+	return shm->size;
+}
diff --git a/3rdparty/plibsys/src/pshm-none.c b/3rdparty/plibsys/src/pshm-none.c
new file mode 100644
index 0000000..17cf4a5
--- /dev/null
+++ b/3rdparty/plibsys/src/pshm-none.c
@@ -0,0 +1,94 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pshm.h"
+
+#include <stdlib.h>
+
+struct PShm_ {
+	pint	hdl;
+};
+
+P_LIB_API PShm *
+p_shm_new (const pchar		*name,
+	   psize		size,
+	   PShmAccessPerms	perms,
+	   PError		**error)
+{
+	P_UNUSED (name);
+	P_UNUSED (size);
+	P_UNUSED (perms);
+	P_UNUSED (error);
+
+	return NULL;
+}
+
+P_LIB_API void
+p_shm_take_ownership (PShm *shm)
+{
+	P_UNUSED (shm);
+}
+
+P_LIB_API void
+p_shm_free (PShm *shm)
+{
+	P_UNUSED (shm);
+}
+
+P_LIB_API pboolean
+p_shm_lock (PShm	*shm,
+	    PError	**error)
+{
+	P_UNUSED (shm);
+	P_UNUSED (error);
+
+	return FALSE;
+}
+
+P_LIB_API pboolean
+p_shm_unlock (PShm	*shm,
+	      PError	**error)
+{
+	P_UNUSED (shm);
+	P_UNUSED (error);
+
+	return FALSE;
+}
+
+P_LIB_API ppointer
+p_shm_get_address (const PShm *shm)
+{
+	P_UNUSED (shm);
+
+	return NULL;
+}
+
+P_LIB_API psize
+p_shm_get_size (const PShm *shm)
+{
+	P_UNUSED (shm);
+
+	return 0;
+}
diff --git a/3rdparty/plibsys/src/pshm-os2.c b/3rdparty/plibsys/src/pshm-os2.c
new file mode 100644
index 0000000..92e0779
--- /dev/null
+++ b/3rdparty/plibsys/src/pshm-os2.c
@@ -0,0 +1,350 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "perror.h"
+#include "pmem.h"
+#include "pshm.h"
+#include "perror-private.h"
+#include "pipc-private.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#define INCL_DOSMEMMGR
+#define INCL_DOSSEMAPHORES
+#define INCL_DOSERRORS
+#include <os2.h>
+
+#define P_SHM_MEM_PREFIX	"\\SHAREMEM\\"
+#define P_SHM_SEM_PREFIX	"\\SEM32\\"
+#define P_SHM_SUFFIX		"_p_shm_object"
+
+struct PShm_ {
+	pchar		*platform_key;
+	ppointer	addr;
+	psize		size;
+	HMTX		sem;
+	PShmAccessPerms	perms;
+};
+
+static pboolean pp_shm_create_handle (PShm *shm, PError **error);
+static void pp_shm_clean_handle (PShm *shm);
+
+static pboolean
+pp_shm_create_handle (PShm	*shm,
+		      PError	**error)
+{
+	pchar	*mem_name;
+	pchar	*sem_name;
+	APIRET	ulrc;
+	ULONG	flags;
+
+	if (P_UNLIKELY (shm == NULL || shm->platform_key == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	flags = PAG_COMMIT | PAG_READ;
+
+	if (shm->perms != P_SHM_ACCESS_READONLY)
+		flags |= PAG_WRITE;
+
+	if (P_UNLIKELY ((mem_name = p_malloc0 (strlen (shm->platform_key) +
+					       strlen (P_SHM_MEM_PREFIX) + 1)) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for shared memory name");
+		return FALSE;
+	}
+
+	strcpy (mem_name, P_SHM_MEM_PREFIX);
+	strcat (mem_name, shm->platform_key);
+
+	while ((ulrc = DosAllocSharedMem ((PPVOID) &shm->addr,
+					  (PSZ) mem_name,
+					  shm->size,
+					  flags)) == ERROR_INTERRUPT)
+		;
+
+	if (P_UNLIKELY (ulrc != NO_ERROR && ulrc != ERROR_ALREADY_EXISTS)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_ipc_from_system ((pint) ulrc),
+				     (pint) ulrc,
+				     "Failed to call DosAllocSharedMem() to allocate shared memory");
+		p_free (mem_name);
+		pp_shm_clean_handle (shm);
+		return FALSE;
+	}
+
+	if (ulrc == ERROR_ALREADY_EXISTS) {
+		ULONG real_size;
+		ULONG real_flags;
+
+		flags = (shm->perms == P_SHM_ACCESS_READONLY) ? PAG_READ : (PAG_WRITE | PAG_READ);
+
+		while ((ulrc = DosGetNamedSharedMem ((PPVOID) &shm->addr,
+						     (PSZ) mem_name,
+						     flags)) == ERROR_INTERRUPT)
+			;
+
+		p_free (mem_name);
+
+		if (P_UNLIKELY (ulrc != NO_ERROR)) {
+			p_error_set_error_p (error,
+					     (pint) p_error_get_ipc_from_system ((pint) ulrc),
+					     (pint) ulrc,
+					     "Failed to call DosGetNamedSharedMem() to get shared memory");
+			pp_shm_clean_handle (shm);
+			return FALSE;
+		}
+
+		real_size = (ULONG) shm->size;
+
+		while ((ulrc = DosQueryMem ((PVOID) shm->addr,
+					    &real_size,
+					    &real_flags)) == ERROR_INTERRUPT)
+			;
+
+		if (P_UNLIKELY (ulrc != NO_ERROR)) {
+			p_error_set_error_p (error,
+					     (pint) p_error_get_ipc_from_system ((pint) ulrc),
+					     (pint) ulrc,
+					     "Failed to call DosQueryMem() to get memory info");
+			pp_shm_clean_handle (shm);
+			return FALSE;
+		}
+
+		shm->size = (psize) real_size;
+	} else
+		p_free (mem_name);
+
+	if (P_UNLIKELY ((sem_name = p_malloc0 (strlen (shm->platform_key) +
+					       strlen (P_SHM_SEM_PREFIX) + 1)) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for shared memory name");
+		pp_shm_clean_handle (shm);
+		return FALSE;
+	}
+
+	strcpy (sem_name, P_SHM_SEM_PREFIX);
+	strcat (sem_name, shm->platform_key);
+
+	ulrc = DosCreateMutexSem ((PSZ) sem_name, &shm->sem, 0, FALSE);
+
+	if (ulrc == ERROR_DUPLICATE_NAME)
+		ulrc = DosOpenMutexSem ((PSZ) sem_name, &shm->sem);
+
+	p_free (sem_name);
+
+	if (P_UNLIKELY (ulrc != NO_ERROR)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_ipc_from_system ((pint) ulrc),
+				     (pint) ulrc,
+				     "Failed to call DosCreateMutexSem() to create a lock");
+		pp_shm_clean_handle (shm);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+pp_shm_clean_handle (PShm *shm)
+{
+	APIRET ulrc;
+
+	if (P_UNLIKELY (shm->addr != NULL)) {
+		while ((ulrc = DosFreeMem ((PVOID) shm->addr)) == ERROR_INTERRUPT)
+			;
+
+		if (P_UNLIKELY (ulrc != NO_ERROR))
+			P_ERROR ("PShm::pp_shm_clean_handle: DosFreeMem() failed");
+
+		shm->addr = NULL;
+	}
+
+	if (P_LIKELY (shm->sem != NULLHANDLE)) {
+		if (P_UNLIKELY (DosCloseMutexSem (shm->sem) != NO_ERROR))
+			P_ERROR ("PShm::pp_shm_clean_handle: DosCloseMutexSem() failed");
+
+		shm->sem = NULLHANDLE;
+	}
+
+	shm->size = 0;
+}
+
+P_LIB_API PShm *
+p_shm_new (const pchar		*name,
+	   psize		size,
+	   PShmAccessPerms	perms,
+	   PError		**error)
+{
+	PShm	*ret;
+	pchar	*new_name;
+
+	if (P_UNLIKELY (name == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PShm))) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for shared segment");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((new_name = p_malloc0 (strlen (name) + strlen (P_SHM_SUFFIX) + 1)) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for segment name");
+		p_shm_free (ret);
+		return NULL;
+	}
+
+	strcpy (new_name, name);
+	strcat (new_name, P_SHM_SUFFIX);
+
+	ret->platform_key = p_ipc_get_platform_key (new_name, FALSE);
+	ret->perms        = perms;
+	ret->size         = size;
+
+	p_free (new_name);
+
+	if (P_UNLIKELY (pp_shm_create_handle (ret, error) == FALSE)) {
+		p_shm_free (ret);
+		return NULL;
+	}
+
+	if (P_LIKELY (ret->size > size && size != 0))
+		ret->size = size;
+
+	return ret;
+}
+
+P_LIB_API void
+p_shm_take_ownership (PShm *shm)
+{
+	P_UNUSED (shm);
+}
+
+P_LIB_API void
+p_shm_free (PShm *shm)
+{
+	if (P_UNLIKELY (shm == NULL))
+		return;
+
+	pp_shm_clean_handle (shm);
+
+	if (P_LIKELY (shm->platform_key != NULL))
+		p_free (shm->platform_key);
+
+	p_free (shm);
+}
+
+P_LIB_API pboolean
+p_shm_lock (PShm	*shm,
+	    PError	**error)
+{
+	APIRET ulrc;
+
+	if (P_UNLIKELY (shm == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	while ((ulrc = DosRequestMutexSem (shm->sem,
+					   (ULONG) SEM_INDEFINITE_WAIT)) == ERROR_INTERRUPT)
+		;
+
+	if (P_UNLIKELY (ulrc != NO_ERROR)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_ipc_from_system ((pint) ulrc),
+				     (pint) ulrc,
+				     "Failed to lock memory segment");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+P_LIB_API pboolean
+p_shm_unlock (PShm	*shm,
+	      PError	**error)
+{
+	APIRET ulrc;
+
+	if (P_UNLIKELY (shm == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	ulrc = DosReleaseMutexSem (shm->sem);
+
+	if (P_UNLIKELY (ulrc != NO_ERROR)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_ipc_from_system ((pint) ulrc),
+				     (pint) ulrc,
+				     "Failed to unlock memory segment");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+P_LIB_API ppointer
+p_shm_get_address (const PShm *shm)
+{
+	if (P_UNLIKELY (shm == NULL))
+		return NULL;
+
+	return shm->addr;
+}
+
+P_LIB_API psize
+p_shm_get_size (const PShm *shm)
+{
+	if (P_UNLIKELY (shm == NULL))
+		return 0;
+
+	return shm->size;
+}
diff --git a/3rdparty/plibsys/src/pshm-posix.c b/3rdparty/plibsys/src/pshm-posix.c
new file mode 100644
index 0000000..3551b2c
--- /dev/null
+++ b/3rdparty/plibsys/src/pshm-posix.c
@@ -0,0 +1,311 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2018 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "perror.h"
+#include "pmem.h"
+#include "psemaphore.h"
+#include "pshm.h"
+#include "perror-private.h"
+#include "pipc-private.h"
+#include "psysclose-private.h"
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+#define P_SHM_SUFFIX		"_p_shm_object"
+#define P_SHM_INVALID_HDL	-1
+
+struct PShm_ {
+	pboolean	shm_created;
+	pchar		*platform_key;
+	ppointer	addr;
+	psize		size;
+	PSemaphore	*sem;
+	PShmAccessPerms	perms;
+};
+
+static pboolean pp_shm_create_handle (PShm *shm, PError **error);
+static void pp_shm_clean_handle (PShm *shm);
+
+static pboolean
+pp_shm_create_handle (PShm	*shm,
+		      PError	**error)
+{
+	pboolean	is_exists;
+	pint		fd, flags;
+	struct stat	stat_buf;
+
+	if (P_UNLIKELY (shm == NULL || shm->platform_key == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	is_exists = FALSE;
+
+	while ((fd = shm_open (shm->platform_key,
+			       O_CREAT | O_EXCL | O_RDWR,
+			       0660)) == P_SHM_INVALID_HDL &&
+	       p_error_get_last_system () == EINTR)
+	;
+
+	if (fd == P_SHM_INVALID_HDL) {
+		if (p_error_get_last_system () == EEXIST) {
+			is_exists = TRUE;
+
+			while ((fd = shm_open (shm->platform_key,
+					       O_RDWR,
+					       0660)) == P_SHM_INVALID_HDL &&
+			       p_error_get_last_system () == EINTR)
+			;
+		}
+	} else
+		shm->shm_created = TRUE;
+
+	if (P_UNLIKELY (fd == P_SHM_INVALID_HDL)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_ipc (),
+				     p_error_get_last_system (),
+				     "Failed to call shm_open() to create memory segment");
+		pp_shm_clean_handle (shm);
+		return FALSE;
+	}
+
+	/* Try to get size of the existing file descriptor */
+	if (is_exists) {
+		if (P_UNLIKELY (fstat (fd, &stat_buf) == -1)) {
+			p_error_set_error_p (error,
+					     (pint) p_error_get_last_ipc (),
+					     p_error_get_last_system (),
+					     "Failed to call fstat() to get memory segment size");
+
+			if (P_UNLIKELY (p_sys_close (fd) != 0))
+				P_WARNING ("PShm::pp_shm_create_handle: p_sys_close() failed(1)");
+
+			pp_shm_clean_handle (shm);
+			return FALSE;
+		}
+
+		shm->size = (psize) stat_buf.st_size;
+	} else {
+		if (P_UNLIKELY ((ftruncate (fd, (off_t) shm->size)) == -1)) {
+			p_error_set_error_p (error,
+					     (pint) p_error_get_last_ipc (),
+					     p_error_get_last_system (),
+					     "Failed to call ftruncate() to set memory segment size");
+
+			if (P_UNLIKELY (p_sys_close (fd) != 0))
+				P_WARNING ("PShm::pp_shm_create_handle: p_sys_close() failed(2)");
+
+			pp_shm_clean_handle (shm);
+			return FALSE;
+		}
+	}
+
+	flags = (shm->perms == P_SHM_ACCESS_READONLY) ? PROT_READ : PROT_READ | PROT_WRITE;
+
+	if (P_UNLIKELY ((shm->addr = mmap (NULL, shm->size, flags, MAP_SHARED, fd, 0)) == (void *) -1)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_ipc (),
+				     p_error_get_last_system (),
+				     "Failed to call mmap() to map memory segment");
+		shm->addr = NULL;
+
+		if (P_UNLIKELY (p_sys_close (fd) != 0))
+			P_WARNING ("PShm::pp_shm_create_handle: p_sys_close() failed(3)");
+
+		pp_shm_clean_handle (shm);
+		return FALSE;
+	}
+
+	if (P_UNLIKELY (p_sys_close (fd) != 0))
+		P_WARNING ("PShm::pp_shm_create_handle: p_sys_close() failed(4)");
+
+	if (P_UNLIKELY ((shm->sem = p_semaphore_new (shm->platform_key, 1,
+						     is_exists ? P_SEM_ACCESS_OPEN : P_SEM_ACCESS_CREATE,
+						     error)) == NULL)) {
+		pp_shm_clean_handle (shm);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+pp_shm_clean_handle (PShm *shm)
+{
+	if (P_UNLIKELY (shm->addr != NULL && munmap (shm->addr, shm->size) == -1))
+		P_ERROR ("PShm::pp_shm_clean_handle: munmap () failed");
+
+	if (shm->shm_created == TRUE && shm_unlink (shm->platform_key) == -1)
+		P_ERROR ("PShm::pp_shm_clean_handle: shm_unlink() failed");
+
+	if (P_LIKELY (shm->sem != NULL)) {
+		p_semaphore_free (shm->sem);
+		shm->sem         = NULL;
+	}
+
+	shm->shm_created = FALSE;
+	shm->addr        = NULL;
+	shm->size        = 0;
+}
+
+P_LIB_API PShm *
+p_shm_new (const pchar		*name,
+	   psize		size,
+	   PShmAccessPerms	perms,
+	   PError		**error)
+{
+	PShm	*ret;
+	pchar	*new_name;
+
+	if (P_UNLIKELY (name == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PShm))) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for shared segment");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((new_name = p_malloc0 (strlen (name) + strlen (P_SHM_SUFFIX) + 1)) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for segment name");
+		p_shm_free (ret);
+		return NULL;
+	}
+
+	strcpy (new_name, name);
+	strcat (new_name, P_SHM_SUFFIX);
+
+#if defined (P_OS_IRIX) || defined (P_OS_TRU64)
+	/* IRIX and Tru64 prefer filename styled IPC names */
+	ret->platform_key = p_ipc_get_platform_key (new_name, FALSE);
+#else
+	ret->platform_key = p_ipc_get_platform_key (new_name, TRUE);
+#endif
+	ret->perms = perms;
+	ret->size  = size;
+
+	p_free (new_name);
+
+	if (P_UNLIKELY (pp_shm_create_handle (ret, error) == FALSE)) {
+		p_shm_free (ret);
+		return NULL;
+	}
+
+	if (P_LIKELY (ret->size > size && size != 0))
+		ret->size = size;
+
+	return ret;
+}
+
+P_LIB_API void
+p_shm_take_ownership (PShm *shm)
+{
+	if (P_UNLIKELY (shm == NULL))
+		return;
+
+	shm->shm_created = TRUE;
+	p_semaphore_take_ownership (shm->sem);
+}
+
+P_LIB_API void
+p_shm_free (PShm *shm)
+{
+	if (P_UNLIKELY (shm == NULL))
+		return;
+
+	pp_shm_clean_handle (shm);
+
+	if (P_LIKELY (shm->platform_key != NULL))
+		p_free (shm->platform_key);
+
+	p_free (shm);
+}
+
+P_LIB_API pboolean
+p_shm_lock (PShm	*shm,
+	    PError	**error)
+{
+	if (P_UNLIKELY (shm == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	return p_semaphore_acquire (shm->sem, error);
+}
+
+P_LIB_API pboolean
+p_shm_unlock (PShm	*shm,
+	      PError	**error)
+{
+	if (P_UNLIKELY (shm == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	return p_semaphore_release (shm->sem, error);
+}
+
+P_LIB_API ppointer
+p_shm_get_address (const PShm *shm)
+{
+	if (P_UNLIKELY (shm == NULL))
+		return NULL;
+
+	return shm->addr;
+}
+
+P_LIB_API psize
+p_shm_get_size (const PShm *shm)
+{
+	if (P_UNLIKELY (shm == NULL))
+		return 0;
+
+	return shm->size;
+}
diff --git a/3rdparty/plibsys/src/pshm-sysv.c b/3rdparty/plibsys/src/pshm-sysv.c
new file mode 100644
index 0000000..d7e6434
--- /dev/null
+++ b/3rdparty/plibsys/src/pshm-sysv.c
@@ -0,0 +1,307 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "perror.h"
+#include "pmem.h"
+#include "psemaphore.h"
+#include "pshm.h"
+#include "perror-private.h"
+#include "pipc-private.h"
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/shm.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <errno.h>
+
+#define P_SHM_SUFFIX		"_p_shm_object"
+#define P_SHM_INVALID_HDL	-1
+
+typedef pint pshm_hdl;
+
+struct PShm_ {
+	pboolean	file_created;
+	key_t		unix_key;
+	pchar		*platform_key;
+	pshm_hdl	shm_hdl;
+	ppointer	addr;
+	psize		size;
+	PSemaphore	*sem;
+	PShmAccessPerms	perms;
+};
+
+static pboolean pp_shm_create_handle (PShm *shm, PError **error);
+static void pp_shm_clean_handle (PShm *shm);
+
+static pboolean
+pp_shm_create_handle (PShm	*shm,
+		      PError	**error)
+{
+	pboolean	is_exists;
+	pint		flags, built;
+	struct shmid_ds	shm_stat;
+
+	if (P_UNLIKELY (shm == NULL || shm->platform_key == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	is_exists = FALSE;
+
+	if (P_UNLIKELY ((built = p_ipc_unix_create_key_file (shm->platform_key)) == -1)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_ipc (),
+				     p_error_get_last_system (),
+				     "Failed to create key file");
+		pp_shm_clean_handle (shm);
+		return FALSE;
+	} else if (built == 0)
+		shm->file_created = TRUE;
+
+	if (P_UNLIKELY ((shm->unix_key = p_ipc_unix_get_ftok_key (shm->platform_key)) == -1)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_ipc (),
+				     p_error_get_last_system (),
+				     "Failed to get unique IPC key");
+		pp_shm_clean_handle (shm);
+		return FALSE;
+	}
+
+	flags = (shm->perms == P_SHM_ACCESS_READONLY) ? 0444 : 0660;
+
+	if ((shm->shm_hdl = shmget (shm->unix_key,
+				    shm->size,
+				    IPC_CREAT | IPC_EXCL | flags)) == P_SHM_INVALID_HDL) {
+		if (p_error_get_last_system () == EEXIST) {
+			is_exists = TRUE;
+
+			shm->shm_hdl = shmget (shm->unix_key, 0, flags);
+		}
+	} else
+		shm->file_created = (built == 1);
+
+	if (P_UNLIKELY (shm->shm_hdl == P_SHM_INVALID_HDL)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_ipc (),
+				     p_error_get_last_system (),
+				     "Failed to call shmget() to create memory segment");
+		pp_shm_clean_handle (shm);
+		return FALSE;
+	}
+
+	if (P_UNLIKELY (shmctl (shm->shm_hdl, IPC_STAT, &shm_stat) == -1)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_ipc (),
+				     p_error_get_last_system (),
+				     "Failed to call shmctl() to get memory segment size");
+		pp_shm_clean_handle (shm);
+		return FALSE;
+	}
+
+	shm->size = shm_stat.shm_segsz;
+
+	flags = (shm->perms == P_SHM_ACCESS_READONLY) ? SHM_RDONLY : 0;
+
+	if (P_UNLIKELY ((shm->addr = shmat (shm->shm_hdl, 0, flags)) == (void *) -1)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_ipc (),
+				     p_error_get_last_system (),
+				     "Failed to call shmat() to attach to the memory segment");
+		pp_shm_clean_handle (shm);
+		return FALSE;
+	}
+
+	if (P_UNLIKELY ((shm->sem = p_semaphore_new (shm->platform_key, 1,
+						     is_exists ? P_SEM_ACCESS_OPEN : P_SEM_ACCESS_CREATE,
+						     error)) == NULL)) {
+		pp_shm_clean_handle (shm);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+pp_shm_clean_handle (PShm *shm)
+{
+	struct shmid_ds shm_stat;
+
+	if (P_LIKELY (shm->addr != NULL)) {
+		if (P_UNLIKELY (shmdt (shm->addr) == -1))
+			P_ERROR ("PShm::pp_shm_clean_handle: shmdt() failed");
+
+		if (P_UNLIKELY (shmctl (shm->shm_hdl, IPC_STAT, &shm_stat) == -1))
+			P_ERROR ("PShm::pp_shm_clean_handle: shmctl() with IPC_STAT failed");
+
+		if (P_UNLIKELY (shm_stat.shm_nattch == 0 && shmctl (shm->shm_hdl, IPC_RMID, 0) == -1))
+			P_ERROR ("PShm::pp_shm_clean_handle: shmctl() with IPC_RMID failed");
+	}
+
+	if (shm->file_created == TRUE && unlink (shm->platform_key) == -1)
+		P_ERROR ("PShm::pp_shm_clean_handle: unlink() failed");
+
+	if (P_LIKELY (shm->sem != NULL)) {
+		p_semaphore_free (shm->sem);
+		shm->sem = NULL;
+	}
+
+	shm->file_created = FALSE;
+	shm->unix_key     = -1;
+	shm->shm_hdl      = P_SHM_INVALID_HDL;
+	shm->addr         = NULL;
+	shm->size         = 0;
+}
+
+P_LIB_API PShm *
+p_shm_new (const pchar		*name,
+	   psize		size,
+	   PShmAccessPerms	perms,
+	   PError		**error)
+{
+	PShm	*ret;
+	pchar	*new_name;
+
+	if (P_UNLIKELY (name == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PShm))) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for shared segment");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((new_name = p_malloc0 (strlen (name) + strlen (P_SHM_SUFFIX) + 1)) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for segment name");
+		p_shm_free (ret);
+		return NULL;
+	}
+
+	strcpy (new_name, name);
+	strcat (new_name, P_SHM_SUFFIX);
+
+	ret->platform_key = p_ipc_get_platform_key (new_name, FALSE);
+	ret->perms        = perms;
+	ret->size         = size;
+
+	p_free (new_name);
+
+	if (P_UNLIKELY (pp_shm_create_handle (ret, error) == FALSE)) {
+		p_shm_free (ret);
+		return NULL;
+	}
+
+	if (P_LIKELY (ret->size > size && size != 0))
+		ret->size = size;
+
+	return ret;
+}
+
+P_LIB_API void
+p_shm_take_ownership (PShm *shm)
+{
+	if (P_UNLIKELY (shm == NULL))
+		return;
+
+	shm->file_created = TRUE;
+	p_semaphore_take_ownership (shm->sem);
+}
+
+P_LIB_API void
+p_shm_free (PShm *shm)
+{
+	if (P_UNLIKELY (shm == NULL))
+		return;
+
+	pp_shm_clean_handle (shm);
+
+	if (P_LIKELY (shm->platform_key != NULL))
+		p_free (shm->platform_key);
+
+	p_free (shm);
+}
+
+P_LIB_API pboolean
+p_shm_lock (PShm	*shm,
+	    PError	**error)
+{
+	if (P_UNLIKELY (shm == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	return p_semaphore_acquire (shm->sem, error);
+}
+
+P_LIB_API pboolean
+p_shm_unlock (PShm	*shm,
+	      PError	**error)
+{
+	if (P_UNLIKELY (shm == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	return p_semaphore_release (shm->sem, error);
+}
+
+P_LIB_API ppointer
+p_shm_get_address (const PShm *shm)
+{
+	if (P_UNLIKELY (shm == NULL))
+		return NULL;
+
+	return shm->addr;
+}
+
+P_LIB_API psize
+p_shm_get_size (const PShm *shm)
+{
+	if (P_UNLIKELY (shm == NULL))
+		return 0;
+
+	return shm->size;
+}
diff --git a/3rdparty/plibsys/src/pshm-win.c b/3rdparty/plibsys/src/pshm-win.c
new file mode 100644
index 0000000..3ae11f9
--- /dev/null
+++ b/3rdparty/plibsys/src/pshm-win.c
@@ -0,0 +1,262 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "perror.h"
+#include "pmem.h"
+#include "psemaphore.h"
+#include "pshm.h"
+#include "perror-private.h"
+#include "pipc-private.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#define P_SHM_INVALID_HDL	NULL
+#define P_SHM_SUFFIX		"_p_shm_object"
+
+typedef HANDLE pshm_hdl;
+
+struct PShm_ {
+	pchar		*platform_key;
+	pshm_hdl	shm_hdl;
+	ppointer	addr;
+	psize		size;
+	PSemaphore	*sem;
+	PShmAccessPerms	perms;
+};
+
+static pboolean pp_shm_create_handle (PShm *shm, PError **error);
+static void pp_shm_clean_handle (PShm *shm);
+
+static pboolean
+pp_shm_create_handle (PShm	*shm,
+		      PError	**error)
+{
+	pboolean			is_exists;
+	MEMORY_BASIC_INFORMATION	mem_stat;
+	DWORD				protect;
+
+	if (P_UNLIKELY (shm == NULL || shm->platform_key == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	is_exists = FALSE;
+
+	protect = (shm->perms == P_SHM_ACCESS_READONLY) ? PAGE_READONLY : PAGE_READWRITE;
+
+	/* Multibyte character set must be enabled */
+	if (P_UNLIKELY ((shm->shm_hdl = CreateFileMappingA (INVALID_HANDLE_VALUE,
+							    NULL,
+							    protect,
+							    0,
+							    (DWORD) shm->size,
+							    shm->platform_key)) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_ipc (),
+				     p_error_get_last_system (),
+				     "Failed to call CreateFileMapping() to create file mapping");
+		pp_shm_clean_handle (shm);
+		return FALSE;
+	}
+
+	protect = (protect == PAGE_READONLY) ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS;
+
+	if (P_UNLIKELY ((shm->addr = MapViewOfFile (shm->shm_hdl, protect, 0, 0, 0)) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_ipc (),
+				     p_error_get_last_system (),
+				     "Failed to call MapViewOfFile() to map file to memory");
+		pp_shm_clean_handle (shm);
+		return FALSE;
+	}
+
+	if (p_error_get_last_system () == ERROR_ALREADY_EXISTS)
+		is_exists = TRUE;
+
+	if (P_UNLIKELY (VirtualQuery (shm->addr, &mem_stat, sizeof (mem_stat)) == 0)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_last_ipc (),
+				     p_error_get_last_system (),
+				     "Failed to call VirtualQuery() to get memory map info");
+		pp_shm_clean_handle (shm);
+		return FALSE;
+	}
+
+	shm->size = mem_stat.RegionSize;
+
+	if (P_UNLIKELY ((shm->sem = p_semaphore_new (shm->platform_key, 1,
+						     is_exists ? P_SEM_ACCESS_OPEN : P_SEM_ACCESS_CREATE,
+						     error)) == NULL)) {
+		pp_shm_clean_handle (shm);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+pp_shm_clean_handle (PShm *shm)
+{
+	if (P_UNLIKELY (shm->addr != NULL && UnmapViewOfFile ((char *) shm->addr) == 0))
+		P_ERROR ("PShm::pp_shm_clean_handle: UnmapViewOfFile() failed");
+
+	if (P_UNLIKELY (shm->shm_hdl != P_SHM_INVALID_HDL && CloseHandle (shm->shm_hdl) == 0))
+		P_ERROR ("PShm::pp_shm_clean_handle: CloseHandle() failed");
+
+	if (P_LIKELY (shm->sem != NULL)) {
+		p_semaphore_free (shm->sem);
+		shm->sem = NULL;
+	}
+
+	shm->shm_hdl = P_SHM_INVALID_HDL;
+	shm->addr    = NULL;
+	shm->size    = 0;
+}
+
+P_LIB_API PShm *
+p_shm_new (const pchar		*name,
+	   psize		size,
+	   PShmAccessPerms	perms,
+	   PError		**error)
+{
+	PShm	*ret;
+	pchar	*new_name;
+
+	if (P_UNLIKELY (name == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PShm))) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for shared segment");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((new_name = p_malloc0 (strlen (name) + strlen (P_SHM_SUFFIX) + 1)) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for segment name");
+		p_shm_free (ret);
+		return NULL;
+	}
+
+	strcpy (new_name, name);
+	strcat (new_name, P_SHM_SUFFIX);
+
+	ret->platform_key = p_ipc_get_platform_key (new_name, FALSE);
+	ret->perms        = perms;
+	ret->size         = size;
+
+	p_free (new_name);
+
+	if (P_UNLIKELY (pp_shm_create_handle (ret, error) == FALSE)) {
+		p_shm_free (ret);
+		return NULL;
+	}
+
+	if (P_LIKELY (ret->size > size && size != 0))
+		ret->size = size;
+
+	return ret;
+}
+
+P_LIB_API void
+p_shm_take_ownership (PShm *shm)
+{
+	P_UNUSED (shm);
+}
+
+P_LIB_API void
+p_shm_free (PShm *shm)
+{
+	if (P_UNLIKELY (shm == NULL))
+		return;
+
+	pp_shm_clean_handle (shm);
+
+	if (P_LIKELY (shm->platform_key != NULL))
+		p_free (shm->platform_key);
+
+	p_free (shm);
+}
+
+P_LIB_API pboolean
+p_shm_lock (PShm	*shm,
+	    PError	**error)
+{
+	if (P_UNLIKELY (shm == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	return p_semaphore_acquire (shm->sem, error);
+}
+
+P_LIB_API pboolean
+p_shm_unlock (PShm	*shm,
+	      PError	**error)
+{
+	if (P_UNLIKELY (shm == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	return p_semaphore_release (shm->sem, error);
+}
+
+P_LIB_API ppointer
+p_shm_get_address (const PShm *shm)
+{
+	if (P_UNLIKELY (shm == NULL))
+		return NULL;
+
+	return shm->addr;
+}
+
+P_LIB_API psize
+p_shm_get_size (const PShm *shm)
+{
+	if (P_UNLIKELY (shm == NULL))
+		return 0;
+
+	return shm->size;
+}
diff --git a/3rdparty/plibsys/src/pshm.h b/3rdparty/plibsys/src/pshm.h
new file mode 100644
index 0000000..096c009
--- /dev/null
+++ b/3rdparty/plibsys/src/pshm.h
@@ -0,0 +1,195 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file pshm.h
+ * @brief Shared memory
+ * @author Alexander Saprykin
+ *
+ * Shared memory is a memory segment which can be accessed from several threads
+ * or processes. It provides an efficient way to transfer large blocks of data
+ * between processes. It can be used as any other regular memory segment in an
+ * application.
+ *
+ * Shared memory acts like an inter-process communication method. This memory
+ * exchange implementation is process-wide so you can transfer data not only
+ * between the threads. But it makes this IPC method (actually like any other
+ * IPC method, as well) relatively heavy. Consider using other approaches
+ * instead if you do not need to cross the process boundary.
+ *
+ * A shared memory segment doesn't provide any synchronization primitives itself
+ * which means that several processes or threads can concurrently write and read
+ * from it. This can lead to data consistency problems. To avoid such situations
+ * a locking mechanism is provided: use p_shm_lock() before entering a critical
+ * section on the memory segment and p_shm_unlock() when leaving this section.
+ * The locking mechanism is working across the process boundary.
+ *
+ * A process-wide shared memory segment is identified by its name across the
+ * system, thus it is also called a named memory segment. Use p_shm_new() to
+ * open the named shared memory segment and p_shm_free() to close it.
+ *
+ * Please note the following platform specific differences:
+ *
+ * - Windows and OS/2 don't own IPC objects (processes own them), which means
+ * that a shared memory segment will be removed after the last process or thread
+ * detaches (or after terminating all the processes and threads attached to the
+ * segment) it.
+ *
+ * - UNIX systems own IPC objects. Because of that UNIX IPC objects can survive
+ * an application crash: the attached shared memory segment can contain data
+ * from the previous working session. This could happen if you have not detached
+ * from all the shared memory segments explicitly before terminating the
+ * application.
+ *
+ * - HP-UX has limitations due to its MPAS/MGAS features, so you couldn't attach
+ * to the same memory segment twice from the same process.
+ *
+ * - IRIX allows to open several instances of the same buffer within the single
+ * process, but it will close the object after the first close call from any of
+ * the threads within the process.
+ *
+ * - OpenVMS (as of 8.4 release) has broken implementation of process-wide named
+ * semaphores which leads to the broken shared memory also.
+ *
+ * - Syllable lacks support for process-wide named semaphores which leads to the
+ * absence of shared memory.
+ *
+ * - BeOS lacks support for process-wide named semaphores which leads to the
+ * absence of shared memory.
+ *
+ * You can take ownership of the shared memory segment with
+ * p_shm_take_ownership() to explicitly remove it from the system after closing.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PSHM_H
+#define PLIBSYS_HEADER_PSHM_H
+
+#include <pmacros.h>
+#include <ptypes.h>
+#include <perror.h>
+
+P_BEGIN_DECLS
+
+/** Enum with shared memory access permissions. */
+typedef enum PShmAccessPerms_ {
+	P_SHM_ACCESS_READONLY	= 0,	/**< Read-only access.	*/
+	P_SHM_ACCESS_READWRITE	= 1	/**< Read/write access.	*/
+} PShmAccessPerms;
+
+/** Shared memory opaque data structure. */
+typedef struct PShm_ PShm;
+
+/**
+ * @brief Creates a new #PShm object.
+ * @param name Shared memory name.
+ * @param size Size of the memory segment in bytes, can't be changed later.
+ * @param perms Memory segment permissions, see #PShmAccessPerms.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return Pointer to a newly created #PShm object in case of success, NULL
+ * otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API PShm *	p_shm_new		(const pchar		*name,
+						 psize			size,
+						 PShmAccessPerms	perms,
+						 PError			**error);
+
+/**
+ * @brief Takes ownership of a shared memory segment.
+ * @param shm Shared memory segment.
+ * @since 0.0.1
+ *
+ * If you take ownership of the shared memory object, p_shm_free() will try to
+ * completely unlink it and remove from the system. This is useful on UNIX
+ * systems where shared memory can survive an application crash. On the Windows
+ * and OS/2 platforms this call has no effect.
+ *
+ * The common usage of this call is upon application startup to ensure that the
+ * memory segment from the previous crash will be unlinked from the system. To
+ * do that, call p_shm_new() and check if its condition is normal (the segment
+ * size, the data). If not, take ownership of the shared memory object and
+ * remove it with the p_shm_free() call. After that, create it again.
+ */
+P_LIB_API void		p_shm_take_ownership	(PShm			*shm);
+
+/**
+ * @brief Frees #PShm object.
+ * @param shm #PShm to free.
+ * @since 0.0.1
+ *
+ * It doesn't unlock a given shared memory segment, be careful to not to make a
+ * deadlock or a segfault while freeing the memory segment which is under usage.
+ */
+P_LIB_API void		p_shm_free		(PShm			*shm);
+
+/**
+ * @brief Locks #PShm object for usage.
+ * @param shm #PShm to lock.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ *
+ * If the object is already locked then the thread will be suspended until the
+ * object becomes unlocked.
+ */
+P_LIB_API pboolean	p_shm_lock		(PShm			*shm,
+						 PError			**error);
+
+/**
+ * @brief Unlocks #PShm object.
+ * @param shm #PShm to unlock.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API pboolean	p_shm_unlock		(PShm			*shm,
+						 PError			**error);
+
+/**
+ * @brief Gets a starting address of a #PShm memory segment.
+ * @param shm #PShm to get the address for.
+ * @return Pointer to the starting address in case of success, NULL otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API ppointer	p_shm_get_address	(const PShm		*shm);
+
+/**
+ * @brief Gets the size of a #PShm memory segment.
+ * @param shm #PShm to get the size for.
+ * @return Size of the given memory segment in case of success, 0 otherwise.
+ * @since 0.0.1
+ *
+ * Note that the returned size would be a slightly larger than specified during
+ * the p_shm_new() call due to service information stored inside.
+ */
+P_LIB_API psize		p_shm_get_size		(const PShm		*shm);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PSHM_H */
diff --git a/3rdparty/plibsys/src/pshmbuffer.c b/3rdparty/plibsys/src/pshmbuffer.c
new file mode 100644
index 0000000..7bdbbd3
--- /dev/null
+++ b/3rdparty/plibsys/src/pshmbuffer.c
@@ -0,0 +1,334 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2020 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "pshm.h"
+#include "pshmbuffer.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#define P_SHM_BUFFER_READ_OFFSET	0
+#define P_SHM_BUFFER_WRITE_OFFSET	sizeof (psize)
+#define P_SHM_BUFFER_DATA_OFFSET	sizeof (psize) * 2
+
+struct PShmBuffer_ {
+	PShm *shm;
+	psize size;
+};
+
+static psize pp_shm_buffer_get_free_space (PShmBuffer *buf);
+static psize pp_shm_buffer_get_used_space (PShmBuffer *buf);
+
+/* Warning: this function is not thread-safe, only for internal usage */
+static psize
+pp_shm_buffer_get_free_space (PShmBuffer *buf)
+{
+	psize		read_pos, write_pos;
+	ppointer	addr;
+
+	addr = p_shm_get_address (buf->shm);
+
+	memcpy (&read_pos, (pchar *) addr + P_SHM_BUFFER_READ_OFFSET, sizeof (read_pos));
+	memcpy (&write_pos, (pchar *) addr + P_SHM_BUFFER_WRITE_OFFSET, sizeof (write_pos));
+
+	if (write_pos < read_pos)
+		return read_pos - write_pos - 1;
+	else if (write_pos > read_pos)
+		return buf->size - (write_pos - read_pos) - 1;
+	else
+		return buf->size - 1;
+}
+
+static psize
+pp_shm_buffer_get_used_space (PShmBuffer *buf)
+{
+	psize		read_pos, write_pos;
+	ppointer	addr;
+
+	addr = p_shm_get_address (buf->shm);
+
+	memcpy (&read_pos, (pchar *) addr + P_SHM_BUFFER_READ_OFFSET, sizeof (read_pos));
+	memcpy (&write_pos, (pchar *) addr + P_SHM_BUFFER_WRITE_OFFSET, sizeof (write_pos));
+
+	if (write_pos > read_pos)
+		return write_pos - read_pos;
+	else if (write_pos < read_pos)
+		return (buf->size - (read_pos - write_pos));
+	else
+		return 0;
+}
+
+P_LIB_API PShmBuffer *
+p_shm_buffer_new (const pchar	*name,
+		  psize		size,
+		  PError	**error)
+{
+	PShmBuffer	*ret;
+	PShm		*shm;
+
+	if (P_UNLIKELY (name == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((shm = p_shm_new (name,
+					  (size != 0) ? size + P_SHM_BUFFER_DATA_OFFSET + 1 : 0,
+					  P_SHM_ACCESS_READWRITE,
+					  error)) == NULL))
+		return NULL;
+
+	if (P_UNLIKELY (p_shm_get_size (shm) <= P_SHM_BUFFER_DATA_OFFSET + 1)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Too small memory segment to hold required data");
+		p_shm_free (shm);
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PShmBuffer))) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for shared buffer");
+		p_shm_free (shm);
+		return NULL;
+	}
+
+	ret->shm  = shm;
+	ret->size = p_shm_get_size (shm) - P_SHM_BUFFER_DATA_OFFSET;
+
+	return ret;
+}
+
+P_LIB_API void
+p_shm_buffer_free (PShmBuffer *buf)
+{
+	if (P_UNLIKELY (buf == NULL))
+		return;
+
+	p_shm_free (buf->shm);
+	p_free (buf);
+}
+
+P_LIB_API void
+p_shm_buffer_take_ownership (PShmBuffer *buf)
+{
+	if (P_UNLIKELY (buf == NULL))
+		return;
+
+	p_shm_take_ownership (buf->shm);
+}
+
+P_LIB_API pint
+p_shm_buffer_read (PShmBuffer	*buf,
+		   ppointer	storage,
+		   psize	len,
+		   PError	**error)
+{
+	psize		read_pos, write_pos;
+	psize		data_aval, to_copy;
+	puint		i;
+	ppointer	addr;
+
+	if (P_UNLIKELY (buf == NULL || storage == NULL || len == 0)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return -1;
+	}
+
+	if (P_UNLIKELY ((addr = p_shm_get_address (buf->shm)) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Unable to get shared memory address");
+		return -1;
+	}
+
+	if (P_UNLIKELY (p_shm_lock (buf->shm, error) == FALSE))
+		return -1;
+
+	memcpy (&read_pos, (pchar *) addr + P_SHM_BUFFER_READ_OFFSET, sizeof (read_pos));
+	memcpy (&write_pos, (pchar *) addr + P_SHM_BUFFER_WRITE_OFFSET, sizeof (write_pos));
+
+	if (read_pos == write_pos) {
+		if (P_UNLIKELY (p_shm_unlock (buf->shm, error) == FALSE))
+			return -1;
+
+		return 0;
+	}
+
+	data_aval = pp_shm_buffer_get_used_space (buf);
+	to_copy   = (data_aval <= len) ? data_aval : len;
+
+	for (i = 0; i < to_copy; ++i)
+		memcpy ((pchar *) storage + i,
+			(pchar *) addr + P_SHM_BUFFER_DATA_OFFSET + ((read_pos + i) % buf->size),
+			1);
+
+	read_pos = (read_pos + to_copy) % buf->size;
+	memcpy ((pchar *) addr + P_SHM_BUFFER_READ_OFFSET, &read_pos, sizeof (read_pos));
+
+	if (P_UNLIKELY (p_shm_unlock (buf->shm, error) == FALSE))
+		return -1;
+
+	return (pint) to_copy;
+}
+
+P_LIB_API pssize
+p_shm_buffer_write (PShmBuffer	*buf,
+		    ppointer	data,
+		    psize	len,
+		    PError	**error)
+{
+	psize		read_pos, write_pos;
+	puint		i;
+	ppointer	addr;
+
+	if (P_UNLIKELY (buf == NULL || data == NULL || len == 0)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return -1;
+	}
+
+	if (P_UNLIKELY ((addr = p_shm_get_address (buf->shm)) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Unable to get shared memory address");
+		return -1;
+	}
+
+	if (P_UNLIKELY (p_shm_lock (buf->shm, error) == FALSE))
+		return -1;
+
+	memcpy (&read_pos, (pchar *) addr + P_SHM_BUFFER_READ_OFFSET, sizeof (read_pos));
+	memcpy (&write_pos, (pchar *) addr + P_SHM_BUFFER_WRITE_OFFSET, sizeof (write_pos));
+
+	if (pp_shm_buffer_get_free_space (buf) < len) {
+		if (P_UNLIKELY (p_shm_unlock (buf->shm, error) == FALSE))
+			return -1;
+
+		return 0;
+	}
+
+	for (i = 0; i < len; ++i)
+		memcpy ((pchar *) addr + P_SHM_BUFFER_DATA_OFFSET + ((write_pos + i) % buf->size),
+			(pchar *) data + i,
+			1);
+
+	write_pos = (write_pos + len) % buf->size;
+	memcpy ((pchar *) addr + P_SHM_BUFFER_WRITE_OFFSET, &write_pos, sizeof (write_pos));
+
+	if (P_UNLIKELY (p_shm_unlock (buf->shm, error) == FALSE))
+		return -1;
+
+	return (pssize) len;
+}
+
+P_LIB_API pssize
+p_shm_buffer_get_free_space (PShmBuffer	*buf,
+			     PError	**error)
+{
+	psize space;
+
+	if (P_UNLIKELY (buf == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return -1;
+	}
+
+	if (P_UNLIKELY (p_shm_lock (buf->shm, error) == FALSE))
+		return -1;
+
+	space = pp_shm_buffer_get_free_space (buf);
+
+	if (P_UNLIKELY (p_shm_unlock (buf->shm, error) == FALSE))
+		return -1;
+
+	return (pssize) space;
+}
+
+P_LIB_API pssize
+p_shm_buffer_get_used_space (PShmBuffer	*buf,
+			     PError	**error)
+{
+	psize space;
+
+	if (P_UNLIKELY (buf == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IPC_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return -1;
+	}
+
+	if (P_UNLIKELY (p_shm_lock (buf->shm, error) == FALSE))
+		return -1;
+
+	space = pp_shm_buffer_get_used_space (buf);
+
+	if (P_UNLIKELY (p_shm_unlock (buf->shm, error) == FALSE))
+		return -1;
+
+	return (pssize) space;
+}
+
+P_LIB_API void
+p_shm_buffer_clear (PShmBuffer *buf)
+{
+	ppointer	addr;
+	psize		size;
+
+	if (P_UNLIKELY (buf == NULL))
+		return;
+
+	if (P_UNLIKELY ((addr = p_shm_get_address (buf->shm)) == NULL)) {
+		P_ERROR ("PShmBuffer::p_shm_buffer_clear: p_shm_get_address() failed");
+		return;
+	}
+
+	size = p_shm_get_size (buf->shm);
+
+	if (P_UNLIKELY (p_shm_lock (buf->shm, NULL) == FALSE)) {
+		P_ERROR ("PShmBuffer::p_shm_buffer_clear: p_shm_lock() failed");
+		return;
+	}
+
+	memset (addr, 0, size);
+
+	if (P_UNLIKELY (p_shm_unlock (buf->shm, NULL) == FALSE))
+		P_ERROR ("PShmBuffer::p_shm_buffer_clear: p_shm_unlock() failed");
+}
diff --git a/3rdparty/plibsys/src/pshmbuffer.h b/3rdparty/plibsys/src/pshmbuffer.h
new file mode 100644
index 0000000..5c8bcc1
--- /dev/null
+++ b/3rdparty/plibsys/src/pshmbuffer.h
@@ -0,0 +1,191 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file pshmbuffer.h
+ * @brief Shared memory buffer
+ * @author Alexander Saprykin
+ *
+ * A shared memory buffer works like any other buffer but it is built upon a
+ * shared memory region instead of the process-only address space. Thus it
+ * inherits all the advantages and disadvantages of shared memory behavior. You
+ * should read about #PShm before using this buffer implementation to understand
+ * underlying restrictions.
+ *
+ * The shared memory buffer is process-wide and identified by its name across
+ * the system, thus it can be opened by any process if it knows its name. Use
+ * p_shm_buffer_new() to open the shared memory buffer and p_shm_buffer_free()
+ * to close it.
+ *
+ * All read/write operations are completely thread- and process-safe, which
+ * means that no other synchronization primitive is required, even for inter-
+ * process access. A #PShm locking mechanism is used for access synchronization.
+ *
+ * The buffer is cyclic and non-overridable which means that you wouldn't get
+ * buffer overflow and wouldn't override previously written data until reading
+ * it.
+ *
+ * The read operation checks whether there is any data available and reads it in
+ * case of successful check. After reading the data used space in the buffer is
+ * marked as free and any subsequent write operation may overwrite it. Thus you
+ * couldn't read the same data twice. The read operation is performed with the
+ * p_shm_buffer_read() call.
+ *
+ * The write operation checks whether there is enough free space available and
+ * writes a given memory block only if the buffer has enough free space.
+ * Otherwise no data is written. The write operation is performed with the
+ * p_shm_buffer_write() call.
+ *
+ * Data can be read and written into the buffer only sequentially. There is no
+ * way to access an arbitrary address inside the buffer.
+ *
+ * You can take ownership of the shared memory buffer with
+ * p_shm_buffer_take_ownership() to explicitly remove it from the system after
+ * closing. Please refer to the #PShm description to understand the intention of
+ * this action.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PSHMBUFFER_H
+#define PLIBSYS_HEADER_PSHMBUFFER_H
+
+#include <ptypes.h>
+#include <pmacros.h>
+#include <perror.h>
+
+P_BEGIN_DECLS
+
+/** Shared memory buffer opaque data structure. */
+typedef struct PShmBuffer_ PShmBuffer;
+
+/**
+ * @brief Creates a new #PShmBuffer structure.
+ * @param name Unique buffer name.
+ * @param size Buffer size in bytes, can't be changed later.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return Pointer to the #PShmBuffer structure in case of success, NULL
+ * otherwise.
+ * @since 0.0.1
+ *
+ * If a buffer with the same name already exists then the @a size will be
+ * ignored and the existing buffer will be returned.
+ */
+P_LIB_API PShmBuffer *	p_shm_buffer_new		(const pchar	*name,
+							 psize		size,
+							 PError		**error);
+
+/**
+ * @brief Frees #PShmBuffer structure.
+ * @param buf #PShmBuffer to free.
+ * @since 0.0.1
+ *
+ * Note that a buffer will be completely removed from the system only after the
+ * last instance of the buffer with the same name is closed.
+ */
+P_LIB_API void		p_shm_buffer_free		(PShmBuffer	*buf);
+
+/**
+ * @brief Takes ownership of a shared memory buffer.
+ * @param buf Shared memory buffer.
+ * @since 0.0.1
+ *
+ * If you take ownership of the shared memory buffer, p_shm_buffer_free() will
+ * try to completely unlink it and remove from the system. This is useful on
+ * UNIX systems, where shared memory can survive an application crash. On the
+ * Windows and OS/2 platforms this call has no effect.
+ *
+ * The common usage of this call is upon application startup to ensure that the
+ * memory segment from the previous crash can be removed from the system. To do
+ * that, call p_shm_buffer_new() and check if its condition is normal (used
+ * space, free space). If not, take ownership of the shared memory buffer object
+ * and remove it with the p_shm_buffer_free() call. After that, create it again.
+ */
+P_LIB_API void		p_shm_buffer_take_ownership	(PShmBuffer	*buf);
+
+/**
+ * @brief Tries to read data from a shared memory buffer.
+ * @param buf #PShmBuffer to read data from.
+ * @param[out] storage Output buffer to put data in.
+ * @param len Storage size in bytes.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return Number of read bytes (can be 0 if buffer is empty), or -1 if error
+ * occured.
+ * @since 0.0.1
+ */
+P_LIB_API pint		p_shm_buffer_read		(PShmBuffer	*buf,
+							 ppointer	storage,
+							 psize		len,
+							 PError		**error);
+
+/**
+ * @brief Tries to write data into a shared memory buffer.
+ * @param buf #PShmBuffer to write data into.
+ * @param data Data to write.
+ * @param len Data size in bytes.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return Number of written bytes (can be 0 if buffer is full), or -1 if error
+ * occured.
+ * @since 0.0.1
+ * @note Write operation is performed only if the buffer has enough space for
+ * the given data size.
+ */
+P_LIB_API pssize	p_shm_buffer_write		(PShmBuffer	*buf,
+							 ppointer	data,
+							 psize		len,
+							 PError		**error);
+
+/**
+ * @brief Gets free space in the shared memory buffer.
+ * @param buf #PShmBuffer to check space in.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return Free space in bytes in case of success, -1 otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API pssize	p_shm_buffer_get_free_space	(PShmBuffer	*buf,
+							 PError		**error);
+
+/**
+ * @brief Gets used space in the shared memory buffer.
+ * @param buf #PShmBuffer to check space in.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return Used space in bytes in case of success, -1 otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API pssize	p_shm_buffer_get_used_space	(PShmBuffer	*buf,
+							 PError		**error);
+
+/**
+ * @brief Clears all data in the buffer and fills it with zeros.
+ * @param buf #PShmBuffer to clear.
+ * @since 0.0.1
+ */
+P_LIB_API void		p_shm_buffer_clear		(PShmBuffer	*buf);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PSHMBUFFER_H */
diff --git a/3rdparty/plibsys/src/psocket.c b/3rdparty/plibsys/src/psocket.c
new file mode 100644
index 0000000..fb04379
--- /dev/null
+++ b/3rdparty/plibsys/src/psocket.c
@@ -0,0 +1,1644 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ * Some workarounds have been used from Glib (comments are kept)
+ *
+ * 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 "pmem.h"
+#include "psocket.h"
+#ifdef P_OS_SCO
+#  include "ptimeprofiler.h"
+#endif
+#include "perror-private.h"
+#include "plibsys-private.h"
+#include "psysclose-private.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef P_OS_WIN
+#  include <fcntl.h>
+#  include <errno.h>
+#  include <unistd.h>
+#  include <signal.h>
+#  ifdef P_OS_VMS
+#    include <stropts.h>
+#  endif
+#endif
+
+#ifndef P_OS_WIN
+#  if defined (P_OS_BEOS) || defined (P_OS_MAC) || defined (P_OS_MAC9) || \
+      defined (P_OS_OS2)  || defined (P_OS_AMIGA)
+#    define P_SOCKET_USE_SELECT
+#    include <sys/select.h>
+#    include <sys/time.h>
+#  else
+#    define P_SOCKET_USE_POLL
+#    include <sys/poll.h>
+#  endif
+#endif
+
+/* On old Solaris systems SOMAXCONN is set to 5 */
+#define P_SOCKET_DEFAULT_BACKLOG	5
+
+struct PSocket_ {
+	PSocketFamily	family;
+	PSocketProtocol	protocol;
+	PSocketType	type;
+	pint		fd;
+	pint		listen_backlog;
+	pint		timeout;
+	puint		blocking	: 1;
+	puint		keepalive	: 1;
+	puint		closed		: 1;
+	puint		connected	: 1;
+	puint		listening	: 1;
+#ifdef P_OS_WIN
+	WSAEVENT	events;
+#endif
+#ifdef P_OS_SCO
+	PTimeProfiler	*timer;
+#endif
+};
+
+#ifndef SHUT_RD
+#  define SHUT_RD			0
+#endif
+
+#ifndef SHUT_WR
+#  define SHUT_WR			1
+#endif
+
+#ifndef SHUT_RDWR
+#  define SHUT_RDWR			2
+#endif
+
+#ifdef MSG_NOSIGNAL
+#  define P_SOCKET_DEFAULT_SEND_FLAGS	MSG_NOSIGNAL
+#else
+#  define P_SOCKET_DEFAULT_SEND_FLAGS	0
+#endif
+
+static pboolean pp_socket_set_fd_blocking (pint fd, pboolean blocking, PError **error);
+static pboolean pp_socket_check (const PSocket *socket, PError **error);
+static pboolean pp_socket_set_details_from_fd (PSocket *socket, PError **error);
+
+static pboolean
+pp_socket_set_fd_blocking (pint		fd,
+			   pboolean	blocking,
+			   PError	**error)
+{
+#ifndef P_OS_WIN
+	pint32 arg;
+#else
+	pulong arg;
+#endif
+
+#ifndef P_OS_WIN
+#  ifdef P_OS_VMS
+	arg = !blocking;
+#    if (PLIBSYS_SIZEOF_VOID_P == 8)
+#      pragma __pointer_size 32
+#    endif
+	/* Explicit (void *) cast is necessary */
+	if (P_UNLIKELY (ioctl (fd, FIONBIO, (void *) &arg) < 0)) {
+#    if (PLIBSYS_SIZEOF_VOID_P == 8)
+#      pragma __pointer_size 64
+#    endif
+#  else
+	if (P_UNLIKELY ((arg = fcntl (fd, F_GETFL, NULL)) < 0)) {
+		P_WARNING ("PSocket::pp_socket_set_fd_blocking: fcntl() failed");
+		arg = 0;
+	}
+
+	arg = (!blocking) ? (arg | O_NONBLOCK) : (arg & ~O_NONBLOCK);
+
+	if (P_UNLIKELY (fcntl (fd, F_SETFL, arg) < 0)) {
+#  endif
+#else
+	arg = !blocking;
+
+	if (P_UNLIKELY (ioctlsocket (fd, FIONBIO, &arg) == SOCKET_ERROR)) {
+#endif
+		p_error_set_error_p (error,
+				     (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+				     (pint) p_error_get_last_net (),
+				     "Failed to set socket blocking flags");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static pboolean
+pp_socket_check (const PSocket *socket,
+		  PError	**error)
+{
+	if (P_UNLIKELY (socket->closed))  {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_NOT_AVAILABLE,
+				     0,
+				     "Socket is already closed");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static pboolean
+pp_socket_set_details_from_fd (PSocket	*socket,
+				PError	**error)
+{
+#ifdef SO_DOMAIN
+	PSocketFamily		family;
+#endif
+	struct sockaddr_storage	address;
+	pint			fd, value;
+	socklen_t		addrlen, optlen;
+#ifdef P_OS_WIN
+	/* See comment below */
+	BOOL			bool_val = FALSE;
+#else
+	pint			bool_val;
+#endif
+
+	fd = socket->fd;
+	optlen = sizeof (value);
+
+	if (P_UNLIKELY (getsockopt (fd, SOL_SOCKET, SO_TYPE, (ppointer) &value, &optlen) != 0)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+				     (pint) p_error_get_last_net (),
+				     "Failed to call getsockopt() to get socket info for fd");
+		return FALSE;
+	}
+
+	if (P_UNLIKELY (optlen != sizeof (value))) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Failed to get socket info for fd, bad option length");
+		return FALSE;
+	}
+
+	switch (value) {
+	case SOCK_STREAM:
+		socket->type = P_SOCKET_TYPE_STREAM;
+		break;
+
+	case SOCK_DGRAM:
+		socket->type = P_SOCKET_TYPE_DATAGRAM;
+		break;
+
+#ifdef SOCK_SEQPACKET
+	case SOCK_SEQPACKET:
+		socket->type = P_SOCKET_TYPE_SEQPACKET;
+		break;
+#endif
+
+	default:
+		socket->type = P_SOCKET_TYPE_UNKNOWN;
+		break;
+	}
+
+	addrlen = sizeof (address);
+
+	if (P_UNLIKELY (getsockname (fd, (struct sockaddr *) &address, &addrlen) != 0)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+				     (pint) p_error_get_last_net (),
+				     "Failed to call getsockname() to get socket address info");
+		return FALSE;
+	}
+
+#ifdef SO_DOMAIN
+	if (!(addrlen > 0)) {
+		optlen = sizeof (family);
+
+		if (P_UNLIKELY (getsockopt (socket->fd,
+					    SOL_SOCKET,
+					    SO_DOMAIN,
+					    (ppointer) &family,
+					    &optlen) != 0)) {
+			p_error_set_error_p (error,
+					     (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+					     (pint) p_error_get_last_net (),
+					     "Failed to call getsockopt() to get socket SO_DOMAIN option");
+			return FALSE;
+		}
+	}
+#endif
+
+	switch (address.ss_family) {
+	case P_SOCKET_FAMILY_INET:
+		socket->family = P_SOCKET_FAMILY_INET;
+		break;
+#ifdef AF_INET6
+	case P_SOCKET_FAMILY_INET6:
+		socket->family = P_SOCKET_FAMILY_INET6;
+		break;
+#endif
+	default:
+		socket->family = P_SOCKET_FAMILY_UNKNOWN;
+		break;
+	}
+
+#ifdef AF_INET6
+	if (socket->family == P_SOCKET_FAMILY_INET6 || socket->family == P_SOCKET_FAMILY_INET) {
+#else
+	if (socket->family == P_SOCKET_FAMILY_INET) {
+#endif
+		switch (socket->type) {
+		case P_SOCKET_TYPE_STREAM:
+			socket->protocol = P_SOCKET_PROTOCOL_TCP;
+			break;
+		case P_SOCKET_TYPE_DATAGRAM:
+			socket->protocol = P_SOCKET_PROTOCOL_UDP;
+			break;
+		case P_SOCKET_TYPE_SEQPACKET:
+			socket->protocol = P_SOCKET_PROTOCOL_SCTP;
+			break;
+		case P_SOCKET_TYPE_UNKNOWN:
+			break;
+		}
+	}
+
+	if (P_LIKELY (socket->family != P_SOCKET_FAMILY_UNKNOWN)) {
+		addrlen = sizeof (address);
+
+		if (getpeername (fd, (struct sockaddr *) &address, &addrlen) >= 0)
+			socket->connected = TRUE;
+	}
+
+	optlen = sizeof (bool_val);
+
+	if (getsockopt (fd, SOL_SOCKET, SO_KEEPALIVE, (ppointer) &bool_val, &optlen) == 0)  {
+#ifndef P_OS_WIN
+		/* Experimentation indicates that the SO_KEEPALIVE value is
+		 * actually a char on Windows, even if documentation claims it
+		 * to be a BOOL which is a typedef for int. */
+		if (optlen != sizeof (bool_val))
+			P_WARNING ("PSocket::pp_socket_set_details_from_fd: getsockopt() with SO_KEEPALIVE failed");
+#endif
+		socket->keepalive = !!bool_val;
+	}  else
+		/* Can't read, maybe not supported, assume FALSE */
+		socket->keepalive = FALSE;
+
+	return TRUE;
+}
+
+pboolean
+p_socket_init_once (void)
+{
+#ifdef P_OS_WIN
+	WORD	ver_req;
+	WSADATA	wsa_data;
+
+	ver_req = MAKEWORD (2, 2);
+
+	if (P_UNLIKELY (WSAStartup (ver_req, &wsa_data) != 0))
+		return FALSE;
+
+	if (P_UNLIKELY (LOBYTE (wsa_data.wVersion) != 2 || HIBYTE (wsa_data.wVersion) != 2)) {
+		WSACleanup ();
+		return FALSE;
+	}
+#else
+#  ifdef SIGPIPE
+	signal (SIGPIPE, SIG_IGN);
+#  endif
+#endif
+	return TRUE;
+}
+
+void
+p_socket_close_once (void)
+{
+#ifdef P_OS_WIN
+	WSACleanup ();
+#endif
+}
+
+P_LIB_API PSocket *
+p_socket_new_from_fd (pint	fd,
+		      PError	**error)
+{
+	PSocket	*ret;
+#if !defined (P_OS_WIN) && defined (SO_NOSIGPIPE)
+	pint	flags;
+#endif
+
+	if (P_UNLIKELY (fd < 0)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Unable to create socket from bad fd");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSocket))) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for socket");
+		return NULL;
+	}
+
+	ret->fd = fd;
+
+	if (P_UNLIKELY (pp_socket_set_details_from_fd (ret, error) == FALSE)) {
+		p_free (ret);
+		return NULL;
+	}
+
+	if (P_UNLIKELY (pp_socket_set_fd_blocking (ret->fd, FALSE, error) == FALSE)) {
+		p_free (ret);
+		return NULL;
+	}
+
+#if !defined (P_OS_WIN) && defined (SO_NOSIGPIPE)
+	flags = 1;
+
+	if (setsockopt (ret->fd, SOL_SOCKET, SO_NOSIGPIPE, &flags, sizeof (flags)) < 0)
+		P_WARNING ("PSocket::p_socket_new_from_fd: setsockopt() with SO_NOSIGPIPE failed");
+#endif
+
+	p_socket_set_listen_backlog (ret, P_SOCKET_DEFAULT_BACKLOG);
+
+	ret->timeout  = 0;
+	ret->blocking = TRUE;
+
+#ifdef P_OS_SCO
+	if (P_UNLIKELY ((ret->timer = p_time_profiler_new ()) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for internal timer");
+		p_free (ret);
+		return NULL;
+	}
+#endif
+
+#ifdef P_OS_WIN
+	if (P_UNLIKELY ((ret->events = WSACreateEvent ()) == WSA_INVALID_EVENT)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_FAILED,
+				     (pint) p_error_get_last_net (),
+				     "Failed to call WSACreateEvent() on socket");
+		p_free (ret);
+		return NULL;
+	}
+#endif
+
+	return ret;
+}
+
+P_LIB_API PSocket *
+p_socket_new (PSocketFamily	family,
+	      PSocketType	type,
+	      PSocketProtocol	protocol,
+	      PError		**error)
+{
+	PSocket	*ret;
+	pint	native_type, fd;
+#ifndef P_OS_WIN
+	pint	flags;
+#endif
+
+	if (P_UNLIKELY (family   == P_SOCKET_FAMILY_UNKNOWN ||
+			type     == P_SOCKET_TYPE_UNKNOWN   ||
+			protocol == P_SOCKET_PROTOCOL_UNKNOWN)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input socket family, type or protocol");
+		return NULL;
+	}
+
+	switch (type) {
+	case P_SOCKET_TYPE_STREAM:
+		native_type = SOCK_STREAM;
+		break;
+
+	case P_SOCKET_TYPE_DATAGRAM:
+		native_type = SOCK_DGRAM;
+		break;
+
+#ifdef SOCK_SEQPACKET
+	case P_SOCKET_TYPE_SEQPACKET:
+		native_type = SOCK_SEQPACKET;
+		break;
+#endif
+
+	default:
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Unable to create socket with unknown family");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSocket))) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for socket");
+		return NULL;
+	}
+
+#ifdef P_OS_SCO
+	if (P_UNLIKELY ((ret->timer = p_time_profiler_new ()) == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_NO_RESOURCES,
+				     0,
+				     "Failed to allocate memory for internal timer");
+		p_free (ret);
+		return NULL;
+	}
+#endif
+
+#ifdef SOCK_CLOEXEC
+	native_type |= SOCK_CLOEXEC;
+#endif
+	if (P_UNLIKELY ((fd = (pint) socket (family, native_type, protocol)) < 0)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+				     (pint) p_error_get_last_net (),
+				     "Failed to call socket() to create socket");
+#ifdef P_OS_SCO
+		p_time_profiler_free (ret->timer);
+#endif
+		p_free (ret);
+		return NULL;
+	}
+
+#ifndef P_OS_WIN
+	flags = fcntl (fd, F_GETFD, 0);
+
+	if (P_LIKELY (flags != -1 && (flags & FD_CLOEXEC) == 0)) {
+		flags |= FD_CLOEXEC;
+
+		if (P_UNLIKELY (fcntl (fd, F_SETFD, flags) < 0))
+			P_WARNING ("PSocket::p_socket_new: fcntl() with FD_CLOEXEC failed");
+	}
+#endif
+
+	ret->fd = fd;
+
+#ifdef P_OS_WIN
+	ret->events = WSA_INVALID_EVENT;
+#endif
+
+	if (P_UNLIKELY (pp_socket_set_fd_blocking (ret->fd, FALSE, error) == FALSE)) {
+		p_socket_free (ret);
+		return NULL;
+	}
+
+#if !defined (P_OS_WIN) && defined (SO_NOSIGPIPE)
+	flags = 1;
+
+	if (setsockopt (ret->fd, SOL_SOCKET, SO_NOSIGPIPE, &flags, sizeof (flags)) < 0)
+		P_WARNING ("PSocket::p_socket_new: setsockopt() with SO_NOSIGPIPE failed");
+#endif
+
+	ret->timeout  = 0;
+	ret->blocking = TRUE;
+	ret->family   = family;
+	ret->protocol = protocol;
+	ret->type     = type;
+
+	p_socket_set_listen_backlog (ret, P_SOCKET_DEFAULT_BACKLOG);
+
+#ifdef P_OS_WIN
+	if (P_UNLIKELY ((ret->events = WSACreateEvent ()) == WSA_INVALID_EVENT)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_FAILED,
+				     (pint) p_error_get_last_net (),
+				     "Failed to call WSACreateEvent() on socket");
+		p_socket_free (ret);
+		return NULL;
+	}
+#endif
+
+	return ret;
+}
+
+P_LIB_API pint
+p_socket_get_fd (const PSocket *socket)
+{
+	if (P_UNLIKELY (socket == NULL))
+		return -1;
+
+	return socket->fd;
+}
+
+P_LIB_API PSocketFamily
+p_socket_get_family (const PSocket *socket)
+{
+	if (P_UNLIKELY (socket == NULL))
+		return P_SOCKET_FAMILY_UNKNOWN;
+
+	return socket->family;
+}
+
+P_LIB_API PSocketType
+p_socket_get_type (const PSocket *socket)
+{
+	if (P_UNLIKELY (socket == NULL))
+		return P_SOCKET_TYPE_UNKNOWN;
+
+	return socket->type;
+}
+
+P_LIB_API PSocketProtocol
+p_socket_get_protocol (const PSocket *socket)
+{
+	if (P_UNLIKELY (socket == NULL))
+		return P_SOCKET_PROTOCOL_UNKNOWN;
+
+	return socket->protocol;
+}
+
+P_LIB_API pboolean
+p_socket_get_keepalive (const PSocket *socket)
+{
+	if (P_UNLIKELY (socket == NULL))
+		return FALSE;
+
+	return socket->keepalive;
+}
+
+P_LIB_API pboolean
+p_socket_get_blocking (PSocket *socket)
+{
+	if (P_UNLIKELY (socket == NULL))
+		return FALSE;
+
+	return socket->blocking;
+}
+
+P_LIB_API int
+p_socket_get_listen_backlog (const PSocket *socket)
+{
+	if (P_UNLIKELY (socket == NULL))
+		return -1;
+
+	return socket->listen_backlog;
+}
+
+P_LIB_API pint
+p_socket_get_timeout (const PSocket *socket)
+{
+	if (P_UNLIKELY (socket == NULL))
+		return -1;
+
+	return socket->timeout;
+}
+
+P_LIB_API PSocketAddress *
+p_socket_get_local_address (const PSocket	*socket,
+			    PError		**error)
+{
+	struct sockaddr_storage	buffer;
+	socklen_t		len;
+	PSocketAddress		*ret;
+
+	if (P_UNLIKELY (socket == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return NULL;
+	}
+
+	len = sizeof (buffer);
+
+	if (P_UNLIKELY (getsockname (socket->fd, (struct sockaddr *) &buffer, &len) < 0)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+				     (pint) p_error_get_last_net (),
+				     "Failed to call getsockname() to get local socket address");
+		return NULL;
+	}
+
+	ret = p_socket_address_new_from_native (&buffer, (psize) len);
+
+	if (P_UNLIKELY (ret == NULL))
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_FAILED,
+				     0,
+				     "Failed to create socket address from native structure");
+
+	return ret;
+}
+
+P_LIB_API PSocketAddress *
+p_socket_get_remote_address (const PSocket	*socket,
+			     PError		**error)
+{
+	struct sockaddr_storage	buffer;
+	socklen_t 		len;
+	PSocketAddress		*ret;
+
+	if (P_UNLIKELY (socket == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return NULL;
+	}
+
+	len = sizeof (buffer);
+
+	if (P_UNLIKELY (getpeername (socket->fd, (struct sockaddr *) &buffer, &len) < 0)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+				     (pint) p_error_get_last_net (),
+				     "Failed to call getpeername() to get remote socket address");
+		return NULL;
+	}
+
+#ifdef P_OS_SYLLABLE
+	/* Syllable has a bug with a wrong byte order for a TCP port,
+	 * as it only supports IPv4 we can easily fix it here. */
+	((struct sockaddr_in *) &buffer)->sin_port =
+			p_htons (((struct sockaddr_in *) &buffer)->sin_port);
+#endif
+
+	ret = p_socket_address_new_from_native (&buffer, (psize) len);
+
+	if (P_UNLIKELY (ret == NULL))
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_FAILED,
+				     0,
+				     "Failed to create socket address from native structure");
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_socket_is_connected (const PSocket *socket)
+{
+	if (P_UNLIKELY (socket == NULL))
+		return FALSE;
+
+	return socket->connected;
+}
+
+P_LIB_API pboolean
+p_socket_is_closed (const PSocket *socket)
+{
+	if (P_UNLIKELY (socket == NULL))
+		return TRUE;
+
+	return socket->closed;
+}
+
+P_LIB_API pboolean
+p_socket_check_connect_result (PSocket  *socket,
+			       PError	**error)
+{
+	socklen_t	optlen;
+	pint		val;
+
+	if (P_UNLIKELY (socket == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	optlen = sizeof (val);
+
+	if (P_UNLIKELY (getsockopt (socket->fd, SOL_SOCKET, SO_ERROR, (ppointer) &val, &optlen) < 0)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+				     (pint) p_error_get_last_net (),
+				     "Failed to call getsockopt() to get connection status");
+		return FALSE;
+	}
+
+	if (P_UNLIKELY (val != 0))
+		p_error_set_error_p (error,
+				     (pint) p_error_get_io_from_system (val),
+				     val,
+				     "Error in socket layer");
+
+	socket->connected = (val == 0);
+
+	return (val == 0);
+}
+
+P_LIB_API void
+p_socket_set_keepalive (PSocket		*socket,
+			pboolean	keepalive)
+{
+#ifdef P_OS_WIN
+	pchar value;
+#else
+	pint value;
+#endif
+
+	if (P_UNLIKELY (socket == NULL))
+		return;
+
+	if (socket->keepalive == (puint) !!keepalive)
+		return;
+
+#ifdef P_OS_WIN
+	value = !! (pchar) keepalive;
+#else
+	value = !! (pint) keepalive;
+#endif
+	if (setsockopt (socket->fd, SOL_SOCKET, SO_KEEPALIVE, &value, sizeof (value)) < 0) {
+		P_WARNING ("PSocket::p_socket_set_keepalive: setsockopt() with SO_KEEPALIVE failed");
+		return;
+	}
+
+	socket->keepalive = !! (pint) keepalive;
+}
+
+P_LIB_API void
+p_socket_set_blocking (PSocket	*socket,
+		       pboolean	blocking)
+{
+	if (P_UNLIKELY (socket == NULL))
+		return;
+
+	socket->blocking = !! blocking;
+}
+
+P_LIB_API void
+p_socket_set_listen_backlog (PSocket	*socket,
+			     pint	backlog)
+{
+	if (P_UNLIKELY (socket == NULL || socket->listening))
+		return;
+
+	socket->listen_backlog = backlog;
+}
+
+P_LIB_API void
+p_socket_set_timeout (PSocket	*socket,
+		      pint	timeout)
+{
+	if (P_UNLIKELY (socket == NULL))
+		return;
+
+	if (timeout < 0)
+		timeout = 0;
+
+	socket->timeout = timeout;
+}
+
+P_LIB_API pboolean
+p_socket_bind (const PSocket	*socket,
+	       PSocketAddress	*address,
+	       pboolean		allow_reuse,
+	       PError		**error)
+{
+	struct sockaddr_storage	addr;
+
+#ifdef SO_REUSEPORT
+	pboolean		reuse_port;
+#endif
+
+#ifdef P_OS_WIN
+	pchar			value;
+#else
+	pint			value;
+#endif
+
+	if (P_UNLIKELY (socket == NULL || address == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE))
+		return FALSE;
+
+	/* Windows allows to reuse the same address even for an active TCP
+	 * connection, that's why on Windows we should use SO_REUSEADDR only
+	 * for UDP sockets, UNIX doesn't have such behavior
+	 *
+	 * Ignore errors here, the only likely error is "not supported", and
+	 * this is a "best effort" thing mainly */
+
+#ifdef P_OS_WIN
+	value = !! (pchar) (allow_reuse && (socket->type == P_SOCKET_TYPE_DATAGRAM));
+#else
+	value = !! (pint) allow_reuse;
+#endif
+
+	if (setsockopt (socket->fd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof (value)) < 0)
+		P_WARNING ("PSocket::p_socket_bind: setsockopt() with SO_REUSEADDR failed");
+
+#ifdef SO_REUSEPORT
+	reuse_port = allow_reuse && (socket->type == P_SOCKET_TYPE_DATAGRAM);
+
+#  ifdef P_OS_WIN
+	value = !! (pchar) reuse_port;
+#  else
+	value = !! (pint) reuse_port;
+#  endif
+
+	if (setsockopt (socket->fd, SOL_SOCKET, SO_REUSEPORT, &value, sizeof (value)) < 0)
+		P_WARNING ("PSocket::p_socket_bind: setsockopt() with SO_REUSEPORT failed");
+#endif
+
+	if (P_UNLIKELY (p_socket_address_to_native (address, &addr, sizeof (addr)) == FALSE)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_FAILED,
+				     0,
+				     "Failed to convert socket address to native structure");
+		return FALSE;
+	}
+
+	if (P_UNLIKELY (bind (socket->fd,
+			      (struct sockaddr *) &addr,
+			      (socklen_t) p_socket_address_get_native_size (address)) < 0)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+				     (pint) p_error_get_last_net (),
+				     "Failed to call bind() on socket");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+P_LIB_API pboolean
+p_socket_connect (PSocket		*socket,
+		  PSocketAddress	*address,
+		  PError		**error)
+{
+	struct sockaddr_storage	buffer;
+	pint			err_code;
+	pint			conn_result;
+	PErrorIO		sock_err;
+
+	if (P_UNLIKELY (socket == NULL || address == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE))
+		return FALSE;
+
+	if (P_UNLIKELY (p_socket_address_to_native (address, &buffer, sizeof (buffer)) == FALSE)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_FAILED,
+				     0,
+				     "Failed to convert socket address to native structure");
+		return FALSE;
+	}
+
+#if !defined (P_OS_WIN) && defined (EINTR)
+	for (;;) {
+		conn_result = connect (socket->fd, (struct sockaddr *) &buffer,
+				       (socklen_t) p_socket_address_get_native_size (address));
+
+		if (P_LIKELY (conn_result == 0))
+			break;
+
+		err_code = p_error_get_last_net ();
+
+		if (err_code == EINTR)
+			continue;
+		else
+			break;
+	}
+#else
+	conn_result = connect (socket->fd, (struct sockaddr *) &buffer,
+			       (pint) p_socket_address_get_native_size (address));
+
+	if (conn_result != 0)
+		err_code = p_error_get_last_net ();
+#endif
+
+	if (conn_result == 0) {
+		socket->connected = TRUE;
+		return TRUE;
+	}
+
+	sock_err = p_error_get_io_from_system (err_code);
+
+	if (P_LIKELY (sock_err == P_ERROR_IO_WOULD_BLOCK || sock_err == P_ERROR_IO_IN_PROGRESS)) {
+		if (socket->blocking) {
+			if (p_socket_io_condition_wait (socket,
+							P_SOCKET_IO_CONDITION_POLLOUT,
+							error) == TRUE &&
+			    p_socket_check_connect_result (socket, error) == TRUE) {
+				socket->connected = TRUE;
+				return TRUE;
+			}
+		} else
+			p_error_set_error_p (error,
+					     (pint) sock_err,
+					     err_code,
+					     "Couldn't block non-blocking socket");
+	} else
+		p_error_set_error_p (error,
+				     (pint) sock_err,
+				     err_code,
+				     "Failed to call connect() on socket");
+
+	return FALSE;
+}
+
+P_LIB_API pboolean
+p_socket_listen (PSocket	*socket,
+		 PError		**error)
+{
+	if (P_UNLIKELY (socket == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE))
+		return FALSE;
+
+	if (P_UNLIKELY (listen (socket->fd, socket->listen_backlog) < 0)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+				     (pint) p_error_get_last_net (),
+				     "Failed to call listen() on socket");
+		return FALSE;
+	}
+
+	socket->listening = TRUE;
+	return TRUE;
+}
+
+P_LIB_API PSocket *
+p_socket_accept (const PSocket	*socket,
+		 PError		**error)
+{
+	PSocket		*ret;
+	PErrorIO	sock_err;
+	pint		res;
+	pint		err_code;
+#ifndef P_OS_WIN
+	pint		flags;
+#endif
+
+	if (P_UNLIKELY (socket == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return NULL;
+	}
+
+	if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE))
+		return NULL;
+
+	for (;;) {
+		if (socket->blocking &&
+		    p_socket_io_condition_wait (socket,
+						P_SOCKET_IO_CONDITION_POLLIN,
+						error) == FALSE)
+			return NULL;
+
+		if ((res = (pint) accept (socket->fd, NULL, 0)) < 0) {
+			err_code = p_error_get_last_net ();
+#if !defined (P_OS_WIN) && defined (EINTR)
+			if (p_error_get_last_net () == EINTR)
+				continue;
+#endif
+			sock_err = p_error_get_io_from_system (err_code);
+
+			if (socket->blocking && sock_err == P_ERROR_IO_WOULD_BLOCK)
+				continue;
+
+			p_error_set_error_p (error,
+					     (pint) sock_err,
+					     err_code,
+					     "Failed to call accept() on socket");
+
+			return NULL;
+		}
+
+		break;
+	}
+
+#ifdef P_OS_WIN
+	/* The socket inherits the accepting sockets event mask and even object,
+	 * we need to remove that */
+	WSAEventSelect (res, NULL, 0);
+#else
+	flags = fcntl (res, F_GETFD, 0);
+
+	if (P_LIKELY (flags != -1 && (flags & FD_CLOEXEC) == 0)) {
+		flags |= FD_CLOEXEC;
+
+		if (P_UNLIKELY (fcntl (res, F_SETFD, flags) < 0))
+			P_WARNING ("PSocket::p_socket_accept: fcntl() with FD_CLOEXEC failed");
+	}
+#endif
+
+	if (P_UNLIKELY ((ret = p_socket_new_from_fd (res, error)) == NULL)) {
+		if (P_UNLIKELY (p_sys_close (res) != 0))
+			P_WARNING ("PSocket::p_socket_accept: p_sys_close() failed");
+	} else
+		ret->protocol = socket->protocol;
+
+	return ret;
+}
+
+P_LIB_API pssize
+p_socket_receive (const PSocket	*socket,
+		  pchar		*buffer,
+		  psize		buflen,
+		  PError	**error)
+{
+	PErrorIO	sock_err;
+	pssize		ret;
+	pint		err_code;
+
+	if (P_UNLIKELY (socket == NULL || buffer == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return -1;
+	}
+
+	if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE))
+		return -1;
+
+	for (;;) {
+		if (socket->blocking &&
+		    p_socket_io_condition_wait (socket,
+						P_SOCKET_IO_CONDITION_POLLIN,
+						error) == FALSE)
+			return -1;
+
+		if ((ret = recv (socket->fd, buffer, (socklen_t) buflen, 0)) < 0) {
+			err_code = p_error_get_last_net ();
+
+#if !defined (P_OS_WIN) && defined (EINTR)
+			if (err_code == EINTR)
+				continue;
+#endif
+			sock_err = p_error_get_io_from_system (err_code);
+
+			if (socket->blocking && sock_err == P_ERROR_IO_WOULD_BLOCK)
+				continue;
+
+			p_error_set_error_p (error,
+					     (pint) sock_err,
+					     err_code,
+					     "Failed to call recv() on socket");
+
+			return -1;
+		}
+
+		break;
+	}
+
+	return ret;
+}
+
+P_LIB_API pssize
+p_socket_receive_from (const PSocket	*socket,
+		       PSocketAddress	**address,
+		       pchar		*buffer,
+		       psize		buflen,
+		       PError		**error)
+{
+	PErrorIO		sock_err;
+	struct sockaddr_storage sa;
+	socklen_t		optlen;
+	pssize			ret;
+	pint			err_code;
+
+	if (P_UNLIKELY (socket == NULL || buffer == NULL || buflen == 0)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return -1;
+	}
+
+	if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE))
+		return -1;
+
+	optlen = sizeof (sa);
+
+	for (;;) {
+		if (socket->blocking &&
+		    p_socket_io_condition_wait (socket,
+						P_SOCKET_IO_CONDITION_POLLIN,
+						error) == FALSE)
+			return -1;
+
+		if ((ret = recvfrom (socket->fd,
+				     buffer,
+				     (socklen_t) buflen,
+				     0,
+				     (struct sockaddr *) &sa,
+				     &optlen)) < 0) {
+			err_code = p_error_get_last_net ();
+
+#if !defined (P_OS_WIN) && defined (EINTR)
+			if (err_code == EINTR)
+				continue;
+#endif
+			sock_err = p_error_get_io_from_system (err_code);
+
+			if (socket->blocking && sock_err == P_ERROR_IO_WOULD_BLOCK)
+				continue;
+
+			p_error_set_error_p (error,
+					     (pint) sock_err,
+					     err_code,
+					     "Failed to call recvfrom() on socket");
+
+			return -1;
+		}
+
+		break;
+	}
+
+	if (address != NULL)
+		*address = p_socket_address_new_from_native (&sa, optlen);
+
+	return ret;
+}
+
+P_LIB_API pssize
+p_socket_send (const PSocket	*socket,
+	       const pchar	*buffer,
+	       psize		buflen,
+	       PError		**error)
+{
+	PErrorIO	sock_err;
+	pssize		ret;
+	pint		err_code;
+
+	if (P_UNLIKELY (socket == NULL || buffer == NULL || buflen == 0)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return -1;
+	}
+
+	if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE))
+		return -1;
+
+	for (;;) {
+		if (socket->blocking &&
+		    p_socket_io_condition_wait (socket,
+						P_SOCKET_IO_CONDITION_POLLOUT,
+						error) == FALSE)
+			return -1;
+
+		if ((ret = send (socket->fd,
+				 buffer,
+				 (socklen_t) buflen,
+				 P_SOCKET_DEFAULT_SEND_FLAGS)) < 0) {
+			err_code = p_error_get_last_net ();
+
+#if !defined (P_OS_WIN) && defined (EINTR)
+			if (err_code == EINTR)
+				continue;
+#endif
+			sock_err = p_error_get_io_from_system (err_code);
+
+			if (socket->blocking && sock_err == P_ERROR_IO_WOULD_BLOCK)
+				continue;
+
+			p_error_set_error_p (error,
+					     (pint) sock_err,
+					     err_code,
+					     "Failed to call send() on socket");
+
+			return -1;
+		}
+
+		break;
+	}
+
+	return ret;
+}
+
+P_LIB_API pssize
+p_socket_send_to (const PSocket		*socket,
+		  PSocketAddress	*address,
+		  const pchar		*buffer,
+		  psize			buflen,
+		  PError		**error)
+{
+	PErrorIO		sock_err;
+	struct sockaddr_storage sa;
+	socklen_t		optlen;
+	pssize			ret;
+	pint			err_code;
+
+	if (!socket || !address || !buffer) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return -1;
+	}
+
+	if (!pp_socket_check (socket, error))
+		return -1;
+
+	if (!p_socket_address_to_native (address, &sa, sizeof (sa))) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_FAILED,
+				     0,
+				     "Failed to convert socket address to native structure");
+		return -1;
+	}
+
+	optlen = (socklen_t) p_socket_address_get_native_size (address);
+
+	for (;;) {
+		if (socket->blocking &&
+		    p_socket_io_condition_wait (socket, P_SOCKET_IO_CONDITION_POLLOUT, error) == FALSE)
+			return -1;
+
+		if ((ret = sendto (socket->fd,
+				   buffer,
+				   (socklen_t) buflen,
+				   0,
+				   (struct sockaddr *) &sa,
+				   optlen)) < 0) {
+			err_code = p_error_get_last_net ();
+
+#if !defined (P_OS_WIN) && defined (EINTR)
+			if (err_code == EINTR)
+				continue;
+#endif
+			sock_err = p_error_get_io_from_system (err_code);
+
+			if (socket->blocking && sock_err == P_ERROR_IO_WOULD_BLOCK)
+				continue;
+
+			p_error_set_error_p (error,
+					     (pint) sock_err,
+					     err_code,
+					     "Failed to call sendto() on socket");
+
+			return -1;
+		}
+
+		break;
+	}
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_socket_close (PSocket	*socket,
+		PError	**error)
+{
+	pint err_code;
+
+	if (P_UNLIKELY (socket == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	if (socket->closed)
+		return TRUE;
+
+	if (P_LIKELY (p_sys_close (socket->fd) == 0)) {
+		socket->connected = FALSE;
+		socket->closed    = TRUE;
+		socket->listening = FALSE;
+		socket->fd        = -1;
+
+		return TRUE;
+	} else {
+		err_code = p_error_get_last_net ();
+
+		p_error_set_error_p (error,
+				     (pint) p_error_get_io_from_system (err_code),
+				     err_code,
+				     "Failed to close socket");
+
+		return FALSE;
+	}
+}
+
+P_LIB_API pboolean
+p_socket_shutdown (PSocket	*socket,
+		   pboolean	shutdown_read,
+		   pboolean	shutdown_write,
+		   PError	**error)
+{
+	pint how;
+
+	if (P_UNLIKELY (socket == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE))
+		return FALSE;
+
+	if (P_UNLIKELY (shutdown_read == FALSE && shutdown_write == FALSE))
+		return TRUE;
+
+#ifndef P_OS_WIN
+	if (shutdown_read == TRUE && shutdown_write == TRUE)
+		how = SHUT_RDWR;
+	else if (shutdown_read == TRUE)
+		how = SHUT_RD;
+	else
+		how = SHUT_WR;
+#else
+	if (shutdown_read == TRUE && shutdown_write == TRUE)
+		how = SD_BOTH;
+	else if (shutdown_read == TRUE)
+		how = SD_RECEIVE;
+	else
+		how = SD_SEND;
+#endif
+
+	if (P_UNLIKELY (shutdown (socket->fd, how) != 0)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+				     (pint) p_error_get_last_net (),
+				     "Failed to call shutdown() on socket");
+		return FALSE;
+	}
+
+	if (shutdown_read == TRUE && shutdown_write == TRUE)
+		socket->connected = FALSE;
+
+	return TRUE;
+}
+
+P_LIB_API void
+p_socket_free (PSocket *socket)
+{
+	if (P_UNLIKELY (socket == NULL))
+		return;
+
+#ifdef P_OS_WIN
+	if (P_LIKELY (socket->events != WSA_INVALID_EVENT))
+		WSACloseEvent (socket->events);
+#endif
+
+	p_socket_close (socket, NULL);
+
+#ifdef P_OS_SCO
+	if (P_LIKELY (socket->timer != NULL))
+		p_time_profiler_free (socket->timer);
+#endif
+
+	p_free (socket);
+}
+
+P_LIB_API pboolean
+p_socket_set_buffer_size (const PSocket		*socket,
+			  PSocketDirection	dir,
+			  psize			size,
+			  PError		**error)
+{
+	pint	optname;
+	pint	optval;
+
+	if (P_UNLIKELY (socket == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE))
+		return FALSE;
+
+	optname = (dir == P_SOCKET_DIRECTION_RCV) ? SO_RCVBUF : SO_SNDBUF;
+	optval  = (pint) size;
+
+	if (P_UNLIKELY (setsockopt (socket->fd,
+				    SOL_SOCKET,
+				    optname,
+				    (pconstpointer) &optval,
+				    sizeof (optval)) != 0)) {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+				     (pint) p_error_get_last_net (),
+				     "Failed to call setsockopt() on socket to set buffer size");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+P_LIB_API pboolean
+p_socket_io_condition_wait (const PSocket	*socket,
+			    PSocketIOCondition	condition,
+			    PError		**error)
+{
+#if defined (P_OS_WIN)
+	long	network_events;
+	pint	evret;
+	pint	timeout;
+
+	if (P_UNLIKELY (socket == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE))
+		return FALSE;
+
+	timeout = socket->timeout > 0 ? socket->timeout : WSA_INFINITE;
+
+	if (condition == P_SOCKET_IO_CONDITION_POLLIN)
+		network_events = FD_READ | FD_ACCEPT;
+	else
+		network_events = FD_WRITE | FD_CONNECT;
+
+	WSAResetEvent (socket->events);
+	WSAEventSelect (socket->fd, socket->events, network_events);
+
+	evret = WSAWaitForMultipleEvents (1, (const HANDLE *) &socket->events, TRUE, timeout, FALSE);
+
+	if (evret == WSA_WAIT_EVENT_0)
+		return TRUE;
+	else if (evret == WSA_WAIT_TIMEOUT) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_TIMED_OUT,
+				     (pint) p_error_get_last_net (),
+				     "Timed out while waiting socket condition");
+		return FALSE;
+	} else {
+		p_error_set_error_p (error,
+				     (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+				     (pint) p_error_get_last_net (),
+				     "Failed to call WSAWaitForMultipleEvents() on socket");
+		return FALSE;
+	}
+#elif defined (P_SOCKET_USE_POLL)
+	struct pollfd	pfd;
+	pint		evret;
+	pint		timeout;
+
+	if (P_UNLIKELY (socket == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE))
+		return FALSE;
+
+	timeout = socket->timeout > 0 ? socket->timeout : -1;
+
+	pfd.fd = socket->fd;
+	pfd.revents = 0;
+
+	if (condition == P_SOCKET_IO_CONDITION_POLLIN)
+		pfd.events = POLLIN;
+	else
+		pfd.events = POLLOUT;
+
+#  ifdef P_OS_SCO
+	p_time_profiler_reset (socket->timer);
+#  endif
+
+	while (TRUE) {
+		evret = poll (&pfd, 1, timeout);
+
+#  ifdef EINTR
+		if (evret == -1 && p_error_get_last_net () == EINTR) {
+#    ifdef P_OS_SCO
+			if (timeout < 0 ||
+			    (p_time_profiler_elapsed_usecs (socket->timer) / 1000) < (puint64) timeout)
+				continue;
+			else
+				evret = 0;
+#    else
+			continue;
+#    endif
+		}
+#  endif
+
+		if (evret == 1)
+			return TRUE;
+		else if (evret == 0) {
+			p_error_set_error_p (error,
+					     (pint) P_ERROR_IO_TIMED_OUT,
+					     (pint) p_error_get_last_net (),
+					     "Timed out while waiting socket condition");
+			return FALSE;
+		} else {
+			p_error_set_error_p (error,
+					     (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+					     (pint) p_error_get_last_net (),
+					     "Failed to call poll() on socket");
+			return FALSE;
+		}
+	}
+#else
+	fd_set			fds;
+	struct timeval		tv;
+	struct timeval *	ptv;
+	pint			evret;
+
+	if (P_UNLIKELY (socket == NULL)) {
+		p_error_set_error_p (error,
+				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
+				     0,
+				     "Invalid input argument");
+		return FALSE;
+	}
+
+	if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE))
+		return FALSE;
+
+	if (socket->timeout > 0)
+		ptv = &tv;
+	else
+		ptv = NULL;
+
+	while (TRUE) {
+		FD_ZERO (&fds);
+		FD_SET (socket->fd, &fds);
+
+		if (socket->timeout > 0) {
+			tv.tv_sec  = socket->timeout / 1000;
+			tv.tv_usec = (socket->timeout % 1000) * 1000;
+		}
+
+		if (condition == P_SOCKET_IO_CONDITION_POLLIN)
+			evret = select (socket->fd + 1, &fds, NULL, NULL, ptv);
+		else
+			evret = select (socket->fd + 1, NULL, &fds, NULL, ptv);
+
+#ifdef EINTR
+		if (evret == -1 && p_error_get_last_net () == EINTR)
+			continue;
+#endif
+
+		if (evret == 1)
+			return TRUE;
+		else if (evret == 0) {
+			p_error_set_error_p (error,
+					     (pint) P_ERROR_IO_TIMED_OUT,
+					     (pint) p_error_get_last_net (),
+					     "Timed out while waiting socket condition");
+			return FALSE;
+		} else {
+			p_error_set_error_p (error,
+					     (pint) p_error_get_io_from_system (p_error_get_last_net ()),
+					     (pint) p_error_get_last_net (),
+					     "Failed to call select() on socket");
+			return FALSE;
+		}
+	}
+#endif
+}
diff --git a/3rdparty/plibsys/src/psocket.h b/3rdparty/plibsys/src/psocket.h
new file mode 100644
index 0000000..989e707
--- /dev/null
+++ b/3rdparty/plibsys/src/psocket.h
@@ -0,0 +1,779 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file psocket.h
+ * @brief Socket implementation
+ * @author Alexander Saprykin
+ *
+ * A socket is a communication primitive usually working over a network. You can
+ * send data to someone's socket by its address and receive data as well through
+ * the same socket. This is one of the most popular and standardizated way for
+ * network communication supported by vast majority of all the modern operating
+ * systems. It also hides all the details of underlying networking protocols and
+ * other layers, providing a unified and transparent approach for communication.
+ *
+ * There are two kinds of socket:
+ * - connection oriented (or stream sockets, i.e. TCP);
+ * - connection-less (or datagram sockets, i.e. UDP).
+ *
+ * Connection oriented sockets work with data in a stream, connection-less
+ * sockets work with data using independent packets (datagrams). The former
+ * guarantees delivery, while the latter doesn't (actually some connection-less
+ * protocols provide delivery quarantee, i.e. SCTP).
+ *
+ * #PSocket supports INET and INET6 address families which specify network
+ * communication addresses used by created sockets: IPv4 and IPv6,
+ * correspondingly. INET6 family is not supported on all platforms, refer to
+ * documentation for a particular target platform.
+ *
+ * #PSocket supports different underlying data transfer protocols: TCP, UDP and
+ * others. Note that not all protocols can be used with any socket type, i.e.
+ * you can use the TCP protocol with a stream socket, but you can't use the UDP
+ * protocol with the stream socket. You can specify #P_SOCKET_PROTOCOL_DEFAULT
+ * protocol when creating a socket and appropriate the best matching socket type
+ * will be selected.
+ *
+ * In a common socket communication case server and client sides are involved.
+ * Depending on whether sockets are connection oriented, there are slightly
+ * different action sequences for data exchanging.
+ *
+ * For connection oriented sockets the server side acts as following:
+ * - creates a socket using p_socket_new();
+ * - binds the socket to a particular local address using p_socket_bind();
+ * - starts to listen incoming connections using p_socket_listen();
+ * - takes an incoming connection from the internal queue using
+ * p_socket_accept().
+ *
+ * The client side acts as following:
+ * - creates a socket using p_socket_new();
+ * - binds the socket to a particular local address using p_socket_bind();
+ * - connects to the server using p_socket_connect().
+ *
+ * After the connection was successfully established, both the sides can send
+ * and receive data from each other using p_socket_send() and
+ * p_socket_receive(). Binding of the client socket is actually optional.
+ *
+ * When using connection-less sockets, all is a bit simpler. There is no server
+ * side or client side - anyone can send and receive data without establishing a
+ * connection. Just create a socket, bind it to a local address and send/receive
+ * data using p_socket_send_to() and p_socket_receive(). You can also call
+ * p_socket_connect() on a connection-less socket to prevent passing the target
+ * address each time when sending data and then use p_socket_send() instead of
+ * p_socket_send_to(). This time binding is required.
+ *
+ * #PSocket can operate in blocking and non-blocking (async) modes. By default
+ * it is in the blocking mode. When using #PSocket in the blocking mode each
+ * non-immediate call on it will block a caller thread until an I/O operation
+ * will be completed. For example, the p_socket_accept() call can wait for an
+ * incoming connection for some time, and calling it on a blocking socket will
+ * prevent the caller thread from further execution until it receives a new
+ * incoming connection. In the non-blocking mode any call will return
+ * immediately and you must check its result. You can set the socket mode using
+ * p_socket_set_blocking().
+ *
+ * #PSocket always puts a socket descriptor (or SOCKET handle on Windows) into
+ * the non-blocking mode and emulates the blocking mode if required. If you need
+ * to perform some hacks and need blocking behavior from the descriptor for some
+ * reason, use p_socket_get_fd() to get an internal socket descriptor (SOCKET
+ * handle on Windows).
+ *
+ * The close-on-exec flag is always set on the socket desciptor. Use
+ * p_socket_get_fd() to overwrite this behavior.
+ *
+ * #PSocket ignores the SIGPIPE signal on UNIX systems if possible. Take it into
+ * account if you want to handle this signal.
+ *
+ * Note that before using the #PSocket API you must call p_libsys_init() in
+ * order to initialize system resources (on UNIX this will do nothing, but on
+ * Windows this routine is required). Usually this routine should be called on a
+ * program's start.
+ *
+ * Here is an example of #PSocket usage:
+ * @code
+ * PSocketAddress *addr;
+ * PSocket	  *sock;
+ *
+ * p_libsys_init ();
+ * ...
+ * if ((addr = p_socket_address_new ("127.0.0.1", 5432)) == NULL) {
+ *	...
+ * }
+ *
+ * if ((sock = p_socket_new (P_SOCKET_FAMILY_INET,
+ *			     P_SOCKET_TYPE_DATAGRAM,
+ *			     P_SOCKET_PROTOCOL_UDP)) == NULL) {
+ *	p_socket_address_free (addr);
+ *	...
+ * }
+ *
+ * if (!p_socket_bind (sock, addr, FALSE)) {
+ *	p_socket_address_free(addr);
+ *	p_socket_free(sock);
+ *	...
+ * }
+ *
+ * ...
+ * p_socket_address_free (addr);
+ * p_socket_close (sock);
+ * p_socket_free (sock);
+ * p_libsys_shutdown ();
+ * @endcode
+ * Here a UDP socket was created, bound to the localhost address and the port
+ * @a 5432. Do not forget to close the socket and free memory after its usage.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PSOCKET_H
+#define PLIBSYS_HEADER_PSOCKET_H
+
+#include <pmacros.h>
+#include <psocketaddress.h>
+#include <perror.h>
+
+P_BEGIN_DECLS
+
+/** Socket protocols specified by the IANA.  */
+typedef enum PSocketProtocol_ {
+	P_SOCKET_PROTOCOL_UNKNOWN	= -1,	/**< Unknown protocol.	*/
+	P_SOCKET_PROTOCOL_DEFAULT	= 0,	/**< Default protocol.	*/
+	P_SOCKET_PROTOCOL_TCP		= 6,	/**< TCP protocol.	*/
+	P_SOCKET_PROTOCOL_UDP		= 17,	/**< UDP protocol.	*/
+	P_SOCKET_PROTOCOL_SCTP		= 132	/**< SCTP protocol.	*/
+} PSocketProtocol;
+
+/** Socket types. */
+typedef enum PSocketType_ {
+	P_SOCKET_TYPE_UNKNOWN		= 0,	/**< Unknown type.							*/
+	P_SOCKET_TYPE_STREAM		= 1,	/**< Connection oritented, reliable, stream of bytes (i.e. TCP).	*/
+	P_SOCKET_TYPE_DATAGRAM		= 2,	/**< Connection-less, unreliable, datagram passing (i.e. UDP).		*/
+	P_SOCKET_TYPE_SEQPACKET		= 3	/**< Connection-less, reliable, datagram passing (i.e. SCTP).		*/
+} PSocketType;
+
+/** Socket direction for data operations. */
+typedef enum PSocketDirection_ {
+	P_SOCKET_DIRECTION_SND		= 0,	/**< Send direction.	*/
+	P_SOCKET_DIRECTION_RCV		= 1	/**< Receive direction.	*/
+} PSocketDirection;
+
+/** Socket IO waiting (polling) conditions. */
+typedef enum PSocketIOCondition_ {
+	P_SOCKET_IO_CONDITION_POLLIN	= 1,	/**< Ready to read.	*/
+	P_SOCKET_IO_CONDITION_POLLOUT	= 2	/**< Ready to write.	*/
+} PSocketIOCondition;
+
+/** Socket opaque structure. */
+typedef struct PSocket_ PSocket;
+
+/**
+ * @brief Creates a new #PSocket object from a file descriptor.
+ * @param fd File descriptor to create the socket from.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return Pointer to #PSocket in case of success, NULL otherwise.
+ * @since 0.0.1
+ * @sa p_socket_new(), p_socket_get_fd()
+ *
+ * The given file descriptor @a fd will be put in a non-blocking mode. #PSocket
+ * will emulate a blocking mode if required.
+ *
+ * If the socket was not bound yet then on some systems (i.e. Windows) call may
+ * fail to get a socket family from the descriptor thus failing to construct the
+ * #PSocket object.
+ */
+P_LIB_API PSocket *		p_socket_new_from_fd		(pint 			fd,
+								 PError			**error);
+
+/**
+ * @brief Creates a new #PSocket object.
+ * @param family Socket family.
+ * @param type Socket type.
+ * @param protocol Socket data transfer protocol.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return Pointer to #PSocket in case of success, NULL otherwise.
+ * @since 0.0.1
+ * @note If all the given parameters are not compatible with each other, then
+ * the function will fail. Use #P_SOCKET_PROTOCOL_DEFAULT to automatically
+ * match the best protocol for a particular @a type.
+ * @sa #PSocketFamily, #PSocketType, #PSocketProtocol, p_socket_new_from_fd()
+ *
+ * The @a protocol is passed directly to the operating system socket() call,
+ * #PSocketProtocol has the same values as the system definitions. You can pass
+ * any existing protocol value to this call if you know it exactly.
+ */
+P_LIB_API PSocket *		p_socket_new 			(PSocketFamily		family,
+								 PSocketType		type,
+								 PSocketProtocol	protocol,
+								 PError			**error);
+
+/**
+ * @brief Gets an underlying file descriptor of a @a socket.
+ * @param socket #PSocket to get the file descriptor for.
+ * @return File descriptor in case of success, -1 otherwise.
+ * @since 0.0.1
+ * @sa p_socket_new_from_fd()
+ */
+P_LIB_API pint			p_socket_get_fd 		(const PSocket		*socket);
+
+/**
+ * @brief Gets a @a socket address family.
+ * @param socket #PSocket to get the address family for.
+ * @return #PSocketFamily in case of success, #P_SOCKET_FAMILY_UNKNOWN
+ * otherwise.
+ * @since 0.0.1
+ * @sa #PSocketFamily, p_socket_new()
+ *
+ * The socket address family specifies address space which will be used to
+ * communicate with other sockets. For now, the INET and INET6 families are
+ * supported. The INET6 family is available only if the operating system
+ * supports it.
+ */
+P_LIB_API PSocketFamily		p_socket_get_family 		(const PSocket		*socket);
+
+/**
+ * @brief Gets a @a socket type.
+ * @param socket #PSocket to get the type for.
+ * @return #PSocketType in case of success, #P_SOCKET_TYPE_UNKNOWN otherwise.
+ * @since 0.0.1
+ * @sa #PSocketType, p_socket_new()
+ */
+P_LIB_API PSocketType		p_socket_get_type 		(const PSocket		*socket);
+
+/**
+ * @brief Gets a @a socket data transfer protocol.
+ * @param socket #PSocket to get the data transfer protocol for.
+ * @return #PSocketProtocol in case of success, #P_SOCKET_PROTOCOL_UNKNOWN
+ * otherwise.
+ * @since 0.0.1
+ * @sa #PSocketProtocol, p_socket_new()
+ */
+P_LIB_API PSocketProtocol	p_socket_get_protocol		(const PSocket		*socket);
+
+/**
+ * @brief Checks whether the SO_KEEPALIVE flag is enabled.
+ * @param socket #PSocket to check the SO_KEEPALIVE flag for.
+ * @return TRUE if the SO_KEEPALIVE flag is enabled, FALSE otherwise.
+ * @since 0.0.1
+ * @sa p_socket_set_keepalive()
+ *
+ * This option only has effect for connection oriented sockets. After a
+ * connection has been established between two sockets, they periodically send
+ * ping packets to each other to make sure that the connection is alive. A
+ * time interval between alive packets is system dependent and varies from
+ * several minutes to several hours.
+ *
+ * The main usage of this option is to detect dead clients on a server side and
+ * close such the broken sockets to free resources for the actual clients which
+ * may want to connect to the server. Some servers may let clients to be idle
+ * for a long time, so such an option helps to detect died clients faster
+ * without sending them real data. It's some kind of garbage collecting.
+ */
+P_LIB_API pboolean		p_socket_get_keepalive		(const PSocket		*socket);
+
+/**
+ * @brief Checks whether @a socket is used in a blocking mode.
+ * @param socket #PSocket to check the blocking mode for.
+ * @return TRUE if @a socket is in the blocking mode, FALSE otherwise.
+ * @note A blocking socket will wait for an I/O operation to be completed before
+ * returning to the caller function.
+ * @since 0.0.1
+ * @sa p_socket_set_blocking()
+ *
+ * The underlying socket descriptor is always set to the non-blocking mode by
+ * default and #PSocket emulates the blocking mode if required.
+ */
+P_LIB_API pboolean		p_socket_get_blocking		(PSocket 		*socket);
+
+/**
+ * @brief Gets a @a socket listen backlog parameter.
+ * @param socket #PSocket to get the listen backlog parameter for.
+ * @return Listen backlog parameter in case of success, -1 otherwise.
+ * @since 0.0.1
+ * @sa p_socket_set_listen_backlog(), p_socket_listen()
+ *
+ * This parameter only has meaning for the connection oriented sockets. The
+ * backlog parameter specifies how much pending connections from other clients
+ * can be stored in the internal (system) queue. If the socket has already the
+ * number of pending connections equal to the backlog parameter, and another
+ * client attempts to connect on that time, it (client) will either be refused
+ * or retransmitted. This behavior is system and protocol dependent.
+ *
+ * Some systems may not allow to set it to high values. By default #PSocket
+ * attempts to set it to 5.
+ */
+P_LIB_API pint			p_socket_get_listen_backlog	(const PSocket 		*socket);
+
+/**
+ * @brief Gets a @a socket timeout for blocking I/O operations.
+ * @param socket #PSocket to get the timeout for.
+ * @return Timeout for blocking I/O operations in milliseconds, -1 in case of
+ * fail.
+ * @since 0.0.1
+ * @sa p_socket_set_timeout(), p_socket_io_condition_wait()
+ *
+ * For a blocking socket a timeout value means maximum amount of time for which
+ * a blocking call will wait until a newtwork I/O operation completes. If the
+ * operation is not finished after the timeout, the blocking call returns with
+ * the error set to #P_ERROR_IO_TIMED_OUT.
+ *
+ * For a non-blocking socket the timeout affects only on the
+ * p_socket_io_condition_wait() maximum waiting time.
+ *
+ * Zero timeout means that the operation which requires a time to complete
+ * network I/O will be blocked until the operation finishes or error occurres.
+ */
+P_LIB_API pint			p_socket_get_timeout		(const PSocket		*socket);
+
+/**
+ * @brief Gets a @a socket local (bound) address.
+ * @param socket #PSocket to get the local address for.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return #PSocketAddress with the socket local address in case of success,
+ * NULL otherwise.
+ * @since 0.0.1
+ * @sa p_socket_bind()
+ *
+ * If the @a socket was not bound explicitly with p_socket_bind() or implicitly
+ * with p_socket_connect(), the call will fail.
+ */
+P_LIB_API PSocketAddress *	p_socket_get_local_address	(const PSocket 		*socket,
+								 PError			**error);
+
+/**
+ * @brief Gets a @a socket remote endpoint address.
+ * @param socket #PSocket to get the remote endpoint address for.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return #PSocketAddress with the socket endpoint remote address in case of
+ * success, NULL otherwise.
+ * @since 0.0.1
+ * @sa p_socket_connect()
+ *
+ * If the @a socket was not connected to the endpoint address with
+ * p_socket_connect(), the call will fail.
+ *
+ * @warning On Syllable this call will always return NULL for connection-less
+ * sockets (though connecting is possible).
+ */
+P_LIB_API PSocketAddress *	p_socket_get_remote_address	(const PSocket 		*socket,
+								 PError			**error);
+
+/**
+ * @brief Checks whether a @a socket is connected.
+ * @param socket #PSocket to check a connection for.
+ * @return TRUE if the @a socket is connected, FALSE otherwise.
+ * @since 0.0.1
+ * @sa p_socket_connect(), p_socket_check_connect_result()
+ *
+ * This function doesn't check if the socket is still connected, it only checks
+ * whether the p_socket_connect() call was successfully performed on the
+ * @a socket.
+ */
+P_LIB_API pboolean		p_socket_is_connected		(const PSocket		*socket);
+
+/**
+ * @brief Checks whether a @a socket is closed.
+ * @param socket #PSocket to check a closed state.
+ * @return TRUE if the @a socket is closed, FALSE otherwise.
+ * @since 0.0.1
+ * @sa p_socket_close(), p_socket_shutdown()
+ *
+ * If the socket is in a non-blocking mode this call will not return TRUE until
+ * p_socket_check_connect_result() is called. The socket will be closed if
+ * p_socket_shutdown() is called for both the directions.
+ */
+P_LIB_API pboolean		p_socket_is_closed		(const PSocket		*socket);
+
+/**
+ * @brief Checks a connection state after calling p_socket_connect().
+ * @param socket #PSocket to check the connection state for.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return TRUE if was no error, FALSE otherwise.
+ * @since 0.0.1
+ * @sa p_socket_io_condition_wait()
+ * @warning Not supported on Syllable for connection-less sockets.
+ *
+ * Usually this call is used after calling p_socket_connect() on a socket in a
+ * non-blocking mode to check the connection state. If call returns the FALSE
+ * result then the connection checking call has failed or there was an error
+ * during the connection and you should check the last error using an @a error
+ * object.
+ *
+ * If the socket is still pending for the connection you will get the
+ * #P_ERROR_IO_IN_PROGRESS error code.
+ *
+ * After calling p_socket_connect() on a non-blocking socket, you can wait for
+ * a connection operation to be finished using p_socket_io_condition_wait()
+ * with the #P_SOCKET_IO_CONDITION_POLLOUT option.
+ */
+P_LIB_API pboolean		p_socket_check_connect_result	(PSocket		*socket,
+								 PError			**error);
+
+/**
+ * @brief Sets the @a socket SO_KEEPALIVE flag.
+ * @param socket #PSocket to set the SO_KEEPALIVE flag for.
+ * @param keepalive Value for the SO_KEEPALIVE flag.
+ * @since 0.0.1
+ * @sa p_socket_get_keepalive()
+ *
+ * See p_socket_get_keepalive() documentation for a description of this option.
+ */
+P_LIB_API void			p_socket_set_keepalive		(PSocket 		*socket,
+								 pboolean		keepalive);
+
+/**
+ * @brief Sets a @a socket blocking mode.
+ * @param socket #PSocket to set the blocking mode for.
+ * @param blocking Whether to set the @a socket into the blocking mode.
+ * @note A blocking socket will wait for an I/O operation to be completed
+ * before returning to the caller function.
+ * @note On some operating systems a blocking timeout may be less than threads
+ * scheduling granularity, so the actual timeout can be greater than specified
+ * one.
+ * @since 0.0.1
+ * @sa p_socket_get_blocking()
+ */
+P_LIB_API void			p_socket_set_blocking		(PSocket 		*socket,
+								 pboolean		blocking);
+
+/**
+ * @brief Sets a @a socket listen backlog parameter.
+ * @param socket #PSocket to set the listen backlog parameter for.
+ * @param backlog Value for the listen backlog parameter.
+ * @note This parameter can take effect only if it was set before calling
+ * p_socket_listen(). Otherwise it will be ignored by underlying socket
+ * system calls.
+ * @since 0.0.1
+ * @sa p_socket_get_listen_backlog()
+ *
+ * See p_socket_get_listen_backlog() documentation for a description of this
+ * option.
+ */
+P_LIB_API void			p_socket_set_listen_backlog	(PSocket		*socket,
+								 pint			backlog);
+
+/**
+ * @brief Sets a @a socket timeout value for blocking I/O operations.
+ * @param socket #PSocket to set the @a timeout for.
+ * @param timeout Timeout value in milliseconds.
+ * @since 0.0.1
+ * @sa p_socket_get_timeoout(), p_socket_io_condition_wait()
+ *
+ * See p_socket_get_timeout() documentation for a description of this option.
+ */
+P_LIB_API void			p_socket_set_timeout		(PSocket		*socket,
+								 pint			timeout);
+
+/**
+ * @brief Binds a @a socket to a given local address.
+ * @param socket #PSocket to bind.
+ * @param address #PSocketAddress to bind the given @a socket to.
+ * @param allow_reuse Whether to allow socket's address reusing.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ * @sa p_socket_get_local_address()
+ *
+ * The @a allow_reuse option allows to resolve address conflicts for several
+ * bound sockets. It controls the SO_REUSEADDR socket flag.
+ *
+ * In a common case two or more sockets can't be bound to the same address
+ * (a network address and a port) for the same data transfer protocol (i.e. TCP
+ * or UDP) because they will be in a conflicted state for data receiving. But
+ * the socket can be also bound for the any network interface (i.e. 0.0.0.0
+ * network address) and a particular port. If you will try to bind another
+ * socket without the @a allow_reuse option to a particular network address
+ * (i.e. 127.0.0.1) and the same port, the p_socket_bind() call will fail.
+ *
+ * With the @a allow_reuse option the system will resolve this conflict: the
+ * socket will be bound to the particular address and port (and will receive
+ * data targeted to this particular address) while the first socket will be
+ * receiving all other data matching the bound address.
+ *
+ * This option is system dependent, some systems do not support it. Also some
+ * systems have option to reuse the address port (SO_REUSEPORT) in the same way,
+ * too.
+ *
+ * Connection oriented sockets have another problem - the so called linger time.
+ * It is a time required by the system to properly close a socket connection
+ * (and this process can be quite complicated). This time can be measured from
+ * several minutes to several hours (!). The socket in such a state is
+ * half-dead, but it holds the bound address and attempt to bind another socket
+ * on this address will fail. The @a allow_reuse option allows to bind another
+ * socket on such a half-dead address, but behavior can be unexpected, it's
+ * better to refer to the system documentation for that.
+ *
+ * In general case, a server socket should be bound with the @a allow_reuse set
+ * to TRUE, while a client socket shouldn't set this option to TRUE. If you
+ * restart the client quickly with the same address it can fail to bind.
+ */
+P_LIB_API pboolean		p_socket_bind			(const PSocket 		*socket,
+								 PSocketAddress		*address,
+								 pboolean		allow_reuse,
+								 PError			**error);
+
+/**
+ * @brief Connects a @a socket to a given remote address.
+ * @param socket #PSocket to connect.
+ * @param address #PSocketAddress to connect the @a socket to.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ * @sa p_socket_is_connected(), p_socket_check_connect_result(),
+ * p_socket_get_remote_address(), p_socket_io_condition_wait()
+ * @warning On Syllable this method changes a local port of the connection
+ * oriented socket in case of success.
+ *
+ * Calling this method on the connection-less socket will bind it to the remote
+ * address and the p_socket_send() method can be used instead of
+ * p_socket_send_to(), so you do not need to specify the remote (target) address
+ * each time you need to send data. The socket will also discard incoming data
+ * from other addresses. The same call again will re-bind it to another remote
+ * address.
+ *
+ * For the connection oriented socket it tries to establish a connection with
+ * a listening remote socket. The same call again will have no effect and will
+ * fail.
+ *
+ * If the @a socket is in a non-blocking mode, then you can wait for the
+ * connection using p_socket_io_condition_wait() with the
+ * #P_SOCKET_IO_CONDITION_POLLOUT parameter. You should check the connection
+ * result after that using p_socket_check_connect_result().
+ */
+P_LIB_API pboolean		p_socket_connect		(PSocket		*socket,
+								 PSocketAddress		*address,
+								 PError			**error);
+
+/**
+ * @brief Puts a @a socket into a listening state.
+ * @param socket #PSocket to start listening.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ * @sa p_socket_get_listen_backlog(), p_socket_set_listen_backlog()
+ *
+ * This call has meaning only for connection oriented sockets. Before starting
+ * to accept incoming connections, the socket must be put into the passive mode
+ * using p_socket_listen(). After that p_socket_accept() can be used to
+ * accept incoming connections.
+ *
+ * Maximum number of pending connections is defined by the backlog parameter,
+ * see p_socket_get_listen_backlog() documentation for more information. The
+ * backlog parameter must be set before calling p_socket_listen() to take
+ * effect.
+ */
+P_LIB_API pboolean		p_socket_listen			(PSocket 		*socket,
+								 PError			**error);
+
+/**
+ * @brief Accepts a @a socket incoming connection.
+ * @param socket #PSocket to accept the incoming connection from.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return New #PSocket with the accepted connection in case of success, NULL
+ * otherwise.
+ * @since 0.0.1
+ *
+ * This call has meaning only for connection oriented sockets. The socket can
+ * accept new incoming connections only after calling p_socket_bind() and
+ * p_socket_listen().
+ */
+P_LIB_API PSocket *		p_socket_accept			(const PSocket		*socket,
+								 PError			**error);
+
+/**
+ * @brief Receives data from a given @a socket.
+ * @param socket #PSocket to receive data from.
+ * @param buffer Buffer to write received data in.
+ * @param buflen Length of @a buffer.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return Size in bytes of written data in case of success, -1 otherwise.
+ * @note If the @a socket is in a blocking mode, then the caller will be blocked
+ * until data arrives.
+ * @since 0.0.1
+ * @sa p_socket_receive_from(), p_socket_connect()
+ *
+ * If the @a buflen is less than the received data size, only @a buflen bytes of
+ * data will be written to the @a buffer, and excess bytes may be discarded
+ * depending on a socket message type.
+ *
+ * This call is normally used only with the a connected socket, see
+ * p_socket_connect().
+ */
+P_LIB_API pssize		p_socket_receive		(const PSocket		*socket,
+								 pchar			*buffer,
+								 psize			buflen,
+								 PError			**error);
+
+/**
+ * @brief Receives data from a given @a socket and saves a remote address.
+ * @param socket #PSocket to receive data from.
+ * @param[out] address Pointer to store the remote address in case of success,
+ * may be NULL. The caller is responsible to free it after usage.
+ * @param buffer Buffer to write received data in.
+ * @param buflen Length of @a buffer.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return Size in bytes of written data in case of success, -1 otherwise.
+ * @note If the @a socket is in a blocking mode, then the caller will be blocked
+ * until data arrives.
+ * @since 0.0.1
+ * @sa p_socket_receive()
+ *
+ * If the @a buflen is less than the received data size, only @a buflen bytes of
+ * data will be written to the @a buffer, and excess bytes may be discarded
+ * depending on a socket message type.
+ *
+ * This call is normally used only with a connection-less socket.
+ */
+P_LIB_API pssize		p_socket_receive_from		(const PSocket 		*socket,
+								 PSocketAddress		**address,
+								 pchar 			*buffer,
+								 psize			buflen,
+								 PError			**error);
+
+/**
+ * @brief Sends data through a given @a socket.
+ * @param socket #PSocket to send data through.
+ * @param buffer Buffer with data to send.
+ * @param buflen Length of @a buffer.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return Size in bytes of sent data in case of success, -1 otherwise.
+ * @note If the @a socket is in a blocking mode, then the caller will be blocked
+ * until data sent.
+ * @since 0.0.1
+ * @sa p_socket_send_to()
+ *
+ * Do not use this call for connection-less sockets which are not connected to a
+ * remote address using p_socket_connect() because it will always fail, use
+ * p_socket_send_to() instead.
+ */
+P_LIB_API pssize		p_socket_send			(const PSocket		*socket,
+								 const pchar		*buffer,
+								 psize			buflen,
+								 PError			**error);
+
+/**
+ * @brief Sends data through a given @a socket to a given address.
+ * @param socket #PSocket to send data through.
+ * @param address #PSocketAddress to send data to.
+ * @param buffer Buffer with data to send.
+ * @param buflen Length of @a buffer.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return Size in bytes of sent data in case of success, -1 otherwise.
+ * @note If the @a socket is in a blocking mode, then the caller will be blocked
+ * until data sent.
+ * @since 0.0.1
+ * @sa p_socket_send()
+ *
+ * This call is used when dealing with connection-less sockets. You can bind
+ * such a socket to a remote address using p_socket_connect() and use
+ * p_socket_send() instead. If you are working with connection oriented sockets
+ * then use p_socket_send() after establishing a connection.
+ */
+P_LIB_API pssize		p_socket_send_to		(const PSocket		*socket,
+								 PSocketAddress		*address,
+								 const pchar		*buffer,
+								 psize			buflen,
+								 PError			**error);
+
+/**
+ * @brief Closes a @a socket.
+ * @param socket #PSocket to close.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ * @sa p_socket_free(), p_socket_is_closed()
+ *
+ * For connection oriented sockets some time is required to completely close
+ * a socket connection. See documentation for p_socket_bind() for more
+ * information.
+ */
+P_LIB_API pboolean		p_socket_close			(PSocket		*socket,
+								 PError			**error);
+
+/**
+ * @brief Shutdowns a full-duplex @a socket data transfer link.
+ * @param socket #PSocket to shutdown link.
+ * @param shutdown_read Whether to shutdown the incoming data transfer link.
+ * @param shutdown_write Whether to shutdown the outcoming data transfer link.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @note Shutdown of any link is possible only on the socket in a connected
+ * state, otherwise the call will fail.
+ * @since 0.0.1
+ *
+ * After shutdowning the data transfer link couldn't be restored in any
+ * direction. It is often used to gracefully close a connection for a connection
+ * oriented socket.
+ */
+P_LIB_API pboolean		p_socket_shutdown		(PSocket		*socket,
+								 pboolean		shutdown_read,
+								 pboolean		shutdown_write,
+								 PError			**error);
+
+/**
+ * @brief Closes a @a socket (if not closed yet) and frees its resources.
+ * @param socket #PSocket to free resources from.
+ * @since 0.0.1
+ * @sa p_socket_close()
+ */
+P_LIB_API void			p_socket_free			(PSocket 		*socket);
+
+/**
+ * @brief Sets the @a socket buffer size for a given data transfer direction.
+ * @param socket #PSocket to set the buffer size for.
+ * @param dir Direction to set the buffer size on.
+ * @param size Size of the buffer to set, in bytes.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ * @warning Not supported on Syllable.
+ */
+P_LIB_API pboolean		p_socket_set_buffer_size	(const PSocket		*socket,
+								 PSocketDirection	dir,
+								 psize			size,
+								 PError			**error);
+
+/**
+ * @brief Waits for a specified I/O @a condition on @a socket.
+ * @param socket #PSocket to wait for @a condition on.
+ * @param condition An I/O condition to wait for on @a socket.
+ * @param[out] error Error report object, NULL to ignore.
+ * @return TRUE if @a condition has been met, FALSE otherwise.
+ * @since 0.0.1
+ * @sa p_socket_get_timeout(), p_socket_set_timeout()
+ *
+ * Waits until @a condition will be met on @a socket or an error occurred. If
+ * timeout was set using p_socket_set_timeout() and a network I/O operation
+ * doesn't finish until timeout expired, call will fail with
+ * #P_ERROR_IO_TIMED_OUT error code.
+ */
+P_LIB_API pboolean		p_socket_io_condition_wait	(const PSocket		*socket,
+								 PSocketIOCondition	condition,
+								 PError			**error);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PSOCKET_H */
diff --git a/3rdparty/plibsys/src/psocketaddress.c b/3rdparty/plibsys/src/psocketaddress.c
new file mode 100644
index 0000000..2338c83
--- /dev/null
+++ b/3rdparty/plibsys/src/psocketaddress.c
@@ -0,0 +1,619 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "pstring.h"
+#include "psocketaddress.h"
+#include "plibsys-private.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef P_OS_WIN
+#  include <arpa/inet.h>
+#endif
+
+#if defined (PLIBSYS_HAS_GETADDRINFO) && !defined (PLIBSYS_SOCKADDR_IN6_HAS_SCOPEID)
+#  undef PLIBSYS_HAS_GETADDRINFO
+#endif
+
+#ifdef PLIBSYS_HAS_GETADDRINFO
+#  include <netdb.h>
+#endif
+
+/* According to Open Group specifications */
+#ifndef INET_ADDRSTRLEN
+#  ifdef P_OS_WIN
+     /* On Windows it includes port number  */
+#    define INET_ADDRSTRLEN 22
+#  else
+#    define INET_ADDRSTRLEN 16
+#  endif
+#endif
+
+#ifdef AF_INET6
+#  ifndef INET6_ADDRSTRLEN
+#    ifdef P_OS_WIN
+       /* On Windows it includes port number */
+#      define INET6_ADDRSTRLEN 65
+#    else
+#      define INET6_ADDRSTRLEN 46
+#    endif
+#  endif
+#endif
+
+#ifdef P_OS_VMS
+#  if PLIBSYS_SIZEOF_VOID_P == 8
+#    define addrinfo __addrinfo64
+#  endif
+#endif
+
+#if defined (P_OS_BEOS) || defined (P_OS_OS2)
+#  ifdef AF_INET6
+#    undef AF_INET6
+#  endif
+#endif
+
+struct PSocketAddress_ {
+	PSocketFamily	family;
+	union addr_ {
+		struct in_addr sin_addr;
+#ifdef AF_INET6
+		struct in6_addr sin6_addr;
+#endif
+	} 		addr;
+	puint16 	port;
+	puint32		flowinfo;
+	puint32		scope_id;
+};
+
+P_LIB_API PSocketAddress *
+p_socket_address_new_from_native (pconstpointer	native,
+				  psize		len)
+{
+	PSocketAddress	*ret;
+	puint16		family;
+
+	if (P_UNLIKELY (native == NULL || len == 0))
+		return NULL;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSocketAddress))) == NULL))
+		return NULL;
+
+	family = ((struct sockaddr *) native)->sa_family;
+
+	if (family == AF_INET) {
+		if (len < sizeof (struct sockaddr_in)) {
+			P_WARNING ("PSocketAddress::p_socket_address_new_from_native: invalid IPv4 native size");
+			p_free (ret);
+			return NULL;
+		}
+
+		memcpy (&ret->addr.sin_addr, &((struct sockaddr_in *) native)->sin_addr, sizeof (struct in_addr));
+		ret->family = P_SOCKET_FAMILY_INET;
+		ret->port   = p_ntohs (((struct sockaddr_in *) native)->sin_port);
+		return ret;
+	}
+#ifdef AF_INET6
+	else if (family == AF_INET6) {
+		if (len < sizeof (struct sockaddr_in6)) {
+			P_WARNING ("PSocketAddress::p_socket_address_new_from_native: invalid IPv6 native size");
+			p_free (ret);
+			return NULL;
+		}
+
+		memcpy (&ret->addr.sin6_addr,
+			&((struct sockaddr_in6 *) native)->sin6_addr,
+			sizeof (struct in6_addr));
+
+		ret->family   = P_SOCKET_FAMILY_INET6;
+		ret->port     = p_ntohs (((struct sockaddr_in *) native)->sin_port);
+#ifdef PLIBSYS_SOCKADDR_IN6_HAS_FLOWINFO
+		ret->flowinfo = ((struct sockaddr_in6 *) native)->sin6_flowinfo;
+#endif
+#ifdef PLIBSYS_SOCKADDR_IN6_HAS_SCOPEID
+		ret->scope_id = ((struct sockaddr_in6 *) native)->sin6_scope_id;
+#endif
+		return ret;
+	}
+#endif
+	else {
+		p_free (ret);
+		return NULL;
+	}
+}
+
+P_LIB_API PSocketAddress *
+p_socket_address_new (const pchar	*address,
+		      puint16		port)
+{
+	PSocketAddress		*ret;
+#if defined (P_OS_WIN) || defined (PLIBSYS_HAS_GETADDRINFO)
+	struct addrinfo		hints;
+	struct addrinfo		*res;
+#endif
+
+#ifdef P_OS_WIN
+	struct sockaddr_storage	sa;
+	struct sockaddr_in 	*sin = (struct sockaddr_in *) &sa;
+#  ifdef AF_INET6
+	struct sockaddr_in6 	*sin6 = (struct sockaddr_in6 *) &sa;
+#  endif /* AF_INET6 */
+	pint 			len;
+#endif /* P_OS_WIN */
+
+	if (P_UNLIKELY (address == NULL))
+		return NULL;
+
+#if (defined (P_OS_WIN) || defined (PLIBSYS_HAS_GETADDRINFO)) && defined (AF_INET6)
+	if (strchr (address, ':') != NULL) {
+		memset (&hints, 0, sizeof (hints));
+
+		hints.ai_family   = AF_UNSPEC;
+		hints.ai_socktype = SOCK_STREAM;
+		hints.ai_protocol = 0;
+#  ifndef P_OS_UNIXWARE
+		hints.ai_flags    = AI_NUMERICHOST;
+#  endif
+
+		if (P_UNLIKELY (getaddrinfo (address, NULL, &hints, &res) != 0))
+			return NULL;
+
+		if (P_LIKELY (res->ai_family  == AF_INET6 &&
+			      res->ai_addrlen == sizeof (struct sockaddr_in6))) {
+			((struct sockaddr_in6 *) res->ai_addr)->sin6_port = p_htons (port);
+			ret = p_socket_address_new_from_native (res->ai_addr, res->ai_addrlen);
+		} else
+			ret = NULL;
+
+		freeaddrinfo (res);
+
+		return ret;
+	}
+#endif
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSocketAddress))) == NULL)) {
+		P_ERROR ("PSocketAddress::p_socket_address_new: failed to allocate memory");
+		return NULL;
+	}
+
+	ret->port = port;
+
+#ifdef P_OS_WIN
+	memset (&sa, 0, sizeof (sa));
+	len = sizeof (sa);
+	sin->sin_family = AF_INET;
+
+	if (WSAStringToAddressA ((LPSTR) address, AF_INET, NULL, (LPSOCKADDR) &sa, &len) == 0) {
+		memcpy (&ret->addr.sin_addr, &sin->sin_addr, sizeof (struct in_addr));
+		ret->family = P_SOCKET_FAMILY_INET;
+		return ret;
+	}
+#  ifdef AF_INET6
+	else {
+		sin6->sin6_family = AF_INET6;
+
+		if (WSAStringToAddressA ((LPSTR) address, AF_INET6, NULL, (LPSOCKADDR) &sa, &len) == 0) {
+			memcpy (&ret->addr.sin6_addr, &sin6->sin6_addr, sizeof (struct in6_addr));
+			ret->family = P_SOCKET_FAMILY_INET6;
+			return ret;
+		}
+	}
+#  endif /* AF_INET6 */
+#else /* P_OS_WIN */
+	if (inet_pton (AF_INET, address, &ret->addr.sin_addr) > 0) {
+		ret->family = P_SOCKET_FAMILY_INET;
+		return ret;
+	}
+#  ifdef AF_INET6
+	else if (inet_pton (AF_INET6, address, &ret->addr.sin6_addr) > 0) {
+		ret->family = P_SOCKET_FAMILY_INET6;
+		return ret;
+	}
+#  endif /* AF_INET6 */
+#endif /* P_OS_WIN */
+
+	p_free (ret);
+	return NULL;
+}
+
+P_LIB_API PSocketAddress *
+p_socket_address_new_any (PSocketFamily	family,
+			  puint16	port)
+{
+	PSocketAddress	*ret;
+	puchar		any_addr[] = {0, 0, 0, 0};
+#ifdef AF_INET6
+	struct in6_addr	any6_addr = IN6ADDR_ANY_INIT;
+#endif
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSocketAddress))) == NULL)) {
+		P_ERROR ("PSocketAddress::p_socket_address_new_any: failed to allocate memory");
+		return NULL;
+	}
+
+	if (family == P_SOCKET_FAMILY_INET)
+		memcpy (&ret->addr.sin_addr, any_addr, sizeof (any_addr));
+#ifdef AF_INET6
+	else if (family == P_SOCKET_FAMILY_INET6)
+		memcpy (&ret->addr.sin6_addr, &any6_addr.s6_addr, sizeof (any6_addr.s6_addr));
+#endif
+	else {
+		p_free (ret);
+		return NULL;
+	}
+
+	ret->family = family;
+	ret->port   = port;
+
+	return ret;
+}
+
+P_LIB_API PSocketAddress *
+p_socket_address_new_loopback (PSocketFamily	family,
+			       puint16		port)
+{
+	PSocketAddress	*ret;
+	puchar		loop_addr[] = {127, 0, 0, 0};
+#ifdef AF_INET6
+	struct in6_addr	loop6_addr = IN6ADDR_LOOPBACK_INIT;
+#endif
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSocketAddress))) == NULL)) {
+		P_ERROR ("PSocketAddress::p_socket_address_new_loopback: failed to allocate memory");
+		return NULL;
+	}
+
+	if (family == P_SOCKET_FAMILY_INET)
+		memcpy (&ret->addr.sin_addr, loop_addr, sizeof (loop_addr));
+#ifdef AF_INET6
+	else if (family == P_SOCKET_FAMILY_INET6)
+		memcpy (&ret->addr.sin6_addr, &loop6_addr.s6_addr, sizeof (loop6_addr.s6_addr));
+#endif
+	else {
+		p_free (ret);
+		return NULL;
+	}
+
+	ret->family = family;
+	ret->port   = port;
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_socket_address_to_native (const PSocketAddress	*addr,
+			    ppointer			dest,
+			    psize			destlen)
+{
+	struct sockaddr_in	*sin;
+#ifdef AF_INET6
+	struct sockaddr_in6	*sin6;
+#endif
+
+	if (P_UNLIKELY (addr == NULL || dest == NULL || destlen == 0))
+		return FALSE;
+
+	sin = (struct sockaddr_in *) dest;
+#ifdef AF_INET6
+	sin6 = (struct sockaddr_in6 *) dest;
+#endif
+
+	if (addr->family == P_SOCKET_FAMILY_INET) {
+		if (P_UNLIKELY (destlen < sizeof (struct sockaddr_in))) {
+			P_WARNING ("PSocketAddress::p_socket_address_to_native: invalid buffer size for IPv4");
+			return FALSE;
+		}
+
+		memcpy (&sin->sin_addr, &addr->addr.sin_addr, sizeof (struct in_addr));
+		sin->sin_family = AF_INET;
+		sin->sin_port   = p_htons (addr->port);
+		memset (sin->sin_zero, 0, sizeof (sin->sin_zero));
+		return TRUE;
+	}
+#ifdef AF_INET6
+	else if (addr->family == P_SOCKET_FAMILY_INET6) {
+		if (P_UNLIKELY (destlen < sizeof (struct sockaddr_in6))) {
+			P_ERROR ("PSocketAddress::p_socket_address_to_native: invalid buffer size for IPv6");
+			return FALSE;
+		}
+
+		memcpy (&sin6->sin6_addr, &addr->addr.sin6_addr, sizeof (struct in6_addr));
+		sin6->sin6_family   = AF_INET6;
+		sin6->sin6_port     = p_htons (addr->port);
+#ifdef PLIBSYS_SOCKADDR_IN6_HAS_FLOWINFO
+		sin6->sin6_flowinfo = addr->flowinfo;
+#endif
+#ifdef PLIBSYS_SOCKADDR_IN6_HAS_SCOPEID
+		sin6->sin6_scope_id = addr->scope_id;
+#endif
+		return TRUE;
+	}
+#endif
+	else {
+		P_WARNING ("PSocketAddress::p_socket_address_to_native: unsupported socket address");
+		return FALSE;
+	}
+}
+
+P_LIB_API psize
+p_socket_address_get_native_size (const PSocketAddress *addr)
+{
+	if (P_UNLIKELY (addr == NULL))
+		return 0;
+
+	if (addr->family == P_SOCKET_FAMILY_INET)
+		return sizeof (struct sockaddr_in);
+#ifdef AF_INET6
+	else if (addr->family == P_SOCKET_FAMILY_INET6)
+		return sizeof (struct sockaddr_in6);
+#endif
+	else {
+		P_WARNING ("PSocketAddress::p_socket_address_get_native_size: unsupported socket family");
+		return 0;
+	}
+}
+
+P_LIB_API PSocketFamily
+p_socket_address_get_family (const PSocketAddress *addr)
+{
+	if (P_UNLIKELY (addr == NULL))
+		return P_SOCKET_FAMILY_UNKNOWN;
+
+	return addr->family;
+}
+
+P_LIB_API pchar *
+p_socket_address_get_address (const PSocketAddress *addr)
+{
+#ifdef AF_INET6
+	pchar			buffer[INET6_ADDRSTRLEN];
+#else
+	pchar			buffer[INET_ADDRSTRLEN];
+#endif
+
+#ifdef P_OS_WIN
+	DWORD			buflen = sizeof (buffer);
+	DWORD			addrlen;
+	struct sockaddr_storage	sa;
+	struct sockaddr_in	*sin;
+#  ifdef AF_INET6
+	struct sockaddr_in6	*sin6;
+#  endif /* AF_INET6 */
+#endif /* P_OS_WIN */
+
+	if (P_UNLIKELY (addr == NULL || addr->family == P_SOCKET_FAMILY_UNKNOWN))
+		return NULL;
+#ifdef P_OS_WIN
+	sin = (struct sockaddr_in *) &sa;
+#  ifdef AF_INET6
+	sin6 = (struct sockaddr_in6 *) &sa;
+#  endif /* AF_INET6 */
+#endif /* P_OS_WIN */
+
+#ifdef P_OS_WIN
+	memset (&sa, 0, sizeof (sa));
+#endif
+
+#ifdef P_OS_WIN
+	sa.ss_family = addr->family;
+
+	if (addr->family == P_SOCKET_FAMILY_INET) {
+		addrlen = sizeof (struct sockaddr_in);
+		memcpy (&sin->sin_addr, &addr->addr.sin_addr, sizeof (struct in_addr));
+	}
+#  ifdef AF_INET6
+	else {
+		addrlen = sizeof (struct sockaddr_in6);
+		memcpy (&sin6->sin6_addr, &addr->addr.sin6_addr, sizeof (struct in6_addr));
+	}
+#  endif /* AF_INET6 */
+
+	if (P_UNLIKELY (WSAAddressToStringA ((LPSOCKADDR) &sa,
+					     addrlen,
+					     NULL,
+					     (LPSTR) buffer,
+					     &buflen) != 0))
+		return NULL;
+#else /* !P_OS_WIN */
+	if (addr->family == P_SOCKET_FAMILY_INET)
+		inet_ntop (AF_INET, &addr->addr.sin_addr, buffer, sizeof (buffer));
+#  ifdef AF_INET6
+	else
+		inet_ntop (AF_INET6, &addr->addr.sin6_addr, buffer, sizeof (buffer));
+#  endif /* AF_INET6 */
+#endif /* P_OS_WIN */
+
+	return p_strdup (buffer);
+}
+
+P_LIB_API puint16
+p_socket_address_get_port (const PSocketAddress *addr)
+{
+	if (P_UNLIKELY (addr == NULL))
+		return 0;
+
+	return addr->port;
+}
+
+P_LIB_API puint32
+p_socket_address_get_flow_info (const PSocketAddress *addr)
+{
+	if (P_UNLIKELY (addr == NULL))
+		return 0;
+
+#if !defined (AF_INET6) || !defined (PLIBSYS_SOCKADDR_IN6_HAS_FLOWINFO)
+	return 0;
+#else
+	if (P_UNLIKELY (addr->family != P_SOCKET_FAMILY_INET6))
+		return 0;
+
+	return addr->flowinfo;
+#endif
+}
+
+P_LIB_API puint32
+p_socket_address_get_scope_id (const PSocketAddress *addr)
+{
+	if (P_UNLIKELY (addr == NULL))
+		return 0;
+
+#if !defined (AF_INET6) || !defined (PLIBSYS_SOCKADDR_IN6_HAS_SCOPEID)
+	return 0;
+#else
+	if (P_UNLIKELY (addr->family != P_SOCKET_FAMILY_INET6))
+		return 0;
+
+	return addr->scope_id;
+#endif
+}
+
+P_LIB_API void
+p_socket_address_set_flow_info (PSocketAddress	*addr,
+				puint32		flowinfo)
+{
+	if (P_UNLIKELY (addr == NULL))
+		return;
+
+#if !defined (AF_INET6) || !defined (PLIBSYS_SOCKADDR_IN6_HAS_FLOWINFO)
+	P_UNUSED (flowinfo);
+	return;
+#else
+	if (P_UNLIKELY (addr->family != P_SOCKET_FAMILY_INET6))
+		return;
+
+	addr->flowinfo = flowinfo;
+#endif
+}
+
+P_LIB_API void
+p_socket_address_set_scope_id (PSocketAddress	*addr,
+			       puint32		scope_id)
+{
+	if (P_UNLIKELY (addr == NULL))
+		return;
+
+#if !defined (AF_INET6) || !defined (PLIBSYS_SOCKADDR_IN6_HAS_SCOPEID)
+	P_UNUSED (scope_id);
+	return;
+#else
+	if (P_UNLIKELY (addr->family != P_SOCKET_FAMILY_INET6))
+		return;
+
+	addr->scope_id = scope_id;
+#endif
+}
+
+P_LIB_API pboolean
+p_socket_address_is_flow_info_supported (void)
+{
+#ifdef AF_INET6
+#  ifdef PLIBSYS_SOCKADDR_IN6_HAS_FLOWINFO
+	return TRUE;
+#  else
+	return FALSE;
+#  endif
+#else
+	return FALSE;
+#endif
+}
+
+P_LIB_API pboolean
+p_socket_address_is_scope_id_supported (void)
+{
+#ifdef AF_INET6
+#  ifdef PLIBSYS_SOCKADDR_IN6_HAS_SCOPEID
+	return TRUE;
+#  else
+	return FALSE;
+#  endif
+#else
+	return FALSE;
+#endif
+}
+
+P_LIB_API pboolean
+p_socket_address_is_ipv6_supported (void)
+{
+#ifdef AF_INET6
+	return TRUE;
+#else
+	return FALSE;
+#endif
+}
+
+P_LIB_API pboolean
+p_socket_address_is_any (const PSocketAddress *addr)
+{
+	puint32 addr4;
+
+	if (P_UNLIKELY (addr == NULL || addr->family == P_SOCKET_FAMILY_UNKNOWN))
+		return FALSE;
+
+	if (addr->family == P_SOCKET_FAMILY_INET) {
+		addr4 = p_ntohl (* ((puint32 *) &addr->addr.sin_addr));
+
+		return (addr4 == INADDR_ANY);
+	}
+#ifdef AF_INET6
+	else
+		return IN6_IS_ADDR_UNSPECIFIED (&addr->addr.sin6_addr);
+#else
+	else
+		return FALSE;
+#endif
+}
+
+P_LIB_API pboolean
+p_socket_address_is_loopback (const PSocketAddress *addr)
+{
+	puint32 addr4;
+
+	if (P_UNLIKELY (addr == NULL || addr->family == P_SOCKET_FAMILY_UNKNOWN))
+		return FALSE;
+
+	if (addr->family == P_SOCKET_FAMILY_INET) {
+		addr4 = p_ntohl (* ((puint32 *) &addr->addr.sin_addr));
+
+		/* 127.0.0.0/8 */
+		return ((addr4 & 0xff000000) == 0x7f000000);
+	}
+#ifdef AF_INET6
+	else
+		return IN6_IS_ADDR_LOOPBACK (&addr->addr.sin6_addr);
+#else
+	else
+		return FALSE;
+#endif
+}
+
+P_LIB_API void
+p_socket_address_free (PSocketAddress *addr)
+{
+	if (P_UNLIKELY (addr == NULL))
+		return;
+
+	p_free (addr);
+}
diff --git a/3rdparty/plibsys/src/psocketaddress.h b/3rdparty/plibsys/src/psocketaddress.h
new file mode 100644
index 0000000..d77ca1c
--- /dev/null
+++ b/3rdparty/plibsys/src/psocketaddress.h
@@ -0,0 +1,284 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file psocketaddress.h
+ * @brief Socket address wrapper
+ * @author Alexander Saprykin
+ *
+ * A socket address is usually represented by a network address (IPv4 or IPv6)
+ * and a port number (though some other naming schemes and parameters are
+ * possible).
+ *
+ * Socket address parameters are stored inside a special system (native)
+ * structure in the binary (raw) form. The native structure varies with an
+ * operating system and a network protocol. #PSocketAddress acts like a thin
+ * wrapper around that native address structure and unifies manipulation of
+ * socket address data.
+ *
+ * #PSocketAddress supports IPv4 and IPv6 addresses which consist of an IP
+ * address and a port number. IPv6 support is system dependent and not provided
+ * for all the platforms. Sometimes you may also need to enable IPv6 support in
+ * the system to make it working.
+ *
+ * Convenient methods to create special addresses are provided: for the loopback
+ * interface use p_socket_address_new_loopback(), for the any-address interface
+ * use p_socket_address_new_any().
+ *
+ * If you want to get the underlying native address structure for further usage
+ * in system calls use p_socket_address_to_native(), and
+ * p_socket_address_new_from_native() for a vice versa conversion.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PSOCKETADDRESS_H
+#define PLIBSYS_HEADER_PSOCKETADDRESS_H
+
+#include <pmacros.h>
+#include <ptypes.h>
+
+#ifndef P_OS_WIN
+#  include <sys/types.h>
+#  include <sys/socket.h>
+#  include <netinet/in.h>
+#endif
+
+P_BEGIN_DECLS
+
+/** Socket address family. */
+typedef enum PSocketFamily_ {
+	P_SOCKET_FAMILY_UNKNOWN = 0,		/**< Unknown family.	*/
+	P_SOCKET_FAMILY_INET	= AF_INET,	/**< IPv4 family.	*/
+#ifdef AF_INET6
+	P_SOCKET_FAMILY_INET6	= AF_INET6	/**< IPv6 family.	*/
+#else
+	P_SOCKET_FAMILY_INET6	= -1		/**< No IPv6 family.	*/
+#endif
+} PSocketFamily;
+
+/** Socket address opaque structure. */
+typedef struct PSocketAddress_ PSocketAddress;
+
+/**
+ * @brief Creates new #PSocketAddress from the native socket address raw data.
+ * @param native Pointer to the native socket address raw data.
+ * @param len Raw data length, in bytes.
+ * @return Pointer to #PSocketAddress in case of success, NULL otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API PSocketAddress *	p_socket_address_new_from_native	(pconstpointer		native,
+									 psize			len);
+
+/**
+ * @brief Creates new #PSocketAddress.
+ * @param address String representation of an address (i.e. "172.146.45.5").
+ * @param port Port number.
+ * @return Pointer to #PSocketAddress in case of success, NULL otherwise.
+ * @since 0.0.1
+ * @note It tries to automatically detect a socket family.
+ *
+ * If the @a address is an IPv6 address, it can also contain a scope index
+ * separated from the address by the '%' literal). Most target platforms should
+ * correctly parse such an address though some old operating systems may fail in
+ * case of lack of the getaddrinfo() call.
+ */
+P_LIB_API PSocketAddress *	p_socket_address_new			(const pchar		*address,
+									 puint16		port);
+
+/**
+ * @brief Creates new #PSocketAddress for the any-address representation.
+ * @param family Socket family.
+ * @param port Port number.
+ * @return Pointer to #PSocketAddress in case of success, NULL otherwise.
+ * @since 0.0.1
+ * @note This call creates a network address for the set of all possible
+ * addresses, so you can't use it for receiving or sending data on a particular
+ * network address. If you need to bind a socket to the specific address
+ * (i.e. 127.0.0.1) use p_socket_address_new() instead.
+ */
+P_LIB_API PSocketAddress *	p_socket_address_new_any		(PSocketFamily		family,
+									 puint16		port);
+
+/**
+ * @brief Creates new #PSocketAddress for the loopback interface.
+ * @param family Socket family.
+ * @param port Port number.
+ * @return Pointer to #PSocketAddress in case of success, NULL otherwise.
+ * @since 0.0.1
+ * @note This call creates a network address for the entire loopback network
+ * interface, so you can't use it for receiving or sending data on a particular
+ * network address. If you need to bind a socket to the specific address
+ * (i.e. 127.0.0.1) use p_socket_address_new() instead.
+ */
+P_LIB_API PSocketAddress *	p_socket_address_new_loopback		(PSocketFamily		family,
+									 puint16		port);
+
+/**
+ * @brief Converts #PSocketAddress to the native socket address raw data.
+ * @param addr #PSocketAddress to convert.
+ * @param[out] dest Output buffer for raw data.
+ * @param destlen Length in bytes of the @a dest buffer.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API pboolean		p_socket_address_to_native		(const PSocketAddress	*addr,
+									 ppointer		dest,
+									 psize			destlen);
+
+/**
+ * @brief Gets the size of the native socket address raw data, in bytes.
+ * @param addr #PSocketAddress to get the size of native address raw data for.
+ * @return Size of the native socket address raw data in case of success, 0
+ * otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API psize			p_socket_address_get_native_size	(const PSocketAddress	*addr);
+
+/**
+ * @brief Gets a family of a socket address.
+ * @param addr #PSocketAddress to get the family for.
+ * @return #PSocketFamily of the socket address.
+ * @since 0.0.1
+ */
+P_LIB_API PSocketFamily		p_socket_address_get_family		(const PSocketAddress	*addr);
+
+/**
+ * @brief Gets a socket address in a string representation, i.e. "172.146.45.5".
+ * @param addr #PSocketAddress to get address string for.
+ * @return Pointer to the string representation of the socket address in case of
+ * success, NULL otherwise. The caller takes ownership of the returned pointer.
+ * @since 0.0.1
+ */
+P_LIB_API pchar *		p_socket_address_get_address		(const PSocketAddress	*addr);
+
+/**
+ * @brief Gets a port number of a socket address.
+ * @param addr #PSocketAddress to get the port number for.
+ * @return Port number in case of success, 0 otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API puint16		p_socket_address_get_port		(const PSocketAddress	*addr);
+
+/**
+ * @brief Gets IPv6 traffic class and flow information.
+ * @param addr #PSocketAddress to get flow information for.
+ * @return IPv6 traffic class and flow information.
+ * @since 0.0.1
+ * @note This call is valid only for an IPv6 address, otherwise 0 is returned.
+ * @note Some operating systems may not support this property.
+ * @sa p_socket_address_is_flow_info_supported()
+ */
+P_LIB_API puint32		p_socket_address_get_flow_info		(const PSocketAddress	*addr);
+
+/**
+ * @brief Gets an IPv6 set of interfaces for a scope.
+ * @param addr #PSocketAddress to get the set of interfaces for.
+ * @return Index that identifies the set of interfaces for a scope.
+ * @since 0.0.1
+ * @note This call is valid only for an IPv6 address, otherwise 0 is returned.
+ * @note Some operating systems may not support this property.
+ * @sa p_socket_address_is_scope_id_supported()
+ */
+P_LIB_API puint32		p_socket_address_get_scope_id		(const PSocketAddress	*addr);
+
+/**
+ * @brief Sets IPv6 traffic class and flow information.
+ * @param addr #PSocketAddress to set flow information for.
+ * @param flowinfo Flow information to set.
+ * @since 0.0.1
+ * @note This call is valid only for an IPv6 address.
+ * @note Some operating systems may not support this property.
+ * @sa p_socket_address_is_flow_info_supported()
+ */
+P_LIB_API void			p_socket_address_set_flow_info		(PSocketAddress		*addr,
+									 puint32		flowinfo);
+
+/**
+ * @brief Sets an IPv6 set of interfaces for a scope.
+ * @param addr #PSocketAddress to set the set of interfaces for.
+ * @param scope_id Index that identifies the set of interfaces for a scope.
+ * @since 0.0.1
+ * @note This call is valid only for an IPv6 address.
+ * @note Some operating systems may not support this property.
+ * @sa p_socket_address_is_scope_id_supported()
+ */
+P_LIB_API void			p_socket_address_set_scope_id		(PSocketAddress		*addr,
+									 puint32		scope_id);
+
+/**
+ * @brief Checks whether flow information is supported in IPv6.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API pboolean		p_socket_address_is_flow_info_supported	(void);
+
+/**
+ * @brief Checks whether a set of interfaces for a scope is supported in IPv6.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API pboolean		p_socket_address_is_scope_id_supported	(void);
+
+/**
+ * @brief Checks whether IPv6 protocol is supported.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.3
+ */
+P_LIB_API pboolean		p_socket_address_is_ipv6_supported	(void);
+
+/**
+ * @brief Checks whether a given socket address is an any-address
+ * representation. Such an address is a 0.0.0.0.
+ * @param addr #PSocketAddress to check.
+ * @return TRUE if the @a addr is the any-address representation, FALSE
+ * otherwise.
+ * @since 0.0.1
+ * @sa p_socket_address_new_any()
+ */
+P_LIB_API pboolean		p_socket_address_is_any			(const PSocketAddress	*addr);
+
+/**
+ * @brief Checks whether a given socket address is for the loopback interface.
+ * Such an address is a 127.x.x.x.
+ * @param addr #PSocketAddress to check.
+ * @return TRUE if the @a addr is for the loopback interface, FALSE otherwise.
+ * @since 0.0.1
+ * @sa p_socket_address_new_loopback()
+ */
+P_LIB_API pboolean		p_socket_address_is_loopback		(const PSocketAddress	*addr);
+
+/**
+ * @brief Frees a socket address structure and its resources.
+ * @param addr #PSocketAddress to free.
+ * @since 0.0.1
+ */
+P_LIB_API void			p_socket_address_free			(PSocketAddress		*addr);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PSOCKETADDRESS_H */
diff --git a/3rdparty/plibsys/src/pspinlock-c11.c b/3rdparty/plibsys/src/pspinlock-c11.c
new file mode 100644
index 0000000..1e77954
--- /dev/null
+++ b/3rdparty/plibsys/src/pspinlock-c11.c
@@ -0,0 +1,103 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "pspinlock.h"
+
+#ifdef P_CC_SUN
+#  define PSPINLOCK_INT_CAST(x) (pint *) (x)
+#else
+#  define PSPINLOCK_INT_CAST(x) x
+#endif
+
+struct PSpinLock_ {
+	volatile pint spin;
+};
+
+P_LIB_API PSpinLock *
+p_spinlock_new (void)
+{
+	PSpinLock *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSpinLock))) == NULL)) {
+		P_ERROR ("PSpinLock::p_spinlock_new: failed to allocate memory");
+		return NULL;
+	}
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_spinlock_lock (PSpinLock *spinlock)
+{
+	pint tmp_int;
+
+	if (P_UNLIKELY (spinlock == NULL))
+		return FALSE;
+
+	do {
+		tmp_int = 0;
+	} while ((pboolean) __atomic_compare_exchange_n (PSPINLOCK_INT_CAST (&(spinlock->spin)),
+							 &tmp_int,
+							 1,
+							 0,
+							 __ATOMIC_ACQUIRE,
+							 __ATOMIC_RELAXED) == FALSE);
+
+	return TRUE;
+}
+
+P_LIB_API pboolean
+p_spinlock_trylock (PSpinLock *spinlock)
+{
+	pint tmp_int = 0;
+
+	if (P_UNLIKELY (spinlock == NULL))
+		return FALSE;
+
+	return (pboolean) __atomic_compare_exchange_n (PSPINLOCK_INT_CAST (&(spinlock->spin)),
+						       &tmp_int,
+						       1,
+						       0,
+						       __ATOMIC_ACQUIRE,
+						       __ATOMIC_RELAXED);
+}
+
+P_LIB_API pboolean
+p_spinlock_unlock (PSpinLock *spinlock)
+{
+	if (P_UNLIKELY (spinlock == NULL))
+		return FALSE;
+
+	__atomic_store_4 (PSPINLOCK_INT_CAST (&(spinlock->spin)), 0, __ATOMIC_RELEASE);
+
+	return TRUE;
+}
+
+P_LIB_API void
+p_spinlock_free (PSpinLock *spinlock)
+{
+	p_free (spinlock);
+}
diff --git a/3rdparty/plibsys/src/pspinlock-decc.c b/3rdparty/plibsys/src/pspinlock-decc.c
new file mode 100644
index 0000000..0a5a044
--- /dev/null
+++ b/3rdparty/plibsys/src/pspinlock-decc.c
@@ -0,0 +1,87 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "pspinlock.h"
+
+#ifdef P_OS_VMS
+#  include <builtins.h>
+#else
+#  include <machine/builtins.h>
+#endif
+
+struct PSpinLock_ {
+	volatile pint spin;
+};
+
+P_LIB_API PSpinLock *
+p_spinlock_new (void)
+{
+	PSpinLock *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSpinLock))) == NULL)) {
+		P_ERROR ("PSpinLock::p_spinlock_new: failed to allocate memory");
+		return NULL;
+	}
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_spinlock_lock (PSpinLock *spinlock)
+{
+	if (P_UNLIKELY (spinlock == NULL))
+		return FALSE;
+
+	(void) __LOCK_LONG ((volatile void *) &(spinlock->spin));
+
+	return TRUE;
+}
+
+P_LIB_API pboolean
+p_spinlock_trylock (PSpinLock *spinlock)
+{
+	if (P_UNLIKELY (spinlock == NULL))
+		return FALSE;
+
+	return __LOCK_LONG_RETRY ((volatile void *) &(spinlock->spin), 1) == 1 ? TRUE : FALSE;
+}
+
+P_LIB_API pboolean
+p_spinlock_unlock (PSpinLock *spinlock)
+{
+	if (P_UNLIKELY (spinlock == NULL))
+		return FALSE;
+
+	(void) __UNLOCK_LONG ((volatile void *) &(spinlock->spin));
+
+	return TRUE;
+}
+
+P_LIB_API void
+p_spinlock_free (PSpinLock *spinlock)
+{
+	p_free (spinlock);
+}
diff --git a/3rdparty/plibsys/src/pspinlock-sim.c b/3rdparty/plibsys/src/pspinlock-sim.c
new file mode 100644
index 0000000..bf9479f
--- /dev/null
+++ b/3rdparty/plibsys/src/pspinlock-sim.c
@@ -0,0 +1,88 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "pmutex.h"
+#include "pspinlock.h"
+
+struct PSpinLock_ {
+	PMutex *mutex;
+};
+
+P_LIB_API PSpinLock *
+p_spinlock_new (void)
+{
+	PSpinLock *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSpinLock))) == NULL)) {
+		P_ERROR ("PSpinLock::p_spinlock_new: failed to allocate memory");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((ret->mutex = p_mutex_new ()) == NULL)) {
+		P_ERROR ("PSpinLock::p_spinlock_new: p_mutex_new() failed");
+		p_free (ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_spinlock_lock (PSpinLock *spinlock)
+{
+	if (P_UNLIKELY (spinlock == NULL))
+		return FALSE;
+
+	return p_mutex_lock (spinlock->mutex);
+}
+
+P_LIB_API pboolean
+p_spinlock_trylock (PSpinLock *spinlock)
+{
+	if (spinlock == NULL)
+		return FALSE;
+
+	return p_mutex_trylock (spinlock->mutex);
+}
+
+P_LIB_API pboolean
+p_spinlock_unlock (PSpinLock *spinlock)
+{
+	if (P_UNLIKELY (spinlock == NULL))
+		return FALSE;
+
+	return p_mutex_unlock (spinlock->mutex);
+}
+
+P_LIB_API void
+p_spinlock_free (PSpinLock *spinlock)
+{
+	if (P_UNLIKELY (spinlock == NULL))
+		return;
+
+	p_mutex_free (spinlock->mutex);
+	p_free (spinlock);
+}
diff --git a/3rdparty/plibsys/src/pspinlock-sync.c b/3rdparty/plibsys/src/pspinlock-sync.c
new file mode 100644
index 0000000..e15e2a8
--- /dev/null
+++ b/3rdparty/plibsys/src/pspinlock-sync.c
@@ -0,0 +1,83 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "pspinlock.h"
+
+struct PSpinLock_ {
+	volatile pint spin;
+};
+
+P_LIB_API PSpinLock *
+p_spinlock_new (void)
+{
+	PSpinLock *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSpinLock))) == NULL)) {
+		P_ERROR ("PSpinLock::p_spinlock_new: failed to allocate memory");
+		return NULL;
+	}
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_spinlock_lock (PSpinLock *spinlock)
+{
+	if (P_UNLIKELY (spinlock == NULL))
+		return FALSE;
+
+	while ((pboolean) __sync_bool_compare_and_swap (&(spinlock->spin), 0, 1) == FALSE)
+		;
+
+	return TRUE;
+}
+
+P_LIB_API pboolean
+p_spinlock_trylock (PSpinLock *spinlock)
+{
+	if (P_UNLIKELY (spinlock == NULL))
+		return FALSE;
+
+	return (pboolean) __sync_bool_compare_and_swap (&(spinlock->spin), 0, 1);
+}
+
+P_LIB_API pboolean
+p_spinlock_unlock (PSpinLock *spinlock)
+{
+	if (P_UNLIKELY (spinlock == NULL))
+		return FALSE;
+
+	spinlock->spin = 0;
+	__sync_synchronize ();
+
+	return TRUE;
+}
+
+P_LIB_API void
+p_spinlock_free (PSpinLock *spinlock)
+{
+	p_free (spinlock);
+}
diff --git a/3rdparty/plibsys/src/pspinlock-win.c b/3rdparty/plibsys/src/pspinlock-win.c
new file mode 100644
index 0000000..73ccbdc
--- /dev/null
+++ b/3rdparty/plibsys/src/pspinlock-win.c
@@ -0,0 +1,83 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "patomic.h"
+#include "pspinlock.h"
+
+struct PSpinLock_ {
+	volatile pint spin;
+};
+
+P_LIB_API PSpinLock *
+p_spinlock_new (void)
+{
+	PSpinLock *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSpinLock))) == NULL)) {
+		P_ERROR ("PSpinLock::p_spinlock_new: failed to allocate memory");
+		return NULL;
+	}
+
+	return ret;
+}
+
+P_LIB_API pboolean
+p_spinlock_lock (PSpinLock *spinlock)
+{
+	if (P_UNLIKELY (spinlock == NULL))
+		return FALSE;
+
+	while (p_atomic_int_compare_and_exchange (&(spinlock->spin), 0, 1) == FALSE)
+		;
+
+	return TRUE;
+}
+
+P_LIB_API pboolean
+p_spinlock_trylock (PSpinLock *spinlock)
+{
+	if (P_UNLIKELY (spinlock == NULL))
+		return FALSE;
+
+	return p_atomic_int_compare_and_exchange (&(spinlock->spin), 0, 1);
+}
+
+P_LIB_API pboolean
+p_spinlock_unlock (PSpinLock *spinlock)
+{
+	if (P_UNLIKELY (spinlock == NULL))
+		return FALSE;
+
+	p_atomic_int_set (&(spinlock->spin), 0);
+
+	return TRUE;
+}
+
+P_LIB_API void
+p_spinlock_free (PSpinLock *spinlock)
+{
+	p_free (spinlock);
+}
diff --git a/3rdparty/plibsys/src/pspinlock.h b/3rdparty/plibsys/src/pspinlock.h
new file mode 100644
index 0000000..aac3b3b
--- /dev/null
+++ b/3rdparty/plibsys/src/pspinlock.h
@@ -0,0 +1,142 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file pspinlock.h
+ * @brief Light-weight atomic spinlock
+ * @author Alexander Saprykin
+ *
+ * A spinlock is an inter-thread synchronization primitive based on atomic
+ * operations. It allows to guard a critical section from concurrent access of
+ * multiple threads at once. It is very similar to a mutex in semantics, but
+ * inside it provides a more light-weight and fast locking mechanism without
+ * thread sleeping and undesirable context switching. Thus spinlocks should be
+ * used only for small code sections, otherwise long-time spinning can cause
+ * extensive CPU time waste by waiting threads.
+ *
+ * As the spinlock is based on atomic operations it would have the real meaning
+ * only if an underlying atomic model is lock-free (not simulated using the
+ * mutex). You can check if the atomic model is lock-free with
+ * p_atomic_is_lock_free(). Otherwise usage of spinlocks will be the same as the
+ * ordinary mutex.
+ *
+ * To create a new spinlock primitive the p_spinlock_new() routine should be
+ * called, to delete the unused spinlock primitive use p_spinlock_free().
+ *
+ * Use p_spinlock_lock() or p_spinlock_trylock() to synchronize access at the
+ * beginning of the critical section. Only the one thread is allowed to pass
+ * this call, others will wait for the p_spinlock_unlock() call which marks the
+ * end of the critical section. This way the critical section code is guarded
+ * against concurrent access of multiple threads at once.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PSPINLOCK_H
+#define PLIBSYS_HEADER_PSPINLOCK_H
+
+#include <pmacros.h>
+#include <ptypes.h>
+
+P_BEGIN_DECLS
+
+/** Spinlock opaque data structure. */
+typedef struct PSpinLock_ PSpinLock;
+
+/**
+ * @brief Creates a new #PSpinLock object.
+ * @return Pointer to a newly created #PSpinLock object.
+ * @since 0.0.1
+ */
+P_LIB_API PSpinLock *	p_spinlock_new		(void);
+
+/**
+ * @brief Locks a spinlock.
+ * @param spinlock #PSpinLock to lock.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ *
+ * A thread will not sleep in this call if another thread is holding the lock,
+ * instead it will try to lock @a spinlock in an infinite loop.
+ *
+ * If the atomic model is not lock-free this call will have the same effect
+ * as p_mutex_lock().
+ *
+ * Do not lock a spinlock recursively - this may lead to an application
+ * deadlock.
+ */
+P_LIB_API pboolean	p_spinlock_lock		(PSpinLock *spinlock);
+
+/**
+ * @brief Tries to lock a spinlock immediately.
+ * @param spinlock #PSpinLock to lock.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ *
+ * Tries to lock @a spinlock and returns immediately if it is not available for
+ * locking.
+ *
+ * If the atomic model is not lock-free this call will have the same effect
+ * as p_mutex_trylock().
+ *
+ * Do not lock a spinlock recursively - this may lead to an application
+ * deadlock.
+ */
+P_LIB_API pboolean	p_spinlock_trylock	(PSpinLock *spinlock);
+
+/**
+ * @brief Releases a locked spinlock.
+ * @param spinlock #PSpinLock to release.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ *
+ * If @a spinlock was previously locked then it becomes unlocked. Any thread
+ * can unlock any spinlock. It is also safe to call this routine on an unlocked
+ * spinlock.
+ *
+ * If the atomic model is not lock-free this call will have the same effect
+ * as p_mutex_unlock(), thus it is not safe to call this routine on an unlocked
+ * spinlock.
+ */
+P_LIB_API pboolean	p_spinlock_unlock	(PSpinLock *spinlock);
+
+/**
+ * @brief Frees #PSpinLock object.
+ * @param spinlock #PSpinLock to free.
+ * @since 0.0.1
+ *
+ * It doesn't unlock @a spinlock before freeing memory, so you should do it
+ * manually.
+ *
+ * If the atomic model is not lock-free this call will have the same effect
+ * as p_mutex_free().
+ */
+P_LIB_API void		p_spinlock_free		(PSpinLock *spinlock);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PSPINLOCK_H */
diff --git a/3rdparty/plibsys/src/pstdarg.h b/3rdparty/plibsys/src/pstdarg.h
new file mode 100644
index 0000000..bd12f8a
--- /dev/null
+++ b/3rdparty/plibsys/src/pstdarg.h
@@ -0,0 +1,318 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2017 Jean-Damien Durand <jeandamiendurand@free.fr>
+ *
+ * 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.
+ */
+
+/**
+ * @file pstdarg.h
+ * @brief Variable arguments
+ * @author Jean-Damien Durand
+ *
+ * Functions declared with a variable number of arguments use macros to step
+ * through them.
+ *
+ * The #p_va_list type must be declared in such function, #p_va_start,
+ * #p_va_arg and #p_va_end are used to initialize, to step through and to end
+ * the navigation, respectively.
+ *
+ * A variable number of arguments can be propagated to another function which
+ * accepts a #p_va_list parameter, using #p_va_copy.
+ *
+ * Any use of #p_va_start or #p_va_copy must have a corresponding #p_va_end.
+ *
+ * Using a variable number of parameters requires a known-in-advance contract
+ * between the caller and the callee on the number of parameters and their types.
+ * Note, that this mechanism is a weakly typed: the compiler will always apply
+ * default type promotion, regardless if you explicitely typecast an argument in
+ * the stack, i.e.:
+ * - Integer arguments of types lower than #pint are always promoted to #pint,
+ *   or #puint if #pint is not enough.
+ * - Arguments of type @a float are always promoted to @a double.
+ * - Arrays are always promoted to pointers.
+ *
+ * You need to be very careful when using variable arguments. Improper usage may
+ * lead to program crash. In order to avoid type casting mistakes, consider
+ * using macros for variable arguments with explicit type casting provided below.
+ * Though you still can use #p_va_arg if you know what are you doing.
+ *
+ * Please note, that stdarg.h implementation is not compatible with varargs.h,
+ * and only one of them should be used in a compilation unit. You must be sure
+ * that you don't use varargs.h along with the current implmenetation, otherwise
+ * runtime failures are inevitable.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PSTDARG_H
+#define PLIBSYS_HEADER_PSTDARG_H
+
+#include <pmacros.h>
+#include <ptypes.h>
+
+#include <stdio.h>
+#include <stdarg.h>
+#ifndef PLIBSYS_VA_COPY
+#  include <string.h>
+#endif
+
+/**
+ * @brief Opaque type holding variable number of arguments navigation.
+ * @since 0.0.4
+ */
+#define p_va_list va_list
+
+/**
+ * @brief Initializes navigation through a variable number of arguments.
+ * @param ap Declared object of type #p_va_list.
+ * @param last Name of the last argument before @a ap.
+ * @since 0.0.4
+ * @note There must be a corresponding call to #p_va_end(ap) afterwards.
+ */
+#define p_va_start(ap, last) va_start(ap, last)
+
+/**
+ * @brief Gets the next argument in the list.
+ * @param ap Declared object of type va_list, previously initalized with
+ * #p_va_start.
+ * @param type Type of the next argument.
+ * @return Value of the next argument.
+ * @since 0.0.4
+ */
+
+#define p_va_arg(ap, type) (type) va_arg(ap, type)
+
+/**
+ * @brief Ends the navigation.
+ * @param ap Declared object of type #p_va_list.
+ * @since 0.0.4
+ * @note There must be a corresponding call to #p_va_start(ap) before.
+ */
+#define p_va_end(ap) va_end(ap)
+
+/**
+ * @brief Copies a navigation object.
+ * @param dst Destination object of type #p_va_list.
+ * @param src Source object of type #p_va_list.
+ * @since 0.0.4
+ * @note The state of @a src is copied as well, @a dst is initialized as if
+ * #p_va_start(dst) would have been called. There must be a corresponding call
+ * to #p_va_end(dst) afterwards.
+ */
+#ifdef PLIBSYS_VA_COPY
+#  define p_va_copy(dst, src) PLIBSYS_VA_COPY(dst, src)
+#else
+#  define p_va_copy(dst, src) ((void) memcpy (&(dst), &(src), sizeof (va_list)))
+#endif
+
+/**
+ * @def pint8_va_arg
+ * @brief Unstacks a #pint8 variable.
+ * @param ap #p_va_list stack.
+ * @return #pint8 value.
+ * @since 0.0.4
+ */
+#define pint8_va_arg(ap)		((pint8) p_va_arg(ap, pint))
+
+/**
+ * @def puint8_va_arg
+ * @brief Unstacks a #puint8 variable.
+ * @param ap #p_va_list stack.
+ * @return #puint8 value.
+ * @since 0.0.4
+ */
+#define puint8_va_arg(ap)		((puint8) p_va_arg(ap, puint))
+
+/**
+ * @def pint16_va_arg
+ * @brief Unstacks a #pint16 variable.
+ * @param ap #p_va_list stack.
+ * @return #pint16 value.
+ * @since 0.0.4
+ */
+#define pint16_va_arg(ap)		((pint16) p_va_arg(ap, pint))
+
+/**
+ * @def puint16_va_arg
+ * @brief Unstacks a #puint16 variable.
+ * @param ap #p_va_list stack.
+ * @return #puint16 value.
+ * @since 0.0.4
+ */
+#define puint16_va_arg(ap)		((puint16) p_va_arg(ap, puint))
+
+/**
+ * @def pint32_va_arg
+ * @brief Unstacks a #pint32 variable.
+ * @param ap #p_va_list stack.
+ * @return #pint32 value.
+ * @since 0.0.4
+ */
+#define pint32_va_arg(ap)		((pint32) p_va_arg(ap, pint))
+
+/**
+ * @def puint32_va_arg
+ * @brief Unstacks a #puint32 variable.
+ * @param ap #p_va_list stack.
+ * @return #puint32 value.
+ * @since 0.0.4
+ */
+#define puint32_va_arg(ap)		((puint32) p_va_arg(ap, puint))
+
+/**
+ * @def pint64_va_arg
+ * @brief Unstacks a #pint64 variable.
+ * @param ap #p_va_list stack.
+ * @return #pint64 value.
+ * @since 0.0.4
+ */
+#define pint64_va_arg(ap)		(p_va_arg(ap, pint64))
+
+/**
+ * @def puint64_va_arg
+ * @brief Unstacks a #puint64 variable.
+ * @param ap #p_va_list stack.
+ * @return #puint64 value.
+ * @since 0.0.4
+ */
+#define puint64_va_arg(ap)		(p_va_arg(ap, puint64))
+
+/**
+ * @def ppointer_va_arg
+ * @brief Unstacks a #ppointer variable.
+ * @param ap #p_va_list stack.
+ * @return #ppointer value.
+ * @since 0.0.4
+ */
+#define ppointer_va_arg(ap)		(p_va_arg(ap, ppointer))
+
+/**
+ * @def pconstpointer_va_arg
+ * @brief Unstacks a #pconstpointer variable.
+ * @param ap #p_va_list stack.
+ * @return #pconstpointer value.
+ * @since 0.0.4
+ */
+#define pconstpointer_va_arg(ap)	(p_va_arg(ap, pconstpointer))
+
+/**
+ * @def pboolean_va_arg
+ * @brief Unstacks a #pboolean variable.
+ * @param ap #p_va_list stack.
+ * @return #pboolean value.
+ * @since 0.0.4
+ */
+#define pboolean_va_arg(ap)		((pboolean) p_va_arg(ap, pint))
+
+/**
+ * @def pchar_va_arg
+ * @brief Unstacks a #pchar variable.
+ * @param ap #p_va_list stack.
+ * @return #pchar value.
+ * @since 0.0.4
+ */
+#define pchar_va_arg(ap)		((pchar) p_va_arg(ap, pint))
+
+/**
+ * @def pshort_va_arg
+ * @brief Unstacks a #pshort variable.
+ * @param ap #p_va_list stack.
+ * @return #pshort value.
+ * @since 0.0.4
+ */
+#define pshort_va_arg(ap)		((pshort) p_va_arg(ap, pint))
+
+/**
+ * @def pint_va_arg
+ * @brief Unstacks a #pint variable.
+ * @param ap #p_va_list stack.
+ * @return #pint value.
+ * @since 0.0.4
+ */
+#define pint_va_arg(ap)			(p_va_arg(ap, pint))
+
+/**
+ * @def plong_va_arg
+ * @brief Unstacks a #plong variable.
+ * @param ap #p_va_list stack.
+ * @return #plong value.
+ * @since 0.0.4
+ */
+#define plong_va_arg(ap)		(p_va_arg(ap, plong))
+
+/**
+ * @def puchar_va_arg
+ * @brief Unstacks a #puchar variable.
+ * @param ap #p_va_list stack.
+ * @return #puchar value.
+ * @since 0.0.4
+ */
+#define puchar_va_arg(ap)		((puchar) p_va_arg(ap, puint))
+
+/**
+ * @def pushort_va_arg
+ * @brief Unstacks a #pushort variable.
+ * @param ap #p_va_list stack.
+ * @return #pushort value.
+ * @since 0.0.4
+ */
+#define pushort_va_arg(ap)		((pushort) p_va_arg(ap, puint))
+
+/**
+ * @def puint_va_arg
+ * @brief Unstacks a #puint variable.
+ * @param ap #p_va_list stack.
+ * @return #puint value.
+ * @since 0.0.4
+ */
+#define puint_va_arg(ap)		(p_va_arg(ap, puint))
+
+/**
+ * @def pulong_va_arg
+ * @brief Unstacks a #pulong variable.
+ * @param ap #p_va_list stack.
+ * @return #pulong value.
+ * @since 0.0.4
+ */
+#define pulong_va_arg(ap)		(p_va_arg(ap, pulong))
+
+/**
+ * @def pfloat_va_arg
+ * @brief Unstacks a #pfloat variable.
+ * @param ap #p_va_list stack.
+ * @return #pfloat value.
+ * @since 0.0.4
+ */
+#define pfloat_va_arg(ap)		((pfloat) p_va_arg(ap, pdouble))
+
+/**
+ * @def pdouble_va_arg
+ * @brief Unstacks a #pdouble variable.
+ * @param ap #p_va_list stack.
+ * @return #pdouble value.
+ * @since 0.0.4
+ */
+#define pdouble_va_arg(ap)		(p_va_arg(ap, pdouble))
+
+#endif /* PLIBSYS_HEADER_PSTDARG_H */
diff --git a/3rdparty/plibsys/src/pstring.c b/3rdparty/plibsys/src/pstring.c
new file mode 100644
index 0000000..ed46ec5
--- /dev/null
+++ b/3rdparty/plibsys/src/pstring.c
@@ -0,0 +1,206 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2011-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ * Copyright (C) 2009 Tom Van Baak (tvb) www.LeapSecond.com
+ *
+ * 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 "pmem.h"
+#include "pstring.h"
+
+#include <string.h>
+#include <ctype.h>
+
+#define P_STR_MAX_EXPON		308
+
+P_LIB_API pchar *
+p_strdup (const pchar *str)
+{
+	pchar	*ret;
+	psize	len;
+
+	if (P_UNLIKELY (str == NULL))
+		return NULL;
+
+	len = strlen (str) + 1;
+
+	if (P_UNLIKELY ((ret = p_malloc (len)) == NULL))
+		return NULL;
+
+	memcpy (ret, str, len);
+
+	return ret;
+}
+
+P_LIB_API pchar *
+p_strchomp (const pchar *str)
+{
+	pssize		pos_start, pos_end;
+	psize		str_len;
+	pchar		*ret;
+	const pchar	*ptr;
+
+	if (P_UNLIKELY (str == NULL))
+		return NULL;
+
+	ptr       = str;
+	pos_start = 0;
+	pos_end   = ((pssize) strlen (str)) - 1;
+
+	while (pos_start < pos_end && isspace (* ((const puchar *) ptr++)))
+		++pos_start;
+
+	ptr = str + pos_end;
+
+	while (pos_end > 0 && isspace (* ((const puchar *) ptr--)))
+		--pos_end;
+
+	if (pos_end < pos_start)
+		return p_strdup ("\0");
+
+	if (pos_end == pos_start && isspace (* ((const puchar *) (str + pos_end))))
+		return p_strdup ("\0");
+
+	str_len = (psize) (pos_end - pos_start + 2);
+
+	if (P_UNLIKELY ((ret = p_malloc0 (str_len)) == NULL))
+		return NULL;
+
+	memcpy (ret, str + pos_start, str_len - 1);
+	*(ret + str_len - 1) = '\0';
+
+	return ret;
+}
+
+P_LIB_API pchar *
+p_strtok (pchar *str, const pchar *delim, pchar **buf)
+{
+	if (P_UNLIKELY (delim == NULL))
+		return str;
+
+#ifdef P_OS_WIN
+#  ifdef P_CC_MSVC
+	if (P_UNLIKELY (buf == NULL))
+		return str;
+#    if _MSC_VER < 1400
+	P_UNUSED (buf);
+	return strtok (str, delim);
+#    else
+	return strtok_s (str, delim, buf);
+#    endif
+#  else
+	P_UNUSED (buf);
+	return strtok (str, delim);
+#  endif
+#else
+	if (P_UNLIKELY (buf == NULL))
+		return str;
+
+	return strtok_r (str, delim, buf);
+#endif
+}
+
+P_LIB_API double
+p_strtod (const pchar *str)
+{
+	double	sign;
+	double	value;
+	double	scale;
+	double	pow10;
+	puint	expon;
+	pint	frac;
+	pchar	*orig_str, *strp;
+
+	orig_str = p_strchomp (str);
+
+	if (P_UNLIKELY (orig_str == NULL))
+		return 0.0;
+
+	strp = orig_str;
+	sign = 1.0;
+
+	if (*strp == '-') {
+		sign = -1.0;
+		strp += 1;
+	} else if (*strp == '+')
+		strp += 1;
+
+	/* Get digits before decimal point or exponent, if any */
+	for (value = 0.0; isdigit ((pint) *strp); strp += 1)
+		value = value * 10.0 + (*strp - '0');
+
+	/* Get digits after decimal point, if any */
+	if (*strp == '.') {
+		pow10 = 10.0;
+		strp += 1;
+
+		while (isdigit ((pint) *strp)) {
+			value += (*strp - '0') / pow10;
+			pow10 *= 10.0;
+			strp += 1;
+		}
+	}
+
+	/* Handle exponent, if any */
+	frac  = 0;
+	scale = 1.0;
+
+	if ((*strp == 'e') || (*strp == 'E')) {
+		/* Get sign of exponent, if any */
+		strp += 1;
+
+		if (*strp == '-') {
+			frac = 1;
+			strp += 1;
+
+		} else if (*strp == '+')
+			strp += 1;
+
+		/* Get digits of exponent, if any */
+		for (expon = 0; isdigit ((pint) *strp); strp += 1)
+			expon = expon * 10 + (puint) ((*strp - '0'));
+
+		if (P_UNLIKELY (expon > P_STR_MAX_EXPON))
+			expon = P_STR_MAX_EXPON;
+
+		/* Calculate scaling factor */
+		while (expon >= 50) {
+			scale *= 1e50;
+			expon -= 50;
+		}
+
+		while (expon >= 8) {
+			scale *= 1e8;
+			expon -= 8;
+		}
+
+		while (expon > 0) {
+			scale *= 10.0;
+			expon -= 1;
+		}
+	}
+
+	p_free (orig_str);
+
+	/* Return signed and scaled floating point result */
+	return sign * (frac ? (value / scale) : (value * scale));
+}
diff --git a/3rdparty/plibsys/src/pstring.h b/3rdparty/plibsys/src/pstring.h
new file mode 100644
index 0000000..76ebf9d
--- /dev/null
+++ b/3rdparty/plibsys/src/pstring.h
@@ -0,0 +1,117 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2011-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file pstring.h
+ * @brief String manipulation routines
+ * @author Alexander Saprykin
+ *
+ * Strings are represented as a sequence of single-byte characters (from the
+ * ASCII table) with the trailing zero character (\0).
+ *
+ * Some useful string manipulation routines are represented here.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PSTRING_H
+#define PLIBSYS_HEADER_PSTRING_H
+
+#include <pmacros.h>
+#include <ptypes.h>
+
+P_BEGIN_DECLS
+
+/**
+ * @brief Copies a string.
+ * @param str String with the trailing zero to copy.
+ * @return Copy of the @a str in case of success, NULL otherwise. The caller
+ * takes ownership of the returned string.
+ * @since 0.0.1
+ */
+P_LIB_API pchar *	p_strdup	(const pchar	*str);
+
+/**
+ * @brief Removes trailing and leading whitespaces.
+ * @param str String with the trailing zero to process.
+ * @return Newlly allocated string in case of success, NULL otherwise. The
+ * caller takes ownership of the returned string.
+ * @since 0.0.1
+ */
+P_LIB_API pchar *	p_strchomp	(const pchar	*str);
+
+/**
+ * @brief Tokenizes a string by given delimiters.
+ * @param[in,out] str String to tokenize.
+ * @param delim List of delimiters to split the string.
+ * @param buf Context to store tokenize info.
+ * @return Pointer to a splitted zero-terminated string in case of success, NULL
+ * otherwise.
+ * @since 0.0.1
+ * @note The @a str is modified by this call, so take care for that. The
+ * returned pointer points on a @a str substring, so you do not need to call
+ * p_free() on it.
+ *
+ * The common usage of this call is following:
+ * @code
+ * pchar *token, *buf;
+ * pchar str[] = "This is a test string"
+ * pchar delim[] = " \t"
+ * ...
+ * token = p_strtok (str, delim, &buf);
+ *
+ * while (token != NULL) {
+ *     printf ("Splitted string: %s\n", token);
+ *     token = p_strtok (NULL, delim, &buf);
+ * }
+ * @endcode
+ * Take attention that you need to pass the original string only once, after
+ * that you should pass NULL instead. You can also pass different delimiters
+ * each time.
+ *
+ * Some platforms do not support the third parameter and it can be remained
+ * unused. In that case this call wouldn't be thread-safe.
+ */
+P_LIB_API pchar *	p_strtok	(pchar		*str,
+					 const pchar	*delim,
+					 pchar		**buf);
+
+/**
+ * @brief Converts a string to @a double without a locale dependency.
+ * @param str String to convert.
+ * @return Floating point value in case of success, 0 otherwise.
+ * @since 0.0.1
+ *
+ * Since the atof() system call is locale dependent, you can use this call to
+ * convert string variables to @a double values. The decimal point is '.' as in
+ * the 'C' locale.
+ */
+P_LIB_API double	p_strtod	(const pchar	*str);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PSTRING_H */
diff --git a/3rdparty/plibsys/src/psysclose-darwin.c b/3rdparty/plibsys/src/psysclose-darwin.c
new file mode 100644
index 0000000..7c44024
--- /dev/null
+++ b/3rdparty/plibsys/src/psysclose-darwin.c
@@ -0,0 +1,93 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/*
+ * Copyright 2013 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * http://crbug.com/269623
+ * http://openradar.appspot.com/14999594
+ *
+ * When the default version of close used on macOS fails with EINTR, the
+ * file descriptor is not in a deterministic state. It may have been closed,
+ * or it may not have been. This makes it impossible to gracefully recover
+ * from the error. If the close is retried after the FD has been closed, the
+ * subsequent close can report EBADF, or worse, it can close an unrelated FD
+ * opened by another thread. If the close is not retried after the FD has been
+ * left open, the FD is leaked. Neither of these are good options.
+ *
+ * macOS provides an alternate version of close, close$NOCANCEL. This
+ * version will never fail with EINTR before the FD is actually closed. With
+ * this version, it is thus safe to call close without checking for EINTR (as
+ * the HANDLE_EINTR macro does) and not risk leaking the FD. In fact, mixing
+ * this verison of close with HANDLE_EINTR is hazardous.
+ *
+ * The $NOCANCEL variants of various system calls are activated by compiling
+ * with __DARWIN_NON_CANCELABLE, which prevents them from being pthread
+ * cancellation points. Rather than taking such a heavy-handed approach, this
+ * file implements an alternative: to use the $NOCANCEL variant of close (thus
+ * preventing it from being a pthread cancellation point) without affecting
+ * any other system calls.
+ *
+ * This file operates by providing a close function with the non-$NOCANCEL
+ * symbol name expected for the compilation environment as set by <unistd.h>
+ * and <sys/cdefs.h> (the DARWIN_ALIAS_C macro). That function calls the
+ * $NOCANCEL variant, which is resolved from libsyscall. By linking with this
+ * version of close prior to the libsyscall version, close's implementation is
+ * overridden.
+ */
+
+#include <sys/cdefs.h>
+
+/* If the non-cancelable variants of all system calls have already been chosen,
+ * do nothing. */
+#if !__DARWIN_NON_CANCELABLE
+#  if __DARWIN_UNIX03 && !__DARWIN_ONLY_UNIX_CONFORMANCE
+/* When there's a choice between UNIX2003 and pre-UNIX2003 and UNIX2003 has
+ * been chosen. */
+extern int close$NOCANCEL$UNIX2003 (int fd);
+#    define PLIBSYS_CLOSE_INTERFACE close$NOCANCEL$UNIX2003
+#  elif !__DARWIN_UNIX03 && !__DARWIN_ONLY_UNIX_CONFORMANCE
+/* When there's a choice between UNIX2003 and pre-UNIX2003 and pre-UNIX2003
+ * has been chosen. There's no close$NOCANCEL symbol in this case, so use
+ * close$NOCANCEL$UNIX2003 as the implementation. It does the same thing that
+ * close$NOCANCEL would do. */
+extern int close$NOCANCEL$UNIX2003 (int fd);
+#    define PLIBSYS_CLOSE_INTERFACE close$NOCANCEL$UNIX2003
+#  else
+/* When only UNIX2003 is supported. */
+extern int close$NOCANCEL (int fd);
+#    define PLIBSYS_CLOSE_INTERFACE close$NOCANCEL
+#  endif
+#endif
+
+#include "psysclose-private.h"
+
+pint
+p_sys_close (pint fd)
+{
+	return PLIBSYS_CLOSE_INTERFACE (fd);
+}
diff --git a/3rdparty/plibsys/src/psysclose-private.h b/3rdparty/plibsys/src/psysclose-private.h
new file mode 100644
index 0000000..629e2cb
--- /dev/null
+++ b/3rdparty/plibsys/src/psysclose-private.h
@@ -0,0 +1,47 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PSYSCLOSE_PRIVATE_H
+#define PLIBSYS_HEADER_PSYSCLOSE_PRIVATE_H
+
+#include "pmacros.h"
+#include "ptypes.h"
+
+P_BEGIN_DECLS
+
+/**
+ * @brief Safely closes a file descriptor.
+ * @param fd File descriptor to close.
+ * @return -1 in case of success, 0 otherwise.
+ */
+pint p_sys_close (pint fd);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PSYSCLOSE_PRIVATE_H */
diff --git a/3rdparty/plibsys/src/psysclose-unix.c b/3rdparty/plibsys/src/psysclose-unix.c
new file mode 100644
index 0000000..1a43dd6
--- /dev/null
+++ b/3rdparty/plibsys/src/psysclose-unix.c
@@ -0,0 +1,54 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "perror.h"
+#include "psysclose-private.h"
+
+#include <unistd.h>
+#include <errno.h>
+
+pint
+p_sys_close (pint fd)
+{
+#if defined (EINTR) && defined (P_OS_HPUX)
+	pint res, err_code;
+
+	for (;;) {
+		res = close (fd);
+
+		if (P_LIKELY (res == 0))
+			return 0;
+
+		err_code = p_error_get_last_system ();
+
+		if (err_code == EINTR)
+			continue;
+		else
+			return -1;
+	}
+#else
+	return close (fd);
+#endif
+}
diff --git a/3rdparty/plibsys/src/psysclose-win.c b/3rdparty/plibsys/src/psysclose-win.c
new file mode 100644
index 0000000..57f83b5
--- /dev/null
+++ b/3rdparty/plibsys/src/psysclose-win.c
@@ -0,0 +1,33 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "psysclose-private.h"
+
+pint
+p_sys_close (pint fd)
+{
+	/* On Windows we can only close a socket descriptor */
+	return closesocket (fd) == 0 ? 0 : -1;
+}
diff --git a/3rdparty/plibsys/src/ptimeprofiler-amiga.c b/3rdparty/plibsys/src/ptimeprofiler-amiga.c
new file mode 100644
index 0000000..c90f1d7
--- /dev/null
+++ b/3rdparty/plibsys/src/ptimeprofiler-amiga.c
@@ -0,0 +1,70 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "ptimeprofiler.h"
+#include "ptimeprofiler-private.h"
+
+#include <proto/timer.h>
+
+static puint64 pp_time_profiler_freq = 1;
+
+puint64
+p_time_profiler_get_ticks_internal ()
+{
+	struct EClockVal eclock;
+
+	ITimer->ReadEClock (&eclock);
+
+	return (((puint64) eclock.ev_hi) * pp_time_profiler_freq + (puint64) eclock.ev_lo);
+}
+
+puint64
+p_time_profiler_elapsed_usecs_internal (const PTimeProfiler *profiler)
+{
+	puint64 value;
+
+	value = p_time_profiler_get_ticks_internal ();
+
+	/* Check for register overflow */
+
+	if (P_UNLIKELY (value < profiler->counter))
+		value += (((puint64) 1) << 32) * pp_time_profiler_freq;
+
+	return (value - profiler->counter) * 1000000ULL / pp_time_profiler_freq;
+}
+
+void
+p_time_profiler_init (void)
+{
+	struct EClockVal eclock;
+
+	pp_time_profiler_freq = (puint64) ITimer->ReadEClock (&eclock);
+}
+
+void
+p_time_profiler_shutdown (void)
+{
+	pp_time_profiler_freq = 1;
+}
diff --git a/3rdparty/plibsys/src/ptimeprofiler-beos.c b/3rdparty/plibsys/src/ptimeprofiler-beos.c
new file mode 100644
index 0000000..8d651d7
--- /dev/null
+++ b/3rdparty/plibsys/src/ptimeprofiler-beos.c
@@ -0,0 +1,51 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "ptimeprofiler.h"
+#include "ptimeprofiler-private.h"
+
+#include <kernel/OS.h>
+
+puint64
+p_time_profiler_get_ticks_internal ()
+{
+	return (puint64) system_time ();
+}
+
+puint64
+p_time_profiler_elapsed_usecs_internal (const PTimeProfiler *profiler)
+{
+	return ((puint64) system_time ()) - profiler->counter;
+}
+
+void
+p_time_profiler_init (void)
+{
+}
+
+void
+p_time_profiler_shutdown (void)
+{
+}
diff --git a/3rdparty/plibsys/src/ptimeprofiler-generic.c b/3rdparty/plibsys/src/ptimeprofiler-generic.c
new file mode 100644
index 0000000..d984d47
--- /dev/null
+++ b/3rdparty/plibsys/src/ptimeprofiler-generic.c
@@ -0,0 +1,58 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2013-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "ptimeprofiler.h"
+#include "ptimeprofiler-private.h"
+
+#include <time.h>
+
+puint64
+p_time_profiler_get_ticks_internal ()
+{
+	pint64 val;
+
+	if (P_UNLIKELY ((val = (pint64) time (NULL)) == -1)) {
+		P_ERROR ("PTimeProfiler::p_time_profiler_get_ticks_internal: time() failed");
+		return 0;
+	}
+
+	return (puint64) (val * 1000000);
+}
+
+puint64
+p_time_profiler_elapsed_usecs_internal (const PTimeProfiler *profiler)
+{
+	return p_time_profiler_get_ticks_internal () - profiler->counter;
+}
+
+void
+p_time_profiler_init (void)
+{
+}
+
+void
+p_time_profiler_shutdown (void)
+{
+}
diff --git a/3rdparty/plibsys/src/ptimeprofiler-mach.c b/3rdparty/plibsys/src/ptimeprofiler-mach.c
new file mode 100644
index 0000000..6bef3c7
--- /dev/null
+++ b/3rdparty/plibsys/src/ptimeprofiler-mach.c
@@ -0,0 +1,73 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2013-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "ptimeprofiler.h"
+#include "ptimeprofiler-private.h"
+
+#include <mach/mach_time.h>
+
+static puint64 pp_time_profiler_freq_num   = 0;
+static puint64 pp_time_profiler_freq_denom = 0;
+
+puint64
+p_time_profiler_get_ticks_internal ()
+{
+	puint64 val = mach_absolute_time ();
+
+	/* To prevent overflow */
+	val /= 1000;
+
+	val *= pp_time_profiler_freq_num;
+	val /= pp_time_profiler_freq_denom;
+
+	return val;
+}
+
+puint64
+p_time_profiler_elapsed_usecs_internal (const PTimeProfiler *profiler)
+{
+	return p_time_profiler_get_ticks_internal () - profiler->counter;
+}
+
+void
+p_time_profiler_init (void)
+{
+	mach_timebase_info_data_t tb;
+
+	if (P_UNLIKELY (mach_timebase_info (&tb) != KERN_SUCCESS || tb.denom == 0)) {
+		P_ERROR ("PTimeProfiler::p_time_profiler_init: mach_timebase_info() failed");
+		return;
+	}
+
+	pp_time_profiler_freq_num   = (puint64) tb.numer;
+	pp_time_profiler_freq_denom = (puint64) tb.denom;
+}
+
+void
+p_time_profiler_shutdown (void)
+{
+	pp_time_profiler_freq_num   = 0;
+	pp_time_profiler_freq_denom = 0;
+}
diff --git a/3rdparty/plibsys/src/ptimeprofiler-os2.c b/3rdparty/plibsys/src/ptimeprofiler-os2.c
new file mode 100644
index 0000000..e511bd1
--- /dev/null
+++ b/3rdparty/plibsys/src/ptimeprofiler-os2.c
@@ -0,0 +1,107 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "ptimeprofiler.h"
+#include "ptimeprofiler-private.h"
+
+#define INCL_DOSPROFILE
+#define INCL_DOSERRORS
+#include <os2.h>
+
+#if PLIBSYS_HAS_LLDIV
+#  ifdef P_CC_GNU
+#    define __USE_ISOC99
+#  endif
+#  include <stdlib.h>
+#endif
+
+static puint64 pp_time_profiler_freq = 1;
+
+puint64
+p_time_profiler_get_ticks_internal ()
+{
+	union {
+		puint64	ticks;
+		QWORD	tcounter;
+	} tick_time;
+
+	if (P_UNLIKELY (DosTmrQueryTime (&tick_time.tcounter) != NO_ERROR)) {
+		P_ERROR ("PTimeProfiler::p_time_profiler_get_ticks_internal: DosTmrQueryTime() failed");
+		return 0;
+	}
+
+	return tick_time.ticks;
+}
+
+puint64
+p_time_profiler_elapsed_usecs_internal (const PTimeProfiler *profiler)
+{
+	puint64	ticks;
+#if PLIBSYS_HAS_LLDIV
+	lldiv_t	ldres;
+#endif
+	puint64	quot;
+	puint64	rem;
+
+	ticks = p_time_profiler_get_ticks_internal ();
+
+	if (ticks < profiler->counter) {
+		P_WARNING ("PTimeProfiler::p_time_profiler_elapsed_usecs_internal: negative jitter");
+		return 1;
+	}
+
+	ticks -= profiler->counter;
+
+#if PLIBSYS_HAS_LLDIV
+	ldres = lldiv ((long long) ticks, (long long) pp_time_profiler_freq);
+
+	quot = ldres.quot;
+	rem  = ldres.rem;
+#else
+	quot = ticks / pp_time_profiler_freq;
+	rem  = ticks % pp_time_profiler_freq;
+#endif
+
+	return (puint64) (quot * 1000000LL + (rem * 1000000LL) / pp_time_profiler_freq);
+}
+
+void
+p_time_profiler_init (void)
+{
+	ULONG freq;
+
+	if (P_UNLIKELY (DosTmrQueryFreq (&freq) != NO_ERROR)) {
+		P_ERROR ("PTimeProfiler::p_time_profiler_init: DosTmrQueryFreq() failed");
+		return;
+	}
+
+	pp_time_profiler_freq = (puint64) freq;
+}
+
+void
+p_time_profiler_shutdown (void)
+{
+	pp_time_profiler_freq = 1;
+}
diff --git a/3rdparty/plibsys/src/ptimeprofiler-posix.c b/3rdparty/plibsys/src/ptimeprofiler-posix.c
new file mode 100644
index 0000000..b913bbb
--- /dev/null
+++ b/3rdparty/plibsys/src/ptimeprofiler-posix.c
@@ -0,0 +1,109 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2013-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "ptimeprofiler.h"
+#include "ptimeprofiler-private.h"
+
+#include <unistd.h>
+#include <time.h>
+#include <sys/time.h>
+
+#ifndef _POSIX_MONOTONIC_CLOCK
+#  define _POSIX_MONOTONIC_CLOCK (-1)
+#endif
+
+typedef puint64 (* PPOSIXTicksFunc) (void);
+
+static PPOSIXTicksFunc pp_time_profiler_ticks_func = NULL;
+
+#if (_POSIX_MONOTONIC_CLOCK >= 0) || defined (P_OS_IRIX)
+static puint64 pp_time_profiler_get_ticks_clock ();
+#endif
+
+static puint64 pp_time_profiler_get_ticks_gtod ();
+
+#if (_POSIX_MONOTONIC_CLOCK >= 0) || defined (P_OS_IRIX)
+static puint64
+pp_time_profiler_get_ticks_clock ()
+{
+	struct timespec	ts;
+
+#ifdef P_OS_IRIX
+	if (P_UNLIKELY (clock_gettime (CLOCK_SGI_CYCLE, &ts) != 0)) {
+#else
+	if (P_UNLIKELY (clock_gettime (CLOCK_MONOTONIC, &ts) != 0)) {
+#endif
+		P_ERROR ("PTimeProfiler::pp_time_profiler_get_ticks_clock: clock_gettime() failed");
+		return pp_time_profiler_get_ticks_gtod ();
+	} else
+		return (puint64) (ts.tv_sec * 1000000 + ts.tv_nsec / 1000);
+}
+#endif
+
+static puint64
+pp_time_profiler_get_ticks_gtod ()
+{
+	struct timeval tv;
+
+	if (P_UNLIKELY (gettimeofday (&tv, NULL) != 0)) {
+		P_ERROR ("PTimeProfiler::pp_time_profiler_get_ticks_gtod: gettimeofday() failed");
+		return 0;
+	}
+
+	return (puint64) (tv.tv_sec * 1000000 + tv.tv_usec);
+}
+
+puint64
+p_time_profiler_get_ticks_internal ()
+{
+	return pp_time_profiler_ticks_func ();
+}
+
+puint64
+p_time_profiler_elapsed_usecs_internal (const PTimeProfiler *profiler)
+{
+	return pp_time_profiler_ticks_func () - profiler->counter;
+}
+
+void
+p_time_profiler_init (void)
+{
+#if defined (P_OS_IRIX) || (_POSIX_MONOTONIC_CLOCK > 0)
+	pp_time_profiler_ticks_func = (PPOSIXTicksFunc) pp_time_profiler_get_ticks_clock;
+#elif (_POSIX_MONOTONIC_CLOCK == 0) && defined (_SC_MONOTONIC_CLOCK)
+	if (P_LIKELY (sysconf (_SC_MONOTONIC_CLOCK) > 0))
+		pp_time_profiler_ticks_func = (PPOSIXTicksFunc) pp_time_profiler_get_ticks_clock;
+	else
+		pp_time_profiler_ticks_func = (PPOSIXTicksFunc) pp_time_profiler_get_ticks_gtod;
+#else
+	pp_time_profiler_ticks_func = (PPOSIXTicksFunc) pp_time_profiler_get_ticks_gtod;
+#endif
+}
+
+void
+p_time_profiler_shutdown (void)
+{
+	pp_time_profiler_ticks_func = NULL;
+}
diff --git a/3rdparty/plibsys/src/ptimeprofiler-private.h b/3rdparty/plibsys/src/ptimeprofiler-private.h
new file mode 100644
index 0000000..c7c250d
--- /dev/null
+++ b/3rdparty/plibsys/src/ptimeprofiler-private.h
@@ -0,0 +1,45 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PTIMEPROFILER_PRIVATE_H
+#define PLIBSYS_HEADER_PTIMEPROFILER_PRIVATE_H
+
+#include "pmacros.h"
+#include "ptypes.h"
+
+P_BEGIN_DECLS
+
+/** Time profiler opaque data structure. */
+struct PTimeProfiler_ {
+	puint64 counter; /**< Ticks counter. */
+};
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PTIMEPROFILER_PRIVATE_H */
diff --git a/3rdparty/plibsys/src/ptimeprofiler-solaris.c b/3rdparty/plibsys/src/ptimeprofiler-solaris.c
new file mode 100644
index 0000000..5832614
--- /dev/null
+++ b/3rdparty/plibsys/src/ptimeprofiler-solaris.c
@@ -0,0 +1,53 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "ptimeprofiler.h"
+#include "ptimeprofiler-private.h"
+
+#include <unistd.h>
+#include <time.h>
+#include <sys/time.h>
+
+puint64
+p_time_profiler_get_ticks_internal ()
+{
+	return (puint64) gethrtime ();
+}
+
+puint64
+p_time_profiler_elapsed_usecs_internal (const PTimeProfiler *profiler)
+{
+	return (((puint64) gethrtime ()) - profiler->counter) / 1000;
+}
+
+void
+p_time_profiler_init (void)
+{
+}
+
+void
+p_time_profiler_shutdown (void)
+{
+}
diff --git a/3rdparty/plibsys/src/ptimeprofiler-win.c b/3rdparty/plibsys/src/ptimeprofiler-win.c
new file mode 100644
index 0000000..22ee72f
--- /dev/null
+++ b/3rdparty/plibsys/src/ptimeprofiler-win.c
@@ -0,0 +1,169 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2015-2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/* https://msdn.microsoft.com/ru-ru/library/windows/desktop/dn553408(v=vs.85).aspx */
+
+#include "ptimeprofiler.h"
+#include "ptimeprofiler-private.h"
+
+#include <time.h>
+
+#if PLIBSYS_HAS_LLDIV
+#  include <stdlib.h>
+#endif
+
+typedef puint64 (WINAPI * PWin32TicksFunc) (void);
+typedef puint64 (* PWin32ElapsedFunc) (puint64 last_counter);
+
+static PWin32TicksFunc   pp_time_profiler_ticks_func   = NULL;
+static PWin32ElapsedFunc pp_time_profiler_elapsed_func = NULL;
+static puint64           pp_time_profiler_freq         = 1;
+
+static puint64 WINAPI pp_time_profiler_get_hr_ticks (void);
+static puint64 pp_time_profiler_elapsed_hr (puint64 last_counter);
+static puint64 pp_time_profiler_elapsed_tick64 (puint64 last_counter);
+static puint64 pp_time_profiler_elapsed_tick (puint64 last_counter);
+
+static puint64 WINAPI
+pp_time_profiler_get_hr_ticks (void)
+{
+	LARGE_INTEGER tcounter;
+
+	if (P_UNLIKELY (QueryPerformanceCounter (&tcounter) == FALSE)) {
+		P_ERROR ("PTimeProfiler::pp_time_profiler_get_hr_ticks: QueryPerformanceCounter() failed");
+		tcounter.QuadPart = 0;
+	}
+
+	return (puint64) tcounter.QuadPart;
+}
+
+static puint64
+pp_time_profiler_elapsed_hr (puint64 last_counter)
+{
+	puint64	ticks;
+#ifdef PLIBSYS_HAS_LLDIV
+	lldiv_t	ldres;
+#endif
+	puint64	quot;
+	puint64	rem;
+
+	ticks = pp_time_profiler_ticks_func () - last_counter;
+
+#ifdef PLIBSYS_HAS_LLDIV
+	ldres = lldiv ((long long) ticks, (long long) pp_time_profiler_freq);
+
+	quot = ldres.quot;
+	rem  = ldres.rem;
+#else
+	quot = ticks / pp_time_profiler_freq;
+	rem  = ticks % pp_time_profiler_freq;
+#endif
+
+	return (puint64) (quot * 1000000 + (rem * 1000000) / pp_time_profiler_freq);
+}
+
+static puint64
+pp_time_profiler_elapsed_tick64 (puint64 last_counter)
+{
+	return (pp_time_profiler_ticks_func () - last_counter) * 1000;
+}
+
+static puint64
+pp_time_profiler_elapsed_tick (puint64 last_counter)
+{
+	puint64 val;
+	puint64 high_bit;
+
+	high_bit = 0;
+	val      = pp_time_profiler_ticks_func ();
+
+	if (P_UNLIKELY (val < last_counter))
+		high_bit = 1;
+
+	return ((val | (high_bit << 32)) - last_counter) * 1000;
+}
+
+puint64
+p_time_profiler_get_ticks_internal ()
+{
+	return pp_time_profiler_ticks_func ();
+}
+
+puint64
+p_time_profiler_elapsed_usecs_internal (const PTimeProfiler *profiler)
+{
+	return pp_time_profiler_elapsed_func (profiler->counter);
+}
+
+void
+p_time_profiler_init (void)
+{
+	LARGE_INTEGER	tcounter;
+	HMODULE		hmodule;
+	pboolean	has_qpc;
+
+	has_qpc = (QueryPerformanceCounter (&tcounter) != 0 && tcounter.QuadPart != 0) ? TRUE : FALSE;
+
+	if (has_qpc == TRUE) {
+		if (P_UNLIKELY (QueryPerformanceFrequency (&tcounter) == 0)) {
+			P_ERROR ("PTimeProfiler::p_time_profiler_init: QueryPerformanceFrequency() failed");
+			has_qpc = FALSE;
+		} else {
+			pp_time_profiler_freq         = (puint64) (tcounter.QuadPart);
+			pp_time_profiler_ticks_func   = (PWin32TicksFunc) pp_time_profiler_get_hr_ticks;
+			pp_time_profiler_elapsed_func = (PWin32ElapsedFunc) pp_time_profiler_elapsed_hr;
+		}
+	}
+
+	if (P_UNLIKELY (has_qpc == FALSE)) {
+		hmodule = GetModuleHandleA ("kernel32.dll");
+
+		if (P_UNLIKELY (hmodule == NULL)) {
+			P_ERROR ("PTimeProfiler::p_time_profiler_init: failed to load kernel32.dll module");
+			return;
+		}
+
+		pp_time_profiler_ticks_func   = (PWin32TicksFunc) GetProcAddress (hmodule, "GetTickCount64");
+		pp_time_profiler_elapsed_func = (PWin32ElapsedFunc) pp_time_profiler_elapsed_tick64;
+
+		if (P_UNLIKELY (pp_time_profiler_ticks_func == NULL)) {
+			pp_time_profiler_ticks_func   = (PWin32TicksFunc) GetProcAddress (hmodule, "GetTickCount");
+			pp_time_profiler_elapsed_func = (PWin32ElapsedFunc) pp_time_profiler_elapsed_tick;
+		}
+
+		if (P_UNLIKELY (pp_time_profiler_ticks_func == NULL)) {
+			P_ERROR ("PTimeProfiler::p_time_profiler_init: no reliable tick counter");
+			pp_time_profiler_elapsed_func = NULL;
+		}
+	}
+}
+
+void
+p_time_profiler_shutdown (void)
+{
+	pp_time_profiler_freq         = 1;
+	pp_time_profiler_ticks_func   = NULL;
+	pp_time_profiler_elapsed_func = NULL;
+}
diff --git a/3rdparty/plibsys/src/ptimeprofiler.c b/3rdparty/plibsys/src/ptimeprofiler.c
new file mode 100644
index 0000000..a23ea54
--- /dev/null
+++ b/3rdparty/plibsys/src/ptimeprofiler.c
@@ -0,0 +1,70 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "ptimeprofiler.h"
+#include "ptimeprofiler-private.h"
+
+extern puint64 p_time_profiler_get_ticks_internal (void);
+extern puint64 p_time_profiler_elapsed_usecs_internal (const PTimeProfiler *profiler);
+
+P_LIB_API PTimeProfiler *
+p_time_profiler_new ()
+{
+	PTimeProfiler *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PTimeProfiler))) == NULL)) {
+		P_ERROR ("PTimeProfiler: failed to allocate memory");
+		return NULL;
+	}
+
+	ret->counter = p_time_profiler_get_ticks_internal ();
+
+	return ret;
+}
+
+P_LIB_API void
+p_time_profiler_reset (PTimeProfiler *profiler)
+{
+	if (P_UNLIKELY (profiler == NULL))
+		return;
+
+	profiler->counter = p_time_profiler_get_ticks_internal ();
+}
+
+P_LIB_API puint64
+p_time_profiler_elapsed_usecs (const PTimeProfiler *profiler)
+{
+	if (P_UNLIKELY (profiler == NULL))
+		return 0;
+
+	return p_time_profiler_elapsed_usecs_internal (profiler);
+}
+
+P_LIB_API void
+p_time_profiler_free (PTimeProfiler *profiler)
+{
+	p_free (profiler);
+}
diff --git a/3rdparty/plibsys/src/ptimeprofiler.h b/3rdparty/plibsys/src/ptimeprofiler.h
new file mode 100644
index 0000000..6dbf4ab
--- /dev/null
+++ b/3rdparty/plibsys/src/ptimeprofiler.h
@@ -0,0 +1,93 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2013-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file ptimeprofiler.h
+ * @brief Time profiler
+ * @author Alexander Saprykin
+ *
+ * #PTimeProfiler acts like a time chronometer: in any moment of time you can
+ * make a time slice to see how much time elapsed since the last slice or timer
+ * start.
+ *
+ * This profiler is useful to gather information about execution time for calls
+ * or parts of the code. It can help to leverage bottle-necks in your code.
+ *
+ * To start using a profiler create a new one with p_time_profiler_new() call
+ * and p_time_profiler_elapsed_usecs() to get elapsed time since the creation.
+ * If you need to reset a profiler use p_time_profiler_reset(). Remove a
+ * profiler with p_time_profiler_free().
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PTIMEPROFILER_H
+#define PLIBSYS_HEADER_PTIMEPROFILER_H
+
+#include <pmacros.h>
+#include <ptypes.h>
+
+P_BEGIN_DECLS
+
+/** Time profiler opaque data structure. */
+typedef struct PTimeProfiler_ PTimeProfiler;
+
+/**
+ * @brief Creates a new #PTimeProfiler object.
+ * @return Pointer to a newly created #PTimeProfiler object.
+ * @since 0.0.1
+ */
+P_LIB_API PTimeProfiler *	p_time_profiler_new		(void);
+
+/**
+ * @brief Resets the #PTimeProfiler's internal counter to zero.
+ * @param profiler Time profiler to reset.
+ * @since 0.0.1
+ *
+ * After a reset the time profiler begins to count elapsed time from that moment
+ * of time.
+ */
+P_LIB_API void			p_time_profiler_reset		(PTimeProfiler *	profiler);
+
+/**
+ * @brief Calculates elapsed time since the last reset or creation.
+ * @param profiler Time profiler to calculate elapsed time for.
+ * @return Microseconds elapsed since the last reset or creation.
+ * @since 0.0.1
+ */
+P_LIB_API puint64		p_time_profiler_elapsed_usecs	(const PTimeProfiler *	profiler);
+
+/**
+ * @brief Frees #PTimeProfiler object.
+ * @param profiler #PTimeProfiler to free.
+ * @since 0.0.1
+ */
+P_LIB_API void			p_time_profiler_free		(PTimeProfiler *	profiler);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PTIMEPROFILER_H */
diff --git a/3rdparty/plibsys/src/ptree-avl.c b/3rdparty/plibsys/src/ptree-avl.c
new file mode 100644
index 0000000..c100e5c
--- /dev/null
+++ b/3rdparty/plibsys/src/ptree-avl.c
@@ -0,0 +1,481 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "ptree-avl.h"
+
+typedef struct PTreeAVLNode_ {
+	struct PTreeBaseNode_	base;
+	struct PTreeAVLNode_	*parent;
+	pint			balance_factor;
+} PTreeAVLNode;
+
+static void pp_tree_avl_rotate_left (PTreeAVLNode *node, PTreeBaseNode **root);
+static void pp_tree_avl_rotate_right (PTreeAVLNode *node, PTreeBaseNode **root);
+static void pp_tree_avl_rotate_left_right (PTreeAVLNode *node, PTreeBaseNode **root);
+static void pp_tree_avl_rotate_right_left (PTreeAVLNode *node, PTreeBaseNode **root);
+static void pp_tree_avl_balance_insert (PTreeAVLNode *node, PTreeBaseNode **root);
+static void pp_tree_avl_balance_remove (PTreeAVLNode *node, PTreeBaseNode **root);
+
+static void
+pp_tree_avl_rotate_left (PTreeAVLNode *node, PTreeBaseNode **root)
+{
+	node->parent->base.right = node->base.left;
+
+	if (node->base.left != NULL)
+		((PTreeAVLNode *) node->base.left)->parent = (PTreeAVLNode *) node->parent;
+
+	node->base.left = (PTreeBaseNode *) node->parent;
+	node->parent = ((PTreeAVLNode *) node->base.left)->parent;
+	((PTreeAVLNode *) node->base.left)->parent = node;
+
+	if (P_LIKELY (node->parent != NULL)) {
+		if (node->parent->base.left == node->base.left)
+			node->parent->base.left = (PTreeBaseNode *) node;
+		else
+			node->parent->base.right = (PTreeBaseNode *) node;
+	} else
+		*root = (PTreeBaseNode *) node;
+
+	/* Restore balance factor */
+	((PTreeAVLNode *) node)->balance_factor +=1;
+	((PTreeAVLNode *) node->base.left)->balance_factor = -((PTreeAVLNode *) node)->balance_factor;
+}
+
+static void
+pp_tree_avl_rotate_right (PTreeAVLNode *node, PTreeBaseNode **root)
+{
+	node->parent->base.left = node->base.right;
+
+	if (node->base.right != NULL)
+		((PTreeAVLNode *) node->base.right)->parent = (PTreeAVLNode *) node->parent;
+
+	node->base.right = (PTreeBaseNode *) node->parent;
+	node->parent = ((PTreeAVLNode *) node->base.right)->parent;
+	((PTreeAVLNode *) node->base.right)->parent = node;
+
+	if (P_LIKELY (node->parent != NULL)) {
+		if (node->parent->base.left == node->base.right)
+			node->parent->base.left = (PTreeBaseNode *) node;
+		else
+			node->parent->base.right = (PTreeBaseNode *) node;
+	} else
+		*root = (PTreeBaseNode *) node;
+
+	/* Restore balance factor */
+	((PTreeAVLNode *) node)->balance_factor -= 1;
+	((PTreeAVLNode *) node->base.right)->balance_factor = -((PTreeAVLNode *) node)->balance_factor;
+}
+
+static void
+pp_tree_avl_rotate_left_right (PTreeAVLNode *node, PTreeBaseNode **root)
+{
+	PTreeAVLNode *tmp_node;
+
+	tmp_node = (PTreeAVLNode *) node->base.right;
+	node->base.right = tmp_node->base.left;
+
+	if (node->base.right != NULL)
+		((PTreeAVLNode *) node->base.right)->parent = node;
+
+	tmp_node->parent = node->parent->parent;
+
+	if (P_LIKELY (tmp_node->parent != NULL)) {
+		if (tmp_node->parent->base.left == (PTreeBaseNode *) node->parent)
+			tmp_node->parent->base.left = (PTreeBaseNode *) tmp_node;
+		else
+			tmp_node->parent->base.right = (PTreeBaseNode *) tmp_node;
+	} else
+		*root = (PTreeBaseNode *) tmp_node;
+
+	node->parent->base.left = tmp_node->base.right;
+
+	if (node->parent->base.left != NULL)
+		((PTreeAVLNode *) node->parent->base.left)->parent = node->parent;
+
+	tmp_node->base.right = (PTreeBaseNode *) node->parent;
+	((PTreeAVLNode *) tmp_node->base.right)->parent = tmp_node;
+
+	tmp_node->base.left = (PTreeBaseNode *) node;
+	node->parent = tmp_node;
+
+	/* Restore balance factor */
+	if (tmp_node->balance_factor == 1) {
+		((PTreeAVLNode *) tmp_node->base.left)->balance_factor  = 0;
+		((PTreeAVLNode *) tmp_node->base.right)->balance_factor = -1;
+	} else if (tmp_node->balance_factor == -1) {
+		((PTreeAVLNode *) tmp_node->base.left)->balance_factor  = 1;
+		((PTreeAVLNode *) tmp_node->base.right)->balance_factor = 0;
+	} else {
+		((PTreeAVLNode *) tmp_node->base.left)->balance_factor  = 0;
+		((PTreeAVLNode *) tmp_node->base.right)->balance_factor = 0;
+	}
+
+	tmp_node->balance_factor = 0;
+}
+
+static void
+pp_tree_avl_rotate_right_left (PTreeAVLNode *node, PTreeBaseNode **root)
+{
+	PTreeAVLNode *tmp_node;
+
+	tmp_node = (PTreeAVLNode *) node->base.left;
+	node->base.left = tmp_node->base.right;
+
+	if (node->base.left != NULL)
+		((PTreeAVLNode *) node->base.left)->parent = node;
+
+	tmp_node->parent = node->parent->parent;
+
+	if (P_LIKELY (tmp_node->parent != NULL)) {
+		if (tmp_node->parent->base.left == (PTreeBaseNode *) node->parent)
+			tmp_node->parent->base.left = (PTreeBaseNode *) tmp_node;
+		else
+			tmp_node->parent->base.right = (PTreeBaseNode *) tmp_node;
+	} else
+		*root = (PTreeBaseNode *) tmp_node;
+
+	node->parent->base.right = tmp_node->base.left;
+
+	if (node->parent->base.right != NULL)
+		((PTreeAVLNode *) node->parent->base.right)->parent = node->parent;
+
+	tmp_node->base.left = (PTreeBaseNode *) node->parent;
+	((PTreeAVLNode *) tmp_node->base.left)->parent = tmp_node;
+
+	tmp_node->base.right = (PTreeBaseNode *) node;
+	node->parent = tmp_node;
+
+	/* Restore balance factor */
+	if (tmp_node->balance_factor == 1) {
+		((PTreeAVLNode *) tmp_node->base.left)->balance_factor  = 0;
+		((PTreeAVLNode *) tmp_node->base.right)->balance_factor = -1;
+	} else if (tmp_node->balance_factor == -1) {
+		((PTreeAVLNode *) tmp_node->base.left)->balance_factor  = 1;
+		((PTreeAVLNode *) tmp_node->base.right)->balance_factor = 0;
+	} else {
+		((PTreeAVLNode *) tmp_node->base.left)->balance_factor  = 0;
+		((PTreeAVLNode *) tmp_node->base.right)->balance_factor = 0;
+	}
+
+	tmp_node->balance_factor = 0;
+}
+
+static void
+pp_tree_avl_balance_insert (PTreeAVLNode *node, PTreeBaseNode **root)
+{
+	PTreeAVLNode *parent;
+
+	while (TRUE) {
+		parent = node->parent;
+
+		if (P_UNLIKELY (parent == NULL))
+			break;
+
+		if (parent->base.left == (PTreeBaseNode *) node) {
+			if (parent->balance_factor == 1) {
+				if (node->balance_factor == -1)
+					/* Case 1: Left-right rotate
+					 *
+					 *      (5)             (4)
+					 *      / \            /   \
+					 *    (3)  A   -->   (3)    (5)
+					 *    / \            / \    / \
+					 *   B  (4)         B   C  D   A
+					 *      / \
+					 *     C   D
+					 */
+					pp_tree_avl_rotate_left_right (node, root);
+				else
+					/* Case 2: Right rotate
+					 *
+					 *        (5)             (4)
+					 *        / \            /   \
+					 *      (4)  A   -->   (3)    (5)
+					 *      / \            / \    / \
+					 *    (3)  B          C   D  B   A
+					 *    / \
+					 *   C   D
+					 */
+					pp_tree_avl_rotate_right (node, root);
+
+				break;
+			} else if (parent->balance_factor == -1) {
+				/* Case 3: Increase parent balance factor */
+				parent->balance_factor = 0;
+				break;
+			} else
+				/* Case 4: Increase parent balance factor */
+				parent->balance_factor = 1;
+		} else {
+			if (parent->balance_factor == -1) {
+				if (node->balance_factor == 1)
+					/* Case 1: Right-left rotate
+					 *
+					 *    (3)               (4)
+					 *    / \              /   \
+					 *   A  (5)    -->   (3)    (5)
+					 *      / \          / \    / \
+					 *    (4)  B        A   C  D   B
+					 *    / \
+					 *   C   D
+					 */
+					pp_tree_avl_rotate_right_left (node, root);
+				else
+					/* Case 2: Left rotate
+					 *
+					 *    (3)              (4)
+					 *    / \             /   \
+					 *   A  (4)   -->   (3)    (5)
+					 *      / \         / \    / \
+					 *     B  (5)      A   B  C   D
+					 *        / \
+					 *       C   D
+					 */
+					pp_tree_avl_rotate_left (node, root);
+
+				break;
+			} else if (parent->balance_factor == 1) {
+				/* Case 3: Decrease parent balance factor */
+				parent->balance_factor = 0;
+				break;
+			} else
+				/* Case 4: Decrease parent balance factor */
+				parent->balance_factor = -1;
+		}
+
+		node = node->parent;
+	}
+}
+
+static void
+pp_tree_avl_balance_remove (PTreeAVLNode *node, PTreeBaseNode **root)
+{
+	PTreeAVLNode	*parent;
+	PTreeAVLNode	*sibling;
+	pint		sibling_balance;
+
+	while (TRUE) {
+		parent = node->parent;
+
+		if (P_UNLIKELY (parent == NULL))
+			break;
+
+		if (parent->base.left == (PTreeBaseNode *) node) {
+			if (parent->balance_factor == -1) {
+				sibling         = (PTreeAVLNode *) parent->base.right;
+				sibling_balance = sibling->balance_factor;
+
+				if (sibling->balance_factor == 1)
+					/* Case 1 */
+					pp_tree_avl_rotate_right_left (sibling, root);
+				 else
+					/* Case 2 */
+					pp_tree_avl_rotate_left (sibling, root);
+
+				node = parent;
+
+				if (sibling_balance == 0)
+					break;
+			} else if (parent->balance_factor == 0) {
+				/* Case 3 */
+				parent->balance_factor = -1;
+				break;
+			} else
+				/* Case 4 */
+				parent->balance_factor = 0;
+		} else {
+			if (parent->balance_factor == 1) {
+				sibling         = (PTreeAVLNode *) parent->base.left;
+				sibling_balance = sibling->balance_factor;
+
+				if (sibling->balance_factor == -1)
+					/* Case 1 */
+					pp_tree_avl_rotate_left_right (sibling, root);
+				else
+					/* Case 2 */
+					pp_tree_avl_rotate_right (sibling, root);
+
+				node = parent;
+
+				if (sibling_balance == 0)
+					break;
+			} else if (parent->balance_factor == 0) {
+				/* Case 3 */
+				parent->balance_factor = 1;
+				break;
+			} else
+				/* Case 4 */
+				parent->balance_factor = 0;
+		}
+
+		node = node->parent;
+	}
+}
+
+pboolean
+p_tree_avl_insert (PTreeBaseNode	**root_node,
+		   PCompareDataFunc	compare_func,
+		   ppointer		data,
+		   PDestroyFunc		key_destroy_func,
+		   PDestroyFunc		value_destroy_func,
+		   ppointer		key,
+		   ppointer		value)
+{
+	PTreeBaseNode	**cur_node;
+	PTreeBaseNode	*parent_node;
+	pint		cmp_result;
+
+	cur_node    = root_node;
+	parent_node = *root_node;
+
+	/* Find where to insert the node */
+	while (*cur_node != NULL) {
+		cmp_result = compare_func (key, (*cur_node)->key, data);
+
+		if (cmp_result < 0) {
+			parent_node = *cur_node;
+			cur_node    = &(*cur_node)->left;
+		} else if (cmp_result > 0) {
+			parent_node = *cur_node;
+			cur_node    = &(*cur_node)->right;
+		} else
+			break;
+	}
+
+	/* If we have existing one - replace a key-value pair */
+	if (*cur_node != NULL) {
+		if (key_destroy_func != NULL)
+			key_destroy_func ((*cur_node)->key);
+
+		if (value_destroy_func != NULL)
+			value_destroy_func ((*cur_node)->value);
+
+		(*cur_node)->key   = key;
+		(*cur_node)->value = value;
+
+		return FALSE;
+	}
+
+	if (P_UNLIKELY ((*cur_node = p_malloc0 (sizeof (PTreeAVLNode))) == NULL))
+		return FALSE;
+
+	(*cur_node)->key   = key;
+	(*cur_node)->value = value;
+
+	((PTreeAVLNode *) *cur_node)->balance_factor = 0;
+	((PTreeAVLNode *) *cur_node)->parent         = (PTreeAVLNode *) parent_node;
+
+	/* Balance the tree */
+	pp_tree_avl_balance_insert (((PTreeAVLNode *) *cur_node), root_node);
+
+	return TRUE;
+}
+
+pboolean
+p_tree_avl_remove (PTreeBaseNode	**root_node,
+		   PCompareDataFunc	compare_func,
+		   ppointer		data,
+		   PDestroyFunc		key_destroy_func,
+		   PDestroyFunc		value_destroy_func,
+		   pconstpointer	key)
+{
+	PTreeBaseNode	*cur_node;
+	PTreeBaseNode	*prev_node;
+	PTreeBaseNode	*child_node;
+	PTreeAVLNode	*child_parent;
+	pint		cmp_result;
+
+	cur_node = *root_node;
+
+	while (cur_node != NULL) {
+		cmp_result = compare_func (key, cur_node->key, data);
+
+		if (cmp_result < 0)
+			cur_node = cur_node->left;
+		else if (cmp_result > 0)
+			cur_node = cur_node->right;
+		else
+			break;
+	}
+
+	if (P_UNLIKELY (cur_node == NULL))
+		return FALSE;
+
+	if (cur_node->left != NULL && cur_node->right != NULL) {
+		prev_node = cur_node->left;
+
+		while (prev_node->right != NULL)
+			prev_node = prev_node->right;
+
+		cur_node->key   = prev_node->key;
+		cur_node->value = prev_node->value;
+
+		/* Mark node for removal */
+		cur_node = prev_node;
+	}
+
+	child_node = cur_node->left == NULL ? cur_node->right : cur_node->left;
+
+	if (child_node == NULL)
+		pp_tree_avl_balance_remove ((PTreeAVLNode *) cur_node, root_node);
+
+	/* Replace node with its child */
+	if (P_UNLIKELY (cur_node == *root_node)) {
+		*root_node   = child_node;
+		child_parent = NULL;
+	} else {
+		child_parent = ((PTreeAVLNode *) cur_node)->parent;
+
+		if (child_parent->base.left == cur_node)
+			child_parent->base.left = child_node;
+		else
+			child_parent->base.right = child_node;
+	}
+
+	if (child_node != NULL) {
+		((PTreeAVLNode *) child_node)->parent = child_parent;
+
+		/* Balance the tree */
+		pp_tree_avl_balance_remove ((PTreeAVLNode *) child_node, root_node);
+	}
+
+	/* Free unused node */
+	if (key_destroy_func != NULL)
+		key_destroy_func (cur_node->key);
+
+	if (value_destroy_func != NULL)
+		value_destroy_func (cur_node->value);
+
+	p_free (cur_node);
+
+	return TRUE;
+}
+
+void
+p_tree_avl_node_free (PTreeBaseNode *node)
+{
+	p_free (node);
+}
diff --git a/3rdparty/plibsys/src/ptree-avl.h b/3rdparty/plibsys/src/ptree-avl.h
new file mode 100644
index 0000000..dccba2a
--- /dev/null
+++ b/3rdparty/plibsys/src/ptree-avl.h
@@ -0,0 +1,58 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PTREEAVL_H
+#define PLIBSYS_HEADER_PTREEAVL_H
+
+#include "pmacros.h"
+#include "ptypes.h"
+#include "ptree-private.h"
+
+P_BEGIN_DECLS
+
+pboolean	p_tree_avl_insert	(PTreeBaseNode		**root_node,
+					 PCompareDataFunc	compare_func,
+					 ppointer		data,
+					 PDestroyFunc		key_destroy_func,
+					 PDestroyFunc		value_destroy_func,
+					 ppointer		key,
+					 ppointer		value);
+
+pboolean	p_tree_avl_remove	(PTreeBaseNode		**root_node,
+					 PCompareDataFunc	compare_func,
+					 ppointer		data,
+					 PDestroyFunc		key_destroy_func,
+					 PDestroyFunc		value_destroy_func,
+					 pconstpointer		key);
+
+void		p_tree_avl_node_free	(PTreeBaseNode	*node);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PTREEAVL_H */
diff --git a/3rdparty/plibsys/src/ptree-bst.c b/3rdparty/plibsys/src/ptree-bst.c
new file mode 100644
index 0000000..02fae28
--- /dev/null
+++ b/3rdparty/plibsys/src/ptree-bst.c
@@ -0,0 +1,140 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2015-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "ptree-bst.h"
+
+pboolean
+p_tree_bst_insert (PTreeBaseNode	**root_node,
+		   PCompareDataFunc	compare_func,
+		   ppointer		data,
+		   PDestroyFunc		key_destroy_func,
+		   PDestroyFunc		value_destroy_func,
+		   ppointer		key,
+		   ppointer		value)
+{
+	PTreeBaseNode	**cur_node;
+	pint		cmp_result;
+
+	cur_node = root_node;
+
+	while (*cur_node != NULL) {
+		cmp_result = compare_func (key, (*cur_node)->key, data);
+
+		if (cmp_result < 0)
+			cur_node = &(*cur_node)->left;
+		else if (cmp_result > 0)
+			cur_node = &(*cur_node)->right;
+		else
+			break;
+	}
+
+	if ((*cur_node) == NULL) {
+		if (P_UNLIKELY ((*cur_node = p_malloc0 (sizeof (PTreeBaseNode))) == NULL))
+			return FALSE;
+
+		(*cur_node)->key   = key;
+		(*cur_node)->value = value;
+
+		return TRUE;
+	} else {
+		if (key_destroy_func != NULL)
+			key_destroy_func ((*cur_node)->key);
+
+		if (value_destroy_func != NULL)
+			value_destroy_func ((*cur_node)->value);
+
+		(*cur_node)->key   = key;
+		(*cur_node)->value = value;
+
+		return FALSE;
+	}
+}
+
+pboolean
+p_tree_bst_remove (PTreeBaseNode	**root_node,
+		   PCompareDataFunc	compare_func,
+		   ppointer		data,
+		   PDestroyFunc		key_destroy_func,
+		   PDestroyFunc		value_destroy_func,
+		   pconstpointer	key)
+{
+	PTreeBaseNode	*cur_node;
+	PTreeBaseNode	*prev_node;
+	PTreeBaseNode	**node_pointer;
+	pint		cmp_result;
+
+	cur_node     = *root_node;
+	node_pointer = root_node;
+
+	while (cur_node != NULL) {
+		cmp_result = compare_func (key, cur_node->key, data);
+
+		if (cmp_result < 0) {
+			node_pointer = &cur_node->left;
+			cur_node     = cur_node->left;
+		} else if (cmp_result > 0) {
+			node_pointer = &cur_node->right;
+			cur_node     = cur_node->right;
+		} else
+			break;
+	}
+
+	if (P_UNLIKELY (cur_node == NULL))
+		return FALSE;
+
+	if (cur_node->left != NULL && cur_node->right != NULL) {
+		node_pointer = &cur_node->left;
+		prev_node    = cur_node->left;
+
+		while (prev_node->right != NULL) {
+			node_pointer = &prev_node->right;
+			prev_node    = prev_node->right;
+		}
+
+		cur_node->key   = prev_node->key;
+		cur_node->value = prev_node->value;
+
+		cur_node = prev_node;
+	}
+
+	*node_pointer = cur_node->left == NULL ? cur_node->right : cur_node->left;
+
+	if (key_destroy_func != NULL)
+		key_destroy_func (cur_node->key);
+
+	if (value_destroy_func != NULL)
+		value_destroy_func (cur_node->value);
+
+	p_free (cur_node);
+
+	return TRUE;
+}
+
+void
+p_tree_bst_node_free (PTreeBaseNode *node)
+{
+	p_free (node);
+}
diff --git a/3rdparty/plibsys/src/ptree-bst.h b/3rdparty/plibsys/src/ptree-bst.h
new file mode 100644
index 0000000..a0ac18d
--- /dev/null
+++ b/3rdparty/plibsys/src/ptree-bst.h
@@ -0,0 +1,58 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2015-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PTREEBST_H
+#define PLIBSYS_HEADER_PTREEBST_H
+
+#include "pmacros.h"
+#include "ptypes.h"
+#include "ptree-private.h"
+
+P_BEGIN_DECLS
+
+pboolean	p_tree_bst_insert	(PTreeBaseNode		**root_node,
+					 PCompareDataFunc	compare_func,
+					 ppointer		data,
+					 PDestroyFunc		key_destroy_func,
+					 PDestroyFunc		value_destroy_func,
+					 ppointer		key,
+					 ppointer		value);
+
+pboolean	p_tree_bst_remove	(PTreeBaseNode		**root_node,
+					 PCompareDataFunc	compare_func,
+					 ppointer		data,
+					 PDestroyFunc		key_destroy_func,
+					 PDestroyFunc		value_destroy_func,
+					 pconstpointer		key);
+
+void		p_tree_bst_node_free	(PTreeBaseNode	*node);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PTREEBST_H */
diff --git a/3rdparty/plibsys/src/ptree-private.h b/3rdparty/plibsys/src/ptree-private.h
new file mode 100644
index 0000000..5af1a67
--- /dev/null
+++ b/3rdparty/plibsys/src/ptree-private.h
@@ -0,0 +1,48 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PTREE_PRIVATE_H
+#define PLIBSYS_HEADER_PTREE_PRIVATE_H
+
+#include "pmacros.h"
+#include "ptypes.h"
+
+P_BEGIN_DECLS
+
+/** Base tree leaf structure. */
+typedef struct PTreeBaseNode_ {
+	struct PTreeBaseNode_	*left;	/**< Left child.	*/
+	struct PTreeBaseNode_	*right;	/**< Right child.	*/
+	ppointer		key;	/**< Node key.		*/
+	ppointer		value;	/**< Node value.	*/
+} PTreeBaseNode;
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PTREE_PRIVATE_H */
diff --git a/3rdparty/plibsys/src/ptree-rb.c b/3rdparty/plibsys/src/ptree-rb.c
new file mode 100644
index 0000000..53c7b09
--- /dev/null
+++ b/3rdparty/plibsys/src/ptree-rb.c
@@ -0,0 +1,484 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2015-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ * Illustrations have been taken from the Linux kernel rbtree.c
+ *
+ * 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 "pmem.h"
+#include "ptree-rb.h"
+
+typedef enum PTreeRBColor_ {
+	P_TREE_RB_COLOR_RED	= 0x01,
+	P_TREE_RB_COLOR_BLACK	= 0x02
+} PTreeRBColor;
+
+typedef struct PTreeRBNode_ {
+	struct PTreeBaseNode_	base;
+	struct PTreeRBNode_	*parent;
+	PTreeRBColor		color;
+} PTreeRBNode;
+
+static pboolean pp_tree_rb_is_black (PTreeRBNode *node);
+static pboolean pp_tree_rb_is_red (PTreeRBNode *node);
+static PTreeRBNode * pp_tree_rb_get_gparent (PTreeRBNode *node);
+static PTreeRBNode * pp_tree_rb_get_uncle (PTreeRBNode *node);
+static PTreeRBNode * pp_tree_rb_get_sibling (PTreeRBNode *node);
+static void pp_tree_rb_rotate_left (PTreeRBNode *node, PTreeBaseNode **root);
+static void pp_tree_rb_rotate_right (PTreeRBNode *node, PTreeBaseNode **root);
+static void pp_tree_rb_balance_insert (PTreeRBNode *node, PTreeBaseNode **root);
+static void pp_tree_rb_balance_remove (PTreeRBNode *node, PTreeBaseNode **root);
+
+static pboolean
+pp_tree_rb_is_black (PTreeRBNode *node)
+{
+	if (node == NULL)
+		return TRUE;
+
+	return ((node->color) & P_TREE_RB_COLOR_BLACK) > 0 ? TRUE : FALSE;
+}
+
+static pboolean
+pp_tree_rb_is_red (PTreeRBNode *node)
+{
+	return ((node->color) & P_TREE_RB_COLOR_RED) > 0 ? TRUE : FALSE;
+}
+
+static PTreeRBNode *
+pp_tree_rb_get_gparent (PTreeRBNode *node)
+{
+	return node->parent->parent;
+}
+
+static PTreeRBNode *
+pp_tree_rb_get_uncle (PTreeRBNode *node)
+{
+	PTreeRBNode *gparent = pp_tree_rb_get_gparent (node);
+
+	if ((PTreeRBNode *) gparent->base.left == node->parent)
+		return (PTreeRBNode *) gparent->base.right;
+	else
+		return (PTreeRBNode *) gparent->base.left;
+}
+
+static PTreeRBNode *
+pp_tree_rb_get_sibling (PTreeRBNode *node)
+{
+	if (node->parent->base.left == (PTreeBaseNode *) node)
+		return (PTreeRBNode *) node->parent->base.right;
+	else
+		return (PTreeRBNode *) node->parent->base.left;
+}
+
+static void
+pp_tree_rb_rotate_left (PTreeRBNode *node, PTreeBaseNode **root)
+{
+	PTreeBaseNode *tmp_node;
+
+	tmp_node = node->base.right;
+
+	if (P_LIKELY (node->parent != NULL)) {
+		if (node->parent->base.left == (PTreeBaseNode *) node)
+			node->parent->base.left = tmp_node;
+		else
+			node->parent->base.right = tmp_node;
+	}
+
+	node->base.right = tmp_node->left;
+
+	if (tmp_node->left != NULL)
+		((PTreeRBNode *) tmp_node->left)->parent = node;
+
+	tmp_node->left = (PTreeBaseNode *) node;
+	((PTreeRBNode *) tmp_node)->parent = node->parent;
+	node->parent = (PTreeRBNode *) tmp_node;
+
+	if (P_UNLIKELY (((PTreeRBNode *) tmp_node)->parent == NULL))
+		*root = tmp_node;
+}
+
+static void
+pp_tree_rb_rotate_right (PTreeRBNode *node, PTreeBaseNode **root)
+{
+	PTreeBaseNode *tmp_node;
+
+	tmp_node = node->base.left;
+
+	if (P_LIKELY (node->parent != NULL)) {
+		if (node->parent->base.left == (PTreeBaseNode *) node)
+			node->parent->base.left = tmp_node;
+		else
+			node->parent->base.right = tmp_node;
+	}
+
+	node->base.left = tmp_node->right;
+
+	if (tmp_node->right != NULL)
+		((PTreeRBNode *) tmp_node->right)->parent = node;
+
+	tmp_node->right = (PTreeBaseNode *) node;
+	((PTreeRBNode *) tmp_node)->parent = node->parent;
+	node->parent = (PTreeRBNode *) tmp_node;
+
+	if (P_UNLIKELY (((PTreeRBNode *) tmp_node)->parent == NULL))
+		*root = tmp_node;
+}
+
+static void
+pp_tree_rb_balance_insert (PTreeRBNode *node, PTreeBaseNode **root)
+{
+	PTreeRBNode *uncle;
+	PTreeRBNode *gparent;
+
+	while (TRUE) {
+		/* Case 1: We are at the root  */
+		if (P_UNLIKELY (node->parent == NULL)) {
+			node->color = P_TREE_RB_COLOR_BLACK;
+			break;
+		}
+
+		/* Case 2: We have a black parent */
+		if (pp_tree_rb_is_black (node->parent) == TRUE)
+			break;
+
+		uncle   = pp_tree_rb_get_uncle (node);
+		gparent = pp_tree_rb_get_gparent (node);
+
+		/* Case 3: Both parent and uncle are red, flip colors
+		 *
+		 *       G            g
+		 *      / \          / \
+		 *     p   u  -->   P   U
+		 *    /            /
+		 *   n            n
+		 */
+		if (uncle != NULL && pp_tree_rb_is_red (uncle) == TRUE) {
+			node->parent->color = P_TREE_RB_COLOR_BLACK;
+			uncle->color        = P_TREE_RB_COLOR_BLACK;
+			gparent->color      = P_TREE_RB_COLOR_RED;
+
+			/* Continue iteratively from gparent */
+			node = gparent;
+			continue;
+		}
+
+		if (node->parent == (PTreeRBNode *) gparent->base.left) {
+			if (node == (PTreeRBNode *) node->parent->base.right) {
+				/* Case 4a: Left rotate at parent
+				 *
+				 *      G             G
+				 *     / \           / \
+				 *    p   U  -->    n   U
+				 *     \           /
+				 *      n         p
+				 */
+				pp_tree_rb_rotate_left (node->parent, root);
+
+				node = (PTreeRBNode *) node->base.left;
+			}
+
+			gparent->color      = P_TREE_RB_COLOR_RED;
+			node->parent->color = P_TREE_RB_COLOR_BLACK;
+
+			/* Case 5a: Right rotate at gparent
+			 *
+			 *        G           P
+			 *       / \         / \
+			 *      p   U  -->  n   g
+			 *     /                 \
+			 *    n                   U
+			 */
+			pp_tree_rb_rotate_right (gparent, root);
+
+			break;
+		} else {
+			if (node == (PTreeRBNode *) node->parent->base.left) {
+				/* Case 4b: Right rotate at parent */
+				pp_tree_rb_rotate_right (node->parent, root);
+
+				node = (PTreeRBNode *) node->base.right;
+			}
+
+			gparent->color      = P_TREE_RB_COLOR_RED;
+			node->parent->color = P_TREE_RB_COLOR_BLACK;
+
+			/* Case 5b: Left rotate at gparent*/
+			pp_tree_rb_rotate_left (gparent, root);
+
+			break;
+		}
+	}
+}
+
+static void
+pp_tree_rb_balance_remove (PTreeRBNode *node, PTreeBaseNode **root)
+{
+	PTreeRBNode *sibling;
+
+	while (TRUE) {
+		/* Case 1: We are at the root */
+		if (P_UNLIKELY (node->parent == NULL))
+			break;
+
+		sibling = pp_tree_rb_get_sibling (node);
+
+		if (pp_tree_rb_is_red (sibling) == TRUE) {
+			/*
+			 * Case 2: Left (right) rotate at parent
+			 *
+			 *     P               S
+			 *    / \             / \
+			 *   N   s    -->    p   Sr
+			 *      / \         / \
+			 *     Sl  Sr      N   Sl
+			 */
+			node->parent->color = P_TREE_RB_COLOR_RED;
+			sibling->color      = P_TREE_RB_COLOR_BLACK;
+
+			if ((PTreeBaseNode *) node == node->parent->base.left)
+				pp_tree_rb_rotate_left (node->parent, root);
+			else
+				pp_tree_rb_rotate_right (node->parent, root);
+
+			sibling = pp_tree_rb_get_sibling (node);
+		}
+
+		/*
+		 * Case 3: Sibling (parent) color flip
+		 *
+		 *    (p)           (p)
+		 *    / \           / \
+		 *   N   S    -->  N   s
+		 *      / \           / \
+		 *     Sl  Sr        Sl  Sr
+		 */
+		if (pp_tree_rb_is_black ((PTreeRBNode *) sibling->base.left)  == TRUE &&
+		    pp_tree_rb_is_black ((PTreeRBNode *) sibling->base.right) == TRUE) {
+			sibling->color = P_TREE_RB_COLOR_RED;
+
+			if (pp_tree_rb_is_black (node->parent) == TRUE) {
+				node = node->parent;
+				continue;
+			} else {
+				node->parent->color = P_TREE_RB_COLOR_BLACK;
+				break;
+			}
+		}
+
+		/*
+		 * Case 4: Right (left) rotate at sibling
+		 *
+		 *   (p)           (p)
+		 *   / \           / \
+		 *  N   S    -->  N   Sl
+		 *     / \             \
+		 *    sl  Sr            s
+		 *                       \
+		 *                        Sr
+		 */
+		if ((PTreeBaseNode *) node == node->parent->base.left &&
+		    pp_tree_rb_is_black ((PTreeRBNode *) sibling->base.right) == TRUE) {
+			sibling->color = P_TREE_RB_COLOR_RED;
+			((PTreeRBNode *) sibling->base.left)->color = P_TREE_RB_COLOR_BLACK;
+
+			pp_tree_rb_rotate_right (sibling, root);
+
+			sibling = pp_tree_rb_get_sibling (node);
+		} else if ((PTreeBaseNode *) node == node->parent->base.right &&
+			   pp_tree_rb_is_black ((PTreeRBNode *) sibling->base.left) == TRUE) {
+			sibling->color = P_TREE_RB_COLOR_RED;
+			((PTreeRBNode *) sibling->base.right)->color = P_TREE_RB_COLOR_BLACK;
+
+			pp_tree_rb_rotate_left (sibling, root);
+
+			sibling = pp_tree_rb_get_sibling (node);
+		}
+
+		/*
+		 * Case 5: Left (right) rotate at parent and color flips
+		 *
+		 *      (p)             (s)
+		 *      / \             / \
+		 *     N   S     -->   P   Sr
+		 *        / \         / \
+		 *      (sl) sr      N  (sl)
+		 */
+		sibling->color      = node->parent->color;
+		node->parent->color = P_TREE_RB_COLOR_BLACK;
+
+		if ((PTreeBaseNode *) node == node->parent->base.left) {
+			((PTreeRBNode *) sibling->base.right)->color = P_TREE_RB_COLOR_BLACK;
+			pp_tree_rb_rotate_left (node->parent, root);
+		} else {
+			((PTreeRBNode *) sibling->base.left)->color = P_TREE_RB_COLOR_BLACK;
+			pp_tree_rb_rotate_right (node->parent, root);
+		}
+
+		break;
+	}
+}
+
+pboolean
+p_tree_rb_insert (PTreeBaseNode		**root_node,
+		  PCompareDataFunc	compare_func,
+		  ppointer		data,
+		  PDestroyFunc		key_destroy_func,
+		  PDestroyFunc		value_destroy_func,
+		  ppointer		key,
+		  ppointer		value)
+{
+	PTreeBaseNode	**cur_node;
+	PTreeBaseNode	*parent_node;
+	pint		cmp_result;
+
+	cur_node    = root_node;
+	parent_node = *root_node;
+
+	/* Find where to insert the node */
+	while (*cur_node != NULL) {
+		cmp_result = compare_func (key, (*cur_node)->key, data);
+
+		if (cmp_result < 0) {
+			parent_node = *cur_node;
+			cur_node    = &(*cur_node)->left;
+		} else if (cmp_result > 0) {
+			parent_node = *cur_node;
+			cur_node    = &(*cur_node)->right;
+		} else
+			break;
+	}
+
+	/* If we have existing one - replace a key-value pair */
+	if (*cur_node != NULL) {
+		if (key_destroy_func != NULL)
+			key_destroy_func ((*cur_node)->key);
+
+		if (value_destroy_func != NULL)
+			value_destroy_func ((*cur_node)->value);
+
+		(*cur_node)->key   = key;
+		(*cur_node)->value = value;
+
+		return FALSE;
+	}
+
+	if (P_UNLIKELY ((*cur_node = p_malloc0 (sizeof (PTreeRBNode))) == NULL))
+		return FALSE;
+
+	(*cur_node)->key   = key;
+	(*cur_node)->value = value;
+
+	((PTreeRBNode *) *cur_node)->color  = P_TREE_RB_COLOR_RED;
+	((PTreeRBNode *) *cur_node)->parent = (PTreeRBNode *) parent_node;
+
+	/* Balance the tree */
+	pp_tree_rb_balance_insert ((PTreeRBNode *) *cur_node, root_node);
+
+	return TRUE;
+}
+
+pboolean
+p_tree_rb_remove (PTreeBaseNode		**root_node,
+		  PCompareDataFunc	compare_func,
+		  ppointer		data,
+		  PDestroyFunc		key_destroy_func,
+		  PDestroyFunc		value_destroy_func,
+		  pconstpointer		key)
+{
+	PTreeBaseNode	*cur_node;
+	PTreeBaseNode	*prev_node;
+	PTreeBaseNode	*child_node;
+	PTreeRBNode	*child_parent;
+	pint		cmp_result;
+
+	cur_node = *root_node;
+
+	while (cur_node != NULL) {
+		cmp_result = compare_func (key, cur_node->key, data);
+
+		if (cmp_result < 0)
+			cur_node = cur_node->left;
+		else if (cmp_result > 0)
+			cur_node = cur_node->right;
+		else
+			break;
+	}
+
+	if (P_UNLIKELY (cur_node == NULL))
+		return FALSE;
+
+	if (cur_node->left != NULL && cur_node->right != NULL) {
+		prev_node = cur_node->left;
+
+		while (prev_node->right != NULL)
+			prev_node = prev_node->right;
+
+		cur_node->key   = prev_node->key;
+		cur_node->value = prev_node->value;
+
+		/* Mark node for removal */
+		cur_node = prev_node;
+	}
+
+	child_node = cur_node->left == NULL ? cur_node->right : cur_node->left;
+
+	if (child_node == NULL && pp_tree_rb_is_black ((PTreeRBNode *) cur_node) == TRUE)
+		pp_tree_rb_balance_remove ((PTreeRBNode *) cur_node, root_node);
+
+	/* Replace node with its child */
+	if (cur_node == *root_node) {
+		*root_node   = child_node;
+		child_parent = NULL;
+	} else {
+		child_parent = ((PTreeRBNode *) cur_node)->parent;
+
+		if (child_parent->base.left == cur_node)
+			child_parent->base.left = child_node;
+		else
+			child_parent->base.right = child_node;
+	}
+
+	if (child_node != NULL) {
+		((PTreeRBNode *) child_node)->parent = child_parent;
+
+		/* Check if we need to repaint the node */
+		if (pp_tree_rb_is_black ((PTreeRBNode *) cur_node) == TRUE)
+				((PTreeRBNode *) child_node)->color = P_TREE_RB_COLOR_BLACK;
+	}
+
+	/* Free unused node */
+	if (key_destroy_func != NULL)
+		key_destroy_func (cur_node->key);
+
+	if (value_destroy_func != NULL)
+		value_destroy_func (cur_node->value);
+
+	p_free (cur_node);
+
+	return TRUE;
+}
+
+void
+p_tree_rb_node_free (PTreeBaseNode *node)
+{
+	p_free (node);
+}
diff --git a/3rdparty/plibsys/src/ptree-rb.h b/3rdparty/plibsys/src/ptree-rb.h
new file mode 100644
index 0000000..7cf20be
--- /dev/null
+++ b/3rdparty/plibsys/src/ptree-rb.h
@@ -0,0 +1,58 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2015-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PTREERB_H
+#define PLIBSYS_HEADER_PTREERB_H
+
+#include "pmacros.h"
+#include "ptypes.h"
+#include "ptree-private.h"
+
+P_BEGIN_DECLS
+
+pboolean	p_tree_rb_insert	(PTreeBaseNode		**root_node,
+					 PCompareDataFunc	compare_func,
+					 ppointer		data,
+					 PDestroyFunc		key_destroy_func,
+					 PDestroyFunc		value_destroy_func,
+					 ppointer		key,
+					 ppointer		value);
+
+pboolean	p_tree_rb_remove	(PTreeBaseNode		**root_node,
+					 PCompareDataFunc	compare_func,
+					 ppointer		data,
+					 PDestroyFunc		key_destroy_func,
+					 PDestroyFunc		value_destroy_func,
+					 pconstpointer		key);
+
+void		p_tree_rb_node_free	(PTreeBaseNode	*node);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PTREERB_H */
diff --git a/3rdparty/plibsys/src/ptree.c b/3rdparty/plibsys/src/ptree.c
new file mode 100644
index 0000000..d61aee7
--- /dev/null
+++ b/3rdparty/plibsys/src/ptree.c
@@ -0,0 +1,315 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2015-2017 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "ptree.h"
+#include "ptree-avl.h"
+#include "ptree-bst.h"
+#include "ptree-rb.h"
+
+typedef pboolean	(*PTreeInsertNode)	(PTreeBaseNode		**root_node,
+						 PCompareDataFunc	compare_func,
+						 ppointer		data,
+						 PDestroyFunc		key_destroy_func,
+						 PDestroyFunc		value_destroy_func,
+						 ppointer		key,
+						 ppointer		value);
+
+typedef pboolean	(*PTreeRemoveNode)	(PTreeBaseNode		**root_node,
+						 PCompareDataFunc	compare_func,
+						 ppointer		data,
+						 PDestroyFunc		key_destroy_func,
+						 PDestroyFunc		value_destroy_func,
+						 pconstpointer		key);
+
+typedef void		(*PTreeFreeNode)	(PTreeBaseNode	*node);
+
+struct PTree_ {
+	PTreeBaseNode		*root;
+	PTreeInsertNode		insert_node_func;
+	PTreeRemoveNode		remove_node_func;
+	PTreeFreeNode		free_node_func;
+	PDestroyFunc		key_destroy_func;
+	PDestroyFunc		value_destroy_func;
+	PCompareDataFunc	compare_func;
+	ppointer		data;
+	PTreeType		type;
+	pint			nnodes;
+};
+
+P_LIB_API PTree *
+p_tree_new (PTreeType		type,
+	    PCompareFunc	func)
+{
+	return p_tree_new_full (type, (PCompareDataFunc) func, NULL, NULL, NULL);
+}
+
+P_LIB_API PTree *
+p_tree_new_with_data (PTreeType		type,
+		      PCompareDataFunc	func,
+		      ppointer		data)
+{
+	return p_tree_new_full (type, func, data, NULL, NULL);
+}
+
+P_LIB_API PTree *
+p_tree_new_full (PTreeType		type,
+		 PCompareDataFunc	func,
+		 ppointer		data,
+		 PDestroyFunc		key_destroy,
+		 PDestroyFunc		value_destroy)
+{
+	PTree *ret;
+
+	if (P_UNLIKELY (!(type >= P_TREE_TYPE_BINARY && type <= P_TREE_TYPE_AVL)))
+		return NULL;
+
+	if (P_UNLIKELY (func == NULL))
+		return NULL;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PTree))) == NULL)) {
+		P_ERROR ("PTree::p_tree_new_full: failed to allocate memory");
+		return NULL;
+	}
+
+	ret->type               = type;
+	ret->compare_func       = func;
+	ret->data               = data;
+	ret->key_destroy_func   = key_destroy;
+	ret->value_destroy_func	= value_destroy;
+
+	switch (type) {
+	case P_TREE_TYPE_BINARY:
+		ret->insert_node_func = p_tree_bst_insert;
+		ret->remove_node_func = p_tree_bst_remove;
+		ret->free_node_func   = p_tree_bst_node_free;
+		break;
+	case P_TREE_TYPE_RB:
+		ret->insert_node_func = p_tree_rb_insert;
+		ret->remove_node_func = p_tree_rb_remove;
+		ret->free_node_func   = p_tree_rb_node_free;
+		break;
+	case P_TREE_TYPE_AVL:
+		ret->insert_node_func = p_tree_avl_insert;
+		ret->remove_node_func = p_tree_avl_remove;
+		ret->free_node_func   = p_tree_avl_node_free;
+		break;
+	}
+
+	return ret;
+}
+
+P_LIB_API void
+p_tree_insert (PTree	*tree,
+	       ppointer	key,
+	       ppointer	value)
+{
+	pboolean result;
+
+	if (P_UNLIKELY (tree == NULL))
+		return;
+
+	result = tree->insert_node_func (&tree->root,
+					 tree->compare_func,
+					 tree->data,
+					 tree->key_destroy_func,
+					 tree->value_destroy_func,
+					 key,
+					 value);
+
+	if (result == TRUE)
+		++tree->nnodes;
+}
+
+P_LIB_API pboolean
+p_tree_remove (PTree		*tree,
+	       pconstpointer	key)
+{
+	pboolean result;
+
+	if (P_UNLIKELY (tree == NULL || tree->root == NULL))
+		return FALSE;
+
+	result = tree->remove_node_func (&tree->root,
+					 tree->compare_func,
+					 tree->data,
+					 tree->key_destroy_func,
+					 tree->value_destroy_func,
+					 key);
+	if (result == TRUE)
+		--tree->nnodes;
+
+	return result;
+}
+
+P_LIB_API ppointer
+p_tree_lookup (PTree		*tree,
+	       pconstpointer	key)
+{
+	PTreeBaseNode	*cur_node;
+	pint		cmp_result;
+
+	if (P_UNLIKELY (tree == NULL))
+		return NULL;
+
+	cur_node = tree->root;
+
+	while (cur_node != NULL) {
+		cmp_result = tree->compare_func (key, cur_node->key, tree->data);
+
+		if (cmp_result < 0)
+			cur_node = cur_node->left;
+		else if (cmp_result > 0)
+			cur_node = cur_node->right;
+		else
+			return cur_node->value;
+	}
+
+	return NULL;
+}
+
+P_LIB_API void
+p_tree_foreach (PTree		*tree,
+		PTraverseFunc	traverse_func,
+		ppointer	user_data)
+{
+	PTreeBaseNode	*cur_node;
+	PTreeBaseNode	*prev_node;
+	pint		mod_counter;
+	pboolean	need_stop;
+
+	if (P_UNLIKELY (tree == NULL || traverse_func == NULL))
+		return;
+
+	if (P_UNLIKELY (tree->root == NULL))
+		return;
+
+	cur_node    = tree->root;
+	mod_counter = 0;
+	need_stop   = FALSE;
+
+	while (cur_node != NULL) {
+		if (cur_node->left == NULL) {
+			if (need_stop == FALSE)
+				need_stop = traverse_func (cur_node->key,
+							   cur_node->value,
+							   user_data);
+
+			cur_node = cur_node->right;
+		} else {
+			prev_node = cur_node->left;
+
+			while (prev_node->right != NULL && prev_node->right != cur_node)
+				prev_node = prev_node->right;
+
+			if (prev_node->right == NULL) {
+				prev_node->right = cur_node;
+				cur_node         = cur_node->left;
+
+				++mod_counter;
+			} else {
+				if (need_stop == FALSE)
+					need_stop = traverse_func (cur_node->key,
+								   cur_node->value,
+								   user_data);
+
+				cur_node         = cur_node->right;
+				prev_node->right = NULL;
+
+				--mod_counter;
+
+				if (need_stop == TRUE && mod_counter == 0)
+					return;
+			}
+		}
+	}
+}
+
+P_LIB_API void
+p_tree_clear (PTree *tree)
+{
+	PTreeBaseNode	*cur_node;
+	PTreeBaseNode	*prev_node;
+	PTreeBaseNode	*next_node;
+
+	if (P_UNLIKELY (tree == NULL || tree->root == NULL))
+		return;
+
+	cur_node = tree->root;
+
+	while (cur_node != NULL) {
+		if (cur_node->left == NULL) {
+			next_node = cur_node->right;
+
+			if (tree->key_destroy_func != NULL)
+				tree->key_destroy_func (cur_node->key);
+
+			if (tree->value_destroy_func != NULL)
+				tree->value_destroy_func (cur_node->value);
+
+			tree->free_node_func (cur_node);
+			--tree->nnodes;
+
+			cur_node = next_node;
+		} else {
+			prev_node = cur_node->left;
+
+			while (prev_node->right != NULL)
+				prev_node = prev_node->right;
+
+			prev_node->right = cur_node;
+			next_node        = cur_node->left;
+			cur_node->left   = NULL;
+			cur_node         = next_node;
+		}
+	}
+
+	tree->root = NULL;
+}
+
+P_LIB_API PTreeType
+p_tree_get_type (const PTree *tree)
+{
+	if (P_UNLIKELY (tree == NULL))
+		return (PTreeType) -1;
+
+	return tree->type;
+}
+
+P_LIB_API pint
+p_tree_get_nnodes (const PTree *tree)
+{
+	if (P_UNLIKELY (tree == NULL))
+		return 0;
+
+	return tree->nnodes;
+}
+
+P_LIB_API void
+p_tree_free (PTree *tree)
+{
+	p_tree_clear (tree);
+	p_free (tree);
+}
diff --git a/3rdparty/plibsys/src/ptree.h b/3rdparty/plibsys/src/ptree.h
new file mode 100644
index 0000000..d43e7ae
--- /dev/null
+++ b/3rdparty/plibsys/src/ptree.h
@@ -0,0 +1,230 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2015-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file ptree.h
+ * @brief Binary tree data structure
+ * @author Alexander Saprykin
+ *
+ * #PTree represents a binary search tree structure for faster lookup than
+ * a plain array or a list. It has average O(logN) time complexity to search
+ * a key-value pair, and O(N) in the worst case (when a tree is degenerated into
+ * the list).
+ *
+ * Currently #PTree supports the following tree types:
+ * - unbalanced binary search tree;
+ * - red-black self-balancing tree;
+ * - AVL self-balancing tree.
+ *
+ * Use p_tree_new(), or its detailed variations like p_tree_new_with_data() and
+ * p_tree_new_full() to create a tree structure. Take attention that a caller
+ * owns the key and the value data passed when inserting new nodes, so you
+ * should manually free the memory after the tree usage. Or you can provide
+ * destroy notification functions for the keys and the values separately.
+ *
+ * New key-value pairs can be inserted with p_tree_insert() and removed with
+ * p_tree_remove().
+ *
+ * Use p_tree_lookup() to find the value by a given key. You can also traverse
+ * the tree in-order with p_tree_foreach().
+ *
+ * Release memory with p_tree_free() or clear a tree with p_tree_clear(). Keys
+ * and values would be destroyed only if the corresponding notification
+ * functions were provided.
+ *
+ * Note: all operations with the tree are non-recursive, only iterative calls
+ * are used.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PTREE_H
+#define PLIBSYS_HEADER_PTREE_H
+
+#include <pmacros.h>
+#include <ptypes.h>
+
+P_BEGIN_DECLS
+
+/** Tree opaque data structure. */
+typedef struct PTree_ PTree;
+
+/** Internal data organization algorithm for #PTree. */
+typedef enum PTreeType_ {
+	P_TREE_TYPE_BINARY	= 0,	/**< Unbalanced binary tree.		*/
+	P_TREE_TYPE_RB		= 1,	/**< Red-black self-balancing tree.	*/
+	P_TREE_TYPE_AVL		= 2	/**< AVL self-balancing tree.		*/
+} PTreeType;
+
+/**
+ * @brief Initializes new #PTree.
+ * @param type Tree algorithm type to use, can't be changed later.
+ * @param func Key compare function.
+ * @return Newly initialized #PTree object in case of success, NULL otherwise.
+ * @since 0.0.1
+ *
+ * The caller takes ownership of all the keys and the values passed to the tree.
+ */
+P_LIB_API PTree *	p_tree_new		(PTreeType		type,
+						 PCompareFunc		func);
+
+/**
+ * @brief Initializes new #PTree with additional data.
+ * @param type Tree algorithm type to use, can't be changed later.
+ * @param func Key compare function.
+ * @param data Data to be passed to @a func along with the keys.
+ * @return Newly initialized #PTree object in case of success, NULL otherwise.
+ * @since 0.0.1
+ *
+ * The caller takes ownership of all the keys and the values passed to the tree.
+ */
+P_LIB_API PTree *	p_tree_new_with_data	(PTreeType		type,
+						 PCompareDataFunc	func,
+						 ppointer		data);
+
+/**
+ * @brief Initializes new #PTree with additional data and memory management.
+ * @param type Tree algorithm type to use, can't be changed later.
+ * @param func Key compare function.
+ * @param data Data to be passed to @a func along with the keys.
+ * @param key_destroy Function to call on every key before the node destruction,
+ * maybe NULL.
+ * @param value_destroy Function to call on every value before the node
+ * destruction, maybe NULL.
+ * @return Newly initialized #PTree object in case of success, NULL otherwise.
+ * @since 0.0.1
+ *
+ * Upon every node destruction the corresponding key and value functions would
+ * be called.
+ */
+P_LIB_API PTree *	p_tree_new_full		(PTreeType		type,
+						 PCompareDataFunc	func,
+						 ppointer		data,
+						 PDestroyFunc		key_destroy,
+						 PDestroyFunc		value_destroy);
+
+/**
+ * @brief Inserts a new key-value pair into a tree.
+ * @param tree #PTree to insert a node in.
+ * @param key Key to insert.
+ * @param value Value corresponding to the given @a key.
+ * @since 0.0.1
+ *
+ * If the @a key already exists in the tree then it will be replaced with the
+ * new one. If a key destroy function was provided it would be called on the old
+ * key. If a value destroy function was provided it would be called on the old
+ * value.
+ */
+P_LIB_API void		p_tree_insert		(PTree			*tree,
+						 ppointer		key,
+						 ppointer		value);
+
+/**
+ * @brief Removes a key from a tree.
+ * @param tree #PTree to remove a key from.
+ * @param key A key to lookup.
+ * @return TRUE if the key was removed, FALSE if the key was not found.
+ * @since 0.0.1
+ *
+ * If a key destroy function was provided it would be called on the key. If a
+ * value destroy function was provided it would be called on the old value.
+ */
+P_LIB_API pboolean	p_tree_remove		(PTree			*tree,
+						 pconstpointer		key);
+
+/**
+ * @brief Lookups a value by a given key.
+ * @param tree #PTree to lookup in.
+ * @param key Key to lookup.
+ * @return Value for the given @a key in case of success, NULL otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API ppointer	p_tree_lookup		(PTree			*tree,
+						 pconstpointer		key);
+
+/**
+ * @brief Iterates in-order through the tree nodes.
+ * @param tree A tree to traverse.
+ * @param traverse_func Function for traversing.
+ * @param user_data Additional (maybe NULL) user-provided data for the
+ * @a traverse_func.
+ * @since 0.0.1
+ * @note Morris (non-recursive, non-stack) traversing algorithm is being used.
+ *
+ * The tree should not be modified while traversing. The internal tree structure
+ * can be modified along the traversing process, so keep it in mind for
+ * concurrent access.
+ */
+P_LIB_API void		p_tree_foreach		(PTree			*tree,
+						 PTraverseFunc		traverse_func,
+						 ppointer		user_data);
+
+/**
+ * @brief Clears a tree.
+ * @param tree #PTree to clear.
+ * @since 0.0.1
+ * @note Modified Morris (non-recursive, non-stack) traversing algorithm is
+ * being used.
+ *
+ * All the keys will be deleted. Key and value destroy functions would be called
+ * on every node if any of them was provided.
+ */
+P_LIB_API void		p_tree_clear		(PTree			*tree);
+
+/**
+ * @brief Gets a tree algorithm type.
+ * @param tree #PTree object to get the type for.
+ * @return Tree internal organization algorithm used for a given object.
+ * @since 0.0.1
+ */
+P_LIB_API PTreeType	p_tree_get_type		(const PTree		*tree);
+
+/**
+ * @brief Gets node count.
+ * @param tree #PTree to get node count for.
+ * @return Node count.
+ * @since 0.0.1
+ *
+ * If the tree is empty or an invalid pointer is given it returns 0.
+ */
+P_LIB_API pint		p_tree_get_nnodes	(const PTree		*tree);
+
+/**
+ * @brief Frees a previously initialized tree object.
+ * @param tree #PTree object to free.
+ * @since 0.0.1
+ * @note Modified Morris (non-recursive, non-stack) traversing algorithm is
+ * being used.
+ *
+ * All the keys will be deleted. Key and value destroy functions would be called
+ * on every node if any of them was provided.
+ */
+P_LIB_API void		p_tree_free		(PTree			*tree);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PTREE_H */
diff --git a/3rdparty/plibsys/src/ptypes.h b/3rdparty/plibsys/src/ptypes.h
new file mode 100644
index 0000000..46f2621
--- /dev/null
+++ b/3rdparty/plibsys/src/ptypes.h
@@ -0,0 +1,1122 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file ptypes.h
+ * @brief Types definitions
+ * @author Alexander Saprykin
+ *
+ * Every operating system in pair with a compiler has its own set of data types.
+ * Here you can find unified platform independent data types which guarantee the
+ * same bit-size on every supported platform: #pint8, #pint16, #pint32, #pint64
+ * and their unsigned variants. Also other types are defined for convinience:
+ * #ppointer, #pboolean, #pint, #plong, #pdouble and more.
+ *
+ * Along with the types, length and format modifiers are defined. They can be
+ * used to print and scan data from/to a variable.
+ *
+ * Sometimes it is useful to use an integer variable as a pointer, i.e. to
+ * prevent memory allocation when using hash tables or trees. Use special macros
+ * for that case: #PINT_TO_POINTER, #PPOINTER_TO_INT and their variants. Note
+ * that it will not work with 64-bit data types.
+ *
+ * To check data type limits use P_MIN* and P_MAX* macros.
+ *
+ * If you need to check system endianness compare the P_BYTE_ORDER definition
+ * with the #P_LITTLE_ENDIAN or #P_BIG_ENDIAN macro.
+ *
+ * To convert between the little and big endian byte orders use the Px_TO_LE,
+ * Px_TO_BE, Px_FROM_LE and Px_FROM_BE macros. Macros for the network<->host
+ * byte order conversion are also provided: #p_ntohl, #p_ntohs, #p_ntohs and
+ * #p_ntohl. All the described above macros depend on the target system
+ * endianness. Use PUINTx_SWAP_BYTES to manually swap data types independently
+ * from the endianness.
+ *
+ * You can also find some of the function definitions used within the library.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PTYPES_H
+#define PLIBSYS_HEADER_PTYPES_H
+
+#include <plibsysconfig.h>
+#include <pmacros.h>
+
+P_BEGIN_DECLS
+
+/** Type for signed 8 bit.	*/
+typedef signed char		pint8;
+/** Type for unsigned 8 bit.	*/
+typedef unsigned char		puint8;
+/** Type for signed 16 bit.	*/
+typedef signed short		pint16;
+/** Type for unsigned 16 bit.	*/
+typedef unsigned short		puint16;
+/** Type for signed 32 bit.	*/
+typedef signed int		pint32;
+/** Type for unsigned 32 bit.	*/
+typedef unsigned int		puint32;
+
+/**
+  * @var pint64
+  * @brief Type for signed 64 bit.
+  */
+
+/**
+  * @var puint64
+  * @brief Type for unsigned 64 bit.
+  */
+
+#if defined (P_OS_WIN) && (defined (P_CC_MSVC) || defined (P_CC_BORLAND))
+  typedef signed __int64	pint64;
+  typedef unsigned __int64	puint64;
+#else
+#  if PLIBSYS_SIZEOF_LONG == 8
+     typedef signed long	pint64;
+     typedef unsigned long	puint64;
+#  else
+     typedef signed long long	pint64;
+     typedef unsigned long long	puint64;
+#  endif
+#endif
+
+/** Type for a pointer.		*/
+typedef void *		ppointer;
+/** Type for a const pointer.	*/
+typedef const void *	pconstpointer;
+
+/** Type for a bool.	*/
+typedef signed int	pboolean;
+/** Type for a char.	*/
+typedef char		pchar;
+/** Type for a short.	*/
+typedef short		pshort;
+/** Type for an int.	*/
+typedef int		pint;
+/** Type for a long.	*/
+typedef long		plong;
+
+/** Type for an unsigned char.	*/
+typedef unsigned char	puchar;
+/** Type for an unsigned short.	*/
+typedef unsigned short	pushort;
+/** Type for an unsigned int.	*/
+typedef unsigned int	puint;
+/** Type for an unsigned long.	*/
+typedef unsigned long	pulong;
+
+/** Type for a float.			*/
+typedef float		pfloat;
+/** Type for a double precision float.	*/
+typedef double		pdouble;
+
+/**
+  * @var pssize
+  * @brief Type for a platform independent signed size_t.
+  */
+
+/**
+  * @var psize
+  * @brief Type for a platform independent size_t.
+  */
+
+/**
+  * @def PSIZE_MODIFIER
+  * @brief Platform dependent length modifier for conversion specifiers of
+  * #psize or #pssize type for printing and scanning values. It is a string
+  * literal, but doesn't include the percent sign so you can add precision and
+  * length modifiers and append a conversion specifier.
+  * @code
+  * psize size_val = 256;
+  * printf ("%#" PSIZE_MODIFIER "x", size_val);
+  * @endcode
+  */
+
+/**
+  * @def PSSIZE_FORMAT
+  * @brief Platform dependent conversion specifier of #pssize type for printing
+  * and scanning values.
+  * @code
+  * pssize size_val = 100;
+  * printf ("%" PSSIZE_FORMAT, size_val);
+  * @endcode
+  */
+
+/**
+  * @def PSIZE_FORMAT
+  * @brief Platform dependent conversion specifier of #psize type for printing
+  * and scanning values.
+  */
+
+/**
+  * @def P_MAXSIZE
+  * @brief Maximum value of a #psize type.
+  */
+
+/**
+  * @def P_MINSSIZE
+  * @brief Minimum value of a #pssize type.
+  */
+
+/**
+  * @def P_MAXSSIZE
+  * @brief Maximum value of a #pssize type.
+  */
+
+#if PLIBSYS_SIZEOF_SIZE_T == 8
+#  if defined (P_OS_WIN) && (defined (P_CC_MSVC) || defined (P_CC_BORLAND))
+     typedef signed __int64	pssize;
+     typedef unsigned __int64	psize;
+     #define PSIZE_MODIFIER	"I64"
+     #define PSSIZE_FORMAT	"I64d"
+     #define PSIZE_FORMAT	"I64u"
+     #define P_MAXSIZE		P_MAXUINT64
+     #define P_MINSSIZE		P_MININT64
+     #define P_MAXSSIZE		P_MAXINT64
+#  else
+#    if PLIBSYS_SIZEOF_LONG == 8
+       typedef long			pssize;
+       typedef unsigned long		psize;
+       #define PSIZE_MODIFIER		"l"
+       #define PSSIZE_FORMAT		"li"
+       #define PSIZE_FORMAT		"lu"
+       #define P_MAXSIZE		P_MAXULONG
+       #define P_MINSSIZE		P_MINLONG
+       #define P_MAXSSIZE		P_MAXLONG
+#    else
+       typedef long long		pssize;
+       typedef unsigned long long	psize;
+       #define PSIZE_MODIFIER		"ll"
+       #define PSSIZE_FORMAT		"lli"
+       #define PSIZE_FORMAT		"llu"
+       #define P_MAXSIZE		P_MAXUINT64
+       #define P_MINSSIZE		P_MININT64
+       #define P_MAXSSIZE		P_MAXINT64
+#    endif
+#  endif
+#else
+  typedef signed int		pssize;
+  typedef unsigned int		psize;
+  #define PSIZE_MODIFIER	""
+  #define PSSIZE_FORMAT		"i"
+  #define PSIZE_FORMAT		"u"
+  #define P_MAXSIZE		P_MAXUINT
+  #define P_MINSSIZE		P_MININT
+  #define P_MAXSSIZE		P_MAXINT
+#endif
+
+/**
+  * @var pintptr
+  * @brief Type for a platform independent signed pointer represented by an
+  * integer.
+  */
+
+/**
+  * @var puintptr
+  * @brief Type for a platform independent unsigned pointer represented by an
+  * integer.
+  */
+
+/**
+  * @def PINTPTR_MODIFIER
+  * @brief Platform dependent length modifier for conversion specifiers of
+  * #pintptr or #puintptr type for printing and scanning values. It is a string
+  * literal, but doesn't include the percent sign so you can add precision and
+  * length modifiers and append a conversion specifier.
+  */
+
+/**
+  * @def PINTPTR_FORMAT
+  * @brief Platform dependent conversion specifier of #pintptr type for printing
+  * and scanning values.
+  */
+
+/**
+  * @def PUINTPTR_FORMAT
+  * @brief Platform dependent conversion specifier of #puintptr type for
+  * printing and scanning values.
+  */
+
+#if PLIBSYS_SIZEOF_VOID_P == 8
+#  if defined (P_OS_WIN) && (defined (P_CC_MSVC) || defined (P_CC_BORLAND))
+     typedef signed __int64	pintptr;
+     typedef unsigned __int64	puintptr;
+     #define PINTPTR_MODIFIER	"I64"
+     #define PINTPTR_FORMAT	"I64i"
+     #define PUINTPTR_FORMAT	"I64u"
+#  else
+#    if PLIBSYS_SIZEOF_LONG == 8
+       typedef long			pintptr;
+       typedef unsigned long		puintptr;
+       #define PINTPTR_MODIFIER		"l"
+       #define PINTPTR_FORMAT		"li"
+       #define PUINTPTR_FORMAT		"lu"
+#    else
+       typedef long long		pintptr;
+       typedef unsigned long long	puintptr;
+       #define PINTPTR_MODIFIER		"ll"
+       #define PINTPTR_FORMAT		"lli"
+       #define PUINTPTR_FORMAT		"llu"
+#    endif
+#  endif
+#else
+  typedef signed int		pintptr;
+  typedef unsigned int		puintptr;
+  #define PINTPTR_MODIFIER	""
+  #define PINTPTR_FORMAT	"i"
+  #define PUINTPTR_FORMAT	"u"
+#endif
+
+/** Platform independent offset_t definition. */
+typedef pint64 poffset;
+
+#if PLIBSYS_SIZEOF_VOID_P == 8
+#  define P_INT_TO_POINTER(i)	((void *)	(long long) (i))
+#  define P_POINTER_TO_INT(p)	((int)		(long long) (p))
+#  define PPOINTER_TO_INT(p)	((pint)		((pint64) (p)))
+#  define PPOINTER_TO_UINT(p)	((puint)	((puint64) (p)))
+#  define PINT_TO_POINTER(i)	((ppointer)	(pint64) (i))
+#  define PUINT_TO_POINTER(u)	((ppointer)	(puint64) (u))
+#else
+#  define P_INT_TO_POINTER(i)	((void *)	(long) (i))
+#  define P_POINTER_TO_INT(p)	((int)		(long) (p))
+#  define PPOINTER_TO_INT(p)	((pint)		((plong) (p)))
+#  define PPOINTER_TO_UINT(p)	((puint)	((pulong) (p)))
+#  define PINT_TO_POINTER(i)	((ppointer)	(plong) (i))
+#  define PUINT_TO_POINTER(u)	((ppointer)	(pulong) (u))
+#endif
+
+/**
+ * @def P_INT_TO_POINTER
+ * @brief Casts an int to a pointer.
+ * @param i Variable to cast.
+ * @return Casted variable.
+ * @since 0.0.1
+ */
+
+/**
+ * @def P_POINTER_TO_INT
+ * @brief Casts a pointer to an int.
+ * @param p Pointer to cast.
+ * @return Casted pointer.
+ * @since 0.0.1
+ */
+
+/**
+ * @def PPOINTER_TO_INT
+ * @brief Casts a #ppointer to a #pint value.
+ * @param p #ppointer to cast.
+ * @return Casted #ppointer.
+ * @since 0.0.1
+ */
+
+ /**
+  * @def PPOINTER_TO_UINT
+  * @brief Casts a #ppointer to a #pint value.
+  * @param p #ppointer to cast.
+  * @return Casted #ppointer.
+  * @since 0.0.1
+  */
+
+ /**
+  * @def PINT_TO_POINTER
+  * @brief Casts a #pint value to a #ppointer.
+  * @param i #pint to cast.
+  * @return Casted #pint.
+  * @since 0.0.1
+  */
+
+ /**
+  * @def PUINT_TO_POINTER
+  * @brief Casts a #puint value to a #ppointer.
+  * @param u #puint to cast.
+  * @return Casted #puint.
+  * @since 0.0.1
+  */
+
+/** Casts a #psize value to a #ppointer.	*/
+#define PSIZE_TO_POINTER(i)	((ppointer)  ((psize) (i)))
+/** Casts a #ppointer to a #psize value.	*/
+#define PPOINTER_TO_PSIZE(p)	((psize)  (p))
+
+/** Min value for a 8-bit int.			*/
+#define P_MININT8	((pint8)  0x80)
+/** Max value for a 8-bit int.			*/
+#define P_MAXINT8	((pint8)  0x7F)
+/** Max value for a 8-bit unsigned int.		*/
+#define P_MAXUINT8	((puint8) 0xFF)
+
+/** Min value for a 16-bit int.			*/
+#define P_MININT16	((pint16)  0x8000)
+/** Max value for a 16-bit int.			*/
+#define P_MAXINT16	((pint16)  0x7FFF)
+/** Max value for a 16-bit unsigned int.	*/
+#define P_MAXUINT16	((puint16) 0xFFFF)
+
+/** Min value for a 32-bit int.			*/
+#define P_MININT32	((pint32)  0x80000000)
+/** Max value for a 32-bit int.			*/
+#define P_MAXINT32	((pint32)  0x7FFFFFFF)
+/** Max value for a 32-bit unsigned int.	*/
+#define P_MAXUINT32	((puint32) 0xFFFFFFFF)
+
+/** Min value for a 64-bit int.			*/
+#define P_MININT64	((pint64)  0x8000000000000000LL)
+/** Max value for a 64-bit int.			*/
+#define P_MAXINT64	((pint64)  0x7FFFFFFFFFFFFFFFLL)
+/** Max value for a 64-bit unsigned int.	*/
+#define P_MAXUINT64	((puint64) 0xFFFFFFFFFFFFFFFFULL)
+
+/**
+ * @def PINT16_MODIFIER
+ * @brief Platform dependent length modifier for conversion specifiers of
+ * #pint16 or #puint16 type for printing and scanning values. It is a string
+ * literal, but doesn't include the percent sign so you can add precision and
+ * length modifiers and append a conversion specifier.
+ */
+
+/**
+ * @def PINT16_FORMAT
+ * @brief Platform dependent conversion specifier of #pint16 type for printing
+ * and scanning values.
+ */
+
+/**
+ * @def PUINT16_FORMAT
+ * @brief Platform dependent conversion specifier of #puint16 type for printing
+ * and scanning values.
+ */
+
+/**
+ * @def PINT32_MODIFIER
+ * @brief Platform dependent length modifier for conversion specifiers of
+ * #pint32 or #puint32 type for printing and scanning values. It is a string
+ * literal, but doesn't include the percent sign so you can add precision and
+ * length modifiers and append a conversion specifier.
+ */
+
+/**
+ * @def PINT32_FORMAT
+ * @brief Platform dependent conversion specifier of #pint32 type for printing
+ * and scanning values.
+ */
+
+/**
+ * @def PUINT32_FORMAT
+ * @brief Platform dependent conversion specifier of #puint32 type for printing
+ * and scanning values.
+ */
+
+/**
+ * @def PINT64_MODIFIER
+ * @brief Platform dependent length modifier for conversion specifiers of
+ * #pint64 or #puint64 type for printing and scanning values. It is a string
+ * literal, but doesn't include the percent sign so you can add precision and
+ * length modifiers and append a conversion specifier.
+ */
+
+/**
+ * @def PINT64_FORMAT
+ * @brief Platform dependent conversion specifier of #pint64 type for printing
+ * and scanning values.
+ */
+
+/**
+ * @def PUINT64_FORMAT
+ * @brief Platform dependent conversion specifier of #puint64 type for printing
+ * and scanning values.
+ */
+
+/**
+ * @def POFFSET_MODIFIER
+ * @brief Platform dependent length modifier for conversion specifiers of
+ * #poffset type for printing and scanning values. It is a string literal, but
+ * doesn't include the percent sign so you can add precision and length
+ * modifiers and append a conversion specifier.
+ */
+
+/**
+ * @def POFFSET_FORMAT
+ * @brief Platform dependent conversion specifier of #poffset type for printing
+ * and scanning values.
+ */
+
+#if defined (P_OS_WIN) && (defined (P_CC_MSVC) || defined (P_CC_BORLAND))
+  #define PINT16_MODIFIER	"h"
+#else
+  #define PINT16_MODIFIER	""
+#endif
+
+#define PINT16_FORMAT		"hi"
+#define PUINT16_FORMAT		"hu"
+
+#define PINT32_MODIFIER		""
+#define PINT32_FORMAT		"i"
+#define PUINT32_FORMAT		"u"
+
+#if defined (P_OS_WIN) && (defined (P_CC_MSVC) || defined (P_CC_BORLAND))
+  #define PINT64_MODIFIER	"I64"
+  #define PINT64_FORMAT		"I64i"
+  #define PUINT64_FORMAT	"I64u"
+#else
+#  if PLIBSYS_SIZEOF_LONG == 8
+     #define PINT64_MODIFIER	"l"
+     #define PINT64_FORMAT	"li"
+     #define PUINT64_FORMAT	"lu"
+#  else
+     #define PINT64_MODIFIER	"ll"
+     #define PINT64_FORMAT	"lli"
+     #define PUINT64_FORMAT	"llu"
+#  endif
+#endif
+
+#define POFFSET_MODIFIER	PINT64_MODIFIER
+#define POFFSET_FORMAT		PINT64_FORMAT
+
+/* Endian checks, see P_BYTE_ORDER in plibsysconfig.h */
+
+/** Little endian mark.	*/
+#define P_LITTLE_ENDIAN	1234
+/** Big endian mark.	*/
+#define P_BIG_ENDIAN	4321
+
+/**
+ * @def PINT16_TO_LE
+ * @brief Swaps a #pint16 variable from the host to the little endian order.
+ * @param val Value to swap.
+ * @return Swapped value.
+ * @since 0.0.1
+ */
+
+/**
+ * @def PUINT16_TO_LE
+ * @brief Swaps a #puint16 variable from the host to the little the endian order.
+ * @param val Value to swap.
+ * @return Swapped value.
+ * @since 0.0.1
+ */
+
+/**
+ * @def PINT16_TO_BE
+ * @brief Swaps a #pint16 variable from the host to the big endian order.
+ * @param val Value to swap.
+ * @return Swapped value.
+ * @since 0.0.1
+ */
+
+/**
+ * @def PUINT16_TO_BE
+ * @brief Swaps a #puint16 variable from the host to the big endian order.
+ * @param val Value to swap.
+ * @return Swapped value.
+ * @since 0.0.1
+ */
+
+/**
+ * @def PINT32_TO_LE
+ * @brief Swaps a #pint32 variable from the host to the little endian order.
+ * @param val Value to swap.
+ * @return Swapped value.
+ * @since 0.0.1
+ */
+
+/**
+ * @def PUINT32_TO_LE
+ * @brief Swaps a #puint32 variable from the host to the little endian order.
+ * @param val Value to swap.
+ * @return Swapped value.
+ * @since 0.0.1
+ */
+
+/**
+ * @def PINT32_TO_BE
+ * @brief Swaps a #pint32 variable from the host to the big endian order.
+ * @param val Value to swap.
+ * @return Swapped value.
+ * @since 0.0.1
+ */
+
+/**
+ * @def PUINT32_TO_BE
+ * @brief Swaps a #puint32 variable from the host to the big endian order.
+ * @param val Value to swap.
+ * @return Swapped value.
+ * @since 0.0.1
+ */
+
+/**
+ * @def PINT64_TO_LE
+ * @brief Swaps a #pint64 variable from the host to the little endian order.
+ * @param val Value to swap.
+ * @return Swapped value.
+ * @since 0.0.1
+ */
+
+/**
+ * @def PUINT64_TO_LE
+ * @brief Swaps a #puint64 variable from the host to the little endian order.
+ * @param val Value to swap.
+ * @return Swapped value.
+ * @since 0.0.1
+ */
+
+/**
+ * @def PINT64_TO_BE
+ * @brief Swaps a #pint64 variable from the host to the big endian order.
+ * @param val Value to swap.
+ * @return Swapped value.
+ * @since 0.0.1
+ */
+
+/**
+ * @def PUINT64_TO_BE
+ * @brief Swaps a #puint64 variable from the host to the big endian order.
+ * @param val Value to swap.
+ * @return Swapped value.
+ * @since 0.0.1
+ */
+
+/**
+ * @def PLONG_TO_LE
+ * @brief Swaps a #plong variable from the host to the little endian order.
+ * @param val Value to swap.
+ * @return Swapped value.
+ * @since 0.0.1
+ */
+
+/**
+ * @def PULONG_TO_LE
+ * @brief Swaps a #pulong variable from the host to the little endian order.
+ * @param val Value to swap.
+ * @return Swapped value.
+ * @since 0.0.1
+ */
+
+/**
+ * @def PLONG_TO_BE
+ * @brief Swaps a #plong variable from the host to the big endian order.
+ * @param val Value to swap.
+ * @return Swapped value.
+ * @since 0.0.1
+ */
+
+/**
+ * @def PULONG_TO_BE
+ * @brief Swaps a #pulong variable from the host to the big endian order.
+ * @param val Value to swap.
+ * @return Swapped value.
+ * @since 0.0.1
+ */
+
+/**
+ * @def PSSIZE_TO_LE
+ * @brief Swaps a #pssize variable from the host to the little endian order.
+ * @param val Value to swap.
+ * @return Swapped value.
+ * @since 0.0.1
+ */
+
+/**
+ * @def PSIZE_TO_LE
+ * @brief Swaps a #psize variable from the host to the little endian order.
+ * @param val Value to swap.
+ * @return Swapped value.
+ * @since 0.0.1
+ */
+
+/**
+ * @def PSSIZE_TO_BE
+ * @brief Swaps a #pssize variable from the host to the big endian order.
+ * @param val Value to swap.
+ * @return Swapped value.
+ * @since 0.0.1
+ */
+
+/**
+ * @def PSIZE_TO_BE
+ * @brief Swaps a #psize variable from the host to the big endian order.
+ * @param val Value to swap.
+ * @return Swapped value.
+ * @since 0.0.1
+ */
+
+/**
+ * @def PINT_TO_LE
+ * @brief Swaps a #pint variable from the host to the little endian order.
+ * @param val Value to swap.
+ * @return Swapped value.
+ * @since 0.0.1
+ */
+
+/**
+ * @def PUINT_TO_LE
+ * @brief Swaps a #puint variable from the host to the little endian order.
+ * @param val Value to swap.
+ * @return Swapped value.
+ * @since 0.0.1
+ */
+
+/**
+ * @def PINT_TO_BE
+ * @brief Swaps a #pint variable from the host to the big endian order.
+ * @param val Value to swap.
+ * @return Swapped value.
+ * @since 0.0.1
+ */
+
+/**
+ * @def PUINT_TO_BE
+ * @brief Swaps a #puint variable from the host to the big endian order.
+ * @param val Value to swap.
+ * @return Swapped value.
+ * @since 0.0.1
+ */
+
+#if P_BYTE_ORDER == P_LITTLE_ENDIAN
+  #define PINT16_TO_LE(val)	((pint16) (val))
+  #define PUINT16_TO_LE(val)	((puint16) (val))
+  #define PINT16_TO_BE(val)	((pint16) PUINT16_SWAP_BYTES (val))
+  #define PUINT16_TO_BE(val)	(PUINT16_SWAP_BYTES (val))
+  #define PINT32_TO_LE(val)	((pint32) (val))
+  #define PUINT32_TO_LE(val)	((puint32) (val))
+  #define PINT32_TO_BE(val)	((pint32) PUINT32_SWAP_BYTES (val))
+  #define PUINT32_TO_BE(val)	(PUINT32_SWAP_BYTES (val))
+  #define PINT64_TO_LE(val)	((pint64) (val))
+  #define PUINT64_TO_LE(val)	((puint64) (val))
+  #define PINT64_TO_BE(val)	((pint64) PUINT64_SWAP_BYTES (val))
+  #define PUINT64_TO_BE(val)	(PUINT64_SWAP_BYTES (val))
+#  if PLIBSYS_SIZEOF_LONG == 8
+     #define PLONG_TO_LE(val)	((plong) PINT64_TO_LE (val))
+     #define PULONG_TO_LE(val)	((pulong) PUINT64_TO_LE (val))
+     #define PLONG_TO_BE(val)	((plong) PINT64_TO_BE (val))
+     #define PULONG_TO_BE(val)	((pulong) PUINT64_TO_BE (val))
+#  else
+     #define PLONG_TO_LE(val)	((plong) PINT32_TO_LE (val))
+     #define PULONG_TO_LE(val)	((pulong) PUINT32_TO_LE (val))
+     #define PLONG_TO_BE(val)	((plong) PINT32_TO_BE (val))
+     #define PULONG_TO_BE(val)	((pulong) PUINT32_TO_BE (val))
+#  endif
+#  if PLIBSYS_SIZEOF_SIZE_T == 8
+     #define PSIZE_TO_LE(val)	((psize) PUINT64_TO_LE (val))
+     #define PSSIZE_TO_LE(val)	((pssize) PINT64_TO_LE (val))
+     #define PSIZE_TO_BE(val)	((psize) PUINT64_TO_BE (val))
+     #define PSSIZE_TO_BE(val)	((pssize) PINT64_TO_BE (val))
+#  else
+     #define PSIZE_TO_LE(val)	((psize) PUINT32_TO_LE (val))
+     #define PSSIZE_TO_LE(val)	((pssize) PINT32_TO_LE (val))
+     #define PSIZE_TO_BE(val)	((psize) PUINT32_TO_BE (val))
+     #define PSSIZE_TO_BE(val)	((pssize) PINT32_TO_BE (val))
+#  endif
+  #define PINT_TO_LE(val)	((pint) PINT32_TO_LE (val))
+  #define PUINT_TO_LE(val)	((puint) PUINT32_TO_LE (val))
+  #define PINT_TO_BE(val)	((pint) PINT32_TO_BE (val))
+  #define PUINT_TO_BE(val)	((puint) PUINT32_TO_BE (val))
+
+#else
+  #define PINT16_TO_LE(val)	((pint16) PUINT16_SWAP_BYTES (val))
+  #define PUINT16_TO_LE(val)	(PUINT16_SWAP_BYTES (val))
+  #define PINT16_TO_BE(val)	((pint16) (val))
+  #define PUINT16_TO_BE(val)	((puint16) (val))
+  #define PINT32_TO_LE(val)	((pint32) PUINT32_SWAP_BYTES (val))
+  #define PUINT32_TO_LE(val)	(PUINT32_SWAP_BYTES (val))
+  #define PINT32_TO_BE(val)	((pint32) (val))
+  #define PUINT32_TO_BE(val)	((puint32) (val))
+  #define PINT64_TO_LE(val)	((pint64) PUINT64_SWAP_BYTES (val))
+  #define PUINT64_TO_LE(val)	(PUINT64_SWAP_BYTES (val))
+  #define PINT64_TO_BE(val)	((pint64) (val))
+  #define PUINT64_TO_BE(val)	((puint64) (val))
+#  if PLIBSYS_SIZEOF_LONG == 8
+     #define PLONG_TO_LE(val)	((plong) PINT64_TO_LE (val))
+     #define PULONG_TO_LE(val)	((pulong) PUINT64_TO_LE (val))
+     #define PLONG_TO_BE(val)	((plong) PINT64_TO_BE (val))
+     #define PULONG_TO_BE(val)	((pulong) PUINT64_TO_BE (val))
+#  else
+     #define PLONG_TO_LE(val)	((plong) PINT32_TO_LE (val))
+     #define PULONG_TO_LE(val)	((pulong) PUINT32_TO_LE (val))
+     #define PLONG_TO_BE(val)	((plong) PINT32_TO_BE (val))
+     #define PULONG_TO_BE(val)	((pulong) PUINT32_TO_BE (val))
+#  endif
+#  if PLIBSYS_SIZEOF_SIZE_T == 8
+     #define PSIZE_TO_LE(val)	((psize) PUINT64_TO_LE (val))
+     #define PSSIZE_TO_LE(val)	((pssize) PINT64_TO_LE (val))
+     #define PSIZE_TO_BE(val)	((psize) PUINT64_TO_BE (val))
+     #define PSSIZE_TO_BE(val)	((pssize) PINT64_TO_BE (val))
+#  else
+     #define PSIZE_TO_LE(val)	((psize) PUINT32_TO_LE (val))
+     #define PSSIZE_TO_LE(val)	((pssize) PINT32_TO_LE (val))
+     #define PSIZE_TO_BE(val)	((psize) PUINT32_TO_BE (val))
+     #define PSSIZE_TO_BE(val)	((pssize) PINT32_TO_BE (val))
+#  endif
+  #define PINT_TO_LE(val)	((pint) PINT32_TO_LE (val))
+  #define PUINT_TO_LE(val)	((puint) PUINT32_TO_LE (val))
+  #define PINT_TO_BE(val)	((pint) PINT32_TO_BE (val))
+  #define PUINT_TO_BE(val)	((puint) PUINT32_TO_BE (val))
+#endif
+
+/* Functions for bit swapping */
+
+/**
+ * @brief Swaps a 16-bit unsigned int.
+ * @param val Value to swap.
+ * @return Swapped 16-bit unsigned int.
+ * @since 0.0.1
+ */
+#define PUINT16_SWAP_BYTES(val)			\
+	((puint16) (((puint16) (val)) >> 8 | ((puint16) (val)) << 8))
+
+/**
+ * @brief Swaps a 32-bit unsigned int.
+ * @param val Value to swap.
+ * @return Swapped 32-bit unsigned int.
+ * @since 0.0.1
+ */
+#define PUINT32_SWAP_BYTES(val) ((puint32) (			\
+	(((puint32) (val)) >> 24) |				\
+	((((puint32) (val)) << 8) & ((puint32) 0x00FF0000U)) |	\
+	((((puint32) (val)) >> 8) & ((puint32) 0x0000FF00U)) |	\
+	(((puint32) (val)) << 24)))
+
+/**
+ * @brief Swaps a 64-bit unsigned int.
+ * @param val Value to swap.
+ * @return Swapped 64-bit unsigned int.
+ * @since 0.0.1
+ */
+#define PUINT64_SWAP_BYTES(val) ((puint64) (					\
+	(((puint64) (val))  >> 56) |						\
+	((((puint64) (val)) << 40) & ((puint64) 0x00FF000000000000ULL)) |	\
+	((((puint64) (val)) << 24) & ((puint64) 0x0000FF0000000000ULL)) |	\
+	((((puint64) (val)) <<  8) & ((puint64) 0x000000FF00000000ULL)) |	\
+	((((puint64) (val)) >>  8) & ((puint64) 0x00000000FF000000ULL)) |	\
+	((((puint64) (val)) >> 24) & ((puint64) 0x0000000000FF0000ULL)) |	\
+	((((puint64) (val)) >> 40) & ((puint64) 0x000000000000FF00ULL)) |	\
+	(((puint64) (val))  << 56)))
+
+/* Functions, similar to ?_TO_? functions */
+
+/**
+ * @brief Swaps a 16-bit int from the little endian byte order to the host one.
+ * @param val Value to swap.
+ * @return Swapped 16-bit int.
+ * @since 0.0.1
+ */
+#define PINT16_FROM_LE(val)	(PINT16_TO_LE (val))
+
+/**
+ * @brief Swaps a 16-bit unsigned int from the little endian byte order to the
+ * host one.
+ * @param val Value to swap.
+ * @return Swapped 16-bit unsigned int.
+ * @since 0.0.1
+ */
+#define PUINT16_FROM_LE(val)	(PUINT16_TO_LE (val))
+
+/**
+ * @brief Swaps a 16-bit int from the big endian byte order to the host one.
+ * @param val Value to swap.
+ * @return Swapped 16-bit int.
+ * @since 0.0.1
+ */
+#define PINT16_FROM_BE(val)	(PINT16_TO_BE (val))
+
+/**
+ * @brief Swaps a 16-bit unsigned int from the big endian byte order to the host
+ * one.
+ * @param val Value to swap.
+ * @return Swapped 16-bit unsigned int.
+ * @since 0.0.1
+ */
+#define PUINT16_FROM_BE(val)	(PUINT16_TO_BE (val))
+
+
+/**
+ * @brief Swaps a 32-bit int from the little endian byte order to the host one.
+ * @param val Value to swap.
+ * @return Swapped 32-bit int.
+ * @since 0.0.1
+ */
+#define PINT32_FROM_LE(val)	(PINT32_TO_LE (val))
+
+/**
+ * @brief Swaps a 32-bit unsigned int from the little endian byte order to the
+ * host one.
+ * @param val Value to swap.
+ * @return Swapped 32-bit unsigned int.
+ * @since 0.0.1
+ */
+#define PUINT32_FROM_LE(val)	(PUINT32_TO_LE (val))
+
+/**
+ * @brief Swaps a 32-bit int from the big endian byte order to the host one.
+ * @param val Value to swap.
+ * @return Swapped 32-bit int.
+ * @since 0.0.1
+ */
+#define PINT32_FROM_BE(val)	(PINT32_TO_BE (val))
+
+/**
+ * @brief Swaps a 32-bit unsigned int from the big endian byte order to the host
+ * one.
+ * @param val Value to swap.
+ * @return Swapped 32-bit unsigned int.
+ * @since 0.0.1
+ */
+#define PUINT32_FROM_BE(val)	(PUINT32_TO_BE (val))
+
+/**
+ * @brief Swaps a 64-bit int from the little endian byte order to the host one.
+ * @param val Value to swap.
+ * @return Swapped 64-bit int.
+ * @since 0.0.1
+ */
+#define PINT64_FROM_LE(val)	(PINT64_TO_LE (val))
+
+/**
+ * @brief Swaps a 64-bit unsigned int from the little endian byte order to the
+ * host one.
+ * @param val Value to swap.
+ * @return Swapped 64-bit unsigned int.
+ * @since 0.0.1
+ */
+#define PUINT64_FROM_LE(val)	(PUINT64_TO_LE (val))
+
+/**
+ * @brief Swaps a 64-bit int from the big endian byte order to the host one.
+ * @param val Value to swap.
+ * @return Swapped 64-bit int.
+ * @since 0.0.1
+ */
+#define PINT64_FROM_BE(val)	(PINT64_TO_BE (val))
+
+/**
+ * @brief Swaps a 64-bit unsigned int from the big endian byte order to the host
+ * one.
+ * @param val Value to swap.
+ * @return Swapped 64-bit unsigned int.
+ * @since 0.0.1
+ */
+#define PUINT64_FROM_BE(val)	(PUINT64_TO_BE (val))
+
+/**
+ * @brief Swaps a long int from the little endian byte order to the host one.
+ * @param val Value to swap.
+ * @return Swapped long int.
+ * @since 0.0.1
+ */
+#define PLONG_FROM_LE(val)	(PLONG_TO_LE (val))
+
+/**
+ * @brief Swaps an unsigned long int from the little endian byte order to the
+ * host one.
+ * @param val Value to swap.
+ * @return Swapped unsigned long int.
+ * @since 0.0.1
+ */
+#define PULONG_FROM_LE(val)	(PULONG_TO_LE (val))
+
+/**
+ * @brief Swaps a long int from the big endian byte order to the host one.
+ * @param val Value to swap.
+ * @return Swapped long int.
+ * @since 0.0.1
+ */
+#define PLONG_FROM_BE(val)	(PLONG_TO_BE (val))
+
+/**
+ * @brief Swaps an unsigned long int from the big endian byte order to the host
+ * one.
+ * @param val Value to swap.
+ * @return Swapped unsigned long int.
+ * @since 0.0.1
+ */
+#define PULONG_FROM_BE(val)	(PULONG_TO_BE (val))
+
+/**
+ * @brief Swaps a #pint from the little endian byte order to the host one.
+ * @param val Value to swap.
+ * @return Swapped #pint.
+ * @since 0.0.1
+ */
+#define PINT_FROM_LE(val)	(PINT_TO_LE (val))
+
+/**
+ * @brief Swaps a #puint from the little endian byte order to the host one.
+ * @param val Value to swap.
+ * @return Swapped #puint.
+ * @since 0.0.1
+ */
+#define PUINT_FROM_LE(val)	(PUINT_TO_LE (val))
+
+/**
+ * @brief Swaps a #pint from the big endian byte order to the host one.
+ * @param val Value to swap.
+ * @return Swapped #pint.
+ * @since 0.0.1
+ */
+#define PINT_FROM_BE(val)	(PINT_TO_BE (val))
+
+/**
+ * @brief Swaps a #puint from the big endian byte order to the host one.
+ * @param val Value to swap.
+ * @return Swapped #puint.
+ * @since 0.0.1
+ */
+#define PUINT_FROM_BE(val)	(PUINT_TO_BE (val))
+
+/**
+ * @brief Swaps a #psize from the little endian byte order to the host one.
+ * @param val Value to swap.
+ * @return Swapped #psize.
+ * @since 0.0.1
+ */
+#define PSIZE_FROM_LE(val)	(PSIZE_TO_LE (val))
+
+/**
+ * @brief Swaps a #pssize from the little endian byte order to the host one.
+ * @param val Value to swap.
+ * @return Swapped #pssize.
+ * @since 0.0.1
+ */
+#define PSSIZE_FROM_LE(val)	(PSSIZE_TO_LE (val))
+
+/**
+ * @brief Swaps a #psize from the big endian byte order to the host one.
+ * @param val Value to swap.
+ * @return Swapped #psize.
+ * @since 0.0.1
+ */
+#define PSIZE_FROM_BE(val)	(PSIZE_TO_BE (val))
+
+/**
+ * @brief Swaps a #pssize from the big endian byte order to the host one.
+ * @param val Value to swap.
+ * @return Swapped #pssize.
+ * @since 0.0.1
+ */
+#define PSSIZE_FROM_BE(val)	(PSSIZE_TO_BE (val))
+
+/* Host-network order functions */
+
+/**
+ * @brief Swaps a long int from the network byte order to the host one.
+ * @param val Value to swap.
+ * @return Swapped long int.
+ * @since 0.0.1
+ */
+#define p_ntohl(val) (PUINT32_FROM_BE (val))
+
+/**
+ * @brief Swaps a short int from the network byte order to the host one.
+ * @param val Value to swap.
+ * @return Swapped short int.
+ * @since 0.0.1
+ */
+#define p_ntohs(val) (PUINT16_FROM_BE (val))
+
+/**
+ * @brief Swaps a long int from the host byte order to the network one.
+ * @param val Value to swap.
+ * @return Swapped long int.
+ * @since 0.0.1
+ */
+#define p_htonl(val) (PUINT32_TO_BE (val))
+
+/**
+ * @brief Swaps a short int from the host byte order to the network one.
+ * @param val Value to swap.
+ * @return Swapped short int.
+ * @since 0.0.1
+ */
+#define p_htons(val) (PUINT16_TO_BE (val))
+
+#ifndef FALSE
+/** Type definition for a false boolean value.	*/
+#  define FALSE (0)
+#endif
+
+#ifndef TRUE
+/** Type definition for a true boolean value.	*/
+#  define TRUE (!FALSE)
+#endif
+
+/**
+ * @brief Platform independent system handle.
+ */
+typedef void * P_HANDLE;
+
+/**
+ * @brief Function to traverse through a key-value container.
+ * @param key The key of an item.
+ * @param value The value of the item.
+ * @param user_data Data provided by a user, maybe NULL.
+ * @return FALSE to continue traversing, TRUE to stop it.
+ * @since 0.0.1
+ */
+typedef pboolean (*PTraverseFunc) (ppointer key,
+				   ppointer value,
+				   ppointer user_data);
+
+/**
+ * @brief General purpose function.
+ * @param data Main argument related to a context value.
+ * @param user_data Additional (maybe NULL) user-provided data.
+ * @since 0.0.1
+ */
+typedef void (*PFunc) (ppointer data, ppointer user_data);
+
+/**
+ * @brief Object destroy notification function.
+ * @param data Pointer to an object to be destroyed.
+ * @since 0.0.1
+ */
+typedef void (*PDestroyFunc) (ppointer data);
+
+/**
+ * @brief Compares two values.
+ * @param a First value to compare.
+ * @param b Second value to compare.
+ * @return -1 if the first value is less than the second, 1 if the first value
+ * is greater than the second, 0 otherwise.
+ * @since 0.0.1
+ */
+typedef pint (*PCompareFunc) (pconstpointer a, pconstpointer b);
+
+/**
+ * @brief Compares two values with additional data.
+ * @param a First value to compare.
+ * @param b Second value to compare.
+ * @param data Addition data, may be NULL.
+ * @return -1 if the first value is less than the second, 1 if the first value
+ * is greater than the second, 0 otherwise.
+ * @since 0.0.1
+ */
+typedef pint (*PCompareDataFunc) (pconstpointer a, pconstpointer b, ppointer data);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PTYPES_H */
diff --git a/3rdparty/plibsys/src/puthread-amiga.c b/3rdparty/plibsys/src/puthread-amiga.c
new file mode 100644
index 0000000..ac23496
--- /dev/null
+++ b/3rdparty/plibsys/src/puthread-amiga.c
@@ -0,0 +1,673 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2017-2019 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "patomic.h"
+#include "pcondvariable.h"
+#include "plist.h"
+#include "pmutex.h"
+#include "puthread.h"
+#include "puthread-private.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <setjmp.h>
+
+#include <proto/exec.h>
+#include <proto/dos.h>
+
+#define PUTHREAD_AMIGA_MAX_TLS_KEYS 128
+#define PUTHREAD_AMIGA_MIN_STACK    524288
+#define PUTHREAD_AMIGA_MAX_CLEANS   4
+
+typedef struct {
+	pboolean 	in_use;
+	PDestroyFunc	free_func;
+} PUThreadTLSKey;
+
+typedef struct {
+	pint		id;
+	struct Task	*task;
+	jmp_buf		jmpbuf;
+	ppointer	tls_values[PUTHREAD_AMIGA_MAX_TLS_KEYS];
+} PUThreadInfo;
+
+struct PUThread_ {
+	PUThreadBase	base;
+	PUThreadFunc	proxy;
+	PCondVariable	*join_cond;
+	struct Task	*task;
+};
+
+typedef pint puthread_key_t;
+
+struct PUThreadKey_ {
+	puthread_key_t	key;
+	PDestroyFunc	free_func;
+};
+
+static PMutex *pp_uthread_glob_mutex = NULL;
+static PList  *pp_uthread_list       = NULL;
+static pint    pp_uthread_last_id    = 0;
+
+static PUThreadTLSKey pp_uthread_tls_keys[PUTHREAD_AMIGA_MAX_TLS_KEYS];
+
+static pint pp_uthread_get_amiga_priority (PUThreadPriority prio);
+static puthread_key_t pp_uthread_get_tls_key (PUThreadKey *key);
+static pint pp_uthread_find_next_id (void);
+static PUThreadInfo * pp_uthread_find_thread_info (struct Task *task);
+static PUThreadInfo * pp_uthread_find_or_create_thread_info (struct Task *task);
+static pint pp_uthread_amiga_proxy (void);
+
+static pint
+pp_uthread_get_amiga_priority (PUThreadPriority prio)
+{
+	/* Priority limit is [-128, 127] */
+
+	switch (prio) {
+		case P_UTHREAD_PRIORITY_INHERIT:
+			return 0;
+		case P_UTHREAD_PRIORITY_IDLE:
+			return -128;
+		case P_UTHREAD_PRIORITY_LOWEST:
+			return -50;
+		case P_UTHREAD_PRIORITY_LOW:
+			return -25;
+		case P_UTHREAD_PRIORITY_NORMAL:
+			return 0;
+		case P_UTHREAD_PRIORITY_HIGH:
+			return 25;
+		case P_UTHREAD_PRIORITY_HIGHEST:
+			return 50;
+		case P_UTHREAD_PRIORITY_TIMECRITICAL:
+			return 127;
+		default:
+			return 0;
+	}
+}
+
+static puthread_key_t
+pp_uthread_get_tls_key (PUThreadKey *key)
+{
+	puthread_key_t	thread_key;
+	pint 		key_idx;
+
+	thread_key = (puthread_key_t) p_atomic_int_get (&key->key);
+
+	if (P_LIKELY (thread_key >= 0))
+		return thread_key;
+
+	p_mutex_lock (pp_uthread_glob_mutex);
+
+	if (key->key >= 0) {
+		p_mutex_unlock (pp_uthread_glob_mutex);
+		return key->key;
+	}
+
+	/* Find free TLS key index */
+
+	for (key_idx = 0; key_idx < PUTHREAD_AMIGA_MAX_TLS_KEYS; ++key_idx) {
+		if (P_LIKELY (pp_uthread_tls_keys[key_idx].in_use == FALSE)) {
+			pp_uthread_tls_keys[key_idx].in_use    = TRUE;
+			pp_uthread_tls_keys[key_idx].free_func = key->free_func;
+
+			break;
+		}
+	}
+
+	if (key_idx == PUTHREAD_AMIGA_MAX_TLS_KEYS) {
+		p_mutex_unlock (pp_uthread_glob_mutex);
+		P_ERROR ("PUThread::pp_uthread_get_tls_key: all slots for TLS keys are used");
+		return -1;
+	}
+
+	key->key = key_idx;
+
+	p_mutex_unlock (pp_uthread_glob_mutex);
+
+	return key_idx;
+}
+
+/* Must be used only inside a protected critical region */
+
+static pint
+pp_uthread_find_next_id (void)
+{
+	PList		*cur_list;
+	PUThreadInfo	*thread_info;
+	pboolean	have_dup;
+	pboolean	was_found  = FALSE;
+	pint		cur_id     = pp_uthread_last_id;
+	pint		of_counter = 0;
+
+	while (was_found == FALSE && of_counter < 2) {
+		have_dup = FALSE;
+		cur_id   = (cur_id == P_MAXINT32) ? 0 : cur_id + 1;
+
+		if (cur_id == 0)
+			++of_counter;
+
+		for (cur_list = pp_uthread_list; cur_list != NULL; cur_list = cur_list->next) {
+			thread_info = (PUThreadInfo *) cur_list->data;
+
+			if (thread_info->id == cur_id) {
+				have_dup = TRUE;
+				break;
+			}
+		}
+
+		if (have_dup == FALSE)
+			was_found = TRUE;
+	}
+
+	if (P_UNLIKELY (of_counter == 2))
+		return -1;
+
+	pp_uthread_last_id = cur_id;
+
+	return cur_id;
+}
+
+/* Must be used only inside a protected critical region */
+
+static PUThreadInfo *
+pp_uthread_find_thread_info (struct Task *task)
+{
+	PList		*cur_list;
+	PUThreadInfo	*thread_info;
+
+	for (cur_list = pp_uthread_list; cur_list != NULL; cur_list = cur_list->next) {
+		thread_info = (PUThreadInfo *) cur_list->data;
+
+		if (thread_info->task == task)
+			return thread_info;
+	}
+
+	return NULL;
+}
+
+/* Must be used only inside a protected critical region */
+
+static PUThreadInfo *
+pp_uthread_find_or_create_thread_info (struct Task *task)
+{
+	PUThreadInfo	*thread_info;
+	pint		task_id;
+
+	thread_info  = pp_uthread_find_thread_info (task);
+
+	if (thread_info == NULL) {
+		/* Call is from a forein thread */
+
+		task_id = pp_uthread_find_next_id ();
+
+		if (P_UNLIKELY (task_id == -1)) {
+			/* Beyond the limit of the number of threads */
+			P_ERROR ("PUThread::pp_uthread_find_or_create_thread_info: no free thread slots left");
+			return NULL;
+		}
+
+		if (P_UNLIKELY ((thread_info = p_malloc0 (sizeof (PUThreadInfo))) == NULL)) {
+			P_ERROR ("PUThread::pp_uthread_find_or_create_thread_info: failed to allocate memory");
+			return NULL;
+		}
+
+		thread_info->id   = task_id;
+		thread_info->task = task;
+
+		pp_uthread_list = p_list_append (pp_uthread_list, thread_info);
+	}
+
+	return thread_info;
+}
+
+static pint
+pp_uthread_amiga_proxy (void)
+{
+	PUThread	*thread;
+	PUThreadInfo	*thread_info;
+	struct Task	*task;
+	PDestroyFunc	dest_func;
+	ppointer	dest_data;
+	pboolean	need_pass;
+	pint		i;
+	pint		clean_counter;
+
+	/* Wait for outer routine to finish data initialization */
+
+	p_mutex_lock (pp_uthread_glob_mutex);
+
+	task        = IExec->FindTask (NULL);
+	thread      = (PUThread *) (task->tc_UserData);
+	thread_info = pp_uthread_find_thread_info (task);
+
+	p_mutex_unlock (pp_uthread_glob_mutex);
+
+	IExec->SetTaskPri (task, pp_uthread_get_amiga_priority (thread->base.prio));
+
+	if (!setjmp (thread_info->jmpbuf))
+		thread->proxy (thread);
+
+	/* Clean up TLS values */
+
+	p_mutex_lock (pp_uthread_glob_mutex);
+
+	need_pass     = TRUE;
+	clean_counter = 0;
+
+	while (need_pass && clean_counter < PUTHREAD_AMIGA_MAX_CLEANS) {
+		need_pass = FALSE;
+
+		for (i = 0; i < PUTHREAD_AMIGA_MAX_TLS_KEYS; ++i) {
+			if (pp_uthread_tls_keys[i].in_use == TRUE) {
+				dest_func = pp_uthread_tls_keys[i].free_func;
+				dest_data = thread_info->tls_values[i];
+
+				if (dest_func != NULL && dest_data != NULL) {
+					/* Destructor may do some trick with TLS as well */
+					thread_info->tls_values[i] = NULL;
+
+					p_mutex_unlock (pp_uthread_glob_mutex);
+					(dest_func) (dest_data);
+					p_mutex_lock (pp_uthread_glob_mutex);
+
+					need_pass = TRUE;
+				}
+			}
+		}
+
+		++clean_counter;
+	}
+
+	pp_uthread_list = p_list_remove (pp_uthread_list, thread_info);
+
+	p_free (thread_info);
+
+	p_mutex_unlock (pp_uthread_glob_mutex);
+
+	/* Signal to possible waiter */
+
+	p_cond_variable_broadcast (thread->join_cond);
+}
+
+void
+p_uthread_init_internal (void)
+{
+	if (P_LIKELY (pp_uthread_glob_mutex == NULL)) {
+		pp_uthread_glob_mutex = p_mutex_new ();
+		pp_uthread_list       = NULL;
+		pp_uthread_last_id    = 0;
+
+		memset (pp_uthread_tls_keys, 0, sizeof (PUThreadTLSKey) * PUTHREAD_AMIGA_MAX_TLS_KEYS);
+	}
+}
+
+void
+p_uthread_shutdown_internal (void)
+{
+	PList		*cur_list;
+	PUThreadInfo	*thread_info;
+	PDestroyFunc	dest_func;
+	ppointer	dest_data;
+	pboolean	need_pass;
+	pint		i;
+	pint		clean_counter;
+
+	/* Perform destructors */
+
+	p_mutex_lock (pp_uthread_glob_mutex);
+
+	need_pass     = TRUE;
+	clean_counter = 0;
+
+	while (need_pass && clean_counter < PUTHREAD_AMIGA_MAX_CLEANS) {
+		need_pass = FALSE;
+
+		for (i = 0; i < PUTHREAD_AMIGA_MAX_TLS_KEYS; ++i) {
+			if (pp_uthread_tls_keys[i].in_use == FALSE)
+				continue;
+
+			dest_func = pp_uthread_tls_keys[i].free_func;
+
+			if (dest_func == NULL)
+				continue;
+
+			for (cur_list = pp_uthread_list; cur_list != NULL; cur_list = cur_list->next) {
+				thread_info = (PUThreadInfo *) cur_list->data;
+				dest_data   = thread_info->tls_values[i];
+
+				if (dest_data != NULL) {
+					/* Destructor may do some trick with TLS as well */
+
+					thread_info->tls_values[i] = NULL;
+
+					p_mutex_unlock (pp_uthread_glob_mutex);
+					(dest_func) (dest_data);
+					p_mutex_lock (pp_uthread_glob_mutex);
+
+					need_pass = TRUE;
+				}
+			}
+		}
+	}
+
+	/* Clean the list */
+
+	p_list_foreach (pp_uthread_list, (PFunc) p_free, NULL);
+	p_list_free (pp_uthread_list);
+
+	pp_uthread_list = NULL;
+
+	p_mutex_unlock (pp_uthread_glob_mutex);
+
+	if (P_LIKELY (pp_uthread_glob_mutex != NULL)) {
+		p_mutex_free (pp_uthread_glob_mutex);
+		pp_uthread_glob_mutex = NULL;
+	}
+}
+
+void
+p_uthread_win32_thread_detach (void)
+{
+}
+
+void
+p_uthread_free_internal (PUThread *thread)
+{
+	if (thread->join_cond != NULL)
+		p_cond_variable_free (thread->join_cond);
+
+	p_free (thread);
+}
+
+PUThread *
+p_uthread_create_internal (PUThreadFunc		func,
+			   pboolean		joinable,
+			   PUThreadPriority	prio,
+			   psize		stack_size)
+{
+	PUThread	*ret;
+	PUThreadInfo	*thread_info;
+	struct Task	*task;
+	pint		task_id;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThread))) == NULL)) {
+		P_ERROR ("PUThread::p_uthread_create_internal: failed to allocate memory");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((ret->join_cond = p_cond_variable_new ()) == NULL)) {
+		P_ERROR ("PUThread::p_uthread_create_internal: failed to allocate condvar");
+		p_uthread_free_internal (ret);
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((thread_info = p_malloc0 (sizeof (PUThreadInfo))) == NULL)) {
+		P_ERROR ("PUThread::p_uthread_create_internal: failed to allocate memory (2)");
+		p_uthread_free_internal (ret);
+		return NULL;
+	}
+
+	p_mutex_lock (pp_uthread_glob_mutex);
+
+	task_id = pp_uthread_find_next_id ();
+
+	if (P_UNLIKELY (task_id == -1)) {
+		p_mutex_unlock (pp_uthread_glob_mutex);
+		P_ERROR ("PUThread::p_uthread_create_internal: no free thread slots left");
+		p_uthread_free_internal (ret);
+		p_free (thread_info);
+		return NULL;
+	}
+
+	ret->proxy         = func;
+	ret->base.prio     = prio;
+	ret->base.joinable = joinable;
+
+	if (stack_size < PUTHREAD_AMIGA_MIN_STACK)
+		stack_size = PUTHREAD_AMIGA_MIN_STACK;
+
+	task = (struct Task *) IDOS->CreateNewProcTags (NP_Entry,     pp_uthread_amiga_proxy,
+							NP_StackSize, stack_size,
+							NP_UserData,  ret,
+							NP_Child,     TRUE,
+							TAG_END);
+
+	if (P_UNLIKELY (task == NULL)) {
+		p_mutex_unlock (pp_uthread_glob_mutex);
+		P_ERROR ("PUThread::p_uthread_create_internal: CreateTaskTags() failed");
+		p_uthread_free_internal (ret);
+		p_free (thread_info);
+		return NULL;
+	}
+
+	thread_info->task = task;
+	thread_info->id   = task_id;
+
+	pp_uthread_list = p_list_append (pp_uthread_list, thread_info);
+
+	ret->task = task;
+
+	p_mutex_unlock (pp_uthread_glob_mutex);
+
+	return ret;
+}
+
+void
+p_uthread_exit_internal (void)
+{
+	PUThreadInfo *thread_info;
+
+	p_mutex_lock (pp_uthread_glob_mutex);
+
+	thread_info = pp_uthread_find_thread_info (IExec->FindTask (NULL));
+
+	p_mutex_unlock (pp_uthread_glob_mutex);
+
+	if (P_UNLIKELY (thread_info == NULL)) {
+		P_WARNING ("PUThread::p_uthread_exit_internal: trying to exit from foreign thread");
+		return;
+	}
+
+	longjmp (thread_info->jmpbuf, 1);
+}
+
+void
+p_uthread_wait_internal (PUThread *thread)
+{
+	PUThreadInfo *thread_info;
+
+	p_mutex_lock (pp_uthread_glob_mutex);
+
+	thread_info = pp_uthread_find_thread_info (thread->task);
+
+	if (thread_info == NULL) {
+		p_mutex_unlock (pp_uthread_glob_mutex);
+		return;
+	}
+
+	p_cond_variable_wait (thread->join_cond, pp_uthread_glob_mutex);
+	p_mutex_unlock (pp_uthread_glob_mutex);
+}
+
+void
+p_uthread_set_name_internal (PUThread *thread)
+{
+	struct Task *task = thread->task;
+
+	task->tc_Node.ln_Name = thread->base.name;
+}
+
+P_LIB_API void
+p_uthread_yield (void)
+{
+	BYTE		old_prio;
+	struct Task	*task;
+
+	task = IExec->FindTask (NULL);
+
+	old_prio = IExec->SetTaskPri (task, -10);
+	IExec->SetTaskPri (task, old_prio);
+}
+
+P_LIB_API pboolean
+p_uthread_set_priority (PUThread		*thread,
+			PUThreadPriority	prio)
+{
+	if (P_UNLIKELY (thread == NULL))
+		return FALSE;
+
+	IExec->SetTaskPri (thread->task, pp_uthread_get_amiga_priority (prio));
+
+	thread->base.prio = prio;
+	return TRUE;
+}
+
+P_LIB_API P_HANDLE
+p_uthread_current_id (void)
+{
+	PUThreadInfo *thread_info;
+	
+	p_mutex_lock (pp_uthread_glob_mutex);
+
+	thread_info  = pp_uthread_find_or_create_thread_info (IExec->FindTask (NULL));
+
+	p_mutex_unlock (pp_uthread_glob_mutex);
+
+	if (P_UNLIKELY (thread_info == NULL))
+		P_WARNING ("PUThread::p_uthread_current_id: failed to integrate foreign thread");
+
+	return (thread_info == NULL) ? NULL : (P_HANDLE) ((psize) thread_info->id);
+}
+
+P_LIB_API PUThreadKey *
+p_uthread_local_new (PDestroyFunc free_func)
+{
+	PUThreadKey *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThreadKey))) == NULL)) {
+		P_ERROR ("PUThread::p_uthread_local_new: failed to allocate memory");
+		return NULL;
+	}
+
+	ret->key       = -1;
+	ret->free_func = free_func;
+
+	return ret;
+}
+
+P_LIB_API void
+p_uthread_local_free (PUThreadKey *key)
+{
+	if (P_UNLIKELY (key == NULL))
+		return;
+
+	p_free (key);
+}
+
+P_LIB_API ppointer
+p_uthread_get_local (PUThreadKey *key)
+{
+	PUThreadInfo	*thread_info;
+	puthread_key_t	tls_key;
+	ppointer	value = NULL;
+
+	if (P_UNLIKELY (key == NULL))
+		return NULL;
+
+	if (P_UNLIKELY ((tls_key = pp_uthread_get_tls_key (key)) == -1))
+		return NULL;
+
+	p_mutex_lock (pp_uthread_glob_mutex);
+
+	thread_info = pp_uthread_find_thread_info (IExec->FindTask (NULL));
+	
+	if (P_LIKELY (thread_info != NULL))
+		value = thread_info->tls_values[tls_key];
+
+	p_mutex_unlock (pp_uthread_glob_mutex);
+
+	return value;
+}
+
+P_LIB_API void
+p_uthread_set_local (PUThreadKey	*key,
+		     ppointer		value)
+{
+	PUThreadInfo	*thread_info;
+	puthread_key_t	tls_key;
+
+	if (P_UNLIKELY (key == NULL))
+		return;
+
+	tls_key = pp_uthread_get_tls_key (key);
+
+	if (P_LIKELY (tls_key != -1)) {
+		p_mutex_lock (pp_uthread_glob_mutex);
+
+		thread_info = pp_uthread_find_or_create_thread_info (IExec->FindTask (NULL));
+
+		if (P_LIKELY (thread_info != NULL)) {
+			if (P_LIKELY (pp_uthread_tls_keys[tls_key].in_use == TRUE))
+				thread_info->tls_values[tls_key] = value;
+		}
+
+		p_mutex_unlock (pp_uthread_glob_mutex);
+	}
+}
+
+P_LIB_API void
+p_uthread_replace_local	(PUThreadKey	*key,
+			 ppointer	value)
+{
+	PUThreadInfo	*thread_info;
+	puthread_key_t	tls_key;
+	ppointer	old_value;
+
+	if (P_UNLIKELY (key == NULL))
+		return;
+
+	tls_key = pp_uthread_get_tls_key (key);
+
+	if (P_UNLIKELY (tls_key == -1))
+		return;
+
+	p_mutex_lock (pp_uthread_glob_mutex);
+
+	if (P_LIKELY (pp_uthread_tls_keys[tls_key].in_use == TRUE)) {
+		thread_info = pp_uthread_find_or_create_thread_info (IExec->FindTask (NULL));
+
+		if (P_LIKELY (thread_info != NULL)) {
+			old_value = thread_info->tls_values[tls_key];
+
+			if (old_value != NULL && key->free_func != NULL)
+				key->free_func (old_value);
+
+			thread_info->tls_values[tls_key] = value;
+		}
+	}
+
+	p_mutex_unlock (pp_uthread_glob_mutex);
+}
diff --git a/3rdparty/plibsys/src/puthread-atheos.c b/3rdparty/plibsys/src/puthread-atheos.c
new file mode 100644
index 0000000..8d33ec5
--- /dev/null
+++ b/3rdparty/plibsys/src/puthread-atheos.c
@@ -0,0 +1,317 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016-2019 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "patomic.h"
+#include "puthread.h"
+#include "puthread-private.h"
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <atheos/threads.h>
+#include <atheos/tld.h>
+
+typedef thread_id puthread_hdl;
+
+struct PUThread_ {
+	PUThreadBase	base;
+	puthread_hdl	hdl;
+};
+
+struct PUThreadKey_ {
+	pint		key;
+	PDestroyFunc	free_func;
+};
+
+static pint pp_uthread_get_atheos_priority (PUThreadPriority prio);
+static pint pp_uthread_get_tls_key (PUThreadKey *key);
+
+static pint
+pp_uthread_get_atheos_priority (PUThreadPriority prio)
+{
+	switch (prio) {
+		case P_UTHREAD_PRIORITY_INHERIT:
+		{
+			thread_info thr_info;
+			
+			memset (&thr_info, 0, sizeof (thr_info));
+
+			if (P_UNLIKELY (get_thread_info (get_thread_id (NULL), &thr_info) != 0)) {
+				P_WARNING ("PUThread::pp_uthread_get_atheos_priority: failed to get thread info");
+				return NORMAL_PRIORITY;
+			} else
+				return thr_info.ti_priority;
+		}
+
+		case P_UTHREAD_PRIORITY_IDLE:
+			return IDLE_PRIORITY;
+		case P_UTHREAD_PRIORITY_LOWEST:
+			return LOW_PRIORITY / 2;
+		case P_UTHREAD_PRIORITY_LOW:
+			return LOW_PRIORITY;
+		case P_UTHREAD_PRIORITY_NORMAL:
+			return NORMAL_PRIORITY;
+		case P_UTHREAD_PRIORITY_HIGH:
+			return DISPLAY_PRIORITY;
+		case P_UTHREAD_PRIORITY_HIGHEST:
+			return URGENT_DISPLAY_PRIORITY;
+		case P_UTHREAD_PRIORITY_TIMECRITICAL:
+			return REALTIME_PRIORITY;
+	}
+}
+
+static pint
+pp_uthread_get_tls_key (PUThreadKey *key)
+{
+	pint thread_key;
+
+	thread_key = p_atomic_int_get ((const volatile pint *) &key->key);
+
+	if (P_LIKELY (thread_key >= 0))
+		return thread_key;
+
+	if (P_UNLIKELY ((thread_key = alloc_tld (key->free_func)) < 0)) {
+		P_ERROR ("PUThread::pp_uthread_get_tls_key: alloc_tld() failed");
+		return -1;
+	}
+
+	if (P_UNLIKELY (p_atomic_int_compare_and_exchange ((volatile pint *) &key->key,
+							   -1,
+							   thread_key) == FALSE)) {
+		if (P_UNLIKELY (free_tld (thread_key) != 0)) {
+			P_ERROR ("PUThread::pp_uthread_get_tls_key: free_tld() failed");
+			return -1;
+		}
+
+		thread_key = key->key;
+	}
+
+	return thread_key;
+}
+
+void
+p_uthread_init_internal (void)
+{
+}
+
+void
+p_uthread_shutdown_internal (void)
+{
+}
+
+void
+p_uthread_win32_thread_detach (void)
+{
+}
+
+PUThread *
+p_uthread_create_internal (PUThreadFunc		func,
+			   pboolean		joinable,
+			   PUThreadPriority	prio,
+			   psize		stack_size)
+{
+	PUThread *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThread))) == NULL)) {
+		P_ERROR ("PUThread::p_uthread_create_internal: failed to allocate memory");
+		return NULL;
+	}
+
+	if (P_UNLIKELY ((ret->hdl = spawn_thread ("",
+						  func,
+						  pp_uthread_get_atheos_priority (prio),
+						  stack_size,
+						  ret)) < 0)) {
+		P_ERROR ("PUThread::p_uthread_create_internal: spawn_thread() failed");
+		p_free (ret);
+		return NULL;
+	}
+
+	if (P_UNLIKELY (resume_thread (ret->hdl) != 0)) {
+		P_ERROR ("PUThread::p_uthread_create_internal: resume_thread() failed");
+		p_free (ret);
+		return NULL;
+	}
+
+	ret->base.joinable = joinable;
+	ret->base.prio     = prio;
+
+	return ret;
+}
+
+void
+p_uthread_exit_internal (void)
+{
+	exit_thread (0);
+}
+
+void
+p_uthread_wait_internal (PUThread *thread)
+{
+	wait_for_thread (thread->hdl);
+}
+
+void
+p_uthread_set_name_internal (PUThread *thread)
+{
+	pchar    *thr_name   = NULL;
+	psize    namelen     = 0;
+	pint     res         = 0;
+	pboolean is_alloc    = FALSE;
+
+	thr_name = thread->base.name;
+	namelen  = strlen (thr_name);
+
+	if (namelen > OS_NAME_LENGTH - 1) {
+		if (P_UNLIKELY ((thr_name = p_malloc0 (namelen + 1)) == NULL)) {
+			P_ERROR ("PUThread::p_uthread_set_name_internal: failed to allocate memory");
+			return;
+		}
+
+		memcpy (thr_name, thread->base.name, OS_NAME_LENGTH - 1);
+
+		is_alloc = TRUE;
+	}
+
+	if (rename_thread (thread->hdl, thr_name) != 0)
+		P_WARNING ("PUThread::p_uthread_set_name_internal: failed to set thread system name");
+
+	if (is_alloc == TRUE)
+		p_free (thr_name);
+}
+
+void
+p_uthread_free_internal (PUThread *thread)
+{
+	p_free (thread);
+}
+
+P_LIB_API void
+p_uthread_yield (void)
+{
+	sched_yield ();
+}
+
+P_LIB_API pboolean
+p_uthread_set_priority (PUThread		*thread,
+			PUThreadPriority	prio)
+{
+	if (P_UNLIKELY (thread == NULL))
+		return FALSE;
+
+	set_thread_priority (thread->hdl, pp_uthread_get_atheos_priority (prio));
+
+	thread->base.prio = prio;
+
+	return TRUE;
+}
+
+P_LIB_API P_HANDLE
+p_uthread_current_id (void)
+{
+	return (P_HANDLE) ((psize) get_thread_id (NULL));
+}
+
+P_LIB_API PUThreadKey *
+p_uthread_local_new (PDestroyFunc free_func)
+{
+	PUThreadKey *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThreadKey))) == NULL)) {
+		P_ERROR ("PUThread::p_uthread_local_new: failed to allocate memory");
+		return NULL;
+	}
+
+	ret->key       = -1;
+	ret->free_func = free_func;
+
+	return ret;
+}
+
+P_LIB_API void
+p_uthread_local_free (PUThreadKey *key)
+{
+	if (P_UNLIKELY (key == NULL))
+		return;
+
+	p_free (key);
+}
+
+P_LIB_API ppointer
+p_uthread_get_local (PUThreadKey *key)
+{
+	pint tls_key;
+
+	if (P_UNLIKELY (key == NULL))
+		return NULL;
+
+	tls_key = pp_uthread_get_tls_key (key);
+
+	if (P_LIKELY (tls_key >= 0))
+		return get_tld (tls_key);
+
+	return NULL;
+}
+
+P_LIB_API void
+p_uthread_set_local (PUThreadKey	*key,
+		     ppointer		value)
+{
+	pint tls_key;
+
+	if (P_UNLIKELY (key == NULL))
+		return;
+
+	tls_key = pp_uthread_get_tls_key (key); 
+
+	if (tls_key >= 0)
+		set_tld (tls_key, value);
+}
+
+P_LIB_API void
+p_uthread_replace_local	(PUThreadKey	*key,
+			 ppointer	value)
+{
+	pint		tls_key;
+	ppointer	old_value;
+
+	if (P_UNLIKELY (key == NULL))
+		return;
+
+	tls_key = pp_uthread_get_tls_key (key);
+
+	if (P_UNLIKELY (tls_key < 0))
+		return;
+
+	old_value = get_tld (tls_key);
+
+	if (old_value != NULL && key->free_func != NULL)
+		key->free_func (old_value);
+
+	set_tld (tls_key, value);
+}
diff --git a/3rdparty/plibsys/src/puthread-beos.c b/3rdparty/plibsys/src/puthread-beos.c
new file mode 100644
index 0000000..1f49747
--- /dev/null
+++ b/3rdparty/plibsys/src/puthread-beos.c
@@ -0,0 +1,431 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016-2019 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "patomic.h"
+#include "pmutex.h"
+#include "puthread.h"
+#include "puthread-private.h"
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <kernel/OS.h>
+#include <kernel/scheduler.h>
+#include <support/TLS.h>
+
+typedef thread_id puthread_hdl;
+
+struct PUThread_ {
+	PUThreadBase	base;
+	puthread_hdl	hdl;
+	PUThreadFunc	proxy;
+};
+
+struct PUThreadKey_ {
+	pint		key;
+	PDestroyFunc	free_func;
+};
+
+typedef struct PUThreadDestructor_ PUThreadDestructor;
+
+struct PUThreadDestructor_ {
+	pint			key_idx;
+	PDestroyFunc		free_func;
+	PUThreadDestructor	*next;
+};
+
+static PUThreadDestructor * volatile pp_uthread_tls_destructors = NULL;
+
+static PMutex *pp_uthread_tls_mutex = NULL;
+
+static pint pp_uthread_get_beos_priority (PUThreadPriority prio);
+static pint pp_uthread_get_tls_key (PUThreadKey *key);
+static void pp_uthread_clean_destructors (void);
+static pint pp_uthread_beos_proxy (ppointer data);
+
+static pint
+pp_uthread_get_beos_priority (PUThreadPriority prio)
+{
+	switch (prio) {
+		case P_UTHREAD_PRIORITY_INHERIT:
+		{
+			thread_info thr_info;
+			
+			memset (&thr_info, 0, sizeof (thr_info));
+
+			if (P_UNLIKELY (get_thread_info (find_thread (NULL), &thr_info) != B_OK)) {
+				P_WARNING ("PUThread::pp_uthread_get_beos_priority: failed to get thread info");
+				return B_NORMAL_PRIORITY;
+			} else
+				return thr_info.priority;
+		}
+
+		case P_UTHREAD_PRIORITY_IDLE:
+			return B_LOW_PRIORITY;
+		case P_UTHREAD_PRIORITY_LOWEST:
+			return B_NORMAL_PRIORITY / 4;
+		case P_UTHREAD_PRIORITY_LOW:
+			return B_NORMAL_PRIORITY / 2;
+		case P_UTHREAD_PRIORITY_NORMAL:
+			return B_NORMAL_PRIORITY;
+		case P_UTHREAD_PRIORITY_HIGH:
+			return B_DISPLAY_PRIORITY;
+		case P_UTHREAD_PRIORITY_HIGHEST:
+			return B_URGENT_DISPLAY_PRIORITY;
+		case P_UTHREAD_PRIORITY_TIMECRITICAL:
+			return B_REAL_TIME_PRIORITY;
+	}
+}
+
+static pint
+pp_uthread_get_tls_key (PUThreadKey *key)
+{
+	pint thread_key;
+
+	thread_key = p_atomic_int_get ((const volatile pint *) &key->key);
+
+	if (P_LIKELY (thread_key >= 0))
+		return thread_key;
+
+	p_mutex_lock (pp_uthread_tls_mutex);
+
+	thread_key = key->key;
+
+	if (P_LIKELY (thread_key == -1)) {
+		PUThreadDestructor *destr = NULL;
+
+		if (key->free_func != NULL) {
+			if (P_UNLIKELY ((destr = p_malloc0 (sizeof (PUThreadDestructor))) == NULL)) {
+				P_ERROR ("PUThread::pp_uthread_get_tls_key: failed to allocate memory");
+				p_mutex_unlock (pp_uthread_tls_mutex);
+				return -1;
+			}
+		}
+
+		if (P_UNLIKELY ((thread_key = tls_allocate ()) < 0)) {
+			P_ERROR ("PUThread::pp_uthread_get_tls_key: tls_allocate() failed");
+			p_free (destr);
+			p_mutex_unlock (pp_uthread_tls_mutex);
+			return -1;
+		}
+
+		if (destr != NULL) {
+			destr->key_idx   = thread_key;
+			destr->free_func = key->free_func;
+			destr->next      = pp_uthread_tls_destructors;
+
+			/* At the same time thread exit could be performed at there is no
+			 * lock for the global destructor list */
+			if (P_UNLIKELY (p_atomic_pointer_compare_and_exchange ((void * volatile *) &pp_uthread_tls_destructors,
+									       (void *) destr->next,
+									       (void *) destr) == FALSE)) {
+				P_ERROR ("PUThread::pp_uthread_get_tls_key: p_atomic_pointer_compare_and_exchange() failed");
+				p_free (destr);
+				p_mutex_unlock (pp_uthread_tls_mutex);
+				return -1;
+			}
+		}
+
+		key->key = thread_key;
+	}
+
+	p_mutex_unlock (pp_uthread_tls_mutex);
+
+	return thread_key;
+}
+
+static void 
+pp_uthread_clean_destructors (void)
+{
+	pboolean was_called;
+
+	do {
+		PUThreadDestructor *destr;
+
+		was_called = FALSE;
+
+		destr = (PUThreadDestructor *) p_atomic_pointer_get ((const void * volatile *) &pp_uthread_tls_destructors);
+
+		while (destr != NULL) {
+			ppointer value;
+
+			value = tls_get (destr->key_idx);
+
+			if (value != NULL && destr->free_func != NULL) {
+				tls_set (destr->key_idx, NULL);
+				destr->free_func (value);
+				was_called = TRUE;
+			}
+
+			destr = destr->next;
+		}
+	} while (was_called);
+}
+
+static pint
+pp_uthread_beos_proxy (ppointer data)
+{
+	PUThread *thread = data;
+
+	thread->proxy (thread);
+
+	pp_uthread_clean_destructors ();
+
+	return 0;
+}
+
+void
+p_uthread_init_internal (void)
+{
+	if (P_LIKELY (pp_uthread_tls_mutex == NULL))
+		pp_uthread_tls_mutex = p_mutex_new ();
+}
+
+void
+p_uthread_shutdown_internal (void)
+{
+	PUThreadDestructor *destr;
+
+	pp_uthread_clean_destructors ();
+
+	destr = pp_uthread_tls_destructors;
+
+	while (destr != NULL) {
+		PUThreadDestructor *next_destr = destr->next;
+
+		p_free (destr);
+		destr = next_destr;
+	}
+
+	pp_uthread_tls_destructors = NULL;
+
+	if (P_LIKELY (pp_uthread_tls_mutex != NULL)) {
+		p_mutex_free (pp_uthread_tls_mutex);
+		pp_uthread_tls_mutex = NULL;
+	}
+}
+
+void
+p_uthread_win32_thread_detach (void)
+{
+}
+
+PUThread *
+p_uthread_create_internal (PUThreadFunc		func,
+			   pboolean		joinable,
+			   PUThreadPriority	prio,
+			   psize		stack_size)
+{
+	PUThread *ret;
+
+	P_UNUSED (stack_size);
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThread))) == NULL)) {
+		P_ERROR ("PUThread::p_uthread_create_internal: failed to allocate memory");
+		return NULL;
+	}
+
+	ret->proxy = func;
+
+	if (P_UNLIKELY ((ret->hdl = spawn_thread ((thread_func) pp_uthread_beos_proxy,
+						  "",
+						  pp_uthread_get_beos_priority (prio),
+						  ret)) < B_OK)) {
+		P_ERROR ("PUThread::p_uthread_create_internal: spawn_thread() failed");
+		p_free (ret);
+		return NULL;
+	}
+
+	if (P_UNLIKELY (resume_thread (ret->hdl) != B_OK)) {
+		P_ERROR ("PUThread::p_uthread_create_internal: resume_thread() failed");
+		p_free (ret);
+		return NULL;
+	}
+
+	ret->base.joinable = joinable;
+	ret->base.prio     = prio;
+
+	return ret;
+}
+
+void
+p_uthread_exit_internal (void)
+{
+	pp_uthread_clean_destructors ();
+	exit_thread (0);
+}
+
+void
+p_uthread_wait_internal (PUThread *thread)
+{
+	status_t exit_value;
+
+	wait_for_thread (thread->hdl, &exit_value);
+}
+
+void
+p_uthread_set_name_internal (PUThread *thread)
+{
+	pchar    *thr_name   = NULL;
+	psize    namelen     = 0;
+	pint     res         = 0;
+	pboolean is_alloc    = FALSE;
+
+	thr_name = thread->base.name;
+	namelen  = strlen (thr_name);
+
+	if (namelen > B_OS_NAME_LENGTH - 1) {
+		if (P_UNLIKELY ((thr_name = p_malloc0 (namelen + 1)) == NULL)) {
+			P_ERROR ("PUThread::p_uthread_set_name_internal: failed to allocate memory");
+			return;
+		}
+
+		memcpy (thr_name, thread->base.name, B_OS_NAME_LENGTH - 1);
+
+		is_alloc = TRUE;
+	}
+
+	if (rename_thread (thread->hdl, thr_name) != 0)
+		P_WARNING ("PUThread::p_uthread_set_name_internal: failed to set thread system name");
+
+	if (is_alloc == TRUE)
+		p_free (thr_name);
+}
+
+void
+p_uthread_free_internal (PUThread *thread)
+{
+	p_free (thread);
+}
+
+P_LIB_API void
+p_uthread_yield (void)
+{
+	snooze ((bigtime_t) 0);
+}
+
+P_LIB_API pboolean
+p_uthread_set_priority (PUThread		*thread,
+			PUThreadPriority	prio)
+{
+	if (P_UNLIKELY (thread == NULL))
+		return FALSE;
+
+	if (set_thread_priority (thread->hdl, pp_uthread_get_beos_priority (prio)) < B_OK) {
+		P_ERROR ("PUThread::p_uthread_create_internal: set_thread_priority() failed");
+		return FALSE;
+	}
+
+	thread->base.prio = prio;
+
+	return TRUE;
+}
+
+P_LIB_API P_HANDLE
+p_uthread_current_id (void)
+{
+	return (P_HANDLE) ((psize) find_thread (NULL));
+}
+
+P_LIB_API PUThreadKey *
+p_uthread_local_new (PDestroyFunc free_func)
+{
+	PUThreadKey *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThreadKey))) == NULL)) {
+		P_ERROR ("PUThread::p_uthread_local_new: failed to allocate memory");
+		return NULL;
+	}
+
+	ret->key       = -1;
+	ret->free_func = free_func;
+
+	return ret;
+}
+
+P_LIB_API void
+p_uthread_local_free (PUThreadKey *key)
+{
+	if (P_UNLIKELY (key == NULL))
+		return;
+
+	p_free (key);
+}
+
+P_LIB_API ppointer
+p_uthread_get_local (PUThreadKey *key)
+{
+	pint tls_key;
+
+	if (P_UNLIKELY (key == NULL))
+		return NULL;
+
+	tls_key = pp_uthread_get_tls_key (key);
+
+	if (P_LIKELY (tls_key >= 0))
+		return tls_get (tls_key);
+
+	return NULL;
+}
+
+P_LIB_API void
+p_uthread_set_local (PUThreadKey	*key,
+		     ppointer		value)
+{
+	pint tls_key;
+
+	if (P_UNLIKELY (key == NULL))
+		return;
+
+	tls_key = pp_uthread_get_tls_key (key); 
+
+	if (tls_key >= 0)
+		tls_set (tls_key, value);
+}
+
+P_LIB_API void
+p_uthread_replace_local	(PUThreadKey	*key,
+			 ppointer	value)
+{
+	pint		tls_key;
+	ppointer	old_value;
+
+	if (P_UNLIKELY (key == NULL))
+		return;
+
+	tls_key = pp_uthread_get_tls_key (key);
+
+	if (P_UNLIKELY (tls_key < 0))
+		return;
+
+	old_value = tls_get (tls_key);
+
+	if (old_value != NULL && key->free_func != NULL)
+		key->free_func (old_value);
+
+	tls_set (tls_key, value);
+}
diff --git a/3rdparty/plibsys/src/puthread-none.c b/3rdparty/plibsys/src/puthread-none.c
new file mode 100644
index 0000000..c2ecc75
--- /dev/null
+++ b/3rdparty/plibsys/src/puthread-none.c
@@ -0,0 +1,142 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2019 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "puthread.h"
+#include "puthread-private.h"
+
+#include <stdlib.h>
+
+struct PUThread_ {
+	pint	hdl;
+};
+
+void
+p_uthread_init_internal (void)
+{
+}
+
+void
+p_uthread_shutdown_internal (void)
+{
+}
+
+void
+p_uthread_win32_thread_detach (void)
+{
+}
+
+PUThread *
+p_uthread_create_internal (PUThreadFunc		func,
+			   pboolean		joinable,
+			   PUThreadPriority	prio,
+			   psize		stack_size)
+{
+	P_UNUSED (func);
+	P_UNUSED (joinable);
+	P_UNUSED (prio);
+	P_UNUSED (stack_size);
+
+	return NULL;
+}
+
+void
+p_uthread_exit_internal (void)
+{
+}
+
+void
+p_uthread_wait_internal (PUThread *thread)
+{
+	P_UNUSED (thread);
+}
+
+void
+p_uthread_set_name_internal (PUThread *thread)
+{
+	P_UNUSED (thread);
+}
+
+void
+p_uthread_free_internal (PUThread *thread)
+{
+	P_UNUSED (thread);
+}
+
+P_LIB_API void
+p_uthread_yield (void)
+{
+}
+
+P_LIB_API pboolean
+p_uthread_set_priority (PUThread		*thread,
+			PUThreadPriority	prio)
+{
+	P_UNUSED (thread);
+	P_UNUSED (prio);
+
+	return FALSE;
+}
+
+P_LIB_API P_HANDLE
+p_uthread_current_id (void)
+{
+	return (P_HANDLE) 0;
+}
+
+P_LIB_API PUThreadKey *
+p_uthread_local_new (PDestroyFunc free_func)
+{
+	P_UNUSED (free_func);
+	return NULL;
+}
+
+P_LIB_API void
+p_uthread_local_free (PUThreadKey *key)
+{
+	P_UNUSED (key);
+}
+
+P_LIB_API ppointer
+p_uthread_get_local (PUThreadKey *key)
+{
+	P_UNUSED (key);
+}
+
+P_LIB_API void
+p_uthread_set_local (PUThreadKey	*key,
+		     ppointer		value)
+{
+	P_UNUSED (key);
+	P_UNUSED (value);
+}
+
+P_LIB_API void
+p_uthread_replace_local	(PUThreadKey	*key,
+			 ppointer	value)
+{
+	P_UNUSED (key);
+	P_UNUSED (value);
+}
diff --git a/3rdparty/plibsys/src/puthread-os2.c b/3rdparty/plibsys/src/puthread-os2.c
new file mode 100644
index 0000000..8bb3731
--- /dev/null
+++ b/3rdparty/plibsys/src/puthread-os2.c
@@ -0,0 +1,458 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2017-2019 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+#define INCL_DOSPROCESS
+#define INCL_DOSERRORS
+#include <os2.h>
+#include <process.h>
+
+#ifdef P_DEBUG
+#  undef P_DEBUG
+#endif
+
+#include "pmem.h"
+#include "patomic.h"
+#include "pmutex.h"
+#include "puthread.h"
+#include "puthread-private.h"
+
+#include <stdlib.h>
+
+typedef TID puthread_hdl;
+
+struct PUThread_ {
+	PUThreadBase	base;
+	puthread_hdl	hdl;
+	PUThreadFunc	proxy;
+};
+
+struct PUThreadKey_ {
+	PULONG		key;
+	PDestroyFunc	free_func;
+};
+
+typedef struct PUThreadDestructor_ PUThreadDestructor;
+
+struct PUThreadDestructor_ {
+	PULONG			key;
+	PDestroyFunc		free_func;
+	PUThreadDestructor	*next;
+};
+
+static PUThreadDestructor * volatile pp_uthread_tls_destructors = NULL;
+static PMutex *pp_uthread_tls_mutex = NULL;
+
+static void pp_uthread_get_os2_priority (PUThreadPriority prio, PULONG thr_class, PLONG thr_level);
+static PULONG pp_uthread_get_tls_key (PUThreadKey *key);
+static void pp_uthread_clean_destructors (void);
+static void pp_uthread_os2_proxy (ppointer data);
+
+static void
+pp_uthread_get_os2_priority (PUThreadPriority prio, PULONG thr_class, PLONG thr_level)
+{
+	switch (prio) {
+		case P_UTHREAD_PRIORITY_INHERIT:
+		{
+			APIRET	ulrc;
+			PTIB	ptib = NULL;
+
+			if (P_UNLIKELY (DosGetInfoBlocks (&ptib, NULL) != NO_ERROR)) {
+				P_WARNING ("PUThread::pp_uthread_get_os2_priority: DosGetInfoBlocks() failed");
+				*thr_class = PRTYC_REGULAR;
+				*thr_level = 0;
+			} else {
+				*thr_class = ((ptib->tib_ptib2->tib2_ulpri) >> 8) & 0x00FF;
+				*thr_level = (ptib->tib_ptib2->tib2_ulpri) & 0x001F;
+			}
+
+			return;
+		}
+		case P_UTHREAD_PRIORITY_IDLE:
+		{
+			*thr_class = PRTYC_IDLETIME;
+			*thr_level = 0;
+
+			return;
+		}
+		case P_UTHREAD_PRIORITY_LOWEST:
+		{
+			*thr_class = PRTYC_REGULAR;
+			*thr_level = PRTYD_MINIMUM;
+
+			return;
+		}
+		case P_UTHREAD_PRIORITY_LOW:
+		{
+			*thr_class = PRTYC_REGULAR;
+			*thr_level = PRTYD_MINIMUM / 2;
+
+			return;
+		}
+		case P_UTHREAD_PRIORITY_NORMAL:
+		{
+			*thr_class = PRTYC_REGULAR;
+			*thr_level = 0;
+
+			return;
+		}
+		case P_UTHREAD_PRIORITY_HIGH:
+		{
+			*thr_class = PRTYC_REGULAR;
+			*thr_level = PRTYD_MAXIMUM / 2;
+
+			return;
+		}
+		case P_UTHREAD_PRIORITY_HIGHEST:
+		{
+			*thr_class = PRTYC_REGULAR;
+			*thr_level = PRTYD_MAXIMUM;
+
+			return;
+		}
+		case P_UTHREAD_PRIORITY_TIMECRITICAL:
+		{
+			*thr_class = PRTYC_TIMECRITICAL;
+			*thr_level = 0;
+
+			return;
+		}
+	}
+}
+
+static PULONG
+pp_uthread_get_tls_key (PUThreadKey *key)
+{
+	PULONG thread_key;
+
+	thread_key = (PULONG) p_atomic_pointer_get ((ppointer) &key->key);
+
+	if (P_LIKELY (thread_key != NULL))
+		return thread_key;
+
+	p_mutex_lock (pp_uthread_tls_mutex);
+
+	thread_key = key->key;
+
+	if (P_LIKELY (thread_key == NULL)) {
+		PUThreadDestructor *destr = NULL;
+
+		if (key->free_func != NULL) {
+			if (P_UNLIKELY ((destr = p_malloc0 (sizeof (PUThreadDestructor))) == NULL)) {
+				P_ERROR ("PUThread::pp_uthread_get_tls_key: failed to allocate memory");
+				p_mutex_unlock (pp_uthread_tls_mutex);
+				return NULL;
+			}
+		}
+
+		if (P_UNLIKELY (DosAllocThreadLocalMemory (1, &thread_key) != NO_ERROR)) {
+			P_ERROR ("PUThread::pp_uthread_get_tls_key: DosAllocThreadLocalMemory() failed");
+			p_free (destr);
+			p_mutex_unlock (pp_uthread_tls_mutex);
+			return NULL;
+		}
+
+		if (destr != NULL) {
+			destr->key       = thread_key;
+			destr->free_func = key->free_func;
+			destr->next      = pp_uthread_tls_destructors;
+
+			/* At the same time thread exit could be performed at there is no
+			 * lock for the global destructor list */
+			if (P_UNLIKELY (p_atomic_pointer_compare_and_exchange ((void * volatile *) &pp_uthread_tls_destructors,
+									       (void *) destr->next,
+									       (void *) destr) == FALSE)) {
+				P_ERROR ("PUThread::pp_uthread_get_tls_key: p_atomic_pointer_compare_and_exchange() failed");
+
+				if (P_UNLIKELY (DosFreeThreadLocalMemory (thread_key) != NO_ERROR))
+					P_ERROR ("PUThread::pp_uthread_get_tls_key: DosFreeThreadLocalMemory() failed");
+
+				p_free (destr);
+				p_mutex_unlock (pp_uthread_tls_mutex);
+				return NULL;
+			}
+		}
+
+		key->key = thread_key;
+	}
+
+	p_mutex_unlock (pp_uthread_tls_mutex);
+
+	return thread_key;
+}
+
+static void 
+pp_uthread_clean_destructors (void)
+{
+	pboolean was_called;
+
+	do {
+		PUThreadDestructor *destr;
+
+		was_called = FALSE;
+
+		destr = (PUThreadDestructor *) p_atomic_pointer_get ((const void * volatile *) &pp_uthread_tls_destructors);
+
+		while (destr != NULL) {
+			PULONG value;
+
+			value = destr->key;
+
+			if (value != NULL && ((ppointer) *value) != NULL && destr->free_func != NULL) {
+				*destr->key = (ULONG) NULL;
+				destr->free_func ((ppointer) *value);
+				was_called = TRUE;
+			}
+
+			destr = destr->next;
+		}
+	} while (was_called);
+}
+
+static void
+pp_uthread_os2_proxy (ppointer data)
+{
+	PUThread *thread = data;
+
+	thread->proxy (thread);
+
+	pp_uthread_clean_destructors ();
+}
+
+void
+p_uthread_init_internal (void)
+{
+	if (P_LIKELY (pp_uthread_tls_mutex == NULL))
+		pp_uthread_tls_mutex = p_mutex_new ();
+}
+
+void
+p_uthread_shutdown_internal (void)
+{
+	PUThreadDestructor *destr;
+
+	pp_uthread_clean_destructors ();
+
+	destr = pp_uthread_tls_destructors;
+
+	while (destr != NULL) {
+		PUThreadDestructor *next_destr = destr->next;
+
+		p_free (destr);
+		destr = next_destr;
+	}
+
+	pp_uthread_tls_destructors = NULL;
+
+	if (P_LIKELY (pp_uthread_tls_mutex != NULL)) {
+		p_mutex_free (pp_uthread_tls_mutex);
+		pp_uthread_tls_mutex = NULL;
+	}
+}
+
+void
+p_uthread_win32_thread_detach (void)
+{
+}
+
+PUThread *
+p_uthread_create_internal (PUThreadFunc		func,
+			   pboolean		joinable,
+			   PUThreadPriority	prio,
+			   psize		stack_size)
+{
+	PUThread *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThread))) == NULL)) {
+		P_ERROR ("PUThread::p_uthread_create_internal: failed to allocate memory");
+		return NULL;
+	}
+
+	ret->base.joinable = joinable;
+	ret->proxy         = func;
+
+	if (P_UNLIKELY ((ret->hdl = _beginthread ((void (*) (void *)) pp_uthread_os2_proxy,
+						  NULL,
+						  (puint) stack_size,
+						  ret)) <= 0)) {
+		P_ERROR ("PUThread::p_uthread_create_internal: _beginthread() failed");
+		p_free (ret);
+		return NULL;
+	}
+
+	ret->base.prio = P_UTHREAD_PRIORITY_INHERIT;
+	p_uthread_set_priority (ret, prio);
+
+	return ret;
+}
+
+void
+p_uthread_exit_internal (void)
+{
+	pp_uthread_clean_destructors ();
+	_endthread ();
+}
+
+void
+p_uthread_wait_internal (PUThread *thread)
+{
+	APIRET ulrc;
+
+	while ((ulrc = DosWaitThread (&thread->hdl, DCWW_WAIT)) == ERROR_INTERRUPT)
+		;
+
+	if (P_UNLIKELY (ulrc != NO_ERROR && ulrc != ERROR_INVALID_THREADID))
+		P_ERROR ("PUThread::p_uthread_wait_internal: DosWaitThread() failed");
+}
+
+void
+p_uthread_set_name_internal (PUThread *thread)
+{
+	P_UNUSED (thread);
+}
+
+void
+p_uthread_free_internal (PUThread *thread)
+{
+	p_free (thread);
+}
+
+P_LIB_API void
+p_uthread_yield (void)
+{
+	DosSleep (0);
+}
+
+P_LIB_API pboolean
+p_uthread_set_priority (PUThread		*thread,
+			PUThreadPriority	prio)
+{
+	APIRET	ulrc;
+	PTIB	ptib = NULL;
+	LONG	cur_level;
+	LONG	new_level;
+	ULONG	new_class;
+
+	if (P_UNLIKELY (thread == NULL))
+		return FALSE;
+
+	if (P_UNLIKELY (DosGetInfoBlocks (&ptib, NULL) != NO_ERROR)) {
+		P_WARNING ("PUThread::p_uthread_set_priority: DosGetInfoBlocks() failed");
+		return FALSE;
+	}
+
+	cur_level = (ptib->tib_ptib2->tib2_ulpri) & 0x001F;
+
+	pp_uthread_get_os2_priority (prio, &new_class, &new_level);
+
+	if (P_UNLIKELY (DosSetPriority (PRTYS_THREAD, new_class, new_level - cur_level, 0) != NO_ERROR)) {
+		P_WARNING ("PUThread::p_uthread_set_priority: DosSetPriority() failed");
+		return FALSE;
+	}
+
+	thread->base.prio = prio;
+	return TRUE;
+}
+
+P_LIB_API P_HANDLE
+p_uthread_current_id (void)
+{
+	return (P_HANDLE) (_gettid ());
+}
+
+P_LIB_API PUThreadKey *
+p_uthread_local_new (PDestroyFunc free_func)
+{
+	PUThreadKey *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThreadKey))) == NULL)) {
+		P_ERROR ("PUThread::p_uthread_local_new: failed to allocate memory");
+		return NULL;
+	}
+
+	ret->free_func = free_func;
+
+	return ret;
+}
+
+P_LIB_API void
+p_uthread_local_free (PUThreadKey *key)
+{
+	if (P_UNLIKELY (key == NULL))
+		return;
+
+	p_free (key);
+}
+
+P_LIB_API ppointer
+p_uthread_get_local (PUThreadKey *key)
+{
+	PULONG tls_key;
+
+	if (P_UNLIKELY (key == NULL))
+		return NULL;
+
+	if (P_UNLIKELY ((tls_key = pp_uthread_get_tls_key (key)) == NULL))
+		return NULL;
+
+	return (ppointer) *tls_key;
+}
+
+P_LIB_API void
+p_uthread_set_local (PUThreadKey	*key,
+		     ppointer		value)
+{
+	PULONG tls_key;
+
+	if (P_UNLIKELY (key == NULL))
+		return;
+
+	tls_key = pp_uthread_get_tls_key (key);
+
+	if (P_LIKELY (tls_key != NULL))
+		*tls_key = (ULONG) value;
+}
+
+P_LIB_API void
+p_uthread_replace_local	(PUThreadKey	*key,
+			 ppointer	value)
+{
+	PULONG		tls_key;
+	ppointer	old_value;
+
+	if (P_UNLIKELY (key == NULL))
+		return;
+
+	tls_key = pp_uthread_get_tls_key (key);
+
+	if (P_UNLIKELY (tls_key == NULL))
+		return;
+
+	old_value = (ppointer) *tls_key;
+
+	if (old_value != NULL && key->free_func != NULL)
+		key->free_func (old_value);
+
+	*tls_key = (ULONG) value;
+}
diff --git a/3rdparty/plibsys/src/puthread-posix.c b/3rdparty/plibsys/src/puthread-posix.c
new file mode 100644
index 0000000..90e14d5
--- /dev/null
+++ b/3rdparty/plibsys/src/puthread-posix.c
@@ -0,0 +1,580 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2019 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "patomic.h"
+#include "puthread.h"
+#include "puthread-private.h"
+
+#ifdef P_OS_SCO
+#  include "pmutex.h"
+#endif
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <pthread.h>
+#include <time.h>
+
+/* On some OS like OpenBSD it must follow <pthread.h> */
+#ifdef PLIBSYS_NEED_PTHREAD_NP_H
+#  include <pthread_np.h>
+#endif
+
+#ifdef PLIBSYS_HAS_POSIX_SCHEDULING
+#  ifndef P_OS_VMS
+#    include <sched.h>
+#  endif
+#endif
+
+#ifdef PLIBSYS_HAS_PTHREAD_PRCTL
+#  include <sys/prctl.h>
+#  include <linux/prctl.h>
+#endif
+
+#ifdef P_OS_QNX6
+#  include <sys/neutrino.h>
+#endif
+
+#ifdef P_OS_HAIKU
+#  include <kernel/OS.h>
+#endif
+
+/* Some systems without native pthreads may lack some of the constants,
+ * leave them zero as we are not going to use them anyway */
+
+#ifndef PTHREAD_CREATE_JOINABLE
+#  define PTHREAD_CREATE_JOINABLE	0
+#endif
+
+#ifndef PTHREAD_CREATE_DETACHED
+#  define PTHREAD_CREATE_DETACHED	0
+#endif
+
+#ifdef PLIBSYS_HAS_POSIX_SCHEDULING
+#  ifndef PTHREAD_INHERIT_SCHED
+#    define PTHREAD_INHERIT_SCHED	0
+#  endif
+
+#  ifndef PTHREAD_EXPLICIT_SCHED
+#    define PTHREAD_EXPLICIT_SCHED	0
+#  endif
+
+/* Old Linux kernels may lack a definition */
+#  if defined (P_OS_LINUX) && !defined (SCHED_IDLE)
+#    define SCHED_IDLE 5
+#  endif
+#endif
+
+/* Max length of a thead name */
+#if defined(P_OS_LINUX)
+#  define PUTHREAD_MAX_NAME		16
+#elif defined(P_OS_NETBSD)
+#  define PUTHREAD_MAX_NAME		PTHREAD_MAX_NAMELEN_NP
+#elif defined(P_OS_DRAGONFLY)
+#  define PUTHREAD_MAX_NAME		16
+#elif defined(P_OS_SOLARIS)
+#  define PUTHREAD_MAX_NAME		32
+#elif defined(P_OS_TRU64)
+#  define PUTHREAD_MAX_NAME		32
+#elif defined(P_OS_VMS)
+#  define PUTHREAD_MAX_NAME		32
+#elif defined(P_OS_QNX6)
+#  define PUTHREAD_MAX_NAME		_NTO_THREAD_NAME_MAX
+#elif defined(P_OS_HAIKU)
+#  define PUTHREAD_MAX_NAME		32
+#endif
+
+typedef pthread_t puthread_hdl;
+
+struct PUThread_ {
+	PUThreadBase	base;
+	puthread_hdl	hdl;
+};
+
+struct PUThreadKey_ {
+	pthread_key_t	*key;
+	PDestroyFunc	free_func;
+};
+
+#ifdef P_OS_SCO
+static PMutex *pp_uthread_tls_mutex = NULL;
+#endif
+
+#ifdef PLIBSYS_HAS_POSIX_SCHEDULING
+static pboolean pp_uthread_get_unix_priority (PUThreadPriority prio, int *sched_policy, int *sched_priority);
+#endif
+
+static pthread_key_t * pp_uthread_get_tls_key (PUThreadKey *key);
+
+#ifdef PLIBSYS_HAS_POSIX_SCHEDULING
+static pboolean
+pp_uthread_get_unix_priority (PUThreadPriority prio, int *sched_policy, int *sched_priority)
+{
+	pint	lowBound, upperBound;
+	pint	prio_min, prio_max;
+	pint	native_prio;
+
+#ifdef SCHED_IDLE
+	if (prio == P_UTHREAD_PRIORITY_IDLE) {
+		*sched_policy = SCHED_IDLE;
+		*sched_priority = 0;
+		return TRUE;
+	}
+
+	lowBound = (pint) P_UTHREAD_PRIORITY_LOWEST;
+#else
+	lowBound = (pint) P_UTHREAD_PRIORITY_IDLE;
+#endif
+	upperBound = (pint) P_UTHREAD_PRIORITY_TIMECRITICAL;
+
+	prio_min = sched_get_priority_min (*sched_policy);
+	prio_max = sched_get_priority_max (*sched_policy);
+
+	if (P_UNLIKELY (prio_min == -1 || prio_max == -1))
+		return FALSE;
+
+	native_prio = ((pint) prio - lowBound) * (prio_max - prio_min) / upperBound + prio_min;
+
+	if (P_UNLIKELY (native_prio > prio_max))
+		native_prio = prio_max;
+
+	if (P_UNLIKELY (native_prio < prio_min))
+		native_prio = prio_min;
+
+	*sched_priority = native_prio;
+
+	return TRUE;
+}
+#endif
+
+static pthread_key_t *
+pp_uthread_get_tls_key (PUThreadKey *key)
+{
+	pthread_key_t *thread_key;
+
+	thread_key = (pthread_key_t *) p_atomic_pointer_get ((ppointer) &key->key);
+
+	if (P_LIKELY (thread_key != NULL))
+		return thread_key;
+
+#ifdef P_OS_SCO
+	p_mutex_lock (pp_uthread_tls_mutex);
+
+	thread_key = key->key;
+
+	if (P_LIKELY (thread_key == NULL)) {
+#endif
+		if (P_UNLIKELY ((thread_key = p_malloc0 (sizeof (pthread_key_t))) == NULL)) {
+			P_ERROR ("PUThread::pp_uthread_get_tls_key: failed to allocate memory");
+#ifdef P_OS_SCO
+			p_mutex_unlock (pp_uthread_tls_mutex);
+#endif
+			return NULL;
+		}
+
+		if (P_UNLIKELY (pthread_key_create (thread_key, key->free_func) != 0)) {
+			P_ERROR ("PUThread::pp_uthread_get_tls_key: pthread_key_create() failed");
+			p_free (thread_key);
+#ifdef P_OS_SCO
+			p_mutex_unlock (pp_uthread_tls_mutex);
+#endif
+			return NULL;
+		}
+
+#ifndef P_OS_SCO
+		if (P_UNLIKELY (p_atomic_pointer_compare_and_exchange ((ppointer) &key->key,
+								       NULL,
+								       (ppointer) thread_key) == FALSE)) {
+			if (P_UNLIKELY (pthread_key_delete (*thread_key) != 0)) {
+				P_ERROR ("PUThread::pp_uthread_get_tls_key: pthread_key_delete() failed");
+				p_free (thread_key);
+				return NULL;
+			}
+
+			p_free (thread_key);
+
+			thread_key = key->key;
+		}
+#else
+		key->key = thread_key;
+	}
+
+	p_mutex_unlock (pp_uthread_tls_mutex);
+#endif
+
+	return thread_key;
+}
+
+void
+p_uthread_init_internal (void)
+{
+#ifdef P_OS_SCO
+	if (P_LIKELY (pp_uthread_tls_mutex == NULL))
+		pp_uthread_tls_mutex = p_mutex_new ();
+#endif
+}
+
+void
+p_uthread_shutdown_internal (void)
+{
+#ifdef P_OS_SCO
+	if (P_LIKELY (pp_uthread_tls_mutex != NULL)) {
+		p_mutex_free (pp_uthread_tls_mutex);
+		pp_uthread_tls_mutex = NULL;
+	}
+#endif
+}
+
+void
+p_uthread_win32_thread_detach (void)
+{
+}
+
+PUThread *
+p_uthread_create_internal (PUThreadFunc		func,
+			   pboolean		joinable,
+			   PUThreadPriority	prio,
+			   psize		stack_size)
+{
+	PUThread		*ret;
+	pthread_attr_t		attr;
+	pint			create_code;
+#ifdef PLIBSYS_HAS_POSIX_SCHEDULING
+	struct sched_param	sched;
+	pint			native_prio;
+	pint			sched_policy;
+#endif
+
+#if defined (PLIBSYS_HAS_POSIX_STACKSIZE) && defined (_SC_THREAD_STACK_MIN)
+	plong			min_stack;
+#endif
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThread))) == NULL)) {
+		P_ERROR ("PUThread::p_uthread_create_internal: failed to allocate memory");
+		return NULL;
+	}
+
+	ret->base.joinable = joinable;
+
+	if (P_UNLIKELY (pthread_attr_init (&attr) != 0)) {
+		P_ERROR ("PUThread::p_uthread_create_internal: pthread_attr_init() failed");
+		p_free (ret);
+		return NULL;
+	}
+
+	if (P_UNLIKELY (pthread_attr_setdetachstate (&attr,
+						     joinable ? PTHREAD_CREATE_JOINABLE
+							      : PTHREAD_CREATE_DETACHED) != 0)) {
+		P_ERROR ("PUThread::p_uthread_create_internal: pthread_attr_setdetachstate() failed");
+		pthread_attr_destroy (&attr);
+		p_free (ret);
+		return NULL;
+	}
+
+#ifdef PLIBSYS_HAS_POSIX_SCHEDULING
+	if (prio == P_UTHREAD_PRIORITY_INHERIT) {
+		if (P_UNLIKELY (pthread_attr_setinheritsched (&attr, PTHREAD_INHERIT_SCHED) != 0))
+			P_WARNING ("PUThread::p_uthread_create_internal: pthread_attr_setinheritsched() failed");
+	} else {
+		if (P_LIKELY (pthread_attr_getschedpolicy (&attr, &sched_policy) == 0)) {
+			if (P_LIKELY (pp_uthread_get_unix_priority (prio,
+								     &sched_policy,
+								     &native_prio) == TRUE)) {
+				memset (&sched, 0, sizeof (sched));
+				sched.sched_priority = native_prio;
+
+				if (P_LIKELY (pthread_attr_setinheritsched (&attr, PTHREAD_EXPLICIT_SCHED) != 0 ||
+					      pthread_attr_setschedpolicy (&attr, sched_policy) != 0 ||
+					      pthread_attr_setschedparam (&attr, &sched) != 0))
+					P_WARNING ("PUThread::p_uthread_create_internal: failed to set priority");
+			} else
+				P_WARNING ("PUThread::p_uthread_create_internal: pp_uthread_get_unix_priority() failed");
+		} else
+			P_WARNING ("PUThread::p_uthread_create_internal: pthread_attr_getschedpolicy() failed");
+	}
+#endif
+
+#ifdef PLIBSYS_HAS_POSIX_STACKSIZE
+#  ifdef _SC_THREAD_STACK_MIN
+	if (stack_size > 0) {
+		min_stack = (plong) sysconf (_SC_THREAD_STACK_MIN);
+
+		if (P_LIKELY (min_stack > 0)) {
+			if (P_UNLIKELY (stack_size < (psize) min_stack))
+				stack_size = (psize) min_stack;
+		} else
+			P_WARNING ("PUThread::p_uthread_create_internal: sysconf() with _SC_THREAD_STACK_MIN failed");
+
+		if (P_UNLIKELY (pthread_attr_setstacksize (&attr, stack_size) != 0))
+			P_WARNING ("PUThread::p_uthread_create_internal: pthread_attr_setstacksize() failed");
+	}
+#  endif
+#endif
+
+	create_code = pthread_create (&ret->hdl, &attr, func, ret);
+
+#ifdef EPERM
+	if (create_code == EPERM) {
+#  ifdef PLIBSYS_HAS_POSIX_SCHEDULING
+		pthread_attr_setinheritsched (&attr, PTHREAD_INHERIT_SCHED);
+#  endif
+		create_code = pthread_create (&ret->hdl, &attr, func, ret);
+	}
+#endif
+
+	if (P_UNLIKELY (create_code != 0)) {
+		P_ERROR ("PUThread::p_uthread_create_internal: pthread_create() failed");
+		pthread_attr_destroy (&attr);
+		p_free (ret);
+		return NULL;
+	}
+
+	ret->base.prio = prio;
+	pthread_attr_destroy (&attr);
+
+	return ret;
+}
+
+void
+p_uthread_exit_internal (void)
+{
+	pthread_exit (P_INT_TO_POINTER (0));
+}
+
+void
+p_uthread_wait_internal (PUThread *thread)
+{
+	if (P_UNLIKELY (pthread_join (thread->hdl, NULL) != 0))
+		P_ERROR ("PUThread::p_uthread_wait_internal: pthread_join() failed");
+}
+
+void
+p_uthread_set_name_internal (PUThread *thread)
+{
+	pchar    *thr_name = NULL;
+	pint     res       = 0;
+	pboolean is_alloc  = FALSE;
+#ifdef PUTHREAD_MAX_NAME
+	psize    namelen   = 0;
+#endif
+
+	thr_name = thread->base.name;
+
+#ifdef PUTHREAD_MAX_NAME
+	namelen  = strlen (thr_name);
+
+	if (namelen > PUTHREAD_MAX_NAME - 1) {
+		if (P_UNLIKELY ((thr_name = p_malloc0 (namelen + 1)) == NULL)) {
+			P_ERROR ("PUThread::p_uthread_set_name_internal: failed to allocate memory");
+			return;
+		}
+
+		memcpy (thr_name, thread->base.name, PUTHREAD_MAX_NAME - 1);
+
+		is_alloc = TRUE;
+	}
+#endif
+
+#if defined(PLIBSYS_HAS_PTHREAD_SETNAME)
+#  if defined(P_OS_MAC)
+	if (thread->hdl != pthread_self ())
+		P_WARNING ("PUThread::p_uthread_set_name_internal: only calling thread is supported in macOS");
+	else
+		res = pthread_setname_np (thr_name);
+#  elif defined(P_OS_NETBSD)
+	res = pthread_setname_np (thread->hdl, "%s", thr_name);
+#  elif defined(P_OS_TRU64) || defined(P_OS_VMS)
+	res = pthread_setname_np (thread->hdl, thr_name, 0);
+#  else
+	res = pthread_setname_np (thread->hdl, thr_name);
+#  endif
+#elif defined(PLIBSYS_HAS_PTHREAD_SET_NAME)
+	pthread_set_name_np (thread->hdl, thr_name);
+#elif defined(PLIBSYS_HAS_PTHREAD_PRCTL)
+	if (thread->hdl != pthread_self ())
+		P_WARNING ("PUThread::p_uthread_set_name_internal: prctl() can be used on calling thread only");
+	else
+		res = prctl (PR_SET_NAME, thr_name, NULL, NULL, NULL);
+#elif defined(P_OS_HAIKU)
+	res = rename_thread (find_thread (NULL), thr_name);
+#endif
+
+	if (is_alloc == TRUE)
+		p_free (thr_name);
+
+	if (res != 0)
+		P_WARNING ("PUThread::p_uthread_set_name_internal: failed to set thread system name");
+}
+
+void
+p_uthread_free_internal (PUThread *thread)
+{
+	p_free (thread);
+}
+
+P_LIB_API void
+p_uthread_yield (void)
+{
+	sched_yield ();
+}
+
+P_LIB_API pboolean
+p_uthread_set_priority (PUThread		*thread,
+			PUThreadPriority	prio)
+{
+#ifdef PLIBSYS_HAS_POSIX_SCHEDULING
+	struct sched_param	sched;
+	pint			policy;
+	pint			native_prio;
+#endif
+
+	if (P_UNLIKELY (thread == NULL))
+		return FALSE;
+
+#ifdef PLIBSYS_HAS_POSIX_SCHEDULING
+	if (P_UNLIKELY (pthread_getschedparam (thread->hdl, &policy, &sched) != 0)) {
+		P_ERROR ("PUThread::p_uthread_set_priority: pthread_getschedparam() failed");
+		return FALSE;
+	}
+
+	if (P_UNLIKELY (pp_uthread_get_unix_priority (prio, &policy, &native_prio) == FALSE)) {
+		P_ERROR ("PUThread::p_uthread_set_priority: pp_uthread_get_unix_priority() failed");
+		return FALSE;
+	}
+
+	memset (&sched, 0, sizeof (sched));
+	sched.sched_priority = native_prio;
+
+	if (P_UNLIKELY (pthread_setschedparam (thread->hdl, policy, &sched) != 0)) {
+		P_ERROR ("PUThread::p_uthread_set_priority: pthread_setschedparam() failed");
+		return FALSE;
+	}
+#endif
+
+	thread->base.prio = prio;
+	return TRUE;
+}
+
+P_LIB_API P_HANDLE
+p_uthread_current_id (void)
+{
+	return (P_HANDLE) ((psize) pthread_self ());
+}
+
+P_LIB_API PUThreadKey *
+p_uthread_local_new (PDestroyFunc free_func)
+{
+	PUThreadKey *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThreadKey))) == NULL)) {
+		P_ERROR ("PUThread::p_uthread_local_new: failed to allocate memory");
+		return NULL;
+	}
+
+	ret->free_func = free_func;
+
+	return ret;
+}
+
+P_LIB_API void
+p_uthread_local_free (PUThreadKey *key)
+{
+	if (P_UNLIKELY (key == NULL))
+		return;
+
+	p_free (key);
+}
+
+P_LIB_API ppointer
+p_uthread_get_local (PUThreadKey *key)
+{
+	pthread_key_t	*tls_key;
+#ifdef P_OS_SCO
+	ppointer	value;
+#endif
+
+	if (P_UNLIKELY (key == NULL))
+		return NULL;
+
+	if (P_UNLIKELY ((tls_key = pp_uthread_get_tls_key (key)) == NULL))
+		return NULL;
+
+#ifdef P_OS_SCO
+	if (P_UNLIKELY (pthread_getspecific (*tls_key, &value) != 0))
+		return NULL;
+
+	return value;
+#else
+	return pthread_getspecific (*tls_key);
+#endif
+}
+
+P_LIB_API void
+p_uthread_set_local (PUThreadKey	*key,
+		     ppointer		value)
+{
+	pthread_key_t *tls_key;
+
+	if (P_UNLIKELY (key == NULL))
+		return;
+
+	tls_key = pp_uthread_get_tls_key (key);
+
+	if (P_LIKELY (tls_key != NULL)) {
+		if (P_UNLIKELY (pthread_setspecific (*tls_key, value) != 0))
+			P_ERROR ("PUThread::p_uthread_set_local: pthread_setspecific() failed");
+	}
+}
+
+P_LIB_API void
+p_uthread_replace_local	(PUThreadKey	*key,
+			 ppointer	value)
+{
+	pthread_key_t	*tls_key;
+	ppointer	old_value;
+
+	if (P_UNLIKELY (key == NULL))
+		return;
+
+	tls_key = pp_uthread_get_tls_key (key);
+
+	if (P_UNLIKELY (tls_key == NULL))
+		return;
+
+#ifdef P_OS_SCO
+	if (P_UNLIKELY (pthread_getspecific (*tls_key, &old_value) != 0))
+		return;
+#else
+	old_value = pthread_getspecific (*tls_key);
+#endif
+
+	if (old_value != NULL && key->free_func != NULL)
+		key->free_func (old_value);
+
+	if (P_UNLIKELY (pthread_setspecific (*tls_key, value) != 0))
+		P_ERROR ("PUThread::p_uthread_replace_local: pthread_setspecific() failed");
+}
diff --git a/3rdparty/plibsys/src/puthread-private.h b/3rdparty/plibsys/src/puthread-private.h
new file mode 100644
index 0000000..9b389d8
--- /dev/null
+++ b/3rdparty/plibsys/src/puthread-private.h
@@ -0,0 +1,53 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2016-2019 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PUTHREAD_PRIVATE_H
+#define PLIBSYS_HEADER_PUTHREAD_PRIVATE_H
+
+#include "pmacros.h"
+#include "ptypes.h"
+#include "puthread.h"
+
+P_BEGIN_DECLS
+
+/** Base thread structure */
+typedef struct PUThreadBase_ {
+	pint			ref_count;	/**< Reference counter.	*/
+	pint			ret_code;	/**< Return code.	*/
+	pboolean		ours;		/**< Our thread flag.	*/
+	pboolean		joinable;	/**< Joinable flag.	*/
+	PUThreadFunc		func;		/**< Thread routine.	*/
+	ppointer		data;		/**< Thread input data.	*/
+	PUThreadPriority	prio;		/**< Thread priority.	*/
+	pchar			*name;		/**< Thread name	*/
+} PUThreadBase;
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PUTHREAD_PRIVATE_H */
diff --git a/3rdparty/plibsys/src/puthread-solaris.c b/3rdparty/plibsys/src/puthread-solaris.c
new file mode 100644
index 0000000..80f9f7b
--- /dev/null
+++ b/3rdparty/plibsys/src/puthread-solaris.c
@@ -0,0 +1,352 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2019 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "patomic.h"
+#include "puthread.h"
+#include "puthread-private.h"
+
+#ifndef P_OS_UNIXWARE
+#  include "pmutex.h"
+#endif
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <thread.h>
+
+#ifdef P_OS_UNIXWARE
+#  define PLIBSYS_THREAD_MIN_PRIO 0
+#  define PLIBSYS_THREAD_MAX_PRIO 126
+#else
+#  define PLIBSYS_THREAD_MIN_PRIO 0
+#  define PLIBSYS_THREAD_MAX_PRIO 127 
+#endif
+
+typedef thread_t puthread_hdl;
+
+struct PUThread_ {
+	PUThreadBase	base;
+	puthread_hdl	hdl;
+};
+
+struct PUThreadKey_ {
+	thread_key_t	*key;
+	PDestroyFunc	free_func;
+};
+
+#ifndef P_OS_UNIXWARE
+static PMutex *pp_uthread_tls_mutex = NULL;
+#endif
+
+static pint pp_uthread_get_unix_priority (PUThreadPriority prio);
+static thread_key_t * pp_uthread_get_tls_key (PUThreadKey *key);
+
+static pint
+pp_uthread_get_unix_priority (PUThreadPriority prio)
+{
+	pint lowBound, upperBound;
+
+	lowBound   = (pint) P_UTHREAD_PRIORITY_IDLE;
+	upperBound = (pint) P_UTHREAD_PRIORITY_TIMECRITICAL;
+
+	return ((pint) prio - lowBound) *
+	       (PLIBSYS_THREAD_MAX_PRIO - PLIBSYS_THREAD_MIN_PRIO) / upperBound +
+	       PLIBSYS_THREAD_MIN_PRIO;
+}
+
+static thread_key_t *
+pp_uthread_get_tls_key (PUThreadKey *key)
+{
+	thread_key_t *thread_key;
+
+	thread_key = (thread_key_t *) p_atomic_pointer_get ((ppointer) &key->key);
+
+	if (P_LIKELY (thread_key != NULL))
+		return thread_key;
+
+#ifndef P_OS_UNIXWARE
+	p_mutex_lock (pp_uthread_tls_mutex);
+
+	thread_key = key->key;
+
+	if (P_LIKELY (thread_key == NULL)) {
+#endif
+		if (P_UNLIKELY ((thread_key = p_malloc0 (sizeof (thread_key_t))) == NULL)) {
+			P_ERROR ("PUThread::pp_uthread_get_tls_key: failed to allocate memory");
+#ifndef P_OS_UNIXWARE
+			p_mutex_unlock (pp_uthread_tls_mutex);
+#endif
+			return NULL;
+		}
+
+		if (P_UNLIKELY (thr_keycreate (thread_key, key->free_func) != 0)) {
+			P_ERROR ("PUThread::pp_uthread_get_tls_key: thr_keycreate() failed");
+			p_free (thread_key);
+#ifndef P_OS_UNIXWARE
+			p_mutex_unlock (pp_uthread_tls_mutex);
+#endif
+			return NULL;
+		}
+#ifdef P_OS_UNIXWARE
+		if (P_UNLIKELY (p_atomic_pointer_compare_and_exchange ((ppointer) &key->key,
+								       NULL,
+								       (ppointer) thread_key) == FALSE)) {
+			if (P_UNLIKELY (thr_keydelete (*thread_key) != 0)) {
+				P_ERROR ("PUThread::pp_uthread_get_tls_key: thr_keydelete() failed");
+				p_free (thread_key);
+				return NULL;
+			}
+
+			p_free (thread_key);
+
+			thread_key = key->key;
+		}
+#else
+		key->key = thread_key;
+	}
+
+	p_mutex_unlock (pp_uthread_tls_mutex);
+#endif
+
+	return thread_key;
+}
+
+void
+p_uthread_init_internal (void)
+{
+#ifndef P_OS_UNIXWARE
+	if (P_LIKELY (pp_uthread_tls_mutex == NULL))
+		pp_uthread_tls_mutex = p_mutex_new ();
+#endif
+}
+
+void
+p_uthread_shutdown_internal (void)
+{
+#ifndef P_OS_UNIXWARE
+	if (P_LIKELY (pp_uthread_tls_mutex != NULL)) {
+		p_mutex_free (pp_uthread_tls_mutex);
+		pp_uthread_tls_mutex = NULL;
+	}
+#endif
+}
+
+void
+p_uthread_win32_thread_detach (void)
+{
+}
+
+PUThread *
+p_uthread_create_internal (PUThreadFunc		func,
+			   pboolean		joinable,
+			   PUThreadPriority	prio,
+			   psize		stack_size)
+{
+	PUThread	*ret;
+	pint32		flags;
+	psize		min_stack;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThread))) == NULL)) {
+		P_ERROR ("PUThread::p_uthread_create_internal: failed to allocate memory");
+		return NULL;
+	}
+
+	if (stack_size > 0) {
+#ifdef P_OS_UNIXWARE
+		min_stack = thr_minstack ();	
+#else
+		min_stack = thr_min_stack ();
+#endif
+
+		if (P_UNLIKELY (stack_size < min_stack))
+			stack_size = min_stack;
+	}
+
+	flags = THR_SUSPENDED;
+	flags |= joinable ? 0 : THR_DETACHED;
+
+	if (P_UNLIKELY (thr_create (NULL, stack_size, func, ret, flags, &ret->hdl) != 0)) {
+		P_ERROR ("PUThread::p_uthread_create_internal: thr_create() failed");
+		p_free (ret);
+		return NULL;
+	}
+
+	if (P_UNLIKELY (thr_setprio (ret->hdl, pp_uthread_get_unix_priority (prio)) != 0))
+		P_WARNING ("PUThread::p_uthread_create_internal: thr_setprio() failed");
+
+	if (P_UNLIKELY (thr_continue (ret->hdl) != 0)) {
+		P_ERROR ("PUThread::p_uthread_create_internal: thr_continue() failed");
+		p_free (ret);
+		return NULL;
+	}
+
+	ret->base.joinable = joinable;
+	ret->base.prio     = prio;
+
+	return ret;
+}
+
+void
+p_uthread_exit_internal (void)
+{
+	thr_exit (P_INT_TO_POINTER (0));
+}
+
+void
+p_uthread_wait_internal (PUThread *thread)
+{
+	if (P_UNLIKELY (thr_join (thread->hdl, NULL, NULL) != 0))
+		P_ERROR ("PUThread::p_uthread_wait_internal: thr_join() failed");
+}
+
+void
+p_uthread_set_name_internal (PUThread *thread)
+{
+	P_UNUSED (thread);
+}
+
+void
+p_uthread_free_internal (PUThread *thread)
+{
+	p_free (thread);
+}
+
+P_LIB_API void
+p_uthread_yield (void)
+{
+	thr_yield ();
+}
+
+P_LIB_API pboolean
+p_uthread_set_priority (PUThread		*thread,
+			PUThreadPriority	prio)
+{
+	if (P_UNLIKELY (thread == NULL))
+		return FALSE;
+
+	if (P_UNLIKELY (thr_setprio (thread->hdl, pp_uthread_get_unix_priority (prio)) != 0)) {
+		P_WARNING ("PUThread::p_uthread_set_priority: thr_setprio() failed");
+		return FALSE;
+	}
+
+	thread->base.prio = prio;
+
+	return TRUE;
+}
+
+P_LIB_API P_HANDLE
+p_uthread_current_id (void)
+{
+	return (P_HANDLE) ((psize) thr_self ());
+}
+
+P_LIB_API PUThreadKey *
+p_uthread_local_new (PDestroyFunc free_func)
+{
+	PUThreadKey *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThreadKey))) == NULL)) {
+		P_ERROR ("PUThread::p_uthread_local_new: failed to allocate memory");
+		return NULL;
+	}
+
+	ret->free_func = free_func;
+
+	return ret;
+}
+
+P_LIB_API void
+p_uthread_local_free (PUThreadKey *key)
+{
+	if (P_UNLIKELY (key == NULL))
+		return;
+
+	p_free (key);
+}
+
+P_LIB_API ppointer
+p_uthread_get_local (PUThreadKey *key)
+{
+	thread_key_t	*tls_key;
+	ppointer	ret = NULL;
+
+	if (P_UNLIKELY (key == NULL))
+		return ret;
+
+	tls_key = pp_uthread_get_tls_key (key);
+
+	if (P_LIKELY (tls_key != NULL)) {
+		if (P_UNLIKELY (thr_getspecific (*tls_key, &ret) != 0))
+			P_ERROR ("PUThread::p_uthread_get_local: thr_getspecific() failed");
+	}
+
+	return ret;
+}
+
+P_LIB_API void
+p_uthread_set_local (PUThreadKey	*key,
+		     ppointer		value)
+{
+	thread_key_t *tls_key;
+
+	if (P_UNLIKELY (key == NULL))
+		return;
+
+	tls_key = pp_uthread_get_tls_key (key);
+
+	if (P_LIKELY (tls_key != NULL)) {
+		if (P_UNLIKELY (thr_setspecific (*tls_key, value) != 0))
+			P_ERROR ("PUThread::p_uthread_set_local: thr_setspecific() failed");
+	}
+}
+
+P_LIB_API void
+p_uthread_replace_local	(PUThreadKey	*key,
+			 ppointer	value)
+{
+	thread_key_t	*tls_key;
+	ppointer	old_value = NULL;
+
+	if (P_UNLIKELY (key == NULL))
+		return;
+
+	tls_key = pp_uthread_get_tls_key (key);
+
+	if (P_UNLIKELY (tls_key == NULL))
+		return;
+
+	if (P_UNLIKELY (thr_getspecific (*tls_key, &old_value) != 0)) {
+		P_ERROR ("PUThread::p_uthread_replace_local: thr_getspecific() failed");
+		return;
+	}
+
+	if (old_value != NULL && key->free_func != NULL)
+		key->free_func (old_value);
+
+	if (P_UNLIKELY (thr_setspecific (*tls_key, value) != 0))
+		P_ERROR ("PUThread::p_uthread_replace_local: thr_setspecific() failed");
+}
diff --git a/3rdparty/plibsys/src/puthread-win.c b/3rdparty/plibsys/src/puthread-win.c
new file mode 100644
index 0000000..57604fd
--- /dev/null
+++ b/3rdparty/plibsys/src/puthread-win.c
@@ -0,0 +1,511 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2019 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "pmem.h"
+#include "pmutex.h"
+#include "patomic.h"
+#include "puthread.h"
+#include "puthread-private.h"
+
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+
+#include <process.h>
+
+typedef HRESULT (WINAPI * PWin32SetThreadDescription) (HANDLE hThread, PCWSTR lpThreadDescription);
+typedef HANDLE puthread_hdl;
+
+struct PUThread_ {
+	PUThreadBase		base;
+	puthread_hdl		hdl;
+	PUThreadFunc		proxy;
+};
+
+struct PUThreadKey_ {
+	DWORD			key_idx;
+	PDestroyFunc		free_func;
+};
+
+typedef struct PUThreadDestructor_ PUThreadDestructor;
+
+struct PUThreadDestructor_ {
+	DWORD			key_idx;
+	PDestroyFunc		free_func;
+	PUThreadDestructor	*next;
+};
+
+/*
+ * For thread names:
+ * https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code
+ */
+
+const DWORD MS_VC_THREAD_NAME_EXCEPTION = 0x406D1388;
+
+#pragma pack(push, 8)
+typedef struct tagTHREADNAME_INFO
+{
+	DWORD  dwType;     /* Must be 0x1000.                        */
+	LPCSTR szName;     /* Pointer to name (in user addr space).  */
+	DWORD  dwThreadID; /* Thread ID (-1 = caller thread).        */
+	DWORD  dwFlags;    /* Reserved for future use, must be zero. */
+} THREADNAME_INFO;
+#pragma pack(pop)
+
+#ifndef P_CC_MSVC
+static void *pp_uthread_name_veh_handle = NULL;
+
+static LONG __stdcall
+pp_uthread_set_thread_name_veh (PEXCEPTION_POINTERS except_info)
+{
+	if (except_info->ExceptionRecord != NULL &&
+	    except_info->ExceptionRecord->ExceptionCode == MS_VC_THREAD_NAME_EXCEPTION)
+		return EXCEPTION_CONTINUE_EXECUTION;
+
+	return EXCEPTION_CONTINUE_SEARCH;
+}
+#endif
+
+/* Rest of definitions */
+
+static PWin32SetThreadDescription     pp_uthread_set_descr_func  = NULL;
+static PUThreadDestructor * volatile  pp_uthread_tls_destructors = NULL;
+static PMutex                        *pp_uthread_tls_mutex       = NULL;
+
+static DWORD pp_uthread_get_tls_key (PUThreadKey *key);
+static puint __stdcall pp_uthread_win32_proxy (ppointer data);
+
+static DWORD
+pp_uthread_get_tls_key (PUThreadKey *key)
+{
+	DWORD tls_key = key->key_idx;
+
+	if (P_LIKELY (tls_key != TLS_OUT_OF_INDEXES))
+		return tls_key;
+
+	p_mutex_lock (pp_uthread_tls_mutex);
+
+	tls_key = key->key_idx;
+
+	if (P_LIKELY (tls_key == TLS_OUT_OF_INDEXES)) {
+		PUThreadDestructor *destr = NULL;
+
+		tls_key = TlsAlloc ();
+
+		if (P_UNLIKELY (tls_key == TLS_OUT_OF_INDEXES)) {
+			P_ERROR ("PUThread::pp_uthread_get_tls_key: TlsAlloc() failed");
+			p_mutex_unlock (pp_uthread_tls_mutex);
+			return TLS_OUT_OF_INDEXES;
+		}
+
+		if (key->free_func != NULL) {
+			if (P_UNLIKELY ((destr = p_malloc0 (sizeof (PUThreadDestructor))) == NULL)) {
+				P_ERROR ("PUThread::pp_uthread_get_tls_key: failed to allocate memory");
+
+				if (P_UNLIKELY (TlsFree (tls_key) == 0))
+					P_ERROR ("PUThread::pp_uthread_get_tls_key: TlsFree() failed(1)");
+
+				p_mutex_unlock (pp_uthread_tls_mutex);
+				return TLS_OUT_OF_INDEXES;
+			}
+
+			destr->key_idx   = tls_key;
+			destr->free_func = key->free_func;
+			destr->next      = pp_uthread_tls_destructors;
+
+			/* At the same time thread exit could be performed at there is no
+			 * lock for the global destructor list */
+			if (P_UNLIKELY (p_atomic_pointer_compare_and_exchange ((PVOID volatile *) &pp_uthread_tls_destructors,
+									       (PVOID) destr->next,
+									       (PVOID) destr) == FALSE)) {
+				P_ERROR ("PUThread::pp_uthread_get_tls_key: p_atomic_pointer_compare_and_exchange() failed");
+
+				if (P_UNLIKELY (TlsFree (tls_key) == 0))
+					P_ERROR ("PUThread::pp_uthread_get_tls_key: TlsFree() failed(2)");
+
+				p_free (destr);
+
+				p_mutex_unlock (pp_uthread_tls_mutex);
+				return TLS_OUT_OF_INDEXES;
+			}
+		}
+
+		key->key_idx = tls_key;
+	}
+
+	p_mutex_unlock (pp_uthread_tls_mutex);
+
+	return tls_key;
+}
+
+static puint __stdcall
+pp_uthread_win32_proxy (ppointer data)
+{
+	PUThread *thread = data;
+
+	thread->proxy (thread);
+
+	_endthreadex (0);
+
+	return 0;
+}
+
+void
+p_uthread_win32_thread_detach (void)
+{
+	pboolean was_called;
+
+	do {
+		PUThreadDestructor *destr;
+
+		was_called = FALSE;
+
+		destr = (PUThreadDestructor *) p_atomic_pointer_get ((const PVOID volatile *) &pp_uthread_tls_destructors);
+
+		while (destr != NULL) {
+			ppointer value;
+
+			value = TlsGetValue (destr->key_idx);
+
+			if (value != NULL && destr->free_func != NULL) {
+				TlsSetValue (destr->key_idx, NULL);
+				destr->free_func (value);
+				was_called = TRUE;
+			}
+
+			destr = destr->next;
+		}
+	} while (was_called);
+}
+
+void
+p_uthread_init_internal (void)
+{
+	HMODULE hmodule;
+
+	if (P_LIKELY (pp_uthread_tls_mutex == NULL))
+		pp_uthread_tls_mutex = p_mutex_new ();
+
+	hmodule = GetModuleHandleA ("kernel32.dll");
+
+	if (P_UNLIKELY (hmodule == NULL)) {
+		P_ERROR ("PUThread::p_uthread_init_internal: failed to load kernel32.dll module");
+		return;
+	}
+
+	pp_uthread_set_descr_func = (PWin32SetThreadDescription) GetProcAddress (hmodule, "SetThreadDescription");
+
+#ifndef P_CC_MSVC
+	pp_uthread_name_veh_handle = AddVectoredExceptionHandler (1, &pp_uthread_set_thread_name_veh);
+#endif
+}
+
+void
+p_uthread_shutdown_internal (void)
+{
+	PUThreadDestructor *destr;
+
+	p_uthread_win32_thread_detach ();
+
+	destr = pp_uthread_tls_destructors;
+
+	while (destr != NULL) {
+		PUThreadDestructor *next_destr = destr->next;
+
+		TlsFree (destr->key_idx);
+		p_free (destr);
+
+		destr = next_destr;
+	}
+
+	pp_uthread_tls_destructors = NULL;
+
+	if (P_LIKELY (pp_uthread_tls_mutex != NULL)) {
+		p_mutex_free (pp_uthread_tls_mutex);
+		pp_uthread_tls_mutex = NULL;
+	}
+
+	pp_uthread_set_descr_func = NULL;
+
+#ifndef P_CC_MSVC
+	if (pp_uthread_name_veh_handle != NULL) {
+		RemoveVectoredExceptionHandler (pp_uthread_name_veh_handle);
+		pp_uthread_name_veh_handle = NULL;
+	}
+#endif
+}
+
+PUThread *
+p_uthread_create_internal (PUThreadFunc		func,
+			   pboolean		joinable,
+			   PUThreadPriority	prio,
+			   psize		stack_size)
+{
+	PUThread *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThread))) == NULL)) {
+		P_ERROR ("PUThread::p_uthread_create_internal: failed to allocate memory");
+		return NULL;
+	}
+
+	ret->proxy = func;
+
+	if (P_UNLIKELY ((ret->hdl = (HANDLE) _beginthreadex (NULL,
+							     (puint) stack_size,
+							     pp_uthread_win32_proxy,
+							     ret,
+							     CREATE_SUSPENDED,
+							     NULL)) == NULL)) {
+		P_ERROR ("PUThread::p_uthread_create_internal: _beginthreadex() failed");
+		p_free (ret);
+		return NULL;
+	}
+
+	ret->base.joinable = joinable;
+
+	p_uthread_set_priority (ret, prio);
+
+	if (P_UNLIKELY (ResumeThread (ret->hdl) == (DWORD) -1)) {
+		P_ERROR ("PUThread::p_uthread_create_internal: ResumeThread() failed");
+		CloseHandle (ret->hdl);
+		p_free (ret);
+	}
+
+	return ret;
+}
+
+void
+p_uthread_exit_internal (void)
+{
+	_endthreadex (0);
+}
+
+void
+p_uthread_wait_internal (PUThread *thread)
+{
+	if (P_UNLIKELY ((WaitForSingleObject (thread->hdl, INFINITE)) != WAIT_OBJECT_0))
+		P_ERROR ("PUThread::p_uthread_wait_internal: WaitForSingleObject() failed");
+}
+
+void
+p_uthread_set_name_internal (PUThread *thread)
+{
+	wchar_t		*thr_wname = NULL;
+	psize		namelen    = 0;
+	HRESULT		hres;
+	THREADNAME_INFO	thr_info;
+
+	if (pp_uthread_set_descr_func != NULL) {
+		namelen = strlen (thread->base.name);
+
+		if (P_UNLIKELY ((thr_wname = p_malloc0 (sizeof (wchar_t) * (namelen + 1))) == NULL)) {
+			P_ERROR ("PUThread::p_uthread_set_name_internal: failed to allocate memory");
+			return;
+		}
+
+		mbstowcs (thr_wname, thread->base.name, namelen + 1);
+
+		hres = pp_uthread_set_descr_func (thread->hdl, thr_wname);
+
+		p_free (thr_wname);
+
+		if (P_UNLIKELY (FAILED (hres))) {
+			P_ERROR ("PUThread::p_uthread_set_name_internal: failed to set thread description");
+			return;
+		}
+	}
+
+	if (!IsDebuggerPresent ())
+		return;
+
+	thr_info.dwType     = 0x1000;
+	thr_info.szName     = thread->base.name;
+	thr_info.dwThreadID = -1;
+	thr_info.dwFlags    = 0;
+
+#ifdef P_CC_MSVC
+#  pragma warning(push)
+#  pragma warning(disable: 6320 6322)
+	__try {
+		RaiseException (MS_VC_THREAD_NAME_EXCEPTION,
+				0,
+				sizeof (thr_info) / sizeof (ULONG_PTR),
+				(ULONG_PTR *) &thr_info);
+	}
+	__except (EXCEPTION_EXECUTE_HANDLER) {}
+#  pragma warning(pop)
+#else
+	if (pp_uthread_name_veh_handle != NULL)
+		RaiseException (MS_VC_THREAD_NAME_EXCEPTION,
+				0,
+				sizeof (thr_info) / sizeof (ULONG_PTR),
+				(ULONG_PTR *) &thr_info);
+#endif
+}
+
+void
+p_uthread_free_internal (PUThread *thread)
+{
+	CloseHandle (thread->hdl);
+	p_free (thread);
+}
+
+P_LIB_API void
+p_uthread_yield (void)
+{
+	Sleep (0);
+}
+
+P_LIB_API pboolean
+p_uthread_set_priority (PUThread		*thread,
+			PUThreadPriority	prio)
+{
+	pint native_prio;
+
+	if (P_UNLIKELY (thread == NULL))
+		return FALSE;
+
+	switch (prio) {
+	case P_UTHREAD_PRIORITY_IDLE:
+		native_prio = THREAD_PRIORITY_IDLE;
+		break;
+	case P_UTHREAD_PRIORITY_LOWEST:
+		native_prio = THREAD_PRIORITY_LOWEST;
+		break;
+	case P_UTHREAD_PRIORITY_LOW:
+		native_prio = THREAD_PRIORITY_BELOW_NORMAL;
+		break;
+	case P_UTHREAD_PRIORITY_NORMAL:
+		native_prio = THREAD_PRIORITY_NORMAL;
+		break;
+	case P_UTHREAD_PRIORITY_HIGH:
+		native_prio = THREAD_PRIORITY_ABOVE_NORMAL;
+		break;
+	case P_UTHREAD_PRIORITY_HIGHEST:
+		native_prio = THREAD_PRIORITY_HIGHEST;
+		break;
+	case P_UTHREAD_PRIORITY_TIMECRITICAL:
+		native_prio = THREAD_PRIORITY_TIME_CRITICAL;
+		break;
+	case P_UTHREAD_PRIORITY_INHERIT:
+	default:
+		native_prio = GetThreadPriority (GetCurrentThread ());
+		break;
+	}
+
+	if (P_UNLIKELY (SetThreadPriority (thread->hdl, native_prio) == 0)) {
+		P_ERROR ("PUThread::p_uthread_set_priority: SetThreadPriority() failed");
+		return FALSE;
+	}
+
+	thread->base.prio = prio;
+
+	return TRUE;
+}
+
+P_LIB_API P_HANDLE
+p_uthread_current_id (void)
+{
+	return (P_HANDLE) ((psize) GetCurrentThreadId ());
+}
+
+P_LIB_API PUThreadKey *
+p_uthread_local_new (PDestroyFunc free_func)
+{
+	PUThreadKey *ret;
+
+	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThreadKey))) == NULL)) {
+		P_ERROR ("PUThread::p_uthread_local_new: failed to allocate memory");
+		return NULL;
+	}
+
+	ret->key_idx   = TLS_OUT_OF_INDEXES;
+	ret->free_func = free_func;
+
+	return ret;
+}
+
+P_LIB_API void
+p_uthread_local_free (PUThreadKey *key)
+{
+	if (P_UNLIKELY (key == NULL))
+		return;
+
+	p_free (key);
+}
+
+P_LIB_API ppointer
+p_uthread_get_local (PUThreadKey *key)
+{
+	DWORD tls_idx;
+
+	if (P_UNLIKELY (key == NULL))
+		return NULL;
+
+	tls_idx = pp_uthread_get_tls_key (key);
+
+	return tls_idx == TLS_OUT_OF_INDEXES ? NULL : TlsGetValue (tls_idx);
+}
+
+P_LIB_API void
+p_uthread_set_local (PUThreadKey	*key,
+		     ppointer		value)
+{
+	DWORD tls_idx;
+
+	if (P_UNLIKELY (key == NULL))
+		return;
+
+	tls_idx = pp_uthread_get_tls_key (key);
+
+	if (P_LIKELY (tls_idx != TLS_OUT_OF_INDEXES)) {
+		if (P_UNLIKELY (TlsSetValue (tls_idx, value) == 0))
+			P_ERROR ("PUThread::p_uthread_set_local: TlsSetValue() failed");
+	}
+}
+
+P_LIB_API void
+p_uthread_replace_local	(PUThreadKey	*key,
+			 ppointer	value)
+{
+	DWORD		tls_idx;
+	ppointer	old_value;
+
+	if (P_UNLIKELY (key == NULL))
+		return;
+
+	tls_idx = pp_uthread_get_tls_key (key);
+
+	if (P_UNLIKELY (tls_idx == TLS_OUT_OF_INDEXES))
+		return;
+
+	old_value = TlsGetValue (tls_idx);
+
+	if (old_value != NULL && key->free_func != NULL)
+		key->free_func (old_value);
+
+	if (P_UNLIKELY (TlsSetValue (tls_idx, value) == 0))
+		P_ERROR ("PUThread::p_uthread_replace_local: TlsSetValue() failed");
+}
diff --git a/3rdparty/plibsys/src/puthread.c b/3rdparty/plibsys/src/puthread.c
new file mode 100644
index 0000000..ad72e78
--- /dev/null
+++ b/3rdparty/plibsys/src/puthread.c
@@ -0,0 +1,558 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2019 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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 "patomic.h"
+#ifndef P_OS_WIN
+#  include "perror.h"
+#endif
+#include "pmem.h"
+#include "pspinlock.h"
+#include "pstring.h"
+#include "puthread.h"
+#include "puthread-private.h"
+
+#ifdef P_OS_OS2
+#  define INCL_DOSPROCESS
+#  define INCL_DOSERRORS
+#  define INCL_DOSMISC
+#  include <os2.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef P_OS_WIN
+#  include <unistd.h>
+#endif
+
+#ifdef P_OS_WIN
+typedef void (WINAPI * SystemInfoFunc) (LPSYSTEM_INFO);
+#endif
+
+#ifdef P_OS_HPUX
+#  include <sys/pstat.h>
+#endif
+
+#ifdef P_OS_BSD4
+#  include <sys/param.h>
+#  include <sys/types.h>
+#  include <sys/sysctl.h>
+#endif
+
+#ifdef P_OS_VMS
+#  define __NEW_STARLET 1
+#  include <starlet.h>
+#  include <ssdef.h>
+#  include <stsdef.h>
+#  include <efndef.h>
+#  include <iledef.h>
+#  include <iosbdef.h>
+#  include <syidef.h>
+#  include <tis.h>
+#  include <lib$routines.h>
+#endif
+
+#ifdef P_OS_QNX6
+#  include <sys/syspage.h>
+#endif
+
+#ifdef P_OS_BEOS
+#  include <kernel/OS.h>
+#endif
+
+#ifdef P_OS_SYLLABLE
+#  include <atheos/sysinfo.h>
+#endif
+
+#if defined (P_OS_SCO) && !defined (_SC_NPROCESSORS_ONLN)
+#  include <sys/utsname.h>
+#endif
+
+#ifdef P_OS_AMIGA
+#  include <clib/alib_protos.h>
+#  include <proto/dos.h>
+#  include <proto/exec.h>
+#endif
+
+extern void p_uthread_init_internal (void);
+extern void p_uthread_shutdown_internal (void);
+extern void p_uthread_exit_internal (void);
+extern void p_uthread_wait_internal (PUThread *thread);
+extern void p_uthread_free_internal (PUThread *thread);
+extern void p_uthread_set_name_internal (PUThread *thread);
+extern PUThread * p_uthread_create_internal (PUThreadFunc	func,
+					     pboolean		joinable,
+					     PUThreadPriority	prio,
+					     psize		stack_size);
+
+static void pp_uthread_cleanup (ppointer data);
+static ppointer pp_uthread_proxy (ppointer data);
+
+#ifndef P_OS_WIN
+#  if !defined (PLIBSYS_HAS_CLOCKNANOSLEEP) && !defined (PLIBSYS_HAS_NANOSLEEP)
+static pint pp_uthread_nanosleep (puint32 msec);
+#  endif
+#endif
+
+static PUThreadKey * pp_uthread_specific_data = NULL;
+static PSpinLock * pp_uthread_new_spin = NULL;
+
+static void
+pp_uthread_cleanup (ppointer data)
+{
+	p_uthread_unref (data);
+}
+
+static ppointer
+pp_uthread_proxy (ppointer data)
+{
+	PUThreadBase *base_thread = data;
+
+	p_uthread_set_local (pp_uthread_specific_data, data);
+
+	p_spinlock_lock (pp_uthread_new_spin);
+	p_spinlock_unlock (pp_uthread_new_spin);
+
+	if (base_thread->name != NULL)
+		p_uthread_set_name_internal ((PUThread *) base_thread);
+
+	base_thread->func (base_thread->data);
+
+	return NULL;
+}
+
+void
+p_uthread_init (void)
+{
+	if (P_LIKELY (pp_uthread_specific_data == NULL))
+		pp_uthread_specific_data = p_uthread_local_new ((PDestroyFunc) pp_uthread_cleanup);
+
+	if (P_LIKELY (pp_uthread_new_spin == NULL))
+		pp_uthread_new_spin = p_spinlock_new ();
+
+	p_uthread_init_internal ();
+}
+
+void
+p_uthread_shutdown (void)
+{
+	PUThread *cur_thread;
+
+	if (P_LIKELY (pp_uthread_specific_data != NULL)) {
+		cur_thread = p_uthread_get_local (pp_uthread_specific_data);
+
+		if (P_UNLIKELY (cur_thread != NULL)) {
+			p_uthread_unref (cur_thread);
+			p_uthread_set_local (pp_uthread_specific_data, NULL);
+		}
+
+		p_uthread_local_free (pp_uthread_specific_data);
+		pp_uthread_specific_data = NULL;
+	}
+
+	if (P_LIKELY (pp_uthread_new_spin != NULL)) {
+		p_spinlock_free (pp_uthread_new_spin);
+		pp_uthread_new_spin = NULL;
+	}
+
+	p_uthread_shutdown_internal ();
+}
+
+P_LIB_API PUThread *
+p_uthread_create_full (PUThreadFunc	func,
+		       ppointer		data,
+		       pboolean		joinable,
+		       PUThreadPriority	prio,
+		       psize		stack_size,
+		       const pchar	*name)
+{
+	PUThreadBase *base_thread;
+
+	if (P_UNLIKELY (func == NULL))
+		return NULL;
+
+	p_spinlock_lock (pp_uthread_new_spin);
+
+	base_thread = (PUThreadBase *) p_uthread_create_internal (pp_uthread_proxy,
+								  joinable,
+								  prio,
+								  stack_size);
+
+	if (P_LIKELY (base_thread != NULL)) {
+		base_thread->ref_count = 2;
+		base_thread->ours      = TRUE;
+		base_thread->joinable  = joinable;
+		base_thread->func      = func;
+		base_thread->data      = data;
+		base_thread->name      = p_strdup (name);
+	}
+
+	p_spinlock_unlock (pp_uthread_new_spin);
+
+	return (PUThread *) base_thread;
+}
+
+P_LIB_API PUThread *
+p_uthread_create (PUThreadFunc	func,
+		  ppointer	data,
+		  pboolean	joinable,
+		  const pchar	*name)
+{
+	/* All checks will be inside */
+	return p_uthread_create_full (func, data, joinable, P_UTHREAD_PRIORITY_INHERIT, 0, name);
+}
+
+P_LIB_API void
+p_uthread_exit (pint code)
+{
+	PUThreadBase *base_thread = (PUThreadBase *) p_uthread_current ();
+
+	if (P_UNLIKELY (base_thread == NULL))
+		return;
+
+	if (P_UNLIKELY (base_thread->ours == FALSE)) {
+		P_WARNING ("PUThread::p_uthread_exit: p_uthread_exit() cannot be called from an unknown thread");
+		return;
+	}
+
+	base_thread->ret_code = code;
+
+	p_uthread_exit_internal ();
+}
+
+P_LIB_API pint
+p_uthread_join (PUThread *thread)
+{
+	PUThreadBase *base_thread;
+
+	if (P_UNLIKELY (thread == NULL))
+		return -1;
+
+	base_thread = (PUThreadBase *) thread;
+
+	if (base_thread->joinable == FALSE)
+		return -1;
+
+	p_uthread_wait_internal (thread);
+
+	return base_thread->ret_code;
+}
+
+P_LIB_API PUThread *
+p_uthread_current (void)
+{
+	PUThreadBase *base_thread = p_uthread_get_local (pp_uthread_specific_data);
+
+	if (P_UNLIKELY (base_thread == NULL)) {
+		if (P_UNLIKELY ((base_thread = p_malloc0 (sizeof (PUThreadBase))) == NULL)) {
+			P_ERROR ("PUThread::p_uthread_current: failed to allocate memory");
+			return NULL;
+		}
+
+		base_thread->ref_count = 1;
+
+		p_uthread_set_local (pp_uthread_specific_data, base_thread);
+	}
+
+	return (PUThread *) base_thread;
+}
+
+P_LIB_API pint
+p_uthread_ideal_count (void)
+{
+#if defined (P_OS_WIN)
+	SYSTEM_INFO	sys_info;
+	SystemInfoFunc	sys_info_func;
+
+	sys_info_func = (SystemInfoFunc) GetProcAddress (GetModuleHandleA ("kernel32.dll"),
+							 "GetNativeSystemInfo");
+
+	if (P_UNLIKELY (sys_info_func == NULL))
+		sys_info_func = (SystemInfoFunc) GetProcAddress (GetModuleHandleA ("kernel32.dll"),
+								 "GetSystemInfo");
+
+	if (P_UNLIKELY (sys_info_func == NULL)) {
+		P_ERROR ("PUThread::p_uthread_ideal_count: failed to get address of system info procedure");
+		return 1;
+	}
+
+	sys_info_func (&sys_info);
+
+	return (pint) sys_info.dwNumberOfProcessors;
+#elif defined (P_OS_HPUX)
+	struct pst_dynamic psd;
+
+	if (P_LIKELY (pstat_getdynamic (&psd, sizeof (psd), 1, 0) != -1))
+		return (pint) psd.psd_proc_cnt;
+	else {
+		P_WARNING ("PUThread::p_uthread_ideal_count: failed to call pstat_getdynamic()");
+		return 1;
+	}
+#elif defined (P_OS_IRIX)
+	pint cores;
+
+	cores = sysconf (_SC_NPROC_ONLN);
+
+	if (P_UNLIKELY (cores < 0)) {
+		P_WARNING ("PUThread::p_uthread_ideal_count: failed to call sysconf(_SC_NPROC_ONLN)");
+		cores = 1;
+	}
+
+	return cores;
+#elif defined (P_OS_BSD4)
+	pint	cores;
+	pint	mib[2];
+	size_t	len = sizeof (cores);
+
+	mib[0] = CTL_HW;
+	mib[1] = HW_NCPU;
+
+	if (P_UNLIKELY (sysctl (mib, 2, &cores, &len, NULL, 0) == -1)) {
+		P_WARNING ("PUThread::p_uthread_ideal_count: failed to call sysctl()");
+		return 1;
+	}
+
+	return (pint) cores;
+#elif defined (P_OS_VMS)
+	pint	cores;
+	pint	status;
+	puint	efn;
+	IOSB	iosb;
+#  if (PLIBSYS_SIZEOF_VOID_P == 4)
+	ILE3	itmlst[] = { { sizeof (cores), SYI$_AVAILCPU_CNT, &cores, NULL},
+			     { 0, 0, NULL, NULL}
+			   };
+#  else
+	ILEB_64	itmlst[] = { { 1, SYI$_AVAILCPU_CNT, -1, sizeof (cores), &cores, NULL},
+			     { 0, 0, 0, 0, NULL, NULL}
+			   };
+#  endif
+
+	status = lib$get_ef (&efn);
+
+	if (P_UNLIKELY (!$VMS_STATUS_SUCCESS (status))) {
+		P_WARNING ("PUThread::p_uthread_ideal_count: failed to call lib$get_ef()");
+		return 1;
+	}
+
+	status = sys$getsyi (efn, NULL, NULL, itmlst, &iosb, tis_io_complete, 0);
+
+	if (P_UNLIKELY (!$VMS_STATUS_SUCCESS (status))) {
+		P_WARNING ("PUThread::p_uthread_ideal_count: failed to call sys$getsyiw()");
+		lib$free_ef (&efn);
+		return 1;
+	}
+
+	status = tis_synch (efn, &iosb);
+
+	if (P_UNLIKELY (!$VMS_STATUS_SUCCESS (status))) {
+		P_WARNING ("PUThread::p_uthread_ideal_count: failed to call tis_synch()");
+		lib$free_ef (&efn);
+		return 1;
+	}
+
+	if (P_UNLIKELY (iosb.iosb$l_getxxi_status != SS$_NORMAL)) {
+		P_WARNING ("PUThread::p_uthread_ideal_count: l_getxxi_status is not normal");
+		lib$free_ef (&efn);
+		return 1;
+	}
+
+	lib$free_ef (&efn);
+
+	return cores;
+#elif defined (P_OS_OS2)
+	APIRET	ulrc;
+	ULONG	cores;
+
+	if (P_UNLIKELY (DosQuerySysInfo (QSV_NUMPROCESSORS,
+					 QSV_NUMPROCESSORS,
+					 &cores,
+					 sizeof (cores)) != NO_ERROR)) {
+		P_WARNING ("PUThread::p_uthread_ideal_count: failed to call DosQuerySysInfo()");
+		return 1;
+	}
+
+	return (pint) cores;
+#elif defined (P_OS_QNX6)
+	return (pint) _syspage_ptr->num_cpu;
+#elif defined (P_OS_BEOS)
+	system_info sys_info;
+
+	get_system_info (&sys_info);
+
+	return (pint) sys_info.cpu_count;
+#elif defined (P_OS_SYLLABLE)
+	system_info sys_info;
+
+	if (P_UNLIKELY (get_system_info_v (&sys_info, SYS_INFO_VERSION) != 0)) {
+		P_WARNING ("PUThread::p_uthread_ideal_count: failed to call get_system_info_v()");
+		return 1;
+	}
+
+	return (pint) sys_info.nCPUCount;
+#elif defined (P_OS_AMIGA)
+	puint32 cores;
+
+	IExec->GetCPUInfoTags (GCIT_NumberOfCPUs, &cores, TAG_END);
+
+	return (pint) cores;
+#elif defined (P_OS_SCO) && !defined (_SC_NPROCESSORS_ONLN)
+	struct scoutsname utsn;
+
+	if (P_UNLIKELY (__scoinfo (&utsn, sizeof (utsn)) == -1)) {
+		P_ERROR ("PUThread::p_uthread_ideal_count: failed to call __scoinfo()");
+		return 1;
+	}
+
+	return (pint) utsn.numcpu;
+#elif defined (_SC_NPROCESSORS_ONLN)
+	pint cores;
+
+	cores = (pint) sysconf (_SC_NPROCESSORS_ONLN);
+
+	if (P_UNLIKELY (cores  == -1)) {
+		P_WARNING ("PUThread::p_uthread_ideal_count: failed to call sysconf(_SC_NPROCESSORS_ONLN)");
+		return 1;
+	}
+
+	return cores;
+#else
+	return 1;
+#endif
+}
+
+P_LIB_API void
+p_uthread_ref (PUThread *thread)
+{
+	if (P_UNLIKELY (thread == NULL))
+		return;
+
+	p_atomic_int_inc (&((PUThreadBase *) thread)->ref_count);
+}
+
+P_LIB_API void
+p_uthread_unref (PUThread *thread)
+{
+	PUThreadBase *base_thread;
+
+	if (P_UNLIKELY (thread == NULL))
+		return;
+
+	base_thread = (PUThreadBase *) thread;
+
+	if (p_atomic_int_dec_and_test (&base_thread->ref_count) == TRUE) {
+		p_free (base_thread->name);
+
+		if (base_thread->ours == TRUE)
+			p_uthread_free_internal (thread);
+		else
+			p_free (thread);
+	}
+}
+
+#ifndef P_OS_WIN
+#  include <errno.h>
+#  if !defined (PLIBSYS_HAS_CLOCKNANOSLEEP) && !defined (PLIBSYS_HAS_NANOSLEEP)
+#    include <sys/select.h>
+#    include <sys/time.h>
+static pint pp_uthread_nanosleep (puint32 msec)
+{
+	pint		rc;
+	struct timeval	tstart, tstop, tremain, time2wait;
+
+	time2wait.tv_sec  = msec / 1000;
+	time2wait.tv_usec = (msec % 1000) * 1000;
+
+	if (P_UNLIKELY (gettimeofday (&tstart, NULL) != 0))
+		return -1;
+
+	rc = -1;
+
+	while (rc != 0) {
+		if (P_UNLIKELY ((rc = select (0, NULL, NULL, NULL, &time2wait)) != 0)) {
+			if (p_error_get_last_system () == EINTR) {
+				if (gettimeofday (&tstop, NULL) != 0)
+					return -1;
+
+				tremain.tv_sec = time2wait.tv_sec -
+						 (tstop.tv_sec - tstart.tv_sec);
+				tremain.tv_usec = time2wait.tv_usec -
+						  (tstop.tv_usec - tstart.tv_usec);
+				tremain.tv_sec += tremain.tv_usec / 1000000L;
+				tremain.tv_usec %= 1000000L;
+			} else
+				return -1;
+		}
+	}
+
+	return 0;
+}
+#  endif
+#endif
+
+P_LIB_API pint
+p_uthread_sleep (puint32 msec)
+{
+#if defined (P_OS_WIN)
+	Sleep (msec);
+	return 0;
+#elif defined (P_OS_OS2)
+	return (DosSleep (msec) == NO_ERROR) ? 0 : -1;
+#elif defined (P_OS_AMIGA)
+	return TimeDelay (0, msec / 1000, (msec % 1000) * 1000) == 0 ? 0 : -1;
+#elif defined (PLIBSYS_HAS_CLOCKNANOSLEEP) || defined (PLIBSYS_HAS_NANOSLEEP)
+	pint result;
+	struct timespec time_req;
+	struct timespec time_rem;
+
+	memset (&time_rem, 0, sizeof (struct timespec));
+
+	time_req.tv_nsec = (msec % 1000) * 1000000L;
+	time_req.tv_sec  = (time_t) (msec / 1000);
+
+	result = -1;
+	while (result != 0) {
+		/* Syllable has unimplemented clock_nanocleep() call */
+#  if defined (PLIBSYS_HAS_CLOCKNANOSLEEP) && !defined (P_OS_SYLLABLE)
+		if (P_UNLIKELY ((result = clock_nanosleep (CLOCK_MONOTONIC,
+							   0,
+							   &time_req,
+							   &time_rem)) != 0)) {
+#  else
+		if (P_UNLIKELY ((result = nanosleep (&time_req, &time_rem)) != 0)) {
+#  endif
+			if (p_error_get_last_system () == EINTR)
+				time_req = time_rem;
+			else
+				return -1;
+		}
+	}
+
+	return 0;
+#else
+	return pp_uthread_nanosleep (msec);
+#endif
+}
diff --git a/3rdparty/plibsys/src/puthread.h b/3rdparty/plibsys/src/puthread.h
new file mode 100644
index 0000000..2c07ac5
--- /dev/null
+++ b/3rdparty/plibsys/src/puthread.h
@@ -0,0 +1,311 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2019 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * @file puthread.h
+ * @brief Multithreading support
+ * @author Alexander Saprykin
+ *
+ * A thread is a system execution unit which is managed independently by the
+ * scheduler of the operating system. It allows to do things in parallel or
+ * concurrently.
+ *
+ * #PUThread provides a convinient way of multithreading support using native
+ * routines to provide the best performance on the target system.
+ *
+ * To create the thread use the p_uthread_create() or p_uthread_create_full()
+ * routines. Joinable threads allow to wait until their execution is finished
+ * before proceeding further. Thus you can synchronize threads' execution within
+ * the main thread.
+ *
+ * A reference counter mechanism is used to keep track of a #PUThread structure.
+ * It means that the structure will be freed automatically when the reference
+ * counter becomes zero. Use p_uthread_ref() to hold the structure and
+ * p_uthread_unref() to decrement the counter back. A running thread holds a
+ * reference to itself structure, so you do not require to hold a reference
+ * to the thread while it is running.
+ *
+ * Priorities (if supported) allow to tune scheduler behavior: threads with
+ * higher priority will be executed more frequently. Be careful that improper
+ * priorities may lead to negative effects when some threads may receive almost
+ * zero execution time.
+ *
+ * Thread priorities are unreliable: not all operating systems respect thread
+ * priorities in favour of process ones. Priorities may be ignored for bound
+ * threads (every thread bound to a kernel light-weight thread as 1:1), other
+ * systems may require administrative privileges to change the thread priority
+ * (i.e. Linux). Windows always respects thread priorities.
+ *
+ * To put the current thread (even if it was not created using the #PUThread
+ * routines) in a sleep state use p_uthread_sleep().
+ *
+ * You can give a hint to the scheduler that the current thread do not need an
+ * execution time with the p_uthread_yield() routine. This is useful when some
+ * of the threads are in an idle state so you do not want to waste a CPU time.
+ * This only tells to the scheduler to skip the current scheduling cycle for the
+ * calling thread, though the scheduler can ingnore it.
+ *
+ * A thread local storage (TLS) is provided. The TLS key's value can be accessed
+ * through a reference key defined as a #PUThreadKey. A TLS reference key is
+ * some sort of a token which has an associated value. But every thread has its
+ * own token value though using the same token object.
+ *
+ * After creating the TLS reference key every thread can use it to access a
+ * local-specific value. Use the p_uthread_local_new() call to create the TLS
+ * reference key and pass it to every thread which needs local-specific values.
+ * You can also provide a destroy notification function which would be called
+ * upon a TLS key removal which is usually performed on the thread exit.
+ *
+ * There are two calls to set a TLS key's value: p_uthread_set_local() and
+ * p_uthread_replace_local(). The only difference is that the former one calls
+ * the provided destroy notification function before replacing the old value.
+ *
+ * Thread names are used on most of operating systems for debugging purposes,
+ * thereby some limitations for long name can be applied and too long names
+ * will be truncated automatically.
+ */
+
+#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION)
+#  error "Header files shouldn't be included directly, consider using <plibsys.h> instead."
+#endif
+
+#ifndef PLIBSYS_HEADER_PUTHREAD_H
+#define PLIBSYS_HEADER_PUTHREAD_H
+
+#include <pmacros.h>
+#include <ptypes.h>
+
+P_BEGIN_DECLS
+
+/** Typedef for a #PUThread running method. */
+typedef ppointer (*PUThreadFunc) (ppointer arg);
+
+/** Thread opaque data type. */
+typedef struct PUThread_ PUThread;
+
+/** TLS key opaque data type. */
+typedef struct PUThreadKey_ PUThreadKey;
+
+/** Thread priority. */
+typedef enum PUThreadPriority_ {
+	P_UTHREAD_PRIORITY_INHERIT	= 0,	/**< Inherits the caller thread priority. Default priority.	*/
+	P_UTHREAD_PRIORITY_IDLE		= 1,	/**< Scheduled only when no other threads are running.		*/
+	P_UTHREAD_PRIORITY_LOWEST	= 2,	/**< Scheduled less often than #P_UTHREAD_PRIORITY_LOW.		*/
+	P_UTHREAD_PRIORITY_LOW		= 3,	/**< Scheduled less often than #P_UTHREAD_PRIORITY_NORMAL.	*/
+	P_UTHREAD_PRIORITY_NORMAL	= 4,	/**< Operating system's default priority.			*/
+	P_UTHREAD_PRIORITY_HIGH		= 5,	/**< Scheduled more often than #P_UTHREAD_PRIORITY_NORMAL.	*/
+	P_UTHREAD_PRIORITY_HIGHEST	= 6,	/**< Scheduled more often than #P_UTHREAD_PRIORITY_HIGH.	*/
+	P_UTHREAD_PRIORITY_TIMECRITICAL	= 7	/**< Scheduled as often as possible.				*/
+} PUThreadPriority;
+
+/**
+ * @brief Creates a new #PUThread and starts it.
+ * @param func Main thread function to run.
+ * @param data Pointer to pass into the thread main function, may be NULL.
+ * @param joinable Whether to create a joinable thread or not.
+ * @param prio Thread priority.
+ * @param stack_size Thread stack size, in bytes. Leave zero to use a default
+ * value.
+ * @param name Thread name, maybe NULL.
+ * @return Pointer to #PUThread in case of success, NULL otherwise.
+ * @since 0.0.1
+ * @note Unreference the returned value after use with p_uthread_unref(). You do
+ * not need to call p_uthread_ref() explicitly on the returned value.
+ */
+P_LIB_API PUThread *	p_uthread_create_full	(PUThreadFunc		func,
+						 ppointer		data,
+						 pboolean		joinable,
+						 PUThreadPriority	prio,
+						 psize			stack_size,
+						 const pchar		*name);
+
+/**
+ * @brief Creates a #PUThread and starts it. A short version of
+ * p_uthread_create_full().
+ * @param func Main thread function to run.
+ * @param data Pointer to pass into the thread main function, may be NULL.
+ * @param joinable Whether to create a joinable thread or not.
+ * @param name Thread name, maybe NULL.
+ * @return Pointer to #PUThread in case of success, NULL otherwise.
+ * @since 0.0.1
+ * @note Unreference the returned value after use with p_uthread_unref(). You do
+ * not need to call p_uthread_ref() explicitly on the returned value.
+ */
+P_LIB_API PUThread *	p_uthread_create	(PUThreadFunc		func,
+						 ppointer		data,
+						 pboolean		joinable,
+						 const pchar		*name);
+
+/**
+ * @brief Exits from the currently running (caller) thread.
+ * @param code Exit code.
+ * @since 0.0.1
+ */
+P_LIB_API void		p_uthread_exit		(pint			code);
+
+/**
+ * @brief Waits for the selected thread to become finished.
+ * @param thread Thread to wait for.
+ * @return Thread exit code in case of success, -1 otherwise.
+ * @since 0.0.1
+ * @note Thread must be joinable to return the non-negative result.
+ */
+P_LIB_API pint		p_uthread_join		(PUThread		*thread);
+
+/**
+ * @brief Sleeps the current thread (caller) for a specified amount of time.
+ * @param msec Milliseconds to sleep.
+ * @return 0 in case of success, -1 otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API pint		p_uthread_sleep		(puint32		msec);
+
+/**
+ * @brief Sets a thread priority.
+ * @param thread Thread to set the priority for.
+ * @param prio Priority to set.
+ * @return TRUE in case of success, FALSE otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API pboolean	p_uthread_set_priority	(PUThread		*thread,
+						 PUThreadPriority	prio);
+
+/**
+ * @brief Tells the scheduler to skip the current (caller) thread in the current
+ * planning stage.
+ * @since 0.0.1
+ *
+ * The scheduler shouldn't give time ticks for the current thread during the
+ * current period, but it may ignore this call.
+ */
+P_LIB_API void		p_uthread_yield		(void);
+
+/**
+ * @brief Gets an ID of the current (caller) thread.
+ * @return The ID of the current thread.
+ * @since 0.0.1
+ *
+ * This is a platform-dependent type. You shouldn't treat it as a number, it
+ * only gives you the uniquer ID of the thread accross the system.
+ */
+P_LIB_API P_HANDLE	p_uthread_current_id	(void);
+
+/**
+ * @brief Gets a thread structure of the current (caller) thread.
+ * @return The thread structure of the current thread.
+ * @since 0.0.1
+ * @note This call doesn't not increment the reference counter of the returned
+ * structure.
+ *
+ * A thread structure will be returned even for the thread which was created
+ * outside the library. But you should not use thread manipulation routines over
+ * that structure.
+ */
+P_LIB_API PUThread *	p_uthread_current	(void);
+
+/**
+ * @brief Gets the ideal number of threads for the system based on the number of
+ * avaialble CPUs and cores (physical and logical).
+ * @return Ideal number of threads, 1 in case of failed detection.
+ * @since 0.0.3
+ */
+P_LIB_API pint		p_uthread_ideal_count	(void);
+
+/**
+ * @brief Increments a thread reference counter
+ * @param thread #PUThread to increment the reference counter.
+ * @since 0.0.1
+ * @note The #PUThread object will not be removed until the reference counter is
+ * positive.
+ */
+P_LIB_API void		p_uthread_ref		(PUThread		*thread);
+
+/**
+ * @brief Decrements a thread reference counter
+ * @param thread #PUThread to decrement the reference counter.
+ * @since 0.0.1
+ * @note When the reference counter becomes zero the #PUThread is removed from
+ * the memory.
+ */
+P_LIB_API void		p_uthread_unref		(PUThread		*thread);
+
+/**
+ * @brief Create a new TLS reference key.
+ * @param free_func TLS key destroy notification call, leave NULL if not need.
+ * @return New TLS reference key in case of success, NULL otherwise.
+ * @since 0.0.1
+ */
+P_LIB_API PUThreadKey *	p_uthread_local_new	(PDestroyFunc		free_func);
+
+/**
+ * @brief Frees a TLS reference key.
+ * @param key TLS reference key to free.
+ * @since 0.0.1
+ *
+ * It doesn't remove the TLS key itself but only removes a reference used to
+ * access the TLS slot.
+ */
+P_LIB_API void		p_uthread_local_free	(PUThreadKey		*key);
+
+/**
+ * @brief Gets a TLS value.
+ * @param key TLS reference key to get the value for.
+ * @return TLS value for the given key.
+ * @since 0.0.1
+ * @note This call may fail only in case of abnormal use or program behavior,
+ * the NULL value will be returned to tolerance the failure.
+ */
+P_LIB_API ppointer	p_uthread_get_local	(PUThreadKey		*key);
+
+/**
+ * @brief Sets a TLS value.
+ * @param key TLS reference key to set the value for.
+ * @param value TLS value to set.
+ * @since 0.0.1
+ * @note This call may fail only in case of abnormal use or program behavior.
+ *
+ * It doesn't call the destructor notification function provided with
+ * p_uthread_local_new().
+ */
+P_LIB_API void		p_uthread_set_local	(PUThreadKey		*key,
+						 ppointer		value);
+
+/**
+ * @brief Replaces a TLS value.
+ * @param key TLS reference key to replace the value for.
+ * @param value TLS value to set.
+ * @since 0.0.1
+ * @note This call may fail only in case of abnormal use or program behavior.
+ *
+ * This call does perform the notification function provided with
+ * p_uthread_local_new() on the old TLS value. This is the only difference with
+ * p_uthread_set_local().
+ */
+P_LIB_API void		p_uthread_replace_local	(PUThreadKey		*key,
+						 ppointer		value);
+
+P_END_DECLS
+
+#endif /* PLIBSYS_HEADER_PUTHREAD_H */
-- 
cgit v1.2.1